1
1
import Lean.Data.RBTree
2
2
3
+ namespace Cli
4
+
3
5
section Utils
4
6
/--
5
7
Matches the lengths of lists `a` and `b` by filling the shorter one with
@@ -20,7 +22,7 @@ section Utils
20
22
return r
21
23
22
24
def flatMap (f : α → Array β) (xs : Array α) : Array β :=
23
- xs.map f |>.join
25
+ join ( xs.map f)
24
26
end Array
25
27
26
28
namespace String
@@ -52,7 +54,7 @@ section Utils
52
54
Panics if `maxWidth = 0`.
53
55
-/
54
56
def wrapAt! (s : String) (maxWidth : Nat) : String :=
55
- s. wrapAt? maxWidth |>.get!
57
+ wrapAt? s maxWidth |>.get!
56
58
57
59
/--
58
60
Deletes all trailing spaces at the end of every line, as seperated by `\n`.
@@ -88,7 +90,7 @@ section Utils
88
90
continue
89
91
-- if `w` is the first proper word, this will insert a `\n` before the text, which we remove later.
90
92
-- `w.wrapAt! maxWidth` ensures that our new line is not already too large.
91
- let wordOnNewLine := "\n " ++ w. wrapAt! maxWidth
93
+ let wordOnNewLine := "\n " ++ wrapAt! w maxWidth
92
94
result := result.push wordOnNewLine
93
95
let wrappedLines : Array String := wordOnNewLine.splitOn "\n " |>.toArray
94
96
currentLineWidth := wrappedLines[wrappedLines.size - 1 ]!.length + 1
@@ -111,7 +113,7 @@ section Utils
111
113
Panics if `maxWidth = 0`.
112
114
-/
113
115
def wrapWordsAt! (s : String) (maxWidth : Nat) : String :=
114
- s. wrapWordsAt? maxWidth |>.get!
116
+ wrapWordsAt? s maxWidth |>.get!
115
117
116
118
/--
117
119
Inserts `n` spaces before each line as seperated by `\n` in `s`.
@@ -146,16 +148,16 @@ section Utils
146
148
if maxWidth - margin - minRightColumnWidth < 1 then
147
149
return none
148
150
let rows : Array (List String × String) := rows.map fun (left, right) =>
149
- (maxWidth - margin - minRightColumnWidth |> left .wrapWordsAt! |>.splitOn "\n " , right)
151
+ (maxWidth - margin - minRightColumnWidth |> String .wrapWordsAt! left |>.splitOn "\n " , right)
150
152
let leftColumnWidth :=
151
- rows. flatMap (·.1 .map (·.length) |>.toArray)
153
+ flatMap (·.1 .map (·.length) |>.toArray) rows
152
154
|>.getMax? (· < ·)
153
155
|>.get!
154
156
let leftColumnWidth := leftColumnWidth + margin
155
157
let rows : Array (List String × List String) := rows.map fun (left, right) =>
156
- (left, maxWidth - leftColumnWidth |> right .wrapWordsAt! |>.splitOn "\n " )
157
- let rows : Array (String × String) := rows. flatMap fun (left, right) =>
158
- let (left, right) : List String × List String := left .matchLength right ""
158
+ (left, maxWidth - leftColumnWidth |> String .wrapWordsAt! right |>.splitOn "\n " )
159
+ let rows : Array (String × String) := flatMap (xs := rows) fun (left, right) =>
160
+ let (left, right) : List String × List String := List .matchLength left right ""
159
161
left.zip right |>.toArray
160
162
let rows : Array String := rows.map fun (left, right) =>
161
163
if right = "" then
@@ -174,7 +176,7 @@ section Utils
174
176
-/
175
177
def renderTable! (rows : Array (String × String)) (maxWidth : Nat) (margin : Nat := 2 )
176
178
: String :=
177
- rows. renderTable? maxWidth margin |>.get!
179
+ renderTable? rows maxWidth margin |>.get!
178
180
end Array
179
181
180
182
namespace Option
@@ -190,8 +192,6 @@ section Utils
190
192
end Option
191
193
end Utils
192
194
193
- namespace Cli
194
-
195
195
section Configuration
196
196
/--
197
197
Represents a type that can be parsed to a string and the corresponding name of the type.
@@ -457,9 +457,9 @@ section Configuration
457
457
/-- Checks whether `m` has a flag with the corresponding `shortName`. -/
458
458
def hasFlagByShortName (m : Meta) (name : String) : Bool := m.flagByShortName? name |>.isSome
459
459
460
- /--
460
+ /--
461
461
Adds help (`-h, --help`) and version (`--version`) flags to `m`. Does not add
462
- a version flag if `m` does not designate a version.
462
+ a version flag if `m` does not designate a version.
463
463
-/
464
464
def addHelpAndVersionFlags (m : Meta) : Meta := Id.run do
465
465
let helpFlag := .paramless
@@ -475,7 +475,7 @@ section Configuration
475
475
{ m with flags := fixedFlags ++ m.flags }
476
476
end Cmd.Meta
477
477
478
- /--
478
+ /--
479
479
Represents a recursive variant of `Cmd.Meta` that is used in `Parsed`
480
480
to replicate the recursive subcommand structure of a command
481
481
without referring to the command itself.
@@ -679,7 +679,7 @@ section Configuration
679
679
(subCmds : Array ExtendableCmd := #[])
680
680
: ExtendableCmd :=
681
681
.mk'
682
- ⟨name, parent.meta.parentNames.push parent.meta.name, version?, description, furtherInformation?,
682
+ ⟨name, parent.meta.parentNames.push parent.meta.name, version?, description, furtherInformation?,
683
683
flags, positionalArgs, variableArg?⟩
684
684
run subCmds
685
685
@@ -710,7 +710,7 @@ section Configuration
710
710
(subCmds : Array ExtendableCmd := #[])
711
711
: ExtendableCmd :=
712
712
.mkWithHelpAndVersionFlags'
713
- ⟨name, parent.meta.parentNames.push parent.meta.name, version?, description, furtherInformation?,
713
+ ⟨name, parent.meta.parentNames.push parent.meta.name, version?, description, furtherInformation?,
714
714
flags, positionalArgs, variableArg?⟩
715
715
run subCmds
716
716
@@ -783,18 +783,18 @@ section Configuration
783
783
- The output of the parser can be postprocessed and validated.
784
784
-/
785
785
structure Extension where
786
- /--
786
+ /--
787
787
Priority that dictates how early an extension is applied.
788
788
The lower the priority, the later it is applied.
789
789
-/
790
790
priority : Nat := 1024
791
- /--
791
+ /--
792
792
Extends a command to adjust the displayed help.
793
793
The recursive subcommand structure may be mutated.
794
794
-/
795
795
extend : ExtendableCmd → ExtendableCmd := id
796
- /--
797
- Processes and validates the output of the parser for the given `ExtendableCmd`.
796
+ /--
797
+ Processes and validates the output of the parser for the given `ExtendableCmd`.
798
798
Takes the `ExtendableCmd` that results from all extensions being applied.
799
799
If postprocessing mutates the subcommand structure in `Parsed.cmd`, care must be taken to update
800
800
`Parsed.parent?` accordingly as well.
@@ -852,7 +852,7 @@ section Configuration
852
852
let subCmdParentNames := parentNames.push c.meta.name
853
853
let subCmds := c.subCmds.map (loop · subCmdParentNames)
854
854
.init { c.meta with parentNames := parentNames } c.run subCmds c.extension?
855
-
855
+
856
856
/--
857
857
Creates a new command. Adds a `-h, --help` and a `--version` flag if `meta` designates a version.
858
858
Updates the `parentNames` of all subcommands.
@@ -947,7 +947,7 @@ section Configuration
947
947
for ⟨fullName, extension?⟩ in extensions do
948
948
extensionIndex := extensionIndex.insert fullName extension?
949
949
let rec loop (c : ExtendableCmd) : Cli.Cmd :=
950
- let extension? := do extensionIndex.find? (← c.originalFullName?) |>.join
950
+ let extension? := do extensionIndex.find? (← c.originalFullName?) |> Option .join
951
951
let subCmds := c.subCmds.map loop
952
952
.init c.meta c.run subCmds extension?
953
953
loop c |>.updateParentNames |> prependOriginalParentNames
@@ -1052,24 +1052,27 @@ section Macro
1052
1052
(description := $description)
1053
1053
(flags := $(quote (← flags.getD #[] |>.mapM expandFlag)))
1054
1054
(positionalArgs := $(quote (← positionalArgs.getD #[] |>.mapM expandPositionalArg)))
1055
- (variableArg? := $(quote (← variableArg .join.mapM expandVariableArg)))
1055
+ (variableArg? := $(quote (← (Option .join variableArg) .mapM expandVariableArg)))
1056
1056
(run := $(← expandRunFun run))
1057
1057
(subCmds := $(quote (subCommands.getD ⟨#[]⟩).getElems))
1058
1058
(extension? := some <| Array.foldl Extension.then { : Extension } <| Array.qsort
1059
1059
$(quote (extensions.getD ⟨#[]⟩).getElems) (·.priority > ·.priority)))
1060
1060
end Macro
1061
1061
1062
1062
section Info
1063
+ open Cli.String
1064
+ open Cli.Option
1065
+
1063
1066
/-- Maximum width within which all formatted text should fit. -/
1064
1067
def maxWidth : Nat := 80
1065
1068
/-- Amount of spaces with which section content is indented. -/
1066
1069
def indentation : Nat := 4
1067
1070
/-- Maximum width within which all formatted text should fit, after indentation. -/
1068
1071
def maxIndentedWidth : Nat := maxWidth - indentation
1069
1072
/-- Formats `xs` by `String.optJoin`ing the components with a single space. -/
1070
- def line (xs : Array String) : String := " " . optJoin xs
1073
+ def line (xs : Array String) : String := optJoin xs " "
1071
1074
/-- Formats `xs` by `String.optJoin`ing the components with a newline `\n`. -/
1072
- def lines (xs : Array String) : String := "\n " .optJoin xs
1075
+ def lines (xs : Array String) : String := optJoin xs "\n "
1073
1076
1074
1077
/--
1075
1078
Renders a help section with the designated `title` and `content`.
@@ -1088,43 +1091,43 @@ section Info
1088
1091
else
1089
1092
return s! "{ title} :"
1090
1093
lines #[
1091
- titleLine?.optStr ,
1092
- content |>. wrapWordsAt! maxIndentedWidth |>. indent indentation
1094
+ optStr titleLine?,
1095
+ wrapWordsAt! content maxIndentedWidth |> indent (n := indentation)
1093
1096
]
1094
1097
1095
1098
/--
1096
- Renders a table using `Array.renderTable!` and then renders a section with
1099
+ Renders a table using `Cli. Array.renderTable!` and then renders a section with
1097
1100
the designated `title` and the rendered table as content.
1098
1101
-/
1099
1102
def renderTable
1100
1103
(title : String)
1101
1104
(columns : Array (String × String))
1102
1105
(emptyTablePlaceholder? : Option String := none)
1103
1106
: String :=
1104
- let table := columns .renderTable! (maxWidth := maxIndentedWidth)
1107
+ let table := Array .renderTable! columns (maxWidth := maxIndentedWidth)
1105
1108
renderSection title table emptyTablePlaceholder?
1106
1109
1107
1110
namespace Cmd
1108
1111
private def metaDataInfo (c : Cmd) : String :=
1109
1112
let version? : Option String := do return s! "[{ ← c.meta.version?} ]"
1110
1113
lines #[
1111
- line #[c.meta.fullName, version?.optStr ] |>. wrapWordsAt! maxWidth,
1112
- c.meta.description.wrapWordsAt! maxWidth
1114
+ line #[c.meta.fullName, optStr version?] |> wrapWordsAt! ( maxWidth := maxWidth) ,
1115
+ wrapWordsAt! c.meta.description maxWidth
1113
1116
]
1114
1117
1115
1118
private def usageInfo (c : Cmd) : String :=
1116
1119
let subCmdTitle? : Option String := if ¬c.subCmds.isEmpty then "[SUBCOMMAND]" else none
1117
1120
let posArgNames : String := line <| c.meta.positionalArgs.map (s! "<{ ·.name} >" )
1118
1121
let varArgName? : Option String := do return s! "<{ (← c.meta.variableArg?).name} >..."
1119
- let info := line #[c.meta.fullName, subCmdTitle?.optStr , "[FLAGS]" , posArgNames, varArgName?.optStr ]
1122
+ let info := line #[c.meta.fullName, optStr subCmdTitle?, "[FLAGS]" , posArgNames, optStr varArgName?]
1120
1123
renderSection "USAGE" info
1121
1124
1122
1125
private def flagInfo (c : Cmd) : String :=
1123
1126
let columns : Array (String × String) := c.meta.flags.map fun flag =>
1124
1127
let shortName? : Option String := do return s! "-{ ← flag.shortName?} "
1125
- let names : String := ", " . optJoin #[shortName?.optStr , s! "--{ flag.longName} " ]
1128
+ let names : String := optJoin #[optStr shortName?, s! "--{ flag.longName} " ] ", "
1126
1129
let type? : Option String := if ¬ flag.isParamless then s! ": { flag.type.name} " else none
1127
- (line #[names, type?.optStr ], flag.description)
1130
+ (line #[names, optStr type?], flag.description)
1128
1131
renderTable "FLAGS" columns (emptyTablePlaceholder? := "None" )
1129
1132
1130
1133
private def positionalArgInfo (c : Cmd) : String :=
@@ -1148,14 +1151,14 @@ section Info
1148
1151
"\n " ++ c.flagInfo,
1149
1152
(if ¬c.meta.positionalArgs.isEmpty ∨ c.meta.hasVariableArg then "\n " else "" ) ++ c.positionalArgInfo,
1150
1153
(if ¬c.subCmds.isEmpty then "\n " else "" ) ++ c.subCommandInfo,
1151
- (if c.meta.hasFurtherInformation then "\n " else "" ) ++ c.meta.furtherInformation?.optStr
1154
+ (if c.meta.hasFurtherInformation then "\n " else "" ) ++ optStr c.meta.furtherInformation?
1152
1155
]
1153
1156
1154
1157
/-- Renders an error for `c` with the designated message `msg`. -/
1155
1158
def error (c : Cmd) (msg : String) : String :=
1156
1159
lines #[
1157
- msg. wrapWordsAt! maxWidth,
1158
- s! "Run `{ c.meta.fullName} -h` for further information.".wrapWordsAt! maxWidth
1160
+ wrapWordsAt! msg maxWidth,
1161
+ wrapWordsAt! s! "Run `{ c.meta.fullName} -h` for further information." maxWidth
1159
1162
]
1160
1163
1161
1164
/-- Prints the help for `c`. -/
@@ -1216,7 +1219,7 @@ section Parsing
1216
1219
return s! " (`--{ flag.longName} `)"
1217
1220
else
1218
1221
return s! " (`-{ ← flag.shortName?} `)"
1219
- s! "Duplicate flag `{ inputFlag} `{ complementaryName? .optStr} ." )
1222
+ s! "Duplicate flag `{ inputFlag} `{ Option .optStr complementaryName? } ." )
1220
1223
| redundantFlagArg
1221
1224
(flag : Flag)
1222
1225
(inputFlag : InputFlag)
@@ -1538,12 +1541,12 @@ section Parsing
1538
1541
partial def applyExtensions (c : Cmd) : Cmd := Id.run do
1539
1542
let subCmds := c.subCmds.map applyExtensions
1540
1543
let c := c.update (subCmds := subCmds)
1541
- let some extension := c.extension?
1544
+ let some extension := c.extension?
1542
1545
| return c
1543
1546
extension.extend (.ofFullCmd c) |>.toFullCmd c
1544
1547
1545
1548
/--
1546
- Processes `args` by applying all extensions in `c`, `Cmd.parse?`ing the input according to `c`
1549
+ Processes `args` by applying all extensions in `c`, `Cmd.parse?`ing the input according to `c`
1547
1550
and then applying the postprocessing extension of the respective (sub)command that was called.
1548
1551
Note that `args` designates the list `<foo>` in `somebinary <foo>`.
1549
1552
Returns either the (sub)command that an error occured in and the corresponding error message or
0 commit comments