Skip to content

Commit

Permalink
Basic implementation of DEBUG trap, help improvements (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
reubeno authored May 18, 2024
1 parent baa7b2e commit aa2a3b7
Show file tree
Hide file tree
Showing 54 changed files with 437 additions and 71 deletions.
3 changes: 3 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ struct CommandLineArgs {
enum TraceEvent {
#[clap(name = "arithmetic")]
Arithmetic,
#[clap(name = "commands")]
Commands,
#[clap(name = "complete")]
Complete,
#[clap(name = "expand")]
Expand Down Expand Up @@ -124,6 +126,7 @@ fn main() {
for event in enabled_trace_events {
let targets = match event {
TraceEvent::Arithmetic => vec!["parser::arithmetic"],
TraceEvent::Commands => vec!["commands"],
TraceEvent::Complete => vec!["shell::completion", "shell::builtins::complete"],
TraceEvent::Expand => vec![],
TraceEvent::Parse => vec!["parse"],
Expand Down
6 changes: 6 additions & 0 deletions cli/tests/cases/builtins/help.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ cases:
ignore_stderr: true
stdin: |
help
- name: "Topic-specific help"
ignore_stdout: true
ignore_stderr: true
stdin: |
help echo
29 changes: 29 additions & 0 deletions cli/tests/cases/builtins/trap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Builtins: trap"
cases:
- name: "trap registration"
stdin: |
trap "echo 1" SIGINT
trap "echo 2" SIGINT
trap
trap "echo 3" int
trap
trap "echo 4" 2
trap
- name: "trap EXIT"
known_failure: true
stdin: |
trap "echo [exit]" EXIT
trap
- name: "trap DEBUG"
stdin: |
trap 'echo [command: ${BASH_COMMAND}]' DEBUG
trap
- name: "trap ERR"
stdin: |
trap "echo [err]" ERR
trap
113 changes: 108 additions & 5 deletions shell/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,14 @@ pub trait BuiltinCommand: Parser {
context: context::CommandExecutionContext<'_>,
) -> Result<BuiltinExitCode, error::Error>;

#[allow(dead_code)]
fn get_long_help() -> String {
Self::command().render_long_help().to_string()
fn get_content(name: &str, content_type: BuiltinContentType) -> String {
match content_type {
BuiltinContentType::DetailedHelp => Self::command().render_help().to_string(),
BuiltinContentType::ShortUsage => get_builtin_short_usage(name, &Self::command()),
BuiltinContentType::ShortDescription => {
get_builtin_short_description(name, &Self::command())
}
}
}
}

Expand All @@ -128,14 +133,21 @@ pub trait BuiltinDeclarationCommand: BuiltinCommand {
fn set_declarations(&mut self, declarations: Vec<CommandArg>);
}

#[allow(clippy::module_name_repetitions)]
pub enum BuiltinContentType {
DetailedHelp,
ShortUsage,
ShortDescription,
}

#[allow(clippy::module_name_repetitions)]
#[derive(Clone)]
pub struct BuiltinRegistration {
/// Function to execute the builtin.
pub execute_func: BuiltinCommandExecuteFunc,

/// Function to retrieve the builtin's detailed help text.
pub help_func: fn() -> String,
/// Function to retrieve the builtin's content/help text.
pub content_func: fn(&str, BuiltinContentType) -> String,

/// Has this registration been disabled?
pub disabled: bool,
Expand All @@ -146,3 +158,94 @@ pub struct BuiltinRegistration {
/// Is this builtin one that takes specially handled declarations?
pub declaration_builtin: bool,
}

fn get_builtin_short_description(name: &str, command: &clap::Command) -> String {
let about = command
.get_about()
.map_or_else(String::new, |s| s.to_string());

std::format!("{name} - {about}\n")
}

fn get_builtin_short_usage(name: &str, command: &clap::Command) -> String {
let mut usage = String::new();

let mut needs_space = false;

let mut optional_short_opts = vec![];
let mut required_short_opts = vec![];
for opt in command.get_opts() {
if opt.is_hide_set() {
continue;
}

if let Some(c) = opt.get_short() {
if !opt.is_required_set() {
optional_short_opts.push(c);
} else {
required_short_opts.push(c);
}
}
}

if !optional_short_opts.is_empty() {
if needs_space {
usage.push(' ');
}

usage.push('[');
usage.push('-');
for c in optional_short_opts {
usage.push(c);
}

usage.push(']');
needs_space = true;
}

if !required_short_opts.is_empty() {
if needs_space {
usage.push(' ');
}

usage.push('-');
for c in required_short_opts {
usage.push(c);
}

needs_space = true;
}

for pos in command.get_positionals() {
if pos.is_hide_set() {
continue;
}

if !pos.is_required_set() {
if needs_space {
usage.push(' ');
}

usage.push('[');
needs_space = false;
}

if let Some(names) = pos.get_value_names() {
for name in names {
if needs_space {
usage.push(' ');
}

usage.push_str(name);
needs_space = true;
}
}

if !pos.is_required_set() {
usage.push(']');
needs_space = true;
}
}

std::format!("{name}: {name} {usage}\n")
}
1 change: 1 addition & 0 deletions shell/src/builtins/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) struct AliasCommand {
#[arg(short = 'p')]
print: bool,

/// List of aliases to display or update.
#[arg(name = "name[=value]")]
aliases: Vec<String>,
}
Expand Down
2 changes: 2 additions & 0 deletions shell/src/builtins/bg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::io::Write;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Moves a job to run in the background.
#[derive(Parser)]
pub(crate) struct BgCommand {
/// List of job specs to move to background.
job_specs: Vec<String>,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Breaks out of a control-flow loop.
#[derive(Parser)]
pub(crate) struct BreakCommand {
#[clap(default_value = "1")]
Expand Down
3 changes: 3 additions & 0 deletions shell/src/builtins/builtin_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ use crate::{
commands,
};

/// Directly invokes a built-in, without going through typical search order.
#[derive(Parser)]
pub(crate) struct BuiltiCommand {
/// Name of built-in to invoke.
builtin_name: Option<String>,

/// Arguments for the built-in.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
}
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/colon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// No-op command.
#[derive(Parser)]
#[clap(disable_help_flag = true, disable_version_flag = true)]
pub(crate) struct ColonCommand {
Expand Down
3 changes: 3 additions & 0 deletions shell/src/builtins/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ impl CommonCompleteCommandArgs {
}
}

/// Configure programmable command completion.
#[derive(Parser)]
pub(crate) struct CompleteCommand {
#[arg(short = 'p')]
Expand Down Expand Up @@ -404,6 +405,7 @@ impl CompleteCommand {
}
}

/// Generate command completions.
#[derive(Parser)]
pub(crate) struct CompGenCommand {
#[clap(flatten)]
Expand Down Expand Up @@ -454,6 +456,7 @@ impl BuiltinCommand for CompGenCommand {
}
}

/// Set programmable command completion options.
#[derive(Parser)]
pub(crate) struct CompOptCommand {
#[arg(short = 'D')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Continue to the next iteration of a control-flow loop.
#[derive(Parser)]
pub(crate) struct ContinueCommand {
#[clap(default_value = "1")]
Expand Down
5 changes: 2 additions & 3 deletions shell/src/builtins/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ builtin::minus_or_plus_flag_arg!(MakeTracedFlag, 't', "");
builtin::minus_or_plus_flag_arg!(UppercaseValueOnAssignmentFlag, 'u', "");
builtin::minus_or_plus_flag_arg!(MakeExportedFlag, 'x', "");

/// Display or update variables and their attributes.
#[derive(Parser)]
pub(crate) struct DeclareCommand {
/// Constrain to function names or definitions.
Expand Down Expand Up @@ -220,7 +221,7 @@ impl DeclareCommand {

// Extract the variable name and the initial value being assigned (if any).
let (name, assigned_index, initial_value, name_is_array) =
self.declaration_to_name_and_value(declaration)?;
Self::declaration_to_name_and_value(declaration)?;

// Figure out where we should look.
let lookup = if create_var_local {
Expand Down Expand Up @@ -283,9 +284,7 @@ impl DeclareCommand {
Ok(true)
}

#[allow(clippy::unused_self)]
fn declaration_to_name_and_value(
&self,
declaration: &CommandArg,
) -> Result<(String, Option<String>, Option<ShellValueLiteral>, bool), error::Error> {
let name;
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io::Write;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Manage the current directory stack.
#[derive(Parser, Debug, Default)]
pub(crate) struct DirsCommand {
#[arg(short = 'c')]
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
interp::ExecutionParameters,
};

/// Evalute the provided script in the current shell environment.
#[derive(Debug, Parser)]
pub(crate) struct DotCommand {
pub script_path: String,
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/echo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::io::Write;
use crate::builtin::{BuiltinCommand, BuiltinExitCode};
use crate::escape;

/// Echo text to standard output.
#[derive(Parser)]
#[clap(disable_help_flag = true, disable_version_flag = true)]
pub(crate) struct EchoCommand {
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/enable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::io::Write;
use crate::builtin::{BuiltinCommand, BuiltinExitCode};
use crate::error;

/// Enable, disable, or display built-in commands.
#[derive(Parser)]
pub(crate) struct EnableCommand {
#[arg(short = 'a')]
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use clap::Parser;

/// Evalute the given string as script.
#[derive(Parser)]
pub(crate) struct EvalCommand {
#[clap(allow_hyphen_values = true)]
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io::Write;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Exec the provided command.
#[derive(Parser)]
pub(crate) struct ExecCommand {
/// Pass given name as zeroth argument to command.
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Exit the shell.
#[derive(Parser)]
pub(crate) struct ExitCommand {
code: Option<i32>,
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
variables,
};

/// Add or update exported shell variables.
#[derive(Parser)]
pub(crate) struct ExportCommand {
#[arg(short = 'f')]
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/false_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Return a non-zero exit code.
#[derive(Parser)]
pub(crate) struct FalseCommand {}

Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/fg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io::Write;

use crate::builtin::{BuiltinCommand, BuiltinExitCode};

/// Move a specified job to the foreground.
#[derive(Parser)]
pub(crate) struct FgCommand {
job_spec: Option<String>,
Expand Down
1 change: 1 addition & 0 deletions shell/src/builtins/getopts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
variables,
};

/// Parse command options.
#[derive(Parser)]
pub(crate) struct GetOptsCommand {
/// Specification for options
Expand Down
Loading

0 comments on commit aa2a3b7

Please sign in to comment.