Add selection navigation

- FocusNextSelection      (ctrl-n)
- FocusPreviousSelection  (ctrl-p)

Also improve batch operations
pull/583/head
Arijit Basu 1 year ago
parent 0e43198093
commit b62765156a

@ -24,7 +24,9 @@ of [modes][4] and the key mappings for each mode.
| V | ctrl-a | select/unselect all | | V | ctrl-a | select/unselect all |
| ctrl-d | | duplicate as | | ctrl-d | | duplicate as |
| ctrl-i | tab | next visited path | | ctrl-i | tab | next visited path |
| ctrl-n | | next selection |
| ctrl-o | | last visited path | | ctrl-o | | last visited path |
| ctrl-p | | prev selection |
| ctrl-r | | refresh screen | | ctrl-r | | refresh screen |
| ctrl-u | | clear selection | | ctrl-u | | clear selection |
| ctrl-w | | switch layout | | ctrl-w | | switch layout |
@ -47,57 +49,65 @@ of [modes][4] and the key mappings for each mode.
| ~ | | go home | | ~ | | go home |
| [0-9] | | input | | [0-9] | | input |
### duplicate_as ### search
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------------ | | ------ | ------ | ---------------- |
| ctrl-n | down | down |
| ctrl-p | up | up |
| enter | | submit | | enter | | submit |
| tab | | try complete | | esc | | cancel |
| left | | back |
| right | | enter |
| tab | | toggle selection |
### vroot ### go_to
| key | remaps | action | | key | remaps | action |
| ------ | ------ | ------------ | | --- | ------ | -------------- |
| . | | vroot $PWD | | f | | follow symlink |
| / | | vroot / | | g | | top |
| ctrl-r | | reset vroot | | i | | initial $PWD |
| ctrl-u | | unset vroot | | p | | path |
| v | | toggle vroot | | x | | open in gui |
| ~ | | vroot $HOME |
### quit ### go_to_path
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ----------------------- | | ----- | ------ | ------------ |
| enter | | just quit | | enter | | submit |
| f | | quit printing focus | | tab | | try complete |
| p | | quit printing pwd |
| r | | quit printing result |
| s | | quit printing selection |
### action ### duplicate_as
| key | remaps | action | | key | remaps | action |
| ----- | ------ | -------------------- | | ----- | ------ | ------------ |
| ! | | shell | | enter | | submit |
| c | | create | | tab | | try complete |
| e | | open in editor |
| l | | logs |
| m | | toggle mouse |
| q | | quit options |
| s | | selection operations |
| v | | vroot |
| [0-9] | | go to index |
### go_to ### sort
| key | remaps | action | | key | remaps | action |
| --- | ------ | -------------- | | --------- | ------ | --------------------------------- |
| f | | follow symlink | | ! | | reverse sorters |
| g | | top | | C | | by created reverse |
| i | | initial $PWD | | E | | by canonical extension reverse |
| p | | path | | L | | by last modified reverse |
| x | | open in gui | | M | | by canonical mime essence reverse |
| N | | by node type reverse |
| R | | by relative path reverse |
| S | | by size reverse |
| backspace | | remove last sorter |
| c | | by created |
| ctrl-r | | reset sorters |
| ctrl-u | | clear sorters |
| e | | by canonical extension |
| enter | | submit |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |
### debug_error ### debug_error
@ -106,39 +116,53 @@ of [modes][4] and the key mappings for each mode.
| enter | | open logs in editor | | enter | | open logs in editor |
| q | | quit | | q | | quit |
### recover ### delete
| key | remaps | action | | key | remaps | action |
| --- | ------ | ------ | | --- | ------ | ------------ |
| D | | force delete |
| d | | delete |
### relative_path_does_not_match_regex ### create
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------ | | --- | ------ | ---------------- |
| enter | | submit | | d | | create directory |
| f | | create file |
### rename ### create_directory
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------------ | | ----- | ------ | ------------ |
| enter | | submit | | enter | | submit |
| tab | | try complete | | tab | | try complete |
### switch_layout ### vroot
| key | remaps | action | | key | remaps | action |
| --- | ------ | -------------------- | | ------ | ------ | ------------ |
| 1 | | default | | . | | vroot $PWD |
| 2 | | no help menu | | / | | vroot / |
| 3 | | no selection panel | | ctrl-r | | reset vroot |
| 4 | | no help or selection | | ctrl-u | | unset vroot |
| v | | toggle vroot |
| ~ | | vroot $HOME |
### create_file ### relative_path_does_match_regex
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------------ | | ----- | ------ | ------ |
| enter | | submit | | enter | | submit |
| tab | | try complete |
### quit
| key | remaps | action |
| ----- | ------ | ----------------------- |
| enter | | just quit |
| f | | quit printing focus |
| p | | quit printing pwd |
| r | | quit printing result |
| s | | quit printing selection |
### filter ### filter
@ -150,7 +174,7 @@ of [modes][4] and the key mappings for each mode.
| ctrl-u | | clear filters | | ctrl-u | | clear filters |
| r | | relative path does match regex | | r | | relative path does match regex |
### create_directory ### create_file
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------------ | | ----- | ------ | ------------ |
@ -169,74 +193,52 @@ of [modes][4] and the key mappings for each mode.
| s | | softlink here | | s | | softlink here |
| u | | clear selection | | u | | clear selection |
### search ### switch_layout
| key | remaps | action |
| ------ | ------ | ---------------- |
| ctrl-n | down | down |
| ctrl-p | up | up |
| enter | | submit |
| esc | | cancel |
| left | | back |
| right | | enter |
| tab | | toggle selection |
### number
| key | remaps | action |
| ----- | ------ | -------- |
| down | j | to down |
| enter | | to index |
| k | up | to up |
| [0-9] | | input |
### delete
| key | remaps | action | | key | remaps | action |
| --- | ------ | ------------ | | --- | ------ | -------------------- |
| D | | force delete | | 1 | | default |
| d | | delete | | 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
### go_to_path ### rename
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------------ | | ----- | ------ | ------------ |
| enter | | submit | | enter | | submit |
| tab | | try complete | | tab | | try complete |
### relative_path_does_match_regex ### relative_path_does_not_match_regex
| key | remaps | action | | key | remaps | action |
| ----- | ------ | ------ | | ----- | ------ | ------ |
| enter | | submit | | enter | | submit |
### create ### number
| key | remaps | action | | key | remaps | action |
| --- | ------ | ---------------- | | ----- | ------ | -------- |
| d | | create directory | | down | j | to down |
| f | | create file | | enter | | to index |
| k | up | to up |
| [0-9] | | input |
### sort ### recover
| key | remaps | action | | key | remaps | action |
| --------- | ------ | --------------------------------- | | --- | ------ | ------ |
| ! | | reverse sorters |
| C | | by created reverse | ### action
| E | | by canonical extension reverse |
| L | | by last modified reverse | | key | remaps | action |
| M | | by canonical mime essence reverse | | ----- | ------ | -------------------- |
| N | | by node type reverse | | ! | | shell |
| R | | by relative path reverse | | c | | create |
| S | | by size reverse | | e | | open in editor |
| backspace | | remove last sorter | | l | | logs |
| c | | by created | | m | | toggle mouse |
| ctrl-r | | reset sorters | | q | | quit options |
| ctrl-u | | clear sorters | | s | | selection operations |
| e | | by canonical extension | | v | | vroot |
| enter | | submit | | [0-9] | | go to index |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |

@ -97,6 +97,15 @@ Example:
- Lua: `"FocusNext"` - Lua: `"FocusNext"`
- YAML: `FocusNext` - YAML: `FocusNext`
#### FocusNextSelection
Focus on the next selected node.
Example:
- Lua: `"FocusNextSelection"`
- YAML: `FocusNextSelection`
#### FocusNextByRelativeIndex #### FocusNextByRelativeIndex
Focus on the `n`th node relative to the current focus where `n` is a Focus on the `n`th node relative to the current focus where `n` is a
@ -128,6 +137,15 @@ Example:
- Lua: `"FocusPrevious"` - Lua: `"FocusPrevious"`
- YAML: `FocusPrevious` - YAML: `FocusPrevious`
#### FocusPreviousSelection
Focus on the previous item.
Example:
- Lua: `"FocusPreviousSelection"`
- YAML: `FocusPreviousSelection`
#### FocusPreviousByRelativeIndex #### FocusPreviousByRelativeIndex
Focus on the `-n`th node relative to the current focus where `n` is a Focus on the `-n`th node relative to the current focus where `n` is a

@ -427,6 +427,7 @@ impl App {
FocusFirst => self.focus_first(true), FocusFirst => self.focus_first(true),
FocusLast => self.focus_last(), FocusLast => self.focus_last(),
FocusPrevious => self.focus_previous(), FocusPrevious => self.focus_previous(),
FocusPreviousSelection => self.focus_previous_selection(),
FocusPreviousByRelativeIndex(i) => { FocusPreviousByRelativeIndex(i) => {
self.focus_previous_by_relative_index(i) self.focus_previous_by_relative_index(i)
} }
@ -435,6 +436,7 @@ impl App {
self.focus_previous_by_relative_index_from_input() self.focus_previous_by_relative_index_from_input()
} }
FocusNext => self.focus_next(), FocusNext => self.focus_next(),
FocusNextSelection => self.focus_next_selection(),
FocusNextByRelativeIndex(i) => self.focus_next_by_relative_index(i), FocusNextByRelativeIndex(i) => self.focus_next_by_relative_index(i),
FocusNextByRelativeIndexFromInput => { FocusNextByRelativeIndexFromInput => {
self.focus_next_by_relative_index_from_input() self.focus_next_by_relative_index_from_input()
@ -689,6 +691,46 @@ impl App {
Ok(self) Ok(self)
} }
fn focus_previous_selection(mut self) -> Result<Self> {
let total = self.selection.len();
if total == 0 {
return Ok(self);
}
let bounded = self.config.general.enforce_bounded_index_navigation;
if let Some(n) = self
.directory_buffer
.as_ref()
.and_then(|d| d.focused_node())
{
if let Some(idx) = self.selection.get_index_of(n) {
let idx = if idx == 0 {
if bounded {
idx
} else {
total.max(1) - 1
}
} else {
idx.max(1) - 1
};
if let Some(p) = self
.selection
.get_index(idx)
.map(|n| n.absolute_path.clone())
{
self = self.focus_path(&p, true)?;
}
} else if let Some(p) =
self.selection.last().map(|n| n.absolute_path.clone())
{
self = self.focus_path(&p, true)?;
}
}
Ok(self)
}
pub fn focus_previous_by_relative_index(mut self, index: usize) -> Result<Self> { pub fn focus_previous_by_relative_index(mut self, index: usize) -> Result<Self> {
let mut history = self.history.clone(); let mut history = self.history.clone();
if let Some(dir) = self.directory_buffer_mut() { if let Some(dir) = self.directory_buffer_mut() {
@ -734,6 +776,46 @@ impl App {
Ok(self) Ok(self)
} }
fn focus_next_selection(mut self) -> Result<Self> {
let total = self.selection.len();
if total == 0 {
return Ok(self);
}
let bounded = self.config.general.enforce_bounded_index_navigation;
if let Some(n) = self
.directory_buffer
.as_ref()
.and_then(|d| d.focused_node())
{
if let Some(idx) = self.selection.get_index_of(n) {
let idx = if idx + 1 == total {
if bounded {
idx
} else {
0
}
} else {
idx + 1
};
if let Some(p) = self
.selection
.get_index(idx)
.map(|n| n.absolute_path.clone())
{
self = self.focus_path(&p, true)?;
}
} else if let Some(p) =
self.selection.first().map(|n| n.absolute_path.clone())
{
self = self.focus_path(&p, true)?;
}
}
Ok(self)
}
pub fn focus_next_by_relative_index(mut self, index: usize) -> Result<Self> { pub fn focus_next_by_relative_index(mut self, index: usize) -> Result<Self> {
let mut history = self.history.clone(); let mut history = self.history.clone();
if let Some(dir) = self.directory_buffer_mut() { if let Some(dir) = self.directory_buffer_mut() {

@ -1242,6 +1242,18 @@ xplr.config.modes.builtin.default = {
"ScrollDownHalf", "ScrollDownHalf",
}, },
}, },
["ctrl-n"] = {
help = "next selection",
messages = {
"FocusNextSelection",
},
},
["ctrl-p"] = {
help = "prev selection",
messages = {
"FocusPreviousSelection",
},
},
}, },
on_number = { on_number = {
help = "input", help = "input",
@ -1468,6 +1480,7 @@ xplr.config.modes.builtin.selection_ops = {
if cp -vr -- "${PTH:?}" "./${BASENAME:?}"; then if cp -vr -- "${PTH:?}" "./${BASENAME:?}"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC copied to ./$BASENAME_ESC" "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC copied to ./$BASENAME_ESC"
"$XPLR" -m 'UnSelectPath: %q' "$PTH" "$XPLR" -m 'UnSelectPath: %q' "$PTH"
"$XPLR" -m 'FocusPath: %q' "$BASENAME"
else else
"$XPLR" -m 'LogError: %q' "Failed to copy $PTH_ESC to ./$BASENAME_ESC" "$XPLR" -m 'LogError: %q' "Failed to copy $PTH_ESC to ./$BASENAME_ESC"
fi fi
@ -1494,7 +1507,7 @@ xplr.config.modes.builtin.selection_ops = {
done done
if mv -v -- "${PTH:?}" "./${BASENAME:?}"; then if mv -v -- "${PTH:?}" "./${BASENAME:?}"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC moved to ./$BASENAME_ESC" "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC moved to ./$BASENAME_ESC"
"$XPLR" -m 'UnSelectPath: %q' "$PTH" "$XPLR" -m 'FocusPath: %q' "$BASENAME"
else else
"$XPLR" -m 'LogError: %q' "Failed to move $PTH_ESC to ./$BASENAME_ESC" "$XPLR" -m 'LogError: %q' "Failed to move $PTH_ESC to ./$BASENAME_ESC"
fi fi
@ -1522,6 +1535,7 @@ xplr.config.modes.builtin.selection_ops = {
if ln -sv -- "${PTH:?}" "./${BASENAME:?}"; then if ln -sv -- "${PTH:?}" "./${BASENAME:?}"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC softlinked as ./$BASENAME_ESC" "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC softlinked as ./$BASENAME_ESC"
"$XPLR" -m 'UnSelectPath: %q' "$PTH" "$XPLR" -m 'UnSelectPath: %q' "$PTH"
"$XPLR" -m 'FocusPath: %q' "$BASENAME"
else else
"$XPLR" -m 'LogError: %q' "Failed to softlink $PTH_ESC as ./$BASENAME_ESC" "$XPLR" -m 'LogError: %q' "Failed to softlink $PTH_ESC as ./$BASENAME_ESC"
fi fi
@ -1549,6 +1563,7 @@ xplr.config.modes.builtin.selection_ops = {
if ln -v -- "${PTH:?}" "./${BASENAME:?}"; then if ln -v -- "${PTH:?}" "./${BASENAME:?}"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC hardlinked as ./$BASENAME_ESC" "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC hardlinked as ./$BASENAME_ESC"
"$XPLR" -m 'UnSelectPath: %q' "$PTH" "$XPLR" -m 'UnSelectPath: %q' "$PTH"
"$XPLR" -m 'FocusPath: %q' "$BASENAME"
else else
"$XPLR" -m 'LogError: %q' "Failed to hardlink $PTH_ESC as ./$BASENAME_ESC" "$XPLR" -m 'LogError: %q' "Failed to hardlink $PTH_ESC as ./$BASENAME_ESC"
fi fi

@ -74,6 +74,14 @@ pub enum ExternalMsg {
/// - YAML: `FocusNext` /// - YAML: `FocusNext`
FocusNext, FocusNext,
/// Focus on the next selected node.
///
/// Example:
///
/// - Lua: `"FocusNextSelection"`
/// - YAML: `FocusNextSelection`
FocusNextSelection,
/// Focus on the `n`th node relative to the current focus where `n` is a /// Focus on the `n`th node relative to the current focus where `n` is a
/// given value. /// given value.
/// ///
@ -102,6 +110,14 @@ pub enum ExternalMsg {
/// - YAML: `FocusPrevious` /// - YAML: `FocusPrevious`
FocusPrevious, FocusPrevious,
/// Focus on the previous item.
///
/// Example:
///
/// - Lua: `"FocusPreviousSelection"`
/// - YAML: `FocusPreviousSelection`
FocusPreviousSelection,
/// Focus on the `-n`th node relative to the current focus where `n` is a /// Focus on the `-n`th node relative to the current focus where `n` is a
/// given value. /// given value.
/// ///

Loading…
Cancel
Save