|
| 1 | +use crate::{ |
| 2 | + types::{ |
| 3 | + CodeLensDisplay, DiagnosticsDisplay, DiagnosticsList, DocumentHighlightDisplay, |
| 4 | + HoverPreviewOption, RootMarkers, SelectionUI, UseVirtualText, |
| 5 | + }, |
| 6 | + vim::Vim, |
| 7 | +}; |
| 8 | +use anyhow::{anyhow, Result}; |
| 9 | +use lsp_types::{DiagnosticSeverity, MarkupKind, MessageType, TraceOption}; |
| 10 | +use serde::Deserialize; |
| 11 | +use std::collections::HashMap; |
| 12 | +use std::{path::PathBuf, str::FromStr, time::Duration}; |
| 13 | + |
| 14 | +#[derive(Debug)] |
| 15 | +pub struct Config { |
| 16 | + pub auto_start: bool, |
| 17 | + pub server_commands: HashMap<String, Vec<String>>, |
| 18 | + pub selection_ui: SelectionUI, |
| 19 | + pub trace: TraceOption, |
| 20 | + pub settings_path: Vec<String>, |
| 21 | + pub load_settings: bool, |
| 22 | + pub root_markers: Option<RootMarkers>, |
| 23 | + pub change_throttle: Option<Duration>, |
| 24 | + pub wait_output_timeout: Duration, |
| 25 | + pub diagnostics_enable: bool, |
| 26 | + pub diagnostics_list: DiagnosticsList, |
| 27 | + pub diagnostics_display: HashMap<u64, DiagnosticsDisplay>, |
| 28 | + pub code_lens_display: CodeLensDisplay, |
| 29 | + pub window_log_message_level: MessageType, |
| 30 | + pub hover_preview: HoverPreviewOption, |
| 31 | + pub completion_prefer_text_edit: bool, |
| 32 | + pub is_nvim: bool, |
| 33 | + pub logging_file: Option<PathBuf>, |
| 34 | + pub logging_level: log::LevelFilter, |
| 35 | + pub server_stderr: Option<String>, |
| 36 | + pub diagnostics_signs_max: Option<usize>, |
| 37 | + pub diagnostics_max_severity: DiagnosticSeverity, |
| 38 | + pub diagnostics_ignore_sources: Vec<String>, |
| 39 | + pub document_highlight_display: HashMap<u64, DocumentHighlightDisplay>, |
| 40 | + pub selection_ui_auto_open: bool, |
| 41 | + pub use_virtual_text: UseVirtualText, |
| 42 | + pub echo_project_root: bool, |
| 43 | + pub semantic_highlight_maps: HashMap<String, HashMap<String, String>>, |
| 44 | + pub semantic_scope_separator: String, |
| 45 | + pub apply_completion_text_edits: bool, |
| 46 | + pub preferred_markup_kind: Option<Vec<MarkupKind>>, |
| 47 | + pub hide_virtual_texts_on_insert: bool, |
| 48 | + pub enable_extensions: Option<HashMap<String, bool>>, |
| 49 | + pub restart_on_crash: bool, |
| 50 | + pub max_restart_retries: u8, |
| 51 | +} |
| 52 | + |
| 53 | +impl Default for Config { |
| 54 | + fn default() -> Self { |
| 55 | + Self { |
| 56 | + server_commands: HashMap::new(), |
| 57 | + semantic_highlight_maps: HashMap::new(), |
| 58 | + semantic_scope_separator: ":".into(), |
| 59 | + auto_start: true, |
| 60 | + selection_ui: SelectionUI::LocationList, |
| 61 | + selection_ui_auto_open: true, |
| 62 | + trace: TraceOption::default(), |
| 63 | + diagnostics_enable: true, |
| 64 | + diagnostics_list: DiagnosticsList::Quickfix, |
| 65 | + diagnostics_display: DiagnosticsDisplay::default(), |
| 66 | + code_lens_display: CodeLensDisplay::default(), |
| 67 | + diagnostics_signs_max: None, |
| 68 | + diagnostics_max_severity: DiagnosticSeverity::Hint, |
| 69 | + diagnostics_ignore_sources: vec![], |
| 70 | + document_highlight_display: DocumentHighlightDisplay::default(), |
| 71 | + window_log_message_level: MessageType::Warning, |
| 72 | + settings_path: vec![format!(".vim{}settings.json", std::path::MAIN_SEPARATOR)], |
| 73 | + load_settings: false, |
| 74 | + root_markers: None, |
| 75 | + change_throttle: None, |
| 76 | + wait_output_timeout: Duration::from_secs(10), |
| 77 | + hover_preview: HoverPreviewOption::default(), |
| 78 | + completion_prefer_text_edit: false, |
| 79 | + apply_completion_text_edits: true, |
| 80 | + use_virtual_text: UseVirtualText::All, |
| 81 | + hide_virtual_texts_on_insert: true, |
| 82 | + echo_project_root: true, |
| 83 | + server_stderr: None, |
| 84 | + preferred_markup_kind: None, |
| 85 | + enable_extensions: None, |
| 86 | + is_nvim: false, |
| 87 | + logging_file: None, |
| 88 | + logging_level: log::LevelFilter::Off, |
| 89 | + restart_on_crash: true, |
| 90 | + max_restart_retries: 5, |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +#[derive(Deserialize)] |
| 96 | +struct DeserializableConfig { |
| 97 | + logging_file: Option<PathBuf>, |
| 98 | + logging_level: log::LevelFilter, |
| 99 | + server_stderr: Option<String>, |
| 100 | + auto_start: u8, |
| 101 | + server_commands: HashMap<String, Vec<String>>, |
| 102 | + selection_ui: Option<String>, |
| 103 | + trace: Option<String>, |
| 104 | + settings_path: Vec<String>, |
| 105 | + load_settings: u8, |
| 106 | + root_markers: Option<RootMarkers>, |
| 107 | + change_throttle: Option<f64>, |
| 108 | + wait_output_timeout: Option<f64>, |
| 109 | + diagnostics_enable: u8, |
| 110 | + diagnostics_list: Option<String>, |
| 111 | + diagnostics_display: HashMap<u64, DiagnosticsDisplay>, |
| 112 | + window_log_message_level: String, |
| 113 | + hover_preview: Option<String>, |
| 114 | + completion_prefer_text_edit: u8, |
| 115 | + is_nvim: u8, |
| 116 | + diagnostics_signs_max: Option<usize>, |
| 117 | + diagnostics_max_severity: String, |
| 118 | + diagnostics_ignore_sources: Vec<String>, |
| 119 | + document_highlight_display: Option<HashMap<u64, DocumentHighlightDisplay>>, |
| 120 | + selection_ui_auto_open: u8, |
| 121 | + use_virtual_text: UseVirtualText, |
| 122 | + echo_project_root: u8, |
| 123 | + semantic_highlight_maps: HashMap<String, HashMap<String, String>>, |
| 124 | + semantic_scope_separator: String, |
| 125 | + apply_completion_text_edits: u8, |
| 126 | + preferred_markup_kind: Option<Vec<MarkupKind>>, |
| 127 | + hide_virtual_texts_on_insert: u8, |
| 128 | + enable_extensions: Option<HashMap<String, bool>>, |
| 129 | + code_lens_display: Option<CodeLensDisplay>, |
| 130 | + restart_on_crash: u8, |
| 131 | + max_restart_retries: u8, |
| 132 | +} |
| 133 | + |
| 134 | +impl Config { |
| 135 | + pub fn parse(vim: Vim) -> Result<Self> { |
| 136 | + let req = r#"{ |
| 137 | + "auto_start": !!get(g:, 'LanguageClient_autoStart', 1), |
| 138 | + "server_commands": s:GetVar('LanguageClient_serverCommands', {}), |
| 139 | + "selection_ui": s:getSelectionUI(), |
| 140 | + "trace": get(g:, 'LanguageClient_trace', v:null), |
| 141 | + "settings_path": map(s:ToList(get(g:, 'LanguageClient_settingsPath', '.vim/settings.json')), 'expand(v:val)'), |
| 142 | + "load_settings": !!get(g:, 'LanguageClient_loadSettings', 1), |
| 143 | + "root_markers": get(g:, 'LanguageClient_rootMarkers', v:null), |
| 144 | + "change_throttle": get(g:, 'LanguageClient_changeThrottle', v:null), |
| 145 | + "wait_output_timeout": get(g:, 'LanguageClient_waitOutputTimeout', v:null), |
| 146 | + "diagnostics_enable": !!get(g:, 'LanguageClient_diagnosticsEnable', 1), |
| 147 | + "diagnostics_list": get(g:, 'LanguageClient_diagnosticsList', 'Quickfix'), |
| 148 | + "diagnostics_display": get(g:, 'LanguageClient_diagnosticsDisplay', {}), |
| 149 | + "window_log_message_level": get(g:, 'LanguageClient_windowLogMessageLevel', 'Warning'), |
| 150 | + "hover_preview": get(g:, 'LanguageClient_hoverPreview', 'Auto'), |
| 151 | + "completion_prefer_text_edit": get(g:, 'LanguageClient_completionPreferTextEdit', 0), |
| 152 | + "is_nvim": has('nvim'), |
| 153 | + "diagnostics_signs_max": get(g:, 'LanguageClient_diagnosticsSignsMax', v:null), |
| 154 | + "diagnostics_max_severity": get(g:, 'LanguageClient_diagnosticsMaxSeverity', 'Hint'), |
| 155 | + "diagnostics_ignore_sources": get(g:, 'LanguageClient_diagnosticsIgnoreSources', []), |
| 156 | + "document_highlight_display": get(g:, 'LanguageClient_documentHighlightDisplay', {}), |
| 157 | + "selection_ui_auto_open": !!s:GetVar('LanguageClient_selectionUI_autoOpen', 1), |
| 158 | + "use_virtual_text": s:useVirtualText(), |
| 159 | + "echo_project_root": !!s:GetVar('LanguageClient_echoProjectRoot', 1), |
| 160 | + "semantic_highlight_maps": s:GetVar('LanguageClient_semanticHighlightMaps', {}), |
| 161 | + "semantic_scope_separator": s:GetVar('LanguageClient_semanticScopeSeparator', ':'), |
| 162 | + "apply_completion_text_edits": get(g:, 'LanguageClient_applyCompletionAdditionalTextEdits', 1), |
| 163 | + "preferred_markup_kind": get(g:, 'LanguageClient_preferredMarkupKind', v:null), |
| 164 | + "hide_virtual_texts_on_insert": s:GetVar('LanguageClient_hideVirtualTextsOnInsert', 0), |
| 165 | + "enable_extensions": get(g:, 'LanguageClient_enableExtensions', v:null), |
| 166 | + "code_lens_display": get(g:, 'LanguageClient_codeLensDisplay', v:null), |
| 167 | + "restart_on_crash": get(g:, 'LanguageClient_restartOnCrash', 1), |
| 168 | + "max_restart_retries": get(g:, 'LanguageClient_maxRestartRetries', 5), |
| 169 | + "logging_file": get(g:, 'LanguageClient_loggingFile', v:null), |
| 170 | + "logging_level": get(g:, 'LanguageClient_loggingLevel', 'WARN'), |
| 171 | + "server_stderr": get(g:, 'LanguageClient_serverStderr', v:null), |
| 172 | + }"#; |
| 173 | + |
| 174 | + let res: DeserializableConfig = vim.eval(req.replace("\n", ""))?; |
| 175 | + |
| 176 | + let loaded_fzf = vim.eval::<_, i64>("get(g:, 'loaded_fzf')")? == 1; |
| 177 | + let selection_ui = match res.selection_ui { |
| 178 | + Some(s) => SelectionUI::from_str(&s)?, |
| 179 | + None if loaded_fzf => SelectionUI::Funcref, |
| 180 | + None => SelectionUI::default(), |
| 181 | + }; |
| 182 | + |
| 183 | + let diagnostics_list = match res.diagnostics_list { |
| 184 | + Some(s) => DiagnosticsList::from_str(&s)?, |
| 185 | + None => DiagnosticsList::Disabled, |
| 186 | + }; |
| 187 | + |
| 188 | + let hover_preview = match res.hover_preview { |
| 189 | + Some(s) => HoverPreviewOption::from_str(&s)?, |
| 190 | + None => HoverPreviewOption::Auto, |
| 191 | + }; |
| 192 | + |
| 193 | + Ok(Config { |
| 194 | + auto_start: res.auto_start == 1, |
| 195 | + server_commands: res.server_commands, |
| 196 | + selection_ui, |
| 197 | + trace: trace(&res.trace.unwrap_or("off".to_string()))?, |
| 198 | + settings_path: res.settings_path, |
| 199 | + load_settings: res.load_settings == 1, |
| 200 | + root_markers: res.root_markers, |
| 201 | + change_throttle: res |
| 202 | + .change_throttle |
| 203 | + .map(|t| Duration::from_millis((t * 1000.0) as u64)), |
| 204 | + wait_output_timeout: Duration::from_millis( |
| 205 | + (res.wait_output_timeout.unwrap_or(10.0) * 1000.0) as u64, |
| 206 | + ), |
| 207 | + diagnostics_enable: res.diagnostics_enable == 1, |
| 208 | + diagnostics_list, |
| 209 | + diagnostics_display: res.diagnostics_display, |
| 210 | + code_lens_display: res.code_lens_display.unwrap_or_default(), |
| 211 | + window_log_message_level: message_type(&res.window_log_message_level)?, |
| 212 | + hover_preview, |
| 213 | + completion_prefer_text_edit: res.completion_prefer_text_edit == 1, |
| 214 | + is_nvim: res.is_nvim == 1, |
| 215 | + logging_file: res.logging_file, |
| 216 | + logging_level: res.logging_level, |
| 217 | + server_stderr: res.server_stderr, |
| 218 | + diagnostics_signs_max: res.diagnostics_signs_max, |
| 219 | + diagnostics_max_severity: diagnostics_severity(&res.diagnostics_max_severity)?, |
| 220 | + diagnostics_ignore_sources: res.diagnostics_ignore_sources, |
| 221 | + document_highlight_display: res.document_highlight_display.unwrap_or_default(), |
| 222 | + selection_ui_auto_open: res.selection_ui_auto_open == 1, |
| 223 | + use_virtual_text: res.use_virtual_text, |
| 224 | + echo_project_root: res.echo_project_root == 1, |
| 225 | + semantic_highlight_maps: res.semantic_highlight_maps, |
| 226 | + semantic_scope_separator: res.semantic_scope_separator, |
| 227 | + apply_completion_text_edits: res.apply_completion_text_edits == 1, |
| 228 | + preferred_markup_kind: res.preferred_markup_kind, |
| 229 | + hide_virtual_texts_on_insert: res.hide_virtual_texts_on_insert == 1, |
| 230 | + enable_extensions: res.enable_extensions, |
| 231 | + restart_on_crash: res.restart_on_crash == 1, |
| 232 | + max_restart_retries: res.max_restart_retries, |
| 233 | + }) |
| 234 | + } |
| 235 | +} |
| 236 | + |
| 237 | +fn trace(s: &str) -> Result<TraceOption> { |
| 238 | + match s.to_ascii_uppercase().as_str() { |
| 239 | + "OFF" => Ok(TraceOption::Off), |
| 240 | + "MESSAGES" => Ok(TraceOption::Messages), |
| 241 | + "VERBOSE" => Ok(TraceOption::Verbose), |
| 242 | + _ => Err(anyhow!("Invalid option for LanguageClient_trace: {}", s)), |
| 243 | + } |
| 244 | +} |
| 245 | + |
| 246 | +fn message_type(s: &str) -> Result<MessageType> { |
| 247 | + match s.to_ascii_uppercase().as_str() { |
| 248 | + "ERROR" => Ok(MessageType::Error), |
| 249 | + "WARNING" => Ok(MessageType::Warning), |
| 250 | + "INFO" => Ok(MessageType::Info), |
| 251 | + "LOG" => Ok(MessageType::Log), |
| 252 | + _ => Err(anyhow!( |
| 253 | + "Invalid option for LanguageClient_windowLogMessageLevel: {}", |
| 254 | + s, |
| 255 | + )), |
| 256 | + } |
| 257 | +} |
| 258 | + |
| 259 | +fn diagnostics_severity(s: &str) -> Result<DiagnosticSeverity> { |
| 260 | + match s.to_ascii_uppercase().as_str() { |
| 261 | + "ERROR" => Ok(DiagnosticSeverity::Error), |
| 262 | + "WARNING" => Ok(DiagnosticSeverity::Warning), |
| 263 | + "INFORMATION" => Ok(DiagnosticSeverity::Information), |
| 264 | + "HINT" => Ok(DiagnosticSeverity::Hint), |
| 265 | + _ => Err(anyhow!( |
| 266 | + "Invalid option for LanguageClient_diagnosticsMaxSeverity: {}", |
| 267 | + s |
| 268 | + )), |
| 269 | + } |
| 270 | +} |
0 commit comments