diff --git a/asm-lsp/bin/main.rs b/asm-lsp/bin/main.rs index d8bf842e..5f360e0d 100644 --- a/asm-lsp/bin/main.rs +++ b/asm-lsp/bin/main.rs @@ -12,8 +12,8 @@ use asm_lsp::handle::{ handle_references_request, handle_signature_help_request, }; use asm_lsp::{ - get_compile_cmd_for_path, get_compile_cmds, get_completes, get_include_dirs, get_root_config, - CompletionItems, DocumentStore, NameToInfoMaps, RootConfig, + get_compile_cmd_for_req, get_compile_cmds_from_file, get_completes, get_include_dirs, + get_root_config, CompletionItems, DocumentStore, NameToInfoMaps, RootConfig, }; use compile_commands::{CompilationDatabase, SourceFile}; @@ -179,8 +179,10 @@ pub fn main() -> Result<()> { Some(CompletionItemKind::OPERATOR), ); - let compile_cmds = get_compile_cmds(¶ms).unwrap_or_default(); - info!("Loaded compile commands: {:?}", compile_cmds); + let compile_cmds = get_compile_cmds_from_file(¶ms).unwrap_or_default(); + if !compile_cmds.is_empty() { + info!("Loaded compile commands: {:?}", compile_cmds); + } let include_dirs = get_include_dirs(&compile_cmds); main_loop( @@ -301,25 +303,22 @@ fn main_loop( } else if let Ok((_id, params)) = cast_req::(req.clone()) { let project_config = config.get_config(¶ms.text_document.uri); - #[allow(clippy::option_if_let_else)] - let cmp_cmds = if let Some(cmd) = - get_compile_cmd_for_path(config, ¶ms.text_document.uri) - { - // If the user provided a compiler invocation command in their config - // for the project config covering this file, use it - &vec![cmd] - } else { - // Otherwise pass the default compile commands object - compile_cmds - }; - // Ok to unwrap, this should never be `None` if project_config.opts.as_ref().unwrap().diagnostics.unwrap() { + let compile_cmds = get_compile_cmd_for_req( + config, + ¶ms.text_document.uri, + compile_cmds, + ); + info!( + "Selected compile command(s) for request: {:#?}", + compile_cmds + ); handle_diagnostics( connection, ¶ms.text_document.uri, project_config, - cmp_cmds, + &compile_cmds, )?; info!( "Diagnostics request serviced in {}ms", @@ -353,22 +352,20 @@ fn main_loop( let project_config = config.get_config(¶ms.text_document.uri); // Ok to unwrap, this should never be `None` if project_config.opts.as_ref().unwrap().diagnostics.unwrap() { - #[allow(clippy::option_if_let_else)] - let cmp_cmds = if let Some(cmd) = - get_compile_cmd_for_path(config, ¶ms.text_document.uri) - { - // If the user provided a compiler invocation command in their config - // for the project config covering this file, use it - &vec![cmd] - } else { - // Otherwise pass the default compile commands object + let compile_cmds = get_compile_cmd_for_req( + config, + ¶ms.text_document.uri, + compile_cmds, + ); + info!( + "Selected compile command(s) for request: {:#?}", compile_cmds - }; + ); handle_diagnostics( connection, ¶ms.text_document.uri, project_config, - cmp_cmds, + &compile_cmds, )?; info!( "Published diagnostics on save in {}ms", diff --git a/asm-lsp/lsp.rs b/asm-lsp/lsp.rs index 3091d402..5bcbe8d1 100644 --- a/asm-lsp/lsp.rs +++ b/asm-lsp/lsp.rs @@ -311,7 +311,7 @@ fn get_additional_include_dirs(compile_cmds: &CompilationDatabase) -> Vec<(Sourc /// `CompilationDatabase` object /// /// If both are present, `compile_commands.json` will override `compile_flags.txt` -pub fn get_compile_cmds(params: &InitializeParams) -> Option { +pub fn get_compile_cmds_from_file(params: &InitializeParams) -> Option { if let Some(mut path) = get_project_root(params) { // Check the project root directory first let db = get_compilation_db_files(&path); @@ -348,16 +348,28 @@ fn get_compilation_db_files(path: &Path) -> Option { None } -/// Attempts to find the user-provided compile command for `req_uri`, as specified within a -/// `project` field in `.asm-lsp.toml` +/// Returns the compile command associated with `uri` if it exists, or the default +/// one from `compile_cmds` otherwise /// -/// NOTE: The `directory` field is intentionally left empty. If this function is being used -/// in a new place, please reconsider this assumption +/// - If the user specified a `compiler` *and* flags in their config, use that +/// - If the user specified a `compiler` but no flags in their config (`None`, +/// *not* an empty `Vec`), try to find flags from `compile_flags.txt` in +/// `compile_cmds` and combine the two +/// - If the user didn't specify any compiler info in the relevant config, return +/// the default commands from `compile_cmds` /// /// # Panics /// -/// Will panic if `req_uri` cannot be canonicalized -pub fn get_compile_cmd_for_path(config: &RootConfig, req_uri: &Uri) -> Option { +/// Will panic if `req_uri` can't be canonicalized +/// +/// NOTE: Several fields within the returned `CompilationDatabase` are intentionally left +/// uninitialized to avoid unnecessary allocations. If you're using this function +/// in a new place, please reconsider this assumption +pub fn get_compile_cmd_for_req( + config: &RootConfig, + req_uri: &Uri, + compile_cmds: &CompilationDatabase, +) -> CompilationDatabase { #[allow(irrefutable_let_patterns)] let Ok(req_path) = PathBuf::from_str(req_uri.path().as_str()) else { unreachable!() @@ -366,51 +378,61 @@ pub fn get_compile_cmd_for_path(config: &RootConfig, req_uri: &Uri) -> Option path, Err(e) => panic!("Invalid request path: \"{}\" - {e}", req_path.display()), }; - // if the path is within a project configuration and that project specifies - // a compiler and/or compiler flags, use it - if let Some(project) = config.get_project(&request_path) { - match ( - project.config.get_compiler(), - project.config.get_compile_flags_txt(), - ) { - // Specify this as a full compiler command - (Some(compiler), Some(flags)) => { - let compiler_with_args = { - let mut compile_cmd = vec![]; - compile_cmd.push(compiler.to_owned()); - compile_cmd.extend(flags.to_owned()); - if let Some(path) = request_path.to_str() { - // append the request path as the last compiler argument - compile_cmd.push(path.to_string()); - } - compile_cmd - }; - Some(CompileCommand { - file: SourceFile::File(request_path.clone()), - directory: PathBuf::new(), - arguments: Some(CompileArgs::Arguments(compiler_with_args)), - command: None, - output: None, - }) + let config = config.get_config(req_uri); + match (config.get_compiler(), config.get_compile_flags_txt()) { + (Some(compiler), Some(flags)) => { + // Fill out the full command invocation + let mut args = vec![compiler.to_owned()]; + args.append(&mut flags.clone()); + args.push(request_path.to_str().unwrap_or_default().to_string()); + vec![CompileCommand { + file: SourceFile::File(request_path), + directory: PathBuf::new(), + arguments: Some(CompileArgs::Arguments(args)), + command: None, + output: None, + }] + } + (Some(compiler), None) => { + // Fill out the full command invocation, check if `compile_cmds` + // has flags to tack on + let mut args = vec![compiler.to_owned()]; + // Check if we have flags as the first compile command from files, + // `compile_flags.txt` files get loaded as a single `CompileCommand` + // object as structured in the below `if` block + if compile_cmds.len() == 1 { + if let CompileCommand { + arguments: Some(CompileArgs::Flags(flags)), + .. + } = &compile_cmds[0] + { + args.append(&mut flags.clone()); + } } - // The default compilers will be added in `apply_compile_cmd`, just - // specify the flags - (None, Some(flags)) => Some(CompileCommand { - file: SourceFile::File(request_path.clone()), + args.push(request_path.to_str().unwrap_or_default().to_string()); + vec![CompileCommand { + file: SourceFile::File(request_path), directory: PathBuf::new(), - arguments: Some(CompileArgs::Flags(flags.to_owned())), + arguments: Some(CompileArgs::Arguments(args)), command: None, output: None, - }), - // - Do nothing if only `compiler` is specified, `get_default_compile_cmd` - // will generate a command with `compiler` in it - // - Do nothing if nothing is specified, a default compmand will be - // generated by `get_default_compile_cmd` in `handle_diagnostics` - (Some(_) | None, None) => None, - }; + }] + } + (None, Some(flags)) => { + // Fill out flags, no compiler + vec![CompileCommand { + file: SourceFile::File(request_path), + directory: PathBuf::new(), + arguments: Some(CompileArgs::Flags(flags.clone())), + command: None, + output: None, + }] + } + (None, None) => { + // Grab the default command from `compile_cmds` + compile_cmds.clone() + } } - - None } /// Returns a default `CompileCommand` for the provided `uri`. diff --git a/asm-lsp/types.rs b/asm-lsp/types.rs index 051e34d2..95eecfe3 100644 --- a/asm-lsp/types.rs +++ b/asm-lsp/types.rs @@ -657,6 +657,7 @@ pub enum Arch { #[serde(rename = "arm")] ARM, #[strum(serialize = "arm64")] + #[serde(rename = "arm64")] ARM64, #[strum(serialize = "riscv")] #[serde(rename = "riscv")]