Skip to content

Code Review

Pending code quality improvements and refactoring opportunities.

Note: file/line references are best-effort and can drift as the code changes; treat line numbers as approximate.

Large Functions

  • src/app/render.rs:5 — top-level render runs ~450 lines covering layout calculation, plugin hook firing, file explorer rendering, and status/prompt UI. The amount of responsibility in one function hurts readability and testing; factoring into helpers (layout, plugin hook prep, explorer rendering, status/prompt rendering) would make regressions easier to spot.
  • src/input/keybindings.rs:376 — Action::from_str is a ~260-line string-to-enum match. A data-driven table would reduce boilerplate and avoid missing new actions when added to the enum.
  • src/input/commands.rs:89 — get_all_commands is a ~540-line literal list in one function. This is brittle to maintain (hard to diff/review additions) and couples command metadata to code; consider a data table or config-driven source with tests for completeness.
  • src/primitives/highlighter.rs:78 — highlight_config is ~450 lines of repetitive per-language setup; moving to a data table (language -> queries/config) would reduce duplication and make it harder to forget highlight names when adding languages.
  • src/config.rs:566 — default_menus is a 420-line literal definition. Consider moving menu data to structured config or a table to make changes easier to diff/test and to avoid bloating code with data.
  • src/view/viewport.rs:521 — ensure_visible runs ~230 lines of layout math and clamping in one method; breaking into smaller helpers (e.g., horizontal/vertical logic, scroll computations) would make correctness checks and future changes safer.
  • src/input/keybindings.rs:1196 — format_action spans ~200 lines mirroring from_str; another sign a data-driven action registry would reduce duplication and risk of drift.
  • src/view/ui/view_pipeline.rs:134 — next is a 160+ line iterator step that handles multiple line types and wrapping; decomposing per-case helpers would make it easier to verify wrapping and newline behaviors.
  • src/model/buffer.rs:575 — get_text_range_mut is a 160+ line method with intertwined chunk handling, file streaming, and cache management. The dense control flow increases the risk of off-by-one or range bugs; extract helpers per buffer variant (loaded vs unloaded) and centralize bounds checks.
  • src/services/plugins/runtime.rs:1861/1967/2066/2143/2159/etc. — repeated pending_responses.lock().unwrap() (and other mutex unwrap()s) in ops will panic if the mutex is poisoned by a plugin panic. These should handle poisoning and return an error to the plugin rather than crashing the runtime.
  • src/view/ui/file_explorer.rs:148 — renderer calls view.tree().get_node(node_id).expect("Node should exist"). A desynced tree (e.g., after FS errors or refresh races) will panic the UI; prefer graceful fallback or placeholder row.
  • src/view/file_tree/tree.rs:138 — expand_node uses self.get_node(id).unwrap() while doing async FS reads. If the node was removed between calls, this panics instead of returning an IO error; return a proper error for missing nodes to avoid crashing the explorer.
  • src/view/ui/status_bar.rs:149 — render_status pulls cursor info with ad-hoc line iteration and cached line numbers in a long method; consider extracting helpers for cursor position and diagnostics summary to reduce the ~200-line render method complexity.
  • src/model/cursor.rs:220 — primary()/primary_mut() use expect("Primary cursor should always exist"). If a bug ever removes the primary cursor (e.g., during multi-cursor deletion), the editor will panic. Prefer returning an error or recreating a primary cursor to keep the UI alive.
  • src/model/marker_tree.rs:494/509/512/517/etc. — AVL rotations and queries use unchecked unwrap() on child pointers. Any tree corruption (e.g., from earlier logic bugs) will panic. Consider defensive checks or debug assertions gated for release builds to avoid crashing the editor.
  • src/view/split.rs:162 — ensure_layout returns self.layout.as_ref().unwrap(). If callers forget to call ensure_layout before get_layout usage, rendering will panic. Consider returning Option<&Layout> or asserting via a Result to make misuse harder in production.
  • src/view/ui/split_rendering.rs:1642/1838 — rendering paths unwrap() optional highlight colors/positions when overlays and syntax spans overlap. If the lookups ever return None (e.g., missing semantic span), the renderer will panic; handle None with defaults to keep the UI resilient.
  • src/view/prompt.rs:974 — selected_text().unwrap() in selection paths; production prompt operations should avoid panics when no selection exists (e.g., racey UI updates or plugin-driven prompt changes).
  • src/input/buffer_mode.rs: ModeRegistry and BufferMode rely on string names without validation; missing mode references during inheritance or resolve silently stop traversal, which can mask misconfigurations. Consider explicit errors/logging for unknown parent/mode to aid debugging.
  • src/view/viewport.rs:895 — ensure_cursors_visible unwraps min/max cursor positions; if called with an empty cursor list (due to upstream logic bugs), it will panic. Guard against empty input to keep scrolling resilient.
  • src/view/ui/suggestions.rs:80/87 — width calculations use .max().unwrap_or(0) on suggestion lists; if any suggestion text/keybinding is extremely wide, width computation and rendering alignment are packed into the main render function. Consider extracting layout calculation and handling unreasonably wide strings to avoid overflow/panic in rendering math.
  • src/services/signal_handler.rs:23/104 — global backtrace storage uses Mutex::lock().unwrap(); any poisoning (e.g., panic during signal handling) will panic again. Signal handlers should avoid unwraps and degrade gracefully to prevent double panics during shutdown.
  • src/services/async_bridge.rs:300/324/329/356/... — multiple unwrap()s on channel send/recv in the bridge; if the channel is closed (e.g., runtime shutdown), the bridge will panic instead of allowing a clean exit. Return errors and let the main loop terminate gracefully.
  • src/services/process_limits.rs:405/406/459/476/496/503 — heavy use of unwrap() around serialization and system resource reads; a failure to read system memory or serialize limits will panic instead of bubbling a configuration error. Prefer error propagation.
  • src/services/plugins/api.rs & event_hooks/process: widespread RwLock::unwrap() and channel unwrap()s. A poisoned lock or closed channel will panic plugins and potentially the editor. Replace with error paths that surface to the plugin caller or log-and-drop to keep the host stable.
  • src/input/command_registry.rs — plugin command registration/history uses RwLock::unwrap() in production paths; a poisoned lock (plugin panic) will crash the editor. Handle lock errors and surface failures to plugins.
  • src/input/actions.rs:68 — block selection reads cursor.block_anchor.unwrap() when in block mode; if state enters block mode without an anchor, movement will panic. Guard against None to keep editing stable.
  • src/services/plugins/process.rs:132/166/195/226 — plugin process bridge uses recv().unwrap(); closed channels during shutdown will panic the host. Treat channel closure as cancellation to allow clean teardown.
  • src/services/plugins/process.rs — blocking recv() loops have no timeout/backoff; a misbehaving plugin can hang the thread. Consider async or timeout-aware handling to avoid host hangs.
  • src/services/async_bridge.rs — uses unbounded std::sync::mpsc channels with no backpressure or eviction; a burst of async messages (e.g., LSP floods) can grow memory unbounded. Consider bounded channels or dropping strategies plus explicit error propagation on lock/channel failure.
  • src/services/plugins/event_hooks.rs — apply_event_with_hooks appears to be legacy/test-only and is not part of the current editor edit path (which applies edits via Editor::apply_event_to_active_buffer and queues plugin hooks asynchronously). If this module becomes production-critical again, it should isolate hook execution (catch_unwind/log) and avoid panics on poisoned locks.
  • src/app/plugin_commands.rs — some plugin text-editing commands (InsertText, DeleteRange, InsertAtCursor) mutate buffers without going through Editor::apply_event_to_active_buffer, which can bypass cross-cutting concerns (LSP change notifications, search highlight clearing, cursor/view sync, plugin after-insert/delete hooks). Consider routing “real edit” plugin commands through the centralized apply path, or document them as “raw/unsafe” edits.
  • src/view/ui/tabs.rs — render_for_split packs tab width calculation, scroll computation, and rendering into one long function; consider splitting into layout calculation and render steps to improve readability and reduce bugs in scroll math.
  • src/services/process_limits.rs — applies limits via cgroups/setrlimit with many unwraps noted; also lacks clear fallback/reporting when limits can't be applied (e.g., non-Linux or missing cgroup perms). Return actionable errors so callers can decide to continue without limits.

Test Code

  • src/view/ui/menu.rs:655/776/811/830 — menu parsing/rendering tests use unwrap(); production parsing should handle malformed plugin menu contributions gracefully.
  • src/services/fs/slow.rs — tests use TempDir::new().unwrap()/read_dir().await unwraps; ensure production slow backend surfaces IO errors cleanly.
  • src/view/popup.rs — selection tests unwrap() the selected item; production popup handling should guard empty lists.

Released under the Apache 2.0 License