@rowsncolumns/ui 15.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +100 -0
- package/dist/cjs/arrow/index.d.ts +17 -0
- package/dist/cjs/arrow/index.d.ts.map +1 -0
- package/dist/cjs/arrow/index.js +46 -0
- package/dist/cjs/button/index.d.ts +14 -0
- package/dist/cjs/button/index.d.ts.map +1 -0
- package/dist/cjs/button/index.js +46 -0
- package/dist/cjs/calendar/index.d.ts +5 -0
- package/dist/cjs/calendar/index.d.ts.map +1 -0
- package/dist/cjs/calendar/index.js +13 -0
- package/dist/cjs/card/index.d.ts +10 -0
- package/dist/cjs/card/index.d.ts.map +1 -0
- package/dist/cjs/card/index.js +32 -0
- package/dist/cjs/cell-tooltip/index.d.ts +17 -0
- package/dist/cjs/cell-tooltip/index.d.ts.map +1 -0
- package/dist/cjs/cell-tooltip/index.js +38 -0
- package/dist/cjs/circular-loader/index.d.ts +9 -0
- package/dist/cjs/circular-loader/index.d.ts.map +1 -0
- package/dist/cjs/circular-loader/index.js +11 -0
- package/dist/cjs/color-picker/index.d.ts +12 -0
- package/dist/cjs/color-picker/index.d.ts.map +1 -0
- package/dist/cjs/color-picker/index.js +101 -0
- package/dist/cjs/color-picker/style.d.ts +5 -0
- package/dist/cjs/color-picker/style.d.ts.map +1 -0
- package/dist/cjs/color-picker/style.js +11 -0
- package/dist/cjs/command/index.d.ts +74 -0
- package/dist/cjs/command/index.d.ts.map +1 -0
- package/dist/cjs/command/index.js +37 -0
- package/dist/cjs/conditional-wrapper/index.d.ts +9 -0
- package/dist/cjs/conditional-wrapper/index.d.ts.map +1 -0
- package/dist/cjs/conditional-wrapper/index.js +4 -0
- package/dist/cjs/dialog/index.d.ts +17 -0
- package/dist/cjs/dialog/index.d.ts.map +1 -0
- package/dist/cjs/dialog/index.js +50 -0
- package/dist/cjs/dropdown/index.d.ts +34 -0
- package/dist/cjs/dropdown/index.d.ts.map +1 -0
- package/dist/cjs/dropdown/index.js +73 -0
- package/dist/cjs/editor/constants.d.ts +7 -0
- package/dist/cjs/editor/constants.d.ts.map +1 -0
- package/dist/cjs/editor/constants.js +50 -0
- package/dist/cjs/editor/editor-error-boundary.d.ts +3 -0
- package/dist/cjs/editor/editor-error-boundary.d.ts.map +1 -0
- package/dist/cjs/editor/editor-error-boundary.js +9 -0
- package/dist/cjs/editor/style.d.ts +12 -0
- package/dist/cjs/editor/style.d.ts.map +1 -0
- package/dist/cjs/editor/style.js +24 -0
- package/dist/cjs/editor/suggestions-dropdown.d.ts +25 -0
- package/dist/cjs/editor/suggestions-dropdown.d.ts.map +1 -0
- package/dist/cjs/editor/suggestions-dropdown.js +120 -0
- package/dist/cjs/editor-pm/constants.d.ts +2 -0
- package/dist/cjs/editor-pm/constants.d.ts.map +1 -0
- package/dist/cjs/editor-pm/constants.js +2 -0
- package/dist/cjs/editor-pm/decoration-plugin.d.ts +15 -0
- package/dist/cjs/editor-pm/decoration-plugin.d.ts.map +1 -0
- package/dist/cjs/editor-pm/decoration-plugin.js +38 -0
- package/dist/cjs/editor-pm/helpers.d.ts +197 -0
- package/dist/cjs/editor-pm/helpers.d.ts.map +1 -0
- package/dist/cjs/editor-pm/helpers.js +1170 -0
- package/dist/cjs/editor-pm/index.d.ts +10 -0
- package/dist/cjs/editor-pm/index.d.ts.map +1 -0
- package/dist/cjs/editor-pm/index.js +9 -0
- package/dist/cjs/editor-pm/mention-nodeview.d.ts +31 -0
- package/dist/cjs/editor-pm/mention-nodeview.d.ts.map +1 -0
- package/dist/cjs/editor-pm/mention-nodeview.js +105 -0
- package/dist/cjs/editor-pm/mentions-dropdown.d.ts +13 -0
- package/dist/cjs/editor-pm/mentions-dropdown.d.ts.map +1 -0
- package/dist/cjs/editor-pm/mentions-dropdown.js +19 -0
- package/dist/cjs/editor-pm/mentions-plugin.d.ts +18 -0
- package/dist/cjs/editor-pm/mentions-plugin.d.ts.map +1 -0
- package/dist/cjs/editor-pm/mentions-plugin.js +72 -0
- package/dist/cjs/editor-pm/placeholder-plugin.d.ts +4 -0
- package/dist/cjs/editor-pm/placeholder-plugin.d.ts.map +1 -0
- package/dist/cjs/editor-pm/placeholder-plugin.js +20 -0
- package/dist/cjs/editor-pm/prosemirror-editor.d.ts +493 -0
- package/dist/cjs/editor-pm/prosemirror-editor.d.ts.map +1 -0
- package/dist/cjs/editor-pm/prosemirror-editor.js +1145 -0
- package/dist/cjs/editor-pm/schema.d.ts +3 -0
- package/dist/cjs/editor-pm/schema.d.ts.map +1 -0
- package/dist/cjs/editor-pm/schema.js +183 -0
- package/dist/cjs/editor-pm/types.d.ts +8 -0
- package/dist/cjs/editor-pm/types.d.ts.map +1 -0
- package/dist/cjs/editor-pm/types.js +1 -0
- package/dist/cjs/editor-pm/unfocused-selection-plugin.d.ts +25 -0
- package/dist/cjs/editor-pm/unfocused-selection-plugin.d.ts.map +1 -0
- package/dist/cjs/editor-pm/unfocused-selection-plugin.js +61 -0
- package/dist/cjs/editor-pm/use-dropdown-change-pm.d.ts +24 -0
- package/dist/cjs/editor-pm/use-dropdown-change-pm.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-dropdown-change-pm.js +140 -0
- package/dist/cjs/editor-pm/use-editor-change-pm.d.ts +29 -0
- package/dist/cjs/editor-pm/use-editor-change-pm.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-editor-change-pm.js +154 -0
- package/dist/cjs/editor-pm/use-editor-decorator-pm.d.ts +17 -0
- package/dist/cjs/editor-pm/use-editor-decorator-pm.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-editor-decorator-pm.js +59 -0
- package/dist/cjs/editor-pm/use-editor-keydown-pm.d.ts +21 -0
- package/dist/cjs/editor-pm/use-editor-keydown-pm.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-editor-keydown-pm.js +81 -0
- package/dist/cjs/editor-pm/use-mention-keydown.d.ts +11 -0
- package/dist/cjs/editor-pm/use-mention-keydown.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-mention-keydown.js +59 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.d.ts +19 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.js +79 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.test.d.ts +2 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.test.d.ts.map +1 -0
- package/dist/cjs/editor-pm/use-parameter-suggestions.test.js +122 -0
- package/dist/cjs/eye-dropper/index.d.ts +10 -0
- package/dist/cjs/eye-dropper/index.d.ts.map +1 -0
- package/dist/cjs/eye-dropper/index.js +24 -0
- package/dist/cjs/function-info/helpers.d.ts +10 -0
- package/dist/cjs/function-info/helpers.d.ts.map +1 -0
- package/dist/cjs/function-info/helpers.js +20 -0
- package/dist/cjs/function-info/index.d.ts +11 -0
- package/dist/cjs/function-info/index.d.ts.map +1 -0
- package/dist/cjs/function-info/index.js +71 -0
- package/dist/cjs/function-info/style.d.ts +7 -0
- package/dist/cjs/function-info/style.d.ts.map +1 -0
- package/dist/cjs/function-info/style.js +13 -0
- package/dist/cjs/hex-color-picker/index.d.ts +8 -0
- package/dist/cjs/hex-color-picker/index.d.ts.map +1 -0
- package/dist/cjs/hex-color-picker/index.js +47 -0
- package/dist/cjs/hooks/use-cycle-reference.d.ts +16 -0
- package/dist/cjs/hooks/use-cycle-reference.d.ts.map +1 -0
- package/dist/cjs/hooks/use-cycle-reference.js +72 -0
- package/dist/cjs/hooks/use-dropdown-categories.d.ts +9 -0
- package/dist/cjs/hooks/use-dropdown-categories.d.ts.map +1 -0
- package/dist/cjs/hooks/use-dropdown-categories.js +45 -0
- package/dist/cjs/hooks/use-dropdown.d.ts +38 -0
- package/dist/cjs/hooks/use-dropdown.d.ts.map +1 -0
- package/dist/cjs/hooks/use-dropdown.js +236 -0
- package/dist/cjs/hooks/use-isomorphic-layout-effect.ts.d.ts +6 -0
- package/dist/cjs/hooks/use-isomorphic-layout-effect.ts.d.ts.map +1 -0
- package/dist/cjs/hooks/use-isomorphic-layout-effect.ts.js +8 -0
- package/dist/cjs/hooks/use-modal.d.ts +28 -0
- package/dist/cjs/hooks/use-modal.d.ts.map +1 -0
- package/dist/cjs/hooks/use-modal.js +101 -0
- package/dist/cjs/hooks/use-visibility.d.ts +2 -0
- package/dist/cjs/hooks/use-visibility.d.ts.map +1 -0
- package/dist/cjs/hooks/use-visibility.js +12 -0
- package/dist/cjs/icon-button/index.d.ts +15 -0
- package/dist/cjs/icon-button/index.d.ts.map +1 -0
- package/dist/cjs/icon-button/index.js +34 -0
- package/dist/cjs/index.d.ts +34 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/input/index.d.ts +9 -0
- package/dist/cjs/input/index.d.ts.map +1 -0
- package/dist/cjs/input/index.js +9 -0
- package/dist/cjs/popover/index.d.ts +13 -0
- package/dist/cjs/popover/index.d.ts.map +1 -0
- package/dist/cjs/popover/index.js +29 -0
- package/dist/cjs/progress/index.d.ts +10 -0
- package/dist/cjs/progress/index.d.ts.map +1 -0
- package/dist/cjs/progress/index.js +15 -0
- package/dist/cjs/select/index.d.ts +14 -0
- package/dist/cjs/select/index.d.ts.map +1 -0
- package/dist/cjs/select/index.js +46 -0
- package/dist/cjs/separator/index.d.ts +5 -0
- package/dist/cjs/separator/index.d.ts.map +1 -0
- package/dist/cjs/separator/index.js +10 -0
- package/dist/cjs/skeleton/index.d.ts +7 -0
- package/dist/cjs/skeleton/index.d.ts.map +1 -0
- package/dist/cjs/skeleton/index.js +11 -0
- package/dist/cjs/stack/index.d.ts +11 -0
- package/dist/cjs/stack/index.d.ts.map +1 -0
- package/dist/cjs/stack/index.js +22 -0
- package/dist/cjs/tabs/index.d.ts +8 -0
- package/dist/cjs/tabs/index.d.ts.map +1 -0
- package/dist/cjs/tabs/index.js +21 -0
- package/dist/cjs/tooltip/index.d.ts +12 -0
- package/dist/cjs/tooltip/index.d.ts.map +1 -0
- package/dist/cjs/tooltip/index.js +20 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/utils.d.ts +3 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +9 -0
- package/dist/cjs/vitest.config.d.ts +3 -0
- package/dist/cjs/vitest.config.d.ts.map +1 -0
- package/dist/cjs/vitest.config.js +21 -0
- package/dist/esm/arrow/index.d.ts +17 -0
- package/dist/esm/arrow/index.d.ts.map +1 -0
- package/dist/esm/arrow/index.js +45 -0
- package/dist/esm/button/index.d.ts +14 -0
- package/dist/esm/button/index.d.ts.map +1 -0
- package/dist/esm/button/index.js +44 -0
- package/dist/esm/calendar/index.d.ts +5 -0
- package/dist/esm/calendar/index.d.ts.map +1 -0
- package/dist/esm/calendar/index.js +34 -0
- package/dist/esm/card/index.d.ts +10 -0
- package/dist/esm/card/index.d.ts.map +1 -0
- package/dist/esm/card/index.js +24 -0
- package/dist/esm/cell-tooltip/index.d.ts +17 -0
- package/dist/esm/cell-tooltip/index.d.ts.map +1 -0
- package/dist/esm/cell-tooltip/index.js +31 -0
- package/dist/esm/circular-loader/index.d.ts +9 -0
- package/dist/esm/circular-loader/index.d.ts.map +1 -0
- package/dist/esm/circular-loader/index.js +7 -0
- package/dist/esm/color-picker/index.d.ts +12 -0
- package/dist/esm/color-picker/index.d.ts.map +1 -0
- package/dist/esm/color-picker/index.js +99 -0
- package/dist/esm/color-picker/style.d.ts +5 -0
- package/dist/esm/color-picker/style.d.ts.map +1 -0
- package/dist/esm/color-picker/style.js +7 -0
- package/dist/esm/command/index.d.ts +74 -0
- package/dist/esm/command/index.d.ts.map +1 -0
- package/dist/esm/command/index.js +18 -0
- package/dist/esm/conditional-wrapper/index.d.ts +9 -0
- package/dist/esm/conditional-wrapper/index.d.ts.map +1 -0
- package/dist/esm/conditional-wrapper/index.js +1 -0
- package/dist/esm/dialog/index.d.ts +17 -0
- package/dist/esm/dialog/index.d.ts.map +1 -0
- package/dist/esm/dialog/index.js +30 -0
- package/dist/esm/dropdown/index.d.ts +34 -0
- package/dist/esm/dropdown/index.d.ts.map +1 -0
- package/dist/esm/dropdown/index.js +42 -0
- package/dist/esm/editor/constants.d.ts +7 -0
- package/dist/esm/editor/constants.d.ts.map +1 -0
- package/dist/esm/editor/constants.js +51 -0
- package/dist/esm/editor/editor-error-boundary.d.ts +3 -0
- package/dist/esm/editor/editor-error-boundary.d.ts.map +1 -0
- package/dist/esm/editor/editor-error-boundary.js +9 -0
- package/dist/esm/editor/style.d.ts +12 -0
- package/dist/esm/editor/style.d.ts.map +1 -0
- package/dist/esm/editor/style.js +17 -0
- package/dist/esm/editor/suggestions-dropdown.d.ts +25 -0
- package/dist/esm/editor/suggestions-dropdown.d.ts.map +1 -0
- package/dist/esm/editor/suggestions-dropdown.js +115 -0
- package/dist/esm/editor-pm/constants.d.ts +2 -0
- package/dist/esm/editor-pm/constants.d.ts.map +1 -0
- package/dist/esm/editor-pm/constants.js +2 -0
- package/dist/esm/editor-pm/decoration-plugin.d.ts +15 -0
- package/dist/esm/editor-pm/decoration-plugin.d.ts.map +1 -0
- package/dist/esm/editor-pm/decoration-plugin.js +38 -0
- package/dist/esm/editor-pm/helpers.d.ts +197 -0
- package/dist/esm/editor-pm/helpers.d.ts.map +1 -0
- package/dist/esm/editor-pm/helpers.js +1113 -0
- package/dist/esm/editor-pm/index.d.ts +10 -0
- package/dist/esm/editor-pm/index.d.ts.map +1 -0
- package/dist/esm/editor-pm/index.js +9 -0
- package/dist/esm/editor-pm/mention-nodeview.d.ts +31 -0
- package/dist/esm/editor-pm/mention-nodeview.d.ts.map +1 -0
- package/dist/esm/editor-pm/mention-nodeview.js +88 -0
- package/dist/esm/editor-pm/mentions-dropdown.d.ts +13 -0
- package/dist/esm/editor-pm/mentions-dropdown.d.ts.map +1 -0
- package/dist/esm/editor-pm/mentions-dropdown.js +17 -0
- package/dist/esm/editor-pm/mentions-plugin.d.ts +18 -0
- package/dist/esm/editor-pm/mentions-plugin.d.ts.map +1 -0
- package/dist/esm/editor-pm/mentions-plugin.js +92 -0
- package/dist/esm/editor-pm/placeholder-plugin.d.ts +4 -0
- package/dist/esm/editor-pm/placeholder-plugin.d.ts.map +1 -0
- package/dist/esm/editor-pm/placeholder-plugin.js +20 -0
- package/dist/esm/editor-pm/prosemirror-editor.d.ts +493 -0
- package/dist/esm/editor-pm/prosemirror-editor.d.ts.map +1 -0
- package/dist/esm/editor-pm/prosemirror-editor.js +1135 -0
- package/dist/esm/editor-pm/schema.d.ts +3 -0
- package/dist/esm/editor-pm/schema.d.ts.map +1 -0
- package/dist/esm/editor-pm/schema.js +179 -0
- package/dist/esm/editor-pm/types.d.ts +8 -0
- package/dist/esm/editor-pm/types.d.ts.map +1 -0
- package/dist/esm/editor-pm/types.js +1 -0
- package/dist/esm/editor-pm/unfocused-selection-plugin.d.ts +25 -0
- package/dist/esm/editor-pm/unfocused-selection-plugin.d.ts.map +1 -0
- package/dist/esm/editor-pm/unfocused-selection-plugin.js +61 -0
- package/dist/esm/editor-pm/use-dropdown-change-pm.d.ts +24 -0
- package/dist/esm/editor-pm/use-dropdown-change-pm.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-dropdown-change-pm.js +137 -0
- package/dist/esm/editor-pm/use-editor-change-pm.d.ts +29 -0
- package/dist/esm/editor-pm/use-editor-change-pm.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-editor-change-pm.js +152 -0
- package/dist/esm/editor-pm/use-editor-decorator-pm.d.ts +17 -0
- package/dist/esm/editor-pm/use-editor-decorator-pm.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-editor-decorator-pm.js +57 -0
- package/dist/esm/editor-pm/use-editor-keydown-pm.d.ts +21 -0
- package/dist/esm/editor-pm/use-editor-keydown-pm.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-editor-keydown-pm.js +79 -0
- package/dist/esm/editor-pm/use-mention-keydown.d.ts +11 -0
- package/dist/esm/editor-pm/use-mention-keydown.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-mention-keydown.js +57 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.d.ts +19 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.js +66 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.test.d.ts +2 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.test.d.ts.map +1 -0
- package/dist/esm/editor-pm/use-parameter-suggestions.test.js +66 -0
- package/dist/esm/eye-dropper/index.d.ts +10 -0
- package/dist/esm/eye-dropper/index.d.ts.map +1 -0
- package/dist/esm/eye-dropper/index.js +23 -0
- package/dist/esm/function-info/helpers.d.ts +10 -0
- package/dist/esm/function-info/helpers.d.ts.map +1 -0
- package/dist/esm/function-info/helpers.js +20 -0
- package/dist/esm/function-info/index.d.ts +11 -0
- package/dist/esm/function-info/index.d.ts.map +1 -0
- package/dist/esm/function-info/index.js +68 -0
- package/dist/esm/function-info/style.d.ts +7 -0
- package/dist/esm/function-info/style.d.ts.map +1 -0
- package/dist/esm/function-info/style.js +10 -0
- package/dist/esm/hex-color-picker/index.d.ts +8 -0
- package/dist/esm/hex-color-picker/index.d.ts.map +1 -0
- package/dist/esm/hex-color-picker/index.js +44 -0
- package/dist/esm/hooks/use-cycle-reference.d.ts +16 -0
- package/dist/esm/hooks/use-cycle-reference.d.ts.map +1 -0
- package/dist/esm/hooks/use-cycle-reference.js +70 -0
- package/dist/esm/hooks/use-dropdown-categories.d.ts +9 -0
- package/dist/esm/hooks/use-dropdown-categories.d.ts.map +1 -0
- package/dist/esm/hooks/use-dropdown-categories.js +38 -0
- package/dist/esm/hooks/use-dropdown.d.ts +38 -0
- package/dist/esm/hooks/use-dropdown.d.ts.map +1 -0
- package/dist/esm/hooks/use-dropdown.js +236 -0
- package/dist/esm/hooks/use-isomorphic-layout-effect.ts.d.ts +6 -0
- package/dist/esm/hooks/use-isomorphic-layout-effect.ts.d.ts.map +1 -0
- package/dist/esm/hooks/use-isomorphic-layout-effect.ts.js +8 -0
- package/dist/esm/hooks/use-modal.d.ts +28 -0
- package/dist/esm/hooks/use-modal.d.ts.map +1 -0
- package/dist/esm/hooks/use-modal.js +93 -0
- package/dist/esm/hooks/use-visibility.d.ts +2 -0
- package/dist/esm/hooks/use-visibility.d.ts.map +1 -0
- package/dist/esm/hooks/use-visibility.js +12 -0
- package/dist/esm/icon-button/index.d.ts +15 -0
- package/dist/esm/icon-button/index.d.ts.map +1 -0
- package/dist/esm/icon-button/index.js +32 -0
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +33 -0
- package/dist/esm/input/index.d.ts +9 -0
- package/dist/esm/input/index.d.ts.map +1 -0
- package/dist/esm/input/index.js +7 -0
- package/dist/esm/popover/index.d.ts +13 -0
- package/dist/esm/popover/index.d.ts.map +1 -0
- package/dist/esm/popover/index.js +22 -0
- package/dist/esm/progress/index.d.ts +10 -0
- package/dist/esm/progress/index.d.ts.map +1 -0
- package/dist/esm/progress/index.js +12 -0
- package/dist/esm/select/index.d.ts +14 -0
- package/dist/esm/select/index.d.ts.map +1 -0
- package/dist/esm/select/index.js +30 -0
- package/dist/esm/separator/index.d.ts +5 -0
- package/dist/esm/separator/index.d.ts.map +1 -0
- package/dist/esm/separator/index.js +6 -0
- package/dist/esm/skeleton/index.d.ts +7 -0
- package/dist/esm/skeleton/index.d.ts.map +1 -0
- package/dist/esm/skeleton/index.js +9 -0
- package/dist/esm/stack/index.d.ts +11 -0
- package/dist/esm/stack/index.d.ts.map +1 -0
- package/dist/esm/stack/index.js +20 -0
- package/dist/esm/tabs/index.d.ts +8 -0
- package/dist/esm/tabs/index.d.ts.map +1 -0
- package/dist/esm/tabs/index.js +11 -0
- package/dist/esm/tooltip/index.d.ts +12 -0
- package/dist/esm/tooltip/index.d.ts.map +1 -0
- package/dist/esm/tooltip/index.js +13 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/esm/utils.d.ts +3 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +5 -0
- package/dist/esm/vitest.config.d.ts +3 -0
- package/dist/esm/vitest.config.d.ts.map +1 -0
- package/dist/esm/vitest.config.js +21 -0
- package/package.json +78 -0
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
import { cleanupColumnName } from "@rowsncolumns/calculator";
|
|
2
|
+
import { tokenVocabulary } from "@rowsncolumns/fast-formula-parser";
|
|
3
|
+
import { CELL_SUGGESTION_PREFIX, CELL_SUGGESTION_SUFFIX, } from "../editor/constants";
|
|
4
|
+
/**
|
|
5
|
+
* Converts string to ProseMirror document
|
|
6
|
+
* @param str
|
|
7
|
+
* @param schema
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export const deserializeString = (str, schema) => {
|
|
11
|
+
try {
|
|
12
|
+
if (typeof str === "object") {
|
|
13
|
+
str = tryParseRichText(str);
|
|
14
|
+
}
|
|
15
|
+
// Validate input
|
|
16
|
+
if (typeof str !== "string") {
|
|
17
|
+
console.warn("deserializeString: input is not a string, using empty string");
|
|
18
|
+
str = "";
|
|
19
|
+
}
|
|
20
|
+
// Sanitize string - remove null characters and other problematic chars
|
|
21
|
+
str = str
|
|
22
|
+
.replace(/\0/g, "")
|
|
23
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
|
24
|
+
const lines = str.split("\n");
|
|
25
|
+
const nodes = lines.map((line) => schema.node("line", null, line ? [schema.text(line)] : []));
|
|
26
|
+
return schema.node("doc", null, nodes);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error("Error deserializing string:", error);
|
|
30
|
+
// Return empty document as fallback
|
|
31
|
+
return schema.node("doc", null, [schema.node("line", null, [])]);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export const tryParseRichText = (str) => {
|
|
35
|
+
// Temporary workaround for .richText
|
|
36
|
+
if ("richText" in str) {
|
|
37
|
+
str = str?.richText?.map((value) => value.text).join("");
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
str = "Unable to parse string";
|
|
41
|
+
}
|
|
42
|
+
return str;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Converts ProseMirror document to string
|
|
46
|
+
* @param doc
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
export const serializeEditorValue = (doc) => {
|
|
50
|
+
let text = "";
|
|
51
|
+
doc.content.forEach((node, offset, index) => {
|
|
52
|
+
if (node.type.name === "line") {
|
|
53
|
+
if (index > 0)
|
|
54
|
+
text += "\n";
|
|
55
|
+
// Serialize line content, handling mention nodes properly
|
|
56
|
+
let lineText = "";
|
|
57
|
+
node.content.forEach((child) => {
|
|
58
|
+
if (child.type.name === "mention") {
|
|
59
|
+
// Serialize mention as just the label
|
|
60
|
+
lineText += child.attrs.label;
|
|
61
|
+
}
|
|
62
|
+
else if (child.isText) {
|
|
63
|
+
// Regular text node
|
|
64
|
+
lineText += child.text;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
text += lineText;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return text;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Extracts text format runs from ProseMirror document
|
|
74
|
+
* @param doc ProseMirror document node
|
|
75
|
+
* @returns Array of TextFormatRun objects
|
|
76
|
+
*/
|
|
77
|
+
export const extractTextFormatRuns = (doc) => {
|
|
78
|
+
if (!doc) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const formatRuns = [];
|
|
82
|
+
let currentIndex = 0;
|
|
83
|
+
doc.content.forEach((node, _, lineIndex) => {
|
|
84
|
+
if (node.type.name === "line") {
|
|
85
|
+
if (lineIndex > 0) {
|
|
86
|
+
// Add newline character to index
|
|
87
|
+
currentIndex++;
|
|
88
|
+
}
|
|
89
|
+
// First pass: collect all child nodes with their start positions
|
|
90
|
+
const childNodes = [];
|
|
91
|
+
let nodeIndex = currentIndex;
|
|
92
|
+
node.content.forEach((child) => {
|
|
93
|
+
childNodes.push({
|
|
94
|
+
node: child,
|
|
95
|
+
startIndex: nodeIndex,
|
|
96
|
+
});
|
|
97
|
+
// Calculate the next start index based on the node type
|
|
98
|
+
if (child.type.name === "mention") {
|
|
99
|
+
// For mentions, use just the label length for positioning
|
|
100
|
+
nodeIndex += child.attrs.label.length;
|
|
101
|
+
}
|
|
102
|
+
else if (child.isText && child.text) {
|
|
103
|
+
nodeIndex += child.text.length;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Second pass: create format runs using next child's start index as end boundary
|
|
107
|
+
childNodes.forEach((childData, index) => {
|
|
108
|
+
const { node: child, startIndex } = childData;
|
|
109
|
+
// Calculate endIndex using next child's startIndex, or end of line
|
|
110
|
+
const nextChild = childNodes[index + 1];
|
|
111
|
+
const endIndex = nextChild ? nextChild.startIndex : nodeIndex;
|
|
112
|
+
if (child.type.name === "mention") {
|
|
113
|
+
formatRuns.push({
|
|
114
|
+
startIndex,
|
|
115
|
+
endIndex,
|
|
116
|
+
nodeType: "mention",
|
|
117
|
+
mention: { ...child.attrs },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else if (child.isText && child.text) {
|
|
121
|
+
if (child.marks && child.marks.length > 0) {
|
|
122
|
+
// Extract format from marks
|
|
123
|
+
const format = {};
|
|
124
|
+
child.marks.forEach((mark) => {
|
|
125
|
+
switch (mark.type.name) {
|
|
126
|
+
case "bold":
|
|
127
|
+
format.bold = true;
|
|
128
|
+
break;
|
|
129
|
+
case "italic":
|
|
130
|
+
format.italic = true;
|
|
131
|
+
break;
|
|
132
|
+
case "underline":
|
|
133
|
+
format.underline = true;
|
|
134
|
+
break;
|
|
135
|
+
case "strikethrough":
|
|
136
|
+
format.strikethrough = true;
|
|
137
|
+
break;
|
|
138
|
+
case "superscript":
|
|
139
|
+
format.baselineOffset = "superscript";
|
|
140
|
+
break;
|
|
141
|
+
case "subscript":
|
|
142
|
+
format.baselineOffset = "subscript";
|
|
143
|
+
break;
|
|
144
|
+
case "color":
|
|
145
|
+
format.color = mark.attrs.color;
|
|
146
|
+
break;
|
|
147
|
+
case "fontSize":
|
|
148
|
+
format.fontSize = mark.attrs.size;
|
|
149
|
+
break;
|
|
150
|
+
case "fontFamily":
|
|
151
|
+
format.fontFamily = mark.attrs.family;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
formatRuns.push({
|
|
156
|
+
startIndex,
|
|
157
|
+
endIndex,
|
|
158
|
+
format,
|
|
159
|
+
nodeType: "text",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Plain text without formatting
|
|
164
|
+
formatRuns.push({
|
|
165
|
+
startIndex,
|
|
166
|
+
endIndex,
|
|
167
|
+
format: {},
|
|
168
|
+
nodeType: "text",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
currentIndex = nodeIndex;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
// Filter out text nodes with empty format objects
|
|
177
|
+
const filteredRuns = formatRuns.filter((run) => {
|
|
178
|
+
if (run.nodeType === "text" && Object.keys(run.format).length === 0) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
});
|
|
183
|
+
return filteredRuns.length === 0 ? undefined : filteredRuns;
|
|
184
|
+
};
|
|
185
|
+
export const applyTextFormatRuns = (text, formatRuns, schema, resolveColor) => {
|
|
186
|
+
// Handle empty text case
|
|
187
|
+
if (text === "") {
|
|
188
|
+
return schema.nodes.doc.create(null, [schema.nodes.line.create(null, [])]);
|
|
189
|
+
}
|
|
190
|
+
// If no format runs, create simple document with plain text
|
|
191
|
+
if (!formatRuns || formatRuns?.length === 0) {
|
|
192
|
+
const lines = text.split("\n");
|
|
193
|
+
const lineNodes = lines.map((lineText) => schema.nodes.line.create(null, lineText ? [schema.text(lineText)] : []));
|
|
194
|
+
return schema.nodes.doc.create(null, lineNodes);
|
|
195
|
+
}
|
|
196
|
+
const sortedFormatRuns = [...formatRuns].sort((a, b) => a.startIndex - b.startIndex || a.endIndex - b.endIndex);
|
|
197
|
+
const createMarks = (formatRun) => {
|
|
198
|
+
const marks = [];
|
|
199
|
+
// Engine-emitted runs (rnc window cells) can omit `format` on the
|
|
200
|
+
// unformatted gap runs between styled spans.
|
|
201
|
+
if (!formatRun.format) {
|
|
202
|
+
return marks;
|
|
203
|
+
}
|
|
204
|
+
if (formatRun.format.bold) {
|
|
205
|
+
marks.push(schema.marks.bold.create());
|
|
206
|
+
}
|
|
207
|
+
if (formatRun.format.italic) {
|
|
208
|
+
marks.push(schema.marks.italic.create());
|
|
209
|
+
}
|
|
210
|
+
if (formatRun.format.underline) {
|
|
211
|
+
marks.push(schema.marks.underline.create());
|
|
212
|
+
}
|
|
213
|
+
if (formatRun.format.strikethrough) {
|
|
214
|
+
marks.push(schema.marks.strikethrough.create());
|
|
215
|
+
}
|
|
216
|
+
if (formatRun.format.color) {
|
|
217
|
+
const css = typeof formatRun.format.color === "string"
|
|
218
|
+
? formatRun.format.color
|
|
219
|
+
: resolveColor?.(formatRun.format.color);
|
|
220
|
+
if (css) {
|
|
221
|
+
marks.push(schema.marks.color.create({ color: css }));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (formatRun.format.baselineOffset === "superscript") {
|
|
225
|
+
marks.push(schema.marks.superscript.create());
|
|
226
|
+
}
|
|
227
|
+
else if (formatRun.format.baselineOffset === "subscript") {
|
|
228
|
+
marks.push(schema.marks.subscript.create());
|
|
229
|
+
}
|
|
230
|
+
if (formatRun.format.fontSize != null) {
|
|
231
|
+
marks.push(schema.marks.fontSize.create({ size: formatRun.format.fontSize }));
|
|
232
|
+
}
|
|
233
|
+
if (formatRun.format.fontFamily) {
|
|
234
|
+
marks.push(schema.marks.fontFamily.create({ family: formatRun.format.fontFamily }));
|
|
235
|
+
}
|
|
236
|
+
return marks;
|
|
237
|
+
};
|
|
238
|
+
// Split text into lines
|
|
239
|
+
const lines = text.split("\n");
|
|
240
|
+
const lineNodes = [];
|
|
241
|
+
let currentTextIndex = 0;
|
|
242
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
243
|
+
const lineText = lines[lineIndex];
|
|
244
|
+
const lineContent = [];
|
|
245
|
+
if (lineIndex > 0) {
|
|
246
|
+
// Account for newline character
|
|
247
|
+
currentTextIndex++;
|
|
248
|
+
}
|
|
249
|
+
const lineStartIndex = currentTextIndex;
|
|
250
|
+
const lineEndIndex = currentTextIndex + lineText.length;
|
|
251
|
+
// Get format runs that overlap this line. Runs may span newlines,
|
|
252
|
+
// but ProseMirror stores each line separately, so split by line.
|
|
253
|
+
const lineFormatRuns = sortedFormatRuns.filter((run) => run.startIndex < lineEndIndex && run.endIndex > lineStartIndex);
|
|
254
|
+
if (lineFormatRuns.length === 0 && lineText.length > 0) {
|
|
255
|
+
// No formatting, create plain text node
|
|
256
|
+
lineContent.push(schema.text(lineText));
|
|
257
|
+
}
|
|
258
|
+
else if (lineText.length > 0) {
|
|
259
|
+
// Process format runs for this line
|
|
260
|
+
let processedIndex = lineStartIndex;
|
|
261
|
+
for (const formatRun of lineFormatRuns) {
|
|
262
|
+
const runStart = Math.max(formatRun.startIndex, lineStartIndex);
|
|
263
|
+
const runEnd = Math.min(formatRun.endIndex, lineEndIndex);
|
|
264
|
+
if (runEnd <= runStart || processedIndex >= lineEndIndex) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
// Add any unformatted text before this run
|
|
268
|
+
if (processedIndex < runStart) {
|
|
269
|
+
const unformattedText = text.substring(processedIndex, runStart);
|
|
270
|
+
if (unformattedText) {
|
|
271
|
+
lineContent.push(schema.text(unformattedText));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (formatRun.nodeType === "mention") {
|
|
275
|
+
// Create mention node from format run data
|
|
276
|
+
if (formatRun.mention && formatRun.startIndex >= lineStartIndex) {
|
|
277
|
+
lineContent.push(schema.nodes.mention.create({
|
|
278
|
+
label: formatRun.mention.label,
|
|
279
|
+
value: formatRun.mention.value,
|
|
280
|
+
}));
|
|
281
|
+
// Since mentions are now stored as just the label, advance by label length
|
|
282
|
+
processedIndex = runStart + formatRun.mention.label.length;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
// Any non-mention run is a text run. xlsx-imported runs (rnc
|
|
287
|
+
// engine and excel-parser) omit `nodeType`, so an exact
|
|
288
|
+
// `=== "text"` check skipped them entirely — processedIndex
|
|
289
|
+
// never advanced and the gap/tail fillers re-emitted the
|
|
290
|
+
// already-covered text (e.g. "kpmg" painting as "kpmgkpmg").
|
|
291
|
+
const runText = text.substring(Math.max(processedIndex, runStart), runEnd);
|
|
292
|
+
if (runText) {
|
|
293
|
+
lineContent.push(schema.text(runText, createMarks(formatRun)));
|
|
294
|
+
processedIndex = runEnd;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Add any remaining unformatted text at the end of the line
|
|
299
|
+
if (processedIndex < lineEndIndex) {
|
|
300
|
+
const remainingText = text.substring(processedIndex, lineEndIndex);
|
|
301
|
+
if (remainingText) {
|
|
302
|
+
lineContent.push(schema.text(remainingText));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Create line node
|
|
307
|
+
lineNodes.push(schema.nodes.line.create(null, lineContent));
|
|
308
|
+
currentTextIndex += lineText.length;
|
|
309
|
+
}
|
|
310
|
+
// Create document
|
|
311
|
+
return schema.nodes.doc.create(null, lineNodes);
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Applies a `TextFormat` patch to the editor's CURRENT SELECTION as marks —
|
|
315
|
+
* the per-run counterpart of the toolbar's cell-level `onChangeFormatting`.
|
|
316
|
+
* This is what makes the toolbar's bold/superscript/font-size controls format
|
|
317
|
+
* just the selected span while the cell editor is open, the way Cmd+B already
|
|
318
|
+
* does through the keymap.
|
|
319
|
+
*
|
|
320
|
+
* Semantics per key (only keys PRESENT on `format` are touched):
|
|
321
|
+
* - `bold`/`italic`/`underline`/`strikethrough`: TOGGLE against the
|
|
322
|
+
* selection's own mark state (same rule as prosemirror-commands
|
|
323
|
+
* `toggleMark`: any marked text in range → remove, else add) — the caller's
|
|
324
|
+
* cell-level boolean is ignored because it describes the cell, not the span.
|
|
325
|
+
* - `baselineOffset`: if the selection already carries the target offset the
|
|
326
|
+
* pair is cleared; otherwise the target replaces both (mutually exclusive).
|
|
327
|
+
* - `fontSize`/`fontFamily`/`color`: SET to the given value (clear on nullish).
|
|
328
|
+
* Theme-object colors resolve through `resolveColor`.
|
|
329
|
+
*
|
|
330
|
+
* A collapsed selection applies the change to the stored marks, so it affects
|
|
331
|
+
* what is typed next — Google Sheets behavior.
|
|
332
|
+
*
|
|
333
|
+
* Returns `false` (and dispatches nothing) when `format` carries none of the
|
|
334
|
+
* run-representable keys, so the caller can fall back to cell-level formatting.
|
|
335
|
+
*/
|
|
336
|
+
export const applyTextFormatToSelection = (state, dispatch, format, schema, resolveColor) => {
|
|
337
|
+
const { from, to, empty } = state.selection;
|
|
338
|
+
const cursorMarks = state.storedMarks ?? state.selection.$from.marks();
|
|
339
|
+
let tr = state.tr;
|
|
340
|
+
let handled = false;
|
|
341
|
+
const hasMark = (markType) => empty
|
|
342
|
+
? markType.isInSet(cursorMarks) != null
|
|
343
|
+
: state.doc.rangeHasMark(from, to, markType);
|
|
344
|
+
const add = (mark) => {
|
|
345
|
+
tr = empty ? tr.addStoredMark(mark) : tr.addMark(from, to, mark);
|
|
346
|
+
};
|
|
347
|
+
const remove = (markType) => {
|
|
348
|
+
tr = empty ? tr.removeStoredMark(markType) : tr.removeMark(from, to, markType);
|
|
349
|
+
};
|
|
350
|
+
for (const key of ["bold", "italic", "underline", "strikethrough"]) {
|
|
351
|
+
if (!(key in format))
|
|
352
|
+
continue;
|
|
353
|
+
handled = true;
|
|
354
|
+
const markType = schema.marks[key];
|
|
355
|
+
if (hasMark(markType)) {
|
|
356
|
+
remove(markType);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
add(markType.create());
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if ("baselineOffset" in format) {
|
|
363
|
+
handled = true;
|
|
364
|
+
const target = format.baselineOffset;
|
|
365
|
+
const targetType = target ? schema.marks[target] : undefined;
|
|
366
|
+
const alreadyOn = targetType ? hasMark(targetType) : false;
|
|
367
|
+
remove(schema.marks.superscript);
|
|
368
|
+
remove(schema.marks.subscript);
|
|
369
|
+
if (targetType && !alreadyOn) {
|
|
370
|
+
add(targetType.create());
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if ("fontSize" in format) {
|
|
374
|
+
handled = true;
|
|
375
|
+
remove(schema.marks.fontSize);
|
|
376
|
+
if (format.fontSize != null) {
|
|
377
|
+
add(schema.marks.fontSize.create({ size: format.fontSize }));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if ("fontFamily" in format) {
|
|
381
|
+
handled = true;
|
|
382
|
+
remove(schema.marks.fontFamily);
|
|
383
|
+
if (format.fontFamily) {
|
|
384
|
+
add(schema.marks.fontFamily.create({ family: format.fontFamily }));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if ("color" in format) {
|
|
388
|
+
handled = true;
|
|
389
|
+
remove(schema.marks.color);
|
|
390
|
+
const css = typeof format.color === "string"
|
|
391
|
+
? format.color
|
|
392
|
+
: format.color
|
|
393
|
+
? resolveColor?.(format.color)
|
|
394
|
+
: undefined;
|
|
395
|
+
if (css) {
|
|
396
|
+
add(schema.marks.color.create({ color: css }));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!handled) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
dispatch?.(tr);
|
|
403
|
+
return true;
|
|
404
|
+
};
|
|
405
|
+
/**
|
|
406
|
+
* Effective fontSize (pt) of the current selection, or `null` when the
|
|
407
|
+
* selection carries no fontSize mark. A collapsed selection reads the stored
|
|
408
|
+
* marks (what the next typed character would get); a range reads the marks of
|
|
409
|
+
* the first text node in the range, matching how the run is rendered.
|
|
410
|
+
*/
|
|
411
|
+
export const getSelectionFontSize = (state) => {
|
|
412
|
+
const { from, to, empty } = state.selection;
|
|
413
|
+
let marks = null;
|
|
414
|
+
if (empty) {
|
|
415
|
+
marks = state.storedMarks ?? state.selection.$from.marks();
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
419
|
+
if (marks)
|
|
420
|
+
return false;
|
|
421
|
+
if (node.isText) {
|
|
422
|
+
marks = node.marks;
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const mark = marks?.find((m) => m.type.name === "fontSize");
|
|
429
|
+
const size = mark?.attrs?.size;
|
|
430
|
+
return typeof size === "number" && Number.isFinite(size) ? size : null;
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Get current cursor position
|
|
434
|
+
* @param state
|
|
435
|
+
* @returns
|
|
436
|
+
*/
|
|
437
|
+
export const getCursorPosition = (state) => {
|
|
438
|
+
try {
|
|
439
|
+
const { selection } = state;
|
|
440
|
+
if (!selection || !selection.empty) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
// Bounds checking
|
|
444
|
+
const pos = selection.from;
|
|
445
|
+
if (pos < 0 || pos > state.doc.content.size) {
|
|
446
|
+
console.warn(`Invalid cursor position: ${pos}, doc size: ${state.doc.content.size}`);
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
return pos;
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
console.warn("Error getting cursor position:", error);
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Get currently focused token
|
|
458
|
+
* @param position
|
|
459
|
+
* @param tokens
|
|
460
|
+
* @returns
|
|
461
|
+
*/
|
|
462
|
+
export const getActiveToken = (position, tokens) => {
|
|
463
|
+
return tokens.find((token) => token.endColumn >= position);
|
|
464
|
+
};
|
|
465
|
+
export const enableCellSelection = (position, cursorPos, tokens) => {
|
|
466
|
+
if (!Number.isFinite(cursorPos) || !tokens.length) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
const [prev, active, next] = getSurroundingTokens(position, tokens);
|
|
470
|
+
const isStringLike = (token) => !!token &&
|
|
471
|
+
(token.tokenType.name === tokenVocabulary.String.name ||
|
|
472
|
+
token.tokenType.name === tokenVocabulary.UnterminatedStringStart.name ||
|
|
473
|
+
token.image === '"');
|
|
474
|
+
const isCoveringPosition = (token) => isStringLike(token) &&
|
|
475
|
+
position >= (token?.startColumn ?? position) &&
|
|
476
|
+
position <= (token?.endColumn ?? position);
|
|
477
|
+
if (isStringLike(active) || isStringLike(next) || isCoveringPosition(prev)) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
const isEmptyFunctionCall = prev?.tokenType.name === tokenVocabulary.Function.name &&
|
|
481
|
+
active?.tokenType.name === tokenVocabulary.CloseParen.name &&
|
|
482
|
+
(prev.image ?? "").endsWith("(") &&
|
|
483
|
+
(active.startColumn ?? 0) === (prev.endColumn ?? -1) + 1;
|
|
484
|
+
if (isEmptyFunctionCall) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
const isAfterCloseParen = active?.tokenType.name === tokenVocabulary.CloseParen.name &&
|
|
488
|
+
cursorPos === position + 1;
|
|
489
|
+
if (isAfterCloseParen) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
const prevAllowed = prev &&
|
|
493
|
+
CELL_SUGGESTION_PREFIX.includes(prev.tokenType.name) &&
|
|
494
|
+
position > (prev.endColumn ?? -Infinity);
|
|
495
|
+
// The cursor must actually be inside (or at the trailing edge of) the
|
|
496
|
+
// active token for it to count as the prefix that enables cell
|
|
497
|
+
// selection. Without the `position >= startColumn` guard, a cursor
|
|
498
|
+
// positioned BEFORE the active token (e.g. caret at index 0 of `=$12`)
|
|
499
|
+
// would still treat the leading `=` as the prefix and flip the editor
|
|
500
|
+
// into cell-suggestion mode. Calculator columns are 1-indexed while
|
|
501
|
+
// `position` (offsetInLine) is 0-indexed, so cursor-just-before-token
|
|
502
|
+
// satisfies `position < startColumn`.
|
|
503
|
+
const activeAllowed = active &&
|
|
504
|
+
CELL_SUGGESTION_PREFIX.includes(active.tokenType.name) &&
|
|
505
|
+
position >= (active.startColumn ?? Infinity) &&
|
|
506
|
+
position <= (active.endColumn ?? position);
|
|
507
|
+
const prefixToken = prevAllowed ? prev : activeAllowed ? active : undefined;
|
|
508
|
+
if (!prefixToken) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
const isActiveReferenceStart = active &&
|
|
512
|
+
isReferenceToken(active) &&
|
|
513
|
+
position + 1 === (active.startColumn ?? position + 1);
|
|
514
|
+
if (isActiveReferenceStart) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
const isNextReferenceStart = next &&
|
|
518
|
+
isReferenceToken(next) &&
|
|
519
|
+
position + 1 === (next.startColumn ?? position + 1);
|
|
520
|
+
if (isNextReferenceStart) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
if (next && !CELL_SUGGESTION_SUFFIX.includes(next.tokenType.name)) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
const activeType = active?.tokenType?.name;
|
|
527
|
+
const isInsideReference = isReferenceToken(active) &&
|
|
528
|
+
position >= (active?.startColumn ?? position) &&
|
|
529
|
+
position <= (active?.endColumn ?? position);
|
|
530
|
+
let isInsideFunctionName = false;
|
|
531
|
+
if (activeType === tokenVocabulary.Function.name && active) {
|
|
532
|
+
const start = active.startColumn ?? position;
|
|
533
|
+
let nameEnd = active.endColumn ?? position;
|
|
534
|
+
const image = active.image ?? "";
|
|
535
|
+
if (image.endsWith("(") && image.length > 1) {
|
|
536
|
+
const nameLength = image.length - 1;
|
|
537
|
+
nameEnd = start + nameLength - 1;
|
|
538
|
+
}
|
|
539
|
+
isInsideFunctionName = position >= start && position <= nameEnd;
|
|
540
|
+
}
|
|
541
|
+
if (isInsideReference ||
|
|
542
|
+
isInsideFunctionName ||
|
|
543
|
+
activeType === tokenVocabulary.String.name ||
|
|
544
|
+
activeType === tokenVocabulary.UnterminatedStringStart.name ||
|
|
545
|
+
activeType === tokenVocabulary.Number.name ||
|
|
546
|
+
activeType === tokenVocabulary.Boolean.name) {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
return true;
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* Get prev, current and next tokens using a position
|
|
553
|
+
* @param position
|
|
554
|
+
* @param tokens
|
|
555
|
+
* @returns
|
|
556
|
+
*/
|
|
557
|
+
export const getSurroundingTokens = (position, tokens) => {
|
|
558
|
+
const activeToken = getActiveToken(position, tokens);
|
|
559
|
+
return [
|
|
560
|
+
getPreviousToken(position, tokens),
|
|
561
|
+
activeToken,
|
|
562
|
+
getNextToken(position, tokens, activeToken),
|
|
563
|
+
];
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Get the previous token from the current position
|
|
567
|
+
* @param position
|
|
568
|
+
* @param tokens
|
|
569
|
+
* @returns
|
|
570
|
+
*/
|
|
571
|
+
export const getPreviousToken = (position, tokens) => {
|
|
572
|
+
let previous;
|
|
573
|
+
for (const token of tokens) {
|
|
574
|
+
if (token.endColumn < position) {
|
|
575
|
+
previous = token;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (token.startColumn <= position && token.endColumn >= position) {
|
|
579
|
+
return previous;
|
|
580
|
+
}
|
|
581
|
+
if (token.startColumn > position) {
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return previous;
|
|
586
|
+
};
|
|
587
|
+
/**
|
|
588
|
+
* Get next token from position
|
|
589
|
+
* @param position
|
|
590
|
+
* @param tokens
|
|
591
|
+
* @returns
|
|
592
|
+
*/
|
|
593
|
+
export const getNextToken = (position, tokens, activeToken) => {
|
|
594
|
+
return tokens.find((token) => {
|
|
595
|
+
if (activeToken && token === activeToken) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
const start = token.startColumn && token.startColumn > 0
|
|
599
|
+
? token.startColumn - 1
|
|
600
|
+
: token.startOffset;
|
|
601
|
+
return start !== undefined && start >= position;
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* Is current token a cell token
|
|
606
|
+
* @param token
|
|
607
|
+
* @returns
|
|
608
|
+
*/
|
|
609
|
+
export const isCellToken = (token) => token.tokenType.name === tokenVocabulary.Cell.name || token.selection;
|
|
610
|
+
export const isTableColumnToken = (token) => token.tokenType.name === tokenVocabulary.TableColumnName.name;
|
|
611
|
+
export const isTableNameToken = (token) => token.tokenType.name === tokenVocabulary.TableName.name;
|
|
612
|
+
export const isNamedRangeToken = (token) => token.tokenType.name === tokenVocabulary.Name.name;
|
|
613
|
+
export const isStringToken = (token) => token.tokenType.name === tokenVocabulary.String.name;
|
|
614
|
+
export const isNumberToken = (token) => token.tokenType.name === tokenVocabulary.Number.name;
|
|
615
|
+
const isReferenceToken = (token) => {
|
|
616
|
+
if (!token)
|
|
617
|
+
return false;
|
|
618
|
+
return (isCellToken(token) ||
|
|
619
|
+
isTableColumnToken(token) ||
|
|
620
|
+
isTableNameToken(token) ||
|
|
621
|
+
isNamedRangeToken(token) ||
|
|
622
|
+
token.tokenType.name === tokenVocabulary.Column.name);
|
|
623
|
+
};
|
|
624
|
+
/**
|
|
625
|
+
* Check if token is a cell reference with property (e.g., A1.title or A1.)
|
|
626
|
+
* @param token
|
|
627
|
+
* @returns
|
|
628
|
+
*/
|
|
629
|
+
export const getPropertyStartIndex = (token) => {
|
|
630
|
+
if (!token?.image) {
|
|
631
|
+
return -1;
|
|
632
|
+
}
|
|
633
|
+
if (token.tokenType.name === tokenVocabulary.Cell.name ||
|
|
634
|
+
token.tokenType.name === tokenVocabulary.Name.name) {
|
|
635
|
+
return token.image.indexOf(".");
|
|
636
|
+
}
|
|
637
|
+
if (token.tokenType.name === tokenVocabulary.TableColumnName.name ||
|
|
638
|
+
token.tokenType.name === tokenVocabulary.SpecialItem.name ||
|
|
639
|
+
token.tokenType.name === tokenVocabulary.TableName.name) {
|
|
640
|
+
const lastClosingBracket = token.image.lastIndexOf("]");
|
|
641
|
+
if (lastClosingBracket === -1) {
|
|
642
|
+
return -1;
|
|
643
|
+
}
|
|
644
|
+
return token.image.indexOf(".", lastClosingBracket + 1);
|
|
645
|
+
}
|
|
646
|
+
return -1;
|
|
647
|
+
};
|
|
648
|
+
export const hasPropertyReference = (token) => {
|
|
649
|
+
const index = getPropertyStartIndex(token);
|
|
650
|
+
return index !== -1;
|
|
651
|
+
};
|
|
652
|
+
/**
|
|
653
|
+
* Get current active function
|
|
654
|
+
* @param curToken
|
|
655
|
+
* @param tokens
|
|
656
|
+
*/
|
|
657
|
+
export const getActiveFunctionToken = (curToken, tokens) => {
|
|
658
|
+
if (!curToken)
|
|
659
|
+
return void 0;
|
|
660
|
+
let i = tokens.findIndex((token) => token === curToken);
|
|
661
|
+
let fn;
|
|
662
|
+
let skip = 0;
|
|
663
|
+
while (i > 0) {
|
|
664
|
+
const token = tokens[i];
|
|
665
|
+
if (token.tokenType.name === tokenVocabulary.CloseParen.name) {
|
|
666
|
+
skip++;
|
|
667
|
+
}
|
|
668
|
+
if (token.tokenType.name === tokenVocabulary.Function.name) {
|
|
669
|
+
if (skip > 0) {
|
|
670
|
+
skip--;
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
fn = token;
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
i--;
|
|
678
|
+
}
|
|
679
|
+
return fn;
|
|
680
|
+
};
|
|
681
|
+
/**
|
|
682
|
+
* get selection index from activetoken
|
|
683
|
+
* @param activeToken
|
|
684
|
+
* @returns
|
|
685
|
+
*/
|
|
686
|
+
export const getActiveSelectionIndex = (activeToken) => {
|
|
687
|
+
return activeToken?.structured ||
|
|
688
|
+
activeToken?.isNamedRange ||
|
|
689
|
+
activeToken?.selection
|
|
690
|
+
? activeToken.index ?? -1
|
|
691
|
+
: -1;
|
|
692
|
+
};
|
|
693
|
+
/**
|
|
694
|
+
* Suggestion token
|
|
695
|
+
* @param position
|
|
696
|
+
* @param activeToken
|
|
697
|
+
* @param view
|
|
698
|
+
* @returns
|
|
699
|
+
*/
|
|
700
|
+
export const getActiveSuggestionToken = (position, activeToken, view) => {
|
|
701
|
+
// If the token is already a function, lets ignore
|
|
702
|
+
if (!activeToken ||
|
|
703
|
+
activeToken.tokenType.name === tokenVocabulary.Function.name) {
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
const propertyStart = hasPropertyReference(activeToken)
|
|
707
|
+
? getPropertyStartIndex(activeToken)
|
|
708
|
+
: -1;
|
|
709
|
+
if (propertyStart !== -1 && activeToken?.image != null) {
|
|
710
|
+
const propertyImage = activeToken.image.slice(propertyStart + 1);
|
|
711
|
+
const propertyStartOffset = (activeToken.startOffset ?? 0) + propertyStart + 1;
|
|
712
|
+
const propertyStartColumn = (activeToken.startColumn ?? 0) + propertyStart + 1;
|
|
713
|
+
const suggestionToken = {
|
|
714
|
+
...activeToken,
|
|
715
|
+
image: propertyImage,
|
|
716
|
+
startOffset: propertyStartOffset,
|
|
717
|
+
endOffset: propertyStartOffset + propertyImage.length,
|
|
718
|
+
startColumn: propertyStartColumn,
|
|
719
|
+
endColumn: propertyStartColumn + propertyImage.length,
|
|
720
|
+
};
|
|
721
|
+
suggestionToken.basePropertyToken = activeToken;
|
|
722
|
+
return suggestionToken;
|
|
723
|
+
}
|
|
724
|
+
// Named ranges.
|
|
725
|
+
// Resolved named ranges have `index` property. If its resolved,
|
|
726
|
+
// lets not show suggestions anymore
|
|
727
|
+
if (activeToken.tokenType.name === tokenVocabulary.Name.name &&
|
|
728
|
+
activeToken.index !== undefined) {
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
// For tables
|
|
732
|
+
if (activeToken.tokenType.name === tokenVocabulary.TableName.name ||
|
|
733
|
+
activeToken.tokenType.name === tokenVocabulary.TableColumnName.name) {
|
|
734
|
+
try {
|
|
735
|
+
const result = getWordAtPosition(view.state, position);
|
|
736
|
+
if (result?.end === position) {
|
|
737
|
+
return undefined;
|
|
738
|
+
}
|
|
739
|
+
// Use the token's image directly for cleanup
|
|
740
|
+
// This avoids complex ProseMirror position calculations
|
|
741
|
+
const tokenImage = activeToken.image || "";
|
|
742
|
+
const tableNameLength = activeToken.tableName
|
|
743
|
+
? activeToken.tableName.length + 1
|
|
744
|
+
: 0;
|
|
745
|
+
if (!tokenImage) {
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
const cleanupImage = cleanupColumnName(tokenImage);
|
|
749
|
+
const change = tokenImage.length - cleanupImage.length;
|
|
750
|
+
return {
|
|
751
|
+
...activeToken,
|
|
752
|
+
startOffset: activeToken.startOffset + tableNameLength + change,
|
|
753
|
+
image: cleanupImage,
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
console.warn("Error processing table suggestion token:", error);
|
|
758
|
+
return activeToken;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return activeToken.tokenType.name === tokenVocabulary.TableColumnName.name ||
|
|
762
|
+
activeToken.tokenType.name === tokenVocabulary.Column.name ||
|
|
763
|
+
activeToken.tokenType.name === tokenVocabulary.Name.name ||
|
|
764
|
+
activeToken.tokenType.name === tokenVocabulary.Function.name ||
|
|
765
|
+
activeToken.tokenType.name === tokenVocabulary.UnterminatedStringStart.name
|
|
766
|
+
? activeToken
|
|
767
|
+
: undefined;
|
|
768
|
+
};
|
|
769
|
+
export const resolveCellPropertyToken = (activeToken, tokens) => {
|
|
770
|
+
if (hasPropertyReference(activeToken)) {
|
|
771
|
+
return activeToken;
|
|
772
|
+
}
|
|
773
|
+
if (!activeToken) {
|
|
774
|
+
return undefined;
|
|
775
|
+
}
|
|
776
|
+
const index = tokens.findIndex((token) => token === activeToken);
|
|
777
|
+
if (index === -1) {
|
|
778
|
+
return undefined;
|
|
779
|
+
}
|
|
780
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
781
|
+
const token = tokens[i];
|
|
782
|
+
if (token.tokenType.name === tokenVocabulary.Cell.name ||
|
|
783
|
+
token.tokenType.name === tokenVocabulary.TableColumnName.name ||
|
|
784
|
+
token.tokenType.name === tokenVocabulary.TableName.name) {
|
|
785
|
+
if (hasPropertyReference(token)) {
|
|
786
|
+
return token;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return undefined;
|
|
791
|
+
};
|
|
792
|
+
/**
|
|
793
|
+
* Get active table token
|
|
794
|
+
* @param position
|
|
795
|
+
* @param activeToken
|
|
796
|
+
* @param tokens
|
|
797
|
+
* @param view
|
|
798
|
+
* @param isEdge
|
|
799
|
+
* @returns
|
|
800
|
+
*/
|
|
801
|
+
export const getActiveTableToken = (position, activeToken, tokens, view, isEdge) => {
|
|
802
|
+
if (!activeToken)
|
|
803
|
+
return undefined;
|
|
804
|
+
let table = void 0;
|
|
805
|
+
// Get character before cursor - use tokenized content for consistency
|
|
806
|
+
const { state } = view;
|
|
807
|
+
const fullContent = state.doc.textContent;
|
|
808
|
+
// Map position back to the original content
|
|
809
|
+
// For now, use simple approach since position is within tokenized text
|
|
810
|
+
const previousCharacter = position > 0 && fullContent.length > position - 1
|
|
811
|
+
? fullContent.charAt(position - 1)
|
|
812
|
+
: "";
|
|
813
|
+
const isFocusOutsideToken = position > activeToken.endColumn;
|
|
814
|
+
// Table1[Column]<--
|
|
815
|
+
if (isEdge && previousCharacter === "]") {
|
|
816
|
+
return undefined;
|
|
817
|
+
}
|
|
818
|
+
// Table[Column] <--- token with spaces
|
|
819
|
+
if (activeToken.tokenType.name === tokenVocabulary.TableName.name &&
|
|
820
|
+
!isFocusOutsideToken) {
|
|
821
|
+
return activeToken;
|
|
822
|
+
}
|
|
823
|
+
// [ <--- Square with space
|
|
824
|
+
if (activeToken.tokenType.name === tokenVocabulary.OpenSquareParen.name &&
|
|
825
|
+
!isFocusOutsideToken) {
|
|
826
|
+
table = activeToken;
|
|
827
|
+
}
|
|
828
|
+
let i = tokens.findIndex((token) => token === activeToken);
|
|
829
|
+
while (i > 0) {
|
|
830
|
+
const token = tokens[i];
|
|
831
|
+
// User starts with [ or [Column
|
|
832
|
+
if (token.tokenType.name === tokenVocabulary.OpenSquareParen.name ||
|
|
833
|
+
token.tokenType.name === tokenVocabulary.TableColumnName.name) {
|
|
834
|
+
const previousToken = tokens[i - 1];
|
|
835
|
+
if (!previousToken ||
|
|
836
|
+
CELL_SUGGESTION_PREFIX.indexOf(previousToken.tokenType.name) !== -1) {
|
|
837
|
+
return token;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
// Find tableName by going back from the
|
|
841
|
+
// current location
|
|
842
|
+
if ([
|
|
843
|
+
tokenVocabulary.Name.name,
|
|
844
|
+
tokenVocabulary.OpenSquareParen.name,
|
|
845
|
+
tokenVocabulary.CloseSquareParen.name,
|
|
846
|
+
tokenVocabulary.Colon.name,
|
|
847
|
+
tokenVocabulary.Column.name,
|
|
848
|
+
].indexOf(token.tokenType.name) !== -1) {
|
|
849
|
+
i--;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
// Exclude certain tokens that should not be inside
|
|
853
|
+
// a structured reference
|
|
854
|
+
if (CELL_SUGGESTION_SUFFIX.indexOf(token.tokenType.name) !== -1 ||
|
|
855
|
+
CELL_SUGGESTION_PREFIX.indexOf(token.tokenType.name) !== -1) {
|
|
856
|
+
return undefined;
|
|
857
|
+
}
|
|
858
|
+
// Found table
|
|
859
|
+
if (token.tokenType.name === tokenVocabulary.TableName.name) {
|
|
860
|
+
table = token;
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
i--;
|
|
864
|
+
}
|
|
865
|
+
return table;
|
|
866
|
+
};
|
|
867
|
+
/**
|
|
868
|
+
* Get current parameter index that use is focused on
|
|
869
|
+
* @param curToken
|
|
870
|
+
* @param functionToken
|
|
871
|
+
* @param tokens
|
|
872
|
+
*/
|
|
873
|
+
export const getParameterIndex = (curToken, functionToken, tokens) => {
|
|
874
|
+
let startIndex = tokens.findIndex((token) => token === curToken);
|
|
875
|
+
const endIndex = tokens.findIndex((token) => token === functionToken);
|
|
876
|
+
let count = 0;
|
|
877
|
+
/* Skips nested functions */
|
|
878
|
+
let skip = 0;
|
|
879
|
+
// Fix for =SUMX2MY2({1, 2, 3}, {4, 5, 6}), Store indices we should skip
|
|
880
|
+
let replace = false;
|
|
881
|
+
const skipCommaIndices = [];
|
|
882
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
883
|
+
const token = tokens[i];
|
|
884
|
+
if (token.image === "}") {
|
|
885
|
+
replace = true;
|
|
886
|
+
}
|
|
887
|
+
else if (token.image === "{") {
|
|
888
|
+
replace = false;
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
if (replace) {
|
|
892
|
+
skipCommaIndices.push(i);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
while (startIndex > endIndex) {
|
|
897
|
+
const token = tokens[startIndex];
|
|
898
|
+
if (token.image === ")") {
|
|
899
|
+
skip++;
|
|
900
|
+
}
|
|
901
|
+
else if (token.tokenType.name === "Comma" &&
|
|
902
|
+
skip === 0 &&
|
|
903
|
+
!(skipCommaIndices.indexOf(startIndex) !== -1)) {
|
|
904
|
+
count++;
|
|
905
|
+
}
|
|
906
|
+
if (token.tokenType.name === "Function") {
|
|
907
|
+
if (skip > 0)
|
|
908
|
+
skip--;
|
|
909
|
+
}
|
|
910
|
+
startIndex--;
|
|
911
|
+
}
|
|
912
|
+
return count;
|
|
913
|
+
};
|
|
914
|
+
export const getFunctionParameters = (input, functionToken) => {
|
|
915
|
+
if (!functionToken) {
|
|
916
|
+
return { parameters: [], ranges: [] };
|
|
917
|
+
}
|
|
918
|
+
const searchStart = functionToken.startColumn ?? 0;
|
|
919
|
+
const openIndex = input.indexOf("(", searchStart);
|
|
920
|
+
if (openIndex === -1) {
|
|
921
|
+
return { parameters: [], ranges: [] };
|
|
922
|
+
}
|
|
923
|
+
let depth = 0;
|
|
924
|
+
let closeIndex = -1;
|
|
925
|
+
for (let i = openIndex; i < input.length; i++) {
|
|
926
|
+
const ch = input[i];
|
|
927
|
+
if (ch === "(")
|
|
928
|
+
depth++;
|
|
929
|
+
else if (ch === ")") {
|
|
930
|
+
depth--;
|
|
931
|
+
if (depth === 0) {
|
|
932
|
+
closeIndex = i;
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
// If user hasn't typed the closing paren yet, use the end of the string
|
|
938
|
+
if (closeIndex === -1) {
|
|
939
|
+
closeIndex = input.length;
|
|
940
|
+
}
|
|
941
|
+
if (closeIndex <= openIndex) {
|
|
942
|
+
return { parameters: [], ranges: [] };
|
|
943
|
+
}
|
|
944
|
+
const argsSlice = input.slice(openIndex + 1, closeIndex);
|
|
945
|
+
const parameters = [];
|
|
946
|
+
const ranges = [];
|
|
947
|
+
let start = 0;
|
|
948
|
+
let brace = 0;
|
|
949
|
+
let bracket = 0;
|
|
950
|
+
let paren = 0;
|
|
951
|
+
let inSingle = false;
|
|
952
|
+
let inDouble = false;
|
|
953
|
+
const pushParam = (endIndex) => {
|
|
954
|
+
const raw = argsSlice.slice(start, endIndex);
|
|
955
|
+
const leading = raw.length - raw.trimStart().length;
|
|
956
|
+
const trailing = raw.length - raw.trimEnd().length;
|
|
957
|
+
const absoluteStart = openIndex + 1 + start + leading;
|
|
958
|
+
const absoluteEnd = openIndex + 1 + endIndex - trailing;
|
|
959
|
+
parameters.push(raw.trim());
|
|
960
|
+
ranges.push({ start: absoluteStart, end: absoluteEnd });
|
|
961
|
+
};
|
|
962
|
+
for (let i = 0; i <= argsSlice.length; i++) {
|
|
963
|
+
const ch = argsSlice[i];
|
|
964
|
+
if (i === argsSlice.length ||
|
|
965
|
+
(ch === "," &&
|
|
966
|
+
paren === 0 &&
|
|
967
|
+
brace === 0 &&
|
|
968
|
+
bracket === 0 &&
|
|
969
|
+
!inSingle &&
|
|
970
|
+
!inDouble)) {
|
|
971
|
+
pushParam(i);
|
|
972
|
+
start = i + 1;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (ch === "'" && !inDouble) {
|
|
976
|
+
inSingle = !inSingle;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (ch === '"' && !inSingle) {
|
|
980
|
+
inDouble = !inDouble;
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (inSingle || inDouble)
|
|
984
|
+
continue;
|
|
985
|
+
if (ch === "(")
|
|
986
|
+
paren++;
|
|
987
|
+
else if (ch === ")")
|
|
988
|
+
paren--;
|
|
989
|
+
else if (ch === "{")
|
|
990
|
+
brace++;
|
|
991
|
+
else if (ch === "}")
|
|
992
|
+
brace--;
|
|
993
|
+
else if (ch === "[")
|
|
994
|
+
bracket++;
|
|
995
|
+
else if (ch === "]")
|
|
996
|
+
bracket--;
|
|
997
|
+
}
|
|
998
|
+
return { parameters, ranges };
|
|
999
|
+
};
|
|
1000
|
+
/**
|
|
1001
|
+
* Get content and offset for tokenization, handling both single and multi-line scenarios
|
|
1002
|
+
* @param state ProseMirror editor state
|
|
1003
|
+
* @param cursorPos Current cursor position
|
|
1004
|
+
* @returns Object with line content and offset within that content
|
|
1005
|
+
*/
|
|
1006
|
+
export const getTokenizationContent = (state, cursorPos) => {
|
|
1007
|
+
try {
|
|
1008
|
+
// Validate cursor position
|
|
1009
|
+
if (cursorPos < 0 || cursorPos > state.doc.content.size) {
|
|
1010
|
+
console.warn(`Invalid cursor position: ${cursorPos}, doc size: ${state.doc.content.size}`);
|
|
1011
|
+
return {
|
|
1012
|
+
line: state.doc.textContent || "",
|
|
1013
|
+
offsetInLine: 0,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
const $pos = state.doc.resolve(cursorPos);
|
|
1017
|
+
// Strategy 1: For typical spreadsheet formulas, tokenize entire content
|
|
1018
|
+
// This works best when content is a single formula that may span lines
|
|
1019
|
+
const fullContent = state.doc.textContent;
|
|
1020
|
+
if (fullContent.startsWith("=") || fullContent.length <= 1000) {
|
|
1021
|
+
// This looks like a formula or is short enough to tokenize entirely
|
|
1022
|
+
// Calculate position in the full text content
|
|
1023
|
+
// Need to account for ProseMirror's document structure vs plain text
|
|
1024
|
+
let textPosition = 0;
|
|
1025
|
+
let found = false;
|
|
1026
|
+
state.doc.descendants((node, pos) => {
|
|
1027
|
+
if (found)
|
|
1028
|
+
return false;
|
|
1029
|
+
if (node.isText) {
|
|
1030
|
+
if (pos <= cursorPos && cursorPos <= pos + node.nodeSize) {
|
|
1031
|
+
// Cursor is within this text node
|
|
1032
|
+
textPosition += cursorPos - pos;
|
|
1033
|
+
found = true;
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
else if (pos + node.nodeSize < cursorPos) {
|
|
1037
|
+
// This text node is before cursor position
|
|
1038
|
+
textPosition += node.text?.length || 0;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
return !found;
|
|
1042
|
+
});
|
|
1043
|
+
return {
|
|
1044
|
+
line: fullContent,
|
|
1045
|
+
offsetInLine: Math.max(0, textPosition),
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
// Strategy 2: For non-formula content, use current line approach
|
|
1049
|
+
// This works better for multi-line text that isn't a single formula
|
|
1050
|
+
const parent = $pos.parent;
|
|
1051
|
+
if (parent && parent.isTextblock) {
|
|
1052
|
+
const lineStart = $pos.start($pos.depth);
|
|
1053
|
+
const offsetInLine = Math.max(0, cursorPos - lineStart);
|
|
1054
|
+
return {
|
|
1055
|
+
line: parent.textContent || "",
|
|
1056
|
+
offsetInLine: offsetInLine,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
// Strategy 3: Fallback to full content with simple position mapping
|
|
1060
|
+
return {
|
|
1061
|
+
line: fullContent,
|
|
1062
|
+
offsetInLine: Math.max(0, Math.min(cursorPos, fullContent.length)),
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
console.warn("Error getting tokenization content:", error);
|
|
1067
|
+
// Safe fallback
|
|
1068
|
+
const safeContent = state.doc.textContent || "";
|
|
1069
|
+
return {
|
|
1070
|
+
line: safeContent,
|
|
1071
|
+
offsetInLine: Math.max(0, Math.min(cursorPos, safeContent.length)),
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
/**
|
|
1076
|
+
* Cleanup function token to remove parentheses
|
|
1077
|
+
* @param text
|
|
1078
|
+
*/
|
|
1079
|
+
export const cleanFunctionToken = (text) => {
|
|
1080
|
+
return text.replace(new RegExp(/\(|\)/, "gi"), "");
|
|
1081
|
+
};
|
|
1082
|
+
// Alternative approach using ProseMirror's built-in word detection:
|
|
1083
|
+
function getWordAtPosition(editorState, pos) {
|
|
1084
|
+
const doc = editorState.doc;
|
|
1085
|
+
const $pos = doc.resolve(pos);
|
|
1086
|
+
// Use ProseMirror's word pattern matching
|
|
1087
|
+
const wordRE = /\w+/g;
|
|
1088
|
+
const textNode = $pos.parent;
|
|
1089
|
+
const textContent = textNode.textContent;
|
|
1090
|
+
const localPos = $pos.parentOffset;
|
|
1091
|
+
let match;
|
|
1092
|
+
let wordStart = null;
|
|
1093
|
+
let wordEnd = null;
|
|
1094
|
+
// Find all word matches and determine which one contains our position
|
|
1095
|
+
while ((match = wordRE.exec(textContent)) !== null) {
|
|
1096
|
+
const matchStart = match.index;
|
|
1097
|
+
const matchEnd = match.index + match[0].length;
|
|
1098
|
+
if (localPos >= matchStart && localPos <= matchEnd) {
|
|
1099
|
+
wordStart = $pos.start() + matchStart;
|
|
1100
|
+
wordEnd = $pos.start() + matchEnd;
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
if (wordStart === null || wordEnd === null) {
|
|
1105
|
+
return undefined;
|
|
1106
|
+
}
|
|
1107
|
+
const wordText = doc.textBetween(wordStart, wordEnd, " ");
|
|
1108
|
+
return {
|
|
1109
|
+
start: wordStart,
|
|
1110
|
+
end: wordEnd,
|
|
1111
|
+
text: wordText,
|
|
1112
|
+
};
|
|
1113
|
+
}
|