Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, Uri } from 'vscode';
|
||||
import { LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { startClient, LanguageClientConstructor } from '../jsonClient';
|
||||
import { LanguageClient } from 'vscode-languageclient/browser';
|
||||
import { RequestService } from '../requests';
|
||||
|
||||
declare const Worker: {
|
||||
new(stringUrl: string): any;
|
||||
};
|
||||
|
||||
declare function fetch(uri: string, options: any): any;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js');
|
||||
try {
|
||||
const worker = new Worker(serverMain.toString());
|
||||
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
|
||||
return new LanguageClient(id, name, clientOptions, worker);
|
||||
};
|
||||
|
||||
const http: RequestService = {
|
||||
getContent(uri: string) {
|
||||
return fetch(uri, { mode: 'cors' })
|
||||
.then(function (response: any) {
|
||||
return response.text();
|
||||
});
|
||||
}
|
||||
};
|
||||
startClient(context, newLanguageClient, { http });
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
@ -0,0 +1,509 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import {
|
||||
workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration,
|
||||
Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken,
|
||||
ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString,
|
||||
} from 'vscode';
|
||||
import {
|
||||
LanguageClientOptions, RequestType, NotificationType,
|
||||
DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams,
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient
|
||||
} from 'vscode-languageclient';
|
||||
|
||||
import { hash } from './utils/hash';
|
||||
import { RequestService, joinPath } from './requests';
|
||||
|
||||
namespace VSCodeContentRequest {
|
||||
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
|
||||
}
|
||||
|
||||
namespace SchemaContentChangeNotification {
|
||||
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
|
||||
}
|
||||
|
||||
namespace ForceValidateRequest {
|
||||
export const type: RequestType<string, Diagnostic[], any, any> = new RequestType('json/validate');
|
||||
}
|
||||
|
||||
export interface ISchemaAssociations {
|
||||
[pattern: string]: string[];
|
||||
}
|
||||
|
||||
export interface ISchemaAssociation {
|
||||
fileMatch: string[];
|
||||
uri: string;
|
||||
}
|
||||
|
||||
namespace SchemaAssociationNotification {
|
||||
export const type: NotificationType<ISchemaAssociations | ISchemaAssociation[], any> = new NotificationType('json/schemaAssociations');
|
||||
}
|
||||
|
||||
namespace ResultLimitReachedNotification {
|
||||
export const type: NotificationType<string, any> = new NotificationType('json/resultLimitReached');
|
||||
}
|
||||
|
||||
interface Settings {
|
||||
json?: {
|
||||
schemas?: JSONSchemaSettings[];
|
||||
format?: { enable: boolean; };
|
||||
resultLimit?: number;
|
||||
};
|
||||
http?: {
|
||||
proxy?: string;
|
||||
proxyStrictSSL?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface JSONSchemaSettings {
|
||||
fileMatch?: string[];
|
||||
url?: string;
|
||||
schema?: any;
|
||||
}
|
||||
|
||||
namespace SettingIds {
|
||||
export const enableFormatter = 'json.format.enable';
|
||||
export const enableSchemaDownload = 'json.schemaDownload.enable';
|
||||
export const maxItemsComputed = 'json.maxItemsComputed';
|
||||
}
|
||||
|
||||
export interface TelemetryReporter {
|
||||
sendTelemetryEvent(eventName: string, properties?: {
|
||||
[key: string]: string;
|
||||
}, measurements?: {
|
||||
[key: string]: number;
|
||||
}): void;
|
||||
}
|
||||
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
|
||||
|
||||
export interface Runtime {
|
||||
http: RequestService;
|
||||
telemetry?: TelemetryReporter
|
||||
}
|
||||
|
||||
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
|
||||
|
||||
const toDispose = context.subscriptions;
|
||||
|
||||
let rangeFormatting: Disposable | undefined = undefined;
|
||||
|
||||
|
||||
const documentSelector = ['json', 'jsonc'];
|
||||
|
||||
const schemaResolutionErrorStatusBarItem = window.createStatusBarItem({
|
||||
id: 'status.json.resolveError',
|
||||
name: localize('json.resolveError', "JSON: Schema Resolution Error"),
|
||||
alignment: StatusBarAlignment.Right,
|
||||
priority: 0,
|
||||
});
|
||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||
toDispose.push(schemaResolutionErrorStatusBarItem);
|
||||
|
||||
const fileSchemaErrors = new Map<string, string>();
|
||||
let schemaDownloadEnabled = true;
|
||||
|
||||
// Options to control the language client
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
// Register the server for json documents
|
||||
documentSelector,
|
||||
initializationOptions: {
|
||||
handledSchemaProtocols: ['file'], // language server only loads file-URI. Fetching schemas with other protocols ('http'...) are made on the client.
|
||||
provideFormatter: false, // tell the server to not provide formatting capability and ignore the `json.format.enable` setting.
|
||||
customCapabilities: { rangeFormatting: { editLimit: 10000 } }
|
||||
},
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'json' to the server
|
||||
configurationSection: ['json', 'http'],
|
||||
fileEvents: workspace.createFileSystemWatcher('**/*.json')
|
||||
},
|
||||
middleware: {
|
||||
workspace: {
|
||||
didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() })
|
||||
},
|
||||
handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => {
|
||||
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
|
||||
|
||||
if (schemaErrorIndex === -1) {
|
||||
fileSchemaErrors.delete(uri.toString());
|
||||
return next(uri, diagnostics);
|
||||
}
|
||||
|
||||
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
|
||||
fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message);
|
||||
|
||||
if (!schemaDownloadEnabled) {
|
||||
diagnostics = diagnostics.filter(d => !isSchemaResolveError(d));
|
||||
}
|
||||
|
||||
if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) {
|
||||
schemaResolutionErrorStatusBarItem.show();
|
||||
}
|
||||
|
||||
next(uri, diagnostics);
|
||||
},
|
||||
// testing the replace / insert mode
|
||||
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
|
||||
function update(item: CompletionItem) {
|
||||
const range = item.range;
|
||||
if (range instanceof Range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) {
|
||||
item.range = { inserting: new Range(range.start, position), replacing: range };
|
||||
}
|
||||
if (item.documentation instanceof MarkdownString) {
|
||||
item.documentation = updateMarkdownString(item.documentation);
|
||||
}
|
||||
|
||||
}
|
||||
function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined {
|
||||
if (r) {
|
||||
(Array.isArray(r) ? r : r.items).forEach(update);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
const r = next(document, position, context, token);
|
||||
if (isThenable<CompletionItem[] | CompletionList | null | undefined>(r)) {
|
||||
return r.then(updateProposals);
|
||||
}
|
||||
return updateProposals(r);
|
||||
},
|
||||
provideHover(document: TextDocument, position: Position, token: CancellationToken, next: ProvideHoverSignature) {
|
||||
function updateHover(r: Hover | null | undefined): Hover | null | undefined {
|
||||
if (r && Array.isArray(r.contents)) {
|
||||
r.contents = r.contents.map(h => h instanceof MarkdownString ? updateMarkdownString(h) : h);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
const r = next(document, position, token);
|
||||
if (isThenable<Hover | null | undefined>(r)) {
|
||||
return r.then(updateHover);
|
||||
}
|
||||
return updateHover(r);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
const client = newLanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), clientOptions);
|
||||
client.registerProposedFeatures();
|
||||
|
||||
const disposable = client.start();
|
||||
toDispose.push(disposable);
|
||||
client.onReady().then(() => {
|
||||
const schemaDocuments: { [uri: string]: boolean } = {};
|
||||
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
const uri = Uri.parse(uriPath);
|
||||
if (uri.scheme === 'untitled') {
|
||||
return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString())));
|
||||
}
|
||||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
schemaDocuments[uri.toString()] = true;
|
||||
return doc.getText();
|
||||
}, error => {
|
||||
return Promise.reject(new ResponseError(2, error.toString()));
|
||||
});
|
||||
} else if (schemaDownloadEnabled) {
|
||||
if (runtime.telemetry && uri.authority === 'schema.management.azure.com') {
|
||||
/* __GDPR__
|
||||
"json.schema" : {
|
||||
"schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
|
||||
}
|
||||
return runtime.http.getContent(uriPath);
|
||||
} else {
|
||||
return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)));
|
||||
}
|
||||
});
|
||||
|
||||
const handleContentChange = (uriString: string) => {
|
||||
if (schemaDocuments[uriString]) {
|
||||
client.sendNotification(SchemaContentChangeNotification.type, uriString);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleActiveEditorChange = (activeEditor?: TextEditor) => {
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeDocUri = activeEditor.document.uri.toString();
|
||||
|
||||
if (activeDocUri && fileSchemaErrors.has(activeDocUri)) {
|
||||
schemaResolutionErrorStatusBarItem.show();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString())));
|
||||
toDispose.push(workspace.onDidCloseTextDocument(d => {
|
||||
const uriString = d.uri.toString();
|
||||
if (handleContentChange(uriString)) {
|
||||
delete schemaDocuments[uriString];
|
||||
}
|
||||
fileSchemaErrors.delete(uriString);
|
||||
}));
|
||||
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));
|
||||
|
||||
const handleRetryResolveSchemaCommand = () => {
|
||||
if (window.activeTextEditor) {
|
||||
schemaResolutionErrorStatusBarItem.text = '$(watch)';
|
||||
const activeDocUri = window.activeTextEditor.document.uri.toString();
|
||||
client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => {
|
||||
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
|
||||
if (schemaErrorIndex !== -1) {
|
||||
// Show schema resolution errors in status bar only; ref: #51032
|
||||
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
|
||||
fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message);
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand));
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
|
||||
extensions.onDidChange(_ => {
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
});
|
||||
|
||||
// manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
updateFormatterRegistration();
|
||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||
|
||||
updateSchemaDownloadSetting();
|
||||
|
||||
toDispose.push(workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(SettingIds.enableFormatter)) {
|
||||
updateFormatterRegistration();
|
||||
} else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) {
|
||||
updateSchemaDownloadSetting();
|
||||
}
|
||||
}));
|
||||
|
||||
client.onNotification(ResultLimitReachedNotification.type, message => {
|
||||
window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`);
|
||||
});
|
||||
|
||||
function updateFormatterRegistration() {
|
||||
const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter);
|
||||
if (!formatEnabled && rangeFormatting) {
|
||||
rangeFormatting.dispose();
|
||||
rangeFormatting = undefined;
|
||||
} else if (formatEnabled && !rangeFormatting) {
|
||||
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
||||
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
||||
const params: DocumentRangeFormattingParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
range: client.code2ProtocolConverter.asRange(range),
|
||||
options: client.code2ProtocolConverter.asFormattingOptions(options)
|
||||
};
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateSchemaDownloadSetting() {
|
||||
schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false;
|
||||
if (schemaDownloadEnabled) {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.');
|
||||
schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema';
|
||||
handleRetryResolveSchemaCommand();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.');
|
||||
schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' };
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const languageConfiguration: LanguageConfiguration = {
|
||||
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/,
|
||||
indentationRules: {
|
||||
increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/,
|
||||
decreaseIndentPattern: /^\s*[}\]],?\s*$/
|
||||
}
|
||||
};
|
||||
languages.setLanguageConfiguration('json', languageConfiguration);
|
||||
languages.setLanguageConfiguration('jsonc', languageConfiguration);
|
||||
|
||||
}
|
||||
|
||||
function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] {
|
||||
const associations: ISchemaAssociation[] = [];
|
||||
extensions.all.forEach(extension => {
|
||||
const packageJSON = extension.packageJSON;
|
||||
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) {
|
||||
const jsonValidation = packageJSON.contributes.jsonValidation;
|
||||
if (Array.isArray(jsonValidation)) {
|
||||
jsonValidation.forEach(jv => {
|
||||
let { fileMatch, url } = jv;
|
||||
if (typeof fileMatch === 'string') {
|
||||
fileMatch = [fileMatch];
|
||||
}
|
||||
if (Array.isArray(fileMatch) && typeof url === 'string') {
|
||||
let uri: string = url;
|
||||
if (uri[0] === '.' && uri[1] === '/') {
|
||||
uri = joinPath(extension.extensionUri, uri).toString();
|
||||
}
|
||||
fileMatch = fileMatch.map(fm => {
|
||||
if (fm[0] === '%') {
|
||||
fm = fm.replace(/%APP_SETTINGS_HOME%/, '/User');
|
||||
fm = fm.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine');
|
||||
fm = fm.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces');
|
||||
} else if (!fm.match(/^(\w+:\/\/|\/|!)/)) {
|
||||
fm = '/' + fm;
|
||||
}
|
||||
return fm;
|
||||
});
|
||||
associations.push({ fileMatch, uri });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return associations;
|
||||
}
|
||||
|
||||
function getSettings(): Settings {
|
||||
const httpSettings = workspace.getConfiguration('http');
|
||||
|
||||
const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000;
|
||||
|
||||
const settings: Settings = {
|
||||
http: {
|
||||
proxy: httpSettings.get('proxy'),
|
||||
proxyStrictSSL: httpSettings.get('proxyStrictSSL')
|
||||
},
|
||||
json: {
|
||||
schemas: [],
|
||||
resultLimit
|
||||
}
|
||||
};
|
||||
const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
|
||||
const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri, isMultiRoot?: boolean) => {
|
||||
|
||||
let fileMatchPrefix = undefined;
|
||||
if (folderUri && isMultiRoot) {
|
||||
fileMatchPrefix = folderUri.toString();
|
||||
if (fileMatchPrefix[fileMatchPrefix.length - 1] === '/') {
|
||||
fileMatchPrefix = fileMatchPrefix.substr(0, fileMatchPrefix.length - 1);
|
||||
}
|
||||
}
|
||||
for (const setting of schemaSettings) {
|
||||
const url = getSchemaId(setting, folderUri);
|
||||
if (!url) {
|
||||
continue;
|
||||
}
|
||||
let schemaSetting = schemaSettingsById[url];
|
||||
if (!schemaSetting) {
|
||||
schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] };
|
||||
settings.json!.schemas!.push(schemaSetting);
|
||||
}
|
||||
const fileMatches = setting.fileMatch;
|
||||
if (Array.isArray(fileMatches)) {
|
||||
const resultingFileMatches = schemaSetting.fileMatch || [];
|
||||
schemaSetting.fileMatch = resultingFileMatches;
|
||||
const addMatch = (pattern: string) => { // filter duplicates
|
||||
if (resultingFileMatches.indexOf(pattern) === -1) {
|
||||
resultingFileMatches.push(pattern);
|
||||
}
|
||||
};
|
||||
for (const fileMatch of fileMatches) {
|
||||
if (fileMatchPrefix) {
|
||||
if (fileMatch[0] === '/') {
|
||||
addMatch(fileMatchPrefix + fileMatch);
|
||||
addMatch(fileMatchPrefix + '/*' + fileMatch);
|
||||
} else {
|
||||
addMatch(fileMatchPrefix + '/' + fileMatch);
|
||||
addMatch(fileMatchPrefix + '/*/' + fileMatch);
|
||||
}
|
||||
} else {
|
||||
addMatch(fileMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (setting.schema && !schemaSetting.schema) {
|
||||
schemaSetting.schema = setting.schema;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const folders = workspace.workspaceFolders;
|
||||
|
||||
// merge global and folder settings. Qualify all file matches with the folder path.
|
||||
const globalSettings = workspace.getConfiguration('json', null).get<JSONSchemaSettings[]>('schemas');
|
||||
if (Array.isArray(globalSettings)) {
|
||||
if (!folders) {
|
||||
collectSchemaSettings(globalSettings);
|
||||
}
|
||||
}
|
||||
if (folders) {
|
||||
const isMultiRoot = folders.length > 1;
|
||||
for (const folder of folders) {
|
||||
const folderUri = folder.uri;
|
||||
|
||||
const schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect<JSONSchemaSettings[]>('schemas');
|
||||
|
||||
const folderSchemas = schemaConfigInfo!.workspaceFolderValue;
|
||||
if (Array.isArray(folderSchemas)) {
|
||||
collectSchemaSettings(folderSchemas, folderUri, isMultiRoot);
|
||||
}
|
||||
if (Array.isArray(globalSettings)) {
|
||||
collectSchemaSettings(globalSettings, folderUri, isMultiRoot);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri): string | undefined {
|
||||
let url = schema.url;
|
||||
if (!url) {
|
||||
if (schema.schema) {
|
||||
url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`;
|
||||
}
|
||||
} else if (folderUri && (url[0] === '.' || url[0] === '/')) {
|
||||
url = joinPath(folderUri, url).toString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function isThenable<T>(obj: ProviderResult<T>): obj is Thenable<T> {
|
||||
return obj && (<any>obj)['then'];
|
||||
}
|
||||
|
||||
function updateMarkdownString(h: MarkdownString): MarkdownString {
|
||||
const n = new MarkdownString(h.value, true);
|
||||
n.isTrusted = h.isTrusted;
|
||||
return n;
|
||||
}
|
||||
|
||||
function isSchemaResolveError(d: Diagnostic) {
|
||||
return d.code === /* SchemaResolveError */ 0x300;
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext } from 'vscode';
|
||||
import { startClient, LanguageClientConstructor } from '../jsonClient';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
|
||||
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { RequestService } from '../requests';
|
||||
|
||||
let telemetry: TelemetryReporter | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
|
||||
const clientPackageJSON = getPackageInfo(context);
|
||||
telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey);
|
||||
|
||||
const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`;
|
||||
const serverModule = context.asAbsolutePath(serverMain);
|
||||
|
||||
// The debug options for the server
|
||||
const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
|
||||
|
||||
// If the extension is launch in debug mode the debug server options are use
|
||||
// Otherwise the run options are used
|
||||
const serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
|
||||
};
|
||||
|
||||
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
|
||||
return new LanguageClient(id, name, serverOptions, clientOptions);
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { http: getHTTPRequestService(), telemetry });
|
||||
}
|
||||
|
||||
export function deactivate(): Promise<any> {
|
||||
return telemetry ? telemetry.dispose() : Promise.resolve(null);
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
main: string;
|
||||
}
|
||||
|
||||
function getPackageInfo(context: ExtensionContext): IPackageInfo {
|
||||
const location = context.asAbsolutePath('./package.json');
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(location).toString());
|
||||
} catch (e) {
|
||||
console.log(`Problems reading ${location}: ${e}`);
|
||||
return { name: '', version: '', aiKey: '', main: '' };
|
||||
}
|
||||
}
|
||||
|
||||
function getHTTPRequestService(): RequestService {
|
||||
return {
|
||||
getContent(uri: string, _encoding?: string) {
|
||||
const headers = { 'Accept-Encoding': 'gzip, deflate' };
|
||||
return xhr({ url: uri, followRedirects: 5, headers }).then(response => {
|
||||
return response.responseText;
|
||||
}, (error: XHRResponse) => {
|
||||
return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString());
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Uri } from 'vscode';
|
||||
|
||||
export interface RequestService {
|
||||
getContent(uri: string, encoding?: string): Thenable<string>;
|
||||
}
|
||||
|
||||
export function getScheme(uri: string) {
|
||||
return uri.substr(0, uri.indexOf(':'));
|
||||
}
|
||||
|
||||
export function dirname(uri: string) {
|
||||
const lastIndexOfSlash = uri.lastIndexOf('/');
|
||||
return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
|
||||
}
|
||||
|
||||
export function basename(uri: string) {
|
||||
const lastIndexOfSlash = uri.lastIndexOf('/');
|
||||
return uri.substr(lastIndexOfSlash + 1);
|
||||
}
|
||||
|
||||
const Slash = '/'.charCodeAt(0);
|
||||
const Dot = '.'.charCodeAt(0);
|
||||
|
||||
export function isAbsolutePath(path: string) {
|
||||
return path.charCodeAt(0) === Slash;
|
||||
}
|
||||
|
||||
export function resolvePath(uri: Uri, path: string): Uri {
|
||||
if (isAbsolutePath(path)) {
|
||||
return uri.with({ path: normalizePath(path.split('/')) });
|
||||
}
|
||||
return joinPath(uri, path);
|
||||
}
|
||||
|
||||
export function normalizePath(parts: string[]): string {
|
||||
const newParts: string[] = [];
|
||||
for (const part of parts) {
|
||||
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
|
||||
// ignore
|
||||
} else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
|
||||
newParts.pop();
|
||||
} else {
|
||||
newParts.push(part);
|
||||
}
|
||||
}
|
||||
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
|
||||
newParts.push('');
|
||||
}
|
||||
let res = newParts.join('/');
|
||||
if (parts[0].length === 0) {
|
||||
res = '/' + res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
export function joinPath(uri: Uri, ...paths: string[]): Uri {
|
||||
const parts = uri.path.split('/');
|
||||
for (let path of paths) {
|
||||
parts.push(...path.split('/'));
|
||||
}
|
||||
return uri.with({ path: normalizePath(parts) });
|
||||
}
|
7
lib/vscode/extensions/json-language-features/client/src/typings/ref.d.ts
vendored
Normal file
7
lib/vscode/extensions/json-language-features/client/src/typings/ref.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path="../../../../../src/vs/vscode.proposed.d.ts" />
|
@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Return a hash value for an object.
|
||||
*/
|
||||
export function hash(obj: any, hashVal = 0): number {
|
||||
switch (typeof obj) {
|
||||
case 'object':
|
||||
if (obj === null) {
|
||||
return numberHash(349, hashVal);
|
||||
} else if (Array.isArray(obj)) {
|
||||
return arrayHash(obj, hashVal);
|
||||
}
|
||||
return objectHash(obj, hashVal);
|
||||
case 'string':
|
||||
return stringHash(obj, hashVal);
|
||||
case 'boolean':
|
||||
return booleanHash(obj, hashVal);
|
||||
case 'number':
|
||||
return numberHash(obj, hashVal);
|
||||
case 'undefined':
|
||||
return 937 * 31;
|
||||
default:
|
||||
return numberHash(obj, 617);
|
||||
}
|
||||
}
|
||||
|
||||
function numberHash(val: number, initialHashVal: number): number {
|
||||
return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
|
||||
}
|
||||
|
||||
function booleanHash(b: boolean, initialHashVal: number): number {
|
||||
return numberHash(b ? 433 : 863, initialHashVal);
|
||||
}
|
||||
|
||||
function stringHash(s: string, hashVal: number) {
|
||||
hashVal = numberHash(149417, hashVal);
|
||||
for (let i = 0, length = s.length; i < length; i++) {
|
||||
hashVal = numberHash(s.charCodeAt(i), hashVal);
|
||||
}
|
||||
return hashVal;
|
||||
}
|
||||
|
||||
function arrayHash(arr: any[], initialHashVal: number): number {
|
||||
initialHashVal = numberHash(104579, initialHashVal);
|
||||
return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal);
|
||||
}
|
||||
|
||||
function objectHash(obj: any, initialHashVal: number): number {
|
||||
initialHashVal = numberHash(181387, initialHashVal);
|
||||
return Object.keys(obj).sort().reduce((hashVal, key) => {
|
||||
hashVal = stringHash(key, hashVal);
|
||||
return hash(obj[key], hashVal);
|
||||
}, initialHashVal);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user