diff --git a/package.json b/package.json index 9ed6864b..d8626bb3 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "ajv": "^8.11.0", + "fast-uri": "^3.0.6", "lodash": "4.17.21", "prettier": "^3.0.0", "request-light": "^0.5.7", diff --git a/src/languageservice/services/yamlOnTypeFormatting.ts b/src/languageservice/services/yamlOnTypeFormatting.ts index 1d2b1cec..7bb9e49e 100644 --- a/src/languageservice/services/yamlOnTypeFormatting.ts +++ b/src/languageservice/services/yamlOnTypeFormatting.ts @@ -50,4 +50,14 @@ export function doDocumentOnTypeFormatting( return [TextEdit.insert(position, ' ')]; } } + + if (params.ch === '\t' && params.options.insertSpaces) { + return [ + TextEdit.replace( + Range.create(position.line, position.character - 1, position.line, position.character), + ' '.repeat(params.options.tabSize) + ), + ]; + } + return; } diff --git a/src/yamlServerInit.ts b/src/yamlServerInit.ts index 9640e48d..8e0a7e77 100644 --- a/src/yamlServerInit.ts +++ b/src/yamlServerInit.ts @@ -105,6 +105,7 @@ export class YAMLServerInit { documentFormattingProvider: false, documentOnTypeFormattingProvider: { firstTriggerCharacter: '\n', + moreTriggerCharacter: ['\t'], }, documentRangeFormattingProvider: false, definitionProvider: true, diff --git a/test/yamlOnTypeFormatting.test.ts b/test/yamlOnTypeFormatting.test.ts index 6984a481..cbd81c15 100644 --- a/test/yamlOnTypeFormatting.test.ts +++ b/test/yamlOnTypeFormatting.test.ts @@ -17,58 +17,66 @@ function createParams(position: Position): DocumentOnTypeFormattingParams { }; } describe('YAML On Type Formatter', () => { - it('should react on "\n" only', () => { - const doc = setupTextDocument('foo:'); - const params = createParams(Position.create(1, 0)); - params.ch = '\t'; - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).is.undefined; - }); + describe('On Enter Formatter', () => { + it('should add indentation for mapping', () => { + const doc = setupTextDocument('foo:\n'); + const params = createParams(Position.create(1, 0)); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).to.deep.include(TextEdit.insert(Position.create(1, 0), ' ')); + }); - it('should add indentation for mapping', () => { - const doc = setupTextDocument('foo:\n'); - const params = createParams(Position.create(1, 0)); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).to.deep.include(TextEdit.insert(Position.create(1, 0), ' ')); - }); + it('should add indentation for scalar array items', () => { + const doc = setupTextDocument('foo:\n - some\n '); + const pos = Position.create(2, 2); + const params = createParams(pos); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result[0]).to.eqls(TextEdit.insert(pos, '- ')); + }); - it('should add indentation for scalar array items', () => { - const doc = setupTextDocument('foo:\n - some\n '); - const pos = Position.create(2, 2); - const params = createParams(pos); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result[0]).to.eqls(TextEdit.insert(pos, '- ')); - }); + it('should add indentation for mapping in array', () => { + const doc = setupTextDocument('some:\n - arr:\n '); + const pos = Position.create(2, 2); + const params = createParams(pos); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).to.deep.include(TextEdit.insert(pos, ' ')); + }); - it('should add indentation for mapping in array', () => { - const doc = setupTextDocument('some:\n - arr:\n '); - const pos = Position.create(2, 2); - const params = createParams(pos); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).to.deep.include(TextEdit.insert(pos, ' ')); - }); + it('should replace all spaces in newline', () => { + const doc = setupTextDocument('some:\n '); + const pos = Position.create(1, 0); + const params = createParams(pos); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).to.deep.include.members([ + TextEdit.del(Range.create(pos, Position.create(1, 3))), + TextEdit.insert(pos, ' '), + ]); + }); - it('should replace all spaces in newline', () => { - const doc = setupTextDocument('some:\n '); - const pos = Position.create(1, 0); - const params = createParams(pos); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).to.deep.include.members([TextEdit.del(Range.create(pos, Position.create(1, 3))), TextEdit.insert(pos, ' ')]); - }); + it('should keep all non white spaces characters in newline', () => { + const doc = setupTextDocument('some:\n foo'); + const pos = Position.create(1, 0); + const params = createParams(pos); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).is.undefined; + }); - it('should keep all non white spaces characters in newline', () => { - const doc = setupTextDocument('some:\n foo'); - const pos = Position.create(1, 0); - const params = createParams(pos); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).is.undefined; + it('should add indentation for multiline string', () => { + const doc = setupTextDocument('some: |\n'); + const pos = Position.create(1, 0); + const params = createParams(pos); + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).to.deep.include(TextEdit.insert(pos, ' ')); + }); }); - it('should add indentation for multiline string', () => { - const doc = setupTextDocument('some: |\n'); - const pos = Position.create(1, 0); - const params = createParams(pos); - const result = doDocumentOnTypeFormatting(doc, params); - expect(result).to.deep.include(TextEdit.insert(pos, ' ')); + describe('On Tab Formatter', () => { + it('should replace Tab with spaces', () => { + const doc = setupTextDocument('some:\n\t'); + const pos = Position.create(1, 1); + const params = createParams(pos); + params.ch = '\t'; + const result = doDocumentOnTypeFormatting(doc, params); + expect(result).to.deep.include(TextEdit.replace(Range.create(1, 0, 1, 1), ' ')); + }); }); }); diff --git a/yarn.lock b/yarn.lock index ee1d8f79..d04b390e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1517,6 +1517,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-uri@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"