Skip to content

Commit

Permalink
Completion and code navigation for labels
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippe Elsass committed Feb 3, 2021
1 parent 87588b6 commit 6eb9dff
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 5 deletions.
3 changes: 2 additions & 1 deletion src/FunctionScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { VariableDeclaration } from './interfaces';
import type { LabelDeclaration, VariableDeclaration } from './interfaces';
import type { FunctionExpression } from './parser/Expression';

//TODO I think this class can be eliminated in favor of moving some of these onto the FunctionExpression AST node
Expand All @@ -24,6 +24,7 @@ export class FunctionScope {
*/
public parentScope: FunctionScope;
public variableDeclarations = [] as VariableDeclaration[];
public labelStatements = [] as LabelDeclaration[];

/**
* Find all variable declarations above the given line index
Expand Down
13 changes: 13 additions & 0 deletions src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,19 @@ describe('BrsFile', () => {
expect(results).to.be.empty;
});

it('does provide intellisence for labels only after a goto keyword', () => {
//eslint-disable-next-line @typescript-eslint/no-floating-promises
program.addOrReplaceFile({ src: `${rootDir}/source/main.brs`, dest: 'source/main.brs' }, `
sub Main(name as string)
something:
goto \nend sub
`);

let results = program.getCompletions(`${rootDir}/source/main.brs`, Position.create(3, 25));
expect(results.length).to.equal(1);
expect(results[0]?.label).to.equal('something');
});

});

describe('comment flags', () => {
Expand Down
51 changes: 47 additions & 4 deletions src/files/BrsFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,14 @@ export class BrsFile {
name: stmt.item.text,
type: new DynamicType()
});
},
LabelStatement: (stmt) => {
const { identifier } = stmt.tokens;
scope.labelStatements.push({
nameRange: identifier.range,
lineIndex: identifier.range.start.line,
name: identifier.text
});
}
}), {
walkMode: WalkMode.visitStatements
Expand Down Expand Up @@ -789,6 +797,10 @@ export class BrsFile {
return result;
}

if (this.tokenFollows(currentToken, TokenKind.Goto)) {
return this.getLabelCompletion(functionScope);
}

if (this.isPositionNextToTokenKind(position, TokenKind.Dot)) {
if (namespaceCompletions.length > 0) {
//if we matched a namespace, after a dot, it can't be anything else but something from our namespace completions
Expand Down Expand Up @@ -830,7 +842,6 @@ export class BrsFile {

//include local variables
let variables = functionScope.variableDeclarations;

for (let variable of variables) {
//skip duplicate variable names
if (names[variable.name.toLowerCase()]) {
Expand Down Expand Up @@ -863,6 +874,13 @@ export class BrsFile {
return result;
}

private getLabelCompletion(functionScope: FunctionScope) {
return functionScope.labelStatements.map(label => ({
label: label.name,
kind: CompletionItemKind.Reference
}));
}

private getClassMemberCompletions(position: Position, currentToken: Token, functionScope: FunctionScope, scope: Scope) {

let classStatement = this.getClassFromMReference(position, currentToken, functionScope);
Expand Down Expand Up @@ -1072,8 +1090,9 @@ export class BrsFile {
}
}

private getTokenBefore(currentToken: Token, tokenKind: TokenKind) {
for (let i = this.parser.tokens.indexOf(currentToken); i >= 0; i--) {
private getTokenBefore(currentToken: Token, tokenKind: TokenKind): Token {
const index = this.parser.tokens.indexOf(currentToken);
for (let i = index - 1; i >= 0; i--) {
currentToken = this.parser.tokens[i];
if (currentToken.kind === TokenKind.Newline) {
break;
Expand All @@ -1084,6 +1103,14 @@ export class BrsFile {
return undefined;
}

private tokenFollows(currentToken: Token, tokenKind: TokenKind): boolean {
const index = this.parser.tokens.indexOf(currentToken);
if (index > 0) {
return this.parser.tokens[index - 1].kind === tokenKind;
}
return false;
}

public getTokensUntil(currentToken: Token, tokenKind: TokenKind, direction: -1 | 1 = -1) {
let tokens = [];
for (let i = this.parser.tokens.indexOf(currentToken); direction === -1 ? i >= 0 : i === this.parser.tokens.length; i += direction) {
Expand Down Expand Up @@ -1341,14 +1368,22 @@ export class BrsFile {
//look through local variables first, get the function scope for this position (if it exists)
const functionScope = this.getFunctionScopeAtPosition(position);
if (functionScope) {
//find any variable with this name
//find any variable or label with this name
for (const varDeclaration of functionScope.variableDeclarations) {
//we found a variable declaration with this token text!
if (varDeclaration.name.toLowerCase() === textToSearchFor) {
const uri = util.pathToUri(this.pathAbsolute);
results.push(Location.create(uri, varDeclaration.nameRange));
}
}
if (this.tokenFollows(token, TokenKind.Goto)) {
for (const label of functionScope.labelStatements) {
if (label.name.toLocaleLowerCase() === textToSearchFor) {
const uri = util.pathToUri(this.pathAbsolute);
results.push(Location.create(uri, label.nameRange));
}
}
}
}

const filesSearched = new Set<BrsFile>();
Expand Down Expand Up @@ -1448,6 +1483,14 @@ export class BrsFile {
};
}
}
for (let labelStatement of functionScope.labelStatements) {
if (labelStatement.name.toLocaleLowerCase() === lowerTokenText) {
return {
range: token.range,
contents: 'Label'
};
}
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ export interface VariableDeclaration {
lineIndex: number;
}

export interface LabelDeclaration {
name: string;
/**
* The range for the label name
*/
nameRange: Range;
/**
* The line of the label
*/
lineIndex: number;
}

/**
* A wrapper around a callable to provide more information about where it came from
*/
Expand Down

0 comments on commit 6eb9dff

Please sign in to comment.