@pi-archimedes/diff 0.7.0 → 0.8.0
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/package.json +1 -1
- package/src/ansi/codes.ts +3 -34
- package/src/ansi/index.ts +0 -1
- package/src/core/diff.ts +4 -0
- package/src/diff-component.ts +32 -7
- package/src/index.ts +4 -0
- package/src/render/split.ts +2 -15
- package/src/render/unified.ts +2 -15
- package/src/word-diff.ts +8 -3
package/package.json
CHANGED
package/src/ansi/codes.ts
CHANGED
|
@@ -54,18 +54,6 @@ export const DEFAULT_DIFF_BG: DiffBg = {
|
|
|
54
54
|
divider: `${FG_RULE}│\x1b[0m`,
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
// Legacy aliases — used by word-diff.ts during transition. Will be removed.
|
|
58
|
-
// These MUST be mutable and live in the same module as the functions that mutate them.
|
|
59
|
-
export let BG_ADD = DEFAULT_DIFF_BG.bgAdd;
|
|
60
|
-
export let BG_DEL = DEFAULT_DIFF_BG.bgDel;
|
|
61
|
-
export let BG_ADD_W = DEFAULT_DIFF_BG.bgAddW;
|
|
62
|
-
export let BG_DEL_W = DEFAULT_DIFF_BG.bgDelW;
|
|
63
|
-
export let BG_GUTTER_ADD = DEFAULT_DIFF_BG.bgGutterAdd;
|
|
64
|
-
export let BG_GUTTER_DEL = DEFAULT_DIFF_BG.bgGutterDel;
|
|
65
|
-
export let BG_EMPTY = DEFAULT_DIFF_BG.bgEmpty;
|
|
66
|
-
export let BG_BASE = DEFAULT_DIFF_BG.bgBase;
|
|
67
|
-
export let DIVIDER = DEFAULT_DIFF_BG.divider;
|
|
68
|
-
|
|
69
57
|
// Theme cache key state — lives here since it's used by resolveDiffColors.
|
|
70
58
|
let _lastThemeKey: string | undefined;
|
|
71
59
|
|
|
@@ -91,21 +79,12 @@ export interface DiffColors {
|
|
|
91
79
|
export const DEFAULT_DIFF_COLORS: DiffColors = { fgAdd: FG_ADD, fgDel: FG_DEL, fgCtx: FG_DIM };
|
|
92
80
|
|
|
93
81
|
// ---------------------------------------------------------------------------
|
|
94
|
-
// Theme-aware color resolution
|
|
82
|
+
// Theme-aware color resolution
|
|
95
83
|
// ---------------------------------------------------------------------------
|
|
96
84
|
|
|
97
85
|
/** Reset auto-derived colors — call when theme changes. */
|
|
98
86
|
export function resetDiffColors(): void {
|
|
99
87
|
_lastThemeKey = undefined;
|
|
100
|
-
BG_ADD = DEFAULT_DIFF_BG.bgAdd;
|
|
101
|
-
BG_DEL = DEFAULT_DIFF_BG.bgDel;
|
|
102
|
-
BG_ADD_W = DEFAULT_DIFF_BG.bgAddW;
|
|
103
|
-
BG_DEL_W = DEFAULT_DIFF_BG.bgDelW;
|
|
104
|
-
BG_GUTTER_ADD = DEFAULT_DIFF_BG.bgGutterAdd;
|
|
105
|
-
BG_GUTTER_DEL = DEFAULT_DIFF_BG.bgGutterDel;
|
|
106
|
-
BG_EMPTY = DEFAULT_DIFF_BG.bgEmpty;
|
|
107
|
-
BG_BASE = DEFAULT_DIFF_BG.bgBase;
|
|
108
|
-
DIVIDER = DEFAULT_DIFF_BG.divider;
|
|
109
88
|
}
|
|
110
89
|
|
|
111
90
|
export function themeCacheKey(theme?: any): string {
|
|
@@ -130,18 +109,8 @@ export { deriveBgFromTheme };
|
|
|
130
109
|
export function resolveDiffColors(theme?: any): DiffColors {
|
|
131
110
|
const themeKey = themeCacheKey(theme);
|
|
132
111
|
|
|
133
|
-
// Re-derive
|
|
134
|
-
if (themeKey !== _lastThemeKey
|
|
135
|
-
const dbg = deriveBgFromTheme(theme);
|
|
136
|
-
BG_ADD = dbg.bgAdd;
|
|
137
|
-
BG_DEL = dbg.bgDel;
|
|
138
|
-
BG_ADD_W = dbg.bgAddW;
|
|
139
|
-
BG_DEL_W = dbg.bgDelW;
|
|
140
|
-
BG_GUTTER_ADD = dbg.bgGutterAdd;
|
|
141
|
-
BG_GUTTER_DEL = dbg.bgGutterDel;
|
|
142
|
-
BG_EMPTY = dbg.bgEmpty;
|
|
143
|
-
BG_BASE = dbg.bgBase;
|
|
144
|
-
DIVIDER = dbg.divider;
|
|
112
|
+
// Re-derive theme key cache (bg colors are now passed explicitly via DiffBg)
|
|
113
|
+
if (themeKey !== _lastThemeKey) {
|
|
145
114
|
_lastThemeKey = themeKey;
|
|
146
115
|
}
|
|
147
116
|
if (!theme?.getFgAnsi) return DEFAULT_DIFF_COLORS;
|
package/src/ansi/index.ts
CHANGED
|
@@ -5,7 +5,6 @@ export {
|
|
|
5
5
|
FG_ADD, FG_DEL, FG_DIM, FG_LNUM, FG_RULE, FG_SAFE_MUTED, FG_STRIPE,
|
|
6
6
|
BORDER_BAR,
|
|
7
7
|
DEFAULT_DIFF_BG,
|
|
8
|
-
BG_ADD, BG_DEL, BG_ADD_W, BG_DEL_W, BG_GUTTER_ADD, BG_GUTTER_DEL, BG_EMPTY, BG_BASE, DIVIDER,
|
|
9
8
|
ANSI_RE, ANSI_CAPTURE_RE, ANSI_PARAM_CAPTURE_RE,
|
|
10
9
|
DEFAULT_DIFF_COLORS,
|
|
11
10
|
resetDiffColors, themeCacheKey, resolveDiffColors,
|
package/src/core/diff.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface ParsedDiff {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function parseDiff(oldContent: string, newContent: string, ctx = 3): ParsedDiff {
|
|
18
|
+
// Early return for identical content — skip expensive diff computation
|
|
19
|
+
if (oldContent === newContent) {
|
|
20
|
+
return { lines: [], added: 0, removed: 0, chars: 0 };
|
|
21
|
+
}
|
|
18
22
|
const patch = Diff.structuredPatch("", "", oldContent, newContent, "", "", { context: ctx });
|
|
19
23
|
const lines: DiffLine[] = [];
|
|
20
24
|
let added = 0;
|
package/src/diff-component.ts
CHANGED
|
@@ -42,8 +42,7 @@ import type { ParsedDiff } from "./core/diff.js";
|
|
|
42
42
|
export class DiffComponent implements Component {
|
|
43
43
|
private diff: ParsedDiff;
|
|
44
44
|
private language: BundledLanguage | undefined;
|
|
45
|
-
private
|
|
46
|
-
private dbg: DiffBg;
|
|
45
|
+
private theme: Theme;
|
|
47
46
|
private maxLines: number;
|
|
48
47
|
|
|
49
48
|
/** Box wrapper with base background — makes the diff self-contained. */
|
|
@@ -58,6 +57,9 @@ export class DiffComponent implements Component {
|
|
|
58
57
|
/** Final cached output (box-wrapped). */
|
|
59
58
|
private _cachedLines: string[] | undefined;
|
|
60
59
|
|
|
60
|
+
/** Theme cache key — invalidates derived colors on theme change. */
|
|
61
|
+
private _themeKey: string | undefined;
|
|
62
|
+
|
|
61
63
|
constructor(
|
|
62
64
|
diff: ParsedDiff,
|
|
63
65
|
language: BundledLanguage | undefined,
|
|
@@ -66,12 +68,31 @@ export class DiffComponent implements Component {
|
|
|
66
68
|
) {
|
|
67
69
|
this.diff = diff;
|
|
68
70
|
this.language = language;
|
|
69
|
-
this.
|
|
70
|
-
this.dbg = deriveBgFromTheme(theme);
|
|
71
|
+
this.theme = theme;
|
|
71
72
|
this.maxLines = maxLines;
|
|
72
73
|
|
|
73
74
|
// Box provides base background so the diff is self-contained.
|
|
74
|
-
|
|
75
|
+
// Background function is re-derived on each render via _deriveBg().
|
|
76
|
+
this.shell = new Box(0, 0, (s: string) => this._deriveBg().bgBase + s + this._deriveBg().rst);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Re-derive colors from the current theme — handles theme changes. */
|
|
80
|
+
private _deriveColors(): { dc: DiffColors; dbg: DiffBg } {
|
|
81
|
+
const themeKey = Ansi.themeCacheKey(this.theme);
|
|
82
|
+
if (themeKey === this._themeKey && this._cachedDc && this._cachedDbg) {
|
|
83
|
+
return { dc: this._cachedDc, dbg: this._cachedDbg };
|
|
84
|
+
}
|
|
85
|
+
this._themeKey = themeKey;
|
|
86
|
+
this._cachedDc = Ansi.resolveDiffColors(this.theme);
|
|
87
|
+
this._cachedDbg = deriveBgFromTheme(this.theme);
|
|
88
|
+
return { dc: this._cachedDc, dbg: this._cachedDbg };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private _cachedDc: DiffColors | undefined;
|
|
92
|
+
private _cachedDbg: DiffBg | undefined;
|
|
93
|
+
|
|
94
|
+
private _deriveBg(): DiffBg {
|
|
95
|
+
return this._deriveColors().dbg;
|
|
75
96
|
}
|
|
76
97
|
|
|
77
98
|
render(width: number): string[] {
|
|
@@ -96,13 +117,14 @@ export class DiffComponent implements Component {
|
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
// Start async render for this width
|
|
120
|
+
const { dc, dbg } = this._deriveColors();
|
|
99
121
|
const promise = renderSplitLines(
|
|
100
122
|
this.diff,
|
|
101
123
|
this.language,
|
|
102
124
|
width,
|
|
103
125
|
this.maxLines,
|
|
104
|
-
|
|
105
|
-
|
|
126
|
+
dc,
|
|
127
|
+
dbg,
|
|
106
128
|
);
|
|
107
129
|
this._renderPromise = promise;
|
|
108
130
|
|
|
@@ -135,5 +157,8 @@ export class DiffComponent implements Component {
|
|
|
135
157
|
invalidate(): void {
|
|
136
158
|
this._cachedWidth = undefined;
|
|
137
159
|
this._cachedLines = undefined;
|
|
160
|
+
this._themeKey = undefined; // Force re-derive on theme change
|
|
161
|
+
this._cachedDc = undefined;
|
|
162
|
+
this._cachedDbg = undefined;
|
|
138
163
|
}
|
|
139
164
|
}
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { setConfigGetter as setShikiConfig } from "./shiki.js";
|
|
|
11
11
|
import { setConfigGetter as setRenderConfig } from "./render/index.js";
|
|
12
12
|
import { DiffComponent } from "./diff-component.js";
|
|
13
13
|
import type { DiffBg, DiffColors } from "./ansi/index.js";
|
|
14
|
+
import { resetDiffColors } from "./ansi/index.js";
|
|
14
15
|
import { registerWriteTool, registerEditTool } from "./tools/index.js";
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
@@ -78,6 +79,9 @@ export function registerDiffTools(
|
|
|
78
79
|
): void {
|
|
79
80
|
_readConfig = readConfig;
|
|
80
81
|
|
|
82
|
+
// Reset theme-derived colors so they are re-derived from the current theme
|
|
83
|
+
resetDiffColors();
|
|
84
|
+
|
|
81
85
|
// Wire config getters into submodules
|
|
82
86
|
setShikiConfig(() => getConfig());
|
|
83
87
|
setRenderConfig(() => getConfig());
|
package/src/render/split.ts
CHANGED
|
@@ -34,20 +34,7 @@ export async function renderSplit(
|
|
|
34
34
|
dc: DiffColors = DEFAULT_DIFF_COLORS,
|
|
35
35
|
): Promise<string> {
|
|
36
36
|
const width = process.stdout.columns ?? DEFAULT_TERM_WIDTH;
|
|
37
|
-
|
|
38
|
-
const dbg: DiffBg = {
|
|
39
|
-
bgAdd: Ansi.BG_ADD,
|
|
40
|
-
bgDel: Ansi.BG_DEL,
|
|
41
|
-
bgAddW: Ansi.BG_ADD_W,
|
|
42
|
-
bgDelW: Ansi.BG_DEL_W,
|
|
43
|
-
bgGutterAdd: Ansi.BG_GUTTER_ADD,
|
|
44
|
-
bgGutterDel: Ansi.BG_GUTTER_DEL,
|
|
45
|
-
bgEmpty: Ansi.BG_EMPTY,
|
|
46
|
-
bgBase: Ansi.BG_BASE,
|
|
47
|
-
rst: Ansi.BG_BASE === "\x1b[49m" ? "\x1b[0m" : `\x1b[0m${Ansi.BG_BASE}`,
|
|
48
|
-
divider: Ansi.DIVIDER,
|
|
49
|
-
};
|
|
50
|
-
const lines = await renderSplitLines(diff, language, width, max, dc, dbg);
|
|
37
|
+
const lines = await renderSplitLines(diff, language, width, max, dc, DEFAULT_DIFF_BG);
|
|
51
38
|
return lines.join("\n");
|
|
52
39
|
}
|
|
53
40
|
|
|
@@ -160,7 +147,7 @@ export async function renderSplitLines(
|
|
|
160
147
|
lResult = half_build(leftLine, lhl, wd.oldRanges, "left");
|
|
161
148
|
rResult = half_build(rightLine, rhl, wd.newRanges, "right");
|
|
162
149
|
} else if (paired && wd && wd.similarity >= WORD_DIFF_MIN_SIM && !canHL) {
|
|
163
|
-
const pwd = plainWordDiff(leftLine.content, rightLine.content);
|
|
150
|
+
const pwd = plainWordDiff(leftLine.content, rightLine.content, dbg);
|
|
164
151
|
lI++; rI++;
|
|
165
152
|
lResult = half_build(leftLine, pwd.old, null, "left");
|
|
166
153
|
rResult = half_build(rightLine, pwd.new, null, "right");
|
package/src/render/unified.ts
CHANGED
|
@@ -31,20 +31,7 @@ export async function renderUnified(
|
|
|
31
31
|
dc: DiffColors = DEFAULT_DIFF_COLORS,
|
|
32
32
|
): Promise<string> {
|
|
33
33
|
const width = process.stdout.columns ?? DEFAULT_TERM_WIDTH;
|
|
34
|
-
|
|
35
|
-
const dbg: DiffBg = {
|
|
36
|
-
bgAdd: Ansi.BG_ADD,
|
|
37
|
-
bgDel: Ansi.BG_DEL,
|
|
38
|
-
bgAddW: Ansi.BG_ADD_W,
|
|
39
|
-
bgDelW: Ansi.BG_DEL_W,
|
|
40
|
-
bgGutterAdd: Ansi.BG_GUTTER_ADD,
|
|
41
|
-
bgGutterDel: Ansi.BG_GUTTER_DEL,
|
|
42
|
-
bgEmpty: Ansi.BG_EMPTY,
|
|
43
|
-
bgBase: Ansi.BG_BASE,
|
|
44
|
-
rst: Ansi.BG_BASE === "\x1b[49m" ? "\x1b[0m" : `\x1b[0m${Ansi.BG_BASE}`,
|
|
45
|
-
divider: Ansi.DIVIDER,
|
|
46
|
-
};
|
|
47
|
-
const lines = await renderUnifiedLines(diff, language, width, max, dc, dbg);
|
|
34
|
+
const lines = await renderUnifiedLines(diff, language, width, max, dc, DEFAULT_DIFF_BG);
|
|
48
35
|
return lines.join("\n");
|
|
49
36
|
}
|
|
50
37
|
|
|
@@ -135,7 +122,7 @@ export async function renderUnifiedLines(
|
|
|
135
122
|
continue;
|
|
136
123
|
}
|
|
137
124
|
if (isPaired && wd && wd.similarity >= WORD_DIFF_MIN_SIM && !canHL) {
|
|
138
|
-
const pwd = plainWordDiff(dels[0]!.l.content, adds[0]!.l.content);
|
|
125
|
+
const pwd = plainWordDiff(dels[0]!.l.content, adds[0]!.l.content, dbg);
|
|
139
126
|
emitRow(dels[0]!.l.oldNum, "-", dbg.bgGutterDel, `${dc.fgDel}${Ansi.BOLD}`, `${dbg.bgDel}${pwd.old}`, dbg.bgDel);
|
|
140
127
|
emitRow(adds[0]!.l.newNum, "+", dbg.bgGutterAdd, `${dc.fgAdd}${Ansi.BOLD}`, `${dbg.bgAdd}${pwd.new}`, dbg.bgAdd);
|
|
141
128
|
continue;
|
package/src/word-diff.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Diff from "diff";
|
|
4
4
|
import * as Ansi from "./ansi/index.js";
|
|
5
|
+
import type { DiffBg } from "./ansi/index.js";
|
|
5
6
|
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// Word diff analysis
|
|
@@ -107,13 +108,17 @@ export function injectBg(
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/** Simple word diff (no syntax hl) — fallback when Shiki isn't available. */
|
|
110
|
-
export function plainWordDiff(
|
|
111
|
+
export function plainWordDiff(
|
|
112
|
+
oldText: string,
|
|
113
|
+
newText: string,
|
|
114
|
+
dbg: DiffBg,
|
|
115
|
+
): { old: string; new: string } {
|
|
111
116
|
const parts = Diff.diffWords(oldText, newText);
|
|
112
117
|
let o = "",
|
|
113
118
|
n = "";
|
|
114
119
|
for (const p of parts) {
|
|
115
|
-
if (p.removed) o += `${
|
|
116
|
-
else if (p.added) n += `${
|
|
120
|
+
if (p.removed) o += `${dbg.bgDelW}${p.value}${Ansi.RST}${dbg.bgDel}`;
|
|
121
|
+
else if (p.added) n += `${dbg.bgAddW}${p.value}${Ansi.RST}${dbg.bgAdd}`;
|
|
117
122
|
else {
|
|
118
123
|
o += p.value;
|
|
119
124
|
n += p.value;
|