Source File
engine.go
Belonging Package
github.com/reeflective/readline/internal/completion
package completionimport ()// Engine is responsible for all completion tasks: generating, computing,// displaying and updating completion values and inserted candidates.type Engine struct {config *inputrc.Config // The inputrc contains options relative to completion.cached Completer // A cached completer function to use when updating.autoCompleter Completer // Completer used by things like autocompletehint *ui.Hint // The completions can feed hint/usage messages// Line parameterskeys *core.Keys // The input keys readerline *core.Line // The real input line of the shell.cursor *core.Cursor // The cursor of the shell.selection *core.Selection // The line selectioncompLine *core.Line // A line that might include a virtually inserted candidate.compCursor *core.Cursor // The adjusted cursor.keymap *keymap.Engine // The main/local keymaps of the shell// Completion parametersgroups []*group // All of our suggestions tree is in heresm SuffixMatcher // The suffix matcher is kept for removal after actually inserting the candidate.selected Candidate // The currently selected item, not yet a real part of the input line.prefix string // The current tab completion prefix against which to build candidatessuffix string // The current word suffixinserted []rune // The selected candidate (inserted in line) without prefix or suffix.usedY int // Comprehensive size offset (terminal rows) of the currently built completions.auto bool // Is the engine autocompleting ?autoForce bool // Special autocompletion mode (isearch-style)skipDisplay bool // Don't display completions if there are some.// Incremental searchIsearchRegex *regexp.Regexp // Holds the current search regex matchisearchBuf *core.Line // The isearch minibufferisearchCur *core.Cursor // Cursor position in the minibuffer.isearchName string // What is being incrementally searched for.isearchInsert bool // Whether to insert the first match in the lineisearchForward bool // Match results in forward order, or backward.isearchSubstring bool // Match results as a substring (regex), or as a prefix.isearchReplaceLine bool // Replace the current line with the search resultisearchStartBuf string // The buffer before starting isearchisearchStartCursor int // The cursor position before starting isearchisearchLast string // The last non-incremental buffer.isearchModeExit keymap.Mode // The main keymap to restore after exiting isearch}// NewEngine initializes a new completion engine with the shell operating parameters.func ( *ui.Hint, *keymap.Engine, *inputrc.Config) *Engine {return &Engine{config: ,hint: ,keymap: ,}}// Init is used once at shell creation time to pass further parameters to the engine.func ( *Engine, *core.Keys, *core.Line, *core.Cursor, *core.Selection, Completer) {.keys =.line =.cursor =.selection =.compLine =.compCursor =.autoCompleter =}// Generate uses a list of completions to group/order and prepares completions before printing them.// If either no completions or only one is available after all constraints are applied, the engine// will automatically insert/accept and/or reset itself.func ( *Engine) ( Values) {.prepare()if .noCompletions() {.ClearMenu(true)}// Incremental search is a special case, because the user may// want to keep searching for another match, so we don't drop// the completion list and exit the incremental search mode.if .hasUniqueCandidate() && .keymap.Local() != keymap.Isearch {.acceptCandidate().ClearMenu(true)}}// GenerateWith generates completions with a completer function, itself cached// so that the next time it must update its results, it can reuse this completer.func ( *Engine) ( Completer) {.cached =if .cached == nil {return}// Call the provided/cached completer// and use the completions as normal.Generate(.cached())}// GenerateCached simply recomputes the grid of completions with the pool// of completions already in memory. This might produce a bigger/smaller/// different completion grid, for example if it's called on terminal resize.func ( *Engine) () {.GenerateWith(.cached)}// SkipDisplay avoids printing completions below the// input line, but still enables cycling through them.func ( *Engine) () {.skipDisplay = true}// Select moves the completion selector by some X or Y value,// and updates the inserted candidate in the input line.func ( *Engine) (, int) {:= .currentGroup()if == nil || len(.rows) == 0 {return}// Ensure the completion keymaps are set..adjustSelectKeymap()// Some keys used to move around completions// will influence the coordinates' offsets., = .adjustCycleKeys(, )// If we already have an inserted candidate// remove it before inserting the new one.if len(.selected.Value) > 0 {.cancelCompletedLine()}defer .refreshLine()// Move the selector, := .moveSelector(, )if ! {return}var *groupif {.cycleNextGroup()= .currentGroup().firstCell()} else {.cyclePreviousGroup()= .currentGroup().lastCell()}}// SelectTag allows to select the first value of the next tag (next=true),// or the last value of the previous tag (next=false).func ( *Engine) ( bool) {// Ensure the completion keymaps are set..adjustSelectKeymap()if len(.groups) <= 1 {return}// If the completion candidate is not empty,// it's also inserted in the line, so remove it.if len(.selected.Value) > 0 {.cancelCompletedLine()}// In the end we will update the line with the// newly/currently selected completion candidate.defer .refreshLine()if {.cycleNextGroup():= .currentGroup().firstCell()} else {.cyclePreviousGroup():= .currentGroup().firstCell()}}// Cancel exits the current completions with the following behavior:// - If inserted is true, any inserted candidate is removed.// - If cached is true, any cached completer function is dropped.//// This function does not exit the completion keymap, so// any active completion menu will still be kept/displayed.func ( *Engine) (, bool) {if {.cached = nil.hint.Reset()}if len(.selected.Value) == 0 && ! {return}defer .cancelCompletedLine()// Either drop the inserted candidate,// or make it part of the real input line.if {.compLine.Set(*.line...).compCursor.Set(.cursor.Pos())} else {.line.Set(*.compLine...).cursor.Set(.compCursor.Pos())}}// ResetForce drops any currently inserted candidate from the line,// drops any cached completer function and generated list, and exits// the incremental-search mode.// All those steps are performed whether or not the engine is active.// If revertLine is true, the line will be reverted to its original state.func ( *Engine) () {.Cancel(!.autoForce, true).ClearMenu(true):= .keymap.Local() == keymap.Isearch ||.keymap.Local() == keymap.MenuSelect.IsearchStop()}// Reset accepts the currently inserted candidate (if any), clears the current// list of completions and exits the incremental-search mode if active.// If the completion engine was not active to begin with, nothing will happen.func ( *Engine) () {.autoForce = falseif !.IsActive() {.ClearMenu(true)return}.Cancel(false, true).ClearMenu(true).IsearchStop(false)}// ClearMenu exits the current completion keymap (if set) and clears// the current list of generated completions (if completions is true).func ( *Engine) ( bool) {.skipDisplay = false.resetValues(, false)if .keymap.Local() == keymap.MenuSelect {.keymap.SetLocal("")}}// IsActive indicates if the engine is currently in possession of a// non-empty list of generated completions (following all constraints).func ( *Engine) () bool {:= .keymap.Local() == keymap.MenuSelect:= .keymap.Local() == keymap.Isearch ||.auto || .autoForce, , := .NonIncrementallySearching()return ( || ) && !}// IsInserting returns true if a candidate is currently virtually inserted.func ( *Engine) () bool {return .selected.Value != ""}// Matches returns the number of completion candidates// matching the current line/settings requirements.func ( *Engine) () int {, := .completionCount()return}// Line returns the relevant input line at the time this function is called:// if a candidate is currently selected, the line returned is the one containing// the candidate. If no candidate is selected, the normal input line is returned.// When the line returned is the completed one, the corresponding, adjusted cursor.func ( *Engine) () (*core.Line, *core.Cursor) {if len(.selected.Value) > 0 {return .compLine, .compCursor}return .line, .cursor}// Autocomplete generates the correct completions in autocomplete mode.// We don't do it when we are currently in the completion keymap,// since that means completions have already been computed.func ( *Engine) () {.auto = .needsAutoComplete()// Clear the current completion list when we are at the// beginning of the line, and not currently completing.if .auto || (!.IsActive() && .cursor.Pos() == 0) {.resetValues(true, false)}// We are not auto when either: autocomplete is disabled,// incremental-search mode is active, or a completion is// currently selected in the menu.if !.auto {return}// Regenerate the completions.if .cached != nil {.prepare(.cached())} else if .autoCompleter != nil {.prepare(.autoCompleter())}}// AutocompleteForce forces as-you-type autocomplete on the// real input line, even if the current cursor position is 0.func ( *Engine) () {.autoForce = true}// AutoCompleting returns true if the completion engine is an// autocompletion mode that has been triggered by a particular// command (like history-search-forward/backward).func ( *Engine) () bool {return .keymap.Local() == keymap.Isearch || .autoForce}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |