From 11ffd36f35004607397f46346ffefd0045ee74fc Mon Sep 17 00:00:00 2001 From: WillLillis Date: Thu, 17 Oct 2024 20:02:54 -0400 Subject: [PATCH] Add docs for multiconfig, validation of project config paths --- README.md | 22 ++++++++++++++++++---- src/bin/main.rs | 4 ++++ src/lsp.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/types.rs | 12 +++++------- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 544696e3..a675bc42 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,13 @@ selectively target specific assemblers and/or instruction sets. Omitting an item from the `assemblers` or `instruction_sets` sections is equivalent to setting it to `false`. Be default, diagnostics are enabled and the server attempts to invoke `gcc` (and then `clang`) to generate them. If the `compiler` config field is specified, -the server will attempt to use the specified path to generate diagnostics. +the server will attempt to use the specified path to generate diagnostics. Different +configurations can be created for different sub-directories within your project as +`projects`. Source files not contained within any `project` configs will use the default +configuration if provided. ```toml +# Root-level, default configuration version = "0.1" [assemblers] @@ -67,14 +71,24 @@ nasm = false [instruction_sets] x86 = false x86_64 = true -z80 = false -arm = false -riscv = false +# z80 = false # Omitting a field is the same as setting it to false +# arm = false +# riscv = false [opts] compiler = "zig" # need "cc" as the first argument in `compile_flags.txt` diagnostics = true default_diagnostics = true + +# Configure the server's treatment of source files in the `arm-project` sub-directory +[[projects]] +path = "arm-project" + +[projects.assemblers] +gas = false + +[projects.instruction_sets] +arm = true ``` ### [OPTIONAL] Extend functionality via `compile_commands.json`/`compile_flags.txt` diff --git a/src/bin/main.rs b/src/bin/main.rs index 2f01c131..47800ab8 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -413,6 +413,10 @@ fn main_loop( info!("Recieved shutdown request"); return Ok(()); } else if let Ok((id, params)) = cast_req::(req.clone()) { + info!( + "Selected config: {:#?}", + config.get_config(¶ms.text_document_position_params.text_document.uri), + ); handle_hover_request( connection, id, diff --git a/src/lsp.rs b/src/lsp.rs index be592ace..321874f8 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -618,7 +618,6 @@ pub fn get_hover_resp( return instr_lookup; } - // TODO: Do the directive lookups next // directive lookup { if config.assemblers.gas.unwrap_or(false) || config.assemblers.masm.unwrap_or(false) { @@ -1134,7 +1133,8 @@ fn get_include_resp( } } -/// Filter out duplicate completion suggestions +/// Filter out duplicate completion suggestions, and those that aren't allowed +/// by `config` fn filtered_comp_list(comps: &[CompletionItem]) -> Vec { let mut seen = HashSet::new(); @@ -1771,8 +1771,47 @@ pub fn get_config(params: &InitializeParams) -> RootConfig { (None, None) => RootConfig::default(), }; - // TODO: Canonicalize `path`s in `projects` + // Validate project paths and enforce default diagnostics settings + if let Some(ref mut projects) = config.projects { + if let Some(ref path) = get_project_root(params) { + let mut project_idx = 0; + while project_idx < projects.len() { + let mut path = path.clone(); + path.push(&projects[project_idx].path); + let Ok(project_path) = path.canonicalize() else { + error!("Failed to canonicalize project path \"{}\", disabling this project configuration.", path.display()); + projects.remove(project_idx); + continue; + }; + if !project_path.is_dir() { + error!("Project path \"{}\" is not a directory, disabling this project configuration.", path.display()); + projects.remove(project_idx); + continue; + } + projects[project_idx].path = project_path; + // Want diagnostics enabled by default + if projects[project_idx].config.opts.diagnostics.is_none() { + projects[project_idx].config.opts.diagnostics = Some(true); + } + + // Want default diagnostics enabled by default + if projects[project_idx] + .config + .opts + .default_diagnostics + .is_none() + { + projects[project_idx].config.opts.default_diagnostics = Some(true); + } + project_idx += 1; + } + } else { + error!("Unable to detect project root directory. The projects configuration feature has been disabled."); + *projects = Vec::new(); + } + } + // Enforce default diagnostics settings for default config if let Some(ref mut default_cfg) = config.default_config { // Want diagnostics enabled by default if default_cfg.opts.diagnostics.is_none() { diff --git a/src/types.rs b/src/types.rs index b48b028c..1a4f980c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -827,7 +827,7 @@ impl Default for Assemblers { } impl Assemblers { - fn empty() -> Self { + const fn empty() -> Self { Self { gas: Some(false), go: Some(false), @@ -861,7 +861,7 @@ impl Default for InstructionSets { } impl InstructionSets { - fn empty() -> Self { + const fn empty() -> Self { Self { x86: Some(false), x86_64: Some(false), @@ -872,8 +872,6 @@ impl InstructionSets { } } -// TODO: Add logic handling empty configs (both `default_config` and `projects` = `None`) - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RootConfig { #[serde(flatten)] @@ -907,7 +905,7 @@ impl RootConfig { }; if let Some(projects) = &self.projects { for project in projects { - if project.path.starts_with(&request_path) { + if request_path.starts_with(&project.path) { return &project.config; } } @@ -1102,7 +1100,7 @@ impl Default for Config { impl Config { #[must_use] - pub fn empty() -> Self { + pub const fn empty() -> Self { Self { version: None, assemblers: Assemblers::empty(), @@ -1134,7 +1132,7 @@ impl Default for ConfigOptions { } impl ConfigOptions { - fn empty() -> Self { + const fn empty() -> Self { Self { compiler: None, diagnostics: Some(false),