Skip to content

Commit

Permalink
Fix GoToDefinition for component imports (and component usage) (#1045)
Browse files Browse the repository at this point in the history
* Fix GoToDefinition for component imports (and component usage)

* Remove extra comment
  • Loading branch information
matthewp authored Aug 9, 2021
1 parent 812c9d8 commit 5293519
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}/tools/astro-vscode"],
"outFiles": ["${workspaceRoot}/tools/astro-vscode/dist/**/*.js"]
"args": ["--extensionDevelopmentPath=${workspaceRoot}/tools/vscode"],
"outFiles": ["${workspaceRoot}/tools/vscode/dist/**/*.js"]
},
{
"type": "node",
"request": "attach",
"name": "Attach to Server",
"port": 6040,
"restart": true,
"outFiles": ["${workspaceRoot}/tools/astro-languageserver/dist/**/*.js"],
"outFiles": ["${workspaceRoot}/tools/languageserver/dist/**/*.js"],
"skipFiles": ["<node_internals>/**"]
}
],
Expand Down
20 changes: 18 additions & 2 deletions tools/language-server/src/plugins/typescript/DocumentSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { isInTag, positionAt, offsetAt } from '../../core/documents/utils';
import { pathToUrl } from '../../utils';
import { getScriptKindFromFileName, isAstroFilePath, toVirtualAstroFilePath } from './utils';

const FILLER_DEFAULT_EXPORT = `\nexport default function() { return ''; };`;

/**
* The mapper to get from original snapshot positions to generated and vice versa.
*/
Expand Down Expand Up @@ -66,7 +68,15 @@ class AstroDocumentSnapshot implements DocumentSnapshot {
}

get text() {
return this.doc.getText();
let raw = this.doc.getText();
return this.transformContent(raw);
}

/** @internal */
private transformContent(content: string) {
return content.replace(/---/g, '///') +
// TypeScript needs this to know there's a default export.
FILLER_DEFAULT_EXPORT;
}

get filePath() {
Expand Down Expand Up @@ -128,7 +138,9 @@ export class DocumentFragmentSnapshot implements Omit<DocumentSnapshot, 'getFrag

/** @internal */
private transformContent(content: string) {
return content.replace(/---/g, '///');
return content.replace(/---/g, '///') +
// TypeScript needs this to know there's a default export.
FILLER_DEFAULT_EXPORT;
}

getText(start: number, end: number) {
Expand Down Expand Up @@ -214,6 +226,10 @@ export class TypeScriptDocumentSnapshot implements DocumentSnapshot {
return this as unknown as any;
}

getOriginalPosition(pos: Position): Position {
return pos;
}

destroyFragment() {
// nothing to clean up
}
Expand Down
72 changes: 66 additions & 6 deletions tools/language-server/src/plugins/typescript/TypeScriptPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { join as pathJoin, dirname as pathDirname } from 'path';
import { Document, DocumentManager, isInsideFrontmatter } from '../../core/documents';
import type { ConfigManager } from '../../core/config';
import type { CompletionsProvider, AppCompletionItem, AppCompletionList } from '../interfaces';
import { SourceFile, ImportDeclaration, Node, SyntaxKind } from 'typescript';
import { CompletionContext, DefinitionLink, FileChangeType, Position, LocationLink } from 'vscode-languageserver';
import * as ts from 'typescript';
import { CompletionsProviderImpl, CompletionEntryWithIdentifer } from './features/CompletionsProvider';
import { LanguageServiceManager } from './LanguageServiceManager';
import { SnapshotManager } from './SnapshotManager';
import { convertToLocationRange, isVirtualFilePath, getScriptKindFromFileName } from './utils';
import { convertToLocationRange, isVirtualAstroFilePath, isVirtualFilePath, getScriptKindFromFileName } from './utils';
import { isNoTextSpanInGeneratedCode, SnapshotFragmentMap } from './features/utils';
import { isNotNullOrUndefined, pathToUrl } from '../../utils';

type BetterTS = typeof ts & {
getTouchingPropertyName(sourceFile: SourceFile, pos: number): Node;
};

export class TypeScriptPlugin implements CompletionsProvider {
private readonly docManager: DocumentManager;
private readonly configManager: ConfigManager;
Expand Down Expand Up @@ -46,26 +52,38 @@ export class TypeScriptPlugin implements CompletionsProvider {
const filePath = tsDoc.filePath;
const tsFilePath = filePath.endsWith('.ts') ? filePath : filePath + '.ts';

const defs = lang.getDefinitionAndBoundSpan(tsFilePath, mainFragment.offsetAt(mainFragment.getGeneratedPosition(position)));
const fragmentPosition = mainFragment.getGeneratedPosition(position);
const fragmentOffset = mainFragment.offsetAt(fragmentPosition);

let defs = lang.getDefinitionAndBoundSpan(tsFilePath, fragmentOffset);

if (!defs || !defs.definitions) {
return [];
}

// Resolve all imports if we can
if(this.goToDefinitionFoundOnlyAlias(tsFilePath, defs.definitions!)) {
let importDef = this.getGoToDefinitionRefsForImportSpecifier(tsFilePath, fragmentOffset, lang);
if(importDef) {
defs = importDef;
}
}

const docs = new SnapshotFragmentMap(this.languageServiceManager);
docs.set(tsDoc.filePath, { fragment: mainFragment, snapshot: tsDoc });

const result = await Promise.all(
defs.definitions.map(async (def) => {
defs.definitions!.map(async (def) => {
const { fragment, snapshot } = await docs.retrieve(def.fileName);

if (isNoTextSpanInGeneratedCode(snapshot.getFullText(), def.textSpan)) {
const fileName = isVirtualFilePath(def.fileName) ? def.fileName.substr(0, def.fileName.length - 3) : def.fileName;
const textSpan = isVirtualAstroFilePath(tsFilePath) ? { start: 0, length: 0 } : def.textSpan;
return LocationLink.create(
pathToUrl(fileName),
convertToLocationRange(fragment, def.textSpan),
convertToLocationRange(fragment, def.textSpan),
convertToLocationRange(mainFragment, defs.textSpan)
convertToLocationRange(fragment, textSpan),
convertToLocationRange(fragment, textSpan),
convertToLocationRange(mainFragment, defs!.textSpan)
);
}
})
Expand Down Expand Up @@ -110,4 +128,46 @@ export class TypeScriptPlugin implements CompletionsProvider {
private isInsideFrontmatter(document: Document, position: Position) {
return isInsideFrontmatter(document.getText(), document.offsetAt(position));
}

private goToDefinitionFoundOnlyAlias(tsFileName: string, defs: readonly ts.DefinitionInfo[]) {
return !!(defs.length === 1 &&
defs[0].kind === 'alias' &&
defs[0].fileName === tsFileName);
}

private getGoToDefinitionRefsForImportSpecifier(tsFilePath: string, offset: number, lang: ts.LanguageService): ts.DefinitionInfoAndBoundSpan | undefined {
const program = lang.getProgram();
const sourceFile = program?.getSourceFile(tsFilePath);
if (sourceFile) {
let node = (ts as BetterTS).getTouchingPropertyName(sourceFile, offset);
if(node && node.kind === SyntaxKind.Identifier) {
if(node.parent.kind === SyntaxKind.ImportClause) {
let decl = node.parent.parent as ImportDeclaration;
let spec = ts.isStringLiteral(decl.moduleSpecifier) && decl.moduleSpecifier.text;
if(spec) {
let fileName = pathJoin(pathDirname(tsFilePath), spec);
let start = node.pos + 1;
let def: ts.DefinitionInfoAndBoundSpan = {
definitions: [{
kind: 'alias',
fileName,
name: '',
containerKind: '',
containerName: '',
textSpan: {
start: 0,
length: 0
}
} as ts.DefinitionInfo],
textSpan: {
start,
length: node.end - start
}
};
return def;
}
}
}
}
}
}
2 changes: 1 addition & 1 deletion tools/language-server/src/plugins/typescript/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export function ensureRealAstroFilePath(filePath: string) {
}

export function ensureRealFilePath(filePath: string) {
return isVirtualFilePath(filePath) ? filePath.slice(0, 3) : filePath;
return isVirtualFilePath(filePath) ? filePath.slice(0, filePath.length - 3) : filePath;
}

export function findTsConfigPath(fileName: string, rootUris: string[]) {
Expand Down

2 comments on commit 5293519

@vercel
Copy link

@vercel vercel bot commented on 5293519 Aug 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

astro-docs – ./docs

astro-docs-git-main-pikapkg.vercel.app
astro-docs-pikapkg.vercel.app
astro-docs.vercel.app
docs.astro.build

@vercel
Copy link

@vercel vercel bot commented on 5293519 Aug 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.