-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgutterDecoration.ts
More file actions
119 lines (100 loc) · 4.3 KB
/
gutterDecoration.ts
File metadata and controls
119 lines (100 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// ABOUTME: Creates and manages CodeMirror line decorations for bookmarks
// ABOUTME: Shows bookmark styling on bookmarked lines using line decorations
import { Extension, StateEffect } from '@codemirror/state';
import { EditorView, ViewUpdate, ViewPlugin, Decoration, DecorationSet, PluginValue } from '@codemirror/view';
import { RangeSetBuilder } from '@codemirror/state';
import { BOOKMARK_MARKER } from './constants';
// State effect for updating bookmark decorations
const updateBookmarkEffect = StateEffect.define<{ lineNumber: number; hasBookmark: boolean }>();
// View plugin to manage bookmark line decorations
class BookmarkDecorationPlugin implements PluginValue {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.buildDecorations(view);
}
update(update: ViewUpdate) {
// Handle bookmark effects
for (const effect of update.transactions.flatMap(tr => tr.effects)) {
if (effect.is(updateBookmarkEffect)) {
this.decorations = this.buildDecorationsFromEffect(update.view, effect.value);
return; // Early return to avoid rebuilding again
}
}
// Rebuild decorations if document changed (to sync with markers)
if (update.docChanged) {
this.decorations = this.buildDecorations(update.view);
}
}
destroy() {}
buildDecorations(view: EditorView): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
const content = view.state.doc.toString();
const lines = content.split('\n');
lines.forEach((lineContent, index) => {
if (lineContent.includes(BOOKMARK_MARKER)) {
try {
const line = view.state.doc.line(index + 1);
if (line) {
// Add line decoration for the bookmark icon
builder.add(
line.from,
line.from,
Decoration.line({ class: 'cm-bookmark-line' })
);
// Hide the bookmark marker text
const markerStart = lineContent.indexOf(BOOKMARK_MARKER);
if (markerStart !== -1) {
const absoluteMarkerStart = line.from + markerStart;
const absoluteMarkerEnd = absoluteMarkerStart + BOOKMARK_MARKER.length;
builder.add(
absoluteMarkerStart,
absoluteMarkerEnd,
Decoration.mark({ class: 'cm-bookmark-marker-hidden' })
);
}
}
} catch {
// Line might not exist, skip
}
}
});
return builder.finish();
}
buildDecorationsFromEffect(view: EditorView, effect: { lineNumber: number; hasBookmark: boolean }): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
if (effect.hasBookmark) {
try {
const line = view.state.doc.line(effect.lineNumber + 1);
if (line) {
builder.add(
line.from,
line.from,
Decoration.line({ class: 'cm-bookmark-line' })
);
}
} catch {
// Line might not exist, skip
}
}
return builder.finish();
}
}
// Create the view plugin
const bookmarkDecorationPlugin = ViewPlugin.fromClass(BookmarkDecorationPlugin, {
decorations: (plugin: BookmarkDecorationPlugin) => plugin.decorations,
});
export class GutterDecorationManager {
createBookmarkGutter(): Extension {
return [bookmarkDecorationPlugin];
}
updateBookmarkDecoration(view: EditorView, lineNumber: number, hasBookmark: boolean): void {
view.dispatch({
effects: [updateBookmarkEffect.of({ lineNumber, hasBookmark })]
});
}
// Helper method to sync decorations with document content
syncWithDocument(view: EditorView): void {
// The view plugin will automatically sync when document changes
// No manual sync needed
}
}