@fresh-editor/fresh-editor 0.1.76 → 0.1.77
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 +29 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/plugins/config-schema.json +31 -1
- package/plugins/git_explorer.ts +159 -0
- package/plugins/lib/fresh.d.ts +25 -0
- package/plugins/lib/index.ts +9 -1
- package/plugins/lib/types.ts +14 -0
- package/plugins/odin-lsp.ts +135 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## 0.1.77
|
|
4
|
+
|
|
5
|
+
### Documentation
|
|
6
|
+
|
|
7
|
+
* **macOS Terminal Tips**: Added keyboard enhancement flags configuration guide.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **LSP Semantic Highlighting** (@Asuka-Minato).
|
|
12
|
+
* **macOS Keybinding Display**: Native symbols (⌃, ⌥, ⇧) instead of Ctrl+/Alt+/Shift+.
|
|
13
|
+
* **Odin Language Support**: Syntax highlighting (sublime-syntax from @Tetralux) and OLS LSP configuration (@xoxorwr).
|
|
14
|
+
* **File Explorer Git Indicators**: Shows modified/added status for files and folders via new plugin (#526) (@Asuka-Minato).
|
|
15
|
+
* **Keyboard Enhancement Flags Config**: New config options for more granular control over kitty protocol usage (`keyboard_disambiguate_escape_codes`, `keyboard_report_event_types`, `keyboard_report_alternate_keys`, `keyboard_report_all_keys_as_escape_codes`).
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **Menu Keybinding Display**: Consistent keybinding symbols in menus on macOS (#703).
|
|
20
|
+
* **Git Find File Popup**: Smart path truncation preserving filename (#707).
|
|
21
|
+
* **File Owner Preservation**: Preserve owner when saving files with group write privileges (#743).
|
|
22
|
+
|
|
23
|
+
### Internal
|
|
24
|
+
|
|
25
|
+
* Telemetry and update checks now debounce to once per day.
|
|
26
|
+
* Terminal mode handling refactored into dedicated module.
|
|
27
|
+
* Resolved ~300+ clippy warnings.
|
|
28
|
+
* Bumped url (2.5.8), libc (0.2.180) (@dependabot).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
3
32
|
## 0.1.76
|
|
4
33
|
|
|
5
34
|
### Features
|
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ Or, pick your preferred method:
|
|
|
71
71
|
|
|
72
72
|
On macOS and some linux distros (Bazzite/Bluefin/Aurora):
|
|
73
73
|
|
|
74
|
+
> **Note:** On macOS, see [macOS Terminal Tips](docs/USER_GUIDE.md#macos-terminal-tips) for recommended terminal configuration.
|
|
75
|
+
|
|
74
76
|
```bash
|
|
75
77
|
brew tap sinelaw/fresh
|
|
76
78
|
brew install fresh-editor
|
package/package.json
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"large_file_threshold_bytes": 1048576,
|
|
43
43
|
"estimated_line_length": 80,
|
|
44
44
|
"enable_inlay_hints": true,
|
|
45
|
+
"enable_semantic_tokens_full": false,
|
|
45
46
|
"recovery_enabled": true,
|
|
46
47
|
"auto_save_interval_secs": 2,
|
|
47
48
|
"highlight_context_bytes": 10000,
|
|
@@ -52,6 +53,10 @@
|
|
|
52
53
|
"file_tree_poll_interval_ms": 3000,
|
|
53
54
|
"default_line_ending": "lf",
|
|
54
55
|
"cursor_style": "default",
|
|
56
|
+
"keyboard_disambiguate_escape_codes": true,
|
|
57
|
+
"keyboard_report_event_types": false,
|
|
58
|
+
"keyboard_report_alternate_keys": true,
|
|
59
|
+
"keyboard_report_all_keys_as_escape_codes": false,
|
|
55
60
|
"quick_suggestions": true,
|
|
56
61
|
"show_menu_bar": true,
|
|
57
62
|
"show_tab_bar": true
|
|
@@ -225,7 +230,7 @@
|
|
|
225
230
|
"default": 100
|
|
226
231
|
},
|
|
227
232
|
"large_file_threshold_bytes": {
|
|
228
|
-
"description": "File size threshold in bytes for \"large file\" behavior\nFiles larger than this will:\n- Skip LSP features\n- Use constant-size scrollbar thumb (1 char)\nFiles smaller will count actual lines for accurate scrollbar rendering",
|
|
233
|
+
"description": "File size threshold in bytes for \"large file\" behavior\nFiles larger than this will:\n- Skip LSP features\n- Use constant-size scrollbar thumb (1 char)\n\nFiles smaller will count actual lines for accurate scrollbar rendering",
|
|
229
234
|
"type": "integer",
|
|
230
235
|
"format": "uint64",
|
|
231
236
|
"minimum": 0,
|
|
@@ -243,6 +248,11 @@
|
|
|
243
248
|
"type": "boolean",
|
|
244
249
|
"default": true
|
|
245
250
|
},
|
|
251
|
+
"enable_semantic_tokens_full": {
|
|
252
|
+
"description": "Whether to request full-document LSP semantic tokens.\nRange requests are still used when supported.\nDefault: false (range-only to avoid heavy full refreshes).",
|
|
253
|
+
"type": "boolean",
|
|
254
|
+
"default": false
|
|
255
|
+
},
|
|
246
256
|
"recovery_enabled": {
|
|
247
257
|
"description": "Whether to enable file recovery (Emacs-style auto-save)\nWhen enabled, buffers are periodically saved to recovery files\nso they can be recovered if the editor crashes.",
|
|
248
258
|
"type": "boolean",
|
|
@@ -305,6 +315,26 @@
|
|
|
305
315
|
"$ref": "#/$defs/CursorStyle",
|
|
306
316
|
"default": "default"
|
|
307
317
|
},
|
|
318
|
+
"keyboard_disambiguate_escape_codes": {
|
|
319
|
+
"description": "Enable keyboard enhancement: disambiguate escape codes using CSI-u sequences.\nThis allows unambiguous reading of Escape and modified keys.\nRequires terminal support (kitty keyboard protocol).\nDefault: true",
|
|
320
|
+
"type": "boolean",
|
|
321
|
+
"default": true
|
|
322
|
+
},
|
|
323
|
+
"keyboard_report_event_types": {
|
|
324
|
+
"description": "Enable keyboard enhancement: report key event types (repeat/release).\nAdds extra events when keys are autorepeated or released.\nRequires terminal support (kitty keyboard protocol).\nDefault: false",
|
|
325
|
+
"type": "boolean",
|
|
326
|
+
"default": false
|
|
327
|
+
},
|
|
328
|
+
"keyboard_report_alternate_keys": {
|
|
329
|
+
"description": "Enable keyboard enhancement: report alternate keycodes.\nSends alternate keycodes in addition to the base keycode.\nRequires terminal support (kitty keyboard protocol).\nDefault: true",
|
|
330
|
+
"type": "boolean",
|
|
331
|
+
"default": true
|
|
332
|
+
},
|
|
333
|
+
"keyboard_report_all_keys_as_escape_codes": {
|
|
334
|
+
"description": "Enable keyboard enhancement: report all keys as escape codes.\nRepresents all keyboard events as CSI-u sequences.\nRequired for repeat/release events on plain-text keys.\nRequires terminal support (kitty keyboard protocol).\nDefault: false",
|
|
335
|
+
"type": "boolean",
|
|
336
|
+
"default": false
|
|
337
|
+
},
|
|
308
338
|
"quick_suggestions": {
|
|
309
339
|
"description": "Enable quick suggestions (VS Code-like behavior).\nWhen enabled, completion suggestions appear automatically while typing,\nnot just on trigger characters (like `.` or `::`).\nDefault: true",
|
|
310
340
|
"type": "boolean",
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/// <reference path="../types/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Git Explorer Decorations
|
|
6
|
+
*
|
|
7
|
+
* Adds VS Code-style status badges (M/A/U/D/...) to the file explorer.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const NAMESPACE = "git-explorer";
|
|
11
|
+
|
|
12
|
+
const COLORS = {
|
|
13
|
+
added: [80, 250, 123] as [number, number, number],
|
|
14
|
+
modified: [255, 184, 108] as [number, number, number],
|
|
15
|
+
deleted: [255, 85, 85] as [number, number, number],
|
|
16
|
+
renamed: [139, 233, 253] as [number, number, number],
|
|
17
|
+
untracked: [241, 250, 140] as [number, number, number],
|
|
18
|
+
conflicted: [255, 121, 198] as [number, number, number],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const PRIORITY = {
|
|
22
|
+
conflicted: 90,
|
|
23
|
+
deleted: 80,
|
|
24
|
+
added: 60,
|
|
25
|
+
modified: 50,
|
|
26
|
+
renamed: 40,
|
|
27
|
+
untracked: 30,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let refreshInFlight = false;
|
|
31
|
+
|
|
32
|
+
function statusToDecoration(status: string, staged: boolean) {
|
|
33
|
+
switch (status) {
|
|
34
|
+
case "A":
|
|
35
|
+
return { symbol: "A", color: COLORS.added, priority: PRIORITY.added };
|
|
36
|
+
case "M":
|
|
37
|
+
return {
|
|
38
|
+
symbol: "M",
|
|
39
|
+
color: staged ? COLORS.added : COLORS.modified,
|
|
40
|
+
priority: PRIORITY.modified + (staged ? 2 : 0),
|
|
41
|
+
};
|
|
42
|
+
case "D":
|
|
43
|
+
return { symbol: "D", color: COLORS.deleted, priority: PRIORITY.deleted };
|
|
44
|
+
case "R":
|
|
45
|
+
return { symbol: "R", color: COLORS.renamed, priority: PRIORITY.renamed };
|
|
46
|
+
case "C":
|
|
47
|
+
return { symbol: "C", color: COLORS.renamed, priority: PRIORITY.renamed };
|
|
48
|
+
case "U":
|
|
49
|
+
return { symbol: "!", color: COLORS.conflicted, priority: PRIORITY.conflicted };
|
|
50
|
+
default:
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseStatusOutput(output: string, repoRoot: string) {
|
|
56
|
+
const separator = output.includes("\0") ? "\0" : "\n";
|
|
57
|
+
const entries = output
|
|
58
|
+
.split(separator)
|
|
59
|
+
.map((entry) => entry.replace(/\r$/, ""))
|
|
60
|
+
.filter((entry) => entry.length > 0);
|
|
61
|
+
const byPath = new Map<string, { path: string; symbol: string; color: [number, number, number]; priority: number }>();
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < entries.length; i++) {
|
|
64
|
+
const entry = entries[i];
|
|
65
|
+
if (entry.length < 3) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const x = entry[0];
|
|
69
|
+
const y = entry[1];
|
|
70
|
+
let path = entry.slice(3);
|
|
71
|
+
|
|
72
|
+
if ((x === "R" || x === "C") && separator === "\0" && i + 1 < entries.length) {
|
|
73
|
+
i += 1;
|
|
74
|
+
path = entries[i];
|
|
75
|
+
} else if (entry.includes(" -> ") && (x === "R" || x === "C" || y === "R" || y === "C")) {
|
|
76
|
+
path = entry.split(" -> ").pop() ?? path;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let decoration = null;
|
|
80
|
+
if (x === "?" && y === "?") {
|
|
81
|
+
decoration = { symbol: "U", color: COLORS.untracked, priority: PRIORITY.untracked };
|
|
82
|
+
} else if (x !== " " && x !== "?") {
|
|
83
|
+
decoration = statusToDecoration(x, true);
|
|
84
|
+
} else if (y !== " ") {
|
|
85
|
+
decoration = statusToDecoration(y, false);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!decoration) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const absolutePath = editor.pathJoin(repoRoot, path);
|
|
93
|
+
const existing = byPath.get(absolutePath);
|
|
94
|
+
if (!existing || decoration.priority >= existing.priority) {
|
|
95
|
+
byPath.set(absolutePath, { path: absolutePath, ...decoration });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return Array.from(byPath.values());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function refreshGitExplorerDecorations() {
|
|
103
|
+
if (refreshInFlight) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
refreshInFlight = true;
|
|
107
|
+
try {
|
|
108
|
+
const cwd = editor.getCwd();
|
|
109
|
+
const rootResult = await editor.spawnProcess("git", ["rev-parse", "--show-toplevel"], cwd);
|
|
110
|
+
if (rootResult.exit_code !== 0) {
|
|
111
|
+
editor.clearFileExplorerDecorations(NAMESPACE);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const repoRoot = rootResult.stdout.trim();
|
|
115
|
+
if (!repoRoot) {
|
|
116
|
+
editor.clearFileExplorerDecorations(NAMESPACE);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const statusResult = await editor.spawnProcess(
|
|
121
|
+
"git",
|
|
122
|
+
["status", "--porcelain"],
|
|
123
|
+
repoRoot
|
|
124
|
+
);
|
|
125
|
+
if (statusResult.exit_code !== 0) {
|
|
126
|
+
editor.clearFileExplorerDecorations(NAMESPACE);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const decorations = parseStatusOutput(statusResult.stdout, repoRoot);
|
|
131
|
+
if (decorations.length === 0) {
|
|
132
|
+
editor.clearFileExplorerDecorations(NAMESPACE);
|
|
133
|
+
} else {
|
|
134
|
+
editor.setFileExplorerDecorations(NAMESPACE, decorations);
|
|
135
|
+
}
|
|
136
|
+
} catch (_err) {
|
|
137
|
+
editor.clearFileExplorerDecorations(NAMESPACE);
|
|
138
|
+
} finally {
|
|
139
|
+
refreshInFlight = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
globalThis.onGitExplorerAfterFileOpen = () => {
|
|
144
|
+
refreshGitExplorerDecorations();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
globalThis.onGitExplorerAfterFileSave = () => {
|
|
148
|
+
refreshGitExplorerDecorations();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
globalThis.onGitExplorerEditorInitialized = () => {
|
|
152
|
+
refreshGitExplorerDecorations();
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
editor.on("after_file_open", "onGitExplorerAfterFileOpen");
|
|
156
|
+
editor.on("after_file_save", "onGitExplorerAfterFileSave");
|
|
157
|
+
editor.on("editor_initialized", "onGitExplorerEditorInitialized");
|
|
158
|
+
|
|
159
|
+
refreshGitExplorerDecorations();
|
package/plugins/lib/fresh.d.ts
CHANGED
|
@@ -170,6 +170,18 @@ interface ProcessHandle extends PromiseLike<SpawnResult> {
|
|
|
170
170
|
kill(): Promise<boolean>;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/** File explorer decoration entry provided by plugins */
|
|
174
|
+
interface FileExplorerDecoration {
|
|
175
|
+
/** Absolute or workspace-relative path to decorate */
|
|
176
|
+
path: string;
|
|
177
|
+
/** Symbol to display (single character recommended) */
|
|
178
|
+
symbol?: string | null;
|
|
179
|
+
/** RGB color for the symbol */
|
|
180
|
+
color?: [u8; 3] | null;
|
|
181
|
+
/** Priority for resolving conflicts (higher wins) */
|
|
182
|
+
priority?: number | null;
|
|
183
|
+
}
|
|
184
|
+
|
|
173
185
|
/** Result from spawnProcess */
|
|
174
186
|
interface SpawnResult {
|
|
175
187
|
/** Complete stdout as string. Newlines preserved; trailing newline included. */
|
|
@@ -786,6 +798,19 @@ interface EditorAPI {
|
|
|
786
798
|
* @returns true if indicators were cleared
|
|
787
799
|
*/
|
|
788
800
|
clearLineIndicators(buffer_id: number, namespace: string): boolean;
|
|
801
|
+
/**
|
|
802
|
+
* Set file explorer decorations for a namespace
|
|
803
|
+
* @param namespace - Namespace for grouping (e.g., "git-status")
|
|
804
|
+
* @param decorations - Decoration entries
|
|
805
|
+
* @returns true if decorations were accepted
|
|
806
|
+
*/
|
|
807
|
+
setFileExplorerDecorations(namespace: string, decorations: FileExplorerDecoration[]): boolean;
|
|
808
|
+
/**
|
|
809
|
+
* Clear file explorer decorations for a namespace
|
|
810
|
+
* @param namespace - Namespace to clear (e.g., "git-status")
|
|
811
|
+
* @returns true if decorations were cleared
|
|
812
|
+
*/
|
|
813
|
+
clearFileExplorerDecorations(namespace: string): boolean;
|
|
789
814
|
/**
|
|
790
815
|
* Submit a transformed view stream for a viewport
|
|
791
816
|
* @param buffer_id - Buffer to apply the transform to
|
package/plugins/lib/index.ts
CHANGED
|
@@ -11,7 +11,15 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
// Types
|
|
14
|
-
export type {
|
|
14
|
+
export type {
|
|
15
|
+
RGB,
|
|
16
|
+
Location,
|
|
17
|
+
PanelOptions,
|
|
18
|
+
PanelState,
|
|
19
|
+
NavigationOptions,
|
|
20
|
+
HighlightPattern,
|
|
21
|
+
FileExplorerDecoration,
|
|
22
|
+
} from "./types.ts";
|
|
15
23
|
|
|
16
24
|
// Panel Management
|
|
17
25
|
export { PanelManager } from "./panel-manager.ts";
|
package/plugins/lib/types.ts
CHANGED
|
@@ -11,6 +11,20 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export type RGB = [number, number, number];
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* File explorer decoration metadata provided by plugins
|
|
16
|
+
*/
|
|
17
|
+
export interface FileExplorerDecoration {
|
|
18
|
+
/** Absolute or workspace-relative path to decorate */
|
|
19
|
+
path: string;
|
|
20
|
+
/** Symbol to display (single character recommended) */
|
|
21
|
+
symbol?: string;
|
|
22
|
+
/** RGB color for the symbol */
|
|
23
|
+
color?: RGB;
|
|
24
|
+
/** Priority for resolving conflicts (higher wins) */
|
|
25
|
+
priority?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
/**
|
|
15
29
|
* File location with line and column
|
|
16
30
|
*/
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Odin LSP Helper Plugin
|
|
7
|
+
*
|
|
8
|
+
* Provides user-friendly error handling for Odin LSP server issues.
|
|
9
|
+
* When ols (Odin Language Server) fails to start, this plugin shows an actionable
|
|
10
|
+
* popup with installation instructions.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Detects Odin LSP server errors (ols)
|
|
14
|
+
* - Shows popup with build instructions
|
|
15
|
+
* - Allows copying build commands to clipboard
|
|
16
|
+
* - Provides option to disable Odin LSP
|
|
17
|
+
*
|
|
18
|
+
* OLS: https://github.com/DanielGavin/ols
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
interface LspServerErrorData {
|
|
22
|
+
language: string;
|
|
23
|
+
server_command: string;
|
|
24
|
+
error_type: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface LspStatusClickedData {
|
|
29
|
+
language: string;
|
|
30
|
+
has_error: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ActionPopupResultData {
|
|
34
|
+
popup_id: string;
|
|
35
|
+
action_id: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// OLS GitHub repository
|
|
39
|
+
const OLS_URL = "https://github.com/DanielGavin/ols";
|
|
40
|
+
|
|
41
|
+
// Track error state for Odin LSP
|
|
42
|
+
let odinLspError: { serverCommand: string; message: string } | null = null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handle LSP server errors for Odin
|
|
46
|
+
*/
|
|
47
|
+
globalThis.on_odin_lsp_server_error = function (data: LspServerErrorData): void {
|
|
48
|
+
// Only handle Odin language errors
|
|
49
|
+
if (data.language !== "odin") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
editor.debug(`odin-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
54
|
+
|
|
55
|
+
// Store error state for later reference
|
|
56
|
+
odinLspError = {
|
|
57
|
+
serverCommand: data.server_command,
|
|
58
|
+
message: data.message,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Show a status message for immediate feedback
|
|
62
|
+
if (data.error_type === "not_found") {
|
|
63
|
+
editor.setStatus(
|
|
64
|
+
`Odin LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
editor.setStatus(`Odin LSP error: ${data.message}`);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Register hook for LSP server errors
|
|
72
|
+
editor.on("lsp_server_error", "on_odin_lsp_server_error");
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Handle status bar click when there's an Odin LSP error
|
|
76
|
+
*/
|
|
77
|
+
globalThis.on_odin_lsp_status_clicked = function (
|
|
78
|
+
data: LspStatusClickedData
|
|
79
|
+
): void {
|
|
80
|
+
// Only handle Odin language clicks when there's an error
|
|
81
|
+
if (data.language !== "odin" || !odinLspError) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
editor.debug("odin-lsp: Status clicked, showing help popup");
|
|
86
|
+
|
|
87
|
+
// Show action popup with install options
|
|
88
|
+
editor.showActionPopup({
|
|
89
|
+
id: "odin-lsp-help",
|
|
90
|
+
title: "Odin Language Server Not Found",
|
|
91
|
+
message: `"${odinLspError.serverCommand}" (OLS) provides code completion, diagnostics, and navigation for Odin files.\n\nInstallation: ${OLS_URL}`,
|
|
92
|
+
actions: [
|
|
93
|
+
{ id: "disable", label: "Disable Odin LSP" },
|
|
94
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Register hook for status bar clicks
|
|
100
|
+
editor.on("lsp_status_clicked", "on_odin_lsp_status_clicked");
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Handle action popup results for Odin LSP help
|
|
104
|
+
*/
|
|
105
|
+
globalThis.on_odin_lsp_action_result = function (
|
|
106
|
+
data: ActionPopupResultData
|
|
107
|
+
): void {
|
|
108
|
+
// Only handle our popup
|
|
109
|
+
if (data.popup_id !== "odin-lsp-help") {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
editor.debug(`odin-lsp: Action selected - ${data.action_id}`);
|
|
114
|
+
|
|
115
|
+
switch (data.action_id) {
|
|
116
|
+
case "disable":
|
|
117
|
+
editor.disableLspForLanguage("odin");
|
|
118
|
+
editor.setStatus("Odin LSP disabled");
|
|
119
|
+
odinLspError = null;
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case "dismiss":
|
|
123
|
+
case "dismissed":
|
|
124
|
+
// Just close the popup without action
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
default:
|
|
128
|
+
editor.debug(`odin-lsp: Unknown action: ${data.action_id}`);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Register hook for action popup results
|
|
133
|
+
editor.on("action_popup_result", "on_odin_lsp_action_result");
|
|
134
|
+
|
|
135
|
+
editor.debug("odin-lsp: Plugin loaded");
|