Skip to content

Commit c033873

Browse files
authored
Merge pull request #436 from fortran-lang/feature/lsp
Feature/lsp
2 parents d9220da + 9c22eac commit c033873

File tree

6 files changed

+89
-128
lines changed

6 files changed

+89
-128
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4747
- Adds native support for the fortran-language-server (`fortls`) making
4848
unnecessary the usage of Fortran Intellisense extension
4949
([#290](https://github.com/krvajal/vscode-fortran-support/issues/290))
50-
- Adds commands for re/starting/stopping the Language Server
50+
- Added command for restarting the Language Server
5151
- Added more options for configuring the `fortls` settings through the UI
5252

5353
## [2.6.2]

package.json

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,6 @@
301301
],
302302
"description": "Specify the word case to use when suggesting autocomplete options."
303303
},
304-
"fortran.ignoreWarning.fortls": {
305-
"type": "boolean",
306-
"default": false,
307-
"description": "Hide error message when the fortls is not detected"
308-
},
309304
"fortran.includePaths": {
310305
"deprecationMessage": "fortran.includePaths has been renamed to fortran.linter.includePaths."
311306
},
@@ -351,16 +346,6 @@
351346
"category": "Fortran",
352347
"command": "fortran.analysis.restartLanguageServer",
353348
"title": "Restart the Fortran Language Server"
354-
},
355-
{
356-
"category": "Fortran",
357-
"command": "fortran.analysis.stopLanguageServer",
358-
"title": "Stop the Fortran Language Server"
359-
},
360-
{
361-
"category": "Fortran",
362-
"command": "fortran.analysis.startLanguageServer",
363-
"title": "Start the Fortran Language Server"
364349
}
365350
],
366351
"menus": {
@@ -370,16 +355,6 @@
370355
"command": "fortran.analysis.restartLanguageServer",
371356
"title": "Restart the Fortran Language Server",
372357
"when": "!virtualWorkspace && shellExecutionSupported"
373-
},
374-
{
375-
"command": "fortran.analysis.stopLanguageServer",
376-
"title": "Stop the Fortran Language Server",
377-
"when": "!virtualWorkspace && shellExecutionSupported"
378-
},
379-
{
380-
"command": "fortran.analysis.startLanguageServer",
381-
"title": "Start the Fortran Language Server",
382-
"when": "!virtualWorkspace && shellExecutionSupported"
383358
}
384359
]
385360
}

src/extension.ts

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
// src/extension.ts
22
import * as vscode from 'vscode';
3-
import which from 'which';
43
import * as pkg from '../package.json';
5-
import { registerCommands } from './features/commands';
64
import { FortranCompletionProvider } from './features/completion-provider';
75
import { FortranDocumentSymbolProvider } from './features/document-symbol-provider';
86
import { FortranFormattingProvider } from './features/formatting-provider';
9-
import { FortranLanguageServer } from './features/fortls-interface';
7+
import { FortlsClient } from './lsp/client';
108
import { FortranHoverProvider } from './features/hover-provider';
119
import { FortranLintingProvider } from './features/linter-provider';
12-
import {
13-
EXTENSION_ID,
14-
FortranDocumentSelector,
15-
LANG_SERVER_TOOL_ID,
16-
promptForMissingTool,
17-
} from './lib/tools';
10+
import { EXTENSION_ID, FortranDocumentSelector } from './lib/tools';
1811
import { LoggingService } from './services/logging-service';
1912

2013
// Make it global to catch errors when activation fails
@@ -65,43 +58,8 @@ export async function activate(context: vscode.ExtensionContext) {
6558
vscode.languages.registerDocumentSymbolProvider(FortranDocumentSelector(), symbolProvider);
6659
}
6760

68-
registerCommands(context.subscriptions);
69-
70-
// Check if the language server is installed and if not prompt to install it
71-
// Not the most elegant solution but we need pip install to have finished
72-
// before the activate function is called so we do a little code duplication
73-
if (!config.get<string>('fortls.disabled')) {
74-
which(config.get<string>('fortls.path'), (err: any) => {
75-
if (!config.get('ignoreWarning.fortls')) {
76-
if (err) {
77-
const msg = `It is highly recommended to use the fortls to
78-
enable IDE features like hover, peeking, gotos and many more.
79-
For a full list of features the language server adds see:
80-
https://github.com/gnikit/fortls`;
81-
promptForMissingTool(
82-
LANG_SERVER_TOOL_ID,
83-
msg,
84-
'Python',
85-
['Install', "Don't Show Again"],
86-
loggingService,
87-
() => {
88-
config.update('ignoreWarning.fortls', true);
89-
}
90-
).then(() => {
91-
// fortls not installed AND Warnings are enabled
92-
new FortranLanguageServer(loggingService).activate();
93-
});
94-
}
95-
// Ignore fortls Warnings NOT set. Activate the LS
96-
else {
97-
new FortranLanguageServer(loggingService).activate();
98-
}
99-
}
100-
// Ignore fortls Warnings are SET. Activate the LS
101-
else {
102-
new FortranLanguageServer(loggingService).activate();
103-
}
104-
});
61+
if (!config.get<boolean>('fortls.disabled')) {
62+
new FortlsClient(loggingService, context).activate();
10563
}
10664
}
10765

src/features/commands.ts

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,5 @@
11
'use strict';
22

3-
import * as vscode from 'vscode';
4-
import { checkLanguageServerActivation, clients } from './fortls-interface';
3+
// Module to hold all command names
54

65
export const RestartLS = 'fortran.analysis.restartLanguageServer';
7-
export const StarttLS = 'fortran.analysis.startLanguageServer';
8-
export const StoptLS = 'fortran.analysis.stopLanguageServer';
9-
10-
let commandsActivated = false;
11-
12-
export function registerCommands(disposables: vscode.Disposable[]): void {
13-
if (commandsActivated) return;
14-
15-
commandsActivated = true;
16-
disposables.push(vscode.commands.registerCommand(RestartLS, onRestartLS));
17-
disposables.push(vscode.commands.registerCommand(StarttLS, onStartLS));
18-
disposables.push(vscode.commands.registerCommand(StoptLS, onStopLS));
19-
}
20-
21-
function onRestartLS(): void {
22-
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
23-
vscode.window.showInformationMessage('Restarting the Fortran Language Server');
24-
const folder = checkLanguageServerActivation(activeEditor.document);
25-
26-
if (!folder) return;
27-
clients.get(folder.uri.toString()).stop();
28-
clients.get(folder.uri.toString()).start();
29-
}
30-
31-
function onStartLS(): void {
32-
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
33-
vscode.window.showInformationMessage('Starting the Fortran Language Server');
34-
const folder = checkLanguageServerActivation(activeEditor.document);
35-
36-
if (!folder) return;
37-
clients.get(folder.uri.toString()).start();
38-
}
39-
40-
function onStopLS(): void {
41-
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
42-
vscode.window.showInformationMessage('Stopping the Fortran Language Server');
43-
const folder = checkLanguageServerActivation(activeEditor.document);
44-
45-
if (!folder) return;
46-
clients.get(folder.uri.toString()).stop();
47-
}

src/lib/tools.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import * as cp from 'child_process';
55
import { LoggingService } from '../services/logging-service';
66
import { isString, isArrayOfString } from './helper';
77

8+
export const LS_NAME = 'fortls';
89
export const EXTENSION_ID = 'fortran';
9-
export const LANG_SERVER_TOOL_ID = 'fortls';
10+
export const EXTENSION_VSSTORE_ID = 'krvajalm.linter-gfortran';
1011
export const FORMATTERS = ['Disabled', 'findent', 'fprettify'];
1112

1213
// Platform-specific environment variable delimiter

src/features/fortls-interface.ts renamed to src/lsp/client.ts

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
'use strict';
44

5+
import * as vscode from 'vscode';
56
import { spawnSync } from 'child_process';
67
import { commands, window, workspace, TextDocument, WorkspaceFolder } from 'vscode';
78
import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
8-
import { EXTENSION_ID, FortranDocumentSelector } from '../lib/tools';
9+
import { EXTENSION_ID, FortranDocumentSelector, LS_NAME } from '../lib/tools';
910
import { LoggingService } from '../services/logging-service';
11+
import { RestartLS } from '../features/commands';
1012

1113
// The clients are non member variables of the class because they need to be
1214
// shared for command registration. The command operates on the client and not
@@ -38,23 +40,38 @@ export function checkLanguageServerActivation(document: TextDocument): Workspace
3840
return folder;
3941
}
4042

41-
export class FortranLanguageServer {
42-
constructor(private logger: LoggingService) {
43+
export class FortlsClient {
44+
constructor(private logger: LoggingService, private context?: vscode.ExtensionContext) {
4345
this.logger.logInfo('Fortran Language Server');
46+
47+
// if context is present
48+
if (context !== undefined) {
49+
// Register Language Server Commands
50+
this.context.subscriptions.push(
51+
vscode.commands.registerCommand(RestartLS, this.restartLS, this)
52+
);
53+
}
4454
}
4555

56+
private client: LanguageClient | undefined;
4657
private _fortlsVersion: string | undefined;
4758

4859
public async activate() {
49-
workspace.onDidOpenTextDocument(this.didOpenTextDocument, this);
50-
workspace.textDocuments.forEach(this.didOpenTextDocument, this);
60+
// Detect if fortls is present, download if missing or disable LS functionality
61+
// Do not allow activating the LS functionality if no fortls is detected
62+
await this.fortlsDownload().then(fortlsDisabled => {
63+
if (fortlsDisabled) return;
64+
workspace.onDidOpenTextDocument(this.didOpenTextDocument, this);
65+
workspace.textDocuments.forEach(this.didOpenTextDocument, this);
66+
});
5167
return;
5268
}
5369

5470
public async deactivate(): Promise<void> {
5571
const promises: Thenable<void>[] = [];
56-
for (const client of clients.values()) {
57-
promises.push(client.stop());
72+
for (const [key, client] of clients.entries()) {
73+
promises.push(client.stop()); // stop the language server
74+
clients.delete(key); // delete the URI from the map
5875
}
5976
await Promise.all(promises);
6077
return undefined;
@@ -99,14 +116,15 @@ export class FortranLanguageServer {
99116
};
100117

101118
// Create the language client, start the client and add it to the registry
102-
const client = new LanguageClient(
103-
'fortls',
119+
this.client = new LanguageClient(
120+
LS_NAME,
104121
'Fortran Language Server',
105122
serverOptions,
106123
clientOptions
107124
);
108-
client.start();
109-
clients.set(folder.uri.toString(), client);
125+
this.client.start();
126+
// Add the Language Client to the global map
127+
clients.set(folder.uri.toString(), this.client);
110128
}
111129
}
112130

@@ -117,7 +135,6 @@ export class FortranLanguageServer {
117135
private async fortlsArguments() {
118136
// Get path for the language server
119137
const conf = workspace.getConfiguration(EXTENSION_ID);
120-
const executablePath = conf.get<string>('fortls.path');
121138
const maxLineLength = conf.get<number>('fortls.maxLineLength');
122139
const maxCommentLineLength = conf.get<number>('fortls.maxCommentLineLength');
123140
const fortlsExtraArgs = conf.get<string[]>('fortls.extraArgs');
@@ -215,4 +232,56 @@ export class FortranLanguageServer {
215232
}
216233
return results.stdout.toString().trim();
217234
}
235+
236+
/**
237+
* Check if fortls is present in the system, if not show prompt to install/disable.
238+
* If disabling or erroring the function will return true.
239+
* For all normal cases it should return false.
240+
*
241+
* @returns false if the fortls has been detected or installed successfully
242+
*/
243+
private async fortlsDownload(): Promise<boolean> {
244+
const config = workspace.getConfiguration(EXTENSION_ID);
245+
const ls = config.get<string>('fortls.path');
246+
247+
// Check for version, if this fails fortls provided is invalid
248+
const results = spawnSync(ls, ['--version']);
249+
const msg = `It is highly recommended to use the fortls to enable IDE features like hover, peeking, GoTos and many more.
250+
For a full list of features the language server adds see: https://github.com/gnikit/fortls`;
251+
return new Promise<boolean>(resolve => {
252+
let fortlsDisabled = false;
253+
if (results.error) {
254+
const selection = window.showInformationMessage(msg, 'Install', 'Disable');
255+
selection.then(opt => {
256+
if (opt === 'Install') {
257+
const install = spawnSync('pip', ['install', '--user', '--upgrade', LS_NAME]);
258+
if (install.error) {
259+
window.showErrorMessage('Had trouble installing fortls, please install manually');
260+
fortlsDisabled = true;
261+
}
262+
if (install.stdout) {
263+
this.logger.logInfo(install.stdout.toString());
264+
fortlsDisabled = false;
265+
}
266+
} else if (opt == 'Disable') {
267+
config.update('fortls.disabled', true);
268+
fortlsDisabled = true;
269+
}
270+
resolve(fortlsDisabled);
271+
});
272+
} else {
273+
resolve(false);
274+
}
275+
});
276+
}
277+
278+
/**
279+
* Restart the language server
280+
*/
281+
private async restartLS(): Promise<void> {
282+
this.logger.logInfo('Restarting language server...');
283+
vscode.window.showInformationMessage('Restarting language server...');
284+
await this.deactivate();
285+
await this.activate();
286+
}
218287
}

0 commit comments

Comments
 (0)