mirror of
https://github.com/atuinsh/atuin.git
synced 2024-11-24 09:15:09 +01:00
feat: Ultracompact Mode (search-only) (#2357)
* feat: add always_show_tabs setting * feat(ultracompact): extra-compact options * feat(ultracompact): enable via auto_hide_height * feat(ultracompact): clarify comment * fix(theming): minor tidyup to theming comment * chore: rustfmt/clippy corrections * chore: testing if CI issue was transient
This commit is contained in:
parent
5651036d8f
commit
4c2d8201dc
@ -125,6 +125,13 @@
|
|||||||
## Configure whether or not to show tabs for search and inspect
|
## Configure whether or not to show tabs for search and inspect
|
||||||
# show_tabs = true
|
# show_tabs = true
|
||||||
|
|
||||||
|
## Configure whether or not the tabs row may be auto-hidden, which includes the current Atuin
|
||||||
|
## tab, such as Search or Inspector, and other tabs you may wish to see. This will
|
||||||
|
## only be hidden if there are fewer than this count of lines available, and does not affect the use
|
||||||
|
## of keyboard shortcuts to switch tab. 0 to never auto-hide, default is 8 (lines).
|
||||||
|
## This is ignored except in `compact` mode.
|
||||||
|
# auto_hide_height = 8
|
||||||
|
|
||||||
## Defaults to true. This matches history against a set of default regex, and will not save it if we get a match. Defaults include
|
## Defaults to true. This matches history against a set of default regex, and will not save it if we get a match. Defaults include
|
||||||
## 1. AWS key id
|
## 1. AWS key id
|
||||||
## 2. Github pat (old and new)
|
## 2. Github pat (old and new)
|
||||||
|
@ -440,6 +440,7 @@ pub struct Settings {
|
|||||||
pub max_preview_height: u16,
|
pub max_preview_height: u16,
|
||||||
pub show_help: bool,
|
pub show_help: bool,
|
||||||
pub show_tabs: bool,
|
pub show_tabs: bool,
|
||||||
|
pub auto_hide_height: u16,
|
||||||
pub exit_mode: ExitMode,
|
pub exit_mode: ExitMode,
|
||||||
pub keymap_mode: KeymapMode,
|
pub keymap_mode: KeymapMode,
|
||||||
pub keymap_mode_shell: KeymapMode,
|
pub keymap_mode_shell: KeymapMode,
|
||||||
@ -722,6 +723,7 @@ impl Settings {
|
|||||||
.set_default("max_preview_height", 4)?
|
.set_default("max_preview_height", 4)?
|
||||||
.set_default("show_help", true)?
|
.set_default("show_help", true)?
|
||||||
.set_default("show_tabs", true)?
|
.set_default("show_tabs", true)?
|
||||||
|
.set_default("auto_hide_height", 8)?
|
||||||
.set_default("invert", false)?
|
.set_default("invert", false)?
|
||||||
.set_default("exit_mode", "return-original")?
|
.set_default("exit_mode", "return-original")?
|
||||||
.set_default("word_jump_mode", "emacs")?
|
.set_default("word_jump_mode", "emacs")?
|
||||||
|
@ -53,8 +53,8 @@ pub struct ThemeDefinitionConfigBlock {
|
|||||||
|
|
||||||
use crossterm::style::{Color, ContentStyle};
|
use crossterm::style::{Color, ContentStyle};
|
||||||
|
|
||||||
// For now, a theme is specifically a mapping of meanings to colors, but it may be desirable to
|
// For now, a theme is loaded as a mapping of meanings to colors, but it may be desirable to
|
||||||
// expand that in the future to general styles.
|
// expand that in the future to general styles, so we populate a Meaning->ContentStyle hashmap.
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parent: Option<String>,
|
pub parent: Option<String>,
|
||||||
|
@ -22,6 +22,7 @@ pub struct HistoryList<'a> {
|
|||||||
/// Apply an alternative highlighting to the selected row
|
/// Apply an alternative highlighting to the selected row
|
||||||
alternate_highlight: bool,
|
alternate_highlight: bool,
|
||||||
now: &'a dyn Fn() -> OffsetDateTime,
|
now: &'a dyn Fn() -> OffsetDateTime,
|
||||||
|
indicator: &'a str,
|
||||||
theme: &'a Theme,
|
theme: &'a Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ impl<'a> StatefulWidget for HistoryList<'a> {
|
|||||||
inverted: self.inverted,
|
inverted: self.inverted,
|
||||||
alternate_highlight: self.alternate_highlight,
|
alternate_highlight: self.alternate_highlight,
|
||||||
now: &self.now,
|
now: &self.now,
|
||||||
|
indicator: self.indicator,
|
||||||
theme: self.theme,
|
theme: self.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,6 +98,7 @@ impl<'a> HistoryList<'a> {
|
|||||||
inverted: bool,
|
inverted: bool,
|
||||||
alternate_highlight: bool,
|
alternate_highlight: bool,
|
||||||
now: &'a dyn Fn() -> OffsetDateTime,
|
now: &'a dyn Fn() -> OffsetDateTime,
|
||||||
|
indicator: &'a str,
|
||||||
theme: &'a Theme,
|
theme: &'a Theme,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -104,6 +107,7 @@ impl<'a> HistoryList<'a> {
|
|||||||
inverted,
|
inverted,
|
||||||
alternate_highlight,
|
alternate_highlight,
|
||||||
now,
|
now,
|
||||||
|
indicator,
|
||||||
theme,
|
theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,6 +141,7 @@ struct DrawState<'a> {
|
|||||||
inverted: bool,
|
inverted: bool,
|
||||||
alternate_highlight: bool,
|
alternate_highlight: bool,
|
||||||
now: &'a dyn Fn() -> OffsetDateTime,
|
now: &'a dyn Fn() -> OffsetDateTime,
|
||||||
|
indicator: &'a str,
|
||||||
theme: &'a Theme,
|
theme: &'a Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +160,12 @@ impl DrawState<'_> {
|
|||||||
let i = self.y as usize + self.state.offset;
|
let i = self.y as usize + self.state.offset;
|
||||||
let i = i.checked_sub(self.state.selected);
|
let i = i.checked_sub(self.state.selected);
|
||||||
let i = i.unwrap_or(10).min(10) * 2;
|
let i = i.unwrap_or(10).min(10) * 2;
|
||||||
self.draw(&SLICES[i..i + 3], Style::default());
|
let prompt: &str = if i == 0 {
|
||||||
|
self.indicator
|
||||||
|
} else {
|
||||||
|
&SLICES[i..i + 3]
|
||||||
|
};
|
||||||
|
self.draw(prompt, Style::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration(&mut self, h: &History) {
|
fn duration(&mut self, h: &History) {
|
||||||
|
@ -622,7 +622,12 @@ impl State {
|
|||||||
preview_width,
|
preview_width,
|
||||||
);
|
);
|
||||||
let show_help = settings.show_help && (!compact || f.size().height > 1);
|
let show_help = settings.show_help && (!compact || f.size().height > 1);
|
||||||
let show_tabs = settings.show_tabs;
|
// This is an OR, as it seems more likely for someone to wish to override
|
||||||
|
// tabs unexpectedly being missed, than unexpectedly present.
|
||||||
|
let hide_extra = settings.auto_hide_height != 0
|
||||||
|
&& compact
|
||||||
|
&& f.size().height <= settings.auto_hide_height;
|
||||||
|
let show_tabs = settings.show_tabs && !hide_extra;
|
||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.margin(0)
|
.margin(0)
|
||||||
@ -636,6 +641,14 @@ impl State {
|
|||||||
Constraint::Length(if show_tabs { 1 } else { 0 }), // tabs
|
Constraint::Length(if show_tabs { 1 } else { 0 }), // tabs
|
||||||
Constraint::Length(if show_help { 1 } else { 0 }), // header (sic)
|
Constraint::Length(if show_help { 1 } else { 0 }), // header (sic)
|
||||||
]
|
]
|
||||||
|
} else if hide_extra {
|
||||||
|
[
|
||||||
|
Constraint::Length(if show_help { 1 } else { 0 }), // header
|
||||||
|
Constraint::Length(0), // tabs
|
||||||
|
Constraint::Min(1), // results list
|
||||||
|
Constraint::Length(0),
|
||||||
|
Constraint::Length(0),
|
||||||
|
]
|
||||||
} else {
|
} else {
|
||||||
[
|
[
|
||||||
Constraint::Length(if show_help { 1 } else { 0 }), // header
|
Constraint::Length(if show_help { 1 } else { 0 }), // header
|
||||||
@ -660,13 +673,15 @@ impl State {
|
|||||||
// also allocate less 🙈
|
// also allocate less 🙈
|
||||||
let titles: Vec<_> = TAB_TITLES.iter().copied().map(Line::from).collect();
|
let titles: Vec<_> = TAB_TITLES.iter().copied().map(Line::from).collect();
|
||||||
|
|
||||||
let tabs = Tabs::new(titles)
|
if show_tabs {
|
||||||
.block(Block::default().borders(Borders::NONE))
|
let tabs = Tabs::new(titles)
|
||||||
.select(self.tab_index)
|
.block(Block::default().borders(Borders::NONE))
|
||||||
.style(Style::default())
|
.select(self.tab_index)
|
||||||
.highlight_style(Style::default().bold().white().on_black());
|
.style(Style::default())
|
||||||
|
.highlight_style(Style::default().bold().white().on_black());
|
||||||
|
|
||||||
f.render_widget(tabs, tabs_chunk);
|
f.render_widget(tabs, tabs_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
let style = StyleState {
|
let style = StyleState {
|
||||||
compact,
|
compact,
|
||||||
@ -695,10 +710,27 @@ impl State {
|
|||||||
let stats_tab = self.build_stats(theme);
|
let stats_tab = self.build_stats(theme);
|
||||||
f.render_widget(stats_tab, header_chunks[2]);
|
f.render_widget(stats_tab, header_chunks[2]);
|
||||||
|
|
||||||
|
let indicator: String = if !hide_extra {
|
||||||
|
" > ".to_string()
|
||||||
|
} else if self.switched_search_mode {
|
||||||
|
format!("S{}>", self.search_mode.as_str().chars().next().unwrap())
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{}> ",
|
||||||
|
self.search.filter_mode.as_str().chars().next().unwrap()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
match self.tab_index {
|
match self.tab_index {
|
||||||
0 => {
|
0 => {
|
||||||
let results_list =
|
let results_list = Self::build_results_list(
|
||||||
Self::build_results_list(style, results, self.keymap_mode, &self.now, theme);
|
style,
|
||||||
|
results,
|
||||||
|
self.keymap_mode,
|
||||||
|
&self.now,
|
||||||
|
indicator.as_str(),
|
||||||
|
theme,
|
||||||
|
);
|
||||||
f.render_stateful_widget(results_list, results_list_chunk, &mut self.results_state);
|
f.render_stateful_widget(results_list, results_list_chunk, &mut self.results_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,31 +770,33 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let input = self.build_input(style);
|
if !hide_extra {
|
||||||
f.render_widget(input, input_chunk);
|
let input = self.build_input(style);
|
||||||
|
f.render_widget(input, input_chunk);
|
||||||
|
|
||||||
let preview_width = if compact {
|
let preview_width = if compact {
|
||||||
preview_width
|
preview_width
|
||||||
} else {
|
} else {
|
||||||
preview_width - 2
|
preview_width - 2
|
||||||
};
|
};
|
||||||
let preview = self.build_preview(
|
let preview = self.build_preview(
|
||||||
results,
|
results,
|
||||||
compact,
|
compact,
|
||||||
preview_width,
|
preview_width,
|
||||||
preview_chunk.width.into(),
|
preview_chunk.width.into(),
|
||||||
theme,
|
theme,
|
||||||
);
|
);
|
||||||
f.render_widget(preview, preview_chunk);
|
f.render_widget(preview, preview_chunk);
|
||||||
|
|
||||||
let extra_width = UnicodeWidthStr::width(self.search.input.substring());
|
let extra_width = UnicodeWidthStr::width(self.search.input.substring());
|
||||||
|
|
||||||
let cursor_offset = if compact { 0 } else { 1 };
|
let cursor_offset = if compact { 0 } else { 1 };
|
||||||
f.set_cursor(
|
f.set_cursor(
|
||||||
// Put cursor past the end of the input text
|
// Put cursor past the end of the input text
|
||||||
input_chunk.x + extra_width as u16 + PREFIX_LENGTH + 1 + cursor_offset,
|
input_chunk.x + extra_width as u16 + PREFIX_LENGTH + 1 + cursor_offset,
|
||||||
input_chunk.y + cursor_offset,
|
input_chunk.y + cursor_offset,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_title(&self, theme: &Theme) -> Paragraph {
|
fn build_title(&self, theme: &Theme) -> Paragraph {
|
||||||
@ -836,6 +870,7 @@ impl State {
|
|||||||
results: &'a [History],
|
results: &'a [History],
|
||||||
keymap_mode: KeymapMode,
|
keymap_mode: KeymapMode,
|
||||||
now: &'a dyn Fn() -> OffsetDateTime,
|
now: &'a dyn Fn() -> OffsetDateTime,
|
||||||
|
indicator: &'a str,
|
||||||
theme: &'a Theme,
|
theme: &'a Theme,
|
||||||
) -> HistoryList<'a> {
|
) -> HistoryList<'a> {
|
||||||
let results_list = HistoryList::new(
|
let results_list = HistoryList::new(
|
||||||
@ -843,6 +878,7 @@ impl State {
|
|||||||
style.invert,
|
style.invert,
|
||||||
keymap_mode == KeymapMode::VimNormal,
|
keymap_mode == KeymapMode::VimNormal,
|
||||||
now,
|
now,
|
||||||
|
indicator,
|
||||||
theme,
|
theme,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user