2
2
3
3
'use strict' ;
4
4
5
+ import * as vscode from 'vscode' ;
5
6
import { spawnSync } from 'child_process' ;
6
7
import { commands , window , workspace , TextDocument , WorkspaceFolder } from 'vscode' ;
7
8
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' ;
9
10
import { LoggingService } from '../services/logging-service' ;
11
+ import { RestartLS } from '../features/commands' ;
10
12
11
13
// The clients are non member variables of the class because they need to be
12
14
// shared for command registration. The command operates on the client and not
@@ -38,23 +40,38 @@ export function checkLanguageServerActivation(document: TextDocument): Workspace
38
40
return folder ;
39
41
}
40
42
41
- export class FortranLanguageServer {
42
- constructor ( private logger : LoggingService ) {
43
+ export class FortlsClient {
44
+ constructor ( private logger : LoggingService , private context ?: vscode . ExtensionContext ) {
43
45
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
+ }
44
54
}
45
55
56
+ private client : LanguageClient | undefined ;
46
57
private _fortlsVersion : string | undefined ;
47
58
48
59
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
+ } ) ;
51
67
return ;
52
68
}
53
69
54
70
public async deactivate ( ) : Promise < void > {
55
71
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
58
75
}
59
76
await Promise . all ( promises ) ;
60
77
return undefined ;
@@ -99,14 +116,15 @@ export class FortranLanguageServer {
99
116
} ;
100
117
101
118
// 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 ,
104
121
'Fortran Language Server' ,
105
122
serverOptions ,
106
123
clientOptions
107
124
) ;
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 ) ;
110
128
}
111
129
}
112
130
@@ -117,7 +135,6 @@ export class FortranLanguageServer {
117
135
private async fortlsArguments ( ) {
118
136
// Get path for the language server
119
137
const conf = workspace . getConfiguration ( EXTENSION_ID ) ;
120
- const executablePath = conf . get < string > ( 'fortls.path' ) ;
121
138
const maxLineLength = conf . get < number > ( 'fortls.maxLineLength' ) ;
122
139
const maxCommentLineLength = conf . get < number > ( 'fortls.maxCommentLineLength' ) ;
123
140
const fortlsExtraArgs = conf . get < string [ ] > ( 'fortls.extraArgs' ) ;
@@ -215,4 +232,56 @@ export class FortranLanguageServer {
215
232
}
216
233
return results . stdout . toString ( ) . trim ( ) ;
217
234
}
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
+ }
218
287
}
0 commit comments