@fresh-editor/fresh-editor 0.2.17 → 0.2.18
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 +84 -0
- package/package.json +1 -1
- package/plugins/astro-lsp.ts +118 -0
- package/plugins/bash-lsp.ts +161 -0
- package/plugins/clojure-lsp.ts +125 -0
- package/plugins/cmake-lsp.ts +138 -0
- package/plugins/config-schema.json +33 -4
- package/plugins/dart-lsp.ts +144 -0
- package/plugins/elixir-lsp.ts +120 -0
- package/plugins/erlang-lsp.ts +121 -0
- package/plugins/fsharp-lsp.ts +125 -0
- package/plugins/gleam-lsp.ts +124 -0
- package/plugins/graphql-lsp.ts +139 -0
- package/plugins/haskell-lsp.ts +125 -0
- package/plugins/julia-lsp.ts +111 -0
- package/plugins/kotlin-lsp.ts +162 -0
- package/plugins/lib/fresh.d.ts +25 -0
- package/plugins/lua-lsp.ts +161 -0
- package/plugins/nim-lsp.ts +118 -0
- package/plugins/nix-lsp.ts +125 -0
- package/plugins/nushell-lsp.ts +144 -0
- package/plugins/ocaml-lsp.ts +119 -0
- package/plugins/perl-lsp.ts +118 -0
- package/plugins/php-lsp.ts +165 -0
- package/plugins/pkg.ts +33 -47
- package/plugins/protobuf-lsp.ts +144 -0
- package/plugins/r-lsp.ts +118 -0
- package/plugins/ruby-lsp.ts +165 -0
- package/plugins/scala-lsp.ts +119 -0
- package/plugins/solidity-lsp.ts +130 -0
- package/plugins/sql-lsp.ts +129 -0
- package/plugins/svelte-lsp.ts +119 -0
- package/plugins/swift-lsp.ts +120 -0
- package/plugins/tailwindcss-lsp.ts +119 -0
- package/plugins/terraform-lsp.ts +144 -0
- package/plugins/theme_editor.i18n.json +70 -14
- package/plugins/theme_editor.ts +71 -39
- package/plugins/toml-lsp.ts +162 -0
- package/plugins/typst-lsp.ts +165 -0
- package/plugins/vue-lsp.ts +118 -0
- package/plugins/yaml-lsp.ts +163 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Perl LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Server: PerlNavigator (github.com/bscan/PerlNavigator)
|
|
8
|
+
* VS Code: "Perl Navigator" extension
|
|
9
|
+
* Neovim: nvim-lspconfig perlnavigator
|
|
10
|
+
* Alternative: Perl::LanguageServer (cpan, older)
|
|
11
|
+
* Note: PerlNavigator includes perlcritic and perltidy integration
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface LspServerErrorData {
|
|
15
|
+
language: string;
|
|
16
|
+
server_command: string;
|
|
17
|
+
error_type: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface LspStatusClickedData {
|
|
22
|
+
language: string;
|
|
23
|
+
has_error: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ActionPopupResultData {
|
|
27
|
+
popup_id: string;
|
|
28
|
+
action_id: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const INSTALL_COMMANDS = {
|
|
32
|
+
npm: "npm install -g perlnavigator-server",
|
|
33
|
+
cpan_alt: "cpanm Perl::LanguageServer",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let perlLspError: { serverCommand: string; message: string } | null = null;
|
|
37
|
+
|
|
38
|
+
function on_perl_lsp_server_error(data: LspServerErrorData): void {
|
|
39
|
+
if (data.language !== "perl") {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
editor.debug(`perl-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
44
|
+
|
|
45
|
+
perlLspError = {
|
|
46
|
+
serverCommand: data.server_command,
|
|
47
|
+
message: data.message,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (data.error_type === "not_found") {
|
|
51
|
+
editor.setStatus(
|
|
52
|
+
`Perl LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
editor.setStatus(`Perl LSP error: ${data.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
registerHandler("on_perl_lsp_server_error", on_perl_lsp_server_error);
|
|
59
|
+
editor.on("lsp_server_error", "on_perl_lsp_server_error");
|
|
60
|
+
|
|
61
|
+
function on_perl_lsp_status_clicked(data: LspStatusClickedData): void {
|
|
62
|
+
if (data.language !== "perl" || !perlLspError) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
editor.debug("perl-lsp: Status clicked, showing help popup");
|
|
67
|
+
|
|
68
|
+
editor.showActionPopup({
|
|
69
|
+
id: "perl-lsp-help",
|
|
70
|
+
title: "Perl Language Server Not Found",
|
|
71
|
+
message: `"${perlLspError.serverCommand}" (PerlNavigator) provides completion, diagnostics, navigation, and perlcritic/perltidy integration for Perl.\n\nAlternative: Perl::LanguageServer (older, via CPAN).\nVS Code users: Install the "Perl Navigator" extension.\nSee: https://github.com/bscan/PerlNavigator`,
|
|
72
|
+
actions: [
|
|
73
|
+
{ id: "copy_npm", label: `Copy: ${INSTALL_COMMANDS.npm}` },
|
|
74
|
+
{ id: "copy_cpan", label: `Copy: ${INSTALL_COMMANDS.cpan_alt}` },
|
|
75
|
+
{ id: "disable", label: "Disable Perl LSP" },
|
|
76
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
registerHandler("on_perl_lsp_status_clicked", on_perl_lsp_status_clicked);
|
|
81
|
+
editor.on("lsp_status_clicked", "on_perl_lsp_status_clicked");
|
|
82
|
+
|
|
83
|
+
function on_perl_lsp_action_result(data: ActionPopupResultData): void {
|
|
84
|
+
if (data.popup_id !== "perl-lsp-help") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
editor.debug(`perl-lsp: Action selected - ${data.action_id}`);
|
|
89
|
+
|
|
90
|
+
switch (data.action_id) {
|
|
91
|
+
case "copy_npm":
|
|
92
|
+
editor.setClipboard(INSTALL_COMMANDS.npm);
|
|
93
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.npm);
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case "copy_cpan":
|
|
97
|
+
editor.setClipboard(INSTALL_COMMANDS.cpan_alt);
|
|
98
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.cpan_alt);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case "disable":
|
|
102
|
+
editor.disableLspForLanguage("perl");
|
|
103
|
+
editor.setStatus("Perl LSP disabled");
|
|
104
|
+
perlLspError = null;
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case "dismiss":
|
|
108
|
+
case "dismissed":
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
default:
|
|
112
|
+
editor.debug(`perl-lsp: Unknown action: ${data.action_id}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
registerHandler("on_perl_lsp_action_result", on_perl_lsp_action_result);
|
|
116
|
+
editor.on("action_popup_result", "on_perl_lsp_action_result");
|
|
117
|
+
|
|
118
|
+
editor.debug("perl-lsp: Plugin loaded");
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PHP LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Provides user-friendly error handling for PHP LSP server issues.
|
|
8
|
+
* When phpactor fails to start, this plugin shows an actionable
|
|
9
|
+
* popup with installation instructions.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Detects PHP LSP server errors (phpactor)
|
|
13
|
+
* - Shows popup with install commands (composer, brew)
|
|
14
|
+
* - Allows copying install commands to clipboard
|
|
15
|
+
* - Provides option to disable PHP LSP
|
|
16
|
+
*
|
|
17
|
+
* Alternatives:
|
|
18
|
+
* - Intelephense: Feature-rich PHP LSP (https://intelephense.com/) - npm i -g intelephense
|
|
19
|
+
* - PHPStan: Static analysis tool (https://phpstan.org/)
|
|
20
|
+
* - Psalm: PHP static analysis (https://psalm.dev/)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
interface LspServerErrorData {
|
|
24
|
+
language: string;
|
|
25
|
+
server_command: string;
|
|
26
|
+
error_type: string;
|
|
27
|
+
message: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface LspStatusClickedData {
|
|
31
|
+
language: string;
|
|
32
|
+
has_error: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ActionPopupResultData {
|
|
36
|
+
popup_id: string;
|
|
37
|
+
action_id: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Install commands for PHP LSP server (phpactor)
|
|
41
|
+
// See: https://phpactor.readthedocs.io/en/master/usage/getting-started.html
|
|
42
|
+
const INSTALL_COMMANDS = {
|
|
43
|
+
composer: "composer global require phpactor/phpactor",
|
|
44
|
+
brew: "brew install phpactor",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Alternative LSP server
|
|
48
|
+
const ALT_INSTALL = "npm i -g intelephense";
|
|
49
|
+
|
|
50
|
+
// Track error state for PHP LSP
|
|
51
|
+
let phpLspError: { serverCommand: string; message: string } | null = null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle LSP server errors for PHP
|
|
55
|
+
*/
|
|
56
|
+
function on_php_lsp_server_error(data: LspServerErrorData): void {
|
|
57
|
+
// Only handle PHP language errors
|
|
58
|
+
if (data.language !== "php") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
editor.debug(`php-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
63
|
+
|
|
64
|
+
// Store error state for later reference
|
|
65
|
+
phpLspError = {
|
|
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
|
+
`PHP LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
editor.setStatus(`PHP LSP error: ${data.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
registerHandler("on_php_lsp_server_error", on_php_lsp_server_error);
|
|
80
|
+
|
|
81
|
+
// Register hook for LSP server errors
|
|
82
|
+
editor.on("lsp_server_error", "on_php_lsp_server_error");
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handle status bar click when there's a PHP LSP error
|
|
86
|
+
*/
|
|
87
|
+
function on_php_lsp_status_clicked(
|
|
88
|
+
data: LspStatusClickedData
|
|
89
|
+
): void {
|
|
90
|
+
// Only handle PHP language clicks when there's an error
|
|
91
|
+
if (data.language !== "php" || !phpLspError) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
editor.debug("php-lsp: Status clicked, showing help popup");
|
|
96
|
+
|
|
97
|
+
// Show action popup with install options
|
|
98
|
+
editor.showActionPopup({
|
|
99
|
+
id: "php-lsp-help",
|
|
100
|
+
title: "PHP Language Server Not Found",
|
|
101
|
+
message: `"${phpLspError.serverCommand}" provides code completion, diagnostics, and navigation for PHP files. Requires PHP and Composer. Copy a command below to install it, or visit https://phpactor.readthedocs.io for details. Alternative: Intelephense (https://intelephense.com/) is a popular Node.js-based PHP LSP.`,
|
|
102
|
+
actions: [
|
|
103
|
+
{ id: "copy_composer", label: `Copy: ${INSTALL_COMMANDS.composer}` },
|
|
104
|
+
{ id: "copy_brew", label: `Copy: ${INSTALL_COMMANDS.brew}` },
|
|
105
|
+
{ id: "copy_alt", label: `Alternative: ${ALT_INSTALL}` },
|
|
106
|
+
{ id: "disable", label: "Disable PHP LSP" },
|
|
107
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
registerHandler("on_php_lsp_status_clicked", on_php_lsp_status_clicked);
|
|
112
|
+
|
|
113
|
+
// Register hook for status bar clicks
|
|
114
|
+
editor.on("lsp_status_clicked", "on_php_lsp_status_clicked");
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle action popup results for PHP LSP help
|
|
118
|
+
*/
|
|
119
|
+
function on_php_lsp_action_result(
|
|
120
|
+
data: ActionPopupResultData
|
|
121
|
+
): void {
|
|
122
|
+
// Only handle our popup
|
|
123
|
+
if (data.popup_id !== "php-lsp-help") {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
editor.debug(`php-lsp: Action selected - ${data.action_id}`);
|
|
128
|
+
|
|
129
|
+
switch (data.action_id) {
|
|
130
|
+
case "copy_composer":
|
|
131
|
+
editor.setClipboard(INSTALL_COMMANDS.composer);
|
|
132
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.composer);
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case "copy_brew":
|
|
136
|
+
editor.setClipboard(INSTALL_COMMANDS.brew);
|
|
137
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.brew);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case "copy_alt":
|
|
141
|
+
editor.setClipboard(ALT_INSTALL);
|
|
142
|
+
editor.setStatus("Copied: " + ALT_INSTALL);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case "disable":
|
|
146
|
+
editor.disableLspForLanguage("php");
|
|
147
|
+
editor.setStatus("PHP LSP disabled");
|
|
148
|
+
phpLspError = null;
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case "dismiss":
|
|
152
|
+
case "dismissed":
|
|
153
|
+
// Just close the popup without action
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
default:
|
|
157
|
+
editor.debug(`php-lsp: Unknown action: ${data.action_id}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
registerHandler("on_php_lsp_action_result", on_php_lsp_action_result);
|
|
161
|
+
|
|
162
|
+
// Register hook for action popup results
|
|
163
|
+
editor.on("action_popup_result", "on_php_lsp_action_result");
|
|
164
|
+
|
|
165
|
+
editor.debug("php-lsp: Plugin loaded");
|
package/plugins/pkg.ts
CHANGED
|
@@ -231,14 +231,10 @@ interface ParsedPackageUrl {
|
|
|
231
231
|
// =============================================================================
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
* Ensure a directory exists
|
|
234
|
+
* Ensure a directory exists (cross-platform)
|
|
235
235
|
*/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
const result = await editor.spawnProcess("mkdir", ["-p", path]);
|
|
241
|
-
return result.exit_code === 0;
|
|
236
|
+
function ensureDir(path: string): boolean {
|
|
237
|
+
return editor.createDir(path);
|
|
242
238
|
}
|
|
243
239
|
|
|
244
240
|
/**
|
|
@@ -395,7 +391,7 @@ async function writeJsonFile(path: string, data: unknown): Promise<boolean> {
|
|
|
395
391
|
async function syncRegistry(): Promise<void> {
|
|
396
392
|
editor.setStatus("Syncing package registry...");
|
|
397
393
|
|
|
398
|
-
|
|
394
|
+
ensureDir(INDEX_DIR);
|
|
399
395
|
|
|
400
396
|
const sources = getRegistrySources();
|
|
401
397
|
let synced = 0;
|
|
@@ -493,7 +489,7 @@ function loadRegistry(type: "plugins" | "themes" | "languages"): RegistryData {
|
|
|
493
489
|
* Cache registry data locally for offline/fast access
|
|
494
490
|
*/
|
|
495
491
|
async function cacheRegistry(): Promise<void> {
|
|
496
|
-
|
|
492
|
+
ensureDir(CACHE_DIR);
|
|
497
493
|
const sources = getRegistrySources();
|
|
498
494
|
|
|
499
495
|
for (const source of sources) {
|
|
@@ -823,7 +819,7 @@ async function installFromRepo(
|
|
|
823
819
|
version?: string
|
|
824
820
|
): Promise<boolean> {
|
|
825
821
|
// Clone to temp directory first to detect package type
|
|
826
|
-
const tempDir =
|
|
822
|
+
const tempDir = editor.pathJoin(editor.getTempDir(), `fresh-pkg-clone-${hashString(repoUrl)}-${Date.now()}`);
|
|
827
823
|
|
|
828
824
|
const cloneArgs = ["clone"];
|
|
829
825
|
if (!version || version === "latest") {
|
|
@@ -857,7 +853,7 @@ async function installFromRepo(
|
|
|
857
853
|
editor.warn(`[pkg] Invalid package '${packageName}': ${validation.error}`);
|
|
858
854
|
editor.setStatus(`Failed to install ${packageName}: ${validation.error}`);
|
|
859
855
|
// Clean up
|
|
860
|
-
|
|
856
|
+
editor.removePath(tempDir);
|
|
861
857
|
return false;
|
|
862
858
|
}
|
|
863
859
|
|
|
@@ -877,16 +873,15 @@ async function installFromRepo(
|
|
|
877
873
|
// Check if already installed in correct location
|
|
878
874
|
if (editor.fileExists(correctTargetDir)) {
|
|
879
875
|
editor.setStatus(`Package '${packageName}' is already installed`);
|
|
880
|
-
|
|
876
|
+
editor.removePath(tempDir);
|
|
881
877
|
return false;
|
|
882
878
|
}
|
|
883
879
|
|
|
884
880
|
// Ensure correct directory exists and move from temp
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
editor.
|
|
889
|
-
await editor.spawnProcess("rm", ["-rf", tempDir]);
|
|
881
|
+
ensureDir(correctPackagesDir);
|
|
882
|
+
if (!editor.renamePath(tempDir, correctTargetDir)) {
|
|
883
|
+
editor.setStatus(`Failed to install ${packageName}: could not move package to target directory`);
|
|
884
|
+
editor.removePath(tempDir);
|
|
890
885
|
return false;
|
|
891
886
|
}
|
|
892
887
|
|
|
@@ -976,13 +971,12 @@ async function installFromLocalPath(
|
|
|
976
971
|
}
|
|
977
972
|
|
|
978
973
|
// Ensure correct directory exists
|
|
979
|
-
|
|
974
|
+
ensureDir(correctPackagesDir);
|
|
980
975
|
|
|
981
976
|
// Copy the directory to correct target
|
|
982
977
|
editor.setStatus(`Copying from ${sourcePath}...`);
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
editor.setStatus(`Failed to copy package: ${copyResult.stderr}`);
|
|
978
|
+
if (!editor.copyPath(sourcePath, correctTargetDir)) {
|
|
979
|
+
editor.setStatus(`Failed to copy package from ${sourcePath}`);
|
|
986
980
|
return false;
|
|
987
981
|
}
|
|
988
982
|
|
|
@@ -992,7 +986,7 @@ async function installFromLocalPath(
|
|
|
992
986
|
editor.warn(`[pkg] Invalid package '${packageName}': ${validation.error}`);
|
|
993
987
|
editor.setStatus(`Failed to install ${packageName}: ${validation.error}`);
|
|
994
988
|
// Clean up the invalid package
|
|
995
|
-
|
|
989
|
+
editor.removePath(correctTargetDir);
|
|
996
990
|
return false;
|
|
997
991
|
}
|
|
998
992
|
|
|
@@ -1037,7 +1031,7 @@ async function installFromMonorepo(
|
|
|
1037
1031
|
packageName: string,
|
|
1038
1032
|
version?: string
|
|
1039
1033
|
): Promise<boolean> {
|
|
1040
|
-
const tempDir =
|
|
1034
|
+
const tempDir = editor.pathJoin(editor.getTempDir(), `fresh-pkg-${hashString(parsed.repoUrl)}-${Date.now()}`);
|
|
1041
1035
|
|
|
1042
1036
|
try {
|
|
1043
1037
|
// Clone the full repo to temp
|
|
@@ -1068,7 +1062,7 @@ async function installFromMonorepo(
|
|
|
1068
1062
|
const subpathDir = editor.pathJoin(tempDir, parsed.subpath!);
|
|
1069
1063
|
if (!editor.fileExists(subpathDir)) {
|
|
1070
1064
|
editor.setStatus(`Subpath '${parsed.subpath}' not found in repository`);
|
|
1071
|
-
|
|
1065
|
+
editor.removePath(tempDir);
|
|
1072
1066
|
return false;
|
|
1073
1067
|
}
|
|
1074
1068
|
|
|
@@ -1077,7 +1071,7 @@ async function installFromMonorepo(
|
|
|
1077
1071
|
if (!validation.valid) {
|
|
1078
1072
|
editor.warn(`[pkg] Invalid package '${packageName}': ${validation.error}`);
|
|
1079
1073
|
editor.setStatus(`Failed to install ${packageName}: ${validation.error}`);
|
|
1080
|
-
|
|
1074
|
+
editor.removePath(tempDir);
|
|
1081
1075
|
return false;
|
|
1082
1076
|
}
|
|
1083
1077
|
|
|
@@ -1097,19 +1091,18 @@ async function installFromMonorepo(
|
|
|
1097
1091
|
// Check if already installed
|
|
1098
1092
|
if (editor.fileExists(correctTargetDir)) {
|
|
1099
1093
|
editor.setStatus(`Package '${packageName}' is already installed`);
|
|
1100
|
-
|
|
1094
|
+
editor.removePath(tempDir);
|
|
1101
1095
|
return false;
|
|
1102
1096
|
}
|
|
1103
1097
|
|
|
1104
1098
|
// Ensure correct directory exists
|
|
1105
|
-
|
|
1099
|
+
ensureDir(correctPackagesDir);
|
|
1106
1100
|
|
|
1107
1101
|
// Copy subdirectory to correct target
|
|
1108
1102
|
editor.setStatus(`Installing ${packageName} from ${parsed.subpath}...`);
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
editor.
|
|
1112
|
-
await editor.spawnProcess("rm", ["-rf", tempDir]);
|
|
1103
|
+
if (!editor.copyPath(subpathDir, correctTargetDir)) {
|
|
1104
|
+
editor.setStatus(`Failed to copy package from ${parsed.subpath}`);
|
|
1105
|
+
editor.removePath(tempDir);
|
|
1113
1106
|
return false;
|
|
1114
1107
|
}
|
|
1115
1108
|
|
|
@@ -1143,7 +1136,7 @@ async function installFromMonorepo(
|
|
|
1143
1136
|
return true;
|
|
1144
1137
|
} finally {
|
|
1145
1138
|
// Cleanup temp directory
|
|
1146
|
-
|
|
1139
|
+
editor.removePath(tempDir);
|
|
1147
1140
|
}
|
|
1148
1141
|
}
|
|
1149
1142
|
|
|
@@ -1408,16 +1401,14 @@ async function reinstallPackage(pkg: InstalledPackage): Promise<boolean> {
|
|
|
1408
1401
|
}
|
|
1409
1402
|
|
|
1410
1403
|
// Remove old copy
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
editor.setStatus(`Failed to remove old copy: ${rmResult.stderr}`);
|
|
1404
|
+
if (!editor.removePath(pkg.path)) {
|
|
1405
|
+
editor.setStatus(`Failed to remove old copy of ${pkg.name}`);
|
|
1414
1406
|
return false;
|
|
1415
1407
|
}
|
|
1416
1408
|
|
|
1417
1409
|
// Re-copy from source
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
editor.setStatus(`Failed to copy from source: ${copyResult.stderr}`);
|
|
1410
|
+
if (!editor.copyPath(sourcePath, pkg.path)) {
|
|
1411
|
+
editor.setStatus(`Failed to copy from source: ${sourcePath}`);
|
|
1421
1412
|
return false;
|
|
1422
1413
|
}
|
|
1423
1414
|
|
|
@@ -1474,13 +1465,8 @@ async function removePackage(pkg: InstalledPackage): Promise<boolean> {
|
|
|
1474
1465
|
}
|
|
1475
1466
|
}
|
|
1476
1467
|
|
|
1477
|
-
//
|
|
1478
|
-
|
|
1479
|
-
if (result.exit_code !== 0) {
|
|
1480
|
-
result = await editor.spawnProcess("rm", ["-rf", pkg.path]);
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
if (result.exit_code === 0) {
|
|
1468
|
+
// Remove package directory
|
|
1469
|
+
if (editor.removePath(pkg.path)) {
|
|
1484
1470
|
// Reload themes if we removed a theme so Select Theme list is updated
|
|
1485
1471
|
if (pkg.type === "theme") {
|
|
1486
1472
|
editor.reloadThemes();
|
|
@@ -1488,7 +1474,7 @@ async function removePackage(pkg: InstalledPackage): Promise<boolean> {
|
|
|
1488
1474
|
editor.setStatus(`Removed ${pkg.name}`);
|
|
1489
1475
|
return true;
|
|
1490
1476
|
} else {
|
|
1491
|
-
editor.setStatus(`Failed to remove ${pkg.name}
|
|
1477
|
+
editor.setStatus(`Failed to remove ${pkg.name}`);
|
|
1492
1478
|
return false;
|
|
1493
1479
|
}
|
|
1494
1480
|
}
|
|
@@ -1608,7 +1594,7 @@ async function installFromLockfile(): Promise<void> {
|
|
|
1608
1594
|
}
|
|
1609
1595
|
} else {
|
|
1610
1596
|
// Need to clone
|
|
1611
|
-
|
|
1597
|
+
ensureDir(PACKAGES_DIR);
|
|
1612
1598
|
const result = await gitCommand(["clone", `${entry.source}`, `${pluginPath}`]);
|
|
1613
1599
|
|
|
1614
1600
|
if (result.exit_code === 0) {
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Protobuf LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Provides user-friendly error handling for Protobuf LSP server issues.
|
|
8
|
+
* When buf fails to start, this plugin shows an actionable
|
|
9
|
+
* popup with installation instructions.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Detects Protobuf LSP server errors (buf)
|
|
13
|
+
* - Shows popup with install commands (brew, npm, etc.)
|
|
14
|
+
* - Provides option to disable Protobuf LSP
|
|
15
|
+
*
|
|
16
|
+
* VS Code: "Buf" extension (auto-installs buf CLI), "Protobuf support" by peterj
|
|
17
|
+
* Neovim: nvim-lspconfig bufls
|
|
18
|
+
* Alternative: protols (open-source, tree-sitter based, standalone)
|
|
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
|
+
// Install commands for Protobuf LSP server (buf)
|
|
39
|
+
// See: https://buf.build/docs/installation
|
|
40
|
+
const INSTALL_COMMANDS = {
|
|
41
|
+
brew: "brew install bufbuild/buf/buf",
|
|
42
|
+
npm: "npm i -g @bufbuild/buf",
|
|
43
|
+
go: "go install github.com/bufbuild/buf/cmd/buf@latest",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Track error state for Protobuf LSP
|
|
47
|
+
let protobufLspError: { serverCommand: string; message: string } | null = null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handle LSP server errors for Protobuf
|
|
51
|
+
*/
|
|
52
|
+
function on_protobuf_lsp_server_error(data: LspServerErrorData): void {
|
|
53
|
+
if (data.language !== "protobuf") {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
editor.debug(`protobuf-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
58
|
+
|
|
59
|
+
protobufLspError = {
|
|
60
|
+
serverCommand: data.server_command,
|
|
61
|
+
message: data.message,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (data.error_type === "not_found") {
|
|
65
|
+
editor.setStatus(
|
|
66
|
+
`Protobuf LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
editor.setStatus(`Protobuf LSP error: ${data.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
registerHandler("on_protobuf_lsp_server_error", on_protobuf_lsp_server_error);
|
|
73
|
+
editor.on("lsp_server_error", "on_protobuf_lsp_server_error");
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Handle status bar click when there's a Protobuf LSP error
|
|
77
|
+
*/
|
|
78
|
+
function on_protobuf_lsp_status_clicked(data: LspStatusClickedData): void {
|
|
79
|
+
if (data.language !== "protobuf" || !protobufLspError) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
editor.debug("protobuf-lsp: Status clicked, showing help popup");
|
|
84
|
+
|
|
85
|
+
editor.showActionPopup({
|
|
86
|
+
id: "protobuf-lsp-help",
|
|
87
|
+
title: "Protobuf Language Server Not Found",
|
|
88
|
+
message: `"${protobufLspError.serverCommand}" (Buf CLI) provides code completion, diagnostics, linting, and formatting for Protocol Buffer files. Copy a command below to install it, or visit https://buf.build/docs/installation for details.`,
|
|
89
|
+
actions: [
|
|
90
|
+
{ id: "copy_brew", label: `Copy: ${INSTALL_COMMANDS.brew}` },
|
|
91
|
+
{ id: "copy_npm", label: `Copy: ${INSTALL_COMMANDS.npm}` },
|
|
92
|
+
{ id: "copy_go", label: `Copy: ${INSTALL_COMMANDS.go}` },
|
|
93
|
+
{ id: "disable", label: "Disable Protobuf LSP" },
|
|
94
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
registerHandler("on_protobuf_lsp_status_clicked", on_protobuf_lsp_status_clicked);
|
|
99
|
+
editor.on("lsp_status_clicked", "on_protobuf_lsp_status_clicked");
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handle action popup results for Protobuf LSP help
|
|
103
|
+
*/
|
|
104
|
+
function on_protobuf_lsp_action_result(data: ActionPopupResultData): void {
|
|
105
|
+
if (data.popup_id !== "protobuf-lsp-help") {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
editor.debug(`protobuf-lsp: Action selected - ${data.action_id}`);
|
|
110
|
+
|
|
111
|
+
switch (data.action_id) {
|
|
112
|
+
case "copy_brew":
|
|
113
|
+
editor.setClipboard(INSTALL_COMMANDS.brew);
|
|
114
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.brew);
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case "copy_npm":
|
|
118
|
+
editor.setClipboard(INSTALL_COMMANDS.npm);
|
|
119
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.npm);
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case "copy_go":
|
|
123
|
+
editor.setClipboard(INSTALL_COMMANDS.go);
|
|
124
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.go);
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case "disable":
|
|
128
|
+
editor.disableLspForLanguage("protobuf");
|
|
129
|
+
editor.setStatus("Protobuf LSP disabled");
|
|
130
|
+
protobufLspError = null;
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case "dismiss":
|
|
134
|
+
case "dismissed":
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
default:
|
|
138
|
+
editor.debug(`protobuf-lsp: Unknown action: ${data.action_id}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
registerHandler("on_protobuf_lsp_action_result", on_protobuf_lsp_action_result);
|
|
142
|
+
editor.on("action_popup_result", "on_protobuf_lsp_action_result");
|
|
143
|
+
|
|
144
|
+
editor.debug("protobuf-lsp: Plugin loaded");
|