2 /*---------------------------------------------------------------------------------------------
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
4 * Licensed under the MIT License. See License.txt in the project root for license information.
\r
5 *--------------------------------------------------------------------------------------------*/
\r
6 Object.defineProperty(exports, "__esModule", { value: true });
\r
7 exports.startServer = void 0;
\r
8 const vscode_languageserver_1 = require("vscode-languageserver");
\r
9 const runner_1 = require("./utils/runner");
\r
10 const vscode_json_languageservice_1 = require("vscode-json-languageservice");
\r
11 const languageModelCache_1 = require("./languageModelCache");
\r
12 const requests_1 = require("./requests");
\r
13 var SchemaAssociationNotification;
\r
14 (function (SchemaAssociationNotification) {
\r
15 SchemaAssociationNotification.type = new vscode_languageserver_1.NotificationType('json/schemaAssociations');
\r
16 })(SchemaAssociationNotification || (SchemaAssociationNotification = {}));
\r
17 var VSCodeContentRequest;
\r
18 (function (VSCodeContentRequest) {
\r
19 VSCodeContentRequest.type = new vscode_languageserver_1.RequestType('vscode/content');
\r
20 })(VSCodeContentRequest || (VSCodeContentRequest = {}));
\r
21 var SchemaContentChangeNotification;
\r
22 (function (SchemaContentChangeNotification) {
\r
23 SchemaContentChangeNotification.type = new vscode_languageserver_1.NotificationType('json/schemaContent');
\r
24 })(SchemaContentChangeNotification || (SchemaContentChangeNotification = {}));
\r
25 var ResultLimitReachedNotification;
\r
26 (function (ResultLimitReachedNotification) {
\r
27 ResultLimitReachedNotification.type = new vscode_languageserver_1.NotificationType('json/resultLimitReached');
\r
28 })(ResultLimitReachedNotification || (ResultLimitReachedNotification = {}));
\r
29 var ForceValidateRequest;
\r
30 (function (ForceValidateRequest) {
\r
31 ForceValidateRequest.type = new vscode_languageserver_1.RequestType('json/validate');
\r
32 })(ForceValidateRequest || (ForceValidateRequest = {}));
\r
33 const workspaceContext = {
\r
34 resolveRelativePath: (relativePath, resource) => {
\r
35 const base = resource.substr(0, resource.lastIndexOf('/') + 1);
\r
36 return requests_1.resolvePath(base, relativePath);
\r
39 function startServer(connection, runtime) {
\r
40 function getSchemaRequestService(handledSchemas = ['https', 'http', 'file']) {
\r
41 const builtInHandlers = {};
\r
42 for (let protocol of handledSchemas) {
\r
43 if (protocol === 'file') {
\r
44 builtInHandlers[protocol] = runtime.file;
\r
46 else if (protocol === 'http' || protocol === 'https') {
\r
47 builtInHandlers[protocol] = runtime.http;
\r
51 const protocol = uri.substr(0, uri.indexOf(':'));
\r
52 const builtInHandler = builtInHandlers[protocol];
\r
53 if (builtInHandler) {
\r
54 return builtInHandler.getContent(uri);
\r
56 return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
\r
57 return responseText;
\r
59 return Promise.reject(error.message);
\r
63 // create the JSON language service
\r
64 let languageService = vscode_json_languageservice_1.getLanguageService({
\r
67 clientCapabilities: vscode_json_languageservice_1.ClientCapabilities.LATEST
\r
69 // Create a text document manager.
\r
70 const documents = new vscode_languageserver_1.TextDocuments(vscode_json_languageservice_1.TextDocument);
\r
71 // Make the text document manager listen on the connection
\r
72 // for open, change and close text document events
\r
73 documents.listen(connection);
\r
74 let clientSnippetSupport = false;
\r
75 let dynamicFormatterRegistration = false;
\r
76 let hierarchicalDocumentSymbolSupport = false;
\r
77 let foldingRangeLimitDefault = Number.MAX_VALUE;
\r
78 let foldingRangeLimit = Number.MAX_VALUE;
\r
79 let resultLimit = Number.MAX_VALUE;
\r
80 let formatterMaxNumberOfEdits = Number.MAX_VALUE;
\r
81 // After the server has started the client sends an initialize request. The server receives
\r
82 // in the passed params the rootPath of the workspace plus the client capabilities.
\r
83 connection.onInitialize((params) => {
\r
84 var _a, _b, _c, _d, _e, _f;
\r
85 const handledProtocols = (_a = params.initializationOptions) === null || _a === void 0 ? void 0 : _a.handledSchemaProtocols;
\r
86 languageService = vscode_json_languageservice_1.getLanguageService({
\r
87 schemaRequestService: getSchemaRequestService(handledProtocols),
\r
90 clientCapabilities: params.capabilities
\r
92 function getClientCapability(name, def) {
\r
93 const keys = name.split('.');
\r
94 let c = params.capabilities;
\r
95 for (let i = 0; c && i < keys.length; i++) {
\r
96 if (!c.hasOwnProperty(keys[i])) {
\r
103 clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
\r
104 dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof ((_b = params.initializationOptions) === null || _b === void 0 ? void 0 : _b.provideFormatter) !== 'boolean');
\r
105 foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
\r
106 hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false);
\r
107 formatterMaxNumberOfEdits = ((_e = (_d = (_c = params.initializationOptions) === null || _c === void 0 ? void 0 : _c.customCapabilities) === null || _d === void 0 ? void 0 : _d.rangeFormatting) === null || _e === void 0 ? void 0 : _e.editLimit) || Number.MAX_VALUE;
\r
108 const capabilities = {
\r
109 textDocumentSync: vscode_languageserver_1.TextDocumentSyncKind.Incremental,
\r
110 completionProvider: clientSnippetSupport ? {
\r
111 resolveProvider: false,
\r
112 triggerCharacters: ['"', ':']
\r
114 hoverProvider: true,
\r
115 documentSymbolProvider: true,
\r
116 documentRangeFormattingProvider: ((_f = params.initializationOptions) === null || _f === void 0 ? void 0 : _f.provideFormatter) === true,
\r
118 foldingRangeProvider: true,
\r
119 selectionRangeProvider: true,
\r
120 documentLinkProvider: {}
\r
122 return { capabilities };
\r
124 const limitExceededWarnings = function () {
\r
125 const pendingWarnings = {};
\r
128 const warning = pendingWarnings[uri];
\r
129 if (warning && warning.timeout) {
\r
130 clearTimeout(warning.timeout);
\r
131 delete pendingWarnings[uri];
\r
134 onResultLimitExceeded(uri, resultLimit, name) {
\r
136 let warning = pendingWarnings[uri];
\r
138 if (!warning.timeout) {
\r
142 warning.features[name] = name;
\r
143 warning.timeout.refresh();
\r
146 warning = { features: { [name]: name } };
\r
147 warning.timeout = setTimeout(() => {
\r
148 connection.sendNotification(ResultLimitReachedNotification.type, `${requests_1.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`);
\r
149 warning.timeout = undefined;
\r
151 pendingWarnings[uri] = warning;
\r
157 let jsonConfigurationSettings = undefined;
\r
158 let schemaAssociations = undefined;
\r
159 let formatterRegistration = null;
\r
160 // The settings have changed. Is send on server activation as well.
\r
161 connection.onDidChangeConfiguration((change) => {
\r
162 let settings = change.settings;
\r
163 if (runtime.configureHttpRequests) {
\r
164 runtime.configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL);
\r
166 jsonConfigurationSettings = settings.json && settings.json.schemas;
\r
167 updateConfiguration();
\r
168 foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0));
\r
169 resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0));
\r
170 // dynamically enable & disable the formatter
\r
171 if (dynamicFormatterRegistration) {
\r
172 const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable;
\r
173 if (enableFormatter) {
\r
174 if (!formatterRegistration) {
\r
175 formatterRegistration = connection.client.register(vscode_languageserver_1.DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] });
\r
178 else if (formatterRegistration) {
\r
179 formatterRegistration.then(r => r.dispose());
\r
180 formatterRegistration = null;
\r
184 // The jsonValidation extension configuration has changed
\r
185 connection.onNotification(SchemaAssociationNotification.type, associations => {
\r
186 schemaAssociations = associations;
\r
187 updateConfiguration();
\r
189 // A schema has changed
\r
190 connection.onNotification(SchemaContentChangeNotification.type, uri => {
\r
191 languageService.resetSchema(uri);
\r
193 // Retry schema validation on all open documents
\r
194 connection.onRequest(ForceValidateRequest.type, uri => {
\r
195 return new Promise(resolve => {
\r
196 const document = documents.get(uri);
\r
198 updateConfiguration();
\r
199 validateTextDocument(document, diagnostics => {
\r
200 resolve(diagnostics);
\r
208 function updateConfiguration() {
\r
209 const languageSettings = {
\r
211 allowComments: true,
\r
212 schemas: new Array()
\r
214 if (schemaAssociations) {
\r
215 if (Array.isArray(schemaAssociations)) {
\r
216 Array.prototype.push.apply(languageSettings.schemas, schemaAssociations);
\r
219 for (const pattern in schemaAssociations) {
\r
220 const association = schemaAssociations[pattern];
\r
221 if (Array.isArray(association)) {
\r
222 association.forEach(uri => {
\r
223 languageSettings.schemas.push({ uri, fileMatch: [pattern] });
\r
229 if (jsonConfigurationSettings) {
\r
230 jsonConfigurationSettings.forEach((schema, index) => {
\r
231 let uri = schema.url;
\r
232 if (!uri && schema.schema) {
\r
233 uri = schema.schema.id || `vscode://schemas/custom/${index}`;
\r
236 languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
\r
240 languageService.configure(languageSettings);
\r
241 // Revalidate any open text documents
\r
242 documents.all().forEach(triggerValidation);
\r
244 // The content of a text document has changed. This event is emitted
\r
245 // when the text document first opened or when its content has changed.
\r
246 documents.onDidChangeContent((change) => {
\r
247 limitExceededWarnings.cancel(change.document.uri);
\r
248 triggerValidation(change.document);
\r
250 // a document has closed: clear all diagnostics
\r
251 documents.onDidClose(event => {
\r
252 limitExceededWarnings.cancel(event.document.uri);
\r
253 cleanPendingValidation(event.document);
\r
254 connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
\r
256 const pendingValidationRequests = {};
\r
257 const validationDelayMs = 300;
\r
258 function cleanPendingValidation(textDocument) {
\r
259 const request = pendingValidationRequests[textDocument.uri];
\r
261 clearTimeout(request);
\r
262 delete pendingValidationRequests[textDocument.uri];
\r
265 function triggerValidation(textDocument) {
\r
266 cleanPendingValidation(textDocument);
\r
267 pendingValidationRequests[textDocument.uri] = setTimeout(() => {
\r
268 delete pendingValidationRequests[textDocument.uri];
\r
269 validateTextDocument(textDocument);
\r
270 }, validationDelayMs);
\r
272 function validateTextDocument(textDocument, callback) {
\r
273 const respond = (diagnostics) => {
\r
274 connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
\r
276 callback(diagnostics);
\r
279 if (textDocument.getText().length === 0) {
\r
280 respond([]); // ignore empty documents
\r
283 const jsonDocument = getJSONDocument(textDocument);
\r
284 const version = textDocument.version;
\r
285 const documentSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' };
\r
286 languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
\r
287 setImmediate(() => {
\r
288 const currDocument = documents.get(textDocument.uri);
\r
289 if (currDocument && currDocument.version === version) {
\r
290 respond(diagnostics); // Send the computed diagnostics to VSCode.
\r
294 connection.console.error(runner_1.formatError(`Error while validating ${textDocument.uri}`, error));
\r
297 connection.onDidChangeWatchedFiles((change) => {
\r
298 // Monitored files have changed in VSCode
\r
299 let hasChanges = false;
\r
300 change.changes.forEach(c => {
\r
301 if (languageService.resetSchema(c.uri)) {
\r
306 documents.all().forEach(triggerValidation);
\r
309 const jsonDocuments = languageModelCache_1.getLanguageModelCache(10, 60, document => languageService.parseJSONDocument(document));
\r
310 documents.onDidClose(e => {
\r
311 jsonDocuments.onDocumentRemoved(e.document);
\r
313 connection.onShutdown(() => {
\r
314 jsonDocuments.dispose();
\r
316 function getJSONDocument(document) {
\r
317 return jsonDocuments.get(document);
\r
319 connection.onCompletion((textDocumentPosition, token) => {
\r
320 return runner_1.runSafeAsync(async () => {
\r
321 const document = documents.get(textDocumentPosition.textDocument.uri);
\r
323 const jsonDocument = getJSONDocument(document);
\r
324 return languageService.doComplete(document, textDocumentPosition.position, jsonDocument);
\r
327 }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
\r
329 connection.onHover((textDocumentPositionParams, token) => {
\r
330 return runner_1.runSafeAsync(async () => {
\r
331 const document = documents.get(textDocumentPositionParams.textDocument.uri);
\r
333 const jsonDocument = getJSONDocument(document);
\r
334 return languageService.doHover(document, textDocumentPositionParams.position, jsonDocument);
\r
337 }, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`, token);
\r
339 connection.onDocumentSymbol((documentSymbolParams, token) => {
\r
340 return runner_1.runSafe(() => {
\r
341 const document = documents.get(documentSymbolParams.textDocument.uri);
\r
343 const jsonDocument = getJSONDocument(document);
\r
344 const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols');
\r
345 if (hierarchicalDocumentSymbolSupport) {
\r
346 return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded });
\r
349 return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded });
\r
353 }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
\r
355 connection.onDocumentRangeFormatting((formatParams, token) => {
\r
356 return runner_1.runSafe(() => {
\r
357 const document = documents.get(formatParams.textDocument.uri);
\r
359 const edits = languageService.format(document, formatParams.range, formatParams.options);
\r
360 if (edits.length > formatterMaxNumberOfEdits) {
\r
361 const newText = vscode_json_languageservice_1.TextDocument.applyEdits(document, edits);
\r
362 return [vscode_languageserver_1.TextEdit.replace(vscode_json_languageservice_1.Range.create(vscode_json_languageservice_1.Position.create(0, 0), document.positionAt(document.getText().length)), newText)];
\r
367 }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
\r
369 connection.onDocumentColor((params, token) => {
\r
370 return runner_1.runSafeAsync(async () => {
\r
371 const document = documents.get(params.textDocument.uri);
\r
373 const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors');
\r
374 const jsonDocument = getJSONDocument(document);
\r
375 return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded });
\r
378 }, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
\r
380 connection.onColorPresentation((params, token) => {
\r
381 return runner_1.runSafe(() => {
\r
382 const document = documents.get(params.textDocument.uri);
\r
384 const jsonDocument = getJSONDocument(document);
\r
385 return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
\r
388 }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
\r
390 connection.onFoldingRanges((params, token) => {
\r
391 return runner_1.runSafe(() => {
\r
392 const document = documents.get(params.textDocument.uri);
\r
394 const onRangeLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges');
\r
395 return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded });
\r
398 }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
\r
400 connection.onSelectionRanges((params, token) => {
\r
401 return runner_1.runSafe(() => {
\r
402 const document = documents.get(params.textDocument.uri);
\r
404 const jsonDocument = getJSONDocument(document);
\r
405 return languageService.getSelectionRanges(document, params.positions, jsonDocument);
\r
408 }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
\r
410 connection.onDocumentLinks((params, token) => {
\r
411 return runner_1.runSafeAsync(async () => {
\r
412 const document = documents.get(params.textDocument.uri);
\r
414 const jsonDocument = getJSONDocument(document);
\r
415 return languageService.findLinks(document, jsonDocument);
\r
418 }, [], `Error while computing links for ${params.textDocument.uri}`, token);
\r
420 // Listen on the connection
\r
421 connection.listen();
\r
423 exports.startServer = startServer;
\r
424 //# sourceMappingURL=jsonServer.js.map