keep popups for some actions

This commit is contained in:
alice pellerin
2026-04-10 20:11:27 -05:00
parent be96c2ed7c
commit 75158ee771
3 changed files with 310 additions and 150 deletions
+182 -23
View File
@@ -13,6 +13,18 @@ pub enum Action {
Cursor(CursorAction), Cursor(CursorAction),
} }
impl Action {
pub const fn clears_popups(self) -> bool {
use Action::*;
match self {
App(app_action) => app_action.clears_popups(),
Buffer(buffer_action) => buffer_action.clears_popups(),
Cursor(cursor_action) => cursor_action.clears_popups(),
}
}
}
impl From<Action> for &str { impl From<Action> for &str {
fn from(action: Action) -> Self { fn from(action: Action) -> Self {
match action { match action {
@@ -46,6 +58,23 @@ pub enum AppAction {
Yank, Yank,
} }
impl AppAction {
const fn clears_popups(self) -> bool {
use AppAction::*;
#[allow(clippy::match_same_arms)]
match self {
QuitIfSaved => true,
Quit => true,
PreviousBuffer => false,
NextBuffer => false,
Yank => false,
}
}
}
impl From<AppAction> for &str { impl From<AppAction> for &str {
fn from(app_action: AppAction) -> Self { fn from(app_action: AppAction) -> Self {
use AppAction::*; use AppAction::*;
@@ -155,6 +184,78 @@ pub enum BufferAction {
InspectSelectionColor, InspectSelectionColor,
} }
impl BufferAction {
const fn clears_popups(self) -> bool {
use BufferAction::*;
#[allow(clippy::match_same_arms)]
match self {
NormalMode => false,
SelectMode => false,
Goto => false,
View => false,
Replace => true,
Space => false,
Repeat => true,
To => false,
ScrollDown => true,
ScrollUp => true,
PageCursorHalfDown => true,
PageCursorHalfUp => true,
PageDown => true,
PageUp => true,
CollapseSelection => true,
FlipSelections => false,
Delete => true,
Undo => true,
Redo => true,
Save => false,
CopySelectionOnNextLine => true,
RotateSelectionsBackward => false,
RotateSelectionsForward => false,
KeepPrimarySelection => true,
RemovePrimarySelection => true,
SplitSelectionsInto1s => true,
SplitSelectionsInto2s => true,
SplitSelectionsInto3s => true,
SplitSelectionsInto4s => true,
SplitSelectionsInto5s => true,
SplitSelectionsInto6s => true,
SplitSelectionsInto7s => true,
SplitSelectionsInto8s => true,
SplitSelectionsInto9s => true,
JumpToSelectedOffset => true,
JumpToSelectedOffsetRelativeToMark => true,
ToggleMark => false,
AlignViewCenter => false,
AlignViewBottom => false,
AlignViewTop => false,
ExtendToMark => true,
ExtendToNull => true,
ExtendToFF => true,
InspectSelection => true,
InspectSelectionColor => true,
}
}
}
impl From<BufferAction> for &str { impl From<BufferAction> for &str {
fn from(buffer_action: BufferAction) -> Self { fn from(buffer_action: BufferAction) -> Self {
use BufferAction::*; use BufferAction::*;
@@ -338,6 +439,41 @@ pub enum CursorAction {
ExtendLineAbove, ExtendLineAbove,
} }
impl CursorAction {
const fn clears_popups(self) -> bool {
use CursorAction::*;
#[allow(clippy::match_same_arms)]
match self {
MoveByteUp => true,
MoveByteDown => true,
MoveByteLeft => true,
MoveByteRight => true,
ExtendByteUp => true,
ExtendByteDown => true,
ExtendByteLeft => true,
ExtendByteRight => true,
GotoLineStart => true,
GotoLineEnd => true,
GotoFileStart => true,
GotoFileEnd => true,
MoveNextWordStart => true,
MoveNextWordEnd => true,
MovePreviousWordStart => true,
ExtendNextWordStart => true,
ExtendNextWordEnd => true,
ExtendPreviousWordStart => true,
ExtendLineBelow => true,
ExtendLineAbove => true,
}
}
}
impl From<CursorAction> for &str { impl From<CursorAction> for &str {
fn from(cursor_action: CursorAction) -> Self { fn from(cursor_action: CursorAction) -> Self {
use CursorAction::*; use CursorAction::*;
@@ -1050,14 +1186,58 @@ impl Buffer {
self.popups.extend( self.popups.extend(
iter::once(&self.primary_cursor) iter::once(&self.primary_cursor)
.chain(&self.cursors) .chain(&self.cursors)
.map(|cursor| { .filter_map(|cursor| {
let selection = &self.contents[cursor.range()]; let selection = &self.contents[cursor.range()];
let popup_lines = inspect(selection);
if popup_lines.is_empty() {
None
} else {
Some(Popup::new(cursor.lower_bound(), popup_lines))
}
})
);
if self.popups.is_empty() {
self.inspecting_selection = false;
}
}
fn inspect_selection_color(&mut self) {
if self.inspecting_selection { return; }
self.inspecting_selection = true;
self.popups.extend(
iter::once(&self.primary_cursor)
.chain(&self.cursors)
.filter_map(|cursor| {
let selection = &self.contents[cursor.range()];
let popup_lines = inspect_color(selection);
if popup_lines.is_empty() {
None
} else {
Some(Popup::new(cursor.lower_bound(), popup_lines))
}
})
);
if self.popups.is_empty() {
self.inspecting_selection = false;
}
}
}
fn inspect(selection: &[u8]) -> Vec<Span<'static>> {
let nat = bytes_to_nat(selection); let nat = bytes_to_nat(selection);
let int = nat.and_then(|nat| nat_to_int_if_different(nat, selection.len())); let int = nat.and_then(|nat| nat_to_int_if_different(nat, selection.len()));
let utf8 = str::from_utf8(selection).ok() let utf8 = str::from_utf8(selection).ok()
.filter(|_| selection.len() == 1)
.map(|utf8| utf8.trim_suffix('\0')) .map(|utf8| utf8.trim_suffix('\0'))
.filter(|utf8| !utf8.contains(is_illegal_control_character)) .filter(|utf8| !utf8.contains(is_illegal_control_character))
.map(|utf8| Span::from(format!("\"{utf8}\"")).red()); .map(|utf8| Span::from(format!("\"{utf8}\"")).red());
@@ -1150,8 +1330,6 @@ impl Buffer {
}); });
Popup::new(
cursor.lower_bound(),
int.map(|int| format!("{int}").into()) int.map(|int| format!("{int}").into())
.into_iter() .into_iter()
.chain(nat.map(|nat| format!("{nat}").into())) .chain(nat.map(|nat| format!("{nat}").into()))
@@ -1166,22 +1344,9 @@ impl Buffer {
.chain(color888) .chain(color888)
.chain(color555) .chain(color555)
.collect() .collect()
)
})
);
} }
fn inspect_selection_color(&mut self) { fn inspect_color(selection: &[u8]) -> Vec<Span<'static>> {
if self.inspecting_selection { return; }
self.inspecting_selection = true;
self.popups.extend(
iter::once(&self.primary_cursor)
.chain(&self.cursors)
.map(|cursor| {
let selection = &self.contents[cursor.range()];
let nat = bytes_to_nat(selection); let nat = bytes_to_nat(selection);
let color888 = (selection.len() == 3) let color888 = (selection.len() == 3)
@@ -1202,16 +1367,10 @@ impl Buffer {
}); });
Popup::new(
cursor.lower_bound(),
color888 color888
.into_iter() .into_iter()
.chain(color555) .chain(color555)
.collect() .collect()
)
})
);
}
} }
// helpers // helpers
+10 -8
View File
@@ -209,12 +209,8 @@ impl Buffer {
window_size: WindowSize window_size: WindowSize
) -> Option<AppAction> { ) -> Option<AppAction> {
self.alert_message = "".into(); self.alert_message = "".into();
self.popups.clear();
// self.logs.push(format!("{event:?}")); // self.logs.push(format!("{event:?}"));
let was_inspecting_selection = self.inspecting_selection;
let app_action = match self.partial_action { let app_action = match self.partial_action {
Some(PartialAction::Replace) => { Some(PartialAction::Replace) => {
self.handle_replace(event, window_size); self.handle_replace(event, window_size);
@@ -233,10 +229,6 @@ impl Buffer {
_ => self.handle_other_modes(event, config, window_size), _ => self.handle_other_modes(event, config, window_size),
}; };
if was_inspecting_selection {
self.inspecting_selection = false;
}
assert!(self.scroll_position.is_multiple_of(BYTES_PER_LINE)); assert!(self.scroll_position.is_multiple_of(BYTES_PER_LINE));
assert!(self.scroll_position < self.contents.len()); assert!(self.scroll_position < self.contents.len());
assert!(self.primary_cursor.head < self.contents.len()); assert!(self.primary_cursor.head < self.contents.len());
@@ -289,6 +281,12 @@ impl Buffer {
let Some(keybinds) = mode_config.0.get(&self.partial_action) && let Some(keybinds) = mode_config.0.get(&self.partial_action) &&
let Some(action) = keybinds.0.get(&event.into()) let Some(action) = keybinds.0.get(&event.into())
{ {
if action.clears_popups() {
self.popups.clear();
}
let was_inspecting_selection = self.inspecting_selection;
match action { match action {
Action::App(app_action) => result = Some(*app_action), Action::App(app_action) => result = Some(*app_action),
Action::Buffer(buffer_action) => self.execute(*buffer_action, window_size), Action::Buffer(buffer_action) => self.execute(*buffer_action, window_size),
@@ -306,6 +304,10 @@ impl Buffer {
self.clamp_screen_to_primary_cursor(window_size); self.clamp_screen_to_primary_cursor(window_size);
}, },
} }
if was_inspecting_selection {
self.inspecting_selection = false;
}
} }
if should_reset_partial { if should_reset_partial {
-1
View File
@@ -28,7 +28,6 @@ const LINES_OF_PADDING: usize = 5;
const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE; const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE;
// TODO: // TODO:
// - keep popups for some actions
// - inspector translations for varint // - inspector translations for varint
// - search // - search
// - ascii and bytes (`/` and `A-/`?) // - ascii and bytes (`/` and `A-/`?)