Skip to content

Fix theme config, command line, search, and sorting regressions#42

Merged
leszek3737 merged 6 commits into
mainfrom
fix30
May 17, 2026
Merged

Fix theme config, command line, search, and sorting regressions#42
leszek3737 merged 6 commits into
mainfrom
fix30

Conversation

@leszek3737

@leszek3737 leszek3737 commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

Fix multiple regressions around theming, search, sorting, configuration persistence, and command-line activation while introducing a more explicit theme palette API and icon theme support.

New Features:

  • Add configurable icon theme support with Emoji, ASCII, and Nerd Font variants, including CLI and menu integration for command-line access.
  • Expose theme color palette and new *_with_colors/_with_palette APIs to allow rendering code to work with explicit palettes instead of hidden globals.

Bug Fixes:

  • Ensure non-UTF-8 panel paths and hotlist entries are skipped instead of being serialized with replacement characters, preventing corrupted setup persistence.
  • Fix file content search to count skipped oversized or invalid lines so reported line numbers remain accurate.
  • Remove mutable natural-sort caching from file entries so sorting no longer affects model equality or relies on internal cache state.
  • Correct Command Line activation from Alt+X and the Command menu so stale previous mode state is cleared and the command buffer is properly reset.
  • Stabilize editor launching by using robust shell-style splitting of the EDITOR command instead of naive whitespace splitting.
  • Improve watcher synchronization so directory removals/renames update panel paths and contents without leaving stale sync flags.

Enhancements:

  • Refactor theme handling to store color palettes in AppState, eliminate global render state, and route all rendering paths through explicit palette-aware functions.
  • Add real Nerd Font icon mappings and centralize icon lookup logic, including ASCII fallbacks and consistent display width handling.
  • Extend viewer, panels, dialogs, directory tree, menu, and loading UI to accept explicit ColorPalette and icon theme parameters, improving testability and future theme customization.
  • Harden directory tree and panel refresh logic by rebuilding path indices deterministically and keeping unfiltered entry state in sync with filesystem changes.

Build:

  • Add shlex dependency for robust parsing of configured external editor commands.

Tests:

  • Add focused tests for theme icon_theme parsing and mapping, search content line-number correctness when skipping long lines, non-UTF-8 path persistence behavior, Alt+X/menu command-line activation, and new menu command wiring.

…h ASCII fallback

- Move theme ColorPalette from static RwLock into AppState.theme_colors
- Thread &ColorPalette through all render functions (panels, dialogs, viewer, etc.)
- Add IconTheme enum (Emoji/Ascii/NerdFont) with serde config support
- Add ASCII fallback icons for each file category
- Update all 50+ Theme::*() accessors to take &ColorPalette parameter
- Remove dead needs_watcher_sync field (set but never read)
- Reset unfiltered_dirty=false after refresh_panel/full_refresh_panel
- Preserve non-UTF-8 config paths via to_string_lossy()
- Parse $EDITOR with shlex::split for quoted-arg support
- Switch watcher upsert from O(n) .find() to O(1) path_index.get()
- Replace BufRead::split with read_until + reusable buffer in content search
- Cache natsort_key result in FileEntry to avoid per-sort recomputation
- Add FileEntry::size() docstring
- Wire Alt+X binding + menu entry to open empty command line
@sourcery-ai

sourcery-ai Bot commented May 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors theme handling to remove global state and introduce palette-aware rendering APIs, adds configurable icon themes (Emoji/ASCII/Nerd Font) with robust config parsing, fixes several regressions around path persistence, search content line numbering, natural sort stability, watcher sync, and command-line/menu behavior, and wires theme colors and icon theme through the rendering pipeline.

Sequence diagram for corrected content search line numbering

sequenceDiagram
    participant FS as FileSearch
    participant BR as BufReader
    participant OUT as SearchOutcome

    FS->>BR: new(file)
    FS->>FS: line_no = 0
    FS->>FS: line_buf.clear()
    loop read_until EOF
        FS->>BR: read_until('\n', line_buf)
        alt bytes_read == 0
            BR-->>FS: Ok(0)
            FS->>FS: break
        else bytes_read > 0
            BR-->>FS: Ok(bytes_read)
            FS->>FS: line_no += 1
            FS->>FS: slice line (trim trailing '\n')
            alt [matches.len() >= MAX_CONTENT_RESULTS]
                FS->>OUT: set truncated = ContentResultLimit
                FS->>FS: return
            else [line contains NUL]
                FS->>OUT: set truncated = BinaryFile
                FS->>FS: return
            else [line.len() > MAX_CONTENT_LINE_BYTES]
                FS->>OUT: set truncated = LineTooLong (if None)
                FS->>FS: continue (skip long line, keep line_no)
            else [UTF-8 ok]
                FS->>FS: line_text = from_utf8(line)
                alt [pattern matches]
                    FS->>OUT: matches.push((path, line_no, line_text))
                end
            end
        else read error
            BR-->>FS: Err(err)
            FS->>OUT: errors.push(format!("Failed to read"))
            FS->>FS: return
        end
        FS->>FS: line_buf.clear()
    end
Loading

File-Level Changes

Change Details Files
Refactor theme system to use explicit ColorPalette state, support icon themes, and add _with_colors APIs while preserving existing callers.
  • Introduce IconTheme enum with robust string deserialization and default Emoji value, integrating it into ThemeConfig and ColorPalette.
  • Remove global THEME_COLORS RwLock, make DEFAULT_COLORS public, and change Theme::apply_from_value to populate an injected ColorPalette via apply_from_value_to_palette.
  • Add *_with_colors and color accessors that operate on ColorPalette instances while keeping existing static Theme methods delegating to DEFAULT_COLORS for backwards compatibility.
  • Update all UI rendering modules (panels, dialogs, viewer, dir_tree, menu, render) to accept ColorPalette (and IconTheme where needed) and thread theme_colors from AppState through render paths.
  • Extend tests to cover icon_theme defaults, alias parsing, icon mappings, and updated function signatures.
src/ui/theme.rs
src/ui/panels/mod.rs
src/ui/dialogs.rs
src/ui/viewer.rs
src/ui/dir_tree.rs
src/ui/menu.rs
src/render.rs
src/app/types.rs
src/main.rs
src/ui/panels/tests.rs
Add ASCII and Nerd Font icon maps and ensure panel rendering uses configured icon theme without breaking layout.
  • Define ASCII_ICONS and NERD_FONT_ICONS lookup tables and a shared find_icon helper.
  • Add get_file_icon_with_theme and thread IconTheme into icon_display_width, format_entry_line, and format_brief_entry_line.
  • Expose render_panel_with_colors and pass IconTheme from AppState.theme_colors.icon_theme in render_ui.
  • Update tests to call new helpers and verify emoji and Nerd Font mappings and widths.
src/ui/panels/mod.rs
src/render.rs
src/ui/theme.rs
src/ui/panels/tests.rs
Fix persistence of non-UTF-8 panel and hotlist paths by cleanly skipping invalid paths and add regression tests.
  • Extract helpers path_to_utf8_string and paths_to_utf8_strings used by PersistedSetup and panel_to_persisted.
  • Change Settings->PersistedSetup and panel_to_persisted to use these helpers rather than lossy serialization, logging skipped paths.
  • Add unix-only tests to assert that non-UTF8 paths are skipped while valid paths are preserved.
src/app/config.rs
Fix content search to correctly account for skipped long/invalid lines and maintain accurate 1-based line numbers.
  • Replace BufRead::split iteration with manual read_until loop tracking line_no explicitly.
  • Skip lines that are too long or invalid UTF-8 without incrementing matches but keeping the line counter accurate.
  • Add regression test ensuring a long first line followed by a matching second line yields line number 2 and LineTooLong truncation reason.
src/ops/search.rs
Make natural sort stable and non-mutating by removing per-entry cache and using pure key functions.
  • Introduce NaturalSortKey and ReverseNaturalSortKey type aliases and helper functions natural_sort_key and reverse_natural_sort_key.
  • Change SortMode::NaturalNameAsc/Desc to use these helpers with sort_by_cached_key rather than any mutable state on FileEntry.
src/ops/sorting.rs
Tighten watcher-driven panel refresh and path index handling to avoid desync and rely on centralized path index maintenance.
  • Remove redundant needs_watcher_sync flag updates when panel paths change; state now tracks refresh via other cues.
  • Expose ensure_path_index from fs::reader and use it in apply_watcher_upsert to look up existing entries via path_index instead of scanning the vector.
  • Ensure full_refresh_panel and refresh_panel reset unfiltered_dirty and clear path_index when rebuilding entries.
src/app/watcher_sync.rs
src/fs/reader.rs
src/app/types.rs
src/app/panel_ops.rs
Enhance command-line access via menu and Alt+X, and ensure stale prev_mode is cleared when entering CommandLine mode.
  • Add MenuAction::CommandLine, wire it into COMMAND_MENU_ACTIONS/MENU_ITEMS and implement behavior in execute_menu_action.
  • Bind Alt+X in keymap to open CommandLine, clearing input, cursor, history_index, and prev_mode, and add tests for Alt+X and menu path clearing prev_mode.
  • Adjust alt-unhandled test to use a truly unhandled key to avoid conflict with new Alt+X binding.
src/menu.rs
src/input/menu_actions.rs
src/app/keymap.rs
src/main.rs
src/tests.rs
Improve external editor spawning robustness by using shlex to parse the EDITOR command string.
  • Add shlex dependency in Cargo.toml and lockfile.
  • Parse the editor string with shlex::split, falling back to whitespace splitting and defaulting to vi when parsing fails or yields empty.
  • Use the parsed argv vector to determine the command and arguments when constructing the Command.
Cargo.toml
Cargo.lock
src/main.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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The logic for entering command-line mode is duplicated between handle_alt_keys (Alt+X) and MenuAction::CommandLine; consider extracting a small helper (e.g., AppState::enter_command_line_mode()) to centralize the state resets for command_line, command_cursor, history_index, and prev_mode.
  • Now that ColorPalette is carried in AppState::theme_colors, the static Theme::icon_theme() and other default-based helpers still read from DEFAULT_COLORS; if any new code starts using these static getters it will silently ignore the active theme, so it may be safer either to route them through AppState::theme_colors or clearly limit their visibility/usage to avoid future confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The logic for entering command-line mode is duplicated between `handle_alt_keys` (Alt+X) and `MenuAction::CommandLine`; consider extracting a small helper (e.g., `AppState::enter_command_line_mode()`) to centralize the state resets for `command_line`, `command_cursor`, `history_index`, and `prev_mode`.
- Now that `ColorPalette` is carried in `AppState::theme_colors`, the static `Theme::icon_theme()` and other default-based helpers still read from `DEFAULT_COLORS`; if any new code starts using these static getters it will silently ignore the active theme, so it may be safer either to route them through `AppState::theme_colors` or clearly limit their visibility/usage to avoid future confusion.

## Individual Comments

### Comment 1
<location path="src/ui/theme.rs" line_range="405-406" />
<code_context>
+        Ok(())
+    }
+
+    pub fn icon_theme() -> IconTheme {
+        DEFAULT_COLORS.icon_theme
+    }
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** `Theme::icon_theme()` ignores the runtime palette and always returns the compile-time default.

Other new APIs take a `&ColorPalette` so they can use `state.theme_colors`. This one reads `DEFAULT_COLORS` instead, so it will never reflect user configuration. If this is meant to expose the active icon theme, it should probably take a `&ColorPalette` (like the other `*_with_colors` helpers) or be removed to avoid misuse.

Suggested implementation:

```rust
        Ok(())
    }

    pub fn icon_theme(colors: &ColorPalette) -> IconTheme {
        colors.icon_theme
    }

```

1. Update all call sites of `Theme::icon_theme()` to pass a `&ColorPalette`, e.g. `Theme::icon_theme(&state.theme_colors)` or whatever palette is appropriate in that context.
2. If `ColorPalette` does not currently expose `icon_theme` as a public field or method, you will need to:
   - Either make the `icon_theme` field public (`pub icon_theme: IconTheme`), or
   - Add a method `impl ColorPalette { pub fn icon_theme(&self) -> IconTheme { self.icon_theme } }` and then change the body above to `colors.icon_theme()`.
</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/ui/theme.rs

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

Copy link
Copy Markdown

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 refactors the theme system to use instance-based ColorPalette management instead of global state, introduces IconTheme support (Emoji, ASCII, NerdFont), and adds a command-line mode triggered by Alt+X. It also integrates the shlex crate for safer command splitting and optimizes directory watcher updates using a path index. Review feedback identifies a potential panic in launch_editor when slicing empty command vectors and points out that apply_from_value currently discards its results. Further suggestions include hardening IconTheme deserialization against non-string types and addressing misleading static theme accessors that now only return default values.

Comment thread src/main.rs Outdated
Comment thread src/ui/theme.rs Outdated
Comment thread src/ui/theme.rs
Comment thread src/ui/theme.rs
@leszek3737
leszek3737 merged commit 284e29f into main May 17, 2026
5 checks passed
@leszek3737
leszek3737 deleted the fix30 branch May 17, 2026 23:27
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