ep: Fix agent edits triggering edit predictions due to diagnostic refresh (#57832)

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A or Added/Fixed/Improved ...

---------

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
This commit is contained in:
Ben Kunkle 2026-05-28 09:27:55 -04:00 committed by GitHub
parent 8042408df4
commit b8c853a63d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 274 additions and 64 deletions

View file

@ -16,7 +16,9 @@ use gpui::{
};
use itertools::Itertools;
use language::language_settings::FormatOnSave;
use language::{Anchor, Buffer, BufferSnapshot, LanguageRegistry, Point, ToPoint, text_diff};
use language::{
Anchor, Buffer, BufferEditSource, BufferSnapshot, LanguageRegistry, Point, ToPoint, text_diff,
};
use markdown::{Markdown, MarkdownOptions};
pub use mention::*;
use project::lsp_store::{FormatTrigger, LspFormatTarget};
@ -2912,7 +2914,9 @@ impl AcpThread {
});
let format_on_save = buffer.update(cx, |buffer, cx| {
buffer.start_transaction();
buffer.edit(edits, None, cx);
buffer.end_transaction_with_source(BufferEditSource::Agent, cx);
let settings =
language::language_settings::LanguageSettings::for_buffer(buffer, cx);

View file

@ -12,7 +12,7 @@ use collections::HashSet;
use futures::{FutureExt, channel::oneshot};
use gpui::{App, AppContext, AsyncApp, Entity, Task, WeakEntity};
use language::language_settings::{self, FormatOnSave};
use language::{Buffer, BufferEvent, LanguageRegistry};
use language::{Buffer, BufferEditSource, BufferEvent, LanguageRegistry};
use language_model::LanguageModelToolResultContent;
use project::lsp_store::{FormatTrigger, LspFormatTarget};
use project::{AgentLocation, Project, ProjectPath};
@ -975,7 +975,9 @@ fn agent_edit_buffer<I, S, T>(
{
cx.update(|cx| {
buffer.update(cx, |buffer, cx| {
buffer.start_transaction();
buffer.edit(edits, None, cx);
buffer.end_transaction_with_source(BufferEditSource::Agent, cx);
});
action_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
});

View file

@ -12,7 +12,9 @@ use futures::{
stream::BoxStream,
};
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task};
use language::{Buffer, IndentKind, LanguageName, Point, TransactionId, line_diff};
use language::{
Buffer, BufferEditSource, IndentKind, LanguageName, Point, TransactionId, line_diff,
};
use language_model::{
CompletionIntent, LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent,
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
@ -978,7 +980,7 @@ impl CodegenAlternative {
buffer.finalize_last_transaction(cx);
buffer.start_transaction(cx);
buffer.edit(edits, None, cx);
buffer.end_transaction(cx)
buffer.end_transaction_with_source(BufferEditSource::Agent, cx)
});
if let Some(transaction) = transaction {

View file

@ -38,9 +38,9 @@ use gpui::{
};
use heapless::Vec as ArrayVec;
use language::{
Anchor, Buffer, BufferSnapshot, EditPredictionPromptFormat, EditPredictionsMode, EditPreview,
File, OffsetRangeExt, Point, TextBufferSnapshot, ToOffset, ToPoint,
language_settings::all_language_settings,
Anchor, Buffer, BufferEditSource, BufferSnapshot, EditPredictionPromptFormat,
EditPredictionsMode, EditPreview, File, OffsetRangeExt, Point, TextBufferSnapshot, ToOffset,
ToPoint, language_settings::all_language_settings,
};
use project::{DisableAiSettings, Project, ProjectPath, WorktreeId};
use release_channel::AppVersion;
@ -324,6 +324,7 @@ struct ProjectState {
recent_paths: VecDeque<ProjectPath>,
registered_buffers: HashMap<gpui::EntityId, RegisteredBuffer>,
current_prediction: Option<CurrentEditPrediction>,
last_edit_source: Option<BufferEditSource>,
next_pending_prediction_id: usize,
pending_predictions: ArrayVec<PendingPrediction, 2, u8>,
debug_tx: Option<mpsc::UnboundedSender<DebugEvent>>,
@ -1212,6 +1213,7 @@ impl EditPredictionStore {
debug_tx: None,
registered_buffers: HashMap::default(),
current_prediction: None,
last_edit_source: None,
cancelled_predictions: HashSet::default(),
pending_predictions: ArrayVec::new(),
next_pending_prediction_id: 0,
@ -1315,6 +1317,9 @@ impl EditPredictionStore {
}
// TODO [zeta2] init with recent paths
match event {
project::Event::BufferEdited { source } => {
self.get_or_init_project(&project, cx).last_edit_source = Some(*source);
}
project::Event::ActiveEntryChanged(Some(active_entry_id)) => {
let Some(project_state) = self.projects.get_mut(&project.entity_id()) else {
return;
@ -1332,6 +1337,15 @@ impl EditPredictionStore {
}
}
project::Event::DiagnosticsUpdated { .. } => {
if self
.projects
.get(&project.entity_id())
.and_then(|project_state| project_state.last_edit_source)
== Some(BufferEditSource::Agent)
{
return;
}
if cx.has_flag::<EditPredictionJumpsFeatureFlag>() {
self.refresh_prediction_from_diagnostics(
project,
@ -1391,11 +1405,17 @@ impl EditPredictionStore {
cx.subscribe(buffer, {
let project = project.downgrade();
move |this, buffer, event, cx| {
if let language::BufferEvent::Edited { is_local } = event
if let language::BufferEvent::Edited { source } = event
&& let Some(project) = project.upgrade()
{
let project_state = this.get_or_init_project(&project, cx);
project_state.last_edit_source = Some(*source);
this.report_changes_for_buffer(
&buffer, &project, false, *is_local, cx,
&buffer,
&project,
false,
source.is_local(),
cx,
);
}
}

View file

@ -26,8 +26,8 @@ use gpui::{
};
use indoc::indoc;
use language::{
Anchor, Buffer, Capability, CursorShape, Diagnostic, DiagnosticEntry, DiagnosticSet,
DiagnosticSeverity, Operation, Point, Selection, SelectionGoal,
Anchor, Buffer, BufferEditSource, Capability, CursorShape, Diagnostic, DiagnosticEntry,
DiagnosticSet, DiagnosticSeverity, Operation, Point, Selection, SelectionGoal,
};
use lsp::LanguageServerId;
@ -352,6 +352,70 @@ async fn test_diagnostics_refresh_suppressed_while_following(cx: &mut TestAppCon
});
}
#[gpui::test]
async fn test_diagnostics_refresh_suppressed_after_agent_edit(cx: &mut TestAppContext) {
let (ep_store, mut requests) = init_test_with_fake_client(cx);
cx.update(|cx| {
cx.update_flags(
false,
vec![EditPredictionJumpsFeatureFlag::NAME.to_string()],
);
});
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
"/root",
json!({
"1.txt": "Hello!\nHow\nBye\n",
"2.txt": "Hola!\nComo\nAdios\n"
}),
)
.await;
let project = Project::test(fs, vec![path!("/root").as_ref()], cx).await;
let buffer = project
.update(cx, |project, cx| {
let path = project.find_project_path(path!("root/1.txt"), cx).unwrap();
project.set_active_path(Some(path.clone()), cx);
project.open_buffer(path, cx)
})
.await
.unwrap();
ep_store.update(cx, |ep_store, cx| {
ep_store.register_project(&project, cx);
ep_store.register_buffer(&buffer, &project, cx);
});
buffer.update(cx, |buffer, cx| {
buffer.start_transaction();
buffer.edit([(Point::new(1, 3)..Point::new(1, 3), "!")], None, cx);
buffer.end_transaction_with_source(BufferEditSource::Agent, cx);
});
cx.run_until_parked();
update_test_diagnostics(&project, path!("/root/2.txt"), "Sentence is incomplete", cx);
cx.run_until_parked();
assert_no_predict_request_ready(&mut requests.predict);
buffer.update(cx, |buffer, cx| {
buffer.edit([(Point::new(1, 4)..Point::new(1, 4), "?")], None, cx);
});
cx.run_until_parked();
update_test_diagnostics(
&project,
path!("/root/2.txt"),
"Sentence is still incomplete",
cx,
);
let (_request, respond_tx) = requests.predict.next().await.unwrap();
respond_tx.send(empty_response()).unwrap();
cx.run_until_parked();
}
#[gpui::test]
async fn test_simple_request(cx: &mut TestAppContext) {
let (ep_store, mut requests) = init_test_with_fake_client(cx);
@ -2498,6 +2562,39 @@ fn assert_no_predict_request_ready(
}
}
fn update_test_diagnostics(
project: &Entity<Project>,
path: &str,
message: &str,
cx: &mut TestAppContext,
) {
let diagnostic = lsp::Diagnostic {
range: lsp::Range::new(lsp::Position::new(1, 1), lsp::Position::new(1, 5)),
severity: Some(lsp::DiagnosticSeverity::ERROR),
message: message.to_string(),
..Default::default()
};
project.update(cx, |project, cx| {
project.lsp_store().update(cx, |lsp_store, cx| {
lsp_store
.update_diagnostics(
LanguageServerId(0),
lsp::PublishDiagnosticsParams {
uri: lsp::Uri::from_file_path(path).unwrap(),
diagnostics: vec![diagnostic],
version: None,
},
None,
language::DiagnosticSourceKind::Pushed,
&[],
cx,
)
.unwrap();
});
});
}
struct RequestChannels {
predict: mpsc::UnboundedReceiver<(
PredictEditsV3Request,

View file

@ -9231,7 +9231,7 @@ impl Editor {
match event {
multi_buffer::Event::Edited {
edited_buffer,
is_local,
source,
} => {
self.scrollbar_marker_state.dirty = true;
self.active_indent_guides_state.dirty = true;
@ -9242,7 +9242,7 @@ impl Editor {
self.refresh_matching_bracket_highlights(&snapshot, cx);
self.refresh_outline_symbols_at_cursor(cx);
self.refresh_sticky_headers(&snapshot, cx);
if *is_local && self.has_active_edit_prediction() {
if source.is_local() && self.has_active_edit_prediction() {
self.update_visible_edit_prediction(window, cx);
}

View file

@ -297,6 +297,19 @@ pub enum Operation {
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BufferEditSource {
User,
Agent,
Remote,
}
impl BufferEditSource {
pub fn is_local(self) -> bool {
!matches!(self, Self::Remote)
}
}
/// An event that occurs in a buffer.
#[derive(Clone, Debug, PartialEq)]
pub enum BufferEvent {
@ -307,7 +320,7 @@ pub enum BufferEvent {
is_local: bool,
},
/// The buffer was edited.
Edited { is_local: bool },
Edited { source: BufferEditSource },
/// The buffer's `dirty` bit changed.
DirtyChanged,
/// The buffer was saved.
@ -2433,6 +2446,14 @@ impl Buffer {
self.end_transaction_at(Instant::now(), cx)
}
pub fn end_transaction_with_source(
&mut self,
source: BufferEditSource,
cx: &mut Context<Self>,
) -> Option<TransactionId> {
self.end_transaction_at_internal(Instant::now(), source, cx)
}
/// Terminates the current transaction, providing the current time. Subsequent transactions
/// that occur within a short period of time will be grouped together. This
/// is controlled by the buffer's undo grouping duration.
@ -2440,6 +2461,15 @@ impl Buffer {
&mut self,
now: Instant,
cx: &mut Context<Self>,
) -> Option<TransactionId> {
self.end_transaction_at_internal(now, BufferEditSource::User, cx)
}
fn end_transaction_at_internal(
&mut self,
now: Instant,
source: BufferEditSource,
cx: &mut Context<Self>,
) -> Option<TransactionId> {
assert!(self.transaction_depth > 0);
self.transaction_depth -= 1;
@ -2449,7 +2479,7 @@ impl Buffer {
false
};
if let Some((transaction_id, start_version)) = self.text.end_transaction_at(now) {
self.did_edit(&start_version, was_dirty, true, cx);
self.did_edit(&start_version, was_dirty, source, cx);
Some(transaction_id)
} else {
None
@ -2844,7 +2874,7 @@ impl Buffer {
&mut self,
old_version: &clock::Global,
was_dirty: bool,
is_local: bool,
source: BufferEditSource,
cx: &mut Context<Self>,
) {
self.was_changed();
@ -2854,7 +2884,7 @@ impl Buffer {
}
self.reparse(cx, true);
cx.emit(BufferEvent::Edited { is_local });
cx.emit(BufferEvent::Edited { source });
let is_dirty = self.is_dirty();
if was_dirty != is_dirty {
cx.emit(BufferEvent::DirtyChanged);
@ -2976,7 +3006,7 @@ impl Buffer {
self.text.apply_ops(buffer_ops);
self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops(cx);
self.did_edit(&old_version, was_dirty, false, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::Remote, cx);
// Notify independently of whether the buffer was edited as the operations could include a
// selection update.
cx.notify();
@ -3131,7 +3161,7 @@ impl Buffer {
if let Some((transaction_id, operation)) = self.text.undo() {
self.send_operation(Operation::Buffer(operation), true, cx);
self.did_edit(&old_version, was_dirty, true, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
self.restore_encoding_for_transaction(transaction_id, was_dirty);
Some(transaction_id)
} else {
@ -3149,7 +3179,7 @@ impl Buffer {
let old_version = self.version.clone();
if let Some(operation) = self.text.undo_transaction(transaction_id) {
self.send_operation(Operation::Buffer(operation), true, cx);
self.did_edit(&old_version, was_dirty, true, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
true
} else {
false
@ -3171,7 +3201,7 @@ impl Buffer {
self.send_operation(Operation::Buffer(operation), true, cx);
}
if undone {
self.did_edit(&old_version, was_dirty, true, cx)
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx)
}
undone
}
@ -3181,7 +3211,7 @@ impl Buffer {
let operation = self.text.undo_operations(counts);
let old_version = self.version.clone();
self.send_operation(Operation::Buffer(operation), true, cx);
self.did_edit(&old_version, was_dirty, true, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
}
/// Manually redoes a specific transaction in the buffer's redo history.
@ -3191,7 +3221,7 @@ impl Buffer {
if let Some((transaction_id, operation)) = self.text.redo() {
self.send_operation(Operation::Buffer(operation), true, cx);
self.did_edit(&old_version, was_dirty, true, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
self.restore_encoding_for_transaction(transaction_id, was_dirty);
Some(transaction_id)
} else {
@ -3232,7 +3262,7 @@ impl Buffer {
self.send_operation(Operation::Buffer(operation), true, cx);
}
if redone {
self.did_edit(&old_version, was_dirty, true, cx)
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx)
}
redone
}
@ -3342,7 +3372,7 @@ impl Buffer {
if !ops.is_empty() {
for op in ops {
self.send_operation(Operation::Buffer(op), true, cx);
self.did_edit(&old_version, was_dirty, true, cx);
self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
}
}
}

View file

@ -460,16 +460,24 @@ fn test_edit_events(cx: &mut gpui::App) {
assert_eq!(
mem::take(&mut *buffer_1_events.lock()),
vec![
BufferEvent::Edited { is_local: true },
BufferEvent::Edited {
source: BufferEditSource::User
},
BufferEvent::DirtyChanged,
BufferEvent::Edited { is_local: true },
BufferEvent::Edited { is_local: true },
BufferEvent::Edited {
source: BufferEditSource::User
},
BufferEvent::Edited {
source: BufferEditSource::User
},
]
);
assert_eq!(
mem::take(&mut *buffer_2_events.lock()),
vec![
BufferEvent::Edited { is_local: false },
BufferEvent::Edited {
source: BufferEditSource::Remote
},
BufferEvent::DirtyChanged
]
);
@ -487,14 +495,18 @@ fn test_edit_events(cx: &mut gpui::App) {
assert_eq!(
mem::take(&mut *buffer_1_events.lock()),
vec![
BufferEvent::Edited { is_local: true },
BufferEvent::Edited {
source: BufferEditSource::User
},
BufferEvent::DirtyChanged,
]
);
assert_eq!(
mem::take(&mut *buffer_2_events.lock()),
vec![
BufferEvent::Edited { is_local: false },
BufferEvent::Edited {
source: BufferEditSource::Remote
},
BufferEvent::DirtyChanged
]
);

View file

@ -20,11 +20,11 @@ use futures_lite::future::yield_now;
use gpui::{App, Context, Entity, EventEmitter};
use itertools::Itertools;
use language::{
AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,
CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntryRef, File, IndentGuideSettings,
IndentSize, Language, LanguageAwareStyling, LanguageScope, OffsetRangeExt, OffsetUtf16,
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _,
ToPoint as _, TransactionId, TreeSitterOptions, Unclipped,
AutoindentMode, Buffer, BufferChunks, BufferEditSource, BufferRow, BufferSnapshot, Capability,
CharClassifier, CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntryRef, File,
IndentGuideSettings, IndentSize, Language, LanguageAwareStyling, LanguageScope, OffsetRangeExt,
OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject,
ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions, Unclipped,
language_settings::{AllLanguageSettings, LanguageSettings},
};
@ -110,7 +110,7 @@ pub enum Event {
DiffHunksToggled,
Edited {
edited_buffer: Option<Entity<Buffer>>,
is_local: bool,
source: BufferEditSource,
},
TransactionUndone {
transaction_id: TransactionId,
@ -1828,7 +1828,7 @@ impl MultiBuffer {
}
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
cx.emit(Event::BuffersRemoved { removed_buffer_ids });
cx.notify();
@ -1952,9 +1952,9 @@ impl MultiBuffer {
use language::BufferEvent;
let buffer_id = buffer.read(cx).remote_id();
cx.emit(match event {
&BufferEvent::Edited { is_local } => Event::Edited {
&BufferEvent::Edited { source } => Event::Edited {
edited_buffer: Some(buffer),
is_local,
source,
},
BufferEvent::DirtyChanged => Event::DirtyChanged,
BufferEvent::Saved => Event::Saved,
@ -2044,7 +2044,7 @@ impl MultiBuffer {
}
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
}
@ -2090,7 +2090,7 @@ impl MultiBuffer {
}
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
}
@ -2313,7 +2313,7 @@ impl MultiBuffer {
cx.emit(Event::DiffHunksToggled);
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
}
@ -2449,7 +2449,7 @@ impl MultiBuffer {
cx.emit(Event::DiffHunksToggled);
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
}
@ -3102,7 +3102,7 @@ impl MultiBuffer {
cx.emit(Event::DiffHunksToggled);
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
}
}

View file

@ -192,15 +192,15 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
&[
Event::Edited {
edited_buffer: None,
is_local: true,
source: language::BufferEditSource::User,
},
Event::Edited {
edited_buffer: None,
is_local: true,
source: language::BufferEditSource::User,
},
Event::Edited {
edited_buffer: None,
is_local: true,
source: language::BufferEditSource::User,
}
]
);

View file

@ -2,7 +2,7 @@ use std::{ops::Range, rc::Rc, sync::Arc};
use gpui::{App, AppContext, Context, Entity};
use itertools::Itertools;
use language::{Buffer, BufferSnapshot};
use language::{Buffer, BufferEditSource, BufferSnapshot};
use rope::Point;
use sum_tree::{Dimensions, SumTree};
use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
@ -603,7 +603,7 @@ impl MultiBuffer {
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
cx.emit(Event::BufferRangesUpdated {
buffer,
@ -687,7 +687,7 @@ impl MultiBuffer {
cx.emit(Event::Edited {
edited_buffer: None,
is_local: true,
source: BufferEditSource::User,
});
cx.notify();
}

View file

@ -1,5 +1,5 @@
use gpui::{App, Context, Entity};
use language::{self, Buffer, TransactionId};
use language::{self, Buffer, BufferEditSource, TransactionId};
use std::{
collections::HashMap,
ops::Range,
@ -288,6 +288,35 @@ impl MultiBuffer {
self.end_transaction_at(Instant::now(), cx)
}
pub fn end_transaction_with_source(
&mut self,
source: BufferEditSource,
cx: &mut Context<Self>,
) -> Option<TransactionId> {
let now = Instant::now();
if let Some(buffer) = self.as_singleton() {
return buffer.update(cx, |buffer, cx| {
buffer.end_transaction_with_source(source, cx)
});
}
let mut buffer_transactions = HashMap::default();
for BufferState { buffer, .. } in self.buffers.values() {
if let Some(transaction_id) = buffer.update(cx, |buffer, cx| {
buffer.end_transaction_with_source(source, cx)
}) {
buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id);
}
}
if self.history.end_transaction(now, buffer_transactions) {
let transaction_id = self.history.group().unwrap();
Some(transaction_id)
} else {
None
}
}
pub fn end_transaction_at(
&mut self,
now: Instant,

View file

@ -89,9 +89,9 @@ use gpui::{
Task, TaskExt, WeakEntity, Window,
};
use language::{
Buffer, BufferEvent, Capability, CodeLabel, CursorShape, DiskState, Language, LanguageName,
LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainMetadata,
ToolchainScope, Transaction, Unclipped, language_settings::InlayHintKind,
Buffer, BufferEditSource, BufferEvent, Capability, CodeLabel, CursorShape, DiskState, Language,
LanguageName, LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain,
ToolchainMetadata, ToolchainScope, Transaction, Unclipped, language_settings::InlayHintKind,
proto::split_operations,
};
use lsp::{
@ -410,7 +410,9 @@ pub enum Event {
EntryRenamed(ProjectTransaction, ProjectPath, PathBuf),
WorkspaceEditApplied(ProjectTransaction),
AgentLocationChanged,
BufferEdited,
BufferEdited {
source: BufferEditSource,
},
}
pub struct AgentLocationChanged;
@ -3810,8 +3812,8 @@ impl Project {
self.request_buffer_diff_recalculation(&buffer, cx);
}
if matches!(event, BufferEvent::Edited { .. }) {
cx.emit(Event::BufferEdited);
if let BufferEvent::Edited { source } = event {
cx.emit(Event::BufferEdited { source: *source });
}
let buffer_id = buffer.read(cx).remote_id();

View file

@ -6073,7 +6073,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
assert_eq!(
*events.lock(),
&[
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
language::BufferEvent::DirtyChanged
]
);
@ -6102,9 +6104,13 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
assert_eq!(
*events.lock(),
&[
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
language::BufferEvent::DirtyChanged,
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
],
);
events.lock().clear();
@ -6119,7 +6125,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
assert_eq!(
*events.lock(),
&[
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
language::BufferEvent::DirtyChanged
]
);
@ -6159,7 +6167,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
assert_eq!(
mem::take(&mut *events.lock()),
&[
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
language::BufferEvent::DirtyChanged
]
);
@ -6174,7 +6184,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
assert_eq!(
*events.lock(),
&[
language::BufferEvent::Edited { is_local: true },
language::BufferEvent::Edited {
source: language::BufferEditSource::User
},
language::BufferEvent::DirtyChanged
]
);