@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.
@@ -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,5 @@
1
+ export { MarkPriorities } from "./types.js";
2
+ export { MarkSet } from "./mark-set.js";
3
+ export { MarkSetBuilder } from "./mark-set-builder.js";
4
+ export { createMarkState, updateMarkState } from "./mark-state.js";
5
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -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"}
@@ -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,10 @@
1
+ export const MarkPriorities = {
2
+ SEARCH_MATCH: 100,
3
+ DIAGNOSTIC: 150,
4
+ TOGGLED: 200,
5
+ USER: 250,
6
+ SELECTION: 300,
7
+ CURRENT_MATCH: 400,
8
+ CURSOR: 500,
9
+ };
10
+ //# sourceMappingURL=types.js.map
@@ -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
+ }
@@ -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]