Skip to content

Commit

Permalink
perf(language-service): html completion should not be affected by ts …
Browse files Browse the repository at this point in the history
…performance

close #4298
  • Loading branch information
johnsoncodehk committed Apr 25, 2024
1 parent 307ef4c commit 65d38a1
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 120 deletions.
2 changes: 1 addition & 1 deletion packages/language-plugin-pug/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
},
"dependencies": {
"@volar/source-map": "2.2.0-alpha.10",
"volar-service-pug": "0.0.38"
"volar-service-pug": "0.0.39"
}
}
2 changes: 1 addition & 1 deletion packages/language-server/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ connection.onRequest(ParseSFCRequest.type, params => {
connection.onRequest(DetectNameCasingRequest.type, async params => {
const languageService = await getService(params.textDocument.uri);
if (languageService) {
return await detect(languageService.context, params.textDocument.uri, getTsPluginClient(languageService.context));
return await detect(languageService.context, params.textDocument.uri);
}
});

Expand Down
53 changes: 17 additions & 36 deletions packages/language-service/lib/ideFeatures/nameCasing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,9 @@ export async function convertAttrName(
return edits;
}

export async function getNameCasing(
context: ServiceContext,
uri: string,
tsPluginClient?: typeof import('@vue/typescript-plugin/lib/client'),
) {
export async function getNameCasing(context: ServiceContext, uri: string) {

const detected = await detect(context, uri, tsPluginClient);
const detected = await detect(context, uri);
const [attr, tag] = await Promise.all([
context.env.getConfiguration?.<'autoKebab' | 'autoCamel' | 'kebab' | 'camel'>('vue.complete.casing.props', uri),
context.env.getConfiguration?.<'autoKebab' | 'autoPascal' | 'kebab' | 'pascal'>('vue.complete.casing.tags', uri),
Expand All @@ -131,7 +127,6 @@ export async function getNameCasing(
export async function detect(
context: ServiceContext,
uri: string,
tsPluginClient?: typeof import('@vue/typescript-plugin/lib/client'),
): Promise<{
tag: TagNameCasing[],
attr: AttrNameCasing[],
Expand Down Expand Up @@ -176,39 +171,25 @@ export async function detect(
}
async function getTagNameCase(file: VueVirtualCode): Promise<TagNameCasing[]> {

const components = await tsPluginClient?.getComponentNames(file.fileName) ?? [];
const tagNames = getTemplateTagsAndAttrs(file);
const result: TagNameCasing[] = [];

let anyComponentUsed = false;

for (const component of components) {
if (tagNames.has(component) || tagNames.has(hyphenateTag(component))) {
anyComponentUsed = true;
break;
}
}
if (!anyComponentUsed) {
return []; // not sure component style, because do not have any component using in <template> for check
}
const result = new Set<TagNameCasing>();

for (const [tagName] of tagNames) {
// TagName
if (tagName !== hyphenateTag(tagName)) {
result.push(TagNameCasing.Pascal);
break;
}
}
for (const component of components) {
// Tagname -> tagname
// TagName -> tag-name
if (component !== hyphenateTag(component) && tagNames.has(hyphenateTag(component))) {
result.push(TagNameCasing.Kebab);
break;
if (file.sfc.template?.ast) {
for (const element of vue.forEachElementNode(file.sfc.template.ast)) {
if (element.tagType === 1 satisfies CompilerDOM.ElementTypes) {
if (element.tag !== hyphenateTag(element.tag)) {
// TagName
result.add(TagNameCasing.Pascal);
}
else {
// Tagname -> tagname
// TagName -> tag-name
result.add(TagNameCasing.Kebab);
}
}
}
}

return result;
return [...result];
}
}

Expand Down
28 changes: 13 additions & 15 deletions packages/language-service/lib/plugins/vue-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function create(

let customData: html.IHTMLDataProvider[] = [];
let extraCustomData: html.IHTMLDataProvider[] = [];
let lastCompletionComponentNames = new Set<string>();

const onDidChangeCustomDataListeners = new Set<() => void>();
const onDidChangeCustomData = (listener: () => void): Disposable => {
Expand Down Expand Up @@ -114,6 +115,10 @@ export function create(
const decoded = context.decodeEmbeddedDocumentUri(document.uri);
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
if (sourceScript?.generated?.root instanceof VueVirtualCode) {

// #4298: Precompute HTMLDocument before provideHtmlData to avoid parseHTMLDocument requesting component names from tsserver
baseServiceInstance.provideCompletionItems?.(document, position, completionContext, token);

sync = (await provideHtmlData(sourceScript.id, sourceScript.generated.root)).sync;
currentVersion = await sync();
}
Expand All @@ -130,7 +135,6 @@ export function create(
await afterHtmlCompletion(
htmlComplete,
context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot),
sourceScript.generated.root,
);
}

Expand Down Expand Up @@ -164,7 +168,7 @@ export function create(
if (code instanceof VueVirtualCode && scanner) {

// visualize missing required props
const casing = await getNameCasing(context, map.sourceDocument.uri, tsPluginClient);
const casing = await getNameCasing(context, map.sourceDocument.uri);
const components = await tsPluginClient?.getComponentNames(code.fileName) ?? [];
const componentProps: Record<string, string[]> = {};
let token: html.TokenType;
Expand All @@ -176,11 +180,9 @@ export function create(
while ((token = scanner.scan()) !== html.TokenType.EOS) {
if (token === html.TokenType.StartTag) {
const tagName = scanner.getTokenText();
const component =
tagName.indexOf('.') >= 0
? components.find(component => component === tagName.split('.')[0])
: components.find(component => component === tagName || hyphenateTag(component) === tagName);
const checkTag = tagName.indexOf('.') >= 0 ? tagName : component;
const checkTag = tagName.indexOf('.') >= 0
? tagName
: components.find(component => component === tagName || hyphenateTag(component) === tagName);
if (checkTag) {
componentProps[checkTag] ??= await tsPluginClient?.getComponentProps(code.fileName, checkTag, true) ?? [];
current = {
Expand Down Expand Up @@ -382,7 +384,7 @@ export function create(

await (initializing ??= initialize());

const casing = await getNameCasing(context, sourceDocumentUri, tsPluginClient);
const casing = await getNameCasing(context, sourceDocumentUri);

if (builtInData.tags) {
for (const tag of builtInData.tags) {
Expand Down Expand Up @@ -431,6 +433,7 @@ export function create(
&& name !== 'Suspense'
&& name !== 'Teleport'
);
lastCompletionComponentNames = new Set(components);
version++;
})());
return [];
Expand Down Expand Up @@ -621,13 +624,9 @@ export function create(
};
}

async function afterHtmlCompletion(completionList: vscode.CompletionList, sourceDocument: TextDocument, code: VueVirtualCode) {
async function afterHtmlCompletion(completionList: vscode.CompletionList, sourceDocument: TextDocument) {

const replacement = getReplacement(completionList, sourceDocument);
const componentNames = new Set(
(await tsPluginClient?.getComponentNames(code.fileName) ?? [])
.map(hyphenateTag)
);

if (replacement) {

Expand Down Expand Up @@ -707,7 +706,7 @@ export function create(
item.documentation = undefined;
}

if (item.kind === 10 satisfies typeof vscode.CompletionItemKind.Property && componentNames.has(hyphenateTag(item.label))) {
if (item.kind === 10 satisfies typeof vscode.CompletionItemKind.Property && lastCompletionComponentNames.has(hyphenateTag(item.label))) {
item.kind = 6 satisfies typeof vscode.CompletionItemKind.Variable;
item.sortText = '\u0000' + (item.sortText ?? item.label);
}
Expand Down Expand Up @@ -749,7 +748,6 @@ export function create(
updateExtraCustomData([]);
}


async function initialize() {
customData = await getHtmlCustomData();
}
Expand Down
18 changes: 9 additions & 9 deletions packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
"@vue/typescript-plugin": "2.0.14",
"computeds": "^0.0.1",
"path-browserify": "^1.0.1",
"volar-service-css": "0.0.38",
"volar-service-emmet": "0.0.38",
"volar-service-html": "0.0.38",
"volar-service-json": "0.0.38",
"volar-service-pug": "0.0.38",
"volar-service-pug-beautify": "0.0.38",
"volar-service-typescript": "0.0.38",
"volar-service-typescript-twoslash-queries": "0.0.38",
"vscode-html-languageservice": "^5.1.0",
"volar-service-css": "0.0.39",
"volar-service-emmet": "0.0.39",
"volar-service-html": "0.0.39",
"volar-service-json": "0.0.39",
"volar-service-pug": "0.0.39",
"volar-service-pug-beautify": "0.0.39",
"volar-service-typescript": "0.0.39",
"volar-service-typescript-twoslash-queries": "0.0.39",
"vscode-html-languageservice": "npm:@johnsoncodehk/[email protected]",
"vscode-languageserver-textdocument": "^1.0.11",
"vscode-uri": "^3.0.8"
},
Expand Down
Loading

0 comments on commit 65d38a1

Please sign in to comment.