]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/caps.rs
Move highlight configuration from protocol into the feature
[rust.git] / crates / rust-analyzer / src / caps.rs
1 //! Advertises the capabilities of the LSP Server.
2 use lsp_types::{
3     CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
4     CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
5     CompletionOptionsCompletionItem, DeclarationCapability, DocumentOnTypeFormattingOptions,
6     FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
7     FileOperationRegistrationOptions, FoldingRangeProviderCapability, HoverProviderCapability,
8     ImplementationProviderCapability, InlayHintOptions, InlayHintServerCapabilities, OneOf,
9     RenameOptions, SaveOptions, SelectionRangeProviderCapability, SemanticTokensFullOptions,
10     SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions,
11     TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
12     TypeDefinitionProviderCapability, WorkDoneProgressOptions,
13     WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
14 };
15 use serde_json::json;
16
17 use crate::config::{Config, RustfmtConfig};
18 use crate::semantic_tokens;
19
20 pub fn server_capabilities(config: &Config) -> ServerCapabilities {
21     ServerCapabilities {
22         text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
23             open_close: Some(true),
24             change: Some(TextDocumentSyncKind::INCREMENTAL),
25             will_save: None,
26             will_save_wait_until: None,
27             save: Some(SaveOptions::default().into()),
28         })),
29         hover_provider: Some(HoverProviderCapability::Simple(true)),
30         completion_provider: Some(CompletionOptions {
31             resolve_provider: completions_resolve_provider(config.caps()),
32             trigger_characters: Some(vec![
33                 ":".to_string(),
34                 ".".to_string(),
35                 "'".to_string(),
36                 "(".to_string(),
37             ]),
38             all_commit_characters: None,
39             completion_item: completion_item(&config),
40             work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
41         }),
42         signature_help_provider: Some(SignatureHelpOptions {
43             trigger_characters: Some(vec!["(".to_string(), ",".to_string(), "<".to_string()]),
44             retrigger_characters: None,
45             work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
46         }),
47         declaration_provider: Some(DeclarationCapability::Simple(true)),
48         definition_provider: Some(OneOf::Left(true)),
49         type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
50         implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
51         references_provider: Some(OneOf::Left(true)),
52         document_highlight_provider: Some(OneOf::Left(true)),
53         document_symbol_provider: Some(OneOf::Left(true)),
54         workspace_symbol_provider: Some(OneOf::Left(true)),
55         code_action_provider: Some(code_action_capabilities(config.caps())),
56         code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
57         document_formatting_provider: Some(OneOf::Left(true)),
58         document_range_formatting_provider: match config.rustfmt() {
59             RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
60             _ => Some(OneOf::Left(false)),
61         },
62         document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
63             first_trigger_character: "=".to_string(),
64             more_trigger_character: Some(more_trigger_character(&config)),
65         }),
66         selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
67         folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
68         rename_provider: Some(OneOf::Right(RenameOptions {
69             prepare_provider: Some(true),
70             work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
71         })),
72         linked_editing_range_provider: None,
73         document_link_provider: None,
74         color_provider: None,
75         execute_command_provider: None,
76         workspace: Some(WorkspaceServerCapabilities {
77             workspace_folders: None,
78             file_operations: Some(WorkspaceFileOperationsServerCapabilities {
79                 did_create: None,
80                 will_create: None,
81                 did_rename: None,
82                 will_rename: Some(FileOperationRegistrationOptions {
83                     filters: vec![
84                         FileOperationFilter {
85                             scheme: Some(String::from("file")),
86                             pattern: FileOperationPattern {
87                                 glob: String::from("**/*.rs"),
88                                 matches: Some(FileOperationPatternKind::File),
89                                 options: None,
90                             },
91                         },
92                         FileOperationFilter {
93                             scheme: Some(String::from("file")),
94                             pattern: FileOperationPattern {
95                                 glob: String::from("**"),
96                                 matches: Some(FileOperationPatternKind::Folder),
97                                 options: None,
98                             },
99                         },
100                     ],
101                 }),
102                 did_delete: None,
103                 will_delete: None,
104             }),
105         }),
106         call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
107         semantic_tokens_provider: Some(
108             SemanticTokensOptions {
109                 legend: SemanticTokensLegend {
110                     token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
111                     token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
112                 },
113
114                 full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
115                 range: Some(true),
116                 work_done_progress_options: Default::default(),
117             }
118             .into(),
119         ),
120         moniker_provider: None,
121         inlay_hint_provider: Some(OneOf::Right(InlayHintServerCapabilities::Options(
122             InlayHintOptions {
123                 work_done_progress_options: Default::default(),
124                 resolve_provider: Some(true),
125             },
126         ))),
127         experimental: Some(json!({
128             "externalDocs": true,
129             "hoverRange": true,
130             "joinLines": true,
131             "matchingBrace": true,
132             "moveItem": true,
133             "onEnter": true,
134             "openCargoToml": true,
135             "parentModule": true,
136             "runnables": {
137                 "kinds": [ "cargo" ],
138             },
139             "ssr": true,
140             "workspaceSymbolScopeKindFiltering": true,
141         })),
142     }
143 }
144
145 fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
146     if completion_item_edit_resolve(client_caps) {
147         Some(true)
148     } else {
149         tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
150         None
151     }
152 }
153
154 /// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
155 pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
156     (|| {
157         Some(
158             caps.text_document
159                 .as_ref()?
160                 .completion
161                 .as_ref()?
162                 .completion_item
163                 .as_ref()?
164                 .resolve_support
165                 .as_ref()?
166                 .properties
167                 .iter()
168                 .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
169         )
170     })() == Some(true)
171 }
172
173 fn completion_item(config: &Config) -> Option<CompletionOptionsCompletionItem> {
174     Some(CompletionOptionsCompletionItem {
175         label_details_support: Some(config.completion_label_details_support()),
176     })
177 }
178
179 fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
180     client_caps
181         .text_document
182         .as_ref()
183         .and_then(|it| it.code_action.as_ref())
184         .and_then(|it| it.code_action_literal_support.as_ref())
185         .map_or(CodeActionProviderCapability::Simple(true), |_| {
186             CodeActionProviderCapability::Options(CodeActionOptions {
187                 // Advertise support for all built-in CodeActionKinds.
188                 // Ideally we would base this off of the client capabilities
189                 // but the client is supposed to fall back gracefully for unknown values.
190                 code_action_kinds: Some(vec![
191                     CodeActionKind::EMPTY,
192                     CodeActionKind::QUICKFIX,
193                     CodeActionKind::REFACTOR,
194                     CodeActionKind::REFACTOR_EXTRACT,
195                     CodeActionKind::REFACTOR_INLINE,
196                     CodeActionKind::REFACTOR_REWRITE,
197                 ]),
198                 resolve_provider: Some(true),
199                 work_done_progress_options: Default::default(),
200             })
201         })
202 }
203
204 fn more_trigger_character(config: &Config) -> Vec<String> {
205     let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()];
206     if config.snippet_cap() {
207         res.push("<".to_string());
208     }
209     res
210 }