Detailed changes
@@ -224,6 +224,7 @@ impl Server {
.add_request_handler(forward_project_request::<proto::OpenBufferByPath>)
.add_request_handler(forward_project_request::<proto::GetCompletions>)
.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::ApplyCodeAction>)
.add_request_handler(forward_project_request::<proto::PrepareRename>)
@@ -60,10 +60,10 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind};
use language::{
language_settings::{self, all_language_settings, InlayHintSettings},
- point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
- CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind, IndentSize,
- Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal,
- TransactionId,
+ markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
+ Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind,
+ IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection,
+ SelectionGoal, TransactionId,
};
use link_go_to_definition::{
hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
@@ -80,7 +80,7 @@ use ordered_float::OrderedFloat;
use parking_lot::RwLock;
use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
use rand::{seq::SliceRandom, thread_rng};
-use rpc::proto::PeerId;
+use rpc::proto::{self, PeerId};
use scroll::{
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
};
@@ -999,7 +999,7 @@ impl CompletionsMenu {
let completions = self.completions.clone();
let completions_guard = completions.read();
let completion = &completions_guard[index];
- if completion.lsp_completion.documentation.is_some() {
+ if completion.documentation.is_some() {
return;
}
@@ -1007,6 +1007,57 @@ impl CompletionsMenu {
let completion = completion.lsp_completion.clone();
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 {
return;
};
@@ -1037,11 +1088,14 @@ impl CompletionsMenu {
let mut completions = completions.write();
let completion = &mut completions[index];
- completion.documentation = documentation;
- completion.lsp_completion.documentation = Some(lsp_documentation);
+ completion.documentation = Some(documentation);
drop(completions);
_ = this.update(&mut cx, |_, cx| cx.notify());
+ } else {
+ let mut completions = completions.write();
+ let completion = &mut completions[index];
+ completion.documentation = Some(Documentation::Undocumented);
}
})
.detach();
@@ -1061,10 +1115,10 @@ impl CompletionsMenu {
.max_by_key(|(_, mat)| {
let completions = self.completions.read();
let completion = &completions[mat.candidate_id];
- let documentation = &completion.lsp_completion.documentation;
+ let documentation = &completion.documentation;
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();
}
@@ -149,28 +149,28 @@ pub async fn prepare_completion_documentation(
documentation: &lsp::Documentation,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
-) -> Option<Documentation> {
+) -> Documentation {
match documentation {
lsp::Documentation::String(text) => {
if text.lines().count() <= 1 {
- Some(Documentation::SingleLine(text.clone()))
+ Documentation::SingleLine(text.clone())
} else {
- Some(Documentation::MultiLinePlainText(text.clone()))
+ Documentation::MultiLinePlainText(text.clone())
}
}
lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind {
lsp::MarkupKind::PlainText => {
if value.lines().count() <= 1 {
- Some(Documentation::SingleLine(value.clone()))
+ Documentation::SingleLine(value.clone())
} else {
- Some(Documentation::MultiLinePlainText(value.clone()))
+ Documentation::MultiLinePlainText(value.clone())
}
}
lsp::MarkupKind::Markdown => {
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)]
pub enum Documentation {
+ Undocumented,
SingleLine(String),
MultiLinePlainText(String),
MultiLineMarkdown(ParsedMarkdown),
@@ -1466,12 +1466,14 @@ impl LspCommand for GetCompletions {
}
let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
- prepare_completion_documentation(
- lsp_docs,
- &language_registry,
- language.clone(),
+ Some(
+ prepare_completion_documentation(
+ lsp_docs,
+ &language_registry,
+ language.clone(),
+ )
+ .await,
)
- .await
} else {
None
};
@@ -580,6 +580,7 @@ impl Project {
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_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_refresh_inlay_hints);
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(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::ApplyCodeAction>,
@@ -89,88 +89,90 @@ message Envelope {
FormatBuffersResponse format_buffers_response = 70;
GetCompletions get_completions = 71;
GetCompletionsResponse get_completions_response = 72;
- ApplyCompletionAdditionalEdits apply_completion_additional_edits = 73;
- ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 74;
- GetCodeActions get_code_actions = 75;
- GetCodeActionsResponse get_code_actions_response = 76;
- GetHover get_hover = 77;
- GetHoverResponse get_hover_response = 78;
- ApplyCodeAction apply_code_action = 79;
- ApplyCodeActionResponse apply_code_action_response = 80;
- PrepareRename prepare_rename = 81;
- PrepareRenameResponse prepare_rename_response = 82;
- PerformRename perform_rename = 83;
- PerformRenameResponse perform_rename_response = 84;
- SearchProject search_project = 85;
- SearchProjectResponse search_project_response = 86;
-
- UpdateContacts update_contacts = 87;
- UpdateInviteInfo update_invite_info = 88;
- ShowContacts show_contacts = 89;
-
- GetUsers get_users = 90;
- FuzzySearchUsers fuzzy_search_users = 91;
- UsersResponse users_response = 92;
- RequestContact request_contact = 93;
- RespondToContactRequest respond_to_contact_request = 94;
- RemoveContact remove_contact = 95;
-
- Follow follow = 96;
- FollowResponse follow_response = 97;
- UpdateFollowers update_followers = 98;
- Unfollow unfollow = 99;
- GetPrivateUserInfo get_private_user_info = 100;
- GetPrivateUserInfoResponse get_private_user_info_response = 101;
- UpdateDiffBase update_diff_base = 102;
-
- OnTypeFormatting on_type_formatting = 103;
- OnTypeFormattingResponse on_type_formatting_response = 104;
-
- UpdateWorktreeSettings update_worktree_settings = 105;
-
- InlayHints inlay_hints = 106;
- InlayHintsResponse inlay_hints_response = 107;
- ResolveInlayHint resolve_inlay_hint = 108;
- ResolveInlayHintResponse resolve_inlay_hint_response = 109;
- RefreshInlayHints refresh_inlay_hints = 110;
-
- CreateChannel create_channel = 111;
- CreateChannelResponse create_channel_response = 112;
- InviteChannelMember invite_channel_member = 113;
- RemoveChannelMember remove_channel_member = 114;
- RespondToChannelInvite respond_to_channel_invite = 115;
- UpdateChannels update_channels = 116;
- JoinChannel join_channel = 117;
- DeleteChannel delete_channel = 118;
- GetChannelMembers get_channel_members = 119;
- GetChannelMembersResponse get_channel_members_response = 120;
- SetChannelMemberAdmin set_channel_member_admin = 121;
- RenameChannel rename_channel = 122;
- RenameChannelResponse rename_channel_response = 123;
-
- JoinChannelBuffer join_channel_buffer = 124;
- JoinChannelBufferResponse join_channel_buffer_response = 125;
- UpdateChannelBuffer update_channel_buffer = 126;
- LeaveChannelBuffer leave_channel_buffer = 127;
- UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 128;
- RejoinChannelBuffers rejoin_channel_buffers = 129;
- RejoinChannelBuffersResponse rejoin_channel_buffers_response = 130;
- AckBufferOperation ack_buffer_operation = 143;
-
- JoinChannelChat join_channel_chat = 131;
- JoinChannelChatResponse join_channel_chat_response = 132;
- LeaveChannelChat leave_channel_chat = 133;
- SendChannelMessage send_channel_message = 134;
- SendChannelMessageResponse send_channel_message_response = 135;
- ChannelMessageSent channel_message_sent = 136;
- GetChannelMessages get_channel_messages = 137;
- GetChannelMessagesResponse get_channel_messages_response = 138;
- RemoveChannelMessage remove_channel_message = 139;
- AckChannelMessage ack_channel_message = 144;
-
- LinkChannel link_channel = 140;
- UnlinkChannel unlink_channel = 141;
- MoveChannel move_channel = 142; // current max: 144
+ ResolveCompletionDocumentation resolve_completion_documentation = 73;
+ ResolveCompletionDocumentationResponse resolve_completion_documentation_response = 74;
+ ApplyCompletionAdditionalEdits apply_completion_additional_edits = 75;
+ ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 76;
+ GetCodeActions get_code_actions = 77;
+ GetCodeActionsResponse get_code_actions_response = 78;
+ GetHover get_hover = 79;
+ GetHoverResponse get_hover_response = 80;
+ ApplyCodeAction apply_code_action = 81;
+ ApplyCodeActionResponse apply_code_action_response = 82;
+ PrepareRename prepare_rename = 83;
+ PrepareRenameResponse prepare_rename_response = 84;
+ PerformRename perform_rename = 85;
+ PerformRenameResponse perform_rename_response = 86;
+ SearchProject search_project = 87;
+ SearchProjectResponse search_project_response = 88;
+
+ UpdateContacts update_contacts = 89;
+ UpdateInviteInfo update_invite_info = 90;
+ ShowContacts show_contacts = 91;
+
+ GetUsers get_users = 92;
+ FuzzySearchUsers fuzzy_search_users = 93;
+ UsersResponse users_response = 94;
+ RequestContact request_contact = 95;
+ RespondToContactRequest respond_to_contact_request = 96;
+ RemoveContact remove_contact = 97;
+
+ Follow follow = 98;
+ FollowResponse follow_response = 99;
+ UpdateFollowers update_followers = 100;
+ Unfollow unfollow = 101;
+ GetPrivateUserInfo get_private_user_info = 102;
+ GetPrivateUserInfoResponse get_private_user_info_response = 103;
+ UpdateDiffBase update_diff_base = 104;
+
+ OnTypeFormatting on_type_formatting = 105;
+ OnTypeFormattingResponse on_type_formatting_response = 106;
+
+ UpdateWorktreeSettings update_worktree_settings = 107;
+
+ InlayHints inlay_hints = 108;
+ InlayHintsResponse inlay_hints_response = 109;
+ ResolveInlayHint resolve_inlay_hint = 110;
+ ResolveInlayHintResponse resolve_inlay_hint_response = 111;
+ RefreshInlayHints refresh_inlay_hints = 112;
+
+ CreateChannel create_channel = 113;
+ CreateChannelResponse create_channel_response = 114;
+ InviteChannelMember invite_channel_member = 115;
+ RemoveChannelMember remove_channel_member = 116;
+ RespondToChannelInvite respond_to_channel_invite = 117;
+ UpdateChannels update_channels = 118;
+ JoinChannel join_channel = 119;
+ DeleteChannel delete_channel = 120;
+ GetChannelMembers get_channel_members = 121;
+ GetChannelMembersResponse get_channel_members_response = 122;
+ SetChannelMemberAdmin set_channel_member_admin = 123;
+ RenameChannel rename_channel = 124;
+ RenameChannelResponse rename_channel_response = 125;
+
+ JoinChannelBuffer join_channel_buffer = 126;
+ JoinChannelBufferResponse join_channel_buffer_response = 127;
+ UpdateChannelBuffer update_channel_buffer = 128;
+ LeaveChannelBuffer leave_channel_buffer = 129;
+ UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 130;
+ RejoinChannelBuffers rejoin_channel_buffers = 131;
+ RejoinChannelBuffersResponse rejoin_channel_buffers_response = 132;
+ AckBufferOperation ack_buffer_operation = 145;
+
+ JoinChannelChat join_channel_chat = 133;
+ JoinChannelChatResponse join_channel_chat_response = 134;
+ LeaveChannelChat leave_channel_chat = 135;
+ SendChannelMessage send_channel_message = 136;
+ SendChannelMessageResponse send_channel_message_response = 137;
+ ChannelMessageSent channel_message_sent = 138;
+ GetChannelMessages get_channel_messages = 139;
+ GetChannelMessagesResponse get_channel_messages_response = 140;
+ RemoveChannelMessage remove_channel_message = 141;
+ AckChannelMessage ack_channel_message = 146;
+
+ LinkChannel link_channel = 142;
+ UnlinkChannel unlink_channel = 143;
+ 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 {
uint64 project_id = 1;
uint64 buffer_id = 2;
@@ -205,6 +205,8 @@ messages!(
(OnTypeFormattingResponse, Background),
(InlayHints, Background),
(InlayHintsResponse, Background),
+ (ResolveCompletionDocumentation, Background),
+ (ResolveCompletionDocumentationResponse, Background),
(ResolveInlayHint, Background),
(ResolveInlayHintResponse, Background),
(RefreshInlayHints, Foreground),
@@ -318,6 +320,10 @@ request_messages!(
(PrepareRename, PrepareRenameResponse),
(OnTypeFormatting, OnTypeFormattingResponse),
(InlayHints, InlayHintsResponse),
+ (
+ ResolveCompletionDocumentation,
+ ResolveCompletionDocumentationResponse
+ ),
(ResolveInlayHint, ResolveInlayHintResponse),
(RefreshInlayHints, Ack),
(ReloadBuffers, ReloadBuffersResponse),
@@ -381,6 +387,7 @@ entity_messages!(
PerformRename,
OnTypeFormatting,
InlayHints,
+ ResolveCompletionDocumentation,
ResolveInlayHint,
RefreshInlayHints,
PrepareRename,
@@ -6,4 +6,4 @@ pub use conn::Connection;
pub use peer::*;
mod macros;
-pub const PROTOCOL_VERSION: u32 = 64;
+pub const PROTOCOL_VERSION: u32 = 65;