From 1830edb4369676084a8767c89c9e3dd50e35129e Mon Sep 17 00:00:00 2001 From: WillLillis Date: Fri, 25 Oct 2024 23:47:38 -0500 Subject: [PATCH] feat: Config builder --- Cargo.lock | 77 ++++++ Cargo.toml | 4 +- asm-lsp-cfg/.asm-lsp.toml | 11 + asm-lsp-cfg/Cargo.toml | 22 ++ asm-lsp-cfg/src/main.rs | 478 ++++++++++++++++++++++++++++++++++++++ asm-lsp/Cargo.toml | 4 +- asm-lsp/lsp.rs | 5 +- 7 files changed, 597 insertions(+), 4 deletions(-) create mode 100644 asm-lsp-cfg/.asm-lsp.toml create mode 100644 asm-lsp-cfg/Cargo.toml create mode 100644 asm-lsp-cfg/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 6fcbda81..d6f68e25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,6 +154,19 @@ dependencies = [ "url-escape", ] +[[package]] +name = "asm-lsp-cfg" +version = "0.1.0" +dependencies = [ + "anyhow", + "asm-lsp", + "clap", + "dialoguer", + "dirs", + "serde", + "toml", +] + [[package]] name = "asm_docs_parsing" version = "0.1.0" @@ -357,6 +370,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -427,6 +453,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "fuzzy-matcher", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "dirs" version = "5.0.1" @@ -477,6 +517,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -632,6 +678,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1729,6 +1784,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -1993,6 +2054,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -2164,6 +2235,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 6e5d34a6..9ca31e30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["asm-lsp", "asm_docs_parsing"] +members = ["asm-lsp", "asm-lsp-cfg", "asm_docs_parsing"] default-members = ["asm-lsp"] [workspace.package] @@ -20,6 +20,8 @@ license = "BSD-2-Clause" [workspace.dependencies] anyhow = "1.0.86" bincode = "1.3.3" +dirs = "5.0.1" +toml = "0.8.1" serde = { version = "1.0.204", features = ["derive"] } [workspace.lints.clippy] diff --git a/asm-lsp-cfg/.asm-lsp.toml b/asm-lsp-cfg/.asm-lsp.toml new file mode 100644 index 00000000..6b88a228 --- /dev/null +++ b/asm-lsp-cfg/.asm-lsp.toml @@ -0,0 +1,11 @@ +[[project]] +path = "" +version = "0.1" +assembler = "gas" +instruction_set = "x86" + +[[project]] +path = "src" +version = "0.1" +assembler = "gas" +instruction_set = "x86" diff --git a/asm-lsp-cfg/Cargo.toml b/asm-lsp-cfg/Cargo.toml new file mode 100644 index 00000000..6670f643 --- /dev/null +++ b/asm-lsp-cfg/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "asm-lsp-cfg" +version = "0.1.0" +description = "Generate configuration files for asm-lsp" +categories.workspace = true +keywords.workspace = true +readme.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +anyhow.workspace = true +serde.workspace = true +dirs.workspace = true +toml.workspace = true +asm-lsp = { path = "../asm-lsp" } +clap = { version = "4.5.8", features = ["derive"] } +dialoguer = { version = "0.11.0", features = ["fuzzy-select"] } + +[lints] +workspace = true diff --git a/asm-lsp-cfg/src/main.rs b/asm-lsp-cfg/src/main.rs new file mode 100644 index 00000000..af52ff6b --- /dev/null +++ b/asm-lsp-cfg/src/main.rs @@ -0,0 +1,478 @@ +use std::process::Command; +use std::string::ToString; +use std::{env::current_dir, path::PathBuf}; + +use anyhow::{anyhow, Result}; +use clap::Parser; +use dirs::config_dir; + +use asm_lsp::{types::RootConfig, Arch, Assembler, Config, ConfigOptions, ProjectConfig}; + +use dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input}; + +const ARCH_LIST: [Arch; 7] = [ + Arch::X86, + Arch::X86_64, + Arch::X86_AND_X86_64, + Arch::ARM, + Arch::ARM64, + Arch::RISCV, + Arch::Z80, +]; + +const ASSEMBLER_LIST: [Assembler; 4] = [ + Assembler::Gas, + Assembler::Go, + Assembler::Masm, + Assembler::Nasm, +]; + +#[derive(Parser, Debug, Clone)] +struct GenerateArgs { + #[clap( + long, + short, + help = "Directory to place .asm-lsp.toml into. (Default is the current directory)" + )] + pub output_dir: Option, + #[clap( + long, + short, + conflicts_with = "output_dir", + help = "Place the config in the global config directory" + )] + pub global_cfg: bool, + #[clap( + long, + short, + conflicts_with = "global_cfg", + help = "Path to the project this config is being generated for. (Default is the current directory)" + )] + pub project_path: Option, + #[clap( + short = 'w', + long, + help = "Overwrite any existing .asm-lsp.toml in the target directory" + )] + pub overwrite: bool, + #[clap( + short, + long, + help = "Don't display the generated config file after generation" + )] + pub quiet: bool, +} + +#[derive(Debug, Clone)] +struct GenerateOpts { + pub output_path: PathBuf, + pub project_path: PathBuf, + pub overwrite: bool, + pub quiet: bool, +} + +impl TryFrom for GenerateOpts { + type Error = String; + fn try_from(value: GenerateArgs) -> Result { + let output_path = { + if value.global_cfg { + let mut path = config_dir().ok_or_else(|| "Failed to detect config directory, try specifying it manually with `--output_dir`".to_string())?; + path.push("asm-lsp"); + path.push(".asm-lsp.toml"); + path + } else if let Some(path) = value.output_dir { + let mut canonicalized_path = path.canonicalize().map_err(|e| { + format!( + "Failed to canonicalize target path: \"{}\" -- {e}", + path.display() + ) + })?; + if !canonicalized_path.is_dir() { + let gave_file_name = canonicalized_path.ends_with(".asm-lsp.toml"); + return Err(format!( + "Target path \"{}\" is not a directory.{}", + canonicalized_path.display(), + if gave_file_name { + "Hint: Don't include the filename \".asm-lsp.toml\" at the end of your target path." + } else { + "" + } + )); + } + canonicalized_path.push(".asm-lsp.toml"); + canonicalized_path + } else { + let mut path = current_dir() + .map_err(|e| format!("Failed to detect current directory -- {e}"))?; + path.push(".asm-lsp.toml"); + path + } + }; + let project_path = { + if let Some(path) = value.project_path { + let canonicalized_path = path.canonicalize().map_err(|e| { + format!( + "Failed to canonicalize project path: \"{}\" -- {e}", + path.display() + ) + })?; + if !canonicalized_path.is_dir() { + return Err(format!( + "Project path \"{}\" is not a directory.", + canonicalized_path.display(), + )); + } + canonicalized_path + } else { + current_dir().map_err(|e| format!("Failed to detect current directory -- {e}"))? + } + }; + + Ok(Self { + output_path, + project_path, + overwrite: value.overwrite, + quiet: value.quiet, + }) + } +} + +fn prompt_arch() -> Arch { + let arch_choices: Vec = ARCH_LIST.iter().map(ToString::to_string).collect(); + let arch_selection = FuzzySelect::with_theme(&ColorfulTheme::default()) + .with_prompt("Select architecture") + .default(0) + .items(&arch_choices[..]) + .interact() + .unwrap(); + + ARCH_LIST[arch_selection] +} + +fn prompt_assembler() -> Assembler { + let assem_choices: Vec = ASSEMBLER_LIST.iter().map(ToString::to_string).collect(); + let assem_selection = FuzzySelect::with_theme(&ColorfulTheme::default()) + .with_prompt("Select assembler") + .default(0) + .items(&assem_choices[..]) + .interact() + .unwrap(); + + ASSEMBLER_LIST[assem_selection] +} + +fn prompt_project_path(opts: &GenerateOpts) -> PathBuf { + println!("Provide a project path:"); + let fallback_enter = |true_path: &mut PathBuf| { + println!( + "Warning: Failed to create directory reader for path \"{}\"", + true_path.display() + ); + let remaining_path: String = Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter remaining path (Enter an empty string to use the current path)") + .allow_empty(true) + .interact_text() + .unwrap(); + true_path.push(remaining_path); + }; + let mut true_path = opts.project_path.clone(); + let mut display_entries = Vec::new(); + let mut path_entries = Vec::new(); + loop { + let selection_text = format!("{}", true_path.display()); + // Dummy entry to account for the accept option as the first displayed + // option + path_entries.push(PathBuf::new()); + display_entries.push("