Skip to content

Commit

Permalink
refactor(language-server): isolate TS dependency for Server and `Se…
Browse files Browse the repository at this point in the history
…rverProjectProviderFactory` (#140)
  • Loading branch information
johnsoncodehk committed Feb 23, 2024
1 parent 9f99ef4 commit fe7f795
Show file tree
Hide file tree
Showing 12 changed files with 429 additions and 551 deletions.
59 changes: 29 additions & 30 deletions packages/language-server/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,6 @@ export function createConnection() {

export function createServer(connection: vscode.Connection) {
return createServerBase(connection, () => ({
console: connection.console,
async loadTypeScript(options) {
const tsdkUrl = options.typescript?.tsdkUrl;
if (!tsdkUrl) {
return;
}
const originalModule = globalThis.module;
try {
globalThis.module = { exports: {} } as typeof originalModule;
await import(`${tsdkUrl}/typescript.js`);
return globalThis.module.exports as typeof import('typescript');
} finally {
globalThis.module = originalModule;
}
},
async loadTypeScriptLocalized(options, locale) {
const tsdkUrl = options.typescript && 'tsdkUrl' in options.typescript
? options.typescript.tsdkUrl
: undefined;
if (!tsdkUrl) {
return;
}
try {
const json = await httpSchemaRequestHandler(`${tsdkUrl}/${locale}/diagnosticMessages.generated.json`);
if (json) {
return JSON.parse(json);
}
}
catch { }
},
fs: {
async stat(uri) {
if (uri.startsWith('http://') || uri.startsWith('https://')) { // perf
Expand Down Expand Up @@ -81,3 +51,32 @@ export function createServer(connection: vscode.Connection) {
},
}));
}

export async function loadTsdkByUrl(tsdkUrl: string, locale: string | undefined) {

return {
typescript: await loadLib(),
diagnosticMessages: await loadLocalizedDiagnosticMessages(),
};

async function loadLib(): Promise<typeof import('typescript')> {
const originalModule = globalThis.module;
try {
globalThis.module = { exports: {} } as typeof originalModule;
await import(`${tsdkUrl}/typescript.js`);
return globalThis.module.exports as typeof import('typescript');
} finally {
globalThis.module = originalModule;
}
}

async function loadLocalizedDiagnosticMessages(): Promise<import('typescript').MapLike<string> | undefined> {
try {
const json = await httpSchemaRequestHandler(`${tsdkUrl}/${locale}/diagnosticMessages.generated.json`);
if (json) {
return JSON.parse(json);
}
}
catch { }
}
}
5 changes: 2 additions & 3 deletions packages/language-server/lib/project/simpleProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ import type { ServerProject } from '../types';
export async function createSimpleServerProject(
context: ServerContext,
serviceEnv: ServiceEnvironment,
serverOptions: ServerOptions,
servicePlugins: ServicePlugin[],
getLanguagePlugins: ServerOptions['getLanguagePlugins'],
): Promise<ServerProject> {

let languageService: LanguageService | undefined;

const languagePlugins = await serverOptions.getLanguagePlugins(serviceEnv, {});
const languagePlugins = await getLanguagePlugins(serviceEnv, {});

return {
serviceEnv,
getLanguageService,
getLanguageServiceDontCreate: () => languageService,
dispose() {
Expand Down
87 changes: 43 additions & 44 deletions packages/language-server/lib/project/simpleProjectProvider.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
import type { ServiceEnvironment } from '@volar/language-service';
import { URI } from 'vscode-uri';
import type { ServerProject, ServerProjectProvider, ServerProjectProviderFactory } from '../types';
import { isFileInDir } from '../utils/isFileInDir';
import type { WorkspaceFolderManager } from '../workspaceFolderManager';
import { createSimpleServerProject } from './simpleProject';
import type { ServerContext } from '../server';
import { fileNameToUri, uriToFileName } from '../uri';
import type { UriMap } from '../utils/uriMap';

export const createSimpleProjectProvider: ServerProjectProviderFactory = (context, serverOptions, servicePlugins): ServerProjectProvider => {
export function createSimpleProjectProviderFactory(): ServerProjectProviderFactory {
return (context, servicePlugins, getLanguagePlugins): ServerProjectProvider => {

const projects = new Map<URI, Promise<ServerProject>>();
const projects = new Map<string, Promise<ServerProject>>();

return {
getProject(uri) {
return {
getProject(uri) {

const workspaceFolder = getWorkspaceFolder(uri, context.workspaceFolders);
const workspaceFolder = getWorkspaceFolder(uri, context.workspaceFolders);

let projectPromise = projects.get(workspaceFolder);
if (!projectPromise) {
const serviceEnv = createServiceEnvironment(context, workspaceFolder);
projectPromise = createSimpleServerProject(context, serviceEnv, serverOptions, servicePlugins);
projects.set(workspaceFolder, projectPromise);
}
let projectPromise = projects.get(workspaceFolder);
if (!projectPromise) {
const serviceEnv = createServiceEnvironment(context, workspaceFolder);
projectPromise = createSimpleServerProject(context, serviceEnv, servicePlugins, getLanguagePlugins);
projects.set(workspaceFolder, projectPromise);
}

return projectPromise;
},
async getProjects() {
return await Promise.all([...projects.values()]);
},
reloadProjects() {
return projectPromise;
},
async getProjects() {
return await Promise.all([...projects.values()]);
},
reloadProjects() {

for (const project of projects.values()) {
project.then(project => project.dispose());
}
for (const project of projects.values()) {
project.then(project => project.dispose());
}

projects.clear();
projects.clear();

context.reloadDiagnostics();
},
context.reloadDiagnostics();
},
};
};
};
}

export function createServiceEnvironment(context: ServerContext, workspaceFolder: URI) {
export function createServiceEnvironment(context: ServerContext, workspaceFolder: string) {
const env: ServiceEnvironment = {
workspaceFolder: workspaceFolder.toString(),
workspaceFolder,
fs: context.runtimeEnv.fs,
console: context.runtimeEnv.console,
locale: context.initializeParams.locale,
clientCapabilities: context.initializeParams.capabilities,
getConfiguration: context.configurationHost?.getConfiguration,
Expand All @@ -59,25 +59,24 @@ export function createServiceEnvironment(context: ServerContext, workspaceFolder
return env;
}

export function getWorkspaceFolder(
uri: string,
workspaceFolderManager: WorkspaceFolderManager,
) {

const fileName = uriToFileName(uri);
export function getWorkspaceFolder(uri: string, workspaceFolders: UriMap<boolean>) {

let folders = workspaceFolderManager
.getAll()
.filter(uri => isFileInDir(fileName, uriToFileName(uri.toString())))
.sort((a, b) => b.toString().length - a.toString().length);
let parsed = URI.parse(uri);

if (!folders.length) {
folders = workspaceFolderManager.getAll();
while (true) {
if (workspaceFolders.uriHas(parsed.toString())) {
return parsed.toString();
}
const next = URI.parse(uri).with({ path: parsed.path.substring(0, parsed.path.lastIndexOf('/')) });
if (next.path === parsed.path) {
break;
}
parsed = next;
}

if (!folders.length) {
folders = [URI.parse(uri).with({ path: '/' })];
for (const folder of workspaceFolders.uriKeys()) {
return folder;
}

return folders[0];
return URI.parse(uri).with({ path: '/' }).toString();
}
12 changes: 4 additions & 8 deletions packages/language-server/lib/project/typescriptProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,19 @@ export interface TypeScriptServerProject extends ServerProject {
}

export async function createTypeScriptServerProject(
ts: typeof import('typescript'),
tsLocalized: ts.MapLike<string> | undefined,
tsconfig: string | ts.CompilerOptions,
context: ServerContext,
serviceEnv: ServiceEnvironment,
getLanguagePlugins: ServerOptions['getLanguagePlugins'],
servicePlugins: ServicePlugin[],
getLanguagePlugins: ServerOptions['getLanguagePlugins'],
): Promise<TypeScriptServerProject> {

if (!context.ts) {
throw '!context.ts';
}

let parsedCommandLine: ts.ParsedCommandLine;
let projectVersion = 0;
let languageService: LanguageService | undefined;

const ts = context.ts;
const sys = createSys(ts, serviceEnv, uriToFileName(serviceEnv.workspaceFolder));
const host: TypeScriptProjectHost = {
getCurrentDirectory: () => uriToFileName(serviceEnv.workspaceFolder),
Expand All @@ -44,7 +41,7 @@ export async function createTypeScriptServerProject(
}
},
getCompilationSettings: () => parsedCommandLine.options,
getLocalizedDiagnosticMessages: context.tsLocalized ? () => context.tsLocalized : undefined,
getLocalizedDiagnosticMessages: tsLocalized ? () => tsLocalized : undefined,
getProjectReferences: () => parsedCommandLine.projectReferences,
getLanguageId: uri => context.documents.get(uri)?.languageId ?? resolveCommonLanguageId(uri),
};
Expand All @@ -67,7 +64,6 @@ export async function createTypeScriptServerProject(

return {
askedFiles,
serviceEnv,
getLanguageService,
getLanguageServiceDontCreate: () => languageService,
tryAddFile(fileName: string) {
Expand Down

0 comments on commit fe7f795

Please sign in to comment.