mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-11 23:40:34 -05:00
Fixes #3494: [snippets] [debt] don't allow snippet syntax in default values
This commit is contained in:
@@ -5,42 +5,43 @@
|
||||
'use strict';
|
||||
|
||||
export interface IJSONSchema {
|
||||
id?:string;
|
||||
id?: string;
|
||||
$schema?: string;
|
||||
type?:any;
|
||||
title?:string;
|
||||
default?:any;
|
||||
definitions?:IJSONSchemaMap;
|
||||
description?:string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
default?: any;
|
||||
definitions?: IJSONSchemaMap;
|
||||
description?: string;
|
||||
properties?: IJSONSchemaMap;
|
||||
patternProperties?:IJSONSchemaMap;
|
||||
additionalProperties?:any;
|
||||
minProperties?:number;
|
||||
maxProperties?:number;
|
||||
dependencies?:any;
|
||||
items?:any;
|
||||
minItems?:number;
|
||||
maxItems?:number;
|
||||
uniqueItems?:boolean;
|
||||
additionalItems?:boolean;
|
||||
pattern?:string;
|
||||
minLength?:number;
|
||||
maxLength?:number;
|
||||
minimum?:number;
|
||||
maximum?:number;
|
||||
exclusiveMinimum?:boolean;
|
||||
exclusiveMaximum?:boolean;
|
||||
multipleOf?:number;
|
||||
required?:string[];
|
||||
$ref?:string;
|
||||
anyOf?:IJSONSchema[];
|
||||
allOf?:IJSONSchema[];
|
||||
oneOf?:IJSONSchema[];
|
||||
not?:IJSONSchema;
|
||||
enum?:any[];
|
||||
patternProperties?: IJSONSchemaMap;
|
||||
additionalProperties?: boolean | IJSONSchema;
|
||||
minProperties?: number;
|
||||
maxProperties?: number;
|
||||
dependencies?: IJSONSchemaMap | string[];
|
||||
items?: IJSONSchema | IJSONSchema[];
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
additionalItems?: boolean;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
exclusiveMinimum?: boolean;
|
||||
exclusiveMaximum?: boolean;
|
||||
multipleOf?: number;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
anyOf?: IJSONSchema[];
|
||||
allOf?: IJSONSchema[];
|
||||
oneOf?: IJSONSchema[];
|
||||
not?: IJSONSchema;
|
||||
enum?: any[];
|
||||
format?: string;
|
||||
|
||||
errorMessage?:string; // VS code internal
|
||||
|
||||
defaultSnippets?: { label?: string; description?: string; body: any; }[]; // VSCode extension
|
||||
errorMessage?: string; // VSCode extension
|
||||
}
|
||||
|
||||
export interface IJSONSchemaMap {
|
||||
|
||||
@@ -172,7 +172,7 @@ export class JSONCompletion {
|
||||
if (schemaProperties) {
|
||||
Object.keys(schemaProperties).forEach((key: string) => {
|
||||
let propertySchema = schemaProperties[key];
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getSnippetForProperty(key, propertySchema, addValue, isLast), documentation: propertySchema.description || '' });
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getTextForProperty(key, propertySchema, addValue, isLast), documentation: propertySchema.description || '' });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ export class JSONCompletion {
|
||||
let collectSuggestionsForSimilarObject = (obj: Parser.ObjectASTNode) => {
|
||||
obj.properties.forEach((p) => {
|
||||
let key = p.key.value;
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getSnippetForSimilarProperty(key, p.value), documentation: '' });
|
||||
collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getTextForSimilarProperty(key, p.value), documentation: '' });
|
||||
});
|
||||
};
|
||||
if (node.parent) {
|
||||
@@ -206,14 +206,14 @@ export class JSONCompletion {
|
||||
}
|
||||
}
|
||||
if (!currentKey && currentWord.length > 0) {
|
||||
collector.add({ kind: CompletionItemKind.Property, label: JSON.stringify(currentWord), insertText: this.getSnippetForProperty(currentWord, null, true, isLast), documentation: '' });
|
||||
collector.add({ kind: CompletionItemKind.Property, label: this.getLabelForValue(currentWord), insertText: this.getTextForProperty(currentWord, null, true, isLast), documentation: '' });
|
||||
}
|
||||
}
|
||||
|
||||
private getSchemaLessValueSuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, document: ITextDocument, collector: ISuggestionsCollector): void {
|
||||
let collectSuggestionsForValues = (value: Parser.ASTNode) => {
|
||||
if (!value.contains(offset)) {
|
||||
let content = this.getMatchingSnippet(value, document);
|
||||
let content = this.getTextForMatchingNode(value, document);
|
||||
collector.add({ kind: this.getSuggestionKind(value.type), label: content, insertText: content, documentation: '' });
|
||||
}
|
||||
if (value.type === 'boolean') {
|
||||
@@ -347,6 +347,16 @@ export class JSONCompletion {
|
||||
detail: nls.localize('json.suggest.default', 'Default value'),
|
||||
});
|
||||
}
|
||||
if (Array.isArray(schema.defaultSnippets)) {
|
||||
schema.defaultSnippets.forEach(s => {
|
||||
collector.add({
|
||||
kind: CompletionItemKind.Snippet,
|
||||
label: this.getLabelForSnippetValue(s.body),
|
||||
insertText: this.getTextForSnippetValue(s.body)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(schema.allOf)) {
|
||||
schema.allOf.forEach((s) => this.addDefaultSuggestion(s, collector));
|
||||
}
|
||||
@@ -360,7 +370,15 @@ export class JSONCompletion {
|
||||
|
||||
private getLabelForValue(value: any): string {
|
||||
let label = JSON.stringify(value);
|
||||
label = label.replace('{{', '').replace('}}', '');
|
||||
if (label.length > 57) {
|
||||
return label.substr(0, 57).trim() + '...';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
private getLabelForSnippetValue(value: any): string {
|
||||
let label = JSON.stringify(value);
|
||||
label = label.replace(/\{\{|\}\}/g, '');
|
||||
if (label.length > 57) {
|
||||
return label.substr(0, 57).trim() + '...';
|
||||
}
|
||||
@@ -368,11 +386,17 @@ export class JSONCompletion {
|
||||
}
|
||||
|
||||
private getTextForValue(value: any): string {
|
||||
var text = JSON.stringify(value, null, '\t');
|
||||
text = text.replace(/[\\\{\}]/g, '\\$&');
|
||||
return text;
|
||||
}
|
||||
|
||||
private getTextForSnippetValue(value: any): string {
|
||||
return JSON.stringify(value, null, '\t');
|
||||
}
|
||||
|
||||
private getSnippetForValue(value: any): string {
|
||||
let snippet = JSON.stringify(value, null, '\t');
|
||||
private getTextForEnumValue(value: any): string {
|
||||
let snippet = this.getTextForValue(value);
|
||||
switch (typeof value) {
|
||||
case 'object':
|
||||
if (value === null) {
|
||||
@@ -405,7 +429,7 @@ export class JSONCompletion {
|
||||
}
|
||||
|
||||
|
||||
private getMatchingSnippet(node: Parser.ASTNode, document: ITextDocument): string {
|
||||
private getTextForMatchingNode(node: Parser.ASTNode, document: ITextDocument): string {
|
||||
switch (node.type) {
|
||||
case 'array':
|
||||
return '[]';
|
||||
@@ -417,9 +441,9 @@ export class JSONCompletion {
|
||||
}
|
||||
}
|
||||
|
||||
private getSnippetForProperty(key: string, propertySchema: JsonSchema.IJSONSchema, addValue: boolean, isLast: boolean): string {
|
||||
private getTextForProperty(key: string, propertySchema: JsonSchema.IJSONSchema, addValue: boolean, isLast: boolean): string {
|
||||
|
||||
let result = '"' + key + '"';
|
||||
let result = this.getTextForValue(key);
|
||||
if (!addValue) {
|
||||
return result;
|
||||
}
|
||||
@@ -428,9 +452,9 @@ export class JSONCompletion {
|
||||
if (propertySchema) {
|
||||
let defaultVal = propertySchema.default;
|
||||
if (typeof defaultVal !== 'undefined') {
|
||||
result = result + this.getSnippetForValue(defaultVal);
|
||||
result = result + this.getTextForEnumValue(defaultVal);
|
||||
} else if (propertySchema.enum && propertySchema.enum.length > 0) {
|
||||
result = result + this.getSnippetForValue(propertySchema.enum[0]);
|
||||
result = result + this.getTextForEnumValue(propertySchema.enum[0]);
|
||||
} else {
|
||||
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
|
||||
switch (type) {
|
||||
@@ -465,8 +489,8 @@ export class JSONCompletion {
|
||||
return result;
|
||||
}
|
||||
|
||||
private getSnippetForSimilarProperty(key: string, templateValue: Parser.ASTNode): string {
|
||||
return '"' + key + '"';
|
||||
private getTextForSimilarProperty(key: string, templateValue: Parser.ASTNode): string {
|
||||
return this.getTextForValue(key);
|
||||
}
|
||||
|
||||
private getCurrentWord(document: ITextDocument, offset: number) {
|
||||
|
||||
@@ -103,7 +103,7 @@ export class ASTNode {
|
||||
if ((<string[]>schema.type).indexOf(this.type) === -1) {
|
||||
validationResult.warnings.push({
|
||||
location: { start: this.start, end: this.end },
|
||||
message: nls.localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', schema.type.join(', '))
|
||||
message: nls.localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', (<string[]>schema.type).join(', '))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -277,14 +277,14 @@ export class ArrayASTNode extends ASTNode {
|
||||
super.validate(schema, validationResult, matchingSchemas, offset);
|
||||
|
||||
if (Array.isArray(schema.items)) {
|
||||
let subSchemas: JsonSchema.IJSONSchema[] = schema.items;
|
||||
let subSchemas = <JsonSchema.IJSONSchema[]> schema.items;
|
||||
subSchemas.forEach((subSchema, index) => {
|
||||
let itemValidationResult = new ValidationResult();
|
||||
let item = this.items[index];
|
||||
if (item) {
|
||||
item.validate(subSchema, itemValidationResult, matchingSchemas, offset);
|
||||
validationResult.mergePropertyMatch(itemValidationResult);
|
||||
} else if (this.items.length >= schema.items.length) {
|
||||
} else if (this.items.length >= subSchemas.length) {
|
||||
validationResult.propertiesValueMatches++;
|
||||
}
|
||||
});
|
||||
@@ -294,8 +294,8 @@ export class ArrayASTNode extends ASTNode {
|
||||
location: { start: this.start, end: this.end },
|
||||
message: nls.localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer', subSchemas.length)
|
||||
});
|
||||
} else if (this.items.length >= schema.items.length) {
|
||||
validationResult.propertiesValueMatches += (this.items.length - schema.items.length);
|
||||
} else if (this.items.length >= subSchemas.length) {
|
||||
validationResult.propertiesValueMatches += (this.items.length - subSchemas.length);
|
||||
}
|
||||
}
|
||||
else if (schema.items) {
|
||||
|
||||
@@ -24,7 +24,7 @@ suite('JSON Completion', () => {
|
||||
var matches = completions.filter(function(completion: CompletionItem) {
|
||||
return completion.label === label && (!documentation || completion.documentation === documentation);
|
||||
});
|
||||
assert.equal(matches.length, 1, label + " should only existing once");
|
||||
assert.equal(matches.length, 1, label + " should only existing once: Actual: " + completions.map(c => c.label).join(', '));
|
||||
if (document && resultText) {
|
||||
assert.equal(applyEdits(document, [ matches[0].textEdit ]), resultText);
|
||||
}
|
||||
@@ -51,8 +51,6 @@ suite('JSON Completion', () => {
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
|
||||
test('Complete keys no schema', function(testDone) {
|
||||
Promise.all([
|
||||
testSuggestionsFor('[ { "name": "John", "age": 44 }, { /**/ }', '/**/', null, result => {
|
||||
@@ -478,4 +476,44 @@ suite('JSON Completion', () => {
|
||||
}),
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
});
|
||||
|
||||
test('Escaping no schema', function(testDone) {
|
||||
Promise.all([
|
||||
testSuggestionsFor('[ { "\\\\{{}}": "John" }, { "/**/" }', '/**/', null, result => {
|
||||
assertSuggestion(result, '\\{{}}');
|
||||
}),
|
||||
testSuggestionsFor('[ { "\\\\{{}}": "John" }, { /**/ }', '/**/', null, (result, document) => {
|
||||
assertSuggestion(result, '\\{{}}', null, document, '[ { "\\\\{{}}": "John" }, { "\\\\\\\\\\{\\{\\}\\}"/**/ }');
|
||||
}),
|
||||
testSuggestionsFor('[ { "name": "\\{" }, { "name": /**/ }', '/**/', null, result => {
|
||||
assertSuggestion(result, '"\\{"');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
test('Escaping with schema', function(testDone) {
|
||||
var schema: JsonSchema.IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'{\\}': {
|
||||
default: "{\\}",
|
||||
defaultSnippets: [ { body: "{{var}}"} ],
|
||||
enum: ['John{\\}']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
testSuggestionsFor('{ /**/ }', '/**/', schema, (result, document) => {
|
||||
assertSuggestion(result, '{\\}', null, document, '{ "\\{\\\\\\\\\\}": "{{\\{\\\\\\\\\\}}}"/**/ }');
|
||||
}),
|
||||
testSuggestionsFor('{ "{\\\\}": /**/ }', '/**/', schema, (result, document) => {
|
||||
assertSuggestion(result, '"{\\\\}"', null, document, '{ "{\\\\}": "\\{\\\\\\\\\\}"/**/ }');
|
||||
assertSuggestion(result, '"John{\\\\}"', null, document, '{ "{\\\\}": "John\\{\\\\\\\\\\}"/**/ }');
|
||||
assertSuggestion(result, '"var"', null, document, '{ "{\\\\}": "{{var}}"/**/ }');
|
||||
})
|
||||
]).then(() => testDone(), (error) => testDone(error));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -5,41 +5,43 @@
|
||||
'use strict';
|
||||
|
||||
export interface IJSONSchema {
|
||||
id?:string;
|
||||
id?: string;
|
||||
$schema?: string;
|
||||
type?:any;
|
||||
title?:string;
|
||||
default?:any;
|
||||
definitions?:IJSONSchemaMap;
|
||||
description?:string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
default?: any;
|
||||
definitions?: IJSONSchemaMap;
|
||||
description?: string;
|
||||
properties?: IJSONSchemaMap;
|
||||
patternProperties?:IJSONSchemaMap;
|
||||
additionalProperties?:any;
|
||||
minProperties?:number;
|
||||
maxProperties?:number;
|
||||
dependencies?:any;
|
||||
items?:any;
|
||||
minItems?:number;
|
||||
maxItems?:number;
|
||||
uniqueItems?:boolean;
|
||||
additionalItems?:boolean;
|
||||
pattern?:string;
|
||||
errorMessage?: string;
|
||||
minLength?:number;
|
||||
maxLength?:number;
|
||||
minimum?:number;
|
||||
maximum?:number;
|
||||
exclusiveMinimum?:boolean;
|
||||
exclusiveMaximum?:boolean;
|
||||
multipleOf?:number;
|
||||
required?:string[];
|
||||
$ref?:string;
|
||||
anyOf?:IJSONSchema[];
|
||||
allOf?:IJSONSchema[];
|
||||
oneOf?:IJSONSchema[];
|
||||
not?:IJSONSchema;
|
||||
enum?:any[];
|
||||
patternProperties?: IJSONSchemaMap;
|
||||
additionalProperties?: boolean | IJSONSchema;
|
||||
minProperties?: number;
|
||||
maxProperties?: number;
|
||||
dependencies?: IJSONSchemaMap | string[];
|
||||
items?: IJSONSchema | IJSONSchema[];
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
uniqueItems?: boolean;
|
||||
additionalItems?: boolean;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
exclusiveMinimum?: boolean;
|
||||
exclusiveMaximum?: boolean;
|
||||
multipleOf?: number;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
anyOf?: IJSONSchema[];
|
||||
allOf?: IJSONSchema[];
|
||||
oneOf?: IJSONSchema[];
|
||||
not?: IJSONSchema;
|
||||
enum?: any[];
|
||||
format?: string;
|
||||
|
||||
defaultSnippets?: { label?: string; description?: string; body: any; }[]; // VSCode extension
|
||||
errorMessage?: string; // VSCode extension
|
||||
}
|
||||
|
||||
export interface IJSONSchemaMap {
|
||||
|
||||
@@ -38,10 +38,10 @@ interface IModeConfigurationMap { [modeId: string]: any; }
|
||||
let languagesExtPoint = PluginsRegistry.registerExtensionPoint<ILanguageExtensionPoint[]>('languages', {
|
||||
description: nls.localize('vscode.extension.contributes.languages', 'Contributes language declarations.'),
|
||||
type: 'array',
|
||||
default: [{ id: '', aliases: [], extensions: [] }],
|
||||
defaultSnippets: [{ body: [{ id: '', aliases: [], extensions: [] }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { id: '', extensions: [] },
|
||||
defaultSnippets: [{ body: { id: '', extensions: [] } }],
|
||||
properties: {
|
||||
id: {
|
||||
description: nls.localize('vscode.extension.contributes.languages.id', 'ID of the language.'),
|
||||
@@ -73,7 +73,7 @@ let languagesExtPoint = PluginsRegistry.registerExtensionPoint<ILanguageExtensio
|
||||
description: nls.localize('vscode.extension.contributes.languages.filenamePatterns', 'File name glob patterns associated to the language.'),
|
||||
default: ['bar*foo.txt'],
|
||||
type: 'array',
|
||||
item: {
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -69,11 +69,6 @@ export class CodeSnippet implements ICodeSnippet {
|
||||
}
|
||||
|
||||
private parseTemplate(template: string): void {
|
||||
if (template === '"`~!@#$%^&*()-=+[{]}\\\\|;:\'\\",.<>/?"') {
|
||||
this.lines.push(template);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var placeHoldersMap: collections.IStringDictionary<IPlaceHolder> = {};
|
||||
var i: number, len: number, j: number, lenJ: number, templateLines = template.split('\n');
|
||||
|
||||
@@ -33,10 +33,10 @@ export function snippetUpdated(modeId: string, filePath: string): TPromise<void>
|
||||
let snippetsExtensionPoint = PluginsRegistry.registerExtensionPoint<ITMSnippetsExtensionPoint[]>('snippets', {
|
||||
description: nls.localize('vscode.extension.contributes.snippets', 'Contributes textmate snippets.'),
|
||||
type: 'array',
|
||||
default: [{ language: '', path: '' }],
|
||||
defaultSnippets: [ { body: [{ language: '', path: '' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { language: '{{id}}', path: './snippets/{{id}}.json.'},
|
||||
defaultSnippets: [ { body: { language: '{{id}}', path: './snippets/{{id}}.json.'} }] ,
|
||||
properties: {
|
||||
language: {
|
||||
description: nls.localize('vscode.extension.contributes.snippets-language', 'Language id for which this snippet is contributed to.'),
|
||||
|
||||
@@ -23,10 +23,10 @@ export interface ITMSyntaxExtensionPoint {
|
||||
let grammarsExtPoint = PluginsRegistry.registerExtensionPoint<ITMSyntaxExtensionPoint[]>('grammars', {
|
||||
description: nls.localize('vscode.extension.contributes.grammars', 'Contributes textmate tokenizers.'),
|
||||
type: 'array',
|
||||
default: [{ id: '', extensions: [] }],
|
||||
defaultSnippets: [ { body: [{ id: '', extensions: [] }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { language: '{{id}}', scopeName: 'source.{{id}}', path: './syntaxes/{{id}}.tmLanguage.'},
|
||||
defaultSnippets: [ { body: { language: '{{id}}', scopeName: 'source.{{id}}', path: './syntaxes/{{id}}.tmLanguage.'} }],
|
||||
properties: {
|
||||
language: {
|
||||
description: nls.localize('vscode.extension.contributes.grammars.language', 'Language id for which this syntax is contributed to.'),
|
||||
|
||||
@@ -30,7 +30,7 @@ configurationRegistry.registerConfiguration({
|
||||
'description': nls.localize('jsonConfiguration.schemas', "Associate schemas to JSON files in the current project"),
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'default': { fileMatch: [ '{{/myfile}}' ], url: '{{schemaURL}}' },
|
||||
'defaultSnippets': [{ body: { fileMatch: [ '{{/myfile}}' ], url: '{{schemaURL}}' } }],
|
||||
'properties': {
|
||||
'url': {
|
||||
'type': 'string',
|
||||
|
||||
@@ -179,7 +179,7 @@ export class JSONIntellisense {
|
||||
if (schemaProperties) {
|
||||
Object.keys(schemaProperties).forEach((key: string) => {
|
||||
var propertySchema = schemaProperties[key];
|
||||
collector.add({ type: 'property', label: key, codeSnippet: this.getSnippetForProperty(key, propertySchema, addValue, isLast), documentationLabel: propertySchema.description || '' });
|
||||
collector.add({ type: 'property', label: key, codeSnippet: this.getTextForProperty(key, propertySchema, addValue, isLast), documentationLabel: propertySchema.description || '' });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export class JSONIntellisense {
|
||||
var collectSuggestionsForSimilarObject = (obj: Parser.ObjectASTNode) => {
|
||||
obj.properties.forEach((p) => {
|
||||
var key = p.key.value;
|
||||
collector.add({ type: 'property', label: key, codeSnippet: this.getSnippetForSimilarProperty(key, p.value), documentationLabel: '' });
|
||||
collector.add({ type: 'property', label: key, codeSnippet: this.getTextForSimilarProperty(key, p.value), documentationLabel: '' });
|
||||
});
|
||||
};
|
||||
if (node.parent.type === 'property') {
|
||||
@@ -214,7 +214,7 @@ export class JSONIntellisense {
|
||||
|
||||
public getSchemaLessValueSuggestions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, modelMirror: EditorCommon.IMirrorModel, collector: JsonWorker.ISuggestionsCollector): void {
|
||||
var collectSuggestionsForValues = (value: Parser.ASTNode) => {
|
||||
var content = this.getMatchingSnippet(value, modelMirror);
|
||||
var content = this.getTextForMatchingNode(value, modelMirror);
|
||||
collector.add({ type: this.getSuggestionType(value.type), label: content, codeSnippet: content, documentationLabel: '' });
|
||||
if (value.type === 'boolean') {
|
||||
this.addBooleanSuggestion(!value.getValue(), collector);
|
||||
@@ -301,12 +301,12 @@ export class JSONIntellisense {
|
||||
}
|
||||
|
||||
private addBooleanSuggestion(value: boolean, collector: JsonWorker.ISuggestionsCollector): void {
|
||||
collector.add({ type: this.getSuggestionType('boolean'), label: value ? 'true' : 'false', codeSnippet: this.getSnippetForValue(value), documentationLabel: '' });
|
||||
collector.add({ type: this.getSuggestionType('boolean'), label: value ? 'true' : 'false', codeSnippet: this.getTextForEnumValue(value), documentationLabel: '' });
|
||||
}
|
||||
|
||||
private addEnumSuggestion(schema: JsonSchema.IJSONSchema, collector: JsonWorker.ISuggestionsCollector): void {
|
||||
if (Array.isArray(schema.enum)) {
|
||||
schema.enum.forEach((enm) => collector.add({ type: this.getSuggestionType(schema.type), label: this.getLabelForValue(enm), codeSnippet: this.getSnippetForValue(enm), documentationLabel: '' }));
|
||||
schema.enum.forEach((enm) => collector.add({ type: this.getSuggestionType(schema.type), label: this.getLabelForValue(enm), codeSnippet: this.getTextForEnumValue(enm), documentationLabel: '' }));
|
||||
} else if (schema.type === 'boolean') {
|
||||
this.addBooleanSuggestion(true, collector);
|
||||
this.addBooleanSuggestion(false, collector);
|
||||
@@ -327,10 +327,19 @@ export class JSONIntellisense {
|
||||
collector.add({
|
||||
type: this.getSuggestionType(schema.type),
|
||||
label: this.getLabelForValue(schema.default),
|
||||
codeSnippet: this.getSnippetForValue(schema.default),
|
||||
codeSnippet: this.getTextForValue(schema.default),
|
||||
typeLabel: nls.localize('json.suggest.default', 'Default value'),
|
||||
});
|
||||
}
|
||||
if (Array.isArray(schema.defaultSnippets)) {
|
||||
schema.defaultSnippets.forEach(s => {
|
||||
collector.add({
|
||||
type: 'snippet',
|
||||
label: this.getLabelForSnippetValue(s.body),
|
||||
codeSnippet: this.getTextForSnippetValue(s.body)
|
||||
});
|
||||
});
|
||||
}
|
||||
if (Array.isArray(schema.allOf)) {
|
||||
schema.allOf.forEach((s) => this.addDefaultSuggestion(s, collector));
|
||||
}
|
||||
@@ -351,7 +360,26 @@ export class JSONIntellisense {
|
||||
return label;
|
||||
}
|
||||
|
||||
private getSnippetForValue(value: any) : string {
|
||||
private getLabelForSnippetValue(value: any): string {
|
||||
let label = JSON.stringify(value);
|
||||
label = label.replace(/\{\{|\}\}/g, '');
|
||||
if (label.length > 57) {
|
||||
return label.substr(0, 57).trim() + '...';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
private getTextForValue(value: any): string {
|
||||
var text = JSON.stringify(value, null, '\t');
|
||||
text = text.replace(/[\\\{\}]/g, '\\$&');
|
||||
return text;
|
||||
}
|
||||
|
||||
private getTextForSnippetValue(value: any): string {
|
||||
return JSON.stringify(value, null, '\t');
|
||||
}
|
||||
|
||||
private getTextForEnumValue(value: any) : string {
|
||||
var snippet = JSON.stringify(value, null, '\t');
|
||||
switch (typeof value) {
|
||||
case 'object':
|
||||
@@ -386,7 +414,7 @@ export class JSONIntellisense {
|
||||
}
|
||||
|
||||
|
||||
private getMatchingSnippet(node: Parser.ASTNode, modelMirror: EditorCommon.IMirrorModel): string {
|
||||
private getTextForMatchingNode(node: Parser.ASTNode, modelMirror: EditorCommon.IMirrorModel): string {
|
||||
switch (node.type) {
|
||||
case 'array':
|
||||
return '[]';
|
||||
@@ -398,9 +426,9 @@ export class JSONIntellisense {
|
||||
}
|
||||
}
|
||||
|
||||
private getSnippetForProperty(key: string, propertySchema: JsonSchema.IJSONSchema, addValue:boolean, isLast: boolean): string {
|
||||
private getTextForProperty(key: string, propertySchema: JsonSchema.IJSONSchema, addValue:boolean, isLast: boolean): string {
|
||||
|
||||
var result = '"' + key + '"';
|
||||
let result = this.getTextForValue(key);
|
||||
if (!addValue) {
|
||||
return result;
|
||||
}
|
||||
@@ -408,9 +436,9 @@ export class JSONIntellisense {
|
||||
|
||||
var defaultVal = propertySchema.default;
|
||||
if (!Types.isUndefined(defaultVal)) {
|
||||
result = result + this.getSnippetForValue(defaultVal);
|
||||
result = result + this.getTextForEnumValue(defaultVal);
|
||||
} else if (propertySchema.enum && propertySchema.enum.length > 0) {
|
||||
result = result + this.getSnippetForValue(propertySchema.enum[0]);
|
||||
result = result + this.getTextForEnumValue(propertySchema.enum[0]);
|
||||
} else {
|
||||
switch (propertySchema.type) {
|
||||
case 'boolean':
|
||||
@@ -442,7 +470,7 @@ export class JSONIntellisense {
|
||||
return result;
|
||||
}
|
||||
|
||||
private getSnippetForSimilarProperty(key: string, templateValue: Parser.ASTNode): string {
|
||||
return '"' + key + '"';
|
||||
private getTextForSimilarProperty(key: string, templateValue: Parser.ASTNode): string {
|
||||
return this.getTextForValue(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +102,10 @@ export class ASTNode {
|
||||
}
|
||||
|
||||
if (Array.isArray(schema.type)) {
|
||||
if (Arrays.contains(schema.type, this.type) === false) {
|
||||
if (Arrays.contains(<string[]> schema.type, this.type) === false) {
|
||||
validationResult.warnings.push({
|
||||
location: { start: this.start, end: this.end },
|
||||
message: nls.localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', schema.type.join())
|
||||
message: nls.localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}', (<string[]> schema.type).join())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -279,14 +279,14 @@ export class ArrayASTNode extends ASTNode {
|
||||
super.validate(schema, validationResult, matchingSchemas, offset);
|
||||
|
||||
if (Array.isArray(schema.items)) {
|
||||
var subSchemas:JsonSchema.IJSONSchema[] = schema.items;
|
||||
var subSchemas = <JsonSchema.IJSONSchema[]> schema.items;
|
||||
subSchemas.forEach((subSchema, index) => {
|
||||
var itemValidationResult = new ValidationResult();
|
||||
var item = this.items[index];
|
||||
if (item) {
|
||||
item.validate(subSchema, itemValidationResult, matchingSchemas, offset);
|
||||
validationResult.mergePropertyMatch(itemValidationResult);
|
||||
} else if (this.items.length >= schema.items.length) {
|
||||
} else if (this.items.length >= subSchemas.length) {
|
||||
validationResult.propertiesValueMatches++;
|
||||
}
|
||||
});
|
||||
@@ -296,8 +296,8 @@ export class ArrayASTNode extends ASTNode {
|
||||
location: { start: this.start, end: this.end },
|
||||
message: nls.localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer', subSchemas.length)
|
||||
});
|
||||
} else if (this.items.length >= schema.items.length) {
|
||||
validationResult.propertiesValueMatches += (this.items.length - schema.items.length);
|
||||
} else if (this.items.length >= subSchemas.length) {
|
||||
validationResult.propertiesValueMatches += (this.items.length - subSchemas.length);
|
||||
}
|
||||
}
|
||||
else if (schema.items) {
|
||||
@@ -361,7 +361,7 @@ export class NumberASTNode extends ASTNode {
|
||||
|
||||
// work around type validation in the base class
|
||||
var typeIsInteger = false;
|
||||
if (schema.type === 'integer' || (Array.isArray(schema.type) && Arrays.contains(schema.type, 'integer'))) {
|
||||
if (schema.type === 'integer' || (Array.isArray(schema.type) && Arrays.contains(<string[]> schema.type, 'integer'))) {
|
||||
typeIsInteger = true;
|
||||
}
|
||||
if (typeIsInteger && this.isInteger === true) {
|
||||
|
||||
@@ -39,7 +39,7 @@ export interface IConfigurationRegistry {
|
||||
export interface IConfigurationNode {
|
||||
id?: string;
|
||||
order?: number;
|
||||
type?: string;
|
||||
type?: string | string[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
default?: any;
|
||||
@@ -96,7 +96,7 @@ platform.Registry.add(Extensions.Configuration, configurationRegistry);
|
||||
let configurationExtPoint = PluginsRegistry.registerExtensionPoint<IConfigurationNode>('configuration', {
|
||||
description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'),
|
||||
type: 'object',
|
||||
default: { title: '', properties: {} },
|
||||
defaultSnippets: [{ body: { title: '', properties: {} } }],
|
||||
properties: {
|
||||
title: {
|
||||
description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'),
|
||||
|
||||
@@ -221,8 +221,9 @@ function addIndent(str: string): string {
|
||||
return str.split('\n').join('\n\t');
|
||||
}
|
||||
|
||||
function getDefaultValue(type: string): any {
|
||||
switch (type) {
|
||||
function getDefaultValue(type: string | string[]): any {
|
||||
let t = Array.isArray(type) ? (<string[]> type)[0] : <string> type;
|
||||
switch (t) {
|
||||
case 'boolean':
|
||||
return false;
|
||||
case 'integer':
|
||||
|
||||
@@ -23,10 +23,10 @@ let schemaRegistry = <JSONContributionRegistry.IJSONContributionRegistry>Registr
|
||||
let configurationExtPoint = PluginsRegistry.registerExtensionPoint<IJSONValidationExtensionPoint[]>('jsonValidation', {
|
||||
description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'),
|
||||
type: 'array',
|
||||
default: [{ fileMatch: '{{file.json}}', url: '{{url}}' }],
|
||||
defaultSnippets: [{ body: [{ fileMatch: '{{file.json}}', url: '{{url}}' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { fileMatch: '{{file.json}}', url: '{{url}}' },
|
||||
defaultSnippets: [{ body: { fileMatch: '{{file.json}}', url: '{{url}}' } }],
|
||||
properties: {
|
||||
fileMatch: {
|
||||
type: 'string',
|
||||
|
||||
@@ -30,10 +30,10 @@ import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickO
|
||||
export var debuggersExtPoint = pluginsRegistry.PluginsRegistry.registerExtensionPoint<debug.IRawAdapter[]>('debuggers', {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers', 'Contributes debug adapters.'),
|
||||
type: 'array',
|
||||
default: [{ type: '', extensions: [] }],
|
||||
defaultSnippets: [{ body: [{ type: '', extensions: [] }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { type: '', program: '', runtime: '', enableBreakpointsFor: { languageIds: [ '' ] } },
|
||||
defaultSnippets: [{ body: { type: '', program: '', runtime: '', enableBreakpointsFor: { languageIds: [ '' ] } } }],
|
||||
properties: {
|
||||
type: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.type', "Unique identifier for this debug adapter."),
|
||||
@@ -205,7 +205,7 @@ export class ConfigurationManager {
|
||||
this.adapters.forEach(adapter => {
|
||||
const schemaAttributes = adapter.getSchemaAttributes();
|
||||
if (schemaAttributes) {
|
||||
schema.properties['configurations'].items.oneOf.push(...schemaAttributes);
|
||||
(<IJSONSchema> schema.properties['configurations'].items).oneOf.push(...schemaAttributes);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,7 +132,10 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenSn
|
||||
let schemaId = 'vscode://schemas/snippets';
|
||||
let schema : IJSONSchema = {
|
||||
'id': schemaId,
|
||||
'default': { '{{snippetName}}': { 'prefix': '{{prefix}}', 'body': '{{snippet}}', 'description': '{{description}}' } },
|
||||
'defaultSnippets': [{
|
||||
'label': nls.localize('snippetSchema.json.default', "Empty snippet"),
|
||||
'body': { '{{snippetName}}': { 'prefix': '{{prefix}}', 'body': '{{snippet}}', 'description': '{{description}}' } }
|
||||
}],
|
||||
'type': 'object',
|
||||
'description': nls.localize('snippetSchema.json', 'User snippet configuration'),
|
||||
'additionalProperties': {
|
||||
|
||||
@@ -292,7 +292,7 @@ let schema : IJSONSchema = {
|
||||
'items': {
|
||||
'required': ['key'],
|
||||
'type': 'object',
|
||||
'default': { 'key': '{{_}}', 'command': '{{_}}', 'when': '{{_}}' },
|
||||
'defaultSnippets': [ { 'body': { 'key': '{{_}}', 'command': '{{_}}', 'when': '{{_}}' } }],
|
||||
'properties': {
|
||||
'key': {
|
||||
'type': 'string',
|
||||
|
||||
@@ -23,10 +23,10 @@ let defaultBaseTheme = Themes.getBaseThemeId(DEFAULT_THEME_ID);
|
||||
let themesExtPoint = PluginsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>('themes', {
|
||||
description: nls.localize('vscode.extension.contributes.themes', 'Contributes textmate color themes.'),
|
||||
type: 'array',
|
||||
default: [{ label: '{{label}}', uiTheme: 'vs-dark', path: './themes/{{id}}.tmTheme.' }],
|
||||
defaultSnippets: [{ body: [{ label: '{{label}}', uiTheme: 'vs-dark', path: './themes/{{id}}.tmTheme.' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
default: { label: '{{label}}', uiTheme: 'vs-dark', path: './themes/{{id}}.tmTheme.' },
|
||||
defaultSnippets: [{ body: { label: '{{label}}', uiTheme: 'vs-dark', path: './themes/{{id}}.tmTheme.' } }],
|
||||
properties: {
|
||||
label: {
|
||||
description: nls.localize('vscode.extension.contributes.themes.label', 'Label of the color theme as shown in the UI.'),
|
||||
|
||||
Reference in New Issue
Block a user