Skip to content

Commit a918fcd

Browse files
fix(language-service): wait for tsserver to be ready when requesting auto insert .value (#3914)
1 parent 79e224d commit a918fcd

File tree

3 files changed

+79
-22
lines changed

3 files changed

+79
-22
lines changed

packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
1010
return {
1111
name: 'vue-autoinsert-dotvalue',
1212
create(context): ServicePluginInstance {
13+
let currentReq = 0;
1314
return {
1415
async provideAutoInsertionEdit(document, position, lastChange) {
1516

@@ -19,34 +20,52 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
1920
if (!isCharacterTyping(document, lastChange))
2021
return;
2122

23+
const req = ++currentReq;
24+
// Wait for tsserver to sync
25+
await sleep(250);
26+
if (req !== currentReq)
27+
return;
28+
2229
const enabled = await context.env.getConfiguration?.<boolean>('vue.autoInsert.dotValue') ?? true;
2330
if (!enabled)
2431
return;
2532

26-
const [_, file] = context.documents.getVirtualCodeByUri(document.uri);
33+
const [code, file] = context.documents.getVirtualCodeByUri(document.uri);
34+
if (!file)
35+
return;
2736

28-
let fileName: string | undefined;
2937
let ast: ts.SourceFile | undefined;
38+
let sourceCodeOffset = document.offsetAt(position);
39+
40+
const fileName = context.env.typescript!.uriToFileName(file.id);
3041

3142
if (file?.generated) {
3243
const script = file.generated.languagePlugin.typescript?.getScript(file.generated.code);
33-
if (script) {
34-
fileName = context.env.typescript!.uriToFileName(file.id);
35-
ast = getAst(fileName, script.code.snapshot, script.scriptKind);
44+
if (script?.code !== code) {
45+
return;
46+
}
47+
ast = getAst(fileName, script.code.snapshot, script.scriptKind);
48+
let mapped = false;
49+
for (const [_1, [_2, map]] of context.language.files.getMaps(code)) {
50+
const sourceOffset = map.getSourceOffset(document.offsetAt(position));
51+
if (sourceOffset !== undefined) {
52+
sourceCodeOffset = sourceOffset[0];
53+
mapped = true;
54+
break;
55+
}
56+
}
57+
if (!mapped) {
58+
return;
3659
}
3760
}
38-
else if (file) {
39-
fileName = context.env.typescript!.uriToFileName(file.id);
61+
else {
4062
ast = getAst(fileName, file.snapshot);
4163
}
4264

43-
if (!ast || !fileName)
44-
return;
45-
4665
if (isBlacklistNode(ts, ast, document.offsetAt(position), false))
4766
return;
4867

49-
const props = await namedPipeClient.getPropertiesAtLocation(fileName, document.offsetAt(position)) ?? [];
68+
const props = await namedPipeClient.getPropertiesAtLocation(fileName, sourceCodeOffset) ?? [];
5069
if (props.some(prop => prop === 'value')) {
5170
return '${1:.value}';
5271
}
@@ -56,6 +75,10 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
5675
};
5776
}
5877

78+
function sleep(ms: number) {
79+
return new Promise<void>(resolve => setTimeout(resolve, ms));
80+
}
81+
5982
function isTsDocument(document: TextDocument) {
6083
return document.languageId === 'javascript' ||
6184
document.languageId === 'typescript' ||

packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isCompletionEnabled } from '@vue/language-core';
12
import { getProject } from '../utils';
23
import type * as ts from 'typescript';
34

@@ -10,6 +11,36 @@ export function getPropertiesAtLocation(fileName: string, position: number, isTs
1011

1112
const { info, files, ts } = match;
1213
const languageService = info.languageService;
14+
15+
// mapping
16+
const file = files.get(fileName);
17+
if (file?.generated) {
18+
const virtualScript = file.generated.languagePlugin.typescript?.getScript(file.generated.code);
19+
if (!virtualScript) {
20+
return;
21+
}
22+
let mapped = false;
23+
for (const [_1, [_2, map]] of files.getMaps(virtualScript.code)) {
24+
for (const [position2, mapping] of map.getGeneratedOffsets(position)) {
25+
if (isCompletionEnabled(mapping.data)) {
26+
position = position2;
27+
mapped = true;
28+
break;
29+
}
30+
}
31+
if (mapped) {
32+
break;
33+
}
34+
}
35+
if (!mapped) {
36+
return;
37+
}
38+
if (isTsPlugin) {
39+
position += file.snapshot.getLength();
40+
}
41+
}
42+
43+
1344
const program: ts.Program = (languageService as any).getCurrentProgram();
1445
if (!program) {
1546
return;
@@ -20,8 +51,7 @@ export function getPropertiesAtLocation(fileName: string, position: number, isTs
2051
return;
2152
}
2253

23-
const volarFile = files.get(fileName);
24-
const node = findPositionIdentifier(sourceFile, sourceFile, position + (isTsPlugin ? (volarFile?.snapshot.getLength() ?? 0) : 0));
54+
const node = findPositionIdentifier(sourceFile, sourceFile, position);
2555
if (!node) {
2656
return;
2757
}

packages/typescript-plugin/lib/server.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import * as fs from 'fs';
22
import * as net from 'net';
3+
import { collectExtractProps } from './requests/collectExtractProps';
4+
import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from './requests/componentInfos';
5+
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
6+
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
37
import { pipeFile } from './utils';
48

59
export interface Request {
@@ -21,39 +25,39 @@ export function startNamedPipeServer() {
2125
if (started) return;
2226
started = true;
2327
const server = net.createServer(connection => {
24-
connection.on('data', async data => {
28+
connection.on('data', data => {
2529
const request: Request = JSON.parse(data.toString());
2630
if (request.type === 'collectExtractProps') {
27-
const result = (await import('./requests/collectExtractProps.js')).collectExtractProps.apply(null, request.args);
31+
const result = collectExtractProps.apply(null, request.args);
2832
connection.write(JSON.stringify(result ?? null));
2933
}
3034
else if (request.type === 'getPropertiesAtLocation') {
31-
const result = (await import('./requests/getPropertiesAtLocation.js')).getPropertiesAtLocation.apply(null, request.args);
35+
const result = getPropertiesAtLocation.apply(null, request.args);
3236
connection.write(JSON.stringify(result ?? null));
3337
}
3438
else if (request.type === 'getQuickInfoAtPosition') {
35-
const result = (await import('./requests/getQuickInfoAtPosition.js')).getQuickInfoAtPosition.apply(null, request.args);
39+
const result = getQuickInfoAtPosition.apply(null, request.args);
3640
connection.write(JSON.stringify(result ?? null));
3741
}
3842
// Component Infos
3943
else if (request.type === 'getComponentProps') {
40-
const result = (await import('./requests/componentInfos.js')).getComponentProps.apply(null, request.args);
44+
const result = getComponentProps.apply(null, request.args);
4145
connection.write(JSON.stringify(result ?? null));
4246
}
4347
else if (request.type === 'getComponentEvents') {
44-
const result = (await import('./requests/componentInfos.js')).getComponentEvents.apply(null, request.args);
48+
const result = getComponentEvents.apply(null, request.args);
4549
connection.write(JSON.stringify(result ?? null));
4650
}
4751
else if (request.type === 'getTemplateContextProps') {
48-
const result = (await import('./requests/componentInfos.js')).getTemplateContextProps.apply(null, request.args);
52+
const result = getTemplateContextProps.apply(null, request.args);
4953
connection.write(JSON.stringify(result ?? null));
5054
}
5155
else if (request.type === 'getComponentNames') {
52-
const result = (await import('./requests/componentInfos.js')).getComponentNames.apply(null, request.args);
56+
const result = getComponentNames.apply(null, request.args);
5357
connection.write(JSON.stringify(result ?? null));
5458
}
5559
else if (request.type === 'getElementAttrs') {
56-
const result = (await import('./requests/componentInfos.js')).getElementAttrs.apply(null, request.args);
60+
const result = getElementAttrs.apply(null, request.args);
5761
connection.write(JSON.stringify(result ?? null));
5862
}
5963
else {

0 commit comments

Comments
 (0)