mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
codex-rs(tui): restore EditPrompt arm and clear merge conflict markers in chat_composer
This commit is contained in:
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -779,6 +779,7 @@ dependencies = [
|
||||
"shlex",
|
||||
"strum 0.27.1",
|
||||
"strum_macros 0.27.1",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
|
||||
@@ -415,4 +415,8 @@ disable_mouse_capture = true # defaults to `false`
|
||||
# The composer will expand up to this many lines; additional content will enable
|
||||
# an internal scrollbar.
|
||||
composer_max_rows = 10 # defaults to `10`
|
||||
|
||||
# Command used to launch an external editor for composing the chat prompt.
|
||||
# Defaults to `$VISUAL`, then `$EDITOR`, falling back to `nvim`.
|
||||
editor = "${VISUAL:-${EDITOR:-nvim}}"
|
||||
```
|
||||
|
||||
@@ -95,17 +95,27 @@ pub struct Tui {
|
||||
/// an internal scrollbar.
|
||||
#[serde(default = "default_composer_max_rows")]
|
||||
pub composer_max_rows: usize,
|
||||
/// Command used to launch the external editor for editing the chat prompt.
|
||||
/// Defaults to the `VISUAL` or `EDITOR` environment variable, falling back to `nvim`.
|
||||
#[serde(default = "default_editor")]
|
||||
pub editor: String,
|
||||
}
|
||||
|
||||
fn default_composer_max_rows() -> usize {
|
||||
10
|
||||
}
|
||||
|
||||
/// Default editor: `$VISUAL`, then `$EDITOR`, falling back to `nvim`.
|
||||
fn default_editor() -> String {
|
||||
std::env::var("VISUAL").or_else(|_| std::env::var("EDITOR")).unwrap_or_else(|_| "nvim".into())
|
||||
}
|
||||
|
||||
impl Default for Tui {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disable_mouse_capture: Default::default(),
|
||||
composer_max_rows: default_composer_max_rows(),
|
||||
editor: default_editor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ serde_json = { version = "1", features = ["preserve_order"] }
|
||||
shlex = "1.3.0"
|
||||
strum = "0.27.1"
|
||||
strum_macros = "0.27.1"
|
||||
tempfile = "3"
|
||||
tokio = { version = "1", features = [
|
||||
"io-std",
|
||||
"macros",
|
||||
|
||||
@@ -383,6 +383,9 @@ impl<'a> App<'a> {
|
||||
tracing::error!("Failed to toggle mouse mode: {e}");
|
||||
}
|
||||
}
|
||||
SlashCommand::EditPrompt => {
|
||||
// External-editor prompt handled inline by the composer; no-op here.
|
||||
}
|
||||
SlashCommand::Quit => {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -130,30 +130,8 @@ impl ChatComposer<'_> {
|
||||
}
|
||||
(InputResult::None, true)
|
||||
}
|
||||
Input {
|
||||
key: Key::Enter,
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
} => {
|
||||
Input { key: Key::Enter, shift: false, alt: false, ctrl: false } => {
|
||||
if let Some(cmd) = popup.selected_command() {
|
||||
let first_line = self.textarea.lines().first().map_or("", |v| v).to_string();
|
||||
let args = first_line
|
||||
.strip_prefix(&format!("/{}", cmd.command()))
|
||||
.unwrap_or("")
|
||||
.trim();
|
||||
if (*cmd == SlashCommand::MountAdd || *cmd == SlashCommand::MountRemove)
|
||||
&& !args.is_empty()
|
||||
{
|
||||
let evt = if *cmd == SlashCommand::MountAdd {
|
||||
AppEvent::InlineMountAdd(args.to_string())
|
||||
} else {
|
||||
AppEvent::InlineMountRemove(args.to_string())
|
||||
};
|
||||
self.app_event_tx.send(evt);
|
||||
} else {
|
||||
self.app_event_tx.send(AppEvent::DispatchCommand(*cmd));
|
||||
}
|
||||
self.textarea.select_all();
|
||||
self.textarea.cut();
|
||||
self.command_popup = None;
|
||||
@@ -214,18 +192,16 @@ impl ChatComposer<'_> {
|
||||
(InputResult::Submitted(text), true)
|
||||
}
|
||||
}
|
||||
Input {
|
||||
key: Key::Enter, ..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('j'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
} => {
|
||||
Input { key: Key::Enter, .. }
|
||||
| Input { key: Key::Char('j'), ctrl: true, alt: false, shift: false } => {
|
||||
self.textarea.insert_newline();
|
||||
(InputResult::None, true)
|
||||
}
|
||||
Input { key: Key::Char('e'), ctrl: true, alt: false, shift: false } => {
|
||||
// Launch external editor for prompt drafting
|
||||
self.open_external_editor();
|
||||
(InputResult::None, true)
|
||||
}
|
||||
input => self.handle_input_basic(input),
|
||||
}
|
||||
}
|
||||
@@ -236,6 +212,40 @@ impl ChatComposer<'_> {
|
||||
(InputResult::None, true)
|
||||
}
|
||||
|
||||
/// Launch an external editor on a temporary file pre-populated with the current draft,
|
||||
/// then reload the edited contents back into the textarea on exit.
|
||||
pub fn open_external_editor(&mut self) {
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
// Dump current draft to a temp file
|
||||
let content = self.textarea.lines().join("\n");
|
||||
let mut tmp = match tempfile::NamedTempFile::new() {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
tracing::error!("failed to create temp file for editor: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = write!(tmp, "{}", content) {
|
||||
tracing::error!("failed to write to temp file for editor: {e}");
|
||||
return;
|
||||
}
|
||||
let path = tmp.path();
|
||||
// Determine editor: VISUAL > EDITOR > nvim
|
||||
let editor = std::env::var("VISUAL").or_else(|_| std::env::var("EDITOR")).unwrap_or_else(|_| "nvim".into());
|
||||
// Launch editor and wait for exit
|
||||
if let Err(e) = Command::new(editor).arg(path).status() {
|
||||
tracing::error!("failed to launch editor: {e}");
|
||||
return;
|
||||
}
|
||||
// Read back edited contents (fall back to original on error)
|
||||
let new_text = std::fs::read_to_string(path).unwrap_or(content);
|
||||
// Replace textarea contents
|
||||
self.textarea.select_all();
|
||||
self.textarea.cut();
|
||||
let _ = self.textarea.insert_str(new_text);
|
||||
}
|
||||
|
||||
/// Synchronize `self.command_popup` with the current text in the
|
||||
/// textarea. This must be called after every modification that can change
|
||||
/// the text so the popup is shown/updated/hidden as appropriate.
|
||||
|
||||
@@ -14,6 +14,8 @@ use strum_macros::IntoStaticStr;
|
||||
pub enum SlashCommand {
|
||||
New,
|
||||
ToggleMouseMode,
|
||||
/// Launch the external editor to edit the current prompt draft.
|
||||
EditPrompt,
|
||||
Quit,
|
||||
/// Add a dynamic mount (host path → container path).
|
||||
MountAdd,
|
||||
@@ -28,6 +30,8 @@ impl SlashCommand {
|
||||
SlashCommand::New => "Start a new chat.",
|
||||
SlashCommand::ToggleMouseMode =>
|
||||
"Toggle mouse mode (enable for scrolling, disable for text selection)",
|
||||
SlashCommand::EditPrompt =>
|
||||
"Open external editor to edit the current prompt.",
|
||||
SlashCommand::Quit => "Exit the application.",
|
||||
SlashCommand::MountAdd => "Add a mount: host path → container path.",
|
||||
SlashCommand::MountRemove => "Remove a mount by container path.",
|
||||
|
||||
Reference in New Issue
Block a user