Enable completion menu to resolve documentation when guest

This commit is contained in:
Julia 2023-10-10 00:16:15 -04:00
parent 7020050b06
commit 354882f2c0
8 changed files with 207 additions and 94 deletions

View file

@ -224,6 +224,7 @@ impl Server {
.add_request_handler(forward_project_request::<proto::OpenBufferByPath>) .add_request_handler(forward_project_request::<proto::OpenBufferByPath>)
.add_request_handler(forward_project_request::<proto::GetCompletions>) .add_request_handler(forward_project_request::<proto::GetCompletions>)
.add_request_handler(forward_project_request::<proto::ApplyCompletionAdditionalEdits>) .add_request_handler(forward_project_request::<proto::ApplyCompletionAdditionalEdits>)
.add_request_handler(forward_project_request::<proto::ResolveCompletionDocumentation>)
.add_request_handler(forward_project_request::<proto::GetCodeActions>) .add_request_handler(forward_project_request::<proto::GetCodeActions>)
.add_request_handler(forward_project_request::<proto::ApplyCodeAction>) .add_request_handler(forward_project_request::<proto::ApplyCodeAction>)
.add_request_handler(forward_project_request::<proto::PrepareRename>) .add_request_handler(forward_project_request::<proto::PrepareRename>)

View file

@ -60,10 +60,10 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind}; pub use language::{char_kind, CharKind};
use language::{ use language::{
language_settings::{self, all_language_settings, InlayHintSettings}, language_settings::{self, all_language_settings, InlayHintSettings},
point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind, IndentSize, Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind,
Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection,
TransactionId, SelectionGoal, TransactionId,
}; };
use link_go_to_definition::{ use link_go_to_definition::{
hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight, hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
@ -80,7 +80,7 @@ use ordered_float::OrderedFloat;
use parking_lot::RwLock; use parking_lot::RwLock;
use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction}; use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
use rand::{seq::SliceRandom, thread_rng}; use rand::{seq::SliceRandom, thread_rng};
use rpc::proto::PeerId; use rpc::proto::{self, PeerId};
use scroll::{ use scroll::{
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
}; };
@ -999,7 +999,7 @@ impl CompletionsMenu {
let completions = self.completions.clone(); let completions = self.completions.clone();
let completions_guard = completions.read(); let completions_guard = completions.read();
let completion = &completions_guard[index]; let completion = &completions_guard[index];
if completion.lsp_completion.documentation.is_some() { if completion.documentation.is_some() {
return; return;
} }
@ -1007,6 +1007,57 @@ impl CompletionsMenu {
let completion = completion.lsp_completion.clone(); let completion = completion.lsp_completion.clone();
drop(completions_guard); drop(completions_guard);
if project.read(cx).is_remote() {
let Some(project_id) = project.read(cx).remote_id() else {
log::error!("Remote project without remote_id");
return;
};
let client = project.read(cx).client();
let request = proto::ResolveCompletionDocumentation {
project_id,
language_server_id: server_id.0 as u64,
lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
};
cx.spawn(|this, mut cx| async move {
let Some(response) = client
.request(request)
.await
.context("completion documentation resolve proto request")
.log_err()
else {
return;
};
if response.text.is_empty() {
let mut completions = completions.write();
let completion = &mut completions[index];
completion.documentation = Some(Documentation::Undocumented);
}
let documentation = if response.is_markdown {
Documentation::MultiLineMarkdown(
markdown::parse_markdown(&response.text, &language_registry, None).await,
)
} else if response.text.lines().count() <= 1 {
Documentation::SingleLine(response.text)
} else {
Documentation::MultiLinePlainText(response.text)
};
let mut completions = completions.write();
let completion = &mut completions[index];
completion.documentation = Some(documentation);
drop(completions);
_ = this.update(&mut cx, |_, cx| cx.notify());
})
.detach();
return;
}
let Some(server) = project.read(cx).language_server_for_id(server_id) else { let Some(server) = project.read(cx).language_server_for_id(server_id) else {
return; return;
}; };
@ -1037,11 +1088,14 @@ impl CompletionsMenu {
let mut completions = completions.write(); let mut completions = completions.write();
let completion = &mut completions[index]; let completion = &mut completions[index];
completion.documentation = documentation; completion.documentation = Some(documentation);
completion.lsp_completion.documentation = Some(lsp_documentation);
drop(completions); drop(completions);
_ = this.update(&mut cx, |_, cx| cx.notify()); _ = this.update(&mut cx, |_, cx| cx.notify());
} else {
let mut completions = completions.write();
let completion = &mut completions[index];
completion.documentation = Some(Documentation::Undocumented);
} }
}) })
.detach(); .detach();
@ -1061,10 +1115,10 @@ impl CompletionsMenu {
.max_by_key(|(_, mat)| { .max_by_key(|(_, mat)| {
let completions = self.completions.read(); let completions = self.completions.read();
let completion = &completions[mat.candidate_id]; let completion = &completions[mat.candidate_id];
let documentation = &completion.lsp_completion.documentation; let documentation = &completion.documentation;
let mut len = completion.label.text.chars().count(); let mut len = completion.label.text.chars().count();
if let Some(lsp::Documentation::String(text)) = documentation { if let Some(Documentation::SingleLine(text)) = documentation {
len += text.chars().count(); len += text.chars().count();
} }

View file

@ -149,28 +149,28 @@ pub async fn prepare_completion_documentation(
documentation: &lsp::Documentation, documentation: &lsp::Documentation,
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
) -> Option<Documentation> { ) -> Documentation {
match documentation { match documentation {
lsp::Documentation::String(text) => { lsp::Documentation::String(text) => {
if text.lines().count() <= 1 { if text.lines().count() <= 1 {
Some(Documentation::SingleLine(text.clone())) Documentation::SingleLine(text.clone())
} else { } else {
Some(Documentation::MultiLinePlainText(text.clone())) Documentation::MultiLinePlainText(text.clone())
} }
} }
lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind { lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind {
lsp::MarkupKind::PlainText => { lsp::MarkupKind::PlainText => {
if value.lines().count() <= 1 { if value.lines().count() <= 1 {
Some(Documentation::SingleLine(value.clone())) Documentation::SingleLine(value.clone())
} else { } else {
Some(Documentation::MultiLinePlainText(value.clone())) Documentation::MultiLinePlainText(value.clone())
} }
} }
lsp::MarkupKind::Markdown => { lsp::MarkupKind::Markdown => {
let parsed = parse_markdown(value, language_registry, language).await; let parsed = parse_markdown(value, language_registry, language).await;
Some(Documentation::MultiLineMarkdown(parsed)) Documentation::MultiLineMarkdown(parsed)
} }
}, },
} }
@ -178,6 +178,7 @@ pub async fn prepare_completion_documentation(
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Documentation { pub enum Documentation {
Undocumented,
SingleLine(String), SingleLine(String),
MultiLinePlainText(String), MultiLinePlainText(String),
MultiLineMarkdown(ParsedMarkdown), MultiLineMarkdown(ParsedMarkdown),

View file

@ -1466,12 +1466,14 @@ impl LspCommand for GetCompletions {
} }
let documentation = if let Some(lsp_docs) = &lsp_completion.documentation { let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
prepare_completion_documentation( Some(
lsp_docs, prepare_completion_documentation(
&language_registry, lsp_docs,
language.clone(), &language_registry,
language.clone(),
)
.await,
) )
.await
} else { } else {
None None
}; };

View file

@ -580,6 +580,7 @@ impl Project {
client.add_model_request_handler(Self::handle_apply_code_action); client.add_model_request_handler(Self::handle_apply_code_action);
client.add_model_request_handler(Self::handle_on_type_formatting); client.add_model_request_handler(Self::handle_on_type_formatting);
client.add_model_request_handler(Self::handle_inlay_hints); client.add_model_request_handler(Self::handle_inlay_hints);
client.add_model_request_handler(Self::handle_resolve_completion_documentation);
client.add_model_request_handler(Self::handle_resolve_inlay_hint); client.add_model_request_handler(Self::handle_resolve_inlay_hint);
client.add_model_request_handler(Self::handle_refresh_inlay_hints); client.add_model_request_handler(Self::handle_refresh_inlay_hints);
client.add_model_request_handler(Self::handle_reload_buffers); client.add_model_request_handler(Self::handle_reload_buffers);
@ -7155,6 +7156,40 @@ impl Project {
}) })
} }
async fn handle_resolve_completion_documentation(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ResolveCompletionDocumentationResponse> {
let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
let completion = this
.read_with(&mut cx, |this, _| {
let id = LanguageServerId(envelope.payload.language_server_id as usize);
let Some(server) = this.language_server_for_id(id) else {
return Err(anyhow!("No language server {id}"));
};
Ok(server.request::<lsp::request::ResolveCompletionItem>(lsp_completion))
})?
.await?;
let mut is_markdown = false;
let text = match completion.documentation {
Some(lsp::Documentation::String(text)) => text,
Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
is_markdown = kind == lsp::MarkupKind::Markdown;
value
}
_ => String::new(),
};
Ok(proto::ResolveCompletionDocumentationResponse { text, is_markdown })
}
async fn handle_apply_code_action( async fn handle_apply_code_action(
this: ModelHandle<Self>, this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::ApplyCodeAction>, envelope: TypedEnvelope<proto::ApplyCodeAction>,

View file

@ -89,88 +89,90 @@ message Envelope {
FormatBuffersResponse format_buffers_response = 70; FormatBuffersResponse format_buffers_response = 70;
GetCompletions get_completions = 71; GetCompletions get_completions = 71;
GetCompletionsResponse get_completions_response = 72; GetCompletionsResponse get_completions_response = 72;
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 73; ResolveCompletionDocumentation resolve_completion_documentation = 73;
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 74; ResolveCompletionDocumentationResponse resolve_completion_documentation_response = 74;
GetCodeActions get_code_actions = 75; ApplyCompletionAdditionalEdits apply_completion_additional_edits = 75;
GetCodeActionsResponse get_code_actions_response = 76; ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 76;
GetHover get_hover = 77; GetCodeActions get_code_actions = 77;
GetHoverResponse get_hover_response = 78; GetCodeActionsResponse get_code_actions_response = 78;
ApplyCodeAction apply_code_action = 79; GetHover get_hover = 79;
ApplyCodeActionResponse apply_code_action_response = 80; GetHoverResponse get_hover_response = 80;
PrepareRename prepare_rename = 81; ApplyCodeAction apply_code_action = 81;
PrepareRenameResponse prepare_rename_response = 82; ApplyCodeActionResponse apply_code_action_response = 82;
PerformRename perform_rename = 83; PrepareRename prepare_rename = 83;
PerformRenameResponse perform_rename_response = 84; PrepareRenameResponse prepare_rename_response = 84;
SearchProject search_project = 85; PerformRename perform_rename = 85;
SearchProjectResponse search_project_response = 86; PerformRenameResponse perform_rename_response = 86;
SearchProject search_project = 87;
SearchProjectResponse search_project_response = 88;
UpdateContacts update_contacts = 87; UpdateContacts update_contacts = 89;
UpdateInviteInfo update_invite_info = 88; UpdateInviteInfo update_invite_info = 90;
ShowContacts show_contacts = 89; ShowContacts show_contacts = 91;
GetUsers get_users = 90; GetUsers get_users = 92;
FuzzySearchUsers fuzzy_search_users = 91; FuzzySearchUsers fuzzy_search_users = 93;
UsersResponse users_response = 92; UsersResponse users_response = 94;
RequestContact request_contact = 93; RequestContact request_contact = 95;
RespondToContactRequest respond_to_contact_request = 94; RespondToContactRequest respond_to_contact_request = 96;
RemoveContact remove_contact = 95; RemoveContact remove_contact = 97;
Follow follow = 96; Follow follow = 98;
FollowResponse follow_response = 97; FollowResponse follow_response = 99;
UpdateFollowers update_followers = 98; UpdateFollowers update_followers = 100;
Unfollow unfollow = 99; Unfollow unfollow = 101;
GetPrivateUserInfo get_private_user_info = 100; GetPrivateUserInfo get_private_user_info = 102;
GetPrivateUserInfoResponse get_private_user_info_response = 101; GetPrivateUserInfoResponse get_private_user_info_response = 103;
UpdateDiffBase update_diff_base = 102; UpdateDiffBase update_diff_base = 104;
OnTypeFormatting on_type_formatting = 103; OnTypeFormatting on_type_formatting = 105;
OnTypeFormattingResponse on_type_formatting_response = 104; OnTypeFormattingResponse on_type_formatting_response = 106;
UpdateWorktreeSettings update_worktree_settings = 105; UpdateWorktreeSettings update_worktree_settings = 107;
InlayHints inlay_hints = 106; InlayHints inlay_hints = 108;
InlayHintsResponse inlay_hints_response = 107; InlayHintsResponse inlay_hints_response = 109;
ResolveInlayHint resolve_inlay_hint = 108; ResolveInlayHint resolve_inlay_hint = 110;
ResolveInlayHintResponse resolve_inlay_hint_response = 109; ResolveInlayHintResponse resolve_inlay_hint_response = 111;
RefreshInlayHints refresh_inlay_hints = 110; RefreshInlayHints refresh_inlay_hints = 112;
CreateChannel create_channel = 111; CreateChannel create_channel = 113;
CreateChannelResponse create_channel_response = 112; CreateChannelResponse create_channel_response = 114;
InviteChannelMember invite_channel_member = 113; InviteChannelMember invite_channel_member = 115;
RemoveChannelMember remove_channel_member = 114; RemoveChannelMember remove_channel_member = 116;
RespondToChannelInvite respond_to_channel_invite = 115; RespondToChannelInvite respond_to_channel_invite = 117;
UpdateChannels update_channels = 116; UpdateChannels update_channels = 118;
JoinChannel join_channel = 117; JoinChannel join_channel = 119;
DeleteChannel delete_channel = 118; DeleteChannel delete_channel = 120;
GetChannelMembers get_channel_members = 119; GetChannelMembers get_channel_members = 121;
GetChannelMembersResponse get_channel_members_response = 120; GetChannelMembersResponse get_channel_members_response = 122;
SetChannelMemberAdmin set_channel_member_admin = 121; SetChannelMemberAdmin set_channel_member_admin = 123;
RenameChannel rename_channel = 122; RenameChannel rename_channel = 124;
RenameChannelResponse rename_channel_response = 123; RenameChannelResponse rename_channel_response = 125;
JoinChannelBuffer join_channel_buffer = 124; JoinChannelBuffer join_channel_buffer = 126;
JoinChannelBufferResponse join_channel_buffer_response = 125; JoinChannelBufferResponse join_channel_buffer_response = 127;
UpdateChannelBuffer update_channel_buffer = 126; UpdateChannelBuffer update_channel_buffer = 128;
LeaveChannelBuffer leave_channel_buffer = 127; LeaveChannelBuffer leave_channel_buffer = 129;
UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 128; UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 130;
RejoinChannelBuffers rejoin_channel_buffers = 129; RejoinChannelBuffers rejoin_channel_buffers = 131;
RejoinChannelBuffersResponse rejoin_channel_buffers_response = 130; RejoinChannelBuffersResponse rejoin_channel_buffers_response = 132;
AckBufferOperation ack_buffer_operation = 143; AckBufferOperation ack_buffer_operation = 145;
JoinChannelChat join_channel_chat = 131; JoinChannelChat join_channel_chat = 133;
JoinChannelChatResponse join_channel_chat_response = 132; JoinChannelChatResponse join_channel_chat_response = 134;
LeaveChannelChat leave_channel_chat = 133; LeaveChannelChat leave_channel_chat = 135;
SendChannelMessage send_channel_message = 134; SendChannelMessage send_channel_message = 136;
SendChannelMessageResponse send_channel_message_response = 135; SendChannelMessageResponse send_channel_message_response = 137;
ChannelMessageSent channel_message_sent = 136; ChannelMessageSent channel_message_sent = 138;
GetChannelMessages get_channel_messages = 137; GetChannelMessages get_channel_messages = 139;
GetChannelMessagesResponse get_channel_messages_response = 138; GetChannelMessagesResponse get_channel_messages_response = 140;
RemoveChannelMessage remove_channel_message = 139; RemoveChannelMessage remove_channel_message = 141;
AckChannelMessage ack_channel_message = 144; AckChannelMessage ack_channel_message = 146;
LinkChannel link_channel = 140; LinkChannel link_channel = 142;
UnlinkChannel unlink_channel = 141; UnlinkChannel unlink_channel = 143;
MoveChannel move_channel = 142; // current max: 144 MoveChannel move_channel = 144; // current max: 146
} }
} }
@ -832,6 +834,17 @@ message ResolveState {
} }
} }
message ResolveCompletionDocumentation {
uint64 project_id = 1;
uint64 language_server_id = 2;
bytes lsp_completion = 3;
}
message ResolveCompletionDocumentationResponse {
string text = 1;
bool is_markdown = 2;
}
message ResolveInlayHint { message ResolveInlayHint {
uint64 project_id = 1; uint64 project_id = 1;
uint64 buffer_id = 2; uint64 buffer_id = 2;

View file

@ -205,6 +205,8 @@ messages!(
(OnTypeFormattingResponse, Background), (OnTypeFormattingResponse, Background),
(InlayHints, Background), (InlayHints, Background),
(InlayHintsResponse, Background), (InlayHintsResponse, Background),
(ResolveCompletionDocumentation, Background),
(ResolveCompletionDocumentationResponse, Background),
(ResolveInlayHint, Background), (ResolveInlayHint, Background),
(ResolveInlayHintResponse, Background), (ResolveInlayHintResponse, Background),
(RefreshInlayHints, Foreground), (RefreshInlayHints, Foreground),
@ -318,6 +320,10 @@ request_messages!(
(PrepareRename, PrepareRenameResponse), (PrepareRename, PrepareRenameResponse),
(OnTypeFormatting, OnTypeFormattingResponse), (OnTypeFormatting, OnTypeFormattingResponse),
(InlayHints, InlayHintsResponse), (InlayHints, InlayHintsResponse),
(
ResolveCompletionDocumentation,
ResolveCompletionDocumentationResponse
),
(ResolveInlayHint, ResolveInlayHintResponse), (ResolveInlayHint, ResolveInlayHintResponse),
(RefreshInlayHints, Ack), (RefreshInlayHints, Ack),
(ReloadBuffers, ReloadBuffersResponse), (ReloadBuffers, ReloadBuffersResponse),
@ -381,6 +387,7 @@ entity_messages!(
PerformRename, PerformRename,
OnTypeFormatting, OnTypeFormatting,
InlayHints, InlayHints,
ResolveCompletionDocumentation,
ResolveInlayHint, ResolveInlayHint,
RefreshInlayHints, RefreshInlayHints,
PrepareRename, PrepareRename,

View file

@ -6,4 +6,4 @@ pub use conn::Connection;
pub use peer::*; pub use peer::*;
mod macros; mod macros;
pub const PROTOCOL_VERSION: u32 = 64; pub const PROTOCOL_VERSION: u32 = 65;