From 2617d7cea16523dac4ea351543f584b36703b846 Mon Sep 17 00:00:00 2001 From: Senith Uthsara Date: Wed, 19 Nov 2025 17:39:09 +0530 Subject: [PATCH] fix completions not displayed for field access --- .../ChipExpressionEditor/CodeUtils.ts | 18 +++++++------- .../components/ChipExpressionEditor.tsx | 24 ++++++++++++++++--- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts index 0a6136dc8e..ecde872bba 100644 --- a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts +++ b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts @@ -397,22 +397,24 @@ export const buildOnChangeListner = (onTrigeer: (newValue: string, cursor: Curso return onChangeListner; } -export const buildCompletionSource = (getCompletions: () => CompletionItem[]) => { - return (context: CompletionContext): CompletionResult | null => { +export const buildCompletionSource = (getCompletions: () => Promise) => { + return async (context: CompletionContext): Promise => { + const textBeforeCursor = context.state.doc.toString().slice(0, context.pos); + const lastNonSpaceChar = textBeforeCursor.trimEnd().slice(-1); + const word = context.matchBefore(/\w*/); - if (!word || (word.from === word.to && !context.explicit)) { + if (lastNonSpaceChar !== '.' && ( + !word || (word.from === word.to && !context.explicit) + )) { return null; } - const textBeforeCursor = context.state.doc.toString().slice(0, context.pos); - const lastNonSpaceChar = textBeforeCursor.trimEnd().slice(-1); - // Don't show completions for trigger characters - if (lastNonSpaceChar === '+' || lastNonSpaceChar === ':') { + if (lastNonSpaceChar === '+') { return null; } - const completions = getCompletions(); + const completions = await getCompletions(); const prefix = word.text; const filteredCompletions = filterCompletionsByPrefixAndType(completions, prefix); diff --git a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx index c86702d3d6..3253b9b3f8 100644 --- a/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx +++ b/workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx @@ -18,7 +18,7 @@ import { EditorState } from "@codemirror/state"; import { EditorView, keymap, tooltips } from "@codemirror/view"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { useFormContext } from "../../../../../context"; import { buildNeedTokenRefetchListner, @@ -88,8 +88,9 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone const fieldContainerRef = useRef(null); const viewRef = useRef(null); const [isTokenUpdateScheduled, setIsTokenUpdateScheduled] = useState(true); - const completionsRef = useRef(props.completions); + const completionsRef = useRef(props.completions); const helperPaneToggleButtonRef = useRef(null); + const completionsFetchScheduledRef = useRef(false); const { expressionEditor } = useFormContext(); const expressionEditorRpcManager = expressionEditor?.rpcManager; @@ -99,6 +100,7 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone }); const handleChangeListner = buildOnChangeListner((newValue, cursor) => { + completionsFetchScheduledRef.current = true; props.onChange(newValue, cursor.position.to); const textBeforeCursor = newValue.slice(0, cursor.position.to); const lastNonSpaceChar = textBeforeCursor.trimEnd().slice(-1); @@ -124,7 +126,22 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone setIsTokenUpdateScheduled(true); }); - const completionSource = buildCompletionSource(() => completionsRef.current); + const waitForStateChange = (): Promise => { + return new Promise((resolve) => { + const checkState = () => { + if (!completionsFetchScheduledRef.current) { + resolve(completionsRef.current); + } else { + requestAnimationFrame(checkState); + } + }; + checkState(); + }); + }; + + const completionSource = useMemo(() => { + return buildCompletionSource(waitForStateChange); + }, [props.completions]); const helperPaneKeymap = buildHelperPaneKeymap(() => helperPaneState.isOpen, () => { setHelperPaneState(prev => ({ ...prev, isOpen: false })); @@ -284,6 +301,7 @@ export const ChipExpressionEditorComponent = (props: ChipExpressionEditorCompone // just don't touch this. useEffect(() => { completionsRef.current = props.completions; + completionsFetchScheduledRef.current = false; }, [props.completions]); useEffect(() => {