From 089277ff759010b4d13b386e7e31f9dc54885db2 Mon Sep 17 00:00:00 2001 From: a-kenji Date: Tue, 26 Mar 2024 21:23:08 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20git=5Frepositories:=20add=20zoxide?= =?UTF-8?q?=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `zoxide` integration for the `git_repositories` plugin. If enabled and installed will sort the paths by their `zoxide` score. --- README.md | 3 + client/src/plugin/git_repositories.rs | 103 +++++++++++++++++++++++++- client/src/plugin/utils.rs | 10 ++- client/src/settings.rs | 3 + home-manager-module.nix | 5 ++ 5 files changed, 118 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4da628a..3269ec4 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,8 @@ It exports the following environment variables: - `$GIT_DIRECTORY`: The path to the git directory. - `$GIT_DIRECTORY_NAME`: The name of the git directory. +If `zoxide` integration is enabled, the plugin will sort your projects based on their respective `zoxide` scores. + **Related config keys** ```yml @@ -129,6 +131,7 @@ It exports the following environment variables: plugin: git_repositories: enable: true + zoxide: true commands: - ["alacritty", "--command", "nvim", "$GIT_DIRECTORY"] - ["alacritty", "--working-directory", "$GIT_DIRECTORY" "--class" "$GIT_DIRECTORY_NAME"] diff --git a/client/src/plugin/git_repositories.rs b/client/src/plugin/git_repositories.rs index 3ad54cf..1b98767 100644 --- a/client/src/plugin/git_repositories.rs +++ b/client/src/plugin/git_repositories.rs @@ -44,7 +44,7 @@ impl Plugin for GitRepositoriesPlugin { let home = std::env::var("HOME").unwrap_or(String::from("")); - self.entries = git_repository_paths + let entries = git_repository_paths .into_iter() .filter_map(|git_repository_path| { let git_repository_display_name = git_repository_path.replacen(&home, "~", 1); @@ -57,7 +57,10 @@ impl Plugin for GitRepositoriesPlugin { command: None, }) }) - .collect(); + .collect::>(); + + self.set_entries(entries); + self.sort(); Ok(()) } @@ -96,4 +99,100 @@ impl Plugin for GitRepositoriesPlugin { Ok(()) } + + fn sort(&mut self) { + let mut entries = self.entries.clone(); + entries.sort_by_key(|entry| entry.title.clone()); + self.set_entries(entries); + + if self.use_zoxide() { + match Zoxide::query() { + Ok(zoxide) => self.sort_with_zoxide(zoxide), + Err(e) => log::warn!("Zoxide Error: {}", e), + } + } + } +} + +impl GitRepositoriesPlugin { + fn use_zoxide(&self) -> bool { + self.settings.plugin.git_repositories.zoxide + } + /// Sorts the returned paths, by their respective zoxide query score + fn sort_with_zoxide(&mut self, index: Zoxide) { + let mut scored_entries: Vec<(crate::model::Entry, f64)> = self + .entries() + .into_iter() + .map(|entry| { + let score = index + .scored_paths + .iter() + .find(|zoxide_result| zoxide_result.path == entry.id) + .map_or(0.0, |zoxide_result| zoxide_result.score); + (entry.clone(), score) + }) + .collect(); + + scored_entries.sort_by(|(_, score1), (_, score2)| { + score2 + .partial_cmp(score1) + .unwrap_or(std::cmp::Ordering::Equal) + }); + + self.entries = scored_entries.into_iter().map(|(entry, _)| entry).collect(); + } +} + +#[derive(Debug)] +pub struct Zoxide { + scored_paths: Vec, +} + +impl Zoxide { + pub fn query() -> anyhow::Result { + let out = std::process::Command::new("zoxide") + .args(["query", "--score", "--list"]) + .output() + .map_err(|e| anyhow::anyhow!("Failed to execute zoxide query command:\n{e}"))?; + + if !out.status.success() { + return Err(anyhow::anyhow!( + "Zoxide query command returned non-zero exit status." + )); + } + + let stdout = String::from_utf8(out.stdout) + .map_err(|_| anyhow::anyhow!("Failed to convert output to utf8"))?; + + let res = stdout + .lines() + .map(ScoredPath::parse_line) + .collect::>()?; + + Ok(Self { scored_paths: res }) + } +} + +#[derive(Debug)] +struct ScoredPath { + path: String, + score: f64, +} + +impl ScoredPath { + fn parse_line(line: &str) -> anyhow::Result { + let (unparsed_score, path): (&str, &str) = line + .trim() + .split_once(' ') + .ok_or(anyhow::anyhow!("Invalid zoxide query result line: {line}"))?; + + let score = unparsed_score + .parse::() + .map_err(|_| anyhow::anyhow!("Failed to parse score"))?; + + Ok(ScoredPath { + path: path.into(), + score, + }) + } } diff --git a/client/src/plugin/utils.rs b/client/src/plugin/utils.rs index 358bbf4..648c5ac 100644 --- a/client/src/plugin/utils.rs +++ b/client/src/plugin/utils.rs @@ -124,8 +124,10 @@ pub trait Plugin { return Ok(()); } - fn sort(&self, entries: &mut Vec) { + fn sort(&mut self) { + let mut entries = self.entries(); entries.sort_by_key(|entry| entry.title.clone()); + self.set_entries(entries) } fn search( @@ -133,11 +135,11 @@ pub trait Plugin { query: &str, plugin_channel_out: &mut iced::futures::channel::mpsc::Sender, ) -> anyhow::Result<()> { - let mut entries = self.entries(); if query.is_empty() { - self.sort(&mut entries); + self.sort(); } - let filtered_entries = entries + let filtered_entries = self + .entries() .into_iter() .filter(|entry| { let keywords = format!("{} {}", entry.title, entry.meta).to_lowercase(); diff --git a/client/src/settings.rs b/client/src/settings.rs index e671828..27633ab 100644 --- a/client/src/settings.rs +++ b/client/src/settings.rs @@ -68,6 +68,8 @@ impl Default for ClockPluginSettings { pub struct GitRepositoriesPluginSettings { #[serde(default = "default_true")] pub enable: bool, + #[serde(default = "default_true")] + pub zoxide: bool, #[serde(default = "default_commands")] pub commands: Vec>, } @@ -92,6 +94,7 @@ impl Default for GitRepositoriesPluginSettings { fn default() -> Self { Self { enable: true, + zoxide: true, commands: default_commands(), } } diff --git a/home-manager-module.nix b/home-manager-module.nix index 392e6e5..a2700c6 100644 --- a/home-manager-module.nix +++ b/home-manager-module.nix @@ -54,6 +54,11 @@ in { type = lib.types.bool; description = lib.mdDoc "Enable / disable the plugin."; }; + zoxide = lib.mkOption { + default = true; + type = lib.types.bool; + description = lib.mdDoc "Enable / disable zoxide integration."; + }; commands = lib.mkOption { default = [ [ "alacritty" "--command" "nvim" "$GIT_DIRECTORY" ]