Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor commands #296

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 71 additions & 15 deletions docs/src/modules_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Add commands to the environment.
```


**Type**: list of (submodule)
**Type**: list of ((package or string convertible to it) or (list with two elements of types: [ string (package or string convertible to it) ]) or (flatOptions))

**Example value**:
```nix
Expand All @@ -72,7 +72,23 @@ Add commands to the environment.
Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)

## `commands.*.package`
## `commands.*`

A config for a command when the `commands` option is a list.


**Type**: (package or string convertible to it) or (list with two elements of types: [ string (package or string convertible to it) ]) or (flatOptions)

**Example value**:
```nix
{"_type":"literalExpression","text":"[\n {\n category = \"scripts\";\n package = \"black\";\n }\n [ \"[package] print hello\" \"hello\" ]\n \"nodePackages.yarn\"\n]\n"}
```


Declared in:
* [nix/commands/types.nix](https://github.com/numtide/devshell/tree/main/nix/commands/types.nix)

## `commands.*.package (flatOptions)`

Used to bring in a specific package. This package will be added to the
environment.
Expand All @@ -84,15 +100,15 @@ environment.
```


**Type**: null or (package or string convertible to it)
**Type**: null or (package or string convertible to it) or package

Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.category`
## `commands.*.category (flatOptions)`

Set a free text category under which this command is grouped
and shown in the help menu.
Sets a free text category under which this command is grouped
and shown in the devshell menu.


**Default value**:
Expand All @@ -104,9 +120,9 @@ and shown in the help menu.
**Type**: string

Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.command`
## `commands.*.command (flatOptions)`

If defined, it will add a script with the name of the command, and the
content of this value.
Expand All @@ -130,9 +146,29 @@ provided.


Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.expose (flatOptions)`

When `true`, the `(flatOptions) command`
or the `(flatOptions) package` will be added to the environment.

Otherwise, they will not be added to the environment, but will be printed
in the devshell menu.


**Default value**:
```nix
{"_type":"literalExpression","text":"true"}
```

## `commands.*.help`

**Type**: boolean

Declared in:
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.help (flatOptions)`

Describes what the command does in one line of text.

Expand All @@ -146,11 +182,15 @@ Describes what the command does in one line of text.
**Type**: null or string

Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.name`
## `commands.*.name (flatOptions)`

Name of this command. Defaults to attribute name in commands.
Name of this command.

Defaults to a `package (flatOptions)` name or pname if present.

The value of this option is required for a `command (flatOptions)`.


**Default value**:
Expand All @@ -162,7 +202,23 @@ Name of this command. Defaults to attribute name in commands.
**Type**: null or string

Declared in:
* [modules/commands.nix](https://github.com/numtide/devshell/tree/main/modules/commands.nix)
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `commands.*.prefix (flatOptions)`

Prefix of the command name in the devshell menu.


**Default value**:
```nix
{"_type":"literalExpression","text":"\"\""}
```


**Type**: string

Declared in:
* [nix/commands/flatOptions.nix](https://github.com/numtide/devshell/tree/main/nix/commands/flatOptions.nix)

## `devshell.packages`

Expand Down
172 changes: 7 additions & 165 deletions modules/commands.nix
Original file line number Diff line number Diff line change
@@ -1,163 +1,17 @@
{ lib, config, pkgs, ... }:
with lib;
let
ansi = import ../nix/ansi.nix;

# Because we want to be able to push pure JSON-like data into the
# environment.
strOrPackage = import ../nix/strOrPackage.nix { inherit lib pkgs; };

writeDefaultShellScript = import ../nix/writeDefaultShellScript.nix {
inherit (pkgs) lib writeTextFile bash;
};

pad = str: num:
if num > 0 then
pad "${str} " (num - 1)
else
str;

# Fallback to the package pname if the name is unset
resolveName = cmd:
if cmd.name == null then
cmd.package.pname or (builtins.parseDrvName cmd.package.name).name
else
cmd.name;

# Fill in default options for a command.
commandToPackage = cmd:
assert lib.assertMsg (cmd.command == null || cmd.name != cmd.command) "[[commands]]: ${toString cmd.name} cannot be set to both the `name` and the `command` attributes. Did you mean to use the `package` attribute?";
assert lib.assertMsg (cmd.package != null || (cmd.command != null && cmd.command != "")) "[[commands]]: ${resolveName cmd} expected either a command or package attribute.";
if cmd.package == null then
writeDefaultShellScript
{
name = cmd.name;
text = cmd.command;
binPrefix = true;
}
else
cmd.package;

commandsToMenu = cmds:
let
cleanName = { name, package, ... }@cmd:
assert lib.assertMsg (cmd.name != null || cmd.package != null) "[[commands]]: some command is missing both a `name` or `package` attribute.";
let
name = resolveName cmd;

help =
if cmd.help == null then
cmd.package.meta.description or ""
else
cmd.help;
in
cmd // {
inherit name help;
};

commands = map cleanName cmds;

commandLengths =
map ({ name, ... }: builtins.stringLength name) commands;

maxCommandLength =
builtins.foldl'
(max: v: if v > max then v else max)
0
commandLengths
;

commandCategories = lib.unique (
(zipAttrsWithNames [ "category" ] (name: vs: vs) commands).category
);

commandByCategoriesSorted =
builtins.attrValues (lib.genAttrs
commandCategories
(category: lib.nameValuePair category (builtins.sort
(a: b: a.name < b.name)
(builtins.filter (x: x.category == category) commands)
))
);

opCat = kv:
let
category = kv.name;
cmd = kv.value;
opCmd = { name, help, ... }:
let
len = maxCommandLength - (builtins.stringLength name);
in
if help == null || help == "" then
" ${name}"
else
" ${pad name len} - ${help}";
in
"\n${ansi.bold}[${category}]${ansi.reset}\n\n" + builtins.concatStringsSep "\n" (map opCmd cmd);
in
builtins.concatStringsSep "\n" (map opCat commandByCategoriesSorted) + "\n";

# These are all the options available for the commands.
commandOptions = {
name = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of this command. Defaults to attribute name in commands.
'';
};

category = mkOption {
type = types.str;
default = "[general commands]";
description = ''
Set a free text category under which this command is grouped
and shown in the help menu.
'';
};

help = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Describes what the command does in one line of text.
'';
};

command = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
If defined, it will add a script with the name of the command, and the
content of this value.

By default it generates a bash script, unless a different shebang is
provided.
'';
example = ''
#!/usr/bin/env python
print("Hello")
'';
};

package = mkOption {
type = types.nullOr strOrPackage;
default = null;
description = ''
Used to bring in a specific package. This package will be added to the
environment.
'';
};
};
inherit (import ../nix/commands/convert.nix { inherit pkgs; }) commandsToMenu commandToPackage;
inherit (import ../nix/commands/devshellMenu.nix { inherit pkgs; }) mkDevshellMenuCommand;
inherit (import ../nix/commands/types.nix { inherit pkgs; }) commandsFlatType;
in
{
options.commands = mkOption {
type = types.listOf (types.submodule { options = commandOptions; });
options.commands = lib.mkOption {
type = commandsFlatType;
default = [ ];
description = ''
Add commands to the environment.
'';
example = literalExpression ''
example = lib.literalExpression ''
[
{
help = "print hello";
Expand All @@ -173,20 +27,8 @@ in
'';
};

config.commands = [
{
help = "prints this menu";
name = "menu";
command = ''
cat <<'DEVSHELL_MENU'
${commandsToMenu config.commands}
DEVSHELL_MENU
'';
}
];

# Add the commands to the devshell packages. Either as wrapper scripts, or
# the whole package.
config.devshell.packages = map commandToPackage config.commands;
config.devshell.packages = map commandToPackage ([ (mkDevshellMenuCommand config.commands) ] ++ config.commands);
# config.devshell.motd = "$(motd)";
}
3 changes: 2 additions & 1 deletion modules/modules-docs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ let
let
# TODO: handle opt.relatedPackages. What is it for?
optToMd = opt:
let heading = (lib.showOption (filter isString opt.loc)) + (concatStrings (filter (x: !(isString x)) opt.loc)); in
''
## `${opt.name}`
## `${heading}`

''
+ (lib.optionalString opt.internal "\n**internal**\n")
Expand Down
Loading