@involvex/fresh-editor 0.1.76 → 0.1.78
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/bin/CHANGELOG.md +1017 -0
- package/bin/LICENSE +117 -0
- package/bin/README.md +248 -0
- package/bin/fresh.exe +0 -0
- package/bin/plugins/README.md +71 -0
- package/bin/plugins/audit_mode.i18n.json +821 -0
- package/bin/plugins/audit_mode.ts +1810 -0
- package/bin/plugins/buffer_modified.i18n.json +67 -0
- package/bin/plugins/buffer_modified.ts +281 -0
- package/bin/plugins/calculator.i18n.json +93 -0
- package/bin/plugins/calculator.ts +770 -0
- package/bin/plugins/clangd-lsp.ts +168 -0
- package/bin/plugins/clangd_support.i18n.json +223 -0
- package/bin/plugins/clangd_support.md +20 -0
- package/bin/plugins/clangd_support.ts +325 -0
- package/bin/plugins/color_highlighter.i18n.json +145 -0
- package/bin/plugins/color_highlighter.ts +304 -0
- package/bin/plugins/config-schema.json +768 -0
- package/bin/plugins/csharp-lsp.ts +147 -0
- package/bin/plugins/csharp_support.i18n.json +80 -0
- package/bin/plugins/csharp_support.ts +170 -0
- package/bin/plugins/css-lsp.ts +143 -0
- package/bin/plugins/diagnostics_panel.i18n.json +236 -0
- package/bin/plugins/diagnostics_panel.ts +642 -0
- package/bin/plugins/examples/README.md +85 -0
- package/bin/plugins/examples/async_demo.ts +165 -0
- package/bin/plugins/examples/bookmarks.ts +329 -0
- package/bin/plugins/examples/buffer_query_demo.ts +110 -0
- package/bin/plugins/examples/git_grep.ts +262 -0
- package/bin/plugins/examples/hello_world.ts +93 -0
- package/bin/plugins/examples/virtual_buffer_demo.ts +116 -0
- package/bin/plugins/find_references.i18n.json +275 -0
- package/bin/plugins/find_references.ts +359 -0
- package/bin/plugins/git_blame.i18n.json +496 -0
- package/bin/plugins/git_blame.ts +707 -0
- package/bin/plugins/git_find_file.i18n.json +314 -0
- package/bin/plugins/git_find_file.ts +300 -0
- package/bin/plugins/git_grep.i18n.json +171 -0
- package/bin/plugins/git_grep.ts +191 -0
- package/bin/plugins/git_gutter.i18n.json +93 -0
- package/bin/plugins/git_gutter.ts +477 -0
- package/bin/plugins/git_log.i18n.json +481 -0
- package/bin/plugins/git_log.ts +1285 -0
- package/bin/plugins/go-lsp.ts +143 -0
- package/bin/plugins/html-lsp.ts +145 -0
- package/bin/plugins/json-lsp.ts +145 -0
- package/bin/plugins/lib/fresh.d.ts +1321 -0
- package/bin/plugins/lib/index.ts +24 -0
- package/bin/plugins/lib/navigation-controller.ts +214 -0
- package/bin/plugins/lib/panel-manager.ts +220 -0
- package/bin/plugins/lib/types.ts +72 -0
- package/bin/plugins/lib/virtual-buffer-factory.ts +130 -0
- package/bin/plugins/live_grep.i18n.json +171 -0
- package/bin/plugins/live_grep.ts +422 -0
- package/bin/plugins/markdown_compose.i18n.json +223 -0
- package/bin/plugins/markdown_compose.ts +630 -0
- package/bin/plugins/merge_conflict.i18n.json +821 -0
- package/bin/plugins/merge_conflict.ts +1810 -0
- package/bin/plugins/path_complete.i18n.json +80 -0
- package/bin/plugins/path_complete.ts +165 -0
- package/bin/plugins/python-lsp.ts +162 -0
- package/bin/plugins/rust-lsp.ts +166 -0
- package/bin/plugins/search_replace.i18n.json +405 -0
- package/bin/plugins/search_replace.ts +484 -0
- package/bin/plugins/test_i18n.i18n.json +67 -0
- package/bin/plugins/test_i18n.ts +18 -0
- package/bin/plugins/theme_editor.i18n.json +3746 -0
- package/bin/plugins/theme_editor.ts +2063 -0
- package/bin/plugins/todo_highlighter.i18n.json +184 -0
- package/bin/plugins/todo_highlighter.ts +206 -0
- package/bin/plugins/typescript-lsp.ts +167 -0
- package/bin/plugins/vi_mode.i18n.json +1549 -0
- package/bin/plugins/vi_mode.ts +2747 -0
- package/bin/plugins/welcome.i18n.json +236 -0
- package/bin/plugins/welcome.ts +76 -0
- package/bin/themes/dark.json +102 -0
- package/bin/themes/dracula.json +62 -0
- package/bin/themes/high-contrast.json +102 -0
- package/bin/themes/light.json +102 -0
- package/bin/themes/nord.json +62 -0
- package/bin/themes/nostalgia.json +102 -0
- package/bin/themes/solarized-dark.json +62 -0
- package/binary-install.js +1 -1
- package/dist/bin/fresh.js +9 -0
- package/dist/binary-install.js +149 -0
- package/dist/binary.js +30 -0
- package/dist/fresh-6yhknp07.exe +0 -0
- package/dist/install.js +158 -0
- package/dist/run-fresh.js +43 -0
- package/package.json +7 -2
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/// <reference path="../types/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Git Gutter Plugin
|
|
7
|
+
*
|
|
8
|
+
* Shows git diff indicators in the gutter for modified, added, and deleted lines.
|
|
9
|
+
* Uses `git diff` to compare the current buffer content against the index (staged changes)
|
|
10
|
+
* or HEAD if nothing is staged.
|
|
11
|
+
*
|
|
12
|
+
* Indicator symbols:
|
|
13
|
+
* - │ (green): Added line
|
|
14
|
+
* - │ (yellow): Modified line
|
|
15
|
+
* - ▾ (red): Deleted line(s) below
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Constants
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
const NAMESPACE = "git-gutter";
|
|
23
|
+
const PRIORITY = 10; // Lower than diagnostics
|
|
24
|
+
|
|
25
|
+
// Colors (RGB)
|
|
26
|
+
const COLORS = {
|
|
27
|
+
added: [80, 250, 123] as [number, number, number], // Green
|
|
28
|
+
modified: [255, 184, 108] as [number, number, number], // Orange/Yellow
|
|
29
|
+
deleted: [255, 85, 85] as [number, number, number], // Red
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Symbols
|
|
33
|
+
const SYMBOLS = {
|
|
34
|
+
added: "│",
|
|
35
|
+
modified: "│",
|
|
36
|
+
deleted: "▾",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Types
|
|
41
|
+
// =============================================================================
|
|
42
|
+
|
|
43
|
+
interface DiffHunk {
|
|
44
|
+
/** Type of change */
|
|
45
|
+
type: "added" | "modified" | "deleted";
|
|
46
|
+
/** Starting line number in the new file (1-indexed) */
|
|
47
|
+
startLine: number;
|
|
48
|
+
/** Number of lines affected */
|
|
49
|
+
lineCount: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface BufferGitState {
|
|
53
|
+
/** File path for this buffer */
|
|
54
|
+
filePath: string;
|
|
55
|
+
/** Last known hunks for this buffer */
|
|
56
|
+
hunks: DiffHunk[];
|
|
57
|
+
/** Whether we're currently updating */
|
|
58
|
+
updating: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// State
|
|
63
|
+
// =============================================================================
|
|
64
|
+
|
|
65
|
+
/** Git state per buffer */
|
|
66
|
+
const bufferStates: Map<number, BufferGitState> = new Map();
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// Git Diff Parsing
|
|
71
|
+
// =============================================================================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parse unified diff output to extract hunks
|
|
75
|
+
* Unified diff format:
|
|
76
|
+
* @@ -start,count +start,count @@
|
|
77
|
+
*/
|
|
78
|
+
function parseDiffOutput(diffOutput: string): DiffHunk[] {
|
|
79
|
+
const hunks: DiffHunk[] = [];
|
|
80
|
+
const lines = diffOutput.split("\n");
|
|
81
|
+
|
|
82
|
+
let currentOldLine = 0;
|
|
83
|
+
let currentNewLine = 0;
|
|
84
|
+
let inHunk = false;
|
|
85
|
+
let addedStart = 0;
|
|
86
|
+
let addedCount = 0;
|
|
87
|
+
let modifiedStart = 0;
|
|
88
|
+
let modifiedCount = 0;
|
|
89
|
+
let deletedAtLine = 0;
|
|
90
|
+
let deletedCount = 0;
|
|
91
|
+
|
|
92
|
+
const flushAdded = () => {
|
|
93
|
+
if (addedCount > 0) {
|
|
94
|
+
hunks.push({ type: "added", startLine: addedStart, lineCount: addedCount });
|
|
95
|
+
addedCount = 0;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const flushModified = () => {
|
|
100
|
+
if (modifiedCount > 0) {
|
|
101
|
+
hunks.push({ type: "modified", startLine: modifiedStart, lineCount: modifiedCount });
|
|
102
|
+
modifiedCount = 0;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const flushDeleted = () => {
|
|
107
|
+
if (deletedCount > 0) {
|
|
108
|
+
// Deleted lines are shown as a marker on the line after the deletion
|
|
109
|
+
hunks.push({ type: "deleted", startLine: deletedAtLine, lineCount: deletedCount });
|
|
110
|
+
deletedCount = 0;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
// Match hunk header: @@ -old_start,old_count +new_start,new_count @@
|
|
116
|
+
const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
117
|
+
if (hunkMatch) {
|
|
118
|
+
// Flush any pending changes from previous hunk
|
|
119
|
+
flushAdded();
|
|
120
|
+
flushModified();
|
|
121
|
+
flushDeleted();
|
|
122
|
+
|
|
123
|
+
currentOldLine = parseInt(hunkMatch[1], 10);
|
|
124
|
+
currentNewLine = parseInt(hunkMatch[3], 10);
|
|
125
|
+
inHunk = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!inHunk) continue;
|
|
130
|
+
|
|
131
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
132
|
+
// Added line
|
|
133
|
+
if (deletedCount > 0) {
|
|
134
|
+
// If there were deletions right before, this is a modification
|
|
135
|
+
if (modifiedCount === 0) {
|
|
136
|
+
modifiedStart = currentNewLine;
|
|
137
|
+
}
|
|
138
|
+
modifiedCount++;
|
|
139
|
+
deletedCount--;
|
|
140
|
+
} else {
|
|
141
|
+
// Pure addition
|
|
142
|
+
if (addedCount === 0) {
|
|
143
|
+
addedStart = currentNewLine;
|
|
144
|
+
}
|
|
145
|
+
addedCount++;
|
|
146
|
+
}
|
|
147
|
+
currentNewLine++;
|
|
148
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
149
|
+
// Deleted line - flush any pending additions first
|
|
150
|
+
flushAdded();
|
|
151
|
+
|
|
152
|
+
if (deletedCount === 0) {
|
|
153
|
+
deletedAtLine = currentNewLine;
|
|
154
|
+
}
|
|
155
|
+
deletedCount++;
|
|
156
|
+
currentOldLine++;
|
|
157
|
+
} else if (line.startsWith(" ")) {
|
|
158
|
+
// Context line (unchanged)
|
|
159
|
+
flushAdded();
|
|
160
|
+
flushModified();
|
|
161
|
+
flushDeleted();
|
|
162
|
+
currentOldLine++;
|
|
163
|
+
currentNewLine++;
|
|
164
|
+
} else if (line === "\") {
|
|
165
|
+
// Ignore this marker
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Flush any remaining changes
|
|
171
|
+
flushAdded();
|
|
172
|
+
flushModified();
|
|
173
|
+
flushDeleted();
|
|
174
|
+
|
|
175
|
+
return hunks;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// Git Operations
|
|
180
|
+
// =============================================================================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get the directory containing a file
|
|
184
|
+
*/
|
|
185
|
+
function getFileDirectory(filePath: string): string {
|
|
186
|
+
const lastSlash = filePath.lastIndexOf("/");
|
|
187
|
+
if (lastSlash > 0) {
|
|
188
|
+
return filePath.substring(0, lastSlash);
|
|
189
|
+
}
|
|
190
|
+
return ".";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check if a file is tracked by git
|
|
195
|
+
*/
|
|
196
|
+
async function isGitTracked(filePath: string): Promise<boolean> {
|
|
197
|
+
const cwd = getFileDirectory(filePath);
|
|
198
|
+
const result = await editor.spawnProcess("git", ["ls-files", "--error-unmatch", filePath], cwd);
|
|
199
|
+
return result.exit_code === 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get git diff for a file
|
|
204
|
+
* Compares working tree against HEAD to show all uncommitted changes
|
|
205
|
+
* (both staged and unstaged)
|
|
206
|
+
*/
|
|
207
|
+
async function getGitDiff(filePath: string): Promise<string> {
|
|
208
|
+
const cwd = getFileDirectory(filePath);
|
|
209
|
+
|
|
210
|
+
// Diff against HEAD to show all changes (staged + unstaged) vs last commit
|
|
211
|
+
const result = await editor.spawnProcess("git", [
|
|
212
|
+
"diff",
|
|
213
|
+
"HEAD",
|
|
214
|
+
"--no-color",
|
|
215
|
+
"--unified=0", // No context lines for cleaner parsing
|
|
216
|
+
"--",
|
|
217
|
+
filePath,
|
|
218
|
+
], cwd);
|
|
219
|
+
|
|
220
|
+
// Exit code 0 = no differences, 1 = differences found, >1 = error
|
|
221
|
+
if (result.exit_code <= 1) {
|
|
222
|
+
return result.stdout;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return "";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// =============================================================================
|
|
229
|
+
// Indicator Management
|
|
230
|
+
// =============================================================================
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Update git gutter indicators for a buffer
|
|
234
|
+
*/
|
|
235
|
+
async function updateGitGutter(bufferId: number): Promise<void> {
|
|
236
|
+
const state = bufferStates.get(bufferId);
|
|
237
|
+
if (!state || state.updating) return;
|
|
238
|
+
|
|
239
|
+
state.updating = true;
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
editor.debug(`Git Gutter: updating for ${state.filePath}`);
|
|
243
|
+
|
|
244
|
+
// Check if file is git tracked
|
|
245
|
+
const tracked = await isGitTracked(state.filePath);
|
|
246
|
+
if (!tracked) {
|
|
247
|
+
// Clear indicators for non-tracked files
|
|
248
|
+
editor.debug("Git Gutter: file not tracked by git");
|
|
249
|
+
editor.clearLineIndicators(bufferId, NAMESPACE);
|
|
250
|
+
state.hunks = [];
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
editor.debug("Git Gutter: file is tracked, getting diff...");
|
|
255
|
+
|
|
256
|
+
// Get diff
|
|
257
|
+
const diffOutput = await getGitDiff(state.filePath);
|
|
258
|
+
editor.debug(`Git Gutter: diff output length = ${diffOutput.length}`);
|
|
259
|
+
if (diffOutput.length > 0 && diffOutput.length < 500) {
|
|
260
|
+
editor.debug(`Git Gutter: diff = ${diffOutput.replace(/\n/g, "\\n")}`);
|
|
261
|
+
}
|
|
262
|
+
const hunks = parseDiffOutput(diffOutput);
|
|
263
|
+
editor.debug(`Git Gutter: parsed ${hunks.length} hunks`);
|
|
264
|
+
|
|
265
|
+
// Clear existing indicators
|
|
266
|
+
editor.clearLineIndicators(bufferId, NAMESPACE);
|
|
267
|
+
|
|
268
|
+
// Apply new indicators
|
|
269
|
+
for (const hunk of hunks) {
|
|
270
|
+
const color = COLORS[hunk.type];
|
|
271
|
+
const symbol = SYMBOLS[hunk.type];
|
|
272
|
+
|
|
273
|
+
if (hunk.type === "deleted") {
|
|
274
|
+
// Deleted indicator shows on a single line
|
|
275
|
+
// Line numbers are 1-indexed in diff, but 0-indexed in editor
|
|
276
|
+
const line = Math.max(0, hunk.startLine - 1);
|
|
277
|
+
editor.setLineIndicator(
|
|
278
|
+
bufferId,
|
|
279
|
+
line,
|
|
280
|
+
NAMESPACE,
|
|
281
|
+
symbol,
|
|
282
|
+
color[0],
|
|
283
|
+
color[1],
|
|
284
|
+
color[2],
|
|
285
|
+
PRIORITY
|
|
286
|
+
);
|
|
287
|
+
} else {
|
|
288
|
+
// Added/modified indicators show on each affected line
|
|
289
|
+
for (let i = 0; i < hunk.lineCount; i++) {
|
|
290
|
+
// Line numbers are 1-indexed in diff, but 0-indexed in editor
|
|
291
|
+
const line = hunk.startLine - 1 + i;
|
|
292
|
+
editor.setLineIndicator(
|
|
293
|
+
bufferId,
|
|
294
|
+
line,
|
|
295
|
+
NAMESPACE,
|
|
296
|
+
symbol,
|
|
297
|
+
color[0],
|
|
298
|
+
color[1],
|
|
299
|
+
color[2],
|
|
300
|
+
PRIORITY
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
state.hunks = hunks;
|
|
307
|
+
} finally {
|
|
308
|
+
state.updating = false;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
// =============================================================================
|
|
314
|
+
// Event Handlers
|
|
315
|
+
// =============================================================================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Handle after file open - initialize git state and update indicators
|
|
319
|
+
*/
|
|
320
|
+
globalThis.onGitGutterAfterFileOpen = function (args: {
|
|
321
|
+
buffer_id: number;
|
|
322
|
+
path: string;
|
|
323
|
+
}): boolean {
|
|
324
|
+
const bufferId = args.buffer_id;
|
|
325
|
+
const filePath = args.path;
|
|
326
|
+
|
|
327
|
+
if (!filePath || filePath === "") {
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Initialize state for this buffer
|
|
332
|
+
bufferStates.set(bufferId, {
|
|
333
|
+
filePath,
|
|
334
|
+
hunks: [],
|
|
335
|
+
updating: false,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Update immediately (no debounce for file open)
|
|
339
|
+
updateGitGutter(bufferId);
|
|
340
|
+
|
|
341
|
+
return true;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Handle buffer activation - update if we have state but indicators might be stale
|
|
346
|
+
*/
|
|
347
|
+
globalThis.onGitGutterBufferActivated = function (args: {
|
|
348
|
+
buffer_id: number;
|
|
349
|
+
}): boolean {
|
|
350
|
+
const bufferId = args.buffer_id;
|
|
351
|
+
|
|
352
|
+
// If we don't have state yet, try to initialize from buffer path
|
|
353
|
+
if (!bufferStates.has(bufferId)) {
|
|
354
|
+
const filePath = editor.getBufferPath(bufferId);
|
|
355
|
+
if (filePath && filePath !== "") {
|
|
356
|
+
bufferStates.set(bufferId, {
|
|
357
|
+
filePath,
|
|
358
|
+
hunks: [],
|
|
359
|
+
updating: false,
|
|
360
|
+
});
|
|
361
|
+
updateGitGutter(bufferId);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// If we already have state, the indicators should be current
|
|
365
|
+
// (they update on file open and save)
|
|
366
|
+
|
|
367
|
+
return true;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Handle after file save - refresh indicators
|
|
372
|
+
*/
|
|
373
|
+
globalThis.onGitGutterAfterSave = function (args: {
|
|
374
|
+
buffer_id: number;
|
|
375
|
+
path: string;
|
|
376
|
+
}): boolean {
|
|
377
|
+
const bufferId = args.buffer_id;
|
|
378
|
+
|
|
379
|
+
// Update state with new path (in case of save-as)
|
|
380
|
+
const state = bufferStates.get(bufferId);
|
|
381
|
+
if (state) {
|
|
382
|
+
state.filePath = args.path;
|
|
383
|
+
} else {
|
|
384
|
+
bufferStates.set(bufferId, {
|
|
385
|
+
filePath: args.path,
|
|
386
|
+
hunks: [],
|
|
387
|
+
updating: false,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Update immediately after save (no debounce)
|
|
392
|
+
updateGitGutter(bufferId);
|
|
393
|
+
|
|
394
|
+
return true;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Note: Git diff compares the file on disk, not the in-memory buffer.
|
|
398
|
+
// Line indicators automatically track position changes via byte-position markers.
|
|
399
|
+
// A full re-diff happens on save. For unsaved changes, see buffer_modified plugin.
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Handle buffer closed - cleanup state
|
|
403
|
+
*/
|
|
404
|
+
globalThis.onGitGutterBufferClosed = function (args: {
|
|
405
|
+
buffer_id: number;
|
|
406
|
+
}): boolean {
|
|
407
|
+
bufferStates.delete(args.buffer_id);
|
|
408
|
+
return true;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// =============================================================================
|
|
412
|
+
// Commands
|
|
413
|
+
// =============================================================================
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Manually refresh git gutter for the current buffer
|
|
417
|
+
*/
|
|
418
|
+
globalThis.git_gutter_refresh = function (): void {
|
|
419
|
+
const bufferId = editor.getActiveBufferId();
|
|
420
|
+
const filePath = editor.getBufferPath(bufferId);
|
|
421
|
+
|
|
422
|
+
if (!filePath || filePath === "") {
|
|
423
|
+
editor.setStatus(editor.t("status.no_file"));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Ensure state exists
|
|
428
|
+
if (!bufferStates.has(bufferId)) {
|
|
429
|
+
bufferStates.set(bufferId, {
|
|
430
|
+
filePath,
|
|
431
|
+
hunks: [],
|
|
432
|
+
updating: false,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Force immediate update
|
|
437
|
+
updateGitGutter(bufferId).then(() => {
|
|
438
|
+
const state = bufferStates.get(bufferId);
|
|
439
|
+
const count = state?.hunks.length || 0;
|
|
440
|
+
editor.setStatus(editor.t("status.changes", { count: String(count) }));
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// =============================================================================
|
|
445
|
+
// Registration
|
|
446
|
+
// =============================================================================
|
|
447
|
+
|
|
448
|
+
// Register event handlers
|
|
449
|
+
// Note: No need to register after-insert/after-delete hooks - indicators
|
|
450
|
+
// automatically track position changes via byte-position markers in the editor.
|
|
451
|
+
editor.on("after_file_open", "onGitGutterAfterFileOpen");
|
|
452
|
+
editor.on("buffer_activated", "onGitGutterBufferActivated");
|
|
453
|
+
editor.on("after_file_save", "onGitGutterAfterSave");
|
|
454
|
+
editor.on("buffer_closed", "onGitGutterBufferClosed");
|
|
455
|
+
|
|
456
|
+
// Register commands
|
|
457
|
+
editor.registerCommand(
|
|
458
|
+
"%cmd.refresh",
|
|
459
|
+
"%cmd.refresh_desc",
|
|
460
|
+
"git_gutter_refresh",
|
|
461
|
+
"normal"
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Initialize for the current buffer
|
|
465
|
+
const initBufferId = editor.getActiveBufferId();
|
|
466
|
+
const initPath = editor.getBufferPath(initBufferId);
|
|
467
|
+
if (initPath && initPath !== "") {
|
|
468
|
+
bufferStates.set(initBufferId, {
|
|
469
|
+
filePath: initPath,
|
|
470
|
+
hunks: [],
|
|
471
|
+
updating: false,
|
|
472
|
+
});
|
|
473
|
+
updateGitGutter(initBufferId);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
editor.debug("Git Gutter plugin loaded");
|
|
477
|
+
editor.setStatus(editor.t("status.ready"));
|