@fresh-editor/fresh-editor 0.3.0 → 0.3.1

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +66 -2
  2. package/package.json +1 -1
  3. package/plugins/astro-lsp.ts +6 -12
  4. package/plugins/audit_mode.ts +106 -113
  5. package/plugins/bash-lsp.ts +15 -22
  6. package/plugins/clangd-lsp.ts +15 -24
  7. package/plugins/clojure-lsp.ts +9 -12
  8. package/plugins/cmake-lsp.ts +9 -12
  9. package/plugins/code-tour.ts +15 -16
  10. package/plugins/config-schema.json +10 -0
  11. package/plugins/csharp_support.ts +25 -30
  12. package/plugins/css-lsp.ts +15 -22
  13. package/plugins/dart-lsp.ts +9 -12
  14. package/plugins/dashboard.ts +118 -0
  15. package/plugins/devcontainer.i18n.json +84 -28
  16. package/plugins/devcontainer.ts +897 -170
  17. package/plugins/diagnostics_panel.ts +10 -17
  18. package/plugins/elixir-lsp.ts +9 -12
  19. package/plugins/erlang-lsp.ts +9 -12
  20. package/plugins/examples/bookmarks.ts +10 -16
  21. package/plugins/find_references.ts +5 -9
  22. package/plugins/flash.ts +577 -0
  23. package/plugins/fsharp-lsp.ts +9 -12
  24. package/plugins/git_explorer.ts +16 -20
  25. package/plugins/git_gutter.ts +65 -79
  26. package/plugins/git_log.ts +8 -8
  27. package/plugins/gleam-lsp.ts +9 -12
  28. package/plugins/go-lsp.ts +15 -22
  29. package/plugins/graphql-lsp.ts +9 -12
  30. package/plugins/haskell-lsp.ts +9 -12
  31. package/plugins/html-lsp.ts +15 -24
  32. package/plugins/java-lsp.ts +9 -12
  33. package/plugins/json-lsp.ts +15 -24
  34. package/plugins/julia-lsp.ts +9 -12
  35. package/plugins/kotlin-lsp.ts +15 -22
  36. package/plugins/latex-lsp.ts +9 -12
  37. package/plugins/lib/fresh.d.ts +378 -0
  38. package/plugins/lua-lsp.ts +15 -22
  39. package/plugins/markdown_compose.ts +78 -122
  40. package/plugins/markdown_source.ts +8 -10
  41. package/plugins/marksman-lsp.ts +9 -12
  42. package/plugins/merge_conflict.ts +15 -17
  43. package/plugins/nim-lsp.ts +9 -12
  44. package/plugins/nix-lsp.ts +9 -12
  45. package/plugins/nushell-lsp.ts +9 -12
  46. package/plugins/ocaml-lsp.ts +9 -12
  47. package/plugins/odin-lsp.ts +15 -22
  48. package/plugins/path_complete.ts +5 -6
  49. package/plugins/perl-lsp.ts +9 -12
  50. package/plugins/php-lsp.ts +15 -22
  51. package/plugins/pkg.ts +10 -21
  52. package/plugins/protobuf-lsp.ts +9 -12
  53. package/plugins/python-lsp.ts +15 -24
  54. package/plugins/r-lsp.ts +9 -12
  55. package/plugins/ruby-lsp.ts +15 -22
  56. package/plugins/rust-lsp.ts +18 -28
  57. package/plugins/scala-lsp.ts +9 -12
  58. package/plugins/schemas/theme.schema.json +18 -0
  59. package/plugins/search_replace.ts +10 -13
  60. package/plugins/solidity-lsp.ts +9 -12
  61. package/plugins/sql-lsp.ts +9 -12
  62. package/plugins/svelte-lsp.ts +9 -12
  63. package/plugins/swift-lsp.ts +9 -12
  64. package/plugins/tailwindcss-lsp.ts +9 -12
  65. package/plugins/templ-lsp.ts +9 -12
  66. package/plugins/terraform-lsp.ts +9 -12
  67. package/plugins/theme_editor.i18n.json +70 -14
  68. package/plugins/theme_editor.ts +152 -208
  69. package/plugins/toml-lsp.ts +15 -22
  70. package/plugins/tsconfig.json +100 -0
  71. package/plugins/typescript-lsp.ts +15 -24
  72. package/plugins/typst-lsp.ts +15 -22
  73. package/plugins/vi_mode.ts +77 -290
  74. package/plugins/vue-lsp.ts +9 -12
  75. package/plugins/yaml-lsp.ts +15 -22
  76. package/plugins/zig-lsp.ts +9 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # Release Notes
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Features
6
+
7
+ * **Animations framework**: tab-switch slide; cursor-jump trail animation. Disable it via Settings UI. Animations available to plugins via API.
8
+
9
+ * **Flash plugin** (label-jump, à la flash.nvim): bundled plugin — type a pattern, press the displayed label to jump, even across split panes.
10
+
11
+ * **Devcontainer fixes**: See below
12
+
13
+ ### Improvements
14
+
15
+ * **Relative-numbers go-to negative** (thanks @paveloparev!): `g<-N>` jumps N lines up in relative-numbers mode.
16
+
17
+ * **Racket language support**: `.rkt` / `.rktd` / `.rktl` / `.scrbl` highlight out of the box; LSP via `racket-langserver`.
18
+
19
+ * **`{remote}` indicator default-on**: rendered on bottom-left for fresh installs (`F6` default keybinding, palette command).
20
+
21
+ * **Quick Open keybindings** (thanks @paveloparev!): `Ctrl+'` for files, `Ctrl+;` for buffers. Some terminals don't like passing these keys, you can rebind in the Keybinding UI (future version will probably change the default shortcuts to be more terminal-friendly across platforms).
22
+
23
+ * **Completion popup rebindable** (#1705): Allow modifying key bindings for the non-lsp completions popup.
24
+
25
+ * **Better scrolling** on markdown preview buffers and very-long-line buffers via a new two-tier line-wrap cache.
26
+
27
+ * **LSP**:
28
+ - Stuck request no longer blocks others (#1679): per-server handlers on independent tokio tasks + 30 s timeout. R `languageserver` 0.3.17 no longer wedges completion / signature help.
29
+ - Empty-server completion fallback to buffer-word completions.
30
+ - Failure-stub log so "View Log" works after a failed spawn.
31
+
32
+ * **Devcontainer** spec-conformance + UX:
33
+ - Lifecycle `cwd = remoteWorkspaceFolder`; object-form entries run in parallel and continue past failures; `remoteEnv` propagated; `shutdownAction: stopContainer` honoured on Detach; `userEnvProbe` captured and merged; `remoteUser` falls back per spec.
34
+ - Lifecycle stdout/stderr surface in the panel; `Show *` commands reuse a single panel; build log split reused not stacked; parse errors surface (and `Open Config` stays registered); `onAutoForward: notify` toasts; state-relevant commands gated by authority; no re-prompt after restart.
35
+ - **Devcontainer Goto-Definition across host / container**: LSP URIs translate at the boundary; container-only paths (e.g. `flask/app.py` under the venv) are fetched into a buffer.
36
+
37
+ * **File explorer preview** no longer loses focus to LSP popups in the editor pane.
38
+
39
+ * **Plugin types**: `tsconfig.json` for `tsc --noEmit` in CI; `editor.on(fn)` infers payload types from `HookEventMap`; `HookArgs` derives serde.
40
+
41
+ * **Conceal substitution** now emits the replacement glyph for whitespace tokens (Space / Newline / Break).
42
+
43
+ ### Bug Fixes
44
+
45
+ * **`plugins/` folder in your project no longer hides bundled commands** (#1722): Fresh stops scanning the working directory for plugins.
46
+
47
+ * **Scroll & viewport**:
48
+ - Mouse-wheel / PageDown / scrollbar-drag now reach EOF on word-wrapped buffers, including compose-mode markdown ending in a table. Within-line scroll re-clamps; gutter calc unified.
49
+ - Search wrap-around Down-arrow stall: stale `scrolled_up_in_wrap` cleared on recenter.
50
+
51
+ * **Non-ASCII truncation panics** (#1715, #1718): settings search/preview/description/changelog, file-browser sort-arrow header, status-bar `truncate_path`, shell `truncate_command`, map-input value previews. Text search inside rows with multi-byte glyphs no longer panics either.
52
+
53
+ * **`setLayoutHints`** binds `compose_width` to the buffer, not whichever buffer is active.
54
+
55
+ ### Under the Hood
56
+
57
+ * **Line-wrap cache** (`LineWrapCache` + tier-2 `VisualRowIndex`) becomes the single source of truth — `wrap_line` / `WrappedSegment` deleted; scroll math, cursor position, and scrollbar thumb share one pipeline.
58
+
59
+ * **Marker-tree `remove_in_range`** for `SoftBreakManager` / `ConcealManager` / `OverlayManager`: O(log N + k), proptest invariants.
60
+
61
+ * **`LspUri` newtype** enforces host / container URI translation at the type level.
62
+
63
+ * **Refactors**: `handle_mouse_click` (764 lines) → 14 helpers; `real_main` decomposed; `plugin_dispatch` match arms extracted; `quickjs_backend` ID-allocation boilerplate collapsed; `FromJs` impls into a macro; mouse multi-click + scroll dispatch deduped.
64
+
65
+ * **Test infrastructure**: fake `devcontainer` / `docker` / `pylsp` CLIs drive e2es without real binaries; plugin fixtures load from `<config_dir>/plugins/` instead of the cwd; animations default off in tests.
66
+
3
67
  ## 0.3.0
4
68
 
5
69
  This version brings major features and many quality-of-life improvements and bug fixes:
@@ -27,8 +91,8 @@ And more (see below). A large version is more likely to contain regression bugs,
27
91
  - Build log streams into a workspace split; failed attaches offer Retry / Show Logs / Detach via a recovery popup.
28
92
  - `initializeCommand` runs on attach.
29
93
 
30
- * **`init.ts`**: Fresh now auto-loads `~/.config/fresh/init.ts`! Allows you to run plugin code on startup, which complements the purely declarative config system with imperative, environment-aware logic. Use command palette `init: Edit` to generate a template with some examples. Use `init: Reload` to run it after editing. Use `--no-init` / `--safe` to skip loading.
31
- - Tip: *Enable LSP* when editing `init.ts` to get help and completions.
94
+ * **`init.ts`**: Fresh now auto-loads `~/.config/fresh/init.ts`! Allows you to run plugin code on startup, which complements the purely declarative config system with imperative, environment-aware logic. Use command palette `init: Edit` to generate a template with some examples. Use `init: Reload` to run it after editing. Use `--no-init` / `--safe` to skip loading.
95
+ - Tip: *Enable LSP* when editing `init.ts` to get help and completions.
32
96
  - Example (for the Dashboard plugin):
33
97
  ```typescript
34
98
  // in your init.ts file:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fresh-editor/fresh-editor",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A modern terminal-based text editor with plugin support",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,7 @@ const INSTALL_COMMANDS = {
35
35
 
36
36
  let astroLspError: { serverCommand: string; message: string } | null = null;
37
37
 
38
- function on_astro_lsp_server_error(data: LspServerErrorData): void {
38
+ editor.on("lsp_server_error", (data) => {
39
39
  if (data.language !== "astro") {
40
40
  return;
41
41
  }
@@ -54,11 +54,9 @@ function on_astro_lsp_server_error(data: LspServerErrorData): void {
54
54
  } else {
55
55
  editor.setStatus(`Astro LSP error: ${data.message}`);
56
56
  }
57
- }
58
- registerHandler("on_astro_lsp_server_error", on_astro_lsp_server_error);
59
- editor.on("lsp_server_error", "on_astro_lsp_server_error");
57
+ });
60
58
 
61
- function on_astro_lsp_status_clicked(data: LspStatusClickedData): void {
59
+ editor.on("lsp_status_clicked", (data) => {
62
60
  if (data.language !== "astro" || !astroLspError) {
63
61
  return;
64
62
  }
@@ -76,11 +74,9 @@ function on_astro_lsp_status_clicked(data: LspStatusClickedData): void {
76
74
  { id: "dismiss", label: "Dismiss (ESC)" },
77
75
  ],
78
76
  });
79
- }
80
- registerHandler("on_astro_lsp_status_clicked", on_astro_lsp_status_clicked);
81
- editor.on("lsp_status_clicked", "on_astro_lsp_status_clicked");
77
+ });
82
78
 
83
- function on_astro_lsp_action_result(data: ActionPopupResultData): void {
79
+ editor.on("action_popup_result", (data) => {
84
80
  if (data.popup_id !== "astro-lsp-help") {
85
81
  return;
86
82
  }
@@ -111,8 +107,6 @@ function on_astro_lsp_action_result(data: ActionPopupResultData): void {
111
107
  default:
112
108
  editor.debug(`astro-lsp: Unknown action: ${data.action_id}`);
113
109
  }
114
- }
115
- registerHandler("on_astro_lsp_action_result", on_astro_lsp_action_result);
116
- editor.on("action_popup_result", "on_astro_lsp_action_result");
110
+ });
117
111
 
118
112
  editor.debug("astro-lsp: Plugin loaded");
@@ -54,6 +54,7 @@ interface Hunk {
54
54
  oldRange: { start: number; end: number }; // old file line range
55
55
  type: 'add' | 'remove' | 'modify';
56
56
  lines: string[];
57
+ status?: string;
57
58
  contextHeader: string;
58
59
  byteOffset: number; // Position in the virtual buffer
59
60
  gitStatus?: 'staged' | 'unstaged' | 'untracked';
@@ -2462,48 +2463,9 @@ function review_discard_file() {
2462
2463
  }
2463
2464
  registerHandler("review_discard_file", review_discard_file);
2464
2465
 
2465
- async function on_review_discard_hunk_confirm(args: { prompt_type: string; input: string; selected_index: number | null }): Promise<boolean> {
2466
- if (args.prompt_type !== "review-discard-hunk-confirm") return true;
2467
- const response = args.input.trim().toLowerCase();
2468
- if (response === "discard" || args.selected_index === 0) {
2469
- const hunk = getHunkAtDiffCursor();
2470
- if (hunk && hunk.file) {
2471
- const patch = buildHunkPatch(hunk.file, hunk);
2472
- const ok = await applyHunkPatch(patch, ["--reverse"]);
2473
- if (ok) {
2474
- editor.setStatus(editor.t("status.hunk_discarded") || "Hunk discarded");
2475
- await refreshMagitData();
2476
- }
2477
- }
2478
- } else {
2479
- editor.setStatus("Discard cancelled");
2480
- }
2481
- return false;
2482
- }
2483
- registerHandler("on_review_discard_hunk_confirm", on_review_discard_hunk_confirm);
2484
2466
 
2485
- async function on_review_discard_confirm(args: { prompt_type: string; input: string; selected_index: number | null }): Promise<boolean> {
2486
- if (args.prompt_type !== "review-discard-confirm") return true;
2487
2467
 
2488
- const response = args.input.trim().toLowerCase();
2489
- if (response === "discard" || args.selected_index === 0) {
2490
- const f = pendingDiscardFile;
2491
- if (f) {
2492
- if (f.category === 'untracked') {
2493
- await editor.spawnProcess("rm", ["--", f.path]);
2494
- } else {
2495
- await editor.spawnProcess("git", ["checkout", "--", f.path]);
2496
- }
2497
- await refreshMagitData();
2498
- editor.setStatus(`Discarded: ${f.path}`);
2499
- }
2500
- } else {
2501
- editor.setStatus("Discard cancelled");
2502
- }
2503
- pendingDiscardFile = null;
2504
- return false;
2505
- }
2506
- registerHandler("on_review_discard_confirm", on_review_discard_confirm);
2468
+
2507
2469
 
2508
2470
  /**
2509
2471
  * Refresh file list and diffs using the new git status approach, then re-render.
@@ -3456,28 +3418,15 @@ async function review_delete_comment() {
3456
3418
  }
3457
3419
  registerHandler("review_delete_comment", review_delete_comment);
3458
3420
 
3459
- function on_review_delete_comment_confirm(args: { prompt_type: string; input: string; selected_index: number | null }): boolean {
3460
- if (args.prompt_type !== "review-delete-comment-confirm") return true;
3461
- const response = args.input.trim().toLowerCase();
3462
- if ((response === "delete" || args.selected_index === 0) && pendingDeleteCommentId) {
3463
- if (pendingDeleteCommentId === '__note__') {
3464
- state.note = '';
3465
- } else {
3466
- state.comments = state.comments.filter(c => c.id !== pendingDeleteCommentId);
3467
- }
3468
- persistReview();
3469
- updateMagitDisplay();
3470
- editor.setStatus("Deleted");
3471
- } else {
3472
- editor.setStatus("Delete cancelled");
3473
- }
3474
- pendingDeleteCommentId = null;
3475
- return false;
3476
- }
3477
- registerHandler("on_review_delete_comment_confirm", on_review_delete_comment_confirm);
3421
+
3478
3422
 
3479
3423
  // Prompt event handlers
3480
- function on_review_prompt_confirm(args: { prompt_type: string; input: string }): boolean {
3424
+
3425
+
3426
+
3427
+
3428
+ // Register prompt event handlers
3429
+ editor.on("prompt_confirmed", (args) => {
3481
3430
  if (args.prompt_type !== "review-comment") {
3482
3431
  return true;
3483
3432
  }
@@ -3540,38 +3489,47 @@ function on_review_prompt_confirm(args: { prompt_type: string; input: string }):
3540
3489
  }
3541
3490
  pendingCommentInfo = null;
3542
3491
  return true;
3543
- }
3544
- registerHandler("on_review_prompt_confirm", on_review_prompt_confirm);
3492
+ });
3493
+ editor.on("prompt_confirmed", async (args) => {
3494
+ if (args.prompt_type !== "review-discard-confirm") return true;
3545
3495
 
3546
- function on_review_prompt_cancel(args: { prompt_type: string }): boolean {
3547
- if (args.prompt_type === "review-comment") {
3548
- pendingCommentInfo = null;
3549
- editingCommentId = null;
3550
- editor.setStatus(editor.t("status.comment_cancelled"));
3496
+ const response = args.input.trim().toLowerCase();
3497
+ if (response === "discard" || args.selected_index === 0) {
3498
+ const f = pendingDiscardFile;
3499
+ if (f) {
3500
+ if (f.category === 'untracked') {
3501
+ await editor.spawnProcess("rm", ["--", f.path]);
3502
+ } else {
3503
+ await editor.spawnProcess("git", ["checkout", "--", f.path]);
3504
+ }
3505
+ await refreshMagitData();
3506
+ editor.setStatus(`Discarded: ${f.path}`);
3507
+ }
3508
+ } else {
3509
+ editor.setStatus("Discard cancelled");
3551
3510
  }
3552
- return true;
3553
- }
3554
- registerHandler("on_review_prompt_cancel", on_review_prompt_cancel);
3555
-
3556
- // Register prompt event handlers
3557
- editor.on("prompt_confirmed", "on_review_prompt_confirm");
3558
- editor.on("prompt_confirmed", "on_review_discard_confirm");
3559
- editor.on("prompt_confirmed", "on_review_discard_hunk_confirm");
3560
- editor.on("prompt_confirmed", "on_review_edit_note_confirm");
3561
- editor.on("prompt_confirmed", "on_review_delete_comment_confirm");
3562
- editor.on("prompt_cancelled", "on_review_prompt_cancel");
3563
-
3564
- async function review_edit_note() {
3565
- const label = editor.t("prompt.overall_comment") || "Note: ";
3566
- if (state.note) {
3567
- editor.startPromptWithInitial(label, "review-edit-note", state.note);
3511
+ pendingDiscardFile = null;
3512
+ return false;
3513
+ });
3514
+ editor.on("prompt_confirmed", async (args) => {
3515
+ if (args.prompt_type !== "review-discard-hunk-confirm") return true;
3516
+ const response = args.input.trim().toLowerCase();
3517
+ if (response === "discard" || args.selected_index === 0) {
3518
+ const hunk = getHunkAtDiffCursor();
3519
+ if (hunk && hunk.file) {
3520
+ const patch = buildHunkPatch(hunk.file, hunk);
3521
+ const ok = await applyHunkPatch(patch, ["--reverse"]);
3522
+ if (ok) {
3523
+ editor.setStatus(editor.t("status.hunk_discarded") || "Hunk discarded");
3524
+ await refreshMagitData();
3525
+ }
3526
+ }
3568
3527
  } else {
3569
- editor.startPrompt(label, "review-edit-note");
3528
+ editor.setStatus("Discard cancelled");
3570
3529
  }
3571
- }
3572
- registerHandler("review_edit_note", review_edit_note);
3573
-
3574
- function on_review_edit_note_confirm(args: { prompt_type: string; input: string }): boolean {
3530
+ return false;
3531
+ });
3532
+ editor.on("prompt_confirmed", (args) => {
3575
3533
  if (args.prompt_type !== "review-edit-note") return true;
3576
3534
  if (args.input && args.input.trim()) {
3577
3535
  state.note = args.input.trim();
@@ -3585,8 +3543,45 @@ function on_review_edit_note_confirm(args: { prompt_type: string; input: string
3585
3543
  }
3586
3544
  }
3587
3545
  return true;
3546
+ });
3547
+ editor.on("prompt_confirmed", (args) => {
3548
+ if (args.prompt_type !== "review-delete-comment-confirm") return true;
3549
+ const response = args.input.trim().toLowerCase();
3550
+ if ((response === "delete" || args.selected_index === 0) && pendingDeleteCommentId) {
3551
+ if (pendingDeleteCommentId === '__note__') {
3552
+ state.note = '';
3553
+ } else {
3554
+ state.comments = state.comments.filter(c => c.id !== pendingDeleteCommentId);
3555
+ }
3556
+ persistReview();
3557
+ updateMagitDisplay();
3558
+ editor.setStatus("Deleted");
3559
+ } else {
3560
+ editor.setStatus("Delete cancelled");
3561
+ }
3562
+ pendingDeleteCommentId = null;
3563
+ return false;
3564
+ });
3565
+ editor.on("prompt_cancelled", (args) => {
3566
+ if (args.prompt_type === "review-comment") {
3567
+ pendingCommentInfo = null;
3568
+ editingCommentId = null;
3569
+ editor.setStatus(editor.t("status.comment_cancelled"));
3570
+ }
3571
+ return true;
3572
+ });
3573
+
3574
+ async function review_edit_note() {
3575
+ const label = editor.t("prompt.overall_comment") || "Note: ";
3576
+ if (state.note) {
3577
+ editor.startPromptWithInitial(label, "review-edit-note", state.note);
3578
+ } else {
3579
+ editor.startPrompt(label, "review-edit-note");
3580
+ }
3588
3581
  }
3589
- registerHandler("on_review_edit_note_confirm", on_review_edit_note_confirm);
3582
+ registerHandler("review_edit_note", review_edit_note);
3583
+
3584
+
3590
3585
 
3591
3586
  async function review_export_session() {
3592
3587
  const cwd = editor.getCwd();
@@ -3736,15 +3731,15 @@ async function openReviewPanels(groupName: string): Promise<boolean> {
3736
3731
 
3737
3732
  updateMagitDisplay();
3738
3733
 
3739
- editor.focusBufferGroupPanel(state.groupId, "diff");
3734
+ editor.focusBufferGroupPanel(state.groupId!, "diff");
3740
3735
 
3741
- editor.on("resize", "onReviewDiffResize");
3736
+ editor.on("resize", onReviewDiffResize);
3742
3737
  updateReviewStatus();
3743
- editor.on("buffer_activated", "on_review_buffer_activated");
3744
- editor.on("buffer_closed", "on_review_buffer_closed");
3745
- editor.on("cursor_moved", "on_review_cursor_moved");
3746
- editor.on("viewport_changed", "on_review_viewport_changed");
3747
- editor.on("mouse_click", "on_review_mouse_click");
3738
+ editor.on("buffer_activated", on_review_buffer_activated);
3739
+ editor.on("buffer_closed", on_review_buffer_closed);
3740
+ editor.on("cursor_moved", on_review_cursor_moved);
3741
+ editor.on("viewport_changed", on_review_viewport_changed);
3742
+ editor.on("mouse_click", on_review_mouse_click);
3748
3743
  return true;
3749
3744
  }
3750
3745
 
@@ -3820,12 +3815,12 @@ function stop_review_diff() {
3820
3815
  }
3821
3816
  state.reviewBufferId = null;
3822
3817
  editor.setContext("review-mode", false);
3823
- editor.off("resize", "onReviewDiffResize");
3824
- editor.off("buffer_activated", "on_review_buffer_activated");
3825
- editor.off("buffer_closed", "on_review_buffer_closed");
3826
- editor.off("cursor_moved", "on_review_cursor_moved");
3827
- editor.off("viewport_changed", "on_review_viewport_changed");
3828
- editor.off("mouse_click", "on_review_mouse_click");
3818
+ editor.off("resize", onReviewDiffResize);
3819
+ editor.off("buffer_activated", on_review_buffer_activated);
3820
+ editor.off("buffer_closed", on_review_buffer_closed);
3821
+ editor.off("cursor_moved", on_review_cursor_moved);
3822
+ editor.off("viewport_changed", on_review_viewport_changed);
3823
+ editor.off("mouse_click", on_review_mouse_click);
3829
3824
  editor.setStatus(editor.t("status.stopped"));
3830
3825
  }
3831
3826
  registerHandler("stop_review_diff", stop_review_diff);
@@ -3968,7 +3963,8 @@ async function start_review_range(): Promise<void> {
3968
3963
  }
3969
3964
  registerHandler("start_review_range", start_review_range);
3970
3965
 
3971
- function on_review_range_confirm(args: { prompt_type: string; input: string }): boolean {
3966
+
3967
+ editor.on("prompt_confirmed", (args) => {
3972
3968
  if (args.prompt_type !== "review-range") return true;
3973
3969
  const range = parseRangeInput(args.input);
3974
3970
  if (!range) {
@@ -3979,9 +3975,7 @@ function on_review_range_confirm(args: { prompt_type: string; input: string }):
3979
3975
  // can return immediately.
3980
3976
  bootstrapRangeReview(range);
3981
3977
  return true;
3982
- }
3983
- registerHandler("on_review_range_confirm", on_review_range_confirm);
3984
- editor.on("prompt_confirmed", "on_review_range_confirm");
3978
+ });
3985
3979
 
3986
3980
  async function bootstrapRangeReview(range: ReviewRange): Promise<void> {
3987
3981
  editor.setStatus(editor.t("status.generating") || "Generating diff…");
@@ -4607,7 +4601,7 @@ async function start_review_branch(): Promise<void> {
4607
4601
  if (branchState.groupId !== null) {
4608
4602
  editor.focusBufferGroupPanel(branchState.groupId, "log");
4609
4603
  }
4610
- editor.on("cursor_moved", "on_review_branch_cursor_moved");
4604
+ editor.on("cursor_moved", on_review_branch_cursor_moved);
4611
4605
 
4612
4606
  editor.setStatus(
4613
4607
  editor.t("status.review_branch_ready", {
@@ -4621,7 +4615,7 @@ registerHandler("start_review_branch", start_review_branch);
4621
4615
  function stop_review_branch(): void {
4622
4616
  if (!branchState.isOpen) return;
4623
4617
  if (branchState.groupId !== null) editor.closeBufferGroup(branchState.groupId);
4624
- editor.off("cursor_moved", "on_review_branch_cursor_moved");
4618
+ editor.off("cursor_moved", on_review_branch_cursor_moved);
4625
4619
  branchState.isOpen = false;
4626
4620
  branchState.groupId = null;
4627
4621
  branchState.logBufferId = null;
@@ -4717,7 +4711,9 @@ editor.registerCommand("%cmd.export_markdown", "%cmd.export_markdown_desc", "rev
4717
4711
  editor.registerCommand("%cmd.export_json", "%cmd.export_json_desc", "review_export_json", "review-mode");
4718
4712
 
4719
4713
  // Handler for when buffers are closed - cleans up scroll sync groups and composite buffers
4720
- function on_buffer_closed(data: any) {
4714
+
4715
+
4716
+ editor.on("buffer_closed", (data) => {
4721
4717
  // If one of the diff view buffers is closed, clean up the scroll sync group
4722
4718
  if (activeSideBySideState) {
4723
4719
  if (data.buffer_id === activeSideBySideState.oldBufferId ||
@@ -4744,10 +4740,7 @@ function on_buffer_closed(data: any) {
4744
4740
  activeCompositeDiffState = null;
4745
4741
  }
4746
4742
  }
4747
- }
4748
- registerHandler("on_buffer_closed", on_buffer_closed);
4749
-
4750
- editor.on("buffer_closed", "on_buffer_closed");
4743
+ });
4751
4744
 
4752
4745
  editor.defineMode("review-mode", [
4753
4746
  // Native cursor motion in the unified diff stream.
@@ -49,7 +49,10 @@ let bashLspError: { serverCommand: string; message: string } | null = null;
49
49
  /**
50
50
  * Handle LSP server errors for Bash
51
51
  */
52
- function on_bash_lsp_server_error(data: LspServerErrorData): void {
52
+
53
+
54
+ // Register hook for LSP server errors
55
+ editor.on("lsp_server_error", (data) => {
53
56
  // Only handle Bash language errors
54
57
  if (data.language !== "bash") {
55
58
  return;
@@ -71,18 +74,15 @@ function on_bash_lsp_server_error(data: LspServerErrorData): void {
71
74
  } else {
72
75
  editor.setStatus(`Bash LSP error: ${data.message}`);
73
76
  }
74
- }
75
- registerHandler("on_bash_lsp_server_error", on_bash_lsp_server_error);
76
-
77
- // Register hook for LSP server errors
78
- editor.on("lsp_server_error", "on_bash_lsp_server_error");
77
+ });
79
78
 
80
79
  /**
81
80
  * Handle status bar click when there's a Bash LSP error
82
81
  */
83
- function on_bash_lsp_status_clicked(
84
- data: LspStatusClickedData
85
- ): void {
82
+
83
+
84
+ // Register hook for status bar clicks
85
+ editor.on("lsp_status_clicked", (data) => {
86
86
  // Only handle Bash language clicks when there's an error
87
87
  if (data.language !== "bash" || !bashLspError) {
88
88
  return;
@@ -103,18 +103,15 @@ function on_bash_lsp_status_clicked(
103
103
  { id: "dismiss", label: "Dismiss (ESC)" },
104
104
  ],
105
105
  });
106
- }
107
- registerHandler("on_bash_lsp_status_clicked", on_bash_lsp_status_clicked);
108
-
109
- // Register hook for status bar clicks
110
- editor.on("lsp_status_clicked", "on_bash_lsp_status_clicked");
106
+ });
111
107
 
112
108
  /**
113
109
  * Handle action popup results for Bash LSP help
114
110
  */
115
- function on_bash_lsp_action_result(
116
- data: ActionPopupResultData
117
- ): void {
111
+
112
+
113
+ // Register hook for action popup results
114
+ editor.on("action_popup_result", (data) => {
118
115
  // Only handle our popup
119
116
  if (data.popup_id !== "bash-lsp-help") {
120
117
  return;
@@ -152,10 +149,6 @@ function on_bash_lsp_action_result(
152
149
  default:
153
150
  editor.debug(`bash-lsp: Unknown action: ${data.action_id}`);
154
151
  }
155
- }
156
- registerHandler("on_bash_lsp_action_result", on_bash_lsp_action_result);
157
-
158
- // Register hook for action popup results
159
- editor.on("action_popup_result", "on_bash_lsp_action_result");
152
+ });
160
153
 
161
154
  editor.debug("bash-lsp: Plugin loaded");
@@ -54,9 +54,10 @@ let clangdLspError: {
54
54
  /**
55
55
  * Handle LSP server errors for C/C++
56
56
  */
57
- function on_clangd_lsp_server_error(
58
- data: LspServerErrorData
59
- ): void {
57
+
58
+
59
+ // Register hook for LSP server errors
60
+ editor.on("lsp_server_error", (data) => {
60
61
  // Only handle C/C++ language errors
61
62
  if (!HANDLED_LANGUAGES.includes(data.language)) {
62
63
  return;
@@ -79,18 +80,15 @@ function on_clangd_lsp_server_error(
79
80
  } else {
80
81
  editor.setStatus(`C/C++ LSP error: ${data.message}`);
81
82
  }
82
- }
83
- registerHandler("on_clangd_lsp_server_error", on_clangd_lsp_server_error);
84
-
85
- // Register hook for LSP server errors
86
- editor.on("lsp_server_error", "on_clangd_lsp_server_error");
83
+ });
87
84
 
88
85
  /**
89
86
  * Handle status bar click when there's a C/C++ LSP error
90
87
  */
91
- function on_clangd_lsp_status_clicked(
92
- data: LspStatusClickedData
93
- ): void {
88
+
89
+
90
+ // Register hook for status bar clicks
91
+ editor.on("lsp_status_clicked", (data) => {
94
92
  // Only handle C/C++ language clicks when there's an error
95
93
  if (!HANDLED_LANGUAGES.includes(data.language) || !clangdLspError) {
96
94
  return;
@@ -111,18 +109,15 @@ function on_clangd_lsp_status_clicked(
111
109
  { id: "dismiss", label: "Dismiss (ESC)" },
112
110
  ],
113
111
  });
114
- }
115
- registerHandler("on_clangd_lsp_status_clicked", on_clangd_lsp_status_clicked);
116
-
117
- // Register hook for status bar clicks
118
- editor.on("lsp_status_clicked", "on_clangd_lsp_status_clicked");
112
+ });
119
113
 
120
114
  /**
121
115
  * Handle action popup results for C/C++ LSP help
122
116
  */
123
- function on_clangd_lsp_action_result(
124
- data: ActionPopupResultData
125
- ): void {
117
+
118
+
119
+ // Register hook for action popup results
120
+ editor.on("action_popup_result", (data) => {
126
121
  // Only handle our popup
127
122
  if (data.popup_id !== "clangd-lsp-help") {
128
123
  return;
@@ -162,10 +157,6 @@ function on_clangd_lsp_action_result(
162
157
  default:
163
158
  editor.debug(`clangd-lsp: Unknown action: ${data.action_id}`);
164
159
  }
165
- }
166
- registerHandler("on_clangd_lsp_action_result", on_clangd_lsp_action_result);
167
-
168
- // Register hook for action popup results
169
- editor.on("action_popup_result", "on_clangd_lsp_action_result");
160
+ });
170
161
 
171
162
  editor.debug("clangd-lsp: Plugin loaded");
@@ -36,7 +36,8 @@ const INSTALL_COMMANDS = {
36
36
 
37
37
  let clojureLspError: { serverCommand: string; message: string } | null = null;
38
38
 
39
- function on_clojure_lsp_server_error(data: LspServerErrorData): void {
39
+
40
+ editor.on("lsp_server_error", (data) => {
40
41
  if (data.language !== "clojure") {
41
42
  return;
42
43
  }
@@ -55,11 +56,10 @@ function on_clojure_lsp_server_error(data: LspServerErrorData): void {
55
56
  } else {
56
57
  editor.setStatus(`Clojure LSP error: ${data.message}`);
57
58
  }
58
- }
59
- registerHandler("on_clojure_lsp_server_error", on_clojure_lsp_server_error);
60
- editor.on("lsp_server_error", "on_clojure_lsp_server_error");
59
+ });
60
+
61
61
 
62
- function on_clojure_lsp_status_clicked(data: LspStatusClickedData): void {
62
+ editor.on("lsp_status_clicked", (data) => {
63
63
  if (data.language !== "clojure" || !clojureLspError) {
64
64
  return;
65
65
  }
@@ -78,11 +78,10 @@ function on_clojure_lsp_status_clicked(data: LspStatusClickedData): void {
78
78
  { id: "dismiss", label: "Dismiss (ESC)" },
79
79
  ],
80
80
  });
81
- }
82
- registerHandler("on_clojure_lsp_status_clicked", on_clojure_lsp_status_clicked);
83
- editor.on("lsp_status_clicked", "on_clojure_lsp_status_clicked");
81
+ });
82
+
84
83
 
85
- function on_clojure_lsp_action_result(data: ActionPopupResultData): void {
84
+ editor.on("action_popup_result", (data) => {
86
85
  if (data.popup_id !== "clojure-lsp-help") {
87
86
  return;
88
87
  }
@@ -118,8 +117,6 @@ function on_clojure_lsp_action_result(data: ActionPopupResultData): void {
118
117
  default:
119
118
  editor.debug(`clojure-lsp: Unknown action: ${data.action_id}`);
120
119
  }
121
- }
122
- registerHandler("on_clojure_lsp_action_result", on_clojure_lsp_action_result);
123
- editor.on("action_popup_result", "on_clojure_lsp_action_result");
120
+ });
124
121
 
125
122
  editor.debug("clojure-lsp: Plugin loaded");