UNPKG

yaml-language-server

Version:
821 lines (813 loc) 55.5 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Red Hat, Inc. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const sinon = __importStar(require("sinon")); const sinon_chai_1 = __importDefault(require("sinon-chai")); const chai = __importStar(require("chai")); const yamlCodeActions_1 = require("../src/languageservice/services/yamlCodeActions"); const vscode_languageserver_types_1 = require("vscode-languageserver-types"); const testHelper_1 = require("./utils/testHelper"); const verifyError_1 = require("./utils/verifyError"); const commands_1 = require("../src/commands"); const jsonLanguageTypes_1 = require("../src/languageservice/jsonLanguageTypes"); const expect = chai.expect; chai.use(sinon_chai_1.default); const JSON_SCHEMA_LOCAL = 'file://some/path/schema.json'; const JSON_SCHEMA2_LOCAL = 'file://some/path/schema2.json'; describe('CodeActions Tests', () => { const sandbox = sinon.createSandbox(); let clientCapabilities; beforeEach(() => { clientCapabilities = {}; }); afterEach(() => { sandbox.restore(); }); describe('JumpToSchema tests', () => { it('should not provide any actions if there are no diagnostics', () => { const doc = (0, testHelper_1.setupTextDocument)(''); const params = { context: vscode_languageserver_types_1.CodeActionContext.create(undefined), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.undefined; }); it('should provide action if diagnostic has uri for schema', () => { const doc = (0, testHelper_1.setupTextDocument)(''); const diagnostics = [(0, verifyError_1.createDiagnosticWithData)('foo', 0, 0, 0, 0, 1, JSON_SCHEMA_LOCAL, JSON_SCHEMA_LOCAL)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; clientCapabilities.window = { showDocument: { support: true } }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); const codeAction = vscode_languageserver_types_1.CodeAction.create('Jump to schema location (schema.json)', vscode_languageserver_types_1.Command.create('JumpToSchema', commands_1.YamlCommands.JUMP_TO_SCHEMA, JSON_SCHEMA_LOCAL)); codeAction.diagnostics = diagnostics; expect(result[0]).to.deep.equal(codeAction); }); it('should provide multiple action if diagnostic has uri for multiple schemas', () => { const doc = (0, testHelper_1.setupTextDocument)(''); const diagnostics = [ (0, verifyError_1.createDiagnosticWithData)('foo', 0, 0, 0, 0, 1, JSON_SCHEMA_LOCAL, [JSON_SCHEMA_LOCAL, JSON_SCHEMA2_LOCAL]), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; clientCapabilities.window = { showDocument: { support: true } }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); const codeAction = vscode_languageserver_types_1.CodeAction.create('Jump to schema location (schema.json)', vscode_languageserver_types_1.Command.create('JumpToSchema', commands_1.YamlCommands.JUMP_TO_SCHEMA, JSON_SCHEMA_LOCAL)); const codeAction2 = vscode_languageserver_types_1.CodeAction.create('Jump to schema location (schema2.json)', vscode_languageserver_types_1.Command.create('JumpToSchema', commands_1.YamlCommands.JUMP_TO_SCHEMA, JSON_SCHEMA2_LOCAL)); codeAction.diagnostics = diagnostics; codeAction2.diagnostics = diagnostics; expect(result[0]).to.deep.equal(codeAction); expect(result[1]).to.deep.equal(codeAction2); }); }); describe('Convert TAB to Spaces', () => { it('should add "Convert TAB to Spaces" CodeAction', () => { const doc = (0, testHelper_1.setupTextDocument)('foo:\n\t- bar'); const diagnostics = [(0, verifyError_1.createExpectedError)('Tabs are not allowed as indentation', 1, 0, 1, 1, 1, JSON_SCHEMA_LOCAL)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.has.length(2); expect(result[0].title).to.be.equal('Convert Tab to Spaces'); expect(vscode_languageserver_types_1.WorkspaceEdit.is(result[0].edit)).to.be.true; expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(1, 0, 1, 1), ' ')]); }); it('should support current indentation chars settings', () => { const doc = (0, testHelper_1.setupTextDocument)('foo:\n\t- bar'); const diagnostics = [(0, verifyError_1.createExpectedError)('Tabs are not allowed as indentation', 1, 0, 1, 1, 1, JSON_SCHEMA_LOCAL)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); actions.configure({ indentation: ' ' }, 80); const result = actions.getCodeAction(doc, params); expect(result[0].title).to.be.equal('Convert Tab to Spaces'); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(1, 0, 1, 1), ' ')]); }); it('should provide "Convert all Tabs to Spaces"', () => { const doc = (0, testHelper_1.setupTextDocument)('foo:\n\t\t\t- bar\n\t\t'); const diagnostics = [(0, verifyError_1.createExpectedError)('Tabs are not allowed as indentation', 1, 0, 1, 3, 1, JSON_SCHEMA_LOCAL)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result[1].title).to.be.equal('Convert all Tabs to Spaces'); expect(result[1].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(1, 0, 1, 3), ' '), vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(2, 0, 2, 2), ' '), ]); }); }); describe('Remove Unused Anchor', () => { it('should generate proper action', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: &bar bar\n'); const diagnostics = [(0, verifyError_1.createUnusedAnchorDiagnostic)('Unused anchor "&bar"', 0, 5, 0, 9)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result[0].title).to.be.equal('Delete unused anchor: &bar'); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.del(vscode_languageserver_types_1.Range.create(0, 5, 0, 10))]); }); it('should delete all whitespace after unused anchor', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: &bar \tbar\n'); const diagnostics = [(0, verifyError_1.createUnusedAnchorDiagnostic)('Unused anchor "&bar"', 0, 5, 0, 9)]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result[0].title).to.be.equal('Delete unused anchor: &bar'); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.del(vscode_languageserver_types_1.Range.create(0, 5, 0, 13))]); }); }); describe('Convert to Block Style', () => { it(' should generate action to convert flow map to block map ', () => { const yaml = `host: phl-42 datacenter: {location: canada , cab: 15} animals: [dog , cat , mouse] `; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Flow style mapping is forbidden', 1, 12, 1, 39, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'flowMap'), (0, verifyError_1.createExpectedError)('Flow style sequence is forbidden', 2, 9, 2, 27, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'flowSeq'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(2); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(1, 12, 1, 39), `\n location: canada \n cab: 15`), ]); expect(result[1].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(2, 9, 2, 27), `\n - dog \n - cat \n - mouse`), ]); }); }); describe('Map Key Order', () => { it(' should generate action to order a map with incorrect key order', () => { const yaml = '- key 2: v\n key 1: val\n key 5: valu\n key 3: ff'; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "key 2" in mapping', 0, 2, 0, 9, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), (0, verifyError_1.createExpectedError)('Wrong ordering of key "key 5" in mapping', 2, 0, 2, 9, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(2); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 3, 11), `key 1: val\n key 2: v\n key 3: ff\n key 5: valu`), ]); expect(result[1].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 3, 11), `key 1: val\n key 2: v\n key 3: ff\n key 5: valu`), ]); }); it(' should generate action to order nested and block maps', () => { const yaml = '- key 2: v\n key 1: val\n key 5: {b: 1, a: 2}\n '; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "key 2" in mapping', 0, 2, 0, 9, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), (0, verifyError_1.createExpectedError)('Wrong ordering of key "key b" in mapping', 2, 9, 3, 0, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(2); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 3, 0), `key 1: val\n key 2: v\n key 5: {b: 1, a: 2}\n`), ]); expect(result[1].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(2, 9, 2, 21), `{a: 2, b: 1}\n`)]); }); it('should preserve line breaks when ordering block maps without trailing newline', () => { const yaml = `one: child: moo two: child: moo three: child: moo`; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "two" in mapping', 2, 0, 3, 2, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 0, 5, 12), `one: child: moo three: child: moo two: child: moo`), ]); }); it('should preserve line breaks when ordering block maps with trailing newline', () => { const yaml = `one: child: moo two: child: moo three: child: moo `; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "two" in mapping', 2, 0, 3, 2, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 0, 6, 0), `one: child: moo three: child: moo two: child: moo `), ]); }); it('reordered entries should stay one-per-line after reordering', () => { const yaml = `one: child: moo two: child: moo three: child: moo alpha: 1`; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "two" in mapping', 2, 0, 3, 2, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 0, 7, 8), `alpha: 1 one: child: moo three: child: moo two: child: moo`), ]); }); it('should preserve trailing content after a map when ordering keys', () => { const yaml = `one: child: moo two: child: moo three: child: moo alpha: 1 # comment`; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "two" in mapping', 2, 0, 3, 2, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); const edit = result[0].edit.changes[testHelper_1.TEST_URI][0]; expect(edit.range).deep.equal(vscode_languageserver_types_1.Range.create(0, 0, 7, 8)); expect(edit.newText).to.equal(`alpha: 1 one: child: moo three: child: moo two: child: moo`); const updated = yaml.slice(0, doc.offsetAt(edit.range.start)) + edit.newText + yaml.slice(doc.offsetAt(edit.range.end)); expect(updated).to.equal(`alpha: 1 one: child: moo three: child: moo two: child: moo # comment`); }); it(' should generate action to order maps with multi-line strings', () => { const yaml = '- cc: 1\n gg: 2\n aa: >\n some\n text\n vv: 4'; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "key gg" in mapping', 1, 0, 1, 8, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 5, 7), `aa: >\n some\n text\n cc: 1\n gg: 2\n vv: 4`), ]); }); it(' should generate actions when values are missing', () => { const yaml = '- cc: 1\n gg: 2\n aa:'; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "key gg" in mapping', 1, 0, 1, 8, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 2, 5), `aa: cc: 1\n gg: 2`)]); }); it(' should preserve comments', () => { const yaml = '- cc: 1\n gg: 2 #a comment\n aa: 1'; const doc = (0, testHelper_1.setupTextDocument)(yaml); const diagnostics = [ (0, verifyError_1.createExpectedError)('Wrong ordering of key "key gg" in mapping', 1, 0, 1, 8, vscode_languageserver_types_1.DiagnosticSeverity.Error, 'YAML', 'mapKeyOrder'), ]; const params = { context: vscode_languageserver_types_1.CodeActionContext.create(diagnostics), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.be.not.empty; expect(result).to.have.lengthOf(1); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 2, 2, 7), `aa: 1\n cc: 1\n gg: 2 #a comment`), ]); }); }); describe('Enum value or property mismatch quick fix', () => { it('should generate proper action for enum mismatch', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: value1'); const diagnostic = (0, verifyError_1.createDiagnosticWithData)('message', 0, 5, 0, 11, vscode_languageserver_types_1.DiagnosticSeverity.Hint, 'YAML', 'schemaUri', jsonLanguageTypes_1.ErrorCode.EnumValueMismatch, { values: ['valueX', 'valueY'] }); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([diagnostic]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result.map((r) => r.title)).deep.equal(['valueX', 'valueY']); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 5, 0, 11), 'valueX')]); }); it('should generate proper action for wrong property', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: value1'); const diagnostic = (0, verifyError_1.createDiagnosticWithData)('message', 0, 0, 0, 3, vscode_languageserver_types_1.DiagnosticSeverity.Hint, 'YAML', 'schemaUri', jsonLanguageTypes_1.ErrorCode.PropertyExpected, { properties: ['fooX', 'fooY'], }); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([diagnostic]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result.map((r) => r.title)).deep.equal(['fooX', 'fooY']); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 0, 0, 3), 'fooX')]); }); it('should generate proper action for enum mismatch, title converted to string value', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: value1'); const diagnostic = (0, verifyError_1.createDiagnosticWithData)('message', 0, 5, 0, 11, vscode_languageserver_types_1.DiagnosticSeverity.Hint, 'YAML', 'schemaUri', jsonLanguageTypes_1.ErrorCode.EnumValueMismatch, { values: [5, 10] }); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([diagnostic]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result.map((r) => r.title)).deep.equal(['5', '10']); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 5, 0, 11), '5')]); }); it('should generate proper action for enum with escaped quote strings', () => { const doc = (0, testHelper_1.setupTextDocument)('foo: value1'); const diagnostic = (0, verifyError_1.createDiagnosticWithData)('message', 0, 5, 0, 11, vscode_languageserver_types_1.DiagnosticSeverity.Hint, 'YAML', 'schemaUri', jsonLanguageTypes_1.ErrorCode.EnumValueMismatch, { values: ['', '""', "''"] }); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([diagnostic]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result.map((r) => r.title)).deep.equal(['""', '"\\"\\""', `"''"`]); expect(result[0].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 5, 0, 11), '""')]); expect(result[1].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 5, 0, 11), '"\\"\\""')]); expect(result[2].edit.changes[testHelper_1.TEST_URI]).deep.equal([vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(0, 5, 0, 11), `"''"`)]); }); }); describe('Change string to block string', function () { it('should split up double quoted text with newlines', function () { const doc = (0, testHelper_1.setupTextDocument)('foo: "line 1\\nline 2\\nline 3"'); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 29)), '>-\n line 1\n\n line 2\n\n line 3'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 29)), '|-\n line 1\n line 2\n line 3'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should split up double quoted text with newlines and trailing newline', function () { const doc = (0, testHelper_1.setupTextDocument)('foo: "line 1\\nline 2\\nline 3\\n"'); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 31)), '>\n line 1\n\n line 2\n\n line 3'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 31)), '|\n line 1\n line 2\n line 3'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should split up double quoted text with newlines and double trailing newline', function () { const doc = (0, testHelper_1.setupTextDocument)('foo: "line 1\\nline 2\\nline 3\\n\\n"'); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 33)), '>+\n line 1\n\n line 2\n\n line 3\n\n\n\n'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 33)), '|+\n line 1\n line 2\n line 3\n\n'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should split up long lines of double quoted text', function () { let docContent = 'foo: "'; for (let i = 0; i < 80 / 4 + 1; i++) { docContent += 'cat '; } docContent += 'cat"'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(1); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; let resultText = '>-\n '; for (let i = 0; i < 80 / 4; i++) { resultText += ' cat'; } resultText += ' cat\n cat'; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 5 + 80 + 8 + 1)), resultText), ]; expect(result[0].edit).to.deep.equal(edit0); }); it('should split up long lines of double quoted text using configured width', function () { let docContent = 'foo: "'; for (let i = 0; i < 40 / 4 + 1; i++) { docContent += 'cat '; } docContent += 'cat"'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); actions.configure({ indentation: ' ' }, 40); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(1); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; let resultText = '>-\n '; for (let i = 0; i < 40 / 4; i++) { resultText += ' cat'; } resultText += ' cat\n cat'; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 5), vscode_languageserver_types_1.Position.create(0, 5 + 40 + 8 + 1)), resultText), ]; expect(result[0].edit).to.deep.equal(edit0); }); it('should convert single quote text with newline', function () { const docContent = `root: 'aaa aaa bbb bbb'`; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(4, 6)), '>-\n aaa aaa\n\n bbb bbb'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(4, 6)), '|-\n aaa aaa\n bbb bbb'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should convert single quote text with leading whitespace', function () { const docContent = `root: ' aaa aaa bbb bbb'`; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(4, 6)), '>-2\n aaa aaa\n\n bbb bbb'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(4, 6)), '|-2\n aaa aaa\n bbb bbb'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should leave the whitespace at the end of the line when folding a double quoted string', function () { const docContent = 'root: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaa"'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(1); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(0, 138)), '>-\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \n aaaa'), ]; expect(result[0].edit).to.deep.equal(edit0); }); it("should use the '+' block chomping indicator when there are trailing newlines", function () { const docContent = 'root: "aaaa\\n\\n\\n"'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(0, 18)), '>+\n aaaa\n\n\n\n\n\n'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 6), vscode_languageserver_types_1.Position.create(0, 18)), '|+\n aaaa\n\n\n'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should handle nested indentation', function () { const docContent = 'root:\n toot:\n boot: "aaaa\\naaaa"'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); expect(result).to.have.length(2); expect(result[0].title).to.equal('Convert string to folded block string'); const edit0 = { changes: {}, }; edit0.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(2, 10), vscode_languageserver_types_1.Position.create(2, 22)), '>-\n aaaa\n\n aaaa'), ]; expect(result[0].edit).to.deep.equal(edit0); expect(result[1].title).to.equal('Convert string to literal block string'); const edit1 = { changes: {}, }; edit1.changes[testHelper_1.TEST_URI] = [ vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(2, 10), vscode_languageserver_types_1.Position.create(2, 22)), '|-\n aaaa\n aaaa'), ]; expect(result[1].edit).to.deep.equal(edit1); }); it('should give up on folded block string if there is trailing whitespace', function () { const docContent = 'root: " "'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), range: undefined, textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); // cannot be represented as folded stinrg // a block string makes no sense since it's one line long expect(result).to.have.length(0); }); it('should not give out of range string conversion suggestions when cursor is placed in string', function () { const docContent = 'foo: "bar\\nbar bar"\n\n\nnope: "bar\\nbar bar"\n\n\nbro: "bar\\nbar bar"\n\n\n'; const doc = (0, testHelper_1.setupTextDocument)(docContent); const params = { context: vscode_languageserver_types_1.CodeActionContext.create([]), // nope: "|bar ... range: vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(3, 7), vscode_languageserver_types_1.Position.create(3, 7)), textDocument: vscode_languageserver_types_1.TextDocumentIdentifier.create(testHelper_1.TEST_URI), }; const actions = new yamlCodeActions_1.YamlCodeActions(clientCapabilities); const result = actions.getCodeAction(doc, params); // 2 results for converting the value of the key 'nope' expect(result).to.have.length(2); }); i