fix: retain semantic colors across editable diff updates
This commit is contained in:
@@ -33,7 +33,7 @@ Frontends drive `EditorSession::handle` or `execute`, periodically call `poll()`
|
||||
|
||||
`Editor::should_quit()` tells the frontend to exit its input/render loop for either quit or restart; `Editor::should_restart()` distinguishes a requested executable relaunch from an ordinary exit. Normal-mode `core.restart` / `:restart` first closes clean buffers across all tabs, panes, and historical views. Remaining dirty buffers retain unsaved text and undo/redo history, block the exit/relaunch request with a warning, and leave the editor interactive; already closed clean buffers remain closed. `core.force_restart` / `:force_restart` (alias `:restart!`) requests relaunch without saving, discarding unsaved text. Both commands take no arguments and have no default binding. Force restart uses palette confirmation, while configured action bindings execute directly.
|
||||
|
||||
Session polling schedules synchronous `Editor::refresh_files()` metadata scans every 250 ms; the editor method itself is unthrottled and reports whether a redraw is needed. Scans include active and parked live buffers across inactive tabs/panes, but never immutable historical snapshots, and read content only after metadata changes. Valid changed disk text replaces only clean buffers, updates the saved baseline and text revision, clears stale undo/redo, preserves/clamps every associated view, invalidates annotations and stale requests, and refreshes live comparisons. Dirty buffers keep edits; missing, unreadable, or invalid changed files preserve text and report a warning.
|
||||
Session polling schedules synchronous `Editor::refresh_files()` metadata scans every 250 ms; the editor method itself is unthrottled and reports whether a redraw is needed. Scans include active and parked live buffers across inactive tabs/panes, but never immutable historical snapshots, and read content only after metadata changes. Valid changed disk text replaces only clean buffers, updates the saved baseline and text revision, clears stale undo/redo, preserves/clamps every associated view, clears diagnostics and stale requests, remaps retained semantic colors, and refreshes live comparisons. Dirty buffers keep edits; missing, unreadable, or invalid changed files preserve text and report a warning.
|
||||
|
||||
Every save independently re-reads disk content and compares its LF-normalized text with the saved baseline, rather than trusting polling or modification timestamps. Normal writes refuse external changes/deletion; newly opened nonexistent paths can be created normally. Typed `core.force_save` / `core.force_save_and_quit` expose canonical `:force_save [path]` / `:force_save_and_quit [path]` and aliases `:w!` / `:wq!`. Force bypasses only the external-change check, retaining snapshot read-only, target-symlink, and another-buffer path-ownership protections. Both save-and-quit variants quit only after a successful save and only when all buffers are clean.
|
||||
Frontends inspect `selections()` for document-ordered, end-exclusive Visual ranges of grapheme coordinates (empty outside Visual), `carets()` for all active cursors in document order, `cursor()` for the primary, and `selection_count()` for the total target count. `visual_linewise()` is true only for the whole-row shape in `Mode::Visual`; it changes neither binding modes nor Ruby mode names. Linewise ranges expand to complete rows. A range ending at the next row's column 0 includes the previous row's newline; an empty final-row range contains no text. `linewise_rows()` exposes inclusive selected row ranges only in linewise Visual, including an empty final row without changing `selections()` cardinality. The TUI highlights the copied text on those rows and one cell for each copied newline, never unused viewport space or the gutter, and labels their mode VISUAL LINE. Selection normalization, clipboard shape and contents, edit planning, and history remain inside the editor module.
|
||||
@@ -55,7 +55,7 @@ Current-line blame is enabled by default. Session loads whole-file incremental a
|
||||
|
||||
Frontends inspect `file_picker()` for query, presentation, view, explorer-local INSERT/filter subfocus, selection, and errors, and iterate borrowed `FileRow` metadata (actual path, display label, directory flag, visible depth, expansion, optional repository status and old path). Dock side is a logical preference. They allocate rectangles, add folder suffixes/colors and sanitized rename labels, and render; hierarchy, filtering, parent/child navigation, focus transitions, and selected-file opening remain backend-owned.
|
||||
Frontends inspect `diagnostics()` and `diagnostic_at_cursor()` for validated active-buffer annotations. Diagnostics are owned by core LSP or a plugin and buffer revision; text changes clear them. Native server retirement removes only that server's contribution, preserving sibling diagnostics. The TUI underlines annotated text, colors affected gutter numbers by severity, and displays the primary caret's diagnostic message without replacing search/selection backgrounds.
|
||||
Frontends borrow `semantic_tokens()` for sorted, single-line grapheme spans from the active buffer. Immutable `Arc` slices are shared by native LSP and buffer storage; painting does not copy tokens. Text/path changes discard overlays, and native updates validate the whole applicable annotation batch before mutation. The TUI merges semantic styling into its existing glyph pass above syntax foregrounds but below diagnostic/search/selection/caret styling.
|
||||
Frontends borrow `semantic_tokens()` for sorted, single-line grapheme spans from the active buffer. Immutable `Arc` slices are shared by native LSP and buffer storage; painting does not copy tokens. Text replacements retain tokens outside the changed range and shift following positions using whole graphemes, including newline changes. Tokens overlapping the changed range are dropped; disjoint edits conservatively invalidate the range between their first and last changes. This applies to typing, undo/redo, completion, plugin replacements, and disk reloads. Path changes discard overlays. Native updates validate the whole applicable annotation batch before mutation. The TUI merges semantic styling into its existing glyph pass above syntax foregrounds but below diagnostic/search/selection/caret styling.
|
||||
Frontends inspect `hover()` for optional read-only content and logical-line/grapheme scroll offsets. Popup request eligibility, dismissal, and scrolling stay editor/session-owned; the TUI clips the panel inside the full editor area without changing the primary cursor or viewport. Its hover-local syntax cache treats content as Markdown, including named fenced-code injections, and rebuilds only when content changes. The popup shares the document syntax palette and preserves raw Markdown text and logical scroll coordinates.
|
||||
Frontends borrow `completion()` for the read-only item list and selected index: zero is Original text, and other indices select `items[selected - 1]`. The editor owns the immutable original document/carets, atomic candidate transactions, cycling/dismissal, pending Insert undo, and final-only repeat recording. It changes only the primary completion target and translates secondary carets through all edits. The TUI places a windowed list near the current caret, bounded by the full editor rather than its pane, sanitizes/clips labels and detail by grapheme, and preserves hardware cursor ownership; wheel input over the list is consumed without dispatching edits.
|
||||
Frontends borrow `snippet_selections()` for the active argument's end-exclusive grapheme ranges, including mirrors and empty tabstops. The editor caches those ranges after previews, argument edits, and navigation; rendering adds no snippet allocation. Template edits stay primary-only, translate other carets, and fold resolved replacement text and caret placement into the pending completion repeat step. Per-occurrence ancestry distinguishes nested arguments from adjacent empty stops, allowing enclosing replacements to retire only overwritten descendants.
|
||||
@@ -119,7 +119,7 @@ Diff snapshots retain a separate immutable document identity, full source text,
|
||||
|
||||
Language configurations can own an ordered server stack. The first matching language wins extension overlap; all its matching servers receive document events, while the first handles hover/definition/completion. The Python preset uses ty then Ruff. LSP merges each worker's latest diagnostic snapshot in assignment order before emitting the unchanged core-owned update. Empty publications/retirement remove only their source; revision/path/root changes and close discard all contributions. Worker snapshots retain identity so queued pre-rename diagnostics cannot attach to a new path with the same text revision. Combined diagnostic message bytes remain bounded to 4 MiB; an overflowing publication retires its worker instead of discarding healthy sibling annotations. Workers are stored in config/root order, avoiding per-poll key cloning/sorting. Shutdown flushes the exit notification and waits for peer termination within the existing bounded grace period before process cleanup.
|
||||
|
||||
Semantic tokens belong only to the first assigned server. Initialization advertises standard token types/modifiers, relative encoding, full/range requests, and server refresh support; deltas, overlapping/multiline tokens, and dynamic registration are not advertised. Each buffer has at most one request in flight; changes/refreshes coalesce into the latest snapshot. Full requests are preferred, with whole-document range fallback. A linear UTF-16 decoder validates raw coordinates and legend indices before outward grapheme rounding; overlong ends truncate at EOL, unknown types and invisible spans fall back to syntax. Both wire frames and decoded spans are bounded to 4 MiB. Snapshot identity rejects stale/renamed/closed replies before decoding. Invalid semantic replies clear the overlay and report an error without retiring diagnostics/navigation; primary retirement clears its overlays, and sibling retirement leaves them intact.
|
||||
Semantic tokens belong only to the first assigned server. Initialization advertises standard token types/modifiers, relative encoding, full/range requests, and server refresh support; deltas, overlapping/multiline tokens, and dynamic registration are not advertised. Each buffer has at most one request in flight; changes/refreshes coalesce into the latest snapshot. Text-change notifications discard cached server coordinates without publishing an empty overlay over the editor's remapped colors. Full requests are preferred, with whole-document range fallback. A linear UTF-16 decoder validates raw coordinates and legend indices before outward grapheme rounding; overlong ends truncate at EOL, unknown types and invisible spans fall back to syntax. Both wire frames and decoded spans are bounded to 4 MiB. Snapshot identity rejects stale/renamed/closed replies before decoding. Invalid semantic replies clear the overlay and report an error without retiring diagnostics/navigation; primary retirement clears its overlays, and sibling retirement leaves them intact.
|
||||
|
||||
Manual completion dispatches the Insert-only `core.lsp.completion` action directly to the session, without the Ex execution path's mode/history transitions. The native worker sends the primary caret as UTF-16 and normalizes results into `CompletionItem { label, detail, primary_edit, edits, snippet }`; each `CompletionEdit` uses original-document, end-exclusive grapheme coordinates. Snippet primary text is expanded and newline-normalized before byte-relative metadata is assigned; additional edits remain plain text. Primary and additional edits form one atomic candidate. Session freshness includes the latest request, buffer/revision, primary caret, Insert source mode, and absence of modal overlays; any intervening input invalidates a pending reply. Decoding/validation errors remain completion-local and preserve healthy diagnostics/semantic tokens. Server commands, indentation adjustment, resolve requests, and automatic triggers are not supported; both frames and aggregate decoded completion text remain bounded.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user