@fresh-editor/fresh-editor 0.2.25 → 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 (82) hide show
  1. package/CHANGELOG.md +216 -0
  2. package/README.md +6 -0
  3. package/package.json +1 -1
  4. package/plugins/astro-lsp.ts +6 -12
  5. package/plugins/audit_mode.i18n.json +14 -14
  6. package/plugins/audit_mode.ts +182 -146
  7. package/plugins/bash-lsp.ts +15 -22
  8. package/plugins/clangd-lsp.ts +15 -24
  9. package/plugins/clojure-lsp.ts +9 -12
  10. package/plugins/cmake-lsp.ts +9 -12
  11. package/plugins/code-tour.ts +15 -16
  12. package/plugins/config-schema.json +79 -6
  13. package/plugins/csharp_support.ts +25 -30
  14. package/plugins/css-lsp.ts +15 -22
  15. package/plugins/dart-lsp.ts +9 -12
  16. package/plugins/dashboard.ts +1903 -0
  17. package/plugins/devcontainer.i18n.json +1472 -0
  18. package/plugins/devcontainer.ts +2793 -0
  19. package/plugins/diagnostics_panel.ts +10 -17
  20. package/plugins/elixir-lsp.ts +9 -12
  21. package/plugins/erlang-lsp.ts +9 -12
  22. package/plugins/examples/bookmarks.ts +10 -16
  23. package/plugins/find_references.ts +5 -9
  24. package/plugins/flash.ts +577 -0
  25. package/plugins/fsharp-lsp.ts +9 -12
  26. package/plugins/git_explorer.ts +16 -20
  27. package/plugins/git_gutter.ts +65 -79
  28. package/plugins/git_log.i18n.json +14 -42
  29. package/plugins/git_log.ts +19 -9
  30. package/plugins/gleam-lsp.ts +9 -12
  31. package/plugins/go-lsp.ts +15 -22
  32. package/plugins/graphql-lsp.ts +9 -12
  33. package/plugins/haskell-lsp.ts +9 -12
  34. package/plugins/html-lsp.ts +15 -24
  35. package/plugins/java-lsp.ts +9 -12
  36. package/plugins/json-lsp.ts +15 -24
  37. package/plugins/julia-lsp.ts +9 -12
  38. package/plugins/kotlin-lsp.ts +15 -22
  39. package/plugins/latex-lsp.ts +9 -12
  40. package/plugins/lib/fresh.d.ts +603 -0
  41. package/plugins/lua-lsp.ts +15 -22
  42. package/plugins/markdown_compose.ts +132 -128
  43. package/plugins/markdown_source.ts +8 -10
  44. package/plugins/marksman-lsp.ts +9 -12
  45. package/plugins/merge_conflict.ts +15 -17
  46. package/plugins/nim-lsp.ts +9 -12
  47. package/plugins/nix-lsp.ts +9 -12
  48. package/plugins/nushell-lsp.ts +9 -12
  49. package/plugins/ocaml-lsp.ts +9 -12
  50. package/plugins/odin-lsp.ts +15 -22
  51. package/plugins/path_complete.ts +5 -6
  52. package/plugins/perl-lsp.ts +9 -12
  53. package/plugins/php-lsp.ts +15 -22
  54. package/plugins/pkg.ts +10 -21
  55. package/plugins/protobuf-lsp.ts +9 -12
  56. package/plugins/python-lsp.ts +15 -24
  57. package/plugins/r-lsp.ts +9 -12
  58. package/plugins/ruby-lsp.ts +15 -22
  59. package/plugins/rust-lsp.ts +18 -28
  60. package/plugins/scala-lsp.ts +9 -12
  61. package/plugins/schemas/theme.schema.json +126 -0
  62. package/plugins/search_replace.ts +10 -13
  63. package/plugins/solidity-lsp.ts +9 -12
  64. package/plugins/sql-lsp.ts +9 -12
  65. package/plugins/svelte-lsp.ts +9 -12
  66. package/plugins/swift-lsp.ts +9 -12
  67. package/plugins/tailwindcss-lsp.ts +9 -12
  68. package/plugins/templ-lsp.ts +9 -12
  69. package/plugins/terraform-lsp.ts +9 -12
  70. package/plugins/theme_editor.i18n.json +98 -14
  71. package/plugins/theme_editor.ts +156 -209
  72. package/plugins/toml-lsp.ts +15 -22
  73. package/plugins/tsconfig.json +100 -0
  74. package/plugins/typescript-lsp.ts +15 -24
  75. package/plugins/typst-lsp.ts +15 -22
  76. package/plugins/vi_mode.ts +77 -290
  77. package/plugins/vue-lsp.ts +9 -12
  78. package/plugins/yaml-lsp.ts +15 -22
  79. package/plugins/zig-lsp.ts +9 -12
  80. package/themes/high-contrast.json +2 -2
  81. package/themes/nord.json +4 -0
  82. package/themes/solarized-dark.json +4 -0
@@ -49,7 +49,10 @@ let luaLspError: { serverCommand: string; message: string } | null = null;
49
49
  /**
50
50
  * Handle LSP server errors for Lua
51
51
  */
52
- function on_lua_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 Lua language errors
54
57
  if (data.language !== "lua") {
55
58
  return;
@@ -71,18 +74,15 @@ function on_lua_lsp_server_error(data: LspServerErrorData): void {
71
74
  } else {
72
75
  editor.setStatus(`Lua LSP error: ${data.message}`);
73
76
  }
74
- }
75
- registerHandler("on_lua_lsp_server_error", on_lua_lsp_server_error);
76
-
77
- // Register hook for LSP server errors
78
- editor.on("lsp_server_error", "on_lua_lsp_server_error");
77
+ });
79
78
 
80
79
  /**
81
80
  * Handle status bar click when there's a Lua LSP error
82
81
  */
83
- function on_lua_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 Lua language clicks when there's an error
87
87
  if (data.language !== "lua" || !luaLspError) {
88
88
  return;
@@ -103,18 +103,15 @@ function on_lua_lsp_status_clicked(
103
103
  { id: "dismiss", label: "Dismiss (ESC)" },
104
104
  ],
105
105
  });
106
- }
107
- registerHandler("on_lua_lsp_status_clicked", on_lua_lsp_status_clicked);
108
-
109
- // Register hook for status bar clicks
110
- editor.on("lsp_status_clicked", "on_lua_lsp_status_clicked");
106
+ });
111
107
 
112
108
  /**
113
109
  * Handle action popup results for Lua LSP help
114
110
  */
115
- function on_lua_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 !== "lua-lsp-help") {
120
117
  return;
@@ -152,10 +149,6 @@ function on_lua_lsp_action_result(
152
149
  default:
153
150
  editor.debug(`lua-lsp: Unknown action: ${data.action_id}`);
154
151
  }
155
- }
156
- registerHandler("on_lua_lsp_action_result", on_lua_lsp_action_result);
157
-
158
- // Register hook for action popup results
159
- editor.on("action_popup_result", "on_lua_lsp_action_result");
152
+ });
160
153
 
161
154
  editor.debug("lua-lsp: Plugin loaded");
@@ -966,6 +966,23 @@ function concealedText(text: string): string {
966
966
 
967
967
  const MIN_COL_W = 3;
968
968
 
969
+ /**
970
+ * Return the effective compose width for layout: the configured compose
971
+ * width clamped to the available viewport width.
972
+ *
973
+ * When `config.composeWidth` is explicitly set (e.g. 80) but the editor
974
+ * content area is smaller (e.g. after the File Explorer sidebar opens),
975
+ * using the configured value verbatim overflows the viewport. The Rust
976
+ * render layer already clamps the compose area the same way in
977
+ * `calculate_compose_layout`; plugin-side computations (table column
978
+ * allocation, soft-wrap width) need to match.
979
+ */
980
+ function effectiveComposeWidth(viewportWidth: number): number {
981
+ const cw = config.composeWidth;
982
+ if (cw == null) return viewportWidth;
983
+ return Math.min(cw, viewportWidth);
984
+ }
985
+
969
986
  /**
970
987
  * W3C-inspired column width distribution.
971
988
  * Constrains columns to fit within `available` width, distributing space
@@ -1142,6 +1159,26 @@ function processLineConceals(
1142
1159
  if (lineContent[i] === '|') pipePositions.push(i);
1143
1160
  }
1144
1161
 
1162
+ // Precompute which cells will be truncated. Per-character conceals
1163
+ // that land inside a truncated cell must be suppressed — the cell-
1164
+ // wide truncate conceal already renders the replacement. When both
1165
+ // fire, the per-char conceal at the cell's first byte emits its
1166
+ // replacement, and the cell-wide conceal emits its replacement one
1167
+ // byte later, producing a cell one character wider than allocated.
1168
+ const truncatedCellCharRanges: Array<{start: number; end: number}> = [];
1169
+ if (!cursorStrictlyOnLine && colWidths) {
1170
+ for (let ci = 0; ci < Math.min(cells.length, colWidths.length); ci++) {
1171
+ const cellText = concealedText(cells[ci]);
1172
+ if (cellText.length > colWidths[ci]) {
1173
+ const prevPipe = pipePositions[ci];
1174
+ const nextPipe = pipePositions[ci + 1];
1175
+ if (prevPipe !== undefined && nextPipe !== undefined) {
1176
+ truncatedCellCharRanges.push({ start: prevPipe + 1, end: nextPipe });
1177
+ }
1178
+ }
1179
+ }
1180
+ }
1181
+
1145
1182
  // Track which pipe index we're on (0 = leading pipe)
1146
1183
  let pipeIdx = 0;
1147
1184
  for (let i = 0; i < lineContent.length; i++) {
@@ -1161,11 +1198,15 @@ function processLineConceals(
1161
1198
  const allocatedWidth = colWidths[cellIdx];
1162
1199
 
1163
1200
  if (cellWidth > allocatedWidth) {
1164
- // Truncate: conceal entire cell content and replace with truncated text
1201
+ // Truncate: conceal entire cell content and replace with truncated text.
1202
+ // Separator rows use box-drawing ─ to match the non-truncated path
1203
+ // (per-char conceals replace source `-` with ─ and pad via pipe replacement).
1165
1204
  const prevPipeCharPos = pipePositions[pipeIdx - 1];
1166
1205
  const cellByteStart = charToByte(lineContent, prevPipeCharPos + 1, byteStart);
1167
1206
  const cellByteEnd = pipeByte;
1168
- const truncated = cellText.slice(0, allocatedWidth - 1) + '-';
1207
+ const truncated = isSeparator
1208
+ ? '─'.repeat(allocatedWidth)
1209
+ : cellText.slice(0, allocatedWidth - 1) + '-';
1169
1210
  editor.addConceal(bufferId, "md-syntax", cellByteStart, cellByteEnd, truncated);
1170
1211
  truncatedByteRanges.push({start: cellByteStart, end: cellByteEnd});
1171
1212
  } else {
@@ -1188,6 +1229,10 @@ function processLineConceals(
1188
1229
  }
1189
1230
  pipeIdx++;
1190
1231
  } else if (isSeparator && lineContent[i] === '-') {
1232
+ // Skip per-character conceals that land inside a truncated cell;
1233
+ // the cell-wide truncate conceal already handles the rendering.
1234
+ const inTruncated = truncatedCellCharRanges.some(r => i >= r.start && i < r.end);
1235
+ if (inTruncated) continue;
1191
1236
  const db = charToByte(lineContent, i, byteStart);
1192
1237
  editor.addConceal(bufferId, "md-syntax", db, charToByte(lineContent, i + 1, byteStart), "─");
1193
1238
  }
@@ -1292,7 +1337,7 @@ function processLineSoftBreaks(
1292
1337
 
1293
1338
  const viewport = editor.getViewport();
1294
1339
  if (!viewport) return;
1295
- const width = config.composeWidth ?? viewport.width;
1340
+ const width = effectiveComposeWidth(viewport.width);
1296
1341
 
1297
1342
  // Parse this single line to get block structure
1298
1343
  const blocks = parseMarkdownBlocks(lineContent);
@@ -1516,9 +1561,12 @@ function processTableAlignment(
1516
1561
  mergeWith(widthMap.get(ln)!.maxW);
1517
1562
  }
1518
1563
 
1519
- // Compute allocated widths constrained to viewport
1564
+ // Compute allocated widths constrained to viewport. Clamp the
1565
+ // configured compose width to the actual viewport — otherwise a
1566
+ // large configured width overflows when the editor area shrinks
1567
+ // (e.g. when the File Explorer sidebar opens).
1520
1568
  const viewport = editor.getViewport();
1521
- const composeW = config.composeWidth ?? (viewport ? viewport.width : 80);
1569
+ const composeW = effectiveComposeWidth(viewport ? viewport.width : 80);
1522
1570
  const numCols = merged.length;
1523
1571
  const available = composeW - (numCols + 1); // subtract pipe/box-drawing characters
1524
1572
  const allocated = distributeColumnWidths(merged, available);
@@ -1562,15 +1610,40 @@ function processTableAlignment(
1562
1610
  }
1563
1611
 
1564
1612
  // lines_changed: called for newly visible or invalidated lines
1565
- function onMarkdownLinesChanged(data: {
1566
- buffer_id: number;
1567
- lines: Array<{
1568
- line_number: number;
1569
- byte_start: number;
1570
- byte_end: number;
1571
- content: string;
1572
- }>;
1573
- }): void {
1613
+
1614
+
1615
+ // after_insert: no-op for conceals/overlays.
1616
+ // The edit automatically invalidates seen_byte_ranges for affected lines,
1617
+ // causing lines_changed to fire on the next render. processLineConceals
1618
+ // handles clearing and rebuilding atomically.
1619
+ // Marker-based positions auto-adjust with buffer edits, so existing conceals
1620
+ // remain visually correct until lines_changed rebuilds them.
1621
+
1622
+
1623
+ // after_delete: no-op for conceals/overlays (same reasoning as after_insert).
1624
+
1625
+
1626
+ // cursor_moved: update cursor-aware reveal/conceal for old and new cursor lines
1627
+
1628
+
1629
+ // view_transform_request is no longer needed — soft wrapping is handled by
1630
+ // marker-based soft breaks (computed in lines_changed), and layout hints
1631
+ // are set directly via setLayoutHints. This eliminates the one-frame flicker
1632
+ // caused by the async view_transform round-trip.
1633
+
1634
+ // Handle buffer close events - clean up compose mode tracking
1635
+
1636
+
1637
+ // viewport_changed: recalculate table column widths on terminal resize
1638
+
1639
+
1640
+ // Re-enable compose mode for buffers restored from a saved session.
1641
+ // The Rust side restores ViewMode::Compose and compose_width, but the plugin
1642
+ // needs to re-apply line numbers, line wrap, and layout hints when activated.
1643
+
1644
+
1645
+ // Register hooks
1646
+ editor.on("lines_changed", (data) => {
1574
1647
  if (!isComposingInAnySplit(data.buffer_id)) return;
1575
1648
  const lineNums = data.lines.map(l => `${l.line_number}(${l.byte_start}..${l.byte_end})`).join(', ');
1576
1649
  editor.debug(`[mc] lines_changed: ${data.lines.length} lines: [${lineNums}]`);
@@ -1605,49 +1678,16 @@ function onMarkdownLinesChanged(data: {
1605
1678
  if (tableWidthsGrew) {
1606
1679
  editor.refreshLines(data.buffer_id);
1607
1680
  }
1608
- }
1609
- registerHandler("onMarkdownLinesChanged", onMarkdownLinesChanged);
1610
-
1611
- // after_insert: no-op for conceals/overlays.
1612
- // The edit automatically invalidates seen_byte_ranges for affected lines,
1613
- // causing lines_changed to fire on the next render. processLineConceals
1614
- // handles clearing and rebuilding atomically.
1615
- // Marker-based positions auto-adjust with buffer edits, so existing conceals
1616
- // remain visually correct until lines_changed rebuilds them.
1617
- function onMarkdownAfterInsert(data: {
1618
- buffer_id: number;
1619
- position: number;
1620
- text: string;
1621
- affected_start: number;
1622
- affected_end: number;
1623
- }): void {
1681
+ });
1682
+ editor.on("after_insert", (data) => {
1624
1683
  if (!isComposingInAnySplit(data.buffer_id)) return;
1625
1684
  editor.debug(`[mc] after_insert: pos=${data.position} text="${data.text.replace(/\n/g,'\\n')}" affected=${data.affected_start}..${data.affected_end}`);
1626
- }
1627
- registerHandler("onMarkdownAfterInsert", onMarkdownAfterInsert);
1628
-
1629
- // after_delete: no-op for conceals/overlays (same reasoning as after_insert).
1630
- function onMarkdownAfterDelete(data: {
1631
- buffer_id: number;
1632
- start: number;
1633
- end: number;
1634
- deleted_text: string;
1635
- affected_start: number;
1636
- deleted_len: number;
1637
- }): void {
1685
+ });
1686
+ editor.on("after_delete", (data) => {
1638
1687
  if (!isComposingInAnySplit(data.buffer_id)) return;
1639
1688
  editor.debug(`[mc] after_delete: start=${data.start} end=${data.end} deleted="${data.deleted_text.replace(/\n/g,'\\n')}" affected_start=${data.affected_start} deleted_len=${data.deleted_len}`);
1640
- }
1641
- registerHandler("onMarkdownAfterDelete", onMarkdownAfterDelete);
1642
-
1643
- // cursor_moved: update cursor-aware reveal/conceal for old and new cursor lines
1644
- function onMarkdownCursorMoved(data: {
1645
- buffer_id: number;
1646
- cursor_id: number;
1647
- old_position: number;
1648
- new_position: number;
1649
- line: number;
1650
- }): void {
1689
+ });
1690
+ editor.on("cursor_moved", (data) => {
1651
1691
  if (!isComposingInAnySplit(data.buffer_id)) return;
1652
1692
 
1653
1693
  const prevLine = editor.getViewState(data.buffer_id, "last-cursor-line") as number | undefined;
@@ -1659,28 +1699,12 @@ function onMarkdownCursorMoved(data: {
1659
1699
  // auto-expose is span-level (cursor entering/leaving an emphasis or link
1660
1700
  // span within the same line must toggle its syntax markers).
1661
1701
  editor.refreshLines(data.buffer_id);
1662
- }
1663
- registerHandler("onMarkdownCursorMoved", onMarkdownCursorMoved);
1664
-
1665
- // view_transform_request is no longer needed — soft wrapping is handled by
1666
- // marker-based soft breaks (computed in lines_changed), and layout hints
1667
- // are set directly via setLayoutHints. This eliminates the one-frame flicker
1668
- // caused by the async view_transform round-trip.
1669
-
1670
- // Handle buffer close events - clean up compose mode tracking
1671
- function onMarkdownBufferClosed(data: { buffer_id: number }) : void {
1702
+ });
1703
+ // view_transform_request hook no longer needed — wrapping is handled by soft breaks
1704
+ editor.on("buffer_closed", (data) => {
1672
1705
  // View state is cleaned up automatically when the buffer is removed from keyed_states
1673
- }
1674
- registerHandler("onMarkdownBufferClosed", onMarkdownBufferClosed);
1675
-
1676
- // viewport_changed: recalculate table column widths on terminal resize
1677
- function onMarkdownViewportChanged(data: {
1678
- split_id: number;
1679
- buffer_id: number;
1680
- top_byte: number;
1681
- width: number;
1682
- height: number;
1683
- }): void {
1706
+ });
1707
+ editor.on("viewport_changed", (data) => {
1684
1708
  if (!isComposingInAnySplit(data.buffer_id)) return;
1685
1709
  if (data.width === lastViewportWidth) return;
1686
1710
  lastViewportWidth = data.width;
@@ -1688,7 +1712,7 @@ function onMarkdownViewportChanged(data: {
1688
1712
  // Recompute allocated table column widths for new viewport width
1689
1713
  const bufWidths = getTableWidths(data.buffer_id);
1690
1714
  if (bufWidths) {
1691
- const composeW = config.composeWidth ?? data.width;
1715
+ const composeW = effectiveComposeWidth(data.width);
1692
1716
  const seen = new Set<string>(); // Track by JSON key to deduplicate shared TableWidthInfo
1693
1717
  for (const [lineNum, info] of bufWidths) {
1694
1718
  const key = info.maxW.join(",");
@@ -1701,13 +1725,39 @@ function onMarkdownViewportChanged(data: {
1701
1725
  setTableWidths(data.buffer_id, bufWidths);
1702
1726
  }
1703
1727
  editor.refreshLines(data.buffer_id);
1704
- }
1705
- registerHandler("onMarkdownViewportChanged", onMarkdownViewportChanged);
1728
+ });
1729
+ editor.on("prompt_confirmed", (args) => {
1730
+ if (args.prompt_type !== "markdown-compose-width") return;
1706
1731
 
1707
- // Re-enable compose mode for buffers restored from a saved session.
1708
- // The Rust side restores ViewMode::Compose and compose_width, but the plugin
1709
- // needs to re-apply line numbers, line wrap, and layout hints when activated.
1710
- function onMarkdownBufferActivated(data: { buffer_id: number }) : void {
1732
+ const input = args.input.trim();
1733
+ if (input.toLowerCase() === "none") {
1734
+ config.composeWidth = null;
1735
+ editor.setStatus(editor.t("status.width_none"));
1736
+
1737
+ const bufferId = editor.getActiveBufferId();
1738
+ if (isComposing(bufferId)) {
1739
+ editor.setLayoutHints(bufferId, null, {});
1740
+ editor.refreshLines(bufferId);
1741
+ }
1742
+ return;
1743
+ }
1744
+
1745
+ const width = parseInt(input, 10);
1746
+ if (!isNaN(width) && width > 20 && width < 300) {
1747
+ config.composeWidth = width;
1748
+ editor.setStatus(editor.t("status.width_set", { width: String(width) }));
1749
+
1750
+ // Re-process active buffer if in compose mode
1751
+ const bufferId = editor.getActiveBufferId();
1752
+ if (isComposing(bufferId)) {
1753
+ editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth ?? undefined });
1754
+ editor.refreshLines(bufferId); // Trigger soft break recomputation
1755
+ }
1756
+ } else {
1757
+ editor.setStatus(editor.t("status.invalid_width"));
1758
+ }
1759
+ });
1760
+ editor.on("buffer_activated", (data) => {
1711
1761
  const bufferId = data.buffer_id;
1712
1762
 
1713
1763
  const info = editor.getBufferInfo(bufferId);
@@ -1726,19 +1776,7 @@ function onMarkdownBufferActivated(data: { buffer_id: number }) : void {
1726
1776
  // markdown buffers that aren't already in compose mode.
1727
1777
  enableMarkdownCompose(bufferId);
1728
1778
  }
1729
- }
1730
- registerHandler("onMarkdownBufferActivated", onMarkdownBufferActivated);
1731
-
1732
- // Register hooks
1733
- editor.on("lines_changed", "onMarkdownLinesChanged");
1734
- editor.on("after_insert", "onMarkdownAfterInsert");
1735
- editor.on("after_delete", "onMarkdownAfterDelete");
1736
- editor.on("cursor_moved", "onMarkdownCursorMoved");
1737
- // view_transform_request hook no longer needed — wrapping is handled by soft breaks
1738
- editor.on("buffer_closed", "onMarkdownBufferClosed");
1739
- editor.on("viewport_changed", "onMarkdownViewportChanged");
1740
- editor.on("prompt_confirmed", "onMarkdownComposeWidthConfirmed");
1741
- editor.on("buffer_activated", "onMarkdownBufferActivated");
1779
+ });
1742
1780
 
1743
1781
  // Set compose width command - starts interactive prompt
1744
1782
  function markdownSetComposeWidth() : void {
@@ -1753,41 +1791,7 @@ function markdownSetComposeWidth() : void {
1753
1791
  registerHandler("markdownSetComposeWidth", markdownSetComposeWidth);
1754
1792
 
1755
1793
  // Handle compose width prompt confirmation
1756
- function onMarkdownComposeWidthConfirmed(args: {
1757
- prompt_type: string;
1758
- input: string;
1759
- }): void {
1760
- if (args.prompt_type !== "markdown-compose-width") return;
1761
-
1762
- const input = args.input.trim();
1763
- if (input.toLowerCase() === "none") {
1764
- config.composeWidth = null;
1765
- editor.setStatus(editor.t("status.width_none"));
1766
-
1767
- const bufferId = editor.getActiveBufferId();
1768
- if (isComposing(bufferId)) {
1769
- editor.setLayoutHints(bufferId, null, {});
1770
- editor.refreshLines(bufferId);
1771
- }
1772
- return;
1773
- }
1774
1794
 
1775
- const width = parseInt(input, 10);
1776
- if (!isNaN(width) && width > 20 && width < 300) {
1777
- config.composeWidth = width;
1778
- editor.setStatus(editor.t("status.width_set", { width: String(width) }));
1779
-
1780
- // Re-process active buffer if in compose mode
1781
- const bufferId = editor.getActiveBufferId();
1782
- if (isComposing(bufferId)) {
1783
- editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth ?? undefined });
1784
- editor.refreshLines(bufferId); // Trigger soft break recomputation
1785
- }
1786
- } else {
1787
- editor.setStatus(editor.t("status.invalid_width"));
1788
- }
1789
- }
1790
- registerHandler("onMarkdownComposeWidthConfirmed", onMarkdownComposeWidthConfirmed);
1791
1795
 
1792
1796
  // Register commands
1793
1797
  editor.registerCommand(
@@ -343,17 +343,15 @@ function updateMarkdownMode(): void {
343
343
  }
344
344
  }
345
345
 
346
- function md_src_on_buffer_activated() : void {
347
- updateMarkdownMode();
348
- }
349
- registerHandler("md_src_on_buffer_activated", md_src_on_buffer_activated);
350
346
 
351
- function md_src_on_language_changed() : void {
352
- updateMarkdownMode();
353
- }
354
- registerHandler("md_src_on_language_changed", md_src_on_language_changed);
355
347
 
356
- editor.on("buffer_activated", "md_src_on_buffer_activated");
357
- editor.on("language_changed", "md_src_on_language_changed");
348
+
349
+
350
+ editor.on("buffer_activated", () => {
351
+ updateMarkdownMode();
352
+ });
353
+ editor.on("language_changed", () => {
354
+ updateMarkdownMode();
355
+ });
358
356
 
359
357
  editor.debug("markdown_source plugin loaded");
@@ -22,7 +22,8 @@ interface ActionPopupResultData {
22
22
  const INSTALL_URL = "https://github.com/artempyanykh/marksman#how-to-install";
23
23
  let markdownLspError: { serverCommand: string; message: string } | null = null;
24
24
 
25
- function on_markdown_lsp_server_error(data: LspServerErrorData) : void {
25
+
26
+ editor.on("lsp_server_error", (data) => {
26
27
  if (data.language !== "markdown") return;
27
28
  markdownLspError = { serverCommand: data.server_command, message: data.message };
28
29
  if (data.error_type === "not_found") {
@@ -30,11 +31,10 @@ function on_markdown_lsp_server_error(data: LspServerErrorData) : void {
30
31
  } else {
31
32
  editor.setStatus(`Markdown LSP error: ${data.message}`);
32
33
  }
33
- }
34
- registerHandler("on_markdown_lsp_server_error", on_markdown_lsp_server_error);
35
- editor.on("lsp_server_error", "on_markdown_lsp_server_error");
34
+ });
35
+
36
36
 
37
- function on_markdown_lsp_status_clicked(data: LspStatusClickedData) : void {
37
+ editor.on("lsp_status_clicked", (data) => {
38
38
  if (data.language !== "markdown" || !markdownLspError) return;
39
39
  editor.showActionPopup({
40
40
  id: "marksman-lsp-help",
@@ -46,11 +46,10 @@ function on_markdown_lsp_status_clicked(data: LspStatusClickedData) : void {
46
46
  { id: "dismiss", label: "Dismiss (ESC)" },
47
47
  ],
48
48
  });
49
- }
50
- registerHandler("on_markdown_lsp_status_clicked", on_markdown_lsp_status_clicked);
51
- editor.on("lsp_status_clicked", "on_markdown_lsp_status_clicked");
49
+ });
50
+
52
51
 
53
- function on_markdown_lsp_action_result(data: ActionPopupResultData) : void {
52
+ editor.on("action_popup_result", (data) => {
54
53
  if (data.popup_id !== "marksman-lsp-help") return;
55
54
  switch (data.action_id) {
56
55
  case "copy_url":
@@ -63,6 +62,4 @@ function on_markdown_lsp_action_result(data: ActionPopupResultData) : void {
63
62
  markdownLspError = null;
64
63
  break;
65
64
  }
66
- }
67
- registerHandler("on_markdown_lsp_action_result", on_markdown_lsp_action_result);
68
- editor.on("action_popup_result", "on_markdown_lsp_action_result");
65
+ });
@@ -1700,7 +1700,18 @@ registerHandler("merge_show_help", merge_show_help);
1700
1700
  /**
1701
1701
  * Handle buffer activation - check for conflict markers
1702
1702
  */
1703
- async function onMergeBufferActivated(data: { buffer_id: number }) : Promise<void> {
1703
+
1704
+
1705
+ /**
1706
+ * Handle file open - check for conflict markers
1707
+ */
1708
+
1709
+
1710
+ // =============================================================================
1711
+ // Hook Registration
1712
+ // =============================================================================
1713
+
1714
+ editor.on("buffer_activated", async (data) => {
1704
1715
  // Don't trigger if already in merge mode
1705
1716
  if (mergeState.isActive) return;
1706
1717
 
@@ -1724,13 +1735,8 @@ async function onMergeBufferActivated(data: { buffer_id: number }) : Promise<voi
1724
1735
  } catch (e) {
1725
1736
  // Not in git repo or other error, ignore
1726
1737
  }
1727
- }
1728
- registerHandler("onMergeBufferActivated", onMergeBufferActivated);
1729
-
1730
- /**
1731
- * Handle file open - check for conflict markers
1732
- */
1733
- async function onMergeAfterFileOpen(data: { buffer_id: number; path: string }) : Promise<void> {
1738
+ });
1739
+ editor.on("after_file_open", async (data) => {
1734
1740
  // Don't trigger if already in merge mode
1735
1741
  if (mergeState.isActive) return;
1736
1742
 
@@ -1750,15 +1756,7 @@ async function onMergeAfterFileOpen(data: { buffer_id: number; path: string }) :
1750
1756
  } catch (e) {
1751
1757
  // Not in git repo or other error, ignore
1752
1758
  }
1753
- }
1754
- registerHandler("onMergeAfterFileOpen", onMergeAfterFileOpen);
1755
-
1756
- // =============================================================================
1757
- // Hook Registration
1758
- // =============================================================================
1759
-
1760
- editor.on("buffer_activated", "onMergeBufferActivated");
1761
- editor.on("after_file_open", "onMergeAfterFileOpen");
1759
+ });
1762
1760
 
1763
1761
  // =============================================================================
1764
1762
  // Command Registration - Dynamic based on merge mode state
@@ -35,7 +35,8 @@ const INSTALL_COMMANDS = {
35
35
 
36
36
  let nimLspError: { serverCommand: string; message: string } | null = null;
37
37
 
38
- function on_nim_lsp_server_error(data: LspServerErrorData): void {
38
+
39
+ editor.on("lsp_server_error", (data) => {
39
40
  if (data.language !== "nim") {
40
41
  return;
41
42
  }
@@ -54,11 +55,10 @@ function on_nim_lsp_server_error(data: LspServerErrorData): void {
54
55
  } else {
55
56
  editor.setStatus(`Nim LSP error: ${data.message}`);
56
57
  }
57
- }
58
- registerHandler("on_nim_lsp_server_error", on_nim_lsp_server_error);
59
- editor.on("lsp_server_error", "on_nim_lsp_server_error");
58
+ });
59
+
60
60
 
61
- function on_nim_lsp_status_clicked(data: LspStatusClickedData): void {
61
+ editor.on("lsp_status_clicked", (data) => {
62
62
  if (data.language !== "nim" || !nimLspError) {
63
63
  return;
64
64
  }
@@ -76,11 +76,10 @@ function on_nim_lsp_status_clicked(data: LspStatusClickedData): void {
76
76
  { id: "dismiss", label: "Dismiss (ESC)" },
77
77
  ],
78
78
  });
79
- }
80
- registerHandler("on_nim_lsp_status_clicked", on_nim_lsp_status_clicked);
81
- editor.on("lsp_status_clicked", "on_nim_lsp_status_clicked");
79
+ });
80
+
82
81
 
83
- function on_nim_lsp_action_result(data: ActionPopupResultData): void {
82
+ editor.on("action_popup_result", (data) => {
84
83
  if (data.popup_id !== "nim-lsp-help") {
85
84
  return;
86
85
  }
@@ -111,8 +110,6 @@ function on_nim_lsp_action_result(data: ActionPopupResultData): void {
111
110
  default:
112
111
  editor.debug(`nim-lsp: Unknown action: ${data.action_id}`);
113
112
  }
114
- }
115
- registerHandler("on_nim_lsp_action_result", on_nim_lsp_action_result);
116
- editor.on("action_popup_result", "on_nim_lsp_action_result");
113
+ });
117
114
 
118
115
  editor.debug("nim-lsp: Plugin loaded");
@@ -36,7 +36,8 @@ const INSTALL_COMMANDS = {
36
36
 
37
37
  let nixLspError: { serverCommand: string; message: string } | null = null;
38
38
 
39
- function on_nix_lsp_server_error(data: LspServerErrorData): void {
39
+
40
+ editor.on("lsp_server_error", (data) => {
40
41
  if (data.language !== "nix") {
41
42
  return;
42
43
  }
@@ -55,11 +56,10 @@ function on_nix_lsp_server_error(data: LspServerErrorData): void {
55
56
  } else {
56
57
  editor.setStatus(`Nix LSP error: ${data.message}`);
57
58
  }
58
- }
59
- registerHandler("on_nix_lsp_server_error", on_nix_lsp_server_error);
60
- editor.on("lsp_server_error", "on_nix_lsp_server_error");
59
+ });
60
+
61
61
 
62
- function on_nix_lsp_status_clicked(data: LspStatusClickedData): void {
62
+ editor.on("lsp_status_clicked", (data) => {
63
63
  if (data.language !== "nix" || !nixLspError) {
64
64
  return;
65
65
  }
@@ -78,11 +78,10 @@ function on_nix_lsp_status_clicked(data: LspStatusClickedData): void {
78
78
  { id: "dismiss", label: "Dismiss (ESC)" },
79
79
  ],
80
80
  });
81
- }
82
- registerHandler("on_nix_lsp_status_clicked", on_nix_lsp_status_clicked);
83
- editor.on("lsp_status_clicked", "on_nix_lsp_status_clicked");
81
+ });
82
+
84
83
 
85
- function on_nix_lsp_action_result(data: ActionPopupResultData): void {
84
+ editor.on("action_popup_result", (data) => {
86
85
  if (data.popup_id !== "nix-lsp-help") {
87
86
  return;
88
87
  }
@@ -118,8 +117,6 @@ function on_nix_lsp_action_result(data: ActionPopupResultData): void {
118
117
  default:
119
118
  editor.debug(`nix-lsp: Unknown action: ${data.action_id}`);
120
119
  }
121
- }
122
- registerHandler("on_nix_lsp_action_result", on_nix_lsp_action_result);
123
- editor.on("action_popup_result", "on_nix_lsp_action_result");
120
+ });
124
121
 
125
122
  editor.debug("nix-lsp: Plugin loaded");