Skip to content

Commit

Permalink
refactor(language-service): re-implement auto-import patching in Type…
Browse files Browse the repository at this point in the history
…Script plugin (#3917)
  • Loading branch information
johnsoncodehk committed Mar 1, 2024
1 parent e8bd161 commit d09ab69
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 191 deletions.
5 changes: 0 additions & 5 deletions extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,6 @@
"default": true,
"description": "Show name casing in status bar."
},
"vue.complete.normalizeComponentImportName": {
"type": "boolean",
"default": true,
"description": "Normalize import name for auto import. (\"myCompVue\" -> \"MyComp\")"
},
"vue.autoInsert.parentheses": {
"type": "boolean",
"default": true,
Expand Down
4 changes: 2 additions & 2 deletions packages/language-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import type { VueCompilerOptions } from './lib/types';
import { create as createEmmetServicePlugin } from 'volar-service-emmet';
import { create as createJsonServicePlugin } from 'volar-service-json';
import { create as createPugFormatServicePlugin } from 'volar-service-pug-beautify';
import { create as createTypeScriptServicePlugin } from 'volar-service-typescript';
import { create as createTypeScriptTwoslashQueriesServicePlugin } from 'volar-service-typescript-twoslash-queries';
import { create as createCssServicePlugin } from './lib/plugins/css';
import { create as createTypeScriptServicePlugin } from './lib/plugins/typescript';
import { create as createVueAutoDotValueServicePlugin } from './lib/plugins/vue-autoinsert-dotvalue';
import { create as createVueAutoWrapParenthesesServicePlugin } from './lib/plugins/vue-autoinsert-parentheses';
import { create as createVueAutoAddSpaceServicePlugin } from './lib/plugins/vue-autoinsert-space';
Expand All @@ -30,7 +30,7 @@ export function createVueServicePlugins(
getVueOptions: (env: ServiceEnvironment) => VueCompilerOptions,
): ServicePlugin[] {
return [
createTypeScriptServicePlugin(ts, getVueOptions),
createTypeScriptServicePlugin(ts),
createTypeScriptTwoslashQueriesServicePlugin(),
createCssServicePlugin(),
createPugFormatServicePlugin(),
Expand Down
179 changes: 0 additions & 179 deletions packages/language-service/lib/plugins/typescript.ts

This file was deleted.

16 changes: 13 additions & 3 deletions packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ import * as namedPipeClient from '@vue/typescript-plugin/lib/client';
import type * as ts from 'typescript';
import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import { getAst } from './typescript';

const asts = new WeakMap<ts.IScriptSnapshot, ts.SourceFile>();

function getAst(ts: typeof import('typescript'), fileName: string, snapshot: ts.IScriptSnapshot, scriptKind?: ts.ScriptKind) {
let ast = asts.get(snapshot);
if (!ast) {
ast = ts.createSourceFile(fileName, snapshot.getText(0, snapshot.getLength()), ts.ScriptTarget.Latest, undefined, scriptKind);
asts.set(snapshot, ast);
}
return ast;
}

export function create(ts: typeof import('typescript')): ServicePlugin {
return {
Expand Down Expand Up @@ -44,7 +54,7 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
if (script?.code !== code) {
return;
}
ast = getAst(fileName, script.code.snapshot, script.scriptKind);
ast = getAst(ts, fileName, script.code.snapshot, script.scriptKind);
let mapped = false;
for (const [_1, [_2, map]] of context.language.files.getMaps(code)) {
const sourceOffset = map.getSourceOffset(document.offsetAt(position));
Expand All @@ -59,7 +69,7 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
}
}
else {
ast = getAst(fileName, file.snapshot);
ast = getAst(ts, fileName, file.snapshot);
}

if (isBlacklistNode(ts, ast, document.offsetAt(position), false))
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/tests/complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const normalizeNewline = (text: string) => text.replace(/\r\n/g, '\n');

for (const dirName of testDirs) {

describe.skipIf(dirName === 'core#8811')(`complete: ${dirName}`, async () => {
describe.skipIf(dirName === 'core#8811' || dirName === '#2511' || dirName === 'component-auto-import')(`complete: ${dirName}`, async () => {

const dir = path.join(baseDir, dirName);
const inputFiles = readFiles(path.join(dir, 'input'));
Expand Down
58 changes: 57 additions & 1 deletion packages/typescript-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { projects } from './lib/utils';
import * as vue from '@vue/language-core';
import { startNamedPipeServer } from './lib/server';
import { _getComponentNames } from './lib/requests/componentInfos';
import { capitalize } from '@vue/shared';

const windowsPathReg = /\\/g;
const externalFiles = new WeakMap<ts.server.Project, string[]>();
Expand Down Expand Up @@ -63,15 +64,70 @@ function createLanguageServicePlugin(): ts.server.PluginModuleFactory {
startNamedPipeServer(info.project.projectKind);

const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition;
const getCompletionEntryDetails = info.languageService.getCompletionEntryDetails;
const getCodeFixesAtPosition = info.languageService.getCodeFixesAtPosition;
const getEncodedSemanticClassifications = info.languageService.getEncodedSemanticClassifications;

info.languageService.getCompletionsAtPosition = (fileName, position, options) => {
const result = getCompletionsAtPosition(fileName, position, options);
if (result) {
result.entries = result.entries.filter(entry => entry.name.indexOf('__VLS_') === -1);
// filter __VLS_
result.entries = result.entries.filter(
entry => entry.name.indexOf('__VLS_') === -1
&& (!entry.labelDetails?.description || entry.labelDetails.description.indexOf('__VLS_') === -1)
);
// modify label
for (const item of result.entries) {
if (item.source) {
const originalName = item.name;
for (const ext of vueOptions.extensions) {
const suffix = capitalize(ext.substring('.'.length)); // .vue -> Vue
if (item.source.endsWith(ext) && item.name.endsWith(suffix)) {
item.name = item.name.slice(0, -suffix.length);
if (item.insertText) {
// #2286
item.insertText = item.insertText.replace(`${suffix}$1`, '$1');
}
if (item.data) {
// @ts-expect-error
item.data.__isComponentAutoImport = {
ext,
suffix,
originalName,
newName: item.insertText,
};
}
break;
}
}
}
}
}
return result;
};
info.languageService.getCompletionEntryDetails = (...args) => {
const details = getCompletionEntryDetails(...args);
// modify import statement
// @ts-expect-error
if (args[6]?.__isComponentAutoImport) {
// @ts-expect-error
const { ext, suffix, originalName, newName } = args[6]?.__isComponentAutoImport;
for (const codeAction of details?.codeActions ?? []) {
for (const change of codeAction.changes) {
for (const textChange of change.textChanges) {
textChange.newText = textChange.newText.replace('import ' + originalName + ' from ', 'import ' + newName + ' from ');
}
}
}
}
return details;
};
info.languageService.getCodeFixesAtPosition = (...args) => {
let result = getCodeFixesAtPosition(...args);
// filter __VLS_
result = result.filter(entry => entry.description.indexOf('__VLS_') === -1);
return result;
};
info.languageService.getEncodedSemanticClassifications = (fileName, span, format) => {
const result = getEncodedSemanticClassifications(fileName, span, format);
const file = files.get(fileName);
Expand Down

0 comments on commit d09ab69

Please sign in to comment.