Update symbol matches as the query changes

This commit is contained in:
Antonio Scandurra 2022-02-22 10:54:25 +01:00
parent 8a8ae0fbcd
commit d59ebb554b
3 changed files with 93 additions and 18 deletions

3
Cargo.lock generated
View file

@ -3566,9 +3566,12 @@ dependencies = [
"editor", "editor",
"fuzzy", "fuzzy",
"gpui", "gpui",
"ordered-float",
"postage", "postage",
"project", "project",
"smol",
"text", "text",
"util",
"workspace", "workspace",
] ]

View file

@ -13,4 +13,7 @@ gpui = { path = "../gpui" }
project = { path = "../project" } project = { path = "../project" }
text = { path = "../text" } text = { path = "../text" }
workspace = { path = "../workspace" } workspace = { path = "../workspace" }
util = { path = "../util" }
ordered-float = "2.1.1"
postage = { version = "0.4", features = ["futures-traits"] } postage = { version = "0.4", features = ["futures-traits"] }
smol = "1.2"

View file

@ -1,18 +1,22 @@
use std::{cmp, sync::Arc};
use editor::{ use editor::{
combine_syntax_and_fuzzy_match_highlights, styled_runs_for_code_label, Editor, EditorSettings, combine_syntax_and_fuzzy_match_highlights, styled_runs_for_code_label, Editor, EditorSettings,
}; };
use fuzzy::StringMatch; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
action, action,
elements::*, elements::*,
keymap::{self, Binding}, keymap::{self, Binding},
AppContext, Axis, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, AppContext, Axis, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View,
ViewHandle, WeakViewHandle, ViewContext, ViewHandle, WeakViewHandle,
}; };
use ordered_float::OrderedFloat;
use postage::watch; use postage::watch;
use project::{Project, ProjectSymbol}; use project::{Project, ProjectSymbol};
use std::{
cmp::{self, Reverse},
sync::Arc,
};
use util::ResultExt;
use workspace::{ use workspace::{
menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}, menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev},
Settings, Workspace, Settings, Workspace,
@ -40,7 +44,9 @@ pub struct ProjectSymbolsView {
selected_match_index: usize, selected_match_index: usize,
list_state: UniformListState, list_state: UniformListState,
symbols: Vec<ProjectSymbol>, symbols: Vec<ProjectSymbol>,
match_candidates: Vec<StringMatchCandidate>,
matches: Vec<StringMatch>, matches: Vec<StringMatch>,
pending_symbols_task: Task<Option<()>>,
query_editor: ViewHandle<Editor>, query_editor: ViewHandle<Editor>,
} }
@ -119,7 +125,9 @@ impl ProjectSymbolsView {
selected_match_index: 0, selected_match_index: 0,
list_state: Default::default(), list_state: Default::default(),
symbols: Default::default(), symbols: Default::default(),
match_candidates: Default::default(),
matches: Default::default(), matches: Default::default(),
pending_symbols_task: Task::ready(None),
query_editor, query_editor,
}; };
this.update_matches(cx); this.update_matches(cx);
@ -137,31 +145,27 @@ impl ProjectSymbolsView {
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) { fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
if self.selected_match_index > 0 { if self.selected_match_index > 0 {
self.select(self.selected_match_index - 1, false, cx); self.select(self.selected_match_index - 1, cx);
} }
} }
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) { fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
if self.selected_match_index + 1 < self.matches.len() { if self.selected_match_index + 1 < self.matches.len() {
self.select(self.selected_match_index + 1, false, cx); self.select(self.selected_match_index + 1, cx);
} }
} }
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) { fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
self.select(0, false, cx); self.select(0, cx);
} }
fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) { fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
self.select(self.matches.len().saturating_sub(1), false, cx); self.select(self.matches.len().saturating_sub(1), cx);
} }
fn select(&mut self, index: usize, center: bool, cx: &mut ViewContext<Self>) { fn select(&mut self, index: usize, cx: &mut ViewContext<Self>) {
self.selected_match_index = index; self.selected_match_index = index;
self.list_state.scroll_to(if center { self.list_state.scroll_to(ScrollTarget::Show(index));
ScrollTarget::Center(index)
} else {
ScrollTarget::Show(index)
});
cx.notify(); cx.notify();
} }
@ -170,10 +174,75 @@ impl ProjectSymbolsView {
} }
fn update_matches(&mut self, cx: &mut ViewContext<Self>) { fn update_matches(&mut self, cx: &mut ViewContext<Self>) {
self.filter(cx);
let query = self.query_editor.read(cx).text(cx); let query = self.query_editor.read(cx).text(cx);
self.project let symbols = self
.update(cx, |project, cx| project.symbols(&query, cx)) .project
.detach_and_log_err(cx); .update(cx, |project, cx| project.symbols(&query, cx));
self.pending_symbols_task = cx.spawn_weak(|this, mut cx| async move {
let symbols = symbols.await.log_err()?;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| {
this.match_candidates = symbols
.iter()
.enumerate()
.map(|(id, symbol)| {
StringMatchCandidate::new(
id,
symbol.label.text[symbol.label.filter_range.clone()].to_string(),
)
})
.collect();
this.symbols = symbols;
this.filter(cx);
});
}
None
});
}
fn filter(&mut self, cx: &mut ViewContext<Self>) {
let query = self.query_editor.read(cx).text(cx);
let mut matches = if query.is_empty() {
self.match_candidates
.iter()
.enumerate()
.map(|(candidate_id, candidate)| StringMatch {
candidate_id,
score: Default::default(),
positions: Default::default(),
string: candidate.string.clone(),
})
.collect()
} else {
smol::block_on(fuzzy::match_strings(
&self.match_candidates,
&query,
false,
100,
&Default::default(),
cx.background().clone(),
))
};
matches.sort_unstable_by_key(|mat| {
let label = &self.symbols[mat.candidate_id].label;
(
Reverse(OrderedFloat(mat.score)),
&label.text[label.filter_range.clone()],
)
});
for mat in &mut matches {
let filter_start = self.symbols[mat.candidate_id].label.filter_range.start;
for position in &mut mat.positions {
*position += filter_start;
}
}
self.matches = matches;
self.selected_match_index = 0;
cx.notify();
} }
fn render_matches(&self) -> ElementBox { fn render_matches(&self) -> ElementBox {