@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,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"en": {
|
|
3
|
+
"status.loaded": "Path completion plugin loaded successfully",
|
|
4
|
+
"suggestion.directory": "directory",
|
|
5
|
+
"suggestion.new_file": "%{filename} (new file)",
|
|
6
|
+
"suggestion.new_file_desc": "File does not exist yet"
|
|
7
|
+
},
|
|
8
|
+
"cs": {
|
|
9
|
+
"status.loaded": "Plugin pro doplnovani cest uspesne nacten",
|
|
10
|
+
"suggestion.directory": "adresar",
|
|
11
|
+
"suggestion.new_file": "%{filename} (novy soubor)",
|
|
12
|
+
"suggestion.new_file_desc": "Soubor zatim neexistuje"
|
|
13
|
+
},
|
|
14
|
+
"de": {
|
|
15
|
+
"status.loaded": "Pfadvervollstandigungs-Plugin erfolgreich geladen",
|
|
16
|
+
"suggestion.directory": "Verzeichnis",
|
|
17
|
+
"suggestion.new_file": "%{filename} (neue Datei)",
|
|
18
|
+
"suggestion.new_file_desc": "Datei existiert noch nicht"
|
|
19
|
+
},
|
|
20
|
+
"es": {
|
|
21
|
+
"status.loaded": "Plugin de autocompletado de rutas cargado correctamente",
|
|
22
|
+
"suggestion.directory": "directorio",
|
|
23
|
+
"suggestion.new_file": "%{filename} (archivo nuevo)",
|
|
24
|
+
"suggestion.new_file_desc": "El archivo aun no existe"
|
|
25
|
+
},
|
|
26
|
+
"fr": {
|
|
27
|
+
"status.loaded": "Plugin de completion de chemin charge avec succes",
|
|
28
|
+
"suggestion.directory": "repertoire",
|
|
29
|
+
"suggestion.new_file": "%{filename} (nouveau fichier)",
|
|
30
|
+
"suggestion.new_file_desc": "Le fichier n'existe pas encore"
|
|
31
|
+
},
|
|
32
|
+
"it": {
|
|
33
|
+
"status.loaded": "Plugin completamento percorsi caricato con successo",
|
|
34
|
+
"suggestion.directory": "directory",
|
|
35
|
+
"suggestion.new_file": "%{filename} (nuovo file)",
|
|
36
|
+
"suggestion.new_file_desc": "Il file non esiste ancora"
|
|
37
|
+
},
|
|
38
|
+
"ja": {
|
|
39
|
+
"status.loaded": "パス補完プラグインを正常に読み込みました",
|
|
40
|
+
"suggestion.directory": "ディレクトリ",
|
|
41
|
+
"suggestion.new_file": "%{filename} (新規ファイル)",
|
|
42
|
+
"suggestion.new_file_desc": "ファイルはまだ存在しません"
|
|
43
|
+
},
|
|
44
|
+
"ko": {
|
|
45
|
+
"status.loaded": "경로 자동완성 플러그인이 성공적으로 로드되었습니다",
|
|
46
|
+
"suggestion.directory": "디렉토리",
|
|
47
|
+
"suggestion.new_file": "%{filename} (새 파일)",
|
|
48
|
+
"suggestion.new_file_desc": "파일이 아직 존재하지 않습니다"
|
|
49
|
+
},
|
|
50
|
+
"pt-BR": {
|
|
51
|
+
"status.loaded": "Plugin de autocompletar caminho carregado com sucesso",
|
|
52
|
+
"suggestion.directory": "diretorio",
|
|
53
|
+
"suggestion.new_file": "%{filename} (novo arquivo)",
|
|
54
|
+
"suggestion.new_file_desc": "O arquivo ainda nao existe"
|
|
55
|
+
},
|
|
56
|
+
"ru": {
|
|
57
|
+
"status.loaded": "Плагин автодополнения пути успешно загружен",
|
|
58
|
+
"suggestion.directory": "каталог",
|
|
59
|
+
"suggestion.new_file": "%{filename} (новый файл)",
|
|
60
|
+
"suggestion.new_file_desc": "Файл еще не существует"
|
|
61
|
+
},
|
|
62
|
+
"th": {
|
|
63
|
+
"status.loaded": "โหลดปลั๊กอินเติมเส้นทางสำเร็จ",
|
|
64
|
+
"suggestion.directory": "ไดเรกทอรี",
|
|
65
|
+
"suggestion.new_file": "%{filename} (ไฟล์ใหม่)",
|
|
66
|
+
"suggestion.new_file_desc": "ไฟล์ยังไม่มีอยู่"
|
|
67
|
+
},
|
|
68
|
+
"uk": {
|
|
69
|
+
"status.loaded": "Плагін автодоповнення шляху успішно завантажено",
|
|
70
|
+
"suggestion.directory": "каталог",
|
|
71
|
+
"suggestion.new_file": "%{filename} (новий файл)",
|
|
72
|
+
"suggestion.new_file_desc": "Файл ще не існує"
|
|
73
|
+
},
|
|
74
|
+
"zh-CN": {
|
|
75
|
+
"status.loaded": "路径补全插件加载成功",
|
|
76
|
+
"suggestion.directory": "目录",
|
|
77
|
+
"suggestion.new_file": "%{filename} (新文件)",
|
|
78
|
+
"suggestion.new_file_desc": "文件尚不存在"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/// <reference path="../types/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Path Completion Plugin
|
|
7
|
+
*
|
|
8
|
+
* Provides path autocompletion for file prompts (Open File, Save File As).
|
|
9
|
+
* Shows directory contents and filters based on user input.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Parse the input to extract directory path and search pattern
|
|
13
|
+
function parsePath(input: string): { dir: string; pattern: string; isAbsolute: boolean } {
|
|
14
|
+
if (input === "") {
|
|
15
|
+
return { dir: ".", pattern: "", isAbsolute: false };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isAbsolute = input.startsWith("/");
|
|
19
|
+
|
|
20
|
+
// Find the last path separator
|
|
21
|
+
const lastSlash = input.lastIndexOf("/");
|
|
22
|
+
|
|
23
|
+
if (lastSlash === -1) {
|
|
24
|
+
// No slash, searching in current directory
|
|
25
|
+
return { dir: ".", pattern: input, isAbsolute: false };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (lastSlash === 0) {
|
|
29
|
+
// Root directory
|
|
30
|
+
return { dir: "/", pattern: input.slice(1), isAbsolute: true };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Has directory component
|
|
34
|
+
const dir = input.slice(0, lastSlash);
|
|
35
|
+
const pattern = input.slice(lastSlash + 1);
|
|
36
|
+
|
|
37
|
+
return { dir: dir || "/", pattern, isAbsolute };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Filter and sort entries based on pattern
|
|
41
|
+
function filterEntries(entries: DirEntry[], pattern: string): DirEntry[] {
|
|
42
|
+
const patternLower = pattern.toLowerCase();
|
|
43
|
+
|
|
44
|
+
// Filter entries that match the pattern
|
|
45
|
+
const filtered = entries.filter((entry) => {
|
|
46
|
+
const nameLower = entry.name.toLowerCase();
|
|
47
|
+
// Match if pattern is prefix of name (case-insensitive)
|
|
48
|
+
return nameLower.startsWith(patternLower);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Sort: directories first, then alphabetically
|
|
52
|
+
filtered.sort((a, b) => {
|
|
53
|
+
// Directories come first
|
|
54
|
+
if (a.is_dir && !b.is_dir) return -1;
|
|
55
|
+
if (!a.is_dir && b.is_dir) return 1;
|
|
56
|
+
// Alphabetical within same type
|
|
57
|
+
return a.name.localeCompare(b.name);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return filtered;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Convert directory entries to suggestions
|
|
64
|
+
function entriesToSuggestions(entries: DirEntry[], basePath: string): PromptSuggestion[] {
|
|
65
|
+
return entries.map((entry) => {
|
|
66
|
+
// Build full path
|
|
67
|
+
let fullPath: string;
|
|
68
|
+
if (basePath === ".") {
|
|
69
|
+
fullPath = entry.name;
|
|
70
|
+
} else if (basePath === "/") {
|
|
71
|
+
fullPath = "/" + entry.name;
|
|
72
|
+
} else {
|
|
73
|
+
fullPath = basePath + "/" + entry.name;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add trailing slash for directories
|
|
77
|
+
const displayName = entry.is_dir ? entry.name + "/" : entry.name;
|
|
78
|
+
const value = entry.is_dir ? fullPath + "/" : fullPath;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
text: displayName,
|
|
82
|
+
description: entry.is_dir ? editor.t("suggestion.directory") : undefined,
|
|
83
|
+
value: value,
|
|
84
|
+
disabled: false,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function missingFileSuggestion(
|
|
90
|
+
input: string,
|
|
91
|
+
pattern: string,
|
|
92
|
+
): PromptSuggestion | null {
|
|
93
|
+
if (pattern === "" || input === "") {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let absolutePath = input;
|
|
98
|
+
if (!editor.pathIsAbsolute(absolutePath)) {
|
|
99
|
+
let cwd: string;
|
|
100
|
+
try {
|
|
101
|
+
cwd = editor.getCwd();
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
absolutePath = editor.pathJoin(cwd, absolutePath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (editor.fileExists(absolutePath)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
text: editor.t("suggestion.new_file", { filename: input }),
|
|
114
|
+
description: editor.t("suggestion.new_file_desc"),
|
|
115
|
+
value: input,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Generate path completions for the given input
|
|
120
|
+
function generateCompletions(input: string): PromptSuggestion[] {
|
|
121
|
+
const { dir, pattern } = parsePath(input);
|
|
122
|
+
|
|
123
|
+
// Read the directory
|
|
124
|
+
const entries = editor.readDir(dir);
|
|
125
|
+
const newFileSuggestion = missingFileSuggestion(input, pattern);
|
|
126
|
+
|
|
127
|
+
if (!entries) {
|
|
128
|
+
// Directory doesn't exist or can't be read
|
|
129
|
+
return newFileSuggestion ? [newFileSuggestion] : [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Filter hidden files (starting with .) unless pattern starts with .
|
|
133
|
+
const showHidden = pattern.startsWith(".");
|
|
134
|
+
const visibleEntries = entries.filter((e) => showHidden || !e.name.startsWith("."));
|
|
135
|
+
|
|
136
|
+
// Filter by pattern
|
|
137
|
+
const filtered = filterEntries(visibleEntries, pattern);
|
|
138
|
+
|
|
139
|
+
// Limit results
|
|
140
|
+
const limited = filtered.slice(0, 100);
|
|
141
|
+
|
|
142
|
+
// Convert to suggestions
|
|
143
|
+
const suggestions = entriesToSuggestions(limited, dir);
|
|
144
|
+
if (newFileSuggestion) {
|
|
145
|
+
suggestions.push(newFileSuggestion);
|
|
146
|
+
}
|
|
147
|
+
return suggestions;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Handle prompt changes for file prompts
|
|
151
|
+
globalThis.onPathCompletePromptChanged = function (args: { prompt_type: string; input: string }): boolean {
|
|
152
|
+
if (args.prompt_type !== "open-file" && args.prompt_type !== "save-file-as") {
|
|
153
|
+
return true; // Not our prompt
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const suggestions = generateCompletions(args.input);
|
|
157
|
+
editor.setPromptSuggestions(suggestions);
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Register event handler
|
|
163
|
+
editor.on("prompt_changed", "onPathCompletePromptChanged");
|
|
164
|
+
|
|
165
|
+
editor.setStatus(editor.t("status.loaded"));
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Python LSP Helper Plugin
|
|
7
|
+
*
|
|
8
|
+
* Provides user-friendly error handling for Python LSP server issues.
|
|
9
|
+
* When the Python LSP server fails to start, this plugin shows an
|
|
10
|
+
* actionable popup with installation instructions.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Detects Python LSP server errors (pylsp, python-lsp-server)
|
|
14
|
+
* - Shows popup with install commands (pip, pipx)
|
|
15
|
+
* - Allows copying install commands to clipboard
|
|
16
|
+
* - Provides option to disable Python LSP
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface LspServerErrorData {
|
|
20
|
+
language: string;
|
|
21
|
+
server_command: string;
|
|
22
|
+
error_type: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LspStatusClickedData {
|
|
27
|
+
language: string;
|
|
28
|
+
has_error: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ActionPopupResultData {
|
|
32
|
+
popup_id: string;
|
|
33
|
+
action_id: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Install commands for Python LSP server (python-lsp-server / pylsp)
|
|
37
|
+
// pipx provides isolated installation (recommended)
|
|
38
|
+
// pip_all includes all optional dependencies (rope, pyflakes, etc.)
|
|
39
|
+
// See: https://github.com/python-lsp/python-lsp-server
|
|
40
|
+
const INSTALL_COMMANDS = {
|
|
41
|
+
pipx: "pipx install python-lsp-server",
|
|
42
|
+
pip: "pip install python-lsp-server",
|
|
43
|
+
pip_all: "pip install 'python-lsp-server[all]'",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Track error state for Python LSP
|
|
47
|
+
let pythonLspError: { serverCommand: string; message: string } | null = null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handle LSP server errors for Python
|
|
51
|
+
*/
|
|
52
|
+
globalThis.on_python_lsp_server_error = function (
|
|
53
|
+
data: LspServerErrorData
|
|
54
|
+
): void {
|
|
55
|
+
// Only handle Python language errors
|
|
56
|
+
if (data.language !== "python") {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
editor.debug(
|
|
61
|
+
`python-lsp: Server error - ${data.error_type}: ${data.message}`
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Store error state for later reference
|
|
65
|
+
pythonLspError = {
|
|
66
|
+
serverCommand: data.server_command,
|
|
67
|
+
message: data.message,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Show a status message for immediate feedback
|
|
71
|
+
if (data.error_type === "not_found") {
|
|
72
|
+
editor.setStatus(
|
|
73
|
+
`Python LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
editor.setStatus(`Python LSP error: ${data.message}`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Register hook for LSP server errors
|
|
81
|
+
editor.on("lsp_server_error", "on_python_lsp_server_error");
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Handle status bar click when there's a Python LSP error
|
|
85
|
+
*/
|
|
86
|
+
globalThis.on_python_lsp_status_clicked = function (
|
|
87
|
+
data: LspStatusClickedData
|
|
88
|
+
): void {
|
|
89
|
+
// Only handle Python language clicks when there's an error
|
|
90
|
+
if (data.language !== "python" || !pythonLspError) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
editor.debug("python-lsp: Status clicked, showing help popup");
|
|
95
|
+
|
|
96
|
+
// Show action popup with install options
|
|
97
|
+
editor.showActionPopup({
|
|
98
|
+
id: "python-lsp-help",
|
|
99
|
+
title: "Python Language Server Not Found",
|
|
100
|
+
message: `"${pythonLspError.serverCommand}" provides code completion, diagnostics, and navigation for Python files. Copy a command below to install it, or search online for your platform.`,
|
|
101
|
+
actions: [
|
|
102
|
+
{ id: "copy_pipx", label: `Copy: ${INSTALL_COMMANDS.pipx}` },
|
|
103
|
+
{ id: "copy_pip", label: `Copy: ${INSTALL_COMMANDS.pip}` },
|
|
104
|
+
{ id: "copy_pip_all", label: `Copy: ${INSTALL_COMMANDS.pip_all}` },
|
|
105
|
+
{ id: "disable", label: "Disable Python LSP" },
|
|
106
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Register hook for status bar clicks
|
|
112
|
+
editor.on("lsp_status_clicked", "on_python_lsp_status_clicked");
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Handle action popup results for Python LSP help
|
|
116
|
+
*/
|
|
117
|
+
globalThis.on_python_lsp_action_result = function (
|
|
118
|
+
data: ActionPopupResultData
|
|
119
|
+
): void {
|
|
120
|
+
// Only handle our popup
|
|
121
|
+
if (data.popup_id !== "python-lsp-help") {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
editor.debug(`python-lsp: Action selected - ${data.action_id}`);
|
|
126
|
+
|
|
127
|
+
switch (data.action_id) {
|
|
128
|
+
case "copy_pipx":
|
|
129
|
+
editor.setClipboard(INSTALL_COMMANDS.pipx);
|
|
130
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.pipx);
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case "copy_pip":
|
|
134
|
+
editor.setClipboard(INSTALL_COMMANDS.pip);
|
|
135
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.pip);
|
|
136
|
+
break;
|
|
137
|
+
|
|
138
|
+
case "copy_pip_all":
|
|
139
|
+
editor.setClipboard(INSTALL_COMMANDS.pip_all);
|
|
140
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.pip_all);
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case "disable":
|
|
144
|
+
editor.disableLspForLanguage("python");
|
|
145
|
+
editor.setStatus("Python LSP disabled");
|
|
146
|
+
pythonLspError = null;
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case "dismiss":
|
|
150
|
+
case "dismissed":
|
|
151
|
+
// Just close the popup without action
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
default:
|
|
155
|
+
editor.debug(`python-lsp: Unknown action: ${data.action_id}`);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Register hook for action popup results
|
|
160
|
+
editor.on("action_popup_result", "on_python_lsp_action_result");
|
|
161
|
+
|
|
162
|
+
editor.debug("python-lsp: Plugin loaded");
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rust LSP Helper Plugin
|
|
7
|
+
*
|
|
8
|
+
* Provides user-friendly error handling for Rust LSP server issues.
|
|
9
|
+
* When rust-analyzer fails to start, this plugin shows an actionable
|
|
10
|
+
* popup with installation instructions.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Detects Rust LSP server errors (rust-analyzer)
|
|
14
|
+
* - Shows popup with install commands (rustup, brew)
|
|
15
|
+
* - Allows copying install commands to clipboard
|
|
16
|
+
* - Provides option to disable Rust LSP
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface LspServerErrorData {
|
|
20
|
+
language: string;
|
|
21
|
+
server_command: string;
|
|
22
|
+
error_type: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LspStatusClickedData {
|
|
27
|
+
language: string;
|
|
28
|
+
has_error: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ActionPopupResultData {
|
|
32
|
+
popup_id: string;
|
|
33
|
+
action_id: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Install commands for Rust LSP server
|
|
37
|
+
// rustup is the official recommended method
|
|
38
|
+
// brew is a good alternative for macOS users
|
|
39
|
+
// See: https://rust-analyzer.github.io/book/installation.html
|
|
40
|
+
const INSTALL_COMMANDS = {
|
|
41
|
+
rustup: "rustup component add rust-analyzer",
|
|
42
|
+
brew: "brew install rust-analyzer",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Track error state for Rust LSP
|
|
46
|
+
let rustLspError: { serverCommand: string; message: string } | null = null;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle LSP server errors for Rust
|
|
50
|
+
*/
|
|
51
|
+
globalThis.on_rust_lsp_server_error = function (
|
|
52
|
+
data: LspServerErrorData
|
|
53
|
+
): void {
|
|
54
|
+
// Only handle Rust language errors
|
|
55
|
+
if (data.language !== "rust") {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
editor.debug(`rust-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
60
|
+
|
|
61
|
+
// Store error state for later reference
|
|
62
|
+
rustLspError = {
|
|
63
|
+
serverCommand: data.server_command,
|
|
64
|
+
message: data.message,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Show a status message for immediate feedback
|
|
68
|
+
if (data.error_type === "not_found") {
|
|
69
|
+
editor.setStatus(
|
|
70
|
+
`Rust LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
editor.setStatus(`Rust LSP error: ${data.message}`);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Register hook for LSP server errors
|
|
78
|
+
editor.on("lsp_server_error", "on_rust_lsp_server_error");
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle status bar click when there's a Rust LSP error
|
|
82
|
+
*/
|
|
83
|
+
globalThis.on_rust_lsp_status_clicked = function (
|
|
84
|
+
data: LspStatusClickedData
|
|
85
|
+
): void {
|
|
86
|
+
editor.debug(
|
|
87
|
+
`rust-lsp: lsp_status_clicked hook received - language=${data.language}, has_error=${data.has_error}, rustLspError=${rustLspError ? "SET" : "NULL"}`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Only handle Rust language clicks when there's an error
|
|
91
|
+
if (data.language !== "rust" || !rustLspError) {
|
|
92
|
+
editor.debug(
|
|
93
|
+
`rust-lsp: Skipping - language check=${data.language !== "rust"}, error check=${!rustLspError}`
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
editor.debug("rust-lsp: Status clicked, showing help popup");
|
|
99
|
+
|
|
100
|
+
// Show action popup with install options
|
|
101
|
+
const result = editor.showActionPopup({
|
|
102
|
+
id: "rust-lsp-help",
|
|
103
|
+
title: "Rust Language Server Not Found",
|
|
104
|
+
message: `"${rustLspError.serverCommand}" provides code completion, diagnostics, and navigation for Rust files. Copy a command below to install it, or search online for your platform.`,
|
|
105
|
+
actions: [
|
|
106
|
+
{ id: "copy_rustup", label: `Copy: ${INSTALL_COMMANDS.rustup}` },
|
|
107
|
+
{ id: "copy_brew", label: `Copy: ${INSTALL_COMMANDS.brew}` },
|
|
108
|
+
{ id: "disable", label: "Disable Rust LSP" },
|
|
109
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
editor.debug(`rust-lsp: showActionPopup returned ${result}`);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Register hook for status bar clicks
|
|
116
|
+
editor.on("lsp_status_clicked", "on_rust_lsp_status_clicked");
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handle action popup results for Rust LSP help
|
|
120
|
+
*/
|
|
121
|
+
globalThis.on_rust_lsp_action_result = function (
|
|
122
|
+
data: ActionPopupResultData
|
|
123
|
+
): void {
|
|
124
|
+
editor.debug(
|
|
125
|
+
`rust-lsp: action_popup_result received - popup_id=${data.popup_id}, action_id=${data.action_id}`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Only handle our popup
|
|
129
|
+
if (data.popup_id !== "rust-lsp-help") {
|
|
130
|
+
editor.debug("rust-lsp: Not our popup, skipping");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
editor.debug(`rust-lsp: Action selected - ${data.action_id}, rustLspError will remain SET`);
|
|
135
|
+
|
|
136
|
+
switch (data.action_id) {
|
|
137
|
+
case "copy_rustup":
|
|
138
|
+
editor.setClipboard(INSTALL_COMMANDS.rustup);
|
|
139
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.rustup);
|
|
140
|
+
break;
|
|
141
|
+
|
|
142
|
+
case "copy_brew":
|
|
143
|
+
editor.setClipboard(INSTALL_COMMANDS.brew);
|
|
144
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.brew);
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case "disable":
|
|
148
|
+
editor.disableLspForLanguage("rust");
|
|
149
|
+
editor.setStatus("Rust LSP disabled");
|
|
150
|
+
rustLspError = null;
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case "dismiss":
|
|
154
|
+
case "dismissed":
|
|
155
|
+
// Just close the popup without action
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
default:
|
|
159
|
+
editor.debug(`rust-lsp: Unknown action: ${data.action_id}`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Register hook for action popup results
|
|
164
|
+
editor.on("action_popup_result", "on_rust_lsp_action_result");
|
|
165
|
+
|
|
166
|
+
editor.debug("rust-lsp: Plugin loaded");
|