From 9633c6f50fafbf7c59c52095b35212511825b01e Mon Sep 17 00:00:00 2001 From: Anthony Leonardo Gracio Date: Fri, 7 Feb 2025 16:31:13 +0000 Subject: [PATCH 1/5] Format file For eng/ide/ada_language_server#1515 --- integration/vscode/ada/ada-snippets.json | 357 +++++++++-------------- 1 file changed, 134 insertions(+), 223 deletions(-) diff --git a/integration/vscode/ada/ada-snippets.json b/integration/vscode/ada/ada-snippets.json index c83d95d98..d7c140091 100644 --- a/integration/vscode/ada/ada-snippets.json +++ b/integration/vscode/ada/ada-snippets.json @@ -2,27 +2,19 @@ "Accept Statement": { "prefix": "accept", "scope": "ada", - "body": [ - "accept ${1:Name} do", - "\t$0", - "end ${1:Name};" - ], + "body": ["accept ${1:Name} do", "\t$0", "end ${1:Name};"], "description": "Accept Statement" }, "Access Type Definition": { "prefix": "access", "scope": "ada", - "body": [ - "type ${1:Name} is ${2|access,access all,access constant|} of ${3:Some_Type};" - ], + "body": ["type ${1:Name} is ${2|access,access all,access constant|} of ${3:Some_Type};"], "description": "Access Type Definition" }, "Array Type Definition": { "prefix": "array", "scope": "ada", - "body": [ - "type ${1:Name} is array($2) of ${3:Element_Type};" - ], + "body": ["type ${1:Name} is array($2) of ${3:Element_Type};"], "description": "Array Type Definition" }, "Aspect Specification": { @@ -34,98 +26,73 @@ "description": "Aspect Specification" }, "Case Statement": { - "prefix": "case", - "scope": "ada", - "body": [ - "case ${1:Variable} is", - "\twhen ${3:Condition} =>", - "\t\t$0", - "\twhen others =>", - "\t\t$2", - "end case;" - ], - "description": "Case Statement" - }, - "Extended Return Statement": { - "prefix": "return", + "prefix": "case", + "scope": "ada", "body": [ - "return ${1:Result} : $2 do", - "\t$0", - "end return;" + "case ${1:Variable} is", + "\twhen ${3:Condition} =>", + "\t\t$0", + "\twhen others =>", + "\t\t$2", + "end case;" ], + "description": "Case Statement" + }, + "Extended Return Statement": { + "prefix": "return", + "body": ["return ${1:Result} : $2 do", "\t$0", "end return;"], "description": "Extended Return Statement" }, "Declare Statement": { "prefix": "declare", - "body": [ - "declare", - "\t$1", - "begin", - "\t$0", - "end;" - ], + "body": ["declare", "\t$1", "begin", "\t$0", "end;"], "description": "Declare Statement" }, "Elsif Statement": { - "prefix": "elsif", - "scope": "ada", - "body": [ - "elsif ${1:Condition} then", - "\t$0" - ], - "description": "Elsif Statement" + "prefix": "elsif", + "scope": "ada", + "body": ["elsif ${1:Condition} then", "\t$0"], + "description": "Elsif Statement" }, "Entry Spec": { - "prefix": "entry-spec", - "scope": "ada", - "body": [ - "entry ${1:Name};" - ], - "description": "Entry Specification" - }, - "Entry Body": { - "prefix": "entry-body", - "scope": "ada", - "body": [ - "entry ${1:Name} when ${2:Guard_Condition} is", - "\t$3", - "begin", - "\t$0", - "end ${1:Name};" - ], - "description": "Entry Body" + "prefix": "entry-spec", + "scope": "ada", + "body": ["entry ${1:Name};"], + "description": "Entry Specification" + }, + "Entry Body": { + "prefix": "entry-body", + "scope": "ada", + "body": [ + "entry ${1:Name} when ${2:Guard_Condition} is", + "\t$3", + "begin", + "\t$0", + "end ${1:Name};" + ], + "description": "Entry Body" }, "Enumeration Type Definition": { - "prefix": "enumeration", - "scope": "ada", - "body": [ - "type ${1:Name} is ($0);" - ], - "description": "Enumeration Type Definition" + "prefix": "enumeration", + "scope": "ada", + "body": ["type ${1:Name} is ($0);"], + "description": "Enumeration Type Definition" }, "Exit": { - "prefix": "exit", - "scope":"ada", - "body": [ - "exit ${1:Loop Name};" - ], - "description": "Exit statement" - }, - "Exit When": { - "prefix": "exit-when", - "scope": "ada", - "body": [ - "exit ${1:Loop Name} when ${2:Condition};" - ], - "description": "Exit when statement" - }, + "prefix": "exit", + "scope": "ada", + "body": ["exit ${1:Loop Name};"], + "description": "Exit statement" + }, + "Exit When": { + "prefix": "exit-when", + "scope": "ada", + "body": ["exit ${1:Loop Name} when ${2:Condition};"], + "description": "Exit when statement" + }, "For Loop Statement": { "prefix": "for", - "body": [ - "for ${1:J} ${2|in,in reverse,of,of reverse|} loop", - "\t$0", - "end loop;" - ], + "body": ["for ${1:J} ${2|in,in reverse,of,of reverse|} loop", "\t$0", "end loop;"], "description": "For Loop Statement" }, "Function Declaration": { @@ -147,63 +114,51 @@ "description": "Function Body" }, "Generic Formal Part": { - "prefix": "generic", - "scope": "ada", - "body": [ - "generic", - "\t$1", - "$0" - ], - "description": "Generic Formal Part" + "prefix": "generic", + "scope": "ada", + "body": ["generic", "\t$1", "$0"], + "description": "Generic Formal Part" }, "If Statement": { - "prefix": "if", - "scope": "ada", - "body": [ - "if ${1:Condition} then", - "\t$0", - "end if;" - ], - "description": "If Statement" + "prefix": "if", + "scope": "ada", + "body": ["if ${1:Condition} then", "\t$0", "end if;"], + "description": "If Statement" }, "Interface Type Definition": { - "prefix": "interface", - "scope": "ada", - "body": [ - "type ${1:Name} is ${2|interface,limited interface,synchronized interface,task interface,protected interface|};" - ], - "description": "Interface Type Definition" + "prefix": "interface", + "scope": "ada", + "body": [ + "type ${1:Name} is ${2|interface,limited interface,synchronized interface,task interface,protected interface|};" + ], + "description": "Interface Type Definition" }, "Loop Statement": { - "prefix": "loop", - "scope": "ada", - "body": [ - "loop", - "\t$0", - "end loop;" - ], - "description": "Loop Statement" + "prefix": "loop", + "scope": "ada", + "body": ["loop", "\t$0", "end loop;"], + "description": "Loop Statement" }, "Operator Declaration": { - "prefix": "operator", - "scope": "ada", - "body": [ - "function \"${1|and,or,xor,=,<,<=,>,>=,+,-,&,*,/,mod,rem,**,not,abs|}\"($2) return ${3:Return_Type};" - ], - "description": "Operator Declaration" - }, + "prefix": "operator", + "scope": "ada", + "body": [ + "function \"${1|and,or,xor,=,<,<=,>,>=,+,-,&,*,/,mod,rem,**,not,abs|}\"($2) return ${3:Return_Type};" + ], + "description": "Operator Declaration" + }, "Operator Body": { - "prefix": "operator", - "scope": "ada", - "body": [ - "function \"${1|and,or,xor,=,<,<=,>,>=,+,-,&,*,/,mod,rem,**,not,abs|}\"($2) return ${3:Return_Type} is", - "\t$2", - "begin", - "\t$0", - "end ${1:Name};" - ], - "description": "Operator Body" - }, + "prefix": "operator", + "scope": "ada", + "body": [ + "function \"${1|and,or,xor,=,<,<=,>,>=,+,-,&,*,/,mod,rem,**,not,abs|}\"($2) return ${3:Return_Type} is", + "\t$2", + "begin", + "\t$0", + "end ${1:Name};" + ], + "description": "Operator Body" + }, "Package Declaration or Body": { "prefix": "package", "body": [ @@ -215,13 +170,13 @@ "description": "Package Declaration or Body" }, "Pragma Directive": { - "prefix": "pragma", - "scope": "ada", - "body": [ - "pragma ${1|Abort_Defer,Ada_83,Ada_95,Ada_2005,Ada_2012,All_Calls_Remote,Annotate,Assert,Assertion_Policy,Assume_No_Invalid_Values,Ast_Entry,Canonical_Streams,Check,Check_Name,Check_Policy,Comment,Common_Object,Compile_Time_Error,Compile_Time_Warning,Complete_Representation,Complex_Representation,Component_Alignment,Convention_Identifier,CPP_Class,CPP_Constructor,CPP_Virtual,CPP_VTable,CPU,C_Pass_By_Copy,Debug,Debug_Policy,Default_Storage_Pool,Detect_Blocking,Discard_Names,Dispatching_Domain,Elaborate,Elaborate_All,Elaborate_Body,Elaboration_Checks,Eliminate,Export_Exception,Export_Function,Export_Object,Export_PRocedure,Export_Value,Export_Valued_Procedure,Extend_System,Extensions_Allowed,External,External_Name_Casing,Fast_Math,Favor_Top_Level,Finalize_Storage_Only,Float_Representation,Ident,Implemented,Implicit_Packing,Import_Exception,Import_Function,Import_Object,Import_Procedure,Import_Valued_Procedure,Independent,Independent_Component,Initialize_Scalars,Inline_Always,Inline_Generic,Inspection_Point,Interface_Name,Interrupt_State,Invariant,Keep_Names,License,Linker_Alias,Linker_Constructor,Linker_Destructor,Linker_Options,Linker_Section,Link_With,List,Locking_Policy,Long_Float,Machine_Attribute,Main,Main_Storage,Normalize_Scalars,No_Body,No_StricT_Aliasing,Obsolescent,Optimize,Optimize_Alignment,Ordered,Page,Partition_Elaboration_Policy,Passive,Persistent_BSS,Polling,Postcondition,Precondition,Preelaborate,Priority_Specific_Dispatching,Profile,Profile_Warnings,Propagate_Exceptions,Psect_Object,Pure,Pure_Function,Queueing_Policy,Relative_Deadline,Remote_Call_Interface,Remote_Types,Restrictions,Restriction_Warnings,Reviewable,Shared_Passive,Share_Generic,Short_Circuit_And_Or,Short_Descriptors,Simple_Storage_Pool_Type,Source_File_Name,Source_File_Name_Project,Source_Reference,Static_Elaboration_Desired,Storage_Size,Stream_Convert,Style_Checks,Subtitle,Suppress,Suppress_All,Suppress_Exception_Locations,Suppress_Initialization,Task_Dispatching_Policy,Task_Info,Task_Name,Task_Storage,Test_Case,Thread_Body,Thread_Local_Storage,Time_Slice,Title,Unchecked_Union,Unimplemented_Unit,Universal_Aliasing,Universal_Data,Unmodified,Unreferenced,Unreferenced_Objects,Unreserve_All_Interrupts,Unsuppress,Use_VADS_Size,Validity_Checks,Volatile,Volatile_Components,Warnings,Weak_External,Wide_Character_Encoding|}$0;" - ], - "description": "Pragma Directive" - }, + "prefix": "pragma", + "scope": "ada", + "body": [ + "pragma ${1|Abort_Defer,Ada_83,Ada_95,Ada_2005,Ada_2012,All_Calls_Remote,Annotate,Assert,Assertion_Policy,Assume_No_Invalid_Values,Ast_Entry,Canonical_Streams,Check,Check_Name,Check_Policy,Comment,Common_Object,Compile_Time_Error,Compile_Time_Warning,Complete_Representation,Complex_Representation,Component_Alignment,Convention_Identifier,CPP_Class,CPP_Constructor,CPP_Virtual,CPP_VTable,CPU,C_Pass_By_Copy,Debug,Debug_Policy,Default_Storage_Pool,Detect_Blocking,Discard_Names,Dispatching_Domain,Elaborate,Elaborate_All,Elaborate_Body,Elaboration_Checks,Eliminate,Export_Exception,Export_Function,Export_Object,Export_PRocedure,Export_Value,Export_Valued_Procedure,Extend_System,Extensions_Allowed,External,External_Name_Casing,Fast_Math,Favor_Top_Level,Finalize_Storage_Only,Float_Representation,Ident,Implemented,Implicit_Packing,Import_Exception,Import_Function,Import_Object,Import_Procedure,Import_Valued_Procedure,Independent,Independent_Component,Initialize_Scalars,Inline_Always,Inline_Generic,Inspection_Point,Interface_Name,Interrupt_State,Invariant,Keep_Names,License,Linker_Alias,Linker_Constructor,Linker_Destructor,Linker_Options,Linker_Section,Link_With,List,Locking_Policy,Long_Float,Machine_Attribute,Main,Main_Storage,Normalize_Scalars,No_Body,No_StricT_Aliasing,Obsolescent,Optimize,Optimize_Alignment,Ordered,Page,Partition_Elaboration_Policy,Passive,Persistent_BSS,Polling,Postcondition,Precondition,Preelaborate,Priority_Specific_Dispatching,Profile,Profile_Warnings,Propagate_Exceptions,Psect_Object,Pure,Pure_Function,Queueing_Policy,Relative_Deadline,Remote_Call_Interface,Remote_Types,Restrictions,Restriction_Warnings,Reviewable,Shared_Passive,Share_Generic,Short_Circuit_And_Or,Short_Descriptors,Simple_Storage_Pool_Type,Source_File_Name,Source_File_Name_Project,Source_Reference,Static_Elaboration_Desired,Storage_Size,Stream_Convert,Style_Checks,Subtitle,Suppress,Suppress_All,Suppress_Exception_Locations,Suppress_Initialization,Task_Dispatching_Policy,Task_Info,Task_Name,Task_Storage,Test_Case,Thread_Body,Thread_Local_Storage,Time_Slice,Title,Unchecked_Union,Unimplemented_Unit,Universal_Aliasing,Universal_Data,Unmodified,Unreferenced,Unreferenced_Objects,Unreserve_All_Interrupts,Unsuppress,Use_VADS_Size,Validity_Checks,Volatile,Volatile_Components,Warnings,Weak_External,Wide_Character_Encoding|}$0;" + ], + "description": "Pragma Directive" + }, "Procedure Declaration": { "prefix": "procedure", "body": [ @@ -241,104 +196,60 @@ "description": "Procedure Body" }, "Protected Type Declaration": { - "prefix": "protected", - "scope": "ada", - "body": [ - "protected ${1:Name} is", - "\t$0", - "private", - "\t", - "end ${1:Name};" - ], - "description": "Protected Declaration" + "prefix": "protected", + "scope": "ada", + "body": ["protected ${1:Name} is", "\t$0", "private", "\t", "end ${1:Name};"], + "description": "Protected Declaration" }, "Protected Type Body": { - "prefix": "protected-body", - "scope": "ada", - "body": [ - "protected body ${1:Name} is", - "\t$0", - "end ${1:Name};" - ], - "description": "Protected Body" + "prefix": "protected-body", + "scope": "ada", + "body": ["protected body ${1:Name} is", "\t$0", "end ${1:Name};"], + "description": "Protected Body" }, "Record Type Definition": { - "prefix": "record", - "scope": "ada", - "body": [ - "type ${1:Name} is ${2|record,tagged record,abstract tagged record,limited record,tagged limited record,abstract tagged limited record|}", - "\t$0", - "end record;" - ], - "description": "Record Type Definition" + "prefix": "record", + "scope": "ada", + "body": [ + "type ${1:Name} is ${2|record,tagged record,abstract tagged record,limited record,tagged limited record,abstract tagged limited record|}", + "\t$0", + "end record;" + ], + "description": "Record Type Definition" }, "Select Statement": { - "prefix": "select", - "scope": "ada", - "body": [ - "select", - "\t$0", - "${1|or,else|}", - "\t", - "end select;" - ], - "description": "Select Statement" + "prefix": "select", + "scope": "ada", + "body": ["select", "\t$0", "${1|or,else|}", "\t", "end select;"], + "description": "Select Statement" }, "Select Statement - Asynchronous": { - "prefix": "select", - "scope": "ada", - "body": [ - "select", - "\t$0", - "then abort", - "\t", - "end select;" - ], - "description": "Asynchronous Select Statement" + "prefix": "select", + "scope": "ada", + "body": ["select", "\t$0", "then abort", "\t", "end select;"], + "description": "Asynchronous Select Statement" }, "Select Statement - Timed": { - "prefix": "select", - "scope": "ada", - "body": [ - "select", - "\t$0", - "or", - "\tdelay ${1:Delay_Amount};", - "end select;" - ], - "description": "Timed Select Statement" + "prefix": "select", + "scope": "ada", + "body": ["select", "\t$0", "or", "\tdelay ${1:Delay_Amount};", "end select;"], + "description": "Timed Select Statement" }, "Task Definition": { - "prefix": "task", - "scope": "ada", - "body": [ - "${1|task,task type|} ${2:Name} is", - "\t$0", - "private", - "\t", - "end ${2:Name};" - ], - "description": "Task Definition" + "prefix": "task", + "scope": "ada", + "body": ["${1|task,task type|} ${2:Name} is", "\t$0", "private", "\t", "end ${2:Name};"], + "description": "Task Definition" }, "Task Body": { - "prefix": "task-body", - "scope": "ada", - "body": [ - "task body ${1:Name} is", - "\t$2", - "begin", - "\t$0", - "end ${1:Name};" - ], - "description": "Task body" - }, + "prefix": "task-body", + "scope": "ada", + "body": ["task body ${1:Name} is", "\t$2", "begin", "\t$0", "end ${1:Name};"], + "description": "Task body" + }, "While Loop Statement": { "prefix": "while", - "body": [ - "while ${1:Condition} loop", - "\t$0", - "end loop;" - ], + "body": ["while ${1:Condition} loop", "\t$0", "end loop;"], "description": "While Loop Statement" } } From 4731c8a9d57dfca7423b33371050103da36ccfa1 Mon Sep 17 00:00:00 2001 From: Anthony Leonardo Gracio Date: Fri, 7 Feb 2025 16:32:59 +0000 Subject: [PATCH 2/5] Add a 'Main Procedure' file template And transform the package snipper into a file template too. This has for effect to show these two snippets when running the 'Snippets: Fill File with Snippet' VS Code command, which is how VS Code provides file templates. For eng/ide/ada_language_server#1515 --- integration/vscode/ada/ada-snippets.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/integration/vscode/ada/ada-snippets.json b/integration/vscode/ada/ada-snippets.json index d7c140091..202dd7b7e 100644 --- a/integration/vscode/ada/ada-snippets.json +++ b/integration/vscode/ada/ada-snippets.json @@ -139,6 +139,12 @@ "body": ["loop", "\t$0", "end loop;"], "description": "Loop Statement" }, + "Main Procedure": { + "prefix": "procedure", + "body": ["procedure ${2:Name} is", "\t$4", "begin", "\t$0", "end ${2:Name};"], + "description": "Main Procedure", + "isFileTemplate": true + }, "Operator Declaration": { "prefix": "operator", "scope": "ada", @@ -167,7 +173,8 @@ "private", "end ${2:Name};" ], - "description": "Package Declaration or Body" + "description": "Package Declaration or Body", + "isFileTemplate": true }, "Pragma Directive": { "prefix": "pragma", From 5eabb82afb20a310ab06293e64e5404d16378a9f Mon Sep 17 00:00:00 2001 From: Anthony Leonardo Gracio Date: Mon, 10 Feb 2025 14:25:26 +0000 Subject: [PATCH 3/5] Add a getSourceDirs function helper To encapsulate the 'als-source-dirs' LSP request. For eng/ide/ada_language_server#1515 --- integration/vscode/ada/src/ExtensionState.ts | 24 ++++++++++++++++++++ integration/vscode/ada/src/commands.ts | 11 ++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/integration/vscode/ada/src/ExtensionState.ts b/integration/vscode/ada/src/ExtensionState.ts index 8c999923d..d2dccff13 100644 --- a/integration/vscode/ada/src/ExtensionState.ts +++ b/integration/vscode/ada/src/ExtensionState.ts @@ -25,6 +25,14 @@ import { } from './taskProviders'; import { isAbsolute } from 'path'; +/** + * Return type of the 'als-source-dirs' LSP request. + */ +export type ALSSourceDirDescription = { + name: string; + uri: string; +}; + /** * This class encapsulates all state that should be maintained throughout the * lifecyle of the extension. This includes e.g. the Ada and GPR LSP clients, @@ -56,6 +64,7 @@ export class ExtensionState { */ cachedProjectUri: vscode.Uri | undefined; cachedObjectDir: string | undefined; + cachedSourceDirs: ALSSourceDirDescription[] | undefined; cachedTargetPrefix: string | undefined; cachedMains: string[] | undefined; cachedExecutables: string[] | undefined; @@ -68,6 +77,7 @@ export class ExtensionState { private clearALSCache() { this.cachedProjectUri = undefined; this.cachedObjectDir = undefined; + this.cachedSourceDirs = undefined; this.cachedTargetPrefix = undefined; this.cachedMains = undefined; this.cachedExecutables = undefined; @@ -381,6 +391,20 @@ export class ExtensionState { return this.cachedObjectDir; } + /** + * + * @returns the list of source directorues defined in the project loaded by the ALS + */ + public async getSourceDirs(): Promise { + if (this.cachedSourceDirs === undefined) { + this.cachedSourceDirs = (await this.adaClient.sendRequest(ExecuteCommandRequest.type, { + command: 'als-source-dirs', + })) as ALSSourceDirDescription[]; + } + + return this.cachedSourceDirs; + } + /** * * @returns the list of full paths of main sources defined in the project from the ALS diff --git a/integration/vscode/ada/src/commands.ts b/integration/vscode/ada/src/commands.ts index e0df018ca..bcb870fd9 100644 --- a/integration/vscode/ada/src/commands.ts +++ b/integration/vscode/ada/src/commands.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { SymbolKind, commands } from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; import { ExecuteCommandRequest } from 'vscode-languageclient'; -import { ExtensionState } from './ExtensionState'; +import { ALSSourceDirDescription, ExtensionState } from './ExtensionState'; import { AdaConfig, getOrAskForProgram, initializeConfig } from './debugConfigProvider'; import { adaExtState, logger, mainOutputChannel } from './extension'; import { findAdaMain, getProjectFileRelPath, getSymbols } from './helpers'; @@ -464,7 +464,6 @@ export async function checkSrcDirectories(atStartup = false, displayYesNoPopup = }; const foldersInSettings = vscode.workspace.getConfiguration().get('folders'); - const alsClient = adaExtState.adaClient; const doNotShowAgainKey = 'ada.addMissingDirsToWorkspace.doNotShowAgain'; const doNotShowAgain = adaExtState.context.workspaceState.get(doNotShowAgainKey); @@ -473,13 +472,7 @@ export async function checkSrcDirectories(atStartup = false, displayYesNoPopup = // triggered at startup while the user previously clicked on the // 'Don't show again' button for this workspace if (foldersInSettings === undefined && !(atStartup && doNotShowAgain)) { - const sourceDirs: ALSSourceDirDescription[] = (await alsClient.sendRequest( - ExecuteCommandRequest.type, - { - command: 'als-source-dirs', - }, - )) as ALSSourceDirDescription[]; - + const sourceDirs: ALSSourceDirDescription[] = await adaExtState.getSourceDirs(); const isSubdirectory = (dir: string, parent: string) => { // Use lower-case on Windows since drives can be specified in VS Code // either with lower or upper case characters. From b8f39c9cbf40409faff08fe42b3b3bc1da02fb88 Mon Sep 17 00:00:00 2001 From: Anthony Leonardo Gracio Date: Fri, 7 Feb 2025 17:38:12 +0000 Subject: [PATCH 4/5] Add commands to create new Ada files These commands are displayed under VS Code's 'File->New File...' toplevel menu, providing file templates based on snippets for main and package units. For eng/ide/ada_language_server#1515 --- integration/vscode/ada/package.json | 20 ++++++++++ integration/vscode/ada/src/ExtensionState.ts | 2 +- integration/vscode/ada/src/commands.ts | 31 ++++++++++++--- .../vscode/ada/test/general/extension.test.ts | 39 +++++++++++++++++++ 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/integration/vscode/ada/package.json b/integration/vscode/ada/package.json index 050efbdfd..c8bfe39e2 100644 --- a/integration/vscode/ada/package.json +++ b/integration/vscode/ada/package.json @@ -768,6 +768,16 @@ } ], "commands": [ + { + "command": "ada.createNewAdaMainUnit", + "title": "Ada: Create Main Unit", + "shortTitle": "Ada Main Unit" + }, + { + "command": "ada.createNewAdaPackage", + "title": "Ada: Create Package", + "shortTitle": "Ada Package" + }, { "command": "ada.otherFile", "title": "Ada: Go to other file" @@ -908,6 +918,16 @@ "when": "editorLangId == ada", "group": "navigation@1" } + ], + "file/newFile": [ + { + "command": "ada.createNewAdaMainUnit", + "group": "ada" + }, + { + "command": "ada.createNewAdaPackage", + "group": "ada" + } ] }, "configurationDefaults": { diff --git a/integration/vscode/ada/src/ExtensionState.ts b/integration/vscode/ada/src/ExtensionState.ts index d2dccff13..2c4cd5ec7 100644 --- a/integration/vscode/ada/src/ExtensionState.ts +++ b/integration/vscode/ada/src/ExtensionState.ts @@ -393,7 +393,7 @@ export class ExtensionState { /** * - * @returns the list of source directorues defined in the project loaded by the ALS + * @returns the list of source directories defined in the project loaded by the ALS */ public async getSourceDirs(): Promise { if (this.cachedSourceDirs === undefined) { diff --git a/integration/vscode/ada/src/commands.ts b/integration/vscode/ada/src/commands.ts index bcb870fd9..46127eade 100644 --- a/integration/vscode/ada/src/commands.ts +++ b/integration/vscode/ada/src/commands.ts @@ -80,6 +80,16 @@ export function registerCommands(context: vscode.ExtensionContext, clients: Exte vscode.commands.registerCommand('ada.walkthroughStartDebugging', walkthroughStartDebugging), ); context.subscriptions.push(vscode.commands.registerCommand('ada.otherFile', otherFileHandler)); + context.subscriptions.push( + vscode.commands.registerCommand('ada.createNewAdaMainUnit', () => + createNewAdaFile('Main Procedure'), + ), + ); + context.subscriptions.push( + vscode.commands.registerCommand('ada.createNewAdaPackage', () => + createNewAdaFile('Package Declaration or Body'), + ), + ); context.subscriptions.push( vscode.commands.registerCommand('ada.subprogramBox', addSubprogramBoxCommand), ); @@ -429,6 +439,22 @@ async function buildAndRunMainAsk() { } } +/** + * Handler for commands that create new Ada files. + * This function creates a new Ada editor, focus it, and insert the specified snippet. + * Used to proivide Ada file templates. + * + * @param snippetName - the name of the snippet to insert in the newly created editor. + */ +async function createNewAdaFile(snippetName: string) { + const doc = await vscode.workspace.openTextDocument({ language: 'ada' }); + await vscode.window.showTextDocument(doc); + await vscode.commands.executeCommand('editor.action.insertSnippet', { + langId: 'ada', + name: snippetName, + }); +} + // Take active editor URI and call execute 'als-other-file' command in LSP const otherFileHandler = () => { const activeEditor = vscode.window.activeTextEditor; @@ -458,11 +484,6 @@ const otherFileHandler = () => { * when missing directories */ export async function checkSrcDirectories(atStartup = false, displayYesNoPopup = true) { - type ALSSourceDirDescription = { - name: string; - uri: string; - }; - const foldersInSettings = vscode.workspace.getConfiguration().get('folders'); const doNotShowAgainKey = 'ada.addMissingDirsToWorkspace.doNotShowAgain'; const doNotShowAgain = adaExtState.context.workspaceState.get(doNotShowAgainKey); diff --git a/integration/vscode/ada/test/general/extension.test.ts b/integration/vscode/ada/test/general/extension.test.ts index 75736571f..bd62a3f26 100644 --- a/integration/vscode/ada/test/general/extension.test.ts +++ b/integration/vscode/ada/test/general/extension.test.ts @@ -68,6 +68,45 @@ suite('Extensions Test Suite', function () { throw new Error('No workspace folder found for the specified URI'); } }); + + test('New File - Ada Main', async () => { + if (vscode.workspace.workspaceFolders !== undefined) { + await vscode.commands.executeCommand('ada.createNewAdaMainUnit'); + const activeEditor = vscode.window.activeTextEditor; + assert.strictEqual( + activeEditor?.document.languageId, + 'ada', + 'We should have a newly created Ada editor', + ); + const text = activeEditor.document.getText() ?? ''; + assert.strictEqual( + text.startsWith('procedure'), + true, + `We should have a main procedure snippet at the beginning ` + + `of the new editor, but instead we have:\n\n${text}`, + ); + } + }); + + test('New File - Ada Package', async () => { + if (vscode.workspace.workspaceFolders !== undefined) { + await vscode.commands.executeCommand('ada.createNewAdaPackage'); + const activeEditor = vscode.window.activeTextEditor; + assert.strictEqual( + activeEditor?.document.languageId, + 'ada', + 'We should have a newly created Ada editor', + ); + const text = activeEditor.document.getText() ?? ''; + assert.strictEqual( + text.startsWith('package'), + true, + `We should have a package declaration snippet at the beginning ` + + `of the new editor, but instead we have:\n\n${text}`, + ); + } + }); + test('Clear Cache On Project Reload', async () => { if (vscode.workspace.workspaceFolders !== undefined) { // Get the workspace root folder From faf4e9e35423720326c8da93645e5bf0059c39ab Mon Sep 17 00:00:00 2001 From: Anthony Leonardo Gracio Date: Tue, 11 Feb 2025 14:36:34 +0000 Subject: [PATCH 5/5] Remove pragmas from package snippet The user is free to add the pragmas that he wants after creating the package file. For eng/ide/ada_language_server#1515 --- CHANGELOG.md | 1 + integration/vscode/ada/ada-snippets.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a90646a69..1422627cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ section below it for the last release. --> * [GNATformat](https://github.com/AdaCore/gnatformat) is now the default back-end for LSP formatting requests * Add tasks and a CodeLens to run a given main with GNATemulator on non-native projects * Diagnostics are now emitted for issues encountered when trying to load an Alire crate +* Commands to create a new main units and packages have been added, both available under the `File->New File...` menu ## 26.0.202412190 diff --git a/integration/vscode/ada/ada-snippets.json b/integration/vscode/ada/ada-snippets.json index 202dd7b7e..d2abd8050 100644 --- a/integration/vscode/ada/ada-snippets.json +++ b/integration/vscode/ada/ada-snippets.json @@ -169,8 +169,9 @@ "prefix": "package", "body": [ "${1|package,package body|} ${2:Name} is", - "\t${3|pragma Preelaborate;,pragma Pure;|}$0", + "\t$0", "private", + "\t", "end ${2:Name};" ], "description": "Package Declaration or Body",