@latentic/live-markdown 0.3.0 → 0.4.0
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/CHANGELOG.md +38 -0
- package/README.md +2 -2
- package/dist/chunk-V6TY7KAL.js +8 -0
- package/dist/chunk-V6TY7KAL.js.map +1 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.d.ts +106 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.js +482 -0
- package/dist/codemirror/CodeMirrorMarkdownEditor.js.map +1 -0
- package/dist/codemirror/byteOffset.d.ts +17 -0
- package/dist/codemirror/byteOffset.js +19 -0
- package/dist/codemirror/byteOffset.js.map +1 -0
- package/dist/codemirror/caretLayer.d.ts +19 -0
- package/dist/codemirror/caretLayer.js +44 -0
- package/dist/codemirror/caretLayer.js.map +1 -0
- package/dist/codemirror/clipboard/copyRich.d.ts +37 -0
- package/dist/codemirror/clipboard/copyRich.js +72 -0
- package/dist/codemirror/clipboard/copyRich.js.map +1 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.d.ts +20 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.js +91 -0
- package/dist/codemirror/clipboard/htmlToMarkdown.js.map +1 -0
- package/dist/codemirror/clipboard/pasteMarkdown.d.ts +18 -0
- package/dist/codemirror/clipboard/pasteMarkdown.js +41 -0
- package/dist/codemirror/clipboard/pasteMarkdown.js.map +1 -0
- package/dist/codemirror/code/codeHighlight.d.ts +13 -0
- package/dist/codemirror/code/codeHighlight.js +15 -0
- package/dist/codemirror/code/codeHighlight.js.map +1 -0
- package/dist/codemirror/code/codeLangAffordance.d.ts +17 -0
- package/dist/codemirror/code/codeLangAffordance.js +80 -0
- package/dist/codemirror/code/codeLangAffordance.js.map +1 -0
- package/dist/codemirror/code/codeLanguageMenu.d.ts +40 -0
- package/dist/codemirror/code/codeLanguageMenu.js +184 -0
- package/dist/codemirror/code/codeLanguageMenu.js.map +1 -0
- package/dist/codemirror/code/codePalette.d.ts +39 -0
- package/dist/codemirror/code/codePalette.js +53 -0
- package/dist/codemirror/code/codePalette.js.map +1 -0
- package/dist/codemirror/code/fenceAutoClose.d.ts +30 -0
- package/dist/codemirror/code/fenceAutoClose.js +171 -0
- package/dist/codemirror/code/fenceAutoClose.js.map +1 -0
- package/dist/codemirror/code/fenceCaretGuard.d.ts +28 -0
- package/dist/codemirror/code/fenceCaretGuard.js +66 -0
- package/dist/codemirror/code/fenceCaretGuard.js.map +1 -0
- package/dist/codemirror/code/fenceDeleteGuards.d.ts +22 -0
- package/dist/codemirror/code/fenceDeleteGuards.js +105 -0
- package/dist/codemirror/code/fenceDeleteGuards.js.map +1 -0
- package/dist/codemirror/code/fenceTabIndent.d.ts +8 -0
- package/dist/codemirror/code/fenceTabIndent.js +50 -0
- package/dist/codemirror/code/fenceTabIndent.js.map +1 -0
- package/dist/codemirror/code/highlightFence.d.ts +25 -0
- package/dist/codemirror/code/highlightFence.js +45 -0
- package/dist/codemirror/code/highlightFence.js.map +1 -0
- package/dist/codemirror/code/index.d.ts +9 -0
- package/dist/codemirror/code/index.js +8 -0
- package/dist/codemirror/code/index.js.map +1 -0
- package/dist/codemirror/core/codeContext.d.ts +32 -0
- package/dist/codemirror/core/codeContext.js +22 -0
- package/dist/codemirror/core/codeContext.js.map +1 -0
- package/dist/codemirror/core/editorTestHarness.d.ts +31 -0
- package/dist/codemirror/core/editorTestHarness.js +69 -0
- package/dist/codemirror/core/editorTestHarness.js.map +1 -0
- package/dist/codemirror/core/editorTheme.d.ts +39 -0
- package/dist/codemirror/core/editorTheme.js +498 -0
- package/dist/codemirror/core/editorTheme.js.map +1 -0
- package/dist/codemirror/core/hostFacets.d.ts +58 -0
- package/dist/codemirror/core/hostFacets.js +27 -0
- package/dist/codemirror/core/hostFacets.js.map +1 -0
- package/dist/codemirror/core/hrWidget.d.ts +22 -0
- package/dist/codemirror/core/hrWidget.js +22 -0
- package/dist/codemirror/core/hrWidget.js.map +1 -0
- package/dist/codemirror/core/htmlEscape.d.ts +6 -0
- package/dist/codemirror/core/htmlEscape.js +12 -0
- package/dist/codemirror/core/htmlEscape.js.map +1 -0
- package/dist/codemirror/core/index.d.ts +13 -0
- package/dist/codemirror/core/index.js +9 -0
- package/dist/codemirror/core/index.js.map +1 -0
- package/dist/codemirror/core/inlineScan.d.ts +49 -0
- package/dist/codemirror/core/inlineScan.js +31 -0
- package/dist/codemirror/core/inlineScan.js.map +1 -0
- package/dist/codemirror/core/lineStructure.d.ts +47 -0
- package/dist/codemirror/core/lineStructure.js +64 -0
- package/dist/codemirror/core/lineStructure.js.map +1 -0
- package/dist/codemirror/core/linkResolution.d.ts +36 -0
- package/dist/codemirror/core/linkResolution.js +58 -0
- package/dist/codemirror/core/linkResolution.js.map +1 -0
- package/dist/codemirror/core/paint.d.ts +118 -0
- package/dist/codemirror/core/paint.js +42 -0
- package/dist/codemirror/core/paint.js.map +1 -0
- package/dist/codemirror/core/parseToEnd.d.ts +24 -0
- package/dist/codemirror/core/parseToEnd.js +38 -0
- package/dist/codemirror/core/parseToEnd.js.map +1 -0
- package/dist/codemirror/core/plugin.d.ts +32 -0
- package/dist/codemirror/core/plugin.js +161 -0
- package/dist/codemirror/core/plugin.js.map +1 -0
- package/dist/codemirror/core/registry.d.ts +38 -0
- package/dist/codemirror/core/registry.js +133 -0
- package/dist/codemirror/core/registry.js.map +1 -0
- package/dist/codemirror/core/testKeys.d.ts +17 -0
- package/dist/codemirror/core/testKeys.js +11 -0
- package/dist/codemirror/core/testKeys.js.map +1 -0
- package/dist/codemirror/core/treeAt.d.ts +31 -0
- package/dist/codemirror/core/treeAt.js +10 -0
- package/dist/codemirror/core/treeAt.js.map +1 -0
- package/dist/codemirror/extensions/footnoteExtension.d.ts +9 -0
- package/dist/codemirror/extensions/footnoteExtension.js +14 -0
- package/dist/codemirror/extensions/footnoteExtension.js.map +1 -0
- package/dist/codemirror/extensions/highlightExtension.d.ts +9 -0
- package/dist/codemirror/extensions/highlightExtension.js +14 -0
- package/dist/codemirror/extensions/highlightExtension.js.map +1 -0
- package/dist/codemirror/extensions/index.d.ts +12 -0
- package/dist/codemirror/extensions/index.js +19 -0
- package/dist/codemirror/extensions/index.js.map +1 -0
- package/dist/codemirror/extensions/loader.d.ts +24 -0
- package/dist/codemirror/extensions/loader.js +33 -0
- package/dist/codemirror/extensions/loader.js.map +1 -0
- package/dist/codemirror/extensions/mathExtension.d.ts +9 -0
- package/dist/codemirror/extensions/mathExtension.js +14 -0
- package/dist/codemirror/extensions/mathExtension.js.map +1 -0
- package/dist/codemirror/extensions/mermaidExtension.d.ts +9 -0
- package/dist/codemirror/extensions/mermaidExtension.js +12 -0
- package/dist/codemirror/extensions/mermaidExtension.js.map +1 -0
- package/dist/codemirror/extensions/tableExtension.d.ts +13 -0
- package/dist/codemirror/extensions/tableExtension.js +39 -0
- package/dist/codemirror/extensions/tableExtension.js.map +1 -0
- package/dist/codemirror/extensions/types.d.ts +39 -0
- package/dist/codemirror/extensions/types.js +1 -0
- package/dist/codemirror/extensions/types.js.map +1 -0
- package/dist/codemirror/extensions/wikilinkExtension.d.ts +9 -0
- package/dist/codemirror/extensions/wikilinkExtension.js +14 -0
- package/dist/codemirror/extensions/wikilinkExtension.js.map +1 -0
- package/dist/codemirror/features/blockCommandSteps.d.ts +20 -0
- package/dist/codemirror/features/blockCommandSteps.js +83 -0
- package/dist/codemirror/features/blockCommandSteps.js.map +1 -0
- package/dist/codemirror/footnote/footnotePlugin.d.ts +10 -0
- package/dist/codemirror/footnote/footnotePlugin.js +78 -0
- package/dist/codemirror/footnote/footnotePlugin.js.map +1 -0
- package/dist/codemirror/footnote/footnoteScanRule.d.ts +9 -0
- package/dist/codemirror/footnote/footnoteScanRule.js +12 -0
- package/dist/codemirror/footnote/footnoteScanRule.js.map +1 -0
- package/dist/codemirror/footnote/index.d.ts +2 -0
- package/dist/codemirror/footnote/index.js +2 -0
- package/dist/codemirror/footnote/index.js.map +1 -0
- package/dist/codemirror/format/blockCommands.d.ts +17 -0
- package/dist/codemirror/format/blockCommands.js +176 -0
- package/dist/codemirror/format/blockCommands.js.map +1 -0
- package/dist/codemirror/format/formatCommands.d.ts +11 -0
- package/dist/codemirror/format/formatCommands.js +132 -0
- package/dist/codemirror/format/formatCommands.js.map +1 -0
- package/dist/codemirror/format/index.d.ts +4 -0
- package/dist/codemirror/format/index.js +3 -0
- package/dist/codemirror/format/index.js.map +1 -0
- package/dist/codemirror/highlight/highlightPlugin.d.ts +10 -0
- package/dist/codemirror/highlight/highlightPlugin.js +66 -0
- package/dist/codemirror/highlight/highlightPlugin.js.map +1 -0
- package/dist/codemirror/highlight/highlightScanRule.d.ts +8 -0
- package/dist/codemirror/highlight/highlightScanRule.js +12 -0
- package/dist/codemirror/highlight/highlightScanRule.js.map +1 -0
- package/dist/codemirror/highlight/index.d.ts +2 -0
- package/dist/codemirror/highlight/index.js +2 -0
- package/dist/codemirror/highlight/index.js.map +1 -0
- package/dist/codemirror/html/htmlWidget.d.ts +23 -0
- package/dist/codemirror/html/htmlWidget.js +67 -0
- package/dist/codemirror/html/htmlWidget.js.map +1 -0
- package/dist/codemirror/html/index.d.ts +4 -0
- package/dist/codemirror/html/index.js +2 -0
- package/dist/codemirror/html/index.js.map +1 -0
- package/dist/codemirror/image/imageActionMenu.d.ts +14 -0
- package/dist/codemirror/image/imageActionMenu.js +112 -0
- package/dist/codemirror/image/imageActionMenu.js.map +1 -0
- package/dist/codemirror/image/imageEditEvent.d.ts +20 -0
- package/dist/codemirror/image/imageEditEvent.js +6 -0
- package/dist/codemirror/image/imageEditEvent.js.map +1 -0
- package/dist/codemirror/image/imageInsertHandlers.d.ts +13 -0
- package/dist/codemirror/image/imageInsertHandlers.js +64 -0
- package/dist/codemirror/image/imageInsertHandlers.js.map +1 -0
- package/dist/codemirror/image/imageWidget.d.ts +49 -0
- package/dist/codemirror/image/imageWidget.js +83 -0
- package/dist/codemirror/image/imageWidget.js.map +1 -0
- package/dist/codemirror/image/index.d.ts +8 -0
- package/dist/codemirror/image/index.js +5 -0
- package/dist/codemirror/image/index.js.map +1 -0
- package/dist/codemirror/interaction/clickModel.d.ts +43 -0
- package/dist/codemirror/interaction/clickModel.js +99 -0
- package/dist/codemirror/interaction/clickModel.js.map +1 -0
- package/dist/codemirror/interaction/cursorModel.d.ts +10 -0
- package/dist/codemirror/interaction/cursorModel.js +58 -0
- package/dist/codemirror/interaction/cursorModel.js.map +1 -0
- package/dist/codemirror/interaction/deleteNormalizer.d.ts +18 -0
- package/dist/codemirror/interaction/deleteNormalizer.js +157 -0
- package/dist/codemirror/interaction/deleteNormalizer.js.map +1 -0
- package/dist/codemirror/interaction/flankingGuard.d.ts +20 -0
- package/dist/codemirror/interaction/flankingGuard.js +48 -0
- package/dist/codemirror/interaction/flankingGuard.js.map +1 -0
- package/dist/codemirror/interaction/index.d.ts +7 -0
- package/dist/codemirror/interaction/index.js +6 -0
- package/dist/codemirror/interaction/index.js.map +1 -0
- package/dist/codemirror/interaction/visiblePosition.d.ts +43 -0
- package/dist/codemirror/interaction/visiblePosition.js +98 -0
- package/dist/codemirror/interaction/visiblePosition.js.map +1 -0
- package/dist/codemirror/list/bulletWidget.d.ts +61 -0
- package/dist/codemirror/list/bulletWidget.js +84 -0
- package/dist/codemirror/list/bulletWidget.js.map +1 -0
- package/dist/codemirror/list/index.d.ts +4 -0
- package/dist/codemirror/list/index.js +3 -0
- package/dist/codemirror/list/index.js.map +1 -0
- package/dist/codemirror/list/listContinuation.d.ts +28 -0
- package/dist/codemirror/list/listContinuation.js +61 -0
- package/dist/codemirror/list/listContinuation.js.map +1 -0
- package/dist/codemirror/list/listIndent.d.ts +8 -0
- package/dist/codemirror/list/listIndent.js +95 -0
- package/dist/codemirror/list/listIndent.js.map +1 -0
- package/dist/codemirror/list/taskCheckboxWidget.d.ts +42 -0
- package/dist/codemirror/list/taskCheckboxWidget.js +47 -0
- package/dist/codemirror/list/taskCheckboxWidget.js.map +1 -0
- package/dist/codemirror/math/index.d.ts +3 -0
- package/dist/codemirror/math/index.js +2 -0
- package/dist/codemirror/math/index.js.map +1 -0
- package/dist/codemirror/math/mathPlugin.d.ts +10 -0
- package/dist/codemirror/math/mathPlugin.js +73 -0
- package/dist/codemirror/math/mathPlugin.js.map +1 -0
- package/dist/codemirror/math/mathScanRule.d.ts +16 -0
- package/dist/codemirror/math/mathScanRule.js +18 -0
- package/dist/codemirror/math/mathScanRule.js.map +1 -0
- package/dist/codemirror/math/mathWidget.d.ts +15 -0
- package/dist/codemirror/math/mathWidget.js +36 -0
- package/dist/codemirror/math/mathWidget.js.map +1 -0
- package/dist/codemirror/mermaid/index.d.ts +4 -0
- package/dist/codemirror/mermaid/index.js +3 -0
- package/dist/codemirror/mermaid/index.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidPlugin.d.ts +25 -0
- package/dist/codemirror/mermaid/mermaidPlugin.js +78 -0
- package/dist/codemirror/mermaid/mermaidPlugin.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidRender.d.ts +42 -0
- package/dist/codemirror/mermaid/mermaidRender.js +136 -0
- package/dist/codemirror/mermaid/mermaidRender.js.map +1 -0
- package/dist/codemirror/mermaid/mermaidWidget.d.ts +42 -0
- package/dist/codemirror/mermaid/mermaidWidget.js +142 -0
- package/dist/codemirror/mermaid/mermaidWidget.js.map +1 -0
- package/dist/codemirror/selectionLayer.d.ts +43 -0
- package/dist/codemirror/selectionLayer.js +135 -0
- package/dist/codemirror/selectionLayer.js.map +1 -0
- package/dist/codemirror/table/cellDividerWidget.d.ts +9 -0
- package/dist/codemirror/table/cellDividerWidget.js +19 -0
- package/dist/codemirror/table/cellDividerWidget.js.map +1 -0
- package/dist/codemirror/table/index.d.ts +5 -0
- package/dist/codemirror/table/index.js +4 -0
- package/dist/codemirror/table/index.js.map +1 -0
- package/dist/codemirror/table/tableArmed.d.ts +27 -0
- package/dist/codemirror/table/tableArmed.js +43 -0
- package/dist/codemirror/table/tableArmed.js.map +1 -0
- package/dist/codemirror/table/tableCell.d.ts +19 -0
- package/dist/codemirror/table/tableCell.js +15 -0
- package/dist/codemirror/table/tableCell.js.map +1 -0
- package/dist/codemirror/table/tableCellNav.d.ts +51 -0
- package/dist/codemirror/table/tableCellNav.js +47 -0
- package/dist/codemirror/table/tableCellNav.js.map +1 -0
- package/dist/codemirror/table/tableContextMenu.d.ts +22 -0
- package/dist/codemirror/table/tableContextMenu.js +139 -0
- package/dist/codemirror/table/tableContextMenu.js.map +1 -0
- package/dist/codemirror/table/tableEditCommands.d.ts +29 -0
- package/dist/codemirror/table/tableEditCommands.js +86 -0
- package/dist/codemirror/table/tableEditCommands.js.map +1 -0
- package/dist/codemirror/table/tableExcerpt.d.ts +29 -0
- package/dist/codemirror/table/tableExcerpt.js +34 -0
- package/dist/codemirror/table/tableExcerpt.js.map +1 -0
- package/dist/codemirror/table/tableField.d.ts +6 -0
- package/dist/codemirror/table/tableField.js +37 -0
- package/dist/codemirror/table/tableField.js.map +1 -0
- package/dist/codemirror/table/tableGeometry.d.ts +33 -0
- package/dist/codemirror/table/tableGeometry.js +28 -0
- package/dist/codemirror/table/tableGeometry.js.map +1 -0
- package/dist/codemirror/table/tableInline.d.ts +37 -0
- package/dist/codemirror/table/tableInline.js +105 -0
- package/dist/codemirror/table/tableInline.js.map +1 -0
- package/dist/codemirror/table/tableModel.d.ts +45 -0
- package/dist/codemirror/table/tableModel.js +98 -0
- package/dist/codemirror/table/tableModel.js.map +1 -0
- package/dist/codemirror/tablev2/bridgeRules.d.ts +48 -0
- package/dist/codemirror/tablev2/bridgeRules.js +42 -0
- package/dist/codemirror/tablev2/bridgeRules.js.map +1 -0
- package/dist/codemirror/tablev2/cellEditingSurface.d.ts +79 -0
- package/dist/codemirror/tablev2/cellEditingSurface.js +70 -0
- package/dist/codemirror/tablev2/cellEditingSurface.js.map +1 -0
- package/dist/codemirror/tablev2/cellText.d.ts +11 -0
- package/dist/codemirror/tablev2/cellText.js +12 -0
- package/dist/codemirror/tablev2/cellText.js.map +1 -0
- package/dist/codemirror/tablev2/inlineCellSurface.d.ts +38 -0
- package/dist/codemirror/tablev2/inlineCellSurface.js +162 -0
- package/dist/codemirror/tablev2/inlineCellSurface.js.map +1 -0
- package/dist/codemirror/tablev2/overlayCellSurface.d.ts +36 -0
- package/dist/codemirror/tablev2/overlayCellSurface.js +150 -0
- package/dist/codemirror/tablev2/overlayCellSurface.js.map +1 -0
- package/dist/codemirror/tablev2/tableEditingSteps.d.ts +12 -0
- package/dist/codemirror/tablev2/tableEditingSteps.js +518 -0
- package/dist/codemirror/tablev2/tableEditingSteps.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.d.ts +23 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.js +89 -0
- package/dist/codemirror/tablev2/tableV2HoverControls.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Interaction.d.ts +29 -0
- package/dist/codemirror/tablev2/tableV2Interaction.js +224 -0
- package/dist/codemirror/tablev2/tableV2Interaction.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Selection.d.ts +45 -0
- package/dist/codemirror/tablev2/tableV2Selection.js +81 -0
- package/dist/codemirror/tablev2/tableV2Selection.js.map +1 -0
- package/dist/codemirror/tablev2/tableV2Sync.d.ts +13 -0
- package/dist/codemirror/tablev2/tableV2Sync.js +13 -0
- package/dist/codemirror/tablev2/tableV2Sync.js.map +1 -0
- package/dist/codemirror/tablev2/tableWidgetV2.d.ts +46 -0
- package/dist/codemirror/tablev2/tableWidgetV2.js +139 -0
- package/dist/codemirror/tablev2/tableWidgetV2.js.map +1 -0
- package/dist/codemirror/updateBus.d.ts +29 -0
- package/dist/codemirror/updateBus.js +26 -0
- package/dist/codemirror/updateBus.js.map +1 -0
- package/dist/codemirror/wikilink/index.d.ts +3 -0
- package/dist/codemirror/wikilink/index.js +2 -0
- package/dist/codemirror/wikilink/index.js.map +1 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.d.ts +40 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.js +89 -0
- package/dist/codemirror/wikilink/wikilinkPlugin.js.map +1 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.d.ts +9 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.js +13 -0
- package/dist/codemirror/wikilink/wikilinkScanRule.js.map +1 -0
- package/dist/frontmatter.d.ts +79 -0
- package/dist/frontmatter.js +58 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/imageInsert.d.ts +66 -0
- package/dist/imageInsert.js +111 -0
- package/dist/imageInsert.js.map +1 -0
- package/dist/imageSrcResolver.d.ts +55 -0
- package/dist/imageSrcResolver.js +49 -0
- package/dist/imageSrcResolver.js.map +1 -0
- package/dist/index.d.ts +35 -880
- package/dist/index.js +128 -5989
- package/dist/index.js.map +1 -1
- package/dist/links/wikilink.d.ts +26 -0
- package/dist/links/wikilink.js +54 -0
- package/dist/links/wikilink.js.map +1 -0
- package/dist/links/workspaceLink.d.ts +34 -0
- package/dist/links/workspaceLink.js +60 -0
- package/dist/links/workspaceLink.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/fenceAutoClose.ts"],"sourcesContent":["/**\n * Fence lifecycle (#91, interaction-spec §9.5, §12.4–.6).\n *\n * An unclosed fence runs to the end of the document per CommonMark, so the\n * moment ``` is typed everything below renders as one giant code block —\n * grammar-correct, wrong WYSIWYG. The behaviors here keep blocks bounded,\n * enterable, and escapable:\n *\n * - TYPE-TIME close (§12.4): the keystroke completing a fence opener at a\n * line's CONTENT start — top level, inside a list item, inside a quote\n * (positions from `lineStructure`, not a line regex) — inserts an empty\n * content line plus the matching closer at the same content column. The\n * caret STAYS on the opener, so typing continues the language tag\n * (```mermaid — the universal fence idiom, rendered live as the visible\n * pill) and Enter steps onto the content line (§12.5). Quote prefixes are\n * carried onto the inserted lines; list prefixes become continuation\n * indent.\n * - ENTER close: Enter at the end of an unclosed opener line (e.g. pasted)\n * closes it with the caret inside.\n * - ENTER step-in (§12.5): Enter on a CLOSED block's opener whose first\n * content line is empty moves the caret onto that line instead of\n * inserting another.\n * - ENTER exit (§12.6): Enter on the block's empty last content line\n * removes that line and moves the caret below the closing fence,\n * creating the line when the block ends the document.\n *\n * Typing on a CLOSED fence's own rows re-sites into content (§12.7) — see\n * resiteFenceLineTyping — which also closes the old gap where lengthening\n * the opener in place re-opened the block.\n */\n\nimport { EditorSelection, EditorState, Prec, type Transaction } from \"@codemirror/state\";\nimport { keymap, type Command } from \"@codemirror/view\";\n\nimport { lineStructure } from \"../core/lineStructure\";\nimport { treeAt } from \"../core/treeAt\";\n\n/** The FencedCode syntax node, as a structural stand-in (`@lezer/common` stays\n * a transitive dep). Exported because `fenceAt` returns it. */\nexport type FenceNode = {\n readonly name: string;\n readonly from: number;\n readonly to: number;\n readonly parent: FenceNode | null;\n getChildren(type: string): readonly FenceNode[];\n};\n\n/** Continuation prefix for a line inserted inside the same containers as\n * `line` up to `contentStart`: quote marks carry over, everything else\n * becomes plain indent. */\nfunction containerPrefix(text: string): string {\n return [...text].map((c) => (c === \">\" ? \">\" : \" \")).join(\"\");\n}\n\n/** §12.7 — typing on a CLOSED fence's own rows can never edit the fence.\n * On the closing line, trailing text stops it closing (CommonMark allows no\n * info string there) and the block re-opens, swallowing everything below.\n * On the opener, typing extends the language tag — which users hit when they\n * click the block's first gray row meaning to type CODE. Both re-site: the\n * closer row onto a fresh content line before the closer; the opener row to\n * the start of the first content line. (An UNCLOSED opener still types in\n * place — that's the language flow for a pasted fence.) Editing an existing\n * language tag moves to RAW mode, spec-noted. Returns the replacement spec,\n * or null when the insertion is elsewhere. */\nfunction resiteFenceLineTyping(\n tr: Transaction,\n from: number,\n text: string,\n): { changes: { from: number; insert: string }; selection: ReturnType<typeof EditorSelection.cursor>; userEvent: string } | null {\n const state = tr.startState;\n let node = fenceAt(state, from);\n if (!node) {\n // Column 0 of the opener row sits ON the fence node's from-boundary,\n // where side -1 resolves to the sibling before the block. Probe from the\n // line's end instead; only accept a fence whose opener IS this line.\n const line = state.doc.lineAt(from);\n if (from !== line.from || line.to === line.from) return null;\n const probed = fenceAt(state, line.to);\n if (!probed || state.doc.lineAt(probed.from).from !== line.from) return null;\n node = probed;\n }\n const marks = node.getChildren(\"CodeMark\");\n if (marks.length < 2) return null;\n const openerLine = state.doc.lineAt(node.from);\n const closer = marks[marks.length - 1];\n const closerLine = state.doc.lineAt(closer.from);\n if (from >= openerLine.from && from <= openerLine.to) {\n // Fresh-block language flow (§12.4): while the block holds no code yet,\n // typing after the opening marks extends the info string IN PLACE —\n // ```mermaid⏎ — with the visible pill as live feedback. A fence character\n // is excluded (a 4th backtick must not grow the marks), and a block WITH\n // content keeps the clicked-the-gray-row-means-code re-site below.\n const bodyIsEmpty =\n closerLine.number - openerLine.number < 2 ||\n state.doc\n .sliceString(openerLine.to, closerLine.from)\n .split(\"\\n\")\n .every((lineText) => /^[>\\s]*$/.test(lineText));\n if (\n bodyIsEmpty &&\n from >= marks[0].to &&\n from <= openerLine.to &&\n text !== \"`\" &&\n text !== \"~\"\n ) {\n return null;\n }\n // No content line exists in a bare ```/``` pair post-§12.4, but a pasted\n // block may lack one — fall back to a fresh line after the opener.\n if (openerLine.to + 1 <= closerLine.from - 1) {\n const contentStart = openerLine.to + 1;\n return {\n changes: { from: contentStart, insert: text },\n selection: EditorSelection.cursor(contentStart + text.length),\n userEvent: \"input.type\",\n };\n }\n const prefix = containerPrefix(state.doc.sliceString(openerLine.from, node.from));\n const insert = `${state.lineBreak}${prefix}${text}`;\n return {\n changes: { from: openerLine.to, insert },\n selection: EditorSelection.cursor(openerLine.to + insert.length),\n userEvent: \"input.type\",\n };\n }\n if (from < closerLine.from || from > closerLine.to) return null;\n const prefix = containerPrefix(state.doc.sliceString(closerLine.from, closer.from));\n const insert = `${state.lineBreak}${prefix}${text}`;\n const at = closerLine.from - 1;\n return {\n changes: { from: at, insert },\n selection: EditorSelection.cursor(at + insert.length),\n userEvent: \"input.type\",\n };\n}\n\n/** The keystroke that completes a ```/~~~ opener at a line's content start\n * closes the fence below, before the unclosed state can swallow the rest of\n * the document; a keystroke on a closed fence's closing line re-sites onto a\n * fresh content line (§12.7). */\nexport const fenceTypeAutoClose = EditorState.transactionFilter.of((tr: Transaction) => {\n if (!tr.docChanged || !tr.isUserEvent(\"input.type\") || tr.isUserEvent(\"input.type.compose\")) {\n return tr;\n }\n let single: { from: number; ch: string } | null = null;\n let eligible = true;\n tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {\n const ch = inserted.toString();\n if (single || fromA !== toA || ch.includes(\"\\n\")) eligible = false;\n else single = { from: fromA, ch };\n });\n if (!eligible || !single) return tr;\n\n const resited = resiteFenceLineTyping(tr, (single as { from: number }).from, (single as { ch: string }).ch);\n if (resited) return [resited];\n\n const { ch: typed } = single as { from: number; ch: string };\n if (typed !== \"`\" && typed !== \"~\") return tr;\n const { from } = single as { from: number };\n\n // The line must have been prose before the keystroke — a backtick typed\n // inside an existing block is literal, and an opener line of a closed\n // fence (typing a 4th backtick) must not stack another closer.\n const oldLine = tr.startState.doc.lineAt(from);\n const info = lineStructure(tr.startState, oldLine);\n if (info.inCode) return tr;\n\n // The fence must occupy the line's whole CONTENT — everything after the\n // block markers the grammar sees (list/task marker, quote marks).\n const contentStart = info.list ? info.list.markTo : info.contentFrom;\n const newLine = tr.newDoc.lineAt(from + 1);\n const content = tr.newDoc.sliceString(contentStart, newLine.to);\n if (!/^(`{3,}|~{3,})$/.test(content) || from + 1 !== newLine.to) return tr;\n\n // Continuation prefix for the inserted lines: quote marks carry over,\n // everything else (list markers, indent) becomes plain indent so the new\n // lines stay inside the same container at the fence's column.\n const prefix = containerPrefix(tr.newDoc.sliceString(newLine.from, contentStart));\n const brk = tr.startState.lineBreak;\n return [\n tr,\n {\n changes: { from: newLine.to, insert: `${brk}${prefix}${brk}${prefix}${content}` },\n // The caret STAYS at the end of the opener: typing continues the\n // language tag (```mermaid), Enter steps into the body (§12.5).\n selection: EditorSelection.cursor(newLine.to),\n sequential: true,\n },\n ];\n});\n\n/** The FencedCode ancestor at `pos`, or null.\n *\n * Resolves from {@link treeAt}, not `syntaxTree`: a tree that stops short of\n * `pos` reports it as bare `Document`, and the delete guards then merge a code\n * block into the prose above it. */\nexport function fenceAt(state: EditorState, pos: number): FenceNode | null {\n let node = treeAt(state, pos).resolveInner(pos, -1) as unknown as FenceNode | null;\n while (node && node.name !== \"FencedCode\") node = node.parent;\n return node;\n}\n\nexport const fenceAutoClose: Command = (view) => {\n const { state } = view;\n const { main } = state.selection;\n if (!main.empty) return false;\n const line = state.doc.lineAt(main.head);\n if (main.head !== line.to) return false;\n\n const node = fenceAt(state, main.head);\n // Only on the fence's OPENING line.\n if (!node || node.from < line.from) return false;\n const marks = node.getChildren(\"CodeMark\");\n const open = marks[0];\n if (!open) return false;\n\n if (marks.length >= 2) {\n // §12.5 — closed block: step onto an existing empty first content line.\n // \"Empty\" includes container prefixes — inside a blockquote that line is\n // \"> \", the same shape bodyIsEmpty accepts above.\n if (line.to < state.doc.length) {\n const next = state.doc.lineAt(line.to + 1);\n const closerLine = state.doc.lineAt(marks[marks.length - 1].from);\n if (next.number < closerLine.number && /^[>\\s]*$/.test(next.text)) {\n view.dispatch({\n selection: EditorSelection.cursor(next.to),\n scrollIntoView: true,\n userEvent: \"select\",\n });\n return true;\n }\n }\n return false;\n }\n\n const fence = state.sliceDoc(open.from, open.to);\n const indent = state.sliceDoc(line.from, open.from);\n view.dispatch({\n changes: { from: line.to, insert: `${state.lineBreak}${state.lineBreak}${indent}${fence}` },\n selection: EditorSelection.cursor(line.to + state.lineBreak.length),\n scrollIntoView: true,\n userEvent: \"input\",\n });\n return true;\n};\n\n/** Enter on the block's empty last content line exits below the fence. */\nexport const fenceExitBlock: Command = (view) => {\n const { state } = view;\n const { main } = state.selection;\n if (!main.empty) return false;\n const line = state.doc.lineAt(main.head);\n if (line.from !== line.to) return false; // only a fully empty line exits\n\n const node = fenceAt(state, main.head);\n if (!node || node.from >= line.from) return false;\n const marks = node.getChildren(\"CodeMark\");\n if (marks.length < 2) return false; // unclosed: Enter just adds code lines\n const closing = marks[marks.length - 1];\n // The empty line must sit directly above the closing fence line.\n if (state.doc.lineAt(closing.from).number !== line.number + 1) return false;\n\n const docLen = state.doc.length;\n const changes = [{ from: line.from - 1, to: line.to, insert: \"\" }] as {\n from: number;\n to?: number;\n insert: string;\n }[];\n if (node.to === docLen) changes.push({ from: docLen, insert: state.lineBreak });\n const removed = line.to - line.from + 1;\n view.dispatch({\n changes,\n // Just past the (shifted) closing fence's newline — the line below.\n selection: EditorSelection.cursor(node.to - removed + 1),\n scrollIntoView: true,\n userEvent: \"input\",\n });\n return true;\n};\n\nexport const fenceAutoCloseKeymap = Prec.highest(\n keymap.of([\n { key: \"Enter\", run: fenceAutoClose },\n { key: \"Enter\", run: fenceExitBlock },\n ]),\n);\n"],"mappings":"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","names":["prefix","insert"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as _codemirror_state from '@codemirror/state';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* §12.9 — a closed fence's marker rows never hold the caret.
|
|
5
|
+
*
|
|
6
|
+
* The opener/closer lines are block chrome: a caret parked there types
|
|
7
|
+
* somewhere else (the §12.7 re-site), which reads as a jump. Instead the
|
|
8
|
+
* caret is re-placed the moment a selection lands on a marker row:
|
|
9
|
+
*
|
|
10
|
+
* - pointer clicks: nearest content edge (opener → first content line's
|
|
11
|
+
* start, closer → last content line's end)
|
|
12
|
+
* - forward motion (Down/Right): through the row into the block, or out
|
|
13
|
+
* below it; holds at the content edge when nothing follows the block
|
|
14
|
+
* - backward motion (Up/Left): out above the block, or back to content
|
|
15
|
+
*
|
|
16
|
+
* A fence currently rendered as a BLOCK WIDGET (a mermaid diagram) inverts
|
|
17
|
+
* the rule: moving the caret INTO its content would reveal the source — so a
|
|
18
|
+
* click landing beside the widget flipped the diagram to code. Those fences
|
|
19
|
+
* snap the caret to the boundary OUTSIDE the block instead; entering one is
|
|
20
|
+
* a deliberate act (double-click / Edit chip / click-to-edit).
|
|
21
|
+
*
|
|
22
|
+
* Unclosed fences keep the caret — typing a language on a pasted opener
|
|
23
|
+
* needs it — and blocks with no content line are left alone (§12.7 re-sites
|
|
24
|
+
* any typing there safely). Range selections and multi-cursor pass through.
|
|
25
|
+
*/
|
|
26
|
+
declare const fenceCaretGuard: _codemirror_state.Extension;
|
|
27
|
+
|
|
28
|
+
export { fenceCaretGuard };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { EditorSelection, EditorState } from "@codemirror/state";
|
|
3
|
+
import { fenceAt } from "./fenceAutoClose.js";
|
|
4
|
+
import { mermaidField } from "../mermaid/mermaidPlugin.js";
|
|
5
|
+
function widgetCovered(state, from, to) {
|
|
6
|
+
const field = state.field(mermaidField, false);
|
|
7
|
+
if (!field) return false;
|
|
8
|
+
let covered = false;
|
|
9
|
+
field.decorations.between(from, to, () => {
|
|
10
|
+
covered = true;
|
|
11
|
+
return false;
|
|
12
|
+
});
|
|
13
|
+
return covered;
|
|
14
|
+
}
|
|
15
|
+
const fenceCaretGuard = EditorState.transactionFilter.of((tr) => {
|
|
16
|
+
if (tr.docChanged || !tr.selection) return tr;
|
|
17
|
+
if (tr.selection.ranges.length > 1) return tr;
|
|
18
|
+
const sel = tr.selection.main;
|
|
19
|
+
if (!sel.empty) return tr;
|
|
20
|
+
const state = tr.startState;
|
|
21
|
+
const line = state.doc.lineAt(sel.head);
|
|
22
|
+
if (line.to === line.from) return tr;
|
|
23
|
+
const node = fenceAt(state, line.to);
|
|
24
|
+
if (!node) return tr;
|
|
25
|
+
const marks = node.getChildren("CodeMark");
|
|
26
|
+
if (marks.length < 2) return tr;
|
|
27
|
+
const openerLine = state.doc.lineAt(node.from);
|
|
28
|
+
const closerLine = state.doc.lineAt(marks[marks.length - 1].from);
|
|
29
|
+
if (closerLine.number - openerLine.number < 2) return tr;
|
|
30
|
+
const onOpener = line.number === openerLine.number;
|
|
31
|
+
if (!onOpener && line.number !== closerLine.number) return tr;
|
|
32
|
+
const covered = widgetCovered(state, node.from, node.to);
|
|
33
|
+
if (!covered) {
|
|
34
|
+
let bodyEmpty = true;
|
|
35
|
+
for (let n = openerLine.number + 1; n < closerLine.number; n += 1) {
|
|
36
|
+
if (!/^[>\s]*$/.test(state.doc.line(n).text)) {
|
|
37
|
+
bodyEmpty = false;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (bodyEmpty) return tr;
|
|
42
|
+
}
|
|
43
|
+
const oldHead = state.selection.main.head;
|
|
44
|
+
const pointer = tr.isUserEvent("select.pointer");
|
|
45
|
+
const backward = !pointer && sel.head < oldHead;
|
|
46
|
+
let target;
|
|
47
|
+
if (covered) {
|
|
48
|
+
target = onOpener ? openerLine.from : closerLine.to;
|
|
49
|
+
} else {
|
|
50
|
+
const contentStart = openerLine.to + 1;
|
|
51
|
+
const lastContentEnd = closerLine.from - 1;
|
|
52
|
+
if (onOpener) {
|
|
53
|
+
target = backward && openerLine.from > 0 ? openerLine.from - 1 : contentStart;
|
|
54
|
+
} else if (pointer || backward) {
|
|
55
|
+
target = lastContentEnd;
|
|
56
|
+
} else {
|
|
57
|
+
target = closerLine.to < state.doc.length ? closerLine.to + 1 : lastContentEnd;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (target === sel.head) return tr;
|
|
61
|
+
return [tr, { selection: EditorSelection.cursor(target), sequential: true }];
|
|
62
|
+
});
|
|
63
|
+
export {
|
|
64
|
+
fenceCaretGuard
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=fenceCaretGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/fenceCaretGuard.ts"],"sourcesContent":["/**\n * §12.9 — a closed fence's marker rows never hold the caret.\n *\n * The opener/closer lines are block chrome: a caret parked there types\n * somewhere else (the §12.7 re-site), which reads as a jump. Instead the\n * caret is re-placed the moment a selection lands on a marker row:\n *\n * - pointer clicks: nearest content edge (opener → first content line's\n * start, closer → last content line's end)\n * - forward motion (Down/Right): through the row into the block, or out\n * below it; holds at the content edge when nothing follows the block\n * - backward motion (Up/Left): out above the block, or back to content\n *\n * A fence currently rendered as a BLOCK WIDGET (a mermaid diagram) inverts\n * the rule: moving the caret INTO its content would reveal the source — so a\n * click landing beside the widget flipped the diagram to code. Those fences\n * snap the caret to the boundary OUTSIDE the block instead; entering one is\n * a deliberate act (double-click / Edit chip / click-to-edit).\n *\n * Unclosed fences keep the caret — typing a language on a pasted opener\n * needs it — and blocks with no content line are left alone (§12.7 re-sites\n * any typing there safely). Range selections and multi-cursor pass through.\n */\n\nimport { EditorSelection, EditorState, type Transaction } from \"@codemirror/state\";\n\nimport { fenceAt } from \"./fenceAutoClose\";\nimport { mermaidField } from \"../mermaid/mermaidPlugin\";\n\n/** Is this fence currently replaced by a rendered block widget? */\nfunction widgetCovered(state: EditorState, from: number, to: number): boolean {\n const field = state.field(mermaidField, false);\n if (!field) return false;\n let covered = false;\n field.decorations.between(from, to, () => {\n covered = true;\n return false;\n });\n return covered;\n}\n\nexport const fenceCaretGuard = EditorState.transactionFilter.of((tr: Transaction) => {\n if (tr.docChanged || !tr.selection) return tr;\n if (tr.selection.ranges.length > 1) return tr;\n const sel = tr.selection.main;\n if (!sel.empty) return tr;\n\n const state = tr.startState;\n const line = state.doc.lineAt(sel.head);\n if (line.to === line.from) return tr;\n const node = fenceAt(state, line.to);\n if (!node) return tr;\n const marks = node.getChildren(\"CodeMark\");\n if (marks.length < 2) return tr;\n\n const openerLine = state.doc.lineAt(node.from);\n const closerLine = state.doc.lineAt(marks[marks.length - 1].from);\n if (closerLine.number - openerLine.number < 2) return tr;\n const onOpener = line.number === openerLine.number;\n if (!onOpener && line.number !== closerLine.number) return tr;\n\n const covered = widgetCovered(state, node.from, node.to);\n // §12.9 exempts no-content blocks, and a body of only blank (or\n // quote-prefixed blank) lines is no content — the shape type-time\n // auto-close leaves while the language is still being typed on the\n // opener, where arrowing over a typo must not throw the caret out.\n // Widget-rendered fences keep the outside-snap: their opener is hidden\n // chrome regardless of body.\n if (!covered) {\n let bodyEmpty = true;\n for (let n = openerLine.number + 1; n < closerLine.number; n += 1) {\n if (!/^[>\\s]*$/.test(state.doc.line(n).text)) {\n bodyEmpty = false;\n break;\n }\n }\n if (bodyEmpty) return tr;\n }\n\n const oldHead = state.selection.main.head;\n const pointer = tr.isUserEvent(\"select.pointer\");\n const backward = !pointer && sel.head < oldHead;\n\n let target: number;\n if (covered) {\n target = onOpener ? openerLine.from : closerLine.to;\n } else {\n const contentStart = openerLine.to + 1;\n const lastContentEnd = closerLine.from - 1;\n if (onOpener) {\n target = backward && openerLine.from > 0 ? openerLine.from - 1 : contentStart;\n } else if (pointer || backward) {\n target = lastContentEnd;\n } else {\n target = closerLine.to < state.doc.length ? closerLine.to + 1 : lastContentEnd;\n }\n }\n if (target === sel.head) return tr;\n return [tr, { selection: EditorSelection.cursor(target), sequential: true }];\n});\n"],"mappings":";AAwBA,SAAS,iBAAiB,mBAAqC;AAE/D,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAG7B,SAAS,cAAc,OAAoB,MAAc,IAAqB;AAC5E,QAAM,QAAQ,MAAM,MAAM,cAAc,KAAK;AAC7C,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,UAAU;AACd,QAAM,YAAY,QAAQ,MAAM,IAAI,MAAM;AACxC,cAAU;AACV,WAAO;AAAA,EACT,CAAC;AACD,SAAO;AACT;AAEO,MAAM,kBAAkB,YAAY,kBAAkB,GAAG,CAAC,OAAoB;AACnF,MAAI,GAAG,cAAc,CAAC,GAAG,UAAW,QAAO;AAC3C,MAAI,GAAG,UAAU,OAAO,SAAS,EAAG,QAAO;AAC3C,QAAM,MAAM,GAAG,UAAU;AACzB,MAAI,CAAC,IAAI,MAAO,QAAO;AAEvB,QAAM,QAAQ,GAAG;AACjB,QAAM,OAAO,MAAM,IAAI,OAAO,IAAI,IAAI;AACtC,MAAI,KAAK,OAAO,KAAK,KAAM,QAAO;AAClC,QAAM,OAAO,QAAQ,OAAO,KAAK,EAAE;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,YAAY,UAAU;AACzC,MAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,QAAM,aAAa,MAAM,IAAI,OAAO,KAAK,IAAI;AAC7C,QAAM,aAAa,MAAM,IAAI,OAAO,MAAM,MAAM,SAAS,CAAC,EAAE,IAAI;AAChE,MAAI,WAAW,SAAS,WAAW,SAAS,EAAG,QAAO;AACtD,QAAM,WAAW,KAAK,WAAW,WAAW;AAC5C,MAAI,CAAC,YAAY,KAAK,WAAW,WAAW,OAAQ,QAAO;AAE3D,QAAM,UAAU,cAAc,OAAO,KAAK,MAAM,KAAK,EAAE;AAOvD,MAAI,CAAC,SAAS;AACZ,QAAI,YAAY;AAChB,aAAS,IAAI,WAAW,SAAS,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AACjE,UAAI,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,EAAE,IAAI,GAAG;AAC5C,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AACA,QAAI,UAAW,QAAO;AAAA,EACxB;AAEA,QAAM,UAAU,MAAM,UAAU,KAAK;AACrC,QAAM,UAAU,GAAG,YAAY,gBAAgB;AAC/C,QAAM,WAAW,CAAC,WAAW,IAAI,OAAO;AAExC,MAAI;AACJ,MAAI,SAAS;AACX,aAAS,WAAW,WAAW,OAAO,WAAW;AAAA,EACnD,OAAO;AACL,UAAM,eAAe,WAAW,KAAK;AACrC,UAAM,iBAAiB,WAAW,OAAO;AACzC,QAAI,UAAU;AACZ,eAAS,YAAY,WAAW,OAAO,IAAI,WAAW,OAAO,IAAI;AAAA,IACnE,WAAW,WAAW,UAAU;AAC9B,eAAS;AAAA,IACX,OAAO;AACL,eAAS,WAAW,KAAK,MAAM,IAAI,SAAS,WAAW,KAAK,IAAI;AAAA,IAClE;AAAA,EACF;AACA,MAAI,WAAW,IAAI,KAAM,QAAO;AAChC,SAAO,CAAC,IAAI,EAAE,WAAW,gBAAgB,OAAO,MAAM,GAAG,YAAY,KAAK,CAAC;AAC7E,CAAC;","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fence walls for deletion (interaction-spec §12.1–.3).
|
|
5
|
+
*
|
|
6
|
+
* Fence lines are structure, not text: a character-level join that merges a
|
|
7
|
+
* fence line with its neighbor corrupts the pair — content becomes the
|
|
8
|
+
* opener's invisible info string, or the closer gains trailing text and
|
|
9
|
+
* stops closing, re-pairing the opener with a later fence and swallowing
|
|
10
|
+
* unrelated content. The delete normalizer consults these guards whenever a
|
|
11
|
+
* Backspace/Delete would cross a line boundary; they answer with the §12
|
|
12
|
+
* behavior (wall, park, plain code-line join, or whole-block deletion) and
|
|
13
|
+
* report whether they handled the press.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Backspace whose deletion would cross upward out of `line`. True when the
|
|
17
|
+
* press was handled (edit, park, or deliberate wall no-op). */
|
|
18
|
+
declare function fenceBackspaceGuard(view: EditorView, pos: number): boolean;
|
|
19
|
+
/** Forward-delete whose deletion would cross downward out of `line`. */
|
|
20
|
+
declare function fenceDeleteGuard(view: EditorView, pos: number): boolean;
|
|
21
|
+
|
|
22
|
+
export { fenceBackspaceGuard, fenceDeleteGuard };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { EditorSelection } from "@codemirror/state";
|
|
3
|
+
import { fenceAt } from "./fenceAutoClose.js";
|
|
4
|
+
function blockInfo(state, node) {
|
|
5
|
+
const marks = node.getChildren("CodeMark");
|
|
6
|
+
const openerLine = state.doc.lineAt(node.from);
|
|
7
|
+
const closerMark = marks.length >= 2 ? marks[marks.length - 1] : null;
|
|
8
|
+
const closerLine = closerMark ? state.doc.lineAt(closerMark.from) : null;
|
|
9
|
+
const contentFrom = Math.min(openerLine.to + 1, node.to);
|
|
10
|
+
const contentTo = closerLine ? Math.max(closerLine.from - 1, contentFrom) : node.to;
|
|
11
|
+
const contentEmpty = !closerLine || contentFrom >= closerLine.from || state.doc.sliceString(contentFrom, contentTo).trim() === "";
|
|
12
|
+
return { openerLine, closerLine, contentFrom, contentTo, contentEmpty };
|
|
13
|
+
}
|
|
14
|
+
function park(view, pos) {
|
|
15
|
+
view.dispatch({
|
|
16
|
+
selection: EditorSelection.cursor(pos),
|
|
17
|
+
scrollIntoView: true,
|
|
18
|
+
userEvent: "select"
|
|
19
|
+
});
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function deleteWholeBlock(view, node, userEvent) {
|
|
23
|
+
const docLen = view.state.doc.length;
|
|
24
|
+
const to = node.to < docLen ? node.to + 1 : node.to;
|
|
25
|
+
const from = node.to >= docLen && node.from > 0 ? node.from - 1 : node.from;
|
|
26
|
+
view.dispatch({
|
|
27
|
+
changes: { from, to, insert: "" },
|
|
28
|
+
selection: EditorSelection.cursor(from),
|
|
29
|
+
scrollIntoView: true,
|
|
30
|
+
userEvent
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
function joinNewline(view, at, userEvent) {
|
|
35
|
+
view.dispatch({
|
|
36
|
+
changes: { from: at, to: at + 1, insert: "" },
|
|
37
|
+
selection: EditorSelection.cursor(at),
|
|
38
|
+
scrollIntoView: true,
|
|
39
|
+
userEvent
|
|
40
|
+
});
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
function fenceBackspaceGuard(view, pos) {
|
|
44
|
+
const { state } = view;
|
|
45
|
+
const line = state.doc.lineAt(pos);
|
|
46
|
+
if (line.from === 0) return false;
|
|
47
|
+
const prevLine = state.doc.lineAt(line.from - 1);
|
|
48
|
+
const inside = fenceAt(state, pos);
|
|
49
|
+
if (inside) {
|
|
50
|
+
const info = blockInfo(state, inside);
|
|
51
|
+
if (line.number === info.openerLine.number) {
|
|
52
|
+
return park(view, prevLine.to);
|
|
53
|
+
}
|
|
54
|
+
if (info.closerLine && line.number === info.closerLine.number) {
|
|
55
|
+
return park(view, prevLine.to);
|
|
56
|
+
}
|
|
57
|
+
if (prevLine.number === info.openerLine.number) {
|
|
58
|
+
if (info.contentEmpty) return deleteWholeBlock(view, inside, "delete.backward");
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return joinNewline(view, line.from - 1, "delete.backward");
|
|
62
|
+
}
|
|
63
|
+
const prevFence = fenceAt(state, prevLine.to);
|
|
64
|
+
if (prevFence) {
|
|
65
|
+
const info = blockInfo(state, prevFence);
|
|
66
|
+
if (info.closerLine && prevLine.number === info.closerLine.number) {
|
|
67
|
+
if (info.contentEmpty) return deleteWholeBlock(view, prevFence, "delete.backward");
|
|
68
|
+
return park(view, info.contentTo);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function fenceDeleteGuard(view, pos) {
|
|
74
|
+
const { state } = view;
|
|
75
|
+
const line = state.doc.lineAt(pos);
|
|
76
|
+
if (line.to >= state.doc.length) return false;
|
|
77
|
+
const nextLine = state.doc.lineAt(line.to + 1);
|
|
78
|
+
const inside = fenceAt(state, pos);
|
|
79
|
+
if (inside) {
|
|
80
|
+
const info = blockInfo(state, inside);
|
|
81
|
+
if (line.number === info.openerLine.number) {
|
|
82
|
+
return park(view, nextLine.from);
|
|
83
|
+
}
|
|
84
|
+
if (info.closerLine && line.number === info.closerLine.number) {
|
|
85
|
+
return park(view, nextLine.from);
|
|
86
|
+
}
|
|
87
|
+
if (info.closerLine && nextLine.number === info.closerLine.number) {
|
|
88
|
+
if (info.contentEmpty) return deleteWholeBlock(view, inside, "delete.forward");
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return joinNewline(view, line.to, "delete.forward");
|
|
92
|
+
}
|
|
93
|
+
const nextFence = fenceAt(state, Math.min(nextLine.to, state.doc.length));
|
|
94
|
+
if (nextFence && state.doc.lineAt(nextFence.from).number === nextLine.number) {
|
|
95
|
+
const info = blockInfo(state, nextFence);
|
|
96
|
+
if (info.contentEmpty) return deleteWholeBlock(view, nextFence, "delete.forward");
|
|
97
|
+
return park(view, info.contentFrom);
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
fenceBackspaceGuard,
|
|
103
|
+
fenceDeleteGuard
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=fenceDeleteGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/fenceDeleteGuards.ts"],"sourcesContent":["/**\n * Fence walls for deletion (interaction-spec §12.1–.3).\n *\n * Fence lines are structure, not text: a character-level join that merges a\n * fence line with its neighbor corrupts the pair — content becomes the\n * opener's invisible info string, or the closer gains trailing text and\n * stops closing, re-pairing the opener with a later fence and swallowing\n * unrelated content. The delete normalizer consults these guards whenever a\n * Backspace/Delete would cross a line boundary; they answer with the §12\n * behavior (wall, park, plain code-line join, or whole-block deletion) and\n * report whether they handled the press.\n */\n\nimport { EditorSelection, type EditorState } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\nimport { fenceAt } from \"./fenceAutoClose\";\n\ninterface BlockInfo {\n openerLine: { number: number; to: number };\n closerLine: { number: number; from: number } | null;\n /** First/last content positions (line-granular). */\n contentFrom: number;\n contentTo: number;\n contentEmpty: boolean;\n}\n\ntype FenceNode = NonNullable<ReturnType<typeof fenceAt>>;\n\nfunction blockInfo(state: EditorState, node: FenceNode): BlockInfo {\n const marks = node.getChildren(\"CodeMark\");\n const openerLine = state.doc.lineAt(node.from);\n const closerMark = marks.length >= 2 ? marks[marks.length - 1] : null;\n const closerLine = closerMark ? state.doc.lineAt(closerMark.from) : null;\n const contentFrom = Math.min(openerLine.to + 1, node.to);\n const contentTo = closerLine ? Math.max(closerLine.from - 1, contentFrom) : node.to;\n const contentEmpty =\n !closerLine ||\n contentFrom >= closerLine.from ||\n state.doc.sliceString(contentFrom, contentTo).trim() === \"\";\n return { openerLine, closerLine, contentFrom, contentTo, contentEmpty };\n}\n\nfunction park(view: EditorView, pos: number): true {\n view.dispatch({\n selection: EditorSelection.cursor(pos),\n scrollIntoView: true,\n userEvent: \"select\",\n });\n return true;\n}\n\nfunction deleteWholeBlock(view: EditorView, node: FenceNode, userEvent: string): true {\n const docLen = view.state.doc.length;\n // Take one bounding newline with the block so no empty line is left behind.\n const to = node.to < docLen ? node.to + 1 : node.to;\n const from = node.to >= docLen && node.from > 0 ? node.from - 1 : node.from;\n view.dispatch({\n changes: { from, to, insert: \"\" },\n selection: EditorSelection.cursor(from),\n scrollIntoView: true,\n userEvent,\n });\n return true;\n}\n\nfunction joinNewline(view: EditorView, at: number, userEvent: string): true {\n view.dispatch({\n changes: { from: at, to: at + 1, insert: \"\" },\n selection: EditorSelection.cursor(at),\n scrollIntoView: true,\n userEvent,\n });\n return true;\n}\n\n/** Backspace whose deletion would cross upward out of `line`. True when the\n * press was handled (edit, park, or deliberate wall no-op). */\nexport function fenceBackspaceGuard(view: EditorView, pos: number): boolean {\n const { state } = view;\n const line = state.doc.lineAt(pos);\n if (line.from === 0) return false;\n const prevLine = state.doc.lineAt(line.from - 1);\n\n const inside = fenceAt(state, pos);\n if (inside) {\n const info = blockInfo(state, inside);\n if (line.number === info.openerLine.number) {\n // Caret on the opener line: joining it onto the prose above dissolves\n // the fence. Move up instead.\n return park(view, prevLine.to);\n }\n if (info.closerLine && line.number === info.closerLine.number) {\n // Caret on the closer line: pulling it up corrupts the pair.\n return park(view, prevLine.to);\n }\n if (prevLine.number === info.openerLine.number) {\n // §12.1 — first content line: the wall is solid; an effectively empty\n // block collapses whole instead.\n if (info.contentEmpty) return deleteWholeBlock(view, inside, \"delete.backward\");\n return true;\n }\n // Interior code lines join plainly — never through blockPrefixLength,\n // which would eat code that merely looks like a list marker.\n return joinNewline(view, line.from - 1, \"delete.backward\");\n }\n\n const prevFence = fenceAt(state, prevLine.to);\n if (prevFence) {\n const info = blockInfo(state, prevFence);\n if (info.closerLine && prevLine.number === info.closerLine.number) {\n // §12.2 — approaching from below: park at the content end (or collapse\n // an empty block whole).\n if (info.contentEmpty) return deleteWholeBlock(view, prevFence, \"delete.backward\");\n return park(view, info.contentTo);\n }\n }\n return false;\n}\n\n/** Forward-delete whose deletion would cross downward out of `line`. */\nexport function fenceDeleteGuard(view: EditorView, pos: number): boolean {\n const { state } = view;\n const line = state.doc.lineAt(pos);\n if (line.to >= state.doc.length) return false;\n const nextLine = state.doc.lineAt(line.to + 1);\n\n const inside = fenceAt(state, pos);\n if (inside) {\n const info = blockInfo(state, inside);\n if (line.number === info.openerLine.number) {\n // Caret on the opener: pulling content up makes it the invisible info\n // string. Step into the block instead.\n return park(view, nextLine.from);\n }\n if (info.closerLine && line.number === info.closerLine.number) {\n return park(view, nextLine.from);\n }\n if (info.closerLine && nextLine.number === info.closerLine.number) {\n // §12.1 mirror — last content line.\n if (info.contentEmpty) return deleteWholeBlock(view, inside, \"delete.forward\");\n return true;\n }\n return joinNewline(view, line.to, \"delete.forward\");\n }\n\n const nextFence = fenceAt(state, Math.min(nextLine.to, state.doc.length));\n if (nextFence && state.doc.lineAt(nextFence.from).number === nextLine.number) {\n const info = blockInfo(state, nextFence);\n // §12.2 mirror — approaching from above: park at the content start.\n if (info.contentEmpty) return deleteWholeBlock(view, nextFence, \"delete.forward\");\n return park(view, info.contentFrom);\n }\n return false;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as _codemirror_state from '@codemirror/state';
|
|
2
|
+
import { Command } from '@codemirror/view';
|
|
3
|
+
|
|
4
|
+
declare const fenceTabIndent: Command;
|
|
5
|
+
declare const fenceTabDedent: Command;
|
|
6
|
+
declare const fenceTabKeymap: _codemirror_state.Extension;
|
|
7
|
+
|
|
8
|
+
export { fenceTabDedent, fenceTabIndent, fenceTabKeymap };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { EditorSelection, Prec } from "@codemirror/state";
|
|
3
|
+
import { keymap } from "@codemirror/view";
|
|
4
|
+
import { fenceAt } from "./fenceAutoClose.js";
|
|
5
|
+
const INDENT = " ";
|
|
6
|
+
function inFenceContent(view) {
|
|
7
|
+
const { state } = view;
|
|
8
|
+
const head = state.selection.main.head;
|
|
9
|
+
const node = fenceAt(state, head);
|
|
10
|
+
if (!node) return false;
|
|
11
|
+
const line = state.doc.lineAt(head);
|
|
12
|
+
const marks = node.getChildren("CodeMark");
|
|
13
|
+
if (line.from <= node.from) return false;
|
|
14
|
+
const closer = marks.length >= 2 ? marks[marks.length - 1] : null;
|
|
15
|
+
return !closer || line.to < state.doc.lineAt(closer.from).from;
|
|
16
|
+
}
|
|
17
|
+
const fenceTabIndent = (view) => {
|
|
18
|
+
if (!inFenceContent(view)) return false;
|
|
19
|
+
const head = view.state.selection.main.head;
|
|
20
|
+
view.dispatch({
|
|
21
|
+
changes: { from: head, insert: INDENT },
|
|
22
|
+
selection: EditorSelection.cursor(head + INDENT.length),
|
|
23
|
+
userEvent: "input.type"
|
|
24
|
+
});
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
const fenceTabDedent = (view) => {
|
|
28
|
+
if (!inFenceContent(view)) return false;
|
|
29
|
+
const { state } = view;
|
|
30
|
+
const line = state.doc.lineAt(state.selection.main.head);
|
|
31
|
+
const leading = line.text.match(/^ {1,2}/)?.[0].length ?? 0;
|
|
32
|
+
if (leading === 0) return true;
|
|
33
|
+
view.dispatch({
|
|
34
|
+
changes: { from: line.from, to: line.from + leading, insert: "" },
|
|
35
|
+
userEvent: "delete.dedent"
|
|
36
|
+
});
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
const fenceTabKeymap = Prec.high(
|
|
40
|
+
keymap.of([
|
|
41
|
+
{ key: "Tab", run: fenceTabIndent },
|
|
42
|
+
{ key: "Shift-Tab", run: fenceTabDedent }
|
|
43
|
+
])
|
|
44
|
+
);
|
|
45
|
+
export {
|
|
46
|
+
fenceTabDedent,
|
|
47
|
+
fenceTabIndent,
|
|
48
|
+
fenceTabKeymap
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=fenceTabIndent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/fenceTabIndent.ts"],"sourcesContent":["/**\n * Tab inside a fenced code block indents; Shift-Tab dedents (§12.8).\n *\n * Without a binding, the browser's default Tab handling moves FOCUS — the\n * caret \"jumped out of the editor to the rich/raw button\" mid-code. Both\n * commands decline outside code so list indentation (`listIndent.ts`) and\n * the accessibility default keep their behavior everywhere else.\n */\n\nimport { EditorSelection, Prec } from \"@codemirror/state\";\nimport { type Command, keymap } from \"@codemirror/view\";\n\nimport { fenceAt } from \"./fenceAutoClose\";\n\nconst INDENT = \" \";\n\n/** Caret strictly inside a fence (not on the opener/closer lines). */\nfunction inFenceContent(view: Parameters<Command>[0]): boolean {\n const { state } = view;\n const head = state.selection.main.head;\n const node = fenceAt(state, head);\n if (!node) return false;\n const line = state.doc.lineAt(head);\n const marks = node.getChildren(\"CodeMark\");\n if (line.from <= node.from) return false;\n const closer = marks.length >= 2 ? marks[marks.length - 1] : null;\n return !closer || line.to < state.doc.lineAt(closer.from).from;\n}\n\nexport const fenceTabIndent: Command = (view) => {\n if (!inFenceContent(view)) return false;\n const head = view.state.selection.main.head;\n view.dispatch({\n changes: { from: head, insert: INDENT },\n selection: EditorSelection.cursor(head + INDENT.length),\n userEvent: \"input.type\",\n });\n return true;\n};\n\nexport const fenceTabDedent: Command = (view) => {\n if (!inFenceContent(view)) return false;\n const { state } = view;\n const line = state.doc.lineAt(state.selection.main.head);\n const leading = line.text.match(/^ {1,2}/)?.[0].length ?? 0;\n if (leading === 0) return true;\n view.dispatch({\n changes: { from: line.from, to: line.from + leading, insert: \"\" },\n userEvent: \"delete.dedent\",\n });\n return true;\n};\n\nexport const fenceTabKeymap = Prec.high(\n keymap.of([\n { key: \"Tab\", run: fenceTabIndent },\n { key: \"Shift-Tab\", run: fenceTabDedent },\n ]),\n);\n"],"mappings":";AASA,SAAS,iBAAiB,YAAY;AACtC,SAAuB,cAAc;AAErC,SAAS,eAAe;AAExB,MAAM,SAAS;AAGf,SAAS,eAAe,MAAuC;AAC7D,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,OAAO,MAAM,UAAU,KAAK;AAClC,QAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,MAAM,IAAI,OAAO,IAAI;AAClC,QAAM,QAAQ,KAAK,YAAY,UAAU;AACzC,MAAI,KAAK,QAAQ,KAAK,KAAM,QAAO;AACnC,QAAM,SAAS,MAAM,UAAU,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI;AAC7D,SAAO,CAAC,UAAU,KAAK,KAAK,MAAM,IAAI,OAAO,OAAO,IAAI,EAAE;AAC5D;AAEO,MAAM,iBAA0B,CAAC,SAAS;AAC/C,MAAI,CAAC,eAAe,IAAI,EAAG,QAAO;AAClC,QAAM,OAAO,KAAK,MAAM,UAAU,KAAK;AACvC,OAAK,SAAS;AAAA,IACZ,SAAS,EAAE,MAAM,MAAM,QAAQ,OAAO;AAAA,IACtC,WAAW,gBAAgB,OAAO,OAAO,OAAO,MAAM;AAAA,IACtD,WAAW;AAAA,EACb,CAAC;AACD,SAAO;AACT;AAEO,MAAM,iBAA0B,CAAC,SAAS;AAC/C,MAAI,CAAC,eAAe,IAAI,EAAG,QAAO;AAClC,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,OAAO,MAAM,IAAI,OAAO,MAAM,UAAU,KAAK,IAAI;AACvD,QAAM,UAAU,KAAK,KAAK,MAAM,SAAS,IAAI,CAAC,EAAE,UAAU;AAC1D,MAAI,YAAY,EAAG,QAAO;AAC1B,OAAK,SAAS;AAAA,IACZ,SAAS,EAAE,MAAM,KAAK,MAAM,IAAI,KAAK,OAAO,SAAS,QAAQ,GAAG;AAAA,IAChE,WAAW;AAAA,EACb,CAAC;AACD,SAAO;AACT;AAEO,MAAM,iBAAiB,KAAK;AAAA,EACjC,OAAO,GAAG;AAAA,IACR,EAAE,KAAK,OAAO,KAAK,eAAe;AAAA,IAClC,EAAE,KAAK,aAAa,KAAK,eAAe;AAAA,EAC1C,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous fence highlighting for the clipboard (#149).
|
|
3
|
+
*
|
|
4
|
+
* A copy event writes `text/html` synchronously, so highlighting must not
|
|
5
|
+
* await anything. The trick: the grammars are the SAME `@codemirror/language-data`
|
|
6
|
+
* singletons the editor's nested fence parse loads — any fence visible in the
|
|
7
|
+
* editor has its grammar warm, so highlighting the copied code is a sync parse.
|
|
8
|
+
* A cold grammar (copy from a doc whose fence never rendered) kicks the load
|
|
9
|
+
* and returns null: THIS copy ships plain, the next one is highlighted.
|
|
10
|
+
*
|
|
11
|
+
* Output is inline-styled spans (the palette from
|
|
12
|
+
* [codePalette](./codePalette.ts), same colors as the editor) — pasted HTML
|
|
13
|
+
* carries no stylesheet, so classes would be dead weight in Docs/Word.
|
|
14
|
+
*/
|
|
15
|
+
interface HighlightedSpan {
|
|
16
|
+
text: string;
|
|
17
|
+
/** Inline CSS for the span; absent for unstyled text (incl. line breaks). */
|
|
18
|
+
style?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Highlight `code` as `lang` into inline-styled spans, or null when no
|
|
21
|
+
* grammar matches or the grammar isn't loaded yet (the load is kicked so a
|
|
22
|
+
* later copy succeeds). Never throws — a parser hiccup falls back to null. */
|
|
23
|
+
declare function highlightFenceSpans(lang: string, code: string): HighlightedSpan[] | null;
|
|
24
|
+
|
|
25
|
+
export { type HighlightedSpan, highlightFenceSpans };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { LanguageDescription } from "@codemirror/language";
|
|
3
|
+
import { languages } from "@codemirror/language-data";
|
|
4
|
+
import { highlightCode, tagHighlighter } from "@lezer/highlight";
|
|
5
|
+
import { CODE_PALETTE } from "./codePalette.js";
|
|
6
|
+
const styleByClass = new Map(
|
|
7
|
+
CODE_PALETTE.map((spec, index) => [
|
|
8
|
+
`tok${index}`,
|
|
9
|
+
`color:${spec.color}${spec.fontStyle ? `;font-style:${spec.fontStyle}` : ""}`
|
|
10
|
+
])
|
|
11
|
+
);
|
|
12
|
+
const paletteHighlighter = tagHighlighter(
|
|
13
|
+
CODE_PALETTE.map((spec, index) => ({ tag: spec.tag, class: `tok${index}` }))
|
|
14
|
+
);
|
|
15
|
+
function styleFor(classes) {
|
|
16
|
+
const styles = classes.split(" ").map((cls) => styleByClass.get(cls)).filter(Boolean);
|
|
17
|
+
return styles.length > 0 ? styles.join(";") : void 0;
|
|
18
|
+
}
|
|
19
|
+
function highlightFenceSpans(lang, code) {
|
|
20
|
+
const description = LanguageDescription.matchLanguageName(languages, lang, true);
|
|
21
|
+
if (!description) return null;
|
|
22
|
+
if (!description.support) {
|
|
23
|
+
void description.load().catch(() => {
|
|
24
|
+
});
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const parser = description.support.language.parser;
|
|
29
|
+
const spans = [];
|
|
30
|
+
highlightCode(
|
|
31
|
+
code,
|
|
32
|
+
parser.parse(code),
|
|
33
|
+
paletteHighlighter,
|
|
34
|
+
(text, classes) => spans.push(classes ? { text, style: styleFor(classes) } : { text }),
|
|
35
|
+
() => spans.push({ text: "\n" })
|
|
36
|
+
);
|
|
37
|
+
return spans;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
highlightFenceSpans
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=highlightFence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/highlightFence.ts"],"sourcesContent":["/**\n * Synchronous fence highlighting for the clipboard (#149).\n *\n * A copy event writes `text/html` synchronously, so highlighting must not\n * await anything. The trick: the grammars are the SAME `@codemirror/language-data`\n * singletons the editor's nested fence parse loads — any fence visible in the\n * editor has its grammar warm, so highlighting the copied code is a sync parse.\n * A cold grammar (copy from a doc whose fence never rendered) kicks the load\n * and returns null: THIS copy ships plain, the next one is highlighted.\n *\n * Output is inline-styled spans (the palette from\n * [codePalette](./codePalette.ts), same colors as the editor) — pasted HTML\n * carries no stylesheet, so classes would be dead weight in Docs/Word.\n */\n\nimport { LanguageDescription } from \"@codemirror/language\";\nimport { languages } from \"@codemirror/language-data\";\nimport { highlightCode, tagHighlighter } from \"@lezer/highlight\";\n\nimport { CODE_PALETTE } from \"./codePalette\";\n\nexport interface HighlightedSpan {\n text: string;\n /** Inline CSS for the span; absent for unstyled text (incl. line breaks). */\n style?: string;\n}\n\nconst styleByClass = new Map(\n CODE_PALETTE.map((spec, index) => [\n `tok${index}`,\n `color:${spec.color}${spec.fontStyle ? `;font-style:${spec.fontStyle}` : \"\"}`,\n ]),\n);\n\n// tagHighlighter implements lezer's tag-containment matching (a token tagged\n// `function(variableName)` matches a spec on either tag) — hand-rolling that\n// gets the precedence subtly wrong.\nconst paletteHighlighter = tagHighlighter(\n CODE_PALETTE.map((spec, index) => ({ tag: spec.tag as never, class: `tok${index}` })),\n);\n\nfunction styleFor(classes: string): string | undefined {\n const styles = classes\n .split(\" \")\n .map((cls) => styleByClass.get(cls))\n .filter(Boolean);\n return styles.length > 0 ? styles.join(\";\") : undefined;\n}\n\n/** Highlight `code` as `lang` into inline-styled spans, or null when no\n * grammar matches or the grammar isn't loaded yet (the load is kicked so a\n * later copy succeeds). Never throws — a parser hiccup falls back to null. */\nexport function highlightFenceSpans(lang: string, code: string): HighlightedSpan[] | null {\n const description = LanguageDescription.matchLanguageName(languages, lang, true);\n if (!description) return null;\n if (!description.support) {\n void description.load().catch(() => {});\n return null;\n }\n try {\n const parser = description.support.language.parser;\n const spans: HighlightedSpan[] = [];\n highlightCode(\n code,\n parser.parse(code),\n paletteHighlighter,\n (text, classes) => spans.push(classes ? { text, style: styleFor(classes) } : { text }),\n () => spans.push({ text: \"\\n\" }),\n );\n return spans;\n } catch {\n return null;\n }\n}\n"],"mappings":";AAeA,SAAS,2BAA2B;AACpC,SAAS,iBAAiB;AAC1B,SAAS,eAAe,sBAAsB;AAE9C,SAAS,oBAAoB;AAQ7B,MAAM,eAAe,IAAI;AAAA,EACvB,aAAa,IAAI,CAAC,MAAM,UAAU;AAAA,IAChC,MAAM,KAAK;AAAA,IACX,SAAS,KAAK,KAAK,GAAG,KAAK,YAAY,eAAe,KAAK,SAAS,KAAK,EAAE;AAAA,EAC7E,CAAC;AACH;AAKA,MAAM,qBAAqB;AAAA,EACzB,aAAa,IAAI,CAAC,MAAM,WAAW,EAAE,KAAK,KAAK,KAAc,OAAO,MAAM,KAAK,GAAG,EAAE;AACtF;AAEA,SAAS,SAAS,SAAqC;AACrD,QAAM,SAAS,QACZ,MAAM,GAAG,EACT,IAAI,CAAC,QAAQ,aAAa,IAAI,GAAG,CAAC,EAClC,OAAO,OAAO;AACjB,SAAO,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAI;AAChD;AAKO,SAAS,oBAAoB,MAAc,MAAwC;AACxF,QAAM,cAAc,oBAAoB,kBAAkB,WAAW,MAAM,IAAI;AAC/E,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,CAAC,YAAY,SAAS;AACxB,SAAK,YAAY,KAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACtC,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,YAAY,QAAQ,SAAS;AAC5C,UAAM,QAA2B,CAAC;AAClC;AAAA,MACE;AAAA,MACA,OAAO,MAAM,IAAI;AAAA,MACjB;AAAA,MACA,CAAC,MAAM,YAAY,MAAM,KAAK,UAAU,EAAE,MAAM,OAAO,SAAS,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC;AAAA,MACrF,MAAM,MAAM,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,IACjC;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { codeHighlight } from './codeHighlight.js';
|
|
2
|
+
export { codeLanguageUI } from './codeLangAffordance.js';
|
|
3
|
+
export { FenceNode, fenceAt, fenceAutoClose, fenceAutoCloseKeymap, fenceExitBlock, fenceTypeAutoClose } from './fenceAutoClose.js';
|
|
4
|
+
export { fenceCaretGuard } from './fenceCaretGuard.js';
|
|
5
|
+
export { fenceBackspaceGuard, fenceDeleteGuard } from './fenceDeleteGuards.js';
|
|
6
|
+
export { fenceTabDedent, fenceTabIndent, fenceTabKeymap } from './fenceTabIndent.js';
|
|
7
|
+
export { HighlightedSpan, highlightFenceSpans } from './highlightFence.js';
|
|
8
|
+
import '@codemirror/state';
|
|
9
|
+
import '@codemirror/view';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./codeHighlight.js";
|
|
2
|
+
export * from "./codeLangAffordance.js";
|
|
3
|
+
export * from "./fenceAutoClose.js";
|
|
4
|
+
export * from "./fenceCaretGuard.js";
|
|
5
|
+
export * from "./fenceDeleteGuards.js";
|
|
6
|
+
export * from "./fenceTabIndent.js";
|
|
7
|
+
export * from "./highlightFence.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/code/index.ts"],"sourcesContent":["/**\n * Fenced code blocks: syntax colors, the opener-row language pill + chooser,\n * fence typing/deletion/caret guards, Tab indentation, and the synchronous\n * highlighter the clipboard uses. Internal: `codeLanguageMenu`, `codePalette`.\n */\nexport * from \"./codeHighlight\";\nexport * from \"./codeLangAffordance\";\nexport * from \"./fenceAutoClose\";\nexport * from \"./fenceCaretGuard\";\nexport * from \"./fenceDeleteGuards\";\nexport * from \"./fenceTabIndent\";\nexport * from \"./highlightFence\";\n"],"mappings":"AAKA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
import { EditorState } from '@codemirror/state';
|
|
3
|
+
import { EditorView } from '@codemirror/view';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Grammar-level code-context test for the regex-scanning inline plugins
|
|
7
|
+
* (wikilink, highlight, footnote, math). Lezer emits no nodes for those
|
|
8
|
+
* constructs, so the plugins scan raw text — but code is literal, and the
|
|
9
|
+
* scans must skip it, exactly as the export pre-pass does (`protected_regions`
|
|
10
|
+
* in `src-tauri/src/export/html.rs`, derived from comrak's parse).
|
|
11
|
+
*
|
|
12
|
+
* A plugin guards a match by its OPENER and CLOSER positions: either sitting
|
|
13
|
+
* in code disqualifies the match, while a code span strictly inside the body
|
|
14
|
+
* (`[[a `b` c]]`) does not — the same rule the export applies.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type Tree = ReturnType<typeof syntaxTree>;
|
|
18
|
+
/** The tree parsed through the viewport — the painter's forced-parse bound
|
|
19
|
+
* (see `plugin.ts`), so a just-typed fence is already in the tree when a
|
|
20
|
+
* viewport-scanning plugin consults the guard. */
|
|
21
|
+
declare function viewportTree(view: EditorView): Tree;
|
|
22
|
+
/** The tree parsed through the whole document, for doc-wide state-level scans
|
|
23
|
+
* (math). Falls back to the partial tree when the parse budget runs out —
|
|
24
|
+
* the guard then degrades to the unguarded behavior past the parse frontier. */
|
|
25
|
+
declare function docTree(state: EditorState): Tree;
|
|
26
|
+
/** Whether the character at `pos` is code — inside a fenced or indented code
|
|
27
|
+
* block or an inline code span. `resolveInner` descends into a fence's nested
|
|
28
|
+
* `codeLanguages` parse; the parent walk crosses back into the markdown tree,
|
|
29
|
+
* so the enclosing FencedCode still answers. */
|
|
30
|
+
declare function inCode(tree: Tree, pos: number): boolean;
|
|
31
|
+
|
|
32
|
+
export { docTree, inCode, viewportTree };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "../../chunk-V6TY7KAL.js";
|
|
2
|
+
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
|
3
|
+
const CODE_NODES = /* @__PURE__ */ new Set(["FencedCode", "CodeBlock", "InlineCode"]);
|
|
4
|
+
function viewportTree(view) {
|
|
5
|
+
return ensureSyntaxTree(view.state, view.viewport.to, 100) ?? syntaxTree(view.state);
|
|
6
|
+
}
|
|
7
|
+
function docTree(state) {
|
|
8
|
+
return ensureSyntaxTree(state, state.doc.length, 20) ?? syntaxTree(state);
|
|
9
|
+
}
|
|
10
|
+
function inCode(tree, pos) {
|
|
11
|
+
let node = tree.resolveInner(pos, 1);
|
|
12
|
+
for (; node; node = node.parent) {
|
|
13
|
+
if (CODE_NODES.has(node.name)) return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
docTree,
|
|
19
|
+
inCode,
|
|
20
|
+
viewportTree
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=codeContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/codemirror/core/codeContext.ts"],"sourcesContent":["/**\n * Grammar-level code-context test for the regex-scanning inline plugins\n * (wikilink, highlight, footnote, math). Lezer emits no nodes for those\n * constructs, so the plugins scan raw text — but code is literal, and the\n * scans must skip it, exactly as the export pre-pass does (`protected_regions`\n * in `src-tauri/src/export/html.rs`, derived from comrak's parse).\n *\n * A plugin guards a match by its OPENER and CLOSER positions: either sitting\n * in code disqualifies the match, while a code span strictly inside the body\n * (`[[a `b` c]]`) does not — the same rule the export applies.\n */\n\nimport { ensureSyntaxTree, syntaxTree } from \"@codemirror/language\";\nimport type { EditorState } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\ntype Tree = ReturnType<typeof syntaxTree>;\n\n/** The slice of Lezer's SyntaxNode the ancestor walk touches. */\ntype NodeLike = { readonly name: string; readonly parent: NodeLike | null };\n\nconst CODE_NODES = new Set([\"FencedCode\", \"CodeBlock\", \"InlineCode\"]);\n\n/** The tree parsed through the viewport — the painter's forced-parse bound\n * (see `plugin.ts`), so a just-typed fence is already in the tree when a\n * viewport-scanning plugin consults the guard. */\nexport function viewportTree(view: EditorView): Tree {\n return ensureSyntaxTree(view.state, view.viewport.to, 100) ?? syntaxTree(view.state);\n}\n\n/** The tree parsed through the whole document, for doc-wide state-level scans\n * (math). Falls back to the partial tree when the parse budget runs out —\n * the guard then degrades to the unguarded behavior past the parse frontier. */\nexport function docTree(state: EditorState): Tree {\n return ensureSyntaxTree(state, state.doc.length, 20) ?? syntaxTree(state);\n}\n\n/** Whether the character at `pos` is code — inside a fenced or indented code\n * block or an inline code span. `resolveInner` descends into a fence's nested\n * `codeLanguages` parse; the parent walk crosses back into the markdown tree,\n * so the enclosing FencedCode still answers. */\nexport function inCode(tree: Tree, pos: number): boolean {\n let node = tree.resolveInner(pos, 1) as unknown as NodeLike | null;\n for (; node; node = node.parent) {\n if (CODE_NODES.has(node.name)) return true;\n }\n return false;\n}\n"],"mappings":";AAYA,SAAS,kBAAkB,kBAAkB;AAS7C,MAAM,aAAa,oBAAI,IAAI,CAAC,cAAc,aAAa,YAAY,CAAC;AAK7D,SAAS,aAAa,MAAwB;AACnD,SAAO,iBAAiB,KAAK,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,WAAW,KAAK,KAAK;AACrF;AAKO,SAAS,QAAQ,OAA0B;AAChD,SAAO,iBAAiB,OAAO,MAAM,IAAI,QAAQ,EAAE,KAAK,WAAW,KAAK;AAC1E;AAMO,SAAS,OAAO,MAAY,KAAsB;AACvD,MAAI,OAAO,KAAK,aAAa,KAAK,CAAC;AACnC,SAAO,MAAM,OAAO,KAAK,QAAQ;AAC/B,QAAI,WAAW,IAAI,KAAK,IAAI,EAAG,QAAO;AAAA,EACxC;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Extension } from '@codemirror/state';
|
|
2
|
+
import { EditorView } from '@codemirror/view';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Test-only harness: a headless CodeMirror `EditorView` wired with the markdown
|
|
6
|
+
* language + the live-preview decoration plugin, for keystroke-level tests of
|
|
7
|
+
* the editor commands (cursor motion, delete, list continuation, formatting).
|
|
8
|
+
*
|
|
9
|
+
* CodeMirror runs under jsdom; each test FILE that uses this must declare
|
|
10
|
+
* `// @vitest-environment jsdom` at its top. The Lezer tree is built eagerly so
|
|
11
|
+
* the plugin's atomic/hidden ranges (which the commands consult) are present.
|
|
12
|
+
*
|
|
13
|
+
* Not shipped — nothing in the app imports it, so it tree-shakes out.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** A headless editor over `doc` with the caret at `caret`. Extra extensions
|
|
17
|
+
* (e.g. a keymap under test) can be appended. Track-and-cleanup via
|
|
18
|
+
* {@link destroyEditors} in an `afterEach`. */
|
|
19
|
+
declare function makeEditor(doc: string, caret?: number, extra?: Extension[]): EditorView;
|
|
20
|
+
/** Like {@link makeEditor} but with every rendering extension the real editor
|
|
21
|
+
* loads — for tests that assert the user-visible rendered output, not just the
|
|
22
|
+
* source. */
|
|
23
|
+
declare function makeFullEditor(doc: string, caret?: number, extra?: Extension[]): EditorView;
|
|
24
|
+
/** Tear down every editor made since the last call. Call in `afterEach`. */
|
|
25
|
+
declare function destroyEditors(): void;
|
|
26
|
+
/** The current caret offset (head of the main selection). */
|
|
27
|
+
declare function caret(view: EditorView): number;
|
|
28
|
+
/** The current document text. */
|
|
29
|
+
declare function text(view: EditorView): string;
|
|
30
|
+
|
|
31
|
+
export { caret, destroyEditors, makeEditor, makeFullEditor, text };
|