Files
codex/codex-rs/tui/src/file_search.rs
T
Michael Bolin b77fe8fefe Apply argument comment lint across codex-rs (#14652)
## Why

Once the repo-local lint exists, `codex-rs` needs to follow the
checked-in convention and CI needs to keep it from drifting. This commit
applies the fallback `/*param*/` style consistently across existing
positional literal call sites without changing those APIs.

The longer-term preference is still to avoid APIs that require comments
by choosing clearer parameter types and call shapes. This PR is
intentionally the mechanical follow-through for the places where the
existing signatures stay in place.

After rebasing onto newer `main`, the rollout also had to cover newly
introduced `tui_app_server` call sites. That made it clear the first cut
of the CI job was too expensive for the common path: it was spending
almost as much time installing `cargo-dylint` and re-testing the lint
crate as a representative test job spends running product tests. The CI
update keeps the full workspace enforcement but trims that extra
overhead from ordinary `codex-rs` PRs.

## What changed

- keep a dedicated `argument_comment_lint` job in `rust-ci`
- mechanically annotate remaining opaque positional literals across
`codex-rs` with exact `/*param*/` comments, including the rebased
`tui_app_server` call sites that now fall under the lint
- keep the checked-in style aligned with the lint policy by using
`/*param*/` and leaving string and char literals uncommented
- cache `cargo-dylint`, `dylint-link`, and the relevant Cargo
registry/git metadata in the lint job
- split changed-path detection so the lint crate's own `cargo test` step
runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes
- continue to run the repo wrapper over the `codex-rs` workspace, so
product-code enforcement is unchanged

Most of the code changes in this commit are intentionally mechanical
comment rewrites or insertions driven by the lint itself.

## Verification

- `./tools/argument-comment-lint/run.sh --workspace`
- `cargo test -p codex-tui-app-server -p codex-tui`
- parsed `.github/workflows/rust-ci.yml` locally with PyYAML

---

* -> #14652
* #14651
2026-03-16 16:48:15 -07:00

134 lines
3.9 KiB
Rust

//! Session-based orchestration for `@` file searches.
//!
//! `ChatComposer` publishes every change of the `@token` as
//! `AppEvent::StartFileSearch(query)`. This manager owns a single
//! `codex-file-search` session for the current search root, updates the query
//! on every keystroke, and drops the session when the query becomes empty.
use codex_file_search as file_search;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender;
pub(crate) struct FileSearchManager {
state: Arc<Mutex<SearchState>>,
search_dir: PathBuf,
app_tx: AppEventSender,
}
struct SearchState {
latest_query: String,
session: Option<file_search::FileSearchSession>,
session_token: usize,
}
impl FileSearchManager {
pub fn new(search_dir: PathBuf, tx: AppEventSender) -> Self {
Self {
state: Arc::new(Mutex::new(SearchState {
latest_query: String::new(),
session: None,
session_token: 0,
})),
search_dir,
app_tx: tx,
}
}
/// Updates the directory used for file searches.
/// This should be called when the session's CWD changes on resume.
/// Drops the current session so it will be recreated with the new directory on next query.
pub fn update_search_dir(&mut self, new_dir: PathBuf) {
self.search_dir = new_dir;
#[expect(clippy::unwrap_used)]
let mut st = self.state.lock().unwrap();
st.session.take();
st.latest_query.clear();
}
/// Call whenever the user edits the `@` token.
pub fn on_user_query(&self, query: String) {
#[expect(clippy::unwrap_used)]
let mut st = self.state.lock().unwrap();
if query == st.latest_query {
return;
}
st.latest_query.clear();
st.latest_query.push_str(&query);
if query.is_empty() {
st.session.take();
return;
}
if st.session.is_none() {
self.start_session_locked(&mut st);
}
if let Some(session) = st.session.as_ref() {
session.update_query(&query);
}
}
fn start_session_locked(&self, st: &mut SearchState) {
st.session_token = st.session_token.wrapping_add(1);
let session_token = st.session_token;
let reporter = Arc::new(TuiSessionReporter {
state: self.state.clone(),
app_tx: self.app_tx.clone(),
session_token,
});
let session = file_search::create_session(
vec![self.search_dir.clone()],
file_search::FileSearchOptions {
compute_indices: true,
..Default::default()
},
reporter,
/*cancel_flag*/ None,
);
match session {
Ok(session) => st.session = Some(session),
Err(err) => {
tracing::warn!("file search session failed to start: {err}");
st.session = None;
}
}
}
}
struct TuiSessionReporter {
state: Arc<Mutex<SearchState>>,
app_tx: AppEventSender,
session_token: usize,
}
impl TuiSessionReporter {
fn send_snapshot(&self, snapshot: &file_search::FileSearchSnapshot) {
#[expect(clippy::unwrap_used)]
let st = self.state.lock().unwrap();
if st.session_token != self.session_token
|| st.latest_query.is_empty()
|| snapshot.query.is_empty()
{
return;
}
let query = snapshot.query.clone();
drop(st);
self.app_tx.send(AppEvent::FileSearchResult {
query,
matches: snapshot.matches.clone(),
});
}
}
impl file_search::SessionReporter for TuiSessionReporter {
fn on_update(&self, snapshot: &file_search::FileSearchSnapshot) {
self.send_snapshot(snapshot);
}
fn on_complete(&self) {}
}