Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/LanguageServer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,7 @@ describe('LanguageServer', () => {
});

expect(locations.length).to.equal(1);
const location: Location = locations[0];
const location: Location = locations[0] as Location;
expect(location.uri).to.equal(functionDocument.uri);
expect(location.range.start.line).to.equal(5);
expect(location.range.start.character).to.equal(16);
Expand All @@ -1569,7 +1569,7 @@ describe('LanguageServer', () => {
});

expect(locations.length).to.equal(1);
const location: Location = locations[0];
const location: Location = locations[0] as Location;
expect(location.uri).to.equal(functionDocument.uri);
expect(location.range.start.line).to.equal(5);
expect(location.range.start.character).to.equal(16);
Expand All @@ -1594,7 +1594,7 @@ describe('LanguageServer', () => {
position: util.createPosition(3, 36)
});
expect(locations.length).to.equal(1);
const location: Location = locations[0];
const location: Location = locations[0] as Location;
expect(location.uri).to.equal(referenceDocument.uri);
expect(location.range.start.line).to.equal(2);
expect(location.range.start.character).to.equal(20);
Expand Down Expand Up @@ -1629,7 +1629,7 @@ describe('LanguageServer', () => {
position: util.createPosition(3, 30)
});
expect(locations.length).to.equal(1);
const location: Location = locations[0];
const location: Location = locations[0] as Location;
expect(location.uri).to.equal(functionDocument.uri);
expect(location.range.start.line).to.equal(2);
expect(location.range.start.character).to.equal(20);
Expand Down
2 changes: 1 addition & 1 deletion src/LanguageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ export class LanguageServer {

const srcPath = util.uriToPath(params.textDocument.uri);

const result = this.projectManager.getDefinition({ srcPath: srcPath, position: params.position });
const result = await this.projectManager.getDefinition({ srcPath: srcPath, position: params.position });
return result;
}

Expand Down
8 changes: 3 additions & 5 deletions src/Program.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as assert from 'assert';
import * as fsExtra from 'fs-extra';
import * as path from 'path';
import type { CodeAction, CompletionItem, Position, Range, SignatureInformation, Location, DocumentSymbol, CancellationToken } from 'vscode-languageserver';
import type { CodeAction, CompletionItem, Position, Range, SignatureInformation, Location, LocationLink, DocumentSymbol, CancellationToken } from 'vscode-languageserver';
import { CancellationTokenSource, CompletionItemKind } from 'vscode-languageserver';
import type { BsConfig, FinalizedBsConfig } from './BsConfig';
import { Scope } from './Scope';
Expand Down Expand Up @@ -1001,7 +1001,7 @@ export class Program {
* Given a position in a file, if the position is sitting on some type of identifier,
* go to the definition of that identifier (where this thing was first defined)
*/
public getDefinition(srcPath: string, position: Position): Location[] {
public getDefinition(srcPath: string, position: Position): Array<Location | LocationLink> {
let file = this.getFile(srcPath);
if (!file) {
return [];
Expand All @@ -1017,12 +1017,10 @@ export class Program {
this.plugins.emit('beforeProvideDefinition', event);
this.plugins.emit('provideDefinition', event);
this.plugins.emit('afterProvideDefinition', event);

return event.definitions;
}

/**
* Get hover information for a file and position
*/
public getHover(srcPath: string, position: Position): Hover[] {
let file = this.getFile(srcPath);
let result: Hover[];
Expand Down
4 changes: 2 additions & 2 deletions src/Scope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CompletionItem, Position, Range, Location } from 'vscode-languageserver';
import type { CompletionItem, Position, Range, Location, LocationLink } from 'vscode-languageserver';
import * as path from 'path';
import { CompletionItemKind } from 'vscode-languageserver';
import chalk from 'chalk';
Expand Down Expand Up @@ -1261,7 +1261,7 @@ export class Scope {
* Get the definition (where was this thing first defined) of the symbol under the position
* @deprecated use `DefinitionProvider.process()`
*/
public getDefinition(file: BscFile, position: Position): Location[] {
public getDefinition(file: BscFile, position: Position): Array<Location | LocationLink> {
// Overridden in XMLScope. Brs files use implementation in BrsFile
return [];
}
Expand Down
2 changes: 1 addition & 1 deletion src/XmlScope.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('XmlScope', () => {
`);
const definition = program.getDefinition(childXmlFile.srcPath, Position.create(1, 48));
expect(definition).to.be.lengthOf(1);
expect(definition[0].uri).to.equal(util.pathToUri(parentXmlFile.srcPath));
expect((definition[0] as any).uri).to.equal(util.pathToUri(parentXmlFile.srcPath));
});
});

Expand Down
4 changes: 2 additions & 2 deletions src/XmlScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Location, Position } from 'vscode-languageserver';
import type { Location, LocationLink, Position } from 'vscode-languageserver';
import { Scope } from './Scope';
import { DiagnosticMessages } from './DiagnosticMessages';
import type { XmlFile } from './files/XmlFile';
Expand Down Expand Up @@ -169,7 +169,7 @@ export class XmlScope extends Scope {
* Get the definition (where was this thing first defined) of the symbol under the position
* @deprecated use `DefinitionProvider.process()`
*/
public getDefinition(file: BscFile, position: Position): Location[] {
public getDefinition(file: BscFile, position: Position): Array<Location | LocationLink> {
return new DefinitionProvider({
program: this.program,
file: file,
Expand Down
193 changes: 193 additions & 0 deletions src/bscPlugin/definition/DefinitionProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,197 @@ describe('DefinitionProvider', () => {
range: util.createRange(1, 0, 1, 0)
}]);
});

it('handles script tag uri go-to-definition', () => {
const brsFile = program.setFile('components/MainScene.brs', `
sub main()
end sub
`);
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<script type="text/brightscript" uri="pkg:/components/MainScene.brs" />
</component>
`);
// Line 2 (0-indexed): ` <script type="text/brightscript" uri="pkg:/components/MainScene.brs" />`
// The uri value range starts at the opening `"` for `pkg:/components/MainScene.brs`
const result = program.getDefinition(xmlFile.srcPath, util.createPosition(2, 60));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(brsFile.srcPath).toString()
});
// originSelectionRange should cover the full URI value (the entire filePathRange)
expect((result[0] as any).originSelectionRange).to.exist;
});

it('handles script tag uri go-to-definition with relative path', () => {
const brsFile = program.setFile('components/MainScene.brs', `
sub main()
end sub
`);
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<script type="text/brightscript" uri="MainScene.brs" />
</component>
`);
// Line 2 (0-indexed): ` <script type="text/brightscript" uri="MainScene.brs" />`
// The uri value range starts at the opening `"` for `MainScene.brs`
const result = program.getDefinition(xmlFile.srcPath, util.createPosition(2, 54));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(brsFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('returns empty array when script tag uri file is not found', () => {
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<script type="text/brightscript" uri="pkg:/components/NotFound.brs" />
</component>
`);
// click within "pkg:/components/NotFound.brs" uri value
expect(
program.getDefinition(xmlFile.srcPath, util.createPosition(2, 60))
).to.eql([]);
});

it('handles pkg:/ string literal in brs assignment (e.g. poster.uri)', () => {
const targetFile = program.setFile('source/assets.brs', `
function getAsset()
end function
`);
const main = program.setFile('source/main.brs', `
sub main()
poster = CreateObject("roSGNode", "Poster")
poster.uri = "pkg:/source/assets.brs"
end sub
`);
// Line 3 (0-indexed): ` poster.uri = "pkg:/source/assets.brs"`
// "pkg:/source/assets.brs" starts at col 29 (opening ") + content at col 30
const result = program.getDefinition(main.srcPath, util.createPosition(3, 35));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('handles relative (./) string literal in brs assignment', () => {
const targetFile = program.setFile('source/utils.brs', `
function helper()
end function
`);
const main = program.setFile('source/main.brs', `
sub main()
m.uri = "./utils.brs"
end sub
`);
// Line 2 (0-indexed): ` m.uri = "./utils.brs"`
// "./utils.brs" starts at col 24 (opening ") + content at col 25
const result = program.getDefinition(main.srcPath, util.createPosition(2, 27));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('handles relative (../) string literal in brs assignment', () => {
const targetFile = program.setFile('source/shared.brs', `
function shared()
end function
`);
const main = program.setFile('source/sub/main.brs', `
sub main()
m.uri = "../shared.brs"
end sub
`);
// Line 2 (0-indexed): ` m.uri = "../shared.brs"`
// "../shared.brs" starts at col 24 (opening ") + content at col 25
const result = program.getDefinition(main.srcPath, util.createPosition(2, 27));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('does not treat arbitrary brs string literals as file paths', () => {
const main = program.setFile('source/main.brs', `
sub main()
print "hello world"
end sub
`);
// "hello world" does not start with a path prefix — must not resolve as a file
// Line 2: ` print "hello world"`
// "hello world" starts at col 22 (opening ") + content at col 23
const result = program.getDefinition(main.srcPath, util.createPosition(2, 25));
expect(result).to.eql([]);
});

it('handles xml child node uri attribute go-to-definition', () => {
const targetFile = program.setFile('components/utils.brs', `
function helper()
end function
`);
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<children>
<Poster uri="pkg:/components/utils.brs" />
</children>
</component>
`);
// Line 3 (0-indexed): ` <Poster uri="pkg:/components/utils.brs" />`
// Attribute value "pkg:/components/utils.brs" starts (after opening ") at col 33
const result = program.getDefinition(xmlFile.srcPath, util.createPosition(3, 36));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('handles xml child node backgroundURI attribute go-to-definition', () => {
const targetFile = program.setFile('components/bg.brs', `
function getBg()
end function
`);
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<children>
<Scene backgroundURI="pkg:/components/bg.brs" />
</children>
</component>
`);
// Line 3 (0-indexed): ` <Scene backgroundURI="pkg:/components/bg.brs" />`
// backgroundURI value starts (after opening ") at col 42
const result = program.getDefinition(xmlFile.srcPath, util.createPosition(3, 45));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});

it('handles xml child node relative uri attribute go-to-definition', () => {
const targetFile = program.setFile('components/utils.brs', `
function helper()
end function
`);
const xmlFile = program.setFile('components/MainScene.xml', `
<component name="MainScene" extends="Scene">
<children>
<Poster uri="./utils.brs" />
</children>
</component>
`);
// Line 3 (0-indexed): ` <Poster uri="./utils.brs" />`
// Attribute value "./utils.brs" starts (after opening ") at col 33
const result = program.getDefinition(xmlFile.srcPath, util.createPosition(3, 35));
expect(result).to.be.lengthOf(1);
expect(result[0]).to.include({
targetUri: URI.file(targetFile.srcPath).toString()
});
expect((result[0] as any).originSelectionRange).to.exist;
});
});
Loading
Loading