From 6b56bb4a79a3aa20160c21ec7e5e17e9c4b2b6c3 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 26 Aug 2022 17:08:20 +0200 Subject: [PATCH 1/7] use indented strings where appropriate --- doc/manual/generate-manpage.nix | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 17701c3a3..7b1ca8c11 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -8,12 +8,18 @@ let showCommand = { command, def, filename }: '' - **Warning**: This program is **experimental** and its interface is subject to change. + > **Warning** + > This program is **experimental** and its interface is subject to change. + + # Name + + `${command}` - ${def.description} + + # Synopsis + + ${showSynopsis { inherit command; args = def.args; }} + '' - + "# Name\n\n" - + "`${command}` - ${def.description}\n\n" - + "# Synopsis\n\n" - + showSynopsis { inherit command; args = def.args; } + (if def.commands or {} != {} then let @@ -24,8 +30,10 @@ let + "[`${command} ${name}`](./${appendName filename name}.md)" + " - ${cmds.${name}.description}\n") (attrNames cmds)); - in - "where *subcommand* is one of the following:\n\n" + in '' + where *subcommand* is one of the following: + + '' # FIXME: group by category + (if length categories > 1 then @@ -76,7 +84,7 @@ let showSynopsis = { command, args }: "`${command}` [*option*...] ${concatStringsSep " " - (map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}\n\n"; + (map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}"; processCommand = { command, def, filename }: [ { name = filename + ".md"; value = showCommand { inherit command def filename; }; inherit command; } ] From 4655563470b59e0ef50a33af003058c2b54db778 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 26 Aug 2022 17:40:34 +0200 Subject: [PATCH 2/7] refactor rendering command documentation to markdown idea: - make document structure visible, like in a template - order functions by descending abstraction - avoid nested let bindings --- doc/manual/generate-manpage.nix | 138 ++++++++++++++++---------------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 7b1ca8c11..c8290685a 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -5,10 +5,70 @@ with import ./utils.nix; let - showCommand = - { command, def, filename }: - '' - > **Warning** + showCommand = { command, def, filename }: + let + showSynopsis = command: args: + let + showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "..."); + arguments = concatStringsSep " " (map showArgument args); + in '' + `${command}` [*option*...] ${arguments} + ''; + maybeSubcommands = if def ? commands && def.commands != {} + then '' + where *subcommand* is one of the following: + + ${subcommands} + '' + else ""; + subcommands = if length categories > 1 + then listCategories + else listSubcommands def.commands; + categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands))); + listCategories = concatStrings (map showCategory categories); + showCategory = cat: '' + **${toString cat.description}:** + + ${listSubcommands (filterAttrs (n: v: v.category == cat) def.commands)} + ''; + listSubcommands = cmds: concatStrings (attrValues (mapAttrs showSubcommand cmds)); + showSubcommand = name: subcmd: '' + * [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description} + ''; + maybeDocumentation = if def ? doc then def.doc else ""; + maybeOptions = if def.flags == {} then "" else '' + # Options + + ${showOptions def.flags} + ''; + showOptions = flags: + let + categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues flags))); + in + concatStrings (map + (cat: + (if cat != "" + then "**${cat}:**\n\n" + else "") + + concatStrings + (map (longName: + let + flag = flags.${longName}; + in + " - `--${longName}`" + + (if flag ? shortName then " / `-${flag.shortName}`" else "") + + (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "") + + " \n" + + " " + flag.description + "\n\n" + ) (attrNames (filterAttrs (n: v: v.category == cat) flags)))) + categories); + squash = string: # squash more than two repeated newlines + let + replaced = replaceStrings [ "\n\n\n" ] [ "\n\n" ] string; + in + if replaced == string then string else squash replaced; + in squash '' + > **Warning** \ > This program is **experimental** and its interface is subject to change. # Name @@ -17,75 +77,17 @@ let # Synopsis - ${showSynopsis { inherit command; args = def.args; }} + ${showSynopsis command def.args} - '' - + (if def.commands or {} != {} - then - let - categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands))); - listCommands = cmds: - concatStrings (map (name: - "* " - + "[`${command} ${name}`](./${appendName filename name}.md)" - + " - ${cmds.${name}.description}\n") - (attrNames cmds)); - in '' - where *subcommand* is one of the following: + ${maybeSubcommands} - '' - # FIXME: group by category - + (if length categories > 1 - then - concatStrings (map - (cat: - "**${toString cat.description}:**\n\n" - + listCommands (filterAttrs (n: v: v.category == cat) def.commands) - + "\n" - ) categories) - + "\n" - else - listCommands def.commands - + "\n") - else "") - + (if def ? doc - then def.doc + "\n\n" - else "") - + (let s = showOptions def.flags; in - if s != "" - then "# Options\n\n${s}" - else "") - ; + ${maybeDocumentation} + + ${maybeOptions} + ''; appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name; - showOptions = flags: - let - categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues flags))); - in - concatStrings (map - (cat: - (if cat != "" - then "**${cat}:**\n\n" - else "") - + concatStrings - (map (longName: - let - flag = flags.${longName}; - in - " - `--${longName}`" - + (if flag ? shortName then " / `-${flag.shortName}`" else "") - + (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "") - + " \n" - + " " + flag.description + "\n\n" - ) (attrNames (filterAttrs (n: v: v.category == cat) flags)))) - categories); - - showSynopsis = - { command, args }: - "`${command}` [*option*...] ${concatStringsSep " " - (map (arg: "*${arg.label}*" + (if arg ? arity then "" else "...")) args)}"; - processCommand = { command, def, filename }: [ { name = filename + ".md"; value = showCommand { inherit command def filename; }; inherit command; } ] ++ concatMap From a85df04fcb2876591ac39d55e920c7cd15411431 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 26 Aug 2022 23:09:19 +0200 Subject: [PATCH 3/7] refactor showOptions it was quite a pain to manipulate strings just with `builtins` --- doc/manual/generate-manpage.nix | 45 ++++++++++++++------------------- doc/manual/utils.nix | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index c8290685a..82aae8b46 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -41,33 +41,26 @@ let ${showOptions def.flags} ''; - showOptions = flags: + showOptions = options: let - categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues flags))); - in - concatStrings (map - (cat: - (if cat != "" - then "**${cat}:**\n\n" - else "") - + concatStrings - (map (longName: - let - flag = flags.${longName}; - in - " - `--${longName}`" - + (if flag ? shortName then " / `-${flag.shortName}`" else "") - + (if flag ? labels then " " + (concatStringsSep " " (map (s: "*${s}*") flag.labels)) else "") - + " \n" - + " " + flag.description + "\n\n" - ) (attrNames (filterAttrs (n: v: v.category == cat) flags)))) - categories); - squash = string: # squash more than two repeated newlines - let - replaced = replaceStrings [ "\n\n\n" ] [ "\n\n" ] string; - in - if replaced == string then string else squash replaced; - in squash '' + showCategory = cat: '' + ${if cat != "" then "**${cat}:**" else ""} + + ${listOptions (filterAttrs (n: v: v.category == cat) options)} + ''; + listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts)); + showOption = name: option: + let + shortName = if option ? shortName then "/ `-${option.shortName}`" else ""; + labels = if option ? labels then (concatStringsSep " " (map (s: "*${s}*") option.labels)) else ""; + in trim '' + - `--${name}` ${shortName} ${labels} + + ${option.description} + ''; + categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues options))); + in concatStrings (map showCategory categories); + in squash '' > **Warning** \ > This program is **experimental** and its interface is subject to change. diff --git a/doc/manual/utils.nix b/doc/manual/utils.nix index d4b18472f..d0643ef46 100644 --- a/doc/manual/utils.nix +++ b/doc/manual/utils.nix @@ -5,6 +5,32 @@ rec { concatStrings = concatStringsSep ""; + replaceStringsRec = from: to: string: + # recursively replace occurrences of `from` with `to` within `string` + # example: + # replaceStringRec "--" "-" "hello-----world" + # => "hello-world" + let + replaced = replaceStrings [ from ] [ to ] string; + in + if replaced == string then string else replaceStringsRec from to replaced; + + squash = replaceStringsRec "\n\n\n" "\n\n"; + + trim = string: + # trim trailing spaces and squash non-leading spaces + let + trimLine = line: + let + # separate leading spaces from the rest + parts = split "(^ *)" line; + spaces = head (elemAt parts 1); + rest = elemAt parts 2; + # drop trailing spaces + body = head (split " *$" rest); + in spaces + replaceStringsRec " " " " body; + in concatStringsSep "\n" (map trimLine (splitLines string)); + # FIXME: O(n^2) unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) []; From 0e0f1832de10bf200bca2b2b3e886c7255999216 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 27 Aug 2022 02:43:57 +0200 Subject: [PATCH 4/7] remove superfluous let-in pair --- doc/manual/generate-manpage.nix | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 82aae8b46..2428ee1b3 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -91,9 +91,6 @@ let }) (attrNames def.commands or {}); -in - -let manpages = processCommand { filename = "nix"; command = "nix"; def = builtins.fromJSON command; }; summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages); in From 61188cb820cd71d7f0ea74f11791c208e69aa632 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 27 Aug 2022 02:44:54 +0200 Subject: [PATCH 5/7] move final template to the top this allows readers to enter the code starting with what is visible from the outside, instead of working themselves up from purely technical details. --- doc/manual/generate-manpage.nix | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 2428ee1b3..273b6bce3 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -7,6 +7,24 @@ let showCommand = { command, def, filename }: let + result = '' + > **Warning** \ + > This program is **experimental** and its interface is subject to change. + + # Name + + `${command}` - ${def.description} + + # Synopsis + + ${showSynopsis command def.args} + + ${maybeSubcommands} + + ${maybeDocumentation} + + ${maybeOptions} + ''; showSynopsis = command: args: let showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "..."); @@ -60,24 +78,7 @@ let ''; categories = sort builtins.lessThan (unique (map (cmd: cmd.category) (attrValues options))); in concatStrings (map showCategory categories); - in squash '' - > **Warning** \ - > This program is **experimental** and its interface is subject to change. - - # Name - - `${command}` - ${def.description} - - # Synopsis - - ${showSynopsis command def.args} - - ${maybeSubcommands} - - ${maybeDocumentation} - - ${maybeOptions} - ''; + in squash result; appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name; From 70eea9774230c5264ced249c9b81e28498027b05 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 27 Aug 2022 03:25:12 +0200 Subject: [PATCH 6/7] use more self-explanatory names --- doc/manual/generate-manpage.nix | 58 ++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 273b6bce3..19943b698 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -5,7 +5,7 @@ with import ./utils.nix; let - showCommand = { command, def, filename }: + showCommand = { command, details, filename }: let result = '' > **Warning** \ @@ -13,11 +13,11 @@ let # Name - `${command}` - ${def.description} + `${command}` - ${details.description} # Synopsis - ${showSynopsis command def.args} + ${showSynopsis command details.args} ${maybeSubcommands} @@ -32,7 +32,7 @@ let in '' `${command}` [*option*...] ${arguments} ''; - maybeSubcommands = if def ? commands && def.commands != {} + maybeSubcommands = if details ? commands && details.commands != {} then '' where *subcommand* is one of the following: @@ -41,23 +41,23 @@ let else ""; subcommands = if length categories > 1 then listCategories - else listSubcommands def.commands; - categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands))); + else listSubcommands details.commands; + categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues details.commands))); listCategories = concatStrings (map showCategory categories); showCategory = cat: '' **${toString cat.description}:** - ${listSubcommands (filterAttrs (n: v: v.category == cat) def.commands)} + ${listSubcommands (filterAttrs (n: v: v.category == cat) details.commands)} ''; listSubcommands = cmds: concatStrings (attrValues (mapAttrs showSubcommand cmds)); showSubcommand = name: subcmd: '' * [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description} ''; - maybeDocumentation = if def ? doc then def.doc else ""; - maybeOptions = if def.flags == {} then "" else '' + maybeDocumentation = if details ? doc then details.doc else ""; + maybeOptions = if details.flags == {} then "" else '' # Options - ${showOptions def.flags} + ${showOptions details.flags} ''; showOptions = options: let @@ -82,17 +82,29 @@ let appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name; - processCommand = { command, def, filename }: - [ { name = filename + ".md"; value = showCommand { inherit command def filename; }; inherit command; } ] - ++ concatMap - (name: processCommand { - filename = appendName filename name; - command = command + " " + name; - def = def.commands.${name}; - }) - (attrNames def.commands or {}); + processCommand = { command, details, filename }: + let + cmd = { + inherit command; + name = filename + ".md"; + value = showCommand { inherit command details filename; }; + }; + subcommand = subCmd: processCommand { + command = command + " " + subCmd; + details = details.commands.${subCmd}; + filename = appendName filename subCmd; + }; + in [ cmd ] ++ concatMap subcommand (attrNames details.commands or {}); - manpages = processCommand { filename = "nix"; command = "nix"; def = builtins.fromJSON command; }; - summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages); -in -(listToAttrs manpages) // { "SUMMARY.md" = summary; } + manpages = processCommand { + command = "nix"; + details = builtins.fromJSON command; + filename = "nix"; + }; + + tableOfContents = let + showEntry = page: + " - [${page.command}](command-ref/new-cli/${page.name})"; + in concatStringsSep "\n" (map showEntry manpages); + +in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; } From d8bef7358f67d11eeef46ae015291a92f8ba1b72 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 30 Sep 2022 01:41:56 +0200 Subject: [PATCH 7/7] bring back lost newline --- doc/manual/generate-manpage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 19943b698..18a1a8bfe 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -105,6 +105,6 @@ let tableOfContents = let showEntry = page: " - [${page.command}](command-ref/new-cli/${page.name})"; - in concatStringsSep "\n" (map showEntry manpages); + in concatStringsSep "\n" (map showEntry manpages) + "\n"; in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }