@tooee/marks 0.1.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/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mark-set-builder.d.ts +10 -0
- package/dist/mark-set-builder.d.ts.map +1 -0
- package/dist/mark-set-builder.js +28 -0
- package/dist/mark-set-builder.js.map +1 -0
- package/dist/mark-set.d.ts +21 -0
- package/dist/mark-set.d.ts.map +1 -0
- package/dist/mark-set.js +103 -0
- package/dist/mark-set.js.map +1 -0
- package/dist/mark-state.d.ts +12 -0
- package/dist/mark-state.d.ts.map +1 -0
- package/dist/mark-state.js +45 -0
- package/dist/mark-state.js.map +1 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -0
- package/src/index.ts +8 -0
- package/src/mark-set-builder.ts +33 -0
- package/src/mark-set.ts +120 -0
- package/src/mark-state.ts +64 -0
- package/src/types.ts +38 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { MarkPosition, MarkRange, MarkStyle, Mark, MarkPriority } from "./types.js";
|
|
2
|
+
export { MarkPriorities } from "./types.js";
|
|
3
|
+
export { MarkSet } from "./mark-set.js";
|
|
4
|
+
export { MarkSetBuilder } from "./mark-set-builder.js";
|
|
5
|
+
export type { MarkState } from "./mark-state.js";
|
|
6
|
+
export { createMarkState, updateMarkState } from "./mark-state.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAGtD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Mark, MarkPosition, MarkStyle } from "./types.js";
|
|
2
|
+
import { MarkSet } from "./mark-set.js";
|
|
3
|
+
export declare class MarkSetBuilder {
|
|
4
|
+
#private;
|
|
5
|
+
addLine(line: number, style: MarkStyle, data?: unknown): this;
|
|
6
|
+
addRange(from: MarkPosition, to: MarkPosition, style: MarkStyle, data?: unknown): this;
|
|
7
|
+
addMark(mark: Mark): this;
|
|
8
|
+
build(namespace: string, priority: number): MarkSet;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=mark-set-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-set-builder.d.ts","sourceRoot":"","sources":["../src/mark-set-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,qBAAa,cAAc;;IAGzB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAS7D,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAStF,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAKzB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGpD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MarkSet } from "./mark-set.js";
|
|
2
|
+
export class MarkSetBuilder {
|
|
3
|
+
#marks = [];
|
|
4
|
+
addLine(line, style, data) {
|
|
5
|
+
this.#marks.push({
|
|
6
|
+
range: { from: { line }, to: { line } },
|
|
7
|
+
style,
|
|
8
|
+
data,
|
|
9
|
+
});
|
|
10
|
+
return this;
|
|
11
|
+
}
|
|
12
|
+
addRange(from, to, style, data) {
|
|
13
|
+
this.#marks.push({
|
|
14
|
+
range: { from, to },
|
|
15
|
+
style,
|
|
16
|
+
data,
|
|
17
|
+
});
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
addMark(mark) {
|
|
21
|
+
this.#marks.push(mark);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
build(namespace, priority) {
|
|
25
|
+
return new MarkSet(namespace, priority, this.#marks);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=mark-set-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-set-builder.js","sourceRoot":"","sources":["../src/mark-set-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,OAAO,cAAc;IAChB,MAAM,GAAW,EAAE,CAAA;IAE5B,OAAO,CAAC,IAAY,EAAE,KAAgB,EAAE,IAAc;QACpD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;YACvC,KAAK;YACL,IAAI;SACL,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAED,QAAQ,CAAC,IAAkB,EAAE,EAAgB,EAAE,KAAgB,EAAE,IAAc;QAC7E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnB,KAAK;YACL,IAAI;SACL,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CAAC,IAAU;QAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,SAAiB,EAAE,QAAgB;QACvC,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACtD,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Mark } from "./types.js";
|
|
2
|
+
export declare class MarkSet {
|
|
3
|
+
#private;
|
|
4
|
+
readonly namespace: string;
|
|
5
|
+
readonly priority: number;
|
|
6
|
+
constructor(namespace: string, priority: number, marks: Mark[]);
|
|
7
|
+
get size(): number;
|
|
8
|
+
marksAtLine(line: number): Mark[];
|
|
9
|
+
marksInRange(from: number, to: number): Mark[];
|
|
10
|
+
forVisibleRows(from: number, to: number): Generator<{
|
|
11
|
+
row: number;
|
|
12
|
+
background?: string;
|
|
13
|
+
gutterBackground?: string;
|
|
14
|
+
sign?: {
|
|
15
|
+
text: string;
|
|
16
|
+
fg?: string;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
[Symbol.iterator](): Iterator<Mark>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=mark-set.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-set.d.ts","sourceRoot":"","sources":["../src/mark-set.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAWtC,qBAAa,OAAO;;IAClB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAKb,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;IAe9D,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE;IAgCjC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE;IAY7C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;QACnD,GAAG,EAAE,MAAM,CAAA;QACX,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,IAAI,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KACrC,CAAC;IA8BF,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC;CAGpC"}
|
package/dist/mark-set.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
function deepFreezeMark(mark) {
|
|
2
|
+
Object.freeze(mark.range.from);
|
|
3
|
+
Object.freeze(mark.range.to);
|
|
4
|
+
Object.freeze(mark.range);
|
|
5
|
+
Object.freeze(mark.style);
|
|
6
|
+
Object.freeze(mark);
|
|
7
|
+
return mark;
|
|
8
|
+
}
|
|
9
|
+
export class MarkSet {
|
|
10
|
+
namespace;
|
|
11
|
+
priority;
|
|
12
|
+
#marks;
|
|
13
|
+
// #maxEnd[i] = max to.line among marks[0..i], enables O(1) early exit in backward scan
|
|
14
|
+
#maxEnd;
|
|
15
|
+
constructor(namespace, priority, marks) {
|
|
16
|
+
this.namespace = namespace;
|
|
17
|
+
this.priority = priority;
|
|
18
|
+
const sorted = [...marks]
|
|
19
|
+
.sort((a, b) => a.range.from.line - b.range.from.line)
|
|
20
|
+
.map(deepFreezeMark);
|
|
21
|
+
this.#marks = sorted;
|
|
22
|
+
const maxEnd = new Array(sorted.length);
|
|
23
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
24
|
+
const end = sorted[i].range.to.line;
|
|
25
|
+
maxEnd[i] = i === 0 ? end : Math.max(maxEnd[i - 1], end);
|
|
26
|
+
}
|
|
27
|
+
this.#maxEnd = maxEnd;
|
|
28
|
+
}
|
|
29
|
+
get size() {
|
|
30
|
+
return this.#marks.length;
|
|
31
|
+
}
|
|
32
|
+
marksAtLine(line) {
|
|
33
|
+
const results = [];
|
|
34
|
+
// Binary search for the first mark whose from.line >= line
|
|
35
|
+
let lo = 0;
|
|
36
|
+
let hi = this.#marks.length;
|
|
37
|
+
while (lo < hi) {
|
|
38
|
+
const mid = (lo + hi) >>> 1;
|
|
39
|
+
if (this.#marks[mid].range.from.line < line) {
|
|
40
|
+
lo = mid + 1;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
hi = mid;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (let i = lo - 1; i >= 0; i--) {
|
|
47
|
+
// If the max end line among marks[0..i] is before the target,
|
|
48
|
+
// no mark at or before index i can reach the target line.
|
|
49
|
+
if (this.#maxEnd[i] < line)
|
|
50
|
+
break;
|
|
51
|
+
const mark = this.#marks[i];
|
|
52
|
+
if (mark.range.to.line >= line) {
|
|
53
|
+
results.push(mark);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Marks starting at this line
|
|
57
|
+
for (let i = lo; i < this.#marks.length; i++) {
|
|
58
|
+
const mark = this.#marks[i];
|
|
59
|
+
if (mark.range.from.line > line)
|
|
60
|
+
break;
|
|
61
|
+
// from.line === line
|
|
62
|
+
results.push(mark);
|
|
63
|
+
}
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
marksInRange(from, to) {
|
|
67
|
+
const results = [];
|
|
68
|
+
for (const mark of this.#marks) {
|
|
69
|
+
// A mark overlaps [from, to] if mark.from.line <= to && mark.to.line >= from
|
|
70
|
+
if (mark.range.from.line > to)
|
|
71
|
+
break;
|
|
72
|
+
if (mark.range.to.line >= from) {
|
|
73
|
+
results.push(mark);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
*forVisibleRows(from, to) {
|
|
79
|
+
const marks = this.marksInRange(from, to);
|
|
80
|
+
for (const mark of marks) {
|
|
81
|
+
const startLine = Math.max(from, mark.range.from.line);
|
|
82
|
+
const endLine = Math.min(to, mark.range.to.line);
|
|
83
|
+
for (let line = startLine; line <= endLine; line++) {
|
|
84
|
+
const decoration = { row: line };
|
|
85
|
+
if (mark.style.background) {
|
|
86
|
+
decoration.background = mark.style.background;
|
|
87
|
+
}
|
|
88
|
+
if (mark.style.gutterBackground) {
|
|
89
|
+
decoration.gutterBackground = mark.style.gutterBackground;
|
|
90
|
+
}
|
|
91
|
+
const signText = (mark.style.signBefore ?? "") + (mark.style.signAfter ?? "");
|
|
92
|
+
if (signText) {
|
|
93
|
+
decoration.sign = { text: signText, fg: mark.style.foreground };
|
|
94
|
+
}
|
|
95
|
+
yield decoration;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
[Symbol.iterator]() {
|
|
100
|
+
return this.#marks[Symbol.iterator]();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=mark-set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-set.js","sourceRoot":"","sources":["../src/mark-set.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Mark, MarkStyle } from "./types.js";
|
|
2
|
+
import type { MarkSet } from "./mark-set.js";
|
|
3
|
+
export interface MarkState {
|
|
4
|
+
readonly sets: readonly MarkSet[];
|
|
5
|
+
readonly namespaces: readonly string[];
|
|
6
|
+
marksAtLine(line: number): Mark[];
|
|
7
|
+
effectiveStyleAtLine(line: number): MarkStyle | null;
|
|
8
|
+
getSet(namespace: string): MarkSet | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function createMarkState(sets: MarkSet[]): MarkState;
|
|
11
|
+
export declare function updateMarkState(state: MarkState, namespace: string, newSet: MarkSet | null): MarkState;
|
|
12
|
+
//# sourceMappingURL=mark-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-state.d.ts","sourceRoot":"","sources":["../src/mark-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,CAAA;IACjC,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACpD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAC/C;AAqCD,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,CAE1D;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,IAAI,GACrB,SAAS,CASX"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function makeMarkState(sets) {
|
|
2
|
+
const sortedSets = [...sets].sort((a, b) => a.priority - b.priority);
|
|
3
|
+
const namespaces = sortedSets.map((s) => s.namespace);
|
|
4
|
+
return {
|
|
5
|
+
sets: sortedSets,
|
|
6
|
+
namespaces,
|
|
7
|
+
marksAtLine(line) {
|
|
8
|
+
const results = [];
|
|
9
|
+
for (const set of sortedSets) {
|
|
10
|
+
const marks = set.marksAtLine(line);
|
|
11
|
+
for (const mark of marks) {
|
|
12
|
+
results.push({
|
|
13
|
+
...mark,
|
|
14
|
+
priority: mark.priority ?? set.priority,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
results.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
19
|
+
return results;
|
|
20
|
+
},
|
|
21
|
+
effectiveStyleAtLine(line) {
|
|
22
|
+
const marks = this.marksAtLine(line);
|
|
23
|
+
if (marks.length === 0)
|
|
24
|
+
return null;
|
|
25
|
+
return marks[marks.length - 1].style;
|
|
26
|
+
},
|
|
27
|
+
getSet(namespace) {
|
|
28
|
+
return sortedSets.find((s) => s.namespace === namespace);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function createMarkState(sets) {
|
|
33
|
+
return makeMarkState(sets);
|
|
34
|
+
}
|
|
35
|
+
export function updateMarkState(state, namespace, newSet) {
|
|
36
|
+
if (newSet && newSet.namespace !== namespace) {
|
|
37
|
+
throw new Error(`Namespace mismatch: expected "${namespace}", got "${newSet.namespace}"`);
|
|
38
|
+
}
|
|
39
|
+
const filtered = state.sets.filter((s) => s.namespace !== namespace);
|
|
40
|
+
if (newSet) {
|
|
41
|
+
filtered.push(newSet);
|
|
42
|
+
}
|
|
43
|
+
return makeMarkState(filtered);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=mark-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark-state.js","sourceRoot":"","sources":["../src/mark-state.ts"],"names":[],"mappings":"AAWA,SAAS,aAAa,CAAC,IAAwB;IAC7C,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;IACpE,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAErD,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,UAAU;QAEV,WAAW,CAAC,IAAY;YACtB,MAAM,OAAO,GAAW,EAAE,CAAA;YAC1B,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;gBACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,OAAO,CAAC,IAAI,CAAC;wBACX,GAAG,IAAI;wBACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;qBACxC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAA;YAC7D,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,oBAAoB,CAAC,IAAY;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;YACnC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAA;QACtC,CAAC;QAED,MAAM,CAAC,SAAiB;YACtB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAA;QAC1D,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAe;IAC7C,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAgB,EAChB,SAAiB,EACjB,MAAsB;IAEtB,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,iCAAiC,SAAS,WAAW,MAAM,CAAC,SAAS,GAAG,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAA;IACpE,IAAI,MAAM,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACvB,CAAC;IACD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface MarkPosition {
|
|
2
|
+
line: number;
|
|
3
|
+
}
|
|
4
|
+
export interface MarkRange {
|
|
5
|
+
from: MarkPosition;
|
|
6
|
+
to: MarkPosition;
|
|
7
|
+
}
|
|
8
|
+
export interface MarkStyle {
|
|
9
|
+
background?: string;
|
|
10
|
+
gutterBackground?: string;
|
|
11
|
+
foreground?: string;
|
|
12
|
+
signBefore?: string;
|
|
13
|
+
signAfter?: string;
|
|
14
|
+
themeColor?: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Mark<T = unknown> {
|
|
18
|
+
id?: string;
|
|
19
|
+
range: MarkRange;
|
|
20
|
+
style: MarkStyle;
|
|
21
|
+
data?: T;
|
|
22
|
+
priority?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare const MarkPriorities: {
|
|
25
|
+
readonly SEARCH_MATCH: 100;
|
|
26
|
+
readonly DIAGNOSTIC: 150;
|
|
27
|
+
readonly TOGGLED: 200;
|
|
28
|
+
readonly USER: 250;
|
|
29
|
+
readonly SELECTION: 300;
|
|
30
|
+
readonly CURRENT_MATCH: 400;
|
|
31
|
+
readonly CURSOR: 500;
|
|
32
|
+
};
|
|
33
|
+
export type MarkPriority = (typeof MarkPriorities)[keyof typeof MarkPriorities];
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,YAAY,CAAA;IAClB,EAAE,EAAE,YAAY,CAAA;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,IAAI,CAAC,CAAC,GAAG,OAAO;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,SAAS,CAAA;IAChB,KAAK,EAAE,SAAS,CAAA;IAChB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,cAAc;;;;;;;;CAQjB,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2BA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,GAAG;IACjB,UAAU,EAAE,GAAG;IACf,OAAO,EAAE,GAAG;IACZ,IAAI,EAAE,GAAG;IACT,SAAS,EAAE,GAAG;IACd,aAAa,EAAE,GAAG;IAClB,MAAM,EAAE,GAAG;CACH,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tooee/marks",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Unified decoration/annotation system for Tooee renderers",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"decorations",
|
|
8
|
+
"marks",
|
|
9
|
+
"opentui",
|
|
10
|
+
"terminal",
|
|
11
|
+
"tui"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/gingerhendrix/tooee",
|
|
14
|
+
"bugs": "https://github.com/gingerhendrix/tooee/issues",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Gareth Andrew",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/gingerhendrix/tooee.git",
|
|
20
|
+
"directory": "packages/marks"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": {
|
|
30
|
+
"@tooee/source": "./src/index.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/bun": "^1.3.10",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { MarkPosition, MarkRange, MarkStyle, Mark, MarkPriority } from "./types.js"
|
|
2
|
+
export { MarkPriorities } from "./types.js"
|
|
3
|
+
|
|
4
|
+
export { MarkSet } from "./mark-set.js"
|
|
5
|
+
export { MarkSetBuilder } from "./mark-set-builder.js"
|
|
6
|
+
|
|
7
|
+
export type { MarkState } from "./mark-state.js"
|
|
8
|
+
export { createMarkState, updateMarkState } from "./mark-state.js"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Mark, MarkPosition, MarkStyle } from "./types.js"
|
|
2
|
+
import { MarkSet } from "./mark-set.js"
|
|
3
|
+
|
|
4
|
+
export class MarkSetBuilder {
|
|
5
|
+
readonly #marks: Mark[] = []
|
|
6
|
+
|
|
7
|
+
addLine(line: number, style: MarkStyle, data?: unknown): this {
|
|
8
|
+
this.#marks.push({
|
|
9
|
+
range: { from: { line }, to: { line } },
|
|
10
|
+
style,
|
|
11
|
+
data,
|
|
12
|
+
})
|
|
13
|
+
return this
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addRange(from: MarkPosition, to: MarkPosition, style: MarkStyle, data?: unknown): this {
|
|
17
|
+
this.#marks.push({
|
|
18
|
+
range: { from, to },
|
|
19
|
+
style,
|
|
20
|
+
data,
|
|
21
|
+
})
|
|
22
|
+
return this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
addMark(mark: Mark): this {
|
|
26
|
+
this.#marks.push(mark)
|
|
27
|
+
return this
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
build(namespace: string, priority: number): MarkSet {
|
|
31
|
+
return new MarkSet(namespace, priority, this.#marks)
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/mark-set.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Mark } from "./types.js"
|
|
2
|
+
|
|
3
|
+
function deepFreezeMark(mark: Mark): Mark {
|
|
4
|
+
Object.freeze(mark.range.from)
|
|
5
|
+
Object.freeze(mark.range.to)
|
|
6
|
+
Object.freeze(mark.range)
|
|
7
|
+
Object.freeze(mark.style)
|
|
8
|
+
Object.freeze(mark)
|
|
9
|
+
return mark
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class MarkSet {
|
|
13
|
+
readonly namespace: string
|
|
14
|
+
readonly priority: number
|
|
15
|
+
readonly #marks: readonly Mark[]
|
|
16
|
+
// #maxEnd[i] = max to.line among marks[0..i], enables O(1) early exit in backward scan
|
|
17
|
+
readonly #maxEnd: readonly number[]
|
|
18
|
+
|
|
19
|
+
constructor(namespace: string, priority: number, marks: Mark[]) {
|
|
20
|
+
this.namespace = namespace
|
|
21
|
+
this.priority = priority
|
|
22
|
+
const sorted = [...marks]
|
|
23
|
+
.sort((a, b) => a.range.from.line - b.range.from.line)
|
|
24
|
+
.map(deepFreezeMark)
|
|
25
|
+
this.#marks = sorted
|
|
26
|
+
const maxEnd: number[] = new Array(sorted.length)
|
|
27
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
28
|
+
const end = sorted[i].range.to.line
|
|
29
|
+
maxEnd[i] = i === 0 ? end : Math.max(maxEnd[i - 1], end)
|
|
30
|
+
}
|
|
31
|
+
this.#maxEnd = maxEnd
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get size(): number {
|
|
35
|
+
return this.#marks.length
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
marksAtLine(line: number): Mark[] {
|
|
39
|
+
const results: Mark[] = []
|
|
40
|
+
// Binary search for the first mark whose from.line >= line
|
|
41
|
+
let lo = 0
|
|
42
|
+
let hi = this.#marks.length
|
|
43
|
+
while (lo < hi) {
|
|
44
|
+
const mid = (lo + hi) >>> 1
|
|
45
|
+
if (this.#marks[mid].range.from.line < line) {
|
|
46
|
+
lo = mid + 1
|
|
47
|
+
} else {
|
|
48
|
+
hi = mid
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (let i = lo - 1; i >= 0; i--) {
|
|
52
|
+
// If the max end line among marks[0..i] is before the target,
|
|
53
|
+
// no mark at or before index i can reach the target line.
|
|
54
|
+
if (this.#maxEnd[i] < line) break
|
|
55
|
+
const mark = this.#marks[i]
|
|
56
|
+
if (mark.range.to.line >= line) {
|
|
57
|
+
results.push(mark)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Marks starting at this line
|
|
61
|
+
for (let i = lo; i < this.#marks.length; i++) {
|
|
62
|
+
const mark = this.#marks[i]
|
|
63
|
+
if (mark.range.from.line > line) break
|
|
64
|
+
// from.line === line
|
|
65
|
+
results.push(mark)
|
|
66
|
+
}
|
|
67
|
+
return results
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
marksInRange(from: number, to: number): Mark[] {
|
|
71
|
+
const results: Mark[] = []
|
|
72
|
+
for (const mark of this.#marks) {
|
|
73
|
+
// A mark overlaps [from, to] if mark.from.line <= to && mark.to.line >= from
|
|
74
|
+
if (mark.range.from.line > to) break
|
|
75
|
+
if (mark.range.to.line >= from) {
|
|
76
|
+
results.push(mark)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return results
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
*forVisibleRows(from: number, to: number): Generator<{
|
|
83
|
+
row: number
|
|
84
|
+
background?: string
|
|
85
|
+
gutterBackground?: string
|
|
86
|
+
sign?: { text: string; fg?: string }
|
|
87
|
+
}> {
|
|
88
|
+
const marks = this.marksInRange(from, to)
|
|
89
|
+
for (const mark of marks) {
|
|
90
|
+
const startLine = Math.max(from, mark.range.from.line)
|
|
91
|
+
const endLine = Math.min(to, mark.range.to.line)
|
|
92
|
+
for (let line = startLine; line <= endLine; line++) {
|
|
93
|
+
const decoration: {
|
|
94
|
+
row: number
|
|
95
|
+
background?: string
|
|
96
|
+
gutterBackground?: string
|
|
97
|
+
sign?: { text: string; fg?: string }
|
|
98
|
+
} = { row: line }
|
|
99
|
+
|
|
100
|
+
if (mark.style.background) {
|
|
101
|
+
decoration.background = mark.style.background
|
|
102
|
+
}
|
|
103
|
+
if (mark.style.gutterBackground) {
|
|
104
|
+
decoration.gutterBackground = mark.style.gutterBackground
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const signText = (mark.style.signBefore ?? "") + (mark.style.signAfter ?? "")
|
|
108
|
+
if (signText) {
|
|
109
|
+
decoration.sign = { text: signText, fg: mark.style.foreground }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
yield decoration
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
[Symbol.iterator](): Iterator<Mark> {
|
|
118
|
+
return this.#marks[Symbol.iterator]()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Mark, MarkStyle } from "./types.js"
|
|
2
|
+
import type { MarkSet } from "./mark-set.js"
|
|
3
|
+
|
|
4
|
+
export interface MarkState {
|
|
5
|
+
readonly sets: readonly MarkSet[]
|
|
6
|
+
readonly namespaces: readonly string[]
|
|
7
|
+
marksAtLine(line: number): Mark[]
|
|
8
|
+
effectiveStyleAtLine(line: number): MarkStyle | null
|
|
9
|
+
getSet(namespace: string): MarkSet | undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function makeMarkState(sets: readonly MarkSet[]): MarkState {
|
|
13
|
+
const sortedSets = [...sets].sort((a, b) => a.priority - b.priority)
|
|
14
|
+
const namespaces = sortedSets.map((s) => s.namespace)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
sets: sortedSets,
|
|
18
|
+
namespaces,
|
|
19
|
+
|
|
20
|
+
marksAtLine(line: number): Mark[] {
|
|
21
|
+
const results: Mark[] = []
|
|
22
|
+
for (const set of sortedSets) {
|
|
23
|
+
const marks = set.marksAtLine(line)
|
|
24
|
+
for (const mark of marks) {
|
|
25
|
+
results.push({
|
|
26
|
+
...mark,
|
|
27
|
+
priority: mark.priority ?? set.priority,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
results.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
|
|
32
|
+
return results
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
effectiveStyleAtLine(line: number): MarkStyle | null {
|
|
36
|
+
const marks = this.marksAtLine(line)
|
|
37
|
+
if (marks.length === 0) return null
|
|
38
|
+
return marks[marks.length - 1].style
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
getSet(namespace: string): MarkSet | undefined {
|
|
42
|
+
return sortedSets.find((s) => s.namespace === namespace)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createMarkState(sets: MarkSet[]): MarkState {
|
|
48
|
+
return makeMarkState(sets)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function updateMarkState(
|
|
52
|
+
state: MarkState,
|
|
53
|
+
namespace: string,
|
|
54
|
+
newSet: MarkSet | null,
|
|
55
|
+
): MarkState {
|
|
56
|
+
if (newSet && newSet.namespace !== namespace) {
|
|
57
|
+
throw new Error(`Namespace mismatch: expected "${namespace}", got "${newSet.namespace}"`)
|
|
58
|
+
}
|
|
59
|
+
const filtered = state.sets.filter((s) => s.namespace !== namespace)
|
|
60
|
+
if (newSet) {
|
|
61
|
+
filtered.push(newSet)
|
|
62
|
+
}
|
|
63
|
+
return makeMarkState(filtered)
|
|
64
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface MarkPosition {
|
|
2
|
+
line: number
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface MarkRange {
|
|
6
|
+
from: MarkPosition
|
|
7
|
+
to: MarkPosition
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MarkStyle {
|
|
11
|
+
background?: string
|
|
12
|
+
gutterBackground?: string
|
|
13
|
+
foreground?: string
|
|
14
|
+
signBefore?: string
|
|
15
|
+
signAfter?: string
|
|
16
|
+
themeColor?: string
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Mark<T = unknown> {
|
|
21
|
+
id?: string
|
|
22
|
+
range: MarkRange
|
|
23
|
+
style: MarkStyle
|
|
24
|
+
data?: T
|
|
25
|
+
priority?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const MarkPriorities = {
|
|
29
|
+
SEARCH_MATCH: 100,
|
|
30
|
+
DIAGNOSTIC: 150,
|
|
31
|
+
TOGGLED: 200,
|
|
32
|
+
USER: 250,
|
|
33
|
+
SELECTION: 300,
|
|
34
|
+
CURRENT_MATCH: 400,
|
|
35
|
+
CURSOR: 500,
|
|
36
|
+
} as const
|
|
37
|
+
|
|
38
|
+
export type MarkPriority = (typeof MarkPriorities)[keyof typeof MarkPriorities]
|