Switch from Arc/RwLock to Rc/RefCell for CodeContextMenu (#22035)

`CodeContextMenu` is always accessed on one thread, so only `Rc`s and
`Rc<RefCell<_>>` are needed. There should be tiny performance benefits
from this. The main benefit of this is that when seeing code accessing a
`RwLock` it would be reasonable to wonder whether it will block. The
only potential downside is the potential for panics due to overlapping
borrows of the RefCells. I think this is an acceptable risk because most
errors of this nature will be local or will be caught by clippy via the
check for holding a RefCell reference over an `await`.

Release Notes:

- N/A
This commit is contained in:
Michael Sloan 2024-12-16 01:50:21 -07:00 committed by GitHub
parent 7b721efe2c
commit a94afbc062
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 108 additions and 93 deletions

1
Cargo.lock generated
View file

@ -2768,7 +2768,6 @@ dependencies = [
"language", "language",
"menu", "menu",
"notifications", "notifications",
"parking_lot",
"picker", "picker",
"pretty_assertions", "pretty_assertions",
"project", "project",

View file

@ -47,6 +47,7 @@ use std::{
iter, mem, iter, mem,
ops::{Range, RangeInclusive}, ops::{Range, RangeInclusive},
pin::Pin, pin::Pin,
rc::Rc,
sync::Arc, sync::Arc,
task::{self, Poll}, task::{self, Poll},
time::{Duration, Instant}, time::{Duration, Instant},
@ -174,7 +175,7 @@ impl InlineAssistant {
if let Some(editor) = item.act_as::<Editor>(cx) { if let Some(editor) = item.act_as::<Editor>(cx) {
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.push_code_action_provider( editor.push_code_action_provider(
Arc::new(AssistantCodeActionProvider { Rc::new(AssistantCodeActionProvider {
editor: cx.view().downgrade(), editor: cx.view().downgrade(),
workspace: workspace.downgrade(), workspace: workspace.downgrade(),
}), }),

View file

@ -7,11 +7,13 @@ use editor::{CompletionProvider, Editor};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext}; use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint}; use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint};
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
use project::CompletionIntent; use project::CompletionIntent;
use rope::Point; use rope::Point;
use std::{ use std::{
cell::RefCell,
ops::Range, ops::Range,
rc::Rc,
sync::{ sync::{
atomic::{AtomicBool, Ordering::SeqCst}, atomic::{AtomicBool, Ordering::SeqCst},
Arc, Arc,
@ -322,7 +324,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
&self, &self,
_: Model<Buffer>, _: Model<Buffer>,
_: Vec<usize>, _: Vec<usize>,
_: Arc<RwLock<Box<[project::Completion]>>>, _: Rc<RefCell<Box<[project::Completion]>>>,
_: &mut ViewContext<Editor>, _: &mut ViewContext<Editor>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
Task::ready(Ok(true)) Task::ready(Ok(true))

View file

@ -45,6 +45,7 @@ use std::{
iter, mem, iter, mem,
ops::{Range, RangeInclusive}, ops::{Range, RangeInclusive},
pin::Pin, pin::Pin,
rc::Rc,
sync::Arc, sync::Arc,
task::{self, Poll}, task::{self, Poll},
time::Instant, time::Instant,
@ -178,7 +179,7 @@ impl InlineAssistant {
if let Some(editor) = item.act_as::<Editor>(cx) { if let Some(editor) = item.act_as::<Editor>(cx) {
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.push_code_action_provider( editor.push_code_action_provider(
Arc::new(AssistantCodeActionProvider { Rc::new(AssistantCodeActionProvider {
editor: cx.view().downgrade(), editor: cx.view().downgrade(),
workspace: workspace.downgrade(), workspace: workspace.downgrade(),
}), }),

View file

@ -44,7 +44,6 @@ gpui.workspace = true
language.workspace = true language.workspace = true
menu.workspace = true menu.workspace = true
notifications.workspace = true notifications.workspace = true
parking_lot.workspace = true
picker.workspace = true picker.workspace = true
project.workspace = true project.workspace = true
release_channel.workspace = true release_channel.workspace = true

View file

@ -12,10 +12,15 @@ use language::{
language_settings::SoftWrap, Anchor, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry, language_settings::SoftWrap, Anchor, Buffer, BufferSnapshot, CodeLabel, LanguageRegistry,
LanguageServerId, ToOffset, LanguageServerId, ToOffset,
}; };
use parking_lot::RwLock;
use project::{search::SearchQuery, Completion}; use project::{search::SearchQuery, Completion};
use settings::Settings; use settings::Settings;
use std::{ops::Range, sync::Arc, sync::LazyLock, time::Duration}; use std::{
cell::RefCell,
ops::Range,
rc::Rc,
sync::{Arc, LazyLock},
time::Duration,
};
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{prelude::*, TextSize}; use ui::{prelude::*, TextSize};
@ -68,7 +73,7 @@ impl CompletionProvider for MessageEditorCompletionProvider {
&self, &self,
_buffer: Model<Buffer>, _buffer: Model<Buffer>,
_completion_indices: Vec<usize>, _completion_indices: Vec<usize>,
_completions: Arc<RwLock<Box<[Completion]>>>, _completions: Rc<RefCell<Box<[Completion]>>>,
_cx: &mut ViewContext<Editor>, _cx: &mut ViewContext<Editor>,
) -> Task<anyhow::Result<bool>> { ) -> Task<anyhow::Result<bool>> {
Task::ready(Ok(false)) Task::ready(Ok(false))

View file

@ -1,4 +1,5 @@
use std::{cell::Cell, cmp::Reverse, ops::Range, sync::Arc}; use std::cell::RefCell;
use std::{cell::Cell, cmp::Reverse, ops::Range, rc::Rc};
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
@ -11,7 +12,6 @@ use language::{CodeLabel, Documentation};
use lsp::LanguageServerId; use lsp::LanguageServerId;
use multi_buffer::{Anchor, ExcerptId}; use multi_buffer::{Anchor, ExcerptId};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use parking_lot::RwLock;
use project::{CodeAction, Completion, TaskSourceKind}; use project::{CodeAction, Completion, TaskSourceKind};
use task::ResolvedTask; use task::ResolvedTask;
use ui::{ use ui::{
@ -137,9 +137,9 @@ pub struct CompletionsMenu {
sort_completions: bool, sort_completions: bool,
pub initial_position: Anchor, pub initial_position: Anchor,
pub buffer: Model<Buffer>, pub buffer: Model<Buffer>,
pub completions: Arc<RwLock<Box<[Completion]>>>, pub completions: Rc<RefCell<Box<[Completion]>>>,
match_candidates: Arc<[StringMatchCandidate]>, match_candidates: Rc<[StringMatchCandidate]>,
pub matches: Arc<[StringMatch]>, pub matches: Rc<[StringMatch]>,
pub selected_item: usize, pub selected_item: usize,
scroll_handle: UniformListScrollHandle, scroll_handle: UniformListScrollHandle,
resolve_completions: bool, resolve_completions: bool,
@ -169,7 +169,7 @@ impl CompletionsMenu {
initial_position, initial_position,
buffer, buffer,
show_completion_documentation, show_completion_documentation,
completions: Arc::new(RwLock::new(completions)), completions: RefCell::new(completions).into(),
match_candidates, match_candidates,
matches: Vec::new().into(), matches: Vec::new().into(),
selected_item: 0, selected_item: 0,
@ -223,7 +223,7 @@ impl CompletionsMenu {
sort_completions, sort_completions,
initial_position: selection.start, initial_position: selection.start,
buffer, buffer,
completions: Arc::new(RwLock::new(completions)), completions: RefCell::new(completions).into(),
match_candidates, match_candidates,
matches, matches,
selected_item: 0, selected_item: 0,
@ -329,13 +329,13 @@ impl CompletionsMenu {
workspace: Option<WeakView<Workspace>>, workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> AnyElement { ) -> AnyElement {
let completions = self.completions.borrow_mut();
let show_completion_documentation = self.show_completion_documentation; let show_completion_documentation = self.show_completion_documentation;
let widest_completion_ix = self let widest_completion_ix = self
.matches .matches
.iter() .iter()
.enumerate() .enumerate()
.max_by_key(|(_, mat)| { .max_by_key(|(_, mat)| {
let completions = self.completions.read();
let completion = &completions[mat.candidate_id]; let completion = &completions[mat.candidate_id];
let documentation = &completion.documentation; let documentation = &completion.documentation;
@ -350,14 +350,12 @@ impl CompletionsMenu {
}) })
.map(|(ix, _)| ix); .map(|(ix, _)| ix);
let completions = self.completions.clone();
let matches = self.matches.clone();
let selected_item = self.selected_item; let selected_item = self.selected_item;
let style = style.clone(); let style = style.clone();
let multiline_docs = if show_completion_documentation { let multiline_docs = if show_completion_documentation {
let mat = &self.matches[selected_item]; let mat = &self.matches[selected_item];
match &self.completions.read()[mat.candidate_id].documentation { match &completions[mat.candidate_id].documentation {
Some(Documentation::MultiLinePlainText(text)) => { Some(Documentation::MultiLinePlainText(text)) => {
Some(div().child(SharedString::from(text.clone()))) Some(div().child(SharedString::from(text.clone())))
} }
@ -401,13 +399,16 @@ impl CompletionsMenu {
.occlude() .occlude()
}); });
drop(completions);
let completions = self.completions.clone();
let matches = self.matches.clone();
let list = uniform_list( let list = uniform_list(
cx.view().clone(), cx.view().clone(),
"completions", "completions",
matches.len(), matches.len(),
move |_editor, range, cx| { move |_editor, range, cx| {
let start_ix = range.start; let start_ix = range.start;
let completions_guard = completions.read(); let completions_guard = completions.borrow_mut();
matches[range] matches[range]
.iter() .iter()
@ -548,7 +549,7 @@ impl CompletionsMenu {
} }
} }
let completions = self.completions.read(); let completions = self.completions.borrow_mut();
if self.sort_completions { if self.sort_completions {
matches.sort_unstable_by_key(|mat| { matches.sort_unstable_by_key(|mat| {
// We do want to strike a balance here between what the language server tells us // We do want to strike a balance here between what the language server tells us
@ -611,13 +612,13 @@ impl CompletionsMenu {
pub struct AvailableCodeAction { pub struct AvailableCodeAction {
pub excerpt_id: ExcerptId, pub excerpt_id: ExcerptId,
pub action: CodeAction, pub action: CodeAction,
pub provider: Arc<dyn CodeActionProvider>, pub provider: Rc<dyn CodeActionProvider>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CodeActionContents { pub struct CodeActionContents {
pub tasks: Option<Arc<ResolvedTasks>>, pub tasks: Option<Rc<ResolvedTasks>>,
pub actions: Option<Arc<[AvailableCodeAction]>>, pub actions: Option<Rc<[AvailableCodeAction]>>,
} }
impl CodeActionContents { impl CodeActionContents {
@ -702,7 +703,7 @@ pub enum CodeActionsItem {
CodeAction { CodeAction {
excerpt_id: ExcerptId, excerpt_id: ExcerptId,
action: CodeAction, action: CodeAction,
provider: Arc<dyn CodeActionProvider>, provider: Rc<dyn CodeActionProvider>,
}, },
} }

View file

@ -127,7 +127,6 @@ pub use multi_buffer::{
use multi_buffer::{ use multi_buffer::{
ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
}; };
use parking_lot::RwLock;
use project::{ use project::{
lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle}, lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle},
project_settings::{GitGutterSetting, ProjectSettings}, project_settings::{GitGutterSetting, ProjectSettings},
@ -606,7 +605,7 @@ pub struct Editor {
scrollbar_marker_state: ScrollbarMarkerState, scrollbar_marker_state: ScrollbarMarkerState,
active_indent_guides_state: ActiveIndentGuidesState, active_indent_guides_state: ActiveIndentGuidesState,
nav_history: Option<ItemNavHistory>, nav_history: Option<ItemNavHistory>,
context_menu: RwLock<Option<CodeContextMenu>>, context_menu: RefCell<Option<CodeContextMenu>>,
mouse_context_menu: Option<MouseContextMenu>, mouse_context_menu: Option<MouseContextMenu>,
hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>, hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>, completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
@ -614,7 +613,7 @@ pub struct Editor {
auto_signature_help: Option<bool>, auto_signature_help: Option<bool>,
find_all_references_task_sources: Vec<Anchor>, find_all_references_task_sources: Vec<Anchor>,
next_completion_id: CompletionId, next_completion_id: CompletionId,
available_code_actions: Option<(Location, Arc<[AvailableCodeAction]>)>, available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
code_actions_task: Option<Task<Result<()>>>, code_actions_task: Option<Task<Result<()>>>,
document_highlights_task: Option<Task<()>>, document_highlights_task: Option<Task<()>>,
linked_editing_range_task: Option<Task<Option<()>>>, linked_editing_range_task: Option<Task<Option<()>>>,
@ -635,7 +634,7 @@ pub struct Editor {
gutter_hovered: bool, gutter_hovered: bool,
hovered_link_state: Option<HoveredLinkState>, hovered_link_state: Option<HoveredLinkState>,
inline_completion_provider: Option<RegisteredInlineCompletionProvider>, inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
code_action_providers: Vec<Arc<dyn CodeActionProvider>>, code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
active_inline_completion: Option<InlineCompletionState>, active_inline_completion: Option<InlineCompletionState>,
// enable_inline_completions is a switch that Vim can use to disable // enable_inline_completions is a switch that Vim can use to disable
// inline completions based on its mode. // inline completions based on its mode.
@ -1192,7 +1191,7 @@ impl Editor {
let mut code_action_providers = Vec::new(); let mut code_action_providers = Vec::new();
if let Some(project) = project.clone() { if let Some(project) = project.clone() {
get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx); get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
code_action_providers.push(Arc::new(project) as Arc<_>); code_action_providers.push(Rc::new(project) as Rc<_>);
} }
let mut this = Self { let mut this = Self {
@ -1238,7 +1237,7 @@ impl Editor {
scrollbar_marker_state: ScrollbarMarkerState::default(), scrollbar_marker_state: ScrollbarMarkerState::default(),
active_indent_guides_state: ActiveIndentGuidesState::default(), active_indent_guides_state: ActiveIndentGuidesState::default(),
nav_history: None, nav_history: None,
context_menu: RwLock::new(None), context_menu: RefCell::new(None),
mouse_context_menu: None, mouse_context_menu: None,
hunk_controls_menu_handle: PopoverMenuHandle::default(), hunk_controls_menu_handle: PopoverMenuHandle::default(),
completion_tasks: Default::default(), completion_tasks: Default::default(),
@ -1384,7 +1383,7 @@ impl Editor {
key_context.add("renaming"); key_context.add("renaming");
} }
if self.context_menu_visible() { if self.context_menu_visible() {
match self.context_menu.read().as_ref() { match self.context_menu.borrow().as_ref() {
Some(CodeContextMenu::Completions(_)) => { Some(CodeContextMenu::Completions(_)) => {
key_context.add("menu"); key_context.add("menu");
key_context.add("showing_completions") key_context.add("showing_completions")
@ -1886,10 +1885,9 @@ impl Editor {
if local { if local {
let new_cursor_position = self.selections.newest_anchor().head(); let new_cursor_position = self.selections.newest_anchor().head();
let mut context_menu = self.context_menu.write(); let mut context_menu = self.context_menu.borrow_mut();
let completion_menu = match context_menu.as_ref() { let completion_menu = match context_menu.as_ref() {
Some(CodeContextMenu::Completions(menu)) => Some(menu), Some(CodeContextMenu::Completions(menu)) => Some(menu),
_ => { _ => {
*context_menu = None; *context_menu = None;
None None
@ -1913,7 +1911,7 @@ impl Editor {
.await; .await;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
let mut context_menu = this.context_menu.write(); let mut context_menu = this.context_menu.borrow_mut();
let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref() let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
else { else {
return; return;
@ -3651,7 +3649,7 @@ impl Editor {
return; return;
}; };
if !self.snippet_stack.is_empty() && self.context_menu.read().as_ref().is_some() { if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
return; return;
} }
@ -3670,7 +3668,7 @@ impl Editor {
let query = Self::completion_query(&self.buffer.read(cx).read(cx), position); let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
let aside_was_displayed = match self.context_menu.read().deref() { let aside_was_displayed = match self.context_menu.borrow().deref() {
Some(CodeContextMenu::Completions(menu)) => menu.aside_was_displayed.get(), Some(CodeContextMenu::Completions(menu)) => menu.aside_was_displayed.get(),
_ => false, _ => false,
}; };
@ -3723,16 +3721,14 @@ impl Editor {
}; };
editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
let mut context_menu = editor.context_menu.write(); let mut context_menu = editor.context_menu.borrow_mut();
match context_menu.as_ref() { match context_menu.as_ref() {
None => {} None => {}
Some(CodeContextMenu::Completions(prev_menu)) => { Some(CodeContextMenu::Completions(prev_menu)) => {
if prev_menu.id > id { if prev_menu.id > id {
return; return;
} }
} }
_ => return, _ => return,
} }
@ -3795,7 +3791,7 @@ impl Editor {
.matches .matches
.get(item_ix.unwrap_or(completions_menu.selected_item))?; .get(item_ix.unwrap_or(completions_menu.selected_item))?;
let buffer_handle = completions_menu.buffer; let buffer_handle = completions_menu.buffer;
let completions = completions_menu.completions.read(); let completions = completions_menu.completions.borrow_mut();
let completion = completions.get(mat.candidate_id)?; let completion = completions.get(mat.candidate_id)?;
cx.stop_propagation(); cx.stop_propagation();
@ -3961,7 +3957,7 @@ impl Editor {
} }
pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) { pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
let mut context_menu = self.context_menu.write(); let mut context_menu = self.context_menu.borrow_mut();
if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() { if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
if code_actions.deployed_from_indicator == action.deployed_from_indicator { if code_actions.deployed_from_indicator == action.deployed_from_indicator {
// Toggle if we're selecting the same one // Toggle if we're selecting the same one
@ -4041,7 +4037,7 @@ impl Editor {
}; };
let resolved_tasks = let resolved_tasks =
tasks.zip(task_context).map(|(tasks, task_context)| { tasks.zip(task_context).map(|(tasks, task_context)| {
Arc::new(ResolvedTasks { Rc::new(ResolvedTasks {
templates: tasks.resolve(&task_context).collect(), templates: tasks.resolve(&task_context).collect(),
position: snapshot.buffer_snapshot.anchor_before(Point::new( position: snapshot.buffer_snapshot.anchor_before(Point::new(
multibuffer_point.row, multibuffer_point.row,
@ -4056,7 +4052,7 @@ impl Editor {
.as_ref() .as_ref()
.map_or(true, |actions| actions.is_empty()); .map_or(true, |actions| actions.is_empty());
if let Ok(task) = editor.update(&mut cx, |editor, cx| { if let Ok(task) = editor.update(&mut cx, |editor, cx| {
*editor.context_menu.write() = *editor.context_menu.borrow_mut() =
Some(CodeContextMenu::CodeActions(CodeActionsMenu { Some(CodeContextMenu::CodeActions(CodeActionsMenu {
buffer, buffer,
actions: CodeActionContents { actions: CodeActionContents {
@ -4241,7 +4237,7 @@ impl Editor {
pub fn push_code_action_provider( pub fn push_code_action_provider(
&mut self, &mut self,
provider: Arc<dyn CodeActionProvider>, provider: Rc<dyn CodeActionProvider>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
self.code_action_providers.push(provider); self.code_action_providers.push(provider);
@ -5004,7 +5000,7 @@ impl Editor {
pub fn context_menu_visible(&self) -> bool { pub fn context_menu_visible(&self) -> bool {
self.context_menu self.context_menu
.read() .borrow()
.as_ref() .as_ref()
.map_or(false, |menu| menu.visible()) .map_or(false, |menu| menu.visible())
} }
@ -5016,7 +5012,7 @@ impl Editor {
max_height: Pixels, max_height: Pixels,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Option<(ContextMenuOrigin, AnyElement)> { ) -> Option<(ContextMenuOrigin, AnyElement)> {
self.context_menu.read().as_ref().map(|menu| { self.context_menu.borrow().as_ref().map(|menu| {
menu.render( menu.render(
cursor_position, cursor_position,
style, style,
@ -5030,7 +5026,7 @@ impl Editor {
fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> { fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
cx.notify(); cx.notify();
self.completion_tasks.clear(); self.completion_tasks.clear();
self.context_menu.write().take() self.context_menu.borrow_mut().take()
} }
fn show_snippet_choices( fn show_snippet_choices(
@ -5047,7 +5043,7 @@ impl Editor {
let id = post_inc(&mut self.next_completion_id); let id = post_inc(&mut self.next_completion_id);
if let Some(buffer) = buffer { if let Some(buffer) = buffer {
*self.context_menu.write() = Some(CodeContextMenu::Completions( *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer), CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
)); ));
} }
@ -7101,7 +7097,7 @@ impl Editor {
if self if self
.context_menu .context_menu
.write() .borrow_mut()
.as_mut() .as_mut()
.map(|menu| menu.select_first(self.completion_provider.as_deref(), cx)) .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
.unwrap_or(false) .unwrap_or(false)
@ -7210,7 +7206,7 @@ impl Editor {
if self if self
.context_menu .context_menu
.write() .borrow_mut()
.as_mut() .as_mut()
.map(|menu| menu.select_last(self.completion_provider.as_deref(), cx)) .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
.unwrap_or(false) .unwrap_or(false)
@ -7263,25 +7259,25 @@ impl Editor {
} }
pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) { pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.write().as_mut() { if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
context_menu.select_first(self.completion_provider.as_deref(), cx); context_menu.select_first(self.completion_provider.as_deref(), cx);
} }
} }
pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) { pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.write().as_mut() { if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
context_menu.select_prev(self.completion_provider.as_deref(), cx); context_menu.select_prev(self.completion_provider.as_deref(), cx);
} }
} }
pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) { pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.write().as_mut() { if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
context_menu.select_next(self.completion_provider.as_deref(), cx); context_menu.select_next(self.completion_provider.as_deref(), cx);
} }
} }
pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) { pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.write().as_mut() { if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
context_menu.select_last(self.completion_provider.as_deref(), cx); context_menu.select_last(self.completion_provider.as_deref(), cx);
} }
} }
@ -12818,7 +12814,7 @@ impl Editor {
} }
pub fn has_active_completions_menu(&self) -> bool { pub fn has_active_completions_menu(&self) -> bool {
self.context_menu.read().as_ref().map_or(false, |menu| { self.context_menu.borrow().as_ref().map_or(false, |menu| {
menu.visible() && matches!(menu, CodeContextMenu::Completions(_)) menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
}) })
} }
@ -13314,7 +13310,7 @@ pub trait CompletionProvider {
&self, &self,
buffer: Model<Buffer>, buffer: Model<Buffer>,
completion_indices: Vec<usize>, completion_indices: Vec<usize>,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Task<Result<bool>>; ) -> Task<Result<bool>>;
@ -13544,7 +13540,7 @@ impl CompletionProvider for Model<Project> {
&self, &self,
buffer: Model<Buffer>, buffer: Model<Buffer>,
completion_indices: Vec<usize>, completion_indices: Vec<usize>,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
self.update(cx, |project, cx| { self.update(cx, |project, cx| {

View file

@ -8342,9 +8342,9 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
additional edit additional edit
"}); "});
cx.simulate_keystroke(" "); cx.simulate_keystroke(" ");
assert!(cx.editor(|e, _| e.context_menu.read().is_none())); assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
cx.simulate_keystroke("s"); cx.simulate_keystroke("s");
assert!(cx.editor(|e, _| e.context_menu.read().is_none())); assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
one.second_completion one.second_completion
@ -8406,12 +8406,12 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
}); });
cx.set_state("editorˇ"); cx.set_state("editorˇ");
cx.simulate_keystroke("."); cx.simulate_keystroke(".");
assert!(cx.editor(|e, _| e.context_menu.read().is_none())); assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
cx.simulate_keystroke("c"); cx.simulate_keystroke("c");
cx.simulate_keystroke("l"); cx.simulate_keystroke("l");
cx.simulate_keystroke("o"); cx.simulate_keystroke("o");
cx.assert_editor_state("editor.cloˇ"); cx.assert_editor_state("editor.cloˇ");
assert!(cx.editor(|e, _| e.context_menu.read().is_none())); assert!(cx.editor(|e, _| e.context_menu.borrow_mut().is_none()));
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
editor.show_completions(&ShowCompletions { trigger: None }, cx); editor.show_completions(&ShowCompletions { trigger: None }, cx);
}); });
@ -8468,7 +8468,8 @@ async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
cx.executor().run_until_parked(); cx.executor().run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!( assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(), menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["first", "last"] &["first", "last"]
@ -8480,7 +8481,8 @@ async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
editor.move_page_down(&MovePageDown::default(), cx); editor.move_page_down(&MovePageDown::default(), cx);
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert!( assert!(
menu.selected_item == 1, menu.selected_item == 1,
"expected PageDown to select the last item from the context menu" "expected PageDown to select the last item from the context menu"
@ -8492,7 +8494,8 @@ async fn test_completion_page_up_down_keys(cx: &mut gpui::TestAppContext) {
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
editor.move_page_up(&MovePageUp::default(), cx); editor.move_page_up(&MovePageUp::default(), cx);
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert!( assert!(
menu.selected_item == 0, menu.selected_item == 0,
"expected PageUp to select the first item from the context menu" "expected PageUp to select the first item from the context menu"
@ -8560,7 +8563,8 @@ async fn test_completion_sort(cx: &mut gpui::TestAppContext) {
cx.executor().run_until_parked(); cx.executor().run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!( assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(), menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["r", "ret", "Range", "return"] &["r", "ret", "Range", "return"]
@ -10704,13 +10708,13 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
cx.condition(|editor, _| editor.context_menu_visible()) cx.condition(|editor, _| editor.context_menu_visible())
.await; .await;
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
let context_menu = editor.context_menu.read(); let context_menu = editor.context_menu.borrow_mut();
let context_menu = context_menu let context_menu = context_menu
.as_ref() .as_ref()
.expect("Should have the context menu deployed"); .expect("Should have the context menu deployed");
match context_menu { match context_menu {
CodeContextMenu::Completions(completions_menu) => { CodeContextMenu::Completions(completions_menu) => {
let completions = completions_menu.completions.read(); let completions = completions_menu.completions.borrow_mut();
assert_eq!( assert_eq!(
completions completions
.iter() .iter()
@ -10761,13 +10765,13 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
cx.run_until_parked(); cx.run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
let context_menu = editor.context_menu.read(); let context_menu = editor.context_menu.borrow_mut();
let context_menu = context_menu let context_menu = context_menu
.as_ref() .as_ref()
.expect("Should have the context menu deployed"); .expect("Should have the context menu deployed");
match context_menu { match context_menu {
CodeContextMenu::Completions(completions_menu) => { CodeContextMenu::Completions(completions_menu) => {
let completions = completions_menu.completions.read(); let completions = completions_menu.completions.borrow_mut();
assert_eq!( assert_eq!(
completions completions
.iter() .iter()
@ -10954,7 +10958,7 @@ async fn test_completions_default_resolve_data_handling(cx: &mut gpui::TestAppCo
.await; .await;
cx.run_until_parked(); cx.run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
let menu = editor.context_menu.read(); let menu = editor.context_menu.borrow_mut();
match menu.as_ref().expect("should have the completions menu") { match menu.as_ref().expect("should have the completions menu") {
CodeContextMenu::Completions(completions_menu) => { CodeContextMenu::Completions(completions_menu) => {
assert_eq!( assert_eq!(
@ -11059,7 +11063,8 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui:
cx.simulate_keystroke("-"); cx.simulate_keystroke("-");
cx.executor().run_until_parked(); cx.executor().run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!( assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(), menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-red", "bg-blue", "bg-yellow"] &["bg-red", "bg-blue", "bg-yellow"]
@ -11072,7 +11077,8 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui:
cx.simulate_keystroke("l"); cx.simulate_keystroke("l");
cx.executor().run_until_parked(); cx.executor().run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!( assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(), menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-blue", "bg-yellow"] &["bg-blue", "bg-yellow"]
@ -11088,7 +11094,8 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui:
cx.simulate_keystroke("l"); cx.simulate_keystroke("l");
cx.executor().run_until_parked(); cx.executor().run_until_parked();
cx.update_editor(|editor, _| { cx.update_editor(|editor, _| {
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
{
assert_eq!( assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(), menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-yellow"] &["bg-yellow"]

View file

@ -1686,7 +1686,7 @@ impl EditorElement {
deployed_from_indicator, deployed_from_indicator,
actions, actions,
.. ..
})) = editor.context_menu.read().as_ref() })) = editor.context_menu.borrow().as_ref()
{ {
actions actions
.tasks .tasks
@ -1777,7 +1777,7 @@ impl EditorElement {
if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu { if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
deployed_from_indicator, deployed_from_indicator,
.. ..
})) = editor.context_menu.read().as_ref() })) = editor.context_menu.borrow().as_ref()
{ {
active = deployed_from_indicator.map_or(true, |indicator_row| indicator_row == row); active = deployed_from_indicator.map_or(true, |indicator_row| indicator_row == row);
}; };

View file

@ -52,7 +52,7 @@ use lsp::{
WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder, WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder,
}; };
use node_runtime::read_package_installed_version; use node_runtime::read_package_installed_version;
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
use postage::watch; use postage::watch;
use rand::prelude::*; use rand::prelude::*;
@ -65,12 +65,14 @@ use smol::channel::Sender;
use snippet::Snippet; use snippet::Snippet;
use std::{ use std::{
any::Any, any::Any,
cell::RefCell,
cmp::Ordering, cmp::Ordering,
convert::TryInto, convert::TryInto,
ffi::OsStr, ffi::OsStr,
iter, mem, iter, mem,
ops::{ControlFlow, Range}, ops::{ControlFlow, Range},
path::{self, Path, PathBuf}, path::{self, Path, PathBuf},
rc::Rc,
str, str,
sync::Arc, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
@ -4137,7 +4139,7 @@ impl LspStore {
&self, &self,
buffer: Model<Buffer>, buffer: Model<Buffer>,
completion_indices: Vec<usize>, completion_indices: Vec<usize>,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
let client = self.upstream_client(); let client = self.upstream_client();
@ -4151,8 +4153,8 @@ impl LspStore {
if let Some((client, project_id)) = client { if let Some((client, project_id)) = client {
for completion_index in completion_indices { for completion_index in completion_indices {
let (server_id, completion) = { let (server_id, completion) = {
let completions_guard = completions.read(); let completions = completions.borrow_mut();
let completion = &completions_guard[completion_index]; let completion = &completions[completion_index];
did_resolve = true; did_resolve = true;
let server_id = completion.server_id; let server_id = completion.server_id;
let completion = completion.lsp_completion.clone(); let completion = completion.lsp_completion.clone();
@ -4175,8 +4177,8 @@ impl LspStore {
} else { } else {
for completion_index in completion_indices { for completion_index in completion_indices {
let (server_id, completion) = { let (server_id, completion) = {
let completions_guard = completions.read(); let completions = completions.borrow_mut();
let completion = &completions_guard[completion_index]; let completion = &completions[completion_index];
let server_id = completion.server_id; let server_id = completion.server_id;
let completion = completion.lsp_completion.clone(); let completion = completion.lsp_completion.clone();
@ -4218,7 +4220,7 @@ impl LspStore {
server: Arc<lsp::LanguageServer>, server: Arc<lsp::LanguageServer>,
adapter: Arc<CachedLspAdapter>, adapter: Arc<CachedLspAdapter>,
snapshot: &BufferSnapshot, snapshot: &BufferSnapshot,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize, completion_index: usize,
completion: lsp::CompletionItem, completion: lsp::CompletionItem,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
@ -4246,11 +4248,11 @@ impl LspStore {
) )
.await; .await;
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
completion.documentation = Some(documentation); completion.documentation = Some(documentation);
} else { } else {
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
completion.documentation = Some(Documentation::Undocumented); completion.documentation = Some(Documentation::Undocumented);
} }
@ -4265,7 +4267,7 @@ impl LspStore {
if let Some((old_range, mut new_text)) = edit { if let Some((old_range, mut new_text)) = edit {
LineEnding::normalize(&mut new_text); LineEnding::normalize(&mut new_text);
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
completion.new_text = new_text; completion.new_text = new_text;
@ -4274,7 +4276,7 @@ impl LspStore {
} }
if completion_item.insert_text_format == Some(InsertTextFormat::SNIPPET) { if completion_item.insert_text_format == Some(InsertTextFormat::SNIPPET) {
// vtsls might change the type of completion after resolution. // vtsls might change the type of completion after resolution.
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
if completion_item.insert_text_format != completion.lsp_completion.insert_text_format { if completion_item.insert_text_format != completion.lsp_completion.insert_text_format {
completion.lsp_completion.insert_text_format = completion_item.insert_text_format; completion.lsp_completion.insert_text_format = completion_item.insert_text_format;
@ -4300,7 +4302,7 @@ impl LspStore {
) )
}); });
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
completion.lsp_completion = completion_item; completion.lsp_completion = completion_item;
if completion.label.filter_text() == new_label.filter_text() { if completion.label.filter_text() == new_label.filter_text() {
@ -4322,7 +4324,7 @@ impl LspStore {
project_id: u64, project_id: u64,
server_id: LanguageServerId, server_id: LanguageServerId,
buffer_id: BufferId, buffer_id: BufferId,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
completion_index: usize, completion_index: usize,
completion: lsp::CompletionItem, completion: lsp::CompletionItem,
client: AnyProtoClient, client: AnyProtoClient,
@ -4360,7 +4362,7 @@ impl LspStore {
Documentation::MultiLinePlainText(response.documentation) Documentation::MultiLinePlainText(response.documentation)
}; };
let mut completions = completions.write(); let mut completions = completions.borrow_mut();
let completion = &mut completions[completion_index]; let completion = &mut completions[completion_index];
completion.documentation = Some(documentation); completion.documentation = Some(documentation);
completion.lsp_completion = lsp_completion; completion.lsp_completion = lsp_completion;

View file

@ -57,7 +57,7 @@ use lsp::{
}; };
use lsp_command::*; use lsp_command::*;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
pub use prettier_store::PrettierStore; pub use prettier_store::PrettierStore;
use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent}; use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
use remote::{SshConnectionOptions, SshRemoteClient}; use remote::{SshConnectionOptions, SshRemoteClient};
@ -73,8 +73,10 @@ use snippet::Snippet;
use snippet_provider::SnippetProvider; use snippet_provider::SnippetProvider;
use std::{ use std::{
borrow::Cow, borrow::Cow,
cell::RefCell,
ops::Range, ops::Range,
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
rc::Rc,
str, str,
sync::Arc, sync::Arc,
time::Duration, time::Duration,
@ -2872,7 +2874,7 @@ impl Project {
&self, &self,
buffer: Model<Buffer>, buffer: Model<Buffer>,
completion_indices: Vec<usize>, completion_indices: Vec<usize>,
completions: Arc<RwLock<Box<[Completion]>>>, completions: Rc<RefCell<Box<[Completion]>>>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
self.lsp_store.update(cx, |lsp_store, cx| { self.lsp_store.update(cx, |lsp_store, cx| {