From ee7edc6883674133abd1dfbfef0dbf81e3dc73ac Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 1 Mar 2024 23:28:02 +0800 Subject: [PATCH 1/2] feat(typescript-plugin): define named pipe file path based on pid --- packages/typescript-plugin/index.ts | 2 +- packages/typescript-plugin/lib/client.ts | 41 ++++++++++++- .../lib/requests/containsFile.ts | 5 ++ packages/typescript-plugin/lib/server.ts | 58 +++++++++++++++++-- packages/typescript-plugin/lib/utils.ts | 12 +++- 5 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 packages/typescript-plugin/lib/requests/containsFile.ts diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts index fbedd50b7..5209126c6 100644 --- a/packages/typescript-plugin/index.ts +++ b/packages/typescript-plugin/index.ts @@ -60,7 +60,7 @@ function createLanguageServicePlugin(): ts.server.PluginModuleFactory { decorateLanguageService(files, info.languageService); decorateLanguageServiceHost(files, info.languageServiceHost, ts); - startNamedPipeServer(); + startNamedPipeServer(info.project.projectKind); const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition; const getEncodedSemanticClassifications = info.languageService.getEncodedSemanticClassifications; diff --git a/packages/typescript-plugin/lib/client.ts b/packages/typescript-plugin/lib/client.ts index 371278560..5c7a004c7 100644 --- a/packages/typescript-plugin/lib/client.ts +++ b/packages/typescript-plugin/lib/client.ts @@ -1,6 +1,9 @@ import * as net from 'net'; +import * as fs from 'fs'; +import type * as ts from 'typescript'; import type { Request } from './server'; -import { pipeFile } from './utils'; +import type { PipeTable } from './utils'; +import { pipeTable } from './utils'; export function collectExtractProps( ...args: Parameters @@ -76,7 +79,41 @@ export function getElementAttrs( }); } -function sendRequest(request: Request) { +async function sendRequest(request: Request) { + const pipeFile = await getPipeFile(request.args[0]); + if (!pipeFile) { + console.error('[Vue Named Pipe Client] pipeFile not found'); + return; + } + return await _sendRequest(request, pipeFile); +} + +async function getPipeFile(fileName: string) { + if (fs.existsSync(pipeTable)) { + const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8')); + const all = Object.values(table); + const configuredServers = all + .filter(item => item.serverKind === 1 satisfies ts.server.ProjectKind.Configured) + .sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid)); + const inferredServers = all + .filter(item => item.serverKind === 0 satisfies ts.server.ProjectKind.Inferred) + .sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid)); + for (const server of configuredServers) { + const response = await _sendRequest({ type: 'containsFile', args: [fileName] }, server.pipeFile); + if (response) { + return server.pipeFile; + } + } + for (const server of inferredServers) { + const response = await _sendRequest({ type: 'containsFile', args: [fileName] }, server.pipeFile); + if (typeof response === 'boolean') { + return server.pipeFile; + } + } + } +} + +function _sendRequest(request: Request, pipeFile: string) { return new Promise(resolve => { try { const client = net.connect(pipeFile); diff --git a/packages/typescript-plugin/lib/requests/containsFile.ts b/packages/typescript-plugin/lib/requests/containsFile.ts new file mode 100644 index 000000000..d191f258a --- /dev/null +++ b/packages/typescript-plugin/lib/requests/containsFile.ts @@ -0,0 +1,5 @@ +import { getProject } from '../utils'; + +export function containsFile(fileName: string) { + return !!getProject(fileName); +} diff --git a/packages/typescript-plugin/lib/server.ts b/packages/typescript-plugin/lib/server.ts index 3e99faaf4..1afdbb8b8 100644 --- a/packages/typescript-plugin/lib/server.ts +++ b/packages/typescript-plugin/lib/server.ts @@ -1,13 +1,16 @@ import * as fs from 'fs'; import * as net from 'net'; +import type * as ts from 'typescript'; import { collectExtractProps } from './requests/collectExtractProps'; import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from './requests/componentInfos'; +import { containsFile } from './requests/containsFile'; import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation'; import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition'; -import { pipeFile } from './utils'; +import { PipeTable, pipeTable } from './utils'; export interface Request { - type: 'collectExtractProps' + type: 'containsFile' + | 'collectExtractProps' | 'getPropertiesAtLocation' | 'getQuickInfoAtPosition' // Component Infos @@ -17,17 +20,27 @@ export interface Request { | 'getComponentNames' | 'getElementAttrs'; args: any; -} +}; let started = false; -export function startNamedPipeServer() { +export function startNamedPipeServer(serverKind: ts.server.ProjectKind) { + if (started) return; started = true; + + const pipeFile = process.platform === 'win32' + ? `\\\\.\\pipe\\vue-tsp-${process.pid}` + : `/tmp/vue-tsp-${process.pid}`; const server = net.createServer(connection => { connection.on('data', data => { - const request: Request = JSON.parse(data.toString()); - if (request.type === 'collectExtractProps') { + const text = data.toString(); + const request: Request = JSON.parse(text); + if (request.type === 'containsFile') { + const result = containsFile.apply(null, request.args); + connection.write(JSON.stringify(result ?? null)); + } + else if (request.type === 'collectExtractProps') { const result = collectExtractProps.apply(null, request.args); connection.write(JSON.stringify(result ?? null)); } @@ -68,9 +81,42 @@ export function startNamedPipeServer() { connection.on('error', err => console.error('[Vue Named Pipe Server]', err.message)); }); + clearupPipeTable(); + + if (!fs.existsSync(pipeTable)) { + fs.writeFileSync(pipeTable, JSON.stringify({})); + } + const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8')); + table[process.pid] = { + pid: process.pid, + pipeFile, + serverKind, + }; + fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2)); + try { fs.unlinkSync(pipeFile); } catch { } server.listen(pipeFile); } + +function clearupPipeTable() { + if (fs.existsSync(pipeTable)) { + const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8')); + for (const pid in table) { + const { pipeFile } = table[pid]; + try { + const client = net.connect(pipeFile); + client.on('connect', () => { + client.end(); + }); + client.on('error', () => { + const table = JSON.parse(fs.readFileSync(pipeTable, 'utf8')); + delete table[pid]; + fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2)); + }); + } catch { } + } + } +} diff --git a/packages/typescript-plugin/lib/utils.ts b/packages/typescript-plugin/lib/utils.ts index ff7f33cc6..5c2c9d4d2 100644 --- a/packages/typescript-plugin/lib/utils.ts +++ b/packages/typescript-plugin/lib/utils.ts @@ -1,7 +1,17 @@ import type * as ts from 'typescript'; import type { FileRegistry, VueCompilerOptions } from '@vue/language-core'; -export const pipeFile = process.platform === 'win32' ? '\\\\.\\pipe\\vue-tsp' : '/tmp/vue-tsp'; +export interface PipeTable { + [pid: string]: { + pid: number; + pipeFile: string; + serverKind: ts.server.ProjectKind; + }; +} + +export const pipeTable = process.platform === 'win32' + ? `\\\\.\\pipe\\vue-tsp-table.json` + : `/tmp/vue-tsp-table.json`; export const projects = new Map Date: Fri, 1 Mar 2024 23:31:36 +0800 Subject: [PATCH 2/2] Update server.ts --- packages/typescript-plugin/lib/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-plugin/lib/server.ts b/packages/typescript-plugin/lib/server.ts index 1afdbb8b8..b42d36989 100644 --- a/packages/typescript-plugin/lib/server.ts +++ b/packages/typescript-plugin/lib/server.ts @@ -20,7 +20,7 @@ export interface Request { | 'getComponentNames' | 'getElementAttrs'; args: any; -}; +} let started = false;