mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-02 05:41:16 -03:00
Port screen.cpp
This commit is contained in:
@@ -98,6 +98,7 @@ fn main() {
|
||||
"fish-rust/src/proc.rs",
|
||||
"fish-rust/src/reader.rs",
|
||||
"fish-rust/src/redirection.rs",
|
||||
"fish-rust/src/screen.rs",
|
||||
"fish-rust/src/signal.rs",
|
||||
"fish-rust/src/smoke.rs",
|
||||
"fish-rust/src/termsize.rs",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::wcstringutil::fish_wcwidth_visible;
|
||||
use crate::{screen::escape_code_length, wcstringutil::fish_wcwidth_visible};
|
||||
// Forward some imports to make subcmd implementations easier
|
||||
use super::prelude::*;
|
||||
|
||||
@@ -267,16 +267,6 @@ fn width_without_escapes(ins: &wstr, start_pos: usize) -> usize {
|
||||
return width as usize;
|
||||
}
|
||||
|
||||
fn escape_code_length(code: &wstr) -> Option<usize> {
|
||||
use crate::ffi::escape_code_length_ffi;
|
||||
use crate::wchar_ffi::wstr_to_u32string;
|
||||
|
||||
match escape_code_length_ffi(wstr_to_u32string(code).as_ptr()).into() {
|
||||
-1 => None,
|
||||
n => Some(n as usize),
|
||||
}
|
||||
}
|
||||
|
||||
/// Empirically determined.
|
||||
/// This is probably down to some pipe buffer or some such,
|
||||
/// but too small means we need to call `read(2)` and str2wcstring a lot.
|
||||
|
||||
@@ -1008,8 +1008,15 @@ pub fn exit_without_destructors(code: libc::c_int) -> ! {
|
||||
unsafe { libc::_exit(code) };
|
||||
}
|
||||
|
||||
/// Save the shell mode on startup so we can restore them on exit.
|
||||
static SHELL_MODES: Lazy<Mutex<libc::termios>> = Lazy::new(|| Mutex::new(unsafe { mem::zeroed() }));
|
||||
pub fn shell_modes() -> &'static libc::termios {
|
||||
let modes = crate::ffi::shell_modes_ffi() as *const libc::termios;
|
||||
unsafe { &*modes }
|
||||
}
|
||||
|
||||
pub fn shell_modes_mut() -> &'static mut libc::termios {
|
||||
let modes = crate::ffi::shell_modes_ffi() as *mut libc::termios;
|
||||
unsafe { &mut *modes }
|
||||
}
|
||||
|
||||
/// The character to use where the text has been truncated. Is an ellipsis on unicode system and a $
|
||||
/// on other systems.
|
||||
|
||||
@@ -113,18 +113,37 @@ pub struct Term {
|
||||
pub exit_underline_mode: Option<CString>,
|
||||
pub enter_reverse_mode: Option<CString>,
|
||||
pub enter_standout_mode: Option<CString>,
|
||||
pub exit_standout_mode: Option<CString>,
|
||||
pub enter_blink_mode: Option<CString>,
|
||||
pub enter_protected_mode: Option<CString>,
|
||||
pub enter_shadow_mode: Option<CString>,
|
||||
pub exit_shadow_mode: Option<CString>,
|
||||
pub enter_secure_mode: Option<CString>,
|
||||
pub enter_alt_charset_mode: Option<CString>,
|
||||
pub exit_alt_charset_mode: Option<CString>,
|
||||
pub set_a_foreground: Option<CString>,
|
||||
pub set_foreground: Option<CString>,
|
||||
pub set_a_background: Option<CString>,
|
||||
pub set_background: Option<CString>,
|
||||
pub exit_attribute_mode: Option<CString>,
|
||||
pub set_title: Option<CString>,
|
||||
pub clear_screen: Option<CString>,
|
||||
pub cursor_up: Option<CString>,
|
||||
pub cursor_down: Option<CString>,
|
||||
pub cursor_left: Option<CString>,
|
||||
pub cursor_right: Option<CString>,
|
||||
pub parm_left_cursor: Option<CString>,
|
||||
pub parm_right_cursor: Option<CString>,
|
||||
pub clr_eol: Option<CString>,
|
||||
pub clr_eos: Option<CString>,
|
||||
|
||||
// Number capabilities
|
||||
pub max_colors: Option<usize>,
|
||||
pub init_tabs: Option<usize>,
|
||||
|
||||
// Flag/boolean capabilities
|
||||
pub eat_newline_glitch: bool,
|
||||
pub auto_right_margin: bool,
|
||||
}
|
||||
|
||||
impl Term {
|
||||
@@ -141,18 +160,37 @@ fn new() -> Self {
|
||||
exit_underline_mode: get_str_cap("ue"),
|
||||
enter_reverse_mode: get_str_cap("mr"),
|
||||
enter_standout_mode: get_str_cap("so"),
|
||||
exit_standout_mode: get_str_cap("se"),
|
||||
enter_blink_mode: get_str_cap("mb"),
|
||||
enter_protected_mode: get_str_cap("mp"),
|
||||
enter_shadow_mode: get_str_cap("ZM"),
|
||||
exit_shadow_mode: get_str_cap("ZU"),
|
||||
enter_secure_mode: get_str_cap("mk"),
|
||||
enter_alt_charset_mode: get_str_cap("as"),
|
||||
exit_alt_charset_mode: get_str_cap("ae"),
|
||||
set_a_foreground: get_str_cap("AF"),
|
||||
set_foreground: get_str_cap("Sf"),
|
||||
set_a_background: get_str_cap("AB"),
|
||||
set_background: get_str_cap("Sb"),
|
||||
exit_attribute_mode: get_str_cap("me"),
|
||||
set_title: get_str_cap("ts"),
|
||||
clear_screen: get_str_cap("cl"),
|
||||
cursor_up: get_str_cap("up"),
|
||||
cursor_down: get_str_cap("do"),
|
||||
cursor_left: get_str_cap("le"),
|
||||
cursor_right: get_str_cap("nd"),
|
||||
parm_left_cursor: get_str_cap("LE"),
|
||||
parm_right_cursor: get_str_cap("RI"),
|
||||
clr_eol: get_str_cap("ce"),
|
||||
clr_eos: get_str_cap("cd"),
|
||||
|
||||
// Number capabilities
|
||||
max_colors: get_num_cap("Co"),
|
||||
init_tabs: get_num_cap("it"),
|
||||
|
||||
// Flag/boolean capabilities
|
||||
eat_newline_glitch: get_flag_cap("xn"),
|
||||
auto_right_margin: get_flag_cap("am"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,6 +305,16 @@ fn get_flag_cap(code: &str) -> bool {
|
||||
[code[0] as c_char, code[1] as c_char, b'\0' as c_char]
|
||||
}
|
||||
|
||||
/// Covers over tparm().
|
||||
pub fn tparm0(cap: &CStr) -> Option<CString> {
|
||||
// Take the lock because tparm races with del_curterm, etc.
|
||||
let _term: std::sync::MutexGuard<Option<Arc<Term>>> = TERM.lock().unwrap();
|
||||
assert!(!cap.to_bytes().is_empty());
|
||||
let cap_ptr = cap.as_ptr() as *mut libc::c_char;
|
||||
// Safety: we're trusting tparm here.
|
||||
unsafe { try_ptr_to_cstr(tparm(cap_ptr)) }
|
||||
}
|
||||
|
||||
/// Covers over tparm().
|
||||
pub fn tparm1(cap: &CStr, param1: i32) -> Option<CString> {
|
||||
// Take the lock because tparm races with del_curterm, etc.
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
use crate::input_common::{update_wait_on_escape_ms, update_wait_on_sequence_key_ms};
|
||||
use crate::output::ColorSupport;
|
||||
use crate::proc::is_interactive_session;
|
||||
use crate::screen::screen_set_midnight_commander_hack;
|
||||
use crate::screen::LAYOUT_CACHE_SHARED;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wchar_ffi::WCharToFFI;
|
||||
use crate::wutil::fish_wcstoi;
|
||||
@@ -578,7 +580,7 @@ fn apply_non_term_hacks(vars: &EnvStack) {
|
||||
// broken if you do '\r' after it like we normally do.
|
||||
// See https://midnight-commander.org/ticket/4258.
|
||||
if vars.get(L!("MC_SID")).is_some() {
|
||||
crate::ffi::screen_set_midnight_commander_hack();
|
||||
screen_set_midnight_commander_hack();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +688,7 @@ fn init_curses(vars: &EnvStack) {
|
||||
|
||||
update_fish_color_support(vars);
|
||||
// Invalidate the cached escape sequences since they may no longer be valid.
|
||||
crate::ffi::screen_clear_layout_cache_ffi();
|
||||
unsafe { LAYOUT_CACHE_SHARED.lock().unwrap() }.clear();
|
||||
CURSES_INITIALIZED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "parser.h"
|
||||
#include "parse_util.h"
|
||||
#include "path.h"
|
||||
#include "pager.h"
|
||||
#include "proc.h"
|
||||
#include "reader.h"
|
||||
#include "screen.h"
|
||||
@@ -77,6 +78,7 @@
|
||||
|
||||
generate_pod!("pipes_ffi_t")
|
||||
|
||||
generate!("shell_modes_ffi")
|
||||
generate!("make_pipes_ffi")
|
||||
|
||||
generate!("log_extra_to_flog_file")
|
||||
@@ -103,14 +105,16 @@
|
||||
generate!("commandline_get_state_text_ffi")
|
||||
generate!("completion_apply_to_command_line")
|
||||
|
||||
generate!("pager_t")
|
||||
generate!("page_rendering_t")
|
||||
generate!("pager_set_term_size_ffi")
|
||||
generate!("pager_update_rendering_ffi")
|
||||
|
||||
generate!("get_history_variable_text_ffi")
|
||||
|
||||
generate_pod!("escape_string_style_t")
|
||||
|
||||
|
||||
generate!("screen_set_midnight_commander_hack")
|
||||
generate!("screen_clear_layout_cache_ffi")
|
||||
generate!("escape_code_length_ffi")
|
||||
generate!("reader_schedule_prompt_repaint")
|
||||
generate!("reader_change_history")
|
||||
generate!("reader_change_cursor_selection_mode")
|
||||
@@ -169,6 +173,8 @@ impl Repin for IoStreams<'_> {}
|
||||
impl Repin for wcstring_list_ffi_t {}
|
||||
impl Repin for rgb_color_t {}
|
||||
impl Repin for OutputStreamFfi<'_> {}
|
||||
impl Repin for pager_t {}
|
||||
impl Repin for page_rendering_t {}
|
||||
|
||||
pub use autocxx::c_int;
|
||||
pub use ffi::*;
|
||||
|
||||
25
fish-rust/src/future.rs
Normal file
25
fish-rust/src/future.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
//! stdlib backports
|
||||
|
||||
pub trait IsSomeAnd {
|
||||
type Type;
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_some_and(self, s: impl FnOnce(Self::Type) -> bool) -> bool;
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_none_or(self, s: impl FnOnce(Self::Type) -> bool) -> bool;
|
||||
}
|
||||
|
||||
impl<T> IsSomeAnd for Option<T> {
|
||||
type Type = T;
|
||||
fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool {
|
||||
match self {
|
||||
Some(v) => f(v),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
|
||||
match self {
|
||||
Some(v) => f(v),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,11 +130,11 @@ pub struct HighlightColorResolver {
|
||||
/// It maintains a cache with no invalidation mechanism. The lifetime of these should typically be
|
||||
/// one screen redraw.
|
||||
impl HighlightColorResolver {
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
/// \return an RGB color for a given highlight spec.
|
||||
fn resolve_spec(
|
||||
pub fn resolve_spec(
|
||||
&mut self,
|
||||
highlight: &HighlightSpec,
|
||||
is_background: bool,
|
||||
@@ -1718,6 +1718,7 @@ fn resolve_spec_ffi(
|
||||
}
|
||||
extern "Rust" {
|
||||
type HighlightSpecListFFI;
|
||||
fn new_highlight_spec_list() -> Box<HighlightSpecListFFI>;
|
||||
fn highlight_shell_ffi(
|
||||
bff: &CxxWString,
|
||||
ctx: &OperationContext<'_>,
|
||||
@@ -1732,6 +1733,7 @@ fn colorize_ffi(
|
||||
colors: &HighlightSpecListFFI,
|
||||
vars: &EnvStackRefFFI,
|
||||
) -> Vec<u8>;
|
||||
fn push(&mut self, highlight: &HighlightSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1743,8 +1745,17 @@ fn colorize_ffi(
|
||||
colorize(text.as_wstr(), &colors.0, &*vars.0)
|
||||
}
|
||||
|
||||
struct HighlightSpecListFFI(Vec<HighlightSpec>);
|
||||
#[derive(Default)]
|
||||
pub struct HighlightSpecListFFI(pub Vec<HighlightSpec>);
|
||||
|
||||
unsafe impl cxx::ExternType for HighlightSpecListFFI {
|
||||
type Id = cxx::type_id!("HighlightSpecListFFI");
|
||||
type Kind = cxx::kind::Opaque;
|
||||
}
|
||||
|
||||
fn new_highlight_spec_list() -> Box<HighlightSpecListFFI> {
|
||||
Box::default()
|
||||
}
|
||||
impl HighlightSpecListFFI {
|
||||
fn size(&self) -> usize {
|
||||
self.0.len()
|
||||
@@ -1752,6 +1763,9 @@ fn size(&self) -> usize {
|
||||
fn at(&self, index: usize) -> &HighlightSpec {
|
||||
&self.0[index]
|
||||
}
|
||||
fn push(&mut self, highlight: &HighlightSpec) {
|
||||
self.0.push(*highlight)
|
||||
}
|
||||
}
|
||||
fn highlight_shell_ffi(
|
||||
buff: &CxxWString,
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
mod flog;
|
||||
mod fork_exec;
|
||||
mod function;
|
||||
mod future;
|
||||
mod future_feature_flags;
|
||||
mod global_safety;
|
||||
mod highlight;
|
||||
@@ -85,6 +86,7 @@
|
||||
mod re;
|
||||
mod reader;
|
||||
mod redirection;
|
||||
mod screen;
|
||||
mod signal;
|
||||
mod smoke;
|
||||
mod termsize;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use crate::wchar::prelude::*;
|
||||
use bitflags::bitflags;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::io::{Result, Write};
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
@@ -406,13 +406,13 @@ fn flush_to(&mut self, fd: RawFd) {
|
||||
|
||||
/// Begins buffering. Output will not be automatically flushed until a corresponding
|
||||
/// end_buffering() call.
|
||||
fn begin_buffering(&mut self) {
|
||||
pub fn begin_buffering(&mut self) {
|
||||
self.buffer_count += 1;
|
||||
assert!(self.buffer_count > 0, "buffer_count overflow");
|
||||
}
|
||||
|
||||
/// Balance a begin_buffering() call.
|
||||
fn end_buffering(&mut self) {
|
||||
pub fn end_buffering(&mut self) {
|
||||
assert!(self.buffer_count > 0, "buffer_count underflow");
|
||||
self.buffer_count -= 1;
|
||||
self.maybe_flush();
|
||||
@@ -467,9 +467,9 @@ pub fn tputs(&mut self, str: &CStr) {
|
||||
|
||||
/// Convenience cover over tputs, in recognition of the fact that our Term has Optional fields.
|
||||
/// If `str` is Some, write it with tputs and return true. Otherwise, return false.
|
||||
pub fn tputs_if_some(&mut self, str: &Option<CString>) -> bool {
|
||||
pub fn tputs_if_some(&mut self, str: &Option<impl AsRef<CStr>>) -> bool {
|
||||
if let Some(str) = str {
|
||||
self.tputs(str);
|
||||
self.tputs(str.as_ref());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
||||
2019
fish-rust/src/screen.rs
Normal file
2019
fish-rust/src/screen.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
||||
mod parser;
|
||||
#[cfg(test)]
|
||||
mod redirection;
|
||||
mod screen;
|
||||
mod string_escape;
|
||||
#[cfg(test)]
|
||||
mod tokenizer;
|
||||
|
||||
238
fish-rust/src/tests/screen.rs
Normal file
238
fish-rust/src/tests/screen.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
use crate::common::get_ellipsis_char;
|
||||
use crate::ffi_tests::add_test;
|
||||
use crate::screen::{LayoutCache, PromptCacheEntry, PromptLayout};
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wcstringutil::join_strings;
|
||||
|
||||
add_test!("test_complete", || {
|
||||
let mut lc = LayoutCache::new();
|
||||
assert_eq!(lc.escape_code_length(L!("")), 0);
|
||||
assert_eq!(lc.escape_code_length(L!("abcd")), 0);
|
||||
assert_eq!(lc.escape_code_length(L!("\x1B[2J")), 4);
|
||||
assert_eq!(
|
||||
lc.escape_code_length(L!("\x1B[38;5;123mABC")),
|
||||
"\x1B[38;5;123m".len()
|
||||
);
|
||||
assert_eq!(lc.escape_code_length(L!("\x1B@")), 2);
|
||||
|
||||
// iTerm2 escape sequences.
|
||||
assert_eq!(
|
||||
lc.escape_code_length(L!("\x1B]50;CurrentDir=test/foo\x07NOT_PART_OF_SEQUENCE")),
|
||||
25
|
||||
);
|
||||
assert_eq!(
|
||||
lc.escape_code_length(L!("\x1B]50;SetMark\x07NOT_PART_OF_SEQUENCE")),
|
||||
13
|
||||
);
|
||||
assert_eq!(
|
||||
lc.escape_code_length(L!("\x1B]6;1;bg;red;brightness;255\x07NOT_PART_OF_SEQUENCE")),
|
||||
28
|
||||
);
|
||||
assert_eq!(
|
||||
lc.escape_code_length(L!("\x1B]Pg4040ff\x1B\\NOT_PART_OF_SEQUENCE")),
|
||||
12
|
||||
);
|
||||
assert_eq!(lc.escape_code_length(L!("\x1B]blahblahblah\x1B\\")), 16);
|
||||
assert_eq!(lc.escape_code_length(L!("\x1B]blahblahblah\x07")), 15);
|
||||
});
|
||||
|
||||
add_test!("test_layout_cache", || {
|
||||
let mut seqs = LayoutCache::new();
|
||||
|
||||
// Verify escape code cache.
|
||||
assert_eq!(seqs.find_escape_code(L!("abc")), 0);
|
||||
seqs.add_escape_code(L!("abc").to_owned());
|
||||
seqs.add_escape_code(L!("abc").to_owned());
|
||||
assert_eq!(seqs.esc_cache_size(), 1);
|
||||
assert_eq!(seqs.find_escape_code(L!("abc")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("abcd")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("abcde")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("xabcde")), 0);
|
||||
seqs.add_escape_code(L!("ac").to_owned());
|
||||
assert_eq!(seqs.find_escape_code(L!("abcd")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("acbd")), 2);
|
||||
seqs.add_escape_code(L!("wxyz").to_owned());
|
||||
assert_eq!(seqs.find_escape_code(L!("abc")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("abcd")), 3);
|
||||
assert_eq!(seqs.find_escape_code(L!("wxyz123")), 4);
|
||||
assert_eq!(seqs.find_escape_code(L!("qwxyz123")), 0);
|
||||
assert_eq!(seqs.esc_cache_size(), 3);
|
||||
seqs.clear();
|
||||
assert_eq!(seqs.esc_cache_size(), 0);
|
||||
assert_eq!(seqs.find_escape_code(L!("abcd")), 0);
|
||||
|
||||
let huge = usize::MAX;
|
||||
|
||||
// Verify prompt layout cache.
|
||||
for i in 0..LayoutCache::PROMPT_CACHE_MAX_SIZE {
|
||||
let input = i.to_wstring();
|
||||
assert!(!seqs.find_prompt_layout(&input, usize::MAX));
|
||||
seqs.add_prompt_layout(PromptCacheEntry {
|
||||
text: input.clone(),
|
||||
max_line_width: huge,
|
||||
trunc_text: input.clone(),
|
||||
layout: PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: i,
|
||||
last_line_width: 0,
|
||||
},
|
||||
});
|
||||
assert!(seqs.find_prompt_layout(&input, usize::MAX));
|
||||
assert_eq!(seqs.prompt_cache.front().unwrap().layout.max_line_width, i);
|
||||
}
|
||||
|
||||
let expected_evictee = 3;
|
||||
for i in 0..LayoutCache::PROMPT_CACHE_MAX_SIZE {
|
||||
if i != expected_evictee {
|
||||
assert!(seqs.find_prompt_layout(&i.to_wstring(), usize::MAX));
|
||||
assert_eq!(seqs.prompt_cache.front().unwrap().layout.max_line_width, i);
|
||||
}
|
||||
}
|
||||
|
||||
seqs.add_prompt_layout(PromptCacheEntry {
|
||||
text: "whatever".into(),
|
||||
max_line_width: huge,
|
||||
trunc_text: "whatever".into(),
|
||||
layout: PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 100,
|
||||
last_line_width: 0,
|
||||
},
|
||||
});
|
||||
assert!(!seqs.find_prompt_layout(&expected_evictee.to_wstring(), usize::MAX));
|
||||
assert!(seqs.find_prompt_layout(L!("whatever"), huge));
|
||||
assert_eq!(
|
||||
seqs.prompt_cache.front().unwrap().layout.max_line_width,
|
||||
100
|
||||
);
|
||||
});
|
||||
|
||||
add_test!("test_prompt_truncation", || {
|
||||
let mut cache = LayoutCache::new();
|
||||
let mut trunc = WString::new();
|
||||
|
||||
let ellipsis = || WString::from_chars([get_ellipsis_char()]);
|
||||
|
||||
// No truncation.
|
||||
let layout = cache.calc_prompt_layout(L!("abcd"), Some(&mut trunc), usize::MAX);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 4,
|
||||
last_line_width: 4,
|
||||
}
|
||||
);
|
||||
assert_eq!(trunc, L!("abcd"));
|
||||
|
||||
// Line break calculation.
|
||||
let layout = cache.calc_prompt_layout(
|
||||
L!(concat!(
|
||||
"0123456789ABCDEF\n",
|
||||
"012345\n",
|
||||
"0123456789abcdef\n",
|
||||
"xyz"
|
||||
)),
|
||||
Some(&mut trunc),
|
||||
80,
|
||||
);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![16, 23, 40],
|
||||
max_line_width: 16,
|
||||
last_line_width: 3,
|
||||
}
|
||||
);
|
||||
|
||||
// Basic truncation.
|
||||
let layout = cache.calc_prompt_layout(L!("0123456789ABCDEF"), Some(&mut trunc), 8);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 8,
|
||||
last_line_width: 8,
|
||||
},
|
||||
);
|
||||
assert_eq!(trunc, ellipsis() + L!("9ABCDEF"));
|
||||
|
||||
// Multiline truncation.
|
||||
let layout = cache.calc_prompt_layout(
|
||||
L!(concat!(
|
||||
"0123456789ABCDEF\n",
|
||||
"012345\n",
|
||||
"0123456789abcdef\n",
|
||||
"xyz"
|
||||
)),
|
||||
Some(&mut trunc),
|
||||
8,
|
||||
);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![8, 15, 24],
|
||||
max_line_width: 8,
|
||||
last_line_width: 3,
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
trunc,
|
||||
join_strings(
|
||||
&[
|
||||
ellipsis() + L!("9ABCDEF"),
|
||||
L!("012345").to_owned(),
|
||||
ellipsis() + L!("9abcdef"),
|
||||
L!("xyz").to_owned(),
|
||||
],
|
||||
'\n',
|
||||
),
|
||||
);
|
||||
|
||||
// Escape sequences are not truncated.
|
||||
let layout = cache.calc_prompt_layout(
|
||||
L!("\x1B]50;CurrentDir=test/foo\x07NOT_PART_OF_SEQUENCE"),
|
||||
Some(&mut trunc),
|
||||
4,
|
||||
);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 4,
|
||||
last_line_width: 4,
|
||||
},
|
||||
);
|
||||
assert_eq!(trunc, ellipsis() + L!("\x1B]50;CurrentDir=test/foo\x07NCE"));
|
||||
|
||||
// Newlines in escape sequences are skipped.
|
||||
let layout = cache.calc_prompt_layout(
|
||||
L!("\x1B]50;CurrentDir=\ntest/foo\x07NOT_PART_OF_SEQUENCE"),
|
||||
Some(&mut trunc),
|
||||
4,
|
||||
);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 4,
|
||||
last_line_width: 4,
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
trunc,
|
||||
ellipsis() + L!("\x1B]50;CurrentDir=\ntest/foo\x07NCE")
|
||||
);
|
||||
|
||||
// We will truncate down to one character if we have to.
|
||||
let layout = cache.calc_prompt_layout(L!("Yay"), Some(&mut trunc), 1);
|
||||
assert_eq!(
|
||||
layout,
|
||||
PromptLayout {
|
||||
line_breaks: vec![],
|
||||
max_line_width: 1,
|
||||
last_line_width: 1,
|
||||
},
|
||||
);
|
||||
assert_eq!(trunc, ellipsis());
|
||||
});
|
||||
Reference in New Issue
Block a user