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 }