Skip to content

Commit

Permalink
Refactor of CEs internal
Browse files Browse the repository at this point in the history
  • Loading branch information
galassie committed Feb 6, 2023
1 parent 1d3eef7 commit 72c04a6
Show file tree
Hide file tree
Showing 25 changed files with 923 additions and 356 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ With F# + FsSpectre:
```fsharp
table {
column_text ""
column (tableColumn { header "Feature"; centerd })
column (tableColumn { header_text "Feature"; centerd })
row_text [| "Baz"; "[green]Qux[/]" |]
row [| markup { text "[blue]Corgi[/]" }; panel { content "Waldo" } |]
} |> AnsiConsole.Write
Expand All @@ -68,9 +68,9 @@ barChart {
width 60
label "[green bold underline]Number of fruits[/]"
center_label
item "Apple" 12 Color.Yellow
item "Oranges" 54 Color.Green
item "Bananas" 33 Color.Red
item ("Apple", 12, Color.Yellow)
item ("Oranges", 54, Color.Green)
item ("Bananas", 33, Color.Red)
} |> AnsiConsole.Write
```

Expand Down
25 changes: 12 additions & 13 deletions Showcase.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ table {

column (
tableColumn {
header "Feature"
header_text "Feature"
no_wrap
right_aligned
width 10
Expand All @@ -22,7 +22,7 @@ table {

column (
tableColumn {
header "Demonstration"
header_text "Demonstration"
pad_right 0
}
)
Expand All @@ -35,8 +35,8 @@ table {
no_headers
no_border

column (tableColumn { header "Desc"; pad_right 3 })
column (tableColumn { header "Colors"; pad_right 0 })
column (tableColumn { header_text "Desc"; pad_right 3 })
column (tableColumn { header_text "Colors"; pad_right 0 })
row [|
markup {
text ("✓ [bold grey]NO_COLOR support[/]\n"+
Expand Down Expand Up @@ -111,8 +111,8 @@ table {
simple_border
border_color Color.Grey
columns [|
tableColumn { header "Overview" }
tableColumn { footer "[grey]3 Files, 225 KiB[/]" }
tableColumn { header_text "Overview" }
tableColumn { footer_text "[grey]3 Files, 225 KiB[/]" }
|]

row [|
Expand Down Expand Up @@ -149,25 +149,24 @@ table {
row [|
markup { text "[red]Tables and Trees[/]" }
grid {
collapse
number_of_columns 2
row [|
panel {
border_color Color.Grey
content_renderable (breakdownChart {
show_percentage
full_size
item "F#" 82 Color.Violet
item "PowerShell" 13 Color.Red
item "Bash" 5 Color.Blue
item ("F#", 82, Color.Violet)
item ("PowerShell", 13, Color.Red)
item ("Bash", 5, Color.Blue)
})
}
panel {
border_color Color.Grey
content_renderable (barChart {
item "Apple" 32 Color.Green
item "Oranges" 13 Color.Orange1
item "Bananas" 22 Color.Yellow
item ("Apple", 32, Color.Green)
item ("Oranges", 13, Color.Orange1)
item ("Bananas", 22, Color.Yellow)
})
}
|]
Expand Down
85 changes: 64 additions & 21 deletions src/FsSpectre/Prompts/MultiSelectionPromptBuilder.fs
Original file line number Diff line number Diff line change
@@ -1,47 +1,90 @@
namespace FsSpectre

open System
open System.Collections.Generic
open Spectre.Console

[<AutoOpen>]
module MultiSelectionPromptBuilder =

type MulitSelectionPromptConfig<'T> =
{ Title: string
Comparer: IEqualityComparer<'T>
Choices: ('T option * 'T array) array
PageSize: int
Required: bool
MoreChoicesText: string
InstructionsText: string
Converter: Option<'T -> string> }

static member Default =
{ Title = String.Empty
Comparer = EqualityComparer<'T>.Default
Choices = Array.empty<('T option * 'T array)>
PageSize = 10
Required = true
MoreChoicesText = "[grey](Move up and down to reveal more choices)[/]"
InstructionsText = "[grey](Press <space> to select, <enter> to accept)[/]"
Converter = None }


type MultiSelectionPromptBuilder<'T>() =
member __.Yield _ = MultiSelectionPrompt<'T>()
member __.Yield _ = MulitSelectionPromptConfig<'T>.Default

member __.Run(config: MulitSelectionPromptConfig<'T>) =
let result = MultiSelectionPrompt<'T>(config.Comparer)
result.Title <- config.Title

config.Choices
|> Array.iter (fun (grpOpt, choices) ->
match grpOpt with
| Some grp -> result.AddChoiceGroup(grp, choices) |> ignore
| None -> result.AddChoices(choices) |> ignore)

result.PageSize <- config.PageSize
result.Required <- config.Required
result.MoreChoicesText <- config.MoreChoicesText
result.InstructionsText <- config.InstructionsText
config.Converter |> Option.iter (fun c -> result.Converter <- c)
result

[<CustomOperation "title">]
member __.Title(multiSelectionPrompt: MultiSelectionPrompt<'T>, title: string) =
multiSelectionPrompt.Title <- title
multiSelectionPrompt
member __.Title(config: MulitSelectionPromptConfig<'T>, title: string) = { config with Title = title }

[<CustomOperation "comparer">]
member __.Title(config: MulitSelectionPromptConfig<'T>, comparer: IEqualityComparer<'T>) =
{ config with Comparer = comparer }

[<CustomOperation "choices">]
member __.Choices(multiSelectionPrompt: MultiSelectionPrompt<'T>, choices: 'T array) =
multiSelectionPrompt.AddChoices(choices)
member __.Choices(config: MulitSelectionPromptConfig<'T>, choices: 'T array) =
{ config with
Choices = Array.append config.Choices [| (None, choices) |] }

[<CustomOperation "choice_group">]
member __.ChoiceGroup(multiSelectionPrompt: MultiSelectionPrompt<'T>, choiceGroup: 'T, choices: 'T array) =
multiSelectionPrompt.AddChoiceGroup(choiceGroup, choices)
member __.ChoiceGroup(config: MulitSelectionPromptConfig<'T>, choiceGroup: 'T, choices: 'T array) =
{ config with
Choices = Array.append config.Choices [| (Some choiceGroup, choices) |] }

[<CustomOperation "not_required">]
member __.PageSize(multiSelectionPrompt: MultiSelectionPrompt<'T>) = multiSelectionPrompt.NotRequired()
member __.PageSize(config: MulitSelectionPromptConfig<'T>) = { config with Required = false }

[<CustomOperation "page_size">]
member __.PageSize(multiSelectionPrompt: MultiSelectionPrompt<'T>, pageSize: int) =
multiSelectionPrompt.PageSize <- pageSize
multiSelectionPrompt
member __.PageSize(config: MulitSelectionPromptConfig<'T>, pageSize: int) = { config with PageSize = pageSize }

[<CustomOperation "more_choices_text">]
member __.MoreChoicesText(multiSelectionPrompt: MultiSelectionPrompt<'T>, moreChoicesText: string) =
multiSelectionPrompt.MoreChoicesText <- moreChoicesText
multiSelectionPrompt
member __.MoreChoicesText(config: MulitSelectionPromptConfig<'T>, moreChoicesText: string) =
{ config with
MoreChoicesText = moreChoicesText }

[<CustomOperation "instructions_text">]
member __.InstructionsText(multiSelectionPrompt: MultiSelectionPrompt<'T>, instructionsText: string) =
multiSelectionPrompt.InstructionsText <- instructionsText
multiSelectionPrompt
member __.InstructionsText(config: MulitSelectionPromptConfig<'T>, instructionsText: string) =
{ config with
InstructionsText = instructionsText }

[<CustomOperation "converter">]
member __.Converter(multiSelectionPrompt: MultiSelectionPrompt<'T>, converter: 'T -> string) =
multiSelectionPrompt.Converter <- converter
multiSelectionPrompt
member __.Converter(config: MulitSelectionPromptConfig<'T>, converter: 'T -> string) =
{ config with
Converter = Some converter }


let multiSelectionPrompt<'T> = MultiSelectionPromptBuilder<'T>()
50 changes: 36 additions & 14 deletions src/FsSpectre/Prompts/SelectionPromptBuilder.fs
Original file line number Diff line number Diff line change
@@ -1,34 +1,56 @@
namespace FsSpectre

open System
open Spectre.Console

[<AutoOpen>]
module SelectionPromptBuilder =

type SelectionPromptConfig<'T> =
{ Title: string
Choices: 'T array
PageSize: int
MoreChoicesText: string
Converter: Option<'T -> string> }

static member Default =
{ Title = String.Empty
Choices = Array.empty<'T>
PageSize = 10
MoreChoicesText = "[grey](Move up and down to reveal more choices)[/]"
Converter = None }

type SelectionPromptBuilder<'T>() =
member __.Yield _ = SelectionPrompt<'T>()
member __.Yield _ = SelectionPromptConfig<'T>.Default

member __.Run(config: SelectionPromptConfig<'T>) =
let result = SelectionPrompt<'T>()
result.Title <- config.Title
result.AddChoices(config.Choices) |> ignore
result.PageSize <- config.PageSize
result.MoreChoicesText <- config.MoreChoicesText
config.Converter |> Option.iter (fun c -> result.Converter <- c)
result

[<CustomOperation "title">]
member __.Title(selectionPrompt: SelectionPrompt<'T>, title: string) =
selectionPrompt.Title <- title
selectionPrompt
member __.Title(config: SelectionPromptConfig<'T>, title: string) = { config with Title = title }

[<CustomOperation "choices">]
member __.Choices(selectionPrompt: SelectionPrompt<'T>, choices: 'T array) = selectionPrompt.AddChoices(choices)
member __.Choices(config: SelectionPromptConfig<'T>, choices: 'T array) =
{ config with
Choices = Array.append config.Choices choices }

[<CustomOperation "page_size">]
member __.PageSize(selectionPrompt: SelectionPrompt<'T>, pageSize: int) =
selectionPrompt.PageSize <- pageSize
selectionPrompt
member __.PageSize(config: SelectionPromptConfig<'T>, pageSize: int) = { config with PageSize = pageSize }

[<CustomOperation "more_choices_text">]
member __.MoreChoicesText(selectionPrompt: SelectionPrompt<'T>, moreChoicesText: string) =
selectionPrompt.MoreChoicesText <- moreChoicesText
selectionPrompt
member __.MoreChoicesText(config: SelectionPromptConfig<'T>, moreChoicesText: string) =
{ config with
MoreChoicesText = moreChoicesText }

[<CustomOperation "converter">]
member __.Converter(selectionPrompt: SelectionPrompt<'T>, converter: 'T -> string) =
selectionPrompt.Converter <- converter
selectionPrompt
member __.Converter(config: SelectionPromptConfig<'T>, converter: 'T -> string) =
{ config with
Converter = Some converter }

let selectionPrompt<'T> = SelectionPromptBuilder<'T>()
95 changes: 74 additions & 21 deletions src/FsSpectre/Prompts/TextPromptBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,92 @@ open Spectre.Console
[<AutoOpen>]
module TextPromptBuilder =

type TextPromptConfig<'T> =
{ Text: string
Comparer: StringComparer option
PromptStyle: Style
IsSecret: bool
Mask: char option
HasHiddenInput: bool
AllowEmpty: bool
ValidationErrorMessage: string
Validator: Option<'T -> ValidationResult>
Converter: Option<'T -> string> }

static member Default =
{ Text = String.Empty
Comparer = None
PromptStyle = Style.Plain
IsSecret = false
Mask = None
HasHiddenInput = false
AllowEmpty = false
ValidationErrorMessage = "[red]Invalid input[/]"
Validator = None
Converter = None }

type TextPromptBuilder<'T>() =
member __.Yield _ = TextPrompt<'T>(String.Empty)
member __.Yield _ = TextPromptConfig<'T>.Default

member __.Run(config: TextPromptConfig<'T>) =
let result = TextPrompt<'T>(config.Text, Option.toObj config.Comparer)
result.IsSecret <- config.IsSecret

if config.HasHiddenInput then
result.Mask <- Nullable()
else
config.Mask |> Option.iter (fun m -> result.Mask <- m)

result.AllowEmpty <- config.AllowEmpty
result.ValidationErrorMessage <- config.ValidationErrorMessage
config.Validator |> Option.iter (fun v -> result.Validator <- v)
config.Converter |> Option.iter (fun c -> result.Converter <- c)

result

[<CustomOperation "text">]
member __.Title(_, text: string) = TextPrompt<'T>(text)
member __.Text(config: TextPromptConfig<'T>, text: string) = { config with Text = text }

[<CustomOperation "comparer">]
member __.Comparer(config: TextPromptConfig<'T>, comparer: StringComparer) =
{ config with Comparer = Some comparer }

[<CustomOperation "prompt_style">]
member __.PromptStyle(textPrompt: TextPrompt<'T>, style: Style) =
textPrompt.PromptStyle <- style
textPrompt
member __.PromptStyle(config: TextPromptConfig<'T>, style: Style) = { config with PromptStyle = style }

[<CustomOperation "secret">]
member __.Secret(textPrompt: TextPrompt<'T>) = textPrompt.Secret()
member __.Secret(config: TextPromptConfig<'T>) = { config with IsSecret = true }

[<CustomOperation "mask">]
member __.Mask(textPrompt: TextPrompt<'T>, mask: char) = textPrompt.Secret(mask)
[<CustomOperation "secret_with_mask">]
member __.SecretWithMask(config: TextPromptConfig<'T>, mask: char) =
{ config with
IsSecret = true
Mask = Some mask
HasHiddenInput = false }

[<CustomOperation "hide_input">]
member __.HideInput(textPrompt: TextPrompt<'T>) = textPrompt.Secret(Nullable())
[<CustomOperation "secret_with_hidden_input">]
member __.SecretWithHiddenInput(config: TextPromptConfig<'T>) =
{ config with
IsSecret = true
Mask = None
HasHiddenInput = true }

[<CustomOperation "allow_empty">]
member __.AllowEmpty(textPrompt: TextPrompt<'T>) =
textPrompt.AllowEmpty <- true
textPrompt
member __.AllowEmpty(config: TextPromptConfig<'T>) = { config with AllowEmpty = true }

[<CustomOperation "validation_error_message">]
member __.ValidationErrorMessage(textPrompt: TextPrompt<'T>, validationErrorMessage: string) =
textPrompt.ValidationErrorMessage <- validationErrorMessage
textPrompt

[<CustomOperation "validate">]
member __.Validate(textPrompt: TextPrompt<'T>, validator: 'T -> ValidationResult) =
textPrompt.Validator <- validator
textPrompt
member __.ValidationErrorMessage(config: TextPromptConfig<'T>, validationErrorMessage: string) =
{ config with
ValidationErrorMessage = validationErrorMessage }

[<CustomOperation "validator">]
member __.Validator(config: TextPromptConfig<'T>, validator: 'T -> ValidationResult) =
{ config with
Validator = Some validator }

[<CustomOperation "converter">]
member __.Converter(config: TextPromptConfig<'T>, converter: 'T -> string) =
{ config with
Converter = Some converter }

let textPrompt<'T> = TextPromptBuilder<'T>()
Loading

0 comments on commit 72c04a6

Please sign in to comment.