@fresh-editor/fresh-editor 0.1.4
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/.gitignore +2 -0
- package/LICENSE +117 -0
- package/README.md +54 -0
- package/binary-install.js +212 -0
- package/binary.js +126 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +900 -0
- package/package.json +100 -0
- package/plugins/README.md +121 -0
- package/plugins/clangd_support.md +20 -0
- package/plugins/clangd_support.ts +323 -0
- package/plugins/color_highlighter.ts +302 -0
- package/plugins/diagnostics_panel.ts +308 -0
- package/plugins/examples/README.md +245 -0
- package/plugins/examples/async_demo.ts +165 -0
- package/plugins/examples/bookmarks.ts +329 -0
- package/plugins/examples/buffer_query_demo.ts +110 -0
- package/plugins/examples/git_grep.ts +262 -0
- package/plugins/examples/hello_world.ts +93 -0
- package/plugins/examples/virtual_buffer_demo.ts +116 -0
- package/plugins/find_references.ts +357 -0
- package/plugins/git_find_file.ts +298 -0
- package/plugins/git_grep.ts +188 -0
- package/plugins/git_log.ts +1283 -0
- package/plugins/lib/fresh.d.ts +849 -0
- package/plugins/lib/index.ts +24 -0
- package/plugins/lib/navigation-controller.ts +214 -0
- package/plugins/lib/panel-manager.ts +218 -0
- package/plugins/lib/types.ts +72 -0
- package/plugins/lib/virtual-buffer-factory.ts +158 -0
- package/plugins/manual_help.ts +243 -0
- package/plugins/markdown_compose.ts +1207 -0
- package/plugins/merge_conflict.ts +1811 -0
- package/plugins/path_complete.ts +163 -0
- package/plugins/search_replace.ts +481 -0
- package/plugins/todo_highlighter.ts +204 -0
- package/plugins/welcome.ts +74 -0
- package/run-fresh.js +4 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/// <reference path="../../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hello World TypeScript Plugin for Fresh Editor
|
|
5
|
+
*
|
|
6
|
+
* This is a simple example plugin that demonstrates:
|
|
7
|
+
* - Querying editor state (buffer info, cursor position)
|
|
8
|
+
* - Sending commands (status messages, text insertion)
|
|
9
|
+
* - Using async/await for plugin actions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Global action: Display buffer information
|
|
13
|
+
globalThis.show_buffer_info = function (): void {
|
|
14
|
+
const bufferId = editor.getActiveBufferId();
|
|
15
|
+
const path = editor.getBufferPath(bufferId);
|
|
16
|
+
const length = editor.getBufferLength(bufferId);
|
|
17
|
+
const modified = editor.isBufferModified(bufferId);
|
|
18
|
+
const cursorPos = editor.getCursorPosition();
|
|
19
|
+
|
|
20
|
+
const status = `Buffer ${bufferId}: ${path || "[untitled]"} | ${length} bytes | ${
|
|
21
|
+
modified ? "modified" : "saved"
|
|
22
|
+
} | cursor@${cursorPos}`;
|
|
23
|
+
|
|
24
|
+
editor.setStatus(status);
|
|
25
|
+
editor.debug(`Buffer info: ${status}`);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Global action: Insert timestamp at cursor
|
|
29
|
+
globalThis.insert_timestamp = function (): void {
|
|
30
|
+
const bufferId = editor.getActiveBufferId();
|
|
31
|
+
const cursorPos = editor.getCursorPosition();
|
|
32
|
+
const timestamp = new Date().toISOString();
|
|
33
|
+
|
|
34
|
+
const success = editor.insertText(bufferId, cursorPos, timestamp);
|
|
35
|
+
if (success) {
|
|
36
|
+
editor.setStatus(`Inserted timestamp: ${timestamp}`);
|
|
37
|
+
} else {
|
|
38
|
+
editor.setStatus("Failed to insert timestamp");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Global action: Highlight current line (demo overlay)
|
|
43
|
+
globalThis.highlight_region = function (): void {
|
|
44
|
+
const bufferId = editor.getActiveBufferId();
|
|
45
|
+
const cursorPos = editor.getCursorPosition();
|
|
46
|
+
|
|
47
|
+
// Highlight 10 characters around cursor
|
|
48
|
+
const start = Math.max(0, cursorPos - 5);
|
|
49
|
+
const end = cursorPos + 5;
|
|
50
|
+
|
|
51
|
+
// Use namespace "demo" for batch operations
|
|
52
|
+
const success = editor.addOverlay(
|
|
53
|
+
bufferId,
|
|
54
|
+
"demo", // namespace
|
|
55
|
+
start,
|
|
56
|
+
end,
|
|
57
|
+
255, // Red
|
|
58
|
+
255, // Green
|
|
59
|
+
0, // Blue (yellow highlight)
|
|
60
|
+
false // No underline
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (success) {
|
|
64
|
+
editor.setStatus(`Highlighted region ${start}-${end}`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Global action: Remove highlight
|
|
69
|
+
globalThis.clear_highlight = function (): void {
|
|
70
|
+
const bufferId = editor.getActiveBufferId();
|
|
71
|
+
// Clear all overlays in the "demo" namespace
|
|
72
|
+
const success = editor.clearNamespace(bufferId, "demo");
|
|
73
|
+
if (success) {
|
|
74
|
+
editor.setStatus("Cleared highlight");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Global async action: Demonstrate async/await
|
|
79
|
+
globalThis.async_demo = async function (): Promise<void> {
|
|
80
|
+
editor.setStatus("Starting async operation...");
|
|
81
|
+
|
|
82
|
+
// Simulate some async work
|
|
83
|
+
await Promise.resolve();
|
|
84
|
+
|
|
85
|
+
const bufferId = editor.getActiveBufferId();
|
|
86
|
+
const length = editor.getBufferLength(bufferId);
|
|
87
|
+
|
|
88
|
+
editor.setStatus(`Async operation complete! Buffer has ${length} bytes`);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Log that plugin loaded
|
|
92
|
+
editor.debug("Hello World plugin loaded!");
|
|
93
|
+
editor.setStatus("Hello World plugin ready");
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Virtual Buffer Demo Plugin
|
|
2
|
+
// Demonstrates the virtual buffer API for creating diagnostic panels, search results, etc.
|
|
3
|
+
|
|
4
|
+
// Register a command to show a demo virtual buffer
|
|
5
|
+
editor.registerCommand(
|
|
6
|
+
"Virtual Buffer Demo",
|
|
7
|
+
"Show a demo virtual buffer with sample diagnostics",
|
|
8
|
+
"show_virtual_buffer_demo",
|
|
9
|
+
"normal"
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
// Define a custom mode for the demo buffer
|
|
13
|
+
editor.defineMode(
|
|
14
|
+
"demo-list", // mode name
|
|
15
|
+
null, // no parent mode
|
|
16
|
+
[
|
|
17
|
+
["Return", "demo_goto_item"],
|
|
18
|
+
["n", "demo_next_item"],
|
|
19
|
+
["p", "demo_prev_item"],
|
|
20
|
+
["q", "demo_close_buffer"],
|
|
21
|
+
],
|
|
22
|
+
true // read-only
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Register actions for the mode
|
|
26
|
+
globalThis.demo_goto_item = () => {
|
|
27
|
+
const bufferId = editor.getActiveBufferId();
|
|
28
|
+
const props = editor.getTextPropertiesAtCursor(bufferId);
|
|
29
|
+
|
|
30
|
+
if (props.length > 0) {
|
|
31
|
+
const location = props[0].location as { file: string; line: number; column: number } | undefined;
|
|
32
|
+
if (location) {
|
|
33
|
+
editor.openFile(location.file, location.line, location.column || 0);
|
|
34
|
+
editor.setStatus(`Jumped to ${location.file}:${location.line}`);
|
|
35
|
+
} else {
|
|
36
|
+
editor.setStatus("No location info for this item");
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
editor.setStatus("No properties at cursor position");
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
globalThis.demo_next_item = () => {
|
|
44
|
+
editor.setStatus("Next item (not implemented in demo)");
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
globalThis.demo_prev_item = () => {
|
|
48
|
+
editor.setStatus("Previous item (not implemented in demo)");
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
globalThis.demo_close_buffer = () => {
|
|
52
|
+
editor.setStatus("Close buffer (not implemented in demo)");
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Main action: show the virtual buffer
|
|
56
|
+
globalThis.show_virtual_buffer_demo = async () => {
|
|
57
|
+
editor.setStatus("Creating virtual buffer demo...");
|
|
58
|
+
|
|
59
|
+
// Create sample diagnostic entries
|
|
60
|
+
const entries = [
|
|
61
|
+
{
|
|
62
|
+
text: "[ERROR] src/main.rs:42:10 - undefined variable 'foo'\n",
|
|
63
|
+
properties: {
|
|
64
|
+
severity: "error",
|
|
65
|
+
location: { file: "src/main.rs", line: 42, column: 10 },
|
|
66
|
+
message: "undefined variable 'foo'",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
text: "[WARNING] src/lib.rs:100:5 - unused variable 'bar'\n",
|
|
71
|
+
properties: {
|
|
72
|
+
severity: "warning",
|
|
73
|
+
location: { file: "src/lib.rs", line: 100, column: 5 },
|
|
74
|
+
message: "unused variable 'bar'",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
text: "[INFO] src/utils.rs:25:1 - consider using 'if let' instead of 'match'\n",
|
|
79
|
+
properties: {
|
|
80
|
+
severity: "info",
|
|
81
|
+
location: { file: "src/utils.rs", line: 25, column: 1 },
|
|
82
|
+
message: "consider using 'if let' instead of 'match'",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
text: "[HINT] src/config.rs:8:20 - type annotation unnecessary\n",
|
|
87
|
+
properties: {
|
|
88
|
+
severity: "hint",
|
|
89
|
+
location: { file: "src/config.rs", line: 8, column: 20 },
|
|
90
|
+
message: "type annotation unnecessary",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
// Create the virtual buffer in a horizontal split
|
|
96
|
+
try {
|
|
97
|
+
const bufferId = await editor.createVirtualBufferInSplit({
|
|
98
|
+
name: "*Demo Diagnostics*",
|
|
99
|
+
mode: "demo-list",
|
|
100
|
+
read_only: true,
|
|
101
|
+
entries: entries,
|
|
102
|
+
ratio: 0.7, // Original pane takes 70%, demo buffer takes 30%
|
|
103
|
+
panel_id: "demo-diagnostics",
|
|
104
|
+
show_line_numbers: false,
|
|
105
|
+
show_cursors: true,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
editor.setStatus(`Created demo virtual buffer (ID: ${bufferId}) with ${entries.length} items - Press RET to jump to location`);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
111
|
+
editor.setStatus(`Failed to create virtual buffer: ${errorMessage}`);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Log that the plugin loaded
|
|
116
|
+
editor.debug("Virtual buffer demo plugin loaded");
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/// <reference path="../types/fresh.d.ts" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Find References Plugin (TypeScript)
|
|
5
|
+
*
|
|
6
|
+
* Displays LSP find references results in a virtual buffer split view.
|
|
7
|
+
* Listens for lsp_references hook from the editor and shows results.
|
|
8
|
+
* Uses cursor movement for navigation (Up/Down/j/k work naturally).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Panel state
|
|
12
|
+
let panelOpen = false;
|
|
13
|
+
let referencesBufferId: number | null = null;
|
|
14
|
+
let sourceSplitId: number | null = null;
|
|
15
|
+
let referencesSplitId: number | null = null; // Track the split we created
|
|
16
|
+
let currentReferences: ReferenceItem[] = [];
|
|
17
|
+
let currentSymbol: string = "";
|
|
18
|
+
let lineCache: Map<string, string[]> = new Map(); // Cache file contents
|
|
19
|
+
|
|
20
|
+
// Maximum number of results to display
|
|
21
|
+
const MAX_RESULTS = 100;
|
|
22
|
+
|
|
23
|
+
// Reference item structure
|
|
24
|
+
interface ReferenceItem {
|
|
25
|
+
file: string;
|
|
26
|
+
line: number;
|
|
27
|
+
column: number;
|
|
28
|
+
lineText?: string; // Cached line text
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Define the references mode with minimal keybindings
|
|
32
|
+
// Navigation uses normal cursor movement (arrows, j/k work naturally)
|
|
33
|
+
editor.defineMode(
|
|
34
|
+
"references-list",
|
|
35
|
+
null, // no parent mode
|
|
36
|
+
[
|
|
37
|
+
["Return", "references_goto"],
|
|
38
|
+
["q", "references_close"],
|
|
39
|
+
["Escape", "references_close"],
|
|
40
|
+
],
|
|
41
|
+
true // read-only
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Get relative path for display
|
|
45
|
+
function getRelativePath(filePath: string): string {
|
|
46
|
+
const cwd = editor.getCwd();
|
|
47
|
+
if (filePath.startsWith(cwd)) {
|
|
48
|
+
return filePath.slice(cwd.length + 1); // Remove cwd and leading /
|
|
49
|
+
}
|
|
50
|
+
return filePath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Format a reference for display with line preview
|
|
54
|
+
function formatReference(item: ReferenceItem): string {
|
|
55
|
+
const displayPath = getRelativePath(item.file);
|
|
56
|
+
const location = `${displayPath}:${item.line}:${item.column}`;
|
|
57
|
+
|
|
58
|
+
// Truncate location if too long, leaving room for line text
|
|
59
|
+
const maxLocationLen = 50;
|
|
60
|
+
const truncatedLocation = location.length > maxLocationLen
|
|
61
|
+
? "..." + location.slice(-(maxLocationLen - 3))
|
|
62
|
+
: location.padEnd(maxLocationLen);
|
|
63
|
+
|
|
64
|
+
// Get line text preview (truncated)
|
|
65
|
+
const lineText = item.lineText || "";
|
|
66
|
+
const trimmedLine = lineText.trim();
|
|
67
|
+
const maxLineLen = 60;
|
|
68
|
+
const displayLine = trimmedLine.length > maxLineLen
|
|
69
|
+
? trimmedLine.slice(0, maxLineLen - 3) + "..."
|
|
70
|
+
: trimmedLine;
|
|
71
|
+
|
|
72
|
+
return ` ${truncatedLocation} ${displayLine}\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Build entries for the virtual buffer
|
|
76
|
+
function buildPanelEntries(): TextPropertyEntry[] {
|
|
77
|
+
const entries: TextPropertyEntry[] = [];
|
|
78
|
+
|
|
79
|
+
// Header with symbol name
|
|
80
|
+
const totalCount = currentReferences.length;
|
|
81
|
+
const limitNote = totalCount >= MAX_RESULTS ? ` (limited to ${MAX_RESULTS})` : "";
|
|
82
|
+
const symbolDisplay = currentSymbol ? `'${currentSymbol}'` : "symbol";
|
|
83
|
+
entries.push({
|
|
84
|
+
text: `═══ References to ${symbolDisplay} (${totalCount}${limitNote}) ═══\n`,
|
|
85
|
+
properties: { type: "header" },
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (currentReferences.length === 0) {
|
|
89
|
+
entries.push({
|
|
90
|
+
text: " No references found\n",
|
|
91
|
+
properties: { type: "empty" },
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
// Add each reference
|
|
95
|
+
for (let i = 0; i < currentReferences.length; i++) {
|
|
96
|
+
const ref = currentReferences[i];
|
|
97
|
+
entries.push({
|
|
98
|
+
text: formatReference(ref),
|
|
99
|
+
properties: {
|
|
100
|
+
type: "reference",
|
|
101
|
+
index: i,
|
|
102
|
+
location: {
|
|
103
|
+
file: ref.file,
|
|
104
|
+
line: ref.line,
|
|
105
|
+
column: ref.column,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Footer
|
|
113
|
+
entries.push({
|
|
114
|
+
text: `───────────────────────────────────────────────────────────────────────────────\n`,
|
|
115
|
+
properties: { type: "separator" },
|
|
116
|
+
});
|
|
117
|
+
entries.push({
|
|
118
|
+
text: `[↑/↓] navigate [RET] jump [q/Esc] close\n`,
|
|
119
|
+
properties: { type: "help" },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return entries;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Load line text for references
|
|
126
|
+
async function loadLineTexts(references: ReferenceItem[]): Promise<void> {
|
|
127
|
+
// Group references by file
|
|
128
|
+
const fileRefs: Map<string, ReferenceItem[]> = new Map();
|
|
129
|
+
for (const ref of references) {
|
|
130
|
+
if (!fileRefs.has(ref.file)) {
|
|
131
|
+
fileRefs.set(ref.file, []);
|
|
132
|
+
}
|
|
133
|
+
fileRefs.get(ref.file)!.push(ref);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Load each file and extract lines
|
|
137
|
+
for (const [filePath, refs] of fileRefs) {
|
|
138
|
+
try {
|
|
139
|
+
// Check cache first
|
|
140
|
+
let lines = lineCache.get(filePath);
|
|
141
|
+
if (!lines) {
|
|
142
|
+
const content = await editor.readFile(filePath);
|
|
143
|
+
lines = content.split("\n");
|
|
144
|
+
lineCache.set(filePath, lines);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Set line text for each reference
|
|
148
|
+
for (const ref of refs) {
|
|
149
|
+
const lineIndex = ref.line - 1; // Convert 1-based to 0-based
|
|
150
|
+
if (lineIndex >= 0 && lineIndex < lines.length) {
|
|
151
|
+
ref.lineText = lines[lineIndex];
|
|
152
|
+
} else {
|
|
153
|
+
ref.lineText = "";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
// If file can't be read, leave lineText empty
|
|
158
|
+
for (const ref of refs) {
|
|
159
|
+
ref.lineText = "";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Show references panel
|
|
166
|
+
async function showReferencesPanel(symbol: string, references: ReferenceItem[]): Promise<void> {
|
|
167
|
+
// Only save the source split ID if panel is not already open
|
|
168
|
+
// (avoid overwriting it with the references split ID on subsequent calls)
|
|
169
|
+
if (!panelOpen) {
|
|
170
|
+
sourceSplitId = editor.getActiveSplitId();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Limit results
|
|
174
|
+
const limitedRefs = references.slice(0, MAX_RESULTS);
|
|
175
|
+
|
|
176
|
+
// Set references and symbol
|
|
177
|
+
currentSymbol = symbol;
|
|
178
|
+
currentReferences = limitedRefs;
|
|
179
|
+
|
|
180
|
+
// Load line texts for preview
|
|
181
|
+
await loadLineTexts(currentReferences);
|
|
182
|
+
|
|
183
|
+
// Build panel entries
|
|
184
|
+
const entries = buildPanelEntries();
|
|
185
|
+
|
|
186
|
+
// Create or update virtual buffer in horizontal split
|
|
187
|
+
// The panel_id mechanism will reuse the existing buffer/split if it exists
|
|
188
|
+
try {
|
|
189
|
+
referencesBufferId = await editor.createVirtualBufferInSplit({
|
|
190
|
+
name: "*References*",
|
|
191
|
+
mode: "references-list",
|
|
192
|
+
read_only: true,
|
|
193
|
+
entries: entries,
|
|
194
|
+
ratio: 0.7, // Original pane takes 70%, references takes 30%
|
|
195
|
+
panel_id: "references-panel",
|
|
196
|
+
show_line_numbers: false,
|
|
197
|
+
show_cursors: true, // Enable cursor for navigation
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
panelOpen = true;
|
|
201
|
+
// Track the references split (it becomes active after creation)
|
|
202
|
+
referencesSplitId = editor.getActiveSplitId();
|
|
203
|
+
|
|
204
|
+
const limitMsg = references.length > MAX_RESULTS
|
|
205
|
+
? ` (showing first ${MAX_RESULTS})`
|
|
206
|
+
: "";
|
|
207
|
+
editor.setStatus(
|
|
208
|
+
`Found ${references.length} reference(s)${limitMsg} - ↑/↓ navigate, RET jump, q close`
|
|
209
|
+
);
|
|
210
|
+
editor.debug(`References panel opened with buffer ID ${referencesBufferId}, split ID ${referencesSplitId}`);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
213
|
+
editor.setStatus("Failed to open references panel");
|
|
214
|
+
editor.debug(`ERROR: createVirtualBufferInSplit failed: ${errorMessage}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Handle lsp_references hook
|
|
219
|
+
globalThis.on_lsp_references = function (data: { symbol: string; locations: ReferenceItem[] }): void {
|
|
220
|
+
editor.debug(`Received ${data.locations.length} references for '${data.symbol}'`);
|
|
221
|
+
|
|
222
|
+
if (data.locations.length === 0) {
|
|
223
|
+
editor.setStatus(`No references found for '${data.symbol}'`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Clear line cache for fresh results
|
|
228
|
+
lineCache.clear();
|
|
229
|
+
|
|
230
|
+
// Show the references panel
|
|
231
|
+
showReferencesPanel(data.symbol, data.locations);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Register the hook handler
|
|
235
|
+
editor.on("lsp_references", "on_lsp_references");
|
|
236
|
+
|
|
237
|
+
// Handle cursor movement to show current reference info
|
|
238
|
+
globalThis.on_references_cursor_moved = function (data: {
|
|
239
|
+
buffer_id: number;
|
|
240
|
+
cursor_id: number;
|
|
241
|
+
old_position: number;
|
|
242
|
+
new_position: number;
|
|
243
|
+
}): void {
|
|
244
|
+
// Only handle cursor movement in our references buffer
|
|
245
|
+
if (referencesBufferId === null || data.buffer_id !== referencesBufferId) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get cursor line to determine which reference is selected
|
|
250
|
+
// getCursorLine() returns the line for the active buffer
|
|
251
|
+
const cursorLine = editor.getCursorLine();
|
|
252
|
+
|
|
253
|
+
// Line 0 is header, lines 1 to N are references
|
|
254
|
+
const refIndex = cursorLine - 1;
|
|
255
|
+
|
|
256
|
+
if (refIndex >= 0 && refIndex < currentReferences.length) {
|
|
257
|
+
editor.setStatus(`Reference ${refIndex + 1}/${currentReferences.length}`);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Register cursor movement handler
|
|
262
|
+
editor.on("cursor_moved", "on_references_cursor_moved");
|
|
263
|
+
|
|
264
|
+
// Hide references panel
|
|
265
|
+
globalThis.hide_references_panel = function (): void {
|
|
266
|
+
if (!panelOpen) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (referencesBufferId !== null) {
|
|
271
|
+
editor.closeBuffer(referencesBufferId);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Close the split we created (if it exists and is different from source)
|
|
275
|
+
if (referencesSplitId !== null && referencesSplitId !== sourceSplitId) {
|
|
276
|
+
editor.closeSplit(referencesSplitId);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
panelOpen = false;
|
|
280
|
+
referencesBufferId = null;
|
|
281
|
+
sourceSplitId = null;
|
|
282
|
+
referencesSplitId = null;
|
|
283
|
+
currentReferences = [];
|
|
284
|
+
currentSymbol = "";
|
|
285
|
+
lineCache.clear();
|
|
286
|
+
editor.setStatus("References panel closed");
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Navigation: go to selected reference (based on cursor position)
|
|
290
|
+
globalThis.references_goto = function (): void {
|
|
291
|
+
if (currentReferences.length === 0) {
|
|
292
|
+
editor.setStatus("No references to jump to");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (sourceSplitId === null) {
|
|
297
|
+
editor.setStatus("Source split not available");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (referencesBufferId === null) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Get text properties at cursor position
|
|
306
|
+
const props = editor.getTextPropertiesAtCursor(referencesBufferId);
|
|
307
|
+
editor.debug(`references_goto: props.length=${props.length}, referencesBufferId=${referencesBufferId}, sourceSplitId=${sourceSplitId}`);
|
|
308
|
+
|
|
309
|
+
if (props.length > 0) {
|
|
310
|
+
editor.debug(`references_goto: props[0]=${JSON.stringify(props[0])}`);
|
|
311
|
+
const location = props[0].location as
|
|
312
|
+
| { file: string; line: number; column: number }
|
|
313
|
+
| undefined;
|
|
314
|
+
if (location) {
|
|
315
|
+
editor.debug(`references_goto: opening ${location.file}:${location.line}:${location.column} in split ${sourceSplitId}`);
|
|
316
|
+
// Open file in the source split, not the references split
|
|
317
|
+
editor.openFileInSplit(
|
|
318
|
+
sourceSplitId,
|
|
319
|
+
location.file,
|
|
320
|
+
location.line,
|
|
321
|
+
location.column || 0
|
|
322
|
+
);
|
|
323
|
+
const displayPath = getRelativePath(location.file);
|
|
324
|
+
editor.setStatus(`Jumped to ${displayPath}:${location.line}`);
|
|
325
|
+
} else {
|
|
326
|
+
editor.debug(`references_goto: no location in props[0]`);
|
|
327
|
+
editor.setStatus("Move cursor to a reference line");
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
editor.debug(`references_goto: no props found at cursor`);
|
|
331
|
+
editor.setStatus("Move cursor to a reference line");
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Close the references panel
|
|
336
|
+
globalThis.references_close = function (): void {
|
|
337
|
+
globalThis.hide_references_panel();
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// Register commands
|
|
341
|
+
editor.registerCommand(
|
|
342
|
+
"Show References Panel",
|
|
343
|
+
"Display current references",
|
|
344
|
+
"show_references_panel",
|
|
345
|
+
"normal"
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
editor.registerCommand(
|
|
349
|
+
"Hide References Panel",
|
|
350
|
+
"Close the references panel",
|
|
351
|
+
"hide_references_panel",
|
|
352
|
+
"normal"
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// Plugin initialization
|
|
356
|
+
editor.setStatus("Find References plugin loaded");
|
|
357
|
+
editor.debug("Find References plugin initialized");
|