@marimo-team/frontend 0.23.16-dev4 → 0.23.16-dev40
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/dist/assets/{CellStatus-BalYv0pK.js → CellStatus-C6Cj3oJc.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-DI7mV-Gw.js → ConnectedDataExplorerComponent-CMaZapRd.js} +1 -1
- package/dist/assets/{ErrorBoundary-C7_5jjWi.js → ErrorBoundary-DzTD8LVq.js} +1 -1
- package/dist/assets/{ImperativeModal-BIavOiUv.js → ImperativeModal-fY-ZfT0b.js} +1 -1
- package/dist/assets/{JsonOutput-CkCD-FgM.js → JsonOutput-CyzvjIjx.js} +12 -12
- package/dist/assets/{LazyAnyLanguageCodeMirror-sVm_hz8A.js → LazyAnyLanguageCodeMirror-B2JvSXP-.js} +2 -2
- package/dist/assets/{MarimoErrorOutput-DO2k2fA5.js → MarimoErrorOutput-CIuzXFFo.js} +4 -4
- package/dist/assets/{RSPContexts-CgC81f0I.js → RSPContexts-WiCgGm__.js} +1 -1
- package/dist/assets/RenderHTML-Cd-PP5ZR.js +1 -0
- package/dist/assets/{RunButton-BKRupqr7.js → RunButton-CnYWedW4.js} +1 -1
- package/dist/assets/{add-cell-with-ai-CEZtaZCK.js → add-cell-with-ai-ByOBQT99.js} +8 -8
- package/dist/assets/add-connection-dialog-C97ylgk0.js +170 -0
- package/dist/assets/{agent-panel-CHB6t7Zy.js → agent-panel-Dxo9WUKV.js} +5 -5
- package/dist/assets/ai-model-dropdown-Bvqy98cI.js +5 -0
- package/dist/assets/{alert-dialog-BXegNC0I.js → alert-dialog-Bb8Lz8ko.js} +1 -1
- package/dist/assets/{any-language-editor-DCpZRrpx.js → any-language-editor--tRHs8C_.js} +1 -1
- package/dist/assets/{app-config-button-NrM9ywXA.js → app-config-button-v1lG63C2.js} +1 -1
- package/dist/assets/{button-eIihcbUw.js → button-DDGiD1NW.js} +1 -1
- package/dist/assets/{cache-panel-wllSTlbM.js → cache-panel-DcQUeUpg.js} +1 -1
- package/dist/assets/{capabilities-B09j1Eor.js → capabilities-DoEg0XwL.js} +1 -1
- package/dist/assets/{cell-editor-B5JVq7_q.js → cell-editor-BKrHbyX_.js} +16 -16
- package/dist/assets/cell-link-w5IbCQV4.js +1 -0
- package/dist/assets/cells-DzyDERZy.js +235 -0
- package/dist/assets/{chat-display-BG08ltQ3.js → chat-display-Ccgk4vbE.js} +1 -1
- package/dist/assets/{chat-panel-P2CzCtXJ.js → chat-panel--53TIc3t.js} +4 -4
- package/dist/assets/{chat-ui-fAJZAoTD.js → chat-ui-B5rBxgsw.js} +4 -4
- package/dist/assets/{column-preview-Dne1dIt0.js → column-preview-Cb8R9kfl.js} +1 -1
- package/dist/assets/{command-CAJLPEBq.js → command-Dqxie4gq.js} +1 -1
- package/dist/assets/{command-palette-iOCqUIs1.js → command-palette-B1S9PjZi.js} +1 -1
- package/dist/assets/{common-CD6Mj8n6.js → common-CJ3baa6D.js} +1 -1
- package/dist/assets/components-DpUIWGaj.js +1 -0
- package/dist/assets/config-B-Q7J_6d.js +1 -0
- package/dist/assets/{config-COUOIPEq.js → config-BJVx3eBW.js} +1 -1
- package/dist/assets/{copy-BtTOqPTS.js → copy-Bxb1_Gtw.js} +1 -1
- package/dist/assets/{copy-icon-C2pG0ZNK.js → copy-icon-neDQUo4Q.js} +1 -1
- package/dist/assets/{createReducer-C7kZ8zEe.js → createReducer-DGx5Af-C.js} +1 -1
- package/dist/assets/{datasource-ySAiC9gr.js → datasource-D_VcQMEn.js} +2 -2
- package/dist/assets/{dates-D-kIKDYA.js → dates-C2LWzsCZ.js} +1 -1
- package/dist/assets/{dependency-graph-panel-BF23EwcD.js → dependency-graph-panel-DY5lvPDx.js} +3 -3
- package/dist/assets/{dialog-D1DEsRZ2.js → dialog-BCVepWLJ.js} +1 -1
- package/dist/assets/{dist-ByVeMWkr.js → dist-BLN8PNJV.js} +1 -1
- package/dist/assets/dist-CiVShZKM.js +2 -0
- package/dist/assets/{documentation-panel-e9Fi9R7e.js → documentation-panel-CGhwxsXK.js} +1 -1
- package/dist/assets/download-DpS57jyt.js +9 -0
- package/dist/assets/{dropdown-menu-BFZLnWeO.js → dropdown-menu-dHmZR8bI.js} +1 -1
- package/dist/assets/{edit-page-7X3HYey7.js → edit-page-DA765yQV.js} +6 -6
- package/dist/assets/{error-banner-B6eIlxzg.js → error-banner-BSxEOIn2.js} +1 -1
- package/dist/assets/error-entries-CTYMwfsh.js +8 -0
- package/dist/assets/{error-panel-DdsUL6b7.js → error-panel-DGAkG5kW.js} +1 -1
- package/dist/assets/{es-1D83cgiu.js → es-gYgfF8y5.js} +1 -1
- package/dist/assets/{esm-BL7jQmzn.js → esm-B30N1GS0.js} +1 -1
- package/dist/assets/{field-BeZbVzXA.js → field-BsxD35VE.js} +1 -1
- package/dist/assets/{file-explorer-panel-Ntq1-VbF.js → file-explorer-panel-BKUo-MDh.js} +3 -3
- package/dist/assets/{file-icons-Cb54mIL8.js → file-icons-DfWENcb8.js} +1 -1
- package/dist/assets/{file-name-input-BWFcK1ey.js → file-name-input-CNVZtm5K.js} +1 -1
- package/dist/assets/floating-outline-LitP0wCD.js +1 -0
- package/dist/assets/focus-CNKbm-Xz.js +1 -0
- package/dist/assets/{form-B6BkSCJb.js → form-nHzLTU1m.js} +2 -2
- package/dist/assets/{formats-ClEFGPy-.js → formats-DzmLpc9A.js} +1 -1
- package/dist/assets/{formatting-B_41gSF5.js → formatting-CDf6w1Kt.js} +1 -1
- package/dist/assets/{fullscreen-dUjPYOmm.js → fullscreen-CTDqNQr6.js} +1 -1
- package/dist/assets/{gallery-page-CWQQAYpa.js → gallery-page-CJ3ms1Hn.js} +1 -1
- package/dist/assets/{glide-data-editor-lMARFWHS.js → glide-data-editor-j2igs9Y_.js} +1 -1
- package/dist/assets/home-page-DM9Z_IJ7.js +4 -0
- package/dist/assets/{html-to-image-CwmX28T9.js → html-to-image-JaxFFZcr.js} +2 -2
- package/dist/assets/index-C7_REKh1.js +38 -0
- package/dist/assets/index-D6Dp_PFj.css +2 -0
- package/dist/assets/{input-CqrUjynb.js → input-JxFwu-3G.js} +1 -1
- package/dist/assets/kiosk-mode-BumOFEtY.js +1 -0
- package/dist/assets/{label-BElwWzf0.js → label-CqjrpwRC.js} +1 -1
- package/dist/assets/{layout-CUlZijwG.js → layout-B29hqiiK.js} +5 -5
- package/dist/assets/{links-CToBjIIj.js → links-CZr5bjvo.js} +1 -1
- package/dist/assets/logs-panel-CQRPYS8j.js +1 -0
- package/dist/assets/{markdown-renderer-QvXG0CRk.js → markdown-renderer-sEayY9-m.js} +2 -2
- package/dist/assets/{menu-items-DbSLBfXR.js → menu-items-CCi6aOPm.js} +1 -1
- package/dist/assets/{mermaid-Bh0flEIb.js → mermaid-CYN0wOyG.js} +1 -1
- package/dist/assets/{name-cell-input-C6cukQ9k.js → name-cell-input-B-aj1KH3.js} +1 -1
- package/dist/assets/node-sql-parser-Si4oZ2P-.js +68 -0
- package/dist/assets/{numbers-CeeaFKqu.js → numbers-rmawRqQP.js} +1 -1
- package/dist/assets/{outline-panel-BPlg9_CE.js → outline-panel-BOryk60D.js} +1 -1
- package/dist/assets/{packages-panel-BzQF60rG.js → packages-panel-DoYDLty3.js} +1 -1
- package/dist/assets/pair-with-agent-modal-BCRSACei.js +4 -0
- package/dist/assets/panels-Bb4fIHgH.js +4 -0
- package/dist/assets/{popover-DrHQ3KEt.js → popover-Bcz1Zikb.js} +1 -1
- package/dist/assets/process-output-d-LEMtGI.js +1 -0
- package/dist/assets/{radio-group-CLBjGkt5.js → radio-group-DfioQthW.js} +1 -1
- package/dist/assets/{readonly-code-display-D63oRtBP.js → readonly-code-display-C9fu8tvK.js} +1 -1
- package/dist/assets/{readonly-python-code-CW_G5e4E.js → readonly-python-code-DHxOnQGj.js} +1 -1
- package/dist/assets/{renderShortcut-BZLr8D7H.js → renderShortcut-Bl8_FVn8.js} +1 -1
- package/dist/assets/{reveal-component-BLMTmd8T.js → reveal-component-5jf0W625.js} +60 -60
- package/dist/assets/{run-page-B6pgDYEJ.js → run-page-BMKx9ld_.js} +1 -1
- package/dist/assets/{runs-D51Xwrsk.js → runs-DBTXqJHS.js} +1 -1
- package/dist/assets/scratchpad-panel-C8z8vzBX.js +1 -0
- package/dist/assets/{scroll-to-bottom-button-Cgr10kaJ.js → scroll-to-bottom-button-C8yfT4wq.js} +1 -1
- package/dist/assets/{secrets-panel-Cc5KDBk6.js → secrets-panel-6J6wcuVR.js} +1 -1
- package/dist/assets/{select-B5TFNjmU.js → select-BP75_5jJ.js} +1 -1
- package/dist/assets/{session-CEFnN9Cq.js → session-BmukRjSi.js} +1 -1
- package/dist/assets/{session-panel-Y0Oon0sD.js → session-panel-BVVk-02h.js} +1 -1
- package/dist/assets/{share-CYpcX4N4.js → share-852X_cqy.js} +1 -1
- package/dist/assets/{skeleton-D3QPxqWC.js → skeleton-Bubr9pIj.js} +1 -1
- package/dist/assets/slides-DkEgzTJB.css +1 -0
- package/dist/assets/{snippets-panel-CXpGflMO.js → snippets-panel-U2-iok-c.js} +1 -1
- package/dist/assets/{spec-CzqcmYQk.js → spec-C7opZZRp.js} +1 -1
- package/dist/assets/{state-BvdK8eiw.js → state-D10U2MwA.js} +1 -1
- package/dist/assets/{state-WBgc7MiA.js → state-DDag0B_B.js} +1 -1
- package/dist/assets/state-DtziTd1T.js +3 -0
- package/dist/assets/{state-DBzEQtWz.js → state-DxQhUsG9.js} +1 -1
- package/dist/assets/{strings-Djnbwmfl.js → strings--BknHX9C.js} +1 -1
- package/dist/assets/{swiper-component-DInvCxhI.js → swiper-component-g8EXE911.js} +1 -1
- package/dist/assets/switch-OVajJBPR.js +1 -0
- package/dist/assets/{terminal-D3IQBBYo.js → terminal-217O8hlZ.js} +1 -1
- package/dist/assets/{textarea-CpFfEUet.js → textarea-Bmzmbbpo.js} +1 -1
- package/dist/assets/{tooltip-CFX4fYHU.js → tooltip-4bZlU7jl.js} +1 -1
- package/dist/assets/{tooltip-HLb6RxVp.js → tooltip-B7U4xkIN.js} +1 -1
- package/dist/assets/{tracing-CKn3gWHw.js → tracing-B5XFvMmV.js} +1 -1
- package/dist/assets/{tracing-panel-BU-o_LCd.js → tracing-panel-DnR2bYDe.js} +2 -2
- package/dist/assets/{tree-actions-CsC2XmGo.js → tree-actions-Dx9CDaxV.js} +1 -1
- package/dist/assets/{useBoolean-D89u30n-.js → useBoolean-CFj86iac.js} +1 -1
- package/dist/assets/useCellActionButton-DypR3yDt.js +1 -0
- package/dist/assets/{useDeleteCell-EPH4kcG7.js → useDeleteCell-vMXAr3rY.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-CZnK7N2L.js → useDependencyPanelTab-CwdwKf7A.js} +1 -1
- package/dist/assets/useEventListener-C0195JsF.js +1 -0
- package/dist/assets/{useHotkey-DIuRH8Wu.js → useHotkey-DLZvrRZT.js} +1 -1
- package/dist/assets/{useIframeCapabilities-Ccb4fvKc.js → useIframeCapabilities-j6fR_dBA.js} +1 -1
- package/dist/assets/{useInstallPackage-BRbUAUFn.js → useInstallPackage-WiYBwhUO.js} +1 -1
- package/dist/assets/{useInterval-CC0vKpWm.js → useInterval-CWBTUy5W.js} +1 -1
- package/dist/assets/useNotebookActions-jWIcmpv0.js +12 -0
- package/dist/assets/{useRunCells-CSSF_mJ0.js → useRunCells-C2dvU2jA.js} +1 -1
- package/dist/assets/{useSplitCell-9Qz93yPZ.js → useSplitCell-C1tubdNi.js} +1 -1
- package/dist/assets/{useTheme-KXqAyDPC.js → useTheme-Bxv9B31g.js} +1 -1
- package/dist/assets/{utils-Z80SV4_d.js → utils-BWBBpCkT.js} +1 -1
- package/dist/assets/{vega-component-Cie3ag_T.js → vega-component-Db8KjZJG.js} +1 -1
- package/dist/assets/{write-secret-modal-DEEn7sZV.js → write-secret-modal-3-v-dHVj.js} +1 -1
- package/dist/index.html +68 -68
- package/package.json +3 -4
- package/src/__mocks__/requests.ts +45 -4
- package/src/components/data-table/__tests__/__snapshots__/chart-spec-model.test.ts.snap +174 -21
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +102 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +163 -103
- package/src/components/data-table/column-summary/legacy-chart-spec.ts +15 -7
- package/src/components/data-table/column-summary/utils.ts +26 -7
- package/src/components/data-table/types.ts +1 -1
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +99 -18
- package/src/components/editor/actions/__tests__/pdf-export.test.ts +0 -4
- package/src/components/editor/actions/pair-with-agent-commands.ts +21 -10
- package/src/components/editor/actions/pair-with-agent-modal.tsx +2 -2
- package/src/components/editor/actions/pdf-export.ts +1 -4
- package/src/components/editor/actions/useNotebookActions.tsx +7 -13
- package/src/components/editor/cell/code/__tests__/cell-editor-activity-lifecycle.test.ts +250 -0
- package/src/components/editor/cell/code/cell-editor.tsx +13 -7
- package/src/components/editor/chrome/panels/__tests__/logs-panel.test.tsx +25 -0
- package/src/components/editor/chrome/panels/logs-panel.tsx +12 -2
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +50 -0
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +87 -8
- package/src/components/editor/connections/storage/as-code.ts +40 -21
- package/src/components/editor/connections/storage/schemas.ts +83 -42
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/components/slides/__tests__/cell-output-block.test.tsx +40 -0
- package/src/components/slides/__tests__/slide-cell-view.test.tsx +33 -0
- package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +198 -0
- package/src/components/slides/reveal-component.tsx +25 -19
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slide-scroll-hint.tsx +161 -0
- package/src/components/slides/slides.css +4 -2
- package/src/components/static-html/share-modal.tsx +1 -1
- package/src/components/ui/input.tsx +1 -1
- package/src/core/cells/__tests__/cells.test.ts +12 -2
- package/src/core/cells/__tests__/logs.test.ts +5 -5
- package/src/core/cells/cells.ts +17 -4
- package/src/core/cells/document-changes.ts +1 -0
- package/src/core/cells/logs.ts +14 -4
- package/src/core/codemirror/copilot/__tests__/language-server.test.ts +396 -100
- package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -540
- package/src/core/codemirror/copilot/language-server.ts +182 -70
- package/src/core/codemirror/copilot/transport.ts +15 -207
- package/src/core/codemirror/copilot/types.ts +9 -2
- package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +42 -30
- package/src/core/codemirror/find-replace/navigate.ts +2 -0
- package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +24 -3
- package/src/core/codemirror/go-to-definition/commands.ts +3 -0
- package/src/core/codemirror/keymaps/__tests__/keymaps.test.ts +31 -2
- package/src/core/codemirror/keymaps/keymaps.ts +12 -0
- package/src/core/codemirror/language/__tests__/sql.test.ts +443 -61
- package/src/core/codemirror/language/languages/python.ts +23 -11
- package/src/core/codemirror/language/languages/sql/completion-store.ts +5 -1
- package/src/core/codemirror/language/languages/sql/sql.ts +210 -3
- package/src/core/codemirror/lsp/__tests__/federated-lsp.test.ts +161 -0
- package/src/core/codemirror/lsp/__tests__/log-messages.test.ts +53 -0
- package/src/core/codemirror/lsp/__tests__/normalize-markdown-math.test.ts +25 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +598 -156
- package/src/core/codemirror/lsp/__tests__/transports.test.ts +112 -0
- package/src/core/codemirror/lsp/federated-lsp.ts +76 -23
- package/src/core/codemirror/lsp/log-messages.ts +37 -0
- package/src/core/codemirror/lsp/normalize-markdown-math.ts +8 -4
- package/src/core/codemirror/lsp/notebook-lsp.ts +220 -56
- package/src/core/codemirror/lsp/transports.ts +8 -1
- package/src/core/codemirror/lsp/types.ts +68 -8
- package/src/core/codemirror/lsp/utils.ts +14 -0
- package/src/core/codemirror/utils.ts +28 -0
- package/src/core/hotkeys/hotkeys.ts +0 -2
- package/src/core/islands/__tests__/bridge.test.ts +107 -0
- package/src/core/islands/bridge.ts +16 -0
- package/src/core/lsp/__tests__/transport.test.ts +509 -337
- package/src/core/lsp/transport.ts +304 -132
- package/src/core/mode.ts +10 -0
- package/src/core/network/__tests__/api.test.ts +42 -0
- package/src/core/network/__tests__/connection.test.ts +45 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +32 -1
- package/src/core/network/__tests__/requests-network.test.ts +21 -0
- package/src/core/network/api.ts +27 -0
- package/src/core/network/connection.ts +15 -0
- package/src/core/network/requests-lazy.ts +1 -0
- package/src/core/network/requests-network.ts +14 -8
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +22 -6
- package/src/core/rtc/state.ts +9 -0
- package/src/core/static/download-html.ts +5 -14
- package/src/core/wasm/bridge.ts +4 -2
- package/src/core/wasm/worker/types.ts +5 -2
- package/src/css/app/codemirror.css +16 -2
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/FormPlugin.test.tsx +162 -0
- package/src/stories/log-viewer.stories.tsx +12 -6
- package/src/utils/__tests__/download.test.tsx +24 -1
- package/src/utils/download.ts +11 -6
- package/dist/assets/RenderHTML-BCvU9dYz.js +0 -1
- package/dist/assets/add-connection-dialog-B-jCRNTN.js +0 -170
- package/dist/assets/ai-model-dropdown-DXgLphFa.js +0 -5
- package/dist/assets/cell-link-DqBkyAFe.js +0 -1
- package/dist/assets/cells-Cf7bHHq3.js +0 -229
- package/dist/assets/components-BH6gDVv6.js +0 -1
- package/dist/assets/config-BbOTTCYY.js +0 -1
- package/dist/assets/dist-BCMBu0rO.js +0 -2
- package/dist/assets/download-BomYt3nl.js +0 -9
- package/dist/assets/error-entries-Birw-8VX.js +0 -8
- package/dist/assets/floating-outline-wz-WSRgP.js +0 -1
- package/dist/assets/focus-DiEyfiAG.js +0 -1
- package/dist/assets/home-page-DJ4jJsZj.js +0 -4
- package/dist/assets/index-A71LAt6h.css +0 -2
- package/dist/assets/index-CMZzy-dF.js +0 -38
- package/dist/assets/kiosk-mode-ABm3oYgq.js +0 -1
- package/dist/assets/logs-panel-iFV4ZJ9b.js +0 -1
- package/dist/assets/node-sql-parser-BWgUjcfV.js +0 -68
- package/dist/assets/pair-with-agent-modal-CKOYwOYw.js +0 -4
- package/dist/assets/panels-Dj45fW-v.js +0 -4
- package/dist/assets/process-output-BEhIGfkO.js +0 -1
- package/dist/assets/scratchpad-panel-B0mEqBNk.js +0 -1
- package/dist/assets/slides-MCbMDtlY.css +0 -1
- package/dist/assets/state-BZQc7xxL.js +0 -3
- package/dist/assets/switch-CvweaCXq.js +0 -1
- package/dist/assets/useCellActionButton-CRfIEVB5.js +0 -1
- package/dist/assets/useEventListener-B_w3A283.js +0 -1
- package/dist/assets/useNotebookActions-DTXAoukG.js +0 -12
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
|
|
5
|
+
import { store } from "@/core/state/jotai";
|
|
6
|
+
import { createTransport } from "../transports";
|
|
7
|
+
|
|
8
|
+
vi.mock("../../../network/connection", () => ({
|
|
9
|
+
waitForConnectionOpen: vi.fn(() => Promise.resolve()),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock("../../../runtime/config", () => ({
|
|
13
|
+
getRuntimeManager: () => ({
|
|
14
|
+
getLSPURL: (server: string) => new URL(`ws://localhost:2718/lsp/${server}`),
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
/** Minimal stand-in for the browser WebSocket used by `WebSocketTransport`. */
|
|
19
|
+
class FakeWebSocket {
|
|
20
|
+
static readonly instances: FakeWebSocket[] = [];
|
|
21
|
+
static readonly OPEN = 1;
|
|
22
|
+
|
|
23
|
+
readyState = 0;
|
|
24
|
+
readonly url: string;
|
|
25
|
+
private readonly listeners = new Map<string, Set<(event: unknown) => void>>();
|
|
26
|
+
|
|
27
|
+
constructor(url: string) {
|
|
28
|
+
this.url = url;
|
|
29
|
+
FakeWebSocket.instances.push(this);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addEventListener(type: string, listener: (event: unknown) => void) {
|
|
33
|
+
const listeners = this.listeners.get(type) ?? new Set();
|
|
34
|
+
listeners.add(listener);
|
|
35
|
+
this.listeners.set(type, listeners);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
send() {
|
|
39
|
+
// no-op
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
close() {
|
|
43
|
+
this.readyState = 3;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
open() {
|
|
47
|
+
this.readyState = FakeWebSocket.OPEN;
|
|
48
|
+
this.emit("open", {});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
receive(message: unknown) {
|
|
52
|
+
this.emit("message", { data: JSON.stringify(message) });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private emit(type: string, event: unknown) {
|
|
56
|
+
for (const listener of this.listeners.get(type) ?? []) {
|
|
57
|
+
listener(event);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe("createTransport", () => {
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
FakeWebSocket.instances.length = 0;
|
|
65
|
+
vi.stubGlobal("WebSocket", FakeWebSocket);
|
|
66
|
+
vi.spyOn(console, "log").mockImplementation(() => undefined);
|
|
67
|
+
store.set(notebookAtom, initialNotebookState());
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
vi.unstubAllGlobals();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("forwards window/logMessage notifications to the logs panel", async () => {
|
|
75
|
+
const transport = createTransport("pylsp");
|
|
76
|
+
const connected = transport.connect();
|
|
77
|
+
await vi.waitFor(() => expect(FakeWebSocket.instances).toHaveLength(1));
|
|
78
|
+
const socket = FakeWebSocket.instances[0];
|
|
79
|
+
socket.open();
|
|
80
|
+
await connected;
|
|
81
|
+
|
|
82
|
+
socket.receive({
|
|
83
|
+
jsonrpc: "2.0",
|
|
84
|
+
method: "window/logMessage",
|
|
85
|
+
params: { type: 1, message: "pylsp failed to start" },
|
|
86
|
+
});
|
|
87
|
+
socket.receive({
|
|
88
|
+
jsonrpc: "2.0",
|
|
89
|
+
method: "window/logMessage",
|
|
90
|
+
params: { type: 3, message: "indexing workspace" },
|
|
91
|
+
});
|
|
92
|
+
// Unrelated notifications are left alone.
|
|
93
|
+
socket.receive({
|
|
94
|
+
jsonrpc: "2.0",
|
|
95
|
+
method: "textDocument/publishDiagnostics",
|
|
96
|
+
params: { uri: "file:///a", diagnostics: [] },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(store.get(notebookAtom).cellLogs).toMatchObject([
|
|
100
|
+
{
|
|
101
|
+
level: "stderr",
|
|
102
|
+
message: "pylsp failed to start",
|
|
103
|
+
source: { type: "lsp", name: "pylsp" },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
level: "stdout",
|
|
107
|
+
message: "indexing workspace",
|
|
108
|
+
source: { type: "lsp", name: "pylsp" },
|
|
109
|
+
},
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type * as LSP from "vscode-languageserver-protocol";
|
|
4
4
|
import { Objects } from "@/utils/objects";
|
|
5
|
-
import type { ILanguageServerClient } from "./types";
|
|
5
|
+
import type { ILanguageServerClient, RoutableLspMethod } from "./types";
|
|
6
6
|
import { getLspDocumentUri } from "./utils";
|
|
7
7
|
|
|
8
8
|
function removeFalseyValues<T extends object>(obj: T): T {
|
|
@@ -54,7 +54,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
54
54
|
}
|
|
55
55
|
return undefined;
|
|
56
56
|
})
|
|
57
|
-
.filter((c) => c != null);
|
|
57
|
+
.filter((c): c is LSP.ClientCapabilities => c != null);
|
|
58
58
|
|
|
59
59
|
return mergeDictsIgnoreFalsey<LSP.ClientCapabilities>(capabilities);
|
|
60
60
|
}
|
|
@@ -72,7 +72,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
72
72
|
get capabilities(): LSP.ServerCapabilities | null {
|
|
73
73
|
const capabilities = this.clients
|
|
74
74
|
.map((client) => client.capabilities)
|
|
75
|
-
.filter((c) => c !== null);
|
|
75
|
+
.filter((c): c is LSP.ServerCapabilities => c !== null);
|
|
76
76
|
return mergeDictsIgnoreFalsey<LSP.ServerCapabilities>(capabilities);
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -92,16 +92,20 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
private
|
|
96
|
-
|
|
95
|
+
private firstWithMethod(
|
|
96
|
+
method: RoutableLspMethod,
|
|
97
97
|
): ILanguageServerClient | undefined {
|
|
98
|
-
return this.clients.find((client) => client.
|
|
98
|
+
return this.clients.find((client) => client.hasCapability(method));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
private
|
|
102
|
-
|
|
101
|
+
private clientsWithMethod(
|
|
102
|
+
method: RoutableLspMethod,
|
|
103
103
|
): ILanguageServerClient[] {
|
|
104
|
-
return this.clients.filter((client) => client.
|
|
104
|
+
return this.clients.filter((client) => client.hasCapability(method));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
hasCapability(method: string): boolean {
|
|
108
|
+
return this.clients.some((client) => client.hasCapability(method));
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
async initialize(): Promise<void> {
|
|
@@ -114,18 +118,16 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
114
118
|
|
|
115
119
|
async textDocumentDidChange(
|
|
116
120
|
params: LSP.DidChangeTextDocumentParams,
|
|
117
|
-
): Promise<
|
|
121
|
+
): Promise<void> {
|
|
118
122
|
await Promise.all(
|
|
119
123
|
this.clients.map((client) => client.textDocumentDidChange(params)),
|
|
120
124
|
);
|
|
121
|
-
|
|
122
|
-
return params;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
async completionItemResolve(
|
|
126
128
|
item: LSP.CompletionItem,
|
|
127
129
|
): Promise<LSP.CompletionItem> {
|
|
128
|
-
const client = this.
|
|
130
|
+
const client = this.firstWithMethod("textDocument/completion");
|
|
129
131
|
if (client) {
|
|
130
132
|
return client.completionItemResolve(item);
|
|
131
133
|
}
|
|
@@ -135,17 +137,22 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
135
137
|
async textDocumentCodeAction(
|
|
136
138
|
params: LSP.CodeActionParams,
|
|
137
139
|
): Promise<(LSP.Command | LSP.CodeAction)[] | null> {
|
|
138
|
-
const client = this.
|
|
140
|
+
const client = this.firstWithMethod("textDocument/codeAction");
|
|
139
141
|
if (client) {
|
|
140
142
|
return client.textDocumentCodeAction(params);
|
|
141
143
|
}
|
|
142
144
|
return null;
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
async codeActionResolve(action: LSP.CodeAction): Promise<LSP.CodeAction> {
|
|
148
|
+
const client = this.firstWithMethod("textDocument/codeAction");
|
|
149
|
+
return client ? client.codeActionResolve(action) : action;
|
|
150
|
+
}
|
|
151
|
+
|
|
145
152
|
async textDocumentRename(
|
|
146
153
|
params: LSP.RenameParams,
|
|
147
154
|
): Promise<LSP.WorkspaceEdit | null> {
|
|
148
|
-
const client = this.
|
|
155
|
+
const client = this.firstWithMethod("textDocument/rename");
|
|
149
156
|
if (client) {
|
|
150
157
|
return client.textDocumentRename(params);
|
|
151
158
|
}
|
|
@@ -155,7 +162,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
155
162
|
async textDocumentPrepareRename(
|
|
156
163
|
params: LSP.PrepareRenameParams,
|
|
157
164
|
): Promise<LSP.PrepareRenameResult | null> {
|
|
158
|
-
const client = this.
|
|
165
|
+
const client = this.firstWithMethod("textDocument/prepareRename");
|
|
159
166
|
if (client) {
|
|
160
167
|
return client.textDocumentPrepareRename(params);
|
|
161
168
|
}
|
|
@@ -165,7 +172,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
165
172
|
async textDocumentSignatureHelp(
|
|
166
173
|
params: LSP.SignatureHelpParams,
|
|
167
174
|
): Promise<LSP.SignatureHelp | null> {
|
|
168
|
-
const client = this.
|
|
175
|
+
const client = this.firstWithMethod("textDocument/signatureHelp");
|
|
169
176
|
if (client) {
|
|
170
177
|
return client.textDocumentSignatureHelp(params);
|
|
171
178
|
}
|
|
@@ -176,7 +183,7 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
176
183
|
async textDocumentCompletion(
|
|
177
184
|
params: LSP.CompletionParams,
|
|
178
185
|
): Promise<LSP.CompletionList | LSP.CompletionItem[] | null> {
|
|
179
|
-
const clients = this.
|
|
186
|
+
const clients = this.clientsWithMethod("textDocument/completion");
|
|
180
187
|
const results = await Promise.allSettled(
|
|
181
188
|
clients.map((client) => client.textDocumentCompletion(params)),
|
|
182
189
|
);
|
|
@@ -187,24 +194,70 @@ export class FederatedLanguageServerClient implements ILanguageServerClient {
|
|
|
187
194
|
async textDocumentDefinition(
|
|
188
195
|
params: LSP.DefinitionParams,
|
|
189
196
|
): Promise<LSP.Definition | LSP.LocationLink[] | null> {
|
|
190
|
-
const client = this.
|
|
197
|
+
const client = this.firstWithMethod("textDocument/definition");
|
|
191
198
|
if (client) {
|
|
192
199
|
return client.textDocumentDefinition(params);
|
|
193
200
|
}
|
|
194
201
|
return null;
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
async textDocumentDidOpen(
|
|
198
|
-
|
|
204
|
+
async textDocumentDidOpen(
|
|
205
|
+
params: LSP.DidOpenTextDocumentParams,
|
|
206
|
+
): Promise<boolean> {
|
|
207
|
+
const results = await Promise.all(
|
|
199
208
|
this.clients.map((client) => client.textDocumentDidOpen(params)),
|
|
200
209
|
);
|
|
201
210
|
|
|
202
|
-
return
|
|
211
|
+
return results.some((result) => result !== false);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async textDocumentDidClose(
|
|
215
|
+
params: LSP.DidCloseTextDocumentParams,
|
|
216
|
+
): Promise<void> {
|
|
217
|
+
await Promise.all(
|
|
218
|
+
this.clients.map((client) => client.textDocumentDidClose(params)),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async textDocumentWillSave(
|
|
223
|
+
params: LSP.WillSaveTextDocumentParams,
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
await Promise.all(
|
|
226
|
+
this.clients.map((client) => client.textDocumentWillSave(params)),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async textDocumentWillSaveWaitUntil(
|
|
231
|
+
params: LSP.WillSaveTextDocumentParams,
|
|
232
|
+
): Promise<LSP.TextEdit[] | null> {
|
|
233
|
+
// This blocks the save, so query concurrently rather than paying the sum of
|
|
234
|
+
// the servers' latencies. The earliest client that returned edits still
|
|
235
|
+
// wins, so the result doesn't depend on who answers first.
|
|
236
|
+
const results = await Promise.allSettled(
|
|
237
|
+
this.clients.map((client) =>
|
|
238
|
+
client.textDocumentWillSaveWaitUntil(params),
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
for (const result of results) {
|
|
243
|
+
if (result.status === "fulfilled" && result.value?.length) {
|
|
244
|
+
return result.value;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async textDocumentDidSave(
|
|
251
|
+
params: LSP.DidSaveTextDocumentParams,
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
await Promise.all(
|
|
254
|
+
this.clients.map((client) => client.textDocumentDidSave(params)),
|
|
255
|
+
);
|
|
203
256
|
}
|
|
204
257
|
|
|
205
258
|
async textDocumentHover(params: LSP.HoverParams): Promise<LSP.Hover> {
|
|
206
259
|
for (const client of this.clients) {
|
|
207
|
-
if (!client.
|
|
260
|
+
if (!client.hasCapability("textDocument/hover")) {
|
|
208
261
|
continue;
|
|
209
262
|
}
|
|
210
263
|
const result = await client.textDocumentHover(params);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { JSONRPCMessage } from "@marimo-team/codemirror-languageserver";
|
|
4
|
+
import { addLogs } from "@/core/cells/cells";
|
|
5
|
+
import { isRecord } from "@/utils/records";
|
|
6
|
+
|
|
7
|
+
// LSP MessageType: 1 = Error, 2 = Warning, 3 = Info, 4 = Log, 5 = Debug
|
|
8
|
+
const ERROR_MESSAGE_TYPES = new Set<unknown>([1, 2]);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Forward a server's `window/logMessage` notifications to the Logs panel.
|
|
12
|
+
* Servers report failures they can't surface as diagnostics this way, such as
|
|
13
|
+
* a missing interpreter or a plugin that failed to load.
|
|
14
|
+
*/
|
|
15
|
+
export function handleLogMessage(
|
|
16
|
+
serverName: string,
|
|
17
|
+
message: JSONRPCMessage,
|
|
18
|
+
): void {
|
|
19
|
+
if (!("method" in message) || message.method !== "window/logMessage") {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const params: unknown = message.params;
|
|
23
|
+
if (!isRecord(params) || typeof params.message !== "string") {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
addLogs({
|
|
28
|
+
logs: [
|
|
29
|
+
{
|
|
30
|
+
timestamp: Date.now() / 1000,
|
|
31
|
+
level: ERROR_MESSAGE_TYPES.has(params.type) ? "stderr" : "stdout",
|
|
32
|
+
message: params.message,
|
|
33
|
+
source: { type: "lsp", name: serverName },
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -6,8 +6,10 @@ import type * as LSP from "vscode-languageserver-protocol";
|
|
|
6
6
|
const INLINE_MATH_ROLE_REGEX = /(?<!`):math:`([^\n`]+)`/g;
|
|
7
7
|
// Matches role variant with HTML code tag: :math:<code>...</code>
|
|
8
8
|
const INLINE_MATH_ROLE_CODE_REGEX = /:math:\s*<code>(.+?)<\/code>/gs;
|
|
9
|
-
// Matches display bracket delimiters: \[...\]
|
|
10
|
-
|
|
9
|
+
// Matches display bracket delimiters: \[...\]. Must start a line or follow
|
|
10
|
+
// whitespace, so escaped brackets in type annotations (`Optional\[Callable\]`)
|
|
11
|
+
// are not read as math.
|
|
12
|
+
const DISPLAY_BRACKET_REGEX = /(?<!\S)\\\[(.+?)\\]/gs;
|
|
11
13
|
// Matches inline paren delimiters: \(...\)
|
|
12
14
|
const INLINE_PAREN_REGEX = /\\\((.+?)\\\)/gs;
|
|
13
15
|
// Matches display dollar delimiters: $$...$$
|
|
@@ -36,6 +38,8 @@ export function normalizeMarkdownMath(markdown: string): string {
|
|
|
36
38
|
return markdown;
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// Segments are split on line boundaries, so the newline between two of them
|
|
42
|
+
// belongs to neither and has to be restored on join.
|
|
39
43
|
return splitByFencedCodeBlocks(markdown)
|
|
40
44
|
.map((segment) => {
|
|
41
45
|
if (segment.isFenced) {
|
|
@@ -55,7 +59,7 @@ export function normalizeMarkdownMath(markdown: string): string {
|
|
|
55
59
|
})
|
|
56
60
|
.join("");
|
|
57
61
|
})
|
|
58
|
-
.join("");
|
|
62
|
+
.join("\n");
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
@@ -132,7 +136,7 @@ function containsMathSyntax(markdown: string): boolean {
|
|
|
132
136
|
markdown.includes(".. math::") ||
|
|
133
137
|
markdown.includes(":math:`") ||
|
|
134
138
|
// Detects display bracket delimiters.
|
|
135
|
-
|
|
139
|
+
/(?<!\S)\\\[[\S\s]+?\\]/.test(markdown) ||
|
|
136
140
|
// Detects inline paren delimiters.
|
|
137
141
|
/\\\([\S\s]+?\\\)/.test(markdown) ||
|
|
138
142
|
// Detects display dollar delimiters.
|