Added file path only search
This commit is contained in:
parent
b8de008862
commit
29ffab74ee
6 changed files with 101 additions and 59 deletions
|
|
@ -14,7 +14,7 @@
|
|||
- [x] Search in buffer switcher
|
||||
- [x] File saving
|
||||
- [x] Syntax highlighting
|
||||
- [x] Project search
|
||||
- [x] Project search (including file path search)
|
||||
- [x] Auto-indent (and related features)
|
||||
- [x] Undo/redo
|
||||
- [x] Ability to resize panes
|
||||
|
|
|
|||
|
|
@ -54,17 +54,17 @@ pub enum Action {
|
|||
// Switch the current pane to the given buffer
|
||||
SwitchBuffer(BufferId),
|
||||
// Open the file (on the given line) and switch the current pane to it
|
||||
OpenFile(PathBuf, usize),
|
||||
OpenFile(PathBuf, Option<usize>),
|
||||
// Create a new file and switch the current pane to it
|
||||
CreateFile(PathBuf),
|
||||
// Start a new command
|
||||
CommandStart(&'static str),
|
||||
// Go to the specified file line
|
||||
GotoLine(isize),
|
||||
// Request to begin a search with the given needle
|
||||
BeginSearch(String),
|
||||
// Start a project-wide search with the given location and needle
|
||||
OpenSearcher(PathBuf, String),
|
||||
// Request to begin a search with the given needle. `None` implies file path search.
|
||||
BeginSearch(Option<String>),
|
||||
// Start a project-wide search with the given location and needle. `None` implies file path search.
|
||||
OpenSearcher(PathBuf, Option<String>),
|
||||
// Fully select the token under the cursor
|
||||
SelectToken,
|
||||
// Fully select the entire input
|
||||
|
|
@ -387,6 +387,22 @@ impl RawEvent {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_path_search(&self) -> Option<Action> {
|
||||
if matches!(
|
||||
&self.0,
|
||||
TerminalEvent::Key(KeyEvent {
|
||||
code: KeyCode::Char('o'),
|
||||
modifiers,
|
||||
kind: KeyEventKind::Press,
|
||||
..
|
||||
}) if *modifiers == KeyModifiers::CONTROL | KeyModifiers::SHIFT
|
||||
) {
|
||||
Some(Action::BeginSearch(None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_go(&self) -> Option<Action> {
|
||||
if matches!(
|
||||
&self.0,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ impl Element for Doc {
|
|||
.or_else(|| e.to_open_finder(None))
|
||||
.or_else(|| e.to_move())
|
||||
.or_else(|| e.to_save())
|
||||
.or_else(|| e.to_path_search())
|
||||
}) {
|
||||
action @ Some(Action::OpenSwitcher) | action @ Some(Action::OpenOpener(_)) => {
|
||||
Ok(Resp::handled(action.map(Into::into)))
|
||||
|
|
@ -114,7 +115,9 @@ impl Element for Doc {
|
|||
Some(Action::OpenFile(path, line_idx)) => match state.open(path) {
|
||||
Ok(buffer_id) => {
|
||||
self.switch_buffer(state, buffer_id);
|
||||
if let Some(buffer) = state.buffers.get_mut(self.buffer) {
|
||||
if let Some(buffer) = state.buffers.get_mut(self.buffer)
|
||||
&& let Some(line_idx) = line_idx
|
||||
{
|
||||
let cursor_id = self.cursors[&self.buffer];
|
||||
buffer.goto_cursor(cursor_id, [0, line_idx as isize], true);
|
||||
self.input.refocus(buffer, cursor_id);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
|
||||
pub enum CannotEnd {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Resp<End = CannotEnd> {
|
||||
ended: Option<End>,
|
||||
pub event: Option<Event>,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ impl Prompt {
|
|||
Ok(Action::GotoLine(line))
|
||||
}
|
||||
Some(arg0 @ "search") => {
|
||||
let needle = cmd.get(arg0.len()..).unwrap().trim().to_string();
|
||||
let needle = Some(cmd.get(arg0.len()..).unwrap().trim().to_string())
|
||||
.filter(|n| !n.is_empty());
|
||||
Ok(Action::BeginSearch(needle))
|
||||
}
|
||||
Some("reload") => Ok(Action::Reload),
|
||||
|
|
@ -451,7 +452,7 @@ impl Element<()> for Opener {
|
|||
self.set_string(&format!("{}/", file.path.display()));
|
||||
Ok(Resp::handled(None))
|
||||
},
|
||||
FileKind::File => Ok(Resp::end(Some(Action::OpenFile(file.path, 0).into()))),
|
||||
FileKind::File => Ok(Resp::end(Some(Action::OpenFile(file.path, None).into()))),
|
||||
FileKind::New => Ok(Resp::end(Some(Action::CreateFile(file.path).into()))),
|
||||
FileKind::Unknown => Ok(Resp::handled(None)),
|
||||
}
|
||||
|
|
|
|||
121
src/ui/search.rs
121
src/ui/search.rs
|
|
@ -6,7 +6,7 @@ pub struct Searcher {
|
|||
options: Options<SearchResult>,
|
||||
path: PathBuf,
|
||||
search_path: PathBuf,
|
||||
needle: String,
|
||||
needle: Option<String>,
|
||||
// Filter
|
||||
buffer: Buffer,
|
||||
cursor_id: CursorId,
|
||||
|
|
@ -15,7 +15,7 @@ pub struct Searcher {
|
|||
}
|
||||
|
||||
impl Searcher {
|
||||
pub fn new(path: PathBuf, needle: String) -> Self {
|
||||
pub fn new(path: PathBuf, needle: Option<String>) -> Self {
|
||||
let mut search_path = path.clone();
|
||||
let search_path = loop {
|
||||
if let Ok(mut entries) = fs::read_dir(&search_path)
|
||||
|
|
@ -34,7 +34,7 @@ impl Searcher {
|
|||
fn search_in(
|
||||
search_path: &Path,
|
||||
path: &Path,
|
||||
needle: &str,
|
||||
needle: Option<&str>,
|
||||
results: &mut Vec<SearchResult>,
|
||||
) {
|
||||
// Cap reached!
|
||||
|
|
@ -55,28 +55,42 @@ impl Searcher {
|
|||
&& md.len() < 1 << 20
|
||||
&& let Ok(s) = fs::read_to_string(path)
|
||||
{
|
||||
for (line_idx, line_text) in
|
||||
s.lines().enumerate().filter(|(_, l)| l.contains(needle))
|
||||
{
|
||||
let mut line_buffer = Buffer::new(
|
||||
false,
|
||||
line_text.trim().chars().collect(),
|
||||
path.to_path_buf(),
|
||||
);
|
||||
let rdir = format!(
|
||||
"./{}",
|
||||
path.parent()
|
||||
.and_then(|p| p.strip_prefix(search_path).ok()?.to_str())
|
||||
.unwrap_or("unknown")
|
||||
);
|
||||
if let Some(needle) = needle {
|
||||
for (line_idx, line_text) in
|
||||
s.lines().enumerate().filter(|(_, l)| l.contains(needle))
|
||||
{
|
||||
let mut line_buffer = Buffer::new(
|
||||
false,
|
||||
line_text.trim().chars().collect(),
|
||||
path.to_path_buf(),
|
||||
);
|
||||
results.push(SearchResult {
|
||||
loc: SearchLoc {
|
||||
path: path.to_path_buf(),
|
||||
line_idx: Some(line_idx),
|
||||
},
|
||||
rdir: rdir.clone(),
|
||||
line: Some((
|
||||
Input::search_result(line_idx),
|
||||
line_buffer.start_session(),
|
||||
line_buffer,
|
||||
)),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
results.push(SearchResult {
|
||||
loc: SearchLoc {
|
||||
path: path.to_path_buf(),
|
||||
line_idx,
|
||||
line_idx: None,
|
||||
},
|
||||
rdir: format!(
|
||||
"./{}",
|
||||
path.parent()
|
||||
.and_then(|p| p.strip_prefix(search_path).ok()?.to_str())
|
||||
.unwrap_or("unknown")
|
||||
),
|
||||
line_input: Input::search_result(line_idx),
|
||||
line_cursor: line_buffer.start_session(),
|
||||
line_buffer,
|
||||
rdir,
|
||||
line: None,
|
||||
});
|
||||
}
|
||||
} else if let Ok(entries) = fs::read_dir(path) {
|
||||
|
|
@ -98,7 +112,7 @@ impl Searcher {
|
|||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
search_in(&search_path, &search_path, &needle, &mut results);
|
||||
search_in(&search_path, &search_path, needle.as_deref(), &mut results);
|
||||
|
||||
let mut buffer = Buffer::default();
|
||||
let cursor_id = buffer.start_session();
|
||||
|
|
@ -210,15 +224,13 @@ impl Element<()> for Searcher {
|
|||
#[derive(Clone, PartialEq)]
|
||||
struct SearchLoc {
|
||||
path: PathBuf,
|
||||
line_idx: usize,
|
||||
line_idx: Option<usize>,
|
||||
}
|
||||
|
||||
struct SearchResult {
|
||||
loc: SearchLoc,
|
||||
rdir: String,
|
||||
line_input: Input,
|
||||
line_cursor: CursorId,
|
||||
line_buffer: Buffer,
|
||||
line: Option<(Input, CursorId, Buffer)>,
|
||||
}
|
||||
|
||||
impl Visual for SearchResult {
|
||||
|
|
@ -240,14 +252,16 @@ impl Visual for SearchResult {
|
|||
.with_fg(state.theme.option_dir)
|
||||
.text([0, 0], &self.rdir);
|
||||
// Code snippet
|
||||
self.line_input.render(
|
||||
state,
|
||||
None,
|
||||
&self.line_buffer,
|
||||
self.line_cursor,
|
||||
None,
|
||||
&mut frame.rect([col_a + col_b, 0], [!0, !0]),
|
||||
);
|
||||
if let Some((input, cursor, buffer)) = &mut self.line {
|
||||
input.render(
|
||||
state,
|
||||
None,
|
||||
buffer,
|
||||
*cursor,
|
||||
None,
|
||||
&mut frame.rect([col_a + col_b, 0], [!0, !0]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,8 +284,10 @@ impl Visual for Searcher {
|
|||
let mut buffer = Buffer::open(result.loc.path.clone()).ok()?;
|
||||
let cursor_id = buffer.start_session();
|
||||
let mut input = Input::default();
|
||||
buffer.goto_cursor(cursor_id, [0, result.loc.line_idx as isize], true);
|
||||
input.focus([0, result.loc.line_idx as isize - preview_sz as isize / 2]);
|
||||
if let Some(line_idx) = result.loc.line_idx {
|
||||
buffer.goto_cursor(cursor_id, [0, line_idx as isize], true);
|
||||
input.focus([0, line_idx as isize - preview_sz as isize / 2]);
|
||||
}
|
||||
Some((buffer, cursor_id, input, result.loc.clone()))
|
||||
})
|
||||
});
|
||||
|
|
@ -291,20 +307,25 @@ impl Visual for Searcher {
|
|||
[frame.size()[0], path_input_sz],
|
||||
)
|
||||
.with(|f| {
|
||||
let title = format!(
|
||||
"{} results for '{}' in {}/",
|
||||
if self.options.ranking.is_empty() {
|
||||
format!("No")
|
||||
} else {
|
||||
format!(
|
||||
"{} of {}",
|
||||
self.options.selected + 1,
|
||||
self.options.ranking.len()
|
||||
)
|
||||
},
|
||||
self.needle,
|
||||
self.path.display()
|
||||
);
|
||||
let num_results = if self.options.ranking.is_empty() {
|
||||
format!("No")
|
||||
} else {
|
||||
format!(
|
||||
"{} of {}",
|
||||
self.options.selected + 1,
|
||||
self.options.ranking.len()
|
||||
)
|
||||
};
|
||||
let title = if let Some(needle) = &self.needle {
|
||||
format!(
|
||||
"{} results for '{}' in {}/",
|
||||
num_results,
|
||||
needle,
|
||||
self.path.display()
|
||||
)
|
||||
} else {
|
||||
format!("{} results in {}/", num_results, self.path.display())
|
||||
};
|
||||
self.input
|
||||
.render(state, Some(&title), &self.buffer, self.cursor_id, None, f)
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue