Skip to content

fix: address comprehensive audit findings across 34 files#60

Merged
leszek3737 merged 2 commits into
mainfrom
fix57
May 26, 2026
Merged

fix: address comprehensive audit findings across 34 files#60
leszek3737 merged 2 commits into
mainfrom
fix57

Conversation

@leszek3737
Copy link
Copy Markdown
Owner

@leszek3737 leszek3737 commented May 25, 2026

File safety:

  • Fix symlink self-overwrite on move (move_ops.rs)
  • Fix TOCTOU race in temp-file reservation for symlink copy (temp.rs, copy.rs)
  • Fix Windows destructive replacement in chunked copy path (temp.rs)
  • Fix Windows directory symlink removal in delete walk (delete.rs)

State & types:

  • Fix size formatting rounding bug (1024.0 KB -> 1.0 MB) (file_entry.rs)
  • Fix pre-1970 timestamp rendering via chrono (file_entry.rs)
  • Fix combining character cursor invariant in TextInput (text_input.rs)
  • Remove dead drag fields, keep only drag_anchor_index (app_state.rs)
  • Add hotlist() getter, fix cache desync in menu_actions (app_state.rs)
  • Deduplicate compute_category via file_type::category delegation (file_entry.rs)
  • Deduplicate owner/group strings with Arc (file_entry.rs, reader.rs)

Input handling:

  • Fix drag-selection bypassing selection stats (mouse.rs)
  • Fix dropdown scroll offset and clipped-area click mismatch (mouse.rs)
  • Fix dialog hitbox min-clamp mismatch via shared centered_rect (mouse.rs)
  • Fix menu action dispatch dropping previous mode on no-op (mode_dispatch.rs)
  • Fix path validation double-wrapping and platform-aware backslash (dialogs.rs)
  • Add Home/End key support to all ListPicker handlers (pickers.rs)
  • Fix hotlist push to use API instead of direct field mutation (menu_actions.rs)

Rendering:

  • Move viewer wrap layout computation from render to pre-render step (render.rs, main.rs)
  • Eliminate per-frame image preview Text clone (render.rs)
  • Reduce hex view and text viewer span allocation churn (render.rs)
  • Fix fullscreen fallback on tiny terminals (layout.rs)
  • Fix adaptive dialog heights for small terminals (confirm.rs, simple.rs)

Performance & reliability:

  • Reduce poll timeout from 100ms to 33ms (main.rs)
  • Add 5s cooldown for failed watcher canonicalize retries (watcher_sync.rs)
  • Reset panel viewport on failed refresh (watcher_sync.rs)
  • Switch debug_log to blocking lock, fix double-open on log clear (debug_log.rs)
  • Remove redundant BufReader/BufWriter in chunk_copy, use heap buffer (chunk_copy.rs)
  • Simplify case-insensitive search, remove SmallCharBuf (pattern.rs)
  • Use DirEntry::file_type() to avoid extra metadata syscalls in delete walk (delete.rs)
  • Add Windows inode loop detection via MetadataExt (helpers.rs)
  • Remove unused blocking detect_mime function (mime.rs)
  • Add dotless config file recognition (mime.rs, file_type.rs)
  • Add undocumented Ctrl+A/E/U/W keybindings to F1 help (keymap.rs)
  • Log ignored panel paths via debug_log (config.rs)

Summary by Sourcery

Tighten file operations, viewer rendering, and watcher syncing for better safety, correctness, and responsiveness across platforms.

Bug Fixes:

  • Correct case-insensitive search matching logic by simplifying to a windowed lowercase comparison.
  • Fix file size and timestamp formatting edge cases, including rounding and pre-1970 times.
  • Resolve dialog hitbox and dropdown interaction issues by aligning mouse handling with layout logic and scroll state.
  • Ensure drag selection updates panel selection state through central helpers to keep stats and invariants in sync.
  • Prevent moving a symlink onto its own target and avoid destructive directory symlink deletions, especially on Windows.
  • Avoid destructive Windows replacements when swapping temp files into place by using a backup-and-restore strategy.
  • Stabilize watcher sync by tracking state with cooldowns, resetting panel viewports on errors, and avoiding excessive retries.
  • Fix path validation and error messaging for dialog inputs, including platform-aware path separators and clearer status messages.
  • Avoid double-opening or racing debug log files by using a blocking lock and safe truncate/reopen handling.
  • Preserve previous app mode when menu actions are no-ops instead of unconditionally dropping back to Normal mode.
  • Fix small-terminal dialog and fullscreen behavior by reusing centered layout helpers and adjusting height constraints.
  • Ensure hex and text viewer rendering avoids unnecessary clones and handles per-frame span allocation more efficiently.
  • Handle Windows inode loop detection via MetadataExt to make delete walks safer on that platform.
  • Align delete walks to use DirEntry::file_type and correct symlink removal semantics across platforms.
  • Reset panel cursor and scroll offset when full refreshes fail to avoid inconsistent UI state.
  • Fix watcher tests and behavior around missing paths, cooldowns, and root deletions to match new semantics.
  • Avoid per-frame Text clone in image previews by rendering cached widgets directly.
  • Correct command-line TextInput cursor handling for multi-byte grapheme insertion to maintain invariants.

Enhancements:

  • Introduce a shared hotlist accessor and API-based mutations to keep cached menu state in sync and simplify hotlist management.
  • Deduplicate owner/group strings with Arc caching and propagate this through file readers and entries to reduce allocations.
  • Centralize file category computation through the file_type module and expand config/source detection, including dotless config names.
  • Add dotless config file MIME recognition and map additional text-based types into config categories for better classification.
  • Reduce event poll latency and move viewer wrap layout computation into a pre-draw step for more responsive rendering.
  • Simplify chunked copy by removing redundant buffered wrappers and using a reusable heap buffer while maintaining progress accounting.
  • Refine batch progress accounting to track bytes_done/total more accurately across successes and cancellations.
  • Expose new Home/End key support in list pickers and document additional command-line Ctrl keybindings in the keymap help.
  • Improve config loading and panel application by logging ignored panel paths and clarifying ownership of persisted values.

Documentation:

  • Document command-line Ctrl+A/E/U/W keybindings in the F1 keymap help to match existing behavior.

Tests:

  • Update watcher sync tests to use the new WatcherSyncState, verify cooldown behavior, and ensure viewport reset on errors.
  • Adjust picker and menu tests to exercise the new hotlist accessors and Home/End navigation semantics.
  • Remove obsolete tests tied to the deleted SmallCharBuf helper and align mouse drag tests with Arc owner/group changes.

File safety:
- Fix symlink self-overwrite on move (move_ops.rs)
- Fix TOCTOU race in temp-file reservation for symlink copy (temp.rs, copy.rs)
- Fix Windows destructive replacement in chunked copy path (temp.rs)
- Fix Windows directory symlink removal in delete walk (delete.rs)

State & types:
- Fix size formatting rounding bug (1024.0 KB -> 1.0 MB) (file_entry.rs)
- Fix pre-1970 timestamp rendering via chrono (file_entry.rs)
- Fix combining character cursor invariant in TextInput (text_input.rs)
- Remove dead drag fields, keep only drag_anchor_index (app_state.rs)
- Add hotlist() getter, fix cache desync in menu_actions (app_state.rs)
- Deduplicate compute_category via file_type::category delegation (file_entry.rs)
- Deduplicate owner/group strings with Arc<str> (file_entry.rs, reader.rs)

Input handling:
- Fix drag-selection bypassing selection stats (mouse.rs)
- Fix dropdown scroll offset and clipped-area click mismatch (mouse.rs)
- Fix dialog hitbox min-clamp mismatch via shared centered_rect (mouse.rs)
- Fix menu action dispatch dropping previous mode on no-op (mode_dispatch.rs)
- Fix path validation double-wrapping and platform-aware backslash (dialogs.rs)
- Add Home/End key support to all ListPicker handlers (pickers.rs)
- Fix hotlist push to use API instead of direct field mutation (menu_actions.rs)

Rendering:
- Move viewer wrap layout computation from render to pre-render step (render.rs, main.rs)
- Eliminate per-frame image preview Text clone (render.rs)
- Reduce hex view and text viewer span allocation churn (render.rs)
- Fix fullscreen fallback on tiny terminals (layout.rs)
- Fix adaptive dialog heights for small terminals (confirm.rs, simple.rs)

Performance & reliability:
- Reduce poll timeout from 100ms to 33ms (main.rs)
- Add 5s cooldown for failed watcher canonicalize retries (watcher_sync.rs)
- Reset panel viewport on failed refresh (watcher_sync.rs)
- Switch debug_log to blocking lock, fix double-open on log clear (debug_log.rs)
- Remove redundant BufReader/BufWriter in chunk_copy, use heap buffer (chunk_copy.rs)
- Simplify case-insensitive search, remove SmallCharBuf (pattern.rs)
- Use DirEntry::file_type() to avoid extra metadata syscalls in delete walk (delete.rs)
- Add Windows inode loop detection via MetadataExt (helpers.rs)
- Remove unused blocking detect_mime function (mime.rs)
- Add dotless config file recognition (mime.rs, file_type.rs)
- Add undocumented Ctrl+A/E/U/W keybindings to F1 help (keymap.rs)
- Log ignored panel paths via debug_log (config.rs)
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 25, 2026

Reviewer's Guide

This PR addresses a broad audit across file safety, state/types, input handling, rendering, and watcher/logging, focusing on closing race conditions and platform bugs, simplifying code paths, and improving selection, dialogs, viewer rendering, and watcher robustness.

File-Level Changes

Change Details Files
Simplified case-insensitive search implementation and removed SmallCharBuf utility and tests.
  • Replaced rolling SmallCharBuf-based matcher with a Vec-based window search
  • Removed SmallCharBuf struct, its index impls, and associated unit tests
src/ops/search/pattern.rs
Fixed file size formatting and timestamp rendering, delegated file category detection, and deduplicated owner/group strings.
  • Adjusted FileSize Display rounding so units promote correctly (e.g., 1024 KB -> 1.0 MB)
  • Switched SystemTime formatting to chrono::Local::from for pre-1970 support
  • Replaced inline compute_category logic with file_type::category delegation
  • Changed FileEntry owner/group to Arc and updated builder/defaults accordingly
src/app/types/file_entry.rs
Centralized dialog geometry, fixed dropdown scrolling and drag-selection to honor selection APIs.
  • Reused dialogs::centered_rect for confirm/overwrite/progress mouse hitboxes
  • Added scroll-window logic to map dropdown clicks to the correct item index
  • Refactored drag selection to use panel clear_selection/set_selection_at
src/input/mouse.rs
Reduced event latency and moved viewer wrap/layout and image preview setup into a pre-draw phase.
  • Lowered EVENT_POLL_TIMEOUT_MS from 100ms to 33ms
  • Added pre_draw helper to start image previews and update viewer wrap layout before drawing
  • Consolidated viewer/image loader polling to set the dirty flag
  • Simplified RunningJob shutdown call sites
src/main.rs
Introduced watcher sync state with cooldown to avoid tight retry loops and reset panel viewport on full refresh errors.
  • Added WatcherSyncState holding last_synced and failed_cooldown with 5s duration
  • Updated sync_watcher_paths to use WatcherSyncState and skip retries while cooldown is active
  • Ensured full_refresh_panel clears listing and resets cursor/scroll_offset while recording last_error
  • Updated tests to cover cooldown behavior and viewport reset
src/app/watcher_sync.rs
src/app/watcher_sync/tests.rs
Removed unused MIME auto-detection from files, added dotless config recognition and richer config MIME mapping.
  • Dropped detect_mime and file-reading logic, keeping only detect_mime_from_bytes
  • Introduced dotless_config_mime for names like Makefile, Dockerfile, Jenkinsfile
  • Extended mime_to_category and extension_mime mappings for new config-like types
src/app/mime.rs
Hardened temp and copy operations for symlinks and Windows replacement, and added safer temp name reservation.
  • Marked reserve_temp_file_for as test-only and added reserve_temp_path_for that probes for free temp names
  • Made replace_file_with_temp perform backup/rename/restore on Windows to avoid destructive failures
  • Updated copy_symlink overwrite path to use reserve_temp_path_for instead of creating and deleting a temp file
src/ops/file_ops/temp.rs
src/ops/file_ops/copy.rs
Improved pickers and hotlist APIs, including Home/End support and safer hotlist mutation.
  • Added Home/End handling across history, hotlist, compare-mode, and user-menu pickers
  • Switched hotlist access to new AppState::hotlist getter and hotlist_* methods in pickers/tests
  • Ensured hotlist_add/remove use API instead of direct field mutation
src/input/pickers.rs
src/tests/pickers.rs
src/app/types/app_state.rs
src/app/panel_ops.rs
src/input/dialogs.rs
src/input/menu_actions.rs
Refined viewer rendering to avoid redundant allocations/clones and moved wrap layout out of render.
  • Stopped calling update_wrap_layout from render_viewer_with_colors; now done pre-draw
  • Reused &'_ text in viewer instead of cloning into owned strings for spans
  • Allocated hex viewer lines/spans with capacity and used per-line String instead of shared buffer
  • Rendered cached image text Paragraph directly without cloning
src/ui/viewer/render.rs
src/main.rs
Expanded keymap help, made delete walk more efficient and safer, and added Windows inode detection.
  • Added undocumented Ctrl+A/E/U/W bindings to CommandLine section of KEYBINDINGS
  • Introduced remove_symlink helper with Windows-specific directory handling
  • Switched delete walk to use DirEntry::file_type() and remove_symlink
  • Added Windows implementation of get_inode_key via MetadataExt
src/app/keymap.rs
src/ops/file_ops/delete.rs
src/ops/helpers.rs
Streamlined drag and selection state, and ensured TextInput cursor remains grapheme-consistent.
  • Removed unused drag_* fields from AppState and simplified reset_drag_state to only clear drag_anchor_index
  • Updated drag-select code to use panel selection helpers and keep cursor/scroll in sync
  • Adjusted TextInput insert to recompute cursor based on grapheme count after insertion
src/app/types/app_state.rs
src/input/mouse.rs
src/app/types/text_input.rs
Made chunked copy and batch progress tracking simpler and more accurate.
  • Replaced BufReader/BufWriter with direct File and heap buffer in chunk_copy
  • Adjusted batch::process_batch_entry to update bytes_done/bytes_total incrementally and saturating
src/ops/chunk_copy.rs
src/ops/batch.rs
Hardened debug logging and config loading, and improved feedback on ignored panel paths.
  • Changed debug_log::log to use a blocking Mutex lock and opened the log file with truncate instead of recreate+sync
  • Documented why toml::Value is cloned in load_setup, and logged ignored panel paths when non-directories
src/app/debug_log.rs
src/app/config.rs
Addressed various smaller correctness and platform issues across dialogs, quick-cd, normal mode, and tests.
  • Relaxed dialog centered_rect minimum size logic and improved progress/confirm dialog layout for small terminals
  • Adjusted validate_path_name to be platform-aware about backslashes and propagate error messages directly
  • Fixed quick_cd and menu hotlist usage to go through hotlist() and hotlist_push
  • Updated FileEntry-related tests and normal-mode info display for Arc owner/group
src/ui/dialogs/layout.rs
src/ui/dialogs/simple.rs
src/ui/dialogs/confirm.rs
src/input/dialogs.rs
src/input/normal.rs
src/input/mouse/tests.rs
src/tests/menu.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces various optimizations, refactorings, and bug fixes across the application. Key changes include transitioning FileEntry owner and group fields to Arc<str> to reduce allocations, implementing a cooldown mechanism in the file watcher to prevent redundant sync attempts on failure, adding Windows-specific handling for symlinks and filesystem identity, and optimizing rendering in the viewer. The review feedback highlights several critical areas for improvement: refining the file watcher's cooldown logic to be path-specific, using fs::symlink_metadata on Windows to correctly handle broken symlinks, avoiding heap allocations in case-insensitive pattern matching, using platform-aware path parsing for mime detection, and ensuring selection stats are recalculated when directory refreshes fail.

Comment thread src/app/watcher_sync.rs
Comment thread src/app/watcher_sync.rs Outdated
Comment thread src/app/watcher_sync.rs
Comment thread src/ops/file_ops/delete.rs
Comment thread src/ops/search/pattern.rs
Comment thread src/app/mime.rs
Comment thread src/app/watcher_sync.rs
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The new contains_case_insensitive implementation in pattern.rs allocates a Vec<char> and then runs .windows() over it for every call; if this is used in hot search paths, consider preserving a streaming, allocation-free approach similar to the previous ring buffer to avoid extra allocations and O(n) char copies per search.
  • In debug_log::log, switching to a blocking Mutex and reopening/truncating the log file while holding the lock may increase contention and latency for other logging calls; it might be worth minimizing the critical section (e.g., prepare/open the new file outside the lock) so high-frequency logging or slow I/O does not stall unrelated code.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `contains_case_insensitive` implementation in `pattern.rs` allocates a `Vec<char>` and then runs `.windows()` over it for every call; if this is used in hot search paths, consider preserving a streaming, allocation-free approach similar to the previous ring buffer to avoid extra allocations and O(n) char copies per search.
- In `debug_log::log`, switching to a blocking `Mutex` and reopening/truncating the log file while holding the lock may increase contention and latency for other logging calls; it might be worth minimizing the critical section (e.g., prepare/open the new file outside the lock) so high-frequency logging or slow I/O does not stall unrelated code.

## Individual Comments

### Comment 1
<location path="src/main.rs" line_range="228-229" />
<code_context>
+        image_preview_loader,
+        (size.width, size.height),
+    );
+    if state.mode == AppMode::Viewing
+        && let Some(vs) = viewer_state
+    {
+        vs.update_wrap_layout(size.width as usize);
</code_context>
<issue_to_address>
**issue (bug_risk):** The `if state.mode == AppMode::Viewing && let Some(vs) = viewer_state` expression likely does not compile on stable Rust.

`let_chains` was stabilized only for `if let pat = expr && guard` forms; `if cond && let pat = expr` is not supported on many stable toolchains. To keep this portable, please rewrite as either:

```rust
if state.mode == AppMode::Viewing {
    if let Some(vs) = viewer_state {
        vs.update_wrap_layout(size.width as usize);
    }
}
```

or using `if let` first with a guard:

```rust
if let Some(vs) = viewer_state && state.mode == AppMode::Viewing {
    vs.update_wrap_layout(size.width as usize);
}
```
</issue_to_address>

### Comment 2
<location path="src/app/types/file_entry.rs" line_range="55-63" />
<code_context>
 }

 pub(crate) fn format_system_time(modified: SystemTime) -> Option<String> {
-    let duration = modified.duration_since(std::time::UNIX_EPOCH).ok()?;
-    let ts = i64::try_from(duration.as_secs()).ok()?;
-    let dt = DateTime::from_timestamp(ts, 0)?;
-    Some(
-        dt.with_timezone(&chrono::Local)
-            .format("%d-%m-%y %H:%M")
-            .to_string(),
-    )
+    let dt = DateTime::<Local>::from(modified);
+    Some(dt.format("%d-%m-%y %H:%M").to_string())
 }
</code_context>
<issue_to_address>
**issue (bug_risk):** Changing to `DateTime::<Local>::from(modified)` may panic for `SystemTime` values outside the representable range instead of returning `None`.

Previously, out-of-range or pre-epoch times produced `None` via `duration_since(UNIX_EPOCH)` and `DateTime::from_timestamp`. `DateTime::<Local>::from(modified)` can instead panic for those values, especially with unusual filesystems or corrupted mtimes. To keep the old, non-panicking behavior, use the checked `duration_since`/`from_timestamp` path (or another checked conversion) and map failures to `None` rather than relying on `From<SystemTime>`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/main.rs Outdated
Comment thread src/app/types/file_entry.rs
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 25, 2026

Greptile Summary

This PR applies a comprehensive sweep of bug fixes and clean-ups across 34 files, covering filesystem-safety hazards (symlink self-overwrite, TOCTOU in temp reservation, Windows directory-symlink deletion), state-management correctness (size-format rounding, pre-1970 timestamps, combining-character cursor, dead drag fields, hotlist encapsulation), input/UI correctness (dialog hitbox alignment, dropdown scroll offset, mode-dispatch no-op, Home/End in pickers), and several performance improvements (33 ms poll, pre-draw wrap layout, Arc dedup for owner/group strings, hex-view pre-allocation).

  • File-safety fixes (move_ops.rs, temp.rs, copy.rs, delete.rs): The Windows symlink-removal and chunked-copy replacement paths look correct; the symlink self-overwrite guard in move_ops.rs is logically too broad (see inline comment).
  • State & type fixes (file_entry.rs, app_state.rs, text_input.rs, reader.rs): All look correct; Arc<str> dedup and timestamp fixes are clean.
  • Rendering & input (render.rs, mouse.rs, mode_dispatch.rs, pickers.rs, layout.rs): Unified centered_rect usage eliminates the hitbox mismatch class of bugs; mode-dispatch restore_prev_mode correctly avoids dropping non-Normal modes on no-op menu actions.

Confidence Score: 3/5

Safe to merge after fixing the symlink-rename guard in move_ops.rs; all other changes are conservative improvements or targeted bug fixes.

The symlink self-overwrite guard in move_ops.rs — applied identically in both move_entry and move_entry_with_progress — blocks case-only renames of symlinks on case-insensitive filesystems and any overwrite of one symlink with another that happens to point to the same real path. Both are valid, previously-working operations that now return InvalidInput without a clear user-visible message. No test covers the two-symlink-same-target scenario, so the regression would be silent in CI.

src/ops/file_ops/move_ops.rs (both move_entry and move_entry_with_progress need the narrower symlink guard); src/ops/file_ops/temp.rs (reserve_temp_path_for TOCTOU noted for awareness); src/app/debug_log.rs (blocking lock tradeoff).

Important Files Changed

Filename Overview
src/ops/file_ops/move_ops.rs Adds symlink self-overwrite guard via canonicalization, but the check is too broad — it fires on legitimate two-symlink overwrites and case-only symlink renames on case-insensitive filesystems.
src/ops/file_ops/temp.rs Adds reserve_temp_path_for (stat-based path reservation for symlinks) and a Windows-safe backup-rename approach for chunked copy; the stat-based reservation still has a TOCTOU window, but failures are surfaced rather than silently clobbered.
src/ops/file_ops/delete.rs Correct Windows directory-symlink removal via remove_symlink helper; also avoids an extra symlink_metadata syscall by using DirEntry::file_type().
src/ops/file_ops/copy.rs Switches symlink copy from reserve_temp_file_for + remove_file to reserve_temp_path_for to avoid the create-then-delete placeholder pattern; change is clean.
src/app/debug_log.rs Switches from try_lock (drop-on-contention) to blocking lock, ensuring log messages are never silently dropped, but introduces a potential stall on the main event-loop thread if I/O is slow.
src/ops/search/pattern.rs Removes the SmallCharBuf ring-buffer and simplifies case-insensitive search to a Vec<char> window scan; correct and much simpler, but allocates O(n) per haystack on every call.
src/app/types/file_entry.rs Fixes size-formatting rounding (1023.9 KB → 1.0 MB boundary), pre-1970 timestamps via DateTime::<Local>::from, Arc dedup for owner/group, and delegates compute_category to file_type::category.
src/app/types/app_state.rs Removes five dead drag fields, adds hotlist() getter to encapsulate direct field access, and simplifies reset_drag_state accordingly.
src/input/mouse.rs Replaces local dialog-geometry helpers with shared centered_rect to fix click-area mismatches; fixes dropdown scroll offset and uses set_selection_at for drag stats propagation.
src/app/watcher_sync.rs Adds 5 s cooldown for failed watcher canonicalize retries via WatcherSyncState, and resets cursor/scroll_offset on failed panel refresh.
src/main.rs Reduces poll timeout to 33 ms, extracts pre_draw to move wrap-layout computation out of the render path, and migrates to WatcherSyncState.
src/ui/viewer/render.rs Eliminates per-frame Text clone for image preview, reduces span lifetime annotations to '_, pre-allocates hex line vector with capacity, and moves wrap layout update out of render.
src/ui/dialogs/confirm.rs Changes max_rows to inner.height.saturating_sub(3) (removing the previous .max(3) floor), which can produce 0 on tiny terminals and silently hide the file list in confirmation dialogs.
src/ui/dialogs/layout.rs Removes the early full-area fallback for tiny terminals from centered_rect, instead clamping to minimum dimensions — aligns with the dialog hitbox fix in mouse.rs.
src/app/types/text_input.rs Fixes cursor position after inserting a combining or multi-codepoint character by counting grapheme clusters rather than incrementing by 1.
src/ops/helpers.rs Adds Windows inode-equivalent via MetadataExt::{volume_serial_number, file_index} for loop detection, with a correct not(any(unix, windows)) fallback.
src/ops/chunk_copy.rs Removes redundant BufReader/BufWriter wrappers (already using an explicit buffer) and moves the buffer to the heap via vec!; semantically equivalent but cleaner.
src/input/pickers.rs Adds Home/End key support to all four ListPicker handlers and consistently uses hotlist() getter instead of direct field access.
src/input/mode_dispatch.rs Fixes no-op menu dispatch that was resetting mode to Normal by saving full mode value (not just discriminant) and calling restore_prev_mode instead.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[move_entry / move_entry_with_progress] --> B{src.canonicalize == dest.canonicalize?}
    B -- No --> C[Normal rename / cross-device copy+delete path]
    B -- Yes --> D{src == dest?}
    D -- Yes --> E[Ok - no-op]
    D -- No --> F{src is symlink?}
    F -- No --> G[fs::rename - case-only rename]
    F -- Yes --> H["NEW CHECK (this PR)"]
    H --> I["Error: cannot move symlink onto its own target"]
    H -.->|"Missing: dest is also symlink?"| J["Should also allow: fs::rename\n(link1->real renamed over link2->real)"]

    style I fill:#f88,color:#000
    style J fill:#ffa,color:#000
    style H fill:#fca,color:#000
Loading

Comments Outside Diff (2)

  1. src/ops/file_ops/move_ops.rs, line 21-42 (link)

    P1 Block the self-overwrite only when dest is not itself a symlink. When dest is a symlink that resolves to the same real path, the rename is safe (replaces one symlink with another). This also restores case-only renames of symlinks on case-insensitive filesystems. The same fix is needed symmetrically in move_entry_with_progress.

  2. src/ops/search/pattern.rs, line 3-14 (link)

    P2 O(n) heap allocation per haystack in the hot search path

    The simplified implementation collects the entire lowercased haystack into a Vec<char> on every call. The removed SmallCharBuf was designed to avoid this allocation for small patterns by keeping state in a fixed-size stack ring buffer. For content search across large files this is called once per line, so the regression is per-line heap alloc + O(n) char collection before the window scan begins. If search performance on large directories or big files is important, consider using str::to_lowercase combined with a byte-level sliding window, or the memchr/aho-corasick crates which already handle Unicode folding without an explicit Vec.

Reviews (1): Last reviewed commit: "fix: address comprehensive audit finding..." | Re-trigger Greptile

Comment thread src/ops/file_ops/move_ops.rs Outdated
Comment thread src/app/debug_log.rs
Comment thread src/ops/file_ops/temp.rs Outdated
- watcher_sync: path-specific cooldown stores failed paths, bypasses on navigation
- watcher_sync: recalculate_selection_stats on failed directory refresh
- delete: Windows remove_symlink uses symlink_metadata for broken symlinks
- mime: platform-aware basename via Path::new().file_name() instead of rsplit('/')
- move_ops: symlink guard refined to only block src-symlink onto non-symlink dest
- file_entry: checked DateTime conversion returns None instead of panicking
- main: reorder let_chains to stable Rust if-let-first form
- copy: inline retry loop for symlink creation eliminates TOCTOU race
- temp: remove dead reserve_temp_path_for function
- pattern: ring buffer replaces Vec<char> allocation in case-insensitive search
- debug_log: document blocking lock behavior
@leszek3737 leszek3737 merged commit bdc1d77 into main May 26, 2026
5 checks passed
@leszek3737 leszek3737 deleted the fix57 branch May 26, 2026 03:26
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