0 | module Idris.CommandLine
4 | import Idris.Doc.Display
5 | import Idris.Doc.String
32 | Show PkgCommand where
33 | show Build = "--build"
34 | show Install = "--install"
35 | show InstallWithSrc = "--install-with-src"
36 | show MkDoc = "--mkdoc"
37 | show Typecheck = "--typecheck"
38 | show Clean = "--clean"
39 | show REPL = "--repl"
40 | show Init = "--init"
41 | show DumpJson = "--dump-ipkg-json"
42 | show DumpInstallDir = "--dump-installdir"
52 | Show DirCommand where
53 | show LibDir = "--libdir"
54 | show Prefix = "--prefix"
55 | show BlodwenPaths = "--paths"
65 | recogniseHelpTopic : String -> Maybe HelpTopic
66 | recogniseHelpTopic "logging" = pure HelpLogging
67 | recogniseHelpTopic "pragma" = pure HelpPragma
68 | recogniseHelpTopic _ = Nothing
99 | Help (Maybe HelpTopic) |
113 | ConsoleWidth (Maybe Nat) |
123 | Package PkgCommand (Maybe String) |
125 | Directory DirCommand |
131 | IdeModeSocket String |
139 | DumpLifted String |
143 | DumpVMCode String |
146 | IgnoreMissingIPKG |
148 | Timing (Maybe Nat) |
150 | AltErrorCount Nat |
154 | IgnoreShadowingWarnings |
157 | HashesInsteadOfModTime |
161 | CaseTreeHeuristics |
163 | IncrementalCG String |
167 | BashCompletion String String |
169 | BashCompletionScript String |
171 | ZshCompletionScript String |
179 | ideSocketModeAddress : List CLOpt -> (String, Int)
180 | ideSocketModeAddress [] = ("localhost", 0)
181 | ideSocketModeAddress (IdeModeSocket hp :: _) =
182 | let (h, p) = String.break (== ':') hp
183 | port = fromMaybe 0 (portPart p >>= parsePositive)
184 | host = if h == "" then "localhost" else h
187 | portPart : String -> Maybe String
188 | portPart p = if p == ""
190 | else Just $
assert_total $
prim__strTail p
191 | ideSocketModeAddress (_ :: rest) = ideSocketModeAddress rest
193 | formatSocketAddress : (String, Int) -> String
194 | formatSocketAddress (host, port) = host ++ ":" ++ show port
199 | | RequiredNat String
201 | | RequiredLogLevel String
204 | show (Required a) = "<" ++ a ++ ">"
205 | show (RequiredNat a) = "<" ++ a ++ ">"
206 | show (RequiredLogLevel a) = "<" ++ a ++ ">"
207 | show (Optional a) = "[" ++ a ++ "]"
208 | show (AutoNat a) = "<" ++ a ++ ">"
210 | ActType : List OptType -> Type
211 | ActType [] = List CLOpt
212 | ActType (Required a :: as) = String -> ActType as
213 | ActType (RequiredNat a :: as) = Nat -> ActType as
214 | ActType (RequiredLogLevel a :: as) = LogLevel -> ActType as
215 | ActType (Optional a :: as) = Maybe String -> ActType as
216 | ActType (AutoNat a :: as) = Maybe Nat -> ActType as
218 | record OptDesc where
220 | flags : List String
221 | argdescs : List OptType
222 | action : ActType argdescs
223 | help : Maybe String
225 | optSeparator : OptDesc
226 | optSeparator = MkOpt [] [] [] Nothing
228 | showDefault : Show a => a -> String
229 | showDefault x = "(default " ++ show x ++ ")"
231 | options : List OptDesc
232 | options = [MkOpt ["--check", "-c"] [] [CheckOnly]
233 | (Just "Exit after checking source file"),
234 | MkOpt ["--output", "-o"] [Required "file"] (\f => [OutputFile f, Quiet])
235 | (Just "Specify output file"),
236 | MkOpt ["--exec", "-x"] [Required "name"] (\f => [ExecFn f, Quiet])
237 | (Just "Execute expression"),
238 | MkOpt ["--no-prelude"] [] [NoPrelude]
239 | (Just "Don't implicitly import Prelude"),
240 | MkOpt ["--codegen", "--cg"] [Required "backend"] (\f => [SetCG f])
241 | (Just $
"Set code generator " ++ showDefault (codegen defaultSession)),
242 | MkOpt ["--incremental-cg", "--inc"] [Required "backend"] (\f => [IncrementalCG f])
243 | (Just "Incremental code generation on given backend"),
244 | MkOpt ["--whole-program", "--wp"] [] [WholeProgram]
245 | (Just "Use whole program compilation (overrides --inc)"),
246 | MkOpt ["--directive"] [Required "directive"] (\d => [Directive d])
247 | (Just $
"Pass a directive to the current code generator"),
248 | MkOpt ["--package", "-p"] [Required "package"] (\f => [PkgPath f])
249 | (Just "Add a package as a dependency"),
250 | MkOpt ["--source-dir"] [Required "dir"] (\d => [SourceDir d])
251 | (Just $
"Set source directory"),
252 | MkOpt ["--build-dir"] [Required "dir"] (\d => [BuildDir d])
253 | (Just $
"Set build directory"),
254 | MkOpt ["--output-dir"] [Required "dir"] (\d => [OutputDir d])
255 | (Just $
"Set output directory"),
256 | MkOpt ["--profile"] [] [Profile]
257 | (Just "Generate profile data when compiling, if supported"),
258 | MkOpt ["--no-cse"] [] [NoCSE]
259 | (Just "Disable common subexpression elimination"),
262 | MkOpt ["--total"] [] [Total]
263 | (Just "Require functions to be total by default"),
264 | MkOpt ["-Werror"] [] [WarningsAsErrors]
265 | (Just "Treat warnings as errors"),
266 | MkOpt ["-Wno-shadowing"] [] [IgnoreShadowingWarnings]
267 | (Just "Do not print shadowing warnings"),
270 | MkOpt ["-Xcheck-hashes"] [] [HashesInsteadOfModTime]
271 | (Just "Use SHA256 hashes instead of modification time to determine if a source file needs rebuilding"),
272 | MkOpt ["-Xcase-tree-opt"] [] [CaseTreeHeuristics]
273 | (Just "Apply experimental optimizations to case tree generation"),
276 | MkOpt ["--prefix"] [] [Directory Prefix]
277 | (Just "Show installation prefix"),
278 | MkOpt ["--paths"] [] [Directory BlodwenPaths]
279 | (Just "Show paths"),
280 | MkOpt ["--libdir"] [] [Directory LibDir]
281 | (Just "Show library directory"),
282 | MkOpt ["--list-packages"] [] [ListPackages]
283 | (Just "List installed packages"),
286 | MkOpt ["--init"] [Optional "package file"]
287 | (\ f => [Package Init f])
288 | (Just "Interactively initialise a new project"),
289 | MkOpt ["--dump-ipkg-json"] [Optional "package file"]
290 | (\ f => [Package DumpJson f])
291 | (Just "Dump an Idris2 package file in the JSON format"),
292 | MkOpt ["--dump-installdir"] [Optional "package file"]
293 | (\ f => [Package DumpInstallDir f])
294 | (Just "Dump the location where the given package will be installed"),
295 | MkOpt ["--build"] [Optional "package file"]
296 | (\f => [Package Build f])
297 | (Just "Build modules/executable for the given package"),
298 | MkOpt ["--install"] [Optional "package file"]
299 | (\f => [Package Install f])
300 | (Just "Install the given package"),
301 | MkOpt ["--install-with-src"] [Optional "package file"]
302 | (\f => [Package InstallWithSrc f])
303 | (Just "Install the given package"),
304 | MkOpt ["--mkdoc"] [Optional "package file"]
305 | (\f => [Package MkDoc f])
306 | (Just "Build documentation for the given package"),
307 | MkOpt ["--typecheck"] [Optional "package file"]
308 | (\f => [Package Typecheck f])
309 | (Just "Typechecks the given package without code generation"),
310 | MkOpt ["--clean"] [Optional "package file"] (\f => [Package Clean f])
311 | (Just "Clean intermediate files/executables for the given package"),
312 | MkOpt ["--repl"] [Optional "package file"] (\f => [Package REPL f])
313 | (Just "Build the given package and launch a REPL instance."),
314 | MkOpt ["--find-ipkg"] [] [FindIPKG]
315 | (Just "Find and use an .ipkg file in a parent directory."),
316 | MkOpt ["--ignore-missing-ipkg"] [] [IgnoreMissingIPKG]
317 | (Just "Fail silently if a dependency is missing."),
320 | MkOpt ["--ide-mode"] [] [IdeMode]
321 | (Just "Run the REPL with machine-readable syntax"),
322 | MkOpt ["--ide-mode-socket"] [Optional "host:port"]
323 | (\hp => [IdeModeSocket $
fromMaybe (formatSocketAddress (ideSocketModeAddress [])) hp])
324 | (Just $
"Run the ide socket mode on given host and port (random open socket by default)"),
327 | MkOpt ["--client"] [Required "REPL command"] (\f => [RunREPL f])
328 | (Just "Run a REPL command then quit immediately"),
329 | MkOpt ["--timing"] [AutoNat "level"] (\ n => [Timing n])
330 | (Just "Display timing logs"),
333 | MkOpt ["--no-banner"] [] [NoBanner]
334 | (Just "Suppress the banner"),
335 | MkOpt ["--quiet", "-q"] [] [Quiet]
336 | (Just "Quiet mode; display fewer messages"),
337 | MkOpt ["--console-width"] [AutoNat "console width"] (\l => [ConsoleWidth l])
338 | (Just "Width for console output (0 for unbounded) (auto by default)"),
339 | MkOpt ["--show-implicits"] [] [ShowImplicits]
340 | (Just "Show implicits when pretty printing"),
341 | MkOpt ["--show-machine-names"] [] [ShowMachineNames]
342 | (Just "Show machine names when pretty printing"),
343 | MkOpt ["--show-namespaces"] [] [ShowNamespaces]
344 | (Just "Show namespaces when pretty printing"),
345 | MkOpt ["--color", "--colour"] [] ([Color True])
346 | (Just "Forces colored console output (enabled by default)"),
347 | MkOpt ["--no-color", "--no-colour"] [] ([Color False])
348 | (Just "Disables colored console output"),
349 | MkOpt ["--verbose"] [] [Verbose]
350 | (Just "Verbose mode (default)"),
351 | MkOpt ["--log"] [RequiredLogLevel "log level"] (\l => [Logging l])
352 | (Just "Global log level (0 by default)"),
355 | MkOpt ["--version", "-v"] [] [Version]
356 | (Just "Display version string"),
357 | MkOpt ["--ttc-version"] [] [TTCVersion]
358 | (Just "Display TTC version string"),
359 | MkOpt ["--help", "-h", "-?"] [Optional "topic"] (\ tp => [Help (tp >>= recogniseHelpTopic)])
360 | (Just "Display help text"),
363 | MkOpt ["--yaffle", "--ttimp"] [Required "ttimp file"] (\f => [Yaffle f])
365 | MkOpt ["--ttm" ] [Required "ttimp file"] (\f => [Metadata f])
367 | MkOpt ["--dumpcases"] [Required "output file"] (\f => [DumpCases f])
369 | MkOpt ["--dumplifted"] [Required "output file"] (\f => [DumpLifted f])
371 | MkOpt ["--dumpanf"] [Required "output file"] (\f => [DumpANF f])
373 | MkOpt ["--dumpvmcode"] [Required "output file"] (\f => [DumpVMCode f])
375 | MkOpt ["--debug-elab-check"] [] [DebugElabCheck]
377 | MkOpt ["--alt-error-count"] [RequiredNat "alternative count"] (\c => [AltErrorCount c])
378 | (Just "Outputs errors for the given number of alternative parsing attempts."),
382 | MkOpt ["--bash-completion"]
384 | , Required "previous input"]
385 | (\w1,w2 => [BashCompletion w1 w2])
386 | (Just "Print bash autocompletion information"),
387 | MkOpt ["--bash-completion-script"]
388 | [ Required "function name" ]
389 | (\n => [BashCompletionScript n])
390 | (Just "Generate a bash script to activate autocompletion for Idris2"),
392 | MkOpt ["--zsh-completion-script"]
393 | [ Required "function name" ]
394 | (\n => [ZshCompletionScript n])
395 | (Just "Generate a zsh script (via bashcompinit) to activate autocompletion for Idris2")
398 | optShow : OptDesc -> (String, Maybe String)
399 | optShow (MkOpt [] _ _ _) = ("", Just "")
400 | optShow (MkOpt flags argdescs action help) = (showSep ", " flags ++ " " ++
401 | showSep " " (map show argdescs),
404 | showSep : String -> List String -> String
405 | showSep sep [] = ""
406 | showSep sep [x] = x
407 | showSep sep (x :: xs) = x ++ sep ++ showSep sep xs
409 | firstColumnWidth : Nat
410 | firstColumnWidth = let maxOpt = foldr max 0 $
map (length . fst . optShow) options
411 | maxEnv = foldr max 0 $
map (length . .name) envs in
414 | makeTextFromOptionsOrEnvs : List (String, Maybe String) -> String
415 | makeTextFromOptionsOrEnvs rows = concatMap (optUsage firstColumnWidth) rows
417 | optUsage : Nat -> (String, Maybe String) -> String
418 | optUsage maxOpt (optshow, help) = maybe ""
420 | (\h => " " ++ optshow ++
421 | pack (List.replicate (minus (maxOpt + 2) (length optshow)) ' ') ++
426 | optsUsage = makeTextFromOptionsOrEnvs $
map optShow options
429 | envsUsage = makeTextFromOptionsOrEnvs $
map (\e => (e.name, Just e.help)) envs
432 | versionMsg : String
433 | versionMsg = "Idris 2, version " ++ show version
439 | Usage: idris2 [options] [input file]
443 | Environment variables:
447 | checkNat : Integer -> Maybe Nat
448 | checkNat n = toMaybe (n >= 0) (integerToNat n)
450 | processArgs : String -> (args : List OptType) -> List String -> ActType args ->
451 | Either String (List CLOpt, List String)
452 | processArgs flag [] xs f = Right (f, xs)
454 | processArgs flag (opt@(Required _) :: as) [] f =
455 | Left $
"Missing required argument " ++ show opt ++ " for flag " ++ flag
456 | processArgs flag (opt@(RequiredNat _) :: as) [] f =
457 | Left $
"Missing required argument " ++ show opt ++ " for flag " ++ flag
458 | processArgs flag (opt@(RequiredLogLevel _) :: as) [] f =
459 | Left $
"Missing required argument " ++ show opt ++ " for flag " ++ flag
460 | processArgs flag (Optional a :: as) [] f =
461 | processArgs flag as [] (f Nothing)
462 | processArgs flag (opt@(AutoNat _) :: as) [] f =
463 | Left $
"Missing required argument " ++ show opt ++ " for flag " ++ flag
465 | processArgs flag (RequiredNat a :: as) (x :: xs) f =
466 | do arg <- maybeToEither ("Expected Nat argument " ++ show x ++ " for flag " ++ flag)
467 | (parseInteger x >>= checkNat)
468 | processArgs flag as xs (f arg)
469 | processArgs flag (RequiredLogLevel a :: as) (x :: xs) f =
470 | do arg <- maybeToEither ("Expected LogLevel argument " ++ show x ++ " for flag " ++ flag)
472 | processArgs flag as xs (f arg)
473 | processArgs flag (AutoNat a :: as) ("auto" :: xs) f =
474 | processArgs flag as xs (f Nothing)
475 | processArgs flag (AutoNat a :: as) (x :: xs) f =
476 | do arg <- maybeToEither ("Expected Nat or \"auto\" argument " ++ show x ++ " for flag " ++ flag)
477 | (parseInteger x >>= checkNat)
478 | processArgs flag as xs (f (Just arg))
479 | processArgs flag (Required a :: as) (x :: xs) f =
480 | processArgs flag as xs (f x)
481 | processArgs flag (Optional a :: as) (x :: xs) f =
482 | if isPrefixOf "-" x
483 | then processArgs flag as (x :: xs) (f Nothing)
484 | else processArgs flag as xs (f $
Just x)
486 | matchFlag : (d : OptDesc) -> List String ->
487 | Either String (Maybe (List CLOpt, List String))
488 | matchFlag d [] = Right Nothing
489 | matchFlag d (x :: xs)
490 | = if x `elem` flags d
491 | then do args <- processArgs x (argdescs d) xs (action d)
495 | findMatch : List OptDesc -> List String ->
496 | Either String (List CLOpt, List String)
497 | findMatch [] [] = Right ([], [])
498 | findMatch [] (f :: args) = case unpack f of
499 | '-' :: '-' :: _ => Left "Unknown flag \{f}"
500 | _ => Right ([InputFile f], args)
501 | findMatch (d :: ds) args
502 | = case !(matchFlag d args) of
503 | Nothing => findMatch ds args
504 | Just res => Right res
506 | parseOpts : List OptDesc -> List String -> Either String (List CLOpt)
507 | parseOpts opts [] = Right []
508 | parseOpts opts args
509 | = do (cl, rest) <- findMatch opts args
510 | cls <- assert_total (parseOpts opts rest)
514 | getOpts : List String -> Either String (List CLOpt)
515 | getOpts opts = parseOpts options opts
518 | getCmdOpts : IO (Either String (List CLOpt))
519 | getCmdOpts = do (_ :: opts) <- getArgs
520 | | _ => pure (Left "Invalid command line")
521 | pure $
getOpts opts
527 | findNearMatchOpt : String -> Maybe String
528 | findNearMatchOpt arg =
529 | let argWithDashes = "--\{arg}"
531 | case (getOpts [argWithDashes]) of
532 | Right [(InputFile _)] => Nothing
533 | Right [_] => Just argWithDashes
540 | nearMatchOptSuggestion : String -> Maybe (Doc IdrisAnn)
541 | nearMatchOptSuggestion arg =
542 | findNearMatchOpt arg <&>
544 | (reflow "Did you mean to type" <++>
545 | (dquotes . meta $
pretty0 opt) <+> "?")
549 | optionFlags : List String
550 | optionFlags = options >>= flags