mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Fix crash when filtering items in Picker (#37929)
Closes #37617 We're already using `get` in a bunch of places, this PR updates the remaining spots to follow the same pattern. Note that the `ix` we read in `render_match` can sometimes be stale. The likely reason is that we run the match-update logic asynchronously (see [here](138117e0b1/crates/picker/src/picker.rs (L643))). That means it's possible to render items after the list's [data update](138117e0b1/crates/picker/src/picker.rs (L652)) but before the [list reset](138117e0b1/crates/picker/src/picker.rs (L662)), in which case the `ix` can be greater than that of our updated data. Release Notes: - Fixed crash when filtering MCP tools.
This commit is contained in:
parent
c0b583c9ef
commit
22e31a0d41
25 changed files with 31 additions and 43 deletions
|
|
@ -318,7 +318,7 @@ impl PickerDelegate for ToolPickerDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let item = &self.filtered_items[ix];
|
||||
let item = &self.filtered_items.get(ix)?;
|
||||
match item {
|
||||
PickerItem::ContextServer { server_id, .. } => Some(
|
||||
div()
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl PickerDelegate for FileContextPickerDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let FileMatch { mat, .. } = &self.matches[ix];
|
||||
let FileMatch { mat, .. } = &self.matches.get(ix)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ impl PickerDelegate for RulesContextPickerDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let thread = &self.matches[ix];
|
||||
let thread = &self.matches.get(ix)?;
|
||||
|
||||
Some(ListItem::new(ix).inset(true).toggle_state(selected).child(
|
||||
render_thread_context_entry(thread, self.context_store.clone(), cx),
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ impl PickerDelegate for SymbolContextPickerDelegate {
|
|||
_window: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mat = &self.matches[ix];
|
||||
let mat = &self.matches.get(ix)?;
|
||||
|
||||
Some(ListItem::new(ix).inset(true).toggle_state(selected).child(
|
||||
render_symbol_context_entry(ElementId::named_usize("symbol-ctx-picker", ix), mat),
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ impl PickerDelegate for ThreadContextPickerDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let thread = &self.matches[ix];
|
||||
let thread = &self.matches.get(ix)?;
|
||||
|
||||
Some(ListItem::new(ix).inset(true).toggle_state(selected).child(
|
||||
render_thread_context_entry(thread, self.context_store.clone(), cx),
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ impl PickerDelegate for ContactFinderDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let user = &self.potential_contacts[ix];
|
||||
let user = &self.potential_contacts.get(ix)?;
|
||||
let request_status = self.user_store.read(cx).contact_request_status(user);
|
||||
|
||||
let icon_path = match request_status {
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ impl PickerDelegate for AttachModalDelegate {
|
|||
_window: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let hit = &self.matches[ix];
|
||||
let hit = &self.matches.get(ix)?;
|
||||
let candidate = self.candidates.get(hit.candidate_id)?;
|
||||
|
||||
Some(
|
||||
|
|
|
|||
|
|
@ -1446,7 +1446,7 @@ impl PickerDelegate for DebugDelegate {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<picker::Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let hit = &self.matches[ix];
|
||||
let hit = &self.matches.get(ix)?;
|
||||
|
||||
let highlighted_location = HighlightedMatch {
|
||||
text: hit.string.clone(),
|
||||
|
|
|
|||
|
|
@ -207,8 +207,8 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let version_match = &self.matches[ix];
|
||||
let extension_version = &self.extension_versions[version_match.candidate_id];
|
||||
let version_match = &self.matches.get(ix)?;
|
||||
let extension_version = &self.extension_versions.get(version_match.candidate_id)?;
|
||||
|
||||
let is_version_compatible =
|
||||
extension_host::is_version_compatible(ReleaseChannel::global(cx), extension_version);
|
||||
|
|
|
|||
|
|
@ -1599,10 +1599,7 @@ impl PickerDelegate for FileFinderDelegate {
|
|||
) -> Option<Self::ListItem> {
|
||||
let settings = FileFinderSettings::get_global(cx);
|
||||
|
||||
let path_match = self
|
||||
.matches
|
||||
.get(ix)
|
||||
.expect("Invalid matches state: no element for index {ix}");
|
||||
let path_match = self.matches.get(ix)?;
|
||||
|
||||
let history_icon = match &path_match {
|
||||
Match::History { .. } => Icon::new(IconName::HistoryRerun)
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ impl PickerDelegate for BranchListDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let entry = &self.matches[ix];
|
||||
let entry = &self.matches.get(ix)?;
|
||||
|
||||
let (commit_time, author_name, subject) = entry
|
||||
.branch
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ impl PickerDelegate for PickerPromptDelegate {
|
|||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let hit = &self.matches[ix];
|
||||
let hit = &self.matches.get(ix)?;
|
||||
let shortened_option = util::truncate_and_trailoff(&hit.string, self.max_match_length);
|
||||
|
||||
Some(
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ impl PickerDelegate for BookmarkPickerDelegate {
|
|||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let entry = &self.matches[ix];
|
||||
let entry = &self.matches.get(ix)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ impl PickerDelegate for LanguageSelectorDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mat = &self.matches[ix];
|
||||
let mat = &self.matches.get(ix)?;
|
||||
let (label, language_icon) = self.language_data_for_match(mat, cx);
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ impl PickerDelegate for LineEndingSelectorDelegate {
|
|||
_: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let line_ending = self.matches[ix];
|
||||
let line_ending = self.matches.get(ix)?;
|
||||
let label = match line_ending {
|
||||
LineEnding::Unix => "LF",
|
||||
LineEnding::Windows => "CRLF",
|
||||
|
|
@ -183,7 +183,7 @@ impl PickerDelegate for LineEndingSelectorDelegate {
|
|||
.toggle_state(selected)
|
||||
.child(Label::new(label));
|
||||
|
||||
if self.line_ending == line_ending {
|
||||
if &self.line_ending == line_ending {
|
||||
list_item = list_item.end_slot(Icon::new(IconName::Check).color(Color::Muted));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
|
|||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let keymap_match = &self.matches[ix];
|
||||
let keymap_match = &self.matches.get(ix)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -217,8 +217,8 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let string_match = &self.matches[ix];
|
||||
let symbol = &self.symbols[string_match.candidate_id];
|
||||
let string_match = &self.matches.get(ix)?;
|
||||
let symbol = &self.symbols.get(string_match.candidate_id)?;
|
||||
let syntax_runs = styled_runs_for_code_label(&symbol.label, cx.theme().syntax());
|
||||
|
||||
let mut path = symbol.path.path.to_string_lossy();
|
||||
|
|
|
|||
|
|
@ -257,8 +257,8 @@ impl PickerDelegate for SettingsProfileSelectorDelegate {
|
|||
_: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mat = &self.matches[ix];
|
||||
let profile_name = &self.profile_names[mat.candidate_id];
|
||||
let mat = &self.matches.get(ix)?;
|
||||
let profile_name = &self.profile_names.get(mat.candidate_id)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ impl PickerDelegate for ScopeSelectorDelegate {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mat = &self.matches[ix];
|
||||
let mat = &self.matches.get(ix)?;
|
||||
let name_label = mat.string.clone();
|
||||
|
||||
let scope_name = ScopeName(Cow::Owned(
|
||||
|
|
|
|||
|
|
@ -649,10 +649,7 @@ impl PickerDelegate for TabSwitcherDelegate {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let tab_match = self
|
||||
.matches
|
||||
.get(ix)
|
||||
.expect("Invalid matches state: no element for index {ix}");
|
||||
let tab_match = self.matches.get(ix)?;
|
||||
|
||||
let params = TabContentParams {
|
||||
detail: Some(tab_match.detail),
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
cx: &mut Context<picker::Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let candidates = self.candidates.as_ref()?;
|
||||
let hit = &self.matches[ix];
|
||||
let hit = &self.matches.get(ix)?;
|
||||
let (source_kind, resolved_task) = &candidates.get(hit.candidate_id)?;
|
||||
let template = resolved_task.original_task();
|
||||
let display_label = resolved_task.display_label();
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
|
|||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let theme_match = &self.matches[ix];
|
||||
let theme_match = &self.matches.get(ix)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
|
|||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let theme_match = &self.matches[ix];
|
||||
let theme_match = &self.matches.get(ix)?;
|
||||
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
|
|
|
|||
|
|
@ -1011,8 +1011,8 @@ impl PickerDelegate for ToolchainSelectorDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mat = &self.matches[ix];
|
||||
let (toolchain, scope) = &self.candidates[mat.candidate_id];
|
||||
let mat = &self.matches.get(ix)?;
|
||||
let (toolchain, scope) = &self.candidates.get(mat.candidate_id)?;
|
||||
|
||||
let label = toolchain.name.clone();
|
||||
let path = Self::relativize_path(toolchain.path.clone(), &self.worktree_abs_path_root);
|
||||
|
|
|
|||
|
|
@ -1192,10 +1192,7 @@ impl PickerDelegate for RegistersViewDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let register_match = self
|
||||
.matches
|
||||
.get(ix)
|
||||
.expect("Invalid matches state: no element for index {ix}");
|
||||
let register_match = self.matches.get(ix)?;
|
||||
|
||||
let mut output = String::new();
|
||||
let mut runs = Vec::new();
|
||||
|
|
@ -1584,10 +1581,7 @@ impl PickerDelegate for MarksViewDelegate {
|
|||
_: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let mark_match = self
|
||||
.matches
|
||||
.get(ix)
|
||||
.expect("Invalid matches state: no element for index {ix}");
|
||||
let mark_match = self.matches.get(ix)?;
|
||||
|
||||
let mut left_output = String::new();
|
||||
let mut left_runs = Vec::new();
|
||||
|
|
|
|||
Loading…
Reference in a new issue