diff --git a/src/platform/packages/private/kbn-esql-editor/src/esql_editor.tsx b/src/platform/packages/private/kbn-esql-editor/src/esql_editor.tsx index 1680425152685..5c84c3e3bc3da 100644 --- a/src/platform/packages/private/kbn-esql-editor/src/esql_editor.tsx +++ b/src/platform/packages/private/kbn-esql-editor/src/esql_editor.tsx @@ -28,7 +28,7 @@ import type { CodeEditorProps } from '@kbn/code-editor'; import { CodeEditor } from '@kbn/code-editor'; import type { CoreStart } from '@kbn/core/public'; import type { AggregateQuery, TimeRange } from '@kbn/es-query'; -import type { FieldType } from '@kbn/esql-ast'; +import { type FieldType } from '@kbn/esql-ast'; import type { ESQLFieldWithMetadata } from '@kbn/esql-ast/src/commands_registry/types'; import type { ESQLTelemetryCallbacks } from '@kbn/esql-types'; import { @@ -303,20 +303,22 @@ const ESQLEditorInternal = function ESQLEditor({ } }, [code, fixedQuery]); - // Enable the variables service if the feature is supported in the consumer app + // If variables are passed to the editor, sync them with the variables service. + // This ensures that the latest variables are always available for suggestions. + // The "Create control" suggestion is also enabled/disabled here based on the supportsControls flag useEffect(() => { + const variables = variablesService?.esqlVariables; + if (!isEqual(variables, esqlVariables)) { + variablesService?.clearVariables(); + esqlVariables?.forEach((variable) => { + variablesService?.addVariable(variable); + }); + } + // Enable or disable suggestions based on whether Create control suggestion is supported if (controlsContext?.supportsControls) { - variablesService?.enableSuggestions(); - - const variables = variablesService?.esqlVariables; - if (!isEqual(variables, esqlVariables)) { - variablesService?.clearVariables(); - esqlVariables?.forEach((variable) => { - variablesService?.addVariable(variable); - }); - } + variablesService?.enableCreateControlSuggestion(); } else { - variablesService?.disableSuggestions(); + variablesService?.disableCreateControlSuggestion(); } }, [variablesService, controlsContext, esqlVariables]); @@ -652,7 +654,7 @@ const ESQLEditorInternal = function ESQLEditor({ return variablesService?.esqlVariables; }, canSuggestVariables: () => { - return variablesService?.areSuggestionsEnabled ?? false; + return variablesService?.isCreateControlSuggestionEnabled ?? false; }, getJoinIndices, getTimeseriesIndices: kibana.services?.esql?.getTimeseriesIndicesAutocomplete, @@ -707,7 +709,7 @@ const ESQLEditorInternal = function ESQLEditor({ memoizedFieldsFromESQL, abortController, variablesService?.esqlVariables, - variablesService?.areSuggestionsEnabled, + variablesService?.isCreateControlSuggestionEnabled, histogramBarTarget, activeSolutionId, canCreateLookupIndex, diff --git a/src/platform/packages/private/kbn-esql-editor/src/types.ts b/src/platform/packages/private/kbn-esql-editor/src/types.ts index aedde5bcc50df..f151853510904 100644 --- a/src/platform/packages/private/kbn-esql-editor/src/types.ts +++ b/src/platform/packages/private/kbn-esql-editor/src/types.ts @@ -107,10 +107,10 @@ export interface ESQLEditorProps { } interface ESQLVariableService { - areSuggestionsEnabled: boolean; + isCreateControlSuggestionEnabled: boolean; esqlVariables: ESQLControlVariable[]; - enableSuggestions: () => void; - disableSuggestions: () => void; + enableCreateControlSuggestion: () => void; + disableCreateControlSuggestion: () => void; clearVariables: () => void; addVariable: (variable: ESQLControlVariable) => void; } diff --git a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/positions/empty_expression.ts b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/positions/empty_expression.ts index c633c0a8f37f6..751b9194ddc32 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/positions/empty_expression.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/expressions/positions/empty_expression.ts @@ -276,26 +276,25 @@ async function handleDefaultContext(ctx: ExpressionContext): Promise - suggestion.command?.id?.includes('esql.control') - ); + // Suggest control variables (e.g., "??fieldName", "?valueName") if supported and not already present + const hasControl = suggestions.some((suggestion) => + suggestion.command?.id?.includes('esql.control') + ); - if (!hasControl) { - const prefix = getVariablePrefix(controlType); - const variableNames = - context.variables - ?.filter(({ type }) => type === controlType) - .map(({ key }) => `${prefix}${key}`) ?? []; - - const controlSuggestions = getControlSuggestion( - controlType, - ControlTriggerSource.SMART_SUGGESTION, - variableNames - ); - suggestions.push(...controlSuggestions); - } + if (!hasControl) { + const prefix = getVariablePrefix(controlType); + const variableNames = + context?.variables + ?.filter(({ type }) => type === controlType) + .map(({ key }) => `${prefix}${key}`) ?? []; + + const controlSuggestions = getControlSuggestion( + controlType, + ControlTriggerSource.SMART_SUGGESTION, + variableNames, + Boolean(context?.supportsControls) + ); + suggestions.push(...controlSuggestions); } return suggestions; diff --git a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/helpers.ts b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/helpers.ts index 17967f04bae2b..17f8cea1aa5c8 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/helpers.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/autocomplete/helpers.ts @@ -316,28 +316,33 @@ export const columnExists = (col: string, context?: ICommandContext) => export function getControlSuggestion( type: ESQLVariableType, triggerSource: ControlTriggerSource, - variables?: string[] + variables?: string[], + suggestCreation = true ): ISuggestionItem[] { return [ - { - label: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlLabel', { - defaultMessage: 'Create control', - }), - text: '', - kind: 'Issue', - detail: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlDetailLabel', { - defaultMessage: 'Click to create', - }), - sortText: '1', - category: SuggestionCategory.CUSTOM_ACTION, - command: { - id: `esql.control.${type}.create`, - title: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlDetailLabel', { - defaultMessage: 'Click to create', - }), - arguments: [{ triggerSource }], - }, - } as ISuggestionItem, + ...(suggestCreation + ? [ + { + label: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlLabel', { + defaultMessage: 'Create control', + }), + text: '', + kind: 'Issue', + detail: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlDetailLabel', { + defaultMessage: 'Click to create', + }), + sortText: '1', + category: SuggestionCategory.CUSTOM_ACTION, + command: { + id: `esql.control.${type}.create`, + title: i18n.translate('kbn-esql-ast.esql.autocomplete.createControlDetailLabel', { + defaultMessage: 'Click to create', + }), + arguments: [{ triggerSource }], + }, + } as ISuggestionItem, + ] + : []), ...(variables?.length ? buildConstantsDefinitions( variables, @@ -365,17 +370,14 @@ export function getControlSuggestionIfSupported( variables?: ESQLControlVariable[], shouldBePrefixed = true ) { - if (!supportsControls) { - return []; - } - const prefix = shouldBePrefixed ? getVariablePrefix(type) : ''; const filteredVariables = variables?.filter((variable) => variable.type === type) ?? []; const controlSuggestion = getControlSuggestion( type, triggerSource, - filteredVariables?.map((v) => `${prefix}${v.key}`) + filteredVariables?.map((v) => `${prefix}${v.key}`), + supportsControls ); return controlSuggestion; diff --git a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/functions.ts b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/functions.ts index 4b85d220c056c..9eb3936b3a4b9 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/functions.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/definitions/utils/functions.ts @@ -466,20 +466,18 @@ export const buildColumnSuggestions = ( }); const suggestions = [...fieldsSuggestions]; - if (options?.supportsControls) { - const variableType = options?.variableType ?? ESQLVariableType.FIELDS; - const userDefinedColumns = - variables?.filter((variable) => variable.type === variableType) ?? []; - - const controlSuggestions = columns.length - ? getControlSuggestion( - variableType, - ControlTriggerSource.SMART_SUGGESTION, - userDefinedColumns?.map((v) => `${getVariablePrefix(variableType)}${v.key}`) - ) - : []; - suggestions.push(...controlSuggestions); - } + const variableType = options?.variableType ?? ESQLVariableType.FIELDS; + const userDefinedColumns = variables?.filter((variable) => variable.type === variableType) ?? []; + + const controlSuggestions = columns.length + ? getControlSuggestion( + variableType, + ControlTriggerSource.SMART_SUGGESTION, + userDefinedColumns?.map((v) => `${getVariablePrefix(variableType)}${v.key}`), + Boolean(options?.supportsControls) + ) + : []; + suggestions.push(...controlSuggestions); return [...suggestions]; }; diff --git a/src/platform/plugins/shared/esql/public/variables_service.test.ts b/src/platform/plugins/shared/esql/public/variables_service.test.ts index 15331fc515b14..1fa7f0022a2d8 100644 --- a/src/platform/plugins/shared/esql/public/variables_service.test.ts +++ b/src/platform/plugins/shared/esql/public/variables_service.test.ts @@ -16,17 +16,17 @@ describe('EsqlVariablesService', () => { esqlVariablesService = new EsqlVariablesService(); }); - describe('enableSuggestions', () => { + describe('enableCreateControlSuggestion', () => { it('should enable suggestions', () => { - esqlVariablesService.enableSuggestions(); - expect(esqlVariablesService.areSuggestionsEnabled).toBe(true); + esqlVariablesService.enableCreateControlSuggestion(); + expect(esqlVariablesService.isCreateControlSuggestionEnabled).toBe(true); }); }); - describe('disableSuggestions', () => { + describe('disableCreateControlSuggestion', () => { it('should disable suggestions', () => { - esqlVariablesService.disableSuggestions(); - expect(esqlVariablesService.areSuggestionsEnabled).toBe(false); + esqlVariablesService.disableCreateControlSuggestion(); + expect(esqlVariablesService.isCreateControlSuggestionEnabled).toBe(false); }); }); diff --git a/src/platform/plugins/shared/esql/public/variables_service.ts b/src/platform/plugins/shared/esql/public/variables_service.ts index ce63c733847d9..9c6019657f073 100644 --- a/src/platform/plugins/shared/esql/public/variables_service.ts +++ b/src/platform/plugins/shared/esql/public/variables_service.ts @@ -9,16 +9,22 @@ import type { ESQLControlVariable } from '@kbn/esql-types'; +/** + * Service to manage ESQL variables for controls + * The service allows adding, retrieving and clearing variables + * and enables/disables the creation of control suggestions based on variables. + */ + export class EsqlVariablesService { esqlVariables: ESQLControlVariable[] = []; - areSuggestionsEnabled: boolean = false; + isCreateControlSuggestionEnabled: boolean = false; - enableSuggestions() { - this.areSuggestionsEnabled = true; + enableCreateControlSuggestion() { + this.isCreateControlSuggestionEnabled = true; } - disableSuggestions() { - this.areSuggestionsEnabled = false; + disableCreateControlSuggestion() { + this.isCreateControlSuggestionEnabled = false; } addVariable(variable: ESQLControlVariable): void {