11'use strict' ;
22
3+ import * as os from 'os' ;
34import * as path from 'path' ;
45import * as vscode from 'vscode' ;
56import { spawnSync } from 'child_process' ;
@@ -13,7 +14,6 @@ import {
1314 getOuterMostWorkspaceFolder ,
1415 pipInstall ,
1516 resolveVariables ,
16- pathRelToAbs ,
1717} from '../lib/tools' ;
1818import { Logger } from '../services/logging' ;
1919import { RestartLS } from '../features/commands' ;
@@ -84,9 +84,7 @@ export class FortlsClient {
8484 if ( ! isFortran ( document ) ) return ;
8585
8686 const args : string [ ] = await this . fortlsArguments ( ) ;
87- const fortlsPath = workspace . getConfiguration ( EXTENSION_ID ) . get < string > ( 'fortls.path' ) ;
88- let executablePath = resolveVariables ( fortlsPath ) ;
89- if ( fortlsPath !== 'fortls' ) executablePath = pathRelToAbs ( fortlsPath , document . uri ) ;
87+ const executablePath : string = await this . fortlsPath ( document ) ;
9088
9189 // Detect language server version and verify selected options
9290 this . version = this . getLSVersion ( executablePath , args ) ;
@@ -308,14 +306,7 @@ export class FortlsClient {
308306 */
309307 private async fortlsDownload ( ) : Promise < boolean > {
310308 const config = workspace . getConfiguration ( EXTENSION_ID ) ;
311- let ls = resolveVariables ( config . get < string > ( 'fortls.path' ) ) ;
312- // The path can be resolved as a relative path if it's part of a workspace
313- // AND it does not have the default value `fortls` or is an absolute path
314- if ( workspace . workspaceFolders == undefined && ls !== 'fortls' && ! path . isAbsolute ( ls ) ) {
315- const root = workspace . workspaceFolders [ 0 ] ;
316- this . logger . debug ( `[lsp.client] Assuming relative fortls path is to ${ root . uri . fsPath } ` ) ;
317- ls = pathRelToAbs ( ls , root . uri ) ;
318- }
309+ const ls = await this . fortlsPath ( ) ;
319310
320311 // Check for version, if this fails fortls provided is invalid
321312 const results = spawnSync ( ls , [ '--version' ] ) ;
@@ -349,6 +340,50 @@ export class FortlsClient {
349340 } ) ;
350341 }
351342
343+ /**
344+ * Try and find the path to the `fortls` executable.
345+ * It will first try and fetch the top-most workspaceFolder from `document`.
346+ * If that fails because the document is standalone and does not belong in a
347+ * workspace it will assume that relative paths are wrt `os.homedir()`.
348+ *
349+ * If the `document` argument is missing, then it will try and find the
350+ * first workspaceFolder and use that as the root. If that fails it will
351+ * revert back to `os.homedir()`.
352+ *
353+ * @param document Optional textdocument
354+ * @returns a promise with the path to the fortls executable
355+ */
356+ private async fortlsPath ( document ?: TextDocument ) : Promise < string > {
357+ // Get the workspace folder that contains the document, this can be undefined
358+ // which means that the document is standalone and not part of any workspace.
359+ let folder : vscode . WorkspaceFolder | undefined ;
360+ if ( document ) {
361+ folder = workspace . getWorkspaceFolder ( document . uri ) ;
362+ }
363+ // If the document argument is missing, such as in the case of the Client's
364+ // activation, then try and fetch the first workspace folder to use as a root.
365+ else {
366+ folder = workspace . workspaceFolders [ 0 ] ? workspace . workspaceFolders [ 0 ] : undefined ;
367+ }
368+
369+ // Get the outer most workspace folder to resolve relative paths, but if
370+ // the folder is undefined then use the home directory of the OS
371+ const root = folder ? getOuterMostWorkspaceFolder ( folder ) . uri : vscode . Uri . parse ( os . homedir ( ) ) ;
372+
373+ const config = workspace . getConfiguration ( EXTENSION_ID ) ;
374+ let executablePath = resolveVariables ( config . get < string > ( 'fortls.path' ) ) ;
375+
376+ // The path can be resolved as a relative path if:
377+ // 1. it does not have the default value `fortls` AND
378+ // 2. is not an absolute path
379+ if ( executablePath !== 'fortls' && ! path . isAbsolute ( executablePath ) ) {
380+ this . logger . debug ( `[lsp.client] Assuming relative fortls path is to ${ root . fsPath } ` ) ;
381+ executablePath = path . join ( root . fsPath , executablePath ) ;
382+ }
383+
384+ return executablePath ;
385+ }
386+
352387 /**
353388 * Restart the language server
354389 */
0 commit comments