Skip to content

Commit 887b649

Browse files
committed
add document symbols provider
1 parent 3023d2f commit 887b649

File tree

5 files changed

+140
-1
lines changed

5 files changed

+140
-1
lines changed

src/features/completion-provider.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CancellationToken, TextDocument, Position, Hover } from "vscode";
44
import * as fs from 'fs';
55
import * as vscode from 'vscode';
66
import { isPositionInString, intrinsics, FORTRAN_KEYWORDS } from "../lib/helper";
7+
import { getDeclaredFunctions } from "../lib/functions";
78

89

910
export class FortranCompletionProvider implements vscode.CompletionItemProvider {
@@ -54,6 +55,13 @@ export class FortranCompletionProvider implements vscode.CompletionItemProvider
5455
}
5556
});
5657
}
58+
const functions = getDeclaredFunctions(document);
59+
// check for available functions
60+
functions.filter(fun => fun.name.startsWith(currentWord))
61+
.forEach(fun =>{
62+
suggestions.push(new vscode.CompletionItem(fun.name, vscode.CompletionItemKind.Function));
63+
});
64+
5765

5866
return resolve(suggestions);
5967

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
3+
import { CancellationToken, TextDocument, Position, Hover } from "vscode";
4+
5+
import * as vscode from 'vscode';
6+
import { getDeclaredFunctions } from '../lib/functions'
7+
8+
9+
export class FortranDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
10+
11+
public provideDocumentSymbols(document: TextDocument, token: CancellationToken){
12+
13+
let functions = getDeclaredFunctions(document).map(fun => {
14+
15+
let range = new vscode.Range(fun.lineNumber, 0, fun.lineNumber, 100);
16+
return new vscode.SymbolInformation(fun.name, vscode.SymbolKind.Function, range );
17+
});
18+
19+
20+
return functions;
21+
}
22+
23+
}

src/lib/functions.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
import * as vscode from 'vscode';
3+
4+
export interface Variable {
5+
name: string;
6+
type?: string;
7+
}
8+
9+
export interface Function {
10+
name: string;
11+
args: Variable[];
12+
return: Variable;
13+
docstr: string;
14+
lineNumber: number
15+
}
16+
17+
18+
19+
export function getDeclaredFunctions(document: vscode.TextDocument): Function[] {
20+
21+
let lines = document.lineCount;
22+
let funs = [];
23+
24+
for (let i = 0; i < lines; i++) {
25+
let line: vscode.TextLine = document.lineAt(i);
26+
if (line.isEmptyOrWhitespace) continue;
27+
let newFunc = parseFunction(line.text)
28+
if(newFunc){
29+
funs.push({...newFunc, lineNumber: i });
30+
}
31+
}
32+
return funs;
33+
}
34+
35+
export const parseFunction = (line: string) => {
36+
37+
const functionRegEx = /function\s*([a-zA-Z][a-zA-Z0-9_]*)\s*\((\s*[a-zA-z][a-zA-z0-9_,\s]*)*\s*\)/g
38+
if (line.match(functionRegEx)) {
39+
40+
let [name, argsstr] = functionRegEx.exec(line).slice(1, 3);
41+
let args = (argsstr)? parseArgs(argsstr): [];
42+
return {
43+
name: name,
44+
args: args,
45+
return: null
46+
};
47+
}
48+
}
49+
50+
export const parseArgs = (argsstr: string) => {
51+
let args = argsstr.trim().split(',');
52+
let variables: Variable[] = args.filter(name => validVariableName(name))
53+
.map(name => {
54+
return { name: name };
55+
});
56+
return variables;
57+
}
58+
59+
export const validVariableName = (name: string) => {
60+
return name.trim().match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) !== null;
61+
}
62+

test/extension.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ suite("Extension Tests", () => {
3131
});
3232
};
3333

34-
test("load doc files", () => {
34+
test.skip("load doc files", () => {
3535
intrinsics.map( keyword => saveKeywordToJson(keyword));
3636
});
3737
});

test/helper.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// Note: This example test is leveraging the Mocha test framework.
3+
// Please refer to their documentation on https://mochajs.org/ for help.
4+
//
5+
6+
// The module 'assert' provides assertion methods from node
7+
import * as assert from 'assert';
8+
import * as fs from 'fs';
9+
import { validVariableName, parseFunction, parseArgs } from '../src/lib/functions';
10+
11+
suite("function helper test", () => {
12+
13+
test("validVariableName does not allow variables starting with number", () => {
14+
assert.equal(false, validVariableName("1as"));
15+
});
16+
test("validVariableName returns true with correct variable", () => {
17+
assert.equal(true, validVariableName("matA"));
18+
});
19+
test("validVariableName returns true for variables starting with uppercase", () => {
20+
assert.equal(true, validVariableName("MatA"));
21+
});
22+
test("validVariableName return true for variable starting with _", () => {
23+
assert.equal(true, validVariableName("_matA"));
24+
});
25+
test("parseFuntion return undefined on empty line", () => {
26+
assert.equal(undefined, parseFunction(""));
27+
});
28+
test("parseFuntion return undefined if function keyword is missing", () => {
29+
assert.equal(undefined, parseFunction("hello"));
30+
});
31+
test("parseFuntion return correct function name", () => {
32+
assert.equal("hello", parseFunction("function hello()").name);
33+
});
34+
35+
test("parseFuntion return correct number of args", () => {
36+
assert.equal(2, parseFunction("function hello( a, b)").args.length);
37+
});
38+
test("parseArgs return the correct number of args", () => {
39+
assert.equal(2, parseArgs("a,b").length);
40+
})
41+
test("parseArgs handle spaces well", () => {
42+
assert.equal(2, parseArgs(" a, b").length);
43+
});
44+
});
45+
46+

0 commit comments

Comments
 (0)