mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-14 17:07:44 +00:00
Attempt to truncate prompts that are too wide
Prior to this change, if the user's prompt was wider than the terminal, we would reduce it to just `> `. With this change, attempt to truncate the prompt. For each line of the prompt, calculate its width. If the width exceeds COLUMNS, prepend ellipsis to that line, and start removing characters until it fits. Escape sequences are skipped. Fixes #904
This commit is contained in:
parent
2c6e95ccf6
commit
a2ae2d6c36
4 changed files with 231 additions and 87 deletions
|
@ -61,6 +61,7 @@ Interactive improvements
|
|||
------------------------
|
||||
|
||||
- The prompt is reprinted after a background job exits (#1018).
|
||||
- Prompts whose width exceeds $COLUMNS will now be truncated instead of replaced with `"> "` (#904).
|
||||
|
||||
New or improved bindings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -5524,23 +5524,81 @@ void test_layout_cache() {
|
|||
do_test(seqs.esc_cache_size() == 0);
|
||||
do_test(seqs.find_escape_code(L"abcd") == 0);
|
||||
|
||||
auto huge = std::numeric_limits<size_t>::max();
|
||||
|
||||
// Verify prompt layout cache.
|
||||
for (size_t i = 0; i < layout_cache_t::prompt_cache_max_size; i++) {
|
||||
wcstring input = std::to_wstring(i);
|
||||
do_test(!seqs.find_prompt_layout(input));
|
||||
seqs.add_prompt_layout(input, {i, 0, 0});
|
||||
do_test(seqs.find_prompt_layout(input)->line_count == i);
|
||||
seqs.add_prompt_layout({input, huge, input, {i, 0, 0}});
|
||||
do_test(seqs.find_prompt_layout(input)->layout.line_count == i);
|
||||
}
|
||||
|
||||
size_t expected_evictee = 3;
|
||||
for (size_t i = 0; i < layout_cache_t::prompt_cache_max_size; i++) {
|
||||
if (i != expected_evictee)
|
||||
do_test(seqs.find_prompt_layout(std::to_wstring(i))->line_count == i);
|
||||
do_test(seqs.find_prompt_layout(std::to_wstring(i))->layout.line_count == i);
|
||||
}
|
||||
|
||||
seqs.add_prompt_layout(L"whatever", {100, 0, 0});
|
||||
seqs.add_prompt_layout({L"whatever", huge, L"whatever", {100, 0, 0}});
|
||||
do_test(!seqs.find_prompt_layout(std::to_wstring(expected_evictee)));
|
||||
do_test(seqs.find_prompt_layout(L"whatever")->line_count == 100);
|
||||
do_test(seqs.find_prompt_layout(L"whatever", huge)->layout.line_count == 100);
|
||||
}
|
||||
|
||||
void test_prompt_truncation() {
|
||||
layout_cache_t cache;
|
||||
wcstring trunc;
|
||||
prompt_layout_t layout;
|
||||
|
||||
/// Helper to return 'layout' formatted as a string for easy comparison.
|
||||
auto format_layout = [&] {
|
||||
return format_string(L"%lu,%lu,%lu", (unsigned long)layout.line_count,
|
||||
(unsigned long)layout.max_line_width,
|
||||
(unsigned long)layout.last_line_width);
|
||||
};
|
||||
|
||||
/// Join some strings with newline.
|
||||
auto join = [](std::initializer_list<wcstring> vals) { return join_strings(vals, L'\n'); };
|
||||
|
||||
wcstring ellipsis = {get_ellipsis_char()};
|
||||
|
||||
// No truncation.
|
||||
layout = cache.calc_prompt_layout(L"abcd", &trunc);
|
||||
do_test(format_layout() == L"1,4,4");
|
||||
do_test(trunc == L"abcd");
|
||||
|
||||
// Basic truncation.
|
||||
layout = cache.calc_prompt_layout(L"0123456789ABCDEF", &trunc, 8);
|
||||
do_test(format_layout() == L"1,8,8");
|
||||
do_test(trunc == ellipsis + L"9ABCDEF");
|
||||
|
||||
// Multiline truncation.
|
||||
layout = cache.calc_prompt_layout(join({
|
||||
L"0123456789ABCDEF", //
|
||||
L"012345", //
|
||||
L"0123456789abcdef", //
|
||||
L"xyz" //
|
||||
}),
|
||||
&trunc, 8);
|
||||
do_test(format_layout() == L"4,8,3");
|
||||
do_test(trunc == join({ellipsis + L"9ABCDEF", L"012345", ellipsis + L"9abcdef", L"xyz"}));
|
||||
|
||||
// Escape sequences are not truncated.
|
||||
layout =
|
||||
cache.calc_prompt_layout(L"\x1B]50;CurrentDir=test/foo\x07NOT_PART_OF_SEQUENCE", &trunc, 4);
|
||||
do_test(format_layout() == L"1,4,4");
|
||||
do_test(trunc == ellipsis + L"\x1B]50;CurrentDir=test/foo\x07NCE");
|
||||
|
||||
// Newlines in escape sequences are skipped.
|
||||
layout = cache.calc_prompt_layout(L"\x1B]50;CurrentDir=\ntest/foo\x07NOT_PART_OF_SEQUENCE",
|
||||
&trunc, 4);
|
||||
do_test(format_layout() == L"1,4,4");
|
||||
do_test(trunc == ellipsis + L"\x1B]50;CurrentDir=\ntest/foo\x07NCE");
|
||||
|
||||
// We will truncate down to one character if we have to.
|
||||
layout = cache.calc_prompt_layout(L"Yay", &trunc, 1);
|
||||
do_test(format_layout() == L"1,1,1");
|
||||
do_test(trunc == ellipsis);
|
||||
}
|
||||
|
||||
void test_normalize_path() {
|
||||
|
@ -5808,6 +5866,7 @@ int main(int argc, char **argv) {
|
|||
if (should_test_function("illegal_command_exit_code")) test_illegal_command_exit_code();
|
||||
if (should_test_function("maybe")) test_maybe();
|
||||
if (should_test_function("layout_cache")) test_layout_cache();
|
||||
if (should_test_function("prompt")) test_prompt_truncation();
|
||||
if (should_test_function("normalize")) test_normalize_path();
|
||||
if (should_test_function("topics")) test_topic_monitor();
|
||||
if (should_test_function("topics")) test_topic_monitor_torture();
|
||||
|
|
214
src/screen.cpp
214
src/screen.cpp
|
@ -257,64 +257,142 @@ size_t layout_cache_t::escape_code_length(const wchar_t *code) {
|
|||
return esc_seq_len;
|
||||
}
|
||||
|
||||
maybe_t<prompt_layout_t> layout_cache_t::find_prompt_layout(const wcstring &input) {
|
||||
auto start = prompt_cache_.begin();
|
||||
auto end = prompt_cache_.end();
|
||||
const layout_cache_t::prompt_cache_entry_t *layout_cache_t::find_prompt_layout(
|
||||
const wcstring &input, size_t max_line_width) {
|
||||
auto start = prompt_cache_.cbegin();
|
||||
auto end = prompt_cache_.cend();
|
||||
for (auto iter = start; iter != end; ++iter) {
|
||||
if (iter->first == input) {
|
||||
if (iter->text == input && iter->max_line_width == max_line_width) {
|
||||
// Found it. Move it to the front if not already there.
|
||||
if (iter != start) prompt_cache_.splice(start, prompt_cache_, iter);
|
||||
return iter->second;
|
||||
return &*prompt_cache_.begin();
|
||||
}
|
||||
}
|
||||
return none();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void layout_cache_t::add_prompt_layout(wcstring input, prompt_layout_t layout) {
|
||||
assert(!find_prompt_layout(input) && "Should not have a prompt layout for this input");
|
||||
prompt_cache_.emplace_front(std::move(input), layout);
|
||||
void layout_cache_t::add_prompt_layout(prompt_cache_entry_t entry) {
|
||||
prompt_cache_.emplace_front(std::move(entry));
|
||||
if (prompt_cache_.size() > prompt_cache_max_size) {
|
||||
prompt_cache_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate layout information for the given prompt. Does some clever magic to detect common
|
||||
/// escape sequences that may be embedded in a prompt, such as those to set visual attributes.
|
||||
/// escape sequences that may be embedded in a prompt, such as those to set visual attributes.
|
||||
static prompt_layout_t calc_prompt_layout(const wcstring &prompt_str, layout_cache_t &cache) {
|
||||
if (auto cached_layout = cache.find_prompt_layout(prompt_str)) {
|
||||
return *cached_layout;
|
||||
}
|
||||
/// \return whether \p c ends a measuring run.
|
||||
static bool is_run_terminator(wchar_t c) {
|
||||
return c == L'\0' || c == L'\n' || c == L'\r' || c == L'\f';
|
||||
}
|
||||
|
||||
prompt_layout_t prompt_layout = {1, 0, 0};
|
||||
size_t current_line_width = 0;
|
||||
|
||||
const wchar_t *prompt = prompt_str.c_str();
|
||||
for (size_t j = 0; prompt[j]; j++) {
|
||||
if (prompt[j] == L'\x1B') {
|
||||
// This is the start of an escape code. Skip over it if it's at least one char long.
|
||||
size_t len = cache.escape_code_length(&prompt[j]);
|
||||
if (len > 0) j += len - 1;
|
||||
} else if (prompt[j] == L'\t') {
|
||||
current_line_width = next_tab_stop(current_line_width);
|
||||
} else if (prompt[j] == L'\n' || prompt[j] == L'\f') {
|
||||
// PCA: At least one prompt uses \f\r as a newline. It's unclear to me what this is
|
||||
// meant to do, but terminals seem to treat it as a newline so we do the same.
|
||||
current_line_width = 0;
|
||||
prompt_layout.line_count += 1;
|
||||
} else if (prompt[j] == L'\r') {
|
||||
current_line_width = 0;
|
||||
/// Measure a run of characters in \p input starting at \p start.
|
||||
/// Stop when we reach a run terminator, and return its index in \p out_end (if not null).
|
||||
/// Note \0 is a run terminator so there will always be one.
|
||||
/// We permit escape sequences to have run terminators other than \0. That is, escape sequences may
|
||||
/// have embedded newlines, etc.; it's unclear if this is possible but we allow it.
|
||||
static size_t measure_run_from(const wchar_t *input, size_t start, size_t *out_end,
|
||||
layout_cache_t &cache) {
|
||||
size_t width = 0;
|
||||
size_t idx = start;
|
||||
for (idx = start; !is_run_terminator(input[idx]); idx++) {
|
||||
if (input[idx] == L'\x1B') {
|
||||
// This is the start of an escape code; we assume it has width 0.
|
||||
// -1 because we are going to increment in the loop.
|
||||
size_t len = cache.escape_code_length(&input[idx]);
|
||||
if (len > 0) idx += len - 1;
|
||||
} else if (input[idx] == L'\t') {
|
||||
width = next_tab_stop(width);
|
||||
} else {
|
||||
// Ordinary char. Add its width with care to ignore control chars which have width -1.
|
||||
current_line_width += fish_wcwidth_min_0(prompt[j]);
|
||||
if (current_line_width > prompt_layout.max_line_width) {
|
||||
prompt_layout.max_line_width = current_line_width;
|
||||
}
|
||||
width += fish_wcwidth_min_0(input[idx]);
|
||||
}
|
||||
}
|
||||
prompt_layout.last_line_width = current_line_width;
|
||||
cache.add_prompt_layout(prompt, prompt_layout);
|
||||
return prompt_layout;
|
||||
if (out_end) *out_end = idx;
|
||||
return width;
|
||||
}
|
||||
|
||||
/// Attempt to truncate the prompt run \p run, which has width \p width, to \p no more than
|
||||
/// desired_width. \return the resulting width and run by reference.
|
||||
static void truncate_run(wcstring *run, size_t desired_width, size_t *width,
|
||||
layout_cache_t &cache) {
|
||||
size_t curr_width = *width;
|
||||
if (curr_width <= desired_width) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bravely prepend ellipsis char and skip it.
|
||||
// Ellipsis is always width 1.
|
||||
wchar_t ellipsis = get_ellipsis_char();
|
||||
run->insert(0, 1, ellipsis); // index, count, char
|
||||
curr_width += 1;
|
||||
|
||||
// Start removing characters after ellipsis.
|
||||
// Note we modify 'run' inside this loop.
|
||||
size_t idx = 1;
|
||||
while (curr_width > desired_width && idx < run->size()) {
|
||||
wchar_t c = run->at(idx);
|
||||
assert(!is_run_terminator(c) && "Should not have run terminator inside run");
|
||||
if (c == L'\x1B') {
|
||||
size_t len = cache.escape_code_length(run->c_str() + idx);
|
||||
idx += std::max(len, (size_t)1);
|
||||
} else if (c == '\t') {
|
||||
// Tabs would seem to be quite annoying to measure while truncating.
|
||||
// We simply remove these and start over.
|
||||
run->erase(idx, 1);
|
||||
curr_width = measure_run_from(run->c_str(), 0, nullptr, cache);
|
||||
idx = 0;
|
||||
} else {
|
||||
size_t char_width = fish_wcwidth_min_0(c);
|
||||
curr_width -= std::min(curr_width, char_width);
|
||||
run->erase(idx, 1);
|
||||
}
|
||||
}
|
||||
*width = curr_width;
|
||||
}
|
||||
|
||||
prompt_layout_t layout_cache_t::calc_prompt_layout(const wcstring &prompt_str,
|
||||
wcstring *out_trunc_prompt,
|
||||
size_t max_line_width) {
|
||||
// FIXME: we could avoid allocating trunc_prompt if max_line_width is max.
|
||||
if (const auto *entry = this->find_prompt_layout(prompt_str, max_line_width)) {
|
||||
if (out_trunc_prompt) out_trunc_prompt->assign(entry->trunc_text);
|
||||
return entry->layout;
|
||||
}
|
||||
|
||||
size_t prompt_len = prompt_str.size();
|
||||
const wchar_t *prompt = prompt_str.c_str();
|
||||
|
||||
prompt_layout_t layout = {1, 0, 0};
|
||||
wcstring trunc_prompt;
|
||||
|
||||
size_t run_start = 0;
|
||||
while (run_start < prompt_len) {
|
||||
size_t run_end;
|
||||
size_t line_width = measure_run_from(prompt, run_start, &run_end, *this);
|
||||
if (line_width <= max_line_width) {
|
||||
// No truncation needed on this line.
|
||||
trunc_prompt.append(&prompt[run_start], run_end - run_start);
|
||||
} else {
|
||||
// Truncation needed on this line.
|
||||
wcstring run_storage(&prompt[run_start], run_end - run_start);
|
||||
truncate_run(&run_storage, max_line_width, &line_width, *this);
|
||||
trunc_prompt.append(run_storage);
|
||||
}
|
||||
layout.max_line_width = std::max(layout.max_line_width, line_width);
|
||||
layout.last_line_width = line_width;
|
||||
|
||||
wchar_t endc = prompt[run_end];
|
||||
if (endc) {
|
||||
layout.line_count += (endc == L'\n' || endc == L'\f');
|
||||
trunc_prompt.push_back(endc);
|
||||
run_start = run_end + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->add_prompt_layout(prompt_cache_entry_t{prompt, max_line_width, trunc_prompt, layout});
|
||||
if (out_trunc_prompt) {
|
||||
*out_trunc_prompt = std::move(trunc_prompt);
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
static size_t calc_prompt_lines(const wcstring &prompt) {
|
||||
|
@ -323,7 +401,7 @@ static size_t calc_prompt_lines(const wcstring &prompt) {
|
|||
// calc_prompt_width_and_lines.
|
||||
size_t result = 1;
|
||||
if (prompt.find_first_of(L"\n\f") != wcstring::npos) {
|
||||
result = calc_prompt_layout(prompt, layout_cache_t::shared).line_count;
|
||||
result = layout_cache_t::shared.calc_prompt_layout(prompt).line_count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -607,10 +685,11 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
layout_cache_t &cached_layouts = layout_cache_t::shared;
|
||||
const environment_t &vars = env_stack_t::principal();
|
||||
const scoped_buffer_t buffering(*scr);
|
||||
const size_t left_prompt_width =
|
||||
calc_prompt_layout(left_prompt, cached_layouts).last_line_width;
|
||||
|
||||
// Determine size of left and right prompt. Note these have already been truncated.
|
||||
const size_t left_prompt_width = cached_layouts.calc_prompt_layout(left_prompt).last_line_width;
|
||||
const size_t right_prompt_width =
|
||||
calc_prompt_layout(right_prompt, cached_layouts).last_line_width;
|
||||
cached_layouts.calc_prompt_layout(right_prompt).last_line_width;
|
||||
|
||||
int screen_width = common_get_width();
|
||||
|
||||
|
@ -852,45 +931,33 @@ static size_t truncation_offset_for_width(const std::vector<size_t> &width_by_of
|
|||
}
|
||||
|
||||
static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
||||
const wcstring &left_prompt_str,
|
||||
const wcstring &right_prompt_str, const wcstring &commandline,
|
||||
const wcstring &left_untrunc_prompt,
|
||||
const wcstring &right_untrunc_prompt,
|
||||
const wcstring &commandline,
|
||||
const wcstring &autosuggestion_str) {
|
||||
UNUSED(s);
|
||||
screen_layout_t result = {};
|
||||
|
||||
// Start by ensuring that the prompts themselves can fit.
|
||||
const wchar_t *left_prompt = left_prompt_str.c_str();
|
||||
const wchar_t *right_prompt = right_prompt_str.c_str();
|
||||
const wchar_t *autosuggestion = autosuggestion_str.c_str();
|
||||
// Truncate both prompts to screen width (#904).
|
||||
wcstring left_prompt;
|
||||
prompt_layout_t left_prompt_layout =
|
||||
layout_cache_t::shared.calc_prompt_layout(left_untrunc_prompt, &left_prompt, screen_width);
|
||||
|
||||
layout_cache_t &cached_layouts = layout_cache_t::shared;
|
||||
prompt_layout_t left_prompt_layout = calc_prompt_layout(left_prompt_str, cached_layouts);
|
||||
prompt_layout_t right_prompt_layout = calc_prompt_layout(right_prompt_str, cached_layouts);
|
||||
wcstring right_prompt;
|
||||
prompt_layout_t right_prompt_layout = layout_cache_t::shared.calc_prompt_layout(
|
||||
right_untrunc_prompt, &right_prompt, screen_width);
|
||||
|
||||
size_t left_prompt_width = left_prompt_layout.last_line_width;
|
||||
size_t right_prompt_width = right_prompt_layout.last_line_width;
|
||||
|
||||
if (left_prompt_layout.max_line_width > screen_width) {
|
||||
// If we have a multi-line prompt, see if the longest line fits; if not neuter the whole
|
||||
// left prompt.
|
||||
left_prompt = L"> ";
|
||||
left_prompt_width = 2;
|
||||
}
|
||||
|
||||
if (left_prompt_width + right_prompt_width >= screen_width) {
|
||||
if (left_prompt_width + right_prompt_width > screen_width) {
|
||||
// Nix right_prompt.
|
||||
right_prompt = L"";
|
||||
right_prompt_width = 0;
|
||||
}
|
||||
|
||||
if (left_prompt_width + right_prompt_width >= screen_width) {
|
||||
// Still doesn't fit, neuter left_prompt.
|
||||
left_prompt = L"> ";
|
||||
left_prompt_width = 2;
|
||||
}
|
||||
|
||||
// Now we should definitely fit.
|
||||
assert(left_prompt_width + right_prompt_width < screen_width);
|
||||
assert(left_prompt_width + right_prompt_width <= screen_width);
|
||||
|
||||
// Get the width of the first line, and if there is more than one line.
|
||||
bool multiline = false;
|
||||
|
@ -906,6 +973,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
|||
const size_t first_command_line_width = first_line_width;
|
||||
|
||||
// If we have more than one line, ensure we have no autosuggestion.
|
||||
const wchar_t *autosuggestion = autosuggestion_str.c_str();
|
||||
size_t autosuggest_total_width = 0;
|
||||
std::vector<size_t> autosuggest_truncated_widths;
|
||||
if (multiline) {
|
||||
|
@ -943,7 +1011,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
|||
if (!done) {
|
||||
calculated_width = left_prompt_width + right_prompt_width + first_command_line_width +
|
||||
autosuggest_total_width;
|
||||
if (calculated_width < screen_width) {
|
||||
if (calculated_width <= screen_width) {
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.right_prompt = right_prompt;
|
||||
|
@ -956,7 +1024,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
|||
// between the left edge and the rprompt.
|
||||
if (!done) {
|
||||
calculated_width = left_prompt_width + right_prompt_width + first_command_line_width;
|
||||
if (calculated_width < screen_width) {
|
||||
if (calculated_width <= screen_width) {
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.right_prompt = right_prompt;
|
||||
|
@ -977,7 +1045,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
|||
// Case 3
|
||||
if (!done) {
|
||||
calculated_width = left_prompt_width + first_command_line_width + autosuggest_total_width;
|
||||
if (calculated_width < screen_width) {
|
||||
if (calculated_width <= screen_width) {
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
result.autosuggestion = autosuggestion;
|
||||
|
@ -988,7 +1056,7 @@ static screen_layout_t compute_layout(screen_t *s, size_t screen_width,
|
|||
// Case 4
|
||||
if (!done) {
|
||||
calculated_width = left_prompt_width + first_command_line_width;
|
||||
if (calculated_width < screen_width) {
|
||||
if (calculated_width <= screen_width) {
|
||||
result.left_prompt = left_prompt;
|
||||
result.left_prompt_space = left_prompt_width;
|
||||
|
||||
|
|
34
src/screen.h
34
src/screen.h
|
@ -221,7 +221,7 @@ struct prompt_layout_t {
|
|||
size_t last_line_width; // width of the last line
|
||||
};
|
||||
|
||||
// Maintain a mapping of escape sequences to their length for fast lookup.
|
||||
// Maintain a mapping of escape sequences to their widths for fast lookup.
|
||||
class layout_cache_t {
|
||||
private:
|
||||
// Cached escape sequences we've already detected in the prompt and similar strings, ordered
|
||||
|
@ -230,11 +230,16 @@ class layout_cache_t {
|
|||
|
||||
// LRU-list of prompts and their layouts.
|
||||
// Use a list so we can promote to the front on a cache hit.
|
||||
using prompt_layout_pair_t = std::pair<wcstring, prompt_layout_t>;
|
||||
std::list<prompt_layout_pair_t> prompt_cache_;
|
||||
struct prompt_cache_entry_t {
|
||||
wcstring text; // Original prompt string.
|
||||
size_t max_line_width; // Max line width when computing layout (for truncation).
|
||||
wcstring trunc_text; // Resulting truncated prompt string.
|
||||
prompt_layout_t layout; // Resulting layout.
|
||||
};
|
||||
std::list<prompt_cache_entry_t> prompt_cache_;
|
||||
|
||||
public:
|
||||
static constexpr size_t prompt_cache_max_size = 8;
|
||||
static constexpr size_t prompt_cache_max_size = 12;
|
||||
|
||||
/// \return the size of the escape code cache.
|
||||
size_t esc_cache_size() const { return esc_cache_.size(); }
|
||||
|
@ -265,11 +270,11 @@ class layout_cache_t {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Finds the layout for a prompt, promoting it to the front. Returns none() if not found.
|
||||
maybe_t<prompt_layout_t> find_prompt_layout(const wcstring &input);
|
||||
|
||||
// Adds a prompt layout for a given string.
|
||||
void add_prompt_layout(wcstring input, prompt_layout_t layout);
|
||||
/// Computes a prompt layout for \p prompt_str, perhaps truncating it to \p desired_line_width.
|
||||
/// \return the layout, and optionally the truncated prompt itself, by reference.
|
||||
prompt_layout_t calc_prompt_layout(
|
||||
const wcstring &prompt_str, wcstring *out_trunc_prompt = nullptr,
|
||||
size_t desired_line_width = std::numeric_limits<size_t>::max());
|
||||
|
||||
void clear() {
|
||||
esc_cache_.clear();
|
||||
|
@ -283,6 +288,17 @@ class layout_cache_t {
|
|||
layout_cache_t() = default;
|
||||
layout_cache_t(const layout_cache_t &) = delete;
|
||||
void operator=(const layout_cache_t &) = delete;
|
||||
|
||||
private:
|
||||
// Add a cache entry.
|
||||
void add_prompt_layout(prompt_cache_entry_t entry);
|
||||
|
||||
// Finds the layout for a prompt, promoting it to the front. Returns nullptr if not found.
|
||||
// Note this points into our cache; do not modify the cache while the pointer lives.
|
||||
const prompt_cache_entry_t *find_prompt_layout(
|
||||
const wcstring &input, size_t max_line_width = std::numeric_limits<size_t>::max());
|
||||
|
||||
friend void test_layout_cache();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue