mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-26 21:28:04 +00:00
[json] run serverless
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 } 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 = context.asAbsolutePath('server/dist/browser/jsonServerMain.js');
|
||||
try {
|
||||
const worker = new Worker(serverMain);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -16,13 +12,13 @@ import {
|
||||
ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString,
|
||||
} from 'vscode';
|
||||
import {
|
||||
LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType,
|
||||
LanguageClientOptions, RequestType, NotificationType,
|
||||
DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams,
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient
|
||||
} from 'vscode-languageclient';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
|
||||
import { hash } from './utils/hash';
|
||||
import { RequestService, joinPath } from './requests';
|
||||
|
||||
namespace VSCodeContentRequest {
|
||||
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
|
||||
@@ -53,13 +49,6 @@ namespace ResultLimitReachedNotification {
|
||||
export const type: NotificationType<string, any> = new NotificationType('json/resultLimitReached');
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
main: string;
|
||||
}
|
||||
|
||||
interface Settings {
|
||||
json?: {
|
||||
schemas?: JSONSchemaSettings[];
|
||||
@@ -84,29 +73,27 @@ namespace SettingIds {
|
||||
export const maxItemsComputed = 'json.maxItemsComputed';
|
||||
}
|
||||
|
||||
let telemetryReporter: TelemetryReporter | undefined;
|
||||
export interface TelemetryReporter {
|
||||
sendTelemetryEvent(eventName: string, properties?: {
|
||||
[key: string]: string;
|
||||
}, measurements?: {
|
||||
[key: string]: number;
|
||||
}): void;
|
||||
}
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
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;
|
||||
|
||||
let clientPackageJSON = getPackageInfo(context);
|
||||
telemetryReporter = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey);
|
||||
|
||||
const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/jsonServerMain`;
|
||||
const serverModule = context.asAbsolutePath(serverMain);
|
||||
|
||||
// The debug options for the server
|
||||
const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] };
|
||||
|
||||
// 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 documentSelector = ['json', 'jsonc'];
|
||||
|
||||
@@ -203,7 +190,7 @@ export function activate(context: ExtensionContext) {
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
const client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions);
|
||||
const client = newLanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), clientOptions);
|
||||
client.registerProposedFeatures();
|
||||
|
||||
const disposable = client.start();
|
||||
@@ -225,24 +212,15 @@ export function activate(context: ExtensionContext) {
|
||||
return Promise.reject(new ResponseError(2, error.toString()));
|
||||
});
|
||||
} else if (schemaDownloadEnabled) {
|
||||
if (telemetryReporter && uri.authority === 'schema.management.azure.com') {
|
||||
if (runtime.telemetry && uri.authority === 'schema.management.azure.com') {
|
||||
/* __GDPR__
|
||||
"json.schema" : {
|
||||
"schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
telemetryReporter.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
|
||||
runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
|
||||
}
|
||||
const headers = { 'Accept-Encoding': 'gzip, deflate' };
|
||||
return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => {
|
||||
return response.responseText;
|
||||
}, (error: XHRResponse) => {
|
||||
let extraInfo = error.responseText || error.toString();
|
||||
if (extraInfo.length > 256) {
|
||||
extraInfo = `${extraInfo.substr(0, 256)}...`;
|
||||
}
|
||||
return Promise.reject(new ResponseError(error.status, getErrorStatusDescription(error.status) + '\n' + extraInfo));
|
||||
});
|
||||
return runtime.http.getContent(uriPath);
|
||||
} else {
|
||||
return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)));
|
||||
}
|
||||
@@ -340,7 +318,7 @@ export function activate(context: ExtensionContext) {
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.logFailedRequest(DocumentRangeFormattingRequest.type, error);
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
@@ -375,12 +353,6 @@ export function activate(context: ExtensionContext) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function deactivate(): Promise<any> {
|
||||
return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null);
|
||||
}
|
||||
|
||||
function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] {
|
||||
const associations: ISchemaAssociation[] = [];
|
||||
extensions.all.forEach(extension => {
|
||||
@@ -393,9 +365,10 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
|
||||
if (typeof fileMatch === 'string') {
|
||||
fileMatch = [fileMatch];
|
||||
}
|
||||
if (Array.isArray(fileMatch) && url) {
|
||||
if (url[0] === '.' && url[1] === '/') {
|
||||
url = Uri.file(path.join(extension.extensionPath, url)).toString();
|
||||
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] === '%') {
|
||||
@@ -407,7 +380,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
|
||||
}
|
||||
return fm;
|
||||
});
|
||||
associations.push({ fileMatch, uri: url });
|
||||
associations.push({ fileMatch, uri });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -509,28 +482,18 @@ function getSettings(): Settings {
|
||||
return settings;
|
||||
}
|
||||
|
||||
function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri) {
|
||||
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 = folderUri.with({ path: path.posix.join(folderUri.path, url) }).toString();
|
||||
url = joinPath(folderUri, url).toString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
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 isThenable<T>(obj: ProviderResult<T>): obj is Thenable<T> {
|
||||
return obj && (<any>obj)['then'];
|
||||
}
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
68
extensions/json-language-features/client/src/requests.ts
Normal file
68
extensions/json-language-features/client/src/requests.ts
Normal file
@@ -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) });
|
||||
}
|
||||
Reference in New Issue
Block a user