Detailed changes
@@ -1291,6 +1291,13 @@ impl Buffer {
self.text.wait_for_edits(edit_ids)
}
+ pub fn wait_for_anchors<'a>(
+ &mut self,
+ anchors: impl IntoIterator<Item = &'a Anchor>,
+ ) -> impl Future<Output = ()> {
+ self.text.wait_for_anchors(anchors)
+ }
+
pub fn wait_for_version(&mut self, version: clock::Global) -> impl Future<Output = ()> {
self.text.wait_for_version(version)
}
@@ -31,10 +31,11 @@ pub(crate) trait LspCommand: 'static + Sized {
) -> Result<Self::Response>;
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
- fn from_proto(
+ async fn from_proto(
message: Self::ProtoRequest,
- project: &mut Project,
- buffer: &Buffer,
+ project: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ cx: AsyncAppContext,
) -> Result<Self>;
fn response_to_proto(
response: Self::Response,
@@ -121,19 +122,28 @@ impl LspCommand for PrepareRename {
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
+ version: (&buffer.version()).into(),
}
}
- fn from_proto(message: proto::PrepareRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
+ async fn from_proto(
+ message: proto::PrepareRename,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
+ ) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- if !buffer.can_resolve(&position) {
- Err(anyhow!("cannot resolve position"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(message.version.into())
+ })
+ .await;
+
Ok(Self {
- position: position.to_point_utf16(buffer),
+ position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
@@ -241,19 +251,27 @@ impl LspCommand for PerformRename {
&buffer.anchor_before(self.position),
)),
new_name: self.new_name.clone(),
+ version: (&buffer.version()).into(),
}
}
- fn from_proto(message: proto::PerformRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
+ async fn from_proto(
+ message: proto::PerformRename,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
+ ) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- if !buffer.can_resolve(&position) {
- Err(anyhow!("cannot resolve position"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(message.version.into())
+ })
+ .await;
Ok(Self {
- position: position.to_point_utf16(buffer),
+ position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
new_name: message.new_name,
push_to_history: false,
})
@@ -385,19 +403,27 @@ impl LspCommand for GetDefinition {
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
+ version: (&buffer.version()).into(),
}
}
- fn from_proto(message: proto::GetDefinition, _: &mut Project, buffer: &Buffer) -> Result<Self> {
+ async fn from_proto(
+ message: proto::GetDefinition,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
+ ) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- if !buffer.can_resolve(&position) {
- Err(anyhow!("cannot resolve position"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(message.version.into())
+ })
+ .await;
Ok(Self {
- position: position.to_point_utf16(buffer),
+ position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
@@ -443,6 +469,9 @@ impl LspCommand for GetDefinition {
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
+ buffer
+ .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
+ .await;
locations.push(Location {
buffer,
range: start..end,
@@ -533,19 +562,27 @@ impl LspCommand for GetReferences {
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
+ version: (&buffer.version()).into(),
}
}
- fn from_proto(message: proto::GetReferences, _: &mut Project, buffer: &Buffer) -> Result<Self> {
+ async fn from_proto(
+ message: proto::GetReferences,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
+ ) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- if !buffer.can_resolve(&position) {
- Err(anyhow!("cannot resolve position"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(message.version.into())
+ })
+ .await;
Ok(Self {
- position: position.to_point_utf16(buffer),
+ position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
@@ -591,6 +628,9 @@ impl LspCommand for GetReferences {
.end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
+ target_buffer
+ .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
+ .await;
locations.push(Location {
buffer: target_buffer,
range: start..end,
@@ -658,23 +698,27 @@ impl LspCommand for GetDocumentHighlights {
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
+ version: (&buffer.version()).into(),
}
}
- fn from_proto(
+ async fn from_proto(
message: proto::GetDocumentHighlights,
- _: &mut Project,
- buffer: &Buffer,
+ _: ModelHandle<Project>,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
- if !buffer.can_resolve(&position) {
- Err(anyhow!("cannot resolve position"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(message.version.into())
+ })
+ .await;
Ok(Self {
- position: position.to_point_utf16(buffer),
+ position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
@@ -705,33 +749,34 @@ impl LspCommand for GetDocumentHighlights {
self,
message: proto::GetDocumentHighlightsResponse,
_: ModelHandle<Project>,
- _: ModelHandle<Buffer>,
- _: AsyncAppContext,
+ buffer: ModelHandle<Buffer>,
+ mut cx: AsyncAppContext,
) -> Result<Vec<DocumentHighlight>> {
- Ok(message
- .highlights
- .into_iter()
- .map(|highlight| {
- let start = highlight
- .start
- .and_then(deserialize_anchor)
- .ok_or_else(|| anyhow!("missing target start"))?;
- let end = highlight
- .end
- .and_then(deserialize_anchor)
- .ok_or_else(|| anyhow!("missing target end"))?;
- let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
- Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
- Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
- Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
- None => DocumentHighlightKind::TEXT,
- };
- Ok(DocumentHighlight {
- range: start..end,
- kind,
- })
- })
- .collect::<Result<Vec<_>>>()?)
+ let mut highlights = Vec::new();
+ for highlight in message.highlights {
+ let start = highlight
+ .start
+ .and_then(deserialize_anchor)
+ .ok_or_else(|| anyhow!("missing target start"))?;
+ let end = highlight
+ .end
+ .and_then(deserialize_anchor)
+ .ok_or_else(|| anyhow!("missing target end"))?;
+ buffer
+ .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
+ .await;
+ let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
+ Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
+ Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
+ Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
+ None => DocumentHighlightKind::TEXT,
+ };
+ highlights.push(DocumentHighlight {
+ range: start..end,
+ kind,
+ });
+ }
+ Ok(highlights)
}
fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
@@ -1815,6 +1815,7 @@ impl Project {
})
} else if let Some(project_id) = self.remote_id() {
let rpc = self.client.clone();
+ let version = buffer.version();
cx.spawn_weak(|_, mut cx| async move {
let response = rpc
.request(proto::GetCodeActions {
@@ -1822,6 +1823,7 @@ impl Project {
buffer_id,
start: Some(language::proto::serialize_anchor(&range.start)),
end: Some(language::proto::serialize_anchor(&range.end)),
+ version: (&version).into(),
})
.await?;
@@ -2840,13 +2842,11 @@ impl Project {
.ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
Ok::<_, anyhow::Error>((project_id, buffer))
})?;
-
- if !buffer
- .read_with(&cx, |buffer, _| buffer.version())
- .observed_all(&requested_version)
- {
- Err(anyhow!("save request depends on unreceived edits"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(requested_version)
+ })
+ .await;
let (saved_version, mtime) = buffer.update(&mut cx, |buffer, cx| buffer.save(cx)).await?;
Ok(proto::BufferSaved {
@@ -2904,12 +2904,9 @@ impl Project {
.map(|buffer| buffer.upgrade(cx).unwrap())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
})?;
- if !buffer
- .read_with(&cx, |buffer, _| buffer.version())
- .observed_all(&version)
- {
- Err(anyhow!("completion request depends on unreceived edits"))?;
- }
+ buffer
+ .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
+ .await;
let version = buffer.read_with(&cx, |buffer, _| buffer.version());
let completions = this
.update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
@@ -2979,10 +2976,13 @@ impl Project {
.map(|buffer| buffer.upgrade(cx).unwrap())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
})?;
+ buffer
+ .update(&mut cx, |buffer, _| {
+ buffer.wait_for_version(envelope.payload.version.into())
+ })
+ .await;
+
let version = buffer.read_with(&cx, |buffer, _| buffer.version());
- if !version.observed(start.timestamp) || !version.observed(end.timestamp) {
- Err(anyhow!("code action request references unreceived edits"))?;
- }
let code_actions = this.update(&mut cx, |this, cx| {
Ok::<_, anyhow::Error>(this.code_actions(&buffer, start..end, cx))
})?;
@@ -3038,19 +3038,26 @@ impl Project {
<T::LspRequest as lsp::request::Request>::Result: Send,
{
let sender_id = envelope.original_sender_id()?;
- let (request, buffer_version) = this.update(&mut cx, |this, cx| {
- let buffer_id = T::buffer_id_from_proto(&envelope.payload);
- let buffer_handle = this
- .opened_buffers
+ let buffer_id = T::buffer_id_from_proto(&envelope.payload);
+ let buffer_handle = this.read_with(&cx, |this, _| {
+ this.opened_buffers
.get(&buffer_id)
- .map(|buffer| buffer.upgrade(cx).unwrap())
- .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?;
- let buffer = buffer_handle.read(cx);
- let buffer_version = buffer.version();
- let request = T::from_proto(envelope.payload, this, buffer)?;
- Ok::<_, anyhow::Error>((this.request_lsp(buffer_handle, request, cx), buffer_version))
+ .map(|buffer| buffer.upgrade(&cx).unwrap())
+ .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))
})?;
- let response = request.await?;
+ let request = T::from_proto(
+ envelope.payload,
+ this.clone(),
+ buffer_handle.clone(),
+ cx.clone(),
+ )
+ .await?;
+ let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version());
+ let response = this
+ .update(&mut cx, |this, cx| {
+ this.request_lsp(buffer_handle, request, cx)
+ })
+ .await?;
this.update(&mut cx, |this, cx| {
Ok(T::response_to_proto(
response,
@@ -164,6 +164,7 @@ message GetDefinition {
uint64 project_id = 1;
uint64 buffer_id = 2;
Anchor position = 3;
+ repeated VectorClockEntry version = 4;
}
message GetDefinitionResponse {
@@ -174,6 +175,7 @@ message GetReferences {
uint64 project_id = 1;
uint64 buffer_id = 2;
Anchor position = 3;
+ repeated VectorClockEntry version = 4;
}
message GetReferencesResponse {
@@ -184,6 +186,7 @@ message GetDocumentHighlights {
uint64 project_id = 1;
uint64 buffer_id = 2;
Anchor position = 3;
+ repeated VectorClockEntry version = 4;
}
message GetDocumentHighlightsResponse {
@@ -328,6 +331,7 @@ message GetCodeActions {
uint64 buffer_id = 2;
Anchor start = 3;
Anchor end = 4;
+ repeated VectorClockEntry version = 5;
}
message GetCodeActionsResponse {
@@ -349,6 +353,7 @@ message PrepareRename {
uint64 project_id = 1;
uint64 buffer_id = 2;
Anchor position = 3;
+ repeated VectorClockEntry version = 4;
}
message PrepareRenameResponse {
@@ -363,6 +368,7 @@ message PerformRename {
uint64 buffer_id = 2;
Anchor position = 3;
string new_name = 4;
+ repeated VectorClockEntry version = 5;
}
message PerformRenameResponse {
@@ -157,15 +157,15 @@ messages!(
(GetChannels, Foreground),
(GetChannelsResponse, Foreground),
(GetCodeActions, Background),
- (GetCodeActionsResponse, Foreground),
+ (GetCodeActionsResponse, Background),
(GetCompletions, Background),
- (GetCompletionsResponse, Foreground),
- (GetDefinition, Foreground),
- (GetDefinitionResponse, Foreground),
+ (GetCompletionsResponse, Background),
+ (GetDefinition, Background),
+ (GetDefinitionResponse, Background),
(GetDocumentHighlights, Background),
(GetDocumentHighlightsResponse, Background),
- (GetReferences, Foreground),
- (GetReferencesResponse, Foreground),
+ (GetReferences, Background),
+ (GetReferencesResponse, Background),
(GetProjectSymbols, Background),
(GetProjectSymbolsResponse, Background),
(GetUsers, Foreground),
@@ -176,10 +176,10 @@ messages!(
(JoinProjectResponse, Foreground),
(LeaveChannel, Foreground),
(LeaveProject, Foreground),
- (OpenBuffer, Foreground),
- (OpenBufferForSymbol, Foreground),
- (OpenBufferForSymbolResponse, Foreground),
- (OpenBufferResponse, Foreground),
+ (OpenBuffer, Background),
+ (OpenBufferForSymbol, Background),
+ (OpenBufferForSymbolResponse, Background),
+ (OpenBufferResponse, Background),
(PerformRename, Background),
(PerformRenameResponse, Background),
(PrepareRename, Background),
@@ -199,7 +199,7 @@ messages!(
(UnregisterProject, Foreground),
(UnregisterWorktree, Foreground),
(UnshareProject, Foreground),
- (UpdateBuffer, Foreground),
+ (UpdateBuffer, Background),
(UpdateBufferFile, Foreground),
(UpdateContacts, Foreground),
(UpdateDiagnosticSummary, Foreground),
@@ -4911,12 +4911,9 @@ mod tests {
);
(buffer.version(), buffer.save(cx))
});
- let save = cx.spawn(|cx| async move {
+ let save = cx.background().spawn(async move {
let (saved_version, _) = save.await.expect("save request failed");
- buffer.read_with(&cx, |buffer, _| {
- assert!(buffer.version().observed_all(&saved_version));
- assert!(saved_version.observed_all(&requested_version));
- });
+ assert!(saved_version.observed_all(&requested_version));
});
if rng.lock().gen_bool(0.3) {
log::info!("Guest {}: detaching save request", guest_id);
@@ -1307,6 +1307,32 @@ impl Buffer {
}
}
+ pub fn wait_for_anchors<'a>(
+ &mut self,
+ anchors: impl IntoIterator<Item = &'a Anchor>,
+ ) -> impl 'static + Future<Output = ()> {
+ let mut futures = Vec::new();
+ for anchor in anchors {
+ if !self.version.observed(anchor.timestamp)
+ && *anchor != Anchor::max()
+ && *anchor != Anchor::min()
+ {
+ let (tx, rx) = oneshot::channel();
+ self.edit_id_resolvers
+ .entry(anchor.timestamp)
+ .or_default()
+ .push(tx);
+ futures.push(rx);
+ }
+ }
+
+ async move {
+ for mut future in futures {
+ future.recv().await;
+ }
+ }
+ }
+
pub fn wait_for_version(&mut self, version: clock::Global) -> impl Future<Output = ()> {
let (tx, mut rx) = barrier::channel();
if !self.snapshot.version.observed_all(&version) {