groove-dev 0.15.1 → 0.16.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/docs/FILE-EDITOR-PLAN.md +253 -0
- package/node_modules/@codemirror/autocomplete/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/autocomplete/CHANGELOG.md +639 -0
- package/node_modules/@codemirror/autocomplete/LICENSE +21 -0
- package/node_modules/@codemirror/autocomplete/README.md +43 -0
- package/node_modules/@codemirror/autocomplete/dist/index.cjs +2151 -0
- package/node_modules/@codemirror/autocomplete/dist/index.d.cts +648 -0
- package/node_modules/@codemirror/autocomplete/dist/index.d.ts +648 -0
- package/node_modules/@codemirror/autocomplete/dist/index.js +2120 -0
- package/node_modules/@codemirror/autocomplete/package.json +41 -0
- package/node_modules/@codemirror/commands/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/commands/CHANGELOG.md +386 -0
- package/node_modules/@codemirror/commands/LICENSE +21 -0
- package/node_modules/@codemirror/commands/README.md +35 -0
- package/node_modules/@codemirror/commands/dist/index.cjs +1909 -0
- package/node_modules/@codemirror/commands/dist/index.d.cts +650 -0
- package/node_modules/@codemirror/commands/dist/index.d.ts +650 -0
- package/node_modules/@codemirror/commands/dist/index.js +1795 -0
- package/node_modules/@codemirror/commands/package.json +42 -0
- package/node_modules/@codemirror/lang-css/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-css/CHANGELOG.md +106 -0
- package/node_modules/@codemirror/lang-css/LICENSE +21 -0
- package/node_modules/@codemirror/lang-css/README.md +50 -0
- package/node_modules/@codemirror/lang-css/dist/index.cjs +269 -0
- package/node_modules/@codemirror/lang-css/dist/index.d.cts +28 -0
- package/node_modules/@codemirror/lang-css/dist/index.d.ts +28 -0
- package/node_modules/@codemirror/lang-css/dist/index.js +264 -0
- package/node_modules/@codemirror/lang-css/package.json +42 -0
- package/node_modules/@codemirror/lang-html/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-html/CHANGELOG.md +210 -0
- package/node_modules/@codemirror/lang-html/LICENSE +21 -0
- package/node_modules/@codemirror/lang-html/README.md +147 -0
- package/node_modules/@codemirror/lang-html/dist/index.cjs +667 -0
- package/node_modules/@codemirror/lang-html/dist/index.d.cts +115 -0
- package/node_modules/@codemirror/lang-html/dist/index.d.ts +115 -0
- package/node_modules/@codemirror/lang-html/dist/index.js +661 -0
- package/node_modules/@codemirror/lang-html/package.json +46 -0
- package/node_modules/@codemirror/lang-javascript/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-javascript/CHANGELOG.md +206 -0
- package/node_modules/@codemirror/lang-javascript/LICENSE +21 -0
- package/node_modules/@codemirror/lang-javascript/README.md +125 -0
- package/node_modules/@codemirror/lang-javascript/dist/index.cjs +526 -0
- package/node_modules/@codemirror/lang-javascript/dist/index.d.cts +93 -0
- package/node_modules/@codemirror/lang-javascript/dist/index.d.ts +93 -0
- package/node_modules/@codemirror/lang-javascript/dist/index.js +513 -0
- package/node_modules/@codemirror/lang-javascript/package.json +45 -0
- package/node_modules/@codemirror/lang-json/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-json/CHANGELOG.md +59 -0
- package/node_modules/@codemirror/lang-json/LICENSE +21 -0
- package/node_modules/@codemirror/lang-json/README.md +56 -0
- package/node_modules/@codemirror/lang-json/dist/index.cjs +68 -0
- package/node_modules/@codemirror/lang-json/dist/index.d.cts +22 -0
- package/node_modules/@codemirror/lang-json/dist/index.d.ts +22 -0
- package/node_modules/@codemirror/lang-json/dist/index.js +64 -0
- package/node_modules/@codemirror/lang-json/package.json +39 -0
- package/node_modules/@codemirror/lang-markdown/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-markdown/CHANGELOG.md +246 -0
- package/node_modules/@codemirror/lang-markdown/LICENSE +21 -0
- package/node_modules/@codemirror/lang-markdown/README.md +162 -0
- package/node_modules/@codemirror/lang-markdown/dist/index.cjs +501 -0
- package/node_modules/@codemirror/lang-markdown/dist/index.d.cts +124 -0
- package/node_modules/@codemirror/lang-markdown/dist/index.d.ts +124 -0
- package/node_modules/@codemirror/lang-markdown/dist/index.js +492 -0
- package/node_modules/@codemirror/lang-markdown/package.json +44 -0
- package/node_modules/@codemirror/lang-python/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lang-python/CHANGELOG.md +174 -0
- package/node_modules/@codemirror/lang-python/LICENSE +21 -0
- package/node_modules/@codemirror/lang-python/README.md +61 -0
- package/node_modules/@codemirror/lang-python/dist/index.cjs +313 -0
- package/node_modules/@codemirror/lang-python/dist/index.d.cts +26 -0
- package/node_modules/@codemirror/lang-python/dist/index.d.ts +26 -0
- package/node_modules/@codemirror/lang-python/dist/index.js +308 -0
- package/node_modules/@codemirror/lang-python/package.json +42 -0
- package/node_modules/@codemirror/language/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/language/CHANGELOG.md +412 -0
- package/node_modules/@codemirror/language/LICENSE +21 -0
- package/node_modules/@codemirror/language/README.md +66 -0
- package/node_modules/@codemirror/language/dist/index.cjs +2742 -0
- package/node_modules/@codemirror/language/dist/index.d.cts +1220 -0
- package/node_modules/@codemirror/language/dist/index.d.ts +1220 -0
- package/node_modules/@codemirror/language/dist/index.js +2687 -0
- package/node_modules/@codemirror/language/package.json +44 -0
- package/node_modules/@codemirror/lint/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/lint/CHANGELOG.md +318 -0
- package/node_modules/@codemirror/lint/LICENSE +21 -0
- package/node_modules/@codemirror/lint/README.md +18 -0
- package/node_modules/@codemirror/lint/dist/index.cjs +958 -0
- package/node_modules/@codemirror/lint/dist/index.d.cts +195 -0
- package/node_modules/@codemirror/lint/dist/index.d.ts +195 -0
- package/node_modules/@codemirror/lint/dist/index.js +945 -0
- package/node_modules/@codemirror/lint/package.json +40 -0
- package/node_modules/@codemirror/search/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/search/CHANGELOG.md +324 -0
- package/node_modules/@codemirror/search/LICENSE +21 -0
- package/node_modules/@codemirror/search/README.md +18 -0
- package/node_modules/@codemirror/search/dist/index.cjs +1237 -0
- package/node_modules/@codemirror/search/dist/index.d.cts +385 -0
- package/node_modules/@codemirror/search/dist/index.d.ts +385 -0
- package/node_modules/@codemirror/search/dist/index.js +1217 -0
- package/node_modules/@codemirror/search/package.json +40 -0
- package/node_modules/@codemirror/state/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/state/CHANGELOG.md +308 -0
- package/node_modules/@codemirror/state/LICENSE +21 -0
- package/node_modules/@codemirror/state/README.md +18 -0
- package/node_modules/@codemirror/state/dist/index.cjs +3922 -0
- package/node_modules/@codemirror/state/dist/index.d.cts +1713 -0
- package/node_modules/@codemirror/state/dist/index.d.ts +1713 -0
- package/node_modules/@codemirror/state/dist/index.js +3892 -0
- package/node_modules/@codemirror/state/package.json +38 -0
- package/node_modules/@codemirror/theme-one-dark/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/theme-one-dark/CHANGELOG.md +97 -0
- package/node_modules/@codemirror/theme-one-dark/LICENSE +21 -0
- package/node_modules/@codemirror/theme-one-dark/README.md +46 -0
- package/node_modules/@codemirror/theme-one-dark/dist/index.cjs +139 -0
- package/node_modules/@codemirror/theme-one-dark/dist/index.d.cts +39 -0
- package/node_modules/@codemirror/theme-one-dark/dist/index.d.ts +39 -0
- package/node_modules/@codemirror/theme-one-dark/dist/index.js +134 -0
- package/node_modules/@codemirror/theme-one-dark/package.json +41 -0
- package/node_modules/@codemirror/view/.github/workflows/dispatch.yml +16 -0
- package/node_modules/@codemirror/view/CHANGELOG.md +2284 -0
- package/node_modules/@codemirror/view/LICENSE +21 -0
- package/node_modules/@codemirror/view/README.md +37 -0
- package/node_modules/@codemirror/view/dist/index.cjs +11796 -0
- package/node_modules/@codemirror/view/dist/index.d.cts +2389 -0
- package/node_modules/@codemirror/view/dist/index.d.ts +2389 -0
- package/node_modules/@codemirror/view/dist/index.js +11745 -0
- package/node_modules/@codemirror/view/package.json +41 -0
- package/node_modules/@groove-dev/daemon/src/api.js +147 -2
- package/node_modules/@groove-dev/daemon/src/filewatcher.js +59 -0
- package/node_modules/@groove-dev/daemon/src/index.js +28 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-Dxg9hdf3.js +103 -0
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +15 -2
- package/node_modules/@groove-dev/gui/src/App.jsx +3 -0
- package/node_modules/@groove-dev/gui/src/components/CodeEditor.jsx +143 -0
- package/node_modules/@groove-dev/gui/src/components/EditorTabs.jsx +83 -0
- package/node_modules/@groove-dev/gui/src/components/FileTree.jsx +193 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +158 -1
- package/node_modules/@groove-dev/gui/src/views/FileEditor.jsx +142 -0
- package/node_modules/@lezer/common/LICENSE +21 -0
- package/node_modules/@lezer/common/README.md +14 -0
- package/node_modules/@lezer/common/dist/index.cjs +2209 -0
- package/node_modules/@lezer/common/dist/index.d.cts +1174 -0
- package/node_modules/@lezer/common/dist/index.d.ts +1174 -0
- package/node_modules/@lezer/common/dist/index.js +2196 -0
- package/node_modules/@lezer/common/package.json +32 -0
- package/node_modules/@lezer/css/CHANGELOG.md +307 -0
- package/node_modules/@lezer/css/LICENSE +21 -0
- package/node_modules/@lezer/css/README.md +6 -0
- package/node_modules/@lezer/css/dist/index.cjs +151 -0
- package/node_modules/@lezer/css/dist/index.d.cts +3 -0
- package/node_modules/@lezer/css/dist/index.d.ts +3 -0
- package/node_modules/@lezer/css/dist/index.js +147 -0
- package/node_modules/@lezer/css/package.json +36 -0
- package/node_modules/@lezer/css/rollup.config.js +16 -0
- package/node_modules/@lezer/css/src/css.grammar +290 -0
- package/node_modules/@lezer/css/src/highlight.js +36 -0
- package/node_modules/@lezer/css/src/parser.js +31 -0
- package/node_modules/@lezer/css/src/parser.terms.js +63 -0
- package/node_modules/@lezer/css/src/tokens.js +75 -0
- package/node_modules/@lezer/css/test/declarations.txt +241 -0
- package/node_modules/@lezer/css/test/selector.txt +178 -0
- package/node_modules/@lezer/css/test/statements.txt +227 -0
- package/node_modules/@lezer/css/test/test-css.js +16 -0
- package/node_modules/@lezer/highlight/LICENSE +21 -0
- package/node_modules/@lezer/highlight/README.md +14 -0
- package/node_modules/@lezer/highlight/dist/index.cjs +936 -0
- package/node_modules/@lezer/highlight/dist/index.d.cts +623 -0
- package/node_modules/@lezer/highlight/dist/index.d.ts +623 -0
- package/node_modules/@lezer/highlight/dist/index.js +927 -0
- package/node_modules/@lezer/highlight/package.json +31 -0
- package/node_modules/@lezer/html/CHANGELOG.md +303 -0
- package/node_modules/@lezer/html/LICENSE +21 -0
- package/node_modules/@lezer/html/README.md +37 -0
- package/node_modules/@lezer/html/dist/index.cjs +354 -0
- package/node_modules/@lezer/html/dist/index.d.cts +14 -0
- package/node_modules/@lezer/html/dist/index.d.ts +14 -0
- package/node_modules/@lezer/html/dist/index.js +349 -0
- package/node_modules/@lezer/html/package.json +37 -0
- package/node_modules/@lezer/html/rollup.config.js +16 -0
- package/node_modules/@lezer/html/src/.tern-port +1 -0
- package/node_modules/@lezer/html/src/content.js +87 -0
- package/node_modules/@lezer/html/src/highlight.js +15 -0
- package/node_modules/@lezer/html/src/html.grammar +181 -0
- package/node_modules/@lezer/html/src/index.js +2 -0
- package/node_modules/@lezer/html/src/parser.js +27 -0
- package/node_modules/@lezer/html/src/parser.terms.js +53 -0
- package/node_modules/@lezer/html/src/tokens.js +199 -0
- package/node_modules/@lezer/html/test/mixed.txt +69 -0
- package/node_modules/@lezer/html/test/tags.txt +370 -0
- package/node_modules/@lezer/html/test/test-html.js +29 -0
- package/node_modules/@lezer/html/test/test-incremental.js +97 -0
- package/node_modules/@lezer/html/test/vue.txt +56 -0
- package/node_modules/@lezer/javascript/CHANGELOG.md +485 -0
- package/node_modules/@lezer/javascript/LICENSE +21 -0
- package/node_modules/@lezer/javascript/README.md +14 -0
- package/node_modules/@lezer/javascript/dist/index.cjs +196 -0
- package/node_modules/@lezer/javascript/dist/index.d.cts +3 -0
- package/node_modules/@lezer/javascript/dist/index.d.ts +3 -0
- package/node_modules/@lezer/javascript/dist/index.js +192 -0
- package/node_modules/@lezer/javascript/package.json +36 -0
- package/node_modules/@lezer/javascript/rollup.config.js +16 -0
- package/node_modules/@lezer/javascript/src/highlight.js +62 -0
- package/node_modules/@lezer/javascript/src/javascript.grammar +735 -0
- package/node_modules/@lezer/javascript/src/parser.js +33 -0
- package/node_modules/@lezer/javascript/src/parser.terms.js +177 -0
- package/node_modules/@lezer/javascript/src/tokens.js +87 -0
- package/node_modules/@lezer/javascript/test/decorator.txt +64 -0
- package/node_modules/@lezer/javascript/test/expression.txt +686 -0
- package/node_modules/@lezer/javascript/test/jsx.txt +79 -0
- package/node_modules/@lezer/javascript/test/semicolon.txt +77 -0
- package/node_modules/@lezer/javascript/test/statement.txt +404 -0
- package/node_modules/@lezer/javascript/test/test-javascript.js +17 -0
- package/node_modules/@lezer/javascript/test/typescript.txt +401 -0
- package/node_modules/@lezer/json/CHANGELOG.md +79 -0
- package/node_modules/@lezer/json/LICENSE +21 -0
- package/node_modules/@lezer/json/README.md +7 -0
- package/node_modules/@lezer/json/dist/index.cjs +41 -0
- package/node_modules/@lezer/json/dist/index.d.cts +3 -0
- package/node_modules/@lezer/json/dist/index.d.ts +3 -0
- package/node_modules/@lezer/json/dist/index.js +37 -0
- package/node_modules/@lezer/json/package.json +36 -0
- package/node_modules/@lezer/json/rollup.config.js +15 -0
- package/node_modules/@lezer/json/src/highlight.js +12 -0
- package/node_modules/@lezer/json/src/json.grammar +38 -0
- package/node_modules/@lezer/json/src/parser.js +23 -0
- package/node_modules/@lezer/json/src/parser.terms.js +12 -0
- package/node_modules/@lezer/json/test/arrays.txt +34 -0
- package/node_modules/@lezer/json/test/literals.txt +23 -0
- package/node_modules/@lezer/json/test/numbers.txt +87 -0
- package/node_modules/@lezer/json/test/objects.txt +34 -0
- package/node_modules/@lezer/json/test/strings.txt +50 -0
- package/node_modules/@lezer/json/test/test-json.js +17 -0
- package/node_modules/@lezer/lr/LICENSE +21 -0
- package/node_modules/@lezer/lr/README.md +25 -0
- package/node_modules/@lezer/lr/dist/constants.d.ts +45 -0
- package/node_modules/@lezer/lr/dist/constants.js +5 -0
- package/node_modules/@lezer/lr/dist/index.cjs +1891 -0
- package/node_modules/@lezer/lr/dist/index.d.cts +303 -0
- package/node_modules/@lezer/lr/dist/index.d.ts +303 -0
- package/node_modules/@lezer/lr/dist/index.js +1884 -0
- package/node_modules/@lezer/lr/package.json +32 -0
- package/node_modules/@lezer/markdown/CHANGELOG.md +279 -0
- package/node_modules/@lezer/markdown/LICENSE +21 -0
- package/node_modules/@lezer/markdown/README.md +725 -0
- package/node_modules/@lezer/markdown/bin/build-readme.cjs +39 -0
- package/node_modules/@lezer/markdown/build.js +16 -0
- package/node_modules/@lezer/markdown/dist/index.cjs +2352 -0
- package/node_modules/@lezer/markdown/dist/index.d.cts +600 -0
- package/node_modules/@lezer/markdown/dist/index.d.ts +600 -0
- package/node_modules/@lezer/markdown/dist/index.js +2335 -0
- package/node_modules/@lezer/markdown/package.json +37 -0
- package/node_modules/@lezer/markdown/src/README.md +83 -0
- package/node_modules/@lezer/markdown/src/extension.ts +301 -0
- package/node_modules/@lezer/markdown/src/index.ts +5 -0
- package/node_modules/@lezer/markdown/src/markdown.ts +1961 -0
- package/node_modules/@lezer/markdown/src/nest.ts +46 -0
- package/node_modules/@lezer/markdown/test/compare-tree.ts +14 -0
- package/node_modules/@lezer/markdown/test/spec.ts +79 -0
- package/node_modules/@lezer/markdown/test/test-extension.ts +269 -0
- package/node_modules/@lezer/markdown/test/test-incremental.ts +265 -0
- package/node_modules/@lezer/markdown/test/test-markdown.ts +3574 -0
- package/node_modules/@lezer/markdown/test/test-nesting.ts +86 -0
- package/node_modules/@lezer/markdown/test/tsconfig.json +12 -0
- package/node_modules/@lezer/markdown/tsconfig.json +14 -0
- package/node_modules/@lezer/python/CHANGELOG.md +243 -0
- package/node_modules/@lezer/python/LICENSE +21 -0
- package/node_modules/@lezer/python/README.md +6 -0
- package/node_modules/@lezer/python/dist/index.cjs +330 -0
- package/node_modules/@lezer/python/dist/index.d.cts +3 -0
- package/node_modules/@lezer/python/dist/index.d.ts +3 -0
- package/node_modules/@lezer/python/dist/index.js +326 -0
- package/node_modules/@lezer/python/package.json +36 -0
- package/node_modules/@lezer/python/rollup.config.js +16 -0
- package/node_modules/@lezer/python/src/highlight.js +35 -0
- package/node_modules/@lezer/python/src/python.grammar +381 -0
- package/node_modules/@lezer/python/src/tokens.js +224 -0
- package/node_modules/@lezer/python/test/expression.txt +231 -0
- package/node_modules/@lezer/python/test/statement.txt +442 -0
- package/node_modules/@lezer/python/test/test-incremental.js +33 -0
- package/node_modules/@lezer/python/test/test-python.js +17 -0
- package/node_modules/@marijn/find-cluster-break/LICENSE +21 -0
- package/node_modules/@marijn/find-cluster-break/README.md +28 -0
- package/node_modules/@marijn/find-cluster-break/dist/index.cjs +85 -0
- package/node_modules/@marijn/find-cluster-break/dist/index.d.cts +15 -0
- package/node_modules/@marijn/find-cluster-break/package.json +35 -0
- package/node_modules/@marijn/find-cluster-break/rollup.config.js +7 -0
- package/node_modules/@marijn/find-cluster-break/src/index.d.ts +15 -0
- package/node_modules/@marijn/find-cluster-break/src/index.js +87 -0
- package/node_modules/@marijn/find-cluster-break/test/test-cluster.js +30 -0
- package/node_modules/crelt/LICENSE +19 -0
- package/node_modules/crelt/README.md +23 -0
- package/node_modules/crelt/dist/index.cjs +31 -0
- package/node_modules/crelt/dist/index.d.cts +4 -0
- package/node_modules/crelt/index.d.ts +4 -0
- package/node_modules/crelt/index.js +28 -0
- package/node_modules/crelt/package.json +35 -0
- package/node_modules/crelt/rollup.config.js +13 -0
- package/node_modules/style-mod/LICENSE +19 -0
- package/node_modules/style-mod/README.md +98 -0
- package/node_modules/style-mod/dist/style-mod.cjs +165 -0
- package/node_modules/style-mod/dist/style-mod.d.cts +16 -0
- package/node_modules/style-mod/package.json +39 -0
- package/node_modules/style-mod/src/README.md +34 -0
- package/node_modules/style-mod/src/style-mod.d.ts +16 -0
- package/node_modules/style-mod/src/style-mod.js +172 -0
- package/node_modules/style-mod/test/test-style-mod.js +104 -0
- package/node_modules/w3c-keyname/.tern-port +1 -0
- package/node_modules/w3c-keyname/LICENSE +19 -0
- package/node_modules/w3c-keyname/README.md +18 -0
- package/node_modules/w3c-keyname/index.cjs +127 -0
- package/node_modules/w3c-keyname/index.d.cts +5 -0
- package/node_modules/w3c-keyname/index.d.ts +5 -0
- package/node_modules/w3c-keyname/index.js +119 -0
- package/node_modules/w3c-keyname/package.json +37 -0
- package/package.json +1 -1
- package/packages/daemon/src/api.js +147 -2
- package/packages/daemon/src/filewatcher.js +59 -0
- package/packages/daemon/src/index.js +28 -1
- package/packages/gui/dist/assets/index-Dxg9hdf3.js +103 -0
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +15 -2
- package/packages/gui/src/App.jsx +3 -0
- package/packages/gui/src/components/CodeEditor.jsx +143 -0
- package/packages/gui/src/components/EditorTabs.jsx +83 -0
- package/packages/gui/src/components/FileTree.jsx +193 -0
- package/packages/gui/src/stores/groove.js +158 -1
- package/packages/gui/src/views/FileEditor.jsx +142 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-8Kqi_LVo.js +0 -74
- package/packages/gui/dist/assets/index-8Kqi_LVo.js +0 -74
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {SyntaxNode, Parser, Input, parseMixed, SyntaxNodeRef} from "@lezer/common"
|
|
2
|
+
import {Type, MarkdownExtension} from "./markdown"
|
|
3
|
+
|
|
4
|
+
function leftOverSpace(node: SyntaxNode, from: number, to: number) {
|
|
5
|
+
let ranges = []
|
|
6
|
+
for (let n = node.firstChild, pos = from;; n = n.nextSibling) {
|
|
7
|
+
let nextPos = n ? n.from : to
|
|
8
|
+
if (nextPos > pos) ranges.push({from: pos, to: nextPos})
|
|
9
|
+
if (!n) break
|
|
10
|
+
pos = n.to
|
|
11
|
+
}
|
|
12
|
+
return ranges
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// Create a Markdown extension to enable nested parsing on code
|
|
16
|
+
/// blocks and/or embedded HTML.
|
|
17
|
+
export function parseCode(config: {
|
|
18
|
+
/// When provided, this will be used to parse the content of code
|
|
19
|
+
/// blocks. `info` is the string after the opening ` ``` ` marker,
|
|
20
|
+
/// or the empty string if there is no such info or this is an
|
|
21
|
+
/// indented code block. If there is a parser available for the
|
|
22
|
+
/// code, it should return a function that can construct the
|
|
23
|
+
/// [parse](https://lezer.codemirror.net/docs/ref/#common.PartialParse).
|
|
24
|
+
codeParser?: (info: string) => null | Parser
|
|
25
|
+
/// The parser used to parse HTML tags (both block and inline).
|
|
26
|
+
htmlParser?: Parser,
|
|
27
|
+
}): MarkdownExtension {
|
|
28
|
+
let {codeParser, htmlParser} = config
|
|
29
|
+
let wrap = parseMixed((node: SyntaxNodeRef, input: Input) => {
|
|
30
|
+
let id = node.type.id
|
|
31
|
+
if (codeParser && (id == Type.CodeBlock || id == Type.FencedCode)) {
|
|
32
|
+
let info = ""
|
|
33
|
+
if (id == Type.FencedCode) {
|
|
34
|
+
let infoNode = node.node.getChild(Type.CodeInfo)
|
|
35
|
+
if (infoNode) info = input.read(infoNode.from, infoNode.to)
|
|
36
|
+
}
|
|
37
|
+
let parser = codeParser(info)
|
|
38
|
+
if (parser)
|
|
39
|
+
return {parser, overlay: node => node.type.id == Type.CodeText, bracketed: id == Type.FencedCode}
|
|
40
|
+
} else if (htmlParser && (id == Type.HTMLBlock || id == Type.HTMLTag || id == Type.CommentBlock)) {
|
|
41
|
+
return {parser: htmlParser, overlay: leftOverSpace(node.node, node.from, node.to)}
|
|
42
|
+
}
|
|
43
|
+
return null
|
|
44
|
+
})
|
|
45
|
+
return {wrap}
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Tree} from "@lezer/common"
|
|
2
|
+
|
|
3
|
+
export function compareTree(a: Tree, b: Tree) {
|
|
4
|
+
let curA = a.cursor(), curB = b.cursor()
|
|
5
|
+
for (;;) {
|
|
6
|
+
let mismatch = null, next = false
|
|
7
|
+
if (curA.type != curB.type) mismatch = `Node type mismatch (${curA.name} vs ${curB.name})`
|
|
8
|
+
else if (curA.from != curB.from) mismatch = `Start pos mismatch for ${curA.name}: ${curA.from} vs ${curB.from}`
|
|
9
|
+
else if (curA.to != curB.to) mismatch = `End pos mismatch for ${curA.name}: ${curA.to} vs ${curB.to}`
|
|
10
|
+
else if ((next = curA.next()) != curB.next()) mismatch = `Tree size mismatch`
|
|
11
|
+
if (mismatch) throw new Error(`${mismatch}\n ${a}\n ${b}`)
|
|
12
|
+
if (!next) break
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {Tree} from "@lezer/common"
|
|
2
|
+
import {MarkdownParser} from ".."
|
|
3
|
+
|
|
4
|
+
const abbrev: {[abbr: string]: string} = {
|
|
5
|
+
__proto__: null as any,
|
|
6
|
+
CB: "CodeBlock",
|
|
7
|
+
FC: "FencedCode",
|
|
8
|
+
Q: "Blockquote",
|
|
9
|
+
HR: "HorizontalRule",
|
|
10
|
+
BL: "BulletList",
|
|
11
|
+
OL: "OrderedList",
|
|
12
|
+
LI: "ListItem",
|
|
13
|
+
H1: "ATXHeading1",
|
|
14
|
+
H2: "ATXHeading2",
|
|
15
|
+
H3: "ATXHeading3",
|
|
16
|
+
H4: "ATXHeading4",
|
|
17
|
+
H5: "ATXHeading5",
|
|
18
|
+
H6: "ATXHeading6",
|
|
19
|
+
SH1: "SetextHeading1",
|
|
20
|
+
SH2: "SetextHeading2",
|
|
21
|
+
HB: "HTMLBlock",
|
|
22
|
+
PI: "ProcessingInstructionBlock",
|
|
23
|
+
CMB: "CommentBlock",
|
|
24
|
+
LR: "LinkReference",
|
|
25
|
+
P: "Paragraph",
|
|
26
|
+
Esc: "Escape",
|
|
27
|
+
Ent: "Entity",
|
|
28
|
+
BR: "HardBreak",
|
|
29
|
+
Em: "Emphasis",
|
|
30
|
+
St: "StrongEmphasis",
|
|
31
|
+
Ln: "Link",
|
|
32
|
+
Al: "Autolink",
|
|
33
|
+
Im: "Image",
|
|
34
|
+
C: "InlineCode",
|
|
35
|
+
HT: "HTMLTag",
|
|
36
|
+
CM: "Comment",
|
|
37
|
+
Pi: "ProcessingInstruction",
|
|
38
|
+
h: "HeaderMark",
|
|
39
|
+
q: "QuoteMark",
|
|
40
|
+
l: "ListMark",
|
|
41
|
+
L: "LinkMark",
|
|
42
|
+
e: "EmphasisMark",
|
|
43
|
+
c: "CodeMark",
|
|
44
|
+
cI: "CodeInfo",
|
|
45
|
+
cT: "CodeText",
|
|
46
|
+
LT: "LinkTitle",
|
|
47
|
+
LL: "LinkLabel"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class SpecParser {
|
|
51
|
+
constructor(readonly parser: MarkdownParser, readonly localAbbrev?: {[name: string]: string}) {}
|
|
52
|
+
|
|
53
|
+
type(name: string) {
|
|
54
|
+
name = (this.localAbbrev && this.localAbbrev[name]) || abbrev[name] || name
|
|
55
|
+
return this.parser.nodeSet.types.find(t => t.name == name)?.id
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
parse(spec: string, specName: string) {
|
|
59
|
+
let doc = "", buffer = [], stack: number[] = []
|
|
60
|
+
for (let pos = 0; pos < spec.length; pos++) {
|
|
61
|
+
let ch = spec[pos]
|
|
62
|
+
if (ch == "{") {
|
|
63
|
+
let name = /^(\w+):/.exec(spec.slice(pos + 1)), tag = name && this.type(name[1])
|
|
64
|
+
if (tag == null) throw new Error(`Invalid node opening mark at ${pos} in ${specName}`)
|
|
65
|
+
pos += name![0].length
|
|
66
|
+
stack.push(tag, doc.length, buffer.length)
|
|
67
|
+
} else if (ch == "}") {
|
|
68
|
+
if (!stack.length) throw new Error(`Mismatched node close mark at ${pos} in ${specName}`)
|
|
69
|
+
let bufStart = stack.pop()!, from = stack.pop()!, type = stack.pop()!
|
|
70
|
+
buffer.push(type, from, doc.length, 4 + buffer.length - bufStart)
|
|
71
|
+
} else {
|
|
72
|
+
doc += ch
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (stack.length) throw new Error(`Unclosed node in ${specName}`)
|
|
76
|
+
return {tree: Tree.build({buffer, nodeSet: this.parser.nodeSet, topID: this.type("Document")!, length: doc.length}), doc}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {parser as cmParser, GFM, Subscript, Superscript, Emoji} from "../dist/index.js"
|
|
2
|
+
import {compareTree} from "./compare-tree.js"
|
|
3
|
+
import {SpecParser} from "./spec.js"
|
|
4
|
+
|
|
5
|
+
const parser = cmParser.configure([GFM, Subscript, Superscript, Emoji])
|
|
6
|
+
|
|
7
|
+
const specParser = new SpecParser(parser, {
|
|
8
|
+
__proto__: null as any,
|
|
9
|
+
Th: "Strikethrough",
|
|
10
|
+
tm: "StrikethroughMark",
|
|
11
|
+
TB: "Table",
|
|
12
|
+
TH: "TableHeader",
|
|
13
|
+
TR: "TableRow",
|
|
14
|
+
TC: "TableCell",
|
|
15
|
+
tb: "TableDelimiter",
|
|
16
|
+
T: "Task",
|
|
17
|
+
t: "TaskMarker",
|
|
18
|
+
Sub: "Subscript",
|
|
19
|
+
sub: "SubscriptMark",
|
|
20
|
+
Sup: "Superscript",
|
|
21
|
+
sup: "SuperscriptMark",
|
|
22
|
+
ji: "Emoji"
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function test(name: string, spec: string, p = parser) {
|
|
26
|
+
it(name, () => {
|
|
27
|
+
let {tree, doc} = specParser.parse(spec, name)
|
|
28
|
+
compareTree(p.parse(doc), tree)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("Extension", () => {
|
|
33
|
+
test("Tables (example 198)", `
|
|
34
|
+
{TB:{TH:{tb:|} {TC:foo} {tb:|} {TC:bar} {tb:|}}
|
|
35
|
+
{tb:| --- | --- |}
|
|
36
|
+
{TR:{tb:|} {TC:baz} {tb:|} {TC:bim} {tb:|}}}`)
|
|
37
|
+
|
|
38
|
+
test("Tables (example 199)", `
|
|
39
|
+
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:defghi} {tb:|}}
|
|
40
|
+
{tb::-: | -----------:}
|
|
41
|
+
{TR:{TC:bar} {tb:|} {TC:baz}}}`)
|
|
42
|
+
|
|
43
|
+
test("Tables (example 200)", `
|
|
44
|
+
{TB:{TH:{tb:|} {TC:f{Esc:\\|}oo} {tb:|}}
|
|
45
|
+
{tb:| ------ |}
|
|
46
|
+
{TR:{tb:|} {TC:b {C:{c:\`}\\|{c:\`}} az} {tb:|}}
|
|
47
|
+
{TR:{tb:|} {TC:b {St:{e:**}{Esc:\\|}{e:**}} im} {tb:|}}}`)
|
|
48
|
+
|
|
49
|
+
test("Tables (example 201)", `
|
|
50
|
+
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
|
51
|
+
{tb:| --- | --- |}
|
|
52
|
+
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|}}}
|
|
53
|
+
{Q:{q:>} {P:bar}}`)
|
|
54
|
+
|
|
55
|
+
test("Tables (example 202)", `
|
|
56
|
+
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
|
57
|
+
{tb:| --- | --- |}
|
|
58
|
+
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|}}
|
|
59
|
+
{TR:{TC:bar}}}
|
|
60
|
+
|
|
61
|
+
{P:bar}`)
|
|
62
|
+
|
|
63
|
+
test("Tables (example 203)", `
|
|
64
|
+
{P:| abc | def |
|
|
65
|
+
| --- |
|
|
66
|
+
| bar |}`)
|
|
67
|
+
|
|
68
|
+
test("Tables (example 204)", `
|
|
69
|
+
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
|
70
|
+
{tb:| --- | --- |}
|
|
71
|
+
{TR:{tb:|} {TC:bar} {tb:|}}
|
|
72
|
+
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|} {TC:boo} {tb:|}}}`)
|
|
73
|
+
|
|
74
|
+
test("Tables (example 205)", `
|
|
75
|
+
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
|
76
|
+
{tb:| --- | --- |}}`)
|
|
77
|
+
|
|
78
|
+
test("Tables (in blockquote)", `
|
|
79
|
+
{Q:{q:>} {TB:{TH:{tb:|} {TC:one} {tb:|} {TC:two} {tb:|}}
|
|
80
|
+
{q:>} {tb:| --- | --- |}
|
|
81
|
+
{q:>} {TR:{tb:|} {TC:123} {tb:|} {TC:456} {tb:|}}}
|
|
82
|
+
{q:>}
|
|
83
|
+
{q:>} {P:Okay}}`)
|
|
84
|
+
|
|
85
|
+
test("Tables (empty header)", `
|
|
86
|
+
{TB:{TH:{tb:|} {tb:|} {tb:|}}
|
|
87
|
+
{tb:| :-: | :-: |}
|
|
88
|
+
{TR:{tb:|} {TC:One} {tb:|} {TC:Two} {tb:|}}}`)
|
|
89
|
+
|
|
90
|
+
test("Tables (end paragraph)", `
|
|
91
|
+
{P:Hello}
|
|
92
|
+
{TB:{TH:{tb:|} {TC:foo} {tb:|} {TC:bar} {tb:|}}
|
|
93
|
+
{tb:| --- | --- |}
|
|
94
|
+
{TR:{tb:|} {TC:baz} {tb:|} {TC:bim} {tb:|}}}`)
|
|
95
|
+
|
|
96
|
+
test("Tables (invalid tables don't end paragraph)", `
|
|
97
|
+
{P:Hello
|
|
98
|
+
| abc | def |
|
|
99
|
+
| --- |
|
|
100
|
+
| bar |}`)
|
|
101
|
+
|
|
102
|
+
test("Task list (example 279)", `
|
|
103
|
+
{BL:{LI:{l:-} {T:{t:[ ]} foo}}
|
|
104
|
+
{LI:{l:-} {T:{t:[x]} bar}}}`)
|
|
105
|
+
|
|
106
|
+
test("Task list (example 280)", `
|
|
107
|
+
{BL:{LI:{l:-} {T:{t:[x]} foo}
|
|
108
|
+
{BL:{LI:{l:-} {T:{t:[ ]} bar}}
|
|
109
|
+
{LI:{l:-} {T:{t:[x]} baz}}}}
|
|
110
|
+
{LI:{l:-} {T:{t:[ ]} bim}}}`)
|
|
111
|
+
|
|
112
|
+
test("Autolink (example 622)", `
|
|
113
|
+
{P:{URL:www.commonmark.org}}`)
|
|
114
|
+
|
|
115
|
+
test("Autolink (example 623)", `
|
|
116
|
+
{P:Visit {URL:www.commonmark.org/help} for more information.}`)
|
|
117
|
+
|
|
118
|
+
test("Autolink (example 624)", `
|
|
119
|
+
{P:Visit {URL:www.commonmark.org}.}
|
|
120
|
+
|
|
121
|
+
{P:Visit {URL:www.commonmark.org/a.b}.}`)
|
|
122
|
+
|
|
123
|
+
test("Autolink (example 625)", `
|
|
124
|
+
{P:{URL:www.google.com/search?q=Markup+(business)}}
|
|
125
|
+
|
|
126
|
+
{P:{URL:www.google.com/search?q=Markup+(business)}))}
|
|
127
|
+
|
|
128
|
+
{P:({URL:www.google.com/search?q=Markup+(business)})}
|
|
129
|
+
|
|
130
|
+
{P:({URL:www.google.com/search?q=Markup+(business)}}`)
|
|
131
|
+
|
|
132
|
+
test("Autolink (example 626)", `
|
|
133
|
+
{P:{URL:www.google.com/search?q=(business))+ok}}`)
|
|
134
|
+
|
|
135
|
+
test("Autolink (example 627)", `
|
|
136
|
+
{P:{URL:www.google.com/search?q=commonmark&hl=en}}
|
|
137
|
+
|
|
138
|
+
{P:{URL:www.google.com/search?q=commonmark}{Entity:&hl;}}`)
|
|
139
|
+
|
|
140
|
+
test("Autolink (example 628)", `
|
|
141
|
+
{P:{URL:www.commonmark.org/he}<lp}`)
|
|
142
|
+
|
|
143
|
+
test("Autolink (example 629)", `
|
|
144
|
+
{P:{URL:http://commonmark.org}}
|
|
145
|
+
|
|
146
|
+
{P:(Visit {URL:https://encrypted.google.com/search?q=Markup+(business)})}`)
|
|
147
|
+
|
|
148
|
+
test("Autolink (example 630)", `
|
|
149
|
+
{P:{URL:foo@bar.baz}}`)
|
|
150
|
+
|
|
151
|
+
test("Autolink (example 631)", `
|
|
152
|
+
{P:hello@mail+xyz.example isn't valid, but {URL:hello+xyz@mail.example} is.}`)
|
|
153
|
+
|
|
154
|
+
test("Autolink (example 632)", `
|
|
155
|
+
{P:{URL:a.b-c_d@a.b}}
|
|
156
|
+
|
|
157
|
+
{P:{URL:a.b-c_d@a.b}.}
|
|
158
|
+
|
|
159
|
+
{P:a.b-c_d@a.b-}
|
|
160
|
+
|
|
161
|
+
{P:a.b-c_d@a.b_}`)
|
|
162
|
+
|
|
163
|
+
test("Autolink (example 633)", `
|
|
164
|
+
{P:{URL:mailto:foo@bar.baz}}
|
|
165
|
+
|
|
166
|
+
{P:{URL:mailto:a.b-c_d@a.b}}
|
|
167
|
+
|
|
168
|
+
{P:{URL:mailto:a.b-c_d@a.b}.}
|
|
169
|
+
|
|
170
|
+
{P:{URL:mailto:a.b-c_d@a.b}/}
|
|
171
|
+
|
|
172
|
+
{P:mailto:a.b-c_d@a.b-}
|
|
173
|
+
|
|
174
|
+
{P:mailto:a.b-c_d@a.b_}
|
|
175
|
+
|
|
176
|
+
{P:{URL:xmpp:foo@bar.baz}}
|
|
177
|
+
|
|
178
|
+
{P:{URL:xmpp:foo@bar.baz}.}`)
|
|
179
|
+
|
|
180
|
+
test("Autolink (example 634)", `
|
|
181
|
+
{P:{URL:xmpp:foo@bar.baz/txt}}
|
|
182
|
+
|
|
183
|
+
{P:{URL:xmpp:foo@bar.baz/txt@bin}}
|
|
184
|
+
|
|
185
|
+
{P:{URL:xmpp:foo@bar.baz/txt@bin.com}}`)
|
|
186
|
+
|
|
187
|
+
test("Autolink (example 635)", `
|
|
188
|
+
{P:{URL:xmpp:foo@bar.baz/txt}/bin}`)
|
|
189
|
+
|
|
190
|
+
test("Task list (in ordered list)", `
|
|
191
|
+
{OL:{LI:{l:1.} {T:{t:[X]} Okay}}}`)
|
|
192
|
+
|
|
193
|
+
test("Task list (versus table)", `
|
|
194
|
+
{BL:{LI:{l:-} {TB:{TH:{TC:[ ] foo} {tb:|} {TC:bar}}
|
|
195
|
+
{tb:--- | ---}}}}`)
|
|
196
|
+
|
|
197
|
+
test("Task list (versus setext header)", `
|
|
198
|
+
{OL:{LI:{l:1.} {SH1:{Ln:{L:[}X{L:]}} foo
|
|
199
|
+
{h:===}}}}`)
|
|
200
|
+
|
|
201
|
+
test("Strikethrough (example 491)", `
|
|
202
|
+
{P:{Th:{tm:~~}Hi{tm:~~}} Hello, world!}`)
|
|
203
|
+
|
|
204
|
+
test("Strikethrough (example 492)", `
|
|
205
|
+
{P:This ~~has a}
|
|
206
|
+
|
|
207
|
+
{P:new paragraph~~.}`)
|
|
208
|
+
|
|
209
|
+
test("Strikethrough (nested)", `
|
|
210
|
+
{P:Nesting {St:{e:**}with {Th:{tm:~~}emphasis{tm:~~}}{e:**}}.}`)
|
|
211
|
+
|
|
212
|
+
test("Strikethrough (overlapping)", `
|
|
213
|
+
{P:One {St:{e:**}two ~~three{e:**}} four~~}
|
|
214
|
+
|
|
215
|
+
{P:One {Th:{tm:~~}two **three{tm:~~}} four**}`)
|
|
216
|
+
|
|
217
|
+
test("Strikethrough (escaped)", `
|
|
218
|
+
{P:A {Esc:\\~}~b c~~}`)
|
|
219
|
+
|
|
220
|
+
test("Strikethrough around spaces", `
|
|
221
|
+
{P:One ~~ two~~ three {Th:{tm:~~}.foo.{tm:~~}} a~~.foo.~~a {Th:{tm:~~}blah{tm:~~}}.}`)
|
|
222
|
+
|
|
223
|
+
test("Subscript", `
|
|
224
|
+
{P:One {Sub:{sub:~}two{sub:~}} {Em:{e:*}one {Sub:{sub:~}two{sub:~}}{e:*}}}`)
|
|
225
|
+
|
|
226
|
+
test("Subscript (no spaces)", `
|
|
227
|
+
{P:One ~two three~}`)
|
|
228
|
+
|
|
229
|
+
test("Subscript (escapes)", `
|
|
230
|
+
{P:One {Sub:{sub:~}two{Esc:\\ }th{Esc:\\~}ree{sub:~}}}`)
|
|
231
|
+
|
|
232
|
+
test("Superscript", `
|
|
233
|
+
{P:One {Sup:{sup:^}two{sup:^}} {Em:{e:*}one {Sup:{sup:^}two{sup:^}}{e:*}}}`)
|
|
234
|
+
|
|
235
|
+
test("Superscript (no spaces)", `
|
|
236
|
+
{P:One ^two three^}`)
|
|
237
|
+
|
|
238
|
+
test("Superscript (escapes)", `
|
|
239
|
+
{P:One {Sup:{sup:^}two{Esc:\\ }th{Esc:\\^}ree{sup:^}}}`)
|
|
240
|
+
|
|
241
|
+
test("Emoji", `
|
|
242
|
+
{P:Hello {ji::smile:} {ji::100:}}`)
|
|
243
|
+
|
|
244
|
+
test("Emoji (format)", `
|
|
245
|
+
{P:Hello :smi le: :1.00: ::}`)
|
|
246
|
+
|
|
247
|
+
test("Disable syntax", `
|
|
248
|
+
{BL:{LI:{l:-} {P:List still {Em:{e:*}works{e:*}}}}}
|
|
249
|
+
|
|
250
|
+
{P:> No quote, no ^sup^}
|
|
251
|
+
|
|
252
|
+
{P:No setext either
|
|
253
|
+
===}`, parser.configure({remove: ["Superscript", "Blockquote", "SetextHeading"]}))
|
|
254
|
+
|
|
255
|
+
test("Autolink (.co.uk)", `
|
|
256
|
+
{P:{URL:www.blah.co.uk/path}}`)
|
|
257
|
+
|
|
258
|
+
test("Autolink (email .co.uk)", `
|
|
259
|
+
{P:{URL:foo@bar.co.uk}}`)
|
|
260
|
+
|
|
261
|
+
test("Autolink (http://www.foo-bar.com/)", `
|
|
262
|
+
{P:{URL:http://www.foo-bar.com/}}`)
|
|
263
|
+
|
|
264
|
+
test("Autolink (exclude underscores)", `
|
|
265
|
+
{P:http://www.foo_/ http://foo_.com}`)
|
|
266
|
+
|
|
267
|
+
test("Autolink (in image)", `
|
|
268
|
+
{P:{Im:{L:![}Link: {URL:http://foo.com/}{L:]}{L:(}{URL:x.jpg}{L:)}}}`)
|
|
269
|
+
})
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {Tree, TreeFragment} from "@lezer/common"
|
|
2
|
+
import ist from "ist"
|
|
3
|
+
import {parser} from "../dist/index.js"
|
|
4
|
+
import {compareTree} from "./compare-tree.js"
|
|
5
|
+
|
|
6
|
+
let doc1 = `
|
|
7
|
+
Header
|
|
8
|
+
---
|
|
9
|
+
One **two**
|
|
10
|
+
three *four*
|
|
11
|
+
five.
|
|
12
|
+
|
|
13
|
+
> Start of quote
|
|
14
|
+
>
|
|
15
|
+
> 1. Nested list
|
|
16
|
+
>
|
|
17
|
+
> 2. More content
|
|
18
|
+
> inside the [list][link]
|
|
19
|
+
>
|
|
20
|
+
> Continued item
|
|
21
|
+
>
|
|
22
|
+
> ~~~
|
|
23
|
+
> Block of code
|
|
24
|
+
> ~~~
|
|
25
|
+
>
|
|
26
|
+
> 3. And so on
|
|
27
|
+
|
|
28
|
+
[link]: /ref
|
|
29
|
+
[another]: /one
|
|
30
|
+
And a final paragraph.
|
|
31
|
+
***
|
|
32
|
+
The end.
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
type ChangeSpec = {from: number, to?: number, insert?: string}[]
|
|
36
|
+
|
|
37
|
+
class State {
|
|
38
|
+
constructor(readonly doc: string,
|
|
39
|
+
readonly tree: Tree,
|
|
40
|
+
readonly fragments: readonly TreeFragment[]) {}
|
|
41
|
+
|
|
42
|
+
static start(doc: string) {
|
|
43
|
+
let tree = parser.parse(doc)
|
|
44
|
+
return new State(doc, tree, TreeFragment.addTree(tree))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
update(changes: ChangeSpec, reparse = true) {
|
|
48
|
+
let changed = [], doc = this.doc, off = 0
|
|
49
|
+
for (let {from, to = from, insert = ""} of changes) {
|
|
50
|
+
doc = doc.slice(0, from) + insert + doc.slice(to)
|
|
51
|
+
changed.push({fromA: from - off, toA: to - off, fromB: from, toB: from + insert.length})
|
|
52
|
+
off += insert.length - (to - from)
|
|
53
|
+
}
|
|
54
|
+
let fragments = TreeFragment.applyChanges(this.fragments, changed, 2)
|
|
55
|
+
if (!reparse) return new State(doc, Tree.empty, fragments)
|
|
56
|
+
let tree = parser.parse(doc, fragments)
|
|
57
|
+
return new State(doc, tree, TreeFragment.addTree(tree, fragments))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let _state1: State | null = null, state1 = () => _state1 || (_state1 = State.start(doc1))
|
|
62
|
+
|
|
63
|
+
function overlap(a: Tree, b: Tree) {
|
|
64
|
+
let inA = new Set<Tree>(), shared = 0, sharingTo = 0
|
|
65
|
+
for (let cur = a.cursor(); cur.next();) if (cur.tree) inA.add(cur.tree)
|
|
66
|
+
for (let cur = b.cursor(); cur.next();) if (cur.tree && inA.has(cur.tree) && cur.type.is("Block") && cur.from >= sharingTo) {
|
|
67
|
+
shared += cur.to - cur.from
|
|
68
|
+
sharingTo = cur.to
|
|
69
|
+
}
|
|
70
|
+
return Math.round(shared * 100 / b.length)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function testChange(change: ChangeSpec, reuse = 10) {
|
|
74
|
+
let state = state1().update(change)
|
|
75
|
+
compareTree(state.tree, parser.parse(state.doc))
|
|
76
|
+
if (reuse) ist(overlap(state.tree, state1().tree), reuse, ">")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
describe("Markdown incremental parsing", () => {
|
|
80
|
+
it("can produce the proper tree", () => {
|
|
81
|
+
// Replace 'three' with 'bears'
|
|
82
|
+
let state = state1().update([{from: 24, to: 29, insert: "bears"}])
|
|
83
|
+
compareTree(state.tree, state1().tree)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it("reuses nodes from the previous parse", () => {
|
|
87
|
+
// Replace 'three' with 'bears'
|
|
88
|
+
let state = state1().update([{from: 24, to: 29, insert: "bears"}])
|
|
89
|
+
ist(overlap(state1().tree, state.tree), 80, ">")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("can reuse content for a change in a block context", () => {
|
|
93
|
+
// Replace 'content' with 'monkeys'
|
|
94
|
+
let state = state1().update([{from: 92, to: 99, insert: "monkeys"}])
|
|
95
|
+
compareTree(state.tree, state1().tree)
|
|
96
|
+
ist(overlap(state1().tree, state.tree), 20, ">")
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it("can handle deleting a quote mark", () => testChange([{from: 82, to: 83}]))
|
|
100
|
+
|
|
101
|
+
it("can handle adding to a quoted block", () => testChange([{from: 37, insert: "> "}, {from: 45, insert: "> "}]))
|
|
102
|
+
|
|
103
|
+
it("can handle a change in a post-linkref paragraph", () => testChange([{from: 249, to: 251}]))
|
|
104
|
+
|
|
105
|
+
it("can handle a change in a paragraph-adjacent linkrefs", () => testChange([{from: 230, to: 231}]))
|
|
106
|
+
|
|
107
|
+
it("can deal with multiple changes applied separately", () => {
|
|
108
|
+
let state = state1().update([{from: 190, to: 191}], false).update([{from: 30, insert: "hi\n\nyou"}])
|
|
109
|
+
compareTree(state.tree, parser.parse(state.doc))
|
|
110
|
+
ist(overlap(state.tree, state1().tree), 20, ">")
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it("works when a change happens directly after a block", () => testChange([{from: 150, to: 167}]))
|
|
114
|
+
|
|
115
|
+
it("works when a change deletes a blank line after a paragraph", () => testChange([{from: 207, to: 213}]))
|
|
116
|
+
|
|
117
|
+
it("doesn't get confused by removing paragraph-breaking markup", () => testChange([{from: 264, to: 265}]))
|
|
118
|
+
|
|
119
|
+
function r(n: number) { return Math.floor(Math.random() * n) }
|
|
120
|
+
function rStr(len: number) {
|
|
121
|
+
let result = "", chars = "\n>x-"
|
|
122
|
+
while (result.length < len) result += chars[r(chars.length)]
|
|
123
|
+
return result
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
it("survives random changes", () => {
|
|
127
|
+
for (let i = 0, l = doc1.length; i < 20; i++) {
|
|
128
|
+
let c = 1 + r(4), changes = []
|
|
129
|
+
for (let i = 0, rFrom = 0; i < c; i++) {
|
|
130
|
+
let rTo = rFrom + Math.floor((l - rFrom) / (c - i))
|
|
131
|
+
let from = rFrom + r(rTo - rFrom - 1), to = r(2) == 1 ? from : from + r(Math.min(rTo - from, 20))
|
|
132
|
+
let iR = r(3), insert = iR == 0 && from != to ? "" : iR == 1 ? "\n\n" : rStr(r(5) + 1)
|
|
133
|
+
changes.push({from, to, insert})
|
|
134
|
+
l += insert.length - (to - from)
|
|
135
|
+
rFrom = to + insert.length
|
|
136
|
+
}
|
|
137
|
+
testChange(changes, 0)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it("can handle large documents", () => {
|
|
142
|
+
let doc = doc1.repeat(50)
|
|
143
|
+
let state = State.start(doc)
|
|
144
|
+
let newState = state.update([{from: doc.length >> 1, insert: "a\n\nb"}])
|
|
145
|
+
ist(overlap(state.tree, newState.tree), 90, ">")
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("properly re-parses a continued indented code block", () => {
|
|
149
|
+
let state = State.start(`
|
|
150
|
+
One paragraph to create a bit of string length here
|
|
151
|
+
|
|
152
|
+
Code
|
|
153
|
+
Block
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
Another paragraph that is long enough to create a fragment
|
|
158
|
+
`).update([{from: 76, insert: " "}])
|
|
159
|
+
compareTree(state.tree, parser.parse(state.doc))
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("properly re-parses a continued list", () => {
|
|
163
|
+
let state = State.start(`
|
|
164
|
+
One paragraph to create a bit of string length here
|
|
165
|
+
|
|
166
|
+
* List
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
More content
|
|
171
|
+
|
|
172
|
+
Another paragraph that is long enough to create a fragment
|
|
173
|
+
`).update([{from: 65, insert: " * "}])
|
|
174
|
+
compareTree(state.tree, parser.parse(state.doc))
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it("can recover from incremental parses that stop in the middle of a list", () => {
|
|
178
|
+
let doc = `
|
|
179
|
+
1. I am a list item with ***some* emphasized
|
|
180
|
+
content inside** and the parser hopefully stops
|
|
181
|
+
parsing after me.
|
|
182
|
+
|
|
183
|
+
2. Oh no the list continues.
|
|
184
|
+
`
|
|
185
|
+
let parse = parser.startParse(doc), tree
|
|
186
|
+
parse.advance()
|
|
187
|
+
ist(parse.parsedPos, doc.length, "<")
|
|
188
|
+
parse.stopAt(parse.parsedPos)
|
|
189
|
+
while (!(tree = parse.advance())) {}
|
|
190
|
+
let state = new State(doc, tree, TreeFragment.addTree(tree)).update([])
|
|
191
|
+
ist(state.tree.topNode.lastChild!.from, 1)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it("can reuse list items", () => {
|
|
195
|
+
let start = State.start(" - List item\n".repeat(100))
|
|
196
|
+
let state = start.update([{from: 18, to: 19}])
|
|
197
|
+
ist(overlap(start.tree, state.tree), 80, ">")
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it("can reuse regular blocks before a continuable block", () => {
|
|
201
|
+
let start = State.start("A reusable paragraph\n\n".repeat(50) + "- etc\n\n")
|
|
202
|
+
let state = start.update([{from: start.doc.length, insert: "x"}])
|
|
203
|
+
ist(overlap(start.tree, state.tree), 85, ">")
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it("returns a tree starting at the first range", () => {
|
|
207
|
+
let result = parser.parse("foo\n\nbar", [], [{from: 5, to: 8}])
|
|
208
|
+
ist(result.toString(), "Document(Paragraph)")
|
|
209
|
+
ist(result.length, 3)
|
|
210
|
+
ist(result.positions[0], 0)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it("Allows gaps in the input", () => {
|
|
214
|
+
let doc = `
|
|
215
|
+
The first X *y* X<
|
|
216
|
+
|
|
217
|
+
>X paragraph.
|
|
218
|
+
|
|
219
|
+
- And *a X<*>X list*
|
|
220
|
+
`
|
|
221
|
+
let tree = parser.parse(doc, [], [{from: 0, to: 11}, {from: 12, to: 17}, {from: 23, to: 46}, {from: 51, to: 58}])
|
|
222
|
+
ist(tree.toString(),
|
|
223
|
+
"Document(Paragraph(Emphasis(EmphasisMark,EmphasisMark)),BulletList(ListItem(ListMark,Paragraph(Emphasis(EmphasisMark,EmphasisMark)))))")
|
|
224
|
+
ist(tree.length, doc.length)
|
|
225
|
+
let top = tree.topNode, first = top.firstChild!
|
|
226
|
+
ist(first.name, "Paragraph")
|
|
227
|
+
ist(first.from, 1)
|
|
228
|
+
ist(first.to, 34)
|
|
229
|
+
let last = top.lastChild!.lastChild!.lastChild!, em = last.lastChild!
|
|
230
|
+
ist(last.name, "Paragraph")
|
|
231
|
+
ist(last.from, 39)
|
|
232
|
+
ist(last.to, 57)
|
|
233
|
+
ist(em.name, "Emphasis")
|
|
234
|
+
ist(em.from, 43)
|
|
235
|
+
ist(em.to, 57)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it("can reuse nodes at the end of the document", () => {
|
|
239
|
+
let doc = `* List item
|
|
240
|
+
|
|
241
|
+
~~~js
|
|
242
|
+
function foo() {
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
~~~
|
|
246
|
+
`
|
|
247
|
+
let tree = parser.parse(doc)
|
|
248
|
+
let ins = 11
|
|
249
|
+
let doc2 = doc.slice(0, ins) + "\n* " + doc.slice(ins)
|
|
250
|
+
let fragments = TreeFragment.applyChanges(TreeFragment.addTree(tree), [{fromA: ins, toA: ins, fromB: ins, toB: ins + 3}])
|
|
251
|
+
let tree2 = parser.parse(doc2, fragments)
|
|
252
|
+
ist(tree2.topNode.lastChild!.tree, tree.topNode.lastChild!.tree)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it("places reused nodes at the right position when there are gaps before them", () => {
|
|
256
|
+
let doc = " {{}}\nb\n{{}}"
|
|
257
|
+
let ast1 = parser.parse(doc, undefined, [{from: 0, to: 1}, {from: 5, to: 8}])
|
|
258
|
+
let frag = TreeFragment.applyChanges(TreeFragment.addTree(ast1), [{fromA: 0, toA: 0, fromB: 0, toB: 1}])
|
|
259
|
+
let ast2 = parser.parse(" " + doc, frag, [{from: 0, to: 2}, {from: 6, to: 9}])
|
|
260
|
+
ist(ast2.toString(), "Document(Paragraph)")
|
|
261
|
+
let p = ast2.topNode.firstChild!
|
|
262
|
+
ist(p.from, 7)
|
|
263
|
+
ist(p.to, 8)
|
|
264
|
+
})
|
|
265
|
+
})
|