Skip to content

Commit

Permalink
Remove current file indicator from global search picker
Browse files Browse the repository at this point in the history
Not sure this has much value. We could add a flags column like in the
buffer picker if it's still nice to have though. I eliminated it here
so that I could provide paths with anchors like "%path:.erl$" to match
on all paths with the .erl extension. The current buffer flag would make
that anchor not apply.
  • Loading branch information
the-mikedavis committed Sep 26, 2023
1 parent 35cfdb7 commit 9bc65b3
Showing 1 changed file with 127 additions and 144 deletions.
271 changes: 127 additions & 144 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2134,162 +2134,145 @@ fn global_search(cx: &mut Context) {
cx.editor.registers.last_search_register = reg;

let columns = vec![
PickerColumn::new(
"path",
|item: &FileResult, current_path: &Option<PathBuf>| {
let relative_path = helix_core::path::get_relative_path(&item.path)
.to_string_lossy()
.into_owned();
if current_path
.as_ref()
.map(|p| p == &item.path)
.unwrap_or(false)
{
format!("{} (*)", relative_path).into()
} else {
relative_path.into()
}
},
),
PickerColumn::new("path", |item: &FileResult, _| {
helix_core::path::get_relative_path(&item.path)
.to_string_lossy()
.into_owned()
.into()
}),
PickerColumn::new("contents", |item: &FileResult, _| {
item.line_content.as_str().into()
})
.without_filtering(),
];
let current_path = doc_mut!(cx.editor).path().cloned();
let (picker, injector) = Picker::stream(columns, current_path);

let get_files =
move |query: String,
editor: &mut Editor,
injector: &ui::picker::Injector<FileResult, Option<PathBuf>>| {
if query.is_empty() {
return async { Ok(()) }.boxed();
}
let (picker, injector) = Picker::stream(columns, ());

let search_root = helix_loader::current_working_dir();
if !search_root.exists() {
return async { Err(anyhow::anyhow!("Current working directory does not exist")) }
.boxed();
}
let get_files = move |query: String,
editor: &mut Editor,
injector: &ui::picker::Injector<_, _>| {
if query.is_empty() {
return async { Ok(()) }.boxed();
}

let documents: Vec<_> = editor
.documents()
.map(|doc| (doc.path().cloned(), doc.text().to_owned()))
.collect();
let search_root = helix_loader::current_working_dir();
if !search_root.exists() {
return async { Err(anyhow::anyhow!("Current working directory does not exist")) }
.boxed();
}

let matcher = match RegexMatcherBuilder::new()
.case_smart(smart_case)
.build(&query)
{
Ok(matcher) => {
// Clear any "Failed to compile regex" errors out of the statusline.
editor.clear_status();
matcher
}
Err(err) => {
log::info!("Failed to compile search pattern in global search: {}", err);
return async { Err(anyhow::anyhow!("Failed to compile regex")) }.boxed();
}
};
let documents: Vec<_> = editor
.documents()
.map(|doc| (doc.path().cloned(), doc.text().to_owned()))
.collect();

let dedup_symlinks = file_picker_config.deduplicate_links;
let absolute_root = search_root
.canonicalize()
.unwrap_or_else(|_| search_root.clone());

let injector = injector.clone();
async move {
let searcher = SearcherBuilder::new()
.binary_detection(BinaryDetection::quit(b'\x00'))
.build();
WalkBuilder::new(search_root)
.hidden(file_picker_config.hidden)
.parents(file_picker_config.parents)
.ignore(file_picker_config.ignore)
.follow_links(file_picker_config.follow_symlinks)
.git_ignore(file_picker_config.git_ignore)
.git_global(file_picker_config.git_global)
.git_exclude(file_picker_config.git_exclude)
.max_depth(file_picker_config.max_depth)
.filter_entry(move |entry| {
filter_picker_entry(entry, &absolute_root, dedup_symlinks)
})
.build_parallel()
.run(|| {
let mut searcher = searcher.clone();
let matcher = matcher.clone();
let injector = injector.clone();
let documents = &documents;
Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
let entry = match entry {
Ok(entry) => entry,
Err(_) => return WalkState::Continue,
};

match entry.file_type() {
Some(entry) if entry.is_file() => {}
// skip everything else
_ => return WalkState::Continue,
};

let mut stop = false;
let sink = sinks::UTF8(|line_num, line_content| {
stop = injector
.push(FileResult::new(
entry.path(),
line_num as usize - 1,
line_content.to_string(),
))
.is_err();

Ok(!stop)
});
let doc = documents.iter().find(|&(doc_path, _)| {
doc_path
.as_ref()
.map_or(false, |doc_path| doc_path == entry.path())
});

let result = if let Some((_, doc)) = doc {
// there is already a buffer for this file
// search the buffer instead of the file because it's faster
// and captures new edits without requiring a save
if searcher.multi_line_with_matcher(&matcher) {
// in this case a continous buffer is required
// convert the rope to a string
let text = doc.to_string();
searcher.search_slice(&matcher, text.as_bytes(), sink)
} else {
searcher.search_reader(
&matcher,
RopeReader::new(doc.slice(..)),
sink,
)
}
} else {
searcher.search_path(&matcher, entry.path(), sink)
};

if let Err(err) = result {
log::error!(
"Global search error: {}, {}",
entry.path().display(),
err
);
}
if stop {
WalkState::Quit
} else {
WalkState::Continue
}
})
});
Ok(())
let matcher = match RegexMatcherBuilder::new()
.case_smart(smart_case)
.build(&query)
{
Ok(matcher) => {
// Clear any "Failed to compile regex" errors out of the statusline.
editor.clear_status();
matcher
}
Err(err) => {
log::info!("Failed to compile search pattern in global search: {}", err);
return async { Err(anyhow::anyhow!("Failed to compile regex")) }.boxed();
}
.boxed()
};

let dedup_symlinks = file_picker_config.deduplicate_links;
let absolute_root = search_root
.canonicalize()
.unwrap_or_else(|_| search_root.clone());

let injector = injector.clone();
async move {
let searcher = SearcherBuilder::new()
.binary_detection(BinaryDetection::quit(b'\x00'))
.build();
WalkBuilder::new(search_root)
.hidden(file_picker_config.hidden)
.parents(file_picker_config.parents)
.ignore(file_picker_config.ignore)
.follow_links(file_picker_config.follow_symlinks)
.git_ignore(file_picker_config.git_ignore)
.git_global(file_picker_config.git_global)
.git_exclude(file_picker_config.git_exclude)
.max_depth(file_picker_config.max_depth)
.filter_entry(move |entry| {
filter_picker_entry(entry, &absolute_root, dedup_symlinks)
})
.build_parallel()
.run(|| {
let mut searcher = searcher.clone();
let matcher = matcher.clone();
let injector = injector.clone();
let documents = &documents;
Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
let entry = match entry {
Ok(entry) => entry,
Err(_) => return WalkState::Continue,
};

match entry.file_type() {
Some(entry) if entry.is_file() => {}
// skip everything else
_ => return WalkState::Continue,
};

let mut stop = false;
let sink = sinks::UTF8(|line_num, line_content| {
stop = injector
.push(FileResult::new(
entry.path(),
line_num as usize - 1,
line_content.to_string(),
))
.is_err();

Ok(!stop)
});
let doc = documents.iter().find(|&(doc_path, _)| {
doc_path
.as_ref()
.map_or(false, |doc_path| doc_path == entry.path())
});

let result = if let Some((_, doc)) = doc {
// there is already a buffer for this file
// search the buffer instead of the file because it's faster
// and captures new edits without requiring a save
if searcher.multi_line_with_matcher(&matcher) {
// in this case a continous buffer is required
// convert the rope to a string
let text = doc.to_string();
searcher.search_slice(&matcher, text.as_bytes(), sink)
} else {
searcher.search_reader(
&matcher,
RopeReader::new(doc.slice(..)),
sink,
)
}
} else {
searcher.search_path(&matcher, entry.path(), sink)
};

if let Err(err) = result {
log::error!("Global search error: {}, {}", entry.path().display(), err);
}
if stop {
WalkState::Quit
} else {
WalkState::Continue
}
})
});
Ok(())
}
.boxed()
};

cx.jobs.callback(async move {
let call = move |_: &mut Editor, compositor: &mut Compositor| {
let picker = Picker::with_stream(
Expand Down

0 comments on commit 9bc65b3

Please sign in to comment.