@fresh-editor/fresh-editor 0.1.83 → 0.1.87

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.
@@ -795,7 +795,7 @@ globalThis.vi_visual_toggle_line = function (): void {
795
795
  };
796
796
 
797
797
  // Enter visual block mode (Ctrl-v)
798
- globalThis.vi_visual_block = function (): void {
798
+ globalThis.vi_visual_block = async function (): Promise<void> {
799
799
  // Store anchor position for block selection
800
800
  state.visualAnchor = editor.getCursorPosition();
801
801
 
@@ -803,7 +803,7 @@ globalThis.vi_visual_block = function (): void {
803
803
  const cursorPos = editor.getCursorPosition();
804
804
  if (cursorPos !== null) {
805
805
  const line = editor.getCursorLine() ?? 1;
806
- const lineStart = editor.getLineStartPosition(line);
806
+ const lineStart = await editor.getLineStartPosition(line);
807
807
  const col = lineStart !== null ? cursorPos - lineStart : 0;
808
808
  state.visualBlockAnchor = { line, col };
809
809
  }
@@ -1561,11 +1561,18 @@ editor.defineMode("vi-normal", null, [
1561
1561
 
1562
1562
  // Command mode
1563
1563
  [":", "vi_command_mode"],
1564
+
1565
+ // Pass through to standard editor shortcuts
1566
+ ["C-p", "command_palette"],
1567
+ ["C-q", "quit"],
1564
1568
  ], true); // read_only = true to prevent character insertion
1565
1569
 
1566
1570
  // Define vi-insert mode - only Escape is special, other keys insert text
1567
1571
  editor.defineMode("vi-insert", null, [
1568
1572
  ["Escape", "vi_escape"],
1573
+ // Pass through to standard editor shortcuts
1574
+ ["C-p", "command_palette"],
1575
+ ["C-q", "quit"],
1569
1576
  ], false); // read_only = false to allow normal typing
1570
1577
 
1571
1578
  // Define vi-find-char mode - binds all printable chars to the handler
@@ -1769,6 +1776,10 @@ editor.defineMode("vi-visual", null, [
1769
1776
  // Exit
1770
1777
  ["Escape", "vi_vis_escape"],
1771
1778
  ["v", "vi_vis_escape"], // v again exits visual mode
1779
+
1780
+ // Pass through to standard editor shortcuts
1781
+ ["C-p", "command_palette"],
1782
+ ["C-q", "quit"],
1772
1783
  ], true);
1773
1784
 
1774
1785
  // Define vi-visual-line mode (line-wise)
@@ -1803,6 +1814,10 @@ editor.defineMode("vi-visual-line", null, [
1803
1814
  // Exit
1804
1815
  ["Escape", "vi_vis_escape"],
1805
1816
  ["V", "vi_vis_escape"], // V again exits visual-line mode
1817
+
1818
+ // Pass through to standard editor shortcuts
1819
+ ["C-p", "command_palette"],
1820
+ ["C-q", "quit"],
1806
1821
  ], true);
1807
1822
 
1808
1823
  // Define vi-visual-block mode (column/block selection)
@@ -1841,6 +1856,10 @@ editor.defineMode("vi-visual-block", null, [
1841
1856
  // Exit
1842
1857
  ["Escape", "vi_vblock_escape"],
1843
1858
  ["C-v", "vi_vblock_escape"], // Ctrl-v again exits visual-block mode
1859
+
1860
+ // Pass through to standard editor shortcuts
1861
+ ["C-p", "command_palette"],
1862
+ ["C-q", "quit"],
1844
1863
  ], true);
1845
1864
 
1846
1865
  // ============================================================================
@@ -2177,31 +2196,62 @@ async function executeCommand(
2177
2196
  switch (command) {
2178
2197
  case "write": {
2179
2198
  // :w - save current file
2180
- // :w filename - save as filename (not implemented yet)
2199
+ // :w filename - save to specified filename
2181
2200
  if (args) {
2182
- return { error: editor.t("error.save_as_not_implemented") };
2201
+ const bufferId = editor.getActiveBufferId();
2202
+ // Resolve path (could be relative or absolute)
2203
+ const path = args.startsWith("/") ? args : `${editor.getCwd()}/${args}`;
2204
+ editor.saveBufferToPath(bufferId, path);
2205
+ return { message: editor.t("status.file_saved") };
2183
2206
  }
2184
2207
  editor.executeAction("save");
2185
2208
  return { message: editor.t("status.file_saved") };
2186
2209
  }
2187
2210
 
2188
2211
  case "quit": {
2189
- // :q - quit (close buffer)
2190
- // :q! - force quit (discard changes)
2191
- const bufferId = editor.getActiveBufferId();
2192
- if (!force && editor.isBufferModified(bufferId)) {
2212
+ // :q - quit editor (like vim)
2213
+ // :q! - force quit (discard unsaved changes)
2214
+ if (force) {
2215
+ editor.executeAction("force_quit");
2216
+ return {};
2217
+ }
2218
+ // Check ALL buffers for unsaved changes
2219
+ const buffers = editor.listBuffers() as Array<{ id: number; modified: boolean }>;
2220
+ const hasModified = buffers.some((b) => b.modified);
2221
+ if (hasModified) {
2193
2222
  return { error: editor.t("error.no_write_since_change", { cmd: ":q!" }) };
2194
2223
  }
2195
- editor.executeAction("close_buffer");
2224
+ editor.executeAction("force_quit");
2196
2225
  return {};
2197
2226
  }
2198
2227
 
2199
2228
  case "wq":
2200
2229
  case "xit":
2201
2230
  case "exit": {
2202
- // :wq or :x - save and quit
2203
- editor.executeAction("save");
2204
- editor.executeAction("close_buffer");
2231
+ // :wq or :x - save current buffer and quit
2232
+ // :wq filename - save to filename and quit
2233
+ const wqBufferId = editor.getActiveBufferId();
2234
+
2235
+ if (args) {
2236
+ // Save to specified filename
2237
+ const path = args.startsWith("/") ? args : `${editor.getCwd()}/${args}`;
2238
+ editor.saveBufferToPath(wqBufferId, path);
2239
+ } else {
2240
+ // Save to existing path
2241
+ const wqPath = editor.getBufferPath(wqBufferId);
2242
+ if (!wqPath) {
2243
+ return { error: editor.t("error.no_file_name") };
2244
+ }
2245
+ editor.executeAction("save");
2246
+ }
2247
+
2248
+ // Check if any OTHER buffers have unsaved changes
2249
+ const allBuffers = editor.listBuffers() as Array<{ id: number; modified: boolean }>;
2250
+ const otherModified = allBuffers.some((b: { id: number; modified: boolean }) => b.id !== wqBufferId && b.modified);
2251
+ if (otherModified) {
2252
+ return { error: editor.t("error.other_buffers_modified", { cmd: ":wqa" }) };
2253
+ }
2254
+ editor.executeAction("force_quit");
2205
2255
  return {};
2206
2256
  }
2207
2257
 
@@ -2215,16 +2265,15 @@ async function executeCommand(
2215
2265
  // :qa - quit all
2216
2266
  // :qa! - force quit all
2217
2267
  if (force) {
2218
- editor.executeAction("quit_all");
2268
+ editor.executeAction("force_quit");
2219
2269
  } else {
2220
2270
  // Check if any buffer is modified
2221
- const buffers = editor.listBuffers();
2222
- for (const buf of buffers) {
2223
- if (buf.modified) {
2224
- return { error: editor.t("error.no_write_since_change", { cmd: ":qa!" }) };
2225
- }
2271
+ const allBufs = editor.listBuffers() as Array<{ id: number; modified: boolean }>;
2272
+ const anyModified = allBufs.some((b) => b.modified);
2273
+ if (anyModified) {
2274
+ return { error: editor.t("error.no_write_since_change", { cmd: ":qa!" }) };
2226
2275
  }
2227
- editor.executeAction("quit_all");
2276
+ editor.executeAction("force_quit");
2228
2277
  }
2229
2278
  return {};
2230
2279
  }
@@ -2232,7 +2281,7 @@ async function executeCommand(
2232
2281
  case "wqall": {
2233
2282
  // :wqa or :xa - save all and quit
2234
2283
  editor.executeAction("save_all");
2235
- editor.executeAction("quit_all");
2284
+ editor.executeAction("force_quit");
2236
2285
  return {};
2237
2286
  }
2238
2287
 
@@ -2307,7 +2356,7 @@ async function executeCommand(
2307
2356
  if (!force && editor.isBufferModified(bufferId)) {
2308
2357
  return { error: editor.t("error.no_write_since_change", { cmd: ":bd!" }) };
2309
2358
  }
2310
- editor.executeAction("close_buffer");
2359
+ editor.executeAction("close");
2311
2360
  return {};
2312
2361
  }
2313
2362
 
@@ -2421,7 +2470,7 @@ async function executeCommand(
2421
2470
  if (!force && editor.isBufferModified(bufferId)) {
2422
2471
  return { error: editor.t("error.no_write_since_change", { cmd: ":close!" }) };
2423
2472
  }
2424
- editor.executeAction("close_buffer");
2473
+ editor.executeAction("close");
2425
2474
  return {};
2426
2475
  }
2427
2476
 
@@ -2442,7 +2491,7 @@ async function executeCommand(
2442
2491
  if (!force && editor.isBufferModified(bufferId)) {
2443
2492
  return { error: editor.t("error.no_write_since_change", { cmd: ":tabclose!" }) };
2444
2493
  }
2445
- editor.executeAction("close_buffer");
2494
+ editor.executeAction("close");
2446
2495
  return {};
2447
2496
  }
2448
2497
 
@@ -2747,11 +2796,10 @@ editor.registerCommand(
2747
2796
  "%cmd.toggle_vi_mode",
2748
2797
  "%cmd.toggle_vi_mode_desc",
2749
2798
  "vi_mode_toggle",
2750
- "normal",
2799
+ null, // Always visible - needed to enable vi mode in the first place
2751
2800
  );
2752
2801
 
2753
2802
  // ============================================================================
2754
2803
  // Initialization
2755
2804
  // ============================================================================
2756
2805
 
2757
- editor.setStatus(editor.t("status.loaded"));
@@ -8,7 +8,6 @@ const editor = getEditor();
8
8
  */
9
9
 
10
10
  // Show welcome message in status bar
11
- editor.setStatus(editor.t("status.loaded"));
12
11
 
13
12
  // Register commands that use built-in actions
14
13
  editor.registerCommand(
@@ -0,0 +1,65 @@
1
+ /// <reference path="./lib/fresh.d.ts" />
2
+ // Provides installation help when zls (Zig LSP) is not found
3
+ const editor = getEditor();
4
+
5
+ interface LspServerErrorData {
6
+ language: string;
7
+ server_command: string;
8
+ error_type: string;
9
+ message: string;
10
+ }
11
+
12
+ interface LspStatusClickedData {
13
+ language: string;
14
+ has_error: boolean;
15
+ }
16
+
17
+ interface ActionPopupResultData {
18
+ popup_id: string;
19
+ action_id: string;
20
+ }
21
+
22
+ const INSTALL_URL = "https://github.com/zigtools/zls#installation";
23
+ let zigLspError: { serverCommand: string; message: string } | null = null;
24
+
25
+ globalThis.on_zig_lsp_server_error = function (data: LspServerErrorData): void {
26
+ if (data.language !== "zig") return;
27
+ zigLspError = { serverCommand: data.server_command, message: data.message };
28
+ if (data.error_type === "not_found") {
29
+ editor.setStatus(`Zig LSP '${data.server_command}' not found. Click status bar for help.`);
30
+ } else {
31
+ editor.setStatus(`Zig LSP error: ${data.message}`);
32
+ }
33
+ };
34
+ editor.on("lsp_server_error", "on_zig_lsp_server_error");
35
+
36
+ globalThis.on_zig_lsp_status_clicked = function (data: LspStatusClickedData): void {
37
+ if (data.language !== "zig" || !zigLspError) return;
38
+ editor.showActionPopup({
39
+ id: "zig-lsp-help",
40
+ title: "Zig Language Server Not Found",
41
+ message: `Install zls for code completion and diagnostics. Visit ${INSTALL_URL}`,
42
+ actions: [
43
+ { id: "copy_url", label: "Copy install URL" },
44
+ { id: "disable", label: "Disable Zig LSP" },
45
+ { id: "dismiss", label: "Dismiss (ESC)" },
46
+ ],
47
+ });
48
+ };
49
+ editor.on("lsp_status_clicked", "on_zig_lsp_status_clicked");
50
+
51
+ globalThis.on_zig_lsp_action_result = function (data: ActionPopupResultData): void {
52
+ if (data.popup_id !== "zig-lsp-help") return;
53
+ switch (data.action_id) {
54
+ case "copy_url":
55
+ editor.setClipboard(INSTALL_URL);
56
+ editor.setStatus("Copied: " + INSTALL_URL);
57
+ break;
58
+ case "disable":
59
+ editor.disableLspForLanguage("zig");
60
+ editor.setStatus("Zig LSP disabled");
61
+ zigLspError = null;
62
+ break;
63
+ }
64
+ };
65
+ editor.on("action_popup_result", "on_zig_lsp_action_result");