cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / ui / lineedit / word.go
word.go 2.5K
1package lineedit23import (4 "slices"5 "unicode"6)78// Word keys, by the names terminals send them under. Option+arrows on9// macOS arrive as alt+b and alt+f in most terminals, Ctrl+Backspace as10// ctrl+h without the kitty keyboard protocol, and Cmd+Backspace as11// ctrl+u.12var (13 wordLeftKeys = []string{"ctrl+left", "alt+left", "alt+b"}14 wordRightKeys = []string{"ctrl+right", "alt+right", "alt+f"}15 wordBackKeys = []string{"ctrl+backspace", "alt+backspace", "ctrl+h", "ctrl+w"}16 wordDelKeys = []string{"ctrl+delete", "alt+delete", "alt+d"}17 lineBackKeys = []string{"ctrl+u", "super+backspace"}18)1920// IsWordKey reports whether key moves or deletes by word or to the start21// of the line, so a field can pass it on to Key.22func IsWordKey(key string) bool {23 for _, keys := range [][]string{wordLeftKeys, wordRightKeys, wordBackKeys, wordDelKeys, lineBackKeys} {24 if slices.Contains(keys, key) {25 return true26 }27 }28 return false29}3031// wordKey applies a word key and reports whether key was one.32func (l *Line) wordKey(key string) bool {33 switch {34 case slices.Contains(wordLeftKeys, key):35 l.Pos = l.wordStart(l.Pos)36 case slices.Contains(wordRightKeys, key):37 l.Pos = l.wordEnd(l.Pos)38 case slices.Contains(wordBackKeys, key):39 l.DeleteBack(l.wordStart(l.Pos))40 case slices.Contains(wordDelKeys, key):41 l.Buf = slices.Delete(l.Buf, l.Pos, l.wordEnd(l.Pos))42 case slices.Contains(lineBackKeys, key):43 l.DeleteBack(0)44 default:45 return false46 }47 return true48}4950// DeleteBack deletes from index from up to the caret.51func (l *Line) DeleteBack(from int) {52 l.Buf = slices.Delete(l.Buf, from, l.Pos)53 l.Pos = from54}5556// A word is a run of letters, digits and underscores, or a run of other57// symbols, so in a formula Ctrl+Backspace takes B2 from SUM(A1:B2 and58// then the colon. Spaces and line breaks around a word go with it.59func wordClass(r rune) int {60 switch {61 case unicode.IsSpace(r):62 return 063 case r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r):64 return 165 }66 return 267}6869// wordStart is the start of the word before i, skipping spaces first.70func (l *Line) wordStart(i int) int {71 for i > 0 && wordClass(l.Buf[i-1]) == 0 {72 i--73 }74 if i == 0 {75 return 076 }77 c := wordClass(l.Buf[i-1])78 for i > 0 && wordClass(l.Buf[i-1]) == c {79 i--80 }81 return i82}8384// wordEnd is the end of the word after i, skipping spaces first.85func (l *Line) wordEnd(i int) int {86 n := len(l.Buf)87 for i < n && wordClass(l.Buf[i]) == 0 {88 i++89 }90 if i == n {91 return n92 }93 c := wordClass(l.Buf[i])94 for i < n && wordClass(l.Buf[i]) == c {95 i++96 }97 return i98}