Skip to content

Commit 0ae300a

Browse files
authored
Merge pull request #672 from fortran-lang:gnikit/issue667
feat: local path resolution
2 parents f737832 + 17845a0 commit 0ae300a

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Added
1111

12+
- Added local path resolution for `fortls`, `findent` and `fprettify` executables
13+
([#667](https://github.com/fortran-lang/vscode-fortran-support/issues/667))
1214
- Added support for variable resolution in `fortls`
1315
([#664](https://github.com/fortran-lang/vscode-fortran-support/issues/664))
1416
- Added Run and Debug buttons for single Fortran files based on linter settings

src/features/formatting-provider.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
promptForMissingTool,
1414
getWholeFileRange,
1515
spawnAsPromise,
16+
pathRelToAbs,
1617
} from '../lib/tools';
1718

1819
export class FortranFormattingProvider implements vscode.DocumentFormattingEditProvider {
@@ -53,7 +54,8 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
5354
}
5455

5556
const formatterName = process.platform !== 'win32' ? 'fprettify' : 'fprettify.exe';
56-
const formatterPath: string = this.getFormatterPath();
57+
const formatterPath: string =
58+
this.formatterPath === '' ? '' : pathRelToAbs(this.formatterPath, document.uri);
5759
const formatter: string = path.join(formatterPath, formatterName);
5860
// If no formatter is detected try and install it
5961
if (!which.sync(formatter, { nothrow: true })) {
@@ -78,7 +80,8 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
7880
*/
7981
private async doFormatFindent(document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
8082
const formatterName = process.platform !== 'win32' ? 'findent' : 'findent.exe';
81-
const formatterPath: string = this.getFormatterPath();
83+
const formatterPath: string =
84+
this.formatterPath === '' ? '' : pathRelToAbs(this.formatterPath, document.uri);
8285
const formatter: string = path.join(formatterPath, formatterName);
8386
// If no formatter is detected try and install it
8487
if (!which.sync(formatter, { nothrow: true })) {
@@ -128,7 +131,7 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
128131
*
129132
* @returns {string} path of formatter
130133
*/
131-
private getFormatterPath(): string {
134+
private get formatterPath(): string {
132135
const formatterPath: string = this.workspace.get('formatting.path', '');
133136
if (formatterPath !== '') {
134137
this.logger.info(`[format] Formatter located in: ${formatterPath}`);

src/lib/tools.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as os from 'os';
2+
import * as path from 'path';
23
import * as vscode from 'vscode';
34
import * as assert from 'assert';
45
import * as cp from 'child_process';
@@ -76,8 +77,9 @@ export function sortedWorkspaceFolders(): string[] | undefined {
7677
* @returns outer most workspace folder
7778
*/
7879
export function getOuterMostWorkspaceFolder(
79-
folder: vscode.WorkspaceFolder
80-
): vscode.WorkspaceFolder {
80+
folder: vscode.WorkspaceFolder | undefined
81+
): vscode.WorkspaceFolder | undefined {
82+
if (folder === undefined) return undefined;
8183
const sorted = sortedWorkspaceFolders();
8284
for (const element of sorted) {
8385
let uri = folder.uri.toString();
@@ -262,6 +264,22 @@ export function resolveVariables(
262264
// return new Promise<string>((resolve) => { resolve(ret) });
263265
}
264266

267+
/**
268+
* Resolves a path relative to the workspace folder, an optional `uri`
269+
* can be provided to help retrieve the correct workspace folder when working
270+
* with multi-root workspaces, else the first workspace folder is used.
271+
*
272+
* @param relPath relative path to resolve against the workspace folder
273+
* @param uri optional uri of a file/folder within the workspace folder
274+
* usefull when using multiroot workspaces to pick the right workspace root
275+
* @returns absolute path relative to the workspace root
276+
*/
277+
export function pathRelToAbs(relPath: string, uri: vscode.Uri): string | undefined {
278+
const root = getOuterMostWorkspaceFolder(vscode.workspace.getWorkspaceFolder(uri));
279+
if (root === undefined) return undefined;
280+
return path.join(root.uri.fsPath, relPath);
281+
}
282+
265283
export function getWholeFileRange(document: vscode.TextDocument): vscode.Range {
266284
return new vscode.Range(0, 0, document.lineCount, 0);
267285
}

src/lsp/client.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getOuterMostWorkspaceFolder,
1414
pipInstall,
1515
resolveVariables,
16+
pathRelToAbs,
1617
} from '../lib/tools';
1718
import { Logger } from '../services/logging';
1819
import { RestartLS } from '../features/commands';
@@ -84,7 +85,8 @@ export class FortlsClient {
8485

8586
const args: string[] = await this.fortlsArguments();
8687
const fortlsPath = workspace.getConfiguration(EXTENSION_ID).get<string>('fortls.path');
87-
const executablePath = resolveVariables(fortlsPath);
88+
let executablePath = resolveVariables(fortlsPath);
89+
if (fortlsPath !== 'fortls') executablePath = pathRelToAbs(fortlsPath, document.uri);
8890

8991
// Detect language server version and verify selected options
9092
this.version = this.getLSVersion(executablePath, args);
@@ -306,7 +308,14 @@ export class FortlsClient {
306308
*/
307309
private async fortlsDownload(): Promise<boolean> {
308310
const config = workspace.getConfiguration(EXTENSION_ID);
309-
const ls = resolveVariables(config.get<string>('fortls.path'));
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+
}
310319

311320
// Check for version, if this fails fortls provided is invalid
312321
const results = spawnSync(ls, ['--version']);

test/tools.test.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as path from 'path';
22
import * as assert from 'assert';
3-
import { shellTask, spawnAsPromise } from '../src/lib/tools';
3+
import { Uri } from 'vscode';
4+
import { shellTask, spawnAsPromise, pathRelToAbs } from '../src/lib/tools';
45

56
suite('Tools tests', () => {
67
test('shellTask returns correct output', async () => {
@@ -49,4 +50,17 @@ suite('Tools tests', () => {
4950
assert.strictEqual(stdout, 'Hello World!');
5051
assert.strictEqual(stderr, 'Errors');
5152
});
53+
54+
test('Resolve local paths: undefined', () => {
55+
const root = Uri.parse('/home/user/project');
56+
const absPath = pathRelToAbs('./sample.f90', root);
57+
console.log(absPath, root);
58+
assert.strictEqual(absPath, undefined);
59+
});
60+
61+
test('Resolve local paths: workspace selection', () => {
62+
const root = Uri.parse(path.resolve(__dirname, '../../test/fortran'));
63+
const absPath = pathRelToAbs('./sample.f90', root);
64+
assert.strictEqual(absPath, path.resolve(__dirname, '../../test/fortran/sample.f90'));
65+
});
5266
});

0 commit comments

Comments
 (0)