mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 06:41:48 +00:00
Auto merge of #17527 - Veykril:caps-config, r=Veykril
internal: Move capability querying out of the config module
This commit is contained in:
commit
8c2adec32c
10 changed files with 525 additions and 515 deletions
493
crates/rust-analyzer/src/capabilities.rs
Normal file
493
crates/rust-analyzer/src/capabilities.rs
Normal file
|
@ -0,0 +1,493 @@
|
||||||
|
//! Advertises the capabilities of the LSP Server.
|
||||||
|
use ide_db::{line_index::WideEncoding, FxHashSet};
|
||||||
|
use lsp_types::{
|
||||||
|
CallHierarchyServerCapability, CodeActionKind, CodeActionOptions, CodeActionProviderCapability,
|
||||||
|
CodeLensOptions, CompletionOptions, CompletionOptionsCompletionItem, DeclarationCapability,
|
||||||
|
DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
|
||||||
|
FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
|
||||||
|
HoverProviderCapability, ImplementationProviderCapability, InlayHintOptions,
|
||||||
|
InlayHintServerCapabilities, OneOf, PositionEncodingKind, RenameOptions, SaveOptions,
|
||||||
|
SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
|
||||||
|
SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
|
||||||
|
TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
|
||||||
|
WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
|
||||||
|
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
|
||||||
|
};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{Config, RustfmtConfig},
|
||||||
|
line_index::PositionEncoding,
|
||||||
|
lsp::{ext, semantic_tokens},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
||||||
|
ServerCapabilities {
|
||||||
|
position_encoding: match config.caps().negotiated_encoding() {
|
||||||
|
PositionEncoding::Utf8 => Some(PositionEncodingKind::UTF8),
|
||||||
|
PositionEncoding::Wide(wide) => match wide {
|
||||||
|
WideEncoding::Utf16 => Some(PositionEncodingKind::UTF16),
|
||||||
|
WideEncoding::Utf32 => Some(PositionEncodingKind::UTF32),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
|
||||||
|
open_close: Some(true),
|
||||||
|
change: Some(TextDocumentSyncKind::INCREMENTAL),
|
||||||
|
will_save: None,
|
||||||
|
will_save_wait_until: None,
|
||||||
|
save: Some(SaveOptions::default().into()),
|
||||||
|
})),
|
||||||
|
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||||
|
completion_provider: Some(CompletionOptions {
|
||||||
|
resolve_provider: config.caps().completions_resolve_provider(),
|
||||||
|
trigger_characters: Some(vec![
|
||||||
|
":".to_owned(),
|
||||||
|
".".to_owned(),
|
||||||
|
"'".to_owned(),
|
||||||
|
"(".to_owned(),
|
||||||
|
]),
|
||||||
|
all_commit_characters: None,
|
||||||
|
completion_item: config.caps().completion_item(),
|
||||||
|
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
||||||
|
}),
|
||||||
|
signature_help_provider: Some(SignatureHelpOptions {
|
||||||
|
trigger_characters: Some(vec!["(".to_owned(), ",".to_owned(), "<".to_owned()]),
|
||||||
|
retrigger_characters: None,
|
||||||
|
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
||||||
|
}),
|
||||||
|
declaration_provider: Some(DeclarationCapability::Simple(true)),
|
||||||
|
definition_provider: Some(OneOf::Left(true)),
|
||||||
|
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
||||||
|
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
|
||||||
|
references_provider: Some(OneOf::Left(true)),
|
||||||
|
document_highlight_provider: Some(OneOf::Left(true)),
|
||||||
|
document_symbol_provider: Some(OneOf::Left(true)),
|
||||||
|
workspace_symbol_provider: Some(OneOf::Left(true)),
|
||||||
|
code_action_provider: Some(config.caps().code_action_capabilities()),
|
||||||
|
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
|
||||||
|
document_formatting_provider: Some(OneOf::Left(true)),
|
||||||
|
document_range_formatting_provider: match config.rustfmt() {
|
||||||
|
RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
|
||||||
|
_ => Some(OneOf::Left(false)),
|
||||||
|
},
|
||||||
|
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
||||||
|
first_trigger_character: "=".to_owned(),
|
||||||
|
more_trigger_character: Some(more_trigger_character(config)),
|
||||||
|
}),
|
||||||
|
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
|
||||||
|
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
|
||||||
|
rename_provider: Some(OneOf::Right(RenameOptions {
|
||||||
|
prepare_provider: Some(true),
|
||||||
|
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
||||||
|
})),
|
||||||
|
linked_editing_range_provider: None,
|
||||||
|
document_link_provider: None,
|
||||||
|
color_provider: None,
|
||||||
|
execute_command_provider: None,
|
||||||
|
workspace: Some(WorkspaceServerCapabilities {
|
||||||
|
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
|
||||||
|
supported: Some(true),
|
||||||
|
change_notifications: Some(OneOf::Left(true)),
|
||||||
|
}),
|
||||||
|
file_operations: Some(WorkspaceFileOperationsServerCapabilities {
|
||||||
|
did_create: None,
|
||||||
|
will_create: None,
|
||||||
|
did_rename: None,
|
||||||
|
will_rename: Some(FileOperationRegistrationOptions {
|
||||||
|
filters: vec![
|
||||||
|
FileOperationFilter {
|
||||||
|
scheme: Some(String::from("file")),
|
||||||
|
pattern: FileOperationPattern {
|
||||||
|
glob: String::from("**/*.rs"),
|
||||||
|
matches: Some(FileOperationPatternKind::File),
|
||||||
|
options: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FileOperationFilter {
|
||||||
|
scheme: Some(String::from("file")),
|
||||||
|
pattern: FileOperationPattern {
|
||||||
|
glob: String::from("**"),
|
||||||
|
matches: Some(FileOperationPatternKind::Folder),
|
||||||
|
options: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
did_delete: None,
|
||||||
|
will_delete: None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
|
||||||
|
semantic_tokens_provider: Some(
|
||||||
|
SemanticTokensOptions {
|
||||||
|
legend: SemanticTokensLegend {
|
||||||
|
token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
|
||||||
|
token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
|
||||||
|
},
|
||||||
|
|
||||||
|
full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
|
||||||
|
range: Some(true),
|
||||||
|
work_done_progress_options: Default::default(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
moniker_provider: None,
|
||||||
|
inlay_hint_provider: Some(OneOf::Right(InlayHintServerCapabilities::Options(
|
||||||
|
InlayHintOptions {
|
||||||
|
work_done_progress_options: Default::default(),
|
||||||
|
resolve_provider: Some(true),
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
inline_value_provider: None,
|
||||||
|
experimental: Some(json!({
|
||||||
|
"externalDocs": true,
|
||||||
|
"hoverRange": true,
|
||||||
|
"joinLines": true,
|
||||||
|
"matchingBrace": true,
|
||||||
|
"moveItem": true,
|
||||||
|
"onEnter": true,
|
||||||
|
"openCargoToml": true,
|
||||||
|
"parentModule": true,
|
||||||
|
"runnables": {
|
||||||
|
"kinds": [ "cargo" ],
|
||||||
|
},
|
||||||
|
"ssr": true,
|
||||||
|
"workspaceSymbolScopeKindFiltering": true,
|
||||||
|
})),
|
||||||
|
diagnostic_provider: None,
|
||||||
|
inline_completion_provider: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Default)]
|
||||||
|
pub struct ClientCapabilities(lsp_types::ClientCapabilities);
|
||||||
|
|
||||||
|
impl ClientCapabilities {
|
||||||
|
pub fn new(caps: lsp_types::ClientCapabilities) -> Self {
|
||||||
|
Self(caps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn completions_resolve_provider(&self) -> Option<bool> {
|
||||||
|
self.completion_item_edit_resolve().then_some(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn experimental_bool(&self, index: &'static str) -> bool {
|
||||||
|
|| -> _ { self.0.experimental.as_ref()?.get(index)?.as_bool() }().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn experimental<T: serde::de::DeserializeOwned>(&self, index: &'static str) -> Option<T> {
|
||||||
|
serde_json::from_value(self.0.experimental.as_ref()?.get(index)?.clone()).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
|
||||||
|
pub fn completion_item_edit_resolve(&self) -> bool {
|
||||||
|
(|| {
|
||||||
|
Some(
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.completion
|
||||||
|
.as_ref()?
|
||||||
|
.completion_item
|
||||||
|
.as_ref()?
|
||||||
|
.resolve_support
|
||||||
|
.as_ref()?
|
||||||
|
.properties
|
||||||
|
.iter()
|
||||||
|
.any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
|
||||||
|
)
|
||||||
|
})() == Some(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn completion_label_details_support(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.completion
|
||||||
|
.as_ref()?
|
||||||
|
.completion_item
|
||||||
|
.as_ref()?
|
||||||
|
.label_details_support
|
||||||
|
.as_ref()
|
||||||
|
})()
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn completion_item(&self) -> Option<CompletionOptionsCompletionItem> {
|
||||||
|
Some(CompletionOptionsCompletionItem {
|
||||||
|
label_details_support: Some(self.completion_label_details_support()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn code_action_capabilities(&self) -> CodeActionProviderCapability {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|it| it.code_action.as_ref())
|
||||||
|
.and_then(|it| it.code_action_literal_support.as_ref())
|
||||||
|
.map_or(CodeActionProviderCapability::Simple(true), |_| {
|
||||||
|
CodeActionProviderCapability::Options(CodeActionOptions {
|
||||||
|
// Advertise support for all built-in CodeActionKinds.
|
||||||
|
// Ideally we would base this off of the client capabilities
|
||||||
|
// but the client is supposed to fall back gracefully for unknown values.
|
||||||
|
code_action_kinds: Some(vec![
|
||||||
|
CodeActionKind::EMPTY,
|
||||||
|
CodeActionKind::QUICKFIX,
|
||||||
|
CodeActionKind::REFACTOR,
|
||||||
|
CodeActionKind::REFACTOR_EXTRACT,
|
||||||
|
CodeActionKind::REFACTOR_INLINE,
|
||||||
|
CodeActionKind::REFACTOR_REWRITE,
|
||||||
|
]),
|
||||||
|
resolve_provider: Some(true),
|
||||||
|
work_done_progress_options: Default::default(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn negotiated_encoding(&self) -> PositionEncoding {
|
||||||
|
let client_encodings = match &self.0.general {
|
||||||
|
Some(general) => general.position_encodings.as_deref().unwrap_or_default(),
|
||||||
|
None => &[],
|
||||||
|
};
|
||||||
|
|
||||||
|
for enc in client_encodings {
|
||||||
|
if enc == &PositionEncodingKind::UTF8 {
|
||||||
|
return PositionEncoding::Utf8;
|
||||||
|
} else if enc == &PositionEncodingKind::UTF32 {
|
||||||
|
return PositionEncoding::Wide(WideEncoding::Utf32);
|
||||||
|
}
|
||||||
|
// NB: intentionally prefer just about anything else to utf-16.
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionEncoding::Wide(WideEncoding::Utf16)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn workspace_edit_resource_operations(
|
||||||
|
&self,
|
||||||
|
) -> Option<&[lsp_types::ResourceOperationKind]> {
|
||||||
|
self.0.workspace.as_ref()?.workspace_edit.as_ref()?.resource_operations.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens
|
||||||
|
})()
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_save_text_document_dynamic_registration(&self) -> bool {
|
||||||
|
let caps = (|| -> _ { self.0.text_document.as_ref()?.synchronization.clone() })()
|
||||||
|
.unwrap_or_default();
|
||||||
|
caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_change_watched_files_relative_pattern_support(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0.workspace.as_ref()?.did_change_watched_files.as_ref()?.relative_pattern_support
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn location_link(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.text_document.as_ref()?.definition?.link_support })().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_folding_only(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only })()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hierarchical_symbols(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.document_symbol
|
||||||
|
.as_ref()?
|
||||||
|
.hierarchical_document_symbol_support
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code_action_literals(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.code_action
|
||||||
|
.as_ref()?
|
||||||
|
.code_action_literal_support
|
||||||
|
.as_ref()
|
||||||
|
})()
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn work_done_progress(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.window.as_ref()?.work_done_progress })().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn will_rename(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.workspace.as_ref()?.file_operations.as_ref()?.will_rename })()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_annotation_support(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0.workspace.as_ref()?.workspace_edit.as_ref()?.change_annotation_support.as_ref()
|
||||||
|
})()
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code_action_resolve(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
Some(
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.code_action
|
||||||
|
.as_ref()?
|
||||||
|
.resolve_support
|
||||||
|
.as_ref()?
|
||||||
|
.properties
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter()
|
||||||
|
.any(|it| it == "edit")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_help_label_offsets(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.signature_help
|
||||||
|
.as_ref()?
|
||||||
|
.signature_information
|
||||||
|
.as_ref()?
|
||||||
|
.parameter_information
|
||||||
|
.as_ref()?
|
||||||
|
.label_offset_support
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code_action_group(&self) -> bool {
|
||||||
|
self.experimental_bool("codeActionGroup")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commands(&self) -> Option<ext::ClientCommandOptions> {
|
||||||
|
self.experimental("commands")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_docs(&self) -> bool {
|
||||||
|
self.experimental_bool("localDocs")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_server_logs(&self) -> bool {
|
||||||
|
self.experimental_bool("openServerLogs")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server_status_notification(&self) -> bool {
|
||||||
|
self.experimental_bool("serverStatusNotification")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snippet_text_edit(&self) -> bool {
|
||||||
|
self.experimental_bool("snippetTextEdit")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hover_actions(&self) -> bool {
|
||||||
|
self.experimental_bool("hoverActions")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the client supports colored output for full diagnostics from `checkOnSave`.
|
||||||
|
pub fn color_diagnostic_output(&self) -> bool {
|
||||||
|
self.experimental_bool("colorDiagnosticOutput")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_explorer(&self) -> bool {
|
||||||
|
self.experimental_bool("testExplorer")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn completion_snippet(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.completion
|
||||||
|
.as_ref()?
|
||||||
|
.completion_item
|
||||||
|
.as_ref()?
|
||||||
|
.snippet_support
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn semantic_tokens_refresh(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support })()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn code_lens_refresh(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.workspace.as_ref()?.code_lens.as_ref()?.refresh_support })()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inlay_hints_refresh(&self) -> bool {
|
||||||
|
(|| -> _ { self.0.workspace.as_ref()?.inlay_hint.as_ref()?.refresh_support })()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<String> {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|text| text.inlay_hint.as_ref())
|
||||||
|
.and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
|
||||||
|
.map(|inlay_resolve| inlay_resolve.properties.iter())
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect::<FxHashSet<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hover_markdown_support(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
Some(self.0.text_document.as_ref()?.hover.as_ref()?.content_format.as_ref()?.as_slice())
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.contains(&lsp_types::MarkupKind::Markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_replace_support(&self) -> bool {
|
||||||
|
(|| -> _ {
|
||||||
|
self.0
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.completion
|
||||||
|
.as_ref()?
|
||||||
|
.completion_item
|
||||||
|
.as_ref()?
|
||||||
|
.insert_replace_support
|
||||||
|
})()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn more_trigger_character(config: &Config) -> Vec<String> {
|
||||||
|
let mut res = vec![".".to_owned(), ">".to_owned(), "{".to_owned(), "(".to_owned()];
|
||||||
|
if config.snippet_cap().is_some() {
|
||||||
|
res.push("<".to_owned());
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
|
@ -1,230 +0,0 @@
|
||||||
//! Advertises the capabilities of the LSP Server.
|
|
||||||
use ide_db::line_index::WideEncoding;
|
|
||||||
use lsp_types::{
|
|
||||||
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
|
|
||||||
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
|
|
||||||
CompletionOptionsCompletionItem, DeclarationCapability, DocumentOnTypeFormattingOptions,
|
|
||||||
FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
|
|
||||||
FileOperationRegistrationOptions, FoldingRangeProviderCapability, HoverProviderCapability,
|
|
||||||
ImplementationProviderCapability, InlayHintOptions, InlayHintServerCapabilities, OneOf,
|
|
||||||
PositionEncodingKind, RenameOptions, SaveOptions, SelectionRangeProviderCapability,
|
|
||||||
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities,
|
|
||||||
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
|
|
||||||
TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
|
|
||||||
WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
|
|
||||||
WorkspaceServerCapabilities,
|
|
||||||
};
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::{Config, RustfmtConfig},
|
|
||||||
line_index::PositionEncoding,
|
|
||||||
lsp::semantic_tokens,
|
|
||||||
lsp_ext::negotiated_encoding,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
|
||||||
ServerCapabilities {
|
|
||||||
position_encoding: match negotiated_encoding(config.caps()) {
|
|
||||||
PositionEncoding::Utf8 => Some(PositionEncodingKind::UTF8),
|
|
||||||
PositionEncoding::Wide(wide) => match wide {
|
|
||||||
WideEncoding::Utf16 => Some(PositionEncodingKind::UTF16),
|
|
||||||
WideEncoding::Utf32 => Some(PositionEncodingKind::UTF32),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
|
|
||||||
open_close: Some(true),
|
|
||||||
change: Some(TextDocumentSyncKind::INCREMENTAL),
|
|
||||||
will_save: None,
|
|
||||||
will_save_wait_until: None,
|
|
||||||
save: Some(SaveOptions::default().into()),
|
|
||||||
})),
|
|
||||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
|
||||||
completion_provider: Some(CompletionOptions {
|
|
||||||
resolve_provider: completions_resolve_provider(config.caps()),
|
|
||||||
trigger_characters: Some(vec![
|
|
||||||
":".to_owned(),
|
|
||||||
".".to_owned(),
|
|
||||||
"'".to_owned(),
|
|
||||||
"(".to_owned(),
|
|
||||||
]),
|
|
||||||
all_commit_characters: None,
|
|
||||||
completion_item: completion_item(config),
|
|
||||||
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
|
||||||
}),
|
|
||||||
signature_help_provider: Some(SignatureHelpOptions {
|
|
||||||
trigger_characters: Some(vec!["(".to_owned(), ",".to_owned(), "<".to_owned()]),
|
|
||||||
retrigger_characters: None,
|
|
||||||
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
|
||||||
}),
|
|
||||||
declaration_provider: Some(DeclarationCapability::Simple(true)),
|
|
||||||
definition_provider: Some(OneOf::Left(true)),
|
|
||||||
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
|
||||||
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
|
|
||||||
references_provider: Some(OneOf::Left(true)),
|
|
||||||
document_highlight_provider: Some(OneOf::Left(true)),
|
|
||||||
document_symbol_provider: Some(OneOf::Left(true)),
|
|
||||||
workspace_symbol_provider: Some(OneOf::Left(true)),
|
|
||||||
code_action_provider: Some(code_action_capabilities(config.caps())),
|
|
||||||
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
|
|
||||||
document_formatting_provider: Some(OneOf::Left(true)),
|
|
||||||
document_range_formatting_provider: match config.rustfmt() {
|
|
||||||
RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
|
|
||||||
_ => Some(OneOf::Left(false)),
|
|
||||||
},
|
|
||||||
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
|
||||||
first_trigger_character: "=".to_owned(),
|
|
||||||
more_trigger_character: Some(more_trigger_character(config)),
|
|
||||||
}),
|
|
||||||
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
|
|
||||||
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
|
|
||||||
rename_provider: Some(OneOf::Right(RenameOptions {
|
|
||||||
prepare_provider: Some(true),
|
|
||||||
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
|
||||||
})),
|
|
||||||
linked_editing_range_provider: None,
|
|
||||||
document_link_provider: None,
|
|
||||||
color_provider: None,
|
|
||||||
execute_command_provider: None,
|
|
||||||
workspace: Some(WorkspaceServerCapabilities {
|
|
||||||
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
|
|
||||||
supported: Some(true),
|
|
||||||
change_notifications: Some(OneOf::Left(true)),
|
|
||||||
}),
|
|
||||||
file_operations: Some(WorkspaceFileOperationsServerCapabilities {
|
|
||||||
did_create: None,
|
|
||||||
will_create: None,
|
|
||||||
did_rename: None,
|
|
||||||
will_rename: Some(FileOperationRegistrationOptions {
|
|
||||||
filters: vec![
|
|
||||||
FileOperationFilter {
|
|
||||||
scheme: Some(String::from("file")),
|
|
||||||
pattern: FileOperationPattern {
|
|
||||||
glob: String::from("**/*.rs"),
|
|
||||||
matches: Some(FileOperationPatternKind::File),
|
|
||||||
options: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
FileOperationFilter {
|
|
||||||
scheme: Some(String::from("file")),
|
|
||||||
pattern: FileOperationPattern {
|
|
||||||
glob: String::from("**"),
|
|
||||||
matches: Some(FileOperationPatternKind::Folder),
|
|
||||||
options: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
did_delete: None,
|
|
||||||
will_delete: None,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
|
|
||||||
semantic_tokens_provider: Some(
|
|
||||||
SemanticTokensOptions {
|
|
||||||
legend: SemanticTokensLegend {
|
|
||||||
token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
|
|
||||||
token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
|
|
||||||
},
|
|
||||||
|
|
||||||
full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
|
|
||||||
range: Some(true),
|
|
||||||
work_done_progress_options: Default::default(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
moniker_provider: None,
|
|
||||||
inlay_hint_provider: Some(OneOf::Right(InlayHintServerCapabilities::Options(
|
|
||||||
InlayHintOptions {
|
|
||||||
work_done_progress_options: Default::default(),
|
|
||||||
resolve_provider: Some(true),
|
|
||||||
},
|
|
||||||
))),
|
|
||||||
inline_value_provider: None,
|
|
||||||
experimental: Some(json!({
|
|
||||||
"externalDocs": true,
|
|
||||||
"hoverRange": true,
|
|
||||||
"joinLines": true,
|
|
||||||
"matchingBrace": true,
|
|
||||||
"moveItem": true,
|
|
||||||
"onEnter": true,
|
|
||||||
"openCargoToml": true,
|
|
||||||
"parentModule": true,
|
|
||||||
"runnables": {
|
|
||||||
"kinds": [ "cargo" ],
|
|
||||||
},
|
|
||||||
"ssr": true,
|
|
||||||
"workspaceSymbolScopeKindFiltering": true,
|
|
||||||
})),
|
|
||||||
diagnostic_provider: None,
|
|
||||||
inline_completion_provider: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
|
|
||||||
if completion_item_edit_resolve(client_caps) {
|
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
|
|
||||||
pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
|
|
||||||
(|| {
|
|
||||||
Some(
|
|
||||||
caps.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.completion
|
|
||||||
.as_ref()?
|
|
||||||
.completion_item
|
|
||||||
.as_ref()?
|
|
||||||
.resolve_support
|
|
||||||
.as_ref()?
|
|
||||||
.properties
|
|
||||||
.iter()
|
|
||||||
.any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
|
|
||||||
)
|
|
||||||
})() == Some(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn completion_item(config: &Config) -> Option<CompletionOptionsCompletionItem> {
|
|
||||||
Some(CompletionOptionsCompletionItem {
|
|
||||||
label_details_support: Some(config.completion_label_details_support()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
|
|
||||||
client_caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|it| it.code_action.as_ref())
|
|
||||||
.and_then(|it| it.code_action_literal_support.as_ref())
|
|
||||||
.map_or(CodeActionProviderCapability::Simple(true), |_| {
|
|
||||||
CodeActionProviderCapability::Options(CodeActionOptions {
|
|
||||||
// Advertise support for all built-in CodeActionKinds.
|
|
||||||
// Ideally we would base this off of the client capabilities
|
|
||||||
// but the client is supposed to fall back gracefully for unknown values.
|
|
||||||
code_action_kinds: Some(vec![
|
|
||||||
CodeActionKind::EMPTY,
|
|
||||||
CodeActionKind::QUICKFIX,
|
|
||||||
CodeActionKind::REFACTOR,
|
|
||||||
CodeActionKind::REFACTOR_EXTRACT,
|
|
||||||
CodeActionKind::REFACTOR_INLINE,
|
|
||||||
CodeActionKind::REFACTOR_REWRITE,
|
|
||||||
]),
|
|
||||||
resolve_provider: Some(true),
|
|
||||||
work_done_progress_options: Default::default(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn more_trigger_character(config: &Config) -> Vec<String> {
|
|
||||||
let mut res = vec![".".to_owned(), ">".to_owned(), "{".to_owned(), "(".to_owned()];
|
|
||||||
if config.snippet_cap().is_some() {
|
|
||||||
res.push("<".to_owned());
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
|
@ -19,7 +19,6 @@ use ide_db::{
|
||||||
SnippetCap,
|
SnippetCap,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lsp_types::{ClientCapabilities, MarkupKind};
|
|
||||||
use paths::{Utf8Path, Utf8PathBuf};
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
|
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
|
||||||
|
@ -35,10 +34,9 @@ use triomphe::Arc;
|
||||||
use vfs::{AbsPath, AbsPathBuf, VfsPath};
|
use vfs::{AbsPath, AbsPathBuf, VfsPath};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
caps::completion_item_edit_resolve,
|
capabilities::ClientCapabilities,
|
||||||
diagnostics::DiagnosticsMapConfig,
|
diagnostics::DiagnosticsMapConfig,
|
||||||
line_index::PositionEncoding,
|
lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
|
||||||
lsp_ext::{self, negotiated_encoding, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod patch_old_style;
|
mod patch_old_style;
|
||||||
|
@ -659,7 +657,7 @@ pub struct Config {
|
||||||
discovered_projects: Vec<ProjectManifest>,
|
discovered_projects: Vec<ProjectManifest>,
|
||||||
/// The workspace roots as registered by the LSP client
|
/// The workspace roots as registered by the LSP client
|
||||||
workspace_roots: Vec<AbsPathBuf>,
|
workspace_roots: Vec<AbsPathBuf>,
|
||||||
caps: lsp_types::ClientCapabilities,
|
caps: ClientCapabilities,
|
||||||
root_path: AbsPathBuf,
|
root_path: AbsPathBuf,
|
||||||
snippets: Vec<Snippet>,
|
snippets: Vec<Snippet>,
|
||||||
visual_studio_code_version: Option<Version>,
|
visual_studio_code_version: Option<Version>,
|
||||||
|
@ -698,6 +696,15 @@ pub struct Config {
|
||||||
detached_files: Vec<AbsPathBuf>,
|
detached_files: Vec<AbsPathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delegate capability fetching methods
|
||||||
|
impl std::ops::Deref for Config {
|
||||||
|
type Target = ClientCapabilities;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.caps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn user_config_path(&self) -> &VfsPath {
|
pub fn user_config_path(&self) -> &VfsPath {
|
||||||
&self.user_config_path
|
&self.user_config_path
|
||||||
|
@ -954,23 +961,6 @@ impl ConfigChange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! try_ {
|
|
||||||
($expr:expr) => {
|
|
||||||
|| -> _ { Some($expr) }()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
macro_rules! try_or {
|
|
||||||
($expr:expr, $or:expr) => {
|
|
||||||
try_!($expr).unwrap_or($or)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! try_or_def {
|
|
||||||
($expr:expr) => {
|
|
||||||
try_!($expr).unwrap_or_default()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum LinkedProject {
|
pub enum LinkedProject {
|
||||||
ProjectManifest(ProjectManifest),
|
ProjectManifest(ProjectManifest),
|
||||||
|
@ -1177,7 +1167,7 @@ impl std::error::Error for ConfigErrors {}
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
root_path: AbsPathBuf,
|
root_path: AbsPathBuf,
|
||||||
caps: ClientCapabilities,
|
caps: lsp_types::ClientCapabilities,
|
||||||
workspace_roots: Vec<AbsPathBuf>,
|
workspace_roots: Vec<AbsPathBuf>,
|
||||||
visual_studio_code_version: Option<Version>,
|
visual_studio_code_version: Option<Version>,
|
||||||
user_config_path: Option<Utf8PathBuf>,
|
user_config_path: Option<Utf8PathBuf>,
|
||||||
|
@ -1205,7 +1195,7 @@ impl Config {
|
||||||
};
|
};
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
caps,
|
caps: ClientCapabilities::new(caps),
|
||||||
discovered_projects: Vec::new(),
|
discovered_projects: Vec::new(),
|
||||||
root_path,
|
root_path,
|
||||||
snippets: Default::default(),
|
snippets: Default::default(),
|
||||||
|
@ -1266,7 +1256,7 @@ impl Config {
|
||||||
&self.root_ratoml_path
|
&self.root_ratoml_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn caps(&self) -> &lsp_types::ClientCapabilities {
|
pub fn caps(&self) -> &ClientCapabilities {
|
||||||
&self.caps
|
&self.caps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1289,7 +1279,7 @@ impl Config {
|
||||||
CompletionConfig {
|
CompletionConfig {
|
||||||
enable_postfix_completions: self.completion_postfix_enable().to_owned(),
|
enable_postfix_completions: self.completion_postfix_enable().to_owned(),
|
||||||
enable_imports_on_the_fly: self.completion_autoimport_enable().to_owned()
|
enable_imports_on_the_fly: self.completion_autoimport_enable().to_owned()
|
||||||
&& completion_item_edit_resolve(&self.caps),
|
&& self.caps.completion_item_edit_resolve(),
|
||||||
enable_self_on_the_fly: self.completion_autoself_enable().to_owned(),
|
enable_self_on_the_fly: self.completion_autoself_enable().to_owned(),
|
||||||
enable_private_editable: self.completion_privateEditable_enable().to_owned(),
|
enable_private_editable: self.completion_privateEditable_enable().to_owned(),
|
||||||
full_function_signatures: self.completion_fullFunctionSignatures_enable().to_owned(),
|
full_function_signatures: self.completion_fullFunctionSignatures_enable().to_owned(),
|
||||||
|
@ -1298,16 +1288,7 @@ impl Config {
|
||||||
CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
|
CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
|
||||||
CallableCompletionDef::None => None,
|
CallableCompletionDef::None => None,
|
||||||
},
|
},
|
||||||
snippet_cap: SnippetCap::new(try_or_def!(
|
snippet_cap: SnippetCap::new(self.completion_snippet()),
|
||||||
self.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.completion
|
|
||||||
.as_ref()?
|
|
||||||
.completion_item
|
|
||||||
.as_ref()?
|
|
||||||
.snippet_support?
|
|
||||||
)),
|
|
||||||
insert_use: self.insert_use_config(source_root),
|
insert_use: self.insert_use_config(source_root),
|
||||||
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
|
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
|
||||||
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
|
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
|
||||||
|
@ -1359,7 +1340,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hover_actions(&self) -> HoverActionsConfig {
|
pub fn hover_actions(&self) -> HoverActionsConfig {
|
||||||
let enable = self.experimental("hoverActions") && self.hover_actions_enable().to_owned();
|
let enable = self.caps.hover_actions() && self.hover_actions_enable().to_owned();
|
||||||
HoverActionsConfig {
|
HoverActionsConfig {
|
||||||
implementations: enable && self.hover_actions_implementations_enable().to_owned(),
|
implementations: enable && self.hover_actions_implementations_enable().to_owned(),
|
||||||
references: enable && self.hover_actions_references_enable().to_owned(),
|
references: enable && self.hover_actions_references_enable().to_owned(),
|
||||||
|
@ -1385,17 +1366,7 @@ impl Config {
|
||||||
}),
|
}),
|
||||||
documentation: self.hover_documentation_enable().to_owned(),
|
documentation: self.hover_documentation_enable().to_owned(),
|
||||||
format: {
|
format: {
|
||||||
let is_markdown = try_or_def!(self
|
if self.caps.hover_markdown_support() {
|
||||||
.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.hover
|
|
||||||
.as_ref()?
|
|
||||||
.content_format
|
|
||||||
.as_ref()?
|
|
||||||
.as_slice())
|
|
||||||
.contains(&MarkupKind::Markdown);
|
|
||||||
if is_markdown {
|
|
||||||
HoverDocFormat::Markdown
|
HoverDocFormat::Markdown
|
||||||
} else {
|
} else {
|
||||||
HoverDocFormat::PlainText
|
HoverDocFormat::PlainText
|
||||||
|
@ -1409,17 +1380,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inlay_hints(&self) -> InlayHintsConfig {
|
pub fn inlay_hints(&self) -> InlayHintsConfig {
|
||||||
let client_capability_fields = self
|
let client_capability_fields = self.inlay_hint_resolve_support_properties();
|
||||||
.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|text| text.inlay_hint.as_ref())
|
|
||||||
.and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
|
|
||||||
.map(|inlay_resolve| inlay_resolve.properties.iter())
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.cloned()
|
|
||||||
.collect::<FxHashSet<_>>();
|
|
||||||
|
|
||||||
InlayHintsConfig {
|
InlayHintsConfig {
|
||||||
render_colons: self.inlayHints_renderColons().to_owned(),
|
render_colons: self.inlayHints_renderColons().to_owned(),
|
||||||
|
@ -1590,165 +1551,10 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn did_save_text_document_dynamic_registration(&self) -> bool {
|
|
||||||
let caps = try_or_def!(self.caps.text_document.as_ref()?.synchronization.clone()?);
|
|
||||||
caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
|
|
||||||
try_or_def!(
|
|
||||||
self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn did_change_watched_files_relative_pattern_support(&self) -> bool {
|
|
||||||
try_or_def!(
|
|
||||||
self.caps
|
|
||||||
.workspace
|
|
||||||
.as_ref()?
|
|
||||||
.did_change_watched_files
|
|
||||||
.as_ref()?
|
|
||||||
.relative_pattern_support?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prefill_caches(&self) -> bool {
|
pub fn prefill_caches(&self) -> bool {
|
||||||
self.cachePriming_enable().to_owned()
|
self.cachePriming_enable().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn location_link(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.text_document.as_ref()?.definition?.link_support?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn line_folding_only(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hierarchical_symbols(&self) -> bool {
|
|
||||||
try_or_def!(
|
|
||||||
self.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.document_symbol
|
|
||||||
.as_ref()?
|
|
||||||
.hierarchical_document_symbol_support?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code_action_literals(&self) -> bool {
|
|
||||||
try_!(self
|
|
||||||
.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.code_action
|
|
||||||
.as_ref()?
|
|
||||||
.code_action_literal_support
|
|
||||||
.as_ref()?)
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn work_done_progress(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.window.as_ref()?.work_done_progress?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn will_rename(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn change_annotation_support(&self) -> bool {
|
|
||||||
try_!(self
|
|
||||||
.caps
|
|
||||||
.workspace
|
|
||||||
.as_ref()?
|
|
||||||
.workspace_edit
|
|
||||||
.as_ref()?
|
|
||||||
.change_annotation_support
|
|
||||||
.as_ref()?)
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code_action_resolve(&self) -> bool {
|
|
||||||
try_or_def!(self
|
|
||||||
.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.code_action
|
|
||||||
.as_ref()?
|
|
||||||
.resolve_support
|
|
||||||
.as_ref()?
|
|
||||||
.properties
|
|
||||||
.as_slice())
|
|
||||||
.iter()
|
|
||||||
.any(|it| it == "edit")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn signature_help_label_offsets(&self) -> bool {
|
|
||||||
try_or_def!(
|
|
||||||
self.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.signature_help
|
|
||||||
.as_ref()?
|
|
||||||
.signature_information
|
|
||||||
.as_ref()?
|
|
||||||
.parameter_information
|
|
||||||
.as_ref()?
|
|
||||||
.label_offset_support?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn completion_label_details_support(&self) -> bool {
|
|
||||||
try_!(self
|
|
||||||
.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.completion
|
|
||||||
.as_ref()?
|
|
||||||
.completion_item
|
|
||||||
.as_ref()?
|
|
||||||
.label_details_support
|
|
||||||
.as_ref()?)
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool {
|
|
||||||
try_!(self.caps.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens?)
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position_encoding(&self) -> PositionEncoding {
|
|
||||||
negotiated_encoding(&self.caps)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn experimental(&self, index: &'static str) -> bool {
|
|
||||||
try_or_def!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code_action_group(&self) -> bool {
|
|
||||||
self.experimental("codeActionGroup")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_docs(&self) -> bool {
|
|
||||||
self.experimental("localDocs")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_server_logs(&self) -> bool {
|
|
||||||
self.experimental("openServerLogs")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn server_status_notification(&self) -> bool {
|
|
||||||
self.experimental("serverStatusNotification")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the client supports colored output for full diagnostics from `checkOnSave`.
|
|
||||||
pub fn color_diagnostic_output(&self) -> bool {
|
|
||||||
self.experimental("colorDiagnosticOutput")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_explorer(&self) -> bool {
|
|
||||||
self.experimental("testExplorer")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish_diagnostics(&self) -> bool {
|
pub fn publish_diagnostics(&self) -> bool {
|
||||||
self.diagnostics_enable().to_owned()
|
self.diagnostics_enable().to_owned()
|
||||||
}
|
}
|
||||||
|
@ -2026,7 +1832,7 @@ impl Config {
|
||||||
pub fn snippet_cap(&self) -> Option<SnippetCap> {
|
pub fn snippet_cap(&self) -> Option<SnippetCap> {
|
||||||
// FIXME: Also detect the proposed lsp version at caps.workspace.workspaceEdit.snippetEditSupport
|
// FIXME: Also detect the proposed lsp version at caps.workspace.workspaceEdit.snippetEditSupport
|
||||||
// once lsp-types has it.
|
// once lsp-types has it.
|
||||||
SnippetCap::new(self.experimental("snippetTextEdit"))
|
SnippetCap::new(self.snippet_text_edit())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_info(&self) -> CallInfoConfig {
|
pub fn call_info(&self) -> CallInfoConfig {
|
||||||
|
@ -2066,36 +1872,8 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn semantic_tokens_refresh(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn code_lens_refresh(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inlay_hints_refresh(&self) -> bool {
|
|
||||||
try_or_def!(self.caps.workspace.as_ref()?.inlay_hint.as_ref()?.refresh_support?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_replace_support(&self) -> bool {
|
|
||||||
try_or_def!(
|
|
||||||
self.caps
|
|
||||||
.text_document
|
|
||||||
.as_ref()?
|
|
||||||
.completion
|
|
||||||
.as_ref()?
|
|
||||||
.completion_item
|
|
||||||
.as_ref()?
|
|
||||||
.insert_replace_support?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn client_commands(&self) -> ClientCommandsConfig {
|
pub fn client_commands(&self) -> ClientCommandsConfig {
|
||||||
let commands =
|
let commands = self.commands();
|
||||||
try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null);
|
|
||||||
let commands: Option<lsp_ext::ClientCommandOptions> =
|
|
||||||
serde_json::from_value(commands.clone()).ok();
|
|
||||||
let force = commands.is_none() && *self.lens_forceCustomCommands();
|
let force = commands.is_none() && *self.lens_forceCustomCommands();
|
||||||
let commands = commands.map(|it| it.commands).unwrap_or_default();
|
let commands = commands.map(|it| it.commands).unwrap_or_default();
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ fn location(
|
||||||
let uri = url_from_abs_path(&file_name);
|
let uri = url_from_abs_path(&file_name);
|
||||||
|
|
||||||
let range = {
|
let range = {
|
||||||
let position_encoding = snap.config.position_encoding();
|
let position_encoding = snap.config.negotiated_encoding();
|
||||||
lsp_types::Range::new(
|
lsp_types::Range::new(
|
||||||
position(
|
position(
|
||||||
&position_encoding,
|
&position_encoding,
|
||||||
|
|
|
@ -529,7 +529,7 @@ impl GlobalStateSnapshot {
|
||||||
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
|
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
|
||||||
let endings = self.vfs.read().1[&file_id];
|
let endings = self.vfs.read().1[&file_id];
|
||||||
let index = self.analysis.file_line_index(file_id)?;
|
let index = self.analysis.file_line_index(file_id)?;
|
||||||
let res = LineIndex { index, endings, encoding: self.config.position_encoding() };
|
let res = LineIndex { index, endings, encoding: self.config.caps().negotiated_encoding() };
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ pub(crate) fn handle_did_change_text_document(
|
||||||
*version = params.text_document.version;
|
*version = params.text_document.version;
|
||||||
|
|
||||||
let new_contents = apply_document_changes(
|
let new_contents = apply_document_changes(
|
||||||
state.config.position_encoding(),
|
state.config.negotiated_encoding(),
|
||||||
std::str::from_utf8(data).unwrap(),
|
std::str::from_utf8(data).unwrap(),
|
||||||
params.content_changes,
|
params.content_changes,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2294,19 +2294,8 @@ fn to_url(path: VfsPath) -> Option<Url> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resource_ops_supported(config: &Config, kind: ResourceOperationKind) -> anyhow::Result<()> {
|
fn resource_ops_supported(config: &Config, kind: ResourceOperationKind) -> anyhow::Result<()> {
|
||||||
#[rustfmt::skip]
|
if !matches!(config.workspace_edit_resource_operations(), Some(resops) if resops.contains(&kind))
|
||||||
let resops = (|| {
|
{
|
||||||
config
|
|
||||||
.caps()
|
|
||||||
.workspace
|
|
||||||
.as_ref()?
|
|
||||||
.workspace_edit
|
|
||||||
.as_ref()?
|
|
||||||
.resource_operations
|
|
||||||
.as_ref()
|
|
||||||
})();
|
|
||||||
|
|
||||||
if !matches!(resops, Some(resops) if resops.contains(&kind)) {
|
|
||||||
return Err(LspError::new(
|
return Err(LspError::new(
|
||||||
ErrorCode::RequestFailed as i32,
|
ErrorCode::RequestFailed as i32,
|
||||||
format!(
|
format!(
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
|
||||||
mod caps;
|
mod capabilities;
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod diff;
|
mod diff;
|
||||||
mod dispatch;
|
mod dispatch;
|
||||||
|
@ -47,7 +47,8 @@ mod integrated_benchmarks;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
caps::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, version::version,
|
capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
|
||||||
|
version::version,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from_json<T: DeserializeOwned>(
|
pub fn from_json<T: DeserializeOwned>(
|
||||||
|
|
|
@ -4,19 +4,16 @@
|
||||||
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use ide_db::line_index::WideEncoding;
|
|
||||||
use lsp_types::request::Request;
|
use lsp_types::request::Request;
|
||||||
|
use lsp_types::Url;
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
|
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
|
||||||
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
|
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
|
||||||
};
|
};
|
||||||
use lsp_types::{PositionEncodingKind, Url};
|
|
||||||
use paths::Utf8PathBuf;
|
use paths::Utf8PathBuf;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::line_index::PositionEncoding;
|
|
||||||
|
|
||||||
pub enum InternalTestingFetchConfig {}
|
pub enum InternalTestingFetchConfig {}
|
||||||
|
|
||||||
impl Request for InternalTestingFetchConfig {
|
impl Request for InternalTestingFetchConfig {
|
||||||
|
@ -737,24 +734,6 @@ pub enum CodeLensResolveDataKind {
|
||||||
References(lsp_types::TextDocumentPositionParams),
|
References(lsp_types::TextDocumentPositionParams),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negotiated_encoding(caps: &lsp_types::ClientCapabilities) -> PositionEncoding {
|
|
||||||
let client_encodings = match &caps.general {
|
|
||||||
Some(general) => general.position_encodings.as_deref().unwrap_or_default(),
|
|
||||||
None => &[],
|
|
||||||
};
|
|
||||||
|
|
||||||
for enc in client_encodings {
|
|
||||||
if enc == &PositionEncodingKind::UTF8 {
|
|
||||||
return PositionEncoding::Utf8;
|
|
||||||
} else if enc == &PositionEncodingKind::UTF32 {
|
|
||||||
return PositionEncoding::Wide(WideEncoding::Utf32);
|
|
||||||
}
|
|
||||||
// NB: intentionally prefer just about anything else to utf-16.
|
|
||||||
}
|
|
||||||
|
|
||||||
PositionEncoding::Wide(WideEncoding::Utf16)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MoveItem {}
|
pub enum MoveItem {}
|
||||||
|
|
||||||
impl Request for MoveItem {
|
impl Request for MoveItem {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!---
|
<!---
|
||||||
lsp/ext.rs hash: a0867710490bf8da
|
lsp/ext.rs hash: 39b47906286ad9c
|
||||||
|
|
||||||
If you need to change the above hash to make the test pass, please check if you
|
If you need to change the above hash to make the test pass, please check if you
|
||||||
need to adjust this doc as well and ping this issue:
|
need to adjust this doc as well and ping this issue:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue