~kennylevinsen/public-inbox

wldash: feat: add ranking based on usage to launcher widget v1 NEEDS REVISION

Cyril Levis: 1
 feat: add ranking based on usage to launcher widget

 5 files changed, 114 insertions(+), 4 deletions(-)
#660122 .build.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~kennylevinsen/public-inbox/patches/27829/mbox | git am -3
Learn more about email & git

[PATCH wldash] feat: add ranking based on usage to launcher widget Export this patch

---
 Cargo.lock              | 51 +++++++++++++++++++++++++++++++++++++++++
 Cargo.toml              |  1 +
 src/data.rs             | 44 +++++++++++++++++++++++++++++++++++
 src/main.rs             |  1 +
 src/widgets/launcher.rs | 21 +++++++++++++----
 5 files changed, 114 insertions(+), 4 deletions(-)
 create mode 100644 src/data.rs

diff --git a/Cargo.lock b/Cargo.lock
index 96bd23d..01a136e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -131,6 +131,26 @@ dependencies = [
 "generic-array",
]

[[package]]
name = "dirs"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
 "dirs-sys",
]

[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
 "libc",
 "redox_users",
 "winapi",
]

[[package]]
name = "dlib"
version = "0.4.2"
@@ -185,6 +205,17 @@ dependencies = [
 "typenum",
]

[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi",
]

[[package]]
name = "itoa"
version = "0.4.7"
@@ -495,6 +526,25 @@ dependencies = [
 "pest_derive",
]

[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
 "bitflags",
]

[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
 "getrandom",
 "redox_syscall",
]

[[package]]
name = "rust-ini"
version = "0.14.0"
@@ -759,6 +809,7 @@ dependencies = [
 "byteorder",
 "chrono",
 "dbus",
 "dirs",
 "dlib",
 "fontconfig",
 "fuzzy-matcher",
diff --git a/Cargo.toml b/Cargo.toml
index c3fc985..232dc15 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,6 +37,7 @@ rcalc_lib = "0.9"
rust-ini = "0.14"
shlex = "0.1"
timerfd = "1.0.0"
dirs = "3.0"

serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
diff --git a/src/data.rs b/src/data.rs
new file mode 100644
index 0000000..34a5dfe
--- /dev/null
+++ b/src/data.rs
@@ -0,0 +1,44 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::fs::{create_dir_all, File, OpenOptions};
use std::path::PathBuf;

#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
pub struct Data {
    pub entries: HashMap<String, i64>,
}

impl Data {
    pub fn load() -> Result<Data, Box<dyn Error>> {
        Ok(serde_yaml::from_reader(Self::read_file()?)?)
    }

    pub fn save(&self) -> Result<(), Box<dyn Error>> {
        Ok(serde_yaml::to_writer(Self::write_file()?, self)?)
    }

    fn read_file() -> Result<File, Box<dyn Error>> {
        Ok(OpenOptions::new()
            .create(true)
            .write(true)
            .read(true)
            .open(Self::path()?)?)
    }

    fn write_file() -> Result<File, Box<dyn Error>> {
        Ok(OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(Self::path()?)?)
    }

    fn path() -> Result<PathBuf, Box<dyn Error>> {
        let datadir = dirs::data_dir()
            .ok_or("Unable to get data dir")?
            .join("wldash");
        create_dir_all(&datadir)?;
        Ok(datadir.join("data.yaml"))
    }
}
diff --git a/src/main.rs b/src/main.rs
index 68dd057..3d36fc6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,6 +16,7 @@ mod cmd;
mod color;
mod config;
mod configfmt;
mod data;
mod desktop;
mod doublemempool;
mod draw;
diff --git a/src/widgets/launcher.rs b/src/widgets/launcher.rs
index 081f266..a8593ba 100644
--- a/src/widgets/launcher.rs
+++ b/src/widgets/launcher.rs
@@ -14,6 +14,7 @@ use std::collections::HashMap;
use std::process::Command;
use std::sync::mpsc::Sender;

use crate::data::Data;
use crate::keyboard::keysyms;
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
@@ -34,6 +35,7 @@ pub struct Launcher<'a> {
    length: u32,
    dirty: bool,
    tx: Sender<Cmd>,
    counter: Data,
}

impl<'a> Launcher<'a> {
@@ -61,6 +63,7 @@ impl<'a> Launcher<'a> {
            length,
            dirty: true,
            tx: listener,
            counter: Data::load().unwrap_or_default(),
        })
    }

@@ -101,6 +104,7 @@ impl<'a> Launcher<'a> {
                let (_, indices) = fuzzy_matcher
                    .fuzzy_indices(&m.name.to_lowercase(), &self.input.to_lowercase())
                    .unwrap_or((0, vec![]));

                let mut colors = Vec::with_capacity(m.name.len());
                for pos in 0..m.name.len() {
                    if indices.contains(&pos) {
@@ -215,12 +219,14 @@ fn wlcopy(s: &str) -> Result<(), String> {

struct Matcher {
    matches: HashMap<Desktop, i64>,
    counter: Data,
}

impl Matcher {
    fn new() -> Self {
    fn new(counter: Data) -> Self {
        Self {
            matches: HashMap::new(),
            counter,
        }
    }

@@ -248,9 +254,12 @@ impl Matcher {
            .collect::<Vec<(i64, Desktop)>>();

        m.sort_by(|(ma1, d1), (ma2, d2)| {
            if ma1 > ma2 {
            let count1 = self.counter.entries.get(&d1.name).unwrap_or(&0);
            let count2 = self.counter.entries.get(&d2.name).unwrap_or(&0);

            if ma1 + count1 > ma2 + count2 {
                Ordering::Less
            } else if ma1 < ma2 {
            } else if ma1 + count1 < ma2 + count2 {
                Ordering::Greater
            } else if d1.name.len() < d2.name.len() {
                Ordering::Less
@@ -306,7 +315,7 @@ impl<'a> Widget for Launcher<'a> {
            }
            Some('!') => (),
            _ => {
                let mut matcher = Matcher::new();
                let mut matcher = Matcher::new(self.counter.clone());

                for desktop in self.options.iter() {
                    matcher.try_match(
@@ -428,6 +437,10 @@ impl<'a> Widget for Launcher<'a> {
                                } else {
                                    lexed
                                };

                                *self.counter.entries.entry(d.name.clone()).or_insert(0) += 1;
                                self.counter.save().expect("Unable to save data");

                                if !lexed.is_empty() {
                                    let _ =
                                        Command::new(lexed[0].clone()).args(&lexed[1..]).spawn();
--
2.34.1
wldash/patches/.build.yml: SUCCESS in 2m28s

[feat: add ranking based on usage to launcher widget][0] from [Cyril Levis][1]

[0]: https://lists.sr.ht/~kennylevinsen/public-inbox/patches/27829
[1]: mailto:git@levis.name

✓ #660122 SUCCESS wldash/patches/.build.yml https://builds.sr.ht/~kennylevinsen/job/660122
Hmm, this `dirs` crate introduces quite a few too many dependencies for 
my taste (getrandom? redox_syscall?!) when all we use is this:

    pub fn data_dir() -> Option<PathBuf> { env::var_os("XDG_DATA_HOME") 
.and_then(dirs_sys::is_absolute_path).or_else(|| home_dir().map(|h| 
h.join(".local/share"))) }

We already have trivial handling of XDG_DATA_HOME and XDG_DATA_DIRS in 
src/desktop.rs, so I would suggest just doing something similar for 
other xdg dirs.

I also consider this a cache file rather than data file, so it should 
use ~/.cache (XDG_CACHE_HOME) instead of ~/.local/share (XDG_DATA_HOME).