Path: blob/main/vendor/github.com/chzyer/readline/complete.go
2875 views
package readline12import (3"bufio"4"bytes"5"fmt"6"io"7)89type AutoCompleter interface {10// Readline will pass the whole line and current offset to it11// Completer need to pass all the candidates, and how long they shared the same characters in line12// Example:13// [go, git, git-shell, grep]14// Do("g", 1) => ["o", "it", "it-shell", "rep"], 115// Do("gi", 2) => ["t", "t-shell"], 216// Do("git", 3) => ["", "-shell"], 317Do(line []rune, pos int) (newLine [][]rune, length int)18}1920type TabCompleter struct{}2122func (t *TabCompleter) Do([]rune, int) ([][]rune, int) {23return [][]rune{[]rune("\t")}, 024}2526type opCompleter struct {27w io.Writer28op *Operation29width int3031inCompleteMode bool32inSelectMode bool33candidate [][]rune34candidateSource []rune35candidateOff int36candidateChoise int37candidateColNum int38}3940func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {41return &opCompleter{42w: w,43op: op,44width: width,45}46}4748func (o *opCompleter) doSelect() {49if len(o.candidate) == 1 {50o.op.buf.WriteRunes(o.candidate[0])51o.ExitCompleteMode(false)52return53}54o.nextCandidate(1)55o.CompleteRefresh()56}5758func (o *opCompleter) nextCandidate(i int) {59o.candidateChoise += i60o.candidateChoise = o.candidateChoise % len(o.candidate)61if o.candidateChoise < 0 {62o.candidateChoise = len(o.candidate) + o.candidateChoise63}64}6566func (o *opCompleter) OnComplete() bool {67if o.width == 0 {68return false69}70if o.IsInCompleteSelectMode() {71o.doSelect()72return true73}7475buf := o.op.buf76rs := buf.Runes()7778if o.IsInCompleteMode() && o.candidateSource != nil && runes.Equal(rs, o.candidateSource) {79o.EnterCompleteSelectMode()80o.doSelect()81return true82}8384o.ExitCompleteSelectMode()85o.candidateSource = rs86newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)87if len(newLines) == 0 {88o.ExitCompleteMode(false)89return true90}9192// only Aggregate candidates in non-complete mode93if !o.IsInCompleteMode() {94if len(newLines) == 1 {95buf.WriteRunes(newLines[0])96o.ExitCompleteMode(false)97return true98}99100same, size := runes.Aggregate(newLines)101if size > 0 {102buf.WriteRunes(same)103o.ExitCompleteMode(false)104return true105}106}107108o.EnterCompleteMode(offset, newLines)109return true110}111112func (o *opCompleter) IsInCompleteSelectMode() bool {113return o.inSelectMode114}115116func (o *opCompleter) IsInCompleteMode() bool {117return o.inCompleteMode118}119120func (o *opCompleter) HandleCompleteSelect(r rune) bool {121next := true122switch r {123case CharEnter, CharCtrlJ:124next = false125o.op.buf.WriteRunes(o.op.candidate[o.op.candidateChoise])126o.ExitCompleteMode(false)127case CharLineStart:128num := o.candidateChoise % o.candidateColNum129o.nextCandidate(-num)130case CharLineEnd:131num := o.candidateColNum - o.candidateChoise%o.candidateColNum - 1132o.candidateChoise += num133if o.candidateChoise >= len(o.candidate) {134o.candidateChoise = len(o.candidate) - 1135}136case CharBackspace:137o.ExitCompleteSelectMode()138next = false139case CharTab, CharForward:140o.doSelect()141case CharBell, CharInterrupt:142o.ExitCompleteMode(true)143next = false144case CharNext:145tmpChoise := o.candidateChoise + o.candidateColNum146if tmpChoise >= o.getMatrixSize() {147tmpChoise -= o.getMatrixSize()148} else if tmpChoise >= len(o.candidate) {149tmpChoise += o.candidateColNum150tmpChoise -= o.getMatrixSize()151}152o.candidateChoise = tmpChoise153case CharBackward:154o.nextCandidate(-1)155case CharPrev:156tmpChoise := o.candidateChoise - o.candidateColNum157if tmpChoise < 0 {158tmpChoise += o.getMatrixSize()159if tmpChoise >= len(o.candidate) {160tmpChoise -= o.candidateColNum161}162}163o.candidateChoise = tmpChoise164default:165next = false166o.ExitCompleteSelectMode()167}168if next {169o.CompleteRefresh()170return true171}172return false173}174175func (o *opCompleter) getMatrixSize() int {176line := len(o.candidate) / o.candidateColNum177if len(o.candidate)%o.candidateColNum != 0 {178line++179}180return line * o.candidateColNum181}182183func (o *opCompleter) OnWidthChange(newWidth int) {184o.width = newWidth185}186187func (o *opCompleter) CompleteRefresh() {188if !o.inCompleteMode {189return190}191lineCnt := o.op.buf.CursorLineCount()192colWidth := 0193for _, c := range o.candidate {194w := runes.WidthAll(c)195if w > colWidth {196colWidth = w197}198}199colWidth += o.candidateOff + 1200same := o.op.buf.RuneSlice(-o.candidateOff)201202// -1 to avoid reach the end of line203width := o.width - 1204colNum := width / colWidth205if colNum != 0 {206colWidth += (width - (colWidth * colNum)) / colNum207}208209o.candidateColNum = colNum210buf := bufio.NewWriter(o.w)211buf.Write(bytes.Repeat([]byte("\n"), lineCnt))212213colIdx := 0214lines := 1215buf.WriteString("\033[J")216for idx, c := range o.candidate {217inSelect := idx == o.candidateChoise && o.IsInCompleteSelectMode()218if inSelect {219buf.WriteString("\033[30;47m")220}221buf.WriteString(string(same))222buf.WriteString(string(c))223buf.Write(bytes.Repeat([]byte(" "), colWidth-runes.WidthAll(c)-runes.WidthAll(same)))224225if inSelect {226buf.WriteString("\033[0m")227}228229colIdx++230if colIdx == colNum {231buf.WriteString("\n")232lines++233colIdx = 0234}235}236237// move back238fmt.Fprintf(buf, "\033[%dA\r", lineCnt-1+lines)239fmt.Fprintf(buf, "\033[%dC", o.op.buf.idx+o.op.buf.PromptLen())240buf.Flush()241}242243func (o *opCompleter) aggCandidate(candidate [][]rune) int {244offset := 0245for i := 0; i < len(candidate[0]); i++ {246for j := 0; j < len(candidate)-1; j++ {247if i > len(candidate[j]) {248goto aggregate249}250if candidate[j][i] != candidate[j+1][i] {251goto aggregate252}253}254offset = i255}256aggregate:257return offset258}259260func (o *opCompleter) EnterCompleteSelectMode() {261o.inSelectMode = true262o.candidateChoise = -1263o.CompleteRefresh()264}265266func (o *opCompleter) EnterCompleteMode(offset int, candidate [][]rune) {267o.inCompleteMode = true268o.candidate = candidate269o.candidateOff = offset270o.CompleteRefresh()271}272273func (o *opCompleter) ExitCompleteSelectMode() {274o.inSelectMode = false275o.candidate = nil276o.candidateChoise = -1277o.candidateOff = -1278o.candidateSource = nil279}280281func (o *opCompleter) ExitCompleteMode(revent bool) {282o.inCompleteMode = false283o.ExitCompleteSelectMode()284}285286287