Skip to content

Commit d274a05

Browse files
V809-013: Add 'subprogram box' command
The command is based on the LSP DocumentSymbols request to retrieve the enclosing subprogram, if any.
1 parent d6b0d92 commit d274a05

File tree

3 files changed

+76
-13
lines changed

3 files changed

+76
-13
lines changed

integration/vscode/ada/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@
418418
{
419419
"command": "ada.otherFile",
420420
"title": "Go to other Ada file"
421+
},
422+
{
423+
"command": "ada.subprogramBox",
424+
"title": "Add subprogram box"
421425
}
422426
],
423427
"keybindings": [
@@ -461,4 +465,4 @@
461465
"vscode-languageclient": "7.0.0",
462466
"ws": "8.5.0"
463467
}
464-
}
468+
}

integration/vscode/ada/src/extension.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import * as process from 'process';
2727
import GPRTaskProvider from './gprTaskProvider';
2828
import cleanTaskProvider from './cleanTaskProvider';
2929
import gnatproveTaskProvider from './gnatproveTaskProvider';
30+
import { getSubprogramSymbol } from './gnatproveTaskProvider';
3031
import { alsCommandExecutor } from './alsExecuteCommand';
3132
import { ALSClientFeatures } from './alsClientFeatures';
3233
import { string } from 'fp-ts';
@@ -41,6 +42,47 @@ let alsTaskProvider: vscode.Disposable[] = [
4142
),
4243
];
4344

45+
/**
46+
* Add a subprogram box above the subprogram enclosing the cursor's position, if any.
47+
*
48+
* @example:
49+
*
50+
* -------
51+
* - Foo -
52+
* -------
53+
*
54+
* procedure Foo is
55+
*/
56+
function addSupbrogramBox() {
57+
const activeEditor = vscode.window.activeTextEditor;
58+
59+
getSubprogramSymbol(activeEditor)
60+
.then(symbol => {
61+
if (symbol !== null) {
62+
const name: string = symbol.name ?? ""
63+
const insertPos = new vscode.Position(symbol.range.start.line, 0);
64+
const indentationRange = new vscode.Range(insertPos, symbol.range.start)
65+
const indentation: string = activeEditor?.document.getText(indentationRange) ?? ""
66+
const eol: string = activeEditor?.document.eol == vscode.EndOfLine.CRLF ? "\r\n" : "\n";
67+
68+
// Generate the subprogram box after retrieving the indentation of the line of
69+
// the subprogram's body declaration.
70+
let text: string = indentation + "---" + '-'.repeat(name.length) + "---" + eol
71+
+ indentation + "-- " + name + " --" + eol
72+
+ indentation + "---" + '-'.repeat(name.length) + "---" + eol
73+
+ eol;
74+
75+
activeEditor?.document.eol.toString
76+
if (activeEditor) {
77+
78+
activeEditor.edit(editBuilder => {
79+
editBuilder.insert(insertPos, text);
80+
});
81+
}
82+
}
83+
})
84+
}
85+
4486
export function activate(context: vscode.ExtensionContext): void {
4587
function createClient(id: string, name: string, extra: string[], pattern: string) {
4688
let serverModule = context.asAbsolutePath(process.platform + '/ada_language_server');
@@ -125,6 +167,7 @@ export function activate(context: vscode.ExtensionContext): void {
125167

126168
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(configChanged));
127169
context.subscriptions.push(vscode.commands.registerCommand('ada.otherFile', otherFileHandler));
170+
context.subscriptions.push(vscode.commands.registerCommand('ada.subprogramBox', addSupbrogramBox));
128171

129172
// Check if we need to add some source directories to the workspace (e.g: when imported projects'
130173
// source directories are not placed under the root project's directory).

integration/vscode/ada/src/gnatproveTaskProvider.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
-- of the license. --
1616
----------------------------------------------------------------------------*/
1717

18+
import { none } from 'fp-ts/lib/Option';
1819
import * as vscode from 'vscode';
1920
import { SymbolKind } from 'vscode';
21+
import { integer } from 'vscode-languageclient';
2022

2123
enum taskKinds {
2224
examineProject, // Examine the project
@@ -63,13 +65,19 @@ export default class gnatproveTaskProvider implements vscode.TaskProvider<vscode
6365
else if (definition.taskKind == taskKinds.examineSubprogram
6466
|| definition.taskKind == taskKinds.proveSubprogram)
6567
{
66-
return getSubprogram(vscode.window.activeTextEditor)
67-
.then(subprogram =>
68+
return getSubprogramSymbol(vscode.window.activeTextEditor)
69+
.then(Symbol =>
6870
{
69-
let args = (definition.taskKind == taskKinds.examineSubprogram) ? ['--mode=flow'] : [];
70-
args = getGnatproveArgs(args.concat(['-u', '${fileBasename}', '--limit-subp=${fileBasename}:' + subprogram]));
71-
const shell = new vscode.ShellExecution('gnatprove', args);
72-
return new vscode.Task(definition, vscode.TaskScope.Workspace, task.name, 'ada', shell, '$ada');
71+
if (Symbol) {
72+
const subprogram_line: string = (Symbol.range.start.line + 1).toString()
73+
let args = (definition.taskKind == taskKinds.examineSubprogram) ? ['--mode=flow'] : [];
74+
args = getGnatproveArgs(args.concat(['-u', '${fileBasename}', '--limit-subp=${fileBasename}:' + subprogram_line]));
75+
const shell = new vscode.ShellExecution('gnatprove', args);
76+
return new vscode.Task(definition, vscode.TaskScope.Workspace, task.name, 'ada', shell, '$ada');
77+
}
78+
else {
79+
return task;
80+
}
7381
});
7482
}
7583
else {
@@ -161,8 +169,16 @@ const getTasks = (): vscode.Task[] => {
161169
return result;
162170
};
163171

164-
const getSubprogram = async (editor: vscode.TextEditor | undefined): Promise<string> => {
165-
let subprogramLine = 0;
172+
/**
173+
* Return the DocumentSymbol associated to the subprogram enclosing the
174+
* the given editor's cursor position, if any.
175+
* @param {vscode.TextEditor | undefined} editor - The editor in which we want
176+
* to find the suprogram's body enclosing the cursor's position.
177+
* @return {vscode.DocumentSymbol | null} Return the symbol corresponding to the
178+
* enclosing subprogram or null if not found.
179+
*/
180+
export const getSubprogramSymbol = async (editor: vscode.TextEditor | undefined): Promise<vscode.DocumentSymbol|null> => {
181+
let subprogramLine = 0;
166182
if (editor) {
167183
const line = editor.selection.active.line;
168184

@@ -190,12 +206,12 @@ const getSubprogram = async (editor: vscode.TextEditor | undefined): Promise<str
190206
let scopeSymbols = subprograms.filter(sym => line >= sym.range.start.line && line <= sym.range.end.line);
191207
if (scopeSymbols.length > 0) {
192208
scopeSymbols.sort((a,b) => (a.range.end.line - a.range.start.line) - (b.range.end.line - b.range.start.line));
193-
subprogramLine = scopeSymbols[0].range.start.line;
209+
return scopeSymbols[0];
194210
};
195211
};
196-
// Line numbers start at 0 in VS Code, and at 1 in GNAT
197-
return (subprogramLine + 1).toString();
198-
};
212+
213+
return null;
214+
}
199215

200216
const getSelectedRegion = (editor: vscode.TextEditor | undefined): string => {
201217
if (editor) {

0 commit comments

Comments
 (0)