One of the most boring tasks when creating console commands is to deal with the styling of the command's input and output. Displaying titles and tables or asking questions to the user involves a lot of repetitive code.
This library is inspired by SymfonyStyle.
dotnet add package ConsoleStyle
open MF.ConsoleStyle
let console = ConsoleStyle()
console.Title "Hello World!"
for output:
Hello world!
============
let name = console.Ask "What's your name?"
let name = console.Ask("What's your %s?", "name")
Example:
What's your name? {stdin}
Function | example | color | note |
---|---|---|---|
mainTitle |
console.MainTitle "ConsoleStyle" |
cyan | see output 👇 |
_____ __ ____ __ __
/ ___/ ___ ___ ___ ___ / / ___ / __/ / /_ __ __ / / ___
/ /__ / _ \ / _ \ (_-</ _ \ / / / -_) _\ \ / __/ / // / / / / -_)
\___/ \___//_//_//___/\___//_/ \__/ /___/ \__/ \_, / /_/ \__/
/___/
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Function | example | color | note |
---|---|---|---|
title |
console.Title "Title of foo" |
cyan | see output 👇 |
Title of foo
============
Function | example | color | note |
---|---|---|---|
section |
console.Secion "Section of foo" |
dark-yellow | see output 👇 |
Section of foo
--------------
Function | example | color | note |
---|---|---|---|
message |
console.Message "a simple message" |
default | |
newLine |
console.NewLine() |
default | |
subTitle |
console.SubTitle "Sub title" |
yellow | Text is natively underlined |
error |
console.Error "Something went wrong!" |
red | This output goes to the stderr |
success |
console.Success "Done" |
green | |
indent |
console.Indent "Something indented" |
default | adds spaces at the beginning |
NOTE: most of the functions allows formatted variant with up to 5 args - see Formatting
Function | example | color | note |
---|---|---|---|
messages |
console.Messages "-prefix-" ["line 1"; "line 2"] |
default | see output 👇 |
-prefix-line 1
-prefix-line 2
Function | example | color | note |
---|---|---|---|
options |
console.Options "Foo options" [ ["first"; "desc 1"]; ["second"; "desc 2"] ] |
default with yellow title | see output 👇 |
Foo options
- first desc 1
- second desc 2
Function | example | color | note |
---|---|---|---|
simpleOptions |
console.SimpleOptions "Foo options" [ ["first"; "desc 1"]; ["second", "desc 2"] ] |
Same as options , but without line prefix. default with yellow title |
see output 👇 |
Foo options
first desc 1
second desc 2
Function | example | color | note |
---|---|---|---|
groupedOptions |
console.GroupedOptions ":" "Grouped options" [ ["first"; "desc 1"]; ["group:first"; "desc"; "group 1"]; ["group:second"; "desc"; "group 2"]; ["second"; "desc 2"] ] |
Grouped options by their prefix, if there is any. default with yellow title | see output 👇 |
Grouped options
first desc 1
second desc 2
group
group:first desc group 1
group:second desc group 2
Function | example | color | note |
---|---|---|---|
list |
console.List ["line 1"; "line 2"] |
default | see output 👇 |
- line 1
- line 2
Since string formatting with sprintf
is handled by compiler and is not easy to reproduce, there are explicit functions for formatting up to 5 parameters.
It is still type safe and compiler friendly (only limitation is for number of parameters, but you can still simply use sprintf
directly).
console.Message("Format %s parameter", "one")
console.Message("Format %s, %s parameter", "one", "two")
console.Message("Format %s, %d and %d parameter", "one", 2, 3)
console.Message(sprintf "Format %s, %s, %s and %s parameter" "one" "two" "three" "more ...")
NOTE: Other functions allowing formatting works the same way.
There is a special tag for formatting a part of text (<c:COLOR>text</c>
).
Formatting -> Usage
- Bold
<c:|b>Bold</c>
- Dim
<c:|d>Dim</c>
- Italic
<c:|i>Italic</c>
- Underline
<c:|u>Underline</c>
- Reverse
<c:|r>Reverse</c>
- StrikeThrough
<c:|s>StrikeThrough</c>
- Foreground
<c:COLOR>Colored</c>
- Background
<c:|bg:COLOR>Colored</c>
Combining formatting:
- minimal markup is
<c:>text</c>
- colors
<c:black|bg:red>text with black foreground and red background</c>
- bold and underlined
<c:|bu>Bold and underlined</c>
- all formatting options
<c:#D20000|bg:blue|bdiurs>Over formatted</c>
Named colors
TIP: Given color is normalized, so you can use -
or _
as separator or even use different case for colors.
(Example: darkblue
is same as dark_blue
, DARK--Blue
, dark-blue
, ...)
You can also use RGB and RGBA color codes
NOTE: You can use colors as both foreground and background for a text.
console.Message "Hello <c:green>world</c>!" // `Hello` and `!` will be in default color, and `world` will be green.
console.Message "<c:red>Hello</c> <c:green>world</c>!" // Different color for every word.
console.SimpleOptions "Options:" [
[ "option1"; "This is the <c:magenta>first</c> option"; "<c:yellow>[default: \"foo\"]</c>" ]
[ "option2"; "This is the <c:magenta>second</c> option" ]
]
console.Table [ "FirstName"; "Surname" ] [
[ "Jon"; "Snow" ]
[ "Peter"; "Parker" ]
]
Output:
----------- ---------
FirstName Surname
----------- ---------
Jon Snow
Peter Parker
----------- ---------
See in the example dir
// First line
[ "red"; "green"; "yellow"; "blue"; "purple"; "orange"; "gray" ]
|> List.map (fun color -> { Tab.parseColor color "Sample" with Value = Some color })
|> console.Tabs
// Second line
[ "#ed1017"; "#67c355"; "#f3d22b"; "#1996f0"; "#9064cb"; "#ff9603"; "#babab8" ]
|> List.map (fun color -> { Tab.parseColor color "Sample" with Value = Some color })
|> fun tabs -> console.Tabs(tabs, 10)
// Third line
[
"#ed1017", "#9e2e22"
"#67c355", "#087a3f"
"#f3d22b", "#faa727"
"#1996f0", "#0278be"
"#9064cb", "#6a3390"
"#ff9603", "#faa727"
"#babab8", "#333333"
]
|> List.mapi (fun i (color, darker) -> {
Tab.parseColor color (sprintf "<c:dark-gray|bg:%s|ub>Metric</c>" color)
with Value = Some <| sprintf "<c:magenta|bg:%s> %02d </c><c:|bg:%s>%% </c>" darker (i * 10) darker
}
)
|> fun tabs -> console.Tabs(tabs, 10)
For more info see https://github.com/Mpdreamz/shellprogressbar
let total = 10
let progressBar = console.ProgressStart "Starting..." total
for _ in 1 .. total do
console.ProgressAdvance progressBar
console.ProgressFinish progressBar
TIP: For more examples (async, with children, etc) see the example/Program.fs
There is a Style
settings where you can set up some attributes
type Style = {
/// Whether and how to show a date time
ShowDateTime: ShowDateTime
/// Underline used for main title
MainTitleUnderline: Underline
/// Underline used for title
TitleUnderline: Underline
/// Underline used for section
SectionUnderline: Underline
/// Indentation used in many places (options, date time, ...)
Indentation: Indentation
/// Block length is shared for all blocks (success, warning, ...)
BlockLength: int
/// Custom tags, available in the markup
CustomTags: CustomTag list
}
let console = ConsoleStyle(style)
NOTE: There is also a default styles which you can just override
let enhancedStyle = { Style.defaults with Indentation = Indentation " " }
See more in the example dir
let style = {
Style.defaults with
CustomTags = [
CustomTag.createWithMarkup (TagName "customTag") {
Bold = true
Dim = true
Italic = true
Underline = true
Reverse = true
StrikeThrough = true
Foreground = Some "black"
Background = Some "red"
}
CustomTag.createAndParseMarkup (TagName "service") ":#0b6695|bg:#f79333|u" |> orFail
{
Tag = TagName "name"
Markup = MarkupString.create "#23ae91"
}
]
}
let console = ConsoleStyle(style)
console.Section "Custom tags"
console.Message ("Now the <customTag>custom tags</customTag> example")
console.Table [ "Tag"; "Value" ] [
[ "service"; "<service>domain-context</service>" ]
[ "name"; "<name>Jon Snow</name>" ]
]
NOTE: There are default custom tags for a simplification of some basic formatting
- number
to style numbers (<c:magenta>
)
- u
for underline (<c:|u>
)
- b
for bold (<c:|b>
)
- i
for italic (<c:|i>
)
There are multiple outputs available
- Console - prints output with
Console.Write
functions (Default) - Print - prints output with
printf
andeprintf
functions - Buffered - buffer every write into string and offers it on
Fetch
method - Stream - writes to the given
System.IO.Stream
- NoMarkup - writes to the given
IOutput
and remove markup by givenStyle
- Combined - combination of other outputs
use bufferedOutput = Output.BufferOutput(Verbosity.Normal)
let console = ConsoleStyle(bufferedOutput)
console.Message("Hello")
let content: string = bufferedOutput.Fetch()
There are 5 levels of verbosity and every higher (more verbose) level will just add more information to previous - meaning, that VeryVerbose
= Normal
+ Verbose
+ More and so on (except of Quiet
which is oposite)
Level =
| Quiet
| Normal
| Verbose
| VeryVerbose
| Debug
Default level is Normal
Some functions have a different output based on current verbosity level
On Quiet
only input functions will show an output, no other output is shown (some components are not even inicialized)
ask
function will show a question
From VeryVerbose
it shows time (and date on Debug
) on the start of each line (not for multiline outputs)
Note: not every level adds information to output
You can set a level of verbosity by
let console = ConsoleStyle(Verbosity.Verbose)
// or change it on the fly
console.Verbosity <- Verbosity.Verbose
You can use a verbosity level in your application directly by
console.IsQuiet()
console.IsNormal()
console.IsVerbose()
console.IsVeryVerbose()
console.IsDebug()
As it was mentioned before, each level is addition to previous, so we have (except of Quiet
)
⬇️ function AND level ➡️ | Quiet |
Normal |
Verbose |
VeryVerbose |
Debug |
---|---|---|---|---|---|
IsQuiet() |
true | false | false | false | false |
IsNormal() |
false | true | true | true | true |
IsVerbose() |
false | false | true | true | true |
IsVeryVerbose() |
false | false | false | true | true |
IsDebug() |
false | false | false | false | true |