1
- // Modified version of Chris Hansen's Fortran Intellisense
2
-
3
1
'use strict' ;
4
2
3
+ import * as path from 'path' ;
5
4
import * as vscode from 'vscode' ;
6
5
import { spawnSync } from 'child_process' ;
7
- import { commands , window , workspace , TextDocument , WorkspaceFolder } from 'vscode' ;
6
+ import { commands , window , workspace , TextDocument } from 'vscode' ;
8
7
import { LanguageClient , LanguageClientOptions , ServerOptions } from 'vscode-languageclient/node' ;
9
- import { EXTENSION_ID , FortranDocumentSelector , LS_NAME } from '../lib/tools' ;
8
+ import {
9
+ EXTENSION_ID ,
10
+ FortranDocumentSelector ,
11
+ LS_NAME ,
12
+ isFortran ,
13
+ getOuterMostWorkspaceFolder ,
14
+ } from '../lib/tools' ;
10
15
import { LoggingService } from '../services/logging-service' ;
11
16
import { RestartLS } from '../features/commands' ;
12
17
@@ -15,31 +20,6 @@ import { RestartLS } from '../features/commands';
15
20
// the server
16
21
export const clients : Map < string , LanguageClient > = new Map ( ) ;
17
22
18
- /**
19
- * Checks if the Language Server should run in the current workspace and return
20
- * the workspace folder if it should else return undefined.
21
- * @param document the active VS Code editor
22
- * @returns the root workspace folder or undefined
23
- */
24
- export function checkLanguageServerActivation ( document : TextDocument ) : WorkspaceFolder | undefined {
25
- // We are only interested in Fortran files
26
- if (
27
- ! FortranDocumentSelector ( ) . some ( e => e . scheme === document . uri . scheme ) ||
28
- ! FortranDocumentSelector ( ) . some ( e => e . language === document . languageId )
29
- ) {
30
- return undefined ;
31
- }
32
- const uri = document . uri ;
33
- const folder = workspace . getWorkspaceFolder ( uri ) ;
34
- // Files outside a folder can't be handled. This might depend on the language.
35
- // Single file languages like JSON might handle files outside the workspace folders.
36
- // This will be undefined if the file does not belong to the workspace
37
- if ( ! folder ) return undefined ;
38
- if ( clients . has ( folder . uri . toString ( ) ) ) return undefined ;
39
-
40
- return folder ;
41
- }
42
-
43
23
export class FortlsClient {
44
24
constructor ( private logger : LoggingService , private context ?: vscode . ExtensionContext ) {
45
25
this . logger . logInfo ( 'Fortran Language Server' ) ;
@@ -54,7 +34,8 @@ export class FortlsClient {
54
34
}
55
35
56
36
private client : LanguageClient | undefined ;
57
- private _fortlsVersion : string | undefined ;
37
+ private version : string | undefined ;
38
+ private readonly name : string = 'Fortran Language Server' ;
58
39
59
40
public async activate ( ) {
60
41
// Detect if fortls is present, download if missing or disable LS functionality
@@ -63,6 +44,15 @@ export class FortlsClient {
63
44
if ( fortlsDisabled ) return ;
64
45
workspace . onDidOpenTextDocument ( this . didOpenTextDocument , this ) ;
65
46
workspace . textDocuments . forEach ( this . didOpenTextDocument , this ) ;
47
+ workspace . onDidChangeWorkspaceFolders ( event => {
48
+ for ( const folder of event . removed ) {
49
+ const client = clients . get ( folder . uri . toString ( ) ) ;
50
+ if ( client ) {
51
+ clients . delete ( folder . uri . toString ( ) ) ;
52
+ client . stop ( ) ;
53
+ }
54
+ }
55
+ } ) ;
66
56
} ) ;
67
57
return ;
68
58
}
@@ -88,43 +78,74 @@ export class FortlsClient {
88
78
* @returns
89
79
*/
90
80
private async didOpenTextDocument ( document : TextDocument ) : Promise < void > {
91
- const folder = checkLanguageServerActivation ( document ) ;
92
- if ( ! folder ) return ;
93
-
94
- this . logger . logInfo ( 'Initialising the Fortran Language Server' ) ;
81
+ if ( ! isFortran ( document ) ) return ;
95
82
96
83
const args : string [ ] = await this . fortlsArguments ( ) ;
97
84
const executablePath = workspace . getConfiguration ( EXTENSION_ID ) . get < string > ( 'fortls.path' ) ;
98
85
99
86
// Detect language server version and verify selected options
100
- this . _fortlsVersion = this . getLSVersion ( executablePath , args ) ;
101
- if ( this . _fortlsVersion ) {
102
- const serverOptions : ServerOptions = {
103
- command : executablePath ,
104
- args : args ,
105
- } ;
87
+ this . version = this . getLSVersion ( executablePath , args ) ;
88
+ if ( ! this . version ) return ;
89
+ const serverOptions : ServerOptions = {
90
+ command : executablePath ,
91
+ args : args ,
92
+ } ;
106
93
94
+ let folder = workspace . getWorkspaceFolder ( document . uri ) ;
95
+
96
+ /**
97
+ * The strategy for registering the language server is to register an individual
98
+ * server for the top-most workspace folder. If we are outside of a workspace
99
+ * then we register the server for folder the standalone Fortran file is located.
100
+ * This has to be done in order for standalone server to NOT interfere with
101
+ * the workspace server.
102
+ *
103
+ * We also set the log channel to the Modern Fortran log output and add
104
+ * system watchers for the default configuration file .fortls and the
105
+ * configuration settings for the entire extension.
106
+ */
107
+
108
+ // If the document is part of a standalone file and not part of a workspace
109
+ if ( ! folder ) {
110
+ const fileRoot : string = path . dirname ( document . uri . fsPath ) ;
111
+ if ( clients . has ( fileRoot ) ) return ; // already registered
112
+ this . logger . logInfo ( 'Initialising Language Server for file: ' + document . uri . fsPath ) ;
107
113
// Options to control the language client
108
114
const clientOptions : LanguageClientOptions = {
109
- // Register the server for all Fortran documents in workspace
110
- // NOTE: remove the folder args and workspaceFolder to ge single file language servers
111
- // will also have to change the checkLanguageServerActivation method
112
- // we want fortran documents but not just in the workspace
113
- // if in the workspace do provide
114
- documentSelector : FortranDocumentSelector ( folder ) ,
115
+ documentSelector : FortranDocumentSelector ( fileRoot ) ,
116
+ outputChannel : this . logger . getOutputChannel ( ) ,
117
+ synchronize : {
118
+ // Synchronize all configuration settings to the server
119
+ configurationSection : EXTENSION_ID ,
120
+ // Notify the server about file changes to '.fortls files contained in the workspace
121
+ // fileEvents: workspace.createFileSystemWatcher('**/.fortls'),
122
+ } ,
123
+ } ;
124
+ this . client = new LanguageClient ( LS_NAME , this . name , serverOptions , clientOptions ) ;
125
+ this . client . start ( ) ;
126
+ clients . set ( fileRoot , this . client ) ; // Add the client to the global map
127
+ return ;
128
+ }
129
+ // The document is part of a workspace folder
130
+ if ( ! clients . has ( folder . uri . toString ( ) ) ) {
131
+ folder = getOuterMostWorkspaceFolder ( folder ) ;
132
+ if ( clients . has ( folder . uri . toString ( ) ) ) return ; // already registered
133
+ this . logger . logInfo ( 'Initialising Language Server for workspace: ' + folder . uri . fsPath ) ;
134
+ // Options to control the language client
135
+ const clientOptions : LanguageClientOptions = {
136
+ documentSelector : FortranDocumentSelector ( folder . uri . fsPath ) ,
115
137
workspaceFolder : folder ,
138
+ outputChannel : this . logger . getOutputChannel ( ) ,
139
+ synchronize : {
140
+ // Synchronize all configuration settings to the server
141
+ configurationSection : EXTENSION_ID ,
142
+ // Notify the server about file changes to '.fortls files contained in the workspace
143
+ // fileEvents: workspace.createFileSystemWatcher('**/.fortls'),
144
+ } ,
116
145
} ;
117
-
118
- // Create the language client, start the client and add it to the registry
119
- this . client = new LanguageClient (
120
- LS_NAME ,
121
- 'Fortran Language Server' ,
122
- serverOptions ,
123
- clientOptions
124
- ) ;
146
+ this . client = new LanguageClient ( LS_NAME , this . name , serverOptions , clientOptions ) ;
125
147
this . client . start ( ) ;
126
- // Add the Language Client to the global map
127
- clients . set ( folder . uri . toString ( ) , this . client ) ;
148
+ clients . set ( folder . uri . toString ( ) , this . client ) ; // Add the client to the global map
128
149
}
129
150
}
130
151
0 commit comments