zed/crates/project/src/lsp_command.rs

794 lines
28 KiB
Rust
Raw Normal View History

use crate::{DocumentHighlight, Location, Project, ProjectTransaction};
2022-02-17 20:44:14 +00:00
use anyhow::{anyhow, Result};
2022-02-19 00:07:30 +00:00
use async_trait::async_trait;
2022-02-18 18:45:06 +00:00
use client::{proto, PeerId};
use gpui::{AppContext, AsyncAppContext, ModelHandle};
2022-02-17 20:44:14 +00:00
use language::{
point_from_lsp,
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
2022-02-17 20:44:14 +00:00
};
use lsp::{DocumentHighlightKind, ServerCapabilities};
use std::{cmp::Reverse, ops::Range, path::Path};
2022-02-17 20:44:14 +00:00
2022-02-19 00:07:30 +00:00
#[async_trait(?Send)]
2022-02-18 18:45:06 +00:00
pub(crate) trait LspCommand: 'static + Sized {
2022-02-17 20:44:14 +00:00
type Response: 'static + Default + Send;
type LspRequest: 'static + Send + lsp::request::Request;
type ProtoRequest: 'static + Send + proto::RequestMessage;
fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
true
}
2022-02-17 20:44:14 +00:00
fn to_lsp(
&self,
path: &Path,
cx: &AppContext,
) -> <Self::LspRequest as lsp::request::Request>::Params;
2022-02-19 00:07:30 +00:00
async fn response_from_lsp(
2022-02-17 20:44:14 +00:00
self,
message: <Self::LspRequest as lsp::request::Request>::Result,
project: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
buffer: ModelHandle<Buffer>,
2022-02-17 20:44:14 +00:00
cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<Self::Response>;
2022-02-18 18:45:06 +00:00
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
async fn from_proto(
2022-02-18 18:45:06 +00:00
message: Self::ProtoRequest,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
cx: AsyncAppContext,
2022-02-18 18:45:06 +00:00
) -> Result<Self>;
fn response_to_proto(
response: Self::Response,
project: &mut Project,
peer_id: PeerId,
buffer_version: &clock::Global,
cx: &AppContext,
2022-02-18 18:45:06 +00:00
) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
2022-02-19 00:07:30 +00:00
async fn response_from_proto(
2022-02-17 20:44:14 +00:00
self,
message: <Self::ProtoRequest as proto::RequestMessage>::Response,
project: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
buffer: ModelHandle<Buffer>,
2022-02-17 20:44:14 +00:00
cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<Self::Response>;
fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
2022-02-17 20:44:14 +00:00
}
pub(crate) struct PrepareRename {
pub position: PointUtf16,
}
pub(crate) struct PerformRename {
pub position: PointUtf16,
pub new_name: String,
pub push_to_history: bool,
2022-02-17 20:44:14 +00:00
}
pub(crate) struct GetDefinition {
pub position: PointUtf16,
}
pub(crate) struct GetReferences {
pub position: PointUtf16,
}
pub(crate) struct GetDocumentHighlights {
pub position: PointUtf16,
}
2022-02-19 00:07:30 +00:00
#[async_trait(?Send)]
2022-02-17 20:44:14 +00:00
impl LspCommand for PrepareRename {
type Response = Option<Range<Anchor>>;
type LspRequest = lsp::request::PrepareRenameRequest;
type ProtoRequest = proto::PrepareRename;
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
lsp::TextDocumentPositionParams {
text_document: lsp::TextDocumentIdentifier {
uri: lsp::Url::from_file_path(path).unwrap(),
},
position: self.position.to_lsp_position(),
}
}
2022-02-19 00:07:30 +00:00
async fn response_from_lsp(
2022-02-17 20:44:14 +00:00
self,
message: Option<lsp::PrepareRenameResponse>,
_: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
buffer: ModelHandle<Buffer>,
2022-02-17 20:44:14 +00:00
cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<Option<Range<Anchor>>> {
buffer.read_with(&cx, |buffer, _| {
if let Some(
2022-02-17 20:44:14 +00:00
lsp::PrepareRenameResponse::Range(range)
2022-02-19 00:07:30 +00:00
| lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
) = message
{
let Range { start, end } = range_from_lsp(range);
if buffer.clip_point_utf16(start, Bias::Left) == start
&& buffer.clip_point_utf16(end, Bias::Left) == end
{
return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
}
}
Ok(None)
})
2022-02-17 20:44:14 +00:00
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
2022-02-18 18:45:06 +00:00
proto::PrepareRename {
project_id,
buffer_id: buffer.remote_id(),
2022-02-18 18:45:06 +00:00
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
2022-02-18 18:45:06 +00:00
)),
version: serialize_version(&buffer.version()),
2022-02-18 18:45:06 +00:00
}
}
async fn from_proto(
message: proto::PrepareRename,
_: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Self> {
2022-02-18 18:45:06 +00:00
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
})
.await;
2022-02-18 18:45:06 +00:00
Ok(Self {
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
2022-02-18 18:45:06 +00:00
})
}
fn response_to_proto(
range: Option<Range<Anchor>>,
_: &mut Project,
_: PeerId,
buffer_version: &clock::Global,
_: &AppContext,
2022-02-18 18:45:06 +00:00
) -> proto::PrepareRenameResponse {
proto::PrepareRenameResponse {
can_rename: range.is_some(),
start: range
.as_ref()
.map(|range| language::proto::serialize_anchor(&range.start)),
end: range
.as_ref()
.map(|range| language::proto::serialize_anchor(&range.end)),
version: serialize_version(buffer_version),
2022-02-18 18:45:06 +00:00
}
}
2022-02-19 00:07:30 +00:00
async fn response_from_proto(
2022-02-17 20:44:14 +00:00
self,
message: proto::PrepareRenameResponse,
_: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<Option<Range<Anchor>>> {
if message.can_rename {
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
2022-02-19 00:07:30 +00:00
})
.await;
let start = message.start.and_then(deserialize_anchor);
let end = message.end.and_then(deserialize_anchor);
Ok(start.zip(end).map(|(start, end)| start..end))
} else {
Ok(None)
2022-02-17 20:44:14 +00:00
}
}
fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
message.buffer_id
}
2022-02-17 20:44:14 +00:00
}
2022-02-19 00:07:30 +00:00
#[async_trait(?Send)]
2022-02-17 20:44:14 +00:00
impl LspCommand for PerformRename {
type Response = ProjectTransaction;
type LspRequest = lsp::request::Rename;
type ProtoRequest = proto::PerformRename;
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
lsp::RenameParams {
text_document_position: lsp::TextDocumentPositionParams {
text_document: lsp::TextDocumentIdentifier {
uri: lsp::Url::from_file_path(path).unwrap(),
},
position: self.position.to_lsp_position(),
},
new_name: self.new_name.clone(),
work_done_progress_params: Default::default(),
}
}
2022-02-19 00:07:30 +00:00
async fn response_from_lsp(
2022-02-17 20:44:14 +00:00
self,
message: Option<lsp::WorkspaceEdit>,
project: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
buffer: ModelHandle<Buffer>,
2022-02-17 20:44:14 +00:00
mut cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<ProjectTransaction> {
if let Some(edit) = message {
let (language_name, language_server) = buffer.read_with(&cx, |buffer, _| {
let language = buffer
.language()
.ok_or_else(|| anyhow!("buffer's language was removed"))?;
let language_server = buffer
.language_server()
.cloned()
.ok_or_else(|| anyhow!("buffer's language server was removed"))?;
Ok::<_, anyhow::Error>((language.name().to_string(), language_server))
})?;
Project::deserialize_workspace_edit(
project,
edit,
self.push_to_history,
language_name,
language_server,
&mut cx,
)
.await
} else {
Ok(ProjectTransaction::default())
2022-02-17 20:44:14 +00:00
}
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
2022-02-18 18:45:06 +00:00
proto::PerformRename {
project_id,
buffer_id: buffer.remote_id(),
2022-02-18 18:45:06 +00:00
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
new_name: self.new_name.clone(),
version: serialize_version(&buffer.version()),
2022-02-18 18:45:06 +00:00
}
}
async fn from_proto(
message: proto::PerformRename,
_: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Self> {
2022-02-18 18:45:06 +00:00
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
})
.await;
2022-02-18 18:45:06 +00:00
Ok(Self {
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
2022-02-18 18:45:06 +00:00
new_name: message.new_name,
push_to_history: false,
})
}
fn response_to_proto(
response: ProjectTransaction,
project: &mut Project,
peer_id: PeerId,
_: &clock::Global,
cx: &AppContext,
2022-02-18 18:45:06 +00:00
) -> proto::PerformRenameResponse {
let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
proto::PerformRenameResponse {
transaction: Some(transaction),
}
}
2022-02-19 00:07:30 +00:00
async fn response_from_proto(
2022-02-17 20:44:14 +00:00
self,
message: proto::PerformRenameResponse,
project: ModelHandle<Project>,
2022-02-18 18:45:06 +00:00
_: ModelHandle<Buffer>,
2022-02-17 20:44:14 +00:00
mut cx: AsyncAppContext,
2022-02-19 00:07:30 +00:00
) -> Result<ProjectTransaction> {
let message = message
.transaction
.ok_or_else(|| anyhow!("missing transaction"))?;
project
.update(&mut cx, |project, cx| {
project.deserialize_project_transaction(message, self.push_to_history, cx)
2022-02-19 00:07:30 +00:00
})
.await
2022-02-17 20:44:14 +00:00
}
fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
message.buffer_id
}
}
#[async_trait(?Send)]
impl LspCommand for GetDefinition {
type Response = Vec<Location>;
type LspRequest = lsp::request::GotoDefinition;
type ProtoRequest = proto::GetDefinition;
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
lsp::GotoDefinitionParams {
text_document_position_params: lsp::TextDocumentPositionParams {
text_document: lsp::TextDocumentIdentifier {
uri: lsp::Url::from_file_path(path).unwrap(),
},
position: self.position.to_lsp_position(),
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}
}
async fn response_from_lsp(
self,
message: Option<lsp::GotoDefinitionResponse>,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<Location>> {
let mut definitions = Vec::new();
let (language, language_server) = buffer
.read_with(&cx, |buffer, _| {
buffer
.language()
.cloned()
.zip(buffer.language_server().cloned())
})
.ok_or_else(|| anyhow!("buffer no longer has language server"))?;
if let Some(message) = message {
let mut unresolved_locations = Vec::new();
match message {
lsp::GotoDefinitionResponse::Scalar(loc) => {
unresolved_locations.push((loc.uri, loc.range));
}
lsp::GotoDefinitionResponse::Array(locs) => {
unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
}
lsp::GotoDefinitionResponse::Link(links) => {
unresolved_locations.extend(
links
.into_iter()
.map(|l| (l.target_uri, l.target_selection_range)),
);
}
}
for (target_uri, target_range) in unresolved_locations {
let target_buffer_handle = project
.update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp(
target_uri,
language.name().to_string(),
language_server.clone(),
cx,
)
})
.await?;
cx.read(|cx| {
let target_buffer = target_buffer_handle.read(cx);
let target_start = target_buffer
.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
let target_end = target_buffer
.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
definitions.push(Location {
buffer: target_buffer_handle,
range: target_buffer.anchor_after(target_start)
..target_buffer.anchor_before(target_end),
});
});
}
}
Ok(definitions)
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
proto::GetDefinition {
project_id,
buffer_id: buffer.remote_id(),
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
version: serialize_version(&buffer.version()),
}
}
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"))?;
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
})
.await;
Ok(Self {
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
fn response_to_proto(
response: Vec<Location>,
project: &mut Project,
peer_id: PeerId,
_: &clock::Global,
cx: &AppContext,
) -> proto::GetDefinitionResponse {
let locations = response
.into_iter()
.map(|definition| {
let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
proto::Location {
start: Some(serialize_anchor(&definition.range.start)),
end: Some(serialize_anchor(&definition.range.end)),
buffer: Some(buffer),
}
})
.collect();
proto::GetDefinitionResponse { locations }
}
async fn response_from_proto(
self,
message: proto::GetDefinitionResponse,
project: ModelHandle<Project>,
_: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<Location>> {
let mut locations = Vec::new();
for location in message.locations {
let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let start = location
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let end = location
.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,
})
}
Ok(locations)
}
fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
message.buffer_id
}
2022-02-17 20:44:14 +00:00
}
#[async_trait(?Send)]
impl LspCommand for GetReferences {
type Response = Vec<Location>;
type LspRequest = lsp::request::References;
type ProtoRequest = proto::GetReferences;
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
lsp::ReferenceParams {
text_document_position: lsp::TextDocumentPositionParams {
text_document: lsp::TextDocumentIdentifier {
uri: lsp::Url::from_file_path(path).unwrap(),
},
position: self.position.to_lsp_position(),
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
context: lsp::ReferenceContext {
include_declaration: true,
},
}
}
async fn response_from_lsp(
self,
locations: Option<Vec<lsp::Location>>,
project: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<Location>> {
let mut references = Vec::new();
let (language, language_server) = buffer
.read_with(&cx, |buffer, _| {
buffer
.language()
.cloned()
.zip(buffer.language_server().cloned())
})
.ok_or_else(|| anyhow!("buffer no longer has language server"))?;
if let Some(locations) = locations {
for lsp_location in locations {
let target_buffer_handle = project
.update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp(
lsp_location.uri,
language.name().to_string(),
language_server.clone(),
cx,
)
})
.await?;
cx.read(|cx| {
let target_buffer = target_buffer_handle.read(cx);
let target_start = target_buffer
.clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
let target_end = target_buffer
.clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
references.push(Location {
buffer: target_buffer_handle,
range: target_buffer.anchor_after(target_start)
..target_buffer.anchor_before(target_end),
});
});
}
}
Ok(references)
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
proto::GetReferences {
project_id,
buffer_id: buffer.remote_id(),
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
version: serialize_version(&buffer.version()),
}
}
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"))?;
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
})
.await;
Ok(Self {
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
fn response_to_proto(
response: Vec<Location>,
project: &mut Project,
peer_id: PeerId,
_: &clock::Global,
cx: &AppContext,
) -> proto::GetReferencesResponse {
let locations = response
.into_iter()
.map(|definition| {
let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
proto::Location {
start: Some(serialize_anchor(&definition.range.start)),
end: Some(serialize_anchor(&definition.range.end)),
buffer: Some(buffer),
}
})
.collect();
proto::GetReferencesResponse { locations }
}
async fn response_from_proto(
self,
message: proto::GetReferencesResponse,
project: ModelHandle<Project>,
_: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<Location>> {
let mut locations = Vec::new();
for location in message.locations {
let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let target_buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let start = location
.start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let end = location
.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,
})
}
Ok(locations)
}
fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
message.buffer_id
}
}
#[async_trait(?Send)]
impl LspCommand for GetDocumentHighlights {
type Response = Vec<DocumentHighlight>;
type LspRequest = lsp::request::DocumentHighlightRequest;
type ProtoRequest = proto::GetDocumentHighlights;
fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
capabilities.document_highlight_provider.is_some()
}
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
lsp::DocumentHighlightParams {
text_document_position_params: lsp::TextDocumentPositionParams {
text_document: lsp::TextDocumentIdentifier {
uri: lsp::Url::from_file_path(path).unwrap(),
},
position: self.position.to_lsp_position(),
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}
}
async fn response_from_lsp(
self,
lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
_: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
cx: AsyncAppContext,
) -> Result<Vec<DocumentHighlight>> {
buffer.read_with(&cx, |buffer, _| {
let mut lsp_highlights = lsp_highlights.unwrap_or_default();
lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
Ok(lsp_highlights
.into_iter()
.map(|lsp_highlight| {
let start = buffer
.clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
let end = buffer
.clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
DocumentHighlight {
range: buffer.anchor_after(start)..buffer.anchor_before(end),
kind: lsp_highlight
.kind
.unwrap_or(lsp::DocumentHighlightKind::READ),
}
})
.collect())
})
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
proto::GetDocumentHighlights {
project_id,
buffer_id: buffer.remote_id(),
position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
version: serialize_version(&buffer.version()),
}
}
async fn from_proto(
message: proto::GetDocumentHighlights,
_: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Self> {
let position = message
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(deserialize_version(message.version))
})
.await;
Ok(Self {
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
})
}
fn response_to_proto(
response: Vec<DocumentHighlight>,
_: &mut Project,
_: PeerId,
_: &clock::Global,
_: &AppContext,
) -> proto::GetDocumentHighlightsResponse {
let highlights = response
.into_iter()
.map(|highlight| proto::DocumentHighlight {
start: Some(serialize_anchor(&highlight.range.start)),
end: Some(serialize_anchor(&highlight.range.end)),
kind: match highlight.kind {
DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
_ => proto::document_highlight::Kind::Text.into(),
},
})
.collect();
proto::GetDocumentHighlightsResponse { highlights }
}
async fn response_from_proto(
self,
message: proto::GetDocumentHighlightsResponse,
_: ModelHandle<Project>,
buffer: ModelHandle<Buffer>,
mut cx: AsyncAppContext,
) -> Result<Vec<DocumentHighlight>> {
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 {
message.buffer_id
}
}