@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.
Files changed (90) hide show
  1. package/bin/CHANGELOG.md +1017 -0
  2. package/bin/LICENSE +117 -0
  3. package/bin/README.md +248 -0
  4. package/bin/fresh.exe +0 -0
  5. package/bin/plugins/README.md +71 -0
  6. package/bin/plugins/audit_mode.i18n.json +821 -0
  7. package/bin/plugins/audit_mode.ts +1810 -0
  8. package/bin/plugins/buffer_modified.i18n.json +67 -0
  9. package/bin/plugins/buffer_modified.ts +281 -0
  10. package/bin/plugins/calculator.i18n.json +93 -0
  11. package/bin/plugins/calculator.ts +770 -0
  12. package/bin/plugins/clangd-lsp.ts +168 -0
  13. package/bin/plugins/clangd_support.i18n.json +223 -0
  14. package/bin/plugins/clangd_support.md +20 -0
  15. package/bin/plugins/clangd_support.ts +325 -0
  16. package/bin/plugins/color_highlighter.i18n.json +145 -0
  17. package/bin/plugins/color_highlighter.ts +304 -0
  18. package/bin/plugins/config-schema.json +768 -0
  19. package/bin/plugins/csharp-lsp.ts +147 -0
  20. package/bin/plugins/csharp_support.i18n.json +80 -0
  21. package/bin/plugins/csharp_support.ts +170 -0
  22. package/bin/plugins/css-lsp.ts +143 -0
  23. package/bin/plugins/diagnostics_panel.i18n.json +236 -0
  24. package/bin/plugins/diagnostics_panel.ts +642 -0
  25. package/bin/plugins/examples/README.md +85 -0
  26. package/bin/plugins/examples/async_demo.ts +165 -0
  27. package/bin/plugins/examples/bookmarks.ts +329 -0
  28. package/bin/plugins/examples/buffer_query_demo.ts +110 -0
  29. package/bin/plugins/examples/git_grep.ts +262 -0
  30. package/bin/plugins/examples/hello_world.ts +93 -0
  31. package/bin/plugins/examples/virtual_buffer_demo.ts +116 -0
  32. package/bin/plugins/find_references.i18n.json +275 -0
  33. package/bin/plugins/find_references.ts +359 -0
  34. package/bin/plugins/git_blame.i18n.json +496 -0
  35. package/bin/plugins/git_blame.ts +707 -0
  36. package/bin/plugins/git_find_file.i18n.json +314 -0
  37. package/bin/plugins/git_find_file.ts +300 -0
  38. package/bin/plugins/git_grep.i18n.json +171 -0
  39. package/bin/plugins/git_grep.ts +191 -0
  40. package/bin/plugins/git_gutter.i18n.json +93 -0
  41. package/bin/plugins/git_gutter.ts +477 -0
  42. package/bin/plugins/git_log.i18n.json +481 -0
  43. package/bin/plugins/git_log.ts +1285 -0
  44. package/bin/plugins/go-lsp.ts +143 -0
  45. package/bin/plugins/html-lsp.ts +145 -0
  46. package/bin/plugins/json-lsp.ts +145 -0
  47. package/bin/plugins/lib/fresh.d.ts +1321 -0
  48. package/bin/plugins/lib/index.ts +24 -0
  49. package/bin/plugins/lib/navigation-controller.ts +214 -0
  50. package/bin/plugins/lib/panel-manager.ts +220 -0
  51. package/bin/plugins/lib/types.ts +72 -0
  52. package/bin/plugins/lib/virtual-buffer-factory.ts +130 -0
  53. package/bin/plugins/live_grep.i18n.json +171 -0
  54. package/bin/plugins/live_grep.ts +422 -0
  55. package/bin/plugins/markdown_compose.i18n.json +223 -0
  56. package/bin/plugins/markdown_compose.ts +630 -0
  57. package/bin/plugins/merge_conflict.i18n.json +821 -0
  58. package/bin/plugins/merge_conflict.ts +1810 -0
  59. package/bin/plugins/path_complete.i18n.json +80 -0
  60. package/bin/plugins/path_complete.ts +165 -0
  61. package/bin/plugins/python-lsp.ts +162 -0
  62. package/bin/plugins/rust-lsp.ts +166 -0
  63. package/bin/plugins/search_replace.i18n.json +405 -0
  64. package/bin/plugins/search_replace.ts +484 -0
  65. package/bin/plugins/test_i18n.i18n.json +67 -0
  66. package/bin/plugins/test_i18n.ts +18 -0
  67. package/bin/plugins/theme_editor.i18n.json +3746 -0
  68. package/bin/plugins/theme_editor.ts +2063 -0
  69. package/bin/plugins/todo_highlighter.i18n.json +184 -0
  70. package/bin/plugins/todo_highlighter.ts +206 -0
  71. package/bin/plugins/typescript-lsp.ts +167 -0
  72. package/bin/plugins/vi_mode.i18n.json +1549 -0
  73. package/bin/plugins/vi_mode.ts +2747 -0
  74. package/bin/plugins/welcome.i18n.json +236 -0
  75. package/bin/plugins/welcome.ts +76 -0
  76. package/bin/themes/dark.json +102 -0
  77. package/bin/themes/dracula.json +62 -0
  78. package/bin/themes/high-contrast.json +102 -0
  79. package/bin/themes/light.json +102 -0
  80. package/bin/themes/nord.json +62 -0
  81. package/bin/themes/nostalgia.json +102 -0
  82. package/bin/themes/solarized-dark.json +62 -0
  83. package/binary-install.js +1 -1
  84. package/dist/bin/fresh.js +9 -0
  85. package/dist/binary-install.js +149 -0
  86. package/dist/binary.js +30 -0
  87. package/dist/fresh-6yhknp07.exe +0 -0
  88. package/dist/install.js +158 -0
  89. package/dist/run-fresh.js +43 -0
  90. 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"));