mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-25 17:54:57 +03:00
feat: fix and enhance unset mode (#27)
This commit is contained in:
parent
badcf99416
commit
e52547de10
75
Cargo.lock
generated
75
Cargo.lock
generated
@ -75,7 +75,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"config",
|
"config",
|
||||||
"core",
|
"core",
|
||||||
"crossterm",
|
"crossterm 0.27.0",
|
||||||
"futures",
|
"futures",
|
||||||
"libc",
|
"libc",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
@ -238,7 +238,7 @@ name = "config"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"crossterm",
|
"crossterm 0.27.0",
|
||||||
"futures",
|
"futures",
|
||||||
"glob",
|
"glob",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -257,7 +257,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"config",
|
"config",
|
||||||
"crossterm",
|
"crossterm 0.27.0",
|
||||||
"futures",
|
"futures",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.0.0",
|
||||||
"notify",
|
"notify",
|
||||||
@ -341,6 +341,22 @@ checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.3.3",
|
||||||
|
"crossterm_winapi",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
@ -436,13 +452,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.21"
|
version = "0.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
|
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.2.16",
|
"redox_syscall",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -778,9 +794,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue"
|
name = "kqueue"
|
||||||
version = "1.0.7"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
|
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kqueue-sys",
|
"kqueue-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -788,9 +804,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue-sys"
|
name = "kqueue-sys"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
|
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1062,7 +1078,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.3.5",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.48.1",
|
"windows-targets 0.48.1",
|
||||||
]
|
]
|
||||||
@ -1081,18 +1097,18 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
|
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-internal",
|
"pin-project-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-internal"
|
name = "pin-project-internal"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1101,9 +1117,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
|
checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
@ -1188,7 +1204,7 @@ checksum = "8285baa38bdc9f879d92c0e37cb562ef38aa3aeefca22b3200186bc39242d3d5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.3.3",
|
"bitflags 2.3.3",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"crossterm",
|
"crossterm 0.26.1",
|
||||||
"indoc",
|
"indoc",
|
||||||
"paste",
|
"paste",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
@ -1217,15 +1233,6 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -1276,18 +1283,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.181"
|
version = "1.0.182"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890"
|
checksum = "bdb30a74471f5b7a1fa299f40b4bf1be93af61116df95465b2b5fc419331e430"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.181"
|
version = "1.0.182"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be02f6cb0cd3a5ec20bbcfbcbd749f57daddb1a0882dc2e46a6c236c90b977ed"
|
checksum = "6f4c2c6ea4bc09b5c419012eafcdb0fcef1d9119d626c8f3a0708a5b92d38a70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1328,7 +1335,7 @@ name = "shared"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"crossterm",
|
"crossterm 0.27.0",
|
||||||
"libc",
|
"libc",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
@ -2005,9 +2012,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46aab759304e4d7b2075a9aecba26228bb073ee8c50db796b2c72c676b5d807"
|
checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -76,11 +76,11 @@ impl Executor {
|
|||||||
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
|
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
|
||||||
cx.manager.active_mut().select(optional_bool(&state))
|
cx.manager.active_mut().select(optional_bool(&state))
|
||||||
}
|
}
|
||||||
"visual_mode" => cx.manager.active_mut().visual_mode(exec.named.contains_key("unset")),
|
|
||||||
"select_all" => {
|
"select_all" => {
|
||||||
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
|
let state = exec.named.get("state").cloned().unwrap_or("none".to_string());
|
||||||
cx.manager.active_mut().select_all(optional_bool(&state))
|
cx.manager.active_mut().select_all(optional_bool(&state))
|
||||||
}
|
}
|
||||||
|
"visual_mode" => cx.manager.active_mut().visual_mode(exec.named.contains_key("unset")),
|
||||||
|
|
||||||
// Operation
|
// Operation
|
||||||
"open" => cx.manager.open(exec.named.contains_key("interactive")),
|
"open" => cx.manager.open(exec.named.contains_key("interactive")),
|
||||||
|
@ -15,7 +15,7 @@ pub(super) struct Folder<'a> {
|
|||||||
|
|
||||||
impl<'a> Folder<'a> {
|
impl<'a> Folder<'a> {
|
||||||
pub(super) fn new(cx: &'a Ctx, folder: &'a core::manager::Folder) -> Self {
|
pub(super) fn new(cx: &'a Ctx, folder: &'a core::manager::Folder) -> Self {
|
||||||
Self { cx, folder, is_preview: false, is_selection: true }
|
Self { cx, folder, is_preview: false, is_selection: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -45,6 +45,7 @@ impl<'a> Folder<'a> {
|
|||||||
impl<'a> Widget for Folder<'a> {
|
impl<'a> Widget for Folder<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let window = self.folder.window();
|
let window = self.folder.window();
|
||||||
|
let mode = self.cx.manager.active().mode();
|
||||||
|
|
||||||
let items = window
|
let items = window
|
||||||
.iter()
|
.iter()
|
||||||
@ -57,7 +58,9 @@ impl<'a> Widget for Folder<'a> {
|
|||||||
.map(|x| x.display.as_ref())
|
.map(|x| x.display.as_ref())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
|
|
||||||
if v.is_selected {
|
if (!self.is_selection && v.is_selected)
|
||||||
|
|| (self.is_selection && mode.pending(i, v.is_selected))
|
||||||
|
{
|
||||||
buf.set_style(
|
buf.set_style(
|
||||||
Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 },
|
Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 },
|
||||||
if self.is_selection {
|
if self.is_selection {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use core::manager::{Mode, ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_RATIO};
|
use core::manager::{ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_RATIO};
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}};
|
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}};
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ impl<'a> Widget for Layout<'a> {
|
|||||||
|
|
||||||
// Current
|
// Current
|
||||||
Folder::new(self.cx, manager.current())
|
Folder::new(self.cx, manager.current())
|
||||||
.with_selection(matches!(manager.active().mode(), Mode::Select(_)))
|
.with_selection(manager.active().mode().is_visual())
|
||||||
.render(chunks[1], buf);
|
.render(chunks[1], buf);
|
||||||
|
|
||||||
// Preview
|
// Preview
|
||||||
|
@ -28,16 +28,16 @@
|
|||||||
- `--state=false`: Deselect the current file.
|
- `--state=false`: Deselect the current file.
|
||||||
- `--state=none`: Default, toggle the selection state of the current file.
|
- `--state=none`: Default, toggle the selection state of the current file.
|
||||||
|
|
||||||
- visual_mode: Enter visual mode (selection mode).
|
|
||||||
|
|
||||||
- `--unset`: Enter visual mode (unset mode).
|
|
||||||
|
|
||||||
- select_all
|
- select_all
|
||||||
|
|
||||||
- `--state=true`: Select all files.
|
- `--state=true`: Select all files.
|
||||||
- `--state=false`: Deselect all files.
|
- `--state=false`: Deselect all files.
|
||||||
- `--state=none`: Default, toggle the selection state of all files.
|
- `--state=none`: Default, toggle the selection state of all files.
|
||||||
|
|
||||||
|
- visual_mode: Enter visual mode (selection mode).
|
||||||
|
|
||||||
|
- `--unset`: Enter visual mode (unset mode).
|
||||||
|
|
||||||
### Operation
|
### Operation
|
||||||
|
|
||||||
- open: Open the selected files.
|
- open: Open the selected files.
|
||||||
|
@ -47,7 +47,7 @@ impl TryFrom<String> for Key {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut key = Self::default();
|
let mut key = Self::default();
|
||||||
if !s.starts_with("<") || !s.ends_with(">") {
|
if !s.starts_with('<') || !s.ends_with('>') {
|
||||||
let c = s.chars().next().unwrap();
|
let c = s.chars().next().unwrap();
|
||||||
key.code = KeyCode::Char(c);
|
key.code = KeyCode::Char(c);
|
||||||
key.shift = c.is_ascii_uppercase();
|
key.shift = c.is_ascii_uppercase();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{fs::Metadata, path::{Path, PathBuf}};
|
use std::{borrow::Cow, fs::Metadata, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
@ -43,7 +43,5 @@ impl File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn name(&self) -> Option<String> {
|
pub fn name(&self) -> Option<Cow<str>> { self.path.file_name().map(|s| s.to_string_lossy()) }
|
||||||
self.path.file_name().map(|s| s.to_string_lossy().to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -180,12 +180,7 @@ impl Folder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, item)| item.is_selected) }
|
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, f)| f.is_selected) }
|
||||||
|
|
||||||
pub fn selected(&self) -> Option<Vec<&File>> {
|
|
||||||
let v = self.files.iter().filter(|(_, f)| f.is_selected).map(|(_, f)| f).collect::<Vec<_>>();
|
|
||||||
if v.is_empty() { None } else { Some(v) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rect_current(&self, path: &Path) -> Option<Rect> {
|
pub fn rect_current(&self, path: &Path) -> Option<Rect> {
|
||||||
let pos = self.position(path)? - self.offset;
|
let pos = self.position(path)? - self.offset;
|
||||||
|
@ -98,8 +98,9 @@ impl Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result =
|
let result = emit!(Input(InputOpt::top(format!(
|
||||||
emit!(Input(InputOpt::top(format!("There are {tasks} tasks running, sure to quit? (y/N)"))));
|
"There are {tasks} tasks running, sure to quit? (y/N)"
|
||||||
|
))));
|
||||||
|
|
||||||
if let Ok(choice) = result.await {
|
if let Ok(choice) = result.await {
|
||||||
if choice.to_lowercase() == "y" {
|
if choice.to_lowercase() == "y" {
|
||||||
@ -199,7 +200,7 @@ impl Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(&self) -> bool {
|
pub fn rename(&self) -> bool {
|
||||||
if self.current().has_selected() {
|
if self.in_selecting() {
|
||||||
return self.bulk_rename();
|
return self.bulk_rename();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,8 +361,26 @@ impl Manager {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn hovered(&self) -> Option<&File> { self.tabs.active().current.hovered.as_ref() }
|
pub fn hovered(&self) -> Option<&File> { self.tabs.active().current.hovered.as_ref() }
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn selected(&self) -> Vec<&File> {
|
pub fn selected(&self) -> Vec<&File> {
|
||||||
self.current().selected().or_else(|| self.hovered().map(|h| vec![h])).unwrap_or_default()
|
let mode = &self.active().mode;
|
||||||
|
let files = &self.current().files;
|
||||||
|
|
||||||
|
let selected: Vec<_> = if !mode.is_visual() {
|
||||||
|
files.iter().filter(|(_, f)| f.is_selected).map(|(_, f)| f).collect()
|
||||||
|
} else {
|
||||||
|
files
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, (_, f))| mode.pending(*i, f.is_selected))
|
||||||
|
.map(|(_, (_, f))| f)
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
if selected.is_empty() { self.hovered().map(|h| vec![h]).unwrap_or_default() } else { selected }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn in_selecting(&self) -> bool {
|
||||||
|
self.active().mode.is_visual() || self.current().has_selected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use std::fmt::Display;
|
use std::{collections::BTreeSet, fmt::Display};
|
||||||
|
|
||||||
use config::theme::{self, ColorGroup};
|
use config::theme::{self, ColorGroup};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
#[default]
|
#[default]
|
||||||
Normal,
|
Normal,
|
||||||
Select(usize),
|
Select(usize, BTreeSet<usize>),
|
||||||
Unset(usize),
|
Unset(usize, BTreeSet<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
@ -15,27 +15,50 @@ impl Mode {
|
|||||||
pub fn color<'a>(&self, group: &'a ColorGroup) -> &'a theme::Color {
|
pub fn color<'a>(&self, group: &'a ColorGroup) -> &'a theme::Color {
|
||||||
match *self {
|
match *self {
|
||||||
Mode::Normal => &group.normal,
|
Mode::Normal => &group.normal,
|
||||||
Mode::Select(_) => &group.select,
|
Mode::Select(..) => &group.select,
|
||||||
Mode::Unset(_) => &group.unset,
|
Mode::Unset(..) => &group.unset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start(&self) -> Option<usize> {
|
pub fn visual(&self) -> Option<(usize, &BTreeSet<usize>)> {
|
||||||
match self {
|
match self {
|
||||||
Mode::Normal => None,
|
Mode::Normal => None,
|
||||||
Mode::Select(n) => Some(*n),
|
Mode::Select(start, indices) => Some((*start, indices)),
|
||||||
Mode::Unset(n) => Some(*n),
|
Mode::Unset(start, indices) => Some((*start, indices)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn visual_mut(&mut self) -> Option<(usize, &mut BTreeSet<usize>)> {
|
||||||
|
match self {
|
||||||
|
Mode::Normal => None,
|
||||||
|
Mode::Select(start, indices) => Some((*start, indices)),
|
||||||
|
Mode::Unset(start, indices) => Some((*start, indices)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pending(&self, idx: usize, state: bool) -> bool {
|
||||||
|
match self {
|
||||||
|
Mode::Normal => state,
|
||||||
|
Mode::Select(_, indices) => state || indices.contains(&idx),
|
||||||
|
Mode::Unset(_, indices) => state && !indices.contains(&idx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mode {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Mode {
|
impl Display for Mode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Mode::Normal => write!(f, "NORMAL"),
|
Mode::Normal => write!(f, "NORMAL"),
|
||||||
Mode::Select(_) => write!(f, "SELECT"),
|
Mode::Select(..) => write!(f, "SELECT"),
|
||||||
Mode::Unset(_) => write!(f, "UN-SET"),
|
Mode::Unset(..) => write!(f, "UN-SET"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{collections::BTreeMap, mem, path::{Path, PathBuf}};
|
use std::{collections::{BTreeMap, BTreeSet}, mem, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use shared::Defer;
|
use shared::Defer;
|
||||||
@ -33,7 +33,12 @@ impl Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape(&mut self) -> bool {
|
pub fn escape(&mut self) -> bool {
|
||||||
if matches!(self.mode, Mode::Select(_) | Mode::Unset(_)) {
|
if let Some((_, indices)) = self.mode.visual() {
|
||||||
|
let b = matches!(self.mode, Mode::Select(..));
|
||||||
|
for idx in indices.iter() {
|
||||||
|
self.current.select(Some(*idx), Some(b));
|
||||||
|
}
|
||||||
|
|
||||||
self.mode = Mode::Normal;
|
self.mode = Mode::Normal;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -46,7 +51,6 @@ impl Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, step: isize) -> bool {
|
pub fn arrow(&mut self, step: isize) -> bool {
|
||||||
let before = self.current.cursor();
|
|
||||||
let ok = if step > 0 {
|
let ok = if step > 0 {
|
||||||
self.current.next(step as usize)
|
self.current.next(step as usize)
|
||||||
} else {
|
} else {
|
||||||
@ -57,15 +61,12 @@ impl Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Visual selection
|
// Visual selection
|
||||||
if let Some(start) = self.mode.start() {
|
if let Some((start, items)) = self.mode.visual_mut() {
|
||||||
let after = self.current.cursor();
|
let after = self.current.cursor();
|
||||||
if (after > before && before < start) || (after < before && before > start) {
|
|
||||||
for i in before.min(start)..=start.max(before) {
|
items.clear();
|
||||||
self.current.select(Some(i), Some(false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in start.min(after)..=after.max(start) {
|
for i in start.min(after)..=after.max(start) {
|
||||||
self.current.select(Some(i), Some(true));
|
items.insert(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,11 +252,9 @@ impl Tab {
|
|||||||
let idx = self.current.cursor();
|
let idx = self.current.cursor();
|
||||||
|
|
||||||
if unset {
|
if unset {
|
||||||
self.mode = Mode::Unset(idx);
|
self.mode = Mode::Unset(idx, BTreeSet::from([idx]));
|
||||||
self.current.select(Some(idx), Some(false));
|
|
||||||
} else {
|
} else {
|
||||||
self.mode = Mode::Select(idx);
|
self.mode = Mode::Select(idx, BTreeSet::from([idx]));
|
||||||
self.current.select(Some(idx), Some(true));
|
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user