mirror of
https://github.com/openai/codex.git
synced 2026-04-28 02:11:08 +03:00
code cleaning
This commit is contained in:
@@ -5,7 +5,6 @@ use crate::file_search::FileSearchManager;
|
||||
use crate::get_git_diff::get_git_diff;
|
||||
use crate::git_warning_screen::GitWarningOutcome;
|
||||
use crate::git_warning_screen::GitWarningScreen;
|
||||
use crate::slash_command::Command;
|
||||
use crate::tui;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::protocol::Event;
|
||||
@@ -29,6 +28,8 @@ use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::slash_command::SlashCommand;
|
||||
|
||||
/// Time window for debouncing redraw requests.
|
||||
const REDRAW_DEBOUNCE: Duration = Duration::from_millis(10);
|
||||
|
||||
@@ -340,7 +341,7 @@ impl App<'_> {
|
||||
AppState::GitWarning { .. } => {}
|
||||
},
|
||||
AppEvent::DispatchCommand(command) => match command {
|
||||
Command::New => {
|
||||
SlashCommand::New => {
|
||||
let new_widget = Box::new(ChatWidget::new(
|
||||
self.config.clone(),
|
||||
self.app_event_tx.clone(),
|
||||
@@ -351,16 +352,16 @@ impl App<'_> {
|
||||
self.app_state = AppState::Chat { widget: new_widget };
|
||||
self.app_event_tx.send(AppEvent::RequestRedraw);
|
||||
}
|
||||
Command::Compact => {
|
||||
SlashCommand::Compact => {
|
||||
if let AppState::Chat { widget } = &mut self.app_state {
|
||||
widget.clear_token_usage();
|
||||
self.app_event_tx.send(AppEvent::CodexOp(Op::Compact));
|
||||
}
|
||||
}
|
||||
Command::Quit => {
|
||||
SlashCommand::Quit => {
|
||||
break;
|
||||
}
|
||||
Command::Diff => {
|
||||
SlashCommand::Diff => {
|
||||
let (is_git_repo, diff_text) = match get_git_diff() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
@@ -382,7 +383,7 @@ impl App<'_> {
|
||||
}
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
Command::TestApproval => {
|
||||
SlashCommand::TestApproval => {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use codex_core::protocol::ApplyPatchApprovalRequestEvent;
|
||||
|
||||
@@ -3,7 +3,7 @@ use codex_file_search::FileMatch;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::text::Line;
|
||||
|
||||
use crate::slash_command::Command;
|
||||
use crate::slash_command::SlashCommand;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -33,7 +33,7 @@ pub(crate) enum AppEvent {
|
||||
|
||||
/// Dispatch a recognized slash command from the UI (composer) to the app
|
||||
/// layer so it can be handled centrally.
|
||||
DispatchCommand(Command),
|
||||
DispatchCommand(SlashCommand),
|
||||
|
||||
/// Kick off an asynchronous file search for the given query (text after
|
||||
/// the `@`). Previous searches may be cancelled by the app layer so there
|
||||
|
||||
@@ -19,7 +19,6 @@ use tui_textarea::TextArea;
|
||||
use super::chat_composer_history::ChatComposerHistory;
|
||||
use super::command_popup::CommandPopup;
|
||||
use super::file_search_popup::FileSearchPopup;
|
||||
use crate::slash_command::Command;
|
||||
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
@@ -56,7 +55,7 @@ pub(crate) struct ChatComposer<'a> {
|
||||
/// Popup state – at most one can be visible at any time.
|
||||
enum ActivePopup {
|
||||
None,
|
||||
Slash(CommandPopup<Command>),
|
||||
Slash(CommandPopup),
|
||||
File(FileSearchPopup),
|
||||
}
|
||||
|
||||
|
||||
@@ -12,38 +12,24 @@ use ratatui::widgets::Table;
|
||||
use ratatui::widgets::Widget;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
|
||||
use crate::slash_command::Command;
|
||||
use crate::slash_command::SlashCommand;
|
||||
use crate::slash_command::built_in_slash_commands;
|
||||
|
||||
pub trait CommandInfo: Copy {
|
||||
fn command(&self) -> &'static str;
|
||||
fn description(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl CommandInfo for Command {
|
||||
fn command(&self) -> &'static str {
|
||||
Command::command(*self)
|
||||
}
|
||||
fn description(&self) -> &'static str {
|
||||
Command::description(*self)
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_POPUP_ROWS: usize = 5;
|
||||
/// Ideally this is enough to show the longest command name.
|
||||
const FIRST_COLUMN_WIDTH: u16 = 20;
|
||||
|
||||
use ratatui::style::Modifier;
|
||||
|
||||
pub(crate) struct CommandPopup<C: CommandInfo> {
|
||||
pub(crate) struct CommandPopup {
|
||||
prefix: char,
|
||||
command_filter: String,
|
||||
all_commands: Vec<(&'static str, C)>,
|
||||
all_commands: Vec<(&'static str, SlashCommand)>,
|
||||
selected_idx: Option<usize>,
|
||||
}
|
||||
|
||||
impl<C: CommandInfo> CommandPopup<C> {
|
||||
pub(crate) fn new(prefix: char, all_commands: Vec<(&'static str, C)>) -> Self {
|
||||
impl CommandPopup {
|
||||
pub(crate) fn new(prefix: char, all_commands: Vec<(&'static str, SlashCommand)>) -> Self {
|
||||
Self {
|
||||
prefix,
|
||||
command_filter: String::new(),
|
||||
@@ -53,17 +39,25 @@ impl<C: CommandInfo> CommandPopup<C> {
|
||||
}
|
||||
|
||||
/// Update the filter string based on the current composer text. The text
|
||||
/// passed in is expected to start with this popup's prefix (e.g. '/' or '@').
|
||||
/// Everything after the prefix up to the first ASCII whitespace becomes
|
||||
/// the active filter that is used to narrow down the list of available commands.
|
||||
/// passed in is expected to start with a leading '/'. Everything after the
|
||||
/// *first* '/' on the *first* line becomes the active filter that is used
|
||||
/// to narrow down the list of available commands.
|
||||
pub(crate) fn on_composer_text_change(&mut self, text: String) {
|
||||
let first_line = text.lines().next().unwrap_or("");
|
||||
if first_line.starts_with(self.prefix) {
|
||||
let stripped = &first_line[self.prefix.len_utf8()..];
|
||||
|
||||
if let Some(stripped) = first_line.strip_prefix('/') {
|
||||
// Extract the *first* token (sequence of non-whitespace
|
||||
// characters) after the slash so that `/clear something` still
|
||||
// shows the help for `/clear`.
|
||||
let token = stripped.trim_start();
|
||||
let cmd_token = token.split_whitespace().next().unwrap_or("");
|
||||
// Update the filter keeping the original case (commands are all
|
||||
// lower-case for now but this may change in the future).
|
||||
self.command_filter = cmd_token.to_string();
|
||||
} else {
|
||||
// The composer no longer starts with '/'. Reset the filter so the
|
||||
// popup shows the *full* command list if it is still displayed
|
||||
// for some reason.
|
||||
self.command_filter.clear();
|
||||
}
|
||||
|
||||
@@ -84,7 +78,7 @@ impl<C: CommandInfo> CommandPopup<C> {
|
||||
|
||||
/// Return the list of commands that match the current filter. Matching is
|
||||
/// performed using a *prefix* comparison on the command name.
|
||||
fn filtered_commands(&self) -> Vec<&C> {
|
||||
fn filtered_commands(&self) -> Vec<&SlashCommand> {
|
||||
self.all_commands
|
||||
.iter()
|
||||
.filter_map(|(_name, cmd)| {
|
||||
@@ -98,7 +92,7 @@ impl<C: CommandInfo> CommandPopup<C> {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<&C>>()
|
||||
.collect::<Vec<&SlashCommand>>()
|
||||
}
|
||||
|
||||
/// Move the selection cursor one step up.
|
||||
@@ -138,24 +132,25 @@ impl<C: CommandInfo> CommandPopup<C> {
|
||||
}
|
||||
|
||||
/// Return currently selected command, if any.
|
||||
pub(crate) fn selected_command(&self) -> Option<&C> {
|
||||
pub(crate) fn selected_command(&self) -> Option<&SlashCommand> {
|
||||
let matches = self.filtered_commands();
|
||||
self.selected_idx.and_then(|idx| matches.get(idx).copied())
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandPopup<Command> {
|
||||
impl CommandPopup {
|
||||
pub(crate) fn slash() -> Self {
|
||||
CommandPopup::new('/', built_in_slash_commands())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CommandInfo> WidgetRef for CommandPopup<C> {
|
||||
impl WidgetRef for CommandPopup {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
let matches = self.filtered_commands();
|
||||
|
||||
let mut rows: Vec<Row> = Vec::new();
|
||||
let visible_matches: Vec<&C> = matches.into_iter().take(MAX_POPUP_ROWS).collect();
|
||||
let visible_matches: Vec<&SlashCommand> =
|
||||
matches.into_iter().take(MAX_POPUP_ROWS).collect();
|
||||
|
||||
if visible_matches.is_empty() {
|
||||
rows.push(Row::new(vec![
|
||||
|
||||
@@ -532,11 +532,6 @@ impl ChatWidget<'_> {
|
||||
&self.token_usage
|
||||
}
|
||||
|
||||
// pub(crate) fn add_background_event(&mut self, msg: String) {
|
||||
// self.add_to_history(HistoryCell::new_background_event(msg));
|
||||
// self.request_redraw();
|
||||
// }
|
||||
|
||||
pub(crate) fn clear_token_usage(&mut self) {
|
||||
self.token_usage = TokenUsage::default();
|
||||
self.bottom_pane
|
||||
|
||||
@@ -9,7 +9,7 @@ use strum_macros::IntoStaticStr;
|
||||
Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, EnumIter, AsRefStr, IntoStaticStr,
|
||||
)]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Command {
|
||||
pub enum SlashCommand {
|
||||
// DO NOT ALPHA-SORT! Enum order is presentation order in the popup, so
|
||||
// more frequently used commands should be listed first.
|
||||
New,
|
||||
@@ -20,16 +20,18 @@ pub enum Command {
|
||||
TestApproval,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
impl SlashCommand {
|
||||
/// User-visible description shown in the popup.
|
||||
pub fn description(self) -> &'static str {
|
||||
match self {
|
||||
Command::New => "Start a new chat.",
|
||||
Command::Compact => "Compact the chat history.",
|
||||
Command::Quit => "Exit the application.",
|
||||
Command::Diff => "Show git diff of the working directory (including untracked files)",
|
||||
SlashCommand::New => "Start a new chat.",
|
||||
SlashCommand::Compact => "Compact the chat history.",
|
||||
SlashCommand::Quit => "Exit the application.",
|
||||
SlashCommand::Diff => {
|
||||
"Show git diff of the working directory (including untracked files)"
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
Command::TestApproval => "Test approval request",
|
||||
SlashCommand::TestApproval => "Test approval request",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +43,6 @@ impl Command {
|
||||
}
|
||||
|
||||
/// Return all built-in commands in a Vec paired with their command string.
|
||||
pub fn built_in_slash_commands() -> Vec<(&'static str, Command)> {
|
||||
Command::iter().map(|c| (c.command(), c)).collect()
|
||||
pub fn built_in_slash_commands() -> Vec<(&'static str, SlashCommand)> {
|
||||
SlashCommand::iter().map(|c| (c.command(), c)).collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user