@fresh-editor/fresh-editor 0.3.9 → 0.3.10
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 +28 -0
- package/package.json +1 -1
- package/plugins/audit_mode.ts +35 -1
- package/plugins/config-schema.json +9 -3
- package/plugins/diagnostics_panel.ts +10 -0
- package/plugins/env-manager.i18n.json +338 -0
- package/plugins/env-manager.ts +23 -33
- package/plugins/lib/finder.ts +44 -7
- package/plugins/lib/fresh.d.ts +55 -37
- package/plugins/live_diff.ts +12 -1
- package/plugins/live_grep.ts +66 -2
- package/plugins/markdown_compose.ts +3 -1
- package/plugins/orchestrator.ts +327 -172
- package/plugins/schemas/theme.schema.json +15 -2
- package/plugins/search_replace.ts +70 -28
- package/plugins/theme_editor.i18n.json +28 -0
package/plugins/lib/finder.ts
CHANGED
|
@@ -116,9 +116,37 @@ export interface FinderConfig<T> {
|
|
|
116
116
|
/** Panel-specific: navigate source split when cursor moves (preview without focus change) */
|
|
117
117
|
navigateOnCursorMove?: boolean;
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Panel-specific: whether selecting an entry (Enter) closes the
|
|
121
|
+
* panel. Defaults to `true` — the panel dismisses as it jumps, which
|
|
122
|
+
* suits one-shot lists like Find References. Set `false` to keep the
|
|
123
|
+
* panel docked so the user can step through entries (Vim quickfix /
|
|
124
|
+
* VS Code results list). Only consulted by the default selection
|
|
125
|
+
* handler — a custom `onSelect` owns close behaviour itself.
|
|
126
|
+
*
|
|
127
|
+
* Note: leaving the panel open while `navigateOnCursorMove` is also
|
|
128
|
+
* on re-introduces the focus race the close was guarding against
|
|
129
|
+
* (a `focusSplit` queued after the jump can re-grab the panel), so
|
|
130
|
+
* the two shouldn't be combined without care.
|
|
131
|
+
*/
|
|
132
|
+
closeOnSelect?: boolean;
|
|
133
|
+
|
|
119
134
|
/** Called when the panel or prompt is closed (e.g. via Escape) */
|
|
120
135
|
onClose?: () => void;
|
|
121
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Panel-specific: extra key bindings active while the panel buffer
|
|
139
|
+
* is focused, as `[key, command]` pairs (e.g. `["q", "my_close"]`).
|
|
140
|
+
* `command` is dispatched as a plugin action, so it should name a
|
|
141
|
+
* handler registered via `registerHandler`. These augment the
|
|
142
|
+
* built-in `Return` (select) and `Escape` (close) bindings.
|
|
143
|
+
*
|
|
144
|
+
* Without this, keys a plugin advertises in its panel UI (e.g.
|
|
145
|
+
* "q: close") fall through to the read-only text layer and trip
|
|
146
|
+
* "Editing disabled in this buffer" (issue #2125).
|
|
147
|
+
*/
|
|
148
|
+
panelKeys?: Array<[string, string]>;
|
|
149
|
+
|
|
122
150
|
/**
|
|
123
151
|
* When true, panels created by this Finder are routed into the
|
|
124
152
|
* shared Utility Dock (issue #1796 / Section 2 of
|
|
@@ -1111,12 +1139,17 @@ export class Finder<T> {
|
|
|
1111
1139
|
private registerPanelHandlers(): void {
|
|
1112
1140
|
const self = this;
|
|
1113
1141
|
|
|
1114
|
-
// Define panel mode
|
|
1142
|
+
// Define panel mode. The built-in Return/Escape bindings are
|
|
1143
|
+
// augmented with any caller-supplied `panelKeys` so plugin-specific
|
|
1144
|
+
// shortcuts (e.g. Diagnostics' "q: close | a: toggle filter") resolve
|
|
1145
|
+
// to their handlers instead of falling through to the read-only text
|
|
1146
|
+
// layer (issue #2125).
|
|
1115
1147
|
this.editor.defineMode(
|
|
1116
1148
|
this.modeName,
|
|
1117
1149
|
[
|
|
1118
1150
|
["Return", `${this.handlerPrefix}_panel_select`],
|
|
1119
1151
|
["Escape", `${this.handlerPrefix}_panel_close`],
|
|
1152
|
+
...(this.config.panelKeys ?? []),
|
|
1120
1153
|
],
|
|
1121
1154
|
true
|
|
1122
1155
|
);
|
|
@@ -1398,13 +1431,17 @@ export class Finder<T> {
|
|
|
1398
1431
|
} else if (entry.location) {
|
|
1399
1432
|
const loc = entry.location;
|
|
1400
1433
|
|
|
1401
|
-
// Close the panel first
|
|
1402
|
-
//
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1405
|
-
|
|
1434
|
+
// Close the panel first (unless the consumer opted to keep it
|
|
1435
|
+
// docked via `closeOnSelect: false`). Closing is the default
|
|
1436
|
+
// because navigateOnCursorMove's focusSplit(panelSplitId) can
|
|
1437
|
+
// interfere with the jump — it queues a FocusSplit that runs after
|
|
1438
|
+
// OpenFileInSplit and restores the panel as the active split.
|
|
1439
|
+
if (this.config.closeOnSelect !== false) {
|
|
1440
|
+
this.closePanel();
|
|
1441
|
+
}
|
|
1406
1442
|
|
|
1407
|
-
//
|
|
1443
|
+
// openFile routes away from the dock leaf, so with the panel kept
|
|
1444
|
+
// open the file lands in the editor pane and the list stays put.
|
|
1408
1445
|
this.editor.openFile(loc.file, loc.line, loc.column);
|
|
1409
1446
|
this.editor.setStatus(`Jumped to ${loc.file}:${loc.line}`);
|
|
1410
1447
|
}
|
package/plugins/lib/fresh.d.ts
CHANGED
|
@@ -736,7 +736,7 @@ type CursorInfo = {
|
|
|
736
736
|
end: number;
|
|
737
737
|
} | null;
|
|
738
738
|
/**
|
|
739
|
-
* 0-indexed line number of the cursor. `
|
|
739
|
+
* 0-indexed line number of the cursor. `null` when the line index is
|
|
740
740
|
* unavailable — e.g. a huge file whose line scan hasn't completed, where
|
|
741
741
|
* the editor positions purely by byte offset. Plugins must treat `null`
|
|
742
742
|
* as "unknown", never as line 0.
|
|
@@ -903,6 +903,15 @@ type WidgetSpec = {
|
|
|
903
903
|
"kind": "row";
|
|
904
904
|
children: Array<WidgetSpec>;
|
|
905
905
|
key?: string | null;
|
|
906
|
+
/**
|
|
907
|
+
* When true, children that don't fit on one line reflow onto
|
|
908
|
+
* additional lines (growing the row's height) instead of being
|
|
909
|
+
* truncated. Children are never split — wrap happens at child
|
|
910
|
+
* boundaries — so wrap a logical group (e.g. a toggle + its
|
|
911
|
+
* accelerator) in a nested non-wrapping `Row` to keep it intact.
|
|
912
|
+
* Ignored when the row contains multi-line (block) children.
|
|
913
|
+
*/
|
|
914
|
+
wrap: boolean;
|
|
906
915
|
} | {
|
|
907
916
|
"kind": "col";
|
|
908
917
|
children: Array<WidgetSpec>;
|
|
@@ -1213,6 +1222,16 @@ interface SearchHandle {
|
|
|
1213
1222
|
take(): SearchTakeResult;
|
|
1214
1223
|
cancel(): void;
|
|
1215
1224
|
}
|
|
1225
|
+
type ReplaceResult = {
|
|
1226
|
+
/**
|
|
1227
|
+
* Number of replacements made
|
|
1228
|
+
*/
|
|
1229
|
+
replacements: number;
|
|
1230
|
+
/**
|
|
1231
|
+
* Buffer ID of the edited buffer
|
|
1232
|
+
*/
|
|
1233
|
+
bufferId: number;
|
|
1234
|
+
};
|
|
1216
1235
|
type AuthorityFilesystem = {
|
|
1217
1236
|
kind: "local";
|
|
1218
1237
|
};
|
|
@@ -1496,16 +1515,6 @@ type RemoteIndicatorStatePayload = {
|
|
|
1496
1515
|
kind: "disconnected";
|
|
1497
1516
|
label?: string | null;
|
|
1498
1517
|
};
|
|
1499
|
-
type ReplaceResult = {
|
|
1500
|
-
/**
|
|
1501
|
-
* Number of replacements made
|
|
1502
|
-
*/
|
|
1503
|
-
replacements: number;
|
|
1504
|
-
/**
|
|
1505
|
-
* Buffer ID of the edited buffer
|
|
1506
|
-
*/
|
|
1507
|
-
bufferId: number;
|
|
1508
|
-
};
|
|
1509
1518
|
type SpawnResult = {
|
|
1510
1519
|
/**
|
|
1511
1520
|
* Complete stdout as string
|
|
@@ -1629,6 +1638,13 @@ interface EditorAPI {
|
|
|
1629
1638
|
*/
|
|
1630
1639
|
executeAction(actionName: string): boolean;
|
|
1631
1640
|
/**
|
|
1641
|
+
* Cancel the active prompt / overlay — the same teardown the
|
|
1642
|
+
* Escape key triggers. Lets a plugin dismiss a prompt it opened
|
|
1643
|
+
* (e.g. exporting Live Grep results to a dock panel) without
|
|
1644
|
+
* routing a synthetic keypress.
|
|
1645
|
+
*/
|
|
1646
|
+
cancelPrompt(): boolean;
|
|
1647
|
+
/**
|
|
1632
1648
|
* Register a custom statusbar token.
|
|
1633
1649
|
* Token will be named "plugin_name:token_name" where plugin_name is the current plugin.
|
|
1634
1650
|
* Returns true if registration succeeded, false if invalid or already registered.
|
|
@@ -2126,21 +2142,21 @@ interface EditorAPI {
|
|
|
2126
2142
|
*/
|
|
2127
2143
|
getDataDir(): string;
|
|
2128
2144
|
/**
|
|
2129
|
-
* Per-working-directory data root for plugin state that should be scoped
|
|
2130
|
-
* to the current project root / worktree rather than shared across all of
|
|
2131
|
-
* them (`<data_dir>/workdirs/<encoded-cwd>/`). Prefer this over
|
|
2132
|
-
* `getDataDir()` for per-project state; the directory is not created for
|
|
2133
|
-
* you. Note: terminal scrollback and orchestrator state use their own
|
|
2134
|
-
* dedicated layouts (see `getTerminalDir()`), not this root.
|
|
2135
|
-
*/
|
|
2136
|
-
getWorkingDataDir(): string;
|
|
2137
|
-
/**
|
|
2138
2145
|
* Directory holding terminal scrollback backing files for the current
|
|
2139
2146
|
* working directory. Each project root / worktree has its own subdir, so
|
|
2140
|
-
*
|
|
2147
|
+
* Universal Search's terminal scope can stay scoped to the active
|
|
2148
|
+
* project rather than spanning every project's terminals.
|
|
2141
2149
|
*/
|
|
2142
2150
|
getTerminalDir(): string;
|
|
2143
2151
|
/**
|
|
2152
|
+
* Per-working-directory data root for plugin state scoped to the current
|
|
2153
|
+
* project root / worktree (`<data_dir>/workdirs/<encoded-cwd>/`). Use
|
|
2154
|
+
* instead of `getDataDir()` for state that should not be shared across
|
|
2155
|
+
* worktrees. The directory is not created here — callers create what
|
|
2156
|
+
* they need under it.
|
|
2157
|
+
*/
|
|
2158
|
+
getWorkingDataDir(): string;
|
|
2159
|
+
/**
|
|
2144
2160
|
* Get themes directory path
|
|
2145
2161
|
*/
|
|
2146
2162
|
getThemesDir(): string;
|
|
@@ -2274,6 +2290,12 @@ interface EditorAPI {
|
|
|
2274
2290
|
*/
|
|
2275
2291
|
clearOverlaysInRange(bufferId: number, start: number, end: number): boolean;
|
|
2276
2292
|
/**
|
|
2293
|
+
* Clear overlays in a single namespace that overlap with a byte range.
|
|
2294
|
+
* Unlike clearOverlaysInRange, overlays in other namespaces (e.g.
|
|
2295
|
+
* editor-owned LSP diagnostics) are left untouched.
|
|
2296
|
+
*/
|
|
2297
|
+
clearOverlaysInRangeForNamespace(bufferId: number, namespace: string, start: number, end: number): boolean;
|
|
2298
|
+
/**
|
|
2277
2299
|
* Remove an overlay by its handle
|
|
2278
2300
|
*/
|
|
2279
2301
|
removeOverlay(bufferId: number, handle: string): boolean;
|
|
@@ -2479,26 +2501,21 @@ interface EditorAPI {
|
|
|
2479
2501
|
*/
|
|
2480
2502
|
setPromptFooter(footer: StyledText[]): boolean;
|
|
2481
2503
|
/**
|
|
2482
|
-
* Set the floating-overlay prompt's
|
|
2483
|
-
*
|
|
2484
|
-
* styled-text title. Give each control a `key` equal to the action it
|
|
2485
|
-
* should fire on click (e.g. `"live_grep_toggle_files"`). Pass `null` to
|
|
2486
|
-
* clear it. No visible effect on non-overlay prompts.
|
|
2504
|
+
* Set the floating-overlay prompt's input-row status text (right-aligned,
|
|
2505
|
+
* left of the match count). Empty string clears it.
|
|
2487
2506
|
*/
|
|
2488
|
-
|
|
2507
|
+
setPromptStatus(status: string): boolean;
|
|
2489
2508
|
/**
|
|
2490
|
-
* Set the floating-overlay prompt's
|
|
2491
|
-
*
|
|
2492
|
-
*
|
|
2493
|
-
* non-overlay prompts.
|
|
2509
|
+
* Set the floating-overlay prompt's toolbar as a `WidgetSpec` (real,
|
|
2510
|
+
* clickable `Toggle`/`Button` widgets rendered in the header band, in
|
|
2511
|
+
* place of the styled-text title). Pass `null`/`undefined` to clear it.
|
|
2494
2512
|
*/
|
|
2495
|
-
|
|
2513
|
+
setPromptToolbar(specObj: unknown): boolean;
|
|
2496
2514
|
/**
|
|
2497
2515
|
* Toggle a floating-overlay toolbar control by its widget `key`. The host
|
|
2498
|
-
* owns the toggle's checked state, flips it
|
|
2499
|
-
*
|
|
2500
|
-
*
|
|
2501
|
-
* a click or Space on the toggle, then react in your `widget_event` handler.
|
|
2516
|
+
* owns the toggle's checked state, flips it, and emits a `widget_event`
|
|
2517
|
+
* the plugin can listen for. Lets a plugin route its own Alt+… shortcut
|
|
2518
|
+
* through the same host path as a click / Space on the toggle.
|
|
2502
2519
|
*/
|
|
2503
2520
|
toggleOverlayToolbarWidget(key: string): boolean;
|
|
2504
2521
|
/**
|
|
@@ -2966,13 +2983,14 @@ interface EditorAPI {
|
|
|
2966
2983
|
caseSensitive?: boolean;
|
|
2967
2984
|
maxResults?: number;
|
|
2968
2985
|
wholeWords?: boolean;
|
|
2986
|
+
sourceBufferId?: number;
|
|
2969
2987
|
}): SearchHandle;
|
|
2970
2988
|
/**
|
|
2971
2989
|
* Replace matches in a file's buffer (async)
|
|
2972
2990
|
* Opens the file if not already in a buffer, applies edits via the buffer model,
|
|
2973
2991
|
* and saves. All edits are grouped as a single undo action.
|
|
2974
2992
|
*/
|
|
2975
|
-
replaceInFile(filePath: string, matches: number[][], replacement: string): Promise<ReplaceResult>;
|
|
2993
|
+
replaceInFile(filePath: string, matches: number[][], replacement: string, bufferId?: number): Promise<ReplaceResult>;
|
|
2976
2994
|
/**
|
|
2977
2995
|
* Send LSP request (async, returns request_id)
|
|
2978
2996
|
*/
|
package/plugins/live_diff.ts
CHANGED
|
@@ -978,7 +978,18 @@ async function recompute(bufferId: number): Promise<void> {
|
|
|
978
978
|
}
|
|
979
979
|
|
|
980
980
|
const length = editor.getBufferLength(bufferId);
|
|
981
|
-
|
|
981
|
+
let newText: string;
|
|
982
|
+
try {
|
|
983
|
+
newText = await editor.getBufferText(bufferId, 0, length);
|
|
984
|
+
} catch (e) {
|
|
985
|
+
// The buffer can close between the awaits above (the git `show`
|
|
986
|
+
// for the reference is slow) and this fetch — e.g. the user
|
|
987
|
+
// closes the file, or an external process churns it while it's
|
|
988
|
+
// open. `buffer_closed` deletes our state, so if it's gone the
|
|
989
|
+
// recompute is moot: bail quietly instead of logging an error.
|
|
990
|
+
if (!states.has(bufferId)) return;
|
|
991
|
+
throw e;
|
|
992
|
+
}
|
|
982
993
|
|
|
983
994
|
// Skip 1: same buffer text as last recompute. `lines_changed` fires
|
|
984
995
|
// on viewport scrolls (cursor up/down past the visible area), and
|
package/plugins/live_grep.ts
CHANGED
|
@@ -179,6 +179,11 @@ let overlayActive = false;
|
|
|
179
179
|
// pre-filled (rather than a bespoke cached-results overlay).
|
|
180
180
|
let lastQuery = "";
|
|
181
181
|
|
|
182
|
+
// The most recent merged result set, captured by `search` so the
|
|
183
|
+
// Quickfix export can snapshot exactly what the user is looking at into
|
|
184
|
+
// the dock panel without re-running the search.
|
|
185
|
+
let lastResults: GrepMatch[] = [];
|
|
186
|
+
|
|
182
187
|
// ── Search modes ──────────────────────────────────────────────────
|
|
183
188
|
//
|
|
184
189
|
// Separate from *where* we search (scopes): these control *how* the
|
|
@@ -314,7 +319,7 @@ function buildToolbarSpec(provider: LiveGrepProvider | null): WidgetSpec {
|
|
|
314
319
|
// an atomic group of `toggle + accelerator` — so the wrapping parent never
|
|
315
320
|
// splits a label from its `Alt+…` hint across lines.
|
|
316
321
|
const prefix = (text: string): WidgetSpec =>
|
|
317
|
-
raw([styledRow([{ text, style: { fg: "ui.
|
|
322
|
+
raw([styledRow([{ text, style: { fg: "ui.suggestion_fg" } }])]);
|
|
318
323
|
|
|
319
324
|
const sources: WidgetSpec[] = [spacer(1), prefix(editor.t("label.search_in"))];
|
|
320
325
|
SCOPES.forEach((s) => {
|
|
@@ -355,13 +360,14 @@ function buildToolbarSpec(provider: LiveGrepProvider | null): WidgetSpec {
|
|
|
355
360
|
function buildMetaRow(provider: LiveGrepProvider | null): WidgetSpec | null {
|
|
356
361
|
const hintStyle = { fg: "ui.help_key_fg" };
|
|
357
362
|
const sepStyle = { fg: "ui.popup_border_fg" };
|
|
363
|
+
const labelStyle = { fg: "ui.suggestion_fg" };
|
|
358
364
|
const parts: WidgetSpec[] = [];
|
|
359
365
|
|
|
360
366
|
// Provider button — only when a file-backed scope is on (irrelevant when
|
|
361
367
|
// searching only buffers/terminals/diagnostics). The button is keyed
|
|
362
368
|
// "provider"; activating it (click / Space / Alt+P) cycles the backend.
|
|
363
369
|
if (provider && (scopeEnabled.files || scopeEnabled.ignored)) {
|
|
364
|
-
parts.push(raw([styledRow([{ text: "Provider: ", style:
|
|
370
|
+
parts.push(raw([styledRow([{ text: "Provider: ", style: labelStyle }])]));
|
|
365
371
|
parts.push(button(provider.name, { key: "provider" }));
|
|
366
372
|
const pAccel = editor.getKeybindingLabel("cycle_live_grep_provider", "prompt");
|
|
367
373
|
if (pAccel) {
|
|
@@ -661,6 +667,63 @@ const finder = new Finder<GrepMatch>(editor, {
|
|
|
661
667
|
maxResults: MAX_RESULTS,
|
|
662
668
|
});
|
|
663
669
|
|
|
670
|
+
// The Quickfix list is just another "list of locations" surface, so it
|
|
671
|
+
// rides the same Finder panel abstraction as Diagnostics and Find
|
|
672
|
+
// References (dockable via `useUtilityDock`, Enter → openFile for free).
|
|
673
|
+
// Exporting hands it a static snapshot of the current matches; the
|
|
674
|
+
// bespoke Rust quickfix buffer it replaced is gone.
|
|
675
|
+
const quickfixFinder = new Finder<GrepMatch>(editor, {
|
|
676
|
+
id: "quickfix",
|
|
677
|
+
format: (match) => ({
|
|
678
|
+
label: `${match.file}:${match.line}:${match.column}`,
|
|
679
|
+
description: match.content.trim(),
|
|
680
|
+
location: {
|
|
681
|
+
file: match.file,
|
|
682
|
+
line: match.line,
|
|
683
|
+
column: match.column,
|
|
684
|
+
},
|
|
685
|
+
}),
|
|
686
|
+
useUtilityDock: true,
|
|
687
|
+
// Keep the Quickfix list docked when jumping to an entry — like Vim's
|
|
688
|
+
// quickfix and VS Code's results list — so the user can step through
|
|
689
|
+
// matches. (Find References / Diagnostics keep the default close.)
|
|
690
|
+
closeOnSelect: false,
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// Snapshot the current Live Grep results into the Quickfix dock panel.
|
|
694
|
+
// Bound to the `live_grep_export_quickfix` keybinding (Alt+M / Alt+Q,
|
|
695
|
+
// when=prompt) — a plain plugin action now that the Rust action is gone.
|
|
696
|
+
function exportQuickfix(): void {
|
|
697
|
+
if (!overlayActive) return;
|
|
698
|
+
if (lastResults.length === 0) {
|
|
699
|
+
editor.setStatus("No Live Grep results to export");
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const query = lastQuery;
|
|
703
|
+
const matches = lastResults;
|
|
704
|
+
// Dismiss the host overlay first so the panel opens against the editor
|
|
705
|
+
// pane (which is then routed into the dock), not behind the overlay.
|
|
706
|
+
// `cancelPrompt` is the same teardown Escape triggers; the plugin's
|
|
707
|
+
// own prompt state is cleared via the resulting `prompt_cancelled`
|
|
708
|
+
// hook.
|
|
709
|
+
editor.cancelPrompt();
|
|
710
|
+
void quickfixFinder.panel({
|
|
711
|
+
title: `Quickfix: ${query} (${matches.length} matches)`,
|
|
712
|
+
items: matches,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
registerHandler("live_grep_export_quickfix", exportQuickfix);
|
|
716
|
+
// Register the action→handler mapping so the `live_grep_export_quickfix`
|
|
717
|
+
// keybinding (a PluginAction now) resolves across the plugin boundary.
|
|
718
|
+
// A never-activated context keeps it out of the palette — it's only
|
|
719
|
+
// meaningful from inside the Live Grep overlay.
|
|
720
|
+
editor.registerCommand(
|
|
721
|
+
"Live Grep: Export to Quickfix (internal)",
|
|
722
|
+
"",
|
|
723
|
+
"live_grep_export_quickfix",
|
|
724
|
+
"live-grep-internal"
|
|
725
|
+
);
|
|
726
|
+
|
|
664
727
|
/**
|
|
665
728
|
* Switch to the next *available* registered provider, in priority
|
|
666
729
|
* order, wrapping at the end. Unavailable providers (those whose
|
|
@@ -936,6 +999,7 @@ async function search(query: string): Promise<GrepMatch[]> {
|
|
|
936
999
|
if (lastSearchTruncated !== wasTruncated) {
|
|
937
1000
|
updateOverlayTitle(cachedSelected ?? null);
|
|
938
1001
|
}
|
|
1002
|
+
lastResults = results;
|
|
939
1003
|
return results;
|
|
940
1004
|
}
|
|
941
1005
|
|
|
@@ -1046,7 +1046,9 @@ function processLineConceals(
|
|
|
1046
1046
|
// the one-frame glitch where conceals are cleared but not yet rebuilt.
|
|
1047
1047
|
editor.debug(`[mc] processLine clear+rebuild bytes=${byteStart}..${byteEnd} content="${lineContent.slice(0,40)}"`);
|
|
1048
1048
|
editor.clearConcealsInRange(bufferId, byteStart, byteEnd);
|
|
1049
|
-
|
|
1049
|
+
// Only clear our own emphasis overlays — clearing ALL overlays in the range
|
|
1050
|
+
// would also wipe editor-owned overlays like LSP diagnostics (issue #2146).
|
|
1051
|
+
editor.clearOverlaysInRangeForNamespace(bufferId, "md-emphasis", byteStart, byteEnd);
|
|
1050
1052
|
|
|
1051
1053
|
const cursorOnLine = cursors.some(c => c >= byteStart && c <= byteEnd);
|
|
1052
1054
|
// Strict version: excludes the boundary at byteEnd so that the cursor
|