diff --git a/src/builtin-addons/core/template-definition-provider.ts b/src/builtin-addons/core/template-definition-provider.ts index 4126d482..e12aa616 100644 --- a/src/builtin-addons/core/template-definition-provider.ts +++ b/src/builtin-addons/core/template-definition-provider.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import { Definition, Location } from 'vscode-languageserver/node'; +import { Definition, Location, Range } from 'vscode-languageserver/node'; import { DefinitionFunctionParams } from './../../utils/addon-api'; import { isLinkToTarget, isLinkComponentRouteTarget, isOutlet } from './../../utils/ast-helpers'; import ASTPath from './../../glimmer-utils'; @@ -67,6 +67,12 @@ export default class TemplateDefinitionProvider { this.server = server; this.project = project; } + isStringPath(focusPath: ASTPath): boolean { + const node = focusPath.node as ASTv1.StringLiteral | ASTv1.TextNode; + const isStringable = node.type === 'StringLiteral' || node.type === 'TextNode'; + + return isStringable ? true : false; + } async onDefinition(_: string, params: DefinitionFunctionParams): Promise { const uri = params.textDocument.uri; @@ -109,6 +115,17 @@ export default class TemplateDefinitionProvider { definitions = await this.provideRouteDefinition((focusPath.node as ASTv1.PathExpression).original); } + // Fallind back to script assets lookup in case of empty definitions + if (!definitions.length && this.isStringPath(focusPath)) { + const node = focusPath.node as ASTv1.StringLiteral | ASTv1.TextNode; + const text = node.type === 'StringLiteral' ? node.original : node.chars; + const itemPath = path.join(this.project.root, 'public', text); + + if (await this.server.fs.exists(itemPath)) { + definitions = [Location.create(URI.file(itemPath).toString(), Range.create(0, 0, 0, 0))]; + } + } + return definitions; } looksLikeClassicComponentName(name: string) { diff --git a/test/__snapshots__/integration-test.ts.snap b/test/__snapshots__/integration-test.ts.snap index 59488e4c..1b9c22bd 100644 --- a/test/__snapshots__/integration-test.ts.snap +++ b/test/__snapshots__/integration-test.ts.snap @@ -3184,6 +3184,62 @@ Object { } `; +exports[`integration async fs enabled: false Go to definition works for all supported cases works for string asset paths in public folder [StringLiteral] 1`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "component": Object { + "hello": Array [ + "app/components/hello.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/public/assets/img.jpg", + }, + ], +} +`; + +exports[`integration async fs enabled: false Go to definition works for all supported cases works for string asset paths in public folder [TextNode] 1`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "component": Object { + "hello": Array [ + "app/components/hello.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/public/assets/img.jpg", + }, + ], +} +`; + exports[`integration async fs enabled: false Initialize request returns an initialize request 1`] = ` Object { "capabilities": Object { @@ -6738,6 +6794,62 @@ Object { } `; +exports[`integration async fs enabled: true Go to definition works for all supported cases works for string asset paths in public folder [StringLiteral] 1`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "component": Object { + "hello": Array [ + "app/components/hello.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/public/assets/img.jpg", + }, + ], +} +`; + +exports[`integration async fs enabled: true Go to definition works for all supported cases works for string asset paths in public folder [TextNode] 1`] = ` +Object { + "addonsMeta": Array [], + "registry": Object { + "component": Object { + "hello": Array [ + "app/components/hello.hbs", + ], + }, + }, + "response": Array [ + Object { + "range": Object { + "end": Object { + "character": 0, + "line": 0, + }, + "start": Object { + "character": 0, + "line": 0, + }, + }, + "uri": "/public/assets/img.jpg", + }, + ], +} +`; + exports[`integration async fs enabled: true Initialize request returns an initialize request 1`] = ` Object { "capabilities": Object { diff --git a/test/integration-test.ts b/test/integration-test.ts index 7ab657aa..20ac9b3b 100644 --- a/test/integration-test.ts +++ b/test/integration-test.ts @@ -83,6 +83,38 @@ describe('integration', function () { }); describe('Go to definition works for all supported cases', () => { + it('works for string asset paths in public folder [TextNode]', async () => { + const key = 'assets/img.jpg'; + const entry = 'app/components/hello.hbs'; + const result = await getResult( + DefinitionRequest.method, + connection, + { + [`public/${key}`]: '', + [entry]: ``, + }, + entry, + { line: 0, character: 11 } + ); + + expect(result).toMatchSnapshot(); + }); + it('works for string asset paths in public folder [StringLiteral]', async () => { + const key = 'assets/img.jpg'; + const entry = 'app/components/hello.hbs'; + const result = await getResult( + DefinitionRequest.method, + connection, + { + [`public/${key}`]: '', + [entry]: `{{img-src "${key}"}}>`, + }, + entry, + { line: 0, character: 12 } + ); + + expect(result).toMatchSnapshot(); + }); it('to to route defintion from LinkTo component', async () => { const result = await getResult( DefinitionRequest.method,