From 970524e7ecb89d65a0ae5abb96f3ae838d395857 Mon Sep 17 00:00:00 2001 From: gnikit Date: Mon, 25 Apr 2022 08:22:49 +0100 Subject: [PATCH 1/5] Moves and renames fortls client to lsp dir --- src/extension.ts | 2 +- src/features/commands.ts | 2 +- src/{features/fortls-interface.ts => lsp/client.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{features/fortls-interface.ts => lsp/client.ts} (100%) diff --git a/src/extension.ts b/src/extension.ts index a54a33f5..8da6d2a8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,7 +6,7 @@ import { registerCommands } from './features/commands'; import { FortranCompletionProvider } from './features/completion-provider'; import { FortranDocumentSymbolProvider } from './features/document-symbol-provider'; import { FortranFormattingProvider } from './features/formatting-provider'; -import { FortranLanguageServer } from './features/fortls-interface'; +import { FortranLanguageServer } from './lsp/client'; import { FortranHoverProvider } from './features/hover-provider'; import { FortranLintingProvider } from './features/linter-provider'; import { diff --git a/src/features/commands.ts b/src/features/commands.ts index baa191af..8b6de62a 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -1,7 +1,7 @@ 'use strict'; import * as vscode from 'vscode'; -import { checkLanguageServerActivation, clients } from './fortls-interface'; +import { checkLanguageServerActivation, clients } from '../lsp/client'; export const RestartLS = 'fortran.analysis.restartLanguageServer'; export const StarttLS = 'fortran.analysis.startLanguageServer'; diff --git a/src/features/fortls-interface.ts b/src/lsp/client.ts similarity index 100% rename from src/features/fortls-interface.ts rename to src/lsp/client.ts From 141c24385f1844efecd96a7fb1bc0b17e91695bb Mon Sep 17 00:00:00 2001 From: gnikit Date: Mon, 25 Apr 2022 17:25:53 +0100 Subject: [PATCH 2/5] Moves all Language Server checks inside the client Removed the temp option of having to disable fortls warnings. Users can not either use fortls or disable it --- package.json | 5 ----- src/extension.ts | 37 ++-------------------------------- src/lsp/client.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 7709d698..fb2c575e 100644 --- a/package.json +++ b/package.json @@ -301,11 +301,6 @@ ], "description": "Specify the word case to use when suggesting autocomplete options." }, - "fortran.ignoreWarning.fortls": { - "type": "boolean", - "default": false, - "description": "Hide error message when the fortls is not detected" - }, "fortran.includePaths": { "deprecationMessage": "fortran.includePaths has been renamed to fortran.linter.includePaths." }, diff --git a/src/extension.ts b/src/extension.ts index 8da6d2a8..7965fb03 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -67,41 +67,8 @@ export async function activate(context: vscode.ExtensionContext) { registerCommands(context.subscriptions); - // Check if the language server is installed and if not prompt to install it - // Not the most elegant solution but we need pip install to have finished - // before the activate function is called so we do a little code duplication - if (!config.get('fortls.disabled')) { - which(config.get('fortls.path'), (err: any) => { - if (!config.get('ignoreWarning.fortls')) { - if (err) { - const msg = `It is highly recommended to use the fortls to - enable IDE features like hover, peeking, gotos and many more. - For a full list of features the language server adds see: - https://github.com/gnikit/fortls`; - promptForMissingTool( - LANG_SERVER_TOOL_ID, - msg, - 'Python', - ['Install', "Don't Show Again"], - loggingService, - () => { - config.update('ignoreWarning.fortls', true); - } - ).then(() => { - // fortls not installed AND Warnings are enabled - new FortranLanguageServer(loggingService).activate(); - }); - } - // Ignore fortls Warnings NOT set. Activate the LS - else { - new FortranLanguageServer(loggingService).activate(); - } - } - // Ignore fortls Warnings are SET. Activate the LS - else { - new FortranLanguageServer(loggingService).activate(); - } - }); + if (!config.get('fortls.disabled')) { + new FortranLanguageServer(loggingService).activate(); } } diff --git a/src/lsp/client.ts b/src/lsp/client.ts index 87e0416a..7c18f939 100644 --- a/src/lsp/client.ts +++ b/src/lsp/client.ts @@ -46,8 +46,13 @@ export class FortranLanguageServer { private _fortlsVersion: string | undefined; public async activate() { - workspace.onDidOpenTextDocument(this.didOpenTextDocument, this); - workspace.textDocuments.forEach(this.didOpenTextDocument, this); + // Detect if fortls is present, download if missing or disable LS functionality + // Do not allow activating the LS functionality if no fortls is detected + await this.fortlsDownload().then(fortlsDisabled => { + if (fortlsDisabled) return; + workspace.onDidOpenTextDocument(this.didOpenTextDocument, this); + workspace.textDocuments.forEach(this.didOpenTextDocument, this); + }); return; } @@ -215,4 +220,46 @@ export class FortranLanguageServer { } return results.stdout.toString().trim(); } + + /** + * Check if fortls is present in the system, if not show prompt to install/disable. + * If disabling or erroring the function will return true. + * For all normal cases it should return false. + * + * @returns false if the fortls has been detected or installed successfully + */ + private async fortlsDownload(): Promise { + const config = workspace.getConfiguration(EXTENSION_ID); + const ls = config.get('fortls.path'); + + // Check for version, if this fails fortls provided is invalid + const results = spawnSync(ls, ['--version']); + const msg = `It is highly recommended to use the fortls to enable IDE features like hover, peeking, GoTos and many more. + For a full list of features the language server adds see: https://github.com/gnikit/fortls`; + return new Promise(resolve => { + let fortlsDisabled = false; + if (results.error) { + const selection = window.showInformationMessage(msg, 'Install', 'Disable'); + selection.then(opt => { + if (opt === 'Install') { + const install = spawnSync('pip', ['install', '--user', '--upgrade', 'fortls']); + if (install.error) { + window.showErrorMessage('Had trouble installing fortls, please install manually'); + fortlsDisabled = true; + } + if (install.stdout) { + this.logger.logInfo(install.stdout.toString()); + fortlsDisabled = false; + } + } else if (opt == 'Disable') { + config.update('fortls.disabled', true); + fortlsDisabled = true; + } + resolve(fortlsDisabled); + }); + } else { + resolve(false); + } + }); + } } From b0a33b410455dbe5735768b3ad509d04b2bc9197 Mon Sep 17 00:00:00 2001 From: gnikit Date: Mon, 25 Apr 2022 17:31:53 +0100 Subject: [PATCH 3/5] Renamed Client class --- src/extension.ts | 12 +++--------- src/lsp/client.ts | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 7965fb03..b2d129b7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,20 +1,14 @@ // src/extension.ts import * as vscode from 'vscode'; -import which from 'which'; import * as pkg from '../package.json'; import { registerCommands } from './features/commands'; import { FortranCompletionProvider } from './features/completion-provider'; import { FortranDocumentSymbolProvider } from './features/document-symbol-provider'; import { FortranFormattingProvider } from './features/formatting-provider'; -import { FortranLanguageServer } from './lsp/client'; +import { FortlsClient } from './lsp/client'; import { FortranHoverProvider } from './features/hover-provider'; import { FortranLintingProvider } from './features/linter-provider'; -import { - EXTENSION_ID, - FortranDocumentSelector, - LANG_SERVER_TOOL_ID, - promptForMissingTool, -} from './lib/tools'; +import { EXTENSION_ID, FortranDocumentSelector } from './lib/tools'; import { LoggingService } from './services/logging-service'; // Make it global to catch errors when activation fails @@ -68,7 +62,7 @@ export async function activate(context: vscode.ExtensionContext) { registerCommands(context.subscriptions); if (!config.get('fortls.disabled')) { - new FortranLanguageServer(loggingService).activate(); + new FortlsClient(loggingService).activate(); } } diff --git a/src/lsp/client.ts b/src/lsp/client.ts index 7c18f939..fbf995ec 100644 --- a/src/lsp/client.ts +++ b/src/lsp/client.ts @@ -38,7 +38,7 @@ export function checkLanguageServerActivation(document: TextDocument): Workspace return folder; } -export class FortranLanguageServer { +export class FortlsClient { constructor(private logger: LoggingService) { this.logger.logInfo('Fortran Language Server'); } From 9b3a3e70ebd02dfd2ec9002030c5933790ef1efb Mon Sep 17 00:00:00 2001 From: gnikit Date: Mon, 25 Apr 2022 18:43:19 +0100 Subject: [PATCH 4/5] Fixes Language Server restarting --- CHANGELOG.md | 2 +- package.json | 20 ------------------ src/extension.ts | 5 +---- src/features/commands.ts | 44 +--------------------------------------- src/lsp/client.ts | 33 +++++++++++++++++++++++------- 5 files changed, 29 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9c2beb4..7ebab5e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Adds native support for the fortran-language-server (`fortls`) making unnecessary the usage of Fortran Intellisense extension ([#290](https://github.com/krvajal/vscode-fortran-support/issues/290)) -- Adds commands for re/starting/stopping the Language Server +- Added command for restarting the Language Server - Added more options for configuring the `fortls` settings through the UI ## [2.6.2] diff --git a/package.json b/package.json index fb2c575e..a2a94476 100644 --- a/package.json +++ b/package.json @@ -346,16 +346,6 @@ "category": "Fortran", "command": "fortran.analysis.restartLanguageServer", "title": "Restart the Fortran Language Server" - }, - { - "category": "Fortran", - "command": "fortran.analysis.stopLanguageServer", - "title": "Stop the Fortran Language Server" - }, - { - "category": "Fortran", - "command": "fortran.analysis.startLanguageServer", - "title": "Start the Fortran Language Server" } ], "menus": { @@ -365,16 +355,6 @@ "command": "fortran.analysis.restartLanguageServer", "title": "Restart the Fortran Language Server", "when": "!virtualWorkspace && shellExecutionSupported" - }, - { - "command": "fortran.analysis.stopLanguageServer", - "title": "Stop the Fortran Language Server", - "when": "!virtualWorkspace && shellExecutionSupported" - }, - { - "command": "fortran.analysis.startLanguageServer", - "title": "Start the Fortran Language Server", - "when": "!virtualWorkspace && shellExecutionSupported" } ] } diff --git a/src/extension.ts b/src/extension.ts index b2d129b7..d4460b69 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,7 +1,6 @@ // src/extension.ts import * as vscode from 'vscode'; import * as pkg from '../package.json'; -import { registerCommands } from './features/commands'; import { FortranCompletionProvider } from './features/completion-provider'; import { FortranDocumentSymbolProvider } from './features/document-symbol-provider'; import { FortranFormattingProvider } from './features/formatting-provider'; @@ -59,10 +58,8 @@ export async function activate(context: vscode.ExtensionContext) { vscode.languages.registerDocumentSymbolProvider(FortranDocumentSelector(), symbolProvider); } - registerCommands(context.subscriptions); - if (!config.get('fortls.disabled')) { - new FortlsClient(loggingService).activate(); + new FortlsClient(context, loggingService).activate(); } } diff --git a/src/features/commands.ts b/src/features/commands.ts index 8b6de62a..9fbaf9b6 100644 --- a/src/features/commands.ts +++ b/src/features/commands.ts @@ -1,47 +1,5 @@ 'use strict'; -import * as vscode from 'vscode'; -import { checkLanguageServerActivation, clients } from '../lsp/client'; +// Module to hold all command names export const RestartLS = 'fortran.analysis.restartLanguageServer'; -export const StarttLS = 'fortran.analysis.startLanguageServer'; -export const StoptLS = 'fortran.analysis.stopLanguageServer'; - -let commandsActivated = false; - -export function registerCommands(disposables: vscode.Disposable[]): void { - if (commandsActivated) return; - - commandsActivated = true; - disposables.push(vscode.commands.registerCommand(RestartLS, onRestartLS)); - disposables.push(vscode.commands.registerCommand(StarttLS, onStartLS)); - disposables.push(vscode.commands.registerCommand(StoptLS, onStopLS)); -} - -function onRestartLS(): void { - const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; - vscode.window.showInformationMessage('Restarting the Fortran Language Server'); - const folder = checkLanguageServerActivation(activeEditor.document); - - if (!folder) return; - clients.get(folder.uri.toString()).stop(); - clients.get(folder.uri.toString()).start(); -} - -function onStartLS(): void { - const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; - vscode.window.showInformationMessage('Starting the Fortran Language Server'); - const folder = checkLanguageServerActivation(activeEditor.document); - - if (!folder) return; - clients.get(folder.uri.toString()).start(); -} - -function onStopLS(): void { - const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; - vscode.window.showInformationMessage('Stopping the Fortran Language Server'); - const folder = checkLanguageServerActivation(activeEditor.document); - - if (!folder) return; - clients.get(folder.uri.toString()).stop(); -} diff --git a/src/lsp/client.ts b/src/lsp/client.ts index fbf995ec..c3eb57af 100644 --- a/src/lsp/client.ts +++ b/src/lsp/client.ts @@ -2,11 +2,13 @@ 'use strict'; +import * as vscode from 'vscode'; import { spawnSync } from 'child_process'; import { commands, window, workspace, TextDocument, WorkspaceFolder } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; import { EXTENSION_ID, FortranDocumentSelector } from '../lib/tools'; import { LoggingService } from '../services/logging-service'; +import { RestartLS } from '../features/commands'; // The clients are non member variables of the class because they need to be // shared for command registration. The command operates on the client and not @@ -39,10 +41,16 @@ export function checkLanguageServerActivation(document: TextDocument): Workspace } export class FortlsClient { - constructor(private logger: LoggingService) { + constructor(private context: vscode.ExtensionContext, private logger: LoggingService) { this.logger.logInfo('Fortran Language Server'); + + // Register Language Server Commands + this.context.subscriptions.push( + vscode.commands.registerCommand(RestartLS, this.restartLS, this) + ); } + private client: LanguageClient | undefined; private _fortlsVersion: string | undefined; public async activate() { @@ -58,8 +66,9 @@ export class FortlsClient { public async deactivate(): Promise { const promises: Thenable[] = []; - for (const client of clients.values()) { - promises.push(client.stop()); + for (const [key, client] of clients.entries()) { + promises.push(client.stop()); // stop the language server + clients.delete(key); // delete the URI from the map } await Promise.all(promises); return undefined; @@ -104,14 +113,15 @@ export class FortlsClient { }; // Create the language client, start the client and add it to the registry - const client = new LanguageClient( + this.client = new LanguageClient( 'fortls', 'Fortran Language Server', serverOptions, clientOptions ); - client.start(); - clients.set(folder.uri.toString(), client); + this.client.start(); + // Add the Language Client to the global map + clients.set(folder.uri.toString(), this.client); } } @@ -122,7 +132,6 @@ export class FortlsClient { private async fortlsArguments() { // Get path for the language server const conf = workspace.getConfiguration(EXTENSION_ID); - const executablePath = conf.get('fortls.path'); const maxLineLength = conf.get('fortls.maxLineLength'); const maxCommentLineLength = conf.get('fortls.maxCommentLineLength'); const fortlsExtraArgs = conf.get('fortls.extraArgs'); @@ -262,4 +271,14 @@ export class FortlsClient { } }); } + + /** + * Restart the language server + */ + private async restartLS(): Promise { + this.logger.logInfo('Restarting language server...'); + vscode.window.showInformationMessage('Restarting language server...'); + await this.deactivate(); + await this.activate(); + } } From 9c22eacb374acc7726be8a8a7d4f9e36dddc5872 Mon Sep 17 00:00:00 2001 From: gnikit Date: Tue, 26 Apr 2022 15:13:43 +0100 Subject: [PATCH 5/5] Small changes - Makes context optional - Introduces variable for extension name --- src/extension.ts | 2 +- src/lib/tools.ts | 3 ++- src/lsp/client.ts | 19 +++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d4460b69..57ff4953 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -59,7 +59,7 @@ export async function activate(context: vscode.ExtensionContext) { } if (!config.get('fortls.disabled')) { - new FortlsClient(context, loggingService).activate(); + new FortlsClient(loggingService, context).activate(); } } diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 104aed18..e2b8833d 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -5,8 +5,9 @@ import * as cp from 'child_process'; import { LoggingService } from '../services/logging-service'; import { isString, isArrayOfString } from './helper'; +export const LS_NAME = 'fortls'; export const EXTENSION_ID = 'fortran'; -export const LANG_SERVER_TOOL_ID = 'fortls'; +export const EXTENSION_VSSTORE_ID = 'krvajalm.linter-gfortran'; export const FORMATTERS = ['Disabled', 'findent', 'fprettify']; // Platform-specific environment variable delimiter diff --git a/src/lsp/client.ts b/src/lsp/client.ts index c3eb57af..2dc1c63e 100644 --- a/src/lsp/client.ts +++ b/src/lsp/client.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { spawnSync } from 'child_process'; import { commands, window, workspace, TextDocument, WorkspaceFolder } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; -import { EXTENSION_ID, FortranDocumentSelector } from '../lib/tools'; +import { EXTENSION_ID, FortranDocumentSelector, LS_NAME } from '../lib/tools'; import { LoggingService } from '../services/logging-service'; import { RestartLS } from '../features/commands'; @@ -41,13 +41,16 @@ export function checkLanguageServerActivation(document: TextDocument): Workspace } export class FortlsClient { - constructor(private context: vscode.ExtensionContext, private logger: LoggingService) { + constructor(private logger: LoggingService, private context?: vscode.ExtensionContext) { this.logger.logInfo('Fortran Language Server'); - // Register Language Server Commands - this.context.subscriptions.push( - vscode.commands.registerCommand(RestartLS, this.restartLS, this) - ); + // if context is present + if (context !== undefined) { + // Register Language Server Commands + this.context.subscriptions.push( + vscode.commands.registerCommand(RestartLS, this.restartLS, this) + ); + } } private client: LanguageClient | undefined; @@ -114,7 +117,7 @@ export class FortlsClient { // Create the language client, start the client and add it to the registry this.client = new LanguageClient( - 'fortls', + LS_NAME, 'Fortran Language Server', serverOptions, clientOptions @@ -251,7 +254,7 @@ export class FortlsClient { const selection = window.showInformationMessage(msg, 'Install', 'Disable'); selection.then(opt => { if (opt === 'Install') { - const install = spawnSync('pip', ['install', '--user', '--upgrade', 'fortls']); + const install = spawnSync('pip', ['install', '--user', '--upgrade', LS_NAME]); if (install.error) { window.showErrorMessage('Had trouble installing fortls, please install manually'); fortlsDisabled = true;