Hilbish/readline/instance.go

264 lines
9.4 KiB
Go

package readline
import (
"bufio"
"os"
"regexp"
"sync"
)
// Instance is used to encapsulate the parameter group and run time of any given
// readline instance so that you can reuse the readline API for multiple entry
// captures without having to repeatedly unload configuration.
type Instance struct {
//
// Input Modes -------------------------------------------------------------------------------
// InputMode - The shell can be used in Vim editing mode, or Emacs (classic).
InputMode InputMode
// Vim parameters/functions
// ShowVimMode - If set to true, a string '[i]' or '[N]' indicating the
// current Vim mode will be appended to the prompt variable, therefore added to
// the user's custom prompt is set. Applies for both single and multiline prompts
ShowVimMode bool
VimModeColorize bool // If set to true, varies colors of the VimModePrompt
//
// Prompt -------------------------------------------------------------------------------------
Multiline bool // If set to true, the shell will have a two-line prompt.
MultilinePrompt string // If multiline is true, this is the content of the 2nd line.
mainPrompt string // If multiline true, the full prompt string / If false, the 1st line of the prompt
rightPrompt string
rightPromptLen int
realPrompt []rune // The prompt that is actually on the same line as the beginning of the input line.
defaultPrompt []rune
promptLen int
stillOnRefresh bool // True if some logs have printed asynchronously since last loop. Check refresh prompt funcs
//
// Input Line ---------------------------------------------------------------------------------
// PasswordMask is what character to hide password entry behind.
// Once enabled, set to 0 (zero) to disable the mask again.
PasswordMask rune
// readline operating parameters
line []rune // This is the input line, with entered text: full line = mlnPrompt + line
pos int
posX int // Cursor position X
fullX int // X coordinate of the full input line, including the prompt if needed.
posY int // Cursor position Y (if multiple lines span)
fullY int // Y offset to the end of input line.
// Buffer received from host programms
multiline []byte
multisplit []string
skipStdinRead bool
// SyntaxHighlight is a helper function to provide syntax highlighting.
// Once enabled, set to nil to disable again.
SyntaxHighlighter func([]rune) string
//
// Completion ---------------------------------------------------------------------------------
// TabCompleter is a simple function that offers completion suggestions.
// It takes the readline line ([]rune) and cursor pos.
// Returns a prefix string, and several completion groups with their items and description
// Asynchronously add/refresh completions
TabCompleter func([]rune, int, DelayedTabContext) (string, []*CompletionGroup)
delayedTabContext DelayedTabContext
// SyntaxCompletion is used to autocomplete code syntax (like braces and
// quotation marks). If you want to complete words or phrases then you might
// be better off using the TabCompletion function.
// SyntaxCompletion takes the line ([]rune) and cursor position, and returns
// the new line and cursor position.
SyntaxCompleter func([]rune, int) ([]rune, int)
// Asynchronously highlight/process the input line
DelayedSyntaxWorker func([]rune) []rune
delayedSyntaxCount int64
// MaxTabCompletionRows is the maximum number of rows to display in the tab
// completion grid.
MaxTabCompleterRows int // = 4
// tab completion operating parameters
tcGroups []*CompletionGroup // All of our suggestions tree is in here
tcPrefix string // The current tab completion prefix aggainst which to build candidates
modeTabCompletion bool
compConfirmWait bool // When too many completions, we ask the user to confirm with another Tab keypress.
tabCompletionSelect bool // We may have completions printed, but no selected candidate yet
tabCompletionReverse bool // Groups sometimes use this indicator to know how they should handle their index
tcUsedY int // Comprehensive offset of the currently built completions
completionOpen bool
// Candidate / virtual completion string / etc
currentComp []rune // The currently selected item, not yet a real part of the input line.
lineComp []rune // Same as rl.line, but with the currentComp inserted.
lineRemain []rune // When we complete in the middle of a line, we cut and keep the remain.
compAddSpace bool // If true, any candidate inserted into the real line is done with an added space.
//
// Completion Search (Normal & History) -----------------------------------------------------
modeTabFind bool // This does not change, because we will search in all options, no matter the group
tfLine []rune // The current search pattern entered
modeAutoFind bool // for when invoked via ^R or ^F outside of [tab]
searchMode FindMode // Used for varying hints, and underlying functions called
regexSearch *regexp.Regexp // Holds the current search regex match
search string
mainHist bool // Which history stdin do we want
histInfo []rune // We store a piece of hist info, for dual history sources
Searcher func(string, []string) []string
//
// History -----------------------------------------------------------------------------------
// mainHistory - current mapped to CtrlR by default, with rl.SetHistoryCtrlR()
mainHistory History
mainHistName string
// altHistory is an alternative history input, in case a console user would
// like to have two different history flows. Mapped to CtrlE by default, with rl.SetHistoryCtrlE()
altHistory History
altHistName string
// HistoryAutoWrite defines whether items automatically get written to
// history.
// Enabled by default. Set to false to disable.
HistoryAutoWrite bool // = true
// history operating params
lineBuf string
histPos int
histOffset int
histNavIdx int // Used for quick history navigation.
//
// Info -------------------------------------------------------------------------------------
// InfoText is a helper function which displays infio text below the prompt.
// InfoText takes the line input from the prompt and the cursor position.
// It returns the info text to display.
InfoText func([]rune, int) []rune
// InfoColor is any ANSI escape codes you wish to use for info formatting. By
// default this will just be blue.
InfoFormatting string
infoText []rune // The actual info text
infoY int // Offset to info, if it spans multiple lines
//
// Hints -----------------------------------------------------------------------------------
// HintText is a helper function which displays hint text right after the user's input.
// It takes the line input and cursor position.
// It returns the hint text to display.
HintText func([]rune, int) []rune
// HintFormatting is just a string to use as the formatting for the hint. By default
// this will be a grey color.
HintFormatting string
hintText []rune
//
// Vim Operatng Parameters -------------------------------------------------------------------
modeViMode ViMode //= vimInsert
viIteration string
viUndoHistory []undoItem
viUndoSkipAppend bool
viIsYanking bool
registers *registers // All memory text registers, can be consulted with Alt"
//
// Other -------------------------------------------------------------------------------------
// TempDirectory is the path to write temporary files when editing a line in
// $EDITOR. This will default to os.TempDir()
TempDirectory string
// GetMultiLine is a callback to your host program. Since multiline support
// is handled by the application rather than readline itself, this callback
// is required when calling $EDITOR. However if this function is not set
// then readline will just use the current line.
GetMultiLine func([]rune) []rune
EnableGetCursorPos bool
// event
evtKeyPress map[string]func(string, []rune, int) *EventReturn
// concurency
mutex sync.Mutex
ViModeCallback func(ViMode)
ViActionCallback func(ViAction, []string)
RawInputCallback func([]rune) // called on all input
bufferedOut *bufio.Writer
}
// NewInstance is used to create a readline instance and initialise it with sane defaults.
func NewInstance() *Instance {
rl := new(Instance)
// Prompt
rl.Multiline = false
rl.mainPrompt = ""
rl.defaultPrompt = []rune{}
rl.promptLen = len(rl.computePrompt())
// Input Editing
rl.InputMode = Emacs
rl.ShowVimMode = true // In case the user sets input mode to Vim, everything is ready.
// Completion
rl.MaxTabCompleterRows = 50
// History
rl.mainHistory = new(ExampleHistory) // In-memory history by default.
rl.HistoryAutoWrite = true
// Others
rl.InfoFormatting = seqFgBlue
rl.HintFormatting = "\x1b[2m"
rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn)
rl.TempDirectory = os.TempDir()
rl.Searcher = func(needle string, haystack []string) []string {
suggs := make([]string, 0)
var err error
rl.regexSearch, err = regexp.Compile("(?i)" + string(rl.tfLine))
if err != nil {
rl.RefreshPromptLog(err.Error())
rl.infoText = []rune(Red("Failed to match search regexp"))
}
for _, hay := range haystack {
if rl.regexSearch == nil { continue }
if rl.regexSearch.MatchString(hay) {
suggs = append(suggs, hay)
}
}
return suggs
}
rl.bufferedOut = bufio.NewWriter(os.Stdout)
// Registers
rl.initRegisters()
return rl
}