github.com/hoop33/elvish@v0.0.0-20160801152013-6d25485beab4/edit/bang.go (about)

     1  package edit
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // Bang mode.
    11  
    12  type bangEntry struct {
    13  	i int
    14  	s string
    15  }
    16  
    17  type bang struct {
    18  	listing
    19  	line     string
    20  	words    []string
    21  	filtered []bangEntry
    22  	minus    bool
    23  }
    24  
    25  func (b *bang) Len() int {
    26  	return len(b.filtered)
    27  }
    28  
    29  func (b *bang) Show(i, width int) styled {
    30  	entry := b.filtered[i]
    31  	var head string
    32  	if entry.i == -1 {
    33  		head = "M-, "
    34  	} else if b.minus {
    35  		head = fmt.Sprintf("%3d ", entry.i-len(b.words))
    36  	} else {
    37  		head = fmt.Sprintf("%3d ", entry.i)
    38  	}
    39  	return unstyled(ForceWcWidth(head+entry.s, width))
    40  }
    41  
    42  func (b *bang) Filter(filter string) int {
    43  	b.filtered = nil
    44  	b.minus = len(filter) > 0 && filter[0] == '-'
    45  	if filter == "" || filter == "-" {
    46  		b.filtered = append(b.filtered, bangEntry{-1, b.line})
    47  	} else if _, err := strconv.Atoi(filter); err != nil {
    48  		return -1
    49  	}
    50  	// Quite inefficient way to filter by prefix of stringified index.
    51  	n := len(b.words)
    52  	for i, word := range b.words {
    53  		if filter == "" ||
    54  			(!b.minus && strings.HasPrefix(strconv.Itoa(i), filter)) ||
    55  			(b.minus && strings.HasPrefix(strconv.Itoa(i-n), filter)) {
    56  			b.filtered = append(b.filtered, bangEntry{i, word})
    57  		}
    58  	}
    59  	if len(b.filtered) == 0 {
    60  		return -1
    61  	}
    62  	return 0
    63  }
    64  
    65  func (b *bang) Accept(i int, ed *Editor) {
    66  	ed.insertAtDot(b.filtered[i].s)
    67  	startInsert(ed)
    68  }
    69  
    70  func (b *bang) ModeTitle(i int) string {
    71  	return " LASTCMD "
    72  }
    73  
    74  var wordSep = regexp.MustCompile("[ \t]+")
    75  
    76  func startBang(ed *Editor) {
    77  	_, line, err := ed.store.LastCmd(-1, "", true)
    78  	if err == nil {
    79  		ed.bang = newBang(line)
    80  		ed.mode = ed.bang
    81  	} else {
    82  		ed.addTip("db error: %s", err.Error())
    83  	}
    84  }
    85  
    86  func bangAltDefault(ed *Editor) {
    87  	l := ed.bang
    88  	if l.handleFilterKey(ed.lastKey) {
    89  		if l.Len() == 1 {
    90  			l.Accept(l.selected, ed)
    91  		}
    92  	} else if ed.lastKey == (Key{',', Alt}) {
    93  		l.Accept(0, ed)
    94  	} else {
    95  		startInsert(ed)
    96  		ed.nextAction = action{typ: reprocessKey}
    97  	}
    98  }
    99  
   100  func newBang(line string) *bang {
   101  	b := &bang{listing{}, line, wordSep.Split(strings.Trim(line, " \t"), -1), nil, false}
   102  	b.listing = newListing(modeBang, b)
   103  	return b
   104  }