From ebbef29557d03f42ef74bda05c2c8c92e9425623 Mon Sep 17 00:00:00 2001 From: Andrew Fryer <3588046+flamewave000@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:53:20 -0400 Subject: [PATCH 1/4] Add --targets and autocomplete script --- Cargo.toml | 4 +++- autocomplete.sh | 13 +++++++++++++ src/compiler.rs | 31 ++++++++++++++++++++++++++----- src/lexer/mod.rs | 2 +- src/main.rs | 7 ++++++- src/parser/mod.rs | 3 +-- 6 files changed, 50 insertions(+), 10 deletions(-) create mode 100755 autocomplete.sh diff --git a/Cargo.toml b/Cargo.toml index 2b2a710..5831582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,6 @@ [package] name = "doit" version = "1.6.0" -edition = "2021" +edition = "2024" + +[dependencies] diff --git a/autocomplete.sh b/autocomplete.sh new file mode 100755 index 0000000..6afc52a --- /dev/null +++ b/autocomplete.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +_doit_completions() { + # Current word being completed + local cur="${COMP_WORDS[COMP_CWORD]}" + # Ask program for list of valid targets + local opts=$(doit -f --targets) + # Generate completions + COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) +} + + +complete -F _doit_completions doit \ No newline at end of file diff --git a/src/compiler.rs b/src/compiler.rs index 80fe688..7acf530 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,7 +1,7 @@ use std::{ fs::{self, File}, hash::{DefaultHasher, Hash, Hasher}, - io::{Error, ErrorKind, Read, Write}, + io::{Error, Read, Write}, path::Path, process::Command, }; @@ -9,10 +9,10 @@ use std::{ use crate::{ generator::Generator, lexer::{ - token::{TokenType, Tokenizer}, Lexer, + token::{TokenType, Tokenizer}, }, - parser::Parser, + parser::{Parser, nodes::NodeType}, utils::log, }; @@ -23,6 +23,7 @@ pub enum CompileMode { PRINT_TOKENS, PRINT_NODES, PRINT_SOURCE, + PRINT_TARGETS, } pub fn build(directory: &String, filename: &str, keep: bool, force: bool, mode: CompileMode) -> Result<(), Error> { @@ -37,7 +38,10 @@ pub fn build(directory: &String, filename: &str, keep: bool, force: bool, mode: let mut hasher = DefaultHasher::new(); source.hash(&mut hasher); let new_hash = hasher.finish(); - if !force && Path::new(&(directory.to_owned() + "/hash")).exists() && Path::new(&(directory.to_owned() + "/targets")).exists() { + if !force + && Path::new(&(directory.to_owned() + "/hash")).exists() + && Path::new(&(directory.to_owned() + "/targets")).exists() + { let old_hash = fs::read_to_string(directory.to_owned() + "/hash")?; if old_hash == new_hash.to_string() { return Ok(()); @@ -48,6 +52,7 @@ pub fn build(directory: &String, filename: &str, keep: bool, force: bool, mode: match mode { CompileMode::PRINT_TOKENS => print_tokens(&mut Lexer::new(filename, &source)), CompileMode::PRINT_NODES => print_nodes(&mut Parser::new(&mut Lexer::new(filename, &source))), + CompileMode::PRINT_TARGETS => print_targets(&mut Parser::new(&mut Lexer::new(filename, &source))), _ => { let mut lexer = Lexer::new(filename, &source); let mut parser = Parser::new(&mut lexer); @@ -78,7 +83,7 @@ fn compile(directory: &String, source: &str) -> Result<(), Error> { .args(["--std=c++20", cpp, "-o", &(directory.to_owned() + "/targets")]) .spawn()?; if !output.wait()?.success() { - return Err(Error::new(ErrorKind::Other, "Failed to compile")); + return Err(Error::other("Failed to compile")); } Ok(()) } @@ -103,3 +108,19 @@ fn print_nodes(parser: &mut Parser) { Err(err) => log::error(&format!("Failed to parse: {}", err)), }; } + +fn print_targets(parser: &mut Parser) { + match parser.parse() { + Ok(root) => { + let mut filtered: Vec = root + .children + .into_iter() + .filter(|node| node.ntype == NodeType::TARGET) + .filter_map(|node| node.value.value) + .collect(); + filtered.sort(); + filtered.into_iter().for_each(|n| println!("{}", n)); + } + Err(err) => log::error(&format!("Failed to parse: {}", err)), + }; +} diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 7ff597c..63ac372 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -73,7 +73,7 @@ impl Lexer<'_> { fn consume_and_ignore(&mut self) -> Result<(), Error> { match self.consume() { Ok(_) => Ok(()), - Err(err) => Err(Error::new(ErrorKind::Other, err)), + Err(err) => Err(Error::other(err)), } } diff --git a/src/main.rs b/src/main.rs index bfbe9cf..b8dbf43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,7 @@ fn print_help(program_name: &str) { println!(" \x1b[90m--nodes\x1b[0m Print out the parser node tree instead of fully compiling."); println!(" \x1b[90m--source\x1b[0m Print the transpiled C++ code to stdout instead of fully compiling."); println!(" \x1b[90m--keep\x1b[0m After compiling, do not delete the .doit/targets.cpp file."); + println!(" \x1b[90m--targets\x1b[0m Print the targets to stdout. Used for autocompletion."); println!(" \x1b[90m--help\x1b[0m Prints out this help message."); } @@ -37,6 +38,7 @@ fn main() -> ExitCode { let mut print_tokens = false; let mut print_nodes = false; let mut print_source = false; + let mut print_targets = false; let mut keep_source = false; let mut filename: String = String::new(); while !args.is_empty() && args[0].starts_with('-') { @@ -46,6 +48,7 @@ fn main() -> ExitCode { "--nodes" => print_nodes = true, "--source" => print_source = true, "--keep" => keep_source = true, + "--targets" => print_targets = true, "--help" => { print_help(&program_name); exit(0); @@ -115,6 +118,8 @@ fn main() -> ExitCode { CompileMode::PRINT_NODES } else if print_source { CompileMode::PRINT_SOURCE + } else if print_targets { + CompileMode::PRINT_TARGETS } else { CompileMode::NORMAL }, @@ -122,7 +127,7 @@ fn main() -> ExitCode { log::error(&err.to_string()); return ExitCode::from(1); } - if print_tokens || print_nodes || print_source { + if print_tokens || print_nodes || print_source || print_targets { return ExitCode::from(0); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 02f99af..96aa065 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -126,8 +126,7 @@ impl Parser<'_> { )), TokenType::HELP => { if scope.help.is_some() { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "Help block has already been defined for the current scope.", )); } From 668a4f0b61f9570fe531167813a7e8060f7f56c1 Mon Sep 17 00:00:00 2001 From: Andrew Fryer <3588046+flamewave000@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:09:01 -0400 Subject: [PATCH 2/4] Add --completion to print completion script --- README.md | 9 +++++++++ autocomplete.sh | 3 --- src/main.rs | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 827e93c..7161c9c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,15 @@ So I know you can just use something like a Makefile to build things, but I get 1. Download executable. 2. Copy to `/usr/local/bin` or place somewhere and link it like above. +### Bash Autocompletion + +DOIT supports target auto-completion in bash. This allows you to `doit ` and get a list of target options, as well as autocompletion for partial entries (ie. `doit cl` will autocomplete to `doit clean` if "clean" is a valid target in your DOIT script.). + +To get auto-completion, simply add the following to your local `.bashrc` file. +```sh +. <(doit --completion) +``` + ## Build Simple run the following commands in the project folder. Requires no additional crates or dependencies to build. diff --git a/autocomplete.sh b/autocomplete.sh index 6afc52a..82df13d 100755 --- a/autocomplete.sh +++ b/autocomplete.sh @@ -1,5 +1,4 @@ #!/bin/bash - _doit_completions() { # Current word being completed local cur="${COMP_WORDS[COMP_CWORD]}" @@ -8,6 +7,4 @@ _doit_completions() { # Generate completions COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) } - - complete -F _doit_completions doit \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b8dbf43..aca875e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,16 @@ fn main() -> ExitCode { "--source" => print_source = true, "--keep" => keep_source = true, "--targets" => print_targets = true, + "--completion" => { + println!("{}", r##"#!/bin/bash +_doit_completions() { + local cur="${COMP_WORDS[COMP_CWORD]}" + local opts=$(doit -f --targets) + COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) +} +complete -F _doit_completions doit"##); + exit(0); + }, "--help" => { print_help(&program_name); exit(0); From d889224a55b3b5d9155f87960c53deb980ec5fcd Mon Sep 17 00:00:00 2001 From: Andrew Fryer <3588046+flamewave000@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:15:35 -0400 Subject: [PATCH 3/4] Fix lint errors --- src/main.rs | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index aca875e..97822c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] use std::{ - env, fs, path::Path, process::{exit, Command, ExitCode} + env, fs, + path::Path, + process::{Command, ExitCode, exit}, }; mod compiler; @@ -14,7 +16,10 @@ use compiler::CompileMode; use crate::utils::log; fn print_help(program_name: &str) { - println!("\x1b[32mUsage: \x1b[33m{} \x1b[90m[options] \x1b[34m \x1b[90m[target_params]\x1b[0m\n", program_name); + println!( + "\x1b[32mUsage: \x1b[33m{} \x1b[90m[options] \x1b[34m \x1b[90m[target_params]\x1b[0m\n", + program_name + ); println!("If the target is ommitted, command will print out a list of available targets."); println!(); println!("\x1b[32m Options:\x1b[0m"); @@ -50,34 +55,35 @@ fn main() -> ExitCode { "--keep" => keep_source = true, "--targets" => print_targets = true, "--completion" => { - println!("{}", r##"#!/bin/bash -_doit_completions() { - local cur="${COMP_WORDS[COMP_CWORD]}" + println!( + r##"#!/bin/bash +_doit_completions() {{ + local cur="${{COMP_WORDS[COMP_CWORD]}}" local opts=$(doit -f --targets) COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) -} -complete -F _doit_completions doit"##); +}} +complete -F _doit_completions doit"## + ); exit(0); - }, + } "--help" => { print_help(&program_name); exit(0); - }, + } "--version" => { println!("v{}", env!("CARGO_PKG_VERSION")); exit(0); - }, + } "-t" => filename = args.remove(0), "-c" => { - if Path::new("./.doit").exists() { - if let Err(err) = fs::remove_dir_all("./.doit") { - log::error(&err.to_string()); - return ExitCode::from(1); - } + if Path::new("./.doit").exists() + && let Err(err) = fs::remove_dir_all("./.doit") { + log::error(&err.to_string()); + return ExitCode::from(1); } log::info("Cleaned!"); exit(0); - }, + } fail => { log::error(&format!("Unknown option: {}", fail)); print_help(&program_name); @@ -98,12 +104,18 @@ complete -F _doit_completions doit"##); if let Some(path_str) = path.to_str() { filename = path_str.to_owned(); } else { - log::error(&format!("Could not calculate and absolute path for the provided doit file '{}'", filename)); + log::error(&format!( + "Could not calculate and absolute path for the provided doit file '{}'", + filename + )); return ExitCode::from(1); } - }, + } Err(err) => { - log::error(&format!("Could not calculate and absolute path for the provided doit file '{}'", filename)); + log::error(&format!( + "Could not calculate and absolute path for the provided doit file '{}'", + filename + )); log::error(&err.to_string()); return ExitCode::from(1); } @@ -151,7 +163,7 @@ complete -F _doit_completions doit"##); let code = status.code().unwrap_or(if status.success() { 0 } else { 1 }); log::debug(&format!("Exit Code: {}", code)); ExitCode::from(code as u8) - }, + } Err(err) => { log::error(&err.to_string()); ExitCode::from(1) From 961761b8838937ddff14bb3460f0d15d90b1166f Mon Sep 17 00:00:00 2001 From: Andrew Fryer <3588046+flamewave000@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:17:27 -0400 Subject: [PATCH 4/4] v1.7.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b02ad91..c63d7ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 4 [[package]] name = "doit" -version = "1.6.0" +version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 5831582..58771dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "doit" -version = "1.6.0" +version = "1.7.0" edition = "2024" [dependencies]