@fresh-editor/fresh-editor 0.1.74 → 0.1.76
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/CHANGELOG.md +54 -0
- package/README.md +6 -0
- package/package.json +1 -1
- package/plugins/audit_mode.ts +9 -4
- package/plugins/buffer_modified.ts +1 -1
- package/plugins/calculator.ts +1 -1
- package/plugins/check-types.sh +41 -0
- package/plugins/clangd_support.ts +1 -1
- package/plugins/color_highlighter.ts +4 -1
- package/plugins/config-schema.json +44 -2
- package/plugins/diagnostics_panel.i18n.json +52 -52
- package/plugins/diagnostics_panel.ts +168 -540
- package/plugins/find_references.ts +82 -324
- package/plugins/git_blame.i18n.json +260 -247
- package/plugins/git_blame.ts +4 -9
- package/plugins/git_find_file.ts +42 -270
- package/plugins/git_grep.ts +50 -167
- package/plugins/git_gutter.ts +1 -1
- package/plugins/git_log.ts +4 -11
- package/plugins/lib/finder.ts +1499 -0
- package/plugins/lib/fresh.d.ts +104 -19
- package/plugins/lib/index.ts +14 -0
- package/plugins/lib/navigation-controller.ts +1 -1
- package/plugins/lib/panel-manager.ts +7 -13
- package/plugins/lib/results-panel.ts +914 -0
- package/plugins/lib/search-utils.ts +343 -0
- package/plugins/lib/virtual-buffer-factory.ts +3 -2
- package/plugins/live_grep.ts +56 -379
- package/plugins/markdown_compose.ts +1 -17
- package/plugins/merge_conflict.ts +16 -14
- package/plugins/path_complete.ts +1 -1
- package/plugins/search_replace.i18n.json +13 -13
- package/plugins/search_replace.ts +11 -9
- package/plugins/theme_editor.ts +57 -30
- package/plugins/todo_highlighter.ts +1 -0
- package/plugins/vi_mode.ts +9 -5
- package/plugins/welcome.ts +1 -1
- package/themes/dark.json +102 -0
- package/themes/high-contrast.json +102 -0
- package/themes/light.json +102 -0
- package/themes/nostalgia.json +102 -0
|
@@ -1,359 +1,117 @@
|
|
|
1
|
-
/// <reference path="
|
|
2
|
-
const editor = getEditor();
|
|
3
|
-
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
|
-
* Find References Plugin
|
|
4
|
+
* Find References Plugin
|
|
7
5
|
*
|
|
8
|
-
* Displays LSP find references results
|
|
9
|
-
*
|
|
10
|
-
* Uses cursor movement for navigation (Up/Down/j/k work naturally).
|
|
6
|
+
* Displays LSP find references results using the Finder abstraction
|
|
7
|
+
* with filter mode for unified prompt-based UX.
|
|
11
8
|
*/
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
let panelOpen = false;
|
|
15
|
-
let referencesBufferId: number | null = null;
|
|
16
|
-
let sourceSplitId: number | null = null;
|
|
17
|
-
let referencesSplitId: number | null = null; // Track the split we created
|
|
18
|
-
let currentReferences: ReferenceItem[] = [];
|
|
19
|
-
let currentSymbol: string = "";
|
|
20
|
-
let lineCache: Map<string, string[]> = new Map(); // Cache file contents
|
|
10
|
+
import { Finder, getRelativePath } from "./lib/finder.ts";
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
const MAX_RESULTS = 100;
|
|
12
|
+
const editor = getEditor();
|
|
24
13
|
|
|
25
|
-
// Reference
|
|
26
|
-
interface
|
|
14
|
+
// Reference location from LSP
|
|
15
|
+
interface ReferenceLocation {
|
|
27
16
|
file: string;
|
|
28
17
|
line: number;
|
|
29
18
|
column: number;
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Define the references mode with minimal keybindings
|
|
34
|
-
// Navigation uses normal cursor movement (arrows, j/k work naturally)
|
|
35
|
-
editor.defineMode(
|
|
36
|
-
"references-list",
|
|
37
|
-
null, // no parent mode
|
|
38
|
-
[
|
|
39
|
-
["Return", "references_goto"],
|
|
40
|
-
["q", "references_close"],
|
|
41
|
-
["Escape", "references_close"],
|
|
42
|
-
],
|
|
43
|
-
true // read-only
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// Get relative path for display
|
|
47
|
-
function getRelativePath(filePath: string): string {
|
|
48
|
-
const cwd = editor.getCwd();
|
|
49
|
-
if (filePath.startsWith(cwd)) {
|
|
50
|
-
return filePath.slice(cwd.length + 1); // Remove cwd and leading /
|
|
51
|
-
}
|
|
52
|
-
return filePath;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Format a reference for display with line preview
|
|
56
|
-
function formatReference(item: ReferenceItem): string {
|
|
57
|
-
const displayPath = getRelativePath(item.file);
|
|
58
|
-
const location = `${displayPath}:${item.line}:${item.column}`;
|
|
59
|
-
|
|
60
|
-
// Truncate location if too long, leaving room for line text
|
|
61
|
-
const maxLocationLen = 50;
|
|
62
|
-
const truncatedLocation = location.length > maxLocationLen
|
|
63
|
-
? "..." + location.slice(-(maxLocationLen - 3))
|
|
64
|
-
: location.padEnd(maxLocationLen);
|
|
65
|
-
|
|
66
|
-
// Get line text preview (truncated)
|
|
67
|
-
const lineText = item.lineText || "";
|
|
68
|
-
const trimmedLine = lineText.trim();
|
|
69
|
-
const maxLineLen = 60;
|
|
70
|
-
const displayLine = trimmedLine.length > maxLineLen
|
|
71
|
-
? trimmedLine.slice(0, maxLineLen - 3) + "..."
|
|
72
|
-
: trimmedLine;
|
|
73
|
-
|
|
74
|
-
return ` ${truncatedLocation} ${displayLine}\n`;
|
|
19
|
+
content?: string;
|
|
75
20
|
}
|
|
76
21
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
type: "reference",
|
|
103
|
-
index: i,
|
|
104
|
-
location: {
|
|
105
|
-
file: ref.file,
|
|
106
|
-
line: ref.line,
|
|
107
|
-
column: ref.column,
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Footer
|
|
115
|
-
entries.push({
|
|
116
|
-
text: `───────────────────────────────────────────────────────────────────────────────\n`,
|
|
117
|
-
properties: { type: "separator" },
|
|
118
|
-
});
|
|
119
|
-
entries.push({
|
|
120
|
-
text: editor.t("panel.help") + "\n",
|
|
121
|
-
properties: { type: "help" },
|
|
122
|
-
});
|
|
22
|
+
// Create the finder instance - same UX as grep plugins
|
|
23
|
+
const finder = new Finder<ReferenceLocation>(editor, {
|
|
24
|
+
id: "references",
|
|
25
|
+
format: (ref) => {
|
|
26
|
+
const displayPath = getRelativePath(editor, ref.file);
|
|
27
|
+
const content = ref.content?.trim() ?? "";
|
|
28
|
+
const description =
|
|
29
|
+
content.length > 60 ? content.substring(0, 57) + "..." : content;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
label: `${displayPath}:${ref.line}`,
|
|
33
|
+
description,
|
|
34
|
+
location: {
|
|
35
|
+
file: ref.file,
|
|
36
|
+
line: ref.line,
|
|
37
|
+
column: ref.column,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
preview: true,
|
|
42
|
+
maxResults: 100,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Pending references for the current prompt
|
|
46
|
+
let pendingRefs: ReferenceLocation[] = [];
|
|
123
47
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// Check cache first
|
|
142
|
-
let lines = lineCache.get(filePath);
|
|
143
|
-
if (!lines) {
|
|
144
|
-
const content = await editor.readFile(filePath);
|
|
48
|
+
/**
|
|
49
|
+
* Load line content for references
|
|
50
|
+
*/
|
|
51
|
+
async function loadLineContent(
|
|
52
|
+
refs: ReferenceLocation[]
|
|
53
|
+
): Promise<ReferenceLocation[]> {
|
|
54
|
+
const result: ReferenceLocation[] = [];
|
|
55
|
+
const fileCache = new Map<string, string[]>();
|
|
56
|
+
|
|
57
|
+
for (const ref of refs) {
|
|
58
|
+
let lines: string[];
|
|
59
|
+
const cached = fileCache.get(ref.file);
|
|
60
|
+
if (cached !== undefined) {
|
|
61
|
+
lines = cached;
|
|
62
|
+
} else {
|
|
63
|
+
try {
|
|
64
|
+
const content = await editor.readFile(ref.file);
|
|
145
65
|
lines = content.split("\n");
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Set line text for each reference
|
|
150
|
-
for (const ref of refs) {
|
|
151
|
-
const lineIndex = ref.line - 1; // Convert 1-based to 0-based
|
|
152
|
-
if (lineIndex >= 0 && lineIndex < lines.length) {
|
|
153
|
-
ref.lineText = lines[lineIndex];
|
|
154
|
-
} else {
|
|
155
|
-
ref.lineText = "";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} catch (error) {
|
|
159
|
-
// If file can't be read, leave lineText empty
|
|
160
|
-
for (const ref of refs) {
|
|
161
|
-
ref.lineText = "";
|
|
66
|
+
} catch {
|
|
67
|
+
lines = [];
|
|
162
68
|
}
|
|
69
|
+
fileCache.set(ref.file, lines);
|
|
163
70
|
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Show references panel
|
|
168
|
-
async function showReferencesPanel(symbol: string, references: ReferenceItem[]): Promise<void> {
|
|
169
|
-
// Only save the source split ID if panel is not already open
|
|
170
|
-
// (avoid overwriting it with the references split ID on subsequent calls)
|
|
171
|
-
if (!panelOpen) {
|
|
172
|
-
sourceSplitId = editor.getActiveSplitId();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Limit results
|
|
176
|
-
const limitedRefs = references.slice(0, MAX_RESULTS);
|
|
177
|
-
|
|
178
|
-
// Set references and symbol
|
|
179
|
-
currentSymbol = symbol;
|
|
180
|
-
currentReferences = limitedRefs;
|
|
181
|
-
|
|
182
|
-
// Load line texts for preview
|
|
183
|
-
await loadLineTexts(currentReferences);
|
|
184
|
-
|
|
185
|
-
// Build panel entries
|
|
186
|
-
const entries = buildPanelEntries();
|
|
187
71
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
referencesBufferId = await editor.createVirtualBufferInSplit({
|
|
192
|
-
name: "*References*",
|
|
193
|
-
mode: "references-list",
|
|
194
|
-
read_only: true,
|
|
195
|
-
entries: entries,
|
|
196
|
-
ratio: 0.7, // Original pane takes 70%, references takes 30%
|
|
197
|
-
panel_id: "references-panel",
|
|
198
|
-
show_line_numbers: false,
|
|
199
|
-
show_cursors: true, // Enable cursor for navigation
|
|
200
|
-
});
|
|
72
|
+
const lineIndex = ref.line - 1;
|
|
73
|
+
const lineContent =
|
|
74
|
+
lineIndex >= 0 && lineIndex < lines.length ? lines[lineIndex] : "";
|
|
201
75
|
|
|
202
|
-
|
|
203
|
-
// Track the references split (it becomes active after creation)
|
|
204
|
-
referencesSplitId = editor.getActiveSplitId();
|
|
205
|
-
|
|
206
|
-
const limitMsg = references.length > MAX_RESULTS
|
|
207
|
-
? editor.t("status.showing_first", { max: String(MAX_RESULTS) })
|
|
208
|
-
: "";
|
|
209
|
-
editor.setStatus(
|
|
210
|
-
editor.t("status.found_references", { count: String(references.length), limit: limitMsg })
|
|
211
|
-
);
|
|
212
|
-
editor.debug(`References panel opened with buffer ID ${referencesBufferId}, split ID ${referencesSplitId}`);
|
|
213
|
-
} catch (error) {
|
|
214
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
215
|
-
editor.setStatus(editor.t("status.failed_open_panel"));
|
|
216
|
-
editor.debug(`ERROR: createVirtualBufferInSplit failed: ${errorMessage}`);
|
|
76
|
+
result.push({ ...ref, content: lineContent });
|
|
217
77
|
}
|
|
78
|
+
|
|
79
|
+
return result;
|
|
218
80
|
}
|
|
219
81
|
|
|
220
82
|
// Handle lsp_references hook
|
|
221
|
-
globalThis.on_lsp_references = function (data: {
|
|
222
|
-
|
|
83
|
+
globalThis.on_lsp_references = async function (data: {
|
|
84
|
+
symbol: string;
|
|
85
|
+
locations: ReferenceLocation[];
|
|
86
|
+
}): Promise<void> {
|
|
87
|
+
editor.debug(
|
|
88
|
+
`Received ${data.locations.length} references for '${data.symbol}'`
|
|
89
|
+
);
|
|
223
90
|
|
|
224
91
|
if (data.locations.length === 0) {
|
|
225
|
-
editor.setStatus(
|
|
92
|
+
editor.setStatus(`No references found for '${data.symbol}'`);
|
|
226
93
|
return;
|
|
227
94
|
}
|
|
228
95
|
|
|
229
|
-
//
|
|
230
|
-
|
|
96
|
+
// Load line content for descriptions
|
|
97
|
+
pendingRefs = await loadLineContent(data.locations);
|
|
231
98
|
|
|
232
|
-
//
|
|
233
|
-
|
|
99
|
+
// Use prompt mode with filter source - same UX as grep plugins
|
|
100
|
+
finder.prompt({
|
|
101
|
+
title: `References to '${data.symbol}' (${data.locations.length})`,
|
|
102
|
+
source: {
|
|
103
|
+
mode: "filter",
|
|
104
|
+
load: async () => pendingRefs,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
234
107
|
};
|
|
235
108
|
|
|
236
109
|
// Register the hook handler
|
|
237
110
|
editor.on("lsp_references", "on_lsp_references");
|
|
238
111
|
|
|
239
|
-
//
|
|
240
|
-
globalThis.
|
|
241
|
-
|
|
242
|
-
cursor_id: number;
|
|
243
|
-
old_position: number;
|
|
244
|
-
new_position: number;
|
|
245
|
-
}): void {
|
|
246
|
-
// Only handle cursor movement in our references buffer
|
|
247
|
-
if (referencesBufferId === null || data.buffer_id !== referencesBufferId) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Get cursor line to determine which reference is selected
|
|
252
|
-
// getCursorLine() returns the line for the active buffer
|
|
253
|
-
const cursorLine = editor.getCursorLine();
|
|
254
|
-
|
|
255
|
-
// Line 0 is header, lines 1 to N are references
|
|
256
|
-
const refIndex = cursorLine - 1;
|
|
257
|
-
|
|
258
|
-
if (refIndex >= 0 && refIndex < currentReferences.length) {
|
|
259
|
-
editor.setStatus(editor.t("status.reference_index", { current: String(refIndex + 1), total: String(currentReferences.length) }));
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
// Register cursor movement handler
|
|
264
|
-
editor.on("cursor_moved", "on_references_cursor_moved");
|
|
265
|
-
|
|
266
|
-
// Hide references panel
|
|
267
|
-
globalThis.hide_references_panel = function (): void {
|
|
268
|
-
if (!panelOpen) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (referencesBufferId !== null) {
|
|
273
|
-
editor.closeBuffer(referencesBufferId);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Close the split we created (if it exists and is different from source)
|
|
277
|
-
if (referencesSplitId !== null && referencesSplitId !== sourceSplitId) {
|
|
278
|
-
editor.closeSplit(referencesSplitId);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
panelOpen = false;
|
|
282
|
-
referencesBufferId = null;
|
|
283
|
-
sourceSplitId = null;
|
|
284
|
-
referencesSplitId = null;
|
|
285
|
-
currentReferences = [];
|
|
286
|
-
currentSymbol = "";
|
|
287
|
-
lineCache.clear();
|
|
288
|
-
editor.setStatus(editor.t("status.closed"));
|
|
112
|
+
// Close function for command palette
|
|
113
|
+
globalThis.close_references = function (): void {
|
|
114
|
+
finder.close();
|
|
289
115
|
};
|
|
290
116
|
|
|
291
|
-
|
|
292
|
-
globalThis.references_goto = function (): void {
|
|
293
|
-
if (currentReferences.length === 0) {
|
|
294
|
-
editor.setStatus(editor.t("status.no_references_to_jump"));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (sourceSplitId === null) {
|
|
299
|
-
editor.setStatus(editor.t("status.source_split_unavailable"));
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (referencesBufferId === null) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Get text properties at cursor position
|
|
308
|
-
const props = editor.getTextPropertiesAtCursor(referencesBufferId);
|
|
309
|
-
editor.debug(`references_goto: props.length=${props.length}, referencesBufferId=${referencesBufferId}, sourceSplitId=${sourceSplitId}`);
|
|
310
|
-
|
|
311
|
-
if (props.length > 0) {
|
|
312
|
-
editor.debug(`references_goto: props[0]=${JSON.stringify(props[0])}`);
|
|
313
|
-
const location = props[0].location as
|
|
314
|
-
| { file: string; line: number; column: number }
|
|
315
|
-
| undefined;
|
|
316
|
-
if (location) {
|
|
317
|
-
editor.debug(`references_goto: opening ${location.file}:${location.line}:${location.column} in split ${sourceSplitId}`);
|
|
318
|
-
// Open file in the source split, not the references split
|
|
319
|
-
editor.openFileInSplit(
|
|
320
|
-
sourceSplitId,
|
|
321
|
-
location.file,
|
|
322
|
-
location.line,
|
|
323
|
-
location.column || 0
|
|
324
|
-
);
|
|
325
|
-
const displayPath = getRelativePath(location.file);
|
|
326
|
-
editor.setStatus(editor.t("status.jumped_to", { file: displayPath, line: String(location.line) }));
|
|
327
|
-
} else {
|
|
328
|
-
editor.debug(`references_goto: no location in props[0]`);
|
|
329
|
-
editor.setStatus(editor.t("status.move_cursor"));
|
|
330
|
-
}
|
|
331
|
-
} else {
|
|
332
|
-
editor.debug(`references_goto: no props found at cursor`);
|
|
333
|
-
editor.setStatus(editor.t("status.move_cursor"));
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// Close the references panel
|
|
338
|
-
globalThis.references_close = function (): void {
|
|
339
|
-
globalThis.hide_references_panel();
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
// Register commands
|
|
343
|
-
editor.registerCommand(
|
|
344
|
-
"%cmd.show_references",
|
|
345
|
-
"%cmd.show_references_desc",
|
|
346
|
-
"show_references_panel",
|
|
347
|
-
"normal"
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
editor.registerCommand(
|
|
351
|
-
"%cmd.hide_references",
|
|
352
|
-
"%cmd.hide_references_desc",
|
|
353
|
-
"hide_references_panel",
|
|
354
|
-
"normal"
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
// Plugin initialization
|
|
358
|
-
editor.setStatus(editor.t("status.ready"));
|
|
359
|
-
editor.debug("Find References plugin initialized");
|
|
117
|
+
editor.debug("Find References plugin loaded (using Finder abstraction)");
|