Implement boolean editor for expression fields#1017
Conversation
…g based modifications
WalkthroughAdds 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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
isTemplateModevariable 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()returningany[]is intentional given CodeMirror's flexible extension system, but you may want to use a more specific type likeExtension[]from@codemirror/stateif applicable.workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (1)
563-566: Add null guard before setting input mode.
targetInputModeRef.currentcould theoretically benullifhandleModeSwitchWarningContinueis invoked unexpectedly. A defensive check prevents passingnulltosetInputMode.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 consistentHelper 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 StringTemplateEditorConfigBehavior 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
📒 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.tsworkspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/utils/utils.tsworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/RecordConfigPreviewEditor/RecordConfigPreviewEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/modes/TemplateMode.tsxworkspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.tsworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/components/ChipExpressionEditor.tsxworkspaces/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.tsworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/NumberExpressionEditor/NumberEditor.tsxworkspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsxworkspaces/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.tsxworkspaces/ballerina/ballerina-visualizer/src/views/BI/HelperPaneNew/Views/DocumentConfig.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/Configurations.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/TextExpressionEditor/TextModeEditor.tsxworkspaces/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.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsxworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionField.tsxworkspaces/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.tsxworkspaces/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/bashFind 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
wrapInDocumentTypealigns with the centralized template handling approach described in the PR objectives.
213-214: LGTM - Consistent wrapping without interpolation.The URL insertion correctly uses
wrapInDocumentTypewithout 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
NUMBERandBOOLEANmodes 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_MAPnow properly mapsinttoInputMode.NUMBERandbooleantoInputMode.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
isSelectionOnTokenmentioned 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
handleOnChangecorrectly wraps the editor value in quotes before callingonChange, 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
onOpenExpandedModeis 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
fieldandprimaryModeprops enables the component to provide proper context to specialized editors and drive configuration-based behavior.
138-146: LGTM - Boolean mode properly integrated.The
BooleanEditoris correctly rendered forBOOLEANinput 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 (usingRawTemplateEditorConfig) modes improves clarity and enables appropriate serialization behavior for each mode.
232-249: LGTM - Default case with primary mode configuration.The fallback to
ChipExpressionEditorComponentwithPrimaryModeChipExpressionEditorConfigensures 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.keyas 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
RawTemplateEditorConfigto 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
NumberExpressionEditorcorrectly:
- Uses
getValueForTextModeEditorto normalize the display value- Passes
NumberExpressionEditorConfigto enforce numeric-only input- Forwards all necessary props to
ChipExpressionEditorComponentworkspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx (3)
539-541: LGTM!The
isExpToBooleanSafehelper correctly validates boolean-like expressions by trimming whitespace and performing case-insensitive comparison against "true" and "false".
673-708: LGTM!The
onChangehandler now consistently usesupdatedValuefor diagnostics, trigger character detection, and completion retrieval, which simplifies the value flow and eliminates potential inconsistencies.
661-664: LGTM!The
ExpressionFieldnow receives thefieldandprimaryModeprops, 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
ChipExpressionEditorComponentwhile correctly transforming the value throughgetValueForTextModeEditor. The essential props for text mode editing are properly forwarded.
60-66: LGTM!The expand button is correctly rendered only when
onOpenExpandedModeis 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
currentValuefromtr.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().lengthexceeds the originaltokenStream[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: EmptyonClickhandler 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
ChipExpressionEditorConfigensures 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 behaviorThe
getHelperValueoverride 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 modeUsing
primaryModeto only wrap helper values in${…}forInputMode.TEXTandInputMode.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, thisisTextOrTemplateModecheck will need updating.
workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpressionEditor.tsx
Show resolved
Hide resolved
| 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 |
There was a problem hiding this comment.
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.
| 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.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| getAdornment(): ({ onClick }: { onClick?: () => void; }) => JSX.Element { | ||
| return () => null; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find and read the Configurations.tsx file
fd "Configurations.tsx" -t fRepository: 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.
| 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.
| 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]; | ||
|
|
||
| } | ||
| } |
There was a problem hiding this comment.
🧩 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 -20Repository: 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 -50Repository: 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 -80Repository: 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:
- Ballerina Language Specification — int/decimal type sections. [ballerina.io/spec]. [1]
- 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:
-
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. -
Negative numbers: Ballerina's
inttype 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 onlyintvalues or alsodecimalvalues—the regex allowing"."suggests possible decimal support. -
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.
There was a problem hiding this comment.
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
onChangewhendeserializeValuediffers fromprops.value. While thedeserializeValueimplementations 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 repeatedonChangecalls.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 emptyonClickhandler purpose.The adornment rendering at line 450 passes an empty
onClickhandler:configuration.getAdornment()({ onClick: () => {}}). If the adornment component expects anonClickprop but it's intentionally a no-op in this context, consider adding a brief comment explaining why, or use a more explicit no-op likeundefinedif 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
📒 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
deserializeValuein 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:
- Repository access unavailable
tokensChangeEffectis not a standard CodeMirror API and cannot be verified through public documentation- 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
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
Approach
ChipExpressionEditor, the boolean field now uses a dedicated dropdown component with two options:trueandfalse.Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.