Fix inline assist panic (#43364)
Fixes a panic that was introduced in #42633. Repro steps: 1. Open the inline assistant and mention a file in the prompt 2. Run the inline assistant 3. Remove the mention and insert a different one 4. 💥 This would happen because the mention set still had a reference to the old editor, because we create a new one in `PromptEditor::unlink`. Also removes the unused `crates/agent_ui/src/context_picker/completion_provider.rs` file, which was not removed by mistake in the previous PR. Release Notes: - N/A
This commit is contained in:
@@ -15,8 +15,9 @@ use anyhow::{Result, anyhow};
|
||||
use collections::HashSet;
|
||||
use editor::{
|
||||
Addon, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement,
|
||||
EditorEvent, EditorMode, EditorStyle, Inlay, MultiBuffer, MultiBufferOffset, ToOffset,
|
||||
actions::Paste, code_context_menus::CodeContextMenu, scroll::Autoscroll,
|
||||
EditorEvent, EditorMode, EditorStyle, Inlay, MultiBuffer, MultiBufferOffset,
|
||||
MultiBufferSnapshot, ToOffset, actions::Paste, code_context_menus::CodeContextMenu,
|
||||
scroll::Autoscroll,
|
||||
};
|
||||
use futures::{FutureExt as _, future::join_all};
|
||||
use gpui::{
|
||||
@@ -133,18 +134,16 @@ impl MessageEditor {
|
||||
editor.register_addon(MessageEditorAddon::new());
|
||||
editor
|
||||
});
|
||||
let mention_set = cx.new(|cx| {
|
||||
let mention_set = cx.new(|_cx| {
|
||||
MentionSet::new(
|
||||
editor.clone(),
|
||||
project.downgrade(),
|
||||
history_store.clone(),
|
||||
prompt_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let completion_provider = Rc::new(PromptCompletionProvider::new(
|
||||
cx.entity(),
|
||||
editor.downgrade(),
|
||||
mention_set.clone(),
|
||||
history_store.clone(),
|
||||
prompt_store.clone(),
|
||||
@@ -166,14 +165,18 @@ impl MessageEditor {
|
||||
let mut has_hint = false;
|
||||
let mut subscriptions = Vec::new();
|
||||
|
||||
subscriptions.push(cx.subscribe(&editor, {
|
||||
move |this, editor, event, cx| {
|
||||
subscriptions.push(cx.subscribe_in(&editor, window, {
|
||||
move |this, editor, event, window, cx| {
|
||||
if let EditorEvent::Edited { .. } = event
|
||||
&& !editor.read(cx).read_only(cx)
|
||||
{
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
this.mention_set
|
||||
.update(cx, |mention_set, _cx| mention_set.remove_invalid(&snapshot));
|
||||
|
||||
let new_hints = this
|
||||
.command_hint(editor.buffer(), cx)
|
||||
.command_hint(snapshot.buffer())
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
let has_new_hint = !new_hints.is_empty();
|
||||
@@ -206,13 +209,12 @@ impl MessageEditor {
|
||||
}
|
||||
}
|
||||
|
||||
fn command_hint(&self, buffer: &Entity<MultiBuffer>, cx: &App) -> Option<Inlay> {
|
||||
fn command_hint(&self, snapshot: &MultiBufferSnapshot) -> Option<Inlay> {
|
||||
let available_commands = self.available_commands.borrow();
|
||||
if available_commands.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let snapshot = buffer.read(cx).snapshot(cx);
|
||||
let parsed_command = SlashCommandCompletion::try_parse(&snapshot.text(), 0)?;
|
||||
if parsed_command.argument.is_some() {
|
||||
return None;
|
||||
@@ -286,6 +288,7 @@ impl MessageEditor {
|
||||
content_len,
|
||||
uri,
|
||||
supports_images,
|
||||
self.editor.clone(),
|
||||
&workspace,
|
||||
window,
|
||||
cx,
|
||||
@@ -480,7 +483,7 @@ impl MessageEditor {
|
||||
editor.remove_creases(
|
||||
self.mention_set.update(cx, |mention_set, _cx| {
|
||||
mention_set
|
||||
.remove_all()
|
||||
.clear()
|
||||
.map(|(crease_id, _)| crease_id)
|
||||
.collect::<Vec<_>>()
|
||||
}),
|
||||
@@ -628,6 +631,7 @@ impl MessageEditor {
|
||||
content_len,
|
||||
uri,
|
||||
supports_images,
|
||||
self.editor.clone(),
|
||||
&workspace,
|
||||
window,
|
||||
cx,
|
||||
@@ -659,6 +663,7 @@ impl MessageEditor {
|
||||
PromptCompletionProvider::<Entity<MessageEditor>>::completion_for_action(
|
||||
PromptContextAction::AddSelections,
|
||||
anchor..anchor,
|
||||
self.editor.downgrade(),
|
||||
self.mention_set.downgrade(),
|
||||
&workspace,
|
||||
cx,
|
||||
|
||||
@@ -181,6 +181,7 @@ pub trait PromptCompletionProviderDelegate: Send + Sync + 'static {
|
||||
|
||||
pub struct PromptCompletionProvider<T: PromptCompletionProviderDelegate> {
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
@@ -190,6 +191,7 @@ pub struct PromptCompletionProvider<T: PromptCompletionProviderDelegate> {
|
||||
impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
pub fn new(
|
||||
source: T,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: Entity<MentionSet>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
@@ -197,6 +199,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
) -> Self {
|
||||
Self {
|
||||
source: Arc::new(source),
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
history_store,
|
||||
@@ -207,6 +210,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
fn completion_for_entry(
|
||||
entry: PromptContextEntry,
|
||||
source_range: Range<Anchor>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: &Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -227,9 +231,14 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
// inserted
|
||||
confirm: Some(Arc::new(|_, _, _| true)),
|
||||
}),
|
||||
PromptContextEntry::Action(action) => {
|
||||
Self::completion_for_action(action, source_range, mention_set, workspace, cx)
|
||||
}
|
||||
PromptContextEntry::Action(action) => Self::completion_for_action(
|
||||
action,
|
||||
source_range,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
cx,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +247,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
source_range: Range<Anchor>,
|
||||
recent: bool,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -269,6 +279,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
new_text_len - 1,
|
||||
uri,
|
||||
source,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
)),
|
||||
@@ -279,6 +290,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
rule: RulesContextEntry,
|
||||
source_range: Range<Anchor>,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -306,6 +318,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
new_text_len - 1,
|
||||
uri,
|
||||
source,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
)),
|
||||
@@ -319,6 +332,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
is_directory: bool,
|
||||
source_range: Range<Anchor>,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
project: Entity<Project>,
|
||||
@@ -364,6 +378,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
new_text_len - 1,
|
||||
uri,
|
||||
source,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
)),
|
||||
@@ -374,6 +389,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
symbol: Symbol,
|
||||
source_range: Range<Anchor>,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -425,6 +441,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
new_text_len - 1,
|
||||
uri,
|
||||
source,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
)),
|
||||
@@ -435,6 +452,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
source_range: Range<Anchor>,
|
||||
url_to_fetch: SharedString,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -463,6 +481,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
new_text.len() - 1,
|
||||
mention_uri,
|
||||
source,
|
||||
editor,
|
||||
mention_set,
|
||||
workspace,
|
||||
)),
|
||||
@@ -472,6 +491,7 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
pub(crate) fn completion_for_action(
|
||||
action: PromptContextAction,
|
||||
source_range: Range<Anchor>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: &Entity<Workspace>,
|
||||
cx: &mut App,
|
||||
@@ -496,20 +516,24 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
|
||||
let callback = Arc::new({
|
||||
let source_range = source_range.clone();
|
||||
move |_, window: &mut Window, cx: &mut App| {
|
||||
let editor = editor.clone();
|
||||
let selections = selections.clone();
|
||||
let mention_set = mention_set.clone();
|
||||
let source_range = source_range.clone();
|
||||
window.defer(cx, move |window, cx| {
|
||||
mention_set
|
||||
.update(cx, |store, cx| {
|
||||
store.confirm_mention_for_selection(
|
||||
source_range,
|
||||
selections,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
if let Some(editor) = editor.upgrade() {
|
||||
mention_set
|
||||
.update(cx, |store, cx| {
|
||||
store.confirm_mention_for_selection(
|
||||
source_range,
|
||||
selections,
|
||||
editor,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
@@ -853,6 +877,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
..snapshot.anchor_after(state.source_range().end);
|
||||
|
||||
let source = self.source.clone();
|
||||
let editor = self.editor.clone();
|
||||
let mention_set = self.mention_set.downgrade();
|
||||
match state {
|
||||
ContextCompletion::SlashCommand(SlashCommandCompletion {
|
||||
@@ -955,6 +980,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
mat.is_dir,
|
||||
source_range.clone(),
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
project.clone(),
|
||||
@@ -967,6 +993,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
symbol,
|
||||
source_range.clone(),
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
@@ -978,6 +1005,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
source_range.clone(),
|
||||
false,
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
@@ -988,6 +1016,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
source_range.clone(),
|
||||
true,
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
@@ -997,6 +1026,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
user_rules,
|
||||
source_range.clone(),
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
@@ -1006,6 +1036,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
source_range.clone(),
|
||||
url,
|
||||
source.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
@@ -1015,6 +1046,7 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
|
||||
Self::completion_for_entry(
|
||||
entry,
|
||||
source_range.clone(),
|
||||
editor.clone(),
|
||||
mention_set.clone(),
|
||||
&workspace,
|
||||
cx,
|
||||
@@ -1091,33 +1123,38 @@ fn confirm_completion_callback<T: PromptCompletionProviderDelegate>(
|
||||
content_len: usize,
|
||||
mention_uri: MentionUri,
|
||||
source: Arc<T>,
|
||||
editor: WeakEntity<Editor>,
|
||||
mention_set: WeakEntity<MentionSet>,
|
||||
workspace: Entity<Workspace>,
|
||||
) -> Arc<dyn Fn(CompletionIntent, &mut Window, &mut App) -> bool + Send + Sync> {
|
||||
Arc::new(move |_, window, cx| {
|
||||
let source = source.clone();
|
||||
let editor = editor.clone();
|
||||
let mention_set = mention_set.clone();
|
||||
let crease_text = crease_text.clone();
|
||||
let mention_uri = mention_uri.clone();
|
||||
let workspace = workspace.clone();
|
||||
window.defer(cx, move |window, cx| {
|
||||
mention_set
|
||||
.clone()
|
||||
.update(cx, |mention_set, cx| {
|
||||
mention_set
|
||||
.confirm_mention_completion(
|
||||
crease_text,
|
||||
start,
|
||||
content_len,
|
||||
mention_uri,
|
||||
source.supports_images(cx),
|
||||
&workspace,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
})
|
||||
.ok();
|
||||
if let Some(editor) = editor.upgrade() {
|
||||
mention_set
|
||||
.clone()
|
||||
.update(cx, |mention_set, cx| {
|
||||
mention_set
|
||||
.confirm_mention_completion(
|
||||
crease_text,
|
||||
start,
|
||||
content_len,
|
||||
mention_uri,
|
||||
source.supports_images(cx),
|
||||
editor,
|
||||
&workspace,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
false
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
use agent::HistoryStore;
|
||||
use collections::VecDeque;
|
||||
use collections::{HashMap, VecDeque};
|
||||
use editor::actions::Paste;
|
||||
use editor::code_context_menus::CodeContextMenu;
|
||||
use editor::display_map::EditorMargins;
|
||||
use editor::display_map::{CreaseId, EditorMargins};
|
||||
use editor::{AnchorRangeExt as _, MultiBufferOffset, ToOffset as _};
|
||||
use editor::{
|
||||
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer,
|
||||
@@ -226,9 +226,10 @@ impl<T: 'static> PromptEditor<T> {
|
||||
}
|
||||
|
||||
fn assign_completion_provider(&mut self, cx: &mut Context<Self>) {
|
||||
self.editor.update(cx, |editor, _cx| {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_completion_provider(Some(Rc::new(PromptCompletionProvider::new(
|
||||
PromptEditorCompletionProviderDelegate,
|
||||
cx.weak_entity(),
|
||||
self.mention_set.clone(),
|
||||
self.history_store.clone(),
|
||||
self.prompt_store.clone(),
|
||||
@@ -253,18 +254,35 @@ impl<T: 'static> PromptEditor<T> {
|
||||
extract_message_creases(editor, &self.mention_set, window, cx)
|
||||
});
|
||||
let focus = self.editor.focus_handle(cx).contains_focused(window, cx);
|
||||
let mut creases = vec![];
|
||||
self.editor = cx.new(|cx| {
|
||||
let mut editor = Editor::auto_height(1, Self::MAX_LINES as usize, window, cx);
|
||||
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
|
||||
editor.set_placeholder_text("Add a prompt…", window, cx);
|
||||
editor.set_text(prompt, window, cx);
|
||||
insert_message_creases(&mut editor, &existing_creases, window, cx);
|
||||
creases = insert_message_creases(&mut editor, &existing_creases, window, cx);
|
||||
|
||||
if focus {
|
||||
window.focus(&editor.focus_handle(cx));
|
||||
}
|
||||
editor
|
||||
});
|
||||
|
||||
self.mention_set.update(cx, |mention_set, _cx| {
|
||||
debug_assert_eq!(
|
||||
creases.len(),
|
||||
mention_set.creases().len(),
|
||||
"Missing creases"
|
||||
);
|
||||
|
||||
let mentions = mention_set
|
||||
.clear()
|
||||
.zip(creases)
|
||||
.map(|((_, value), id)| (id, value))
|
||||
.collect::<HashMap<_, _>>();
|
||||
mention_set.set_mentions(mentions);
|
||||
});
|
||||
|
||||
self.assign_completion_provider(cx);
|
||||
self.subscribe_to_editor(window, cx);
|
||||
}
|
||||
@@ -304,13 +322,18 @@ impl<T: 'static> PromptEditor<T> {
|
||||
|
||||
fn handle_prompt_editor_events(
|
||||
&mut self,
|
||||
_: &Entity<Editor>,
|
||||
editor: &Entity<Editor>,
|
||||
event: &EditorEvent,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match event {
|
||||
EditorEvent::Edited { .. } => {
|
||||
let snapshot = editor.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
|
||||
self.mention_set
|
||||
.update(cx, |mention_set, _cx| mention_set.remove_invalid(&snapshot));
|
||||
|
||||
if let Some(workspace) = window.root::<Workspace>().flatten() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let is_via_ssh = workspace.project().read(cx).is_via_remote_server();
|
||||
@@ -321,7 +344,7 @@ impl<T: 'static> PromptEditor<T> {
|
||||
.log_edit_event("inline assist", is_via_ssh);
|
||||
});
|
||||
}
|
||||
let prompt = self.editor.read(cx).text(cx);
|
||||
let prompt = snapshot.text();
|
||||
if self
|
||||
.prompt_history_ix
|
||||
.is_none_or(|ix| self.prompt_history[ix] != prompt)
|
||||
@@ -848,16 +871,8 @@ impl PromptEditor<BufferCodegen> {
|
||||
editor
|
||||
});
|
||||
|
||||
let mention_set = cx.new(|cx| {
|
||||
MentionSet::new(
|
||||
prompt_editor.clone(),
|
||||
project,
|
||||
history_store.clone(),
|
||||
prompt_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let mention_set =
|
||||
cx.new(|_cx| MentionSet::new(project, history_store.clone(), prompt_store.clone()));
|
||||
|
||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||
|
||||
@@ -999,16 +1014,8 @@ impl PromptEditor<TerminalCodegen> {
|
||||
editor
|
||||
});
|
||||
|
||||
let mention_set = cx.new(|cx| {
|
||||
MentionSet::new(
|
||||
prompt_editor.clone(),
|
||||
project,
|
||||
history_store.clone(),
|
||||
prompt_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let mention_set =
|
||||
cx.new(|_cx| MentionSet::new(project, history_store.clone(), prompt_store.clone()));
|
||||
|
||||
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||
|
||||
@@ -1203,7 +1210,7 @@ fn insert_message_creases(
|
||||
message_creases: &[MessageCrease],
|
||||
window: &mut Window,
|
||||
cx: &mut Context<'_, Editor>,
|
||||
) {
|
||||
) -> Vec<CreaseId> {
|
||||
let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let creases = message_creases
|
||||
.iter()
|
||||
@@ -1218,6 +1225,7 @@ fn insert_message_creases(
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
editor.insert_creases(creases.clone(), cx);
|
||||
let ids = editor.insert_creases(creases.clone(), cx);
|
||||
editor.fold_creases(creases, false, window, cx);
|
||||
ids
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_slash_commands::codeblock_fence_for_path;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
Anchor, Editor, EditorEvent, EditorSnapshot, ExcerptId, FoldPlaceholder, ToOffset,
|
||||
Anchor, Editor, EditorSnapshot, ExcerptId, FoldPlaceholder, ToOffset,
|
||||
display_map::{Crease, CreaseId, CreaseMetadata, FoldId},
|
||||
scroll::Autoscroll,
|
||||
};
|
||||
use futures::{AsyncReadExt as _, FutureExt as _, future::Shared};
|
||||
use gpui::{
|
||||
Animation, AnimationExt as _, AppContext, ClipboardEntry, Context, Empty, Entity, EntityId,
|
||||
Image, ImageFormat, Img, SharedString, Subscription, Task, WeakEntity, pulsating_between,
|
||||
Image, ImageFormat, Img, SharedString, Task, WeakEntity, pulsating_between,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClientWithUrl};
|
||||
use itertools::Either;
|
||||
@@ -58,40 +58,23 @@ pub struct MentionImage {
|
||||
}
|
||||
|
||||
pub struct MentionSet {
|
||||
editor: Entity<Editor>,
|
||||
project: WeakEntity<Project>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
mentions: HashMap<CreaseId, (MentionUri, MentionTask)>,
|
||||
_editor_subscription: Subscription,
|
||||
}
|
||||
|
||||
impl MentionSet {
|
||||
pub fn new(
|
||||
editor: Entity<Editor>,
|
||||
project: WeakEntity<Project>,
|
||||
history_store: Entity<HistoryStore>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let editor_subscription =
|
||||
cx.subscribe_in(&editor, window, move |this, editor, event, window, cx| {
|
||||
if let EditorEvent::Edited { .. } = event
|
||||
&& !editor.read(cx).read_only(cx)
|
||||
{
|
||||
let snapshot = editor.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
this.remove_invalid(snapshot);
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
editor,
|
||||
project,
|
||||
history_store,
|
||||
prompt_store,
|
||||
mentions: HashMap::default(),
|
||||
_editor_subscription: editor_subscription,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +105,9 @@ impl MentionSet {
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_invalid(&mut self, snapshot: EditorSnapshot) {
|
||||
pub fn remove_invalid(&mut self, snapshot: &EditorSnapshot) {
|
||||
for (crease_id, crease) in snapshot.crease_snapshot.creases() {
|
||||
if !crease.range().start.is_valid(&snapshot.buffer_snapshot()) {
|
||||
if !crease.range().start.is_valid(snapshot.buffer_snapshot()) {
|
||||
self.mentions.remove(&crease_id);
|
||||
}
|
||||
}
|
||||
@@ -146,7 +129,11 @@ impl MentionSet {
|
||||
self.mentions.values().map(|(uri, _)| uri.clone()).collect()
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> impl Iterator<Item = (CreaseId, (MentionUri, MentionTask))> {
|
||||
pub fn set_mentions(&mut self, mentions: HashMap<CreaseId, (MentionUri, MentionTask)>) {
|
||||
self.mentions = mentions;
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) -> impl Iterator<Item = (CreaseId, (MentionUri, MentionTask))> {
|
||||
self.mentions.drain()
|
||||
}
|
||||
|
||||
@@ -157,6 +144,7 @@ impl MentionSet {
|
||||
content_len: usize,
|
||||
mention_uri: MentionUri,
|
||||
supports_images: bool,
|
||||
editor: Entity<Editor>,
|
||||
workspace: &Entity<Workspace>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
@@ -165,9 +153,7 @@ impl MentionSet {
|
||||
return Task::ready(());
|
||||
};
|
||||
|
||||
let snapshot = self
|
||||
.editor
|
||||
.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
let snapshot = editor.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
let Some(start_anchor) = snapshot.buffer_snapshot().as_singleton_anchor(start) else {
|
||||
return Task::ready(());
|
||||
};
|
||||
@@ -206,7 +192,7 @@ impl MentionSet {
|
||||
mention_uri.name().into(),
|
||||
IconName::Image.path().into(),
|
||||
Some(image),
|
||||
self.editor.clone(),
|
||||
editor.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -218,7 +204,7 @@ impl MentionSet {
|
||||
crease_text,
|
||||
mention_uri.icon_path(cx),
|
||||
None,
|
||||
self.editor.clone(),
|
||||
editor.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -265,7 +251,7 @@ impl MentionSet {
|
||||
drop(tx);
|
||||
if result.is_none() {
|
||||
this.update(cx, |this, cx| {
|
||||
this.editor.update(cx, |editor, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
// Remove mention
|
||||
editor.edit([(start_anchor..end_anchor, "")], cx);
|
||||
});
|
||||
@@ -405,6 +391,7 @@ impl MentionSet {
|
||||
&mut self,
|
||||
source_range: Range<text::Anchor>,
|
||||
selections: Vec<(Entity<Buffer>, Range<text::Anchor>, Range<usize>)>,
|
||||
editor: Entity<Editor>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
@@ -412,7 +399,7 @@ impl MentionSet {
|
||||
return;
|
||||
};
|
||||
|
||||
let snapshot = self.editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let Some(start) = snapshot.as_singleton_anchor(source_range.start) else {
|
||||
return;
|
||||
};
|
||||
@@ -443,10 +430,10 @@ impl MentionSet {
|
||||
selection_name(abs_path.as_deref(), &line_range).into(),
|
||||
uri.icon_path(cx),
|
||||
range,
|
||||
self.editor.downgrade(),
|
||||
editor.downgrade(),
|
||||
);
|
||||
|
||||
let crease_id = self.editor.update(cx, |editor, cx| {
|
||||
let crease_id = editor.update(cx, |editor, cx| {
|
||||
let crease_ids = editor.insert_creases(vec![crease.clone()], cx);
|
||||
editor.fold_creases(vec![crease], false, window, cx);
|
||||
crease_ids.first().copied().unwrap()
|
||||
@@ -471,7 +458,6 @@ impl MentionSet {
|
||||
// expected. We're leveraging `cx.on_next_frame` to wait 2 frames and
|
||||
// ensure that the layout has been recalculated so that the autoscroll
|
||||
// request actually shows the cursor's new position.
|
||||
let editor = self.editor.clone();
|
||||
cx.on_next_frame(window, move |_, window, cx| {
|
||||
cx.on_next_frame(window, move |_, _, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
|
||||
Reference in New Issue
Block a user