@pierre/diffs 1.3.0-beta.7 → 1.3.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeView.d.ts +86 -4
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +248 -33
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts +1 -0
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +15 -4
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts +2 -0
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +21 -6
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/FileStream.d.ts.map +1 -1
- package/dist/components/FileStream.js +2 -1
- package/dist/components/FileStream.js.map +1 -1
- package/dist/components/VirtualizedFile.d.ts.map +1 -1
- package/dist/components/VirtualizedFile.js +3 -1
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.d.ts +3 -1
- package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +14 -6
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.d.ts +2 -0
- package/dist/components/Virtualizer.d.ts.map +1 -1
- package/dist/components/Virtualizer.js +18 -4
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/editor/command.d.ts +1 -1
- package/dist/editor/command.d.ts.map +1 -1
- package/dist/editor/command.js +8 -0
- package/dist/editor/command.js.map +1 -1
- package/dist/editor/editStack.d.ts.map +1 -1
- package/dist/editor/editStack.js +1 -1
- package/dist/editor/editStack.js.map +1 -1
- package/dist/editor/editor.d.ts +8 -1
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +188 -42
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +1 -1
- package/dist/editor/lineAnnotations.d.ts.map +1 -1
- package/dist/editor/lineAnnotations.js +84 -12
- package/dist/editor/lineAnnotations.js.map +1 -1
- package/dist/editor/marker.d.ts +5 -3
- package/dist/editor/marker.d.ts.map +1 -1
- package/dist/editor/marker.js +35 -12
- package/dist/editor/marker.js.map +1 -1
- package/dist/editor/matchBrackets.d.ts +8 -0
- package/dist/editor/matchBrackets.d.ts.map +1 -0
- package/dist/editor/matchBrackets.js +146 -0
- package/dist/editor/matchBrackets.js.map +1 -0
- package/dist/editor/popover.d.ts +45 -0
- package/dist/editor/popover.d.ts.map +1 -0
- package/dist/editor/popover.js +145 -0
- package/dist/editor/popover.js.map +1 -0
- package/dist/editor/searchPanel.d.ts +1 -0
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +6 -6
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.d.ts +13 -1
- package/dist/editor/selection.d.ts.map +1 -1
- package/dist/editor/selection.js +55 -4
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/selectionAction.d.ts +20 -3
- package/dist/editor/selectionAction.d.ts.map +1 -1
- package/dist/editor/selectionAction.js +41 -10
- package/dist/editor/selectionAction.js.map +1 -1
- package/dist/editor/textDocument.d.ts +2 -0
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +22 -6
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/tokenzier.d.ts +4 -7
- package/dist/editor/tokenzier.d.ts.map +1 -1
- package/dist/editor/tokenzier.js +76 -35
- package/dist/editor/tokenzier.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -5
- package/dist/managers/ResizeManager.d.ts +10 -2
- package/dist/managers/ResizeManager.d.ts.map +1 -1
- package/dist/managers/ResizeManager.js +33 -13
- package/dist/managers/ResizeManager.js.map +1 -1
- package/dist/managers/UniversalRenderingManager.d.ts +2 -1
- package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
- package/dist/managers/UniversalRenderingManager.js +6 -1
- package/dist/managers/UniversalRenderingManager.js.map +1 -1
- package/dist/react/CodeView.d.ts +25 -1
- package/dist/react/CodeView.d.ts.map +1 -1
- package/dist/react/CodeView.js +76 -17
- package/dist/react/CodeView.js.map +1 -1
- package/dist/react/EditorContext.d.ts +2 -2
- package/dist/react/EditorContext.d.ts.map +1 -1
- package/dist/react/EditorContext.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/renderers/DiffHunksRenderer.d.ts +1 -1
- package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
- package/dist/renderers/DiffHunksRenderer.js +24 -15
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/renderers/FileRenderer.d.ts +2 -1
- package/dist/renderers/FileRenderer.d.ts.map +1 -1
- package/dist/renderers/FileRenderer.js +58 -38
- package/dist/renderers/FileRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/types.d.ts +42 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/applyLineTextWithNewline.d.ts +5 -0
- package/dist/utils/applyLineTextWithNewline.d.ts.map +1 -0
- package/dist/utils/applyLineTextWithNewline.js +11 -0
- package/dist/utils/applyLineTextWithNewline.js.map +1 -0
- package/dist/utils/areManagedSnapshotsEqual.d.ts +2 -2
- package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
- package/dist/utils/areManagedSnapshotsEqual.js +5 -0
- package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
- package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts +5 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts.map +1 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.js +19 -0
- package/dist/utils/createCodeViewHeaderFooterHostElement.js.map +1 -0
- package/dist/utils/updateDiffHunks.d.ts.map +1 -1
- package/dist/utils/updateDiffHunks.js +23 -1
- package/dist/utils/updateDiffHunks.js.map +1 -1
- package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
- package/dist/worker/WorkerPoolManager.js +4 -2
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/types.d.ts +4 -0
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js.map +1 -1
- package/package.json +4 -2
package/dist/editor/editor2.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/editor/editor.css?inline
|
|
2
|
-
var editor_default = "::selection {\n background-color: #0000;\n}\n\n[data-deleted-text-selection] [data-deletions] ::selection, [data-deleted-text-selection] [data-line-type=\"change-deletion\"] ::selection {\n background-color: var(--diffs-editor-selection-bg);\n}\n\n@keyframes blinking {\n 0% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n:host, [data-code], [data-content], [data-diff-type=\"split\"][data-overflow=\"wrap\"] {\n position: relative;\n}\n\n[data-code] {\n container-type: inline-size;\n}\n\n[data-content] {\n cursor: text;\n caret-color: var(--diffs-bg-caret);\n background-color: #0000;\n outline: none;\n}\n\n[data-line] {\n cursor: text;\n background-color: #0000;\n}\n\n[data-line]:after {\n content: \"\";\n z-index: -20;\n background-color: var(--diffs-line-bg);\n pointer-events: none;\n position: absolute;\n inset: 0;\n}\n\n[data-line][data-selected-line]:after {\n background-color: var(--diffs-computed-diff-line-bg, var(--diffs-bg));\n background-image: linear-gradient(var(--diffs-editor-line-highlight-bg),\n var(--diffs-editor-line-highlight-bg));\n}\n\n[data-editor-overlay] {\n display: contents;\n}\n\n[data-caret], [data-selection-range], [data-match-range], [data-marker-range] {\n height: 1lh;\n line-height: var(--diffs-line-height);\n pointer-events: none;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n[data-caret] {\n background-color: var(--diffs-bg-caret-override, var(--diffs-editor-cursor-fg, light-dark(color-mix(in lab, var(--diffs-fg) 50%, var(--diffs-bg)), color-mix(in lab, var(--diffs-fg) 75%, var(--diffs-bg)))));\n visibility: hidden;\n width: 2px;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n [data-caret] {\n animation: 1.2s .8s infinite blinking;\n }\n}\n\n[data-selection-range] {\n z-index: -10;\n background-color: var(--diffs-editor-selection-bg);\n}\n\n[data-selection-corner] {\n background-color: var(--diffs-selection-corner-bg, var(--diffs-bg));\n width: 100%;\n height: 100%;\n}\n\n[data-match-range] {\n z-index: -10;\n background-color: var(--diffs-editor-match-bg, var(--diffs-editor-selection-bg));\n}\n\n[data-match-range]:not([data-focus]) {\n background-color: var(--diffs-editor-match-highlight-bg, light-dark(#ff963288, #ff963266));\n}\n\n[data-rtl] {\n border-top-left-radius: 3px;\n}\n\n[data-rtr] {\n border-top-right-radius: 3px;\n}\n\n[data-rbl] {\n border-bottom-left-radius: 3px;\n}\n\n[data-rbr] {\n border-bottom-right-radius: 3px;\n}\n\n[data-marker-range] {\n z-index: 1;\n background-color: var(--diffs-marker-bg);\n mask-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2IiBoZWlnaHQ9IjMiPjxwYXRoIGQ9Im0wIDIuNSBsMiAtMS41IGwxIDAgbDIgMS41IGwxIDAiIHN0cm9rZT0iI2ZmZiIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9zdmc+\");\n mask-position: 0 100%;\n mask-size: 6px 3px;\n mask-repeat: repeat-x;\n}\n\n[data-marker-error] {\n --diffs-marker-contrast: #fff;\n --diffs-marker-bg: var(--diffs-editor-error-fg, var(--diffs-deletion-base));\n}\n\n[data-marker-warning] {\n --diffs-marker-contrast: #000;\n --diffs-marker-bg: var(--diffs-editor-warning-fg, light-dark(var(--diffs-warning-light), var(--diffs-warning-dark)));\n}\n\n[data-marker-info] {\n --diffs-marker-contrast: #fff;\n --diffs-marker-bg: var(--diffs-editor-info-fg, var(--diffs-modified-base));\n}\n\n[data-marker-hint] {\n --diffs-marker-contrast: light-dark(#fff, #000);\n --diffs-marker-bg: var(--diffs-editor-hint-fg, var(--diffs-fg-number));\n}\n\n@media (width >= 480px) {\n [data-content] {\n caret-color: #0000;\n }\n\n [data-content]:focus ~ [data-editor-overlay] [data-caret] {\n visibility: visible;\n }\n}\n\n[data-editor-widget] {\n --diffs-widget-fg: var(--diffs-fg);\n --diffs-widget-bg: color-mix(in lab, var(--diffs-fg) 4%, var(--diffs-bg));\n --diffs-widget-border: color-mix(in lab, var(--diffs-fg) 20%, transparent);\n --diffs-widget-shadow: inset 0 0 0 1px var(--diffs-bg), 0 4px 8px #00000013,\n 0 6px 18px #00000013;\n z-index: 100;\n width: fit-content;\n max-width: calc(100% - 24px);\n font: 14px/20px var(--diffs-header-font-fallback);\n border: 1px solid var(--diffs-widget-border);\n background-color: var(--diffs-widget-bg);\n box-shadow: var(--diffs-widget-shadow);\n background-clip: padding-box;\n border-radius: 9px;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n[data-marker-popup], [data-selection-action-popover] {\n --popover-min-left: calc(var(--gutter-width, 0px) + 8px);\n --popover-available-width: calc(100cqw - var(--gutter-width, 0px) - 16px);\n max-width: min(640px, var(--popover-available-width));\n translate: clamp(var(--popover-min-left),\n var(--popover-x, var(--popover-min-left)),\n calc(100cqw - 8px - 100%))\n var(--popover-y, 0);\n}\n\n[data-marker-popup] {\n --diffs-widget-bg: var(--diffs-marker-bg);\n --diffs-widget-shadow: 0 4px 8px #00000026, 0 6px 18px #00000026;\n overflow-wrap: anywhere;\n min-width: min(180px, 100cqw - 24px);\n color: var(--diffs-marker-contrast, contrast-color(#fff, #000));\n white-space: normal;\n pointer-events: auto;\n border: 0;\n padding: 8px 12px;\n\n & [data-marker-message] {\n white-space: normal;\n overflow-wrap: anywhere;\n\n & code {\n white-space: normal;\n color: inherit;\n background-color: #0000;\n display: inline;\n }\n }\n}\n\n[data-selection-action-popover] {\n pointer-events: auto;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: max-content;\n padding: 4px;\n display: flex;\n}\n\n[data-search-panel] {\n z-index: 100;\n flex-direction: column;\n justify-content: right;\n width: 100%;\n height: 0;\n display: flex;\n position: sticky;\n top: 16px;\n right: 16px;\n container: search-panel / inline-size;\n\n & [data-editor-widget] {\n z-index: 100;\n flex-shrink: 0;\n align-items: stretch;\n gap: 8px;\n min-width: 260px;\n max-width: 100%;\n margin-inline: auto 16px;\n padding: 4px;\n display: flex;\n position: relative;\n }\n\n & svg {\n fill: currentColor;\n width: 12px;\n height: 12px;\n display: block;\n }\n}\n\n[data-search-grid] {\n grid-template-columns: auto auto auto;\n grid-template-areas: \"find matches nav\"\n \"replace actions .\";\n align-items: center;\n gap: 4px 6px;\n width: 100%;\n display: grid;\n}\n\n[data-input-box][data-find] {\n grid-area: find;\n}\n\n[data-search-nav] {\n grid-area: nav;\n}\n\n[data-input-box][data-replace] {\n grid-area: replace;\n}\n\n[data-replace-actions] {\n grid-area: actions;\n}\n\n[data-search-grid][data-mode=\"find\"] {\n grid-template-areas: \"find matches nav\";\n}\n\n[data-search-grid][data-mode=\"find\"] [data-replace-cell] {\n display: none;\n}\n\n@container search-panel (width < 400px) {\n [data-search-panel] [data-editor-widget] {\n padding: 10px;\n }\n\n [data-search-grid][data-mode=\"replace\"] {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"find find find\"\n \"replace replace replace\"\n \"nav matches actions\";\n }\n\n [data-search-grid][data-mode=\"find\"] {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"find find find\"\n \"nav matches matches\";\n }\n\n [data-replace-actions] {\n justify-self: end;\n }\n\n [data-search-grid] [data-input-box] {\n width: auto;\n }\n}\n\n[data-input-box] {\n align-items: center;\n width: 200px;\n display: flex;\n position: relative;\n\n & input {\n width: 100%;\n min-width: 0;\n color: var(--diffs-fg);\n background-color: var(--diffs-bg);\n border: 1px solid color-mix(in lab, var(--diffs-fg) 12%, var(--diffs-bg));\n border-radius: 6px;\n outline: none;\n flex-grow: 1;\n padding-inline: 6px;\n font-size: 13px;\n line-height: 24px;\n\n &::selection {\n background-color: color-mix(in lab, var(--diffs-fg) 8%, var(--diffs-bg));\n }\n\n &:focus-visible {\n outline: 1px solid var(--diffs-modified-base);\n }\n }\n\n &[data-find] input {\n padding-inline-end: 72px;\n }\n\n & [data-search-icon] {\n --diffs-search-icon-size: 20px;\n }\n}\n\n[data-search-toggles] {\n align-items: center;\n gap: 1px;\n display: flex;\n position: absolute;\n top: 50%;\n right: 4px;\n transform: translateY(-50%);\n}\n\n[data-matches] {\n white-space: nowrap;\n min-width: 50px;\n color: color-mix(in lab, var(--diffs-fg) 50%, var(--diffs-bg));\n flex-shrink: 0;\n grid-area: matches;\n font-size: 12px;\n font-weight: 500;\n line-height: 20px;\n\n &[data-no-matches] {\n color: color-mix(in lab, var(--diffs-fg) 35%, var(--diffs-bg));\n }\n}\n\n[data-replace-actions], [data-search-nav] {\n align-items: center;\n display: flex;\n}\n\n[data-search-icon] {\n appearance: none;\n width: var(--diffs-search-icon-size, 24px);\n height: var(--diffs-search-icon-size, 24px);\n color: color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg));\n border-radius: var(--diffs-search-icon-radius, 4px);\n cursor: pointer;\n background: none;\n border: 0;\n flex-shrink: 0;\n justify-content: center;\n align-items: center;\n margin: 0;\n padding: 0;\n transition: background-color .1s ease-in-out, color .1s ease-in-out;\n display: flex;\n\n &:disabled {\n opacity: .25;\n cursor: default;\n }\n\n &:not(:disabled):hover {\n color: var(--diffs-fg);\n }\n\n &:focus-visible {\n outline: 1px solid var(--diffs-modified-base);\n }\n\n &[aria-pressed=\"true\"] {\n color: var(--diffs-modified-base);\n }\n}\n\n[data-search-close] {\n --diffs-search-icon-size: 20px;\n --diffs-search-icon-radius: 50%;\n color: var(--diffs-widget-bg);\n background-color: color-mix(in lab,\n var(--diffs-widget-fg) 50%,\n var(--diffs-bg));\n position: absolute;\n top: 0;\n right: 0;\n transform: translate(50%, -50%);\n\n &:not(:disabled):hover {\n opacity: 1;\n color: var(--diffs-widget-bg);\n background-color: var(--diffs-widget-fg);\n }\n}\n";
|
|
2
|
+
var editor_default = "::selection {\n background-color: #0000;\n}\n\n[data-deleted-text-selection] [data-deletions] ::selection, [data-deleted-text-selection] [data-line-type=\"change-deletion\"] ::selection {\n background-color: var(--diffs-editor-selection-bg);\n}\n\n@keyframes blinking {\n 0% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n:host, [data-code], [data-content], [data-diff-type=\"split\"][data-overflow=\"wrap\"] {\n position: relative;\n}\n\n[data-code] {\n container-type: inline-size;\n}\n\n[data-content] {\n cursor: text;\n caret-color: var(--diffs-bg-caret);\n background-color: #0000;\n outline: none;\n}\n\n[data-line] {\n cursor: text;\n background-color: #0000;\n}\n\n[data-line]:after {\n content: \"\";\n z-index: -20;\n background-color: var(--diffs-line-bg);\n pointer-events: none;\n position: absolute;\n inset: 0;\n}\n\n[data-line][data-selected-line]:after {\n background-color: var(--diffs-computed-diff-line-bg, var(--diffs-bg));\n background-image: linear-gradient(var(--diffs-editor-line-highlight-bg),\n var(--diffs-editor-line-highlight-bg));\n}\n\n[data-editor-overlay] {\n display: contents;\n}\n\n[data-caret], [data-selection-range], [data-match-range], [data-bracket-match-range], [data-marker-range] {\n height: 1lh;\n line-height: var(--diffs-line-height);\n pointer-events: none;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n[data-caret] {\n background-color: var(--diffs-bg-caret-override, var(--diffs-editor-cursor-fg, light-dark(color-mix(in lab, var(--diffs-fg) 50%, var(--diffs-bg)), color-mix(in lab, var(--diffs-fg) 75%, var(--diffs-bg)))));\n visibility: hidden;\n width: 2px;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n [data-caret] {\n animation: 1.2s .8s infinite blinking;\n }\n}\n\n[data-selection-range] {\n z-index: -10;\n background-color: var(--diffs-editor-selection-bg);\n}\n\n[data-selection-corner] {\n background-color: var(--diffs-selection-corner-bg, var(--diffs-bg));\n width: 100%;\n height: 100%;\n}\n\n[data-match-range] {\n z-index: -10;\n background-color: var(--diffs-editor-match-bg, var(--diffs-editor-selection-bg));\n}\n\n[data-match-range]:not([data-focus]) {\n background-color: var(--diffs-editor-match-highlight-bg, light-dark(#ff963288, #ff963266));\n}\n\n[data-bracket-match-range] {\n z-index: -9;\n box-sizing: border-box;\n border-bottom: 1px solid\n var(--diffs-editor-bracket-match-border, color-mix(in lab, var(--diffs-fg) 60%, transparent));\n background-color: var(--diffs-editor-bracket-match-bg, color-mix(in lab, var(--diffs-fg) 5%, transparent));\n}\n\n[data-rtl] {\n border-top-left-radius: 3px;\n}\n\n[data-rtr] {\n border-top-right-radius: 3px;\n}\n\n[data-rbl] {\n border-bottom-left-radius: 3px;\n}\n\n[data-rbr] {\n border-bottom-right-radius: 3px;\n}\n\n[data-marker-range] {\n z-index: 1;\n background-color: var(--diffs-marker-bg);\n mask-image: url(\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2IiBoZWlnaHQ9IjMiPjxwYXRoIGQ9Im0wIDIuNSBsMiAtMS41IGwxIDAgbDIgMS41IGwxIDAiIHN0cm9rZT0iI2ZmZiIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9zdmc+\");\n mask-position: 0 100%;\n mask-size: 6px 3px;\n mask-repeat: repeat-x;\n}\n\n[data-marker-error] {\n --diffs-marker-contrast: #fff;\n --diffs-marker-bg: var(--diffs-editor-error-fg, var(--diffs-deletion-base));\n}\n\n[data-marker-warning] {\n --diffs-marker-contrast: #000;\n --diffs-marker-bg: var(--diffs-editor-warning-fg, light-dark(var(--diffs-warning-light), var(--diffs-warning-dark)));\n}\n\n[data-marker-info] {\n --diffs-marker-contrast: #fff;\n --diffs-marker-bg: var(--diffs-editor-info-fg, var(--diffs-modified-base));\n}\n\n[data-marker-hint] {\n --diffs-marker-contrast: light-dark(#fff, #000);\n --diffs-marker-bg: var(--diffs-editor-hint-fg, var(--diffs-fg-number));\n}\n\n@media (width >= 480px) {\n [data-content] {\n caret-color: #0000;\n }\n\n [data-content]:focus ~ [data-editor-overlay] [data-caret] {\n visibility: visible;\n }\n}\n\n[data-editor-widget] {\n --diffs-widget-fg: var(--diffs-fg);\n --diffs-widget-bg: color-mix(in lab, var(--diffs-fg) 4%, var(--diffs-bg));\n --diffs-widget-border: color-mix(in lab, var(--diffs-fg) 20%, transparent);\n --diffs-widget-shadow: inset 0 0 0 1px var(--diffs-bg), 0 4px 8px #00000013,\n 0 6px 18px #00000013;\n z-index: 100;\n width: fit-content;\n max-width: calc(100% - 24px);\n font: 14px/20px var(--diffs-header-font-fallback);\n border: 1px solid var(--diffs-widget-border);\n background-color: var(--diffs-widget-bg);\n box-shadow: var(--diffs-widget-shadow);\n background-clip: padding-box;\n border-radius: 9px;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n[data-marker-popup], [data-selection-action-popover] {\n --popover-min-left: calc(var(--gutter-width, 0px) + 8px);\n --popover-available-width: calc(100cqw - var(--gutter-width, 0px) - 16px);\n max-width: min(640px, var(--popover-available-width));\n translate: clamp(var(--popover-min-left),\n var(--popover-x, var(--popover-min-left)),\n calc(100cqw - 8px - 100%))\n calc(var(--popover-y, 0px) + var(--popover-y-shift, 0px));\n}\n\n[data-marker-popup] {\n --diffs-widget-bg: var(--diffs-marker-bg);\n --diffs-widget-shadow: 0 4px 8px #00000026, 0 6px 18px #00000026;\n overflow-wrap: anywhere;\n min-width: min(180px, 100cqw - 24px);\n color: var(--diffs-marker-contrast, contrast-color(#fff, #000));\n white-space: normal;\n pointer-events: auto;\n border: 0;\n padding: 8px 12px;\n\n & [data-marker-message] {\n white-space: normal;\n overflow-wrap: anywhere;\n\n & code {\n white-space: normal;\n color: inherit;\n background-color: #0000;\n display: inline;\n }\n }\n}\n\n[data-selection-action-popover] {\n pointer-events: auto;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: max-content;\n padding: 4px;\n display: flex;\n}\n\n[data-search-panel] {\n z-index: 100;\n flex-direction: column;\n justify-content: right;\n width: 100%;\n height: 0;\n display: flex;\n position: sticky;\n top: 16px;\n right: 16px;\n container: search-panel / inline-size;\n\n & [data-editor-widget] {\n z-index: 100;\n flex-shrink: 0;\n align-items: stretch;\n gap: 8px;\n min-width: 260px;\n max-width: 100%;\n margin-inline: auto 16px;\n padding: 4px;\n display: flex;\n position: relative;\n }\n\n & svg {\n fill: currentColor;\n width: 12px;\n height: 12px;\n display: block;\n }\n}\n\n[data-search-grid] {\n grid-template-columns: auto auto auto;\n grid-template-areas: \"find matches nav\"\n \"replace actions .\";\n align-items: center;\n gap: 4px 6px;\n width: 100%;\n display: grid;\n}\n\n[data-input-box][data-find] {\n grid-area: find;\n}\n\n[data-search-nav] {\n grid-area: nav;\n}\n\n[data-input-box][data-replace] {\n grid-area: replace;\n}\n\n[data-replace-actions] {\n grid-area: actions;\n}\n\n[data-search-grid][data-mode=\"find\"] {\n grid-template-areas: \"find matches nav\";\n}\n\n[data-search-grid][data-mode=\"find\"] [data-replace-cell] {\n display: none;\n}\n\n@container search-panel (width < 400px) {\n [data-search-panel] [data-editor-widget] {\n padding: 10px;\n }\n\n [data-search-grid][data-mode=\"replace\"] {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"find find find\"\n \"replace replace replace\"\n \"nav matches actions\";\n }\n\n [data-search-grid][data-mode=\"find\"] {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"find find find\"\n \"nav matches matches\";\n }\n\n [data-replace-actions] {\n justify-self: end;\n }\n\n [data-search-grid] [data-input-box] {\n width: auto;\n }\n}\n\n[data-input-box] {\n align-items: center;\n width: 200px;\n display: flex;\n position: relative;\n\n & input {\n width: 100%;\n min-width: 0;\n color: var(--diffs-fg);\n background-color: var(--diffs-bg);\n border: 1px solid color-mix(in lab, var(--diffs-fg) 12%, var(--diffs-bg));\n border-radius: 6px;\n outline: none;\n flex-grow: 1;\n padding-inline: 6px;\n font-size: 13px;\n line-height: 24px;\n\n &::selection {\n background-color: color-mix(in lab, var(--diffs-fg) 8%, var(--diffs-bg));\n }\n\n &:focus-visible {\n outline: 1px solid var(--diffs-modified-base);\n }\n }\n\n &[data-find] input {\n padding-inline-end: 72px;\n }\n\n & [data-search-icon] {\n --diffs-search-icon-size: 20px;\n }\n}\n\n[data-search-toggles] {\n align-items: center;\n gap: 1px;\n display: flex;\n position: absolute;\n top: 50%;\n right: 4px;\n transform: translateY(-50%);\n}\n\n[data-matches] {\n white-space: nowrap;\n min-width: 50px;\n color: color-mix(in lab, var(--diffs-fg) 50%, var(--diffs-bg));\n flex-shrink: 0;\n grid-area: matches;\n font-size: 12px;\n font-weight: 500;\n line-height: 20px;\n\n &[data-no-matches] {\n color: color-mix(in lab, var(--diffs-fg) 35%, var(--diffs-bg));\n }\n}\n\n[data-replace-actions], [data-search-nav] {\n align-items: center;\n display: flex;\n}\n\n[data-search-icon] {\n appearance: none;\n width: var(--diffs-search-icon-size, 24px);\n height: var(--diffs-search-icon-size, 24px);\n color: color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg));\n border-radius: var(--diffs-search-icon-radius, 4px);\n cursor: pointer;\n background: none;\n border: 0;\n flex-shrink: 0;\n justify-content: center;\n align-items: center;\n margin: 0;\n padding: 0;\n transition: background-color .1s ease-in-out, color .1s ease-in-out;\n display: flex;\n\n &:disabled {\n opacity: .25;\n cursor: default;\n }\n\n &:not(:disabled):hover {\n color: var(--diffs-fg);\n }\n\n &:focus-visible {\n outline: 1px solid var(--diffs-modified-base);\n }\n\n &[aria-pressed=\"true\"] {\n color: var(--diffs-modified-base);\n }\n}\n\n[data-search-close] {\n --diffs-search-icon-size: 20px;\n --diffs-search-icon-radius: 50%;\n color: var(--diffs-widget-bg);\n background-color: color-mix(in lab,\n var(--diffs-widget-fg) 50%,\n var(--diffs-bg));\n position: absolute;\n top: 0;\n right: 0;\n transform: translate(50%, -50%);\n\n &:not(:disabled):hover {\n opacity: 1;\n color: var(--diffs-widget-bg);\n background-color: var(--diffs-widget-fg);\n }\n}\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { editor_default as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lineAnnotations.d.ts","names":[],"sources":["../../src/editor/lineAnnotations.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"lineAnnotations.d.ts","names":[],"sources":["../../src/editor/lineAnnotations.ts"],"mappings":";;;;iBAcgB,oCAAA,IACd,MAAA,EAAQ,kBAAA,EACR,eAAA,EAAiB,kBAAA,CAAmB,CAAA,MACnC,kBAAA,CAAmB,CAAA;AAAA,iBA0MN,qBAAA,cACd,eAAA,EAAiB,kBAAA,CAAmB,WAAA,KACpC,SAAA,EAAW,WAAA,EACX,QAAA,GAAW,WAAA"}
|
|
@@ -2,28 +2,39 @@ import { getLineAnnotationName } from "../utils/getLineAnnotationName.js";
|
|
|
2
2
|
import { getLineNumberAttr, h } from "./utils.js";
|
|
3
3
|
//#region src/editor/lineAnnotations.ts
|
|
4
4
|
function applyDocumentChangeToLineAnnotations(change, lineAnnotations) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const removedLineCount = Math.max(0, -change.lineDelta);
|
|
8
|
-
const deletedStartLine = removedLineCount === 0 ? void 0 : change.startLine + (startCharacter === 0 ? 0 : 1);
|
|
9
|
-
const deletedEndLine = deletedStartLine === void 0 ? void 0 : deletedStartLine + removedLineCount;
|
|
10
|
-
const shiftFromLine = removedLineCount > 0 ? change.startLine + removedLineCount : change.startLine + (startCharacter === 0 ? 0 : 1);
|
|
5
|
+
const annotationChanges = getLineAnnotationChanges(change);
|
|
6
|
+
if (annotationChanges.length === 0) return;
|
|
11
7
|
const nextLineAnnotations = [];
|
|
12
8
|
let changed = false;
|
|
13
9
|
for (const annotation of lineAnnotations) {
|
|
14
|
-
if (annotation.side === "deletions") {
|
|
10
|
+
if (annotation.side === "deletions" || annotation.lineNumber <= 0) {
|
|
15
11
|
nextLineAnnotations.push(annotation);
|
|
16
12
|
continue;
|
|
17
13
|
}
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
let line = annotation.lineNumber - 1;
|
|
15
|
+
let lineCount = change.previousLineCount;
|
|
16
|
+
let annotationChanged = false;
|
|
17
|
+
for (const lineChange of annotationChanges) {
|
|
18
|
+
const nextLineCount = Math.max(1, lineCount + lineChange.lineDelta);
|
|
19
|
+
const nextLine = mapLineThroughLineChange(line, lineChange, nextLineCount);
|
|
20
|
+
if (nextLine === void 0) {
|
|
21
|
+
annotationChanged = true;
|
|
22
|
+
line = void 0;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
if (nextLine !== line || lineChangeTouchesAnnotationLine(line, lineChange)) annotationChanged = true;
|
|
26
|
+
line = nextLine;
|
|
27
|
+
lineCount = nextLineCount;
|
|
28
|
+
}
|
|
29
|
+
if (line === void 0) {
|
|
20
30
|
changed = true;
|
|
21
31
|
continue;
|
|
22
32
|
}
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
const lineNumber = line + 1;
|
|
34
|
+
if (annotationChanged) {
|
|
35
|
+
nextLineAnnotations.push(lineNumber === annotation.lineNumber ? annotation : {
|
|
25
36
|
...annotation,
|
|
26
|
-
lineNumber
|
|
37
|
+
lineNumber
|
|
27
38
|
});
|
|
28
39
|
changed = true;
|
|
29
40
|
continue;
|
|
@@ -32,6 +43,67 @@ function applyDocumentChangeToLineAnnotations(change, lineAnnotations) {
|
|
|
32
43
|
}
|
|
33
44
|
return changed ? nextLineAnnotations : void 0;
|
|
34
45
|
}
|
|
46
|
+
function getLineAnnotationChanges(change) {
|
|
47
|
+
if (change.lineDelta === 0) {
|
|
48
|
+
if (change.changedLineChanges !== void 0) return change.changedLineChanges.flatMap(([startLine, _endLine, lineDelta]) => {
|
|
49
|
+
if (lineDelta === 0) return [];
|
|
50
|
+
return [{
|
|
51
|
+
startLine,
|
|
52
|
+
startCharacter: 0,
|
|
53
|
+
endLine: startLine + Math.max(0, -lineDelta),
|
|
54
|
+
deletesEndLine: false,
|
|
55
|
+
insertedLineBreaks: Math.max(0, lineDelta),
|
|
56
|
+
lineDelta
|
|
57
|
+
}];
|
|
58
|
+
});
|
|
59
|
+
return change.changedLineRanges.flatMap(([startLine, endLine]) => {
|
|
60
|
+
const insertedLineBreaks = endLine - startLine;
|
|
61
|
+
if (insertedLineBreaks <= 0) return [];
|
|
62
|
+
return [{
|
|
63
|
+
startLine,
|
|
64
|
+
startCharacter: 0,
|
|
65
|
+
endLine: startLine,
|
|
66
|
+
deletesEndLine: false,
|
|
67
|
+
insertedLineBreaks,
|
|
68
|
+
lineDelta: insertedLineBreaks
|
|
69
|
+
}];
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const removedLineCount = Math.max(0, -change.lineDelta);
|
|
73
|
+
const deletedToDocumentEnd = change.startLine === 0 && change.startCharacter === 0 && change.lineCount === 1 && change.lineDelta === 1 - change.previousLineCount;
|
|
74
|
+
return [{
|
|
75
|
+
startLine: change.startLine,
|
|
76
|
+
startCharacter: change.startCharacter,
|
|
77
|
+
endLine: change.startLine + removedLineCount,
|
|
78
|
+
deletesEndLine: deletedToDocumentEnd,
|
|
79
|
+
insertedLineBreaks: Math.max(0, change.lineDelta),
|
|
80
|
+
lineDelta: change.lineDelta
|
|
81
|
+
}];
|
|
82
|
+
}
|
|
83
|
+
function mapLineThroughLineChange(line, lineChange, nextLineCount) {
|
|
84
|
+
if (line < lineChange.startLine) return line;
|
|
85
|
+
if (line > lineChange.endLine || lineChange.endLine > lineChange.startLine && line === lineChange.endLine && !lineChange.deletesEndLine) return line + lineChange.lineDelta;
|
|
86
|
+
if (lineChange.startLine === lineChange.endLine) {
|
|
87
|
+
if (lineChange.startCharacter === 0) return line + lineChange.insertedLineBreaks;
|
|
88
|
+
return line;
|
|
89
|
+
}
|
|
90
|
+
if (lineChangeDeletesAnnotationLine(line, lineChange)) return;
|
|
91
|
+
const replacementLineOffset = Math.min(Math.max(0, line - lineChange.startLine), lineChange.insertedLineBreaks);
|
|
92
|
+
return clampLine(lineChange.startLine + replacementLineOffset, nextLineCount);
|
|
93
|
+
}
|
|
94
|
+
function lineChangeDeletesAnnotationLine(line, lineChange) {
|
|
95
|
+
if (lineChange.lineDelta >= 0 || line < lineChange.startLine || line > lineChange.endLine) return false;
|
|
96
|
+
if (line === lineChange.startLine && lineChange.startCharacter > 0) return false;
|
|
97
|
+
if (line === lineChange.endLine && !lineChange.deletesEndLine) return false;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function lineChangeTouchesAnnotationLine(line, lineChange) {
|
|
101
|
+
if (lineChange.lineDelta === 0 || line < lineChange.startLine || line > lineChange.endLine) return false;
|
|
102
|
+
return !(lineChange.endLine > lineChange.startLine && line === lineChange.endLine && !lineChange.deletesEndLine);
|
|
103
|
+
}
|
|
104
|
+
function clampLine(line, lineCount) {
|
|
105
|
+
return Math.max(0, Math.min(line, Math.max(0, lineCount - 1)));
|
|
106
|
+
}
|
|
35
107
|
function renderLineAnnotations(lineAnnotations, contentEl, gutterEl) {
|
|
36
108
|
const additionAnnotations = /* @__PURE__ */ new Map();
|
|
37
109
|
const deletionAnnotations = /* @__PURE__ */ new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lineAnnotations.js","names":[],"sources":["../../src/editor/lineAnnotations.ts"],"sourcesContent":["import type { DiffLineAnnotation } from '../types';\nimport { getLineAnnotationName } from '../utils/getLineAnnotationName';\nimport type { TextDocumentChange } from './textDocument';\nimport { getLineNumberAttr, h } from './utils';\n\nexport function applyDocumentChangeToLineAnnotations<T>(\n change: TextDocumentChange,\n lineAnnotations: DiffLineAnnotation<T>[]\n): DiffLineAnnotation<T>[] | undefined {\n if (change.lineDelta === 0) {\n return undefined;\n }\n\n const startCharacter = change.startCharacter;\n const removedLineCount = Math.max(0, -change.lineDelta);\n const deletedStartLine =\n removedLineCount === 0\n ? undefined\n : change.startLine + (startCharacter === 0 ? 0 : 1);\n const deletedEndLine =\n deletedStartLine === undefined\n ? undefined\n : deletedStartLine + removedLineCount;\n const shiftFromLine =\n removedLineCount > 0\n ? change.startLine + removedLineCount\n : change.startLine + (startCharacter === 0 ? 0 : 1);\n const nextLineAnnotations: DiffLineAnnotation<T>[] = [];\n\n let changed = false;\n for (const annotation of lineAnnotations) {\n if (annotation.side === 'deletions') {\n nextLineAnnotations.push(annotation);\n continue;\n }\n\n const line = annotation.lineNumber - 1;\n if (\n deletedStartLine !== undefined &&\n deletedEndLine !== undefined &&\n line >= deletedStartLine &&\n line < deletedEndLine\n ) {\n changed = true;\n continue;\n }\n\n if (line >= shiftFromLine) {\n nextLineAnnotations.push({\n ...annotation,\n lineNumber: line + change.lineDelta + 1,\n });\n changed = true;\n continue;\n }\n\n nextLineAnnotations.push(annotation);\n }\n\n return changed ? nextLineAnnotations : undefined;\n}\n\nexport function renderLineAnnotations<LAnnotation>(\n lineAnnotations: DiffLineAnnotation<LAnnotation>[],\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): void {\n const additionAnnotations = new Map<number, string[]>();\n const deletionAnnotations = new Map<number, string[]>();\n for (const annotation of lineAnnotations) {\n const lineNumber = annotation.lineNumber;\n if (!additionAnnotations.has(lineNumber)) {\n additionAnnotations.set(lineNumber, []);\n }\n if (!deletionAnnotations.has(lineNumber)) {\n deletionAnnotations.set(lineNumber, []);\n }\n const map =\n annotation.side === 'deletions'\n ? deletionAnnotations\n : additionAnnotations;\n map.get(lineNumber)!.push(getLineAnnotationName(annotation));\n }\n\n const leftCodeElement = contentEl.parentElement?.previousElementSibling;\n let leftGutterElement: HTMLElement | undefined;\n let leftContentElement: HTMLElement | undefined;\n if (\n leftCodeElement != null &&\n leftCodeElement instanceof HTMLElement &&\n leftCodeElement.dataset.deletions !== undefined\n ) {\n for (const child of leftCodeElement.children) {\n const el = child as HTMLElement;\n const { gutter, content } = el.dataset;\n if (gutter !== undefined) {\n leftGutterElement = el;\n } else if (content !== undefined) {\n leftContentElement = el;\n }\n }\n }\n\n cleanLineAnnotationElements(contentEl, gutterEl);\n if (leftContentElement !== undefined) {\n cleanLineAnnotationElements(leftContentElement, leftGutterElement);\n }\n\n const additionsAnnotationElements = createLineAnnotationElements(\n additionAnnotations,\n contentEl,\n gutterEl\n );\n if (leftContentElement === undefined) {\n return;\n }\n\n const deletionsAnnotationElements = createLineAnnotationElements(\n deletionAnnotations,\n leftContentElement,\n leftGutterElement\n );\n\n requestAnimationFrame(() => {\n syncPairedLineAnnotationHeights(\n additionAnnotations,\n deletionAnnotations,\n additionsAnnotationElements,\n deletionsAnnotationElements\n );\n });\n}\n\nfunction cleanLineAnnotationElements(\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): void {\n const staleElements: HTMLElement[] = [];\n for (let i = 1; i < contentEl.childElementCount; i++) {\n const el = contentEl.children[i] as HTMLElement;\n if (el.dataset.lineAnnotation !== undefined) {\n staleElements.push(el);\n if (gutterEl !== undefined) {\n staleElements.push(gutterEl.children[i] as HTMLElement);\n }\n }\n }\n for (const el of staleElements) {\n el.remove();\n }\n}\n\nfunction createLineAnnotationElements(\n lineAnnotations: Map<number, string[]>,\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): Map<number, HTMLElement> {\n const annotationElements = new Map<number, HTMLElement>();\n for (const el of contentEl.children) {\n const lineNumber = getLineNumberAttr(el as HTMLElement);\n if (lineNumber !== undefined) {\n const annotations = lineAnnotations.get(lineNumber);\n if (annotations !== undefined) {\n const lineIndex = lineNumber - 1;\n const annotationElement = h('div', {\n dataset: {\n lineAnnotation: '0,' + lineIndex,\n },\n children: [\n h('div', {\n dataset: 'annotationContent',\n children: annotations.map((name) => h('slot', { name })),\n }),\n ],\n });\n el.after(annotationElement);\n annotationElements.set(lineNumber, annotationElement);\n }\n }\n }\n\n if (gutterEl !== undefined) {\n for (const el of gutterEl.children) {\n const lineNumber = getLineNumberAttr(el as HTMLElement, 'columnNumber');\n if (lineNumber !== undefined && lineAnnotations.has(lineNumber)) {\n const bufferEl = h('div', {\n dataset: {\n gutterBuffer: 'annotation',\n bufferSize: '1',\n },\n style: {\n gridRow: 'span 1',\n },\n });\n el.after(bufferEl);\n }\n }\n }\n\n return annotationElements;\n}\n\nfunction syncPairedLineAnnotationHeights(\n additionAnnotations: Map<number, string[]>,\n deletionAnnotations: Map<number, string[]>,\n additionAnnotationElements: Map<number, HTMLElement>,\n deletionAnnotationElements: Map<number, HTMLElement>\n): void {\n const offsetHeights = new Map<number, number>();\n for (const [lineNumber, annotations] of additionAnnotations.entries()) {\n const annotationElement = deletionAnnotationElements.get(lineNumber);\n if (annotations.length === 0 && annotationElement !== undefined) {\n const height = measureAnnotationContentHeight(annotationElement);\n if (height > 0) {\n offsetHeights.set(lineNumber, height);\n }\n }\n }\n for (const [lineNumber, annotations] of deletionAnnotations.entries()) {\n const annotationElement = additionAnnotationElements.get(lineNumber);\n if (annotations.length === 0 && annotationElement !== undefined) {\n const height = measureAnnotationContentHeight(annotationElement);\n if (height > 0) {\n offsetHeights.set(lineNumber, height);\n }\n }\n }\n applyLineAnnotationMinHeights(\n additionAnnotations,\n additionAnnotationElements,\n offsetHeights\n );\n applyLineAnnotationMinHeights(\n deletionAnnotations,\n deletionAnnotationElements,\n offsetHeights\n );\n}\n\nfunction measureAnnotationContentHeight(lineAnnotationEl: HTMLElement): number {\n const content = lineAnnotationEl.firstElementChild;\n if (!(content instanceof HTMLElement)) {\n return 0;\n }\n return content.getBoundingClientRect().height;\n}\n\nfunction applyLineAnnotationMinHeights(\n lineAnnotations: Map<number, string[]>,\n annotationElements: Map<number, HTMLElement>,\n offsetHeights: Map<number, number>\n): void {\n for (const [lineNumber, annotationElement] of annotationElements.entries()) {\n const annotations = lineAnnotations.get(lineNumber);\n const offsetHeight = offsetHeights.get(lineNumber);\n if (annotations?.length === 0 && offsetHeight !== undefined) {\n annotationElement.style.setProperty(\n '--diffs-annotation-min-height',\n `${offsetHeight}px`\n );\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lineAnnotations.js","names":[],"sources":["../../src/editor/lineAnnotations.ts"],"sourcesContent":["import type { DiffLineAnnotation } from '../types';\nimport { getLineAnnotationName } from '../utils/getLineAnnotationName';\nimport type { TextDocumentChange } from './textDocument';\nimport { getLineNumberAttr, h } from './utils';\n\ninterface LineAnnotationChange {\n readonly startLine: number;\n readonly startCharacter: number;\n readonly endLine: number;\n readonly deletesEndLine: boolean;\n readonly insertedLineBreaks: number;\n readonly lineDelta: number;\n}\n\nexport function applyDocumentChangeToLineAnnotations<T>(\n change: TextDocumentChange,\n lineAnnotations: DiffLineAnnotation<T>[]\n): DiffLineAnnotation<T>[] | undefined {\n const annotationChanges = getLineAnnotationChanges(change);\n if (annotationChanges.length === 0) {\n return undefined;\n }\n\n const nextLineAnnotations: DiffLineAnnotation<T>[] = [];\n let changed = false;\n for (const annotation of lineAnnotations) {\n if (annotation.side === 'deletions' || annotation.lineNumber <= 0) {\n nextLineAnnotations.push(annotation);\n continue;\n }\n\n let line: number | undefined = annotation.lineNumber - 1;\n let lineCount = change.previousLineCount;\n let annotationChanged = false;\n for (const lineChange of annotationChanges) {\n const nextLineCount = Math.max(1, lineCount + lineChange.lineDelta);\n const nextLine = mapLineThroughLineChange(\n line,\n lineChange,\n nextLineCount\n );\n if (nextLine === undefined) {\n annotationChanged = true;\n line = undefined;\n break;\n }\n if (\n nextLine !== line ||\n lineChangeTouchesAnnotationLine(line, lineChange)\n ) {\n annotationChanged = true;\n }\n line = nextLine;\n lineCount = nextLineCount;\n }\n\n if (line === undefined) {\n changed = true;\n continue;\n }\n\n const lineNumber = line + 1;\n if (annotationChanged) {\n nextLineAnnotations.push(\n lineNumber === annotation.lineNumber\n ? annotation\n : {\n ...annotation,\n lineNumber,\n }\n );\n changed = true;\n continue;\n }\n\n nextLineAnnotations.push(annotation);\n }\n\n return changed ? nextLineAnnotations : undefined;\n}\n\nfunction getLineAnnotationChanges(\n change: TextDocumentChange\n): readonly LineAnnotationChange[] {\n if (change.lineDelta === 0) {\n if (change.changedLineChanges !== undefined) {\n return change.changedLineChanges.flatMap(\n ([startLine, _endLine, lineDelta]) => {\n if (lineDelta === 0) {\n return [];\n }\n const removedLineCount = Math.max(0, -lineDelta);\n return [\n {\n startLine,\n startCharacter: 0,\n endLine: startLine + removedLineCount,\n deletesEndLine: false,\n insertedLineBreaks: Math.max(0, lineDelta),\n lineDelta,\n },\n ];\n }\n );\n }\n\n return change.changedLineRanges.flatMap(([startLine, endLine]) => {\n const insertedLineBreaks = endLine - startLine;\n if (insertedLineBreaks <= 0) {\n return [];\n }\n return [\n {\n startLine,\n startCharacter: 0,\n endLine: startLine,\n deletesEndLine: false,\n insertedLineBreaks,\n lineDelta: insertedLineBreaks,\n },\n ];\n });\n }\n const removedLineCount = Math.max(0, -change.lineDelta);\n const deletedToDocumentEnd =\n change.startLine === 0 &&\n change.startCharacter === 0 &&\n change.lineCount === 1 &&\n change.lineDelta === 1 - change.previousLineCount;\n return [\n {\n startLine: change.startLine,\n startCharacter: change.startCharacter,\n endLine: change.startLine + removedLineCount,\n deletesEndLine: deletedToDocumentEnd,\n insertedLineBreaks: Math.max(0, change.lineDelta),\n lineDelta: change.lineDelta,\n },\n ];\n}\n\nfunction mapLineThroughLineChange(\n line: number,\n lineChange: LineAnnotationChange,\n nextLineCount: number\n): number | undefined {\n if (line < lineChange.startLine) {\n return line;\n }\n\n if (\n line > lineChange.endLine ||\n (lineChange.endLine > lineChange.startLine &&\n line === lineChange.endLine &&\n !lineChange.deletesEndLine)\n ) {\n return line + lineChange.lineDelta;\n }\n\n if (lineChange.startLine === lineChange.endLine) {\n if (lineChange.startCharacter === 0) {\n return line + lineChange.insertedLineBreaks;\n }\n return line;\n }\n\n if (lineChangeDeletesAnnotationLine(line, lineChange)) {\n return undefined;\n }\n\n const replacementLineOffset = Math.min(\n Math.max(0, line - lineChange.startLine),\n lineChange.insertedLineBreaks\n );\n return clampLine(lineChange.startLine + replacementLineOffset, nextLineCount);\n}\n\nfunction lineChangeDeletesAnnotationLine(\n line: number,\n lineChange: LineAnnotationChange\n): boolean {\n if (\n lineChange.lineDelta >= 0 ||\n line < lineChange.startLine ||\n line > lineChange.endLine\n ) {\n return false;\n }\n if (line === lineChange.startLine && lineChange.startCharacter > 0) {\n return false;\n }\n if (line === lineChange.endLine && !lineChange.deletesEndLine) {\n return false;\n }\n return true;\n}\n\nfunction lineChangeTouchesAnnotationLine(\n line: number,\n lineChange: LineAnnotationChange\n): boolean {\n if (\n lineChange.lineDelta === 0 ||\n line < lineChange.startLine ||\n line > lineChange.endLine\n ) {\n return false;\n }\n return !(\n lineChange.endLine > lineChange.startLine &&\n line === lineChange.endLine &&\n !lineChange.deletesEndLine\n );\n}\n\nfunction clampLine(line: number, lineCount: number): number {\n return Math.max(0, Math.min(line, Math.max(0, lineCount - 1)));\n}\n\nexport function renderLineAnnotations<LAnnotation>(\n lineAnnotations: DiffLineAnnotation<LAnnotation>[],\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): void {\n const additionAnnotations = new Map<number, string[]>();\n const deletionAnnotations = new Map<number, string[]>();\n for (const annotation of lineAnnotations) {\n const lineNumber = annotation.lineNumber;\n if (!additionAnnotations.has(lineNumber)) {\n additionAnnotations.set(lineNumber, []);\n }\n if (!deletionAnnotations.has(lineNumber)) {\n deletionAnnotations.set(lineNumber, []);\n }\n const map =\n annotation.side === 'deletions'\n ? deletionAnnotations\n : additionAnnotations;\n map.get(lineNumber)!.push(getLineAnnotationName(annotation));\n }\n\n const leftCodeElement = contentEl.parentElement?.previousElementSibling;\n let leftGutterElement: HTMLElement | undefined;\n let leftContentElement: HTMLElement | undefined;\n if (\n leftCodeElement != null &&\n leftCodeElement instanceof HTMLElement &&\n leftCodeElement.dataset.deletions !== undefined\n ) {\n for (const child of leftCodeElement.children) {\n const el = child as HTMLElement;\n const { gutter, content } = el.dataset;\n if (gutter !== undefined) {\n leftGutterElement = el;\n } else if (content !== undefined) {\n leftContentElement = el;\n }\n }\n }\n\n cleanLineAnnotationElements(contentEl, gutterEl);\n if (leftContentElement !== undefined) {\n cleanLineAnnotationElements(leftContentElement, leftGutterElement);\n }\n\n const additionsAnnotationElements = createLineAnnotationElements(\n additionAnnotations,\n contentEl,\n gutterEl\n );\n if (leftContentElement === undefined) {\n return;\n }\n\n const deletionsAnnotationElements = createLineAnnotationElements(\n deletionAnnotations,\n leftContentElement,\n leftGutterElement\n );\n\n requestAnimationFrame(() => {\n syncPairedLineAnnotationHeights(\n additionAnnotations,\n deletionAnnotations,\n additionsAnnotationElements,\n deletionsAnnotationElements\n );\n });\n}\n\nfunction cleanLineAnnotationElements(\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): void {\n const staleElements: HTMLElement[] = [];\n for (let i = 1; i < contentEl.childElementCount; i++) {\n const el = contentEl.children[i] as HTMLElement;\n if (el.dataset.lineAnnotation !== undefined) {\n staleElements.push(el);\n if (gutterEl !== undefined) {\n staleElements.push(gutterEl.children[i] as HTMLElement);\n }\n }\n }\n for (const el of staleElements) {\n el.remove();\n }\n}\n\nfunction createLineAnnotationElements(\n lineAnnotations: Map<number, string[]>,\n contentEl: HTMLElement,\n gutterEl?: HTMLElement\n): Map<number, HTMLElement> {\n const annotationElements = new Map<number, HTMLElement>();\n for (const el of contentEl.children) {\n const lineNumber = getLineNumberAttr(el as HTMLElement);\n if (lineNumber !== undefined) {\n const annotations = lineAnnotations.get(lineNumber);\n if (annotations !== undefined) {\n const lineIndex = lineNumber - 1;\n const annotationElement = h('div', {\n dataset: {\n lineAnnotation: '0,' + lineIndex,\n },\n children: [\n h('div', {\n dataset: 'annotationContent',\n children: annotations.map((name) => h('slot', { name })),\n }),\n ],\n });\n el.after(annotationElement);\n annotationElements.set(lineNumber, annotationElement);\n }\n }\n }\n\n if (gutterEl !== undefined) {\n for (const el of gutterEl.children) {\n const lineNumber = getLineNumberAttr(el as HTMLElement, 'columnNumber');\n if (lineNumber !== undefined && lineAnnotations.has(lineNumber)) {\n const bufferEl = h('div', {\n dataset: {\n gutterBuffer: 'annotation',\n bufferSize: '1',\n },\n style: {\n gridRow: 'span 1',\n },\n });\n el.after(bufferEl);\n }\n }\n }\n\n return annotationElements;\n}\n\nfunction syncPairedLineAnnotationHeights(\n additionAnnotations: Map<number, string[]>,\n deletionAnnotations: Map<number, string[]>,\n additionAnnotationElements: Map<number, HTMLElement>,\n deletionAnnotationElements: Map<number, HTMLElement>\n): void {\n const offsetHeights = new Map<number, number>();\n for (const [lineNumber, annotations] of additionAnnotations.entries()) {\n const annotationElement = deletionAnnotationElements.get(lineNumber);\n if (annotations.length === 0 && annotationElement !== undefined) {\n const height = measureAnnotationContentHeight(annotationElement);\n if (height > 0) {\n offsetHeights.set(lineNumber, height);\n }\n }\n }\n for (const [lineNumber, annotations] of deletionAnnotations.entries()) {\n const annotationElement = additionAnnotationElements.get(lineNumber);\n if (annotations.length === 0 && annotationElement !== undefined) {\n const height = measureAnnotationContentHeight(annotationElement);\n if (height > 0) {\n offsetHeights.set(lineNumber, height);\n }\n }\n }\n applyLineAnnotationMinHeights(\n additionAnnotations,\n additionAnnotationElements,\n offsetHeights\n );\n applyLineAnnotationMinHeights(\n deletionAnnotations,\n deletionAnnotationElements,\n offsetHeights\n );\n}\n\nfunction measureAnnotationContentHeight(lineAnnotationEl: HTMLElement): number {\n const content = lineAnnotationEl.firstElementChild;\n if (!(content instanceof HTMLElement)) {\n return 0;\n }\n return content.getBoundingClientRect().height;\n}\n\nfunction applyLineAnnotationMinHeights(\n lineAnnotations: Map<number, string[]>,\n annotationElements: Map<number, HTMLElement>,\n offsetHeights: Map<number, number>\n): void {\n for (const [lineNumber, annotationElement] of annotationElements.entries()) {\n const annotations = lineAnnotations.get(lineNumber);\n const offsetHeight = offsetHeights.get(lineNumber);\n if (annotations?.length === 0 && offsetHeight !== undefined) {\n annotationElement.style.setProperty(\n '--diffs-annotation-min-height',\n `${offsetHeight}px`\n );\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/editor/marker.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Range, TextDocument } from "./textDocument.js";
|
|
2
|
+
import { PopoverManager } from "./popover.js";
|
|
2
3
|
|
|
3
4
|
//#region src/editor/marker.d.ts
|
|
4
5
|
type MarkerSeverity = 'error' | 'warning' | 'info' | 'hint';
|
|
@@ -10,7 +11,8 @@ interface Marker extends Range {
|
|
|
10
11
|
source?: string;
|
|
11
12
|
metadata?: Record<string, unknown>;
|
|
12
13
|
}
|
|
13
|
-
interface
|
|
14
|
+
interface MarkerRenderOptions {
|
|
15
|
+
popoverManager: PopoverManager;
|
|
14
16
|
getLineHeight: () => number;
|
|
15
17
|
getOverlayElement: () => HTMLElement | undefined;
|
|
16
18
|
getGutterWidth: () => number;
|
|
@@ -20,7 +22,7 @@ interface EditorStub {
|
|
|
20
22
|
}
|
|
21
23
|
declare class MarkerRenderer {
|
|
22
24
|
#private;
|
|
23
|
-
constructor(editor:
|
|
25
|
+
constructor(editor: MarkerRenderOptions);
|
|
24
26
|
get markers(): readonly Marker[];
|
|
25
27
|
isPopupVisible(): boolean;
|
|
26
28
|
setMarkers<LAnnotation>(markers: Marker[], textDocument: TextDocument<LAnnotation>): void;
|
|
@@ -30,5 +32,5 @@ declare class MarkerRenderer {
|
|
|
30
32
|
}
|
|
31
33
|
declare function markerSeverityDatasetKey(severity: MarkerSeverity): string;
|
|
32
34
|
//#endregion
|
|
33
|
-
export {
|
|
35
|
+
export { Marker, MarkerRenderOptions, MarkerRenderer, MarkerSeverity, markerSeverityDatasetKey };
|
|
34
36
|
//# sourceMappingURL=marker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marker.d.ts","names":[],"sources":["../../src/editor/marker.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"marker.d.ts","names":[],"sources":["../../src/editor/marker.ts"],"mappings":";;;;KAYY,cAAA;AAAA,UAEK,MAAA,SAAe,KAAA;EAC9B,QAAA,EAAU,cAAA;EACV,OAAA;IAAoB,IAAA;EAAA,IAAiB,WAAA;EACrC,MAAA;EACA,QAAA,GAAW,MAAA;AAAA;AAAA,UAGI,mBAAA;EACf,cAAA,EAAgB,cAAA;EAChB,aAAA;EACA,iBAAA,QAAyB,WAAW;EACpC,cAAA;EACA,QAAA,GAAW,IAAA,UAAc,SAAA;EACzB,QAAA,GAAW,IAAA;EACX,WAAA;AAAA;AAAA,cAGW,cAAA;EAAA;EAcX,WAAA,CAAY,MAAA,EAAQ,mBAAA;EAAA,IAIhB,OAAA,aAAoB,MAAA;EAIxB,cAAA;EAIA,UAAA,cACE,OAAA,EAAS,MAAA,IACT,YAAA,EAAc,YAAA,CAAa,WAAA;EAW7B,WAAA,CAAY,SAAA,EAAW,WAAA;EAgCvB,WAAA;EAMA,OAAA;AAAA;AAAA,iBAoQc,wBAAA,CAAyB,QAAwB,EAAd,cAAc"}
|
package/dist/editor/marker.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { addEventListener, getLineNumberAttr, h } from "./utils.js";
|
|
2
|
+
import "./popover.js";
|
|
2
3
|
import { selectionIntersects } from "./selection.js";
|
|
3
4
|
//#region src/editor/marker.ts
|
|
4
5
|
const MARKER_POPUP_SHOW_DELAY_MS = 300;
|
|
5
6
|
const MARKER_POPUP_HIDE_DELAY_MS = 100;
|
|
6
7
|
var MarkerRenderer = class {
|
|
7
|
-
#
|
|
8
|
+
#options;
|
|
8
9
|
#markers = [];
|
|
10
|
+
#lineCount = 0;
|
|
9
11
|
#markerPopupElement;
|
|
10
12
|
#markerPopupEventDisposes;
|
|
11
13
|
#markerEventDisposes;
|
|
@@ -15,7 +17,7 @@ var MarkerRenderer = class {
|
|
|
15
17
|
#hoveredMarkerIndex;
|
|
16
18
|
#isMarkerPopupHovered = false;
|
|
17
19
|
constructor(editor) {
|
|
18
|
-
this.#
|
|
20
|
+
this.#options = editor;
|
|
19
21
|
}
|
|
20
22
|
get markers() {
|
|
21
23
|
return this.#markers;
|
|
@@ -24,6 +26,7 @@ var MarkerRenderer = class {
|
|
|
24
26
|
return this.#hoveredMarkerIndex !== void 0;
|
|
25
27
|
}
|
|
26
28
|
setMarkers(markers, textDocument) {
|
|
29
|
+
this.#lineCount = textDocument.lineCount;
|
|
27
30
|
this.#markers = markers.map((marker) => ({
|
|
28
31
|
...marker,
|
|
29
32
|
start: textDocument.normalizePosition(marker.start),
|
|
@@ -36,7 +39,7 @@ var MarkerRenderer = class {
|
|
|
36
39
|
this.#markerEventDisposes = void 0;
|
|
37
40
|
if (this.#markers.length === 0) return;
|
|
38
41
|
this.#markerEventDisposes = [addEventListener(contentEl, "mouseover", (e) => {
|
|
39
|
-
if (this.#
|
|
42
|
+
if (this.#options.isMouseDown()) return;
|
|
40
43
|
const target = e.composedPath()[0];
|
|
41
44
|
if (target === void 0) return;
|
|
42
45
|
const hoverMarkerIndex = this.#findHoveredMarkerIndex(target);
|
|
@@ -122,10 +125,34 @@ var MarkerRenderer = class {
|
|
|
122
125
|
if (!this.#isMarkerPopupHovered) this.removePopup();
|
|
123
126
|
}, MARKER_POPUP_HIDE_DELAY_MS);
|
|
124
127
|
}
|
|
125
|
-
#setMarkerPopupPosition(popup, x, y) {
|
|
126
|
-
popup.style.setProperty("--gutter-width", this.#
|
|
128
|
+
#setMarkerPopupPosition(popup, x, y, placeAbove) {
|
|
129
|
+
popup.style.setProperty("--gutter-width", this.#options.getGutterWidth() + "px");
|
|
127
130
|
popup.style.setProperty("--popover-x", x + "px");
|
|
128
131
|
popup.style.setProperty("--popover-y", y + "px");
|
|
132
|
+
popup.style.setProperty("--popover-y-shift", placeAbove ? "-100%" : "0px");
|
|
133
|
+
}
|
|
134
|
+
#positionMarkerPopup(popup, line, character) {
|
|
135
|
+
const { getCharX, getLineY, getLineHeight, popoverManager } = this.#options;
|
|
136
|
+
const [left, wrapLine] = getCharX(line, character);
|
|
137
|
+
const lineHeight = getLineHeight();
|
|
138
|
+
const rowTop = getLineY(line) + wrapLine * lineHeight;
|
|
139
|
+
const popoverHeight = popup.offsetHeight;
|
|
140
|
+
const preferred = {
|
|
141
|
+
top: rowTop + lineHeight,
|
|
142
|
+
bottom: rowTop + lineHeight + popoverHeight
|
|
143
|
+
};
|
|
144
|
+
const fallback = {
|
|
145
|
+
top: rowTop - popoverHeight,
|
|
146
|
+
bottom: rowTop
|
|
147
|
+
};
|
|
148
|
+
const atDocumentEdge = line >= this.#lineCount - 3;
|
|
149
|
+
const viewport = popoverManager.getPlacementBounds();
|
|
150
|
+
let placeAbove;
|
|
151
|
+
if (viewport !== void 0 && popoverHeight > 0) {
|
|
152
|
+
const fits = (bounds) => bounds.top >= viewport.top && bounds.bottom <= viewport.bottom;
|
|
153
|
+
placeAbove = !fits(preferred) && fits(fallback);
|
|
154
|
+
} else placeAbove = atDocumentEdge;
|
|
155
|
+
this.#setMarkerPopupPosition(popup, left, placeAbove ? rowTop : rowTop + lineHeight, placeAbove);
|
|
129
156
|
}
|
|
130
157
|
#dismissMarkerPopup() {
|
|
131
158
|
this.#markerPopupEventDisposes?.forEach((dispose) => dispose());
|
|
@@ -137,22 +164,18 @@ var MarkerRenderer = class {
|
|
|
137
164
|
}
|
|
138
165
|
#renderMarkerPopup(hoveredMarkerIndex) {
|
|
139
166
|
if (hoveredMarkerIndex === this.#hoveredMarkerIndex) return;
|
|
140
|
-
const overlayElement = this.#
|
|
167
|
+
const overlayElement = this.#options.getOverlayElement();
|
|
141
168
|
if (hoveredMarkerIndex >= this.#markers.length || overlayElement == null) return;
|
|
142
169
|
const { start, message, severity } = this.#markers[hoveredMarkerIndex];
|
|
143
170
|
const { line, character } = start;
|
|
144
|
-
const { getCharX, getLineY, getLineHeight } = this.#editor;
|
|
145
|
-
const [left, wrapLine] = getCharX(line, character);
|
|
146
|
-
const lineHeight = getLineHeight();
|
|
147
|
-
const y = getLineY(line) + wrapLine * lineHeight + lineHeight;
|
|
148
171
|
const popup = this.#markerPopupElement;
|
|
149
172
|
if (popup !== void 0) {
|
|
150
|
-
this.#setMarkerPopupPosition(popup, left, y);
|
|
151
173
|
setMarkerPopupSeverity(popup, severity);
|
|
152
174
|
const content = popup.firstElementChild;
|
|
153
175
|
if (content?.dataset.markerMessage !== void 0) if (typeof message === "string") content.textContent = message;
|
|
154
176
|
else if (message instanceof HTMLElement) content.replaceChildren(message);
|
|
155
177
|
else content.innerHTML = message.html;
|
|
178
|
+
this.#positionMarkerPopup(popup, line, character);
|
|
156
179
|
this.#hoveredMarkerIndex = hoveredMarkerIndex;
|
|
157
180
|
return;
|
|
158
181
|
}
|
|
@@ -167,7 +190,7 @@ var MarkerRenderer = class {
|
|
|
167
190
|
...typeof message === "string" ? { textContent: message } : message instanceof HTMLElement ? { children: [message] } : { innerHTML: message.html }
|
|
168
191
|
})]
|
|
169
192
|
}, overlayElement);
|
|
170
|
-
this.#
|
|
193
|
+
this.#positionMarkerPopup(this.#markerPopupElement, line, character);
|
|
171
194
|
this.#hoveredMarkerIndex = hoveredMarkerIndex;
|
|
172
195
|
this.#markerPopupEventDisposes = [addEventListener(this.#markerPopupElement, "mouseenter", () => {
|
|
173
196
|
this.#isMarkerPopupHovered = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marker.js","names":["#editor","#markers","#hoveredMarkerIndex","#markerEventDisposes","#findHoveredMarkerIndex","#scheduleMarkerPopup","#cancelMarkerPopupShow","#scheduleMarkerPopupHide","#cancelMarkerPopupHide","#dismissMarkerPopup","#markerPopupShowTimeout","#pendingMarkerPopupIndex","#markerPopupHideTimeout","#markerPopupElement","#renderMarkerPopup","#isMarkerPopupHovered","#markerPopupEventDisposes","#setMarkerPopupPosition"],"sources":["../../src/editor/marker.ts"],"sourcesContent":["import { selectionIntersects } from './selection';\nimport type { Position, Range, TextDocument } from './textDocument';\nimport { addEventListener, getLineNumberAttr, h } from './utils';\n\nconst MARKER_POPUP_SHOW_DELAY_MS = 300;\nconst MARKER_POPUP_HIDE_DELAY_MS = 100;\n\nexport type MarkerSeverity = 'error' | 'warning' | 'info' | 'hint';\n\nexport interface Marker extends Range {\n severity: MarkerSeverity;\n message: string | { html: string } | HTMLElement;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface EditorStub {\n getLineHeight: () => number;\n getOverlayElement: () => HTMLElement | undefined;\n getGutterWidth: () => number;\n getCharX: (line: number, character: number) => [number, number];\n getLineY: (line: number) => number;\n isMouseDown: () => boolean;\n}\n\nexport class MarkerRenderer {\n #editor: EditorStub;\n #markers: Marker[] = [];\n #markerPopupElement?: HTMLElement;\n #markerPopupEventDisposes?: (() => void)[];\n #markerEventDisposes?: (() => void)[];\n #markerPopupShowTimeout?: ReturnType<typeof setTimeout>;\n #markerPopupHideTimeout?: ReturnType<typeof setTimeout>;\n #pendingMarkerPopupIndex?: number;\n #hoveredMarkerIndex?: number;\n #isMarkerPopupHovered = false;\n\n constructor(editor: EditorStub) {\n this.#editor = editor;\n }\n\n get markers(): readonly Marker[] {\n return this.#markers;\n }\n\n isPopupVisible(): boolean {\n return this.#hoveredMarkerIndex !== undefined;\n }\n\n setMarkers<LAnnotation>(\n markers: Marker[],\n textDocument: TextDocument<LAnnotation>\n ): void {\n this.#markers = markers.map((marker) => ({\n ...marker,\n start: textDocument.normalizePosition(marker.start),\n end: textDocument.normalizePosition(marker.end),\n }));\n this.removePopup();\n }\n\n listenHover(contentEl: HTMLElement): void {\n this.#markerEventDisposes?.forEach((dispose) => dispose());\n this.#markerEventDisposes = undefined;\n if (this.#markers.length === 0) {\n return;\n }\n\n this.#markerEventDisposes = [\n addEventListener(contentEl, 'mouseover', (e) => {\n if (this.#editor.isMouseDown()) {\n return;\n }\n const target = e.composedPath()[0] as HTMLElement | undefined;\n if (target === undefined) {\n return;\n }\n\n const hoverMarkerIndex = this.#findHoveredMarkerIndex(target);\n if (hoverMarkerIndex !== undefined) {\n this.#scheduleMarkerPopup(hoverMarkerIndex);\n } else {\n this.#cancelMarkerPopupShow();\n this.#scheduleMarkerPopupHide();\n }\n }),\n addEventListener(contentEl, 'mouseleave', () => {\n this.#cancelMarkerPopupShow();\n this.#scheduleMarkerPopupHide();\n }),\n ];\n }\n\n removePopup(): void {\n this.#cancelMarkerPopupShow();\n this.#cancelMarkerPopupHide();\n this.#dismissMarkerPopup();\n }\n\n cleanup(): void {\n this.#markerEventDisposes?.forEach((dispose) => dispose());\n this.#markerEventDisposes = undefined;\n this.removePopup();\n this.#markers = [];\n }\n\n #findHoveredMarkerIndex(target: HTMLElement): number | undefined {\n const lineElement = target.closest('[data-line]');\n if (lineElement == null) {\n return;\n }\n\n const lineNumber = getLineNumberAttr(lineElement as HTMLElement);\n if (lineNumber === undefined) {\n return;\n }\n\n let character: number | undefined;\n if (target.tagName === 'SPAN') {\n const char = target.dataset.char;\n if (char === undefined) {\n return;\n }\n character = parseInt(char, 10);\n if (Number.isNaN(character)) {\n return;\n }\n } else if (target.tagName === 'BR') {\n character = 0;\n } else {\n return;\n }\n\n const position: Position = { line: lineNumber - 1, character };\n for (let i = this.#markers.length - 1; i >= 0; i--) {\n if (\n selectionIntersects(\n { start: position, end: position },\n this.#markers[i]\n )\n ) {\n return i;\n }\n }\n return undefined;\n }\n\n #cancelMarkerPopupShow(): void {\n if (this.#markerPopupShowTimeout !== undefined) {\n clearTimeout(this.#markerPopupShowTimeout);\n this.#markerPopupShowTimeout = undefined;\n }\n this.#pendingMarkerPopupIndex = undefined;\n }\n\n #cancelMarkerPopupHide(): void {\n if (this.#markerPopupHideTimeout !== undefined) {\n clearTimeout(this.#markerPopupHideTimeout);\n this.#markerPopupHideTimeout = undefined;\n }\n }\n\n #scheduleMarkerPopup(markerIndex: number): void {\n if (\n markerIndex === this.#hoveredMarkerIndex ||\n markerIndex === this.#pendingMarkerPopupIndex\n ) {\n this.#cancelMarkerPopupHide();\n return;\n }\n\n this.#cancelMarkerPopupShow();\n this.#cancelMarkerPopupHide();\n if (this.#markerPopupElement !== undefined) {\n this.#renderMarkerPopup(markerIndex);\n return;\n }\n\n this.#pendingMarkerPopupIndex = markerIndex;\n this.#markerPopupShowTimeout = setTimeout(() => {\n this.#markerPopupShowTimeout = undefined;\n this.#pendingMarkerPopupIndex = undefined;\n this.#renderMarkerPopup(markerIndex);\n }, MARKER_POPUP_SHOW_DELAY_MS);\n }\n\n #scheduleMarkerPopupHide(): void {\n if (this.#isMarkerPopupHovered) {\n return;\n }\n\n this.#cancelMarkerPopupHide();\n this.#markerPopupHideTimeout = setTimeout(() => {\n this.#markerPopupHideTimeout = undefined;\n if (!this.#isMarkerPopupHovered) {\n this.removePopup();\n }\n }, MARKER_POPUP_HIDE_DELAY_MS);\n }\n\n // Positions the popup in overlay coordinate space and feeds the current gutter\n // width to CSS so the shared popover rule can keep the popup clear of the\n // line-number gutter (see [data-marker-popup] in editor.css).\n #setMarkerPopupPosition(popup: HTMLElement, x: number, y: number): void {\n popup.style.setProperty(\n '--gutter-width',\n this.#editor.getGutterWidth() + 'px'\n );\n popup.style.setProperty('--popover-x', x + 'px');\n popup.style.setProperty('--popover-y', y + 'px');\n }\n\n #dismissMarkerPopup(): void {\n this.#markerPopupEventDisposes?.forEach((dispose) => dispose());\n this.#markerPopupEventDisposes = undefined;\n this.#markerPopupElement?.remove();\n this.#markerPopupElement = undefined;\n this.#hoveredMarkerIndex = undefined;\n this.#isMarkerPopupHovered = false;\n }\n\n #renderMarkerPopup(hoveredMarkerIndex: number): void {\n if (hoveredMarkerIndex === this.#hoveredMarkerIndex) {\n return;\n }\n\n const overlayElement = this.#editor.getOverlayElement();\n if (hoveredMarkerIndex >= this.#markers.length || overlayElement == null) {\n return;\n }\n\n const { start, message, severity } = this.#markers[hoveredMarkerIndex];\n const { line, character } = start;\n const { getCharX, getLineY, getLineHeight } = this.#editor;\n const [left, wrapLine] = getCharX(line, character);\n const lineHeight = getLineHeight();\n const y = getLineY(line) + wrapLine * lineHeight + lineHeight;\n const popup = this.#markerPopupElement;\n\n if (popup !== undefined) {\n this.#setMarkerPopupPosition(popup, left, y);\n setMarkerPopupSeverity(popup, severity);\n const content = popup.firstElementChild as HTMLElement | null;\n if (content?.dataset.markerMessage !== undefined) {\n if (typeof message === 'string') {\n content.textContent = message;\n } else if (message instanceof HTMLElement) {\n content.replaceChildren(message);\n } else {\n content.innerHTML = message.html;\n }\n }\n this.#hoveredMarkerIndex = hoveredMarkerIndex;\n return;\n }\n\n this.#markerPopupElement = h(\n 'div',\n {\n dataset: {\n editorWidget: '',\n markerPopup: '',\n [markerSeverityDatasetKey(severity)]: '',\n },\n children: [\n h('div', {\n dataset: 'markerMessage',\n ...(typeof message === 'string'\n ? { textContent: message }\n : message instanceof HTMLElement\n ? { children: [message] }\n : { innerHTML: message.html }),\n }),\n ],\n },\n overlayElement\n );\n this.#setMarkerPopupPosition(this.#markerPopupElement, left, y);\n this.#hoveredMarkerIndex = hoveredMarkerIndex;\n this.#markerPopupEventDisposes = [\n addEventListener(this.#markerPopupElement, 'mouseenter', () => {\n this.#isMarkerPopupHovered = true;\n this.#cancelMarkerPopupHide();\n }),\n addEventListener(this.#markerPopupElement, 'mouseleave', () => {\n this.#isMarkerPopupHovered = false;\n this.#scheduleMarkerPopupHide();\n }),\n ];\n }\n}\n\nconst MARKER_SEVERITIES: readonly MarkerSeverity[] = [\n 'error',\n 'warning',\n 'info',\n 'hint',\n];\n\n// Marks the popup with data-marker-<severity> (the same boolean attribute the\n// squiggle range element uses) so the CSS severity palette can target both with\n// a single selector. The popup element is reused across hovers, so any\n// previously applied severity attribute is cleared first.\nfunction setMarkerPopupSeverity(\n popup: HTMLElement,\n severity: MarkerSeverity\n): void {\n for (const candidate of MARKER_SEVERITIES) {\n delete popup.dataset[markerSeverityDatasetKey(candidate)];\n }\n popup.dataset[markerSeverityDatasetKey(severity)] = '';\n}\n\nexport function markerSeverityDatasetKey(severity: MarkerSeverity): string {\n switch (severity) {\n case 'error':\n return 'markerError';\n case 'warning':\n return 'markerWarning';\n case 'info':\n return 'markerInfo';\n case 'hint':\n return 'markerHint';\n }\n}\n"],"mappings":";;;AAIA,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AAoBnC,IAAa,iBAAb,MAA4B;CAC1B;CACA,WAAqB,CAAC;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA,wBAAwB;CAExB,YAAY,QAAoB;EAC9B,KAAKA,UAAU;CACjB;CAEA,IAAI,UAA6B;EAC/B,OAAO,KAAKC;CACd;CAEA,iBAA0B;EACxB,OAAO,KAAKC,wBAAwB,KAAA;CACtC;CAEA,WACE,SACA,cACM;EACN,KAAKD,WAAW,QAAQ,KAAK,YAAY;GACvC,GAAG;GACH,OAAO,aAAa,kBAAkB,OAAO,KAAK;GAClD,KAAK,aAAa,kBAAkB,OAAO,GAAG;EAChD,EAAE;EACF,KAAK,YAAY;CACnB;CAEA,YAAY,WAA8B;EACxC,KAAKE,sBAAsB,SAAS,YAAY,QAAQ,CAAC;EACzD,KAAKA,uBAAuB,KAAA;EAC5B,IAAI,KAAKF,SAAS,WAAW,GAC3B;EAGF,KAAKE,uBAAuB,CAC1B,iBAAiB,WAAW,cAAc,MAAM;GAC9C,IAAI,KAAKH,QAAQ,YAAY,GAC3B;GAEF,MAAM,SAAS,EAAE,aAAa,CAAC,CAAC;GAChC,IAAI,WAAW,KAAA,GACb;GAGF,MAAM,mBAAmB,KAAKI,wBAAwB,MAAM;GAC5D,IAAI,qBAAqB,KAAA,GACvB,KAAKC,qBAAqB,gBAAgB;QACrC;IACL,KAAKC,uBAAuB;IAC5B,KAAKC,yBAAyB;GAChC;EACF,CAAC,GACD,iBAAiB,WAAW,oBAAoB;GAC9C,KAAKD,uBAAuB;GAC5B,KAAKC,yBAAyB;EAChC,CAAC,CACH;CACF;CAEA,cAAoB;EAClB,KAAKD,uBAAuB;EAC5B,KAAKE,uBAAuB;EAC5B,KAAKC,oBAAoB;CAC3B;CAEA,UAAgB;EACd,KAAKN,sBAAsB,SAAS,YAAY,QAAQ,CAAC;EACzD,KAAKA,uBAAuB,KAAA;EAC5B,KAAK,YAAY;EACjB,KAAKF,WAAW,CAAC;CACnB;CAEA,wBAAwB,QAAyC;EAC/D,MAAM,cAAc,OAAO,QAAQ,aAAa;EAChD,IAAI,eAAe,MACjB;EAGF,MAAM,aAAa,kBAAkB,WAA0B;EAC/D,IAAI,eAAe,KAAA,GACjB;EAGF,IAAI;EACJ,IAAI,OAAO,YAAY,QAAQ;GAC7B,MAAM,OAAO,OAAO,QAAQ;GAC5B,IAAI,SAAS,KAAA,GACX;GAEF,YAAY,SAAS,MAAM,EAAE;GAC7B,IAAI,OAAO,MAAM,SAAS,GACxB;EAEJ,OAAO,IAAI,OAAO,YAAY,MAC5B,YAAY;OAEZ;EAGF,MAAM,WAAqB;GAAE,MAAM,aAAa;GAAG;EAAU;EAC7D,KAAK,IAAI,IAAI,KAAKA,SAAS,SAAS,GAAG,KAAK,GAAG,KAC7C,IACE,oBACE;GAAE,OAAO;GAAU,KAAK;EAAS,GACjC,KAAKA,SAAS,EAChB,GAEA,OAAO;CAIb;CAEA,yBAA+B;EAC7B,IAAI,KAAKS,4BAA4B,KAAA,GAAW;GAC9C,aAAa,KAAKA,uBAAuB;GACzC,KAAKA,0BAA0B,KAAA;EACjC;EACA,KAAKC,2BAA2B,KAAA;CAClC;CAEA,yBAA+B;EAC7B,IAAI,KAAKC,4BAA4B,KAAA,GAAW;GAC9C,aAAa,KAAKA,uBAAuB;GACzC,KAAKA,0BAA0B,KAAA;EACjC;CACF;CAEA,qBAAqB,aAA2B;EAC9C,IACE,gBAAgB,KAAKV,uBACrB,gBAAgB,KAAKS,0BACrB;GACA,KAAKH,uBAAuB;GAC5B;EACF;EAEA,KAAKF,uBAAuB;EAC5B,KAAKE,uBAAuB;EAC5B,IAAI,KAAKK,wBAAwB,KAAA,GAAW;GAC1C,KAAKC,mBAAmB,WAAW;GACnC;EACF;EAEA,KAAKH,2BAA2B;EAChC,KAAKD,0BAA0B,iBAAiB;GAC9C,KAAKA,0BAA0B,KAAA;GAC/B,KAAKC,2BAA2B,KAAA;GAChC,KAAKG,mBAAmB,WAAW;EACrC,GAAG,0BAA0B;CAC/B;CAEA,2BAAiC;EAC/B,IAAI,KAAKC,uBACP;EAGF,KAAKP,uBAAuB;EAC5B,KAAKI,0BAA0B,iBAAiB;GAC9C,KAAKA,0BAA0B,KAAA;GAC/B,IAAI,CAAC,KAAKG,uBACR,KAAK,YAAY;EAErB,GAAG,0BAA0B;CAC/B;CAKA,wBAAwB,OAAoB,GAAW,GAAiB;EACtE,MAAM,MAAM,YACV,kBACA,KAAKf,QAAQ,eAAe,IAAI,IAClC;EACA,MAAM,MAAM,YAAY,eAAe,IAAI,IAAI;EAC/C,MAAM,MAAM,YAAY,eAAe,IAAI,IAAI;CACjD;CAEA,sBAA4B;EAC1B,KAAKgB,2BAA2B,SAAS,YAAY,QAAQ,CAAC;EAC9D,KAAKA,4BAA4B,KAAA;EACjC,KAAKH,qBAAqB,OAAO;EACjC,KAAKA,sBAAsB,KAAA;EAC3B,KAAKX,sBAAsB,KAAA;EAC3B,KAAKa,wBAAwB;CAC/B;CAEA,mBAAmB,oBAAkC;EACnD,IAAI,uBAAuB,KAAKb,qBAC9B;EAGF,MAAM,iBAAiB,KAAKF,QAAQ,kBAAkB;EACtD,IAAI,sBAAsB,KAAKC,SAAS,UAAU,kBAAkB,MAClE;EAGF,MAAM,EAAE,OAAO,SAAS,aAAa,KAAKA,SAAS;EACnD,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,EAAE,UAAU,UAAU,kBAAkB,KAAKD;EACnD,MAAM,CAAC,MAAM,YAAY,SAAS,MAAM,SAAS;EACjD,MAAM,aAAa,cAAc;EACjC,MAAM,IAAI,SAAS,IAAI,IAAI,WAAW,aAAa;EACnD,MAAM,QAAQ,KAAKa;EAEnB,IAAI,UAAU,KAAA,GAAW;GACvB,KAAKI,wBAAwB,OAAO,MAAM,CAAC;GAC3C,uBAAuB,OAAO,QAAQ;GACtC,MAAM,UAAU,MAAM;GACtB,IAAI,SAAS,QAAQ,kBAAkB,KAAA,GACrC,IAAI,OAAO,YAAY,UACrB,QAAQ,cAAc;QACjB,IAAI,mBAAmB,aAC5B,QAAQ,gBAAgB,OAAO;QAE/B,QAAQ,YAAY,QAAQ;GAGhC,KAAKf,sBAAsB;GAC3B;EACF;EAEA,KAAKW,sBAAsB,EACzB,OACA;GACE,SAAS;IACP,cAAc;IACd,aAAa;KACZ,yBAAyB,QAAQ,IAAI;GACxC;GACA,UAAU,CACR,EAAE,OAAO;IACP,SAAS;IACT,GAAI,OAAO,YAAY,WACnB,EAAE,aAAa,QAAQ,IACvB,mBAAmB,cACjB,EAAE,UAAU,CAAC,OAAO,EAAE,IACtB,EAAE,WAAW,QAAQ,KAAK;GAClC,CAAC,CACH;EACF,GACA,cACF;EACA,KAAKI,wBAAwB,KAAKJ,qBAAqB,MAAM,CAAC;EAC9D,KAAKX,sBAAsB;EAC3B,KAAKc,4BAA4B,CAC/B,iBAAiB,KAAKH,qBAAqB,oBAAoB;GAC7D,KAAKE,wBAAwB;GAC7B,KAAKP,uBAAuB;EAC9B,CAAC,GACD,iBAAiB,KAAKK,qBAAqB,oBAAoB;GAC7D,KAAKE,wBAAwB;GAC7B,KAAKR,yBAAyB;EAChC,CAAC,CACH;CACF;AACF;AAEA,MAAM,oBAA+C;CACnD;CACA;CACA;CACA;AACF;AAMA,SAAS,uBACP,OACA,UACM;CACN,KAAK,MAAM,aAAa,mBACtB,OAAO,MAAM,QAAQ,yBAAyB,SAAS;CAEzD,MAAM,QAAQ,yBAAyB,QAAQ,KAAK;AACtD;AAEA,SAAgB,yBAAyB,UAAkC;CACzE,QAAQ,UAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;CACX;AACF"}
|
|
1
|
+
{"version":3,"file":"marker.js","names":["#options","#markers","#hoveredMarkerIndex","#lineCount","#markerEventDisposes","#findHoveredMarkerIndex","#scheduleMarkerPopup","#cancelMarkerPopupShow","#scheduleMarkerPopupHide","#cancelMarkerPopupHide","#dismissMarkerPopup","#markerPopupShowTimeout","#pendingMarkerPopupIndex","#markerPopupHideTimeout","#markerPopupElement","#renderMarkerPopup","#isMarkerPopupHovered","#setMarkerPopupPosition","#markerPopupEventDisposes","#positionMarkerPopup"],"sources":["../../src/editor/marker.ts"],"sourcesContent":["import {\n POPOVER_BOUNDARY_LINES,\n type PopoverManager,\n type PopoverPlacementBounds,\n} from './popover';\nimport { selectionIntersects } from './selection';\nimport type { Position, Range, TextDocument } from './textDocument';\nimport { addEventListener, getLineNumberAttr, h } from './utils';\n\nconst MARKER_POPUP_SHOW_DELAY_MS = 300;\nconst MARKER_POPUP_HIDE_DELAY_MS = 100;\n\nexport type MarkerSeverity = 'error' | 'warning' | 'info' | 'hint';\n\nexport interface Marker extends Range {\n severity: MarkerSeverity;\n message: string | { html: string } | HTMLElement;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MarkerRenderOptions {\n popoverManager: PopoverManager;\n getLineHeight: () => number;\n getOverlayElement: () => HTMLElement | undefined;\n getGutterWidth: () => number;\n getCharX: (line: number, character: number) => [number, number];\n getLineY: (line: number) => number;\n isMouseDown: () => boolean;\n}\n\nexport class MarkerRenderer {\n #options: MarkerRenderOptions;\n #markers: Marker[] = [];\n // Document line count, used only by the no-viewport document-edge fallback.\n #lineCount = 0;\n #markerPopupElement?: HTMLElement;\n #markerPopupEventDisposes?: (() => void)[];\n #markerEventDisposes?: (() => void)[];\n #markerPopupShowTimeout?: ReturnType<typeof setTimeout>;\n #markerPopupHideTimeout?: ReturnType<typeof setTimeout>;\n #pendingMarkerPopupIndex?: number;\n #hoveredMarkerIndex?: number;\n #isMarkerPopupHovered = false;\n\n constructor(editor: MarkerRenderOptions) {\n this.#options = editor;\n }\n\n get markers(): readonly Marker[] {\n return this.#markers;\n }\n\n isPopupVisible(): boolean {\n return this.#hoveredMarkerIndex !== undefined;\n }\n\n setMarkers<LAnnotation>(\n markers: Marker[],\n textDocument: TextDocument<LAnnotation>\n ): void {\n this.#lineCount = textDocument.lineCount;\n this.#markers = markers.map((marker) => ({\n ...marker,\n start: textDocument.normalizePosition(marker.start),\n end: textDocument.normalizePosition(marker.end),\n }));\n this.removePopup();\n }\n\n listenHover(contentEl: HTMLElement): void {\n this.#markerEventDisposes?.forEach((dispose) => dispose());\n this.#markerEventDisposes = undefined;\n if (this.#markers.length === 0) {\n return;\n }\n\n this.#markerEventDisposes = [\n addEventListener(contentEl, 'mouseover', (e) => {\n if (this.#options.isMouseDown()) {\n return;\n }\n const target = e.composedPath()[0] as HTMLElement | undefined;\n if (target === undefined) {\n return;\n }\n\n const hoverMarkerIndex = this.#findHoveredMarkerIndex(target);\n if (hoverMarkerIndex !== undefined) {\n this.#scheduleMarkerPopup(hoverMarkerIndex);\n } else {\n this.#cancelMarkerPopupShow();\n this.#scheduleMarkerPopupHide();\n }\n }),\n addEventListener(contentEl, 'mouseleave', () => {\n this.#cancelMarkerPopupShow();\n this.#scheduleMarkerPopupHide();\n }),\n ];\n }\n\n removePopup(): void {\n this.#cancelMarkerPopupShow();\n this.#cancelMarkerPopupHide();\n this.#dismissMarkerPopup();\n }\n\n cleanup(): void {\n this.#markerEventDisposes?.forEach((dispose) => dispose());\n this.#markerEventDisposes = undefined;\n this.removePopup();\n this.#markers = [];\n }\n\n #findHoveredMarkerIndex(target: HTMLElement): number | undefined {\n const lineElement = target.closest('[data-line]');\n if (lineElement == null) {\n return;\n }\n\n const lineNumber = getLineNumberAttr(lineElement as HTMLElement);\n if (lineNumber === undefined) {\n return;\n }\n\n let character: number | undefined;\n if (target.tagName === 'SPAN') {\n const char = target.dataset.char;\n if (char === undefined) {\n return;\n }\n character = parseInt(char, 10);\n if (Number.isNaN(character)) {\n return;\n }\n } else if (target.tagName === 'BR') {\n character = 0;\n } else {\n return;\n }\n\n const position: Position = { line: lineNumber - 1, character };\n for (let i = this.#markers.length - 1; i >= 0; i--) {\n if (\n selectionIntersects(\n { start: position, end: position },\n this.#markers[i]\n )\n ) {\n return i;\n }\n }\n return undefined;\n }\n\n #cancelMarkerPopupShow(): void {\n if (this.#markerPopupShowTimeout !== undefined) {\n clearTimeout(this.#markerPopupShowTimeout);\n this.#markerPopupShowTimeout = undefined;\n }\n this.#pendingMarkerPopupIndex = undefined;\n }\n\n #cancelMarkerPopupHide(): void {\n if (this.#markerPopupHideTimeout !== undefined) {\n clearTimeout(this.#markerPopupHideTimeout);\n this.#markerPopupHideTimeout = undefined;\n }\n }\n\n #scheduleMarkerPopup(markerIndex: number): void {\n if (\n markerIndex === this.#hoveredMarkerIndex ||\n markerIndex === this.#pendingMarkerPopupIndex\n ) {\n this.#cancelMarkerPopupHide();\n return;\n }\n\n this.#cancelMarkerPopupShow();\n this.#cancelMarkerPopupHide();\n if (this.#markerPopupElement !== undefined) {\n this.#renderMarkerPopup(markerIndex);\n return;\n }\n\n this.#pendingMarkerPopupIndex = markerIndex;\n this.#markerPopupShowTimeout = setTimeout(() => {\n this.#markerPopupShowTimeout = undefined;\n this.#pendingMarkerPopupIndex = undefined;\n this.#renderMarkerPopup(markerIndex);\n }, MARKER_POPUP_SHOW_DELAY_MS);\n }\n\n #scheduleMarkerPopupHide(): void {\n if (this.#isMarkerPopupHovered) {\n return;\n }\n\n this.#cancelMarkerPopupHide();\n this.#markerPopupHideTimeout = setTimeout(() => {\n this.#markerPopupHideTimeout = undefined;\n if (!this.#isMarkerPopupHovered) {\n this.removePopup();\n }\n }, MARKER_POPUP_HIDE_DELAY_MS);\n }\n\n // Positions the popup in overlay coordinate space (see [data-marker-popup]\n // in editor.css). When `placeAbove` is true, `y` is the top edge of the\n // marker's row and the popup is shifted up by its own height.\n #setMarkerPopupPosition(\n popup: HTMLElement,\n x: number,\n y: number,\n placeAbove: boolean\n ): void {\n popup.style.setProperty(\n '--gutter-width',\n this.#options.getGutterWidth() + 'px'\n );\n popup.style.setProperty('--popover-x', x + 'px');\n popup.style.setProperty('--popover-y', y + 'px');\n popup.style.setProperty('--popover-y-shift', placeAbove ? '-100%' : '0px');\n }\n\n // Positions the popup for a marker at `(line, character)`: prefers below\n // (the default), flipping above when below would be clipped by the visible\n // scrollport. Must run after the popup's content is final, since it reads\n // the popup's rendered height to decide whether either side fits.\n #positionMarkerPopup(\n popup: HTMLElement,\n line: number,\n character: number\n ): void {\n const { getCharX, getLineY, getLineHeight, popoverManager } = this.#options;\n const [left, wrapLine] = getCharX(line, character);\n const lineHeight = getLineHeight();\n const rowTop = getLineY(line) + wrapLine * lineHeight;\n const popoverHeight = popup.offsetHeight;\n\n const preferred: PopoverPlacementBounds = {\n top: rowTop + lineHeight,\n bottom: rowTop + lineHeight + popoverHeight,\n };\n const fallback: PopoverPlacementBounds = {\n top: rowTop - popoverHeight,\n bottom: rowTop,\n };\n const atDocumentEdge = line >= this.#lineCount - POPOVER_BOUNDARY_LINES;\n const viewport = popoverManager.getPlacementBounds();\n let placeAbove: boolean;\n if (viewport !== undefined && popoverHeight > 0) {\n const fits = (bounds: PopoverPlacementBounds): boolean =>\n bounds.top >= viewport.top && bounds.bottom <= viewport.bottom;\n placeAbove = !fits(preferred) && fits(fallback);\n } else {\n placeAbove = atDocumentEdge;\n }\n\n this.#setMarkerPopupPosition(\n popup,\n left,\n placeAbove ? rowTop : rowTop + lineHeight,\n placeAbove\n );\n }\n\n #dismissMarkerPopup(): void {\n this.#markerPopupEventDisposes?.forEach((dispose) => dispose());\n this.#markerPopupEventDisposes = undefined;\n this.#markerPopupElement?.remove();\n this.#markerPopupElement = undefined;\n this.#hoveredMarkerIndex = undefined;\n this.#isMarkerPopupHovered = false;\n }\n\n #renderMarkerPopup(hoveredMarkerIndex: number): void {\n if (hoveredMarkerIndex === this.#hoveredMarkerIndex) {\n return;\n }\n\n const overlayElement = this.#options.getOverlayElement();\n if (hoveredMarkerIndex >= this.#markers.length || overlayElement == null) {\n return;\n }\n\n const { start, message, severity } = this.#markers[hoveredMarkerIndex];\n const { line, character } = start;\n const popup = this.#markerPopupElement;\n\n if (popup !== undefined) {\n setMarkerPopupSeverity(popup, severity);\n const content = popup.firstElementChild as HTMLElement | null;\n if (content?.dataset.markerMessage !== undefined) {\n if (typeof message === 'string') {\n content.textContent = message;\n } else if (message instanceof HTMLElement) {\n content.replaceChildren(message);\n } else {\n content.innerHTML = message.html;\n }\n }\n // Position after updating content: a different message size changes\n // the offsetHeight #positionMarkerPopup reads to decide placement.\n this.#positionMarkerPopup(popup, line, character);\n this.#hoveredMarkerIndex = hoveredMarkerIndex;\n return;\n }\n\n this.#markerPopupElement = h(\n 'div',\n {\n dataset: {\n editorWidget: '',\n markerPopup: '',\n [markerSeverityDatasetKey(severity)]: '',\n },\n children: [\n h('div', {\n dataset: 'markerMessage',\n ...(typeof message === 'string'\n ? { textContent: message }\n : message instanceof HTMLElement\n ? { children: [message] }\n : { innerHTML: message.html }),\n }),\n ],\n },\n overlayElement\n );\n this.#positionMarkerPopup(this.#markerPopupElement, line, character);\n this.#hoveredMarkerIndex = hoveredMarkerIndex;\n this.#markerPopupEventDisposes = [\n addEventListener(this.#markerPopupElement, 'mouseenter', () => {\n this.#isMarkerPopupHovered = true;\n this.#cancelMarkerPopupHide();\n }),\n addEventListener(this.#markerPopupElement, 'mouseleave', () => {\n this.#isMarkerPopupHovered = false;\n this.#scheduleMarkerPopupHide();\n }),\n ];\n }\n}\n\nconst MARKER_SEVERITIES: readonly MarkerSeverity[] = [\n 'error',\n 'warning',\n 'info',\n 'hint',\n];\n\n// Marks the popup with data-marker-<severity> (the same boolean attribute the\n// squiggle range element uses) so the CSS severity palette can target both with\n// a single selector. The popup element is reused across hovers, so any\n// previously applied severity attribute is cleared first.\nfunction setMarkerPopupSeverity(\n popup: HTMLElement,\n severity: MarkerSeverity\n): void {\n for (const candidate of MARKER_SEVERITIES) {\n delete popup.dataset[markerSeverityDatasetKey(candidate)];\n }\n popup.dataset[markerSeverityDatasetKey(severity)] = '';\n}\n\nexport function markerSeverityDatasetKey(severity: MarkerSeverity): string {\n switch (severity) {\n case 'error':\n return 'markerError';\n case 'warning':\n return 'markerWarning';\n case 'info':\n return 'markerInfo';\n case 'hint':\n return 'markerHint';\n }\n}\n"],"mappings":";;;;AASA,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AAqBnC,IAAa,iBAAb,MAA4B;CAC1B;CACA,WAAqB,CAAC;CAEtB,aAAa;CACb;CACA;CACA;CACA;CACA;CACA;CACA;CACA,wBAAwB;CAExB,YAAY,QAA6B;EACvC,KAAKA,WAAW;CAClB;CAEA,IAAI,UAA6B;EAC/B,OAAO,KAAKC;CACd;CAEA,iBAA0B;EACxB,OAAO,KAAKC,wBAAwB,KAAA;CACtC;CAEA,WACE,SACA,cACM;EACN,KAAKC,aAAa,aAAa;EAC/B,KAAKF,WAAW,QAAQ,KAAK,YAAY;GACvC,GAAG;GACH,OAAO,aAAa,kBAAkB,OAAO,KAAK;GAClD,KAAK,aAAa,kBAAkB,OAAO,GAAG;EAChD,EAAE;EACF,KAAK,YAAY;CACnB;CAEA,YAAY,WAA8B;EACxC,KAAKG,sBAAsB,SAAS,YAAY,QAAQ,CAAC;EACzD,KAAKA,uBAAuB,KAAA;EAC5B,IAAI,KAAKH,SAAS,WAAW,GAC3B;EAGF,KAAKG,uBAAuB,CAC1B,iBAAiB,WAAW,cAAc,MAAM;GAC9C,IAAI,KAAKJ,SAAS,YAAY,GAC5B;GAEF,MAAM,SAAS,EAAE,aAAa,CAAC,CAAC;GAChC,IAAI,WAAW,KAAA,GACb;GAGF,MAAM,mBAAmB,KAAKK,wBAAwB,MAAM;GAC5D,IAAI,qBAAqB,KAAA,GACvB,KAAKC,qBAAqB,gBAAgB;QACrC;IACL,KAAKC,uBAAuB;IAC5B,KAAKC,yBAAyB;GAChC;EACF,CAAC,GACD,iBAAiB,WAAW,oBAAoB;GAC9C,KAAKD,uBAAuB;GAC5B,KAAKC,yBAAyB;EAChC,CAAC,CACH;CACF;CAEA,cAAoB;EAClB,KAAKD,uBAAuB;EAC5B,KAAKE,uBAAuB;EAC5B,KAAKC,oBAAoB;CAC3B;CAEA,UAAgB;EACd,KAAKN,sBAAsB,SAAS,YAAY,QAAQ,CAAC;EACzD,KAAKA,uBAAuB,KAAA;EAC5B,KAAK,YAAY;EACjB,KAAKH,WAAW,CAAC;CACnB;CAEA,wBAAwB,QAAyC;EAC/D,MAAM,cAAc,OAAO,QAAQ,aAAa;EAChD,IAAI,eAAe,MACjB;EAGF,MAAM,aAAa,kBAAkB,WAA0B;EAC/D,IAAI,eAAe,KAAA,GACjB;EAGF,IAAI;EACJ,IAAI,OAAO,YAAY,QAAQ;GAC7B,MAAM,OAAO,OAAO,QAAQ;GAC5B,IAAI,SAAS,KAAA,GACX;GAEF,YAAY,SAAS,MAAM,EAAE;GAC7B,IAAI,OAAO,MAAM,SAAS,GACxB;EAEJ,OAAO,IAAI,OAAO,YAAY,MAC5B,YAAY;OAEZ;EAGF,MAAM,WAAqB;GAAE,MAAM,aAAa;GAAG;EAAU;EAC7D,KAAK,IAAI,IAAI,KAAKA,SAAS,SAAS,GAAG,KAAK,GAAG,KAC7C,IACE,oBACE;GAAE,OAAO;GAAU,KAAK;EAAS,GACjC,KAAKA,SAAS,EAChB,GAEA,OAAO;CAIb;CAEA,yBAA+B;EAC7B,IAAI,KAAKU,4BAA4B,KAAA,GAAW;GAC9C,aAAa,KAAKA,uBAAuB;GACzC,KAAKA,0BAA0B,KAAA;EACjC;EACA,KAAKC,2BAA2B,KAAA;CAClC;CAEA,yBAA+B;EAC7B,IAAI,KAAKC,4BAA4B,KAAA,GAAW;GAC9C,aAAa,KAAKA,uBAAuB;GACzC,KAAKA,0BAA0B,KAAA;EACjC;CACF;CAEA,qBAAqB,aAA2B;EAC9C,IACE,gBAAgB,KAAKX,uBACrB,gBAAgB,KAAKU,0BACrB;GACA,KAAKH,uBAAuB;GAC5B;EACF;EAEA,KAAKF,uBAAuB;EAC5B,KAAKE,uBAAuB;EAC5B,IAAI,KAAKK,wBAAwB,KAAA,GAAW;GAC1C,KAAKC,mBAAmB,WAAW;GACnC;EACF;EAEA,KAAKH,2BAA2B;EAChC,KAAKD,0BAA0B,iBAAiB;GAC9C,KAAKA,0BAA0B,KAAA;GAC/B,KAAKC,2BAA2B,KAAA;GAChC,KAAKG,mBAAmB,WAAW;EACrC,GAAG,0BAA0B;CAC/B;CAEA,2BAAiC;EAC/B,IAAI,KAAKC,uBACP;EAGF,KAAKP,uBAAuB;EAC5B,KAAKI,0BAA0B,iBAAiB;GAC9C,KAAKA,0BAA0B,KAAA;GAC/B,IAAI,CAAC,KAAKG,uBACR,KAAK,YAAY;EAErB,GAAG,0BAA0B;CAC/B;CAKA,wBACE,OACA,GACA,GACA,YACM;EACN,MAAM,MAAM,YACV,kBACA,KAAKhB,SAAS,eAAe,IAAI,IACnC;EACA,MAAM,MAAM,YAAY,eAAe,IAAI,IAAI;EAC/C,MAAM,MAAM,YAAY,eAAe,IAAI,IAAI;EAC/C,MAAM,MAAM,YAAY,qBAAqB,aAAa,UAAU,KAAK;CAC3E;CAMA,qBACE,OACA,MACA,WACM;EACN,MAAM,EAAE,UAAU,UAAU,eAAe,mBAAmB,KAAKA;EACnE,MAAM,CAAC,MAAM,YAAY,SAAS,MAAM,SAAS;EACjD,MAAM,aAAa,cAAc;EACjC,MAAM,SAAS,SAAS,IAAI,IAAI,WAAW;EAC3C,MAAM,gBAAgB,MAAM;EAE5B,MAAM,YAAoC;GACxC,KAAK,SAAS;GACd,QAAQ,SAAS,aAAa;EAChC;EACA,MAAM,WAAmC;GACvC,KAAK,SAAS;GACd,QAAQ;EACV;EACA,MAAM,iBAAiB,QAAQ,KAAKG,aAAAA;EACpC,MAAM,WAAW,eAAe,mBAAmB;EACnD,IAAI;EACJ,IAAI,aAAa,KAAA,KAAa,gBAAgB,GAAG;GAC/C,MAAM,QAAQ,WACZ,OAAO,OAAO,SAAS,OAAO,OAAO,UAAU,SAAS;GAC1D,aAAa,CAAC,KAAK,SAAS,KAAK,KAAK,QAAQ;EAChD,OACE,aAAa;EAGf,KAAKc,wBACH,OACA,MACA,aAAa,SAAS,SAAS,YAC/B,UACF;CACF;CAEA,sBAA4B;EAC1B,KAAKC,2BAA2B,SAAS,YAAY,QAAQ,CAAC;EAC9D,KAAKA,4BAA4B,KAAA;EACjC,KAAKJ,qBAAqB,OAAO;EACjC,KAAKA,sBAAsB,KAAA;EAC3B,KAAKZ,sBAAsB,KAAA;EAC3B,KAAKc,wBAAwB;CAC/B;CAEA,mBAAmB,oBAAkC;EACnD,IAAI,uBAAuB,KAAKd,qBAC9B;EAGF,MAAM,iBAAiB,KAAKF,SAAS,kBAAkB;EACvD,IAAI,sBAAsB,KAAKC,SAAS,UAAU,kBAAkB,MAClE;EAGF,MAAM,EAAE,OAAO,SAAS,aAAa,KAAKA,SAAS;EACnD,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,QAAQ,KAAKa;EAEnB,IAAI,UAAU,KAAA,GAAW;GACvB,uBAAuB,OAAO,QAAQ;GACtC,MAAM,UAAU,MAAM;GACtB,IAAI,SAAS,QAAQ,kBAAkB,KAAA,GACrC,IAAI,OAAO,YAAY,UACrB,QAAQ,cAAc;QACjB,IAAI,mBAAmB,aAC5B,QAAQ,gBAAgB,OAAO;QAE/B,QAAQ,YAAY,QAAQ;GAKhC,KAAKK,qBAAqB,OAAO,MAAM,SAAS;GAChD,KAAKjB,sBAAsB;GAC3B;EACF;EAEA,KAAKY,sBAAsB,EACzB,OACA;GACE,SAAS;IACP,cAAc;IACd,aAAa;KACZ,yBAAyB,QAAQ,IAAI;GACxC;GACA,UAAU,CACR,EAAE,OAAO;IACP,SAAS;IACT,GAAI,OAAO,YAAY,WACnB,EAAE,aAAa,QAAQ,IACvB,mBAAmB,cACjB,EAAE,UAAU,CAAC,OAAO,EAAE,IACtB,EAAE,WAAW,QAAQ,KAAK;GAClC,CAAC,CACH;EACF,GACA,cACF;EACA,KAAKK,qBAAqB,KAAKL,qBAAqB,MAAM,SAAS;EACnE,KAAKZ,sBAAsB;EAC3B,KAAKgB,4BAA4B,CAC/B,iBAAiB,KAAKJ,qBAAqB,oBAAoB;GAC7D,KAAKE,wBAAwB;GAC7B,KAAKP,uBAAuB;EAC9B,CAAC,GACD,iBAAiB,KAAKK,qBAAqB,oBAAoB;GAC7D,KAAKE,wBAAwB;GAC7B,KAAKR,yBAAyB;EAChC,CAAC,CACH;CACF;AACF;AAEA,MAAM,oBAA+C;CACnD;CACA;CACA;CACA;AACF;AAMA,SAAS,uBACP,OACA,UACM;CACN,KAAK,MAAM,aAAa,mBACtB,OAAO,MAAM,QAAQ,yBAAyB,SAAS;CAEzD,MAAM,QAAQ,yBAAyB,QAAQ,KAAK;AACtD;AAEA,SAAgB,yBAAyB,UAAkC;CACzE,QAAQ,UAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;CACX;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Position, Range, TextDocument } from "./textDocument.js";
|
|
2
|
+
import { EditorTokenizer } from "./tokenzier.js";
|
|
3
|
+
|
|
4
|
+
//#region src/editor/matchBrackets.d.ts
|
|
5
|
+
declare function findBracketMatchRanges<LAnnotation>(textDocument: TextDocument<LAnnotation>, tokenizer: EditorTokenizer, position: Position): [open: Range, close: Range] | undefined;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { findBracketMatchRanges };
|
|
8
|
+
//# sourceMappingURL=matchBrackets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchBrackets.d.ts","names":[],"sources":["../../src/editor/matchBrackets.ts"],"mappings":";;;;iBAoBgB,sBAAA,cACd,YAAA,EAAc,YAAA,CAAa,WAAA,GAC3B,SAAA,EAAW,eAAA,EACX,QAAA,EAAU,QAAA,IACR,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,KAAA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//#region src/editor/matchBrackets.ts
|
|
2
|
+
const OPEN_BRACKETS = /* @__PURE__ */ new Map([
|
|
3
|
+
["(", ")"],
|
|
4
|
+
["[", "]"],
|
|
5
|
+
["{", "}"]
|
|
6
|
+
]);
|
|
7
|
+
const CLOSE_BRACKETS = new Map([...OPEN_BRACKETS].map(([open, close]) => [close, open]));
|
|
8
|
+
const MAX_BRACKET_SCAN_LINES = 1e3;
|
|
9
|
+
const MAX_BRACKET_SCAN_CHARACTERS = 5e4;
|
|
10
|
+
function findBracketMatchRanges(textDocument, tokenizer, position) {
|
|
11
|
+
const bracketPosition = findAdjacentBracket(textDocument, tokenizer, textDocument.normalizePosition(position));
|
|
12
|
+
if (bracketPosition === void 0) return;
|
|
13
|
+
const closingBracket = OPEN_BRACKETS.get(bracketPosition.char);
|
|
14
|
+
const openingBracket = CLOSE_BRACKETS.get(bracketPosition.char);
|
|
15
|
+
if (closingBracket !== void 0) return createBracketMatchRanges(bracketPosition, findClosingBracket(textDocument, tokenizer, bracketPosition, closingBracket));
|
|
16
|
+
if (openingBracket !== void 0) return createBracketMatchRanges(findOpeningBracket(textDocument, tokenizer, bracketPosition, openingBracket), bracketPosition);
|
|
17
|
+
}
|
|
18
|
+
function findAdjacentBracket(textDocument, tokenizer, position) {
|
|
19
|
+
const previousPosition = getPreviousCharacterPosition(textDocument, position);
|
|
20
|
+
if (previousPosition !== void 0) {
|
|
21
|
+
const previousBracket = getBracketAtPosition(textDocument, tokenizer, previousPosition);
|
|
22
|
+
if (previousBracket !== void 0) return previousBracket;
|
|
23
|
+
}
|
|
24
|
+
return getBracketAtPosition(textDocument, tokenizer, position);
|
|
25
|
+
}
|
|
26
|
+
function getPreviousCharacterPosition(textDocument, position) {
|
|
27
|
+
if (position.character > 0) return {
|
|
28
|
+
line: position.line,
|
|
29
|
+
character: position.character - 1
|
|
30
|
+
};
|
|
31
|
+
if (position.line <= 0) return;
|
|
32
|
+
const previousLine = position.line - 1;
|
|
33
|
+
const previousLineLength = textDocument.getLineText(previousLine).length;
|
|
34
|
+
if (previousLineLength === 0) return;
|
|
35
|
+
return {
|
|
36
|
+
line: previousLine,
|
|
37
|
+
character: previousLineLength - 1
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getBracketAtPosition(textDocument, tokenizer, position) {
|
|
41
|
+
const char = textDocument.getLineText(position.line)[position.character];
|
|
42
|
+
if (char === void 0 || !OPEN_BRACKETS.has(char) && !CLOSE_BRACKETS.has(char) || isInIgnoredTokenRange(tokenizer, position)) return;
|
|
43
|
+
return {
|
|
44
|
+
...position,
|
|
45
|
+
char
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function findClosingBracket(textDocument, tokenizer, bracketPosition, closingBracket) {
|
|
49
|
+
let depth = 0;
|
|
50
|
+
let scannedLines = 0;
|
|
51
|
+
let scannedCharacters = 0;
|
|
52
|
+
for (let line = bracketPosition.line; line < textDocument.lineCount; line++) {
|
|
53
|
+
if (scannedLines >= MAX_BRACKET_SCAN_LINES) return;
|
|
54
|
+
scannedLines++;
|
|
55
|
+
const lineText = textDocument.getLineText(line);
|
|
56
|
+
const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);
|
|
57
|
+
const ignoredRangeCursor = { index: 0 };
|
|
58
|
+
const startCharacter = line === bracketPosition.line ? bracketPosition.character : 0;
|
|
59
|
+
for (let character = startCharacter; character < lineText.length; character++) {
|
|
60
|
+
if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) return;
|
|
61
|
+
scannedCharacters++;
|
|
62
|
+
if (isCharacterIgnoredForward(ignoredRanges, character, ignoredRangeCursor)) continue;
|
|
63
|
+
const char = lineText[character];
|
|
64
|
+
if (char === bracketPosition.char) depth++;
|
|
65
|
+
else if (char === closingBracket) {
|
|
66
|
+
depth--;
|
|
67
|
+
if (depth === 0) return {
|
|
68
|
+
line,
|
|
69
|
+
character,
|
|
70
|
+
char
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function findOpeningBracket(textDocument, tokenizer, bracketPosition, openingBracket) {
|
|
77
|
+
let depth = 0;
|
|
78
|
+
let scannedLines = 0;
|
|
79
|
+
let scannedCharacters = 0;
|
|
80
|
+
for (let line = bracketPosition.line; line >= 0; line--) {
|
|
81
|
+
if (scannedLines >= MAX_BRACKET_SCAN_LINES) return;
|
|
82
|
+
scannedLines++;
|
|
83
|
+
const lineText = textDocument.getLineText(line);
|
|
84
|
+
const ignoredRanges = tokenizer.getStringCommentRegexpRangesInLine(line);
|
|
85
|
+
const ignoredRangeCursor = { index: ignoredRanges === null ? -1 : ignoredRanges.length - 1 };
|
|
86
|
+
const startCharacter = line === bracketPosition.line ? bracketPosition.character : lineText.length - 1;
|
|
87
|
+
for (let character = startCharacter; character >= 0; character--) {
|
|
88
|
+
if (scannedCharacters >= MAX_BRACKET_SCAN_CHARACTERS) return;
|
|
89
|
+
scannedCharacters++;
|
|
90
|
+
if (isCharacterIgnoredBackward(ignoredRanges, character, ignoredRangeCursor)) continue;
|
|
91
|
+
const char = lineText[character];
|
|
92
|
+
if (char === bracketPosition.char) depth++;
|
|
93
|
+
else if (char === openingBracket) {
|
|
94
|
+
depth--;
|
|
95
|
+
if (depth === 0) return {
|
|
96
|
+
line,
|
|
97
|
+
character,
|
|
98
|
+
char
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isInIgnoredTokenRange(tokenizer, position) {
|
|
105
|
+
return isCharacterInIgnoredRanges(tokenizer.getStringCommentRegexpRangesInLine(position.line), position.character);
|
|
106
|
+
}
|
|
107
|
+
function isCharacterInIgnoredRanges(ranges, character) {
|
|
108
|
+
if (ranges === null) return false;
|
|
109
|
+
for (const [start, end] of ranges) {
|
|
110
|
+
if (character < start) return false;
|
|
111
|
+
if (character < end) return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
function isCharacterIgnoredForward(ranges, character, cursor) {
|
|
116
|
+
if (ranges === null) return false;
|
|
117
|
+
while (cursor.index < ranges.length && character >= ranges[cursor.index][1]) cursor.index++;
|
|
118
|
+
const range = ranges[cursor.index];
|
|
119
|
+
return range !== void 0 && character >= range[0];
|
|
120
|
+
}
|
|
121
|
+
function isCharacterIgnoredBackward(ranges, character, cursor) {
|
|
122
|
+
if (ranges === null) return false;
|
|
123
|
+
while (cursor.index >= 0 && character < ranges[cursor.index][0]) cursor.index--;
|
|
124
|
+
const range = ranges[cursor.index];
|
|
125
|
+
return range !== void 0 && character < range[1];
|
|
126
|
+
}
|
|
127
|
+
function createBracketMatchRanges(firstPosition, secondPosition) {
|
|
128
|
+
if (firstPosition === void 0 || secondPosition === void 0) return;
|
|
129
|
+
return [createCharacterRange(firstPosition), createCharacterRange(secondPosition)];
|
|
130
|
+
}
|
|
131
|
+
function createCharacterRange(position) {
|
|
132
|
+
return {
|
|
133
|
+
start: {
|
|
134
|
+
line: position.line,
|
|
135
|
+
character: position.character
|
|
136
|
+
},
|
|
137
|
+
end: {
|
|
138
|
+
line: position.line,
|
|
139
|
+
character: position.character + 1
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
export { findBracketMatchRanges };
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=matchBrackets.js.map
|