Skip to content

Improve editor rendering, plugins, LSP, and undo#68

Open
fcoury wants to merge 30 commits into
masterfrom
codex/editor-e2e-improvements
Open

Improve editor rendering, plugins, LSP, and undo#68
fcoury wants to merge 30 commits into
masterfrom
codex/editor-e2e-improvements

Conversation

@fcoury
Copy link
Copy Markdown
Contributor

@fcoury fcoury commented May 9, 2026

Summary

This PR bundles the editor hardening and modularization work from the E2E
analysis branch. It improves rendering safety, editor motion correctness,
plugin panel support, LSP modularity, and replaces the ad hoc undo stack with
buffer-local transaction undo/redo.

What changed

  • Hardened TUI rendering against cramped terminal sizes and wide Unicode text.
  • Fixed diagnostics, completion popups, dialogs, info tooltips, overlays,
    commandline/statusline rendering, and picker/list layout edge cases.
  • Added active-window synchronization fixes for split windows, cursor rendering,
    sibling close behavior, and nested split resizing.
  • Modularized LSP support so rust-analyzer is a configured default server
    instead of a hard-coded singleton.
  • Added plugin panel APIs and a NeoTree-style file tree plugin, including
    focus handling and Ctrl-e toggle behavior.
  • Fixed cursor and viewport edge cases for file-end movement, word movement,
    page movement, $, visual mode $, and insert-mode escape clamping.
  • Reworked undo/redo around per-buffer edit transactions and added Ctrl-r
    redo.
  • Added dirty-state revision checkpoints so the modified indicator clears when
    undo returns a buffer to the saved revision.

Testing

  • cargo fmt --check
  • cargo test

fcoury added 30 commits May 8, 2026 01:12
Remove the duplicate test-only action reducer so integration tests execute the same editor dispatch path as production.

Tighten cursor, line-boundary, and Unicode handling around grapheme-aware editing so movement, deletion, and diagnostics avoid byte-index assumptions.
Route window switching and layout mutations through editor helpers so active-window state is saved and reloaded consistently.

Add coverage that verifies split window navigation preserves each window cursor independently.
Extract cursor terminal-position calculation so it can be tested without terminal output.

Use the active window buffer line directly when computing display columns, avoiding double application of `vtop` after scrolling.
Use the same terminal cursor position for overlay avoid-cursor placement that rendering uses for the actual cursor.

Mark overlays dirty when recalculated placement changes so cursor-driven overlays redraw after moving across screen regions.
Treat render-buffer text writes as terminal cell writes so wide characters reserve their continuation cell.

Tighten boundary checks to ignore `x == width` and `y == height` writes instead of allowing them to spill into later cells.
Measure plugin overlay width and right alignment in terminal display columns so wide characters align correctly.

Clear the full overlay area before rendering each row to prevent stale cells when content shrinks or shifts.
Use character-aware deletion for command and search backspace instead of byte slicing.

Clear the command line before rendering prompt text so Unicode search input and narrow terminal widths do not leave stale cells or underflow padding.
Use display-width-aware, saturating layout math for statusline segments so
narrow terminals do not underflow while computing file and position areas.

Clear the row before writing statusline segments to avoid stale cells when
segments shrink or overlap.
Measure completion labels, icons, details, and documentation with terminal
display widths so popup rows stay aligned for emoji and CJK text.

Add shared helpers for fitting strings to display columns without splitting
grapheme clusters during truncation.
Keep picker lists safe when filtering leaves no matches, and make list row
truncation use terminal display widths for wide Unicode labels.

Use character-aware backspace and display-width cursor math for picker search
text so non-ASCII search terms do not panic or misplace the cursor.
Draw info tooltip text from the dialog content origin instead of applying the
horizontal offset twice, which misplaced content away from its border.

Measure and pad tooltip rows by display width so wide Unicode text is clipped
and filled consistently with other popup surfaces.
Use display-width-aware title measurement and truncation when drawing dialog
borders, so long titles cannot underflow the centering calculation.

This also keeps Unicode titles aligned with the rest of the popup rendering
surfaces that measure terminal columns instead of bytes.
Use terminal display width instead of byte length when reporting the cursor
position for command and search prompts.

This keeps the cursor aligned with rendered commandline text when prompts
contain wide Unicode characters such as emoji or CJK text.
Use saturating geometry for picker dialog, list, separator, and cursor rows so
small terminal heights cannot underflow popup layout calculations.

Make editor viewport height saturating as well, matching the window manager's
reserved status and command line handling on very small screens.
Move info tooltip placement into a small geometry helper that uses saturating
math for cursor offsets, horizontal fitting, and available height checks.

This prevents hover/info popup layout from underflowing on tiny terminal
heights after viewport height is clamped to zero.
Skip statusline and commandline drawing when terminal dimensions leave no valid
row for those chrome elements, and use saturating command/search cursor rows.

This prevents refresh rendering from underflowing on one-row or zero-size test
terminal layouts.
Keep split membership checks from mutating the traversal index before recursing
into nested split trees, so resize commands can reach the active window's
inner parent split.

Also report a no-op when resizing a single window, since there is no parent
split ratio to adjust.
Advance the window traversal index when removing the target leaf so later
siblings are not mistaken for the same active window during close.

This lets closing the first window in a split collapse to the remaining sibling
instead of failing to remove the active window.
Build diagnostic rows with display-width-aware fitting so indicators and
messages cannot spill beyond the available window columns.

Preserve truncation with an ellipsis for wide Unicode diagnostics and keep
cramped rows bounded when many diagnostics share a line.
Add a bounded completion popup layout path that clamps popup width, flips above
the cursor when there is not enough space below, and limits rendered rows to
the available terminal height.

Use terminal cursor coordinates for LSP completion popups so placement includes
gutter and window offsets instead of buffer-local cursor positions.
Add configurable language server definitions and route editor LSP
requests through a manager keyed by document language and workspace root.

Keep Rust enabled by default while moving `rust-analyzer` details into
server config so other stdio LSPs can be added through `config.toml`.
Add a plugin-owned side panel surface with focus routing, layout
reservation, filesystem listing, and directory watch APIs so plugins can
build persistent tree-style UI.

Register a sample `NeoTree` plugin on `Ctrl-e`, including toggle behavior,
file opening, and explicit editor focus handoff after file activation.
Clamp viewport and cursor state after movement actions so commands that
overshoot the buffer cannot land past the final navigable line.

Cover `G`, oversized line jumps, direct cursor positioning, paging, and
scrolling against files that end with a trailing newline.
Keep `w` and `b` from scrolling when their destination line is already
visible. Word movement should update the cursor within the current viewport
and only scroll when the target word is offscreen.

Add a movement regression that asserts both next-word and previous-word
motions preserve `vtop` for visible targets.
Allow page movement to use partial pages at file edges so `Ctrl-b` and
`Ctrl-f` land on the top or final navigable line instead of refusing to
move when a full page is unavailable.

Fix visual selection rendering to include the final visible character on
selected lines and resolve selection line lengths against buffer lines.
Move `$` to the final visible character instead of one cell past the end
of the line, while keeping append-at-end behavior by moving right before
entering insert mode for `A`.

Bind `$` and `End` in visual modes so visual selections can extend to the
line end, and update movement and append tests for the new cursor behavior.
When leaving insert mode, move a one-past-end cursor back onto the final
visible character so normal mode does not remain past the line end.

Keep insert mode free to use one-past-end positions while editing and add a
regression for escaping from an append-at-end cursor.
Add buffer-local transaction undo and redo backed by text edit ranges.
Route editor and plugin mutations through the transaction path so insert
sessions, deletes, visual edits, paste, indent, and redo share one model.

Track clean revisions in the undo history so the dirty indicator clears
when undo returns a buffer to its saved state and updates after save.
Derive the default panel side and use key-based reverse sorting for picker
matches to satisfy the current Clippy toolchain. Update `bytes` in
`Cargo.lock` to the patched release required by the security audit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant