mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
move more into config-editing extension (for #20049)
This commit is contained in:
parent
24b6838819
commit
4d9ee74935
|
@ -44,7 +44,7 @@ function registerKeybindingsCompletions(): vscode.Disposable {
|
|||
if (location.path[1] === 'command') {
|
||||
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
return commands.then(ids => ids.map(id => newCompletionItem(JSON.stringify(id), range)));
|
||||
return commands.then(ids => ids.map(id => newSimpleCompletionItem(JSON.stringify(id), range)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -61,13 +61,107 @@ function registerSettingsCompletions(): vscode.Disposable {
|
|||
if (location.path[0] === 'window.title') {
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
|
||||
completions.push(newCompletionItem('${activeEditorName}', range, localize('activeEditorName', "e.g. myFile.txt")));
|
||||
completions.push(newCompletionItem('${activeFilePath}', range, localize('activeFilePath', "e.g. /Users/Development/myProject/myFile.txt")));
|
||||
completions.push(newCompletionItem('${rootName}', range, localize('rootName', "e.g. myProject")));
|
||||
completions.push(newCompletionItem('${rootPath}', range, localize('rootPath', "e.g. /Users/Development/myProject")));
|
||||
completions.push(newCompletionItem('${appName}', range, localize('appName', "e.g. VS Code")));
|
||||
completions.push(newCompletionItem('${dirty}', range, localize('dirty', "a dirty indicator if the active editor is dirty")));
|
||||
completions.push(newCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values")));
|
||||
completions.push(newSimpleCompletionItem('${activeEditorName}', range, localize('activeEditorName', "e.g. myFile.txt")));
|
||||
completions.push(newSimpleCompletionItem('${activeFilePath}', range, localize('activeFilePath', "e.g. /Users/Development/myProject/myFile.txt")));
|
||||
completions.push(newSimpleCompletionItem('${rootName}', range, localize('rootName', "e.g. myProject")));
|
||||
completions.push(newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "e.g. /Users/Development/myProject")));
|
||||
completions.push(newSimpleCompletionItem('${appName}', range, localize('appName', "e.g. VS Code")));
|
||||
completions.push(newSimpleCompletionItem('${dirty}', range, localize('dirty', "a dirty indicator if the active editor is dirty")));
|
||||
completions.push(newSimpleCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values")));
|
||||
}
|
||||
|
||||
// files.association
|
||||
else if (location.path[0] === 'files.associations') {
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
|
||||
// Key
|
||||
if (location.path.length === 1) {
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('assocLabelFile', "Files with Extension"),
|
||||
documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier."),
|
||||
snippet: '"*.${1:extension}": "${2:language}"',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('assocLabelPath', "Files with Path"),
|
||||
documentation: localize('assocDescriptionPath', "Map all files matching the absolute path glob pattern in their path to the language with the given identifier."),
|
||||
snippet: '"/${1:path to file}/*.${2:extension}": "${3:language}"',
|
||||
range
|
||||
}));
|
||||
}
|
||||
|
||||
// Value
|
||||
else {
|
||||
return vscode.languages.getLanguages().then(languages => {
|
||||
return Promise.resolve(languages.map(l => {
|
||||
return newSimpleCompletionItem(l, range);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// files.exclude, search.exclude
|
||||
else if (location.path[0] === 'files.exclude' || location.path[0] === 'search.exclude') {
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
|
||||
// Key
|
||||
if (location.path.length === 1) {
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('fileLabel', "Files by Extension"),
|
||||
documentation: localize('fileDescription', "Match all files of a specific file extension."),
|
||||
snippet: '"**/*.${1:extension}": true',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('filesLabel', "Files with Multiple Extensions"),
|
||||
documentation: localize('filesDescription', "Match all files with any of the file extensions."),
|
||||
snippet: '"**/*.{ext1,ext2,ext3}": true',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('derivedLabel', "Files with Siblings by Name"),
|
||||
documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension."),
|
||||
snippet: '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('topFolderLabel', "Folder by Name (Top Level)"),
|
||||
documentation: localize('topFolderDescription', "Match a top level folder with a specific name."),
|
||||
snippet: '"${1:name}": true',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"),
|
||||
documentation: localize('topFoldersDescription', "Match multiple top level folders."),
|
||||
snippet: '"{folder1,folder2,folder3}": true',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('folderLabel', "Folder by Name (Any Location)"),
|
||||
documentation: localize('folderDescription', "Match a folder with a specific name in any location."),
|
||||
snippet: '"**/${1:name}": true',
|
||||
range
|
||||
}));
|
||||
}
|
||||
|
||||
// Value
|
||||
else {
|
||||
completions.push(newSimpleCompletionItem('false', range, localize('falseDescription', "Disable the pattern.")));
|
||||
completions.push(newSimpleCompletionItem('true', range, localize('trueDescription', "Enable the pattern.")));
|
||||
|
||||
completions.push(newSnippetCompletionItem({
|
||||
label: localize('derivedLabel', "Files with Siblings by Name"),
|
||||
documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension."),
|
||||
snippet: '{ "when": "$(basename).${1:extension}" }',
|
||||
range
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(completions);
|
||||
|
@ -75,7 +169,7 @@ function registerSettingsCompletions(): vscode.Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
function newCompletionItem(text: string, range: vscode.Range, description?: string): vscode.CompletionItem {
|
||||
function newSimpleCompletionItem(text: string, range: vscode.Range, description?: string): vscode.CompletionItem {
|
||||
const item = new vscode.CompletionItem(text);
|
||||
item.kind = vscode.CompletionItemKind.Value;
|
||||
item.detail = description;
|
||||
|
@ -87,6 +181,16 @@ function newCompletionItem(text: string, range: vscode.Range, description?: stri
|
|||
return item;
|
||||
}
|
||||
|
||||
function newSnippetCompletionItem(o: { label: string; documentation?: string; snippet: string; range: vscode.Range; }): vscode.CompletionItem {
|
||||
const item = new vscode.CompletionItem(o.label);
|
||||
item.kind = vscode.CompletionItemKind.Value;
|
||||
item.documentation = o.documentation;
|
||||
item.insertText = new vscode.SnippetString(o.snippet);
|
||||
item.range = o.range;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void {
|
||||
if (!editor || path.basename(editor.document.fileName) !== 'launch.json') {
|
||||
return;
|
||||
|
|
|
@ -36,65 +36,58 @@ export function activate(context: ExtensionContext) {
|
|||
let packageInfo = getPackageInfo(context);
|
||||
let telemetryReporter: TelemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
|
||||
// Resolve language ids to pass around as initialization data
|
||||
languages.getLanguages().then(languageIds => {
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'jsonServerMain.js'));
|
||||
// The debug options for the server
|
||||
let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] };
|
||||
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'jsonServerMain.js'));
|
||||
// The debug options for the server
|
||||
let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] };
|
||||
// If the extension is launch in debug mode the debug server options are use
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
|
||||
};
|
||||
|
||||
// If the extension is launch in debug mode the debug server options are use
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
|
||||
};
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
// Register the server for json documents
|
||||
documentSelector: ['json'],
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'json' to the server
|
||||
configurationSection: ['json', 'http.proxy', 'http.proxyStrictSSL'],
|
||||
fileEvents: workspace.createFileSystemWatcher('**/*.json')
|
||||
}
|
||||
};
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
// Register the server for json documents
|
||||
documentSelector: ['json'],
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'json' to the server
|
||||
configurationSection: ['json', 'http.proxy', 'http.proxyStrictSSL'],
|
||||
fileEvents: workspace.createFileSystemWatcher('**/*.json')
|
||||
},
|
||||
initializationOptions: {
|
||||
languageIds
|
||||
// Create the language client and start the client.
|
||||
let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions);
|
||||
let disposable = client.start();
|
||||
client.onReady().then(() => {
|
||||
client.onTelemetry(e => {
|
||||
if (telemetryReporter) {
|
||||
telemetryReporter.sendTelemetryEvent(e.key, e.data);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions);
|
||||
let disposable = client.start();
|
||||
client.onReady().then(() => {
|
||||
client.onTelemetry(e => {
|
||||
if (telemetryReporter) {
|
||||
telemetryReporter.sendTelemetryEvent(e.key, e.data);
|
||||
}
|
||||
});
|
||||
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
let uri = Uri.parse(uriPath);
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
return doc.getText();
|
||||
}, error => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
|
||||
});
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
|
||||
languages.setLanguageConfiguration('json', {
|
||||
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
let uri = Uri.parse(uriPath);
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
return doc.getText();
|
||||
}, error => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
|
||||
});
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
|
||||
languages.setLanguageConfiguration('json', {
|
||||
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ import * as URL from 'url';
|
|||
import Strings = require('./utils/strings');
|
||||
import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice';
|
||||
import { ProjectJSONContribution } from './jsoncontributions/projectJSONContribution';
|
||||
import { GlobPatternContribution } from './jsoncontributions/globPatternContribution';
|
||||
import { FileAssociationContribution } from './jsoncontributions/fileAssociationContribution';
|
||||
import { getLanguageModelCache } from './languageModelCache';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
|
@ -53,16 +51,12 @@ documents.listen(connection);
|
|||
let clientSnippetSupport = false;
|
||||
let clientDynamicRegisterSupport = false;
|
||||
|
||||
const filesAssociationContribution = new FileAssociationContribution();
|
||||
|
||||
// After the server has started the client sends an initilize request. The server receives
|
||||
// in the passed params the rootPath of the workspace plus the client capabilities.
|
||||
let workspaceRoot: URI;
|
||||
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
workspaceRoot = URI.parse(params.rootPath);
|
||||
if (params.initializationOptions) {
|
||||
filesAssociationContribution.setLanguageIds(params.initializationOptions.languageIds);
|
||||
}
|
||||
|
||||
function hasClientCapability(...keys: string[]) {
|
||||
let c = params.capabilities;
|
||||
for (let i = 0; c && i < keys.length; i++) {
|
||||
|
@ -126,9 +120,7 @@ let languageService = getLanguageService({
|
|||
schemaRequestService,
|
||||
workspaceContext,
|
||||
contributions: [
|
||||
new ProjectJSONContribution(),
|
||||
new GlobPatternContribution(),
|
||||
filesAssociationContribution
|
||||
new ProjectJSONContribution()
|
||||
]
|
||||
});
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MarkedString, CompletionItemKind, CompletionItem, InsertTextFormat } from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import { JSONWorkerContribution, JSONPath, CompletionsCollector } from 'vscode-json-languageservice';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
let globProperties: CompletionItem[] = [
|
||||
{ kind: CompletionItemKind.Value, label: localize('assocLabelFile', "Files with Extension"), insertText: '"*.${1:extension}": "${2:language}"', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('assocLabelPath', "Files with Path"), insertText: '"/${1:path to file}/*.${2:extension}": "${3:language}"', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('assocDescriptionPath', "Map all files matching the absolute path glob pattern in their path to the language with the given identifier.") }
|
||||
];
|
||||
|
||||
export class FileAssociationContribution implements JSONWorkerContribution {
|
||||
private languageIds: string[];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public setLanguageIds(ids: string[]): void {
|
||||
this.languageIds = ids;
|
||||
}
|
||||
|
||||
private isSettingsFile(resource: string): boolean {
|
||||
return Strings.endsWith(resource, '/settings.json');
|
||||
}
|
||||
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && location[0] === 'files.associations') {
|
||||
globProperties.forEach(e => {
|
||||
result.add(e);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && location[0] === 'files.associations') {
|
||||
this.languageIds.forEach(l => {
|
||||
result.add({
|
||||
kind: CompletionItemKind.Value,
|
||||
label: l,
|
||||
insertText: JSON.stringify('${1:' + l + '}'),
|
||||
insertTextFormat: InsertTextFormat.Snippet,
|
||||
filterText: JSON.stringify(l)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MarkedString, CompletionItemKind, CompletionItem, InsertTextFormat } from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import { JSONWorkerContribution, JSONPath, CompletionsCollector } from 'vscode-json-languageservice';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
let globProperties: CompletionItem[] = [
|
||||
{ kind: CompletionItemKind.Value, label: localize('fileLabel', "Files by Extension"), insertText: '"**/*.${1:extension}": true', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('fileDescription', "Match all files of a specific file extension.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('filesLabel', "Files with Multiple Extensions"), insertText: '"**/*.{ext1,ext2,ext3}": true', documentation: localize('filesDescription', "Match all files with any of the file extensions.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('derivedLabel', "Files with Siblings by Name"), insertText: '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('topFolderLabel', "Folder by Name (Top Level)"), insertText: '"${1:name}": true', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('topFolderDescription', "Match a top level folder with a specific name.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"), insertText: '"{folder1,folder2,folder3}": true', documentation: localize('topFoldersDescription', "Match multiple top level folders.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('folderLabel', "Folder by Name (Any Location)"), insertText: '"**/${1:name}": true', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('folderDescription', "Match a folder with a specific name in any location.") },
|
||||
];
|
||||
|
||||
let globValues: CompletionItem[] = [
|
||||
{ kind: CompletionItemKind.Value, label: localize('trueLabel', "true"), filterText: 'true', insertText: 'true', documentation: localize('trueDescription', "Enable the pattern.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('falseLabel', "false"), filterText: 'false', insertText: 'false', documentation: localize('falseDescription', "Disable the pattern.") },
|
||||
{ kind: CompletionItemKind.Value, label: localize('derivedLabel', "Files with Siblings by Name"), insertText: '{ "when": "$(basename).${1:extension}" }', insertTextFormat: InsertTextFormat.Snippet, documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension.") }
|
||||
];
|
||||
|
||||
export class GlobPatternContribution implements JSONWorkerContribution {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
private isSettingsFile(resource: string): boolean {
|
||||
return Strings.endsWith(resource, '/settings.json');
|
||||
}
|
||||
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && ((location[0] === 'files.exclude') || (location[0] === 'search.exclude'))) {
|
||||
globProperties.forEach(e => {
|
||||
result.add(e);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isSettingsFile(resource) && location.length === 1 && ((location[0] === 'files.exclude') || (location[0] === 'search.exclude'))) {
|
||||
globValues.forEach(e => {
|
||||
result.add(e);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue