Skip to content

Conversation

@senithkay
Copy link
Contributor

@senithkay senithkay commented Nov 27, 2025

Purpose

This PR introduces string template support for string fields, solving the long-standing issue where users had to manually manage backticks, quotes, and string escaping when inserting variables. This often resulted in syntax errors and a poor editing experience.

The PR also addresses extensibility limitations in the ChipExpressionEditor component. Previously, editor-specific behavior required direct modifications inside the component. This made it hard to reuse the component across different editors with varying requirements.

Resolves: wso2/product-ballerina-integrator#1988

Goals

  • Allow users to insert and manipulate variables inside string fields without worrying about backticks or quotation handling.
  • Provide a more intuitive and consistent experience for editing dynamic string values.
  • Improve the extensibility of the ChipExpressionEditor by enabling consumers to provide a configuration object to customize behavior.
  • Reduce the need for future code changes inside the component when supporting new editor types.

Approach

  • Added string template editing support to string fields, with internal handling of quoting and backtick rules.
  • Updated the UI and logic to allow variable insertion, dynamic previews, and safer string manipulation.
  • Refactored the ChipExpressionEditor to:
    • Accept a config object from consuming editors.
    • Allow behavior customization without modifying the component itself.
    • Improve overall component structure for scalability and easier future extension.
  • Verified UI behavior and interactions.

Summary by CodeRabbit

  • New Features

    • Added a record preview editor, multiple mode-specific editor configurations, and a configuration prop for the chip-style editor to enable mode-aware helper/serialization.
  • Bug Fixes

    • Simplified mode switching and removed unintended auto-quoting when changing modes.
  • Refactor

    • Unified expression rendering around a primary input mode, improved selection/token detection and helper behavior, and centralized serialization/deserialization for consistent editor behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Walkthrough

Introduce a configuration layer for multi-mode expression editors, add mode-aware config classes, refactor Text/Record/Template editors to use configurations, update ChipExpressionEditor to use serialization/deserialization and helper logic from configs, and adjust token/selection utilities and template-interpolation helpers.

Changes

Cohort / File(s) Summary
Configuration classes
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
Add ChipExpressionEditorDefaultConfiguration base class and concrete configs (ChipExpressionEditorConfig, StringTemplateEditorConfig, RawTemplateEditorConfig, PrimaryModeChipExpressionEditorConfig) implementing getHelperValue and serialization/deserialization strategies.
Expression editor core
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
Simplify input mode switching, remove auto-quoting on mode change, add primaryMode prop to ExpressionField, and route TEXT/RECORD/TEMPLATE to mode-specific editors with appropriate configs.
Chip expression editor
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx, .../ChipExpressionEditor/CodeUtils.ts, .../ChipExpressionEditor/utils.ts
Export token index constants; add isSelectionOnToken util; make ChipExpressionEditorComponent accept optional configuration; use configuration.serialize/deserialize and getHelperValue for value handling and helper insertion; token parsing uses current document value and applies token-start offset adjustments.
Text / Record / Template editors
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx, .../RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx, .../ExpandedEditor/modes/TemplateMode.tsx
Replace TextModeEditor to render ChipExpressionEditorComponent and forward configuration; add RecordConfigPreviewEditor (quotes values for record preview); wire RawTemplateEditorConfig into TemplateMode expanded editor.
Helper utilities / visualizer
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx, workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts
Simplify template/document wrapping helpers: remove interpolation injection and unused input-mode-based wrapping; functions now return wrapped values without adding ${...}.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant ExpressionEditor
    participant ExpressionField
    participant Config as ConfigClass
    participant ChipEditor
    participant TokenUtils

    User->>ExpressionEditor: edit / change mode
    ExpressionEditor->>ExpressionField: render(primaryMode)
    ExpressionField->>Config: select / new Config(primaryMode)
    ExpressionField->>ChipEditor: render(configuration)
    User->>ChipEditor: type / select helper
    ChipEditor->>TokenUtils: isSelectionOnToken(from,to,view)
    TokenUtils-->>ChipEditor: token | undefined
    ChipEditor->>Config: getHelperValue(value, token)
    Config-->>ChipEditor: helperText
    ChipEditor->>Config: deserializeValue(serialized)
    Config-->>ChipEditor: deserializedValue
    ChipEditor->>ExpressionEditor: onChange(deserializedValue)
    ExpressionEditor->>User: update UI
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~40 minutes

  • Areas needing extra attention:
    • serialize/deserialize round-trips for each new config class.
    • token start offset adjustments and correctness of isSelectionOnToken.
    • Prop surface changes: primaryMode and configuration propagation.
    • Call sites updated from FormExpressionEditor → ChipExpressionEditorComponent.

Possibly related PRs

Suggested reviewers

  • hevayo
  • gigara
  • kanushka

Poem

🐇 Hopped in the code with a twitch and a wink,
Configs for my chips, no more messy ink,
Tokens aligned and helpers snug-fit,
Templates and text now play and commit,
Rabbity cheers — the editor's in sync!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers Purpose, Goals, and Approach sections well, but most sections from the template (UI Component Development, Icons, User Stories, Release Notes, Documentation, Training, Certification, Marketing, Tests, Security, Samples, etc.) are missing or incomplete. Complete missing template sections including Release Notes, Documentation, Testing details, Security checks, and other required sections to meet repository standards.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Introduce String Template Support' is concise, clear, and directly summarizes the main feature being added without vague language or excessive detail.
Linked Issues check ✅ Passed The PR implements string template support across expression editors and refactors ChipExpressionEditor with configuration-based extensibility, which addresses the main requirements from issue #1988 to extend string templating beyond the Prompt Editor.
Out of Scope Changes check ✅ Passed All changes are within scope: configuration classes, editor components, and template support for string fields directly relate to the string template support objective; helper utilities and index exports support the new configuration system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b2a285 and ebaf3b9.

📒 Files selected for processing (2)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (8 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-11-24T22:16:28.380Z
Learnt from: dan-niles
Repo: wso2/vscode-extensions PR: 980
File: workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx:384-412
Timestamp: 2025-11-24T22:16:28.380Z
Learning: In the RichTextTemplateEditor component (workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx), token fetching on external `value` prop changes is intentionally disabled. Users cannot edit both the minimized and expanded editors simultaneously, so tokens only need to be generated based on user typing in the expanded editor view, not on external prop updates.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use React 18.2.0 features including concurrent rendering and automatic batching; avoid class components in favor of functional components with hooks

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use Emotion for CSS-in-JS styling instead of inline styles or external CSS files

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-24T14:51:49.267Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 998
File: workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx:113-132
Timestamp: 2025-11-24T14:51:49.267Z
Learning: In workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx, if `textFieldRef.current` is not undefined, `textFieldRef.current.inputElement` is guaranteed to exist. If `inputElement` doesn't exist when `current` exists, it's a fatal error that should reach the error boundary rather than being handled with defensive null checks.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/**/*.tsx : Implement lazy expansion of functions: only render function items when they are visible to improve performance for large function lists

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/**/*.{ts,tsx} : Define all constants (node types, sizing, spacing) in src/resources/constants.ts and import them where needed instead of hardcoding values

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/**/*.{ts,tsx} : Use TypeScript 5.8.3 with strict type checking enabled for all source files

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/resources/icons/**/*.tsx : Create separate SVG icon components in src/resources/icons/ for all diagram icons and import them as React components

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-27T07:59:33.522Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 897
File: workspaces/ballerina/data-mapper/src/components/DataMapper/SidePanel/QueryClauses/ClausesPanel.tsx:50-59
Timestamp: 2025-11-27T07:59:33.522Z
Learning: In workspaces/ballerina/data-mapper/src/components/DataMapper/SidePanel/QueryClauses/ClausesPanel.tsx, the `clause.properties.expression` property in the `fillDefaults` function does not require defensive null/undefined checks because it's a required property enforced by form validation in ClauseEditor.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
🧬 Code graph analysis (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
  • getValueForTextModeEditor (98-107)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/FloatingToggleButton.tsx (1)
  • FloatingToggleButton (30-45)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)
  • ChipExpressionEditorConfig (82-87)
  • deserializeValue (43-50)
  • deserializeValue (72-79)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
  • isSelectionOnToken (609-622)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)
  • deserializeValue (34-36)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • TOKEN_START_CHAR_OFFSET_INDEX (23-23)
🔇 Additional comments (7)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (5)

38-41: LGTM!

The new imports and configuration prop addition align well with the configuration-driven architecture. The default initialization using ChipExpressionEditorConfig() provides sensible fallback behavior when no configuration is provided.

Also applies to: 53-54, 96-96, 100-100


177-179: LGTM!

The token selection check prevents double-wrapping of values when the selection is already on a token, which is the correct behavior for variable insertion in templates.


367-371: LGTM! Mutation issue resolved.

The code now creates a copy of tokenStream using the spread operator before modifying it, which prevents unintended mutation of the original array returned from getExpressionTokens(). This resolves the issue flagged in the previous review.


121-121: LGTM!

The configuration-driven serialization/deserialization flow is consistent throughout:

  • onChange deserializes before emitting to parent
  • Sanitization operates on serialized values
  • Token fetching uses deserialized values

The styling enhancement at lines 456-460 appropriately applies full height when in expanded mode or when a custom height is provided via props.sx.

Also applies to: 355-355, 363-363, 456-460


348-353: I'm unable to access the repository due to persistent cloning failures. However, based on the code snippet provided in your review comment and the learnings from similar components in this codebase, here's my assessment:

Remove the unused serializedValue variable and clarify the normalization intent.

Line 348 computes serializedValue but never uses it—remove this line. The core concern about trim() masking whitespace differences is valid for expression editors, but without access to the full component context (whether this is in a useEffect, the configuration implementation, and parent component behavior), I cannot definitively confirm whether this causes render loops. The learnings note that similar components in this codebase intentionally manage prop changes carefully, suggesting the team is aware of these patterns.

Verify: (1) this code is guarded by appropriate dependency tracking to prevent unnecessary prop updates, and (2) that parent components don't re-render with the same normalized value.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (2)

44-66: LGTM!

The prop forwarding strategy is appropriate for the text mode editor:

  • Essential props are forwarded to ChipExpressionEditorComponent
  • isExpandedVersion={false} correctly reflects that this is always the minimized view
  • The separate expand button rendering (lines 60-66) provides a cleaner UX for text mode
  • Optional props like onTokenRemove, onTokenClick, and sx are intentionally not forwarded, which is reasonable for this simplified text editor wrapper

49-49: I'm unable to complete the verification of this review comment due to repository access limitations. The repository clone failed, and ChipExpressionEditorComponent appears to be an internal custom component specific to this project with no publicly available documentation.

To properly verify this concern about value transformation conflicts with configuration serialization, a developer would need to:

  1. Examine the getValueForTextModeEditor() function to see exactly what transformations it applies
  2. Inspect the ChipExpressionEditorDefaultConfiguration class implementations and their serializeValue()/deserializeValue() methods
  3. Trace the data flow through the component to confirm whether double-transformation occurs

Since this requires direct code inspection that I cannot perform, I'm marking this for manual verification.

Verify that getValueForTextModeEditor() quote removal doesn't conflict with configuration-driven serialization, especially for string template modes. The code applies getValueForTextModeEditor(props.value) before passing to ChipExpressionEditorComponent. Since the component now uses configuration-driven serialization/deserialization, confirm that this quote removal doesn't create unintended value transformations for different template modes.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (7)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)

21-24: Update helper name/comment or confirm behavior change for template interpolation

wrapInTemplateInterpolation now just returns value and ignores _inputMode, but the name and comment still suggest ${} wrapping in template mode. That’s a bit misleading and makes it hard to tell whether call sites still depend on interpolation happening here vs in the new config layer.

Either update the comment/name to reflect the no-op behavior or restore interpolation if any consumers still expect it. Also worth double‑checking all callers to ensure the removal of wrapping is fully intentional.

workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)

47-49: Document wrapping no longer depends on template mode; clean up comments and unused code, and verify syntax

A couple of things to flag together:

  • wrapInDocumentType now always emits <${documentType}>{content: ${content}} with no template interpolation; this is fine if interpolation is now entirely handled at the editor/config layer, but then:
    • The comment in handleItemSelect (“wrap in string interpolation only in template mode”) is no longer accurate.
    • The template vs non‑template branches for AI document types are effectively identical (onChange(fullPath, false) either way).
  • In URLInputModal.handleCreate, isTemplateMode is computed but never used; you can drop that local.

I’d update the comments to reflect that this helper always returns a raw document expression (and doesn’t care about InputMode), remove the unused isTemplateMode variable, and double‑check that, for TEMPLATE fields, whatever consumes onChange(wrappedValue, …) still produces valid Ballerina string‑template syntax around the <…>{content: …} snippet.

Also applies to: 146-178, 206-215

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)

29-29: TemplateMode correctly wires in RawTemplateEditorConfig

Passing configuration={new RawTemplateEditorConfig()} into ChipExpressionEditorComponent here aligns TemplateMode with the new configuration-driven editing model and should give you consistent string-template behavior between minimized and expanded views.

If RawTemplateEditorConfig ever gains state or becomes heavier, consider memoizing the instance, but as-is this looks fine.

Also applies to: 137-155

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx (1)

41-97: Tighten prop typing and confirm quote-wrapping semantics in RecordConfigPreviewEditor

Two points here:

  1. Props typing
    RecordConfigPreviewEditorProps is defined as:

    type RecordConfigPreviewEditorProps = Pick<ExpressionField, 'name' | 'value' | ...>;

    This only type-checks if there is a type/interface ExpressionField exported from ../../ExpressionField. If ExpressionField is just the React component (which is how it’s used in ExpressionEditor.tsx), it’d be clearer and safer to base this on the component props instead, e.g.:

    type RecordConfigPreviewEditorProps =
        Pick<React.ComponentProps<typeof ExpressionField>, 'name' | 'value' | ...>;

    or on a dedicated ExpressionFieldProps type if one exists.

  2. Quote wrapping on change
    handleOnChange always wraps the edited value in double quotes before calling onChange:

    const newValue = "\"" + value + "\"";
    onChange(newValue, updatedCursorPosition);

    That’s fine if getValueForTextModeEditor always strips a single pair of outer quotes and the stored representation is always exactly "foo". It’d be good to confirm:

    • There’s no path where value already includes quotes, which would lead to ""foo"".
    • Any escaping rules for embedded quotes are handled consistently elsewhere.

If those invariants hold, the component’s behavior is consistent; otherwise you may want to centralize the quote add/remove logic in a shared helper instead of duplicating it here.

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)

673-719: Verify new ExpressionField wiring and inline onChange behavior in TEMPLATE mode

In the Controller render, two notable changes:

  1. New primaryMode prop

    <ExpressionField
        inputMode={inputMode}
        primaryMode={getInputModeFromTypes(field.valueTypeConstraint)}
        ...
    />

    This gives ExpressionField direct access to the “natural” mode from the type constraint, which is good. Just make sure its prop type allows undefined if getInputModeFromTypes returns nothing (e.g., unconstrained or unsupported types).

  2. Inline onChange now uses updatedValue everywhere

    onChange(updatedValue);
    ...
    getExpressionEditorDiagnostics(
        (required ?? !field.optional) || updatedValue !== '',
        updatedValue,
        ...
    );
    ...
    const triggerCharacter =
        updatedCursorPosition > 0
            ? triggerCharacters.find((char) => updatedValue[updatedCursorPosition - 1] === char)
            : undefined;
    ...
    await retrieveCompletions(updatedValue, ..., updatedCursorPosition, triggerCharacter?);

    This is a deliberate shift from the previous rawExpression-based flow, and it lines up with the new configuration-driven string/template handling. However, it does mean that diagnostics and completions in TEMPLATE mode now operate on whatever representation ExpressionField surfaces as updatedValue, while the expanded editor path still derives and uses a rawValue.

    It’d be good to re-validate, in TEMPLATE mode specifically, that:

    • LS diagnostics/completions behave as expected when fed updatedValue from the inline editor.
    • Switching between inline and expanded editors doesn’t cause observable inconsistencies in how the same expression is parsed or tokenized.

If those checks pass, the new wiring looks consistent with the rest of the refactor.

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (1)

183-183: Consider memoizing configuration instances.

New configuration instances are created on every render. While these are lightweight objects, memoizing them would be more efficient and prevent unnecessary re-renders in child components that depend on reference equality.

const stringTemplateConfig = useMemo(() => new StringTemplateEditorConfig(), []);
const rawTemplateConfig = useMemo(() => new RawTemplateEditorConfig(), []);
// For ChipExpressionEditorConfig, include primaryMode in dependencies if moved outside

Also applies to: 204-204, 225-225

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1)

35-50: Duplicate serialization logic between configurations.

serializeValue and deserializeValue implementations are identical between StringTemplateEditorConfig and RawTemplateEditorConfig. Since they already call this.getSerializationPrefix() and this.getSerializationSuffix(), consider moving these methods to the base class ChipExpressionEditorDefaultConfiguration to reduce duplication.

Also applies to: 65-80

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 585575d and a18a690.

📒 Files selected for processing (12)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (2 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (4 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (2 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (8 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx (1 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (2 hunks)
  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (3 hunks)
  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/**/*.{ts,tsx} : Define all constants (node types, sizing, spacing) in src/resources/constants.ts and import them where needed instead of hardcoding values

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts
📚 Learning: 2025-11-24T22:16:28.380Z
Learnt from: dan-niles
Repo: wso2/vscode-extensions PR: 980
File: workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx:384-412
Timestamp: 2025-11-24T22:16:28.380Z
Learning: In the RichTextTemplateEditor component (workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx), token fetching on external `value` prop changes is intentionally disabled. Users cannot edit both the minimized and expanded editors simultaneously, so tokens only need to be generated based on user typing in the expanded editor view, not on external prop updates.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts
  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use React 18.2.0 features including concurrent rendering and automatic batching; avoid class components in favor of functional components with hooks

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/**/*.tsx : Implement lazy expansion of functions: only render function items when they are visible to improve performance for large function lists

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-24T14:51:49.267Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 998
File: workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx:113-132
Timestamp: 2025-11-24T14:51:49.267Z
Learning: In workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx, if `textFieldRef.current` is not undefined, `textFieldRef.current.inputElement` is guaranteed to exist. If `inputElement` doesn't exist when `current` exists, it's a fatal error that should reach the error boundary rather than being handled with defensive null checks.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
📚 Learning: 2025-11-26T06:34:09.752Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 653
File: workspaces/bi/bi-extension/src/test/e2e-playwright-tests/data-mapper/DataMapperUtils.ts:128-134
Timestamp: 2025-11-26T06:34:09.752Z
Learning: In workspaces/bi/bi-extension/src/test/e2e-playwright-tests/data-mapper/DataMapperUtils.ts, the goPrevViewBreadcrumb() method is only called when in a focused view, ensuring breadcrumbs are always present. No guard for empty breadcrumbs is needed.

Applied to files:

  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
📚 Learning: 2025-11-26T07:49:56.428Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 653
File: workspaces/bi/bi-extension/src/test/e2e-playwright-tests/data-mapper/DataMapperUtils.ts:136-141
Timestamp: 2025-11-26T07:49:56.428Z
Learning: In workspaces/bi/bi-extension/src/test/e2e-playwright-tests/data-mapper/DataMapperUtils.ts, the goPrevViewBackButton() method is only called when in a focused view, ensuring breadcrumbs are always present. No guard for empty breadcrumbs is needed.

Applied to files:

  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use Emotion for CSS-in-JS styling instead of inline styles or external CSS files

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
🧬 Code graph analysis (7)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (3)
  • getParsedExpressionTokens (111-137)
  • detectTokenPatterns (277-331)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (2)
  • StringTemplateEditorConfig (23-51)
  • RawTemplateEditorConfig (53-81)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx (1)
  • RecordConfigPreviewEditor (43-98)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (1)
  • TextModeEditor (43-72)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1)
  • RawTemplateEditorConfig (53-81)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
  • getValueForTextModeEditor (98-107)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/FloatingToggleButton.tsx (1)
  • FloatingToggleButton (30-45)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • getInputModeFromTypes (42-61)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (2)
  • InputMode (31-31)
  • getPropertyFromFormField (30-30)
workspaces/ballerina/ballerina-side-panel/src/components/editors/utils.ts (1)
  • getPropertyFromFormField (64-81)
🔇 Additional comments (9)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)

22-26: Exporting token index constants looks good

Making the TOKEN_*_INDEX constants public for reuse keeps the encoding of token streams centralized and avoids magic indices in other modules. The numeric values still match the existing TOKEN_TYPE_INDEX_MAP/chunk layout, so behavior stays intact.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (2)

223-243: Token parsing now correctly uses the post-transaction document

In the tokensChangeEffect branch of tokenField.update, switching to:

const currentValue = tr.newDoc.toString();
tokens = getParsedExpressionTokens(payload.tokens, currentValue);
compounds = detectTokenPatterns(tokens, currentValue);

keeps parsed token ranges and detected compounds aligned with the actual editor content after the transaction, which is what downstream consumers (chips, selection logic) expect.

This looks like a solid simplification compared to carrying around separate raw/sanitized values.


609-622: isSelectionOnToken behavior is well-scoped; confirm tokenField is always present when used

isSelectionOnToken:

  • Ignores selections that exactly match a compound’s [start, end) range.
  • Returns a token only when the selection exactly matches that token’s [start, end) range.
  • Otherwise returns undefined.

That aligns nicely with a UX where compound chips are treated as indivisible, and only “bare” tokens can be manipulated via this helper.

One thing to keep in mind: view.state.field(tokenField) will throw if the tokenField extension isn’t present in the state. Make sure this helper is only ever called in editors that have registered tokenField in their extensions (which seems true for the ChipExpressionEditor path), or switch to view.state.field(tokenField, false) with a guard if you anticipate reuse elsewhere.

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)

436-453: Input mode derivation from valueTypeConstraint looks consistent

The updated effect:

let newInputMode = getInputModeFromTypes(field.valueTypeConstraint);
if (isModeSwitcherRestricted()) {
    setInputMode(InputMode.EXP);
    return;
}
if (!newInputMode) {
    setInputMode(InputMode.EXP);
    return;
}
setInputMode(newInputMode);

is a nice simplification: it keeps RECORD mode special-cased, respects mode-switcher restrictions, and otherwise defers to the type constraint mapping, with a sensible EXP fallback.

Given inputModeRef is kept in sync via the separate useEffect, the rest of the component continues to see the right mode.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)

21-37: I encountered a repository clone failure, which prevents me from directly verifying the code. However, based on TypeScript's typing rules and the web search results, I need to verify specific details about the actual implementation:

To complete verification, I need you to provide:

  1. The actual return type annotation (if any) on getHelperValue in ChipExpressionEditorDefaultConfiguration
  2. The full implementation of RawTemplateEditorConfig.getHelperValue() from Configurations.tsx
  3. Whether TypeScript compilation currently produces errors for this code (and if so, which error codes)

Alternatively, if you have access to the repository, please share:

  • The contents of ChipExpressionDefaultConfig.ts (lines 1-40)
  • The contents of Configurations.tsx showing the RawTemplateEditorConfig class

This will allow me to verify whether the original review comment's claims about the typing contract mismatch are accurate.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (2)

346-353: Potential infinite update loop risk.

The logic at lines 348-353 calls props.onChange when deserializeValue !== props.value, which triggers a re-render with the new value. If the configuration's deserializeValue implementation doesn't stabilize (i.e., deserializeValue(deserializeValue(x)) !== deserializeValue(x)), this could cause an infinite loop.

Verify that all configuration implementations are idempotent—calling deserializeValue on an already-deserialized value should return the same value.


99-100: LGTM on configuration defaulting.

Using destructuring with a default value for the configuration prop is clean and ensures the component always has a valid configuration instance.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (1)

43-70: LGTM on the component refactor.

The simplification to delegate rendering to ChipExpressionEditorComponent with configuration-driven behavior is a clean approach. The value transformation via getValueForTextModeEditor is correctly preserved, and props are properly forwarded.

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (1)

83-100: I'll help you verify the review comment about the unused EditorRibbon component. Let me search the codebase to confirm whether it's truly unused.
<function_calls>

#!/bin/bash

Search for EditorRibbon usage across the entire codebase

rg "EditorRibbon" -n --type=tsx --type=ts --type=jsx --type=js


</function_calls>

Let me also check the file structure to understand the context better:
<function_calls>

#!/bin/bash

First, find the ExpressionField.tsx file

fd "ExpressionField.tsx" -type f

Also check if there are any barrel exports or re-exports

fd "index.tsx" -o "index.ts" | head -20


</function_calls>

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (1)

136-144: Move class definition outside the component (duplicate concern).

As previously noted, defining ChipExpressionEditorConfig inside the component creates a new class definition on every render, which is inefficient and unconventional.

Move this class to Configurations.tsx alongside the other configuration classes, or define it at module scope. Since the class needs access to primaryMode, modify its API to accept primaryMode as a constructor parameter:

class ChipExpressionEditorConfig extends ChipExpressionEditorDefaultConfiguration {
    constructor(private primaryMode: InputMode) {
        super();
    }
    
    getHelperValue(value: string, token?: ParsedToken): string {
        const isTextOrTemplateMode = this.primaryMode === InputMode.TEXT || this.primaryMode === InputMode.TEMPLATE;
        if (isTextOrTemplateMode && (!token || token.type !== TokenType.FUNCTION)) {
            return `\$\{${value}\}`;
        }
        return value;
    }
}

Then at line 225, instantiate it with the prop:

configuration={new ChipExpressionEditorConfig(primaryMode)}

Based on past review comments and coding best practices.

🧹 Nitpick comments (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (1)

225-225: Consider memoizing the configuration instance.

This instantiation creates a new ChipExpressionEditorConfig instance on every render. Once the class is moved outside the component (as noted in lines 136-144), consider memoizing the instantiation:

const config = useMemo(() => new ChipExpressionEditorConfig(primaryMode), [primaryMode]);

Then use:

configuration={config}

This prevents unnecessary re-instantiation when other props change.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a18a690 and 701e4bd.

📒 Files selected for processing (2)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-11-24T22:16:28.380Z
Learnt from: dan-niles
Repo: wso2/vscode-extensions PR: 980
File: workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx:384-412
Timestamp: 2025-11-24T22:16:28.380Z
Learning: In the RichTextTemplateEditor component (workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx), token fetching on external `value` prop changes is intentionally disabled. Users cannot edit both the minimized and expanded editors simultaneously, so tokens only need to be generated based on user typing in the expanded editor view, not on external prop updates.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
📚 Learning: 2025-11-24T14:51:49.267Z
Learnt from: KCSAbeywickrama
Repo: wso2/vscode-extensions PR: 998
File: workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx:113-132
Timestamp: 2025-11-24T14:51:49.267Z
Learning: In workspaces/ballerina/data-mapper/src/components/DataMapper/Header/ExpressionBar.tsx, if `textFieldRef.current` is not undefined, `textFieldRef.current.inputElement` is guaranteed to exist. If `inputElement` doesn't exist when `current` exists, it's a fatal error that should reach the error boundary rather than being handled with defensive null checks.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/**/*.{ts,tsx} : Define all constants (node types, sizing, spacing) in src/resources/constants.ts and import them where needed instead of hardcoding values

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use React 18.2.0 features including concurrent rendering and automatic batching; avoid class components in favor of functional components with hooks

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use Emotion for CSS-in-JS styling instead of inline styles or external CSS files

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/nodes/EntryNode/components/**/*.tsx : Implement lazy expansion of functions: only render function items when they are visible to improve performance for large function lists

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
🧬 Code graph analysis (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
  • InputMode (31-31)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)
  • ChipExpressionEditorConfig (81-86)
  • StringTemplateEditorConfig (23-50)
  • RawTemplateEditorConfig (52-79)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx (1)
  • RecordConfigPreviewEditor (43-98)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (1)
  • TextModeEditor (43-72)
🔇 Additional comments (5)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5)

30-30: LGTM!

The new imports are clean and necessary for the configuration-driven functionality.

Also applies to: 34-37


41-41: LGTM!

The primaryMode prop enables dynamic configuration of the default editor's behavior based on the primary mode context.


145-166: LGTM!

The RECORD mode integration is clean and passes all necessary props to RecordConfigPreviewEditor.


167-187: LGTM!

The TEXT mode rendering correctly uses StringTemplateEditorConfig to handle string template wrapping and variable insertion.


188-208: LGTM!

The TEMPLATE mode rendering correctly uses RawTemplateEditorConfig to handle raw template wrapping and variable insertion.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)

346-374: Avoid mutating tokenStream from the RPC manager and clarify serialized vs. deserialized usage

Two points here:

  1. Mutation of the original token stream

prefixCorrectedTokenStream is currently just an alias of tokenStream, so decrementing TOKEN_START_CHAR_OFFSET_INDEX mutates the array returned by expressionEditorRpcManager.getExpressionTokens(). If that result is cached or reused, this can cause subtle bugs.

You can fix this by copying before adjustment:

-            const tokenStream = await expressionEditorRpcManager?.getExpressionTokens(
-                deserializeValue,
+            const tokenStream = await expressionEditorRpcManager?.getExpressionTokens(
+                deserializeValue,
                 props.fileName,
                 startLine !== undefined ? startLine : undefined
             );
-            const prefixCorrectedTokenStream = tokenStream;
-            if (tokenStream && tokenStream.length >= 5) {
-                prefixCorrectedTokenStream[TOKEN_START_CHAR_OFFSET_INDEX] -= configuration.getSerializationPrefix().length;
-            }
+            let prefixCorrectedTokenStream = tokenStream;
+            if (Array.isArray(tokenStream) && tokenStream.length >= 5) {
+                prefixCorrectedTokenStream = [...tokenStream];
+                prefixCorrectedTokenStream[TOKEN_START_CHAR_OFFSET_INDEX] -=
+                    configuration.getSerializationPrefix().length;
+            }

This addresses the earlier concern about mutating the original token stream while keeping the prefix offset correction.

  1. Serialized vs. deserialized value alignment

Tokens are fetched using deserializeValue (the fully wrapped/raw expression), while the editor document uses sanitizedValue derived from serializedValue. As long as the only structural difference is the configurable prefix/suffix (which you correct via getSerializationPrefix().length), this is fine. If sanitizedExpression performs additional transformations (beyond stripping prefixes/suffixes), token offsets may drift. In that case, consider using mapSanitizedToRaw from ../utils to keep token positions aligned with the sanitized document.

🧹 Nitpick comments (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1)

89-104: Token parameter in getHelperValue is currently unused from ChipExpressionEditor

PrimaryModeChipExpressionEditorConfig.getHelperValue branches on token?.type === TokenType.FUNCTION, but in ChipExpressionEditor.tsx the call site currently invokes configuration.getHelperValue(value) without passing the ParsedToken from isSelectionOnToken. That makes the token‑aware logic effectively unreachable from the chip editor path.

To fully benefit from this API, consider threading the token through from the editor, e.g.:

-        const selectionIsOnToken = isSelectionOnToken(currentSelection.from, currentSelection.to, view);
-        const newValue = selectionIsOnToken ? value : configuration.getHelperValue(value);
+        const tokenOnSelection = isSelectionOnToken(currentSelection.from, currentSelection.to, view);
+        const newValue = tokenOnSelection
+            ? value
+            : configuration.getHelperValue(value, tokenOnSelection);

This keeps the “replace existing token verbatim” behavior while allowing configs to specialize on token type when inserting new content.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (3)

96-101: Configuration prop and default instance wiring are fine (minor optimization possible)

Allowing a configuration?: ChipExpressionEditorDefaultConfiguration prop and defaulting to new ChipExpressionEditorConfig() gives good flexibility. Since the default is instantiated on every render when configuration is undefined, if these configs ever gain internal state or heavier construction, you might wrap the default in useMemo:

-    const { configuration = new ChipExpressionEditorConfig() } = props;
+    const configuration = useMemo(
+        () => props.configuration ?? new ChipExpressionEditorConfig(),
+        [props.configuration]
+    );

Not required with the current stateless configs, but worth considering if they evolve.


119-133: onChange now emits deserialized (wrapped) values – verify external callers expect this

Using configuration.deserializeValue(newValue) before calling props.onChange means parents always receive the fully wrapped expression (e.g., string \...`` or backticked templates) rather than the “editor‑internal” representation.

This is consistent with the new config abstraction but does subtly change the contract for existing consumers of ChipExpressionEditorComponent. Please double‑check all call sites to ensure they now expect serialized/typed string templates instead of the raw inner content.


170-180: Leverage the returned token when delegating to getHelperValue

isSelectionOnToken returns a ParsedToken, but here it’s only used as a truthy flag to decide between value and configuration.getHelperValue(value). As a result, configuration implementations that branch on the optional token argument (e.g., to treat FUNCTION tokens specially) can’t take advantage of that context.

A minimal improvement that preserves current behavior while making configs more expressive:

-        const selectionIsOnToken = isSelectionOnToken(currentSelection.from, currentSelection.to, view);
-        const newValue = selectionIsOnToken ? value : configuration.getHelperValue(value);
+        const tokenOnSelection = isSelectionOnToken(currentSelection.from, currentSelection.to, view);
+        const newValue = tokenOnSelection
+            ? value
+            : configuration.getHelperValue(value, tokenOnSelection);

You may also want to confirm the interaction with options.replaceFullText: when replacing the full expression, using the original currentSelection to detect “on token” might be surprising if the saved selection happened to be over a token.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 701e4bd and 0b2a285.

📒 Files selected for processing (3)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (8 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-24T22:16:28.380Z
Learnt from: dan-niles
Repo: wso2/vscode-extensions PR: 980
File: workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx:384-412
Timestamp: 2025-11-24T22:16:28.380Z
Learning: In the RichTextTemplateEditor component (workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RichTextTemplateEditor/RichTextTemplateEditor.tsx), token fetching on external `value` prop changes is intentionally disabled. Users cannot edit both the minimized and expanded editors simultaneously, so tokens only need to be generated based on user typing in the expanded editor view, not on external prop updates.

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use React 18.2.0 features including concurrent rendering and automatic batching; avoid class components in favor of functional components with hooks

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/components/**/*.tsx : Use Emotion for CSS-in-JS styling instead of inline styles or external CSS files

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
📚 Learning: 2025-11-25T06:34:10.812Z
Learnt from: CR
Repo: wso2/vscode-extensions PR: 0
File: workspaces/ballerina/component-diagram/AGENTS.md:0-0
Timestamp: 2025-11-25T06:34:10.812Z
Learning: Applies to workspaces/ballerina/component-diagram/src/**/*.{ts,tsx} : Define all constants (node types, sizing, spacing) in src/resources/constants.ts and import them where needed instead of hardcoding values

Applied to files:

  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
🧬 Code graph analysis (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)
  • ChipExpressionEditorConfig (82-87)
  • deserializeValue (43-50)
  • deserializeValue (72-79)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
  • isSelectionOnToken (609-622)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)
  • deserializeValue (34-36)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • TOKEN_START_CHAR_OFFSET_INDEX (23-23)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (2)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • ParsedToken (104-109)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
  • InputMode (31-31)
🔇 Additional comments (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)

24-50: StringTemplateEditorConfig serialization/deserialization and helper wrapping look consistent

The helper wrapping (${value}) for non-function tokens and raw passthrough for function tokens, plus the `string ``/\ prefix–suffix aware serialize/deserialize, are coherent and match the intended Ballerina string template shape. The logic also safely normalizes legacy unwrapped values on first load. No correctness issues from this file’s side.


53-80: RawTemplateEditorConfig behavior is symmetric and sound

Using bare backticks as serialization prefix/suffix with the same ${value} wrapping rule gives a clean “raw template” variant. serializeValue and deserializeValue mirror the StringTemplate behavior and are side‑effect free apart from the intentional normalization of unwrapped values. This looks correct.


82-87: ChipExpressionEditorConfig default helper behavior is reasonable

Providing a default implementation that only wraps non‑FUNCTION tokens in ${…} and passes through function tokens is a sensible baseline for non‑string‑template modes. Given the simplicity and absence of serialization overrides here, there’s no obvious risk.

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)

455-459: Layout wrapper for editor height handling looks good

The additional wrapper div with conditional { height: '100%' } when expanded or when an explicit sx.height is provided should resolve previous height inconsistencies without regressing the default max‑height behavior on the inner editor container. This change is straightforward and low‑risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add string template support for the expression editors

1 participant