From e4b7349815b1444dc4cf48d6f57ac1cdd9dcd673 Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 18 Feb 2025 11:17:07 +0530 Subject: [PATCH 1/2] chore: visually show adjucent line git marks for add/del/change as a single scroll line --- src/search/FindReplace.js | 12 +- src/search/ScrollTrackMarkers.js | 238 +++++++++++++++------ test/spec/ScrollTrackHandler-integ-test.js | 186 ++++++++++++++-- 3 files changed, 350 insertions(+), 86 deletions(-) diff --git a/src/search/FindReplace.js b/src/search/FindReplace.js index 9d8c9e8bf6..adf3ef165c 100644 --- a/src/search/FindReplace.js +++ b/src/search/FindReplace.js @@ -396,7 +396,7 @@ define(function (require, exports, module) { function clearCurrentMatchHighlight(editor, state) { if (state.markedCurrent) { state.markedCurrent.clear(); - ScrollTrackMarkers.markCurrent(-1, editor); + ScrollTrackMarkers._markCurrent(-1, editor); } } @@ -424,7 +424,7 @@ define(function (require, exports, module) { {from: thisMatch.start, to: thisMatch.end}, false); // Update current-tickmark indicator - only if highlighting enabled (disabled if FIND_HIGHLIGHT_MAX threshold hit) if (state.marked.length) { - ScrollTrackMarkers.markCurrent(state.matchIndex, editor); // _updateFindBarWithMatchInfo() has updated this index + ScrollTrackMarkers._markCurrent(state.matchIndex, editor); // _updateFindBarWithMatchInfo() has updated this index } } @@ -464,7 +464,7 @@ define(function (require, exports, module) { {from: nextMatch.start, to: nextMatch.end}, searchBackwards); // Update current-tickmark indicator - only if highlighting enabled (disabled if FIND_HIGHLIGHT_MAX threshold hit) if (state.marked.length) { - ScrollTrackMarkers.markCurrent(state.matchIndex, editor); // _updateFindBarWithMatchInfo() has updated this index + ScrollTrackMarkers._markCurrent(state.matchIndex, editor); // _updateFindBarWithMatchInfo() has updated this index } } @@ -522,8 +522,6 @@ define(function (require, exports, module) { } else { $(editor.getRootElement()).removeClass("find-highlighting"); } - - ScrollTrackMarkers.setVisible(editor, enabled); } /** @@ -579,7 +577,9 @@ define(function (require, exports, module) { return result.from; }); - ScrollTrackMarkers.addTickmarks(editor, scrollTrackPositions); + ScrollTrackMarkers.addTickmarks(editor, scrollTrackPositions, { + dontMerge: true + }); } // Here we only update find bar with no result. In the case of a match diff --git a/src/search/ScrollTrackMarkers.js b/src/search/ScrollTrackMarkers.js index a4cb26071e..99bc5332c4 100644 --- a/src/search/ScrollTrackMarkers.js +++ b/src/search/ScrollTrackMarkers.js @@ -169,53 +169,50 @@ define(function (require, exports, module) { * @param {Array.<{line: number, ch: number}>} posArray */ function _renderMarks(editor, posArray) { - const cm = editor._codeMirror; - const markerState = _getMarkerState(editor); - const $track = $(".tickmark-track", editor.getRootElement()); - const editorHt = cm.getScrollerElement().scrollHeight; + const cm = editor._codeMirror; + const markerState = _getMarkerState(editor); + const $track = $(".tickmark-track", editor.getRootElement()); + const editorHt = cm.getScrollerElement().scrollHeight; + const wrapping = cm.getOption("lineWrapping"); - const wrapping = cm.getOption("lineWrapping"); - const singleLineH = wrapping && cm.defaultTextHeight() * 1.5; - - // For performance, precompute top for each mark + // We'll collect all the normalized (top, bottom) positions here const markPositions = []; - let curLine = null, curLineObj = null; - - function getY(pos) { - if (curLine !== pos.line) { - curLine = pos.line; - curLineObj = cm.getLineHandle(curLine); - } - if (wrapping && curLineObj && curLineObj.height > singleLineH) { - return cm.charCoords(pos, "local").top; - } - return cm.heightAtLine(curLineObj, "local"); - } posArray.forEach(function (pos) { - const y = getY(pos); - const ratio = editorHt ? (y / editorHt) : 0; - const top = Math.round(ratio * markerState.trackHt) + markerState.trackOffset - 1; - - let markerHeight = MARKER_HEIGHT_LINE; - let isLine = true; - if (pos.options.trackStyle === TRACK_STYLES.ON_LEFT) { - markerHeight = MARKER_HEIGHT_LEFT; - isLine = false; - } + // Extract the style info + const trackStyle = pos.options.trackStyle || TRACK_STYLES.LINE; + const cssColorClass = pos.options.cssColorClass || ""; + + // Decide which marker height to use + const isLineMarker = (trackStyle === TRACK_STYLES.LINE); + const markerHeight = isLineMarker ? MARKER_HEIGHT_LINE : MARKER_HEIGHT_LEFT; + + // We'll measure the 'start' of the range and the 'end' of the range + const startPos = pos.start || pos; // Fallback, in case it's single + const endPos = pos.end || pos; // Fallback, in case it's single + + // Compute the top offset for the start + const startY = _computeY(startPos); + // Compute the top offset for the end + const endY = _computeY(endPos); + + // Put them in ascending order + const topY = Math.min(startY, endY); + const bottomY = Math.max(startY, endY) + markerHeight; markPositions.push({ - top: top, - bottom: top + markerHeight, - isLine: isLine, - cssColorClass: pos.options.cssColorClass || "" + top: topY, + bottom: bottomY, + isLine: isLineMarker, + cssColorClass }); }); - // Sort them by top coordinate - markPositions.sort(function (a, b) { return a.top - b.top; }); + // Merge/condense overlapping or adjacent segments, same as before + markPositions.sort(function (a, b) { + return a.top - b.top; + }); - // Merge nearby or overlapping segments const mergedLineMarks = []; const mergedLeftMarks = []; @@ -234,20 +231,38 @@ define(function (require, exports, module) { mergedMarks.push(mark); }); - // Build HTML for horizontal marks + // Now render them into the DOM + // (1) For the "line" style let html = mergedLineMarks.map(function (m) { - return `
`; + return `
`; }).join(""); $track.append($(html)); - // Build HTML for vertical marks + // (2) For the "left" style html = mergedLeftMarks.map(function (m) { - return `
`; + return `
`; }).join(""); $track.append($(html)); + + /** + * Helper function to compute Y offset for a given {line, ch} position + */ + function _computeY(cmPos) { + if (wrapping) { + // For wrapped lines, measure the exact Y-position in the editor + return cm.charCoords(cmPos, "local").top / editorHt * markerState.trackHt + + markerState.trackOffset - 1; + } + // For unwrapped lines, we can do a simpler approach + const cursorTop = cm.heightAtLine(cmPos.line, "local"); + const ratio = editorHt ? (cursorTop / editorHt) : 0; + return Math.round(ratio * markerState.trackHt) + markerState.trackOffset - 1; + } } + /** * Private helper: Show the track if it's not already visible. * @param {!Editor} editor @@ -381,15 +396,84 @@ define(function (require, exports, module) { } /** - * Adds tickmarks for the given positions into the editor's tickmark track. + * Merges an array of tickmark ranges if they are adjacent or overlapping in lines. + * All items are assumed to be in the shape: + * { + * start: { line: number, ch: number }, + * end: { line: number, ch: number }, + * options: Object + * } + * + * @param {Array} markArray + * @return {Array} A new array with merged ranges. + */ + function _mergeMarks(markArray) { + // 1) Sort by starting line (and ch if you want a finer sort) + markArray.sort((a, b) => { + if (a.start.line !== b.start.line) { + return a.start.line - b.start.line; + } + return a.start.ch - b.start.ch; + }); + + const merged = []; + let current = null; + + for (const mark of markArray) { + // If we're not currently building a merged range, start one + if (!current) { + current = { + start: { ...mark.start }, + end: { ...mark.end }, + options: mark.options + }; + } else { + // Check if the new mark is adjacent or overlaps the current range + // i.e. if mark's start is <= current's end.line + 1 + if (mark.start.line <= current.end.line + 1) { + // Merge them by extending current.end if needed + if (mark.end.line > current.end.line) { + current.end.line = mark.end.line; + current.end.ch = mark.end.ch; + } else if (mark.end.line === current.end.line && mark.end.ch > current.end.ch) { + current.end.ch = mark.end.ch; + } + // If you need to unify other fields (like color classes), + // decide how to handle current.options vs. mark.options here. + } else { + // Not adjacent => push the old range and start a fresh one + merged.push(current); + current = { + start: { ...mark.start }, + end: { ...mark.end }, + options: mark.options + }; + } + } + } + + // Flush any final in-progress range + if (current) { + merged.push(current); + } + + return merged; + } + + /** + * Adds tickmarks or range-markers for the given positions (or ranges) into the editor's tickmark track. * If the track was not visible and new marks are added, it is automatically shown. + * * @param {!Editor} editor - * @param {Array.<{line: number, ch: number}>} posArray + * @param {Array.<{line: number, ch: number} | {start: {line, ch}, end: {line, ch}}>} posArray + * Each element can be: + * (A) a single point: `{ line: number, ch: number }`, or + * (B) a range: `{ start: { line, ch }, end: { line, ch } }` * @param {Object} [options] - * @param {string} [options.name] you can assign a name to marks and then use this name to selectively - * clear these marks. - * @param {string} [options.trackStyle] one of TRACK_STYLES.* - * @param {string} [options.cssColorClass] a css class that should override the --mark-color css var. + * @param {string} [options.name] Optionally assign a name to these marks and later selectively clear them. + * @param {string} [options.trackStyle] one of TRACK_STYLES.* (e.g., "line" or "left"). + * @param {string} [options.cssColorClass] A CSS class that can override or extend styling. + * @param {string} [options.dontMerge] If set to true, will not merge nearby lines in posArray to single mark. */ function addTickmarks(editor, posArray, options = {}) { const markerState = _getMarkerState(editor); @@ -397,42 +481,58 @@ define(function (require, exports, module) { return; } - // Make sure we have a valid editor instance - if (!editor) { - console.error("Calling ScrollTrackMarkers.addTickmarks without an editor is deprecated."); - editor = EditorManager.getActiveEditor(); - } - - // If track was empty before, note it + // Keep track of whether the track was empty before adding marks const wasEmpty = (markerState.marks.length === 0); - // Normalize the new positions to include the same options object - const newPosArray = posArray.map(pos => ({ ...pos, options })); + // Normalize each incoming item so that every mark has both {start, end} internally + const newMarks = posArray.map(pos => { + // If this looks like { start: {...}, end: {...} }, use it directly + if (pos.start && pos.end) { + return { + start: pos.start, + end: pos.end, + options + }; + } - // Concat the new positions - markerState.marks = markerState.marks.concat(newPosArray); + // Otherwise assume it's a single point { line, ch } + // Treat it as a zero-length range + return { + start: pos, + end: pos, + options + }; + }); - // If we were empty and now have tickmarks, show track + const mergedMarks = options.dontMerge ? newMarks : _mergeMarks(newMarks); + + // Concat the new marks onto the existing marks + markerState.marks = markerState.marks.concat(mergedMarks); + + // If we were empty and now have marks, show the scroll track if (wasEmpty && markerState.marks.length > 0) { _showTrack(editor); } - // If track is visible, re-render + // If the track is visible, re-render everything if (markerState.visible) { $(".tickmark-track", editor.getRootElement()).empty(); _renderMarks(editor, markerState.marks); } } + /** * Highlights the "current" tickmark at the given index (into the marks array provided to addTickmarks), * or clears if `index === -1`. * @param {number} index * @param {!Editor} editor + * @private */ - function markCurrent(index, editor) { + function _markCurrent(index, editor) { if (!editor) { - throw new Error("Calling ScrollTrackMarkers.markCurrent without editor instance is deprecated."); + throw new Error( + "Calling private API ScrollTrackMarkers._markCurrent without editor instance is deprecated."); } const markerState = _getMarkerState(editor); @@ -446,7 +546,7 @@ define(function (require, exports, module) { } // Position the highlight - const top = _getTop(editor, markerState.marks[index]); + const top = _getTop(editor, markerState.marks[index].start); const $currentTick = $( `
` ); @@ -468,11 +568,15 @@ define(function (require, exports, module) { // For unit tests exports._getTickmarks = _getTickmarks; + // private API + exports._markCurrent = _markCurrent; + + // deprecated public API + exports.setVisible = setVisible; // Deprecated + // Public API + exports.addTickmarks = addTickmarks; exports.clear = clear; exports.clearAll = clearAll; - exports.setVisible = setVisible; // Deprecated - exports.addTickmarks = addTickmarks; - exports.markCurrent = markCurrent; exports.TRACK_STYLES = TRACK_STYLES; }); diff --git a/test/spec/ScrollTrackHandler-integ-test.js b/test/spec/ScrollTrackHandler-integ-test.js index fb60e4339b..1830dec982 100644 --- a/test/spec/ScrollTrackHandler-integ-test.js +++ b/test/spec/ScrollTrackHandler-integ-test.js @@ -1,8 +1,10 @@ /* * GNU AGPL-3.0 License * - * Copyright (c) 2021 - present core.ai . All rights reserved. - * Original work Copyright (c) 2013 - 2021 Adobe Systems Incorporated. All rights reserved. + * Copyright (c) 2021 - present core.ai + * All rights reserved. + * Original work Copyright (c) 2013 - 2021 Adobe Systems Incorporated. + * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by @@ -23,17 +25,17 @@ define(function (require, exports, module) { - // Load dependent modules let ScrollTrackMarkers, __PR, SpecRunnerUtils = require("spec/SpecRunnerUtils"); - describe("integration:Scroll Track markers", function () { let testWindow, - $, editor1Large, editor2; + $, + editor1Large, // "bootstrap.css" (~3000 lines) + editor2; // "variables.less" (~40 lines) let currentProjectPath; @@ -46,14 +48,18 @@ define(function (require, exports, module) { __PR = testWindow.__PR; currentProjectPath = await SpecRunnerUtils.getTestPath("/spec/CSSUtils-test-files"); await SpecRunnerUtils.loadProjectInTestWindow(currentProjectPath); + + // Split the view horizontally, open two files await __PR.EDITING.splitHorizontal(); - await __PR.EDITING.openFileInFirstPane("bootstrap.css", true); - await __PR.EDITING.openFileInSecondPane("variables.less", true); + await __PR.EDITING.openFileInFirstPane("bootstrap.css", true); // ~3000 lines + await __PR.EDITING.openFileInSecondPane("variables.less", true); // ~40 lines + editor1Large = __PR.EDITING.getFirstPaneEditor(); editor2 = __PR.EDITING.getSecondPaneEditor(); }, 30000); afterAll(async function () { + // Cleanup await SpecRunnerUtils.parkProject(true); await __PR.EDITING.splitNone(); testWindow = null; @@ -62,30 +68,37 @@ define(function (require, exports, module) { }, 30000); beforeEach(function () { + // Clear all tickmarks from both editors before each test ScrollTrackMarkers.clearAll(editor1Large); ScrollTrackMarkers.clearAll(editor2); }); - + // + // EXISTING TESTS + // it("should be able to add a line and side mark to both editors and clearAll", async function () { const trackerList = [{line: 10, ch: 0}]; ScrollTrackMarkers.addTickmarks(editor1Large, trackerList); expect($(".tickmark").length).toBe(1); + ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT }); expect($(".tickmark").length).toBe(2); expect($(".tickmark-side").length).toBe(1); + + // Add to second editor ScrollTrackMarkers.addTickmarks(editor2, trackerList); expect($(".tickmark").length).toBe(3); expect($(".tickmark-side").length).toBe(1); + ScrollTrackMarkers.addTickmarks(editor2, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT }); expect($(".tickmark").length).toBe(4); expect($(".tickmark-side").length).toBe(2); - // now clear all + // now clear all from first and second ScrollTrackMarkers.clearAll(editor1Large); expect($(".tickmark").length).toBe(2); expect($(".tickmark-side").length).toBe(1); @@ -96,12 +109,15 @@ define(function (require, exports, module) { it("should be able to add and clear named marks", async function () { const trackerList = [{line: 10, ch: 0}]; - const trackName1= "line_ed_1"; - const trackName2= "side_ed_1"; + const trackName1 = "line_ed_1"; + const trackName2 = "side_ed_1"; + + // Add two named sets ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { name: trackName1 }); expect($(".tickmark").length).toBe(1); + ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT, name: trackName2 @@ -113,6 +129,7 @@ define(function (require, exports, module) { ScrollTrackMarkers.clear(editor1Large, trackName1); expect($(".tickmark").length).toBe(1); expect($(".tickmark-side").length).toBe(1); + ScrollTrackMarkers.clear(editor1Large, trackName2); expect($(".tickmark").length).toBe(0); expect($(".tickmark-side").length).toBe(0); @@ -120,14 +137,16 @@ define(function (require, exports, module) { it("should be able to add css classes marks", async function () { const trackerList = [{line: 10, ch: 0}]; - const trackClass1= "line_ed_1"; - const trackClass2= "side_ed_1"; + const trackClass1 = "line_ed_1"; + const trackClass2 = "side_ed_1"; + ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { name: trackClass1, cssColorClass: trackClass1 }); expect($(`.tickmark.${trackClass1}`).length).toBe(1); expect($(".tickmark").length).toBe(1); + ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT, name: trackClass2, @@ -138,28 +157,169 @@ define(function (require, exports, module) { }); it("should merge nearby scroll marks to single mark", async function () { + // lines 10 and 11 are close, so they get merged into one track segment const trackerList = [{line: 10, ch: 0}, {line: 11, ch: 0}]; ScrollTrackMarkers.addTickmarks(editor1Large, trackerList); + // verify there's exactly 1 ".tickmark" block for lines 10 & 11 expect($(".tickmark").length).toBe(1); expect($(".tickmark-side").length).toBe(0); + + // If we add them again but with 'ON_LEFT', that merges into a single side mark ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT }); + // Now we have 2 total marks (1 line style, 1 side style) expect($(".tickmark").length).toBe(2); expect($(".tickmark-side").length).toBe(1); }); it("should not merge far away scroll marks to single mark", async function () { + // lines 10 and 2000 are definitely not adjacent const trackerList = [{line: 10, ch: 0}, {line: 2000, ch: 0}]; ScrollTrackMarkers.addTickmarks(editor1Large, trackerList); + // We expect 2 distinct line-type marks expect($(".tickmark").length).toBe(2); expect($(".tickmark-side").length).toBe(0); + + // Add them again with 'ON_LEFT' ScrollTrackMarkers.addTickmarks(editor1Large, trackerList, { trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT }); + // Now we have 4 total marks: 2 line style, 2 side style expect($(".tickmark").length).toBe(4); expect($(".tickmark-side").length).toBe(2); }); + it("should merge when lines are unsorted in the array, but adjacent or overlapping", async function () { + // This array is out of order, but lines 10, 11, and 12 are all adjacent + const unsorted = [ + { line: 12, ch: 3 }, + { line: 10, ch: 0 }, + { line: 11, ch: 9 } + ]; + + ScrollTrackMarkers.addTickmarks(editor1Large, unsorted); + // Because they're adjacent, we expect them all to merge into a single mark + expect($(".tickmark").length).toBe(1); + + // The final result in the scrollbar track is effectively lines 10..12 merged. + }); + + it("should merge single-point lines with an existing multi-line range if overlapping or adjacent", async function () { + // We'll have a big range from lines 100..105 plus a single line 106 => merges + const rangePlusSingle = [ + { start: { line: 100, ch: 0 }, end: { line: 105, ch: 10 } }, + { line: 106, ch: 0 } + ]; + ScrollTrackMarkers.addTickmarks(editor1Large, rangePlusSingle); + // lines 100..105 plus 106 => a single merged range 100..106 + expect($(".tickmark").length).toBe(1); + + // Additionally, add a single far-away line 200 to confirm it doesn't merge + ScrollTrackMarkers.addTickmarks(editor1Large, [{ line: 200, ch: 0 }]); + // Expect one merged block for 100..106, plus another block for line 200 + expect($(".tickmark").length).toBe(2); + }); + + it("should handle large line numbers in the 3000-line file when merging", async function () { + // Mark lines near 2500..2501..2502 => merged + // Also mark lines near 2999 => separate + const bigLines = [ + { line: 2502, ch: 0 }, + { line: 2501, ch: 0 }, + { line: 2500, ch: 0 }, + { line: 2999, ch: 0 } + ]; + ScrollTrackMarkers.addTickmarks(editor1Large, bigLines); + + // Expect 2 total marks: + // - One merged range for 2500..2502 + // - One separate for line 2999 + expect($(".tickmark").length).toBe(2); + }); + + it("should merge lines in the smaller file (variables.less) that only has ~40 lines", async function () { + // We'll add lines 5, 6, 7 => they should merge + // And lines 8, 9 => they should merge as well (and combine into single block 5..9 if adjacency is direct) + // Then line 15 is separate + // NOTE: If there's a gap between 7 and 8, they're still adjacent lines => merges all 5..9. + const smallFileMarks = [ + { line: 7, ch: 0 }, + { line: 5, ch: 0 }, + { line: 6, ch: 0 }, + { line: 9, ch: 0 }, + { line: 8, ch: 0 }, + { line: 15, ch: 0 } + ]; + + ScrollTrackMarkers.addTickmarks(editor2, smallFileMarks); + + // We expect 2 tickmark blocks total: + // - One merged block for lines 5..9 + // - One single block for line 15 + expect($(".tickmark").length).toBe(2); + + // Also verify that it's in the second editor, not the first + // But since we didn't add anything to editor1Large, total ".tickmark" in the DOM + // is 2 anyway (due to how they're appended per-editor). + }); + + it("should correctly handle merging single-line points that overlap exact multi-line boundaries", async function () { + // If we have a range from lines 20..22 and then a single line 22, + // that single line is effectively 'inside' that range (or at boundary). + // This test checks if the merging logic lumps them correctly. + const overlapping = [ + { start: { line: 20, ch: 0 }, end: { line: 22, ch: 10 } }, + { line: 22, ch: 5 } + ]; + ScrollTrackMarkers.addTickmarks(editor2, overlapping); + // Expect 1 merged block total for 20..22 + expect($(".tickmark").length).toBe(1); + + // Now add line 23 => we dont merge existing marks into addTickmarks, IF we need to merge it, we need to + // handle cases of merging different optioned marks seperateley while still merging marks of the same name + // individually. Even if different named marks overlap, we should only merge individual named marks. + ScrollTrackMarkers.addTickmarks(editor2, [{ line: 23, ch: 0 }]); + expect($(".tickmark").length).toBe(2); // thoug overlap, wont be merged. + }); + + it("should not merge adjacent marks when dontMerge option is enabled", async function () { + // Even though lines 10 and 11 are adjacent, they should remain separate if dontMerge is true. + const trackerList = [{line: 10, ch: 0}, {line: 11, ch: 0}]; + ScrollTrackMarkers.addTickmarks(editor2, trackerList, { dontMerge: true }); + // Expect two separate tickmarks instead of one merged block. + expect(ScrollTrackMarkers._getTickmarks(editor2).length).toBe(2); + + // Also test for ON_LEFT style with dontMerge. + ScrollTrackMarkers.addTickmarks(editor2, trackerList, { + trackStyle: ScrollTrackMarkers.TRACK_STYLES.ON_LEFT, + dontMerge: true + }); + // Now we expect two more separate side marks. + expect(ScrollTrackMarkers._getTickmarks(editor2).length).toBe(4); + }); + + it("should merge overlapping marks by default", async function () { + // Even if marks overlap, with dontMerge they should be added as-is. + const overlapping = [ + { start: { line: 20, ch: 0 }, end: { line: 22, ch: 10 } }, + { line: 22, ch: 5 } + ]; + ScrollTrackMarkers.addTickmarks(editor2, overlapping); + // Without merging, both marks are rendered separately. + expect(ScrollTrackMarkers._getTickmarks(editor2).length).toBe(1); + }); + + it("should not merge overlapping marks when dontMerge option is enabled", async function () { + // Even if marks overlap, with dontMerge they should be added as-is. + const overlapping = [ + { start: { line: 20, ch: 0 }, end: { line: 22, ch: 10 } }, + { line: 22, ch: 5 } + ]; + ScrollTrackMarkers.addTickmarks(editor2, overlapping, { dontMerge: true }); + // Without merging, both marks are rendered separately. + expect(ScrollTrackMarkers._getTickmarks(editor2).length).toBe(2); + }); + }); }); From ab3e978d98db9b84f8171d2ce8d7efe5ff0b6706 Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 18 Feb 2025 14:36:38 +0530 Subject: [PATCH 2/2] fix: find replace integ test after scroll track markers --- test/spec/FindReplace-integ-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/FindReplace-integ-test.js b/test/spec/FindReplace-integ-test.js index a5799547ff..599021ce10 100644 --- a/test/spec/FindReplace-integ-test.js +++ b/test/spec/FindReplace-integ-test.js @@ -401,7 +401,7 @@ define(function (require, exports, module) { expect(marks.length).toEql(fooExpectedMatches.length); marks.forEach(function (mark, index) { - expect(mark.line).toEql(fooExpectedMatches[index].start.line); + expect(mark.start.line).toEql(fooExpectedMatches[index].start.line); }); });