- Applies repo-wide. Keep changes scoped; do not create issues or pull requests, or post comments.
- Rust 2024 Cargo workspace containing all
yazi-*crates; default members areyazi-fm(yazi) andyazi-cli(ya).
- Follow nearby code and use idiomatic Rust and Lua. In Rust, treat directories as modules and files as types: always prefer
mod_pub!for directory exports andmod_flat!for file exports. Keepmod.rsandlib.rslimited to exports; put module-wide implementation in a same-named file (for example,core/core.rs). Rust usessnake_casefor modules, functions, and fields andPascalCasefor types, traits, and variants. Lua uses PascalCase component tables,local Mplugin modules,snake_casemethods/locals, and_nameprivate fields. - Preserve established terms and type families:
Url/UrlBuf/UrlCow,PathDyn/PathBufDyn/PathCow,*Ref,*Arc,*Opt,*State,*Job,*Prog,File,Folder,Tab,Mgr, andTask. UseUrlfor logical locations andPathfor filesystem paths. key()identifies a file-list entry;urn()is the raw URL path tail. Usekey()for list state andurn()for filesystem-path semantics; do not substitute them mechanically.- Reuse established plugin and event names (
fetch,preload,peek,seek,spot,entry,setup,yank,hover, andselect) across Rust, Lua, and configuration. - Use Rust prefixes (
as_,to_,into_,try_,is_,has_) according to their usual semantics; prefer descriptive names. - Name variables, modules, methods, and other symbols simply, elegantly, and expressively. Be creative while keeping names clear, consistent with established terminology, and idiomatic.
- When passing arguments, use the parameter's conversion traits directly (such as
Into<_>orAsRef<_>); avoid eager conversions like.to_string(),.to_owned(), and.as_ref()unless ownership, type inference, or semantics require them. At Lua boundaries, preferLuaStringtoString, useBorrowedByteswhen string semantics are unnecessary, and return binding errors as(nil, error)alongside a value (or(false, error)for no-value operations); successful calls return(value, nil)or(true, nil). Reserve directErrreturns for invalid API usage that violates the call contract and can be handled withpcall. - Let Rust infer types whenever the context is sufficient; when an annotation is needed, put it on the binding (
let value: Type = ...) instead of turbofishing the expression. - Prefer methods provided by
UrlLike,PathLike, orStrandLikedirectly on the original value (for example,buf.parent()overbuf.as_url().parent()), rather than converting it first withas_url(),dyn_path(), orto_strand(). - Prefer
&*valuefor dereferencing overAsRefwhen both are suitable. - Prefer general-purpose traits and conversion APIs already provided by the codebase or its dependencies over manual construction or adapter closures; for example, use
into_lua()where applicable. Unless method resolution is genuinely ambiguous, callvalue.method()instead ofTrait::method(value).
- Search and reuse first. Prefer established types, variants, helpers, and data structures over adding new wrappers or abstractions; introduce a new type only when it represents a genuinely distinct responsibility or invariant. For new features, extend existing infrastructure or data structures with general, reusable capabilities when that keeps the final code concise and the total type count small.
- Use
ghto read GitHub issues, pull requests, and their discussions. - For refactors, inspect the whole target module, its callers, and the surrounding lifecycle first. Understand the system's established assumptions before adding local safeguards; distinguish required invariants from acceptable compromises, and ask when that boundary materially affects the design. Look for duplicated work, redundant I/O, underpowered return values, one-use wrappers, and reusable cross-platform abstractions; implement high-confidence, behavior-preserving simplifications while preserving error, fallback, and platform semantics.
- Prefer the simplest design that satisfies the requirements. Keep diffs minimal and avoid overengineering, unrelated refactors, speculative abstractions, and defensive handling for states the system already excludes. When conditions are mutually exclusive and have a clear priority, express them as flat, peer-level
if/else if/elsebranches in that priority order; do not compress the state checks into compound predicates merely to deduplicate a small result body. Prefer clear, flat control flow, expressions, and positive predicates; use standard combinators, early returns, ordered branches, and match guards to avoid nested conditionals, compound negation, and unnecessary wrapper syntax. Treat small, deliberate repetition in branch results as a good trade-off when it makes the decision structure easier to understand. Prefer available convenience macros when they simplify the code. When idiomatic and equivalent, prefer visually parallel forms such astrue as usizeoverusize::from(true). Comment only behavior the code cannot explain. - Keep responsibility boundaries clear and cohesive. Prefer pure functions, explicit invariants, and idempotent operations when repeated calls are natural and idempotency removes coordination or state. Favor convention over configuration when invariants can eliminate state or coordination. Put reusable code in the lowest suitable shared layer; place business-independent adapters and wrappers that mainly compensate for a dependency's limitations in
yazi-shim, and add that dependency there. Avoid unnecessary dependencies and allocations. Prefer borrowed values and existing wrappers. - Initialize crates explicitly from the application entrypoint in dependency order; a module must not initialize another module as a side effect.
- Use stable Rust APIs; nightly is formatting-only鈥攁pply Rust formatting directly with
rustfmt +nightly **/*.rs. Use onlypub,pub(super), andpub(crate)鈥攏everpub(in ...). - Keep async I/O non-blocking, preserve platform/fork behavior, and follow existing error boundaries with
?. - For renames or refactors, update all related variables, functions, parameters, modules, methods, types, derived types, exports, tests, configuration keys, documentation, Lua bindings, and, when a type and file share a name, the file as well; do not preserve renamed terms through aliases or re-exports.
- When adding a changelog entry, leave the PR number blank for a human to fill in.
- Do not add tests or change test behavior unless requested.
- Prefer targeted debug checks; use multiple
-pflags for affected crates before the whole workspace. - When investigating bugs, add temporary diagnostics when useful (
tracingin Rust andya.dbgin Lua), reproduce in a simulated terminal withYAZI_LOG=debug, and inspect the log file to pinpoint the cause. When debugging on a real terminal, use its IPC remote-control interface whenever supported. Remove temporary diagnostics before handoff.
cargo check -p <package>
cargo test -p <package>
cargo clippy -p <package>
rustfmt +nightly **/*.rs
stylua --color always --check .- Use
cargo checkinstead ofcargo buildunless artifacts are needed. Do not use--releaseunless requested; usescripts/build.sh <target>for release or cross-target packaging. - Run relevant existing tests when needed, then inspect
git diffand verify that only intended files changed.