Skip to content

Commit 26a9eb5

Browse files
committed
chore: merge Overlapping Scrollbar Tickmarks for Efficiency
Refactored _renderMarks(posArray) to merge overlapping or adjacent tickmarks, reducing DOM elements while maintaining accuracy. Instead of rendering multiple 2px markers, contiguous marks are merged into a single div. Adjusted styles (brackets.less): .tickmark height is now based on 2px bg color, added .tickmark-side (5px) for better side markers in scrollbar instead of line markers
1 parent 28a3742 commit 26a9eb5

File tree

2 files changed

+76
-20
lines changed

2 files changed

+76
-20
lines changed

src/search/ScrollTrackMarkers.js

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ define(function (require, exports, module) {
104104
}
105105

106106

107-
function _getScrollbar(editor) {
107+
function _getScrollbar(editorInstance) {
108108
// Be sure to select only the direct descendant, not also elements within nested inline editors
109-
return $(editor.getRootElement()).children(".CodeMirror-vscrollbar");
109+
return $(editorInstance.getRootElement()).children(".CodeMirror-vscrollbar");
110110
}
111111

112112
/**
@@ -123,29 +123,47 @@ define(function (require, exports, module) {
123123
trackHt -= trackOffset * 2;
124124
} else {
125125
// No scrollbar: use the height of the entire code content
126-
var codeContainer = $(editor.getRootElement()).find("> .CodeMirror-scroll > .CodeMirror-sizer > div > .CodeMirror-lines > div")[0];
126+
var codeContainer = $(editor.getRootElement())
127+
.find("> .CodeMirror-scroll > .CodeMirror-sizer > div > .CodeMirror-lines > div")[0];
127128
trackHt = codeContainer.offsetHeight;
128129
trackOffset = codeContainer.offsetTop;
129130
}
130131
}
131132

133+
function _getTop(editorInstance, pos) {
134+
let cm = editorInstance._codeMirror,
135+
editorHt = cm.getScrollerElement().scrollHeight,
136+
cursorTop;
137+
138+
let wrapping = cm.getOption("lineWrapping"),
139+
singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
140+
let curLine = pos.line;
141+
let curLineObj = cm.getLineHandle(curLine);
142+
if (wrapping && curLineObj.height > singleLineH) {
143+
cursorTop = cm.charCoords(pos, "local").top;
144+
} else {
145+
cursorTop = cm.heightAtLine(curLineObj, "local");
146+
}
147+
// return top
148+
return (Math.round(cursorTop / editorHt * trackHt) + trackOffset) - 1; // 1px Centering correction
149+
}
150+
151+
const MARKER_HEIGHT = 2;
152+
132153
/**
133-
* Add all the given tickmarks to the DOM in a batch
154+
* Add merged tickmarks to the scrollbar track
134155
* @private
135156
*/
136157
function _renderMarks(posArray) {
137-
var html = "",
138-
cm = editor._codeMirror,
158+
let cm = editor._codeMirror,
139159
editorHt = cm.getScrollerElement().scrollHeight;
140160

141-
// We've pretty much taken these vars and the getY function from CodeMirror's annotatescrollbar addon
142-
// https://github.com/codemirror/CodeMirror/blob/master/addon/scroll/annotatescrollbar.js
143-
var wrapping = cm.getOption("lineWrapping"),
161+
let wrapping = cm.getOption("lineWrapping"),
144162
singleLineH = wrapping && cm.defaultTextHeight() * 1.5,
145163
curLine = null,
146164
curLineObj = null;
147165

148-
function getY(cm, pos) {
166+
function getY(pos) {
149167
if (curLine !== pos.line) {
150168
curLine = pos.line;
151169
curLineObj = cm.getLineHandle(curLine);
@@ -156,13 +174,41 @@ define(function (require, exports, module) {
156174
return cm.heightAtLine(curLineObj, "local");
157175
}
158176

177+
var markPositions = [];
178+
179+
// Convert line positions to scrollbar positions
159180
posArray.forEach(function (pos) {
160-
var cursorTop = getY(cm, pos),
181+
var cursorTop = getY(pos),
161182
top = Math.round(cursorTop / editorHt * trackHt) + trackOffset;
162-
top--; // subtract ~1/2 the ht of a tickmark to center it on ideal pos
183+
top--; // Centering correction
184+
markPositions.push({ top: top, bottom: top + MARKER_HEIGHT }); // Default height is 2px
185+
});
163186

164-
html += "<div class='tickmark' style='top:" + top + "px'></div>";
187+
// Sort positions by top coordinate
188+
markPositions.sort((a, b) => a.top - b.top);
189+
190+
// Merge overlapping or adjacent tickmarks
191+
var mergedMarks = [];
192+
markPositions.forEach(({ top, bottom }) => {
193+
if (mergedMarks.length > 0) {
194+
let lastMark = mergedMarks[mergedMarks.length - 1];
195+
196+
if (top <= lastMark.bottom + 1) {
197+
// Extend the existing mark
198+
lastMark.bottom = Math.max(lastMark.bottom, bottom);
199+
lastMark.height = lastMark.bottom - lastMark.top; // Adjust height
200+
return;
201+
}
202+
}
203+
// Create a new mark
204+
mergedMarks.push({ top, bottom, height: bottom - top });
165205
});
206+
207+
// Generate and insert tickmarks into the DOM
208+
var html = mergedMarks.map(({ top, height }) =>
209+
`<div class='tickmark' style='top:${top}px; height:${height}px;'></div>`
210+
).join("");
211+
166212
$(".tickmark-track", editor.getRootElement()).append($(html));
167213
}
168214

@@ -239,11 +285,15 @@ define(function (require, exports, module) {
239285
function markCurrent(index) {
240286
// Remove previous highlight first
241287
if ($markedTickmark) {
242-
$markedTickmark.removeClass("tickmark-current");
288+
$markedTickmark.remove();
243289
$markedTickmark = null;
244290
}
245291
if (index !== -1) {
246-
$markedTickmark = $(".tickmark-track > .tickmark", editor.getRootElement()).eq(index).addClass("tickmark-current");
292+
const parkPos = marks[index];
293+
const top = _getTop(editor, parkPos);
294+
$markedTickmark = $(
295+
`<div class='tickmark tickmark-current' style='top:${top}px; height:${MARKER_HEIGHT}px;'></div>`);
296+
$(".tickmark-track ").append($markedTickmark);
247297
}
248298
}
249299

src/styles/brackets.less

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,18 +2621,24 @@ textarea.exclusions-editor {
26212621
.tickmark {
26222622
position: absolute;
26232623
width: 12px;
2624-
height: 1px;
2624+
height: 2px;
26252625
background-color: #eddd23;
2626-
border-top: 1px solid #e0d123;
2627-
border-bottom: 1px solid #d4c620;
2626+
26282627
opacity: 0.85; // allow thumb to show through
26292628
&.tickmark-current {
26302629
background-color: #ed9823;
2631-
border-top: 1px solid #dd9128;
2632-
border-bottom: 1px solid #cb8320;
26332630
z-index: 1; // ensure this one appears above overlapping sibling highlights
26342631
}
26352632
}
2633+
2634+
.tickmark-side {
2635+
position: absolute;
2636+
width: 12px;
2637+
height: 5px;
2638+
border-left: 2px solid;
2639+
border-left-color: #e0d123;
2640+
opacity: 0.85; // allow thumb to show through
2641+
}
26362642
}
26372643

26382644

0 commit comments

Comments
 (0)