From fae39cd8a03a29ac2f1617003c16b9537b6909cd Mon Sep 17 00:00:00 2001
From: Petr Spacek
Date: Thu, 2 May 2024 11:23:09 +0200
Subject: [PATCH] fix: default value with special chars with anyOf
---
.../services/yamlCompletion.ts | 25 ++++++++++-------
test/autoCompletion.test.ts | 28 +++++++++++++++++++
2 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/src/languageservice/services/yamlCompletion.ts b/src/languageservice/services/yamlCompletion.ts
index 513a23d6..25188cbe 100644
--- a/src/languageservice/services/yamlCompletion.ts
+++ b/src/languageservice/services/yamlCompletion.ts
@@ -1116,7 +1116,7 @@ export class YamlCompletion {
case 'anyOf': {
let value = propertySchema.default || propertySchema.const;
if (value) {
- if (type === 'string') {
+ if (type === 'string' || typeof value === 'string') {
value = convertToStringValue(value);
}
insertText += `${indent}${key}: \${${insertIndex++}:${value}}\n`;
@@ -1230,7 +1230,7 @@ export class YamlCompletion {
case 'string': {
let snippetValue = JSON.stringify(value);
snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes
- snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and }
+ snippetValue = getInsertTextForPlainText(snippetValue); // escape \ and }
if (type === 'string') {
snippetValue = convertToStringValue(snippetValue);
}
@@ -1243,10 +1243,6 @@ export class YamlCompletion {
return this.getInsertTextForValue(value, separatorAfter, type);
}
- private getInsertTextForPlainText(text: string): string {
- return text.replace(/[\\$}]/g, '\\$&'); // escape $, \ and }
- }
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private getInsertTextForValue(value: any, separatorAfter: string, type: string | string[]): string {
if (value === null) {
@@ -1259,13 +1255,13 @@ export class YamlCompletion {
}
case 'number':
case 'boolean':
- return this.getInsertTextForPlainText(value + separatorAfter);
+ return getInsertTextForPlainText(value + separatorAfter);
}
type = Array.isArray(type) ? type[0] : type;
if (type === 'string') {
value = convertToStringValue(value);
}
- return this.getInsertTextForPlainText(value + separatorAfter);
+ return getInsertTextForPlainText(value + separatorAfter);
}
private getInsertTemplateForValue(
@@ -1290,14 +1286,14 @@ export class YamlCompletion {
if (typeof element === 'object') {
valueTemplate = `${this.getInsertTemplateForValue(element, indent + this.indentation, navOrder, separatorAfter)}`;
} else {
- valueTemplate = ` \${${navOrder.index++}:${this.getInsertTextForPlainText(element + separatorAfter)}}\n`;
+ valueTemplate = ` \${${navOrder.index++}:${getInsertTextForPlainText(element + separatorAfter)}}\n`;
}
insertText += `${valueTemplate}`;
}
}
return insertText;
}
- return this.getInsertTextForPlainText(value + separatorAfter);
+ return getInsertTextForPlainText(value + separatorAfter);
}
private addSchemaValueCompletions(
@@ -1669,6 +1665,13 @@ export class YamlCompletion {
}
}
+/**
+ * escape $, \ and }
+ */
+function getInsertTextForPlainText(text: string): string {
+ return text.replace(/[\\$}]/g, '\\$&'); //
+}
+
const isNumberExp = /^\d+$/;
function convertToStringValue(param: unknown): string {
let value: string;
@@ -1681,6 +1684,8 @@ function convertToStringValue(param: unknown): string {
return value;
}
+ value = getInsertTextForPlainText(value); // escape $, \ and }
+
if (value === 'true' || value === 'false' || value === 'null' || isNumberExp.test(value)) {
return `"${value}"`;
}
diff --git a/test/autoCompletion.test.ts b/test/autoCompletion.test.ts
index eba8fa6d..173eb37b 100644
--- a/test/autoCompletion.test.ts
+++ b/test/autoCompletion.test.ts
@@ -931,6 +931,34 @@ describe('Auto Completion Tests', () => {
);
});
+ it('Autocompletion should escape $ in defaultValue in anyOf', async () => {
+ schemaProvider.addSchema(SCHEMA_ID, {
+ type: 'object',
+ properties: {
+ car: {
+ type: 'object',
+ required: ['engine'],
+ properties: {
+ engine: {
+ anyOf: [
+ {
+ type: 'object',
+ },
+ {
+ type: 'string',
+ },
+ ],
+ default: 'type$1234',
+ },
+ },
+ },
+ },
+ });
+ const content = '';
+ const completion = await parseSetup(content, 0);
+ expect(completion.items.map((i) => i.insertText)).to.deep.equal(['car:\n engine: ${1:type\\$1234}']);
+ });
+
it('Autocompletion should escape colon when indicating map', async () => {
schemaProvider.addSchema(SCHEMA_ID, {
type: 'object',