Move buffer change reporting to a background task

This commit is contained in:
Antonio Scandurra 2023-04-20 11:57:37 +02:00
parent 4151bd39da
commit df71a9cfae

View file

@ -5,7 +5,7 @@ use anyhow::{anyhow, Context, Result};
use async_compression::futures::bufread::GzipDecoder; use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive; use async_tar::Archive;
use collections::HashMap; use collections::HashMap;
use futures::{future::Shared, Future, FutureExt, TryFutureExt}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{ use gpui::{
actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle, actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle,
}; };
@ -171,56 +171,97 @@ impl Status {
} }
struct RegisteredBuffer { struct RegisteredBuffer {
id: usize,
uri: lsp::Url, uri: lsp::Url,
language_id: String, language_id: String,
snapshot: BufferSnapshot, snapshot: BufferSnapshot,
snapshot_version: i32, snapshot_version: i32,
_subscriptions: [gpui::Subscription; 2], _subscriptions: [gpui::Subscription; 2],
pending_buffer_change: Task<Option<()>>,
} }
impl RegisteredBuffer { impl RegisteredBuffer {
fn report_changes( fn report_changes(
&mut self, &mut self,
buffer: &ModelHandle<Buffer>, buffer: &ModelHandle<Buffer>,
server: &LanguageServer, cx: &mut ModelContext<Copilot>,
cx: &AppContext, ) -> oneshot::Receiver<(i32, BufferSnapshot)> {
) -> Result<()> { let id = self.id;
let buffer = buffer.read(cx); let (done_tx, done_rx) = oneshot::channel();
let new_snapshot = buffer.snapshot();
let content_changes = buffer
.edits_since::<(PointUtf16, usize)>(self.snapshot.version())
.map(|edit| {
let edit_start = edit.new.start.0;
let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
let new_text = new_snapshot
.text_for_range(edit.new.start.1..edit.new.end.1)
.collect();
lsp::TextDocumentContentChangeEvent {
range: Some(lsp::Range::new(
point_to_lsp(edit_start),
point_to_lsp(edit_end),
)),
range_length: None,
text: new_text,
}
})
.collect::<Vec<_>>();
if !content_changes.is_empty() { if buffer.read(cx).version() == self.snapshot.version {
self.snapshot_version += 1; let _ = done_tx.send((self.snapshot_version, self.snapshot.clone()));
self.snapshot = new_snapshot; } else {
server.notify::<lsp::notification::DidChangeTextDocument>( let buffer = buffer.downgrade();
lsp::DidChangeTextDocumentParams { let prev_pending_change =
text_document: lsp::VersionedTextDocumentIdentifier::new( mem::replace(&mut self.pending_buffer_change, Task::ready(None));
self.uri.clone(), self.pending_buffer_change = cx.spawn_weak(|copilot, mut cx| async move {
self.snapshot_version, prev_pending_change.await;
),
content_changes, let old_version = copilot.upgrade(&cx)?.update(&mut cx, |copilot, _| {
}, let server = copilot.server.as_authenticated().log_err()?;
)?; let buffer = server.registered_buffers.get_mut(&id)?;
Some(buffer.snapshot.version.clone())
})?;
let new_snapshot = buffer
.upgrade(&cx)?
.read_with(&cx, |buffer, _| buffer.snapshot());
let content_changes = cx
.background()
.spawn({
let new_snapshot = new_snapshot.clone();
async move {
new_snapshot
.edits_since::<(PointUtf16, usize)>(&old_version)
.map(|edit| {
let edit_start = edit.new.start.0;
let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
let new_text = new_snapshot
.text_for_range(edit.new.start.1..edit.new.end.1)
.collect();
lsp::TextDocumentContentChangeEvent {
range: Some(lsp::Range::new(
point_to_lsp(edit_start),
point_to_lsp(edit_end),
)),
range_length: None,
text: new_text,
}
})
.collect::<Vec<_>>()
}
})
.await;
copilot.upgrade(&cx)?.update(&mut cx, |copilot, _| {
let server = copilot.server.as_authenticated().log_err()?;
let buffer = server.registered_buffers.get_mut(&id)?;
if !content_changes.is_empty() {
buffer.snapshot_version += 1;
buffer.snapshot = new_snapshot;
server
.lsp
.notify::<lsp::notification::DidChangeTextDocument>(
lsp::DidChangeTextDocumentParams {
text_document: lsp::VersionedTextDocumentIdentifier::new(
buffer.uri.clone(),
buffer.snapshot_version,
),
content_changes,
},
)
.log_err();
}
let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone()));
Some(())
})?;
Some(())
});
} }
Ok(()) done_rx
} }
} }
@ -567,10 +608,12 @@ impl Copilot {
.log_err(); .log_err();
RegisteredBuffer { RegisteredBuffer {
id: buffer_id,
uri, uri,
language_id, language_id,
snapshot, snapshot,
snapshot_version: 0, snapshot_version: 0,
pending_buffer_change: Task::ready(Some(())),
_subscriptions: [ _subscriptions: [
cx.subscribe(buffer, |this, buffer, event, cx| { cx.subscribe(buffer, |this, buffer, event, cx| {
this.handle_buffer_event(buffer, event, cx).log_err(); this.handle_buffer_event(buffer, event, cx).log_err();
@ -595,7 +638,7 @@ impl Copilot {
if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) { if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) {
match event { match event {
language::Event::Edited => { language::Event::Edited => {
registered_buffer.report_changes(&buffer, &server.lsp, cx)?; let _ = registered_buffer.report_changes(&buffer, cx);
} }
language::Event::Saved => { language::Event::Saved => {
server server
@ -750,38 +793,38 @@ impl Copilot {
Ok(server) => server, Ok(server) => server,
Err(error) => return Task::ready(Err(error)), Err(error) => return Task::ready(Err(error)),
}; };
let lsp = server.lsp.clone();
let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap(); let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap();
if let Err(error) = registered_buffer.report_changes(buffer, &server.lsp, cx) { let snapshot = registered_buffer.report_changes(buffer, cx);
return Task::ready(Err(error)); let buffer = buffer.read(cx);
}
let uri = registered_buffer.uri.clone(); let uri = registered_buffer.uri.clone();
let snapshot = registered_buffer.snapshot.clone();
let version = registered_buffer.snapshot_version;
let settings = cx.global::<Settings>(); let settings = cx.global::<Settings>();
let position = position.to_point_utf16(&snapshot); let position = position.to_point_utf16(buffer);
let language = snapshot.language_at(position); let language = buffer.language_at(position);
let language_name = language.map(|language| language.name()); let language_name = language.map(|language| language.name());
let language_name = language_name.as_deref(); let language_name = language_name.as_deref();
let tab_size = settings.tab_size(language_name); let tab_size = settings.tab_size(language_name);
let hard_tabs = settings.hard_tabs(language_name); let hard_tabs = settings.hard_tabs(language_name);
let relative_path = snapshot let relative_path = buffer
.file() .file()
.map(|file| file.path().to_path_buf()) .map(|file| file.path().to_path_buf())
.unwrap_or_default(); .unwrap_or_default();
let request = server.lsp.request::<R>(request::GetCompletionsParams {
doc: request::GetCompletionsDocument { cx.foreground().spawn(async move {
uri, let (version, snapshot) = snapshot.await?;
tab_size: tab_size.into(), let result = lsp
indent_size: 1, .request::<R>(request::GetCompletionsParams {
insert_spaces: !hard_tabs, doc: request::GetCompletionsDocument {
relative_path: relative_path.to_string_lossy().into(), uri,
position: point_to_lsp(position), tab_size: tab_size.into(),
version: version.try_into().unwrap(), indent_size: 1,
}, insert_spaces: !hard_tabs,
}); relative_path: relative_path.to_string_lossy().into(),
cx.background().spawn(async move { position: point_to_lsp(position),
let result = request.await?; version: version.try_into().unwrap(),
},
})
.await?;
let completions = result let completions = result
.completions .completions
.into_iter() .into_iter()