Skip to content

Commit 8df4eed

Browse files
committed
add starting directory option
1 parent ee0e797 commit 8df4eed

File tree

7 files changed

+68
-24
lines changed

7 files changed

+68
-24
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 = "fpick"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
license = "MIT"
55
edition = "2021"
66
description = "Interactive file picker"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Navigate with keyboard:
3636
- Type in the phrase of a filename to filter the list of files
3737
- Enter to select a file, exit and print its path to stdout.
3838

39-
You can use it in combination with other commands, for example to open a file in an editor:
39+
You can use it in combination with other commands, for example to print the selected file:
4040
```sh
41-
vim `fpick`
41+
cat `fpick`
4242
```

src/app.rs

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
use anyhow::Result;
1+
use anyhow::{anyhow, Result};
22
use ratatui::widgets::ListState;
33
use signal_hook::{consts::SIGINT, consts::SIGTERM, iterator::Signals};
44
use std::sync::mpsc;
55
use std::thread;
66

7-
use crate::filesystem::{list_files, FileNode};
7+
use crate::filesystem::{list_files, normalize_path, trim_end_slash, FileNode};
88
use crate::numbers::ClampNumExt;
99
use crate::tree::{render_tree_nodes, TreeNode};
1010
use crate::tui::Tui;
1111

1212
#[derive(Debug, Default)]
1313
pub struct App {
1414
pub should_quit: bool,
15+
pub starting_dir: String,
1516
pub parent_nodes: Vec<FileNode>, // nodes leading to the current directory
1617
pub child_nodes: Vec<FileNode>, // nodes in the current directory
1718
pub child_tree_nodes: Vec<TreeNode>, // nodes of whole filesystem tree
@@ -28,7 +29,7 @@ impl App {
2829
}
2930

3031
pub fn run(&mut self) -> Result<()> {
31-
self.pre_init();
32+
self.pre_init()?;
3233
let signal_rx = self.handle_signals();
3334
self.init();
3435
let mut tui = Tui::new();
@@ -62,12 +63,27 @@ impl App {
6263

6364
pub fn tick(&mut self) {}
6465

65-
pub fn pre_init(&mut self) {
66+
pub fn pre_init(&mut self) -> Result<()> {
6667
let args: Vec<String> = std::env::args().collect();
67-
if args.last().map(|s| s == "--version").unwrap_or(false) {
68-
println!("{}", env!("CARGO_PKG_VERSION"));
69-
std::process::exit(0);
68+
if args.len() == 2 {
69+
let last: &String = args.last().unwrap();
70+
if last == "--version" {
71+
println!("{}", env!("CARGO_PKG_VERSION"));
72+
std::process::exit(0);
73+
} else if last == "--help" {
74+
println!("fpick - interactive file picker. Usage:");
75+
println!("fpick - to select a file in a current directory and return its path");
76+
println!("fpick <path> - to select a file starting from a specified directory");
77+
println!("fpick --version - to print version");
78+
println!("fpick --help - to print usage");
79+
std::process::exit(0);
80+
} else {
81+
self.starting_dir = trim_end_slash(last.to_string());
82+
}
83+
} else if args.len() > 2 {
84+
return Err(anyhow!("unrecognized arguments. Use --help for usage"));
7085
}
86+
Ok(())
7187
}
7288

7389
pub fn init(&mut self) {
@@ -98,6 +114,10 @@ impl App {
98114
}
99115
}
100116

117+
pub fn reset_cursor_offset(&mut self) {
118+
self.file_tree_state = self.file_tree_state.clone().with_offset(0);
119+
}
120+
101121
pub fn move_cursor(&mut self, delta: i32) {
102122
let new_cursor = (self.dir_cursor as i32 + delta)
103123
.clamp_max(self.child_tree_nodes.len() as i32 - 1)
@@ -106,14 +126,18 @@ impl App {
106126
}
107127

108128
pub fn get_current_string_path(&self) -> String {
109-
if self.parent_nodes.is_empty() {
110-
return ".".to_string();
111-
}
112-
self.parent_nodes
129+
let mut all_names = self
130+
.parent_nodes
113131
.iter()
114132
.map(|node| node.name.to_string())
115-
.collect::<Vec<String>>()
116-
.join("/")
133+
.collect::<Vec<String>>();
134+
if !self.starting_dir.is_empty() {
135+
all_names.insert(0, self.starting_dir.clone());
136+
}
137+
if all_names.is_empty() {
138+
return ".".to_string();
139+
}
140+
return normalize_path(all_names.join("/"));
117141
}
118142

119143
pub fn populate_current_child_nodes(&mut self) {
@@ -157,6 +181,7 @@ impl App {
157181
self.dir_cursor = 0;
158182
}
159183
}
184+
self.reset_cursor_offset();
160185
self.set_dir_cursor(self.dir_cursor);
161186
}
162187

@@ -182,6 +207,7 @@ impl App {
182207
self.parent_nodes.push(selected_node);
183208
self.filter_text.clear();
184209
self.populate_current_child_nodes();
210+
self.reset_cursor_offset();
185211
self.set_dir_cursor(0);
186212
}
187213

@@ -195,20 +221,24 @@ impl App {
195221
}
196222
let selected_node: FileNode = selected_node_o.unwrap();
197223

198-
let mut all_nodes = self.parent_nodes.clone();
199-
all_nodes.push(selected_node);
200-
let selected_path = all_nodes
224+
let mut all_names = self
225+
.parent_nodes
201226
.iter()
202227
.map(|node| node.name.to_string())
203-
.collect::<Vec<String>>()
204-
.join("/");
228+
.collect::<Vec<String>>();
229+
if !self.starting_dir.is_empty() {
230+
all_names.insert(0, self.starting_dir.clone());
231+
}
232+
all_names.push(selected_node.name.to_string());
233+
let selected_path = normalize_path(all_names.join("/"));
205234

206235
self.picked_path = Some(selected_path);
207236
self.quit();
208237
}
209238

210239
pub fn render_tree_nodes(&mut self) {
211240
self.child_tree_nodes = render_tree_nodes(&self.child_nodes, &self.filter_text);
241+
self.reset_cursor_offset();
212242
self.move_cursor(0); // validate cursor position
213243
}
214244
}

src/filesystem.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,17 @@ pub fn list_files(dir_path: &Path) -> Result<Vec<FileNode>> {
5454

5555
return Ok(files);
5656
}
57+
58+
pub fn trim_end_slash(path: String) -> String {
59+
if path == "/" {
60+
return path;
61+
}
62+
if path.ends_with('/') {
63+
return path[..path.len() - 1].to_string();
64+
}
65+
path.to_string()
66+
}
67+
68+
pub fn normalize_path(path: String) -> String {
69+
path.replace("//", "/")
70+
}

src/tree.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub fn evaluate_relevance(text: &str, words: &Vec<String>) -> i32 {
3939
for word in words {
4040
if text.contains(word) {
4141
relevance += 1;
42+
} else {
43+
return 0;
4244
}
4345
}
4446
let firs_word = words.first().unwrap();

src/ui.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,9 @@ fn render_dir_tree(app: &mut App, frame: &mut Frame, area: Rect) {
5050
.child_tree_nodes
5151
.iter()
5252
.map(|it: &TreeNode| it.render_list_item())
53-
// .map(|it: &TreeNode| ListItem::new(it.display_name()))
5453
.collect();
5554

5655
let title = app.get_current_string_path();
57-
5856
let widget = List::new(list_items)
5957
.block(Block::default().title(title).borders(Borders::ALL))
6058
.style(Style::default().fg(Color::White))

0 commit comments

Comments
 (0)