diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8737dd70a9..9990616529 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,13 @@ jobs: run: npm run lint - name: Lint commit message if: github.ref != 'refs/heads/main' && github.actor != 'dependabot[bot]' - run: npx commitlint --from HEAD~${{ github.event.pull_request.commits }} --to HEAD + run: | + echo PR COMMITS: ${{ github.event.pull_request.commits }} + echo LOG prev + git log -n 4 HEAD~${{ github.event.pull_request.commits }} + echo LOG head + git log -n 4 HEAD + npx commitlint --from HEAD~${{ github.event.pull_request.commits }} --to HEAD check-typescript-types: runs-on: ubuntu-latest diff --git a/packages/apidom-ls/src/apidom-language-types.ts b/packages/apidom-ls/src/apidom-language-types.ts index fd2b4cc54b..957d117c60 100644 --- a/packages/apidom-ls/src/apidom-language-types.ts +++ b/packages/apidom-ls/src/apidom-language-types.ts @@ -277,6 +277,7 @@ export interface ValidationContext { export interface CompletionContext { maxNumberOfItems?: number; + enableLSPFilter?: boolean; } export interface DerefContext { diff --git a/packages/apidom-ls/src/services/completion/completion-service.ts b/packages/apidom-ls/src/services/completion/completion-service.ts index b6b279d65e..71f6b8b7e3 100644 --- a/packages/apidom-ls/src/services/completion/completion-service.ts +++ b/packages/apidom-ls/src/services/completion/completion-service.ts @@ -169,7 +169,7 @@ export class DefaultCompletionService implements CompletionService { } // eslint-disable-next-line class-methods-use-this - private resolveCaretContext(node: Element, offset: number): CaretContext { + private resolveCaretContext(node: Element, offset: number, textModified: boolean): CaretContext { let caretContext: CaretContext = CaretContext.UNDEFINED; if (node) { const sm = getSourceMap(node); @@ -180,7 +180,11 @@ export class DefaultCompletionService implements CompletionService { if (offset > sm.offset && offset < sm.endOffset!) { caretContext = CaretContext.KEY_INNER; } else if (offset === sm.offset) { - caretContext = CaretContext.KEY_START; + if (sm.length === 0 && textModified) { + caretContext = CaretContext.KEY_END; + } else { + caretContext = CaretContext.KEY_START; + } } else { caretContext = CaretContext.KEY_END; } @@ -251,6 +255,7 @@ export class DefaultCompletionService implements CompletionService { ): Promise { perfStart(PerfLabels.START); const context = !completionContext ? this.settings?.completionContext : completionContext; + const enableFiltering = context?.enableLSPFilter; const completionList: CompletionList = { items: [], isIncomplete: false, @@ -543,7 +548,7 @@ export class DefaultCompletionService implements CompletionService { // only if we have a node let completionNode: Element | undefined; if (node) { - const caretContext = this.resolveCaretContext(node, targetOffset); + const caretContext = this.resolveCaretContext(node, targetOffset, textModified); completionNode = this.resolveCompletionNode(node, caretContext); const completionNodeContext = this.resolveCompletionNodeContext(caretContext); @@ -707,7 +712,15 @@ export class DefaultCompletionService implements CompletionService { } else if (contentLanguage.format === 'YAML') { // item.insertText = `${item.insertText}\n`; } - collector.add(item); + if (word && word.length > 0) { + if (enableFiltering && item.insertText?.includes(word)) { + collector.add(item); + } else if (!enableFiltering) { + collector.add(item); + } + } else if (!word) { + collector.add(item); + } } } else if ( // in a primitive value node @@ -781,8 +794,12 @@ export class DefaultCompletionService implements CompletionService { */ item.filterText = text.substring(nodeSourceMap.offset, nodeSourceMap.endOffset!); - if (word && word.length > 0 && unquotedOriginalInsertText?.includes(word)) { - collector.add(item); + if (word && word.length > 0) { + if (enableFiltering && unquotedOriginalInsertText?.includes(word)) { + collector.add(item); + } else if (!enableFiltering) { + collector.add(item); + } } else if (!word) { collector.add(item); } diff --git a/packages/apidom-ls/test/complete.ts b/packages/apidom-ls/test/complete.ts index 80b7d1d941..dc9a096de0 100644 --- a/packages/apidom-ls/test/complete.ts +++ b/packages/apidom-ls/test/complete.ts @@ -1243,4 +1243,84 @@ describe('apidom-ls-complete', function () { }, ] as ApidomCompletionItem[]); }); + + it('openapi / yaml - test LSP provided filter', async function () { + const completionContext: CompletionContext = { + maxNumberOfItems: 100, + enableLSPFilter: true, + }; + + const spec = fs + .readFileSync(path.join(__dirname, 'fixtures', 'openapi-complete-filter.yaml')) + .toString(); + + const doc: TextDocument = TextDocument.create( + 'foo://bar/penapi-complete-filter.yaml', + 'yaml', + 0, + spec, + ); + + const pos = Position.create(4, 8); + const result = await languageService.doCompletion( + doc, + { textDocument: doc, position: pos }, + completionContext, + ); + assert.deepEqual(result!.items, [ + { + label: 'responses', + insertText: 'responses: \n $1', + kind: 14, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#responsesObject)\n\\\n\\\nThe list of possible responses as they are returned from executing this operation.', + }, + targetSpecs: [{ namespace: 'openapi', version: '3.1.0' }], + filterText: 'se', + textEdit: { + range: { start: { line: 4, character: 6 }, end: { line: 4, character: 8 } }, + newText: 'responses: \n $1', + }, + }, + { + label: 'security', + insertText: 'security: \n - $1', + kind: 14, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[[Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#serverObject)]\n\\\n\\\nA declaration of which security mechanisms can be used for this operation. The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. To make security optional, an empty security requirement (`{}`) can be included in the array. This definition overrides any declared top-level [`security`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#oasSecurity). To remove a top-level security declaration, an empty array can be used.', + }, + targetSpecs: [{ namespace: 'openapi', version: '3.1.0' }], + preselect: true, + filterText: 'se', + textEdit: { + range: { start: { line: 4, character: 6 }, end: { line: 4, character: 8 } }, + newText: 'security: \n - $1', + }, + }, + { + label: 'servers', + insertText: 'servers: \n - $1', + kind: 14, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[[Server Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#serverObject)]\n\\\n\\\nAn alternative `server` array to service this operation. If an alternative `server` object is specified at the Path Item Object or Root level, it will be overridden by this value.', + }, + targetSpecs: [{ namespace: 'openapi', version: '3.1.0' }], + preselect: true, + filterText: 'se', + textEdit: { + range: { start: { line: 4, character: 6 }, end: { line: 4, character: 8 } }, + newText: 'servers: \n - $1', + }, + }, + ] as ApidomCompletionItem[]); + }); }); diff --git a/packages/apidom-ls/test/fixtures/openapi-complete-filter.yaml b/packages/apidom-ls/test/fixtures/openapi-complete-filter.yaml new file mode 100644 index 0000000000..5fe71f98aa --- /dev/null +++ b/packages/apidom-ls/test/fixtures/openapi-complete-filter.yaml @@ -0,0 +1,6 @@ +openapi: 3.1.0 +paths: + /pet: + put: + se + summary: Update an existing pet