Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(typescript-plugin): improve named pipes reliability #4603

Merged
merged 3 commits into from
Jul 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 24 additions & 48 deletions packages/language-server/lib/hybridModeProject.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import type { Language, LanguagePlugin, LanguageServer, LanguageServerProject, P
import { createLanguageServiceEnvironment } from '@volar/language-server/lib/project/simpleProject';
import { createLanguage } from '@vue/language-core';
import { createLanguageService, createUriMap, LanguageService } from '@vue/language-service';
import { searchNamedPipeServerForFile, readPipeTable } from '@vue/typescript-plugin/lib/utils';
import { getReadyNamedPipePaths, onSomePipeReadyCallbacks, searchNamedPipeServerForFile } from '@vue/typescript-plugin/lib/utils';
import { URI } from 'vscode-uri';

export function createHybridModeProject(
@@ -17,26 +17,39 @@ export function createHybridModeProject(
}): void;
}>
): LanguageServerProject {
let initialized = false;
let simpleLs: Promise<LanguageService> | undefined;
let server: LanguageServer;
let pipeTableWatcher: NodeJS.Timeout | undefined;

const tsconfigProjects = createUriMap<Promise<LanguageService>>();
const project: LanguageServerProject = {
setup(_server) {
server = _server;
onSomePipeReadyCallbacks.push(() => {
server.refresh(project);
});
server.onDidChangeWatchedFiles(({ changes }) => {
for (const change of changes) {
const changeUri = URI.parse(change.uri);
if (tsconfigProjects.has(changeUri)) {
tsconfigProjects.get(changeUri)?.then(project => project.dispose());
tsconfigProjects.delete(changeUri);
server.clearPushDiagnostics();
}
}
});
const end = Date.now() + 60000;
const pipeWatcher = setInterval(() => {
getReadyNamedPipePaths();
if (Date.now() > end) {
clearInterval(pipeWatcher!);
}
}, 1000);
},
async getLanguageService(uri) {
if (!initialized) {
initialized = true;
initialize();
trackPipeTableChanges();
}
const fileName = asFileName(uri);
const projectInfo = (await searchNamedPipeServerForFile(fileName))?.projectInfo;
if (projectInfo?.kind === 1) {
const tsconfig = projectInfo.name;
const namedPipeServer = (await searchNamedPipeServerForFile(fileName));
if (namedPipeServer?.projectInfo?.kind === 1) {
const tsconfig = namedPipeServer.projectInfo.name;
const tsconfigUri = URI.file(tsconfig);
if (!tsconfigProjects.has(tsconfigUri)) {
tsconfigProjects.set(tsconfigUri, createLs(server, tsconfig));
@@ -63,10 +76,6 @@ export function createHybridModeProject(
}
tsconfigProjects.clear();
simpleLs = undefined;
if (pipeTableWatcher) {
clearInterval(pipeTableWatcher);
pipeTableWatcher = undefined;
}
},
};

@@ -76,39 +85,6 @@ export function createHybridModeProject(
return uri.fsPath.replace(/\\/g, '/');
}

function initialize() {
server.onDidChangeWatchedFiles(({ changes }) => {
for (const change of changes) {
const changeUri = URI.parse(change.uri);
if (tsconfigProjects.has(changeUri)) {
tsconfigProjects.get(changeUri)?.then(project => project.dispose());
tsconfigProjects.delete(changeUri);
server.clearPushDiagnostics();
}
}
});
}

function trackPipeTableChanges() {
if (pipeTableWatcher) {
clearInterval(pipeTableWatcher);
pipeTableWatcher = undefined;
}
let table = readPipeTable();
let remaining = 20;
pipeTableWatcher = setInterval(() => {
const newTable = readPipeTable();
if (JSON.stringify(table) !== JSON.stringify(newTable)) {
table = newTable;
server.refresh(project);
}
if (remaining-- <= 0) {
clearInterval(pipeTableWatcher);
pipeTableWatcher = undefined;
}
}, 1000);
}

async function createLs(server: LanguageServer, tsconfig: string | undefined) {
const { languagePlugins, setup } = await create({
configFileName: tsconfig,
11 changes: 7 additions & 4 deletions packages/typescript-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createLanguageServicePlugin, externalFiles } from '@volar/typescript/lib/quickstart/createLanguageServicePlugin';
import * as vue from '@vue/language-core';
import { proxyLanguageServiceForVue } from './lib/common';
import { projects, startNamedPipeServer } from './lib/server';
import { startNamedPipeServer } from './lib/server';

const windowsPathReg = /\\/g;

@@ -25,10 +25,13 @@ const plugin = createLanguageServicePlugin(
return {
languagePlugins: [languagePlugin],
setup: language => {
projects.set(info.project, { info, language, vueOptions });

info.languageService = proxyLanguageServiceForVue(ts, language, info.languageService, vueOptions, fileName => fileName);
startNamedPipeServer(ts, info.project.projectKind, info.project.getCurrentDirectory());
if (
info.project.projectKind === ts.server.ProjectKind.Configured
|| info.project.projectKind === ts.server.ProjectKind.Inferred
) {
startNamedPipeServer(ts, info, language, info.project.projectKind);
}

// #3963
const timer = setInterval(() => {
13 changes: 5 additions & 8 deletions packages/typescript-plugin/lib/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Request } from './server';
import { connect, searchNamedPipeServerForFile, sendRequestWorker } from './utils';
import { searchNamedPipeServerForFile, sendRequestWorker } from './utils';

export function collectExtractProps(
...args: Parameters<typeof import('./requests/collectExtractProps.js')['collectExtractProps']>
@@ -85,15 +85,12 @@ export function getElementAttrs(
}

async function sendRequest<T>(request: Request) {
const server = (await searchNamedPipeServerForFile(request.args[0]))?.server;
const server = (await searchNamedPipeServerForFile(request.args[0]));
if (!server) {
console.warn('[Vue Named Pipe Client] No server found for', request.args[0]);
return;
}
const client = await connect(server.path);
if (!client) {
console.warn('[Vue Named Pipe Client] Failed to connect to', server.path);
return;
}
return await sendRequestWorker<T>(request, client);
const res = await sendRequestWorker<T>(request, server.socket);
server.socket.end();
return res;
}
Loading
Loading