Skip to content

Commit 0ee6b4b

Browse files
committed
chore: add support for named scroll markers
1 parent 010462a commit 0ee6b4b

File tree

2 files changed

+102
-52
lines changed

2 files changed

+102
-52
lines changed

src/search/ScrollTrackMarkers.js

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ define(function (require, exports, module) {
3333
EditorManager = require("editor/EditorManager"),
3434
WorkspaceManager = require("view/WorkspaceManager");
3535

36+
const TRACK_STYLES = {
37+
LINE: "line",
38+
ON_LEFT: "left"
39+
};
40+
3641
/**
3742
* Vertical space above/below the scrollbar (set per OS).
3843
* This remains global but applies to all editors.
@@ -57,24 +62,8 @@ define(function (require, exports, module) {
5762
* The (fixed) height of each individual tickmark.
5863
* @const
5964
*/
60-
const MARKER_HEIGHT = 2;
61-
62-
/**
63-
* Returns the vertical space above and below the scrollbar, which depends
64-
* on OS or may be altered by other CSS/extension changes.
65-
* @return {number}
66-
*/
67-
function getScrollbarTrackOffset() {
68-
return scrollbarTrackOffset;
69-
}
70-
71-
/**
72-
* Sets how much vertical space there is above and below the scrollbar.
73-
* @param {number} offset Value in pixels
74-
*/
75-
function setScrollbarTrackOffset(offset) {
76-
scrollbarTrackOffset = offset;
77-
}
65+
const MARKER_HEIGHT_LINE = 2;
66+
const MARKER_HEIGHT_LEFT = 5;
7867

7968
/**
8069
* Helper: get or create the scrollTrackMarker state object for an editor.
@@ -124,7 +113,7 @@ define(function (require, exports, module) {
124113

125114
var trackHeight = $sb[0].offsetHeight;
126115
if (trackHeight > 0) {
127-
markerState.trackOffset = getScrollbarTrackOffset();
116+
markerState.trackOffset = scrollbarTrackOffset;
128117
markerState.trackHt = trackHeight - markerState.trackOffset * 2;
129118
} else {
130119
// No scrollbar: use the height of the entire code content
@@ -166,7 +155,7 @@ define(function (require, exports, module) {
166155
/**
167156
* Renders the given list of positions as merged tickmarks in the scrollbar track.
168157
* @param {!Editor} editor
169-
* @param {!Array.<{line: number, ch: number}>} posArray
158+
* @param {Array.<{line: number, ch: number}>} posArray
170159
*/
171160
function _renderMarks(editor, posArray) {
172161
const cm = editor._codeMirror;
@@ -197,59 +186,113 @@ define(function (require, exports, module) {
197186
const ratio = editorHt ? (y / editorHt) : 0;
198187
const top = Math.round(ratio * markerState.trackHt) + markerState.trackOffset - 1;
199188
// default 2px height => from top..(top+2)
200-
markPositions.push({ top: top, bottom: top + MARKER_HEIGHT });
189+
let markerHeight = MARKER_HEIGHT_LINE, isLine = true;
190+
if(pos.options.trackStyle === TRACK_STYLES.ON_LEFT) {
191+
markerHeight = MARKER_HEIGHT_LEFT;
192+
isLine = false;
193+
}
194+
markPositions.push({ top: top, bottom: top + markerHeight, isLine,
195+
cssColorClass: pos.options.cssColorClass || ""});
201196
});
202197

203198
// Sort them by top coordinate
204199
markPositions.sort(function (a, b) { return a.top - b.top; });
205200

206201
// Merge nearby or overlapping segments
207-
const mergedMarks = [];
208-
markPositions.forEach(function (m) {
202+
const mergedLineMarks = [], mergedLeftMarks = [];
203+
markPositions.forEach(function (mark) {
204+
const mergedMarks = mark.isLine ? mergedLineMarks : mergedLeftMarks;
209205
if (mergedMarks.length > 0) {
210206
const last = mergedMarks[mergedMarks.length - 1];
211207
// If overlapping or adjacent, merge them
212-
if (m.top <= last.bottom + 1) {
213-
last.bottom = Math.max(last.bottom, m.bottom);
208+
if (mark.top <= last.bottom + 1) {
209+
last.bottom = Math.max(last.bottom, mark.bottom);
214210
last.height = last.bottom - last.top;
215211
return;
216212
}
217213
}
218-
m.height = m.bottom - m.top;
219-
mergedMarks.push(m);
214+
mark.height = mark.bottom - mark.top;
215+
mergedMarks.push(mark);
220216
});
221217

222-
// Build HTML
223-
const html = mergedMarks.map(function (m) {
224-
return "<div class='tickmark' style='top:" + m.top + "px; height:" + m.height + "px;'></div>";
218+
// Build HTML for horizontal marks
219+
let html = mergedLineMarks.map(function (m) {
220+
return `<div class='tickmark ${m.cssColorClass}' style='top: ${m.top}px; height: ${m.height}px;'></div>`;
221+
}).join("");
222+
223+
// Append to track
224+
$track.append($(html));
225+
226+
// Build HTML for hertical marks
227+
html = mergedLeftMarks.map(function (m) {
228+
return `<div class='tickmark tickmark-side ${
229+
m.cssColorClass}' style='top: ${m.top}px; height: ${m.height}px;'></div>`;
225230
}).join("");
226231

227232
// Append to track
228233
$track.append($(html));
229234
}
230235

231236
/**
232-
* Clear any markers in the editor's tickmark track, leaving it visible if it was shown.
233-
* Safe to call when the tickmark track is not visible also.
237+
* Clear tickmarks from the editor's tickmark track.
238+
* - If `markName` is provided, only clears marks with that name.
239+
* - If `markName` is omitted, clears **all unnamed marks** but leaves named marks.
240+
* - To **clear all marks**, including named ones, use `clearAll()`.
234241
* @param {!Editor} editor
242+
* @param {string} [markName] Optional. If given, only clears marks with that name.
235243
*/
236-
function clear(editor) {
237-
if(!editor) {
238-
console.error("Calling ScrollTrackMarkers.clear without editor instance is deprecated.");
244+
function clear(editor, markName) {
245+
if (!editor) {
246+
console.error("Calling ScrollTrackMarkers.clear without an editor instance is deprecated.");
239247
editor = EditorManager.getActiveEditor();
240248
}
241249
const markerState = editor && editor._scrollTrackMarker;
242250
if (!markerState) {
243251
return;
244252
}
253+
254+
if (markName) {
255+
// Filter out only the named marks that match the given `markName`
256+
markerState.marks = markerState.marks.filter(mark => mark.options && mark.options.name !== markName);
257+
} else {
258+
// Remove only unnamed marks (marks where options.name is undefined or null)
259+
markerState.marks = markerState.marks.filter(mark => mark.options && mark.options.name);
260+
}
261+
262+
// Re-render the marks after clearing
245263
$(".tickmark-track", editor.getRootElement()).empty();
264+
_renderMarks(editor, markerState.marks);
265+
266+
if (markerState.$markedTickmark && markName) {
267+
markerState.$markedTickmark.remove();
268+
markerState.$markedTickmark = null;
269+
}
270+
}
271+
272+
/**
273+
* Clears all tickmarks from the editor's tickmark track, including named and unnamed marks.
274+
* @param {!Editor} editor
275+
*/
276+
function clearAll(editor) {
277+
if (!editor) {
278+
throw new Error("Called ScrollTrackMarkers.clearAll without an editor!");
279+
}
280+
const markerState = editor && editor._scrollTrackMarker;
281+
if (!markerState) {
282+
return;
283+
}
284+
285+
// Completely remove all tickmarks
246286
markerState.marks = [];
287+
$(".tickmark-track", editor.getRootElement()).empty();
288+
247289
if (markerState.$markedTickmark) {
248290
markerState.$markedTickmark.remove();
249291
markerState.$markedTickmark = null;
250292
}
251293
}
252294

295+
253296
/**
254297
* Shows or hides the tickmark track for the given editor.
255298
* @param {!Editor} editor
@@ -306,16 +349,22 @@ define(function (require, exports, module) {
306349
/**
307350
* Adds tickmarks for the given positions into the editor's tickmark track, if visible.
308351
* @param {!Editor} editor
309-
* @param {!Array.<{line: number, ch: number}>} posArray
352+
* @param {Array.<{line: number, ch: number}>} posArray
353+
* @param {Object} [options]
354+
* @param {string} [options.name] you can assign a name to marks and then use this name to selectively
355+
* these clear marks.
356+
* @param {string} [options.trackStyle] one of TRACK_STYLES.*
357+
* @param {string} [options.cssColorClass] a css class that should only override the --mark-color css var.
310358
*/
311-
function addTickmarks(editor, posArray) {
359+
function addTickmarks(editor, posArray, options = {}) {
312360
const markerState = _getMarkerState(editor);
313361
if (!markerState.visible) {
314362
return;
315363
}
364+
const newPosArray = posArray.map(pos => ({ ...pos, options }));
316365
// Concat new positions
317-
markerState.marks = markerState.marks.concat(posArray);
318-
_renderMarks(editor, posArray);
366+
markerState.marks = markerState.marks.concat(newPosArray);
367+
_renderMarks(editor, markerState.marks);
319368
}
320369

321370
/**
@@ -340,7 +389,7 @@ define(function (require, exports, module) {
340389

341390
const top = _getTop(editor, markerState.marks[index]);
342391
const $tick = $(
343-
`<div class='tickmark tickmark-current' style='top: ${top}px; height: ${MARKER_HEIGHT}px;'></div>`);
392+
`<div class='tickmark tickmark-current' style='top: ${top}px; height: ${MARKER_HEIGHT_LINE}px;'></div>`);
344393

345394
$(".tickmark-track", editor.getRootElement()).append($tick);
346395
markerState.$markedTickmark = $tick;
@@ -361,9 +410,9 @@ define(function (require, exports, module) {
361410

362411
// public API
363412
exports.clear = clear;
413+
exports.clearAll = clearAll;
364414
exports.setVisible = setVisible;
365415
exports.addTickmarks = addTickmarks;
366416
exports.markCurrent = markCurrent;
367-
exports.getScrollbarTrackOffset = getScrollbarTrackOffset;
368-
exports.setScrollbarTrackOffset = setScrollbarTrackOffset;
417+
exports.TRACK_STYLES = TRACK_STYLES;
369418
});

src/styles/brackets.less

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,32 +2617,33 @@ textarea.exclusions-editor {
26172617
width: 12px;
26182618
z-index: @z-index-cm-max;
26192619
pointer-events: none;
2620+
--mark-color: #eddd23;
26202621

26212622
.tickmark {
26222623
position: absolute;
26232624
width: 12px;
26242625
height: 2px;
2625-
background-color: #eddd23;
2626+
background-color: var(--mark-color);
26262627

26272628
opacity: 0.85; // allow thumb to show through
26282629
&.tickmark-current {
26292630
background-color: #ed9823;
26302631
z-index: 1; // ensure this one appears above overlapping sibling highlights
26312632
opacity: 1;
26322633
}
2633-
}
26342634

2635-
.tickmark-side {
2636-
position: absolute;
2637-
width: 12px;
2638-
height: 5px;
2639-
border-left: 2px solid;
2640-
border-left-color: #e0d123;
2641-
opacity: 0.85; // allow thumb to show through
2635+
&.tickmark-side {
2636+
background-color: unset;
2637+
position: absolute;
2638+
width: 12px;
2639+
height: 5px;
2640+
border-left: 2px solid;
2641+
border-left-color: var(--mark-color);
2642+
opacity: 1;
2643+
}
26422644
}
26432645
}
26442646

2645-
26462647
/* Quick Open search bar & dropdown */
26472648

26482649
.find-dialog-label {

0 commit comments

Comments
 (0)