Skip to content

Commit

Permalink
feat: drop support for typed slots (#1152)
Browse files Browse the repository at this point in the history
* feat: combine multiple TS language services to one

* refactor: remove `__VLS_options` logics

* refactor: remove `__VLS_slots` logics
  • Loading branch information
johnsoncodehk committed Apr 9, 2022
1 parent 49c408f commit a887ef4
Show file tree
Hide file tree
Showing 50 changed files with 443 additions and 789 deletions.
15 changes: 3 additions & 12 deletions extensions/vscode-vue-language-features/package.json
Expand Up @@ -447,13 +447,8 @@
"category": "Volar"
},
{
"command": "volar.action.writeTemplateLsVirtualFiles",
"title": "Write Template LS Virtual Files",
"category": "Volar (Debug)"
},
{
"command": "volar.action.writeScriptLsVirtualFiles",
"title": "Write Script LS Virtual Files",
"command": "volar.action.writeVirtualFiles",
"title": "Write Virtual Files",
"category": "Volar (Debug)"
},
{
Expand Down Expand Up @@ -539,11 +534,7 @@
"when": "volar.activated"
},
{
"command": "volar.action.writeTemplateLsVirtualFiles",
"when": "volar.activated"
},
{
"command": "volar.action.writeScriptLsVirtualFiles",
"command": "volar.action.writeVirtualFiles",
"when": "volar.activated"
},
{
Expand Down
Expand Up @@ -4,10 +4,7 @@ import type { CommonLanguageClient } from 'vscode-languageclient';

export async function activate(context: vscode.ExtensionContext, languageClient: CommonLanguageClient) {
await languageClient.onReady();
context.subscriptions.push(vscode.commands.registerCommand('volar.action.writeTemplateLsVirtualFiles', () => {
languageClient.sendNotification(shared.WriteVirtualFilesNotification.type, { lsType: 'template' });
}));
context.subscriptions.push(vscode.commands.registerCommand('volar.action.writeScriptLsVirtualFiles', () => {
languageClient.sendNotification(shared.WriteVirtualFilesNotification.type, { lsType: 'script' });
context.subscriptions.push(vscode.commands.registerCommand('volar.action.writeVirtualFiles', () => {
languageClient.sendNotification(shared.WriteVirtualFilesNotification.type);
}));
}
3 changes: 1 addition & 2 deletions packages/shared/src/requests.ts
Expand Up @@ -91,8 +91,7 @@ export namespace VerifyAllScriptsNotification {
}

export namespace WriteVirtualFilesNotification {
export type ParamsType = { lsType: 'template' | 'script' };
export const type = new vscode.NotificationType<ParamsType>('volar.action.writeVirtualFiles');
export const type = new vscode.NotificationType0('volar.action.writeVirtualFiles');
}

export namespace DetectDocumentNameCasesRequest {
Expand Down
261 changes: 120 additions & 141 deletions packages/typescript-vue-plugin/src/apis.ts
Expand Up @@ -16,29 +16,28 @@ export function register(context: TypeScriptRuntime) {

// apis
function getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined): ReturnType<ts.LanguageService['getCompletionsAtPosition']> {
const finalResult = context.getTsLs('script').getCompletionsAtPosition(fileName, position, options);
const finalResult = context.getTsLs().getCompletionsAtPosition(fileName, position, options);
if (finalResult) {
finalResult.entries = finalResult.entries.filter(entry => entry.name.indexOf('__VLS_') === -1);
}
return finalResult;
}
function getReferencesAtPosition(fileName: string, position: number): ReturnType<ts.LanguageService['getReferencesAtPosition']> {
return findLocations(['script', 'template'], fileName, position, 'references') as ts.ReferenceEntry[];
return findLocations(fileName, position, 'references') as ts.ReferenceEntry[];
}
function getDefinitionAtPosition(fileName: string, position: number): ReturnType<ts.LanguageService['getDefinitionAtPosition']> {
return findLocations(['script'], fileName, position, 'definition') as ts.DefinitionInfo[];
return findLocations(fileName, position, 'definition') as ts.DefinitionInfo[];
}
function getTypeDefinitionAtPosition(fileName: string, position: number): ReturnType<ts.LanguageService['getDefinitionAtPosition']> {
return findLocations(['script'], fileName, position, 'typeDefinition') as ts.DefinitionInfo[];
return findLocations(fileName, position, 'typeDefinition') as ts.DefinitionInfo[];
}
function getImplementationAtPosition(fileName: string, position: number): ReturnType<ts.LanguageService['getImplementationAtPosition']> {
return findLocations(['script', 'template'], fileName, position, 'implementation') as ts.ImplementationLocation[];
return findLocations(fileName, position, 'implementation') as ts.ImplementationLocation[];
}
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ReturnType<ts.LanguageService['findRenameLocations']> {
return findLocations(['script', 'template'], fileName, position, 'rename', findInStrings, findInComments, providePrefixAndSuffixTextForRename) as ts.RenameLocation[];
return findLocations(fileName, position, 'rename', findInStrings, findInComments, providePrefixAndSuffixTextForRename) as ts.RenameLocation[];
}
function findLocations(
lsTypes: ('script' | 'template')[],
fileName: string,
position: number,
mode: 'definition' | 'typeDefinition' | 'references' | 'implementation' | 'rename',
Expand All @@ -47,102 +46,132 @@ export function register(context: TypeScriptRuntime) {
providePrefixAndSuffixTextForRename?: boolean
) {

return lsTypes.map(lsType => worker(lsType)).flat();

function worker(lsType: 'script' | 'template') {

const tsLs = context.getTsLs(lsType);
const loopChecker = new Set<string>();
let symbols: (ts.DefinitionInfo | ts.ReferenceEntry | ts.ImplementationLocation | ts.RenameLocation)[] = [];
const tsLs = context.getTsLs();
const loopChecker = new Set<string>();
let symbols: (ts.DefinitionInfo | ts.ReferenceEntry | ts.ImplementationLocation | ts.RenameLocation)[] = [];

if (tsLs)
withTeleports(fileName, position, tsLs);

return symbols.map(s => transformDocumentSpanLike(s)).filter(notEmpty);

function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = mode === 'definition' ? tsLs.getDefinitionAtPosition(fileName, position)
: mode === 'typeDefinition' ? tsLs.getTypeDefinitionAtPosition(fileName, position)
: mode === 'references' ? tsLs.getReferencesAtPosition(fileName, position)
: mode === 'implementation' ? tsLs.getImplementationAtPosition(fileName, position)
: mode === 'rename' ? tsLs.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
: undefined;
if (!_symbols) return;
symbols = symbols.concat(_symbols);
for (const ref of _symbols) {
loopChecker.add(ref.fileName + ':' + ref.textSpan.start);
const teleport = context.vueFiles.getTeleport(ref.fileName);

if (!teleport)
continue;

for (const [teleRange] of teleport.findTeleports(
ref.textSpan.start,
ref.textSpan.start + ref.textSpan.length,
sideData => {
if ((mode === 'definition' || mode === 'typeDefinition' || mode === 'implementation') && !sideData.capabilities.definitions)
return false;
if ((mode === 'references') && !sideData.capabilities.references)
return false;
if ((mode === 'rename') && !sideData.capabilities.rename)
return false;
return true;
},
)) {
if (loopChecker.has(ref.fileName + ':' + teleRange.start))
continue;
withTeleports(ref.fileName, teleRange.start, tsLs);
}
}
}
}
function getDefinitionAndBoundSpan(fileName: string, position: number): ReturnType<ts.LanguageService['getDefinitionAndBoundSpan']> {

if (tsLs)
withTeleports(fileName, position, tsLs);
const tsLs = context.getTsLs();
const loopChecker = new Set<string>();
let textSpan: ts.TextSpan | undefined;
let symbols: ts.DefinitionInfo[] = [];

return symbols.map(s => transformDocumentSpanLike(lsType, s)).filter(notEmpty);
if (tsLs)
withTeleports(fileName, position, tsLs);

function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = mode === 'definition' ? tsLs.getDefinitionAtPosition(fileName, position)
: mode === 'typeDefinition' ? tsLs.getTypeDefinitionAtPosition(fileName, position)
: mode === 'references' ? tsLs.getReferencesAtPosition(fileName, position)
: mode === 'implementation' ? tsLs.getImplementationAtPosition(fileName, position)
: mode === 'rename' ? tsLs.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
: undefined;
if (!_symbols) return;
symbols = symbols.concat(_symbols);
for (const ref of _symbols) {
loopChecker.add(ref.fileName + ':' + ref.textSpan.start);
const teleport = context.vueFiles.getTeleport(lsType, ref.fileName);
if (!textSpan) return;
return {
textSpan: textSpan,
definitions: symbols?.map(s => transformDocumentSpanLike(s)).filter(notEmpty),
};

if (!teleport)
function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = tsLs.getDefinitionAndBoundSpan(fileName, position);
if (!_symbols) return;
if (!textSpan) {
textSpan = _symbols.textSpan;
}
if (!_symbols.definitions) return;
symbols = symbols.concat(_symbols.definitions);
for (const ref of _symbols.definitions) {

loopChecker.add(ref.fileName + ':' + ref.textSpan.start);

const teleport = context.vueFiles.getTeleport(ref.fileName);
if (!teleport)
continue;

for (const [teleRange] of teleport.findTeleports(
ref.textSpan.start,
ref.textSpan.start + ref.textSpan.length,
sideData => !!sideData.capabilities.definitions,
)) {
if (loopChecker.has(ref.fileName + ':' + teleRange.start))
continue;

for (const [teleRange] of teleport.findTeleports(
ref.textSpan.start,
ref.textSpan.start + ref.textSpan.length,
sideData => {
if ((mode === 'definition' || mode === 'typeDefinition' || mode === 'implementation') && !sideData.capabilities.definitions)
return false;
if ((mode === 'references') && !sideData.capabilities.references)
return false;
if ((mode === 'rename') && !sideData.capabilities.rename)
return false;
return true;
},
)) {
if (loopChecker.has(ref.fileName + ':' + teleRange.start))
continue;
withTeleports(ref.fileName, teleRange.start, tsLs);
}
withTeleports(ref.fileName, teleRange.start, tsLs);
}
}
}
}
function getDefinitionAndBoundSpan(fileName: string, position: number): ReturnType<ts.LanguageService['getDefinitionAndBoundSpan']> {

return worker('script');

function worker(lsType: 'script' | 'template') {
function findReferences(fileName: string, position: number): ReturnType<ts.LanguageService['findReferences']> {

const tsLs = context.getTsLs(lsType);
const loopChecker = new Set<string>();
let textSpan: ts.TextSpan | undefined;
let symbols: ts.DefinitionInfo[] = [];
const tsLs = context.getTsLs();
const loopChecker = new Set<string>();
let symbols: ts.ReferencedSymbol[] = [];

if (tsLs)
withTeleports(fileName, position, tsLs);
if (tsLs)
withTeleports(fileName, position, tsLs);

if (!textSpan) return;
return {
textSpan: textSpan,
definitions: symbols?.map(s => transformDocumentSpanLike(lsType, s)).filter(notEmpty),
};
return symbols.map(s => transformReferencedSymbol(s)).filter(notEmpty);

function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = tsLs.getDefinitionAndBoundSpan(fileName, position);
if (!_symbols) return;
if (!textSpan) {
textSpan = _symbols.textSpan;
}
if (!_symbols.definitions) return;
symbols = symbols.concat(_symbols.definitions);
for (const ref of _symbols.definitions) {
function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = tsLs.findReferences(fileName, position);
if (!_symbols) return;
symbols = symbols.concat(_symbols);
for (const symbol of _symbols) {
for (const ref of symbol.references) {

loopChecker.add(ref.fileName + ':' + ref.textSpan.start);

const teleport = context.vueFiles.getTeleport(lsType, ref.fileName);
const teleport = context.vueFiles.getTeleport(ref.fileName);
if (!teleport)
continue;

for (const [teleRange] of teleport.findTeleports(
ref.textSpan.start,
ref.textSpan.start + ref.textSpan.length,
sideData => !!sideData.capabilities.definitions,
sideData => !!sideData.capabilities.references,
)) {
if (loopChecker.has(ref.fileName + ':' + teleRange.start))
continue;
Expand All @@ -152,61 +181,11 @@ export function register(context: TypeScriptRuntime) {
}
}
}
function findReferences(fileName: string, position: number): ReturnType<ts.LanguageService['findReferences']> {

const scriptResult = worker('script');
const templateResult = worker('template');
return [
...scriptResult,
...templateResult,
];

function worker(lsType: 'script' | 'template') {

const tsLs = context.getTsLs(lsType);
const loopChecker = new Set<string>();
let symbols: ts.ReferencedSymbol[] = [];

if (tsLs)
withTeleports(fileName, position, tsLs);

return symbols.map(s => transformReferencedSymbol(lsType, s)).filter(notEmpty);

function withTeleports(fileName: string, position: number, tsLs: ts.LanguageService) {
if (loopChecker.has(fileName + ':' + position))
return;
loopChecker.add(fileName + ':' + position);
const _symbols = tsLs.findReferences(fileName, position);
if (!_symbols) return;
symbols = symbols.concat(_symbols);
for (const symbol of _symbols) {
for (const ref of symbol.references) {

loopChecker.add(ref.fileName + ':' + ref.textSpan.start);

const teleport = context.vueFiles.getTeleport(lsType, ref.fileName);
if (!teleport)
continue;

for (const [teleRange] of teleport.findTeleports(
ref.textSpan.start,
ref.textSpan.start + ref.textSpan.length,
sideData => !!sideData.capabilities.references,
)) {
if (loopChecker.has(ref.fileName + ':' + teleRange.start))
continue;
withTeleports(ref.fileName, teleRange.start, tsLs);
}
}
}
}
}
}

// transforms
function transformReferencedSymbol(lsType: 'script' | 'template', symbol: ts.ReferencedSymbol): ts.ReferencedSymbol | undefined {
const definition = transformDocumentSpanLike(lsType, symbol.definition);
const references = symbol.references.map(r => transformDocumentSpanLike(lsType, r)).filter(notEmpty);
function transformReferencedSymbol(symbol: ts.ReferencedSymbol): ts.ReferencedSymbol | undefined {
const definition = transformDocumentSpanLike(symbol.definition);
const references = symbol.references.map(r => transformDocumentSpanLike(r)).filter(notEmpty);
if (definition) {
return {
definition,
Expand All @@ -224,12 +203,12 @@ export function register(context: TypeScriptRuntime) {
};
}
}
function transformDocumentSpanLike<T extends ts.DocumentSpan>(lsType: 'script' | 'template', documentSpan: T): T | undefined {
const textSpan = transformSpan(lsType, documentSpan.fileName, documentSpan.textSpan);
function transformDocumentSpanLike<T extends ts.DocumentSpan>(documentSpan: T): T | undefined {
const textSpan = transformSpan(documentSpan.fileName, documentSpan.textSpan);
if (!textSpan) return;
const contextSpan = transformSpan(lsType, documentSpan.fileName, documentSpan.contextSpan);
const originalTextSpan = transformSpan(lsType, documentSpan.originalFileName, documentSpan.originalTextSpan);
const originalContextSpan = transformSpan(lsType, documentSpan.originalFileName, documentSpan.originalContextSpan);
const contextSpan = transformSpan(documentSpan.fileName, documentSpan.contextSpan);
const originalTextSpan = transformSpan(documentSpan.originalFileName, documentSpan.originalTextSpan);
const originalContextSpan = transformSpan(documentSpan.originalFileName, documentSpan.originalContextSpan);
return {
...documentSpan,
fileName: textSpan.fileName,
Expand All @@ -240,10 +219,10 @@ export function register(context: TypeScriptRuntime) {
originalContextSpan: originalContextSpan?.textSpan,
};
}
function transformSpan(lsType: 'script' | 'template', fileName: string | undefined, textSpan: ts.TextSpan | undefined) {
function transformSpan(fileName: string | undefined, textSpan: ts.TextSpan | undefined) {
if (!fileName) return;
if (!textSpan) return;
for (const vueLoc of context.vueFiles.fromEmbeddedLocation(lsType, fileName, textSpan.start, textSpan.start + textSpan.length)) {
for (const vueLoc of context.vueFiles.fromEmbeddedLocation(fileName, textSpan.start, textSpan.start + textSpan.length)) {
return {
fileName: vueLoc.fileName,
textSpan: {
Expand Down

0 comments on commit a887ef4

Please sign in to comment.