Skip to content

Commit

Permalink
fix(language-service): wait for tsserver to be ready when requesting …
Browse files Browse the repository at this point in the history
…auto insert `.value` (vuejs#3914)
  • Loading branch information
johnsoncodehk authored and so1ve committed Mar 4, 2024
1 parent 77224a4 commit 72345cd
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 22 deletions.
45 changes: 34 additions & 11 deletions packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function create(ts: typeof import('typescript')): ServicePlugin {
return {
name: 'vue-autoinsert-dotvalue',
create(context): ServicePluginInstance {
let currentReq = 0;
return {
async provideAutoInsertionEdit(document, position, lastChange) {

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

const req = ++currentReq;
// Wait for tsserver to sync
await sleep(250);
if (req !== currentReq)
return;

const enabled = await context.env.getConfiguration?.<boolean>('vue.autoInsert.dotValue') ?? true;
if (!enabled)
return;

const [_, file] = context.documents.getVirtualCodeByUri(document.uri);
const [code, file] = context.documents.getVirtualCodeByUri(document.uri);
if (!file)
return;

let fileName: string | undefined;
let ast: ts.SourceFile | undefined;
let sourceCodeOffset = document.offsetAt(position);

const fileName = context.env.typescript!.uriToFileName(file.id);

if (file?.generated) {
const script = file.generated.languagePlugin.typescript?.getScript(file.generated.code);
if (script) {
fileName = context.env.typescript!.uriToFileName(file.id);
ast = getAst(fileName, script.code.snapshot, script.scriptKind);
if (script?.code !== code) {
return;
}
ast = getAst(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));
if (sourceOffset !== undefined) {
sourceCodeOffset = sourceOffset[0];
mapped = true;
break;
}
}
if (!mapped) {
return;
}
}
else if (file) {
fileName = context.env.typescript!.uriToFileName(file.id);
else {
ast = getAst(fileName, file.snapshot);
}

if (!ast || !fileName)
return;

if (isBlacklistNode(ts, ast, document.offsetAt(position), false))
return;

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

function sleep(ms: number) {
return new Promise<void>(resolve => setTimeout(resolve, ms));
}

function isTsDocument(document: TextDocument) {
return document.languageId === 'javascript' ||
document.languageId === 'typescript' ||
Expand Down
34 changes: 32 additions & 2 deletions packages/typescript-plugin/lib/requests/getPropertiesAtLocation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isCompletionEnabled } from '@vue/language-core';
import { getProject } from '../utils';
import type * as ts from 'typescript';

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

const { info, files, ts } = match;
const languageService = info.languageService;

// mapping
const file = files.get(fileName);
if (file?.generated) {
const virtualScript = file.generated.languagePlugin.typescript?.getScript(file.generated.code);
if (!virtualScript) {
return;
}
let mapped = false;
for (const [_1, [_2, map]] of files.getMaps(virtualScript.code)) {
for (const [position2, mapping] of map.getGeneratedOffsets(position)) {
if (isCompletionEnabled(mapping.data)) {
position = position2;
mapped = true;
break;
}
}
if (mapped) {
break;
}
}
if (!mapped) {
return;
}
if (isTsPlugin) {
position += file.snapshot.getLength();
}
}


const program: ts.Program = (languageService as any).getCurrentProgram();
if (!program) {
return;
Expand All @@ -20,8 +51,7 @@ export function getPropertiesAtLocation(fileName: string, position: number, isTs
return;
}

const volarFile = files.get(fileName);
const node = findPositionIdentifier(sourceFile, sourceFile, position + (isTsPlugin ? (volarFile?.snapshot.getLength() ?? 0) : 0));
const node = findPositionIdentifier(sourceFile, sourceFile, position);
if (!node) {
return;
}
Expand Down
22 changes: 13 additions & 9 deletions packages/typescript-plugin/lib/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as fs from 'fs';
import * as net from 'net';
import { collectExtractProps } from './requests/collectExtractProps';
import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from './requests/componentInfos';
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
import { pipeFile } from './utils';

export interface Request {
Expand All @@ -21,39 +25,39 @@ export function startNamedPipeServer() {
if (started) return;
started = true;
const server = net.createServer(connection => {
connection.on('data', async data => {
connection.on('data', data => {
const request: Request = JSON.parse(data.toString());
if (request.type === 'collectExtractProps') {
const result = (await import('./requests/collectExtractProps.js')).collectExtractProps.apply(null, request.args);
const result = collectExtractProps.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getPropertiesAtLocation') {
const result = (await import('./requests/getPropertiesAtLocation.js')).getPropertiesAtLocation.apply(null, request.args);
const result = getPropertiesAtLocation.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getQuickInfoAtPosition') {
const result = (await import('./requests/getQuickInfoAtPosition.js')).getQuickInfoAtPosition.apply(null, request.args);
const result = getQuickInfoAtPosition.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
// Component Infos
else if (request.type === 'getComponentProps') {
const result = (await import('./requests/componentInfos.js')).getComponentProps.apply(null, request.args);
const result = getComponentProps.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getComponentEvents') {
const result = (await import('./requests/componentInfos.js')).getComponentEvents.apply(null, request.args);
const result = getComponentEvents.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getTemplateContextProps') {
const result = (await import('./requests/componentInfos.js')).getTemplateContextProps.apply(null, request.args);
const result = getTemplateContextProps.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getComponentNames') {
const result = (await import('./requests/componentInfos.js')).getComponentNames.apply(null, request.args);
const result = getComponentNames.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'getElementAttrs') {
const result = (await import('./requests/componentInfos.js')).getElementAttrs.apply(null, request.args);
const result = getElementAttrs.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else {
Expand Down

0 comments on commit 72345cd

Please sign in to comment.