Skip to content

Commit c45c357

Browse files
committed
S314-015 Added 'Add_Parameter' refactoring tool
Added 'Check_Syntax' protocol extension Added test case for this tool Adjusted existing test cases of other refactoring tools
1 parent beea44b commit c45c357

File tree

51 files changed

+1513
-782
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1513
-782
lines changed

doc/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ related resources.
4444
* [Other File](other_file.md)
4545
* [Reference kinds](reference_kinds.md)
4646
* [Show Dependencies](show_dependencies.md)
47+
* [Check Syntax](check_syntax.md)

doc/check_syntax.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Check Syntax
2+
3+
## Short introduction
4+
5+
This implements a functionality to query if a given input has a valid syntax according to a set of rules.
6+
7+
## Change description
8+
9+
We introduce a new request:
10+
11+
method: `alsCheckSyntax`
12+
13+
And two types to represent the request and response parameters:
14+
15+
```typescript
16+
type AdaSyntaxCheckRequest = {
17+
input: string;
18+
rules: string[];
19+
};
20+
21+
type AdaSyntaxCheckResponse = {
22+
diagnostic?: string;
23+
};
24+
```
25+
26+
`rules` is a `string[]` and its content must be Libadalang `Ada_Node_Kind_Type` values.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*----------------------------------------------------------------------------
2+
-- Language Server Protocol --
3+
-- --
4+
-- Copyright (C) 2021, AdaCore --
5+
-- --
6+
-- This is free software; you can redistribute it and/or modify it under --
7+
-- terms of the GNU General Public License as published by the Free Soft- --
8+
-- ware Foundation; either version 3, or (at your option) any later ver- --
9+
-- sion. This software is distributed in the hope that it will be useful, --
10+
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
11+
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
12+
-- License for more details. You should have received a copy of the GNU --
13+
-- General Public License distributed with this software; see file --
14+
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy --
15+
-- of the license. --
16+
----------------------------------------------------------------------------*/
17+
18+
import { ExecuteCommandSignature } from 'vscode-languageclient';
19+
import { LanguageClient } from 'vscode-languageclient/node';
20+
import {
21+
AddParameterCommandArgs,
22+
alsAddParameterCommandExecutor,
23+
} from './refactoring/alsAddParameterCommand';
24+
25+
type CommandExecuter = (
26+
command: string,
27+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28+
args: any[],
29+
next: ExecuteCommandSignature
30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31+
) => Promise<ExecuteCommandSignature | undefined>;
32+
33+
export const alsCommandExecuter = (client: LanguageClient): CommandExecuter => {
34+
return async (
35+
command: string,
36+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
37+
args: any[],
38+
next: ExecuteCommandSignature
39+
): Promise<ExecuteCommandSignature | undefined> => {
40+
if (command === 'als-refactor-add-parameters') {
41+
const proceedWithExecution = await alsAddParameterCommandExecutor(
42+
client,
43+
args[0] as AddParameterCommandArgs
44+
);
45+
if (!proceedWithExecution) return Promise.resolve(undefined);
46+
}
47+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
48+
return next(command, args);
49+
};
50+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*----------------------------------------------------------------------------
2+
-- Language Server Protocol --
3+
-- --
4+
-- Copyright (C) 2021, AdaCore --
5+
-- --
6+
-- This is free software; you can redistribute it and/or modify it under --
7+
-- terms of the GNU General Public License as published by the Free Soft- --
8+
-- ware Foundation; either version 3, or (at your option) any later ver- --
9+
-- sion. This software is distributed in the hope that it will be useful, --
10+
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
11+
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
12+
-- License for more details. You should have received a copy of the GNU --
13+
-- General Public License distributed with this software; see file --
14+
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy --
15+
-- of the license. --
16+
----------------------------------------------------------------------------*/
17+
18+
import { LanguageClient } from 'vscode-languageclient/node';
19+
20+
export enum AdaGrammarRule {
21+
Defining_Id_Rule = 'Defining_Id_Rule',
22+
Defining_Id_List_Rule = 'Defining_Id_List_Rule',
23+
Param_Spec_Rule = 'Param_Spec_Rule',
24+
}
25+
26+
type AdaSyntaxCheckRequest = {
27+
input: string;
28+
rules: string[];
29+
};
30+
31+
type AdaSyntaxCheckResponse = {
32+
diagnostic?: string;
33+
};
34+
35+
export class AdaSyntaxCheckProvider {
36+
private readonly client: LanguageClient;
37+
rules: string[];
38+
diagnostic?: string;
39+
40+
constructor(client: LanguageClient, rules: AdaGrammarRule[], diagnotic?: string) {
41+
this.client = client;
42+
this.rules = rules.map((rule) => rule.toString());
43+
this.diagnostic = diagnotic;
44+
}
45+
46+
public sendCheckSyntaxRequest = async (input: string): Promise<undefined | string> => {
47+
const method = '$/alsCheckSyntax';
48+
49+
const request: AdaSyntaxCheckRequest = {
50+
input: input,
51+
rules: this.rules,
52+
};
53+
54+
const response: AdaSyntaxCheckResponse = await this.client.sendRequest(method, request);
55+
56+
return new Promise<undefined | string>((resolve, reject) => {
57+
if (Object.keys(response).length === 0) {
58+
resolve(undefined);
59+
} else if (Object.keys(response).length > 1) {
60+
reject('Invalid response from $/alsCheckSyntax request');
61+
} else {
62+
if (typeof response.diagnostic !== 'string') {
63+
reject('Invalid response from $/alsCheckSyntax request');
64+
}
65+
resolve(this.diagnostic ? this.diagnostic : response.diagnostic);
66+
}
67+
});
68+
};
69+
}

integration/vscode/ada/src/extension.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@
1616
----------------------------------------------------------------------------*/
1717

1818
import * as vscode from 'vscode';
19-
import * as vscode_languageclient from 'vscode-languageclient/node';
19+
import {
20+
ExecuteCommandRequest,
21+
LanguageClient,
22+
LanguageClientOptions,
23+
Middleware,
24+
ServerOptions,
25+
} from 'vscode-languageclient/node';
2026
import * as process from 'process';
2127

2228
import GPRTaskProvider from './gprTaskProvider';
2329
import gnatproveTaskProvider from './gnatproveTaskProvider';
30+
import { alsCommandExecuter } from './alsExecuteCommand';
2431

2532
let alsTaskProvider: vscode.Disposable[] = [
2633
vscode.tasks.registerTaskProvider(GPRTaskProvider.gprBuildType, new GPRTaskProvider()),
@@ -38,12 +45,12 @@ export function activate(context: vscode.ExtensionContext): void {
3845
// let debugOptions = { execArgv: [] };
3946
// If the extension is launched in debug mode then the debug server options are used
4047
// Otherwise the run options are used
41-
const serverOptions: vscode_languageclient.ServerOptions = {
48+
const serverOptions: ServerOptions = {
4249
run: { command: serverModule },
4350
debug: { command: serverModule },
4451
};
4552
// Options to control the language client
46-
const clientOptions: vscode_languageclient.LanguageClientOptions = {
53+
const clientOptions: LanguageClientOptions = {
4754
// Register the server for ada sources documents
4855
documentSelector: [{ scheme: 'file', language: 'ada' }],
4956
synchronize: {
@@ -53,14 +60,13 @@ export function activate(context: vscode.ExtensionContext): void {
5360
fileEvents: vscode.workspace.createFileSystemWatcher('**/.clientrc'),
5461
},
5562
};
56-
5763
// Create the language client and start the client.
58-
const client = new vscode_languageclient.LanguageClient(
59-
'ada',
60-
'Ada Language Server',
61-
serverOptions,
62-
clientOptions
63-
);
64+
const client = new LanguageClient('ada', 'Ada Language Server', serverOptions, clientOptions);
65+
const alsMiddleware: Middleware = {
66+
executeCommand: alsCommandExecuter(client),
67+
};
68+
client.clientOptions.middleware = alsMiddleware;
69+
6470
const disposable = client.start();
6571
// Push the disposable to the context's subscriptions so that the
6672
// client can be deactivated on extension deactivation
@@ -72,7 +78,7 @@ export function activate(context: vscode.ExtensionContext): void {
7278
if (!activeEditor) {
7379
return;
7480
}
75-
void client.sendRequest(vscode_languageclient.ExecuteCommandRequest.type, {
81+
void client.sendRequest(ExecuteCommandRequest.type, {
7682
command: 'als-other-file',
7783
arguments: [
7884
{
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*----------------------------------------------------------------------------
2+
-- Language Server Protocol --
3+
-- --
4+
-- Copyright (C) 2021, AdaCore --
5+
-- --
6+
-- This is free software; you can redistribute it and/or modify it under --
7+
-- terms of the GNU General Public License as published by the Free Soft- --
8+
-- ware Foundation; either version 3, or (at your option) any later ver- --
9+
-- sion. This software is distributed in the hope that it will be useful, --
10+
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
11+
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
12+
-- License for more details. You should have received a copy of the GNU --
13+
-- General Public License distributed with this software; see file --
14+
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy --
15+
-- of the license. --
16+
----------------------------------------------------------------------------*/
17+
18+
import { window, InputBoxOptions } from 'vscode';
19+
import { LanguageClient } from 'vscode-languageclient/node';
20+
21+
import { AdaGrammarRule, AdaSyntaxCheckProvider } from '../alsProtocolExtensions';
22+
23+
export type AddParameterCommandArgs = {
24+
newParameter: string;
25+
requiresFullSpecification: boolean;
26+
};
27+
28+
export const alsAddParameterCommandExecutor = async (
29+
client: LanguageClient,
30+
args: AddParameterCommandArgs
31+
): Promise<boolean> => {
32+
// If the server command attributes changed, some of args fields might be undefined
33+
if (args.requiresFullSpecification === undefined || args.newParameter === undefined) {
34+
return Promise.reject(
35+
'Invalid als-refactor-add-parameters command: missing "requiresFullSpecification" field'
36+
);
37+
}
38+
39+
// Create an input box with the messages adjusted according to if we require a full parameter
40+
// specification or not
41+
42+
const prompt = args.requiresFullSpecification
43+
? 'Insert a full parameter specification'
44+
: 'Insert one or more comma-separated parameter names or a full parameter specification';
45+
46+
const rules = args.requiresFullSpecification
47+
? [AdaGrammarRule.Param_Spec_Rule]
48+
: [
49+
AdaGrammarRule.Defining_Id_Rule,
50+
AdaGrammarRule.Defining_Id_List_Rule,
51+
AdaGrammarRule.Param_Spec_Rule,
52+
];
53+
54+
const diagnostic = args.requiresFullSpecification
55+
? 'This is not a syntactically valid full parameter specification'
56+
: 'This is not a syntactically valid parameter name or full parameter specification';
57+
58+
const adaSyntaxCheckProvider = new AdaSyntaxCheckProvider(client, rules, diagnostic);
59+
const { sendCheckSyntaxRequest } = adaSyntaxCheckProvider;
60+
61+
const inputBoxOptions: InputBoxOptions = {
62+
title: 'Add Parameter(s)',
63+
prompt: prompt,
64+
ignoreFocusOut: true,
65+
validateInput: sendCheckSyntaxRequest,
66+
};
67+
68+
const input = await window.showInputBox(inputBoxOptions);
69+
70+
// If input is undefined, then the user cancelled the operation. Return false
71+
// to indicate that the executor should no proced. Otherwise, set the
72+
// newParameter.
73+
74+
if (input !== undefined) {
75+
args.newParameter = input;
76+
return Promise.resolve(true);
77+
} else {
78+
return Promise.resolve(false);
79+
}
80+
};

scripts/generate.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
1717
package LSP.Messages.Server_{kind}s is
1818
19-
type Server_{kind} is abstract new LSP.Messages.{kind}Message
20-
with null record;
19+
type Server_{kind} is abstract new LSP.Messages.{kind}Message{record};
2120
2221
function Decode
2322
(JS : not null access LSP.JSON_Streams.JSON_Stream)
@@ -312,6 +311,8 @@
312311
'ALS_ShowDependenciesParams',
313312
'ALS_ShowDependencies_Response'),
314313
('$/alsDebug', 'ALS_Debug', 'ALSDebugParams', 'ALS_Debug_Response'),
314+
('$/alsCheckSyntax', 'ALS_Check_Syntax', 'ALS_Check_Syntax_Params',
315+
'ALS_Check_Syntax_Response'),
315316
]
316317
# Names of requests in the form (protocol name, Ada name, parameter name,
317318
# response name)
@@ -343,12 +344,12 @@
343344
def write_message_types():
344345
""" Write source/protocol/lsp-messages-request.* """
345346

346-
def write_package(data_array, kind, ads_name, handler_is_procedure):
347+
def write_package(data_array, kind, record, ads_name, handler_is_procedure):
347348
"""Factorization function"""
348349

349350
# Write the .ads
350351
with open(ads_name, 'w') as ads:
351-
ads.write(LSP_Messages_Generic_Header.format(kind=kind))
352+
ads.write(LSP_Messages_Generic_Header.format(kind=kind, record=record))
352353

353354
for l in data_array:
354355
request_name = l[1]
@@ -410,9 +411,10 @@ def write_body(data_array, kind, adb_name, handler_is_procedure):
410411

411412
gen_dir = join(basedir, 'source', 'protocol', 'generated')
412413
write_package(REQUESTS, 'Request',
414+
""" with record\n Canceled : Boolean := False with Atomic;\n end record""",
413415
join(gen_dir, 'lsp-messages-server_requests.ads'),
414416
False)
415-
write_package(NOTIFICATIONS, 'Notification',
417+
write_package(NOTIFICATIONS, 'Notification', '\n with null record',
416418
join(gen_dir, 'lsp-messages-server_notifications.ads'),
417419
True)
418420
write_body(NOTIFICATIONS, 'Notification',

scripts/io_gen.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@
280280
# 'ALS_Debug_Kinds',
281281
# 'ALSDebugParams',
282282
'Search_Kind',
283+
# 'ALS_Check_Syntax_Params',
284+
# 'ALS_Check_Syntax_Result',
283285
}
284286

285287
spec_header = """-- Automatically generated, do not edit.

source/ada/lsp-ada_driver.adb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ with LSP.Ada_Handlers;
3737
with LSP.Ada_Handlers.Named_Parameters_Commands;
3838
with LSP.Ada_Handlers.Other_File_Commands;
3939
with LSP.Ada_Handlers.Refactor_Imports_Commands;
40+
with LSP.Ada_Handlers.Refactor_Add_Parameter;
4041
with LSP.Ada_Handlers.Refactor_Remove_Parameter;
4142
with LSP.Ada_Handlers.Refactor_Move_Parameter;
4243
with LSP.Ada_Handlers.Refactor_Change_Parameter_Mode;
@@ -139,6 +140,8 @@ procedure LSP.Ada_Driver is
139140
(LSP.Ada_Handlers.Named_Parameters_Commands.Command'Tag);
140141
LSP.Commands.Register
141142
(LSP.Ada_Handlers.Refactor_Imports_Commands.Command'Tag);
143+
LSP.Commands.Register
144+
(LSP.Ada_Handlers.Refactor_Add_Parameter.Command'Tag);
142145
LSP.Commands.Register
143146
(LSP.Ada_Handlers.Refactor_Remove_Parameter.Command'Tag);
144147
LSP.Commands.Register

0 commit comments

Comments
 (0)