Indicate unsaved work with U+25CF in the terminal title (#523)
Some checks failed
CI / check (ubuntu-latest) (push) Has been cancelled
CI / check (windows-latest) (push) Has been cancelled

Co-authored-by: Leonard Hecker <leonard@hecker.io>
This commit is contained in:
adamjoer 2025-07-02 18:23:36 +02:00 committed by GitHub
parent e9ad75685f
commit dd61854ad5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 13 deletions

View file

@ -168,13 +168,7 @@ fn run() -> apperr::Result<()> {
let scratch = scratch_arena(None); let scratch = scratch_arena(None);
let mut output = tui.render(&scratch); let mut output = tui.render(&scratch);
{ write_terminal_title(&mut output, &mut state);
let filename = state.documents.active().map_or("", |d| &d.filename);
if filename != state.osc_title_filename {
write_terminal_title(&mut output, filename);
state.osc_title_filename = filename.to_string();
}
}
if state.osc_clipboard_sync { if state.osc_clipboard_sync {
write_osc_clipboard(&mut tui, &mut state, &mut output); write_osc_clipboard(&mut tui, &mut state, &mut output);
@ -377,16 +371,30 @@ fn draw_handle_wants_exit(_ctx: &mut Context, state: &mut State) {
} }
} }
#[cold] fn write_terminal_title(output: &mut ArenaString, state: &mut State) {
fn write_terminal_title(output: &mut ArenaString, filename: &str) { let (filename, dirty) = state
output.push_str("\x1b]0;"); .documents
.active()
.map_or(("", false), |d| (&d.filename, d.buffer.borrow().is_dirty()));
if filename == state.osc_title_file_status.filename
&& dirty == state.osc_title_file_status.dirty
{
return;
}
output.push_str("\x1b]0;");
if !filename.is_empty() { if !filename.is_empty() {
if dirty {
output.push_str("");
}
output.push_str(&sanitize_control_chars(filename)); output.push_str(&sanitize_control_chars(filename));
output.push_str(" - "); output.push_str(" - ");
} }
output.push_str("edit\x1b\\"); output.push_str("edit\x1b\\");
state.osc_title_file_status.filename = filename.to_string();
state.osc_title_file_status.dirty = dirty;
} }
const LARGE_CLIPBOARD_THRESHOLD: usize = 128 * KIBI; const LARGE_CLIPBOARD_THRESHOLD: usize = 128 * KIBI;

View file

@ -120,6 +120,12 @@ pub enum StateEncodingChange {
Reopen, Reopen,
} }
#[derive(Default)]
pub struct OscTitleFileStatus {
pub filename: String,
pub dirty: bool,
}
pub struct State { pub struct State {
pub menubar_color_bg: u32, pub menubar_color_bg: u32,
pub menubar_color_fg: u32, pub menubar_color_fg: u32,
@ -161,7 +167,7 @@ pub struct State {
pub goto_target: String, pub goto_target: String,
pub goto_invalid: bool, pub goto_invalid: bool,
pub osc_title_filename: String, pub osc_title_file_status: OscTitleFileStatus,
pub osc_clipboard_sync: bool, pub osc_clipboard_sync: bool,
pub osc_clipboard_always_send: bool, pub osc_clipboard_always_send: bool,
pub exit: bool, pub exit: bool,
@ -209,7 +215,7 @@ impl State {
goto_target: Default::default(), goto_target: Default::default(),
goto_invalid: false, goto_invalid: false,
osc_title_filename: Default::default(), osc_title_file_status: Default::default(),
osc_clipboard_sync: false, osc_clipboard_sync: false,
osc_clipboard_always_send: false, osc_clipboard_always_send: false,
exit: false, exit: false,