289 lines
12 KiB
Plaintext
289 lines
12 KiB
Plaintext
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.createServerBase = void 0;
|
||
|
const language_service_1 = require("@volar/language-service");
|
||
|
const l10n = require("@vscode/l10n");
|
||
|
const request_light_1 = require("request-light");
|
||
|
const vscode = require("vscode-languageserver");
|
||
|
const vscode_uri_1 = require("vscode-uri");
|
||
|
const types_js_1 = require("./types.js");
|
||
|
const configurationHost_js_1 = require("./configurationHost.js");
|
||
|
const setupCapabilities_js_1 = require("./setupCapabilities.js");
|
||
|
const workspaceFolderManager_js_1 = require("./workspaceFolderManager.js");
|
||
|
const snapshot_document_1 = require("@volar/snapshot-document");
|
||
|
function createServerBase(connection, getRuntimeEnv) {
|
||
|
let context;
|
||
|
let projects;
|
||
|
let serverOptions;
|
||
|
let semanticTokensReq = 0;
|
||
|
let documentUpdatedReq = 0;
|
||
|
const documents = new vscode.TextDocuments({
|
||
|
create(uri, languageId, version, text) {
|
||
|
return new snapshot_document_1.SnapshotDocument(uri, languageId, version, text);
|
||
|
},
|
||
|
update(snapshot, contentChanges, version) {
|
||
|
snapshot.update(contentChanges, version);
|
||
|
return snapshot;
|
||
|
},
|
||
|
});
|
||
|
const didChangeWatchedFilesCallbacks = new Set();
|
||
|
const workspaceFolderManager = (0, workspaceFolderManager_js_1.createWorkspaceFolderManager)();
|
||
|
documents.listen(connection);
|
||
|
return {
|
||
|
initialize,
|
||
|
initialized,
|
||
|
shutdown,
|
||
|
get projects() {
|
||
|
return projects;
|
||
|
},
|
||
|
get env() {
|
||
|
return context.runtimeEnv;
|
||
|
},
|
||
|
get modules() {
|
||
|
return {
|
||
|
typescript: context.ts,
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
async function initialize(params, projectProviderFactory, _serverOptions) {
|
||
|
serverOptions = _serverOptions;
|
||
|
const env = getRuntimeEnv(params);
|
||
|
context = {
|
||
|
initializeParams: params,
|
||
|
connection,
|
||
|
runtimeEnv: {
|
||
|
...env,
|
||
|
fs: createFsWithCache(env.fs),
|
||
|
},
|
||
|
configurationHost: params.capabilities.workspace?.configuration
|
||
|
? (0, configurationHost_js_1.createConfigurationHost)(params, connection)
|
||
|
: undefined,
|
||
|
onDidChangeWatchedFiles: cb => {
|
||
|
didChangeWatchedFilesCallbacks.add(cb);
|
||
|
return {
|
||
|
dispose: () => {
|
||
|
didChangeWatchedFilesCallbacks.delete(cb);
|
||
|
},
|
||
|
};
|
||
|
},
|
||
|
ts: undefined,
|
||
|
tsLocalized: undefined,
|
||
|
documents,
|
||
|
workspaceFolders: workspaceFolderManager,
|
||
|
reloadDiagnostics,
|
||
|
updateDiagnosticsAndSemanticTokens,
|
||
|
};
|
||
|
context.ts = await env.loadTypeScript(context.initializeParams.initializationOptions ?? {});
|
||
|
if (context.initializeParams.locale) {
|
||
|
context.tsLocalized = await env.loadTypeScriptLocalized(context.initializeParams.initializationOptions ?? {}, context.initializeParams.locale);
|
||
|
}
|
||
|
if (context.initializeParams.initializationOptions?.l10n) {
|
||
|
await l10n.config({ uri: context.initializeParams.initializationOptions.l10n.location });
|
||
|
}
|
||
|
if (params.capabilities.workspace?.workspaceFolders && params.workspaceFolders) {
|
||
|
for (const folder of params.workspaceFolders) {
|
||
|
workspaceFolderManager.add(vscode_uri_1.URI.parse(folder.uri));
|
||
|
}
|
||
|
}
|
||
|
else if (params.rootUri) {
|
||
|
workspaceFolderManager.add(vscode_uri_1.URI.parse(params.rootUri));
|
||
|
}
|
||
|
else if (params.rootPath) {
|
||
|
workspaceFolderManager.add(vscode_uri_1.URI.file(params.rootPath));
|
||
|
}
|
||
|
const result = {
|
||
|
capabilities: {
|
||
|
textDocumentSync: vscode.TextDocumentSyncKind.Incremental,
|
||
|
workspace: {
|
||
|
// #18
|
||
|
workspaceFolders: {
|
||
|
supported: true,
|
||
|
changeNotifications: true,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
const servicePlugins = await serverOptions.getServicePlugins();
|
||
|
(0, setupCapabilities_js_1.setupCapabilities)(result.capabilities, context.initializeParams.initializationOptions ?? {}, serverOptions.watchFileExtensions ?? [], servicePlugins, getSemanticTokensLegend());
|
||
|
projects = projectProviderFactory({
|
||
|
...context,
|
||
|
}, serverOptions, servicePlugins);
|
||
|
documents.onDidChangeContent(({ document }) => {
|
||
|
updateDiagnostics(document.uri);
|
||
|
});
|
||
|
documents.onDidClose(({ document }) => {
|
||
|
context.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
||
|
});
|
||
|
context.configurationHost?.onDidChangeConfiguration?.(updateDiagnosticsAndSemanticTokens);
|
||
|
(await import('./register/registerEditorFeatures.js')).registerEditorFeatures(connection, projects, context.runtimeEnv);
|
||
|
(await import('./register/registerLanguageFeatures.js')).registerLanguageFeatures(connection, projects, params, context.initializeParams.initializationOptions ?? {}, getSemanticTokensLegend(), context.runtimeEnv, documents);
|
||
|
try {
|
||
|
// show version on LSP logs
|
||
|
const packageJson = require('../package.json');
|
||
|
result.serverInfo = {
|
||
|
name: packageJson.name,
|
||
|
version: packageJson.version,
|
||
|
};
|
||
|
}
|
||
|
catch { }
|
||
|
return result;
|
||
|
}
|
||
|
function initialized() {
|
||
|
context.configurationHost?.ready();
|
||
|
context.configurationHost?.onDidChangeConfiguration?.(updateHttpSettings);
|
||
|
updateHttpSettings();
|
||
|
if (context.initializeParams.capabilities.workspace?.workspaceFolders) {
|
||
|
connection.workspace.onDidChangeWorkspaceFolders(e => {
|
||
|
for (const folder of e.added) {
|
||
|
workspaceFolderManager.add(vscode_uri_1.URI.parse(folder.uri));
|
||
|
}
|
||
|
for (const folder of e.removed) {
|
||
|
workspaceFolderManager.remove(vscode_uri_1.URI.parse(folder.uri));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
if (context.initializeParams.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
|
||
|
if (serverOptions.watchFileExtensions?.length) {
|
||
|
connection.client.register(vscode.DidChangeWatchedFilesNotification.type, {
|
||
|
watchers: [
|
||
|
{
|
||
|
globPattern: `**/*.{${serverOptions.watchFileExtensions.join(',')}}`
|
||
|
},
|
||
|
]
|
||
|
});
|
||
|
connection.onDidChangeWatchedFiles(e => {
|
||
|
for (const cb of didChangeWatchedFilesCallbacks) {
|
||
|
cb(e);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
async function updateHttpSettings() {
|
||
|
const httpSettings = await context.configurationHost?.getConfiguration?.('http');
|
||
|
(0, request_light_1.configure)(httpSettings?.proxy, httpSettings?.proxyStrictSSL ?? false);
|
||
|
}
|
||
|
}
|
||
|
function shutdown() {
|
||
|
projects?.reloadProjects();
|
||
|
}
|
||
|
function createFsWithCache(fs) {
|
||
|
const readFileCache = new Map();
|
||
|
const statCache = new Map();
|
||
|
const readDirectoryCache = new Map();
|
||
|
didChangeWatchedFilesCallbacks.add(({ changes }) => {
|
||
|
for (const change of changes) {
|
||
|
if (change.type === vscode.FileChangeType.Deleted) {
|
||
|
readFileCache.set(change.uri, undefined);
|
||
|
statCache.set(change.uri, undefined);
|
||
|
const dir = change.uri.substring(0, change.uri.lastIndexOf('/'));
|
||
|
readDirectoryCache.delete(dir);
|
||
|
}
|
||
|
else if (change.type === vscode.FileChangeType.Changed) {
|
||
|
readFileCache.delete(change.uri);
|
||
|
statCache.delete(change.uri);
|
||
|
}
|
||
|
else if (change.type === vscode.FileChangeType.Created) {
|
||
|
readFileCache.delete(change.uri);
|
||
|
statCache.delete(change.uri);
|
||
|
const dir = change.uri.substring(0, change.uri.lastIndexOf('/'));
|
||
|
readDirectoryCache.delete(dir);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return {
|
||
|
readFile: uri => {
|
||
|
if (!readFileCache.has(uri)) {
|
||
|
readFileCache.set(uri, fs.readFile(uri));
|
||
|
}
|
||
|
return readFileCache.get(uri);
|
||
|
},
|
||
|
stat: uri => {
|
||
|
if (!statCache.has(uri)) {
|
||
|
statCache.set(uri, fs.stat(uri));
|
||
|
}
|
||
|
return statCache.get(uri);
|
||
|
},
|
||
|
readDirectory: uri => {
|
||
|
if (!readDirectoryCache.has(uri)) {
|
||
|
readDirectoryCache.set(uri, fs.readDirectory(uri));
|
||
|
}
|
||
|
return readDirectoryCache.get(uri);
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
function getSemanticTokensLegend() {
|
||
|
if (!context.initializeParams.initializationOptions?.semanticTokensLegend) {
|
||
|
return language_service_1.standardSemanticTokensLegend;
|
||
|
}
|
||
|
return {
|
||
|
tokenTypes: [...language_service_1.standardSemanticTokensLegend.tokenTypes, ...context.initializeParams.initializationOptions.semanticTokensLegend.tokenTypes],
|
||
|
tokenModifiers: [...language_service_1.standardSemanticTokensLegend.tokenModifiers, ...context.initializeParams.initializationOptions.semanticTokensLegend.tokenModifiers],
|
||
|
};
|
||
|
}
|
||
|
function reloadDiagnostics() {
|
||
|
for (const document of documents.all()) {
|
||
|
context.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
||
|
}
|
||
|
updateDiagnosticsAndSemanticTokens();
|
||
|
}
|
||
|
async function updateDiagnosticsAndSemanticTokens() {
|
||
|
const req = ++semanticTokensReq;
|
||
|
await updateDiagnostics();
|
||
|
const delay = 250;
|
||
|
await sleep(delay);
|
||
|
if (req === semanticTokensReq) {
|
||
|
if (context.initializeParams.capabilities.workspace?.semanticTokens?.refreshSupport) {
|
||
|
context.connection.languages.semanticTokens.refresh();
|
||
|
}
|
||
|
if (context.initializeParams.capabilities.workspace?.inlayHint?.refreshSupport) {
|
||
|
context.connection.languages.inlayHint.refresh();
|
||
|
}
|
||
|
if ((context.initializeParams.initializationOptions?.diagnosticModel ?? types_js_1.DiagnosticModel.Push) === types_js_1.DiagnosticModel.Pull) {
|
||
|
if (context.initializeParams.capabilities.workspace?.diagnostics?.refreshSupport) {
|
||
|
context.connection.languages.diagnostics.refresh();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
async function updateDiagnostics(docUri) {
|
||
|
if ((context.initializeParams.initializationOptions?.diagnosticModel ?? types_js_1.DiagnosticModel.Push) !== types_js_1.DiagnosticModel.Push) {
|
||
|
return;
|
||
|
}
|
||
|
const req = ++documentUpdatedReq;
|
||
|
const delay = 250;
|
||
|
const token = {
|
||
|
get isCancellationRequested() {
|
||
|
return req !== documentUpdatedReq;
|
||
|
},
|
||
|
onCancellationRequested: vscode.Event.None,
|
||
|
};
|
||
|
const changeDoc = docUri ? documents.get(docUri) : undefined;
|
||
|
const otherDocs = [...documents.all()].filter(doc => doc !== changeDoc);
|
||
|
if (changeDoc) {
|
||
|
await sleep(delay);
|
||
|
if (token.isCancellationRequested) {
|
||
|
return;
|
||
|
}
|
||
|
await sendDocumentDiagnostics(changeDoc.uri, changeDoc.version, token);
|
||
|
}
|
||
|
for (const doc of otherDocs) {
|
||
|
await sleep(delay);
|
||
|
if (token.isCancellationRequested) {
|
||
|
break;
|
||
|
}
|
||
|
await sendDocumentDiagnostics(doc.uri, doc.version, token);
|
||
|
}
|
||
|
}
|
||
|
async function sendDocumentDiagnostics(uri, version, cancel) {
|
||
|
const languageService = (await projects.getProject(uri)).getLanguageService();
|
||
|
const errors = await languageService.doValidation(uri, cancel, result => {
|
||
|
context.connection.sendDiagnostics({ uri: uri, diagnostics: result, version });
|
||
|
});
|
||
|
context.connection.sendDiagnostics({ uri: uri, diagnostics: errors, version });
|
||
|
}
|
||
|
}
|
||
|
exports.createServerBase = createServerBase;
|
||
|
function sleep(ms) {
|
||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||
|
}
|
||
|
//# sourceMappingURL=server.js.map
|