Skip to content

Comments

Implement boolean editor for expression fields#1017

Merged
kanushka merged 15 commits intowso2:mainfrom
senithkay:implement-boolean-editor
Dec 3, 2025
Merged

Implement boolean editor for expression fields#1017
kanushka merged 15 commits intowso2:mainfrom
senithkay:implement-boolean-editor

Conversation

@senithkay
Copy link
Contributor

@senithkay senithkay commented Nov 28, 2025

Purpose

The existing boolean input handling relies on free-text entry within the ChipExpressionEditor, which allows users to enter invalid or non-boolean values. This leads to inconsistent validation and a poor user experience when working with boolean expressions.
This PR introduces a controlled mechanism to ensure only valid boolean values can be selected.

Resolves: wso2/product-ballerina-integrator#2027

Goals

  • Implement strict boolean input mode.
  • Provide a clear and guided selection method for boolean values.
  • Improve user experience and maintain consistency with other controlled input types.

Approach

  • A new dropdown-based boolean selector component is introduced to handle boolean inputs.
  • Instead of extending or modifying the ChipExpressionEditor, the boolean field now uses a dedicated dropdown component with two options: true and false.
  • Implement current value based default mode selection to open the correct editor based on current field value.
  • This approach enhances usability while keeping the implementation simple and consistent with UI patterns.

Summary by CodeRabbit

  • New Features

    • Boolean and Number editors, a record preview editor, and per-editor configuration options for tailored helper, serialization, and adornment behavior.
  • Improvements

    • Safer input-mode switching with user warnings for boolean transitions.
    • More consistent template/helper interpolation and document wrapping.
    • Improved completions/diagnostics and selection-aware helper insertion for more accurate editing suggestions.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

Walkthrough

Adds per-editor configuration to the chip-based editors, new specialized editors (Boolean, Number, Record preview), refactors mode-switch logic and text-mode to reuse ChipExpressionEditor, exposes token index constants and selection helper, and removes template interpolation emission from the helper pane.

Changes

Cohort / File(s) Summary
Mode & Field wiring
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx
Centralizes input-mode switching via a queued ref (targetInputModeRef) and boolean-safety checks; adds primaryMode prop to ExpressionField; TemplateMode now supplies RawTemplateEditorConfig to the chip editor.
ChipExpressionEditor configuration & behavior
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
Adds ChipExpressionEditorDefaultConfiguration and multiple configuration subclasses; ChipExpressionEditor accepts an optional configuration prop and uses it for helper mapping, serialization/deserialization, plugins, adornment, and helper-pane visibility.
New editors
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsx, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
Adds BooleanEditor (dropdown none/true/false), NumberExpressionEditor (numeric input filtering via config), and RecordConfigPreviewEditor (quoted text-mode preview with expand toggle).
Text mode refactor
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
Replaces prior FormExpressionEditor-based implementation with a thin wrapper around ChipExpressionEditorComponent, forwarding props and using getValueForTextModeEditor.
Token utilities & CodeUtils
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts, workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts
Exports token index constants (TOKEN_*_INDEX), removes rawValue/sanitizedValue from TokensChangePayload, and adds isSelectionOnToken(from,to,view) to detect exact-token selections.
InputMode enum update
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts
Adds NUMBER and BOOLEAN to InputMode and maps int -> NUMBER and boolean -> BOOLEAN in INPUT_MODE_MAP.
Helper pane / template interpolation changes
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx, workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts
Removes interpolation flag and makes wrapInTemplateInterpolation return the raw input (no template interpolation) and updates call sites accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • ChipExpressionEditor configuration subclasses (serialization/deserialization and helper mapping).
    • Mode-switch logic in ExpressionEditor (targetInputModeRef, isExpToBooleanSafe, warning queue).
    • Token selection/synchronization changes and removal of raw/sanitized fields (CodeUtils callers).
    • Value round-trip correctness when deserialize/serialize are applied during sync and helper inserts.

Possibly related PRs

Suggested reviewers

  • hevayo
  • gigara
  • kanushka

"I nibble keys and hop on code,
Booleans choose, no errors bode.
Templates bare, tokens find their place,
Numbers pruned with careful grace,
A rabbit cheers the editor's pace 🐇"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Multiple out-of-scope changes detected beyond boolean editor implementation: ChipExpressionEditor configuration system, InputMode refactoring, token utilities exports, template interpolation removal, and expression editor mode-switching logic. Remove unrelated changes (configurations system, template interpolation removal, mode-switching refactoring) and focus solely on BooleanEditor implementation, integration, and InputMode.BOOLEAN enum addition.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Implement boolean editor for expression fields' clearly summarizes the main change: introducing a new boolean editor component for handling boolean expressions in a controlled manner.
Description check ✅ Passed The PR description includes all critical sections: Purpose (with issue link), Goals, and Approach. It explains the problem, objectives, and implementation strategy clearly.
Linked Issues check ✅ Passed The PR fully addresses issue #2027 by implementing a dropdown-based BooleanEditor component, integrating it into ExpressionField rendering logic, and ensuring only valid boolean values can be selected.
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

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx (1)

161-177: Remove unused variable.

The isTemplateMode variable is declared at line 161 but is no longer used after the template mode handling was removed at line 166.

Apply this diff to remove the unused variable:

-        // Check if we're in template mode
-        const isTemplateMode = inputMode === InputMode.TEMPLATE;
-
         if (isAIDocumentType) {
🧹 Nitpick comments (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)

22-46: Consider adding explicit return type annotations for better type safety.

The abstract class provides good default implementations, but explicit return types would improve clarity and catch type mismatches at compile time.

-export abstract class ChipExpressionEditorDefaultConfiguration {
-    getHelperValue(value: string, token?: ParsedToken) {
+export abstract class ChipExpressionEditorDefaultConfiguration {
+    getHelperValue(value: string, token?: ParsedToken): string {
         return value;
     }
-    getSerializationPrefix() {
+    getSerializationPrefix(): string {
         return "";
     }
-    getSerializationSuffix() {
+    getSerializationSuffix(): string {
         return "";
     }
-    serializeValue(value: string) {
-        return value
+    serializeValue(value: string): string {
+        return value;
     }
-    deserializeValue(value: string) {
+    deserializeValue(value: string): string {
         return value;
     }
-    showHelperPane() {
+    showHelperPane(): boolean {
         return true;
     }
-    getPlugins() {
+    getPlugins(): any[] {
         return [];
     }
-    getAdornment() {
+    getAdornment(): React.ComponentType<{ onClick: () => void }> {
         return (FXButton)
     }
 }

Note: getPlugins() returning any[] is intentional given CodeMirror's flexible extension system, but you may want to use a more specific type like Extension[] from @codemirror/state if applicable.

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

563-566: Add null guard before setting input mode.

targetInputModeRef.current could theoretically be null if handleModeSwitchWarningContinue is invoked unexpectedly. A defensive check prevents passing null to setInputMode.

 const handleModeSwitchWarningContinue = () => {
+    if (targetInputModeRef.current === null) return;
     setInputMode(targetInputModeRef.current);
     setShowModeSwitchWarning(false);
 };
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (2)

24-54: StringTemplateEditorConfig serialization logic looks consistent

Helper wrapping and the prefix/suffix-based serialize/deserialize behavior are coherent and symmetrical, so round-tripping string \...`values should be safe. If you want to micro-optimize/readability, you could computeconst trimmed = value.trim();once inserializeValue/deserializeValueinstead of callingtrim()` multiple times, but it’s not required.


56-86: RawTemplateEditorConfig is correct but largely duplicates StringTemplateEditorConfig

Behavior for raw templates looks sound (\...`wrapping and helper behavior match expectations). Given how close this is toStringTemplateEditorConfig` (only the serialization prefix differs), you might later consider pulling the common serialize/deserialize + helper logic into a small shared helper/base to reduce duplication and keep future fixes in one place.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cada6a6 and 532ac57.

📒 Files selected for processing (15)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (2 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (6 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (5 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.tsx (1 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 (9 hunks)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1 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/NumberExpressionEditor/NumberEditor.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 (11)
📓 Common learnings
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.
📚 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/NumberExpressionEditor/NumberEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx
  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
  • 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
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.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/utils.ts
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsx
  • workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
  • 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/**/*.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-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.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/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.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/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/ExpressionEditor.tsx
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx
📚 Learning: 2025-11-27T07:59:33.534Z
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.534Z
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/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/**/*.config.{js,ts} : Use minimatch-compatible glob patterns for file matching in build and test configuration files

Applied to files:

  • 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 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 (10)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsx (3)
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsx (2)
  • ExpressionField (39-82)
  • ExpressionField (103-250)
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/FloatingButtonIcons.tsx (1)
  • ExpandIcon (43-52)
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 (56-86)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/types.ts (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
  • InputMode (31-31)
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/ExpressionField.tsx (7)
workspaces/ballerina/ballerina-side-panel/src/components/Form/types.ts (1)
  • FormField (29-65)
workspaces/ballerina/ballerina-side-panel/src/components/editors/index.ts (1)
  • InputMode (31-31)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/BooleanEditor/BooleanEditor.tsx (1)
  • BooleanEditor (48-63)
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 (40-69)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)
  • StringTemplateEditorConfig (24-54)
  • RawTemplateEditorConfig (56-86)
  • PrimaryModeChipExpressionEditorConfig (95-110)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsx (1)
  • NumberExpressionEditor (57-77)
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)
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/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsx (1)
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/ChipExpressionEditor.tsx (4)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx (3)
  • ChipExpressionEditorConfig (88-93)
  • deserializeValue (46-53)
  • deserializeValue (78-85)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (2)
  • isSelectionOnToken (609-622)
  • tokensChangeEffect (197-197)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)
  • deserializeValue (35-37)
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/BooleanEditor/BooleanEditor.tsx (1)
workspaces/ballerina/ballerina-core/src/interfaces/bi.ts (1)
  • OptionProps (523-528)
🔇 Additional comments (30)
workspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.ts (1)

21-23: I'll verify the review comment by examining the function's usage across the codebase and understanding the architectural implications of this change.
<function_calls>

#!/bin/bash

Find all usages of wrapInTemplateInterpolation

rg -n --type=ts --type=tsx 'wrapInTemplateInterpolation' -C3


</function_calls>

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

47-49: LGTM - Function signature simplified.

The removal of the interpolation parameter from wrapInDocumentType aligns with the centralized template handling approach described in the PR objectives.


213-214: LGTM - Consistent wrapping without interpolation.

The URL insertion correctly uses wrapInDocumentType without an interpolation flag, consistent with the architectural changes to centralize template handling.

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

23-26: LGTM - InputMode enum properly extended.

The addition of NUMBER and BOOLEAN modes enables type-specific editing paths, which is central to the PR's objective of introducing controlled boolean input.


28-33: LGTM - Type mappings correctly updated.

The INPUT_MODE_MAP now properly maps int to InputMode.NUMBER and boolean to InputMode.BOOLEAN, enabling automatic mode selection based on field types.

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

22-26: LGTM - Token index constants exposed for external use.

Exporting these constants to the public API enables token-aware helper utilities (like isSelectionOnToken mentioned in the PR summary) to function correctly. The numeric values remain unchanged, ensuring backward compatibility.

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

27-39: LGTM - Styled component for hover interaction.

The styled component properly implements hover-reveal behavior for the expand button, consistent with the UI patterns used in other editor components.


62-65: LGTM - Quote wrapping ensures proper serialization.

The handleOnChange correctly wraps the editor value in quotes before calling onChange, ensuring that RECORD mode values are properly serialized. This complements the quote stripping at line 74.


89-95: LGTM - Conditional expand button rendering.

The expand button is properly conditionally rendered only when onOpenExpandedMode is provided and the editor is not already expanded, preventing redundant UI elements.

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

39-42: LGTM - Interface extended for multi-mode support.

The addition of field and primaryMode props enables the component to provide proper context to specialized editors and drive configuration-based behavior.


138-146: LGTM - Boolean mode properly integrated.

The BooleanEditor is correctly rendered for BOOLEAN input mode with the necessary props (field, value, onChange), fulfilling the PR's primary objective of introducing controlled boolean input.


169-210: LGTM - TEXT and TEMPLATE modes properly differentiated.

The explicit separation of TEXT (using StringTemplateEditorConfig) and TEMPLATE (using RawTemplateEditorConfig) modes improves clarity and enables appropriate serialization behavior for each mode.


232-249: LGTM - Default case with primary mode configuration.

The fallback to ChipExpressionEditorComponent with PrimaryModeChipExpressionEditorConfig ensures that expression mode and other inputs not covered by specialized editors still function correctly with primary-mode-aware helper value logic.

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

30-46: LGTM - Dropdown items provide complete boolean selection.

The dropdown items correctly include:

  • None Selected (empty value) for clearing the field
  • True and False for valid boolean values

This prevents invalid boolean entries, fulfilling the PR's primary objective.


48-63: LGTM - Boolean editor implementation is sound.

The component:

  • Uses field.key as the dropdown ID for proper form integration
  • Trims the value for display consistency
  • Passes cursor position as value length (appropriate for dropdown selection)
  • Enforces full width for consistent layout
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsx (1)

29-29: LGTM - Configuration-driven template mode.

The addition of RawTemplateEditorConfig to the template mode editor enables consistent template serialization behavior across minimized and expanded editor views, aligning with the broader architectural improvements in this PR.

Also applies to: 154-154

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

57-77: LGTM - Number editor properly configured.

The NumberExpressionEditor correctly:

  • Uses getValueForTextModeEditor to normalize the display value
  • Passes NumberExpressionEditorConfig to enforce numeric-only input
  • Forwards all necessary props to ChipExpressionEditorComponent
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (3)

539-541: LGTM!

The isExpToBooleanSafe helper correctly validates boolean-like expressions by trimming whitespace and performing case-insensitive comparison against "true" and "false".


673-708: LGTM!

The onChange handler now consistently uses updatedValue for diagnostics, trigger character detection, and completion retrieval, which simplifies the value flow and eliminates potential inconsistencies.


661-664: LGTM!

The ExpressionField now receives the field and primaryMode props, enabling it to be mode-aware and properly configure child editors based on the field's value type constraint.

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

44-59: LGTM!

The refactor cleanly delegates to ChipExpressionEditorComponent while correctly transforming the value through getValueForTextModeEditor. The essential props for text mode editing are properly forwarded.


60-66: LGTM!

The expand button is correctly rendered only when onOpenExpandedMode is provided and the editor is not already in expanded mode.

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

224-231: LGTM!

Deriving currentValue from tr.newDoc.toString() is the correct approach for token parsing, as it uses the document's authoritative state after the transaction rather than requiring external payload fields.

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

177-179: LGTM!

The token-aware helper value selection correctly distinguishes between replacing an existing token (use raw value) vs. inserting at cursor position (apply template wrapping via configuration.getHelperValue).


368-372: Verify prefix length doesn't exceed token offset.

The prefix correction could result in a negative token offset if configuration.getSerializationPrefix().length exceeds the original tokenStream[TOKEN_START_CHAR_OFFSET_INDEX] value. This might cause rendering issues.

Consider adding a bounds check:

 let prefixCorrectedTokenStream = tokenStream;
 if (tokenStream && tokenStream.length >= 5) {
     prefixCorrectedTokenStream = [...tokenStream];
-    prefixCorrectedTokenStream[TOKEN_START_CHAR_OFFSET_INDEX] -= configuration.getSerializationPrefix().length;
+    const prefixLength = configuration.getSerializationPrefix().length;
+    prefixCorrectedTokenStream[TOKEN_START_CHAR_OFFSET_INDEX] = Math.max(
+        0,
+        prefixCorrectedTokenStream[TOKEN_START_CHAR_OFFSET_INDEX] - prefixLength
+    );
 }

456-456: Empty onClick handler passed to adornment.

The adornment receives onClick: () => {} which does nothing. If the FXButton (or other adornment) is expected to trigger an action, this should be wired to actual functionality.

Is the empty click handler intentional? If the adornment should trigger mode switching or other actions, consider passing the appropriate handler.


99-100: LGTM!

The configuration prop with a default ChipExpressionEditorConfig ensures backward compatibility while enabling per-instance customization.


467-476: LGTM!

The helper pane visibility is now configuration-driven via showHelperPane(), allowing specialized editor configurations (like NumberExpressionEditor) to disable the helper pane as needed.

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

88-93: ChipExpressionEditorConfig helper wrapping matches existing template behavior

The getHelperValue override keeping FUNCTION tokens unwrapped and wrapping everything else as ${value} aligns with the pattern used in the template configs and should preserve existing helper behavior for the chip editor.


95-110: PrimaryModeChipExpressionEditorConfig correctly gates interpolation based on mode

Using primaryMode to only wrap helper values in ${…} for InputMode.TEXT and InputMode.TEMPLATE, while passing through values unchanged for other modes or FUNCTION tokens, fits the multi‑mode design and supports the boolean-editor goal (booleans won’t be force‑wrapped). Just keep in mind that if you introduce new input modes that also require interpolation, this isTextOrTemplateMode check will need updating.

Comment on lines +609 to +622
export const isSelectionOnToken = (from: number, to: number, view: EditorView): ParsedToken => {
if (!view) return undefined;
const { tokens, compounds } = view.state.field(tokenField);

const matchingCompound = compounds.find(
compound => compound.start === from && compound.end === to
);
if (matchingCompound) return undefined;

const matchingToken = tokens.find(
token => token.start === from && token.end === to
);
return matchingToken;
}; No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Return type should be ParsedToken | undefined.

The function returns undefined in multiple cases (line 610, 616, and when no token matches), but the return type is declared as ParsedToken.

-export const isSelectionOnToken = (from: number, to: number, view: EditorView): ParsedToken => {
+export const isSelectionOnToken = (from: number, to: number, view: EditorView): ParsedToken | undefined => {
     if (!view) return undefined;
     const { tokens, compounds } = view.state.field(tokenField);
 
     const matchingCompound = compounds.find(
         compound => compound.start === from && compound.end === to
     );
     if (matchingCompound) return undefined;
 
     const matchingToken = tokens.find(
         token => token.start === from && token.end === to
     );
     return matchingToken;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const isSelectionOnToken = (from: number, to: number, view: EditorView): ParsedToken => {
if (!view) return undefined;
const { tokens, compounds } = view.state.field(tokenField);
const matchingCompound = compounds.find(
compound => compound.start === from && compound.end === to
);
if (matchingCompound) return undefined;
const matchingToken = tokens.find(
token => token.start === from && token.end === to
);
return matchingToken;
};
export const isSelectionOnToken = (from: number, to: number, view: EditorView): ParsedToken | undefined => {
if (!view) return undefined;
const { tokens, compounds } = view.state.field(tokenField);
const matchingCompound = compounds.find(
compound => compound.start === from && compound.end === to
);
if (matchingCompound) return undefined;
const matchingToken = tokens.find(
token => token.start === from && token.end === to
);
return matchingToken;
};
🤖 Prompt for AI Agents
In
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts
around lines 609 to 622, the function isSelectionOnToken currently declares a
return type of ParsedToken but returns undefined in several branches; change the
function signature return type to ParsedToken | undefined and update any related
type annotations/usages to accept undefined so the three return paths (no view,
matchingCompound, or no matching token) are correctly typed as returning
undefined.

Comment on lines 340 to +354
useEffect(() => {
if (props.value == null || !viewRef.current) return;
const serializedValue = configuration.serializeValue(props.value);
const deserializeValue = configuration.deserializeValue(props.value);
if (deserializeValue.trim() !== props.value.trim()) {
props.onChange(deserializeValue, deserializeValue.length);
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential infinite update loop if deserializeValue consistently differs from input.

The check at line 351 compares deserializeValue.trim() against props.value.trim(). If a configuration's deserializeValue always adds/modifies content (e.g., wrapping with prefix/suffix), this will call props.onChange on every render, causing an infinite loop.

Consider adding a guard to track whether the value was already normalized, or compare against the serialized form:

 useEffect(() => {
     if (props.value == null || !viewRef.current) return;
     const serializedValue = configuration.serializeValue(props.value);
     const deserializeValue = configuration.deserializeValue(props.value);
-    if (deserializeValue.trim() !== props.value.trim()) {
-        props.onChange(deserializeValue, deserializeValue.length);
-        return
-    }
+    // Only normalize if the value doesn't already have the expected format
+    const isAlreadyNormalized = serializedValue === props.value || 
+        (props.value.startsWith(configuration.getSerializationPrefix()) && 
+         props.value.endsWith(configuration.getSerializationSuffix()));
+    if (!isAlreadyNormalized && deserializeValue.trim() !== props.value.trim()) {
+        props.onChange(deserializeValue, deserializeValue.length);
+        return;
+    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx
around lines 347-354, the current trim-based comparison can trigger
props.onChange every render if deserializeValue continually mutates the input;
prevent an infinite update loop by adding a guard: either compare
deserializeValue against the serializedValue (use
configuration.serializeValue(props.value)) instead of props.value, or introduce
a ref flag (e.g., normalizationAppliedRef) that you set before calling
props.onChange and check at the top of the effect to skip re-applying
normalization; ensure the flag is reset only when props.value actually changes
so normalization runs once per incoming value.

Comment on lines +35 to +37
getAdornment(): ({ onClick }: { onClick?: () => void; }) => JSX.Element {
return () => null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find and read the Configurations.tsx file
fd "Configurations.tsx" -t f

Repository: wso2/vscode-extensions

Length of output: 188


🌐 Web query:

TypeScript JSX.Element null return type mismatch strict mode

💡 Result:

Cause

  • With TypeScript strict null checks on, JSX.Element does not include null, so a component that sometimes returns null conflicts with a declared return type of JSX.Element. [1][2]

Fixes (pick one)

  • Allow null in the return type:
    const MyComp = (): JSX.Element | null => { return condition ?
    : null; }. [1]
  • Use a broader React type that already allows null/undefined, e.g. React.ReactNode or React.ReactElement | null:
    const MyComp = (): React.ReactNode => condition ?
    : null;. [2]
  • If you’re using React.FC, its render signature already permits returning null (but be careful with FC’s other semantics). [1][2]

Notes

  • Don’t disable strictNullChecks; prefer explicit union types (| null) or a more appropriate React return type. [3]

Sources

  • [1] Discussion showing JSX.Element doesn’t include null and solution to use JSX.Element | null.
  • [2] MUI/React typing examples recommending JSX.Element | null or ReactNode.
  • [3] TypeScript strictNullChecks explanation.

Fix TypeScript type mismatch in getAdornment implementations

getAdornment is declared to return ({ onClick }: { onClick?: () => void }) => JSX.Element, but the implementation returns () => null. TypeScript's strict null checks will reject this because null is not assignable to JSX.Element, and the returned function signature doesn't match the expected parameter.

Update both occurrences (lines 35-37 and 67-69) to accept the expected props and return a valid JSX element:

-    getAdornment(): ({ onClick }: { onClick?: () => void; }) => JSX.Element {
-        return () => null;
-    }
+    getAdornment(): ({ onClick }: { onClick?: () => void }) => JSX.Element {
+        return ({ onClick }: { onClick?: () => void }) => <></>;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getAdornment(): ({ onClick }: { onClick?: () => void; }) => JSX.Element {
return () => null;
}
getAdornment(): ({ onClick }: { onClick?: () => void }) => JSX.Element {
return ({ onClick }: { onClick?: () => void }) => <></>;
}
🤖 Prompt for AI Agents
In
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsx
around lines 35-37 and 67-69, the getAdornment implementations return () => null
which mismatches the declared type ({ onClick }: { onClick?: () => void }) =>
JSX.Element; change each implementation to accept the expected props parameter
(e.g., ({ onClick }) => ...) and return a valid JSX.Element (for example a
button or empty fragment) instead of null, ensuring the returned element calls
or forwards onClick appropriately and satisfies JSX.Element typing.

Comment on lines +25 to +55
class NumberExpressionEditorConfig extends ChipExpressionEditorDefaultConfiguration {
showHelperPane() {
return false;
}
getAdornment() {
return () => null;
}
getPlugins() {
const numericOnly = EditorState.changeFilter.of(tr => {
let allow = true;
tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
const text = inserted.toString();

if (!/^[0-9.]*$/.test(text)) {
allow = false;
return;
}

if ((text.match(/\./g) || []).length > 1) {
allow = false;
return;
}
});

return allow;
});

return [numericOnly];

}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for int type definitions and usage patterns
echo "=== Searching for int type references ==="
rg -n "type.*int|int.*type" --type=ts -A 3 -B 3 | head -80

echo -e "\n=== Looking for numeric validation patterns ==="
rg -n "numberValidation|numericPattern|intPattern|numeric.*filter" --type=ts -A 3 -B 3 | head -80

echo -e "\n=== Looking at NumberEditor file and related editors ==="
fd -e "ts" -e "tsx" "NumberEditor|ExpressionEditor" --type f | head -20

Repository: wso2/vscode-extensions

Length of output: 188


🏁 Script executed:

#!/bin/bash
# Search for EditorState.changeFilter usage patterns to understand typical numeric validation
echo "=== Searching for changeFilter patterns ==="
rg -n "changeFilter" --type=ts -A 5 -B 3 | head -100

echo -e "\n=== Looking for negative number or minus sign handling ==="
rg -n "minus|negative|-\[0-9\]|\\\-" --type=ts | grep -i "filter\|validate\|editor" | head -50

Repository: wso2/vscode-extensions

Length of output: 188


🏁 Script executed:

#!/bin/bash
# Check the specific file structure and imports to understand the type system
echo "=== Checking the NumberEditor file content ==="
fd -n "NumberEditor.tsx" --type f | xargs -I {} head -100 "{}"

echo -e "\n=== Looking for ChipExpressionEditorDefaultConfiguration ==="
rg -n "ChipExpressionEditorDefaultConfiguration" --type=ts -A 5 -B 5 | head -80

Repository: wso2/vscode-extensions

Length of output: 188


🌐 Web query:

Ballerina language int type negative numbers decimals specification

💡 Result:

Short answer:

  • Integer literals themselves are unsigned in the grammar (int-literal := DecimalNumber | HexIntLiteral); a leading +/− is produced by the unary +/− operators, not part of the literal. Negatives are formed by applying unary - to an int expression. The int type is a signed 64‑bit two's‑complement integer: range −9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807; negation of −2^63 is an overflow (exception). [1][2]

  • The decimal type is a subset of IEEE‑754‑2008 decimal128 (radix‑10). A decimal value is (sign, coefficient, exponent) with up to 34 decimal digits of coefficient; +0/−0 are not distinguished; NaNs, infinities and subnormals are not supported (operations producing them panic). Decimal literals follow the decimal floating‑point grammar. [1]

Sources:

  1. Ballerina Language Specification — int/decimal type sections. [ballerina.io/spec]. [1]
  2. Ballerina Language Specification — expressions (unary +/− semantics, overflow on int negation). [ballerina.io/spec]. [2]

🌐 Web query:

CodeMirror EditorState changeFilter numeric validation patterns

💡 Result:

You can use EditorState.changeFilter (a facet) to accept or reject transactions before they apply. In the filter inspect the transaction’s changes and the resulting doc (or the inserted text) and return true to allow or false to block. Example that only allows digits (and common edits like delete/replace that keep the doc matching /^\d*$/):

Code (CodeMirror 6):

import {EditorState} from "@codemirror/state"
import {EditorView} from "@codemirror/view"

const onlyDigits = EditorState.changeFilter.of(tr => {
  // allow non-document transactions (selection, effect-only)
  if (!tr.docChanged) return true

  // compute the new document text after the transaction
  const newDoc = tr.newState?.doc ?? tr.state.apply(tr).doc
  const text = newDoc.toString()
  // allow only if the whole document matches digits (empty allowed)
  return /^\d*$/.test(text)
})

const view = new EditorView({
  state: EditorState.create({
    doc: "",
    extensions: [onlyDigits]
  }),
  parent: document.body
})

Notes

  • The filter runs for every transaction; keep it fast.
  • You can be more granular by inspecting tr.changes (from/to/inserted) to reject only specific insertions instead of checking full doc.
  • Use EditorView.updateListener or a ViewPlugin when you only need to observe changes rather than block them.

References: CodeMirror guide (changeFilter facet) and API reference. [1][2]

Sources
[1] CodeMirror guide — facets / changeFilter example.
[2] CodeMirror reference — EditorView/EditorState and Update docs.


Fix changeFilter to validate the complete resulting document, not just inserted text.

The current implementation has a critical flaw: it validates only the inserted text, not the full resulting document. Per CodeMirror documentation, changeFilter should inspect tr.newState?.doc to validate the final state.

Issues confirmed:

  1. Multiple decimal points: If the document is "5.5" and the user pastes ".", the inserted text "." passes the filter (no new dot violation), but the result "5.5." is invalid. The filter must validate the complete document after the transaction applies.

  2. Negative numbers: Ballerina's int type is a signed 64-bit integer supporting negative values, but negation is applied via the unary operator, not as a literal prefix. If the editor is for integer literals (not expressions), rejecting "−" is correct. However, clarify whether this editor accepts only int values or also decimal values—the regex allowing "." suggests possible decimal support.

  3. Malformed decimals: The regex allows incomplete decimal strings like ".", "5.", ".5" which would be invalid in most contexts.

Recommended fix: Replace the iterated inserted-text check with a full-document validation:

const numericOnly = EditorState.changeFilter.of(tr => {
  if (!tr.docChanged) return true;
  const newDoc = tr.newState?.doc ?? tr.state.apply(tr).doc;
  const text = newDoc.toString();
  return /^[0-9]*$/.test(text); // for int only, or adjust regex for expected format
});
🤖 Prompt for AI Agents
In
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsx
around lines 25-55, the changeFilter currently validates only the inserted chunk
instead of the full resulting document; change it to return true when
tr.docChanged is false and otherwise validate the complete post-transaction
document by reading tr.newState?.doc (or falling back to applying the
transaction to obtain the new doc) and testing newDoc.toString() against the
correct numeric pattern (use /^[0-9]*$/ for integer-only or an adjusted regex
for decimals), so cases like pasting "." into "5.5" are rejected and malformed
decimals are handled correctly.

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)

341-348: Potential infinite loop if parent doesn't stabilize value.

The deserialization check at lines 345-348 triggers onChange when deserializeValue differs from props.value. While the deserializeValue implementations appear idempotent (they check for existing prefix/suffix), this relies on the parent component immediately reflecting the updated value. If the parent applies its own transformation or delays the update, this could cause repeated onChange calls.

Consider adding a ref to track that normalization was already applied for the current value cycle:

+const normalizationAppliedRef = useRef<string | null>(null);

 useEffect(() => {
     if (props.value == null || !viewRef.current) return;
     const serializedValue = configuration.serializeValue(props.value);
     const deserializeValue = configuration.deserializeValue(props.value);
-    if (deserializeValue.trim() !== props.value.trim()) {
+    if (deserializeValue.trim() !== props.value.trim() && normalizationAppliedRef.current !== props.value) {
+        normalizationAppliedRef.current = props.value;
         props.onChange(deserializeValue, deserializeValue.length);
         return
     }
+    normalizationAppliedRef.current = null;
🧹 Nitpick comments (1)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (1)

450-461: Consider clarifying the empty onClick handler purpose.

The adornment rendering at line 450 passes an empty onClick handler: configuration.getAdornment()({ onClick: () => {}}). If the adornment component expects an onClick prop but it's intentionally a no-op in this context, consider adding a brief comment explaining why, or use a more explicit no-op like undefined if the prop is optional.

Otherwise, the configuration-based rendering for both the adornment and helper pane visibility is clean and follows the established pattern well.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 224a6b0 and 3301ba8.

📒 Files selected for processing (1)
  • workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (8 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
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.
📚 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
📚 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/ChipExpressionEditor/components/ChipExpressionEditor.tsx
📚 Learning: 2025-11-27T07:59:33.534Z
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.534Z
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/ChipExpressionEditor/components/ChipExpressionEditor.tsx
🧬 Code graph analysis (1)
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 (88-93)
  • deserializeValue (46-53)
  • deserializeValue (78-85)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts (1)
  • isSelectionOnToken (607-620)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/ChipExpressionDefaultConfig.ts (1)
  • deserializeValue (35-37)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/utils.ts (1)
  • TOKEN_START_CHAR_OFFSET_INDEX (23-23)
🔇 Additional comments (6)
workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsx (6)

38-53: LGTM!

The new imports for configuration classes and utility functions are well-organized and appropriate for the configuration-driven behavior being introduced.


95-99: LGTM!

Good use of optional configuration prop with a sensible default. This maintains backward compatibility while enabling customization.


118-132: LGTM!

Applying deserializeValue in the change listener ensures consistent value transformation when propagating changes to the parent. This aligns well with the configuration-driven approach.


176-178: LGTM!

The conditional logic correctly differentiates between selecting an existing token (use raw value) vs. selecting plain text (apply configuration's helper value transformation). This ensures proper value wrapping behavior.


281-324: LGTM!

Configuration plugins are correctly spread at the beginning of the extensions array, allowing configuration-specific behavior to take precedence. The initialization logic is clean and properly handles the various editor mode configurations.


362-370: Only the first token's offset is adjusted; subsequent tokens may have incorrect positions.

The concern raised assumes the token stream is a flat array with tokens packed in 5-element groups (line, startChar, length, type, modifiers). However, verification could not be completed due to:

  1. Repository access unavailable
  2. tokensChangeEffect is not a standard CodeMirror API and cannot be verified through public documentation
  3. Cannot inspect the actual token stream structure, token constants definitions, or usage patterns in the codebase

To validate this issue, you will need to:

  • Confirm the actual token stream format (flat array with 5-element groups vs. other structure)
  • Check if the code path typically handles single or multiple tokens
  • Review whether all token start offsets require adjustment or if adjusting only the first is intentional

@kanushka kanushka merged commit ad64115 into wso2:main Dec 3, 2025
6 checks passed
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.

Introduce boolean editor for the expression fields

2 participants