Skip to content

Commit ca9203d

Browse files
committed
Improve help text + arg parsing
1 parent 68de57c commit ca9203d

File tree

1 file changed

+115
-119
lines changed

1 file changed

+115
-119
lines changed

src/Fantomas/Program.fs

Lines changed: 115 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ open Fantomas.Daemon
66
open Fantomas.Logging
77
open System.Text
88
open Spectre.Console
9-
open Fargo
10-
open Fargo.Operators
119

1210
let extensions = set [ ".fs"; ".fsx"; ".fsi"; ".ml"; ".mli" ]
1311

@@ -38,118 +36,118 @@ type Args =
3836
Verbosity: string option
3937
Input: InputPath }
4038

41-
let parseVerbosity =
42-
function
43-
| "normal"
44-
| "n" -> Ok "normal"
45-
| "detailed"
46-
| "d" -> Ok "detailed"
47-
| v -> Error $"Unknown verbosity {v}"
48-
49-
let verbosityCompleter = Completer.choices [ "normal"; "detailed"; "n"; "d" ]
50-
51-
let many (arg: Arg<'t>) =
52-
let rec findAll tokens result usages =
53-
match tokens with
54-
| [] -> Ok(List.rev result), [], usages
55-
| _ ->
56-
match arg.Parse(tokens) with
57-
| Ok v, tokens, us -> findAll tokens (v :: result) (Usages.merge usages us)
58-
| Error e, tokens, us -> Error e, tokens, us
59-
60-
{ Parse = fun tokens -> findAll tokens [] Usages.empty
61-
Complete =
62-
fun i tokens ->
63-
match arg.Complete i tokens with
64-
| [ x ], b -> [ $"{x}..." ], b
65-
| r -> r }
66-
67-
let pOut =
68-
opt "out" "o" "<path>" "Output path for files/folders"
69-
|> map (function
70-
| None -> OutputPath.NotKnown
71-
| Some path -> OutputPath.IO path)
72-
73-
let pVerbosity =
74-
optc "verbosity" "v" "normal|detailed" "Set the verbosity level" verbosityCompleter
75-
|> optParse parseVerbosity
76-
77-
let pInput =
78-
arg "input" "Input files or folders"
79-
|> many
80-
|> map (List.choose id)
81-
|> map (function
82-
| [] -> InputPath.Unspecified
83-
| [ input ] ->
84-
if Directory.Exists(input) then
85-
InputPath.Folder input
86-
elif File.Exists input && isFSharpFile input then
87-
InputPath.File input
88-
elif File.Exists input then
89-
InputPath.NoFSharpFile input
39+
module Args =
40+
41+
open Fargo
42+
43+
let parseVerbosity =
44+
function
45+
| "normal"
46+
| "n" -> Ok "normal"
47+
| "detailed"
48+
| "d" -> Ok "detailed"
49+
| v -> Error $"Unknown verbosity {v}"
50+
51+
let verbosityCompleter = Completer.choices [ "normal"; "detailed"; "n"; "d" ]
52+
53+
// Taken from https://github.com/thinkbeforecoding/Fargo/issues/6
54+
let many (arg: Arg<'t>) =
55+
let rec findAll tokens result usages =
56+
match tokens with
57+
| [] -> Ok(List.rev result), [], usages
58+
| _ ->
59+
match arg.Parse(tokens) with
60+
| Ok v, tokens, us -> findAll tokens (v :: result) (Usages.merge usages us)
61+
| Error e, tokens, us -> Error e, tokens, us
62+
63+
{ Parse = fun tokens -> findAll tokens [] Usages.empty
64+
Complete =
65+
fun i tokens ->
66+
match arg.Complete i tokens with
67+
| [ x ], b -> [ $"%s{x}..." ], b
68+
| r -> r }
69+
70+
let pOut =
71+
opt "out" "o" "path" "Output path for files/folders"
72+
|> map (function
73+
| None -> OutputPath.NotKnown
74+
| Some path -> OutputPath.IO path)
75+
76+
let pVerbosity =
77+
optc "verbosity" "v" "normal|detailed" "Set the verbosity level" verbosityCompleter
78+
|> optParse parseVerbosity
79+
80+
let pInput =
81+
arg "input" "Input files or folders"
82+
|> reqArg
83+
|> parse (fun arg ->
84+
if arg.StartsWith "--" then
85+
Error "Not a positional input arg"
9086
else
91-
InputPath.NotFound input
92-
| inputs ->
93-
let missing =
94-
inputs |> List.tryFind (fun x -> not (Directory.Exists(x) || File.Exists(x)))
95-
96-
match missing with
97-
| Some x -> InputPath.NotFound x
98-
| None ->
99-
let isFolder (path: string) =
100-
String.IsNullOrWhiteSpace(Path.GetExtension(path))
101-
102-
let rec loop
103-
(files: string list)
104-
(finalContinuation: string list * string list -> string list * string list)
105-
=
106-
match files with
107-
| [] -> finalContinuation ([], [])
108-
| h :: rest ->
109-
loop rest (fun (files, folders) ->
110-
if isFolder h then
111-
files, (h :: folders)
112-
else
113-
(h :: files), folders
114-
|> finalContinuation)
115-
116-
let filesAndFolders = loop inputs id
117-
InputPath.Multiple filesAndFolders)
118-
119-
let args =
120-
fargo {
121-
let! force = flag "force" "f" "Print the output even if it is not valid F# code. For debugging purposes only."
122-
and! profile = flag "profile" "p" "Print performance profiling information."
123-
and! out = pOut
124-
125-
and! check =
126-
flag
127-
"check"
128-
"c"
129-
"Don't format files, just check if they have changed. Exits with 0 if it's formatted correctly, with 1 if some files need formatting and 99 if there was an internal error"
130-
131-
// Applicative builders with more than 5 bindings break under AOT: https://github.com/dotnet/fsharp/issues/15488
132-
let! (daemon, version, verbosity, input) =
133-
fargo {
134-
let! daemon =
135-
flag "daemon" "d" "Daemon mode, launches an LSP-like server that can be used by editor tooling."
136-
137-
and! version = flag "version" "v" "Displays the version of Fantomas"
138-
and! verbosity = pVerbosity
139-
and! input = pInput
140-
return (daemon, version, verbosity, input)
141-
}
142-
143-
return
87+
Ok arg)
88+
|> many
89+
|> map (function
90+
| [] -> InputPath.Unspecified
91+
| [ input ] ->
92+
if Directory.Exists(input) then
93+
InputPath.Folder input
94+
elif File.Exists input && isFSharpFile input then
95+
InputPath.File input
96+
elif File.Exists input then
97+
InputPath.NoFSharpFile input
98+
else
99+
InputPath.NotFound input
100+
| inputs ->
101+
let missing =
102+
inputs |> List.tryFind (fun x -> not (Directory.Exists(x) || File.Exists(x)))
103+
104+
match missing with
105+
| Some x -> InputPath.NotFound x
106+
| None ->
107+
let isFolder (path: string) =
108+
String.IsNullOrWhiteSpace(Path.GetExtension(path))
109+
110+
let rec loop
111+
(files: string list)
112+
(finalContinuation: string list * string list -> string list * string list)
113+
=
114+
match files with
115+
| [] -> finalContinuation ([], [])
116+
| h :: rest ->
117+
loop rest (fun (files, folders) ->
118+
if isFolder h then
119+
files, (h :: folders)
120+
else
121+
(h :: files), folders
122+
|> finalContinuation)
123+
124+
let filesAndFolders = loop inputs id
125+
InputPath.Multiple filesAndFolders)
126+
127+
// Applicative builders with more than 5 bindings break under AOT: https://github.com/dotnet/fsharp/issues/15488
128+
let (<~|) a b = Fargo.map2 (<|) a b
129+
130+
let args =
131+
ret (fun force profile out check daemon version verbosity input ->
144132
{ Force = force
145133
Profile = profile
146134
Out = out
147135
Check = check
148136
Daemon = daemon
149137
Version = version
150138
Verbosity = verbosity
151-
Input = input }
152-
}
139+
Input = input })
140+
<~| flag "force" "f" "Print the output even if it is not valid F# code. For debugging purposes only."
141+
<~| flag "profile" "p" "Print performance profiling information."
142+
<~| pOut
143+
<~| flag
144+
"check"
145+
"c"
146+
"Don't format files, just check if they have changed. Exits with 0 if it's formatted correctly, with 1 if some files need formatting and 99 if there was an internal error"
147+
<~| flag "daemon" "d" "Daemon mode, launches an LSP-like server that can be used by editor tooling."
148+
<~| flag "version" "v" "Displays the version of Fantomas"
149+
<~| pVerbosity
150+
<~| pInput
153151

154152
type Table with
155153

@@ -299,7 +297,17 @@ let runCheckCommand (inputPath: InputPath) : int =
299297

300298
[<EntryPoint>]
301299
let main argv =
302-
run "fantomas" args argv (fun _ctok args ->
300+
if Array.contains "--help" argv then
301+
// As of 2024-07-08, Fargo does not support any application-level help text or docs for positional arguments. Hardcode it here.
302+
printfn
303+
"""Learn more about Fantomas: https://fsprojects.github.io/fantomas/docs
304+
Join our Discord community: https://discord.gg/Cpq9vf8BJH
305+
306+
INPUT:
307+
<string>... Input paths: can be multiple folders or files with *.fs,*.fsi,*.fsx,*.ml,*.mli extension.
308+
"""
309+
310+
Fargo.Run.run "fantomas" Args.args argv (fun _ctok args ->
303311

304312
// let errorHandler =
305313
// ProcessExiter(
@@ -309,18 +317,6 @@ let main argv =
309317
// | _ -> Some ConsoleColor.Red
310318
// )
311319

312-
// let helpTextMessage =
313-
// """Learn more about Fantomas: https://fsprojects.github.io/fantomas/docs
314-
// Join our Discord community: https://discord.gg/Cpq9vf8BJH
315-
// """
316-
317-
// let parser =
318-
// ArgumentParser.Create<Arguments>(
319-
// programName = "dotnet fantomas",
320-
// errorHandler = errorHandler,
321-
// helpTextMessage = helpTextMessage
322-
// )
323-
324320
let outputPath = args.Out
325321

326322
let force = args.Force

0 commit comments

Comments
 (0)