feat: add a /mention slash command (#2114)

- To help people discover @mentions.
- Command just places a @ in the composer.
- #2115 then improves the behavior of @mentions with empty queries.
This commit is contained in:
ae
2025-08-11 14:15:41 -07:00
committed by GitHub
parent 5f8984aa7d
commit a48372ce5d
5 changed files with 62 additions and 0 deletions

View File

@@ -366,6 +366,11 @@ impl App<'_> {
widget.add_diff_output(text);
}
}
SlashCommand::Mention => {
if let AppState::Chat { widget } = &mut self.app_state {
widget.insert_str("@");
}
}
SlashCommand::Status => {
if let AppState::Chat { widget } = &mut self.app_state {
widget.add_status_output();

View File

@@ -198,6 +198,12 @@ impl ChatComposer {
self.set_has_focus(has_focus);
}
pub(crate) fn insert_str(&mut self, text: &str) {
self.textarea.insert_str(text);
self.sync_command_popup();
self.sync_file_search_popup();
}
/// Handle a key event coming from the main UI.
pub fn handle_key_event(&mut self, key_event: KeyEvent) -> (InputResult, bool) {
let result = match &mut self.active_popup {
@@ -1078,6 +1084,46 @@ mod tests {
}
}
#[test]
fn slash_mention_dispatches_command_and_inserts_at() {
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
use std::sync::mpsc::TryRecvError;
let (tx, rx) = std::sync::mpsc::channel();
let sender = AppEventSender::new(tx);
let mut composer = ChatComposer::new(true, sender, false);
for ch in ['/', 'm', 'e', 'n', 't', 'i', 'o', 'n'] {
let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Char(ch), KeyModifiers::NONE));
}
let (result, _needs_redraw) =
composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
match result {
InputResult::None => {}
InputResult::Submitted(text) => {
panic!("expected command dispatch, but composer submitted literal text: {text}")
}
}
assert!(composer.textarea.is_empty(), "composer should be cleared");
match rx.try_recv() {
Ok(AppEvent::DispatchCommand(cmd)) => {
assert_eq!(cmd.command(), "mention");
composer.insert_str("@");
}
Ok(_other) => panic!("unexpected app event"),
Err(TryRecvError::Empty) => panic!("expected a DispatchCommand event for '/mention'"),
Err(TryRecvError::Disconnected) => {
panic!("app event channel disconnected")
}
}
assert_eq!(composer.textarea.text(), "@");
}
#[test]
fn test_multiple_pastes_submission() {
use crossterm::event::KeyCode;

View File

@@ -196,6 +196,11 @@ impl BottomPane<'_> {
}
}
pub(crate) fn insert_str(&mut self, text: &str) {
self.composer.insert_str(text);
self.request_redraw();
}
/// Update the status indicator text. Prefer replacing the composer with
/// the StatusIndicatorView so the input pane shows a single-line status
/// like: `▌ Working waiting for model`.

View File

@@ -676,6 +676,10 @@ impl ChatWidget<'_> {
self.submit_user_message(text.into());
}
pub(crate) fn insert_str(&mut self, text: &str) {
self.bottom_pane.insert_str(text);
}
pub(crate) fn token_usage(&self) -> &TokenUsage {
&self.total_token_usage
}

View File

@@ -16,6 +16,7 @@ pub enum SlashCommand {
Init,
Compact,
Diff,
Mention,
Status,
Prompts,
Logout,
@@ -33,6 +34,7 @@ impl SlashCommand {
SlashCommand::Compact => "summarize conversation to prevent hitting the context limit",
SlashCommand::Quit => "exit Codex",
SlashCommand::Diff => "show git diff (including untracked files)",
SlashCommand::Mention => "mention a file",
SlashCommand::Status => "show current session configuration and token usage",
SlashCommand::Prompts => "show example prompts",
SlashCommand::Logout => "log out of Codex",