Skip to content

Commit db6d1e3

Browse files
authored
Improve location of external tools (#91)
* Extract intrisinc list into its own file * Add type attributes completion gif * Add a basic language server implementation * Update the minum supported version of vscode to 1.22.0 * Update changelog * Locate bin tools by path * Improve bin tools lookup * Add command for install the fortran lang server
1 parent 02e24e1 commit db6d1e3

File tree

7 files changed

+128
-14
lines changed

7 files changed

+128
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
7171

7272
### Added
7373

74-
- Autoindentation rules for code blocks (thx @graceyangfan for the feature request)
74+
- Auto indentation rules for code blocks (thx @graceyangfan for the feature request)
7575

7676
### Fixed
7777

@@ -81,7 +81,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
8181

8282
### Fixed
8383

84-
- A bug in the regex to parse output errors from gfortran
84+
- A bug in the regex to parse output errors from `gfortran`
8585
- Now the spawn command uses the directory of the file `gfortran` is analyzing
8686

8787
## [0.5.1] - 2017-07-06

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ This is a list of some of the snippets included, if you like to include addition
9090

9191
To trigger code validations you must save the file first.
9292

93+
## Fortran Language Server (Experimental)
94+
95+
This extension uses a host of tools to provide the various language features. An alternative is to use a single language server that provides the same feature.
96+
97+
Set `fortran.useLanguageServer` to `true` to use the Fortran language server from [Chris Hansen](https://github.com/hansec/fortran-language-server) for features like Hover, Definition, Find All References, Signature Help, Go to Symbol in File and Workspace.
98+
99+
- This is an experimental feature and is not available in Windows yet.
100+
- Since only a single language server is spun up for given VS Code instance, having multi-root setup does not work
101+
- If set to true, you will be prompted to install the Fortran language server. Once installed, you will have to reload VS Code window. The language server will then be run by the Fortran extension in the background to provide services needed for the above mentioned features.
102+
- Every time you change the value of the setting `fortran.useLanguageServer`, you need to reload the VS Code window for it to take effect.
103+
93104
## Requirements
94105

95106
For the linter to work you need to have `gfortran` on your path, or wherever you configure it to be.

src/extension.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import FortranLintingProvider from './features/linter-provider'
55
import FortranHoverProvider from './features/hover-provider'
66
import { FortranCompletionProvider } from './features/completion-provider'
77
import { FortranDocumentSymbolProvider } from './features/document-symbol-provider'
8-
import { FORTRAN_FREE_FORM_ID } from './lib/helper'
9-
import { FortranLangServer } from './lang-server'
10-
import { ConfigurationFeature } from 'vscode-languageclient/lib/configuration'
8+
9+
import { FORTRAN_FREE_FORM_ID, EXTENSION_ID } from './lib/helper'
10+
import { FortranLangServer, checkForLangServer } from './lang-server'
1111

1212

1313
export function activate(context: vscode.ExtensionContext) {
1414
let hoverProvider = new FortranHoverProvider()
1515
let completionProvider = new FortranCompletionProvider()
1616
let symbolProvider = new FortranDocumentSymbolProvider()
1717

18-
const extensionConfig = vscode.workspace.getConfiguration('LANGUAGE_ID')
18+
const extensionConfig = vscode.workspace.getConfiguration(EXTENSION_ID)
1919

2020
if (extensionConfig.get('linterEnabled', true)) {
2121
let linter = new FortranLintingProvider()
@@ -33,7 +33,9 @@ export function activate(context: vscode.ExtensionContext) {
3333
FORTRAN_FREE_FORM_ID,
3434
symbolProvider
3535
)
36-
if (extensionConfig.get('useLanguageServer')) {
36+
37+
if (checkForLangServer(extensionConfig)) {
38+
3739
const langServer = new FortranLangServer(context, extensionConfig)
3840
langServer.start()
3941
langServer.onReady().then(() => {

src/lang-server.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@ import {
33
LanguageClientOptions,
44
Executable,
55
} from 'vscode-languageclient'
6-
import { getBinPath, FORTRAN_FREE_FORM_ID } from './lib/helper'
6+
import * as vscode from 'vscode'
7+
import {
8+
getBinPath,
9+
FORTRAN_FREE_FORM_ID,
10+
promptForMissingTool,
11+
} from './lib/helper'
12+
import { LANG_SERVER_TOOL_ID } from './lib/tools'
13+
14+
export class FortranLangServer {
715

8-
class FortranLangServer {
916
c: LanguageClient
1017
constructor(context, config) {
1118
let langServerFlags: string[] = config.get('languageServerFlags', [])
1219

1320
const serverOptions: Executable = {
14-
command: getBinPath('fortran-langserver'),
21+
command: getBinPath(LANG_SERVER_TOOL_ID),
1522
args: [...langServerFlags],
1623
options: {},
1724
}
@@ -42,4 +49,22 @@ class FortranLangServer {
4249
}
4350
}
4451

45-
export { FortranLangServer }
52+
export function checkForLangServer(config) {
53+
const useLangServer = config.get('useLanguageServer')
54+
if (!useLangServer) return false
55+
if (process.platform === 'win32') {
56+
vscode.window.showInformationMessage(
57+
'The Fortran language server is not supported on Windows yet.'
58+
)
59+
return false
60+
}
61+
let langServerAvailable = getBinPath('fortran-langserver')
62+
if (!langServerAvailable) {
63+
promptForMissingTool(LANG_SERVER_TOOL_ID)
64+
vscode.window.showInformationMessage(
65+
'Reload VS Code window after installing the Fortran language server'
66+
)
67+
}
68+
return true
69+
}
70+

src/lib/helper.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as fs from 'fs'
22
import * as vscode from 'vscode'
33
import intrinsics from './fortran-intrinsics'
4+
import { installTool } from './tools'
45

56
// IMPORTANT: this should match the value
67
// on the package.json otherwise the extension won't
@@ -10,7 +11,6 @@ export const FORTRAN_FREE_FORM_ID = { language: LANGUAGE_ID, scheme: 'file' }
1011
export { intrinsics }
1112
export const EXTENSION_ID = 'fortran'
1213

13-
1414
export const FORTRAN_KEYWORDS = [
1515
'FUNCTION',
1616
'MODULE',
@@ -118,6 +118,19 @@ let saveKeywordToJson = keyword => {
118118
})
119119
}
120120

121-
export function getBinPath(tool: string): string {
122-
return '/usr/local/bin/fortls'
121+
export { default as getBinPath } from './paths'
122+
123+
export function promptForMissingTool(tool: string) {
124+
const items = ['Install']
125+
let message = ''
126+
if (tool === 'fortran-langserver') {
127+
message =
128+
'You choose to use the fortranLanguageServer functionality but it is not installed. Please press the Install button to install it'
129+
}
130+
vscode.window.showInformationMessage(message, ...items).then(selected => {
131+
if (selected === 'Install') {
132+
installTool(tool)
133+
}
134+
})
135+
123136
}

src/lib/paths.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import { toolBinNames } from './tools'
4+
5+
let binPathCache: { [tool: string]: string } = {}
6+
7+
export const envPath =
8+
process.env['PATH'] ||
9+
(process.platform === 'win32' ? process.env['Path'] : null)
10+
11+
// checks in the PATH defined in the PATH env variables
12+
// for the specified tools and returns its complete path
13+
export default function getBinPath(tool: string): string | null {
14+
if (binPathCache[tool]) return binPathCache[tool]
15+
const binDirPaths = envPath.split(path.delimiter)
16+
const binName = getBinName(tool)
17+
const possiblePaths = binDirPaths.map(binDirPath =>
18+
path.join(binDirPath, binName)
19+
)
20+
for (let p of possiblePaths) {
21+
if (fileExists(p)) {
22+
// save in cache
23+
binPathCache[tool] = p
24+
return p
25+
}
26+
}
27+
return null
28+
}
29+
30+
function getBinName(tool: string): string {
31+
return toolBinNames[tool]
32+
}
33+
34+
function fileExists(filePath: string): boolean {
35+
try {
36+
return fs.statSync(filePath).isFile()
37+
} catch (e) {
38+
return false
39+
}
40+
}

src/lib/tools.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const LANG_SERVER_TOOL_ID = 'fortran-langserver'
2+
import * as cp from 'child_process'
3+
export const toolBinNames = {
4+
[LANG_SERVER_TOOL_ID]: 'fortls',
5+
'gnu-compiler': 'gfortran',
6+
}
7+
8+
export function installTool(toolname) {
9+
if (toolname === LANG_SERVER_TOOL_ID) {
10+
const installProcess = cp.spawn(
11+
'pip',
12+
'install fortran-language-server'.split(' ')
13+
)
14+
installProcess.on('exit', (code, signal) => {
15+
if (code !== 0) {
16+
// extension failed to install
17+
}
18+
})
19+
installProcess.on('error', err => {
20+
//failed to install
21+
})
22+
}
23+
}

0 commit comments

Comments
 (0)