@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
package/plugins/theme_editor.ts
CHANGED
|
@@ -74,28 +74,28 @@ type PickerFocusTarget =
|
|
|
74
74
|
// =============================================================================
|
|
75
75
|
|
|
76
76
|
const NAMED_COLORS_PER_ROW = 6;
|
|
77
|
-
const NAMED_COLOR_GRID: Array<Array<{ display: string; value: string; rgb:
|
|
77
|
+
const NAMED_COLOR_GRID: Array<Array<{ display: string; value: string; rgb: OverlayColorSpec | null }>> = [
|
|
78
78
|
[
|
|
79
|
-
{ display: "Black", value: "Black", rgb:
|
|
80
|
-
{ display: "Red", value: "Red", rgb:
|
|
81
|
-
{ display: "Green", value: "Green", rgb:
|
|
82
|
-
{ display: "Yellow", value: "Yellow", rgb:
|
|
83
|
-
{ display: "Blue", value: "Blue", rgb:
|
|
84
|
-
{ display: "Magenta", value: "Magenta", rgb:
|
|
79
|
+
{ display: "Black", value: "Black", rgb: "Black" },
|
|
80
|
+
{ display: "Red", value: "Red", rgb: "Red" },
|
|
81
|
+
{ display: "Green", value: "Green", rgb: "Green" },
|
|
82
|
+
{ display: "Yellow", value: "Yellow", rgb: "Yellow" },
|
|
83
|
+
{ display: "Blue", value: "Blue", rgb: "Blue" },
|
|
84
|
+
{ display: "Magenta", value: "Magenta", rgb: "Magenta" },
|
|
85
85
|
],
|
|
86
86
|
[
|
|
87
|
-
{ display: "Cyan", value: "Cyan", rgb:
|
|
88
|
-
{ display: "Gray", value: "Gray", rgb:
|
|
89
|
-
{ display: "DkGray", value: "DarkGray", rgb:
|
|
90
|
-
{ display: "LtRed", value: "LightRed", rgb:
|
|
91
|
-
{ display: "LtGreen", value: "LightGreen", rgb:
|
|
92
|
-
{ display: "LtYellw", value: "LightYellow", rgb:
|
|
87
|
+
{ display: "Cyan", value: "Cyan", rgb: "Cyan" },
|
|
88
|
+
{ display: "Gray", value: "Gray", rgb: "Gray" },
|
|
89
|
+
{ display: "DkGray", value: "DarkGray", rgb: "DarkGray" },
|
|
90
|
+
{ display: "LtRed", value: "LightRed", rgb: "LightRed" },
|
|
91
|
+
{ display: "LtGreen", value: "LightGreen", rgb: "LightGreen" },
|
|
92
|
+
{ display: "LtYellw", value: "LightYellow", rgb: "LightYellow" },
|
|
93
93
|
],
|
|
94
94
|
[
|
|
95
|
-
{ display: "LtBlue", value: "LightBlue", rgb:
|
|
96
|
-
{ display: "LtMag", value: "LightMagenta", rgb:
|
|
97
|
-
{ display: "LtCyan", value: "LightCyan", rgb:
|
|
98
|
-
{ display: "White", value: "White", rgb:
|
|
95
|
+
{ display: "LtBlue", value: "LightBlue", rgb: "LightBlue" },
|
|
96
|
+
{ display: "LtMag", value: "LightMagenta", rgb: "LightMagenta" },
|
|
97
|
+
{ display: "LtCyan", value: "LightCyan", rgb: "LightCyan" },
|
|
98
|
+
{ display: "White", value: "White", rgb: "White" },
|
|
99
99
|
{ display: "Default", value: "Default", rgb: null },
|
|
100
100
|
{ display: "Reset", value: "Reset", rgb: null },
|
|
101
101
|
],
|
|
@@ -555,6 +555,27 @@ function parseColorToRgb(value: ColorValue): RGB | null {
|
|
|
555
555
|
return null;
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Convert a color value to an OverlayColorSpec for rendering.
|
|
560
|
+
* Named colors (e.g. "Yellow") are sent as strings so the editor renders them
|
|
561
|
+
* using native ANSI color codes, matching the actual theme rendering.
|
|
562
|
+
* RGB arrays are passed through directly.
|
|
563
|
+
*/
|
|
564
|
+
function colorValueToOverlaySpec(value: ColorValue): OverlayColorSpec | null {
|
|
565
|
+
if (Array.isArray(value) && value.length === 3) {
|
|
566
|
+
return value as RGB;
|
|
567
|
+
}
|
|
568
|
+
if (typeof value === "string") {
|
|
569
|
+
// For recognized named colors, send the name directly so the editor
|
|
570
|
+
// uses native ANSI rendering (matching actual theme output)
|
|
571
|
+
if (NAMED_COLORS[value] !== undefined) {
|
|
572
|
+
return value;
|
|
573
|
+
}
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
|
|
558
579
|
/**
|
|
559
580
|
* Convert RGB to hex string
|
|
560
581
|
*/
|
|
@@ -856,14 +877,19 @@ function buildPickerLines(): PickerLine[] {
|
|
|
856
877
|
lines.push({ text: `"${field.def.description}"`, type: "picker-desc" });
|
|
857
878
|
lines.push({ text: "─".repeat(RIGHT_WIDTH - 2), type: "picker-separator" });
|
|
858
879
|
|
|
859
|
-
//
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
880
|
+
// Color value display
|
|
881
|
+
const isNamed = typeof field.value === "string" && NAMED_COLORS[field.value] !== undefined;
|
|
882
|
+
if (isNamed) {
|
|
883
|
+
lines.push({ text: `Color: ${field.value} (terminal native)`, type: "picker-hex" });
|
|
884
|
+
} else {
|
|
885
|
+
const colorStr = formatColorValue(field.value);
|
|
886
|
+
const rgb = parseColorToRgb(field.value);
|
|
887
|
+
let valueLine = `Hex: ${colorStr}`;
|
|
888
|
+
if (rgb) {
|
|
889
|
+
valueLine += ` RGB: ${rgb[0]}, ${rgb[1]}, ${rgb[2]}`;
|
|
890
|
+
}
|
|
891
|
+
lines.push({ text: valueLine, type: "picker-hex" });
|
|
865
892
|
}
|
|
866
|
-
lines.push({ text: valueLine, type: "picker-hex" });
|
|
867
893
|
|
|
868
894
|
lines.push({ text: "", type: "picker-blank" });
|
|
869
895
|
|
|
@@ -929,14 +955,22 @@ function styleForLeftEntry(item: TreeLine | undefined): { style?: Partial<Overla
|
|
|
929
955
|
const paddedLen = getUtf8ByteLength(text.padEnd(LEFT_WIDTH));
|
|
930
956
|
const colorValue = item.colorValue;
|
|
931
957
|
const swatchIdx = colorValue !== undefined ? text.indexOf("██") : -1;
|
|
932
|
-
const rgb = colorValue !== undefined ? parseColorToRgb(colorValue) : null;
|
|
933
958
|
|
|
934
|
-
|
|
959
|
+
// For the swatch, use the native color representation to ensure it matches
|
|
960
|
+
// how the theme actually renders: named colors (e.g. "Yellow") should use
|
|
961
|
+
// the terminal's native ANSI color, not an RGB approximation.
|
|
962
|
+
const swatchColor: OverlayColorSpec | null = colorValue !== undefined
|
|
963
|
+
? (typeof colorValue === "string" && NAMED_COLORS[colorValue] !== undefined
|
|
964
|
+
? colorValue as OverlayColorSpec // Send named color string directly
|
|
965
|
+
: parseColorToRgb(colorValue)) // Send RGB for array values
|
|
966
|
+
: null;
|
|
967
|
+
|
|
968
|
+
if (swatchColor && swatchIdx >= 0) {
|
|
935
969
|
const swatchStart = getUtf8ByteLength(text.substring(0, swatchIdx));
|
|
936
970
|
const swatchEnd = swatchStart + getUtf8ByteLength("██");
|
|
937
971
|
// Non-overlapping segments: fieldName | swatch | value
|
|
938
972
|
inlines.push({ start: 0, end: swatchStart, style: { fg: colors.fieldName } });
|
|
939
|
-
inlines.push({ start: swatchStart, end: swatchEnd, style: { fg:
|
|
973
|
+
inlines.push({ start: swatchStart, end: swatchEnd, style: { fg: swatchColor, bg: swatchColor } });
|
|
940
974
|
const valueStart = swatchEnd + getUtf8ByteLength(" ");
|
|
941
975
|
if (valueStart < paddedLen) {
|
|
942
976
|
inlines.push({ start: valueStart, end: paddedLen, style: { fg: colors.customValue } });
|
|
@@ -1015,10 +1049,10 @@ function styleForRightEntry(item: PickerLine | undefined): { style?: Partial<Ove
|
|
|
1015
1049
|
// entry text = " " + item.text
|
|
1016
1050
|
// item.text = " " + token texts concatenated
|
|
1017
1051
|
const editorBg = getNestedValue(state.themeData, "editor.bg") as ColorValue;
|
|
1018
|
-
const
|
|
1052
|
+
const bgSpec = colorValueToOverlaySpec(editorBg);
|
|
1019
1053
|
const entryText = " " + item.text;
|
|
1020
1054
|
const entryLen = getUtf8ByteLength(entryText);
|
|
1021
|
-
const baseStyle: Partial<OverlayOptions> | undefined =
|
|
1055
|
+
const baseStyle: Partial<OverlayOptions> | undefined = bgSpec ? { bg: bgSpec } : undefined;
|
|
1022
1056
|
|
|
1023
1057
|
// Skip the leading " " + " " (from entry " " prefix + item.text leading " ")
|
|
1024
1058
|
let charPos = 2; // " " prefix + " " in item.text
|
|
@@ -1028,15 +1062,15 @@ function styleForRightEntry(item: PickerLine | undefined): { style?: Partial<Ove
|
|
|
1028
1062
|
if (token.syntaxType) {
|
|
1029
1063
|
const syntaxPath = `syntax.${token.syntaxType}`;
|
|
1030
1064
|
const syntaxColor = getNestedValue(state.themeData, syntaxPath) as ColorValue;
|
|
1031
|
-
const
|
|
1032
|
-
if (
|
|
1033
|
-
inlines.push({ start: bytePos, end: bytePos + tokenLen, style: { fg:
|
|
1065
|
+
const fgSpec = colorValueToOverlaySpec(syntaxColor);
|
|
1066
|
+
if (fgSpec) {
|
|
1067
|
+
inlines.push({ start: bytePos, end: bytePos + tokenLen, style: { fg: fgSpec } });
|
|
1034
1068
|
}
|
|
1035
1069
|
} else {
|
|
1036
1070
|
const fgColor = getNestedValue(state.themeData, "editor.fg") as ColorValue;
|
|
1037
|
-
const
|
|
1038
|
-
if (
|
|
1039
|
-
inlines.push({ start: bytePos, end: bytePos + tokenLen, style: { fg:
|
|
1071
|
+
const fgSpec = colorValueToOverlaySpec(fgColor);
|
|
1072
|
+
if (fgSpec) {
|
|
1073
|
+
inlines.push({ start: bytePos, end: bytePos + tokenLen, style: { fg: fgSpec } });
|
|
1040
1074
|
}
|
|
1041
1075
|
}
|
|
1042
1076
|
bytePos += tokenLen;
|
|
@@ -1344,11 +1378,9 @@ function buildColorSuggestions(field: ThemeField): PromptSuggestion[] {
|
|
|
1344
1378
|
suggestions.push({ text: name, description: editor.t("suggestion.terminal_native"), value: name });
|
|
1345
1379
|
}
|
|
1346
1380
|
|
|
1347
|
-
// Add named colors
|
|
1381
|
+
// Add named colors (terminal native - no hex shown since actual color depends on terminal)
|
|
1348
1382
|
for (const name of NAMED_COLOR_LIST) {
|
|
1349
|
-
|
|
1350
|
-
const hexValue = rgbToHex(rgb[0], rgb[1], rgb[2]);
|
|
1351
|
-
suggestions.push({ text: name, description: hexValue, value: name });
|
|
1383
|
+
suggestions.push({ text: name, description: editor.t("suggestion.terminal_native"), value: name });
|
|
1352
1384
|
}
|
|
1353
1385
|
|
|
1354
1386
|
return suggestions;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TOML LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Provides user-friendly error handling for TOML LSP server issues.
|
|
8
|
+
* When taplo fails to start, this plugin shows an actionable
|
|
9
|
+
* popup with installation instructions.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Detects TOML LSP server errors (taplo)
|
|
13
|
+
* - Shows popup with install commands (cargo, npm, brew)
|
|
14
|
+
* - Allows copying install commands to clipboard
|
|
15
|
+
* - Provides option to disable TOML LSP
|
|
16
|
+
*
|
|
17
|
+
* Notes:
|
|
18
|
+
* - Taplo supports schema validation for Cargo.toml, pyproject.toml, etc.
|
|
19
|
+
* - Also available as a VS Code extension and CLI formatter
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
interface LspServerErrorData {
|
|
23
|
+
language: string;
|
|
24
|
+
server_command: string;
|
|
25
|
+
error_type: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface LspStatusClickedData {
|
|
30
|
+
language: string;
|
|
31
|
+
has_error: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ActionPopupResultData {
|
|
35
|
+
popup_id: string;
|
|
36
|
+
action_id: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Install commands for TOML LSP server (taplo)
|
|
40
|
+
// See: https://taplo.tamasfe.dev/cli/installation.html
|
|
41
|
+
const INSTALL_COMMANDS = {
|
|
42
|
+
cargo: "cargo install taplo-cli --locked",
|
|
43
|
+
npm: "npm i -g @taplo/cli",
|
|
44
|
+
brew: "brew install taplo",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Track error state for TOML LSP
|
|
48
|
+
let tomlLspError: { serverCommand: string; message: string } | null = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Handle LSP server errors for TOML
|
|
52
|
+
*/
|
|
53
|
+
function on_toml_lsp_server_error(data: LspServerErrorData): void {
|
|
54
|
+
// Only handle TOML language errors
|
|
55
|
+
if (data.language !== "toml") {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
editor.debug(`toml-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
60
|
+
|
|
61
|
+
// Store error state for later reference
|
|
62
|
+
tomlLspError = {
|
|
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
|
+
`TOML LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
editor.setStatus(`TOML LSP error: ${data.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
registerHandler("on_toml_lsp_server_error", on_toml_lsp_server_error);
|
|
77
|
+
|
|
78
|
+
// Register hook for LSP server errors
|
|
79
|
+
editor.on("lsp_server_error", "on_toml_lsp_server_error");
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Handle status bar click when there's a TOML LSP error
|
|
83
|
+
*/
|
|
84
|
+
function on_toml_lsp_status_clicked(
|
|
85
|
+
data: LspStatusClickedData
|
|
86
|
+
): void {
|
|
87
|
+
// Only handle TOML language clicks when there's an error
|
|
88
|
+
if (data.language !== "toml" || !tomlLspError) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
editor.debug("toml-lsp: Status clicked, showing help popup");
|
|
93
|
+
|
|
94
|
+
// Show action popup with install options
|
|
95
|
+
editor.showActionPopup({
|
|
96
|
+
id: "toml-lsp-help",
|
|
97
|
+
title: "TOML Language Server Not Found",
|
|
98
|
+
message: `"${tomlLspError.serverCommand}" provides code completion, validation, formatting, and schema support for TOML files (Cargo.toml, pyproject.toml, etc.). Copy a command below to install it, or visit https://taplo.tamasfe.dev/cli/installation.html for details.`,
|
|
99
|
+
actions: [
|
|
100
|
+
{ id: "copy_cargo", label: `Copy: ${INSTALL_COMMANDS.cargo}` },
|
|
101
|
+
{ id: "copy_npm", label: `Copy: ${INSTALL_COMMANDS.npm}` },
|
|
102
|
+
{ id: "copy_brew", label: `Copy: ${INSTALL_COMMANDS.brew}` },
|
|
103
|
+
{ id: "disable", label: "Disable TOML LSP" },
|
|
104
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
registerHandler("on_toml_lsp_status_clicked", on_toml_lsp_status_clicked);
|
|
109
|
+
|
|
110
|
+
// Register hook for status bar clicks
|
|
111
|
+
editor.on("lsp_status_clicked", "on_toml_lsp_status_clicked");
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handle action popup results for TOML LSP help
|
|
115
|
+
*/
|
|
116
|
+
function on_toml_lsp_action_result(
|
|
117
|
+
data: ActionPopupResultData
|
|
118
|
+
): void {
|
|
119
|
+
// Only handle our popup
|
|
120
|
+
if (data.popup_id !== "toml-lsp-help") {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
editor.debug(`toml-lsp: Action selected - ${data.action_id}`);
|
|
125
|
+
|
|
126
|
+
switch (data.action_id) {
|
|
127
|
+
case "copy_cargo":
|
|
128
|
+
editor.setClipboard(INSTALL_COMMANDS.cargo);
|
|
129
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.cargo);
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case "copy_npm":
|
|
133
|
+
editor.setClipboard(INSTALL_COMMANDS.npm);
|
|
134
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.npm);
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case "copy_brew":
|
|
138
|
+
editor.setClipboard(INSTALL_COMMANDS.brew);
|
|
139
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.brew);
|
|
140
|
+
break;
|
|
141
|
+
|
|
142
|
+
case "disable":
|
|
143
|
+
editor.disableLspForLanguage("toml");
|
|
144
|
+
editor.setStatus("TOML LSP disabled");
|
|
145
|
+
tomlLspError = null;
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case "dismiss":
|
|
149
|
+
case "dismissed":
|
|
150
|
+
// Just close the popup without action
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
editor.debug(`toml-lsp: Unknown action: ${data.action_id}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
registerHandler("on_toml_lsp_action_result", on_toml_lsp_action_result);
|
|
158
|
+
|
|
159
|
+
// Register hook for action popup results
|
|
160
|
+
editor.on("action_popup_result", "on_toml_lsp_action_result");
|
|
161
|
+
|
|
162
|
+
editor.debug("toml-lsp: Plugin loaded");
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Typst LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Provides user-friendly error handling for Typst LSP server issues.
|
|
8
|
+
* When tinymist fails to start, this plugin shows an actionable
|
|
9
|
+
* popup with installation instructions.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Detects Typst LSP server errors (tinymist)
|
|
13
|
+
* - Shows popup with install commands (cargo, brew, etc.)
|
|
14
|
+
* - Allows copying install commands to clipboard
|
|
15
|
+
* - Provides option to disable Typst LSP
|
|
16
|
+
*
|
|
17
|
+
* Alternatives:
|
|
18
|
+
* - typst-lsp: Original Typst LSP (superseded by tinymist)
|
|
19
|
+
*
|
|
20
|
+
* Notes:
|
|
21
|
+
* - Tinymist also provides PDF preview and export features
|
|
22
|
+
* - Also available as VS Code extension "Tinymist Typst"
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
interface LspServerErrorData {
|
|
26
|
+
language: string;
|
|
27
|
+
server_command: string;
|
|
28
|
+
error_type: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface LspStatusClickedData {
|
|
33
|
+
language: string;
|
|
34
|
+
has_error: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ActionPopupResultData {
|
|
38
|
+
popup_id: string;
|
|
39
|
+
action_id: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Install commands for Typst LSP server (tinymist)
|
|
43
|
+
// See: https://github.com/Myriad-Dreamin/tinymist
|
|
44
|
+
const INSTALL_COMMANDS = {
|
|
45
|
+
cargo: "cargo install tinymist",
|
|
46
|
+
brew: "brew install tinymist",
|
|
47
|
+
nix: "nix-env -iA nixpkgs.tinymist",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Track error state for Typst LSP
|
|
51
|
+
let typstLspError: { serverCommand: string; message: string } | null = null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle LSP server errors for Typst
|
|
55
|
+
*/
|
|
56
|
+
function on_typst_lsp_server_error(data: LspServerErrorData): void {
|
|
57
|
+
// Only handle Typst language errors
|
|
58
|
+
if (data.language !== "typst") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
editor.debug(`typst-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
63
|
+
|
|
64
|
+
// Store error state for later reference
|
|
65
|
+
typstLspError = {
|
|
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
|
+
`Typst LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
editor.setStatus(`Typst LSP error: ${data.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
registerHandler("on_typst_lsp_server_error", on_typst_lsp_server_error);
|
|
80
|
+
|
|
81
|
+
// Register hook for LSP server errors
|
|
82
|
+
editor.on("lsp_server_error", "on_typst_lsp_server_error");
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handle status bar click when there's a Typst LSP error
|
|
86
|
+
*/
|
|
87
|
+
function on_typst_lsp_status_clicked(
|
|
88
|
+
data: LspStatusClickedData
|
|
89
|
+
): void {
|
|
90
|
+
// Only handle Typst language clicks when there's an error
|
|
91
|
+
if (data.language !== "typst" || !typstLspError) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
editor.debug("typst-lsp: Status clicked, showing help popup");
|
|
96
|
+
|
|
97
|
+
// Show action popup with install options
|
|
98
|
+
editor.showActionPopup({
|
|
99
|
+
id: "typst-lsp-help",
|
|
100
|
+
title: "Typst Language Server Not Found",
|
|
101
|
+
message: `"${typstLspError.serverCommand}" provides code completion, diagnostics, and preview support for Typst documents. Copy a command below to install it, or visit https://github.com/Myriad-Dreamin/tinymist for details and pre-built binaries. Also available as the "Tinymist Typst" VS Code extension.`,
|
|
102
|
+
actions: [
|
|
103
|
+
{ id: "copy_cargo", label: `Copy: ${INSTALL_COMMANDS.cargo}` },
|
|
104
|
+
{ id: "copy_brew", label: `Copy: ${INSTALL_COMMANDS.brew}` },
|
|
105
|
+
{ id: "copy_nix", label: `Copy: ${INSTALL_COMMANDS.nix}` },
|
|
106
|
+
{ id: "disable", label: "Disable Typst LSP" },
|
|
107
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
registerHandler("on_typst_lsp_status_clicked", on_typst_lsp_status_clicked);
|
|
112
|
+
|
|
113
|
+
// Register hook for status bar clicks
|
|
114
|
+
editor.on("lsp_status_clicked", "on_typst_lsp_status_clicked");
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle action popup results for Typst LSP help
|
|
118
|
+
*/
|
|
119
|
+
function on_typst_lsp_action_result(
|
|
120
|
+
data: ActionPopupResultData
|
|
121
|
+
): void {
|
|
122
|
+
// Only handle our popup
|
|
123
|
+
if (data.popup_id !== "typst-lsp-help") {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
editor.debug(`typst-lsp: Action selected - ${data.action_id}`);
|
|
128
|
+
|
|
129
|
+
switch (data.action_id) {
|
|
130
|
+
case "copy_cargo":
|
|
131
|
+
editor.setClipboard(INSTALL_COMMANDS.cargo);
|
|
132
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.cargo);
|
|
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_nix":
|
|
141
|
+
editor.setClipboard(INSTALL_COMMANDS.nix);
|
|
142
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.nix);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case "disable":
|
|
146
|
+
editor.disableLspForLanguage("typst");
|
|
147
|
+
editor.setStatus("Typst LSP disabled");
|
|
148
|
+
typstLspError = 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(`typst-lsp: Unknown action: ${data.action_id}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
registerHandler("on_typst_lsp_action_result", on_typst_lsp_action_result);
|
|
161
|
+
|
|
162
|
+
// Register hook for action popup results
|
|
163
|
+
editor.on("action_popup_result", "on_typst_lsp_action_result");
|
|
164
|
+
|
|
165
|
+
editor.debug("typst-lsp: Plugin loaded");
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/// <reference path="./lib/fresh.d.ts" />
|
|
2
|
+
const editor = getEditor();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vue LSP Helper Plugin
|
|
6
|
+
*
|
|
7
|
+
* Server: vue-language-server (@vue/language-server, formerly Volar)
|
|
8
|
+
* VS Code: "Vue - Official" extension (replaces deprecated Vetur)
|
|
9
|
+
* Neovim: nvim-lspconfig volar
|
|
10
|
+
* Note: Supports hybrid mode with @vue/typescript-plugin for TS integration
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface LspServerErrorData {
|
|
14
|
+
language: string;
|
|
15
|
+
server_command: string;
|
|
16
|
+
error_type: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface LspStatusClickedData {
|
|
21
|
+
language: string;
|
|
22
|
+
has_error: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ActionPopupResultData {
|
|
26
|
+
popup_id: string;
|
|
27
|
+
action_id: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const INSTALL_COMMANDS = {
|
|
31
|
+
npm: "npm install -g @vue/language-server",
|
|
32
|
+
yarn: "yarn global add @vue/language-server",
|
|
33
|
+
pnpm: "pnpm add -g @vue/language-server",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let vueLspError: { serverCommand: string; message: string } | null = null;
|
|
37
|
+
|
|
38
|
+
function on_vue_lsp_server_error(data: LspServerErrorData): void {
|
|
39
|
+
if (data.language !== "vue") {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
editor.debug(`vue-lsp: Server error - ${data.error_type}: ${data.message}`);
|
|
44
|
+
|
|
45
|
+
vueLspError = {
|
|
46
|
+
serverCommand: data.server_command,
|
|
47
|
+
message: data.message,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (data.error_type === "not_found") {
|
|
51
|
+
editor.setStatus(
|
|
52
|
+
`Vue LSP server '${data.server_command}' not found. Click status bar for help.`
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
editor.setStatus(`Vue LSP error: ${data.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
registerHandler("on_vue_lsp_server_error", on_vue_lsp_server_error);
|
|
59
|
+
editor.on("lsp_server_error", "on_vue_lsp_server_error");
|
|
60
|
+
|
|
61
|
+
function on_vue_lsp_status_clicked(data: LspStatusClickedData): void {
|
|
62
|
+
if (data.language !== "vue" || !vueLspError) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
editor.debug("vue-lsp: Status clicked, showing help popup");
|
|
67
|
+
|
|
68
|
+
editor.showActionPopup({
|
|
69
|
+
id: "vue-lsp-help",
|
|
70
|
+
title: "Vue Language Server Not Found",
|
|
71
|
+
message: `"${vueLspError.serverCommand}" (formerly Volar) provides completion, diagnostics, and refactoring for Vue SFCs. It replaces the deprecated Vetur.\n\nFor TypeScript integration, also install @vue/typescript-plugin.\nVS Code users: Install the "Vue - Official" extension.\nSee: https://github.com/vuejs/language-tools`,
|
|
72
|
+
actions: [
|
|
73
|
+
{ id: "copy_npm", label: `Copy: ${INSTALL_COMMANDS.npm}` },
|
|
74
|
+
{ id: "copy_pnpm", label: `Copy: ${INSTALL_COMMANDS.pnpm}` },
|
|
75
|
+
{ id: "disable", label: "Disable Vue LSP" },
|
|
76
|
+
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
registerHandler("on_vue_lsp_status_clicked", on_vue_lsp_status_clicked);
|
|
81
|
+
editor.on("lsp_status_clicked", "on_vue_lsp_status_clicked");
|
|
82
|
+
|
|
83
|
+
function on_vue_lsp_action_result(data: ActionPopupResultData): void {
|
|
84
|
+
if (data.popup_id !== "vue-lsp-help") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
editor.debug(`vue-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_pnpm":
|
|
97
|
+
editor.setClipboard(INSTALL_COMMANDS.pnpm);
|
|
98
|
+
editor.setStatus("Copied: " + INSTALL_COMMANDS.pnpm);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case "disable":
|
|
102
|
+
editor.disableLspForLanguage("vue");
|
|
103
|
+
editor.setStatus("Vue LSP disabled");
|
|
104
|
+
vueLspError = null;
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case "dismiss":
|
|
108
|
+
case "dismissed":
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
default:
|
|
112
|
+
editor.debug(`vue-lsp: Unknown action: ${data.action_id}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
registerHandler("on_vue_lsp_action_result", on_vue_lsp_action_result);
|
|
116
|
+
editor.on("action_popup_result", "on_vue_lsp_action_result");
|
|
117
|
+
|
|
118
|
+
editor.debug("vue-lsp: Plugin loaded");
|