@fresh-editor/fresh-editor 0.2.11 → 0.2.13
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 +89 -0
- package/README.md +10 -0
- package/package.json +1 -1
- package/plugins/audit_mode.ts +79 -58
- package/plugins/check-types.sh +1 -0
- package/plugins/clangd-lsp.ts +9 -6
- package/plugins/clangd_support.ts +12 -8
- package/plugins/code-tour.ts +15 -10
- package/plugins/config-schema.json +70 -3
- package/plugins/csharp_support.ts +15 -10
- package/plugins/css-lsp.ts +9 -6
- package/plugins/diagnostics_panel.ts +25 -18
- package/plugins/examples/README.md +1 -2
- package/plugins/examples/async_demo.ts +28 -28
- package/plugins/examples/bookmarks.ts +34 -32
- package/plugins/examples/buffer_query_demo.ts +20 -20
- package/plugins/examples/hello_world.ts +46 -10
- package/plugins/examples/virtual_buffer_demo.ts +16 -12
- package/plugins/find_references.ts +7 -5
- package/plugins/git_blame.ts +13 -9
- package/plugins/git_explorer.ts +9 -6
- package/plugins/git_find_file.ts +7 -5
- package/plugins/git_grep.ts +3 -2
- package/plugins/git_gutter.ts +15 -10
- package/plugins/git_log.ts +27 -18
- package/plugins/go-lsp.ts +9 -6
- package/plugins/html-lsp.ts +9 -6
- package/plugins/java-lsp.ts +9 -6
- package/plugins/json-lsp.ts +9 -6
- package/plugins/latex-lsp.ts +9 -6
- package/plugins/lib/finder.ts +1 -0
- package/plugins/lib/fresh.d.ts +141 -16
- package/plugins/live_grep.ts +3 -2
- package/plugins/markdown_compose.ts +33 -23
- package/plugins/markdown_source.ts +24 -10
- package/plugins/marksman-lsp.ts +9 -6
- package/plugins/merge_conflict.ts +33 -22
- package/plugins/odin-lsp.ts +9 -6
- package/plugins/path_complete.ts +3 -2
- package/plugins/pkg.ts +70 -48
- package/plugins/python-lsp.ts +9 -6
- package/plugins/rust-lsp.ts +102 -6
- package/plugins/search_replace.ts +32 -21
- package/plugins/templ-lsp.ts +9 -6
- package/plugins/test_i18n.ts +3 -2
- package/plugins/theme_editor.i18n.json +28 -14
- package/plugins/theme_editor.ts +1230 -495
- package/plugins/typescript-lsp.ts +9 -6
- package/plugins/vi_mode.ts +487 -297
- package/plugins/welcome.ts +9 -6
- package/plugins/zig-lsp.ts +9 -6
package/plugins/lib/fresh.d.ts
CHANGED
|
@@ -16,6 +16,23 @@
|
|
|
16
16
|
* Plugins must call this at the top of their file to get a scoped editor object.
|
|
17
17
|
*/
|
|
18
18
|
declare function getEditor(): EditorAPI;
|
|
19
|
+
/**
|
|
20
|
+
* Register a function as a named handler on the global scope.
|
|
21
|
+
*
|
|
22
|
+
* Handler functions registered this way can be referenced by name in
|
|
23
|
+
* `editor.registerCommand()`, `editor.on()`, and mode keybindings.
|
|
24
|
+
*
|
|
25
|
+
* The `fn` parameter is typed as `Function` because the runtime passes
|
|
26
|
+
* different argument shapes depending on the caller: command handlers
|
|
27
|
+
* receive no arguments, event handlers receive an event-specific data
|
|
28
|
+
* object (e.g. `{ buffer_id: number }`), and prompt handlers receive
|
|
29
|
+
* `{ prompt_type: string, input: string }`. Type-annotate your handler
|
|
30
|
+
* parameters to match the event you are handling.
|
|
31
|
+
*
|
|
32
|
+
* @param name - Handler name (referenced by registerCommand, on, etc.)
|
|
33
|
+
* @param fn - The handler function
|
|
34
|
+
*/
|
|
35
|
+
declare function registerHandler(name: string, fn: Function): void;
|
|
19
36
|
/** Handle for a cancellable async operation */
|
|
20
37
|
interface ProcessHandle<T> extends PromiseLike<T> {
|
|
21
38
|
/** Promise that resolves to the result when complete */
|
|
@@ -36,6 +53,14 @@ type TextPropertyEntry = {
|
|
|
36
53
|
* Optional properties attached to this text (e.g., file path, line number)
|
|
37
54
|
*/
|
|
38
55
|
properties?: Record<string, unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* Optional whole-entry styling
|
|
58
|
+
*/
|
|
59
|
+
style?: Partial<OverlayOptions>;
|
|
60
|
+
/**
|
|
61
|
+
* Optional sub-range styling within this entry
|
|
62
|
+
*/
|
|
63
|
+
inlineOverlays?: Array<InlineOverlay>;
|
|
39
64
|
};
|
|
40
65
|
type TsCompositeLayoutConfig = {
|
|
41
66
|
/**
|
|
@@ -45,7 +70,7 @@ type TsCompositeLayoutConfig = {
|
|
|
45
70
|
/**
|
|
46
71
|
* Width ratios for side-by-side (e.g., [0.5, 0.5])
|
|
47
72
|
*/
|
|
48
|
-
ratios
|
|
73
|
+
ratios?: Array<number>;
|
|
49
74
|
/**
|
|
50
75
|
* Show separator between panes
|
|
51
76
|
*/
|
|
@@ -53,7 +78,7 @@ type TsCompositeLayoutConfig = {
|
|
|
53
78
|
/**
|
|
54
79
|
* Spacing for stacked layout
|
|
55
80
|
*/
|
|
56
|
-
spacing
|
|
81
|
+
spacing?: number;
|
|
57
82
|
};
|
|
58
83
|
type TsCompositeSourceConfig = {
|
|
59
84
|
/**
|
|
@@ -78,19 +103,19 @@ type TsCompositePaneStyle = {
|
|
|
78
103
|
* Background color for added lines (RGB)
|
|
79
104
|
* Using [u8; 3] instead of (u8, u8, u8) for better rquickjs_serde compatibility
|
|
80
105
|
*/
|
|
81
|
-
addBg
|
|
106
|
+
addBg?: [number, number, number];
|
|
82
107
|
/**
|
|
83
108
|
* Background color for removed lines (RGB)
|
|
84
109
|
*/
|
|
85
|
-
removeBg
|
|
110
|
+
removeBg?: [number, number, number];
|
|
86
111
|
/**
|
|
87
112
|
* Background color for modified lines (RGB)
|
|
88
113
|
*/
|
|
89
|
-
modifyBg
|
|
114
|
+
modifyBg?: [number, number, number];
|
|
90
115
|
/**
|
|
91
116
|
* Gutter style: "line-numbers", "diff-markers", "both", or "none"
|
|
92
117
|
*/
|
|
93
|
-
gutterStyle
|
|
118
|
+
gutterStyle?: string;
|
|
94
119
|
};
|
|
95
120
|
type TsCompositeHunk = {
|
|
96
121
|
/**
|
|
@@ -138,7 +163,7 @@ type ViewportInfo = {
|
|
|
138
163
|
*/
|
|
139
164
|
topByte: number;
|
|
140
165
|
/**
|
|
141
|
-
* Line number of the first visible line (
|
|
166
|
+
* Line number of the first visible line (None when line index unavailable, e.g. large file before scan)
|
|
142
167
|
*/
|
|
143
168
|
topLine: number | null;
|
|
144
169
|
/**
|
|
@@ -158,11 +183,11 @@ type LayoutHints = {
|
|
|
158
183
|
/**
|
|
159
184
|
* Optional compose width for centering/wrapping
|
|
160
185
|
*/
|
|
161
|
-
composeWidth
|
|
186
|
+
composeWidth?: number;
|
|
162
187
|
/**
|
|
163
188
|
* Optional column guides for aligned tables
|
|
164
189
|
*/
|
|
165
|
-
columnGuides
|
|
190
|
+
columnGuides?: Array<number>;
|
|
166
191
|
};
|
|
167
192
|
type ViewTokenWire = {
|
|
168
193
|
/**
|
|
@@ -386,6 +411,20 @@ type FormatterPackConfig = {
|
|
|
386
411
|
*/
|
|
387
412
|
args: Array<string>;
|
|
388
413
|
};
|
|
414
|
+
type ProcessLimitsPackConfig = {
|
|
415
|
+
/**
|
|
416
|
+
* Maximum memory usage as percentage of total system memory (null = no limit)
|
|
417
|
+
*/
|
|
418
|
+
maxMemoryPercent: number | null;
|
|
419
|
+
/**
|
|
420
|
+
* Maximum CPU usage as percentage of total CPU (null = no limit)
|
|
421
|
+
*/
|
|
422
|
+
maxCpuPercent: number | null;
|
|
423
|
+
/**
|
|
424
|
+
* Enable resource limiting
|
|
425
|
+
*/
|
|
426
|
+
enabled: boolean | null;
|
|
427
|
+
};
|
|
389
428
|
type TerminalResult = {
|
|
390
429
|
/**
|
|
391
430
|
* The created buffer ID (for use with setSplitBuffer, etc.)
|
|
@@ -431,6 +470,61 @@ type CursorInfo = {
|
|
|
431
470
|
end: number;
|
|
432
471
|
} | null;
|
|
433
472
|
};
|
|
473
|
+
type OverlayOptions = {
|
|
474
|
+
/**
|
|
475
|
+
* Foreground color - RGB array or theme key string
|
|
476
|
+
*/
|
|
477
|
+
fg?: OverlayColorSpec | null;
|
|
478
|
+
/**
|
|
479
|
+
* Background color - RGB array or theme key string
|
|
480
|
+
*/
|
|
481
|
+
bg?: OverlayColorSpec | null;
|
|
482
|
+
/**
|
|
483
|
+
* Whether to render with underline
|
|
484
|
+
*/
|
|
485
|
+
underline: boolean;
|
|
486
|
+
/**
|
|
487
|
+
* Whether to render in bold
|
|
488
|
+
*/
|
|
489
|
+
bold: boolean;
|
|
490
|
+
/**
|
|
491
|
+
* Whether to render in italic
|
|
492
|
+
*/
|
|
493
|
+
italic: boolean;
|
|
494
|
+
/**
|
|
495
|
+
* Whether to render with strikethrough
|
|
496
|
+
*/
|
|
497
|
+
strikethrough: boolean;
|
|
498
|
+
/**
|
|
499
|
+
* Whether to extend background color to end of line
|
|
500
|
+
*/
|
|
501
|
+
extendToLineEnd: boolean;
|
|
502
|
+
/**
|
|
503
|
+
* Optional URL for OSC 8 terminal hyperlinks.
|
|
504
|
+
* When set, the overlay text becomes a clickable hyperlink in terminals
|
|
505
|
+
* that support OSC 8 escape sequences.
|
|
506
|
+
*/
|
|
507
|
+
url?: string | null;
|
|
508
|
+
};
|
|
509
|
+
type OverlayColorSpec = [number, number, number] | string;
|
|
510
|
+
type InlineOverlay = {
|
|
511
|
+
/**
|
|
512
|
+
* Start byte offset within the entry's text
|
|
513
|
+
*/
|
|
514
|
+
start: number;
|
|
515
|
+
/**
|
|
516
|
+
* End byte offset within the entry's text (exclusive)
|
|
517
|
+
*/
|
|
518
|
+
end: number;
|
|
519
|
+
/**
|
|
520
|
+
* Styling options for this range
|
|
521
|
+
*/
|
|
522
|
+
style: Partial<OverlayOptions>;
|
|
523
|
+
/**
|
|
524
|
+
* Optional properties for this sub-range (e.g., click target metadata)
|
|
525
|
+
*/
|
|
526
|
+
properties?: Record<string, any>;
|
|
527
|
+
};
|
|
434
528
|
type BackgroundProcessResult = {
|
|
435
529
|
/**
|
|
436
530
|
* Unique process ID for later reference
|
|
@@ -621,6 +715,10 @@ type LspServerPackConfig = {
|
|
|
621
715
|
* LSP initialization options
|
|
622
716
|
*/
|
|
623
717
|
initializationOptions: Record<string, unknown> | null;
|
|
718
|
+
/**
|
|
719
|
+
* Process resource limits (memory and CPU)
|
|
720
|
+
*/
|
|
721
|
+
processLimits: ProcessLimitsPackConfig | null;
|
|
624
722
|
};
|
|
625
723
|
type SpawnResult = {
|
|
626
724
|
/**
|
|
@@ -683,10 +781,16 @@ interface EditorAPI {
|
|
|
683
781
|
copyToClipboard(text: string): void;
|
|
684
782
|
setClipboard(text: string): void;
|
|
685
783
|
/**
|
|
686
|
-
* Register a command
|
|
687
|
-
*
|
|
784
|
+
* Register a command in the command palette (Ctrl+P).
|
|
785
|
+
*
|
|
786
|
+
* Usually you should omit `context` so the command is always visible.
|
|
787
|
+
* If provided, the command is **hidden** unless your plugin has activated
|
|
788
|
+
* that context with `editor.setContext(name, true)` or the focused buffer's
|
|
789
|
+
* virtual mode (from `defineMode()`) matches. This is for plugin-defined
|
|
790
|
+
* contexts only (e.g. `"tour-active"`, `"review-mode"`), not built-in
|
|
791
|
+
* editor modes.
|
|
688
792
|
*/
|
|
689
|
-
registerCommand(name: string, description: string, handlerName: string, context?:
|
|
793
|
+
registerCommand(name: string, description: string, handlerName: string, context?: string | null): boolean;
|
|
690
794
|
/**
|
|
691
795
|
* Unregister a command by name
|
|
692
796
|
*/
|
|
@@ -899,6 +1003,10 @@ interface EditorAPI {
|
|
|
899
1003
|
*/
|
|
900
1004
|
reloadThemes(): void;
|
|
901
1005
|
/**
|
|
1006
|
+
* Reload theme registry and apply a theme atomically
|
|
1007
|
+
*/
|
|
1008
|
+
reloadAndApplyTheme(themeName: string): void;
|
|
1009
|
+
/**
|
|
902
1010
|
* Register a TextMate grammar file for a language
|
|
903
1011
|
* The grammar will be pending until reload_grammars() is called
|
|
904
1012
|
*/
|
|
@@ -912,10 +1020,11 @@ interface EditorAPI {
|
|
|
912
1020
|
*/
|
|
913
1021
|
registerLspServer(language: string, config: LspServerPackConfig): boolean;
|
|
914
1022
|
/**
|
|
915
|
-
* Reload the grammar registry to apply registered grammars
|
|
916
|
-
* Call this after registering one or more grammars
|
|
1023
|
+
* Reload the grammar registry to apply registered grammars (async)
|
|
1024
|
+
* Call this after registering one or more grammars.
|
|
1025
|
+
* Returns a Promise that resolves when the grammar rebuild completes.
|
|
917
1026
|
*/
|
|
918
|
-
reloadGrammars(): void
|
|
1027
|
+
reloadGrammars(): Promise<void>;
|
|
919
1028
|
/**
|
|
920
1029
|
* Get config directory path
|
|
921
1030
|
*/
|
|
@@ -941,6 +1050,18 @@ interface EditorAPI {
|
|
|
941
1050
|
*/
|
|
942
1051
|
deleteTheme(name: string): boolean;
|
|
943
1052
|
/**
|
|
1053
|
+
* Get theme data (JSON) by name from the in-memory cache
|
|
1054
|
+
*/
|
|
1055
|
+
getThemeData(name: string): unknown;
|
|
1056
|
+
/**
|
|
1057
|
+
* Save a theme file to the user themes directory, returns the saved path
|
|
1058
|
+
*/
|
|
1059
|
+
saveThemeFile(name: string, content: string): string;
|
|
1060
|
+
/**
|
|
1061
|
+
* Check if a user theme file exists
|
|
1062
|
+
*/
|
|
1063
|
+
themeFileExists(name: string): boolean;
|
|
1064
|
+
/**
|
|
944
1065
|
* Get file stat information
|
|
945
1066
|
*/
|
|
946
1067
|
fileStat(path: string): unknown;
|
|
@@ -1167,7 +1288,7 @@ interface EditorAPI {
|
|
|
1167
1288
|
*/
|
|
1168
1289
|
setLineIndicator(bufferId: number, line: number, namespace: string, symbol: string, r: number, g: number, b: number, priority: number): boolean;
|
|
1169
1290
|
/**
|
|
1170
|
-
* Batch set line indicators in the gutter
|
|
1291
|
+
* Batch set line indicators in the gutter
|
|
1171
1292
|
*/
|
|
1172
1293
|
setLineIndicators(bufferId: number, lines: number[], namespace: string, symbol: string, r: number, g: number, b: number, priority: number): boolean;
|
|
1173
1294
|
/**
|
|
@@ -1223,6 +1344,10 @@ interface EditorAPI {
|
|
|
1223
1344
|
*/
|
|
1224
1345
|
disableLspForLanguage(language: string): boolean;
|
|
1225
1346
|
/**
|
|
1347
|
+
* Restart LSP server for a specific language
|
|
1348
|
+
*/
|
|
1349
|
+
restartLspForLanguage(language: string): boolean;
|
|
1350
|
+
/**
|
|
1226
1351
|
* Set the workspace root URI for a specific language's LSP server
|
|
1227
1352
|
* This allows plugins to specify project roots (e.g., directory containing .csproj)
|
|
1228
1353
|
*/
|
package/plugins/live_grep.ts
CHANGED
|
@@ -75,7 +75,7 @@ async function searchWithRipgrep(query: string): Promise<GrepMatch[]> {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// Start live grep
|
|
78
|
-
|
|
78
|
+
function start_live_grep() : void {
|
|
79
79
|
finder.prompt({
|
|
80
80
|
title: editor.t("prompt.live_grep"),
|
|
81
81
|
source: {
|
|
@@ -85,7 +85,8 @@ globalThis.start_live_grep = function (): void {
|
|
|
85
85
|
minQueryLength: 2,
|
|
86
86
|
},
|
|
87
87
|
});
|
|
88
|
-
}
|
|
88
|
+
}
|
|
89
|
+
registerHandler("start_live_grep", start_live_grep);
|
|
89
90
|
|
|
90
91
|
// Register command
|
|
91
92
|
editor.registerCommand(
|
|
@@ -390,7 +390,7 @@ function enableMarkdownCompose(bufferId: number): void {
|
|
|
390
390
|
editor.setLineWrap(bufferId, null, true);
|
|
391
391
|
|
|
392
392
|
// Set layout hints for centered margins
|
|
393
|
-
editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth });
|
|
393
|
+
editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth ?? undefined });
|
|
394
394
|
|
|
395
395
|
// Trigger a refresh so lines_changed hooks fire for visible content
|
|
396
396
|
editor.refreshLines(bufferId);
|
|
@@ -421,7 +421,7 @@ function disableMarkdownCompose(bufferId: number): void {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
// Toggle markdown compose mode for current buffer
|
|
424
|
-
|
|
424
|
+
function markdownToggleCompose() : void {
|
|
425
425
|
const bufferId = editor.getActiveBufferId();
|
|
426
426
|
const info = editor.getBufferInfo(bufferId);
|
|
427
427
|
|
|
@@ -442,7 +442,8 @@ globalThis.markdownToggleCompose = function(): void {
|
|
|
442
442
|
editor.refreshLines(bufferId);
|
|
443
443
|
editor.setStatus(editor.t("status.compose_on"));
|
|
444
444
|
}
|
|
445
|
-
}
|
|
445
|
+
}
|
|
446
|
+
registerHandler("markdownToggleCompose", markdownToggleCompose);
|
|
446
447
|
|
|
447
448
|
/**
|
|
448
449
|
* Extract text content from incoming tokens
|
|
@@ -1360,7 +1361,7 @@ function processTableAlignment(
|
|
|
1360
1361
|
}
|
|
1361
1362
|
|
|
1362
1363
|
// lines_changed: called for newly visible or invalidated lines
|
|
1363
|
-
|
|
1364
|
+
function onMarkdownLinesChanged(data: {
|
|
1364
1365
|
buffer_id: number;
|
|
1365
1366
|
lines: Array<{
|
|
1366
1367
|
line_number: number;
|
|
@@ -1394,7 +1395,8 @@ globalThis.onMarkdownLinesChanged = function(data: {
|
|
|
1394
1395
|
if (tableWidthsGrew) {
|
|
1395
1396
|
editor.refreshLines(data.buffer_id);
|
|
1396
1397
|
}
|
|
1397
|
-
}
|
|
1398
|
+
}
|
|
1399
|
+
registerHandler("onMarkdownLinesChanged", onMarkdownLinesChanged);
|
|
1398
1400
|
|
|
1399
1401
|
// after_insert: no-op for conceals/overlays.
|
|
1400
1402
|
// The edit automatically invalidates seen_byte_ranges for affected lines,
|
|
@@ -1402,7 +1404,7 @@ globalThis.onMarkdownLinesChanged = function(data: {
|
|
|
1402
1404
|
// handles clearing and rebuilding atomically.
|
|
1403
1405
|
// Marker-based positions auto-adjust with buffer edits, so existing conceals
|
|
1404
1406
|
// remain visually correct until lines_changed rebuilds them.
|
|
1405
|
-
|
|
1407
|
+
function onMarkdownAfterInsert(data: {
|
|
1406
1408
|
buffer_id: number;
|
|
1407
1409
|
position: number;
|
|
1408
1410
|
text: string;
|
|
@@ -1411,10 +1413,11 @@ globalThis.onMarkdownAfterInsert = function(data: {
|
|
|
1411
1413
|
}): void {
|
|
1412
1414
|
if (!isComposingInAnySplit(data.buffer_id)) return;
|
|
1413
1415
|
editor.debug(`[mc] after_insert: pos=${data.position} text="${data.text.replace(/\n/g,'\\n')}" affected=${data.affected_start}..${data.affected_end}`);
|
|
1414
|
-
}
|
|
1416
|
+
}
|
|
1417
|
+
registerHandler("onMarkdownAfterInsert", onMarkdownAfterInsert);
|
|
1415
1418
|
|
|
1416
1419
|
// after_delete: no-op for conceals/overlays (same reasoning as after_insert).
|
|
1417
|
-
|
|
1420
|
+
function onMarkdownAfterDelete(data: {
|
|
1418
1421
|
buffer_id: number;
|
|
1419
1422
|
start: number;
|
|
1420
1423
|
end: number;
|
|
@@ -1424,10 +1427,11 @@ globalThis.onMarkdownAfterDelete = function(data: {
|
|
|
1424
1427
|
}): void {
|
|
1425
1428
|
if (!isComposingInAnySplit(data.buffer_id)) return;
|
|
1426
1429
|
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}`);
|
|
1427
|
-
}
|
|
1430
|
+
}
|
|
1431
|
+
registerHandler("onMarkdownAfterDelete", onMarkdownAfterDelete);
|
|
1428
1432
|
|
|
1429
1433
|
// cursor_moved: update cursor-aware reveal/conceal for old and new cursor lines
|
|
1430
|
-
|
|
1434
|
+
function onMarkdownCursorMoved(data: {
|
|
1431
1435
|
buffer_id: number;
|
|
1432
1436
|
cursor_id: number;
|
|
1433
1437
|
old_position: number;
|
|
@@ -1445,7 +1449,8 @@ globalThis.onMarkdownCursorMoved = function(data: {
|
|
|
1445
1449
|
// auto-expose is span-level (cursor entering/leaving an emphasis or link
|
|
1446
1450
|
// span within the same line must toggle its syntax markers).
|
|
1447
1451
|
editor.refreshLines(data.buffer_id);
|
|
1448
|
-
}
|
|
1452
|
+
}
|
|
1453
|
+
registerHandler("onMarkdownCursorMoved", onMarkdownCursorMoved);
|
|
1449
1454
|
|
|
1450
1455
|
// view_transform_request is no longer needed — soft wrapping is handled by
|
|
1451
1456
|
// marker-based soft breaks (computed in lines_changed), and layout hints
|
|
@@ -1453,12 +1458,13 @@ globalThis.onMarkdownCursorMoved = function(data: {
|
|
|
1453
1458
|
// caused by the async view_transform round-trip.
|
|
1454
1459
|
|
|
1455
1460
|
// Handle buffer close events - clean up compose mode tracking
|
|
1456
|
-
|
|
1461
|
+
function onMarkdownBufferClosed(data: { buffer_id: number }) : void {
|
|
1457
1462
|
// View state is cleaned up automatically when the buffer is removed from keyed_states
|
|
1458
|
-
}
|
|
1463
|
+
}
|
|
1464
|
+
registerHandler("onMarkdownBufferClosed", onMarkdownBufferClosed);
|
|
1459
1465
|
|
|
1460
1466
|
// viewport_changed: recalculate table column widths on terminal resize
|
|
1461
|
-
|
|
1467
|
+
function onMarkdownViewportChanged(data: {
|
|
1462
1468
|
split_id: number;
|
|
1463
1469
|
buffer_id: number;
|
|
1464
1470
|
top_byte: number;
|
|
@@ -1485,12 +1491,13 @@ globalThis.onMarkdownViewportChanged = function(data: {
|
|
|
1485
1491
|
setTableWidths(data.buffer_id, bufWidths);
|
|
1486
1492
|
}
|
|
1487
1493
|
editor.refreshLines(data.buffer_id);
|
|
1488
|
-
}
|
|
1494
|
+
}
|
|
1495
|
+
registerHandler("onMarkdownViewportChanged", onMarkdownViewportChanged);
|
|
1489
1496
|
|
|
1490
1497
|
// Re-enable compose mode for buffers restored from a saved session.
|
|
1491
1498
|
// The Rust side restores ViewMode::Compose and compose_width, but the plugin
|
|
1492
1499
|
// needs to re-apply line numbers, line wrap, and layout hints when activated.
|
|
1493
|
-
|
|
1500
|
+
function onMarkdownBufferActivated(data: { buffer_id: number }) : void {
|
|
1494
1501
|
const bufferId = data.buffer_id;
|
|
1495
1502
|
|
|
1496
1503
|
const info = editor.getBufferInfo(bufferId);
|
|
@@ -1505,7 +1512,8 @@ globalThis.onMarkdownBufferActivated = function(data: { buffer_id: number }): vo
|
|
|
1505
1512
|
}
|
|
1506
1513
|
enableMarkdownCompose(bufferId);
|
|
1507
1514
|
}
|
|
1508
|
-
}
|
|
1515
|
+
}
|
|
1516
|
+
registerHandler("onMarkdownBufferActivated", onMarkdownBufferActivated);
|
|
1509
1517
|
|
|
1510
1518
|
// Register hooks
|
|
1511
1519
|
editor.on("lines_changed", "onMarkdownLinesChanged");
|
|
@@ -1519,7 +1527,7 @@ editor.on("prompt_confirmed", "onMarkdownComposeWidthConfirmed");
|
|
|
1519
1527
|
editor.on("buffer_activated", "onMarkdownBufferActivated");
|
|
1520
1528
|
|
|
1521
1529
|
// Set compose width command - starts interactive prompt
|
|
1522
|
-
|
|
1530
|
+
function markdownSetComposeWidth() : void {
|
|
1523
1531
|
const currentValue = config.composeWidth === null ? "None" : String(config.composeWidth);
|
|
1524
1532
|
editor.startPromptWithInitial(editor.t("prompt.compose_width"), "markdown-compose-width", currentValue);
|
|
1525
1533
|
editor.setPromptInputSync(true);
|
|
@@ -1527,10 +1535,11 @@ globalThis.markdownSetComposeWidth = function(): void {
|
|
|
1527
1535
|
{ text: "None", description: editor.t("suggestion.none") },
|
|
1528
1536
|
{ text: "120", description: editor.t("suggestion.default") },
|
|
1529
1537
|
]);
|
|
1530
|
-
}
|
|
1538
|
+
}
|
|
1539
|
+
registerHandler("markdownSetComposeWidth", markdownSetComposeWidth);
|
|
1531
1540
|
|
|
1532
1541
|
// Handle compose width prompt confirmation
|
|
1533
|
-
|
|
1542
|
+
function onMarkdownComposeWidthConfirmed(args: {
|
|
1534
1543
|
prompt_type: string;
|
|
1535
1544
|
input: string;
|
|
1536
1545
|
}): void {
|
|
@@ -1543,7 +1552,7 @@ globalThis.onMarkdownComposeWidthConfirmed = function(args: {
|
|
|
1543
1552
|
|
|
1544
1553
|
const bufferId = editor.getActiveBufferId();
|
|
1545
1554
|
if (isComposing(bufferId)) {
|
|
1546
|
-
editor.setLayoutHints(bufferId, null, {
|
|
1555
|
+
editor.setLayoutHints(bufferId, null, {});
|
|
1547
1556
|
editor.refreshLines(bufferId);
|
|
1548
1557
|
}
|
|
1549
1558
|
return;
|
|
@@ -1557,13 +1566,14 @@ globalThis.onMarkdownComposeWidthConfirmed = function(args: {
|
|
|
1557
1566
|
// Re-process active buffer if in compose mode
|
|
1558
1567
|
const bufferId = editor.getActiveBufferId();
|
|
1559
1568
|
if (isComposing(bufferId)) {
|
|
1560
|
-
editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth });
|
|
1569
|
+
editor.setLayoutHints(bufferId, null, { composeWidth: config.composeWidth ?? undefined });
|
|
1561
1570
|
editor.refreshLines(bufferId); // Trigger soft break recomputation
|
|
1562
1571
|
}
|
|
1563
1572
|
} else {
|
|
1564
1573
|
editor.setStatus(editor.t("status.invalid_width"));
|
|
1565
1574
|
}
|
|
1566
|
-
}
|
|
1575
|
+
}
|
|
1576
|
+
registerHandler("onMarkdownComposeWidthConfirmed", onMarkdownComposeWidthConfirmed);
|
|
1567
1577
|
|
|
1568
1578
|
// Register commands
|
|
1569
1579
|
editor.registerCommand(
|
|
@@ -148,13 +148,22 @@ async function readRestOfLine(bufferId: number, cursorPos: number): Promise<stri
|
|
|
148
148
|
// Enter handler: auto-continue list items or match indentation
|
|
149
149
|
// ---------------------------------------------------------------------------
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
async function md_src_enter() : Promise<void> {
|
|
152
152
|
const bufferId = editor.getActiveBufferId();
|
|
153
153
|
if (!bufferId) {
|
|
154
154
|
editor.executeAction("insert_newline");
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// When multiple cursors are active, fall back to the built-in insert_newline
|
|
159
|
+
// action which correctly handles all cursors atomically. The markdown-specific
|
|
160
|
+
// list continuation logic below only operates on the primary cursor.
|
|
161
|
+
const allCursors = editor.getAllCursors();
|
|
162
|
+
if (allCursors.length > 1) {
|
|
163
|
+
editor.executeAction("insert_newline");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
158
167
|
const cursorPos = editor.getCursorPosition();
|
|
159
168
|
|
|
160
169
|
// Read a window of text before the cursor to find the current line.
|
|
@@ -194,13 +203,14 @@ globalThis.md_src_enter = async function (): Promise<void> {
|
|
|
194
203
|
}
|
|
195
204
|
}
|
|
196
205
|
editor.insertAtCursor("\n" + indent);
|
|
197
|
-
}
|
|
206
|
+
}
|
|
207
|
+
registerHandler("md_src_enter", md_src_enter);
|
|
198
208
|
|
|
199
209
|
// ---------------------------------------------------------------------------
|
|
200
210
|
// Tab handler: indent + cycle bullet on blank list items, else insert spaces
|
|
201
211
|
// ---------------------------------------------------------------------------
|
|
202
212
|
|
|
203
|
-
|
|
213
|
+
async function md_src_tab() : Promise<void> {
|
|
204
214
|
const bufferId = editor.getActiveBufferId();
|
|
205
215
|
if (!bufferId) {
|
|
206
216
|
editor.insertAtCursor(" ".repeat(TAB_SIZE));
|
|
@@ -240,13 +250,14 @@ globalThis.md_src_tab = async function (): Promise<void> {
|
|
|
240
250
|
|
|
241
251
|
// Default: insert spaces
|
|
242
252
|
editor.insertAtCursor(" ".repeat(TAB_SIZE));
|
|
243
|
-
}
|
|
253
|
+
}
|
|
254
|
+
registerHandler("md_src_tab", md_src_tab);
|
|
244
255
|
|
|
245
256
|
// ---------------------------------------------------------------------------
|
|
246
257
|
// Shift+Tab handler: de-indent + reverse-cycle bullet on blank list items
|
|
247
258
|
// ---------------------------------------------------------------------------
|
|
248
259
|
|
|
249
|
-
|
|
260
|
+
async function md_src_shift_tab() : Promise<void> {
|
|
250
261
|
const bufferId = editor.getActiveBufferId();
|
|
251
262
|
if (!bufferId) {
|
|
252
263
|
editor.executeAction("dedent_selection");
|
|
@@ -291,7 +302,8 @@ globalThis.md_src_shift_tab = async function (): Promise<void> {
|
|
|
291
302
|
|
|
292
303
|
// Default: fall through to built-in dedent
|
|
293
304
|
editor.executeAction("dedent_selection");
|
|
294
|
-
}
|
|
305
|
+
}
|
|
306
|
+
registerHandler("md_src_shift_tab", md_src_shift_tab);
|
|
295
307
|
|
|
296
308
|
// ---------------------------------------------------------------------------
|
|
297
309
|
// Mode definition
|
|
@@ -331,13 +343,15 @@ function updateMarkdownMode(): void {
|
|
|
331
343
|
}
|
|
332
344
|
}
|
|
333
345
|
|
|
334
|
-
|
|
346
|
+
function md_src_on_buffer_activated() : void {
|
|
335
347
|
updateMarkdownMode();
|
|
336
|
-
}
|
|
348
|
+
}
|
|
349
|
+
registerHandler("md_src_on_buffer_activated", md_src_on_buffer_activated);
|
|
337
350
|
|
|
338
|
-
|
|
351
|
+
function md_src_on_language_changed() : void {
|
|
339
352
|
updateMarkdownMode();
|
|
340
|
-
}
|
|
353
|
+
}
|
|
354
|
+
registerHandler("md_src_on_language_changed", md_src_on_language_changed);
|
|
341
355
|
|
|
342
356
|
editor.on("buffer_activated", "md_src_on_buffer_activated");
|
|
343
357
|
editor.on("language_changed", "md_src_on_language_changed");
|
package/plugins/marksman-lsp.ts
CHANGED
|
@@ -22,7 +22,7 @@ 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
|
-
|
|
25
|
+
function on_markdown_lsp_server_error(data: LspServerErrorData) : void {
|
|
26
26
|
if (data.language !== "markdown") return;
|
|
27
27
|
markdownLspError = { serverCommand: data.server_command, message: data.message };
|
|
28
28
|
if (data.error_type === "not_found") {
|
|
@@ -30,10 +30,11 @@ globalThis.on_markdown_lsp_server_error = function (data: LspServerErrorData): v
|
|
|
30
30
|
} else {
|
|
31
31
|
editor.setStatus(`Markdown LSP error: ${data.message}`);
|
|
32
32
|
}
|
|
33
|
-
}
|
|
33
|
+
}
|
|
34
|
+
registerHandler("on_markdown_lsp_server_error", on_markdown_lsp_server_error);
|
|
34
35
|
editor.on("lsp_server_error", "on_markdown_lsp_server_error");
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
function on_markdown_lsp_status_clicked(data: LspStatusClickedData) : void {
|
|
37
38
|
if (data.language !== "markdown" || !markdownLspError) return;
|
|
38
39
|
editor.showActionPopup({
|
|
39
40
|
id: "marksman-lsp-help",
|
|
@@ -45,10 +46,11 @@ globalThis.on_markdown_lsp_status_clicked = function (data: LspStatusClickedData
|
|
|
45
46
|
{ id: "dismiss", label: "Dismiss (ESC)" },
|
|
46
47
|
],
|
|
47
48
|
});
|
|
48
|
-
}
|
|
49
|
+
}
|
|
50
|
+
registerHandler("on_markdown_lsp_status_clicked", on_markdown_lsp_status_clicked);
|
|
49
51
|
editor.on("lsp_status_clicked", "on_markdown_lsp_status_clicked");
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
function on_markdown_lsp_action_result(data: ActionPopupResultData) : void {
|
|
52
54
|
if (data.popup_id !== "marksman-lsp-help") return;
|
|
53
55
|
switch (data.action_id) {
|
|
54
56
|
case "copy_url":
|
|
@@ -61,5 +63,6 @@ globalThis.on_markdown_lsp_action_result = function (data: ActionPopupResultData
|
|
|
61
63
|
markdownLspError = null;
|
|
62
64
|
break;
|
|
63
65
|
}
|
|
64
|
-
}
|
|
66
|
+
}
|
|
67
|
+
registerHandler("on_markdown_lsp_action_result", on_markdown_lsp_action_result);
|
|
65
68
|
editor.on("action_popup_result", "on_markdown_lsp_action_result");
|