Add --man subcommand (#2041)

This commit is contained in:
Casey Rodarmor 2024-05-15 00:28:50 -07:00 committed by GitHub
parent 07aaa4f440
commit a9b0912b2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 220 additions and 159 deletions

17
Cargo.lock generated
View File

@ -215,6 +215,16 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "clap_mangen"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1dd95b5ebb5c1c54581dd6346f3ed6a79a3eef95dd372fc2ac13d535535300e"
dependencies = [
"clap 4.5.4",
"roff",
]
[[package]]
name = "colorchoice"
version = "1.0.1"
@ -512,6 +522,7 @@ dependencies = [
"camino",
"clap 4.5.4",
"clap_complete",
"clap_mangen",
"cradle",
"ctrlc",
"derivative",
@ -774,6 +785,12 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "roff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "rustix"
version = "0.38.34"

View File

@ -15,7 +15,7 @@ repository = "https://github.com/casey/just"
rust-version = "1.63"
[workspace]
members = [".", "bin/ref-type", "bin/generate-book", "bin/update-contributors"]
members = [".", "crates/*"]
[dependencies]
ansi_term = "0.12.0"
@ -24,6 +24,7 @@ blake3 = { version = "1.5.0", features = ["rayon", "mmap"] }
camino = "1.0.4"
clap = { version = "4.0.0", features = ["env", "wrap_help"] }
clap_complete = "4.0.0"
clap_mangen = "0.2.20"
ctrlc = { version = "3.1.1", features = ["termination"] }
derivative = "2.0.0"
dirs = "5.0.1"
@ -64,13 +65,6 @@ path = "src/main.rs"
name = "just"
test = false
[features]
# No features are active by default.
default = []
# The `help4help2man` feature modifies the message produced by `--help`
# so that `help2man` produces a reasonable man page.
help4help2man = []
# The public documentation is minimal and doesn't change between
# platforms, so we only build them for linux on docs.rs to save
# their build machines some cycles.

View File

@ -3158,6 +3158,14 @@ fpath=($HOMEBREW_PREFIX/share/zsh/site-functions $fpath)
# compinit
```
### Man Page
`just` can print its own man page with `just --man`. Man pages are written in
[`roff`](https://en.wikipedia.org/wiki/Roff_%28software%29), a venerable markup
language and one of the first practical applications of Unix. If you have
[`groff`](https://www.gnu.org/software/groff/) installed you can view the man
page with `just --man | groff -mandoc -Tascii | less`.
### Grammar
A non-normative grammar of `justfile`s can be found in

View File

@ -30,7 +30,7 @@ _just() {
case "${cmd}" in
"$1")
opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..."
opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --man --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..."
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0

View File

@ -66,6 +66,7 @@ set edit:completion:arg-completer[just] = {|@words|
cand --init 'Initialize new justfile in project root'
cand -l 'List available recipes and their arguments'
cand --list 'List available recipes and their arguments'
cand --man 'Print man page'
cand --summary 'List names of available recipes'
cand --variables 'List names of variables'
cand -h 'Print help'

View File

@ -73,6 +73,7 @@ complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable
complete -c just -l fmt -d 'Format and overwrite justfile'
complete -c just -l init -d 'Initialize new justfile in project root'
complete -c just -s l -l list -d 'List available recipes and their arguments'
complete -c just -l man -d 'Print man page'
complete -c just -l summary -d 'List names of available recipes'
complete -c just -l variables -d 'List names of variables'
complete -c just -s h -l help -d 'Print help'

View File

@ -69,6 +69,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
[CompletionResult]::new('--init', 'init', [CompletionResultType]::ParameterName, 'Initialize new justfile in project root')
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes and their arguments')
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')

View File

@ -64,6 +64,7 @@ _just() {
'--init[Initialize new justfile in project root]' \
'-l[List available recipes and their arguments]' \
'--list[List available recipes and their arguments]' \
'--man[Print man page]' \
'--summary[List names of available recipes]' \
'--variables[List names of variables]' \
'-h[Print help]' \

View File

@ -43,13 +43,7 @@ shellcheck:
shellcheck www/install.sh
man:
cargo build --features help4help2man
help2man \
--name 'save and run commands' \
--manual 'Just Manual' \
--no-info \
target/debug/just \
> man/just.1
cargo run -- --man > man/just.1
view-man: man
man man/just.1
@ -115,10 +109,6 @@ install-dev-deps:
cargo install cargo-watch
cargo install mdbook mdbook-linkcheck
# install system development dependencies with homebrew
install-dev-deps-homebrew:
brew install help2man
# everyone's favorite animate paper clip
clippy:
cargo clippy --all --all-targets --all-features

View File

@ -1,159 +1,168 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH JUST "1" "May 2024" "just 1.26.0" "Just Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH just 1 "just 1.26.0"
.SH NAME
just \- save and run commands
just \- 🤖 Just a command runner \- https://github.com/casey/just
.SH SYNOPSIS
\fBjust\fR [\fB\-\-check\fR] [\fB\-\-chooser\fR] [\fB\-\-color\fR] [\fB\-\-command\-color\fR] [\fB\-\-yes\fR] [\fB\-n\fR|\fB\-\-dry\-run\fR] [\fB\-\-dump\-format\fR] [\fB\-\-highlight\fR] [\fB\-\-list\-heading\fR] [\fB\-\-list\-prefix\fR] [\fB\-\-no\-aliases\fR] [\fB\-\-no\-deps\fR] [\fB\-\-no\-dotenv\fR] [\fB\-\-no\-highlight\fR] [\fB\-f\fR|\fB\-\-justfile\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-set\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-arg\fR] [\fB\-\-shell\-command\fR] [\fB\-\-clear\-shell\-args\fR] [\fB\-u\fR|\fB\-\-unsorted\fR] [\fB\-\-unstable\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-d\fR|\fB\-\-working\-directory\fR] [\fB\-\-changelog\fR] [\fB\-\-choose\fR] [\fB\-c\fR|\fB\-\-command\fR] [\fB\-\-completions\fR] [\fB\-\-dump\fR] [\fB\-e\fR|\fB\-\-edit\fR] [\fB\-\-evaluate\fR] [\fB\-\-fmt\fR] [\fB\-\-init\fR] [\fB\-l\fR|\fB\-\-list\fR] [\fB\-\-man\fR] [\fB\-s\fR|\fB\-\-show\fR] [\fB\-\-summary\fR] [\fB\-\-variables\fR] [\fB\-\-dotenv\-filename\fR] [\fB\-E\fR|\fB\-\-dotenv\-path\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIARGUMENTS\fR]
.SH DESCRIPTION
just 1.26.0
\- Please see https://github.com/casey/just for more information.
.SS "USAGE:"
.IP
just [FLAGS] [OPTIONS] [\-\-] [ARGUMENTS]...
.SS "FLAGS:"
.TP
\fB\-\-changelog\fR
Print changelog
🤖 Just a command runner \- https://github.com/casey/just
.SH OPTIONS
.TP
\fB\-\-check\fR
Run `\-\-fmt` in 'check' mode. Exits with 0 if justfile is formatted
correctly. Exits with 1 and prints a diff if formatting is required.
Run `\-\-fmt` in \*(Aqcheck\*(Aq mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.
.TP
\fB\-\-choose\fR
Select one or more recipes to run using a binary chooser. If `\-\-chooser` is
not passed the chooser defaults to the value of $JUST_CHOOSER, falling back
to `fzf`
\fB\-\-chooser\fR
Override binary invoked by `\-\-choose`
.RS
May also be specified with the \fBJUST_CHOOSER\fR environment variable.
.RE
.TP
\fB\-\-color\fR [default: auto]
Print colorful output
.br
.br
[\fIpossible values: \fRauto, always, never]
.TP
\fB\-\-command\-color\fR
Echo recipe lines in <COMMAND\-COLOR>
.br
.br
[\fIpossible values: \fRblack, blue, cyan, green, purple, red, yellow]
.TP
\fB\-\-yes\fR
Automatically confirm all recipes.
.TP
\fB\-n\fR, \fB\-\-dry\-run\fR
Print what just would do without doing it
.TP
\fB\-\-dump\-format\fR=\fIFORMAT\fR [default: just]
Dump justfile as <FORMAT>
.br
.br
[\fIpossible values: \fRjust, json]
.TP
\fB\-\-highlight\fR
Highlight echoed recipe lines in bold
.TP
\fB\-\-list\-heading\fR=\fITEXT\fR
Print <TEXT> before list
.TP
\fB\-\-list\-prefix\fR=\fITEXT\fR
Print <TEXT> before each list item
.TP
\fB\-\-no\-aliases\fR
Don\*(Aqt show aliases in list
.TP
\fB\-\-no\-deps\fR
Don\*(Aqt run recipe dependencies
.TP
\fB\-\-no\-dotenv\fR
Don\*(Aqt load `.env` file
.TP
\fB\-\-no\-highlight\fR
Don\*(Aqt highlight echoed recipe lines in bold
.TP
\fB\-f\fR, \fB\-\-justfile\fR
Use <JUSTFILE> as justfile
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Suppress all output
.TP
\fB\-\-set\fR=\fIVARIABLE VALUE\fR
Override <VARIABLE> with <VALUE>
.TP
\fB\-\-shell\fR
Invoke <SHELL> to run recipes
.TP
\fB\-\-shell\-arg\fR
Invoke shell with <SHELL\-ARG> as an argument
.TP
\fB\-\-shell\-command\fR
Invoke <COMMAND> with the shell used to run recipe lines and backticks
.TP
\fB\-\-clear\-shell\-args\fR
Clear shell arguments
.TP
\fB\-n\fR, \fB\-\-dry\-run\fR
Print what just would do without doing it
\fB\-u\fR, \fB\-\-unsorted\fR
Return list and summary entries in source order
.TP
\fB\-\-unstable\fR
Enable unstable features
.RS
May also be specified with the \fBJUST_UNSTABLE\fR environment variable.
.RE
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Use verbose output
.TP
\fB\-d\fR, \fB\-\-working\-directory\fR
Use <WORKING\-DIRECTORY> as working directory. \-\-justfile must also be set
.TP
\fB\-\-changelog\fR
Print changelog
.TP
\fB\-\-choose\fR
Select one or more recipes to run using a binary chooser. If `\-\-chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`
.TP
\fB\-c\fR, \fB\-\-command\fR
Run an arbitrary command with the working directory, `.env`, overrides, and exports set
.TP
\fB\-\-completions\fR=\fISHELL\fR
Print shell completion script for <SHELL>
.br
.br
[\fIpossible values: \fRbash, elvish, fish, powershell, zsh]
.TP
\fB\-\-dump\fR
Print justfile
.TP
\fB\-e\fR, \fB\-\-edit\fR
Edit justfile with editor given by $VISUAL or $EDITOR, falling back to
`vim`
Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`
.TP
\fB\-\-evaluate\fR
Evaluate and print all variables. If a variable name is given as an
argument, only print that variable's value.
Evaluate and print all variables. If a variable name is given as an argument, only print that variable\*(Aqs value.
.TP
\fB\-\-fmt\fR
Format and overwrite justfile
.TP
\fB\-\-highlight\fR
Highlight echoed recipe lines in bold
.TP
\fB\-\-init\fR
Initialize new justfile in project root
.TP
\fB\-l\fR, \fB\-\-list\fR
List available recipes and their arguments
.TP
\fB\-\-no\-aliases\fR
Don't show aliases in list
\fB\-\-man\fR
Print man page
.TP
\fB\-\-no\-deps\fR
Don't run recipe dependencies
.TP
\fB\-\-no\-dotenv\fR
Don't load `.env` file
.TP
\fB\-\-no\-highlight\fR
Don't highlight echoed recipe lines in bold
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Suppress all output
.TP
\fB\-\-shell\-command\fR
Invoke <COMMAND> with the shell used to run recipe lines and backticks
\fB\-s\fR, \fB\-\-show\fR=\fIRECIPE\fR
Show information about <RECIPE>
.TP
\fB\-\-summary\fR
List names of available recipes
.TP
\fB\-u\fR, \fB\-\-unsorted\fR
Return list and summary entries in source order
.TP
\fB\-\-unstable\fR
Enable unstable features
.TP
\fB\-\-variables\fR
List names of variables
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Use verbose output
\fB\-\-dotenv\-filename\fR
Search for environment file named <DOTENV\-FILENAME> instead of `.env`
.TP
\fB\-\-yes\fR
Automatically confirm all recipes.
\fB\-E\fR, \fB\-\-dotenv\-path\fR
Load <DOTENV\-PATH> as environment file instead of searching for one
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help information
Print help
.TP
\fB\-V\fR, \fB\-\-version\fR
Print version information
.SS "OPTIONS:"
Print version
.TP
\fB\-\-chooser\fR <CHOOSER>
Override binary invoked by `\-\-choose`
.HP
\fB\-\-color\fR <COLOR>
.TP
Print colorful output [default: auto]
[possible values: auto, always, never]
.HP
\fB\-c\fR, \fB\-\-command\fR <COMMAND>
.IP
Run an arbitrary command with the working directory, `.env`, overrides, and exports set
.HP
\fB\-\-command\-color\fR <COMMAND\-COLOR>
.IP
Echo recipe lines in <COMMAND\-COLOR> [possible values: black, blue, cyan, green, purple, red,
yellow]
.HP
\fB\-\-completions\fR <SHELL>
.IP
Print shell completion script for <SHELL> [possible values: zsh, bash, fish, powershell,
elvish]
.HP
\fB\-\-dotenv\-filename\fR <DOTENV\-FILENAME>
.IP
Search for environment file named <DOTENV\-FILENAME> instead of `.env`
.HP
\fB\-E\fR, \fB\-\-dotenv\-path\fR <DOTENV\-PATH>
.IP
Load <DOTENV\-PATH> as environment file instead of searching for one
.HP
\fB\-\-dump\-format\fR <FORMAT>
.TP
Dump justfile as <FORMAT> [default: just]
[possible values: just, json]
.TP
\fB\-f\fR, \fB\-\-justfile\fR <JUSTFILE>
Use <JUSTFILE> as justfile
.TP
\fB\-\-list\-heading\fR <TEXT>
Print <TEXT> before list
.TP
\fB\-\-list\-prefix\fR <TEXT>
Print <TEXT> before each list item
.TP
\fB\-\-set\fR <VARIABLE> <VALUE>
Override <VARIABLE> with <VALUE>
.TP
\fB\-\-shell\fR <SHELL>
Invoke <SHELL> to run recipes
.TP
\fB\-\-shell\-arg\fR <SHELL\-ARG>...
Invoke shell with <SHELL\-ARG> as an argument
.TP
\fB\-s\fR, \fB\-\-show\fR <RECIPE>
Show information about <RECIPE>
.HP
\fB\-d\fR, \fB\-\-working\-directory\fR <WORKING\-DIRECTORY>
.IP
Use <WORKING\-DIRECTORY> as working directory. \fB\-\-justfile\fR must also be set
.SS "ARGS:"
.TP
<ARGUMENTS>...
[\fIARGUMENTS\fR]
Overrides and recipe(s) to run, defaulting to the first recipe in the justfile
.SH VERSION
v1.26.0
.SH AUTHORS
Casey Rodarmor <casey@rodarmor.com>

View File

@ -56,6 +56,7 @@ mod cmd {
pub(crate) const FORMAT: &str = "FORMAT";
pub(crate) const INIT: &str = "INIT";
pub(crate) const LIST: &str = "LIST";
pub(crate) const MAN: &str = "MAN";
pub(crate) const SHOW: &str = "SHOW";
pub(crate) const SUMMARY: &str = "SUMMARY";
pub(crate) const VARIABLES: &str = "VARIABLES";
@ -71,13 +72,14 @@ mod cmd {
FORMAT,
INIT,
LIST,
MAN,
SHOW,
SUMMARY,
VARIABLES,
];
pub(crate) const ARGLESS: &[&str] = &[
CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, SUMMARY, VARIABLES,
CHANGELOG, DUMP, EDIT, FORMAT, INIT, LIST, MAN, SUMMARY, VARIABLES,
];
}
@ -140,8 +142,15 @@ mod arg {
impl Config {
pub(crate) fn app() -> Command {
let app = Command::new(env!("CARGO_PKG_NAME"))
Command::new(env!("CARGO_PKG_NAME"))
.bin_name(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
.about(concat!(
env!("CARGO_PKG_DESCRIPTION"),
" - ",
env!("CARGO_PKG_HOMEPAGE")
))
.trailing_var_arg(true)
.styles(
Styles::styled()
@ -400,6 +409,12 @@ impl Config {
.action(ArgAction::SetTrue)
.help("List available recipes and their arguments"),
)
.arg(
Arg::new(cmd::MAN)
.long("man")
.action(ArgAction::SetTrue)
.help("Print man page"),
)
.arg(
Arg::new(cmd::SHOW)
.short('s')
@ -442,24 +457,7 @@ impl Config {
.num_args(1..)
.action(ArgAction::Append)
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
);
if cfg!(feature = "help4help2man") {
app.version(env!("CARGO_PKG_VERSION")).about(concat!(
"- Please see ",
env!("CARGO_PKG_HOMEPAGE"),
" for more information."
))
} else {
app
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
.about(concat!(
env!("CARGO_PKG_DESCRIPTION"),
" - ",
env!("CARGO_PKG_HOMEPAGE")
))
}
)
}
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
@ -629,6 +627,8 @@ impl Config {
Subcommand::Init
} else if matches.get_flag(cmd::LIST) {
Subcommand::List
} else if matches.get_flag(cmd::MAN) {
Subcommand::Man
} else if let Some(name) = matches.get_one::<String>(cmd::SHOW).map(Into::into) {
Subcommand::Show { name }
} else if matches.get_flag(cmd::EVALUATE) {

View File

@ -142,6 +142,9 @@ pub(crate) enum Error<'src> {
line_number: Option<usize>,
signal: i32,
},
StdoutIo {
io_error: io::Error,
},
TempdirIo {
recipe: &'src str,
io_error: io::Error,
@ -406,6 +409,9 @@ impl<'src> ColorDisplay for Error<'src> {
write!(f, "Recipe `{recipe}` was terminated by signal {signal}")?;
}
}
StdoutIo { io_error } => {
write!(f, "I/O error writing to stdout: {io_error}?")?;
}
TempdirIo { recipe, io_error } => {
write!(f, "Recipe `{recipe}` could not be run because of an IO error while trying to create a temporary \
directory or write a file to that directory: {io_error}")?;

View File

@ -1,5 +1,6 @@
use {
super::*,
clap_mangen::Man,
std::io::{Read, Seek},
tempfile::tempfile,
};
@ -30,6 +31,7 @@ pub(crate) enum Subcommand {
Format,
Init,
List,
Man,
Run {
arguments: Vec<String>,
overrides: BTreeMap<String, String>,
@ -56,6 +58,7 @@ impl Subcommand {
}
Completions { shell } => return Self::completions(*shell),
Init => return Self::init(config),
Man => return Self::man(),
Run {
arguments,
overrides,
@ -87,7 +90,7 @@ impl Subcommand {
Show { ref name } => Self::show(config, name, justfile)?,
Summary => Self::summary(config, justfile),
Variables => Self::variables(justfile),
Changelog | Completions { .. } | Edit | Init | Run { .. } => unreachable!(),
Changelog | Completions { .. } | Edit | Init | Man | Run { .. } => unreachable!(),
}
Ok(())
@ -440,6 +443,26 @@ impl Subcommand {
}
}
fn man() -> Result<(), Error<'static>> {
let mut buffer = Vec::<u8>::new();
Man::new(Config::app())
.render(&mut buffer)
.expect("writing to buffer cannot fail");
let mut stdout = io::stdout().lock();
stdout
.write_all(&buffer)
.map_err(|io_error| Error::StdoutIo { io_error })?;
stdout
.flush()
.map_err(|io_error| Error::StdoutIo { io_error })?;
Ok(())
}
fn list(config: &Config, level: usize, justfile: &Justfile) {
const MAX_WIDTH: usize = 50;

View File

@ -66,6 +66,7 @@ mod interrupts;
mod invocation_directory;
mod json;
mod line_prefixes;
mod man;
mod misc;
mod modules;
mod multibyte_char;

9
tests/man.rs Normal file
View File

@ -0,0 +1,9 @@
use super::*;
#[test]
fn output() {
Test::new()
.arg("--man")
.stdout_regex("(?s).*.TH just 1.*")
.run();
}