Update symbol matches as the query changes
This commit is contained in:
parent
8a8ae0fbcd
commit
d59ebb554b
3 changed files with 93 additions and 18 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -3566,9 +3566,12 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"ordered-float",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue