Skip to content

Commit

Permalink
git_repositories: add zoxide integration (#111)
Browse files Browse the repository at this point in the history
Add `zoxide` integration for the `git_repositories` plugin. If enabled
and installed will sort the paths by their `zoxide` score.

Closes: #36
  • Loading branch information
a-kenji authored Apr 3, 2024
1 parent 4c1727f commit a03d7eb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 6 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,16 @@ 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
# ~/.config/centerpiece/config.yml
plugin:
git_repositories:
enable: true
zoxide: true
commands:
- ["alacritty", "--command", "nvim", "$GIT_DIRECTORY"]
- ["alacritty", "--working-directory", "$GIT_DIRECTORY" "--class" "$GIT_DIRECTORY_NAME"]
Expand Down
103 changes: 101 additions & 2 deletions client/src/plugin/git_repositories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -57,7 +57,10 @@ impl Plugin for GitRepositoriesPlugin {
command: None,
})
})
.collect();
.collect::<Vec<_>>();

self.set_entries(entries);
self.sort();

Ok(())
}
Expand Down Expand Up @@ -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<ScoredPath>,
}

impl Zoxide {
pub fn query() -> anyhow::Result<Self> {
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::<anyhow::Result<_>>()?;

Ok(Self { scored_paths: res })
}
}

#[derive(Debug)]
struct ScoredPath {
path: String,
score: f64,
}

impl ScoredPath {
fn parse_line(line: &str) -> anyhow::Result<ScoredPath> {
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::<f64>()
.map_err(|_| anyhow::anyhow!("Failed to parse score"))?;

Ok(ScoredPath {
path: path.into(),
score,
})
}
}
10 changes: 6 additions & 4 deletions client/src/plugin/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,22 @@ pub trait Plugin {
return Ok(());
}

fn sort(&self, entries: &mut Vec<crate::model::Entry>) {
fn sort(&mut self) {
let mut entries = self.entries();
entries.sort_by_key(|entry| entry.title.clone());
self.set_entries(entries)
}

fn search(
&mut self,
query: &str,
plugin_channel_out: &mut iced::futures::channel::mpsc::Sender<crate::Message>,
) -> 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();
Expand Down
3 changes: 3 additions & 0 deletions client/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<String>>,
}
Expand All @@ -92,6 +94,7 @@ impl Default for GitRepositoriesPluginSettings {
fn default() -> Self {
Self {
enable: true,
zoxide: true,
commands: default_commands(),
}
}
Expand Down
5 changes: 5 additions & 0 deletions home-manager-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
Expand Down

0 comments on commit a03d7eb

Please sign in to comment.