Skip to main content

API

commandinput

import "github.com/aschey/bubbleprompt/input/commandinput"

Package commandinput provides an implementation of the input.Input interface. It should be used to build interactive CLI applications.

Example

package main

import (
"fmt"
"os"
"strconv"

prompt "github.com/aschey/bubbleprompt"
"github.com/aschey/bubbleprompt/completer"
"github.com/aschey/bubbleprompt/executor"
"github.com/aschey/bubbleprompt/input/commandinput"
"github.com/aschey/bubbleprompt/suggestion"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

type secretMsg string

type cmdMetadata = commandinput.CommandMetadata[any]

type model struct {
suggestions []suggestion.Suggestion[cmdMetadata]
textInput *commandinput.Model[any]
secret string
executorValueStyle lipgloss.Style
filterer completer.RecursiveFilterer[cmdMetadata]
}

func (m model) Complete(
promptModel prompt.Model[cmdMetadata],
) ([]suggestion.Suggestion[cmdMetadata], error) {
parsed := m.textInput.ParsedValue()
completed := m.textInput.CompletedArgsBeforeCursor()
if len(completed) == 1 && parsed.Command.Value == "get" && parsed.Args[0].Value == "weather" {
flags := []commandinput.FlagInput{
{
Short: "d",
Long: "days",
ArgPlaceholder: m.textInput.NewFlagPlaceholder("<int>"),
Description: "Forecast days",
},
}
return m.textInput.FlagSuggestions(
m.textInput.CurrentTokenBeforeCursor().Value,
flags,
nil,
), nil
}
return m.filterer.GetRecursiveSuggestions(
m.textInput.Tokens(),
m.textInput.CursorIndex(),
m.suggestions,
), nil
}

func (m model) Execute(input string, promptModel *prompt.Model[cmdMetadata]) (tea.Model, error) {
parsed := m.textInput.ParsedValue()
args := parsed.Args
flags := parsed.Flags
if len(args) == 0 {
return nil, fmt.Errorf("1 argument required")
}
arg := args[0]
switch parsed.Command.Value {
case "get":
switch arg.Value {
case "weather":
days := "1"
if len(flags) > 0 {
flag := flags[0]
if flag.Name.Value == "-d" || flag.Name.Value == "--days" {
if flag.Value == nil {
return nil, fmt.Errorf("flag value required")
}
_, err := strconv.ParseInt(flag.Value.Value, 10, 64)
if err != nil {
return nil, fmt.Errorf("flag value must be a valid int")
}
days = flag.Value.Value
}
}
days = m.executorValueStyle.Render(days)
value := m.executorValueStyle.Render("cloudy with a chance of meatballs")
return executor.NewStringModel(
fmt.Sprintf("weather for the next %s day(s) is: %s", days, value),
), nil
case "secret":
return executor.NewStringModel(
"the secret is: " + m.executorValueStyle.Render(m.secret),
), nil
}
case "set":
switch arg.Value {
case "secret":
if len(args) < 2 {
return nil, fmt.Errorf("secret value required")
}
secretVal := args[1]

return executor.NewCmdModel("Secret updated", func() tea.Msg {
return secretMsg(secretVal.Unquote())
}), nil
}
}
return nil, fmt.Errorf("Invalid input")
}

func (m model) Init() tea.Cmd {
return nil
}

func (m model) Update(msg tea.Msg) (prompt.InputHandler[cmdMetadata], tea.Cmd) {
if msg, ok := msg.(secretMsg); ok {
m.secret = string(msg)
}
return m, nil
}

func main() {
textInput := commandinput.New[any]()
secretArgs := textInput.NewPositionalArgs("<secret value>")
secretArgs[0].ArgStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("6"))

suggestions := []suggestion.Suggestion[cmdMetadata]{
{
Text: "get",
Description: "retrieve things",
Metadata: cmdMetadata{
PositionalArgs: textInput.NewPositionalArgs("<command"),
Children: []suggestion.Suggestion[cmdMetadata]{
{
Text: "secret",
Description: "get the secret",
},
{
Text: "weather",
Description: "get the weather",
Metadata: cmdMetadata{
ShowFlagPlaceholder: true,
},
},
},
},
},
{
Text: "set",
Description: "update things",
Metadata: cmdMetadata{
Children: []suggestion.Suggestion[cmdMetadata]{
{
Text: "secret",
Description: "update the secret",
Metadata: cmdMetadata{
PositionalArgs: secretArgs,
},
},
},
},
},
}
model := model{
suggestions: suggestions,
textInput: textInput,
secret: "hunter2",
executorValueStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("13")),
filterer: completer.NewRecursiveFilterer[cmdMetadata](),
}

promptModel := prompt.New[cmdMetadata](
model,
textInput,
)

if _, err := tea.NewProgram(promptModel, tea.WithFilter(prompt.MsgFilter)).Run(); err != nil {
fmt.Printf("Could not start program\n%v\n", err)
os.Exit(1)
}
}

Index

Variables

var (
DefaultPlaceholderForeground = "14"
DefaultCurrentPlaceholderSuggestion = "240"
DefaultSelectedTextColor = "10"
DefaultFlagForeground = "245"
DefaultFlagPlaceholderForeground = "14"
DefaultBoolFlagForeground = "13"
DefaultNumberFlagForeground = "5"
)

type Arg

type Arg struct {
Start int
Value string
}

type CommandMetadata

CommandMetadata defines the metadata that the Model uses to get information about the supplied suggestion.Suggestion. You can extend this struct to provide additional metadata.

type CommandMetadata[T any] struct {
// PositionalArgs is the list of positional args that this suggestion accepts.
PositionalArgs []PositionalArg
// ShowFlagPlaceholder is whether or not the input should display a placeholder
// indicating that this command has flags available.
ShowFlagPlaceholder bool
// FlagArgPlaceholder is the placeholder
FlagArgPlaceholder FlagArgPlaceholder
PreservePlaceholder bool
Variadic bool
Children []suggestion.Suggestion[CommandMetadata[T]]
Extra T
}

func MetadataFromPositionalArgs

func MetadataFromPositionalArgs[T any](positionalArgs ...PositionalArg) CommandMetadata[T]

MetadataFromPositionalArgs is a convenience function for creating a CommandMetadata from one or more PositionalArg.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
"github.com/aschey/bubbleprompt/suggestion"
)

func main() {
textInput := commandinput.New[commandinput.CommandMetadata[any]]()
commandMetadata := commandinput.MetadataFromPositionalArgs[any](textInput.NewPositionalArg("<arg1>"))

suggestions := []suggestion.Suggestion[commandinput.CommandMetadata[any]]{
{Text: "test", Metadata: commandMetadata},
}

fmt.Println(suggestions[0].Metadata.PositionalArgs[0].Placeholder())
}

Output

<arg1>

func (CommandMetadata[T]) GetChildren

func (c CommandMetadata[T]) GetChildren() []suggestion.Suggestion[CommandMetadata[T]]

type Flag

type Flag struct {
Name input.Token
Value *input.Token
}

type FlagArgPlaceholder

FlagArgPlaceholder is a flag placeholder for completions.

type FlagArgPlaceholder struct {
Style lipgloss.Style
// contains filtered or unexported fields
}

func (FlagArgPlaceholder) Text

func (p FlagArgPlaceholder) Text() string

Text returns the placeholder text.

type FlagFormatter

FlagFormatter handles styling for flags.

type FlagFormatter struct {
// Flag handles styling for the flag itself.
Flag lipgloss.Style
// Placeholder handles styling for the placeholder that appears before the flag's argument is supplied (if applicable).
Placeholder lipgloss.Style
}

type FlagInput

FlagInput is used to generate a list of flag suggestions.

type FlagInput struct {
// Short is a short (single letter) flag with a single dash.
// The leading dash can be optionally included.
Short string
// Long is a long (multi-letter) flag with multiple dashes.
// The leading dashes can optionally be included.
Long string
// ArgPlaceholder is the placeholder for the flag argument (if applicable).
ArgPlaceholder FlagArgPlaceholder
// Description is the flag description.
Description string
}

func (FlagInput) LongFlag

func (f FlagInput) LongFlag() string

ShortFlag returns the Long property formatted as a flag with the leading dashes.

func (FlagInput) RequiresArg

func (f FlagInput) RequiresArg() bool

RequiresArg returns whether or not the input has an argument placeholder. If no placeholder is supplied, then it is assumed that the FlagInput does not require an argument.

func (FlagInput) ShortFlag

func (f FlagInput) ShortFlag() string

ShortFlag returns the Short property formatted as a flag with a leading dash.

type FlagValueFormatter

FlagValueFormatter handles styling for different flag value data types.

type FlagValueFormatter struct {
// String handles styling for string values.
String lipgloss.Style
// Bool handles styling for boolean values.
Bool lipgloss.Style
// Number handles styling for numeric values.
Number lipgloss.Style
}

type Formatters

Formatters handles styling for the command input.

type Formatters struct {
// PositionalArg handles styling for positional arguments.
PositionalArg PositionalArgFormatter
// Flag handles styling for flags.
Flag FlagFormatter
// FlagValue handles styling for a flag's value (if applicable).
FlagValue FlagValueFormatter
// Placeholder handles styling for placeholder that's shown as the user types the current argument.
Placeholder lipgloss.Style
// Prompt handles styling for the prompt that's shown before the user input.
Prompt lipgloss.Style
// Command handles styling for the command.
Command lipgloss.Style
// SelectedText handles styling for the text that's selected by the suggestion manager.
SelectedText lipgloss.Style
// Cursor handles styling for the cursor.
Cursor lipgloss.Style
}

func DefaultFormatters

func DefaultFormatters() Formatters

DefaultFormatters initializes the Formatters with sensible defaults. You can modify any settings that you wish after calling this function.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
"github.com/charmbracelet/lipgloss"
)

func main() {
defaultFormatters := commandinput.DefaultFormatters()
defaultFormatters.Cursor = lipgloss.NewStyle().Foreground(lipgloss.Color("128"))
fmt.Println(defaultFormatters.Cursor.GetForeground())

}

Output

128

type Model

A Model is an input for handling CLI-style inputs. It provides advanced features such as placeholders and context-aware suggestions.

type Model[T any] struct {
// contains filtered or unexported fields
}

func New

func New[T any](opts ...Option[T]) *Model[T]

New creates a new model.

func (*Model[T]) ArgsBeforeCursor

func (m *Model[T]) ArgsBeforeCursor() []string

ArgsBeforeCursor returns the positional arguments before the cursor position.

func (*Model[T]) Blur

func (m *Model[T]) Blur()

Blur removes the focus from the input.

func (Model[T]) CommandBeforeCursor

func (m Model[T]) CommandBeforeCursor() string

CommandBeforeCursor returns the portion of the command (first input token) before the cursor position.

func (Model[T]) CommandCompleted

func (m Model[T]) CommandCompleted() bool

CommandCompleted returns whether the user finished typing the entire command (first token).

func (*Model[T]) CompletedArgsBeforeCursor

func (m *Model[T]) CompletedArgsBeforeCursor() []string

CompletedArgsBeforeCursor returns the positional arguments before the cursor that have already been completed. In other words, there needs to be a delimiter after the argument to indicate that the user has finished entering in that argument.

func (Model[T]) CurrentToken

func (m Model[T]) CurrentToken() input.Token

CurrentToken returns the token under the cursor. If the cursor is between two tokens, it will take the token after the cursor.

func (Model[T]) CurrentTokenBeforeCursor

func (m Model[T]) CurrentTokenBeforeCursor() input.Token

CurrentTokenBeforeCursor returns the portion of the current token before the cursor. If the cursor is between two tokens, it will take the token after the cursor.

func (Model[T]) CurrentTokenBeforeCursorRoundDown

func (m Model[T]) CurrentTokenBeforeCursorRoundDown() input.Token

CurrentTokenBeforeCursorRoundDown returns the portion of the current token before the cursor. If the cursor is between two tokens, it will take the token before the cursor.

func (Model[T]) CurrentTokenRoundDown

func (m Model[T]) CurrentTokenRoundDown() input.Token

CurrentTokenRoundDown returns the token under the cursor. If the cursor is between two tokens, it will take the token before the cursor.

func (Model[T]) CursorIndex

func (m Model[T]) CursorIndex() int

CursorIndex returns the cursor index in terms of number of unicode characters. Use this to calculate input lengths in terms of number of characters entered.

func (Model[T]) CursorOffset

func (m Model[T]) CursorOffset() int

CursorOffset returns the visual offset of the cursor in terms of number of terminal cells. Use this for calculating visual dimensions such as input width/height.

func (*Model[T]) FlagSuggestions

func (m *Model[T]) FlagSuggestions(inputStr string, flags []FlagInput, suggestionFunc func(FlagInput) CommandMetadata[T]) []suggestion.Suggestion[CommandMetadata[T]]

FlagSuggestions generates a list of suggestion.Suggestion based on the input string and the list of FlagInput supplied. The last parameter can be used to customize the metadata for the returned suggestions.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
)

func main() {
textInput := commandinput.New[any]()
flags := []commandinput.FlagInput{
{
Short: "i",
Long: "interval",
Description: "refresh interval",
ArgPlaceholder: textInput.NewFlagPlaceholder("<value>"),
},
}

suggestions := textInput.FlagSuggestions("", flags, nil)
fmt.Printf("Text: %s, Description: %s\n", suggestions[0].Text, suggestions[0].Description)

suggestions = textInput.FlagSuggestions("--", flags, nil)
fmt.Printf("Text: %s, Description: %s\n", suggestions[0].Text, suggestions[0].Description)

suggestions = textInput.FlagSuggestions("", flags,
func(flagInput commandinput.FlagInput) commandinput.CommandMetadata[any] {
return commandinput.CommandMetadata[any]{
FlagArgPlaceholder: flagInput.ArgPlaceholder,
PreservePlaceholder: true,
}
})
fmt.Printf(
"Text: %s, Description: %s, Preserve Placeholder: %t\n",
suggestions[0].Text,
suggestions[0].Description,
suggestions[0].Metadata.PreservePlaceholder,
)

}

Output

Text: -i, Description: refresh interval
Text: --interval, Description: refresh interval
Text: -i, Description: refresh interval, Preserve Placeholder: true

func (*Model[T]) Focus

func (m *Model[T]) Focus() tea.Cmd

Focus sets the keyboard focus on the editor so the user can enter text.

func (Model[T]) Focused

func (m Model[T]) Focused() bool

Focused returns whether the keyboard is focused on the input.

func (Model[T]) Formatters

func (m Model[T]) Formatters() Formatters

Formatters returns the formatters used by the input.

func (Model[T]) HasArgs

func (m Model[T]) HasArgs() bool

HasArgs returns whether the input has any positional arguments.

func (Model[T]) LastArg

func (m Model[T]) LastArg() *input.Token

LastArg returns the last positional argument in the input. If there are no arguments, it returns nil.

func (*Model[T]) NewFlagPlaceholder

func (m *Model[T]) NewFlagPlaceholder(placeholder string) FlagArgPlaceholder

NewFlagPlaceholder creates a flag placeholder for completions.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
)

func main() {
textInput := commandinput.New[commandinput.CommandMetadata[any]]()

flags := []commandinput.FlagInput{
{
Short: "d",
Long: "days",
ArgPlaceholder: textInput.NewFlagPlaceholder("<number of days>"),
Description: "Forecast days",
},
}

fmt.Println(flags[0].ArgPlaceholder.Text())
}

Output

<number of days>

func (*Model[T]) NewPositionalArg

func (m *Model[T]) NewPositionalArg(placeholder string) PositionalArg

NewPositionalArg creates a positional arg placeholder for completions.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
"github.com/aschey/bubbleprompt/suggestion"
)

func main() {
textInput := commandinput.New[commandinput.CommandMetadata[any]]()
commandMetadata := commandinput.CommandMetadata[any]{
PositionalArgs: []commandinput.PositionalArg{textInput.NewPositionalArg("<arg1>")},
}

suggestions := []suggestion.Suggestion[commandinput.CommandMetadata[any]]{
{Text: "test", Metadata: commandMetadata},
}

fmt.Println(suggestions[0].Metadata.PositionalArgs[0].Placeholder())
}

Output

<arg1>

func (*Model[T]) NewPositionalArgs

func (m *Model[T]) NewPositionalArgs(placeholders ...string) []PositionalArg

NewPositionalArgs creates multiple positional arg placeholders for completions.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
"github.com/aschey/bubbleprompt/suggestion"
)

func main() {
textInput := commandinput.New[commandinput.CommandMetadata[any]]()
commandMetadata := commandinput.CommandMetadata[any]{
PositionalArgs: textInput.NewPositionalArgs("<arg1>", "<arg2>"),
}

suggestions := []suggestion.Suggestion[commandinput.CommandMetadata[any]]{
{Text: "test", Metadata: commandMetadata},
}

fmt.Println(suggestions[0].Metadata.PositionalArgs[0].Placeholder())
fmt.Println(suggestions[0].Metadata.PositionalArgs[1].Placeholder())
}

Output

<arg1>
<arg2>

func (*Model[T]) OnExecutorFinished

func (m *Model[T]) OnExecutorFinished()

OnExecutorFinished is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) OnSuggestionChanged

func (m *Model[T]) OnSuggestionChanged(suggestion suggestion.Suggestion[CommandMetadata[T]])

OnSuggestionChanged is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) OnSuggestionUnselected

func (m *Model[T]) OnSuggestionUnselected()

OnSuggestionUnselected is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) OnUpdateFinish

func (m *Model[T]) OnUpdateFinish(msg tea.Msg, suggestion *suggestion.Suggestion[CommandMetadata[T]], isSelected bool) tea.Cmd

OnUpdateFinish is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) OnUpdateStart

func (m *Model[T]) OnUpdateStart(msg tea.Msg) tea.Cmd

OnUpdateStart is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) ParseUsage

func (m *Model[T]) ParseUsage(placeholders string) ([]PositionalArg, error)

ParseUsage generates a list of PositionalArg from a usage string.

Example

package main

import (
"fmt"

"github.com/aschey/bubbleprompt/input/commandinput"
)

func main() {
textInput := commandinput.New[commandinput.CommandMetadata[any]]()

usage := `<mandatory arg> [optional arg] 'quoted arg' "double quoted arg" normal-arg`
args, err := textInput.ParseUsage(usage)
if err != nil {
panic(err)
}
fmt.Printf("%s\n%s\n%s\n%s\n%s",
args[0].Placeholder(),
args[1].Placeholder(),
args[2].Placeholder(),
args[3].Placeholder(),
args[4].Placeholder())

}

Output

<mandatory arg>
[optional arg]
'quoted arg'
"double quoted arg"
normal-arg

func (Model[T]) ParsedValue

func (m Model[T]) ParsedValue() Statement

ParsedValue returns the input parsed into a Statement.

func (*Model[T]) Prompt

func (m *Model[T]) Prompt() string

Prompt returns the terminal prompt.

func (*Model[T]) ResetValue

func (m *Model[T]) ResetValue()

ResetValue clears the entire input.

func (Model[T]) Runes

func (m Model[T]) Runes() []rune

Runes returns the raw text entered by the user as a list of runes. This is useful for indexing and length checks because doing these operations on strings does not work well with some unicode characters.

func (*Model[T]) SetCursor

func (m *Model[T]) SetCursor(pos int)

SetCursor sets the cursor position.

func (*Model[T]) SetCursorMode

func (m *Model[T]) SetCursorMode(cursorMode cursor.Mode) tea.Cmd

SetCursorMode sets the mode of the cursor.

func (*Model[T]) SetFormatters

func (m *Model[T]) SetFormatters(formatters Formatters)

SetFormatters sets the formatters used by the input.

func (*Model[T]) SetPrompt

func (m *Model[T]) SetPrompt(prompt string)

SetPrompt sets the terminal prompt.

func (*Model[T]) SetValue

func (m *Model[T]) SetValue(s string)

SetValue overwrites the entire input with the given string.

func (*Model[T]) ShouldClearSuggestions

func (m *Model[T]) ShouldClearSuggestions(prevText []rune, msg tea.KeyMsg) bool

ShouldClearSuggestions is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) ShouldSelectSuggestion

func (m *Model[T]) ShouldSelectSuggestion(suggestion suggestion.Suggestion[CommandMetadata[T]]) bool

ShouldSelectSuggestion is part of the input.Input interface. It should not be invoked by users of this library.

func (*Model[T]) ShouldUnselectSuggestion

func (m *Model[T]) ShouldUnselectSuggestion(prevRunes []rune, msg tea.KeyMsg) bool

ShouldUnselectSuggestion is part of the input.Input interface. It should not be invoked by users of this library.

func (Model[T]) SuggestionRunes

func (m Model[T]) SuggestionRunes(runes []rune) []rune

SuggestionRunes is part of the input.Input interface. It should not be invoked by users of this library.

func (Model[T]) Tokens

func (m Model[T]) Tokens() []input.Token

Tokens returns the entire input as a list of input.Token.

func (Model[T]) TokensBeforeCursor

func (m Model[T]) TokensBeforeCursor() []input.Token

TokensBeforeCursor returns the tokenized input before the cursor position.

func (Model[T]) Value

func (m Model[T]) Value() string

Value returns the raw text entered by the user.

func (Model[T]) Values

func (m Model[T]) Values() []string

Values returns the tokenized input values.

func (Model[T]) ValuesBeforeCursor

func (m Model[T]) ValuesBeforeCursor() []string

ValuesBeforeCursor returns the token values of the entire input before the cursor position.

func (Model[T]) View

func (m Model[T]) View(viewMode input.ViewMode) string

View is part of the input.Input interface. It should not be invoked by users of this library.

type Option

type Option[T any] func(model *Model[T])

func WithCursorMode

func WithCursorMode[T any](cursorMode cursor.Mode) Option[T]

func WithDefaultDelimiter

func WithDefaultDelimiter[T any](defaultDelimiter string) Option[T]

func WithFormatters

func WithFormatters[T any](formatters Formatters) Option[T]

func WithPrompt

func WithPrompt[T any](prompt string) Option[T]

type PositionalArg

PositionalArg is a positional arg placeholder for completions.

type PositionalArg struct {
PlaceholderStyle lipgloss.Style
ArgStyle lipgloss.Style
// contains filtered or unexported fields
}

func (PositionalArg) Placeholder

func (p PositionalArg) Placeholder() string

Placeholder returns the text value of the placeholder text.

type PositionalArgFormatter

PositionalArgFormatter handles styling for positional arguments.

type PositionalArgFormatter struct {
// Placeholder handles styling for the placeholder that appears before the argument is supplied.
Placeholder lipgloss.Style
// Arg handles styling for the argument that is supplied.
Arg lipgloss.Style
}

type Statement

type Statement struct {
Command input.Token
Args []input.Token
Flags []Flag
}

Generated by gomarkdoc