Merge pull request #2349 from zed-industries/randomized-tests-operation-script
Restructure randomized integration test to allow editing and replaying a plan, fix discovered bugs
This commit is contained in:
commit
6d8635fa29
28 changed files with 2879 additions and 1863 deletions
|
@ -4,11 +4,13 @@ use crate::{
|
|||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::proto::{self, PeerId};
|
||||
use fs::LineEnding;
|
||||
use gpui::{AppContext, AsyncAppContext, ModelHandle};
|
||||
use language::{
|
||||
point_from_lsp, point_to_lsp,
|
||||
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
|
||||
range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16,
|
||||
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction,
|
||||
Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Unclipped,
|
||||
};
|
||||
use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities};
|
||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
|
||||
|
@ -27,6 +29,8 @@ pub(crate) trait LspCommand: 'static + Sized {
|
|||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
buffer: &Buffer,
|
||||
language_server: &Arc<LanguageServer>,
|
||||
cx: &AppContext,
|
||||
) -> <Self::LspRequest as lsp::request::Request>::Params;
|
||||
async fn response_from_lsp(
|
||||
|
@ -49,7 +53,7 @@ pub(crate) trait LspCommand: 'static + Sized {
|
|||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
buffer_version: &clock::Global,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
|
@ -91,6 +95,14 @@ pub(crate) struct GetHover {
|
|||
pub position: PointUtf16,
|
||||
}
|
||||
|
||||
pub(crate) struct GetCompletions {
|
||||
pub position: PointUtf16,
|
||||
}
|
||||
|
||||
pub(crate) struct GetCodeActions {
|
||||
pub range: Range<Anchor>,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for PrepareRename {
|
||||
type Response = Option<Range<Anchor>>;
|
||||
|
@ -105,7 +117,13 @@ impl LspCommand for PrepareRename {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::TextDocumentPositionParams {
|
||||
lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: lsp::Url::from_file_path(path).unwrap(),
|
||||
|
@ -161,9 +179,9 @@ impl LspCommand for PrepareRename {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
|
@ -175,7 +193,7 @@ impl LspCommand for PrepareRename {
|
|||
_: &mut Project,
|
||||
_: PeerId,
|
||||
buffer_version: &clock::Global,
|
||||
_: &AppContext,
|
||||
_: &mut AppContext,
|
||||
) -> proto::PrepareRenameResponse {
|
||||
proto::PrepareRenameResponse {
|
||||
can_rename: range.is_some(),
|
||||
|
@ -199,9 +217,9 @@ impl LspCommand for PrepareRename {
|
|||
if message.can_rename {
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.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))
|
||||
|
@ -221,7 +239,13 @@ impl LspCommand for PerformRename {
|
|||
type LspRequest = lsp::request::Rename;
|
||||
type ProtoRequest = proto::PerformRename;
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::RenameParams {
|
||||
lsp::RenameParams {
|
||||
text_document_position: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -281,9 +305,9 @@ impl LspCommand for PerformRename {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
new_name: message.new_name,
|
||||
|
@ -296,7 +320,7 @@ impl LspCommand for PerformRename {
|
|||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
_: &clock::Global,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> proto::PerformRenameResponse {
|
||||
let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
|
||||
proto::PerformRenameResponse {
|
||||
|
@ -332,7 +356,13 @@ impl LspCommand for GetDefinition {
|
|||
type LspRequest = lsp::request::GotoDefinition;
|
||||
type ProtoRequest = proto::GetDefinition;
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::GotoDefinitionParams {
|
||||
lsp::GotoDefinitionParams {
|
||||
text_document_position_params: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -378,9 +408,9 @@ impl LspCommand for GetDefinition {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
|
@ -391,7 +421,7 @@ impl LspCommand for GetDefinition {
|
|||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
_: &clock::Global,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> proto::GetDefinitionResponse {
|
||||
let links = location_links_to_proto(response, project, peer_id, cx);
|
||||
proto::GetDefinitionResponse { links }
|
||||
|
@ -418,7 +448,13 @@ impl LspCommand for GetTypeDefinition {
|
|||
type LspRequest = lsp::request::GotoTypeDefinition;
|
||||
type ProtoRequest = proto::GetTypeDefinition;
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoTypeDefinitionParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::GotoTypeDefinitionParams {
|
||||
lsp::GotoTypeDefinitionParams {
|
||||
text_document_position_params: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -464,9 +500,9 @@ impl LspCommand for GetTypeDefinition {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
|
@ -477,7 +513,7 @@ impl LspCommand for GetTypeDefinition {
|
|||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
_: &clock::Global,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> proto::GetTypeDefinitionResponse {
|
||||
let links = location_links_to_proto(response, project, peer_id, cx);
|
||||
proto::GetTypeDefinitionResponse { links }
|
||||
|
@ -537,7 +573,7 @@ async fn location_links_from_proto(
|
|||
.ok_or_else(|| anyhow!("missing origin end"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||
.await;
|
||||
.await?;
|
||||
Some(Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
|
@ -562,7 +598,7 @@ async fn location_links_from_proto(
|
|||
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||
.await;
|
||||
.await?;
|
||||
let target = Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
|
@ -658,7 +694,7 @@ fn location_links_to_proto(
|
|||
links: Vec<LocationLink>,
|
||||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> Vec<proto::LocationLink> {
|
||||
links
|
||||
.into_iter()
|
||||
|
@ -693,7 +729,13 @@ impl LspCommand for GetReferences {
|
|||
type LspRequest = lsp::request::References;
|
||||
type ProtoRequest = proto::GetReferences;
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::ReferenceParams {
|
||||
lsp::ReferenceParams {
|
||||
text_document_position: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -774,9 +816,9 @@ impl LspCommand for GetReferences {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
|
@ -787,7 +829,7 @@ impl LspCommand for GetReferences {
|
|||
project: &mut Project,
|
||||
peer_id: PeerId,
|
||||
_: &clock::Global,
|
||||
cx: &AppContext,
|
||||
cx: &mut AppContext,
|
||||
) -> proto::GetReferencesResponse {
|
||||
let locations = response
|
||||
.into_iter()
|
||||
|
@ -827,7 +869,7 @@ impl LspCommand for GetReferences {
|
|||
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||
target_buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||
.await;
|
||||
.await?;
|
||||
locations.push(Location {
|
||||
buffer: target_buffer,
|
||||
range: start..end,
|
||||
|
@ -851,7 +893,13 @@ impl LspCommand for GetDocumentHighlights {
|
|||
capabilities.document_highlight_provider.is_some()
|
||||
}
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::DocumentHighlightParams {
|
||||
lsp::DocumentHighlightParams {
|
||||
text_document_position_params: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -915,9 +963,9 @@ impl LspCommand for GetDocumentHighlights {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
|
@ -928,7 +976,7 @@ impl LspCommand for GetDocumentHighlights {
|
|||
_: &mut Project,
|
||||
_: PeerId,
|
||||
_: &clock::Global,
|
||||
_: &AppContext,
|
||||
_: &mut AppContext,
|
||||
) -> proto::GetDocumentHighlightsResponse {
|
||||
let highlights = response
|
||||
.into_iter()
|
||||
|
@ -965,7 +1013,7 @@ impl LspCommand for GetDocumentHighlights {
|
|||
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||
.await;
|
||||
.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,
|
||||
|
@ -991,7 +1039,13 @@ impl LspCommand for GetHover {
|
|||
type LspRequest = lsp::request::HoverRequest;
|
||||
type ProtoRequest = proto::GetHover;
|
||||
|
||||
fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::HoverParams {
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::HoverParams {
|
||||
lsp::HoverParams {
|
||||
text_document_position_params: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
|
@ -1117,9 +1171,9 @@ impl LspCommand for GetHover {
|
|||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(message.version))
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await;
|
||||
.await?;
|
||||
Ok(Self {
|
||||
position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
|
@ -1130,7 +1184,7 @@ impl LspCommand for GetHover {
|
|||
_: &mut Project,
|
||||
_: PeerId,
|
||||
_: &clock::Global,
|
||||
_: &AppContext,
|
||||
_: &mut AppContext,
|
||||
) -> proto::GetHoverResponse {
|
||||
if let Some(response) = response {
|
||||
let (start, end) = if let Some(range) = response.range {
|
||||
|
@ -1199,3 +1253,342 @@ impl LspCommand for GetHover {
|
|||
message.buffer_id
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for GetCompletions {
|
||||
type Response = Vec<Completion>;
|
||||
type LspRequest = lsp::request::Completion;
|
||||
type ProtoRequest = proto::GetCompletions;
|
||||
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::CompletionParams {
|
||||
lsp::CompletionParams {
|
||||
text_document_position: lsp::TextDocumentPositionParams::new(
|
||||
lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
|
||||
point_to_lsp(self.position),
|
||||
),
|
||||
context: Default::default(),
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_lsp(
|
||||
self,
|
||||
completions: Option<lsp::CompletionResponse>,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
cx: AsyncAppContext,
|
||||
) -> Result<Vec<Completion>> {
|
||||
let completions = if let Some(completions) = completions {
|
||||
match completions {
|
||||
lsp::CompletionResponse::Array(completions) => completions,
|
||||
lsp::CompletionResponse::List(list) => list.items,
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let completions = buffer.read_with(&cx, |buffer, _| {
|
||||
let language = buffer.language().cloned();
|
||||
let snapshot = buffer.snapshot();
|
||||
let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
|
||||
let mut range_for_token = None;
|
||||
completions
|
||||
.into_iter()
|
||||
.filter_map(move |mut lsp_completion| {
|
||||
// For now, we can only handle additional edits if they are returned
|
||||
// when resolving the completion, not if they are present initially.
|
||||
if lsp_completion
|
||||
.additional_text_edits
|
||||
.as_ref()
|
||||
.map_or(false, |edits| !edits.is_empty())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
|
||||
// If the language server provides a range to overwrite, then
|
||||
// check that the range is valid.
|
||||
Some(lsp::CompletionTextEdit::Edit(edit)) => {
|
||||
let range = range_from_lsp(edit.range);
|
||||
let start = snapshot.clip_point_utf16(range.start, Bias::Left);
|
||||
let end = snapshot.clip_point_utf16(range.end, Bias::Left);
|
||||
if start != range.start.0 || end != range.end.0 {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
edit.new_text.clone(),
|
||||
)
|
||||
}
|
||||
// If the language server does not provide a range, then infer
|
||||
// the range based on the syntax tree.
|
||||
None => {
|
||||
if self.position != clipped_position {
|
||||
log::info!("completion out of expected range");
|
||||
return None;
|
||||
}
|
||||
let Range { start, end } = range_for_token
|
||||
.get_or_insert_with(|| {
|
||||
let offset = self.position.to_offset(&snapshot);
|
||||
let (range, kind) = snapshot.surrounding_word(offset);
|
||||
if kind == Some(CharKind::Word) {
|
||||
range
|
||||
} else {
|
||||
offset..offset
|
||||
}
|
||||
})
|
||||
.clone();
|
||||
let text = lsp_completion
|
||||
.insert_text
|
||||
.as_ref()
|
||||
.unwrap_or(&lsp_completion.label)
|
||||
.clone();
|
||||
(
|
||||
snapshot.anchor_before(start)..snapshot.anchor_after(end),
|
||||
text,
|
||||
)
|
||||
}
|
||||
Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
|
||||
log::info!("unsupported insert/replace completion");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let language = language.clone();
|
||||
LineEnding::normalize(&mut new_text);
|
||||
Some(async move {
|
||||
let mut label = None;
|
||||
if let Some(language) = language {
|
||||
language.process_completion(&mut lsp_completion).await;
|
||||
label = language.label_for_completion(&lsp_completion).await;
|
||||
}
|
||||
Completion {
|
||||
old_range,
|
||||
new_text,
|
||||
label: label.unwrap_or_else(|| {
|
||||
language::CodeLabel::plain(
|
||||
lsp_completion.label.clone(),
|
||||
lsp_completion.filter_text.as_deref(),
|
||||
)
|
||||
}),
|
||||
lsp_completion,
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
Ok(futures::future::join_all(completions).await)
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
|
||||
let anchor = buffer.anchor_after(self.position);
|
||||
proto::GetCompletions {
|
||||
project_id,
|
||||
buffer_id: buffer.remote_id(),
|
||||
position: Some(language::proto::serialize_anchor(&anchor)),
|
||||
version: serialize_version(&buffer.version()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_proto(
|
||||
message: proto::GetCompletions,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
let version = deserialize_version(&message.version);
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| buffer.wait_for_version(version))
|
||||
.await?;
|
||||
let position = message
|
||||
.position
|
||||
.and_then(language::proto::deserialize_anchor)
|
||||
.map(|p| {
|
||||
buffer.read_with(&cx, |buffer, _| {
|
||||
buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||
Ok(Self { position })
|
||||
}
|
||||
|
||||
fn response_to_proto(
|
||||
completions: Vec<Completion>,
|
||||
_: &mut Project,
|
||||
_: PeerId,
|
||||
buffer_version: &clock::Global,
|
||||
_: &mut AppContext,
|
||||
) -> proto::GetCompletionsResponse {
|
||||
proto::GetCompletionsResponse {
|
||||
completions: completions
|
||||
.iter()
|
||||
.map(language::proto::serialize_completion)
|
||||
.collect(),
|
||||
version: serialize_version(&buffer_version),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::GetCompletionsResponse,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<Completion>> {
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await?;
|
||||
|
||||
let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
|
||||
let completions = message.completions.into_iter().map(|completion| {
|
||||
language::proto::deserialize_completion(completion, language.clone())
|
||||
});
|
||||
futures::future::try_join_all(completions).await
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
|
||||
message.buffer_id
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for GetCodeActions {
|
||||
type Response = Vec<CodeAction>;
|
||||
type LspRequest = lsp::request::CodeActionRequest;
|
||||
type ProtoRequest = proto::GetCodeActions;
|
||||
|
||||
fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
|
||||
capabilities.code_action_provider.is_some()
|
||||
}
|
||||
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
buffer: &Buffer,
|
||||
language_server: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> lsp::CodeActionParams {
|
||||
let relevant_diagnostics = buffer
|
||||
.snapshot()
|
||||
.diagnostics_in_range::<_, usize>(self.range.clone(), false)
|
||||
.map(|entry| entry.to_lsp_diagnostic_stub())
|
||||
.collect();
|
||||
lsp::CodeActionParams {
|
||||
text_document: lsp::TextDocumentIdentifier::new(
|
||||
lsp::Url::from_file_path(path).unwrap(),
|
||||
),
|
||||
range: range_to_lsp(self.range.to_point_utf16(buffer)),
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
context: lsp::CodeActionContext {
|
||||
diagnostics: relevant_diagnostics,
|
||||
only: language_server.code_action_kinds(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_lsp(
|
||||
self,
|
||||
actions: Option<lsp::CodeActionResponse>,
|
||||
_: ModelHandle<Project>,
|
||||
_: ModelHandle<Buffer>,
|
||||
_: AsyncAppContext,
|
||||
) -> Result<Vec<CodeAction>> {
|
||||
Ok(actions
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
|
||||
Some(CodeAction {
|
||||
range: self.range.clone(),
|
||||
lsp_action,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
|
||||
proto::GetCodeActions {
|
||||
project_id,
|
||||
buffer_id: buffer.remote_id(),
|
||||
start: Some(language::proto::serialize_anchor(&self.range.start)),
|
||||
end: Some(language::proto::serialize_anchor(&self.range.end)),
|
||||
version: serialize_version(&buffer.version()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_proto(
|
||||
message: proto::GetCodeActions,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
let start = message
|
||||
.start
|
||||
.and_then(language::proto::deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("invalid start"))?;
|
||||
let end = message
|
||||
.end
|
||||
.and_then(language::proto::deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("invalid end"))?;
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(Self { range: start..end })
|
||||
}
|
||||
|
||||
fn response_to_proto(
|
||||
code_actions: Vec<CodeAction>,
|
||||
_: &mut Project,
|
||||
_: PeerId,
|
||||
buffer_version: &clock::Global,
|
||||
_: &mut AppContext,
|
||||
) -> proto::GetCodeActionsResponse {
|
||||
proto::GetCodeActionsResponse {
|
||||
actions: code_actions
|
||||
.iter()
|
||||
.map(language::proto::serialize_code_action)
|
||||
.collect(),
|
||||
version: serialize_version(&buffer_version),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::GetCodeActionsResponse,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<CodeAction>> {
|
||||
buffer
|
||||
.update(&mut cx, |buffer, _| {
|
||||
buffer.wait_for_version(deserialize_version(&message.version))
|
||||
})
|
||||
.await?;
|
||||
message
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(language::proto::deserialize_code_action)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
|
||||
message.buffer_id
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -221,7 +221,7 @@ impl Worktree {
|
|||
root_char_bag: root_name.chars().map(|c| c.to_ascii_lowercase()).collect(),
|
||||
entries_by_path: Default::default(),
|
||||
entries_by_id: Default::default(),
|
||||
scan_id: 0,
|
||||
scan_id: 1,
|
||||
completed_scan_id: 0,
|
||||
},
|
||||
};
|
||||
|
@ -298,7 +298,7 @@ impl Worktree {
|
|||
.collect(),
|
||||
entries_by_path: Default::default(),
|
||||
entries_by_id: Default::default(),
|
||||
scan_id: 0,
|
||||
scan_id: 1,
|
||||
completed_scan_id: 0,
|
||||
};
|
||||
|
||||
|
@ -1063,7 +1063,7 @@ impl RemoteWorktree {
|
|||
version: serialize_version(&version),
|
||||
})
|
||||
.await?;
|
||||
let version = deserialize_version(response.version);
|
||||
let version = deserialize_version(&response.version);
|
||||
let fingerprint = deserialize_fingerprint(&response.fingerprint)?;
|
||||
let mtime = response
|
||||
.mtime
|
||||
|
@ -1223,11 +1223,10 @@ impl Snapshot {
|
|||
let mut entries_by_path_edits = Vec::new();
|
||||
let mut entries_by_id_edits = Vec::new();
|
||||
for entry_id in update.removed_entries {
|
||||
let entry = self
|
||||
.entry_for_id(ProjectEntryId::from_proto(entry_id))
|
||||
.ok_or_else(|| anyhow!("unknown entry {}", entry_id))?;
|
||||
entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
|
||||
entries_by_id_edits.push(Edit::Remove(entry.id));
|
||||
if let Some(entry) = self.entry_for_id(ProjectEntryId::from_proto(entry_id)) {
|
||||
entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
|
||||
entries_by_id_edits.push(Edit::Remove(entry.id));
|
||||
}
|
||||
}
|
||||
|
||||
for entry in update.updated_entries {
|
||||
|
@ -3725,7 +3724,7 @@ mod tests {
|
|||
) {
|
||||
let mut files = Vec::new();
|
||||
let mut dirs = Vec::new();
|
||||
for path in fs.as_fake().paths().await {
|
||||
for path in fs.as_fake().paths() {
|
||||
if path.starts_with(root_path) {
|
||||
if fs.is_file(&path).await {
|
||||
files.push(path);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue