reedline/painting/
prompt_lines.rs1use super::utils::{coerce_crlf, estimate_required_lines, line_width};
2use crate::{
3 menu::{Menu, ReedlineMenu},
4 prompt::PromptEditMode,
5 Prompt, PromptHistorySearch,
6};
7use std::borrow::Cow;
8
9#[derive(Debug)]
11pub(crate) struct PromptLines<'prompt> {
12 pub(crate) prompt_str_left: Cow<'prompt, str>,
13 pub(crate) prompt_str_right: Cow<'prompt, str>,
14 pub(crate) prompt_indicator: Cow<'prompt, str>,
15 pub(crate) before_cursor: Cow<'prompt, str>,
16 pub(crate) after_cursor: Cow<'prompt, str>,
17 pub(crate) hint: Cow<'prompt, str>,
18 pub(crate) right_prompt_on_last_line: bool,
19}
20
21impl<'prompt> PromptLines<'prompt> {
22 pub fn new(
26 prompt: &'prompt dyn Prompt,
27 prompt_mode: PromptEditMode,
28 history_indicator: Option<PromptHistorySearch>,
29 before_cursor: &'prompt str,
30 after_cursor: &'prompt str,
31 hint: &'prompt str,
32 ) -> Self {
33 let prompt_str_left = prompt.render_prompt_left();
34 let prompt_str_right = prompt.render_prompt_right();
35
36 let prompt_indicator = match history_indicator {
37 Some(prompt_search) => prompt.render_prompt_history_search_indicator(prompt_search),
38 None => prompt.render_prompt_indicator(prompt_mode),
39 };
40
41 let before_cursor = coerce_crlf(before_cursor);
42 let after_cursor = coerce_crlf(after_cursor);
43 let hint = coerce_crlf(hint);
44 let right_prompt_on_last_line = prompt.right_prompt_on_last_line();
45
46 Self {
47 prompt_str_left,
48 prompt_str_right,
49 prompt_indicator,
50 before_cursor,
51 after_cursor,
52 hint,
53 right_prompt_on_last_line,
54 }
55 }
56
57 pub(crate) fn required_lines(&self, terminal_columns: u16, menu: Option<&ReedlineMenu>) -> u16 {
61 let input = if menu.is_none() {
62 self.prompt_str_left.to_string()
63 + &self.prompt_indicator
64 + &self.before_cursor
65 + &self.after_cursor
66 + &self.hint
67 } else {
68 self.prompt_str_left.to_string()
69 + &self.prompt_indicator
70 + &self.before_cursor
71 + &self.after_cursor
72 };
73
74 let lines = estimate_required_lines(&input, terminal_columns);
75
76 if let Some(menu) = menu {
77 lines as u16 + menu.menu_required_lines(terminal_columns)
78 } else {
79 lines as u16
80 }
81 }
82
83 pub(crate) fn distance_from_prompt(&self, terminal_columns: u16) -> u16 {
86 let input = self.prompt_str_left.to_string() + &self.prompt_indicator + &self.before_cursor;
87 let lines = estimate_required_lines(&input, terminal_columns);
88 lines.saturating_sub(1) as u16
89 }
90
91 pub(crate) fn prompt_lines_with_wrap(&self, screen_width: u16) -> u16 {
93 let complete_prompt = self.prompt_str_left.to_string() + &self.prompt_indicator;
94 let lines = estimate_required_lines(&complete_prompt, screen_width);
95 lines.saturating_sub(1) as u16
96 }
97
98 pub(crate) fn estimate_right_prompt_line_width(&self, terminal_columns: u16) -> u16 {
100 let first_line_left_prompt = self.prompt_str_left.lines().next();
101 let last_line_left_prompt = self.prompt_str_left.lines().last();
102
103 let prompt_lines_total = self.before_cursor.to_string() + &self.after_cursor + &self.hint;
104 let prompt_lines_first = prompt_lines_total.lines().next();
105
106 let mut estimate = 0; if self.right_prompt_on_last_line {
109 if let Some(last_line_left_prompt) = last_line_left_prompt {
110 estimate += line_width(last_line_left_prompt);
111 estimate += line_width(&self.prompt_indicator);
112
113 if let Some(prompt_lines_first) = prompt_lines_first {
114 estimate += line_width(prompt_lines_first);
115 }
116 }
117 } else {
118 let required_lines = estimate_required_lines(&self.prompt_str_left, terminal_columns);
120 if let Some(first_line_left_prompt) = first_line_left_prompt {
121 estimate += line_width(first_line_left_prompt);
122 }
123
124 if required_lines == 1 {
126 estimate += line_width(&self.prompt_indicator);
127
128 if let Some(prompt_lines_first) = prompt_lines_first {
129 estimate += line_width(prompt_lines_first);
130 }
131 }
132 }
133
134 if estimate > u16::MAX as usize {
135 u16::MAX
136 } else {
137 estimate as u16
138 }
139 }
140}