1 //! Config used by the language server.
3 //! We currently get this config from `initialize` LSP request, which is not the
4 //! best way to do it, but was the simplest thing we could implement.
6 //! Of particular interest is the `feature_flags` hash map: while other fields
7 //! configure the server itself, feature flags are passed into analysis, and
8 //! tweak things like automatic insertion of `()` in completions.
10 use std::{ffi::OsString, path::PathBuf};
12 use crate::diagnostics::DiagnosticsConfig;
13 use lsp_types::ClientCapabilities;
14 use ra_flycheck::FlycheckConfig;
15 use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
16 use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
17 use serde::Deserialize;
19 #[derive(Debug, Clone)]
21 pub client_caps: ClientCapsConfig,
23 pub publish_diagnostics: bool,
24 pub diagnostics: DiagnosticsConfig,
25 pub lru_capacity: Option<usize>,
26 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
27 pub files: FilesConfig,
28 pub notifications: NotificationsConfig,
30 pub cargo: CargoConfig,
31 pub rustfmt: RustfmtConfig,
32 pub check: Option<FlycheckConfig>,
34 pub inlay_hints: InlayHintsConfig,
35 pub completion: CompletionConfig,
36 pub assist: AssistConfig,
37 pub call_info_full: bool,
39 pub hover: HoverConfig,
41 pub with_sysroot: bool,
42 pub linked_projects: Vec<LinkedProject>,
43 pub root_path: PathBuf,
46 #[derive(Debug, Clone)]
47 pub enum LinkedProject {
48 ProjectManifest(ProjectManifest),
49 InlineJsonProject(JsonProject),
52 impl From<ProjectManifest> for LinkedProject {
53 fn from(v: ProjectManifest) -> Self {
54 LinkedProject::ProjectManifest(v)
58 impl From<JsonProject> for LinkedProject {
59 fn from(v: JsonProject) -> Self {
60 LinkedProject::InlineJsonProject(v)
64 #[derive(Clone, Debug, PartialEq, Eq)]
65 pub struct LensConfig {
68 pub impementations: bool,
71 impl Default for LensConfig {
72 fn default() -> Self {
73 Self { run: true, debug: true, impementations: true }
78 pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false };
80 pub fn any(&self) -> bool {
81 self.impementations || self.runnable()
84 pub fn none(&self) -> bool {
88 pub fn runnable(&self) -> bool {
89 self.run || self.debug
93 #[derive(Debug, Clone)]
94 pub struct FilesConfig {
95 pub watcher: FilesWatcher,
96 pub exclude: Vec<String>,
99 #[derive(Debug, Clone)]
100 pub enum FilesWatcher {
105 #[derive(Debug, Clone)]
106 pub struct NotificationsConfig {
107 pub cargo_toml_not_found: bool,
110 #[derive(Debug, Clone)]
111 pub enum RustfmtConfig {
113 extra_args: Vec<String>,
122 #[derive(Debug, Clone, Default)]
123 pub struct ClientCapsConfig {
124 pub location_link: bool,
125 pub line_folding_only: bool,
126 pub hierarchical_symbols: bool,
127 pub code_action_literals: bool,
128 pub work_done_progress: bool,
129 pub code_action_group: bool,
130 pub resolve_code_action: bool,
131 pub hover_actions: bool,
134 impl Default for Config {
135 fn default() -> Self {
137 client_caps: ClientCapsConfig::default(),
140 publish_diagnostics: true,
141 diagnostics: DiagnosticsConfig::default(),
143 proc_macro_srv: None,
144 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
145 notifications: NotificationsConfig { cargo_toml_not_found: true },
147 cargo: CargoConfig::default(),
148 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
149 check: Some(FlycheckConfig::CargoCommand {
150 command: "check".to_string(),
153 extra_args: Vec::new(),
154 features: Vec::new(),
157 inlay_hints: InlayHintsConfig {
159 parameter_hints: true,
160 chaining_hints: true,
163 completion: CompletionConfig {
164 enable_postfix_completions: true,
165 add_call_parenthesis: true,
166 add_call_argument_snippets: true,
167 ..CompletionConfig::default()
169 assist: AssistConfig::default(),
170 call_info_full: true,
171 lens: LensConfig::default(),
172 hover: HoverConfig::default(),
173 linked_projects: Vec::new(),
174 root_path: PathBuf::new(),
181 pub fn update(&mut self, value: &serde_json::Value) {
182 log::info!("Config::update({:#})", value);
184 let client_caps = self.client_caps.clone();
185 *self = Default::default();
186 self.client_caps = client_caps;
188 set(value, "/withSysroot", &mut self.with_sysroot);
189 set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
190 set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
191 set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
192 set(value, "/lruCapacity", &mut self.lru_capacity);
193 self.files.watcher = match get(value, "/files/watcher") {
194 Some("client") => FilesWatcher::Client,
195 Some("notify") | _ => FilesWatcher::Notify
197 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
199 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
200 set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
201 set(value, "/cargo/features", &mut self.cargo.features);
202 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
203 set(value, "/cargo/target", &mut self.cargo.target);
205 match get(value, "/procMacro/enable") {
207 if let Ok(path) = std::env::current_exe() {
208 self.proc_macro_srv = Some((path, vec!["proc-macro".into()]));
211 _ => self.proc_macro_srv = None,
214 match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
215 Some(mut args) if !args.is_empty() => {
216 let command = args.remove(0);
217 self.rustfmt = RustfmtConfig::CustomCommand {
223 if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
224 set(value, "/rustfmt/extraArgs", extra_args);
229 if let Some(false) = get(value, "/checkOnSave/enable") {
234 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
235 // first see if the user has completely overridden the command
236 Some(mut args) if !args.is_empty() => {
237 let command = args.remove(0);
238 self.check = Some(FlycheckConfig::CustomCommand {
243 // otherwise configure command customizations
245 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
248 set(value, "/checkOnSave/extraArgs", extra_args);
249 set(value, "/checkOnSave/command", command);
250 set(value, "/checkOnSave/allTargets", all_targets);
251 *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
252 *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
258 set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
259 set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
260 set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
261 set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
262 set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
263 set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
264 set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
265 set(value, "/callInfo/full", &mut self.call_info_full);
267 let mut lens_enabled = true;
268 set(value, "/lens/enable", &mut lens_enabled);
270 set(value, "/lens/run", &mut self.lens.run);
271 set(value, "/lens/debug", &mut self.lens.debug);
272 set(value, "/lens/implementations", &mut self.lens.impementations);
274 self.lens = LensConfig::NO_LENS;
277 if let Some(linked_projects) = get::<Vec<ManifestOrJsonProject>>(value, "/linkedProjects") {
278 if !linked_projects.is_empty() {
279 self.linked_projects.clear();
280 for linked_project in linked_projects {
281 let linked_project = match linked_project {
282 ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
286 ManifestOrJsonProject::JsonProject(it) => it.into(),
288 self.linked_projects.push(linked_project);
293 let mut use_hover_actions = false;
294 set(value, "/hoverActions/enable", &mut use_hover_actions);
295 if use_hover_actions {
296 set(value, "/hoverActions/implementations", &mut self.hover.implementations);
297 set(value, "/hoverActions/run", &mut self.hover.run);
298 set(value, "/hoverActions/debug", &mut self.hover.debug);
299 set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
301 self.hover = HoverConfig::NO_ACTIONS;
304 log::info!("Config::update() = {:#?}", self);
306 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
307 value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
310 fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
311 if let Some(new_value) = get(value, pointer) {
317 pub fn update_caps(&mut self, caps: &ClientCapabilities) {
318 if let Some(doc_caps) = caps.text_document.as_ref() {
319 if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
320 self.client_caps.location_link = value;
322 if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
324 self.client_caps.line_folding_only = value
326 if let Some(value) = doc_caps
329 .and_then(|it| it.hierarchical_document_symbol_support)
331 self.client_caps.hierarchical_symbols = value
334 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
336 self.client_caps.code_action_literals = value;
339 self.completion.allow_snippets(false);
340 if let Some(completion) = &doc_caps.completion {
341 if let Some(completion_item) = &completion.completion_item {
342 if let Some(value) = completion_item.snippet_support {
343 self.completion.allow_snippets(value);
349 if let Some(window_caps) = caps.window.as_ref() {
350 if let Some(value) = window_caps.work_done_progress {
351 self.client_caps.work_done_progress = value;
355 self.assist.allow_snippets(false);
356 if let Some(experimental) = &caps.experimental {
358 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
360 let snippet_text_edit = get_bool("snippetTextEdit");
361 self.assist.allow_snippets(snippet_text_edit);
363 self.client_caps.code_action_group = get_bool("codeActionGroup");
364 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
365 self.client_caps.hover_actions = get_bool("hoverActions");
370 #[derive(Deserialize)]
372 enum ManifestOrJsonProject {
374 JsonProject(JsonProject),