diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts index 3683c4d86a0c9..850a239019bad 100644 --- a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts @@ -183,12 +183,17 @@ describe('autocomplete', () => { return { ...parseListener.getAst() }; }; - type TestArgs = [string, string[], string?, Parameters?]; + type TestArgs = [ + string, + string[], + (string | number)?, + Parameters? + ]; const testSuggestionsFn = ( statement: string, expected: string[], - triggerCharacter: string = '', + triggerCharacter: string | number = '', customCallbacksArgs: Parameters = [ undefined, undefined, @@ -196,12 +201,21 @@ describe('autocomplete', () => { ], { only, skip }: { only?: boolean; skip?: boolean } = {} ) => { - const context = createSuggestContext(statement, triggerCharacter); - const offset = statement.lastIndexOf(context.triggerCharacter) + 2; + const triggerCharacterString = + triggerCharacter == null || typeof triggerCharacter === 'string' + ? triggerCharacter + : statement[triggerCharacter + 1]; + const context = createSuggestContext(statement, triggerCharacterString); + const offset = + typeof triggerCharacter === 'string' + ? statement.lastIndexOf(context.triggerCharacter) + 2 + : triggerCharacter; const testFn = only ? test.only : skip ? test.skip : test; testFn( - `${statement} (triggerChar: "${context.triggerCharacter}")=> ["${expected.join('","')}"]`, + `${statement} (triggerChar: "${context.triggerCharacter}" @ ${offset})=> ["${expected.join( + '","' + )}"]`, async () => { const callbackMocks = createCustomCallbackMocks(...customCallbacksArgs); const { model, position } = createModelAndPosition(statement, offset); @@ -400,7 +414,7 @@ describe('autocomplete', () => { testSuggestions('from a | stats a=', [...allAggFunctions]); testSuggestions('from a | stats a=max(b) by ', [...fields.map(({ name }) => name)]); testSuggestions('from a | stats a=max(b) BY ', [...fields.map(({ name }) => name)]); - testSuggestions('from a | stats a=c by d', ['|', ',']); + testSuggestions('from a | stats a=c by d ', ['|', ',']); testSuggestions('from a | stats a=c by d, ', [...fields.map(({ name }) => name)]); testSuggestions('from a | stats a=max(b), ', ['var0 =', ...allAggFunctions]); testSuggestions( @@ -422,6 +436,10 @@ describe('autocomplete', () => { 'var0', 'var1', ]); + + // smoke testing with suggestions not at the end of the string + // but more the cursor back after the min(b) function + testSuggestions('from a | stats a = min(b) | sort b', ['by', '|', ','], 27); }); describe('enrich', () => { @@ -431,7 +449,7 @@ describe('autocomplete', () => { '| enrich other-policy on b ', '| enrich other-policy with c ', ]) { - testSuggestions(`from a ${prevCommand}| enrich`, ['policy']); + testSuggestions(`from a ${prevCommand}| enrich `, ['policy']); testSuggestions(`from a ${prevCommand}| enrich policy `, ['on', 'with', '|']); testSuggestions(`from a ${prevCommand}| enrich policy on `, [ 'stringField', @@ -473,7 +491,7 @@ describe('autocomplete', () => { 'var0 =', ...getPolicyFields('policy'), ]); - testSuggestions(`from a ${prevCommand}| enrich policy with stringField`, ['= $0', '|', ',']); + testSuggestions(`from a ${prevCommand}| enrich policy with stringField `, ['= $0', '|', ',']); } }); diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts index 9857b3c77dce7..c83290868d6a3 100644 --- a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts @@ -128,8 +128,11 @@ export async function suggest( astProvider: AstProviderFn, resourceRetriever?: ESQLCallbacks ): Promise { - const innerText = model.getValue(); - const offset = monacoPositionToOffset(innerText, position); + // take the full text but then slice it to the current position + const fullText = model.getValue(); + const offset = monacoPositionToOffset(fullText, position); + const innerText = fullText.substring(0, offset); + let finalText = innerText; // if it's a comma by the user or a forced trigger by a function argument suggestion // add a marker to make the expression still valid @@ -142,6 +145,12 @@ export async function suggest( ) { finalText = `${innerText.substring(0, offset)}${EDITOR_MARKER}${innerText.substring(offset)}`; } + // check if all brackets are closed, otherwise close them + // @TODO: improve this in the future + if (innerText.lastIndexOf('(') > innerText.lastIndexOf(')')) { + // inject the closing brackets + finalText += ')'; + } const { ast } = await astProvider(finalText);