Compare commits
10 Commits
570d3058cf
...
09867c48ca
Author | SHA1 | Date | |
---|---|---|---|
|
09867c48ca | ||
|
39b2783c4b | ||
|
208187fbb6 | ||
|
7683c81c08 | ||
|
e0c031272d | ||
|
ef6a813dd1 | ||
|
e07da79d40 | ||
|
97c32e60ae | ||
|
929fd695d5 | ||
|
23f1c1ca9f |
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -613,6 +613,8 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
@ -737,6 +739,51 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"thiserror",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -1198,6 +1245,12 @@ version = "1.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
@ -37,6 +37,8 @@ libc = "0.2.0"
|
|||||||
log = "0.4.4"
|
log = "0.4.4"
|
||||||
num_cpus = "1.15.0"
|
num_cpus = "1.15.0"
|
||||||
percent-encoding = "2.3.1"
|
percent-encoding = "2.3.1"
|
||||||
|
pest = "2.7.10"
|
||||||
|
pest_derive = "2.7.10"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.10.4"
|
regex = "1.10.4"
|
||||||
semver = "1.0.20"
|
semver = "1.0.20"
|
||||||
|
19
README.md
19
README.md
@ -603,8 +603,9 @@ testing… all tests passed!
|
|||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
A variety of example `justfile`s can be found in the
|
A variety of `justfile`s can be found in the
|
||||||
[examples directory](https://github.com/casey/just/tree/master/examples).
|
[examples directory](https://github.com/casey/just/tree/master/examples) and on
|
||||||
|
[GitHub](https://github.com/search?q=path%3A**%2Fjustfile&type=code).
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
@ -3227,6 +3228,20 @@ mod? foo 'bar.just'
|
|||||||
mod? foo 'baz.just'
|
mod? foo 'baz.just'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Modules may be given doc comments which appear in `--list`
|
||||||
|
output<sup>master</sup>:
|
||||||
|
|
||||||
|
```mf
|
||||||
|
# foo is a great module!
|
||||||
|
mod foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ just --list
|
||||||
|
Available recipes:
|
||||||
|
foo ... # foo is a great module!
|
||||||
|
```
|
||||||
|
|
||||||
See the
|
See the
|
||||||
[module stabilization tracking issue](https://github.com/casey/just/issues/929)
|
[module stabilization tracking issue](https://github.com/casey/just/issues/929)
|
||||||
for more information.
|
for more information.
|
||||||
|
@ -9,22 +9,24 @@ pub(crate) struct Analyzer<'src> {
|
|||||||
|
|
||||||
impl<'src> Analyzer<'src> {
|
impl<'src> Analyzer<'src> {
|
||||||
pub(crate) fn analyze(
|
pub(crate) fn analyze(
|
||||||
loaded: &[PathBuf],
|
|
||||||
paths: &HashMap<PathBuf, PathBuf>,
|
|
||||||
asts: &HashMap<PathBuf, Ast<'src>>,
|
asts: &HashMap<PathBuf, Ast<'src>>,
|
||||||
root: &Path,
|
doc: Option<&'src str>,
|
||||||
|
loaded: &[PathBuf],
|
||||||
name: Option<Name<'src>>,
|
name: Option<Name<'src>>,
|
||||||
|
paths: &HashMap<PathBuf, PathBuf>,
|
||||||
|
root: &Path,
|
||||||
) -> CompileResult<'src, Justfile<'src>> {
|
) -> CompileResult<'src, Justfile<'src>> {
|
||||||
Self::default().justfile(loaded, paths, asts, root, name)
|
Self::default().justfile(asts, doc, loaded, name, paths, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justfile(
|
fn justfile(
|
||||||
mut self,
|
mut self,
|
||||||
loaded: &[PathBuf],
|
|
||||||
paths: &HashMap<PathBuf, PathBuf>,
|
|
||||||
asts: &HashMap<PathBuf, Ast<'src>>,
|
asts: &HashMap<PathBuf, Ast<'src>>,
|
||||||
root: &Path,
|
doc: Option<&'src str>,
|
||||||
|
loaded: &[PathBuf],
|
||||||
name: Option<Name<'src>>,
|
name: Option<Name<'src>>,
|
||||||
|
paths: &HashMap<PathBuf, PathBuf>,
|
||||||
|
root: &Path,
|
||||||
) -> CompileResult<'src, Justfile<'src>> {
|
) -> CompileResult<'src, Justfile<'src>> {
|
||||||
let mut recipes = Vec::new();
|
let mut recipes = Vec::new();
|
||||||
|
|
||||||
@ -84,10 +86,22 @@ impl<'src> Analyzer<'src> {
|
|||||||
stack.push(asts.get(absolute).unwrap());
|
stack.push(asts.get(absolute).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item::Module { absolute, name, .. } => {
|
Item::Module {
|
||||||
|
absolute,
|
||||||
|
name,
|
||||||
|
doc,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if let Some(absolute) = absolute {
|
if let Some(absolute) = absolute {
|
||||||
define(*name, "module", false)?;
|
define(*name, "module", false)?;
|
||||||
modules.insert(Self::analyze(loaded, paths, asts, absolute, Some(*name))?);
|
modules.insert(Self::analyze(
|
||||||
|
asts,
|
||||||
|
*doc,
|
||||||
|
loaded,
|
||||||
|
Some(*name),
|
||||||
|
paths,
|
||||||
|
absolute,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item::Recipe(recipe) => {
|
Item::Recipe(recipe) => {
|
||||||
@ -172,6 +186,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
Rc::clone(next)
|
Rc::clone(next)
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
doc,
|
||||||
loaded: loaded.into(),
|
loaded: loaded.into(),
|
||||||
modules,
|
modules,
|
||||||
name,
|
name,
|
||||||
|
10
src/color.rs
10
src/color.rs
@ -38,6 +38,7 @@ impl Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub(crate) fn always() -> Self {
|
pub(crate) fn always() -> Self {
|
||||||
Self {
|
Self {
|
||||||
use_color: UseColor::Always,
|
use_color: UseColor::Always,
|
||||||
@ -133,6 +134,15 @@ impl Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<UseColor> for Color {
|
||||||
|
fn from(use_color: UseColor) -> Self {
|
||||||
|
Self {
|
||||||
|
use_color,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Color {
|
impl Default for Color {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
26
src/command_color.rs
Normal file
26
src/command_color.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, ValueEnum)]
|
||||||
|
pub(crate) enum CommandColor {
|
||||||
|
Black,
|
||||||
|
Blue,
|
||||||
|
Cyan,
|
||||||
|
Green,
|
||||||
|
Purple,
|
||||||
|
Red,
|
||||||
|
Yellow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CommandColor> for ansi_term::Color {
|
||||||
|
fn from(command_color: CommandColor) -> Self {
|
||||||
|
match command_color {
|
||||||
|
CommandColor::Black => Self::Black,
|
||||||
|
CommandColor::Blue => Self::Blue,
|
||||||
|
CommandColor::Cyan => Self::Cyan,
|
||||||
|
CommandColor::Green => Self::Green,
|
||||||
|
CommandColor::Purple => Self::Purple,
|
||||||
|
CommandColor::Red => Self::Red,
|
||||||
|
CommandColor::Yellow => Self::Yellow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,7 @@ impl Compiler {
|
|||||||
name,
|
name,
|
||||||
optional,
|
optional,
|
||||||
relative,
|
relative,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
if !unstable {
|
if !unstable {
|
||||||
return Err(Error::Unstable {
|
return Err(Error::Unstable {
|
||||||
@ -107,7 +108,7 @@ impl Compiler {
|
|||||||
asts.insert(current.path, ast.clone());
|
asts.insert(current.path, ast.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let justfile = Analyzer::analyze(&loaded, &paths, &asts, root, None)?;
|
let justfile = Analyzer::analyze(&asts, None, &loaded, None, &paths, root)?;
|
||||||
|
|
||||||
Ok(Compilation {
|
Ok(Compilation {
|
||||||
asts,
|
asts,
|
||||||
@ -184,7 +185,7 @@ impl Compiler {
|
|||||||
asts.insert(root.clone(), ast);
|
asts.insert(root.clone(), ast);
|
||||||
let mut paths: HashMap<PathBuf, PathBuf> = HashMap::new();
|
let mut paths: HashMap<PathBuf, PathBuf> = HashMap::new();
|
||||||
paths.insert(root.clone(), root.clone());
|
paths.insert(root.clone(), root.clone());
|
||||||
Analyzer::analyze(&[], &paths, &asts, &root, None)
|
Analyzer::analyze(&asts, None, &[], None, &paths, &root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use {super::*, clap::ValueEnum};
|
use super::*;
|
||||||
|
|
||||||
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq)]
|
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq)]
|
||||||
pub(crate) enum Shell {
|
pub(crate) enum Shell {
|
||||||
@ -255,7 +255,7 @@ const POWERSHELL_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[(
|
|||||||
r#"function Get-JustFileRecipes([string[]]$CommandElements) {
|
r#"function Get-JustFileRecipes([string[]]$CommandElements) {
|
||||||
$justFileIndex = $commandElements.IndexOf("--justfile");
|
$justFileIndex = $commandElements.IndexOf("--justfile");
|
||||||
|
|
||||||
if ($justFileIndex -ne -1 && $justFileIndex + 1 -le $commandElements.Length) {
|
if ($justFileIndex -ne -1 -and $justFileIndex + 1 -le $commandElements.Length) {
|
||||||
$justFileLocation = $commandElements[$justFileIndex + 1]
|
$justFileLocation = $commandElements[$justFileIndex + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
src/config.rs
120
src/config.rs
@ -1,7 +1,7 @@
|
|||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
clap::{
|
clap::{
|
||||||
builder::{styling::AnsiColor, FalseyValueParser, PossibleValuesParser, Styles},
|
builder::{styling::AnsiColor, FalseyValueParser, Styles},
|
||||||
parser::ValuesRef,
|
parser::ValuesRef,
|
||||||
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command,
|
||||||
},
|
},
|
||||||
@ -108,32 +108,6 @@ mod arg {
|
|||||||
pub(crate) const VERBOSE: &str = "VERBOSE";
|
pub(crate) const VERBOSE: &str = "VERBOSE";
|
||||||
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
|
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
|
||||||
pub(crate) const YES: &str = "YES";
|
pub(crate) const YES: &str = "YES";
|
||||||
|
|
||||||
pub(crate) const COLOR_ALWAYS: &str = "always";
|
|
||||||
pub(crate) const COLOR_AUTO: &str = "auto";
|
|
||||||
pub(crate) const COLOR_NEVER: &str = "never";
|
|
||||||
pub(crate) const COLOR_VALUES: &[&str] = &[COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER];
|
|
||||||
|
|
||||||
pub(crate) const COMMAND_COLOR_BLACK: &str = "black";
|
|
||||||
pub(crate) const COMMAND_COLOR_BLUE: &str = "blue";
|
|
||||||
pub(crate) const COMMAND_COLOR_CYAN: &str = "cyan";
|
|
||||||
pub(crate) const COMMAND_COLOR_GREEN: &str = "green";
|
|
||||||
pub(crate) const COMMAND_COLOR_PURPLE: &str = "purple";
|
|
||||||
pub(crate) const COMMAND_COLOR_RED: &str = "red";
|
|
||||||
pub(crate) const COMMAND_COLOR_YELLOW: &str = "yellow";
|
|
||||||
pub(crate) const COMMAND_COLOR_VALUES: &[&str] = &[
|
|
||||||
COMMAND_COLOR_BLACK,
|
|
||||||
COMMAND_COLOR_BLUE,
|
|
||||||
COMMAND_COLOR_CYAN,
|
|
||||||
COMMAND_COLOR_GREEN,
|
|
||||||
COMMAND_COLOR_PURPLE,
|
|
||||||
COMMAND_COLOR_RED,
|
|
||||||
COMMAND_COLOR_YELLOW,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) const DUMP_FORMAT_JSON: &str = "json";
|
|
||||||
pub(crate) const DUMP_FORMAT_JUST: &str = "just";
|
|
||||||
pub(crate) const DUMP_FORMAT_VALUES: &[&str] = &[DUMP_FORMAT_JUST, DUMP_FORMAT_JSON];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -184,8 +158,8 @@ impl Config {
|
|||||||
.long("color")
|
.long("color")
|
||||||
.env("JUST_COLOR")
|
.env("JUST_COLOR")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.value_parser(PossibleValuesParser::new(arg::COLOR_VALUES))
|
.value_parser(clap::value_parser!(UseColor))
|
||||||
.default_value(arg::COLOR_AUTO)
|
.default_value("auto")
|
||||||
.help("Print colorful output"),
|
.help("Print colorful output"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -193,7 +167,7 @@ impl Config {
|
|||||||
.long("command-color")
|
.long("command-color")
|
||||||
.env("JUST_COMMAND_COLOR")
|
.env("JUST_COMMAND_COLOR")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.value_parser(PossibleValuesParser::new(arg::COMMAND_COLOR_VALUES))
|
.value_parser(clap::value_parser!(CommandColor))
|
||||||
.help("Echo recipe lines in <COMMAND-COLOR>"),
|
.help("Echo recipe lines in <COMMAND-COLOR>"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -225,8 +199,8 @@ impl Config {
|
|||||||
.long("dump-format")
|
.long("dump-format")
|
||||||
.env("JUST_DUMP_FORMAT")
|
.env("JUST_DUMP_FORMAT")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.value_parser(PossibleValuesParser::new(arg::DUMP_FORMAT_VALUES))
|
.value_parser(clap::value_parser!(DumpFormat))
|
||||||
.default_value(arg::DUMP_FORMAT_JUST)
|
.default_value("just")
|
||||||
.value_name("FORMAT")
|
.value_name("FORMAT")
|
||||||
.help("Dump justfile as <FORMAT>"),
|
.help("Dump justfile as <FORMAT>"),
|
||||||
)
|
)
|
||||||
@ -262,6 +236,7 @@ impl Config {
|
|||||||
.env("JUST_LIST_HEADING")
|
.env("JUST_LIST_HEADING")
|
||||||
.help("Print <TEXT> before list")
|
.help("Print <TEXT> before list")
|
||||||
.value_name("TEXT")
|
.value_name("TEXT")
|
||||||
|
.default_value("Available recipes:\n")
|
||||||
.action(ArgAction::Set),
|
.action(ArgAction::Set),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -270,6 +245,7 @@ impl Config {
|
|||||||
.env("JUST_LIST_PREFIX")
|
.env("JUST_LIST_PREFIX")
|
||||||
.help("Print <TEXT> before each list item")
|
.help("Print <TEXT> before each list item")
|
||||||
.value_name("TEXT")
|
.value_name("TEXT")
|
||||||
|
.default_value(" ")
|
||||||
.action(ArgAction::Set),
|
.action(ArgAction::Set),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -531,59 +507,6 @@ impl Config {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
|
|
||||||
let value = matches
|
|
||||||
.get_one::<String>(arg::COLOR)
|
|
||||||
.ok_or_else(|| ConfigError::Internal {
|
|
||||||
message: "`--color` had no value".to_string(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match value.as_str() {
|
|
||||||
arg::COLOR_AUTO => Ok(Color::auto()),
|
|
||||||
arg::COLOR_ALWAYS => Ok(Color::always()),
|
|
||||||
arg::COLOR_NEVER => Ok(Color::never()),
|
|
||||||
_ => Err(ConfigError::Internal {
|
|
||||||
message: format!("Invalid argument `{value}` to --color."),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_color_from_matches(matches: &ArgMatches) -> ConfigResult<Option<ansi_term::Color>> {
|
|
||||||
if let Some(value) = matches.get_one::<String>(arg::COMMAND_COLOR) {
|
|
||||||
match value.as_str() {
|
|
||||||
arg::COMMAND_COLOR_BLACK => Ok(Some(ansi_term::Color::Black)),
|
|
||||||
arg::COMMAND_COLOR_BLUE => Ok(Some(ansi_term::Color::Blue)),
|
|
||||||
arg::COMMAND_COLOR_CYAN => Ok(Some(ansi_term::Color::Cyan)),
|
|
||||||
arg::COMMAND_COLOR_GREEN => Ok(Some(ansi_term::Color::Green)),
|
|
||||||
arg::COMMAND_COLOR_PURPLE => Ok(Some(ansi_term::Color::Purple)),
|
|
||||||
arg::COMMAND_COLOR_RED => Ok(Some(ansi_term::Color::Red)),
|
|
||||||
arg::COMMAND_COLOR_YELLOW => Ok(Some(ansi_term::Color::Yellow)),
|
|
||||||
value => Err(ConfigError::Internal {
|
|
||||||
message: format!("Invalid argument `{value}` to --command-color."),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_format_from_matches(matches: &ArgMatches) -> ConfigResult<DumpFormat> {
|
|
||||||
let value =
|
|
||||||
matches
|
|
||||||
.get_one::<String>(arg::DUMP_FORMAT)
|
|
||||||
.ok_or_else(|| ConfigError::Internal {
|
|
||||||
message: "`--dump-format` had no value".to_string(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match value.as_str() {
|
|
||||||
arg::DUMP_FORMAT_JSON => Ok(DumpFormat::Json),
|
|
||||||
arg::DUMP_FORMAT_JUST => Ok(DumpFormat::Just),
|
|
||||||
_ => Err(ConfigError::Internal {
|
|
||||||
message: format!("Invalid argument `{value}` to --dump-format."),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_module_path(values: ValuesRef<String>) -> ConfigResult<ModulePath> {
|
fn parse_module_path(values: ValuesRef<String>) -> ConfigResult<ModulePath> {
|
||||||
let path = values.clone().map(|s| (*s).as_str()).collect::<Vec<&str>>();
|
let path = values.clone().map(|s| (*s).as_str()).collect::<Vec<&str>>();
|
||||||
|
|
||||||
@ -744,24 +667,28 @@ impl Config {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let unstable = matches.get_flag(arg::UNSTABLE) || subcommand == Subcommand::Summary;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
check: matches.get_flag(arg::CHECK),
|
check: matches.get_flag(arg::CHECK),
|
||||||
color: Self::color_from_matches(matches)?,
|
color: (*matches.get_one::<UseColor>(arg::COLOR).unwrap()).into(),
|
||||||
command_color: Self::command_color_from_matches(matches)?,
|
command_color: matches
|
||||||
|
.get_one::<CommandColor>(arg::COMMAND_COLOR)
|
||||||
|
.copied()
|
||||||
|
.map(CommandColor::into),
|
||||||
dotenv_filename: matches
|
dotenv_filename: matches
|
||||||
.get_one::<String>(arg::DOTENV_FILENAME)
|
.get_one::<String>(arg::DOTENV_FILENAME)
|
||||||
.map(Into::into),
|
.map(Into::into),
|
||||||
dotenv_path: matches.get_one::<PathBuf>(arg::DOTENV_PATH).map(Into::into),
|
dotenv_path: matches.get_one::<PathBuf>(arg::DOTENV_PATH).map(Into::into),
|
||||||
dry_run: matches.get_flag(arg::DRY_RUN),
|
dry_run: matches.get_flag(arg::DRY_RUN),
|
||||||
dump_format: Self::dump_format_from_matches(matches)?,
|
dump_format: matches
|
||||||
|
.get_one::<DumpFormat>(arg::DUMP_FORMAT)
|
||||||
|
.unwrap()
|
||||||
|
.clone(),
|
||||||
highlight: !matches.get_flag(arg::NO_HIGHLIGHT),
|
highlight: !matches.get_flag(arg::NO_HIGHLIGHT),
|
||||||
invocation_directory: env::current_dir().context(config_error::CurrentDirContext)?,
|
invocation_directory: env::current_dir().context(config_error::CurrentDirContext)?,
|
||||||
list_heading: matches
|
list_heading: matches.get_one::<String>(arg::LIST_HEADING).unwrap().into(),
|
||||||
.get_one::<String>(arg::LIST_HEADING)
|
list_prefix: matches.get_one::<String>(arg::LIST_PREFIX).unwrap().into(),
|
||||||
.map_or_else(|| "Available recipes:\n".into(), Into::into),
|
|
||||||
list_prefix: matches
|
|
||||||
.get_one::<String>(arg::LIST_PREFIX)
|
|
||||||
.map_or_else(|| " ".into(), Into::into),
|
|
||||||
list_submodules: matches.get_flag(arg::LIST_SUBMODULES),
|
list_submodules: matches.get_flag(arg::LIST_SUBMODULES),
|
||||||
load_dotenv: !matches.get_flag(arg::NO_DOTENV),
|
load_dotenv: !matches.get_flag(arg::NO_DOTENV),
|
||||||
no_aliases: matches.get_flag(arg::NO_ALIASES),
|
no_aliases: matches.get_flag(arg::NO_ALIASES),
|
||||||
@ -783,7 +710,7 @@ impl Config {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.into(),
|
.into(),
|
||||||
unsorted: matches.get_flag(arg::UNSORTED),
|
unsorted: matches.get_flag(arg::UNSORTED),
|
||||||
unstable: matches.get_flag(arg::UNSTABLE),
|
unstable,
|
||||||
verbosity: if matches.get_flag(arg::QUIET) {
|
verbosity: if matches.get_flag(arg::QUIET) {
|
||||||
Verbosity::Quiet
|
Verbosity::Quiet
|
||||||
} else {
|
} else {
|
||||||
@ -834,6 +761,7 @@ mod tests {
|
|||||||
$(shell_args: $shell_args:expr,)?
|
$(shell_args: $shell_args:expr,)?
|
||||||
$(subcommand: $subcommand:expr,)?
|
$(subcommand: $subcommand:expr,)?
|
||||||
$(unsorted: $unsorted:expr,)?
|
$(unsorted: $unsorted:expr,)?
|
||||||
|
$(unstable: $unstable:expr,)?
|
||||||
$(verbosity: $verbosity:expr,)?
|
$(verbosity: $verbosity:expr,)?
|
||||||
} => {
|
} => {
|
||||||
#[test]
|
#[test]
|
||||||
@ -854,6 +782,7 @@ mod tests {
|
|||||||
$(shell_args: $shell_args,)?
|
$(shell_args: $shell_args,)?
|
||||||
$(subcommand: $subcommand,)?
|
$(subcommand: $subcommand,)?
|
||||||
$(unsorted: $unsorted,)?
|
$(unsorted: $unsorted,)?
|
||||||
|
$(unstable: $unstable,)?
|
||||||
$(verbosity: $verbosity,)?
|
$(verbosity: $verbosity,)?
|
||||||
..testing::config(&[])
|
..testing::config(&[])
|
||||||
};
|
};
|
||||||
@ -1368,6 +1297,7 @@ mod tests {
|
|||||||
name: subcommand_summary,
|
name: subcommand_summary,
|
||||||
args: ["--summary"],
|
args: ["--summary"],
|
||||||
subcommand: Subcommand::Summary,
|
subcommand: Subcommand::Summary,
|
||||||
|
unstable: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#[derive(Debug, PartialEq)]
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, ValueEnum)]
|
||||||
pub(crate) enum DumpFormat {
|
pub(crate) enum DumpFormat {
|
||||||
Json,
|
Json,
|
||||||
Just,
|
Just,
|
||||||
|
@ -14,6 +14,7 @@ pub(crate) enum Item<'src> {
|
|||||||
},
|
},
|
||||||
Module {
|
Module {
|
||||||
absolute: Option<PathBuf>,
|
absolute: Option<PathBuf>,
|
||||||
|
doc: Option<&'src str>,
|
||||||
name: Name<'src>,
|
name: Name<'src>,
|
||||||
optional: bool,
|
optional: bool,
|
||||||
relative: Option<StringLiteral<'src>>,
|
relative: Option<StringLiteral<'src>>,
|
||||||
|
9
src/justfile.pest
Normal file
9
src/justfile.pest
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
identifier = { (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")+ }
|
||||||
|
|
||||||
|
recipe = { identifier ~ ":" ~ NEWLINE ~ recipe_body }
|
||||||
|
|
||||||
|
recipe_body = { recipe_line* }
|
||||||
|
|
||||||
|
recipe_line = {
|
||||||
|
|
@ -13,6 +13,7 @@ struct Invocation<'src: 'run, 'run> {
|
|||||||
pub(crate) struct Justfile<'src> {
|
pub(crate) struct Justfile<'src> {
|
||||||
pub(crate) aliases: Table<'src, Alias<'src>>,
|
pub(crate) aliases: Table<'src, Alias<'src>>,
|
||||||
pub(crate) assignments: Table<'src, Assignment<'src>>,
|
pub(crate) assignments: Table<'src, Assignment<'src>>,
|
||||||
|
pub(crate) doc: Option<&'src str>,
|
||||||
#[serde(rename = "first", serialize_with = "keyed::serialize_option")]
|
#[serde(rename = "first", serialize_with = "keyed::serialize_option")]
|
||||||
pub(crate) default: Option<Rc<Recipe<'src>>>,
|
pub(crate) default: Option<Rc<Recipe<'src>>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
36
src/lib.rs
36
src/lib.rs
@ -23,29 +23,30 @@ pub(crate) use {
|
|||||||
crate::{
|
crate::{
|
||||||
alias::Alias, analyzer::Analyzer, argument_parser::ArgumentParser, assignment::Assignment,
|
alias::Alias, analyzer::Analyzer, argument_parser::ArgumentParser, assignment::Assignment,
|
||||||
assignment_resolver::AssignmentResolver, ast::Ast, attribute::Attribute, binding::Binding,
|
assignment_resolver::AssignmentResolver, ast::Ast, attribute::Attribute, binding::Binding,
|
||||||
color::Color, color_display::ColorDisplay, command_ext::CommandExt, compilation::Compilation,
|
color::Color, color_display::ColorDisplay, command_color::CommandColor,
|
||||||
compile_error::CompileError, compile_error_kind::CompileErrorKind, compiler::Compiler,
|
command_ext::CommandExt, compilation::Compilation, compile_error::CompileError,
|
||||||
condition::Condition, conditional_operator::ConditionalOperator, config::Config,
|
compile_error_kind::CompileErrorKind, compiler::Compiler, condition::Condition,
|
||||||
config_error::ConfigError, constants::constants, count::Count, delimiter::Delimiter,
|
conditional_operator::ConditionalOperator, config::Config, config_error::ConfigError,
|
||||||
dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error,
|
constants::constants, count::Count, delimiter::Delimiter, dependency::Dependency,
|
||||||
evaluator::Evaluator, execution_context::ExecutionContext, expression::Expression,
|
dump_format::DumpFormat, enclosure::Enclosure, error::Error, evaluator::Evaluator,
|
||||||
fragment::Fragment, function::Function, interrupt_guard::InterruptGuard,
|
execution_context::ExecutionContext, expression::Expression, fragment::Fragment,
|
||||||
interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyed::Keyed,
|
function::Function, interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler,
|
||||||
keyword::Keyword, lexer::Lexer, line::Line, list::List, load_dotenv::load_dotenv,
|
item::Item, justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line,
|
||||||
loader::Loader, module_path::ModulePath, name::Name, namepath::Namepath, ordinal::Ordinal,
|
list::List, load_dotenv::load_dotenv, loader::Loader, module_path::ModulePath, name::Name,
|
||||||
output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind,
|
namepath::Namepath, ordinal::Ordinal, output::output, output_error::OutputError,
|
||||||
parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position,
|
parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform,
|
||||||
positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe,
|
platform_interface::PlatformInterface, position::Position, positional::Positional, ran::Ran,
|
||||||
recipe_resolver::RecipeResolver, recipe_signature::RecipeSignature, scope::Scope,
|
range_ext::RangeExt, recipe::Recipe, recipe_resolver::RecipeResolver,
|
||||||
search::Search, search_config::SearchConfig, search_error::SearchError, set::Set,
|
recipe_signature::RecipeSignature, scope::Scope, search::Search, search_config::SearchConfig,
|
||||||
setting::Setting, settings::Settings, shebang::Shebang, shell::Shell,
|
search_error::SearchError, set::Set, setting::Setting, settings::Settings, shebang::Shebang,
|
||||||
show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
|
shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
|
||||||
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
|
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
|
||||||
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
|
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
|
||||||
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
|
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
|
||||||
verbosity::Verbosity, warning::Warning,
|
verbosity::Verbosity, warning::Warning,
|
||||||
},
|
},
|
||||||
camino::Utf8Path,
|
camino::Utf8Path,
|
||||||
|
clap::ValueEnum,
|
||||||
derivative::Derivative,
|
derivative::Derivative,
|
||||||
edit_distance::edit_distance,
|
edit_distance::edit_distance,
|
||||||
lexiclean::Lexiclean,
|
lexiclean::Lexiclean,
|
||||||
@ -128,6 +129,7 @@ mod attribute;
|
|||||||
mod binding;
|
mod binding;
|
||||||
mod color;
|
mod color;
|
||||||
mod color_display;
|
mod color_display;
|
||||||
|
mod command_color;
|
||||||
mod command_ext;
|
mod command_ext;
|
||||||
mod compilation;
|
mod compilation;
|
||||||
mod compile_error;
|
mod compile_error;
|
||||||
|
@ -36,7 +36,18 @@ pub(crate) struct Parser<'run, 'src> {
|
|||||||
working_directory: &'run Path,
|
working_directory: &'run Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(pest_derive::Parser)]
|
||||||
|
#[grammar = "justfile.pest"]
|
||||||
|
struct JustfileParser;
|
||||||
|
|
||||||
impl<'run, 'src> Parser<'run, 'src> {
|
impl<'run, 'src> Parser<'run, 'src> {
|
||||||
|
|
||||||
|
pub(crate) fn parse_new(path: &'src Path, src: &'src str) -> CompileResult<'src, Ast<'src>> {
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Parse `tokens` into an `Ast`
|
/// Parse `tokens` into an `Ast`
|
||||||
pub(crate) fn parse(
|
pub(crate) fn parse(
|
||||||
file_depth: u32,
|
file_depth: u32,
|
||||||
@ -365,12 +376,15 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(Keyword::Mod)
|
Some(Keyword::Mod)
|
||||||
if self.next_are(&[Identifier, Identifier, StringToken])
|
if self.next_are(&[Identifier, Identifier, Comment])
|
||||||
|| self.next_are(&[Identifier, Identifier, Identifier, StringToken])
|
|
||||||
|| self.next_are(&[Identifier, Identifier, Eof])
|
|| self.next_are(&[Identifier, Identifier, Eof])
|
||||||
|| self.next_are(&[Identifier, Identifier, Eol])
|
|| self.next_are(&[Identifier, Identifier, Eol])
|
||||||
|
|| self.next_are(&[Identifier, Identifier, Identifier, StringToken])
|
||||||
|
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||||
|| self.next_are(&[Identifier, QuestionMark]) =>
|
|| self.next_are(&[Identifier, QuestionMark]) =>
|
||||||
{
|
{
|
||||||
|
let doc = pop_doc_comment(&mut items, eol_since_last_comment);
|
||||||
|
|
||||||
self.presume_keyword(Keyword::Mod)?;
|
self.presume_keyword(Keyword::Mod)?;
|
||||||
|
|
||||||
let optional = self.accepted(QuestionMark)?;
|
let optional = self.accepted(QuestionMark)?;
|
||||||
@ -386,6 +400,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
|
|
||||||
items.push(Item::Module {
|
items.push(Item::Module {
|
||||||
absolute: None,
|
absolute: None,
|
||||||
|
doc,
|
||||||
name,
|
name,
|
||||||
optional,
|
optional,
|
||||||
relative,
|
relative,
|
||||||
|
@ -435,6 +435,27 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn list_module(config: &Config, module: &Justfile, depth: usize) {
|
fn list_module(config: &Config, module: &Justfile, depth: usize) {
|
||||||
|
fn format_doc(
|
||||||
|
config: &Config,
|
||||||
|
name: &str,
|
||||||
|
doc: Option<&str>,
|
||||||
|
max_signature_width: usize,
|
||||||
|
signature_widths: &BTreeMap<&str, usize>,
|
||||||
|
) {
|
||||||
|
if let Some(doc) = doc {
|
||||||
|
if doc.lines().count() <= 1 {
|
||||||
|
print!(
|
||||||
|
"{:padding$}{} {}",
|
||||||
|
"",
|
||||||
|
config.color.stdout().doc().paint("#"),
|
||||||
|
config.color.stdout().doc().paint(doc),
|
||||||
|
padding = max_signature_width.saturating_sub(signature_widths[name]) + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
let aliases = if config.no_aliases {
|
let aliases = if config.no_aliases {
|
||||||
BTreeMap::new()
|
BTreeMap::new()
|
||||||
} else {
|
} else {
|
||||||
@ -468,6 +489,11 @@ impl Subcommand {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !config.list_submodules {
|
||||||
|
for (name, _) in &module.modules {
|
||||||
|
signature_widths.insert(name, UnicodeWidthStr::width(format!("{name} ...").as_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signature_widths
|
signature_widths
|
||||||
};
|
};
|
||||||
@ -554,18 +580,13 @@ impl Subcommand {
|
|||||||
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(doc) = doc {
|
format_doc(
|
||||||
if doc.lines().count() <= 1 {
|
config,
|
||||||
print!(
|
name,
|
||||||
"{:padding$}{} {}",
|
doc.as_deref(),
|
||||||
"",
|
max_signature_width,
|
||||||
config.color.stdout().doc().paint("#"),
|
&signature_widths,
|
||||||
config.color.stdout().doc().paint(&doc),
|
);
|
||||||
padding = max_signature_width.saturating_sub(signature_widths[name]) + 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,7 +603,14 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for submodule in module.modules(config) {
|
for submodule in module.modules(config) {
|
||||||
println!("{list_prefix}{} ...", submodule.name(),);
|
print!("{list_prefix}{} ...", submodule.name());
|
||||||
|
format_doc(
|
||||||
|
config,
|
||||||
|
submodule.name(),
|
||||||
|
submodule.doc,
|
||||||
|
max_signature_width,
|
||||||
|
&signature_widths,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ pub(crate) fn analysis_error(
|
|||||||
let mut paths: HashMap<PathBuf, PathBuf> = HashMap::new();
|
let mut paths: HashMap<PathBuf, PathBuf> = HashMap::new();
|
||||||
paths.insert("justfile".into(), "justfile".into());
|
paths.insert("justfile".into(), "justfile".into());
|
||||||
|
|
||||||
match Analyzer::analyze(&[], &paths, &asts, &root, None) {
|
match Analyzer::analyze(&asts, None, &[], None, &paths, &root) {
|
||||||
Ok(_) => panic!("Analysis unexpectedly succeeded"),
|
Ok(_) => panic!("Analysis unexpectedly succeeded"),
|
||||||
Err(have) => {
|
Err(have) => {
|
||||||
let want = CompileError {
|
let want = CompileError {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, ValueEnum)]
|
||||||
pub(crate) enum UseColor {
|
pub(crate) enum UseColor {
|
||||||
Auto,
|
Auto,
|
||||||
Always,
|
Always,
|
||||||
|
@ -34,10 +34,10 @@ b := env_var_or_default('ZADDY', 'HTAP')
|
|||||||
x := env_var_or_default('XYZ', 'ABC')
|
x := env_var_or_default('XYZ', 'ABC')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{p}}' '{{b}}' '{{x}}'
|
/usr/bin/env echo '{{p}}' '{{b}}' '{{x}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: format!("{} HTAP ABC\n", env::var("USER").unwrap()).as_str(),
|
stdout: format!("{} HTAP ABC\n", env::var("USER").unwrap()).as_str(),
|
||||||
stderr: format!("/bin/echo '{}' 'HTAP' 'ABC'\n", env::var("USER").unwrap()).as_str(),
|
stderr: format!("/usr/bin/env echo '{}' 'HTAP' 'ABC'\n", env::var("USER").unwrap()).as_str(),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -52,10 +52,10 @@ ext := extension('/foo/bar/baz.hello')
|
|||||||
jn := join('a', 'b')
|
jn := join('a', 'b')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}' '{{jn}}'
|
/usr/bin/env echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}' '{{jn}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "/foo/bar/baz baz baz.hello /foo/bar hello a/b\n",
|
stdout: "/foo/bar/baz baz baz.hello /foo/bar hello a/b\n",
|
||||||
stderr: "/bin/echo '/foo/bar/baz' 'baz' 'baz.hello' '/foo/bar' 'hello' 'a/b'\n",
|
stderr: "/usr/bin/env echo '/foo/bar/baz' 'baz' 'baz.hello' '/foo/bar' 'hello' 'a/b'\n",
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -69,10 +69,10 @@ dir := parent_directory('/foo/')
|
|||||||
ext := extension('/foo/bar/baz.hello.ciao')
|
ext := extension('/foo/bar/baz.hello.ciao')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}'
|
/usr/bin/env echo '{{we}}' '{{fs}}' '{{fn}}' '{{dir}}' '{{ext}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "/foo/bar/baz baz.hello baz.hello.ciao / ciao\n",
|
stdout: "/foo/bar/baz baz.hello baz.hello.ciao / ciao\n",
|
||||||
stderr: "/bin/echo '/foo/bar/baz' 'baz.hello' 'baz.hello.ciao' '/' 'ciao'\n",
|
stderr: "/usr/bin/env echo '/foo/bar/baz' 'baz.hello' 'baz.hello.ciao' '/' 'ciao'\n",
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -82,7 +82,7 @@ test! {
|
|||||||
we := without_extension('')
|
we := without_extension('')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -102,7 +102,7 @@ test! {
|
|||||||
we := extension('')
|
we := extension('')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -121,7 +121,7 @@ test! {
|
|||||||
we := extension('foo')
|
we := extension('foo')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -140,7 +140,7 @@ test! {
|
|||||||
we := file_stem('')
|
we := file_stem('')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -159,7 +159,7 @@ test! {
|
|||||||
we := file_name('')
|
we := file_name('')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -178,7 +178,7 @@ test! {
|
|||||||
we := parent_directory('')
|
we := parent_directory('')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -198,7 +198,7 @@ test! {
|
|||||||
we := parent_directory('/')
|
we := parent_directory('/')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{we}}'
|
/usr/bin/env echo '{{we}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: "",
|
stdout: "",
|
||||||
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
stderr: format!("{} {}\n{}\n{}\n{}\n{}\n",
|
||||||
@ -220,10 +220,10 @@ b := env_var_or_default('ZADDY', 'HTAP')
|
|||||||
x := env_var_or_default('XYZ', 'ABC')
|
x := env_var_or_default('XYZ', 'ABC')
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
/bin/echo '{{p}}' '{{b}}' '{{x}}'
|
/usr/bin/env echo '{{p}}' '{{b}}' '{{x}}'
|
||||||
"#,
|
"#,
|
||||||
stdout: format!("{} HTAP ABC\n", env::var("USERNAME").unwrap()).as_str(),
|
stdout: format!("{} HTAP ABC\n", env::var("USERNAME").unwrap()).as_str(),
|
||||||
stderr: format!("/bin/echo '{}' 'HTAP' 'ABC'\n", env::var("USERNAME").unwrap()).as_str(),
|
stderr: format!("/usr/bin/env echo '{}' 'HTAP' 'ABC'\n", env::var("USERNAME").unwrap()).as_str(),
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
|
@ -18,6 +18,7 @@ fn alias() {
|
|||||||
",
|
",
|
||||||
json!({
|
json!({
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"f": {
|
"f": {
|
||||||
"name": "f",
|
"name": "f",
|
||||||
@ -80,6 +81,7 @@ fn assignment() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"first": null,
|
"first": null,
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {},
|
"recipes": {},
|
||||||
"settings": {
|
"settings": {
|
||||||
@ -117,6 +119,7 @@ fn body() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -170,6 +173,7 @@ fn dependencies() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"bar": {
|
"bar": {
|
||||||
@ -248,6 +252,7 @@ fn dependency_argument() {
|
|||||||
json!({
|
json!({
|
||||||
"aliases": {},
|
"aliases": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"assignments": {
|
"assignments": {
|
||||||
"x": {
|
"x": {
|
||||||
"export": false,
|
"export": false,
|
||||||
@ -341,6 +346,7 @@ fn duplicate_recipes() {
|
|||||||
",
|
",
|
||||||
json!({
|
json!({
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"f": {
|
"f": {
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
@ -414,6 +420,7 @@ fn duplicate_variables() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"first": null,
|
"first": null,
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {},
|
"recipes": {},
|
||||||
"settings": {
|
"settings": {
|
||||||
@ -446,6 +453,7 @@ fn doc_comment() {
|
|||||||
json!({
|
json!({
|
||||||
"aliases": {},
|
"aliases": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
@ -494,6 +502,7 @@ fn empty_justfile() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": null,
|
"first": null,
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {},
|
"recipes": {},
|
||||||
"settings": {
|
"settings": {
|
||||||
@ -533,6 +542,7 @@ fn parameters() {
|
|||||||
json!({
|
json!({
|
||||||
"aliases": {},
|
"aliases": {},
|
||||||
"first": "a",
|
"first": "a",
|
||||||
|
"doc": null,
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
@ -685,6 +695,7 @@ fn priors() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "a",
|
"first": "a",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"a": {
|
"a": {
|
||||||
@ -768,6 +779,7 @@ fn private() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "_foo",
|
"first": "_foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"_foo": {
|
"_foo": {
|
||||||
@ -815,6 +827,7 @@ fn quiet() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -874,6 +887,7 @@ fn settings() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -927,6 +941,7 @@ fn shebang() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -974,6 +989,7 @@ fn simple() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -1024,6 +1040,7 @@ fn attribute() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "foo",
|
"first": "foo",
|
||||||
|
"doc": null,
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"foo": {
|
"foo": {
|
||||||
@ -1068,6 +1085,7 @@ fn module() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
|
# hello
|
||||||
mod foo
|
mod foo
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
@ -1082,11 +1100,13 @@ fn module() {
|
|||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": null,
|
"first": null,
|
||||||
|
"doc": null,
|
||||||
"modules": {
|
"modules": {
|
||||||
"foo": {
|
"foo": {
|
||||||
"aliases": {},
|
"aliases": {},
|
||||||
"assignments": {},
|
"assignments": {},
|
||||||
"first": "bar",
|
"first": "bar",
|
||||||
|
"doc": "hello",
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"recipes": {
|
"recipes": {
|
||||||
"bar": {
|
"bar": {
|
||||||
|
@ -353,3 +353,55 @@ fn nested_modules_are_properly_indented() {
|
|||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_doc_rendered() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
# Module foo
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
foo ... # Module foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_doc_aligned() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "")
|
||||||
|
.write("bar.just", "")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
# Module foo
|
||||||
|
mod foo
|
||||||
|
|
||||||
|
# comment
|
||||||
|
mod very_long_name_for_module \"bar.just\" # comment
|
||||||
|
|
||||||
|
# will change your world
|
||||||
|
recipe:
|
||||||
|
@echo Hi
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
recipe # will change your world
|
||||||
|
foo ... # Module foo
|
||||||
|
very_long_name_for_module ... # comment
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -8,8 +8,6 @@ fn modules_are_unstable() {
|
|||||||
mod foo
|
mod foo
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.arg("foo")
|
|
||||||
.arg("foo")
|
|
||||||
.stderr(
|
.stderr(
|
||||||
"error: Modules are currently unstable. \
|
"error: Modules are currently unstable. \
|
||||||
Invoke `just` with the `--unstable` flag to enable unstable features.\n",
|
Invoke `just` with the `--unstable` flag to enable unstable features.\n",
|
||||||
@ -781,3 +779,18 @@ fn colon_separated_path_components_are_not_used_as_arguments() {
|
|||||||
.status(1)
|
.status(1)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn comments_can_follow_modules() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "foo:\n @echo FOO")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo # this is foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "foo", "foo"])
|
||||||
|
.stdout("FOO\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -71,3 +71,18 @@ fn submodule_recipes() {
|
|||||||
.stdout("bar foo::foo foo::bar::bar foo::bar::baz::baz foo::bar::baz::biz::biz\n")
|
.stdout("bar foo::foo foo::bar::bar foo::bar::baz::baz foo::bar::baz::biz::biz\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summary_implies_unstable() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "foo:")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.arg("--summary")
|
||||||
|
.stdout("foo::foo\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user