Skip to content

Commit 86197e1

Browse files
committed
Dim picker
1 parent e9769a3 commit 86197e1

File tree

5 files changed

+101
-33
lines changed

5 files changed

+101
-33
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pathmarks"
3-
version = "0.1.3"
3+
version = "0.1.4"
44
edition = "2024"
55
description = "Simple path bookmarks for your shell"
66
license = "MIT"

src/init.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn init(shell: Shell, command: Option<String>) -> String {
2525
end
2626
2727
28-
function {command}i'
28+
function {command}i
2929
while true
3030
set -l dest (pathmarks pick)
3131
set -l code $status

src/main.rs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ use std::path::{Path, PathBuf};
55
use std::{env, io};
66

77
use clap::{Parser, Subcommand};
8-
use nucleo_picker::Picker;
98
use nucleo_picker::nucleo::pattern::{CaseMatching, Normalization, Pattern};
109
use nucleo_picker::nucleo::{Config, Matcher};
1110

1211
use crate::error::{AppError, AppResult};
13-
use crate::index_renderer::IndexPathRenderer;
1412
use crate::init::{Shell, init};
13+
use crate::pickers::{pick_one, pick_one_last_dim};
1514

1615
mod error;
1716
mod index_renderer;
1817
mod init;
18+
mod pickers;
1919

2020
#[derive(Parser)]
2121
#[command(name = "pathmarks")]
@@ -133,9 +133,14 @@ fn app(cli: Cli, bookmarks_file: PathBuf) -> AppResult<Option<String>> {
133133
Ok(Some(out.join("\n")))
134134
}
135135
Cmd::Pick => {
136-
let directories = merged_directories(bookmarks_file)?;
136+
let bookmarks = read_bookmarks(&bookmarks_file)?;
137+
let current_dir = env::current_dir()?;
138+
139+
let relative_bookmarks = map_relative_paths(&current_dir, bookmarks);
140+
let sub_directories = list_child_dirs(&current_dir, false)?;
141+
let relative_sub_directories = map_relative_paths(&current_dir, sub_directories);
137142

138-
match pick_one(&directories)? {
143+
match pick_one_last_dim(&relative_sub_directories, &relative_bookmarks)? {
139144
Some(bookmark) => Ok(bookmark.to_str().map(|x| x.into())),
140145
None => Ok(None),
141146
}
@@ -149,23 +154,8 @@ fn merged_directories(bookmarks_file: PathBuf) -> AppResult<Vec<PathBuf>> {
149154
let merged_directories = merge_with_cwd_dirs(bookmarks)?;
150155

151156
let cwd = env::current_dir()?;
152-
let mut out = Vec::with_capacity(merged_directories.len());
153-
154-
for path in merged_directories {
155-
if let Some(relative) = relative_if_descendant(&cwd, &path) {
156-
if let Some(s) = relative.to_str() {
157-
if s != "." {
158-
out.push(s.into());
159-
}
160-
} else {
161-
out.push(path);
162-
}
163-
} else {
164-
out.push(path);
165-
}
166-
}
167157

168-
Ok(out)
158+
Ok(map_relative_paths(&cwd, merged_directories))
169159
}
170160

171161
fn best_bookmark_match<'a>(
@@ -250,16 +240,6 @@ fn is_absolute(p: &str) -> bool {
250240
Path::new(p).is_absolute()
251241
}
252242

253-
fn pick_one(bookmarks: &[PathBuf]) -> AppResult<Option<&PathBuf>> {
254-
let mut picker = Picker::new(IndexPathRenderer::new(bookmarks));
255-
let mut injector = picker.injector();
256-
injector.extend(0..bookmarks.len());
257-
258-
let selected_idx = picker.pick()?.copied();
259-
260-
Ok(selected_idx.map(|i| &bookmarks[i]))
261-
}
262-
263243
fn list_child_dirs(dir: &Path, include_hidden: bool) -> io::Result<Vec<PathBuf>> {
264244
let mut out = Vec::new();
265245

@@ -333,6 +313,16 @@ fn relative_if_descendant(base: &Path, child: &Path) -> Option<PathBuf> {
333313
.ok()
334314
}
335315

316+
fn map_relative_paths<I>(base: &Path, paths: I) -> Vec<PathBuf>
317+
where
318+
I: IntoIterator<Item = PathBuf>,
319+
{
320+
paths
321+
.into_iter()
322+
.map(|p| relative_if_descendant(base, &p).unwrap_or(p))
323+
.collect()
324+
}
325+
336326
#[cfg(test)]
337327
mod tests {
338328
use super::*;

src/pickers.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::path::PathBuf;
2+
3+
use nucleo_picker::{Picker, Render};
4+
5+
use crate::{error::AppResult, index_renderer::IndexPathRenderer};
6+
7+
pub fn pick_one(bookmarks: &[PathBuf]) -> AppResult<Option<&PathBuf>> {
8+
let mut picker = Picker::new(IndexPathRenderer::new(bookmarks));
9+
let mut injector = picker.injector();
10+
injector.extend(0..bookmarks.len());
11+
12+
let selected_idx = picker.pick()?.copied();
13+
14+
Ok(selected_idx.map(|i| &bookmarks[i]))
15+
}
16+
17+
#[derive(Clone, Copy)]
18+
enum Source {
19+
First,
20+
Second,
21+
}
22+
23+
struct Entry<'a> {
24+
path: &'a PathBuf,
25+
source: Source,
26+
}
27+
28+
pub fn pick_one_last_dim<'a>(
29+
first: &'a [PathBuf],
30+
second: &'a [PathBuf],
31+
) -> AppResult<Option<&'a PathBuf>> {
32+
let entries: Vec<Entry<'a>> = first
33+
.iter()
34+
.map(|p| Entry {
35+
path: p,
36+
source: Source::First,
37+
})
38+
.chain(second.iter().map(|p| Entry {
39+
path: p,
40+
source: Source::Second,
41+
}))
42+
.collect();
43+
44+
let renderer = DualListIndexRenderer { entries: &entries };
45+
46+
let mut picker = Picker::new(renderer);
47+
let mut injector = picker.injector();
48+
49+
injector.extend(0..entries.len());
50+
51+
let selected_idx = picker.pick()?.copied();
52+
Ok(selected_idx.map(|i| entries[i].path))
53+
}
54+
55+
pub struct DualListIndexRenderer<'a> {
56+
entries: &'a [Entry<'a>],
57+
}
58+
59+
impl<'a> Render<usize> for DualListIndexRenderer<'a> {
60+
type Str<'b>
61+
= String
62+
where
63+
usize: 'b;
64+
65+
fn render<'b>(&self, idx: &'b usize) -> Self::Str<'b> {
66+
let entry = &self.entries[*idx];
67+
let path = entry.path.to_string_lossy();
68+
69+
const ITALIC: &str = "\x1b[3m";
70+
const DIM: &str = "\x1b[2m";
71+
const RESET: &str = "\x1b[0m";
72+
73+
match entry.source {
74+
Source::First => path.to_string(),
75+
Source::Second => format!("{DIM}{ITALIC}{path}{RESET}"),
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)