Skip to content

Commit

Permalink
Merge pull request #230 from redhat-developer/more-default-snippet-fixes
Browse files Browse the repository at this point in the history
Default snippet fixes for arrays
  • Loading branch information
JPinkney committed Jan 31, 2020
2 parents 5a7349c + c069ce8 commit 189621c
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 40 deletions.
43 changes: 19 additions & 24 deletions src/languageservice/services/yamlCompletion.ts
Expand Up @@ -236,7 +236,7 @@ export class YAMLCompletion extends JSONCompletion {
newLineFirst: false,
indentFirstObject: false,
shouldIndentWithTab: false
}, false);
});
const schemaProperties = s.schema.properties;
if (schemaProperties) {
Object.keys(schemaProperties).forEach((key: string) => {
Expand Down Expand Up @@ -282,13 +282,13 @@ export class YAMLCompletion extends JSONCompletion {
newLineFirst: false,
indentFirstObject: false,
shouldIndentWithTab: true
}, false);
}, 1);
} else {
this.collectDefaultSnippets(s.schema, separatorAfter, collector, {
newLineFirst: false,
indentFirstObject: true,
shouldIndentWithTab: false
}, false);
}, 1);
}
}
});
Expand Down Expand Up @@ -433,13 +433,13 @@ export class YAMLCompletion extends JSONCompletion {
newLineFirst: true,
indentFirstObject: true,
shouldIndentWithTab: true
}, schema.type === 'array');
});
if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items)) {
this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1);
}
}

private collectDefaultSnippets(schema: JSONSchema, separatorAfter: string, collector: CompletionsCollector, settings: StringifySettings, isArray: boolean, arrayDepth = 0) {
private collectDefaultSnippets(schema: JSONSchema, separatorAfter: string, collector: CompletionsCollector, settings: StringifySettings, arrayDepth = 0) {
if (Array.isArray(schema.defaultSnippets)) {
schema.defaultSnippets.forEach(s => {
let type = schema.type;
Expand All @@ -449,11 +449,19 @@ export class YAMLCompletion extends JSONCompletion {
let filterText: string;
if (isDefined(value)) {
let type = schema.type;
for (let i = arrayDepth; i > 0; i--) {
value = [value];
type = 'array';
if (arrayDepth === 0 && type === 'array') {
// We know that a - isn't present yet so we need to add one
const fixedObj = { };
Object.keys(value).forEach((val, index) => {
if (index === 0 && !val.startsWith('-')) {
fixedObj[`- ${val}`] = value[val];
} else {
fixedObj[` ${val}`] = value[val];
}
});
value = fixedObj;
}
insertText = this.getInsertTextForSnippetValue(value, separatorAfter, settings, isArray);
insertText = this.getInsertTextForSnippetValue(value, separatorAfter, settings);
label = label || this.getLabelForSnippetValue(value);
} else if (typeof s.bodyText === 'string') {
let prefix = '', suffix = '', indent = '';
Expand All @@ -480,7 +488,7 @@ export class YAMLCompletion extends JSONCompletion {
}

// tslint:disable-next-line:no-any
private getInsertTextForSnippetValue(value: any, separatorAfter: string, settings: StringifySettings, isArray?: boolean, depth?: number): string {
private getInsertTextForSnippetValue(value: any, separatorAfter: string, settings: StringifySettings, depth?: number): string {
// tslint:disable-next-line:no-any
const replacer = (value: any) => {
if (typeof value === 'string') {
Expand All @@ -490,19 +498,6 @@ export class YAMLCompletion extends JSONCompletion {
}
return value;
};
// If it is an array then we need to manually indent the keys
// of that array item
if (isArray && typeof value === 'object' && value !== null) {
const fixedObj = { };
Object.keys(value).forEach((val, index) => {
if (index === 0 && !val.startsWith('-')) {
fixedObj[`- ${val}`] = value[val];
} else {
fixedObj[` ${val}`] = value[val];
}
});
value = fixedObj;
}
return stringifyObject(value, '', replacer, settings, depth) + separatorAfter;
}

Expand Down Expand Up @@ -680,7 +675,7 @@ export class YAMLCompletion extends JSONCompletion {
newLineFirst: true,
indentFirstObject: false,
shouldIndentWithTab: false
}, false, 1);
}, 1);
}
}
nValueProposals += propertySchema.defaultSnippets.length;
Expand Down
28 changes: 23 additions & 5 deletions src/languageservice/utils/json.ts
Expand Up @@ -11,7 +11,7 @@ export interface StringifySettings {
}

// tslint:disable-next-line: no-any
export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val: any) => string, settings: StringifySettings, depth = 0): string {
export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val: any) => string, settings: StringifySettings, depth = 0, consecutiveArrays = 0): string {
if (obj !== null && typeof obj === 'object') {

/**
Expand All @@ -21,12 +21,17 @@ export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val
*/
let newIndent = ((depth === 0 && settings.shouldIndentWithTab) || depth > 0) ? (indent + ' ') : '';
if (Array.isArray(obj)) {
consecutiveArrays += 1;
if (obj.length === 0) {
return '';
}
let result = ((depth === 0 && settings.newLineFirst) || depth > 0) ? '\n' : '';
let result = '';
for (let i = 0; i < obj.length; i++) {
result += newIndent + stringifyObject(obj[i], indent, stringifyLiteral, settings, depth += 1);
let pseudoObj = obj[i];
if (!Array.isArray(obj[i])) {
pseudoObj = preprendToObject(obj[i], consecutiveArrays);
}
result += newIndent + stringifyObject(pseudoObj, indent, stringifyLiteral, settings, depth += 1, consecutiveArrays);
if (i < obj.length - 1) {
result += '\n';
}
Expand All @@ -44,9 +49,9 @@ export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val

// The first child of an array needs to be treated specially, otherwise identations will be off
if (depth === 0 && i === 0 && !settings.indentFirstObject) {
result += indent + key + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral, settings, depth += 1);
result += indent + key + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral, settings, depth += 1, 0);
} else {
result += newIndent + key + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral, settings, depth += 1);
result += newIndent + key + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral, settings, depth += 1, 0);
}
if (i < keys.length - 1) {
result += '\n';
Expand All @@ -58,3 +63,16 @@ export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val
}
return stringifyLiteral(obj);
}

function preprendToObject(obj, consecutiveArrays) {
const newObj = { };
for (let i = 0; i < Object.keys(obj).length; i++) {
const key = Object.keys(obj)[i];
if (i === 0) {
newObj['- '.repeat(consecutiveArrays) + key] = obj[key];
} else {
newObj[' '.repeat(consecutiveArrays) + key] = obj[key];
}
}
return newObj;
}
67 changes: 56 additions & 11 deletions test/defaultSnippets.test.ts
Expand Up @@ -43,6 +43,26 @@ suite('Default Snippet Tests', () => {
}).then(done, done);
});

it('Snippet in array schema should autocomplete with - if none is present', done => {
const content = 'array:\n ';
const completion = parseSetup(content, 9);
completion.then(function (result) {
assert.equal(result.items.length, 1);
assert.equal(result.items[0].insertText, '- item1: $1\n item2: $2');
assert.equal(result.items[0].label, 'My array item');
}).then(done, done);
});

it('Snippet in array schema should autocomplete on same line as array', done => {
const content = 'array: ';
const completion = parseSetup(content, 7);
completion.then(function (result) {
assert.equal(result.items.length, 1);
assert.equal(result.items[0].insertText, '\n - item1: $1\n item2: $2');
assert.equal(result.items[0].label, 'My array item');
}).then(done, done);
});

it('Snippet in array schema should autocomplete on next line with depth', done => {
const content = 'array:\n - item1:\n - ';
const completion = parseSetup(content, 24);
Expand All @@ -63,14 +83,6 @@ suite('Default Snippet Tests', () => {
}).then(done, done);
});

it('Snippet in array schema should autocomplete on same line as array', done => {
const content = 'array: ';
const completion = parseSetup(content, 7);
completion.then(function (result) {
assert.equal(result.items.length, 1);
}).then(done, done);
});

it('Snippet in object schema should autocomplete on next line ', done => {
const content = 'object:\n ';
const completion = parseSetup(content, 11);
Expand Down Expand Up @@ -130,18 +142,51 @@ suite('Default Snippet Tests', () => {
assert.equal(result.items.length, 1);
assert.equal(result.items[0].label, 'apply-manifests');
// tslint:disable-next-line:max-line-length
assert.equal(result.items[0].insertText, '\n name: $1\n taskRef: \n name: apply-manifests \n resources: \n inputs: \n \n name: source\n resource: $3 \n params: \n \n name: manifest_dir\n value: $2 ');
assert.equal(result.items[0].insertText, '\n name: $1\n taskRef: \n name: apply-manifests \n resources: \n inputs: \n - name: source\n resource: $3 \n params: \n - name: manifest_dir\n value: $2 ');
}).then(done, done);
});

it('Snippet in boolean schema should autocomplete on same line', done => {
const content = 'lon ';
const completion = parseSetup(content, 3);
completion.then(function (result) {
assert.equal(result.items.length, 5);
assert.equal(result.items.length, 6); // This is just checking the total number of snippets in the defaultSnippets.json
assert.equal(result.items[4].label, 'longSnippet');
// tslint:disable-next-line:max-line-length
assert.equal(result.items[4].insertText, 'longSnippet:\n name: $1\n taskRef: \n name: apply-manifests \n resources: \n inputs: \n \n name: source\n resource: $3 \n params: \n \n name: manifest_dir\n value: $2 ');
assert.equal(result.items[4].insertText, 'longSnippet:\n name: $1\n taskRef: \n name: apply-manifests \n resources: \n inputs: \n - name: source\n resource: $3 \n params: \n - name: manifest_dir\n value: $2 ');
}).then(done, done);
});

it('Test array of arrays on properties completion', done => {
const content = 'arrayArrayS ';
const completion = parseSetup(content, 11);
completion.then(function (result) {
assert.equal(result.items.length, 6); // This is just checking the total number of snippets in the defaultSnippets.json
assert.equal(result.items[5].label, 'arrayArraySnippet');
// tslint:disable-next-line:max-line-length
assert.equal(result.items[5].insertText, 'arrayArraySnippet:\n apple: \n - - name: source\n resource: $3 ');
}).then(done, done);
});

it('Test array of arrays on value completion', done => {
const content = 'arrayArraySnippet: ';
const completion = parseSetup(content, 20);
completion.then(function (result) {
assert.equal(result.items.length, 1);
assert.equal(result.items[0].label, 'Array Array Snippet');
// tslint:disable-next-line:max-line-length
assert.equal(result.items[0].insertText, '\n apple: \n - - name: source\n resource: $3 ');
}).then(done, done);
});

it('Test array of arrays on indented completion', done => {
const content = 'arrayArraySnippet:\n ';
const completion = parseSetup(content, 21);
completion.then(function (result) {
assert.equal(result.items.length, 1);
assert.equal(result.items[0].label, 'Array Array Snippet');
// tslint:disable-next-line:max-line-length
assert.equal(result.items[0].insertText, 'apple: \n - - name: source\n resource: $3');
}).then(done, done);
});
});
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/defaultSnippets.json
Expand Up @@ -79,6 +79,25 @@
}
}
]
},
"arrayArraySnippet": {
"type": "object",
"defaultSnippets": [
{
"label": "Array Array Snippet",
"description": "Task",
"body": {
"apple": [
[
{
"name": "source",
"resource": "$3"
}
]
]
}
}
]
}
}
}

0 comments on commit 189621c

Please sign in to comment.