@fresh-editor/fresh-editor 0.2.17 → 0.2.20
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 +144 -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 +275 -29
- package/plugins/dart-lsp.ts +144 -0
- package/plugins/diagnostics_panel.ts +4 -12
- package/plugins/diff_nav.i18n.json +128 -0
- package/plugins/diff_nav.ts +196 -0
- package/plugins/elixir-lsp.ts +120 -0
- package/plugins/erlang-lsp.ts +121 -0
- package/plugins/fsharp-lsp.ts +125 -0
- package/plugins/git_gutter.ts +5 -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/finder.ts +19 -12
- package/plugins/lib/fresh.d.ts +30 -1
- 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 +37 -76
- 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/schemas/package.schema.json +437 -272
- package/plugins/schemas/theme.schema.json +18 -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
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) {
|
|
@@ -3049,34 +3035,9 @@ editor.registerCommand("%cmd.install_url", "%cmd.install_url_desc", "pkg_install
|
|
|
3049
3035
|
// Note: Other commands (install_plugin, install_theme, update, remove, sync, etc.)
|
|
3050
3036
|
// are available via the package manager UI and don't need global command palette entries.
|
|
3051
3037
|
|
|
3052
|
-
//
|
|
3053
|
-
//
|
|
3054
|
-
//
|
|
3055
|
-
|
|
3056
|
-
(async function loadInstalledPackages() {
|
|
3057
|
-
// Load language packs
|
|
3058
|
-
const languages = getInstalledPackages("language");
|
|
3059
|
-
for (const pkg of languages) {
|
|
3060
|
-
if (pkg.manifest) {
|
|
3061
|
-
editor.debug(`[pkg] Loading language pack: ${pkg.name}`);
|
|
3062
|
-
await loadLanguagePack(pkg.path, pkg.manifest);
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
if (languages.length > 0) {
|
|
3066
|
-
editor.debug(`[pkg] Loaded ${languages.length} language pack(s)`);
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
// Load bundles
|
|
3070
|
-
const bundles = getInstalledPackages("bundle");
|
|
3071
|
-
for (const pkg of bundles) {
|
|
3072
|
-
if (pkg.manifest) {
|
|
3073
|
-
editor.debug(`[pkg] Loading bundle: ${pkg.name}`);
|
|
3074
|
-
await loadBundle(pkg.path, pkg.manifest);
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
if (bundles.length > 0) {
|
|
3078
|
-
editor.debug(`[pkg] Loaded ${bundles.length} bundle(s)`);
|
|
3079
|
-
}
|
|
3080
|
-
})();
|
|
3038
|
+
// Note: Startup loading of installed language packs and bundles is now handled
|
|
3039
|
+
// by Rust (services::packages::scan_installed_packages) during editor init.
|
|
3040
|
+
// The loadLanguagePack() and loadBundle() functions above are still used for
|
|
3041
|
+
// runtime installs via the package manager UI.
|
|
3081
3042
|
|
|
3082
3043
|
editor.debug("Package Manager plugin loaded");
|
|
@@ -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");
|
package/plugins/r-lsp.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* R LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Server: languageserver (R package)
|
|
8
|
+
* VS Code: "R" extension by REditorSupport (uses languageserver)
|
|
9
|
+
* Neovim: nvim-lspconfig r_language_server
|
|
10
|
+
* Install via: R's install.packages() - runs as an R script
|
|
11
|
+
* Note: Also install httpgd for plot viewer support
|
|
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
|
+
r: 'R -e \'install.packages("languageserver")\'',
|
|
33
|
+
conda: "conda install -c conda-forge r-languageserver",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let rLspError: { serverCommand: string; message: string } | null = null;
|
|
37
|
+
|
|
38
|
+
function on_r_lsp_server_error(data: LspServerErrorData): void {
|
|
39
|
+
if (data.language !== "r") {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
editor.debug(`r-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
44
|
+
|
|
45
|
+
rLspError = {
|
|
46
|
+
serverCommand: data.server_command,
|
|
47
|
+
message: data.message,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (data.error_type === "not_found") {
|
|
51
|
+
editor.setStatus(
|
|
52
|
+
`R LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
editor.setStatus(`R LSP error: ${data.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
registerHandler("on_r_lsp_server_error", on_r_lsp_server_error);
|
|
59
|
+
editor.on("lsp_server_error", "on_r_lsp_server_error");
|
|
60
|
+
|
|
61
|
+
function on_r_lsp_status_clicked(data: LspStatusClickedData): void {
|
|
62
|
+
if (data.language !== "r" || !rLspError) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
editor.debug("r-lsp: Status clicked, showing help popup");
|
|
67
|
+
|
|
68
|
+
editor.showActionPopup({
|
|
69
|
+
id: "r-lsp-help",
|
|
70
|
+
title: "R Language Server Not Found",
|
|
71
|
+
message: `The R language server provides completion, diagnostics, hover, formatting, and go-to-definition for R files. It runs as an R script, so R must be installed.\n\nInstall the languageserver R package, then the server runs via: R --vanilla -e 'languageserver::run()'\nVS Code users: Install the "R" extension by REditorSupport.\nSee: https://github.com/REditorSupport/languageserver`,
|
|
72
|
+
actions: [
|
|
73
|
+
{ id: "copy_r", label: `Copy: ${INSTALL_COMMANDS.r}` },
|
|
74
|
+
{ id: "copy_conda", label: `Copy: ${INSTALL_COMMANDS.conda}` },
|
|
75
|
+
{ id: "disable", label: "Disable R LSP" },
|
|
76
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
registerHandler("on_r_lsp_status_clicked", on_r_lsp_status_clicked);
|
|
81
|
+
editor.on("lsp_status_clicked", "on_r_lsp_status_clicked");
|
|
82
|
+
|
|
83
|
+
function on_r_lsp_action_result(data: ActionPopupResultData): void {
|
|
84
|
+
if (data.popup_id !== "r-lsp-help") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
editor.debug(`r-lsp: Action selected - ${data.action_id}`);
|
|
89
|
+
|
|
90
|
+
switch (data.action_id) {
|
|
91
|
+
case "copy_r":
|
|
92
|
+
editor.setClipboard(INSTALL_COMMANDS.r);
|
|
93
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.r);
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case "copy_conda":
|
|
97
|
+
editor.setClipboard(INSTALL_COMMANDS.conda);
|
|
98
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.conda);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case "disable":
|
|
102
|
+
editor.disableLspForLanguage("r");
|
|
103
|
+
editor.setStatus("R LSP disabled");
|
|
104
|
+
rLspError = null;
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case "dismiss":
|
|
108
|
+
case "dismissed":
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
default:
|
|
112
|
+
editor.debug(`r-lsp: Unknown action: ${data.action_id}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
registerHandler("on_r_lsp_action_result", on_r_lsp_action_result);
|
|
116
|
+
editor.on("action_popup_result", "on_r_lsp_action_result");
|
|
117
|
+
|
|
118
|
+
editor.debug("r-lsp: Plugin loaded");
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ruby LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Provides user-friendly error handling for Ruby LSP server issues.
|
|
8
|
+
* When solargraph fails to start, this plugin shows an actionable
|
|
9
|
+
* popup with installation instructions.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Detects Ruby LSP server errors (solargraph)
|
|
13
|
+
* - Shows popup with install commands (gem)
|
|
14
|
+
* - Allows copying install commands to clipboard
|
|
15
|
+
* - Provides option to disable Ruby LSP
|
|
16
|
+
*
|
|
17
|
+
* Alternatives:
|
|
18
|
+
* - ruby-lsp (Shopify): Modern Ruby LSP - gem install ruby-lsp (https://github.com/Shopify/ruby-lsp)
|
|
19
|
+
* - Steep: Ruby type checker with LSP support (https://github.com/soutaro/steep)
|
|
20
|
+
* - Sorbet: Gradual type checker for Ruby (https://sorbet.org/)
|
|
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 Ruby LSP server (solargraph)
|
|
41
|
+
// See: https://solargraph.org/guides/getting-started
|
|
42
|
+
const INSTALL_COMMANDS = {
|
|
43
|
+
gem: "gem install solargraph",
|
|
44
|
+
bundler: "bundle add solargraph --group development",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Alternative LSP server
|
|
48
|
+
const ALT_INSTALL = "gem install ruby-lsp";
|
|
49
|
+
|
|
50
|
+
// Track error state for Ruby LSP
|
|
51
|
+
let rubyLspError: { serverCommand: string; message: string } | null = null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle LSP server errors for Ruby
|
|
55
|
+
*/
|
|
56
|
+
function on_ruby_lsp_server_error(data: LspServerErrorData): void {
|
|
57
|
+
// Only handle Ruby language errors
|
|
58
|
+
if (data.language !== "ruby") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
editor.debug(`ruby-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
63
|
+
|
|
64
|
+
// Store error state for later reference
|
|
65
|
+
rubyLspError = {
|
|
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
|
+
`Ruby LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
editor.setStatus(`Ruby LSP error: ${data.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
registerHandler("on_ruby_lsp_server_error", on_ruby_lsp_server_error);
|
|
80
|
+
|
|
81
|
+
// Register hook for LSP server errors
|
|
82
|
+
editor.on("lsp_server_error", "on_ruby_lsp_server_error");
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handle status bar click when there's a Ruby LSP error
|
|
86
|
+
*/
|
|
87
|
+
function on_ruby_lsp_status_clicked(
|
|
88
|
+
data: LspStatusClickedData
|
|
89
|
+
): void {
|
|
90
|
+
// Only handle Ruby language clicks when there's an error
|
|
91
|
+
if (data.language !== "ruby" || !rubyLspError) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
editor.debug("ruby-lsp: Status clicked, showing help popup");
|
|
96
|
+
|
|
97
|
+
// Show action popup with install options
|
|
98
|
+
editor.showActionPopup({
|
|
99
|
+
id: "ruby-lsp-help",
|
|
100
|
+
title: "Ruby Language Server Not Found",
|
|
101
|
+
message: `"${rubyLspError.serverCommand}" provides code completion, diagnostics, and navigation for Ruby files. Requires Ruby/RubyGems. Copy a command below to install it, or visit https://solargraph.org/guides/getting-started for details. Alternative: Shopify's ruby-lsp (https://github.com/Shopify/ruby-lsp).`,
|
|
102
|
+
actions: [
|
|
103
|
+
{ id: "copy_gem", label: `Copy: ${INSTALL_COMMANDS.gem}` },
|
|
104
|
+
{ id: "copy_bundler", label: `Copy: ${INSTALL_COMMANDS.bundler}` },
|
|
105
|
+
{ id: "copy_alt", label: `Alternative: ${ALT_INSTALL}` },
|
|
106
|
+
{ id: "disable", label: "Disable Ruby LSP" },
|
|
107
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
registerHandler("on_ruby_lsp_status_clicked", on_ruby_lsp_status_clicked);
|
|
112
|
+
|
|
113
|
+
// Register hook for status bar clicks
|
|
114
|
+
editor.on("lsp_status_clicked", "on_ruby_lsp_status_clicked");
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle action popup results for Ruby LSP help
|
|
118
|
+
*/
|
|
119
|
+
function on_ruby_lsp_action_result(
|
|
120
|
+
data: ActionPopupResultData
|
|
121
|
+
): void {
|
|
122
|
+
// Only handle our popup
|
|
123
|
+
if (data.popup_id !== "ruby-lsp-help") {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
editor.debug(`ruby-lsp: Action selected - ${data.action_id}`);
|
|
128
|
+
|
|
129
|
+
switch (data.action_id) {
|
|
130
|
+
case "copy_gem":
|
|
131
|
+
editor.setClipboard(INSTALL_COMMANDS.gem);
|
|
132
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.gem);
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case "copy_bundler":
|
|
136
|
+
editor.setClipboard(INSTALL_COMMANDS.bundler);
|
|
137
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.bundler);
|
|
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("ruby");
|
|
147
|
+
editor.setStatus("Ruby LSP disabled");
|
|
148
|
+
rubyLspError = 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(`ruby-lsp: Unknown action: ${data.action_id}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
registerHandler("on_ruby_lsp_action_result", on_ruby_lsp_action_result);
|
|
161
|
+
|
|
162
|
+
// Register hook for action popup results
|
|
163
|
+
editor.on("action_popup_result", "on_ruby_lsp_action_result");
|
|
164
|
+
|
|
165
|
+
editor.debug("ruby-lsp: Plugin loaded");
|