mirror of
https://github.com/atuinsh/atuin.git
synced 2024-11-07 17:04:51 +01:00
chore: use fork of skim (#803)
* use fuzzy-matcher instead of skim switch to a search-engine abstraction * fmt * fix deprecated warnings
This commit is contained in:
parent
4325ec4624
commit
bb7f00dbef
330
Cargo.lock
generated
330
Cargo.lock
generated
@ -37,12 +37,6 @@ version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.58"
|
||||
@ -93,6 +87,7 @@ dependencies = [
|
||||
"eyre",
|
||||
"fs-err",
|
||||
"futures-util",
|
||||
"fuzzy-matcher",
|
||||
"indicatif",
|
||||
"interim",
|
||||
"itertools",
|
||||
@ -103,7 +98,6 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"skim",
|
||||
"tiny-bip39",
|
||||
"tokio",
|
||||
"tracing-subscriber",
|
||||
@ -329,7 +323,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time 0.1.44",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
@ -457,20 +451,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
@ -481,30 +461,6 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset 0.7.1",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.6"
|
||||
@ -561,82 +517,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defer-drop"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f613ec9fa66a6b28cdb1842b27f9adf24f39f9afc4dcdd9fdecee4aca7945c57"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.5"
|
||||
@ -666,16 +546,6 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
@ -687,17 +557,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.3"
|
||||
@ -1122,12 +981,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
@ -1349,24 +1202,6 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
@ -1397,31 +1232,6 @@ dependencies = [
|
||||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
@ -1537,9 +1347,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
@ -1773,28 +1583,6 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@ -1817,9 +1605,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -1837,9 +1625,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
@ -2203,31 +1991,6 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90531723b08e4d6d71b791108faf51f03e1b4a7784f96b2b87f852ebc247228"
|
||||
|
||||
[[package]]
|
||||
name = "skim"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cebed5f897cd6c0d80fbe30adb36c0abf7400e93043a63ae56458495642b3485"
|
||||
dependencies = [
|
||||
"beef",
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"crossbeam",
|
||||
"defer-drop",
|
||||
"derive_builder",
|
||||
"fuzzy-matcher",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nix 0.25.1",
|
||||
"rayon",
|
||||
"regex",
|
||||
"time 0.3.17",
|
||||
"timer",
|
||||
"tuikit",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@ -2449,17 +2212,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
@ -2519,31 +2271,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "timer"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-bip39"
|
||||
version = "1.0.0"
|
||||
@ -2757,20 +2484,6 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "tuikit"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e19c6ab038babee3d50c8c12ff8b910bdb2196f62278776422f50390d8e53d8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nix 0.24.3",
|
||||
"term",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
@ -2845,12 +2558,6 @@ version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.2.1"
|
||||
@ -2872,27 +2579,6 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aae21c12ad2ec2d168c236f369c38ff332bc1134f7246350dca641437365045"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
|
@ -73,8 +73,8 @@ semver = "1.0.14"
|
||||
runtime-format = "0.1.2"
|
||||
tiny-bip39 = "1"
|
||||
futures-util = "0.3"
|
||||
skim = { version = "0.10.2", default-features = false }
|
||||
ratatui = "0.20.1"
|
||||
fuzzy-matcher = "0.3.7"
|
||||
|
||||
[dependencies.tracing-subscriber]
|
||||
version = "0.3"
|
||||
|
@ -41,7 +41,7 @@ pub fn current_context() -> Context {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Database: Send + Sync {
|
||||
pub trait Database: Send + Sync + 'static {
|
||||
async fn save(&mut self, h: &History) -> Result<()>;
|
||||
async fn save_bulk(&mut self, h: &[History]) -> Result<()>;
|
||||
|
||||
@ -375,7 +375,7 @@ impl Database for Sqlite {
|
||||
match search_mode {
|
||||
SearchMode::Prefix => sql.and_where_like_left("command", query),
|
||||
SearchMode::FullText => sql.and_where_like_any("command", query),
|
||||
SearchMode::Skim | SearchMode::Fuzzy => {
|
||||
_ => {
|
||||
// don't recompile the regex on successive calls!
|
||||
lazy_static! {
|
||||
static ref SPLIT_REGEX: Regex = Regex::new(r" +").unwrap();
|
||||
|
@ -46,16 +46,11 @@ impl SearchMode {
|
||||
pub fn next(&self, settings: &Settings) -> Self {
|
||||
match self {
|
||||
SearchMode::Prefix => SearchMode::FullText,
|
||||
SearchMode::FullText => {
|
||||
// if the user is using skim, we go to skim, otherwise fuzzy.
|
||||
if settings.search_mode == SearchMode::Skim {
|
||||
SearchMode::Skim
|
||||
} else {
|
||||
SearchMode::Fuzzy
|
||||
}
|
||||
}
|
||||
SearchMode::Fuzzy => SearchMode::Prefix,
|
||||
SearchMode::Skim => SearchMode::Prefix,
|
||||
// if the user is using skim, we go to skim
|
||||
SearchMode::FullText if settings.search_mode == SearchMode::Skim => SearchMode::Skim,
|
||||
// otherwise fuzzy.
|
||||
SearchMode::FullText => SearchMode::Fuzzy,
|
||||
SearchMode::Fuzzy | SearchMode::Skim => SearchMode::Prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use chrono::{Months, NaiveDate};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn uuid_v4() -> String {
|
||||
@ -49,19 +49,9 @@ pub fn get_current_dir() -> String {
|
||||
}
|
||||
|
||||
pub fn get_days_from_month(year: i32, month: u32) -> i64 {
|
||||
NaiveDate::from_ymd(
|
||||
match month {
|
||||
12 => year + 1,
|
||||
_ => year,
|
||||
},
|
||||
match month {
|
||||
12 => 1,
|
||||
_ => month + 1,
|
||||
},
|
||||
1,
|
||||
)
|
||||
.signed_duration_since(NaiveDate::from_ymd(year, month, 1))
|
||||
.num_days()
|
||||
let Some(start) = NaiveDate::from_ymd_opt(year, month, 1) else { return 30 };
|
||||
let Some(end) = start.checked_add_months(Months::new(1)) else { return 30 };
|
||||
end.signed_duration_since(start).num_days()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -108,4 +98,23 @@ mod tests {
|
||||
assert_eq!(data_dir(), PathBuf::from("/home/user/.local/share/atuin"));
|
||||
env::remove_var("HOME");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn days_from_month() {
|
||||
assert_eq!(get_days_from_month(2023, 1), 31);
|
||||
assert_eq!(get_days_from_month(2023, 2), 28);
|
||||
assert_eq!(get_days_from_month(2023, 3), 31);
|
||||
assert_eq!(get_days_from_month(2023, 4), 30);
|
||||
assert_eq!(get_days_from_month(2023, 5), 31);
|
||||
assert_eq!(get_days_from_month(2023, 6), 30);
|
||||
assert_eq!(get_days_from_month(2023, 7), 31);
|
||||
assert_eq!(get_days_from_month(2023, 8), 31);
|
||||
assert_eq!(get_days_from_month(2023, 9), 30);
|
||||
assert_eq!(get_days_from_month(2023, 10), 31);
|
||||
assert_eq!(get_days_from_month(2023, 11), 30);
|
||||
assert_eq!(get_days_from_month(2023, 12), 31);
|
||||
|
||||
// leap years
|
||||
assert_eq!(get_days_from_month(2024, 2), 29);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl Cmd {
|
||||
Self::History(history) => history.run(&settings, &mut db).await,
|
||||
Self::Import(import) => import.run(&mut db).await,
|
||||
Self::Stats(stats) => stats.run(&mut db, &settings).await,
|
||||
Self::Search(search) => search.run(&mut db, &mut settings).await,
|
||||
Self::Search(search) => search.run(db, &mut settings).await,
|
||||
#[cfg(feature = "sync")]
|
||||
Self::Sync(sync) => sync.run(settings, &mut db).await,
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ use super::history::ListMode;
|
||||
|
||||
mod cursor;
|
||||
mod duration;
|
||||
mod engines;
|
||||
mod history_list;
|
||||
mod interactive;
|
||||
mod skim_impl;
|
||||
pub use duration::{format_duration, format_duration_into};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
@ -87,7 +87,7 @@ pub struct Cmd {
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
pub async fn run(self, db: &mut impl Database, settings: &mut Settings) -> Result<()> {
|
||||
pub async fn run(self, mut db: impl Database, settings: &mut Settings) -> Result<()> {
|
||||
if self.search_mode.is_some() {
|
||||
settings.search_mode = self.search_mode.unwrap();
|
||||
}
|
||||
@ -113,7 +113,7 @@ impl Cmd {
|
||||
self.after.clone(),
|
||||
self.limit,
|
||||
&self.query,
|
||||
db,
|
||||
&mut db,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -142,7 +142,7 @@ impl Cmd {
|
||||
self.after.clone(),
|
||||
self.limit,
|
||||
&self.query,
|
||||
db,
|
||||
&mut db,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
46
src/command/client/search/engines.rs
Normal file
46
src/command/client/search/engines.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use async_trait::async_trait;
|
||||
use atuin_client::{
|
||||
database::{Context, Database},
|
||||
history::History,
|
||||
settings::{FilterMode, SearchMode},
|
||||
};
|
||||
use eyre::Result;
|
||||
|
||||
use super::cursor::Cursor;
|
||||
|
||||
pub mod db;
|
||||
pub mod skim;
|
||||
|
||||
pub fn engine(search_mode: SearchMode) -> Box<dyn SearchEngine> {
|
||||
match search_mode {
|
||||
SearchMode::Skim => Box::new(skim::Search::new()) as Box<_>,
|
||||
mode => Box::new(db::Search(mode)) as Box<_>,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SearchState {
|
||||
pub input: Cursor,
|
||||
pub filter_mode: FilterMode,
|
||||
pub context: Context,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SearchEngine: Send + Sync + 'static {
|
||||
async fn full_query(
|
||||
&mut self,
|
||||
state: &SearchState,
|
||||
db: &mut dyn Database,
|
||||
) -> Result<Vec<History>>;
|
||||
|
||||
async fn query(&mut self, state: &SearchState, db: &mut dyn Database) -> Result<Vec<History>> {
|
||||
if state.input.as_str().is_empty() {
|
||||
Ok(db
|
||||
.list(state.filter_mode, &state.context, Some(200), true)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>())
|
||||
} else {
|
||||
self.full_query(state, db).await
|
||||
}
|
||||
}
|
||||
}
|
30
src/command/client/search/engines/db.rs
Normal file
30
src/command/client/search/engines/db.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use async_trait::async_trait;
|
||||
use atuin_client::{database::Database, history::History, settings::SearchMode};
|
||||
use eyre::Result;
|
||||
|
||||
use super::{SearchEngine, SearchState};
|
||||
|
||||
pub struct Search(pub SearchMode);
|
||||
|
||||
#[async_trait]
|
||||
impl SearchEngine for Search {
|
||||
async fn full_query(
|
||||
&mut self,
|
||||
state: &SearchState,
|
||||
db: &mut dyn Database,
|
||||
) -> Result<Vec<History>> {
|
||||
Ok(db
|
||||
.search(
|
||||
self.0,
|
||||
state.filter_mode,
|
||||
&state.context,
|
||||
state.input.as_str(),
|
||||
Some(200),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
}
|
145
src/command/client/search/engines/skim.rs
Normal file
145
src/command/client/search/engines/skim.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use std::path::Path;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use atuin_client::{database::Database, history::History, settings::FilterMode};
|
||||
use chrono::Utc;
|
||||
use eyre::Result;
|
||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||
use tokio::task::yield_now;
|
||||
|
||||
use super::{SearchEngine, SearchState};
|
||||
|
||||
pub struct Search {
|
||||
all_history: Vec<(History, i32)>,
|
||||
engine: SkimMatcherV2,
|
||||
}
|
||||
|
||||
impl Search {
|
||||
pub fn new() -> Self {
|
||||
Search {
|
||||
all_history: vec![],
|
||||
engine: SkimMatcherV2::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SearchEngine for Search {
|
||||
async fn full_query(
|
||||
&mut self,
|
||||
state: &SearchState,
|
||||
db: &mut dyn Database,
|
||||
) -> Result<Vec<History>> {
|
||||
if self.all_history.is_empty() {
|
||||
self.all_history = db.all_with_count().await.unwrap();
|
||||
}
|
||||
|
||||
Ok(fuzzy_search(&self.engine, state, &self.all_history).await)
|
||||
}
|
||||
}
|
||||
|
||||
async fn fuzzy_search(
|
||||
engine: &SkimMatcherV2,
|
||||
state: &SearchState,
|
||||
all_history: &[(History, i32)],
|
||||
) -> Vec<History> {
|
||||
let mut set = Vec::with_capacity(200);
|
||||
let mut ranks = Vec::with_capacity(200);
|
||||
let query = state.input.as_str();
|
||||
let now = Utc::now();
|
||||
|
||||
for (i, (history, count)) in all_history.iter().enumerate() {
|
||||
if i % 256 == 0 {
|
||||
yield_now().await;
|
||||
}
|
||||
match state.filter_mode {
|
||||
FilterMode::Global => {}
|
||||
FilterMode::Host if history.hostname == state.context.hostname => {}
|
||||
FilterMode::Session if history.session == state.context.session => {}
|
||||
FilterMode::Directory if history.cwd == state.context.cwd => {}
|
||||
_ => continue,
|
||||
}
|
||||
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
|
||||
if let Some((score, indices)) = engine.fuzzy_indices(&history.command, query) {
|
||||
let begin = indices.first().copied().unwrap_or_default();
|
||||
|
||||
let mut duration = ((now - history.timestamp).num_seconds() as f64).log2();
|
||||
if !duration.is_finite() || duration <= 1.0 {
|
||||
duration = 1.0;
|
||||
}
|
||||
// these + X.0 just make the log result a bit smoother.
|
||||
// log is very spiky towards 1-4, but I want a gradual decay.
|
||||
// eg:
|
||||
// log2(4) = 2, log2(5) = 2.3 (16% increase)
|
||||
// log2(8) = 3, log2(9) = 3.16 (5% increase)
|
||||
// log2(16) = 4, log2(17) = 4.08 (2% increase)
|
||||
let count = (*count as f64 + 8.0).log2();
|
||||
let begin = (begin as f64 + 16.0).log2();
|
||||
let path = path_dist(history.cwd.as_ref(), state.context.cwd.as_ref());
|
||||
let path = (path as f64 + 8.0).log2();
|
||||
|
||||
// reduce longer durations, raise higher counts, raise matches close to the start
|
||||
let score = (-score as f64) * count / path / duration / begin;
|
||||
|
||||
'insert: {
|
||||
// algorithm:
|
||||
// 1. find either the position that this command ranks
|
||||
// 2. find the same command positioned better than our rank.
|
||||
for i in 0..set.len() {
|
||||
// do we out score the corrent position?
|
||||
if ranks[i] > score {
|
||||
ranks.insert(i, score);
|
||||
set.insert(i, history.clone());
|
||||
let mut j = i + 1;
|
||||
while j < set.len() {
|
||||
// remove duplicates that have a worse score
|
||||
if set[j].command == history.command {
|
||||
ranks.remove(j);
|
||||
set.remove(j);
|
||||
|
||||
// break this while loop because there won't be any other
|
||||
// duplicates.
|
||||
break;
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
|
||||
// keep it limited
|
||||
if ranks.len() > 200 {
|
||||
ranks.pop();
|
||||
set.pop();
|
||||
}
|
||||
|
||||
break 'insert;
|
||||
}
|
||||
// don't continue if this command has a better score already
|
||||
if set[i].command == history.command {
|
||||
break 'insert;
|
||||
}
|
||||
}
|
||||
|
||||
if set.len() < 200 {
|
||||
ranks.push(score);
|
||||
set.push(history.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
|
||||
fn path_dist(a: &Path, b: &Path) -> usize {
|
||||
let mut a: Vec<_> = a.components().collect();
|
||||
let b: Vec<_> = b.components().collect();
|
||||
|
||||
let mut dist = 0;
|
||||
|
||||
// pop a until there's a common anscestor
|
||||
while !b.starts_with(&a) {
|
||||
dist += 1;
|
||||
a.pop();
|
||||
}
|
||||
|
||||
b.len() - a.len() + dist
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use atuin_client::history::History;
|
||||
use ratatui::{
|
||||
@ -8,10 +8,10 @@ use ratatui::{
|
||||
widgets::{Block, StatefulWidget, Widget},
|
||||
};
|
||||
|
||||
use super::{format_duration, interactive::HistoryWrapper};
|
||||
use super::format_duration;
|
||||
|
||||
pub struct HistoryList<'a> {
|
||||
history: &'a [Arc<HistoryWrapper>],
|
||||
history: &'a [History],
|
||||
block: Option<Block<'a>>,
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ impl<'a> StatefulWidget for HistoryList<'a> {
|
||||
}
|
||||
|
||||
impl<'a> HistoryList<'a> {
|
||||
pub fn new(history: &'a [Arc<HistoryWrapper>]) -> Self {
|
||||
pub fn new(history: &'a [History]) -> Self {
|
||||
Self {
|
||||
history,
|
||||
block: None,
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::{
|
||||
io::{stdout, Write},
|
||||
ops::Deref,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@ -12,21 +10,21 @@ use crossterm::{
|
||||
use eyre::Result;
|
||||
use futures_util::FutureExt;
|
||||
use semver::Version;
|
||||
use skim::SkimItem;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use atuin_client::{
|
||||
database::current_context,
|
||||
database::Database,
|
||||
database::{current_context, Context},
|
||||
history::History,
|
||||
settings::{ExitMode, FilterMode, SearchMode, Settings},
|
||||
};
|
||||
|
||||
use super::{
|
||||
cursor::Cursor,
|
||||
engines::{SearchEngine, SearchState},
|
||||
history_list::{HistoryList, ListState, PREFIX_LENGTH},
|
||||
};
|
||||
use crate::VERSION;
|
||||
use crate::{command::client::search::engines, VERSION};
|
||||
use ratatui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
layout::{Alignment, Constraint, Direction, Layout},
|
||||
@ -43,68 +41,16 @@ struct State {
|
||||
history_count: i64,
|
||||
update_needed: Option<Version>,
|
||||
results_state: ListState,
|
||||
search: SearchState,
|
||||
|
||||
// only allocated if using skim
|
||||
all_history: Vec<Arc<HistoryWrapper>>,
|
||||
}
|
||||
|
||||
pub struct SearchState {
|
||||
pub input: Cursor,
|
||||
pub filter_mode: FilterMode,
|
||||
pub search_mode: SearchMode,
|
||||
/// Store if the user has _just_ changed the search mode.
|
||||
/// If so, we change the UI to show the search mode instead
|
||||
/// of the filter mode until user starts typing again.
|
||||
switched_search_mode: bool,
|
||||
pub context: Context,
|
||||
search_mode: SearchMode,
|
||||
|
||||
search: SearchState,
|
||||
engine: Box<dyn SearchEngine>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn query_results(&mut self, db: &mut impl Database) -> Result<Vec<Arc<HistoryWrapper>>> {
|
||||
let i = self.search.input.as_str();
|
||||
let results = if i.is_empty() {
|
||||
db.list(
|
||||
self.search.filter_mode,
|
||||
&self.search.context,
|
||||
Some(200),
|
||||
true,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|history| HistoryWrapper { history, count: 1 })
|
||||
.map(Arc::new)
|
||||
.collect::<Vec<_>>()
|
||||
} else if self.search.search_mode == SearchMode::Skim {
|
||||
if self.all_history.is_empty() {
|
||||
self.all_history = db
|
||||
.all_with_count()
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(history, count)| HistoryWrapper { history, count })
|
||||
.map(Arc::new)
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
super::skim_impl::fuzzy_search(&self.search, &self.all_history).await
|
||||
} else {
|
||||
db.search(
|
||||
self.search.search_mode,
|
||||
self.search.filter_mode,
|
||||
&self.search.context,
|
||||
i,
|
||||
Some(200),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|history| HistoryWrapper { history, count: 1 })
|
||||
.map(Arc::new)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
async fn query_results(&mut self, db: &mut dyn Database) -> Result<Vec<History>> {
|
||||
let results = self.engine.query(&self.search, db).await?;
|
||||
self.results_state.select(0);
|
||||
Ok(results)
|
||||
}
|
||||
@ -154,7 +100,7 @@ impl State {
|
||||
let ctrl = input.modifiers.contains(KeyModifiers::CONTROL);
|
||||
let alt = input.modifiers.contains(KeyModifiers::ALT);
|
||||
// reset the state, will be set to true later if user really did change it
|
||||
self.search.switched_search_mode = false;
|
||||
self.switched_search_mode = false;
|
||||
match input.code {
|
||||
KeyCode::Char('c' | 'd' | 'g') if ctrl => return Some(RETURN_ORIGINAL),
|
||||
KeyCode::Esc => {
|
||||
@ -228,8 +174,9 @@ impl State {
|
||||
self.search.filter_mode = FILTER_MODES[i];
|
||||
}
|
||||
KeyCode::Char('s') if ctrl => {
|
||||
self.search.switched_search_mode = true;
|
||||
self.search.search_mode = self.search.search_mode.next(settings);
|
||||
self.switched_search_mode = true;
|
||||
self.search_mode = self.search_mode.next(settings);
|
||||
self.engine = engines::engine(self.search_mode);
|
||||
}
|
||||
KeyCode::Down if self.results_state.selected() == 0 => {
|
||||
return Some(match settings.exit_mode {
|
||||
@ -275,7 +222,7 @@ impl State {
|
||||
fn draw<T: Backend>(
|
||||
&mut self,
|
||||
f: &mut Frame<'_, T>,
|
||||
results: &[Arc<HistoryWrapper>],
|
||||
results: &[History],
|
||||
compact: bool,
|
||||
show_preview: bool,
|
||||
) {
|
||||
@ -391,7 +338,7 @@ impl State {
|
||||
stats
|
||||
}
|
||||
|
||||
fn build_results_list(compact: bool, results: &[Arc<HistoryWrapper>]) -> HistoryList {
|
||||
fn build_results_list(compact: bool, results: &[History]) -> HistoryList {
|
||||
let results_list = if compact {
|
||||
HistoryList::new(results)
|
||||
} else {
|
||||
@ -407,8 +354,8 @@ impl State {
|
||||
fn build_input(&mut self, compact: bool, chunk_width: usize) -> Paragraph {
|
||||
/// Max width of the UI box showing current mode
|
||||
const MAX_WIDTH: usize = 14;
|
||||
let (pref, mode) = if self.search.switched_search_mode {
|
||||
(" SRCH:", self.search.search_mode.as_str())
|
||||
let (pref, mode) = if self.switched_search_mode {
|
||||
(" SRCH:", self.search_mode.as_str())
|
||||
} else {
|
||||
("", self.search.filter_mode.as_str())
|
||||
};
|
||||
@ -431,7 +378,7 @@ impl State {
|
||||
|
||||
fn build_preview(
|
||||
&mut self,
|
||||
results: &[Arc<HistoryWrapper>],
|
||||
results: &[History],
|
||||
compact: bool,
|
||||
preview_width: u16,
|
||||
chunk_width: usize,
|
||||
@ -513,23 +460,6 @@ impl Write for Stdout {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HistoryWrapper {
|
||||
history: History,
|
||||
pub count: i32,
|
||||
}
|
||||
impl Deref for HistoryWrapper {
|
||||
type Target = History;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.history
|
||||
}
|
||||
}
|
||||
impl SkimItem for HistoryWrapper {
|
||||
fn text(&self) -> std::borrow::Cow<str> {
|
||||
std::borrow::Cow::Borrowed(self.history.command.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
// this is a big blob of horrible! clean it up!
|
||||
// for now, it works. But it'd be great if it were more easily readable, and
|
||||
// modular. I'd like to add some more stats and stuff at some point
|
||||
@ -537,7 +467,7 @@ impl SkimItem for HistoryWrapper {
|
||||
pub async fn history(
|
||||
query: &[String],
|
||||
settings: &Settings,
|
||||
db: &mut impl Database,
|
||||
mut db: impl Database,
|
||||
) -> Result<String> {
|
||||
let stdout = Stdout::new(settings.inline_height > 0)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
@ -562,10 +492,14 @@ pub async fn history(
|
||||
|
||||
let context = current_context();
|
||||
|
||||
let history_count = db.history_count().await?;
|
||||
|
||||
let mut app = State {
|
||||
history_count: db.history_count().await?,
|
||||
history_count,
|
||||
results_state: ListState::default(),
|
||||
update_needed: None,
|
||||
switched_search_mode: false,
|
||||
search_mode: settings.search_mode,
|
||||
search: SearchState {
|
||||
input,
|
||||
context,
|
||||
@ -576,13 +510,11 @@ pub async fn history(
|
||||
} else {
|
||||
settings.filter_mode
|
||||
},
|
||||
search_mode: settings.search_mode,
|
||||
switched_search_mode: false,
|
||||
},
|
||||
all_history: Vec::new(),
|
||||
engine: engines::engine(settings.search_mode),
|
||||
};
|
||||
|
||||
let mut results = app.query_results(db).await?;
|
||||
let mut results = app.query_results(&mut db).await?;
|
||||
|
||||
let index = 'render: loop {
|
||||
let compact = match settings.style {
|
||||
@ -596,7 +528,7 @@ pub async fn history(
|
||||
|
||||
let initial_input = app.search.input.as_str().to_owned();
|
||||
let initial_filter_mode = app.search.filter_mode;
|
||||
let initial_search_mode = app.search.search_mode;
|
||||
let initial_search_mode = app.search_mode;
|
||||
|
||||
let event_ready = tokio::task::spawn_blocking(|| event::poll(Duration::from_millis(250)));
|
||||
|
||||
@ -620,9 +552,9 @@ pub async fn history(
|
||||
|
||||
if initial_input != app.search.input.as_str()
|
||||
|| initial_filter_mode != app.search.filter_mode
|
||||
|| initial_search_mode != app.search.search_mode
|
||||
|| initial_search_mode != app.search_mode
|
||||
{
|
||||
results = app.query_results(db).await?;
|
||||
results = app.query_results(&mut db).await?;
|
||||
}
|
||||
};
|
||||
|
||||
@ -632,7 +564,7 @@ pub async fn history(
|
||||
|
||||
if index < results.len() {
|
||||
// index is in bounds so we return that entry
|
||||
Ok(results.swap_remove(index).command.clone())
|
||||
Ok(results.swap_remove(index).command)
|
||||
} else if index == RETURN_ORIGINAL {
|
||||
Ok(String::new())
|
||||
} else {
|
||||
|
@ -1,92 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use atuin_client::settings::FilterMode;
|
||||
use chrono::Utc;
|
||||
use skim::{prelude::ExactOrFuzzyEngineFactory, MatchEngineFactory};
|
||||
use tokio::task::yield_now;
|
||||
|
||||
use super::interactive::{HistoryWrapper, SearchState};
|
||||
|
||||
pub async fn fuzzy_search(
|
||||
state: &SearchState,
|
||||
all_history: &[Arc<HistoryWrapper>],
|
||||
) -> Vec<Arc<HistoryWrapper>> {
|
||||
let mut set = Vec::with_capacity(200);
|
||||
let mut ranks = Vec::with_capacity(200);
|
||||
let engine = ExactOrFuzzyEngineFactory::builder().fuzzy_algorithm(skim::FuzzyAlgorithm::SkimV2);
|
||||
let query = state.input.as_str();
|
||||
let engine = engine.create_engine(query);
|
||||
let now = Utc::now();
|
||||
|
||||
for (i, item) in all_history.iter().enumerate() {
|
||||
if i % 256 == 0 {
|
||||
yield_now().await;
|
||||
}
|
||||
match state.filter_mode {
|
||||
FilterMode::Global => {}
|
||||
FilterMode::Host if item.hostname == state.context.hostname => {}
|
||||
FilterMode::Session if item.session == state.context.session => {}
|
||||
FilterMode::Directory if item.cwd == state.context.cwd => {}
|
||||
_ => continue,
|
||||
}
|
||||
#[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
|
||||
if let Some(res) = engine.match_item(item.clone()) {
|
||||
let [score, begin, _, _] = res.rank;
|
||||
|
||||
let mut duration = ((now - item.timestamp).num_seconds() as f64).log2();
|
||||
if !duration.is_finite() || duration <= 1.0 {
|
||||
duration = 1.0;
|
||||
}
|
||||
let count = (item.count as f64 + 16.0).log2();
|
||||
let begin = (begin as f64 + 16.0).log2();
|
||||
|
||||
// reduce longer durations, raise higher counts, raise matches close to the start
|
||||
let score = (score as f64) * count / duration / begin;
|
||||
|
||||
'insert: {
|
||||
// algorithm:
|
||||
// 1. find either the position that this command ranks
|
||||
// 2. find the same command positioned better than our rank.
|
||||
for i in 0..set.len() {
|
||||
// do we out score the corrent position?
|
||||
if ranks[i] > score {
|
||||
ranks.insert(i, score);
|
||||
set.insert(i, item.clone());
|
||||
let mut j = i + 1;
|
||||
while j < set.len() {
|
||||
// remove duplicates that have a worse score
|
||||
if set[j].command == item.command {
|
||||
ranks.remove(j);
|
||||
set.remove(j);
|
||||
|
||||
// break this while loop because there won't be any other
|
||||
// duplicates.
|
||||
break;
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
|
||||
// keep it limited
|
||||
if ranks.len() > 200 {
|
||||
ranks.pop();
|
||||
set.pop();
|
||||
}
|
||||
|
||||
break 'insert;
|
||||
}
|
||||
// don't continue if this command has a better score already
|
||||
if set[i].command == item.command {
|
||||
break 'insert;
|
||||
}
|
||||
}
|
||||
|
||||
if set.len() < 200 {
|
||||
ranks.push(score);
|
||||
set.push(item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
}
|
Loading…
Reference in New Issue
Block a user