1package readline 2 3import ( 4 "bufio" 5 "bytes" 6 "container/list" 7 "fmt" 8 "os" 9 "strconv" 10 "strings" 11 "sync" 12 "time" 13 "unicode" 14) 15 16var ( 17 isWindows = false 18) 19 20const ( 21 CharLineStart = 1 22 CharBackward = 2 23 CharInterrupt = 3 24 CharDelete = 4 25 CharLineEnd = 5 26 CharForward = 6 27 CharBell = 7 28 CharCtrlH = 8 29 CharTab = 9 30 CharCtrlJ = 10 31 CharKill = 11 32 CharCtrlL = 12 33 CharEnter = 13 34 CharNext = 14 35 CharPrev = 16 36 CharBckSearch = 18 37 CharFwdSearch = 19 38 CharTranspose = 20 39 CharCtrlU = 21 40 CharCtrlW = 23 41 CharCtrlY = 25 42 CharCtrlZ = 26 43 CharEsc = 27 44 CharEscapeEx = 91 45 CharBackspace = 127 46) 47 48const ( 49 MetaBackward rune = -iota - 1 50 MetaForward 51 MetaDelete 52 MetaBackspace 53 MetaTranspose 54) 55 56// WaitForResume need to call before current process got suspend. 57// It will run a ticker until a long duration is occurs, 58// which means this process is resumed. 59func WaitForResume() chan struct{} { 60 ch := make(chan struct{}) 61 var wg sync.WaitGroup 62 wg.Add(1) 63 go func() { 64 ticker := time.NewTicker(10 * time.Millisecond) 65 t := time.Now() 66 wg.Done() 67 for { 68 now := <-ticker.C 69 if now.Sub(t) > 100*time.Millisecond { 70 break 71 } 72 t = now 73 } 74 ticker.Stop() 75 ch <- struct{}{} 76 }() 77 wg.Wait() 78 return ch 79} 80 81func Restore(fd int, state *State) error { 82 err := restoreTerm(fd, state) 83 if err != nil { 84 // errno 0 means everything is ok :) 85 if err.Error() == "errno 0" { 86 return nil 87 } else { 88 return err 89 } 90 } 91 return nil 92} 93 94func IsPrintable(key rune) bool { 95 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff 96 return key >= 32 && !isInSurrogateArea 97} 98 99// translate Esc[X 100func escapeExKey(key *escapeKeyPair) rune { 101 var r rune 102 switch key.typ { 103 case 'D': 104 r = CharBackward 105 case 'C': 106 r = CharForward 107 case 'A': 108 r = CharPrev 109 case 'B': 110 r = CharNext 111 case 'H': 112 r = CharLineStart 113 case 'F': 114 r = CharLineEnd 115 case '~': 116 if key.attr == "3" { 117 r = CharDelete 118 } 119 default: 120 } 121 return r 122} 123 124type escapeKeyPair struct { 125 attr string 126 typ rune 127} 128 129func (e *escapeKeyPair) Get2() (int, int, bool) { 130 sp := strings.Split(e.attr, ";") 131 if len(sp) < 2 { 132 return -1, -1, false 133 } 134 s1, err := strconv.Atoi(sp[0]) 135 if err != nil { 136 return -1, -1, false 137 } 138 s2, err := strconv.Atoi(sp[1]) 139 if err != nil { 140 return -1, -1, false 141 } 142 return s1, s2, true 143} 144 145func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair { 146 p := escapeKeyPair{} 147 buf := bytes.NewBuffer(nil) 148 for { 149 if r == ';' { 150 } else if unicode.IsNumber(r) { 151 } else { 152 p.typ = r 153 break 154 } 155 buf.WriteRune(r) 156 r, _, _ = reader.ReadRune() 157 } 158 p.attr = buf.String() 159 return &p 160} 161 162// translate EscX to Meta+X 163func escapeKey(r rune, reader *bufio.Reader) rune { 164 switch r { 165 case 'b': 166 r = MetaBackward 167 case 'f': 168 r = MetaForward 169 case 'd': 170 r = MetaDelete 171 case CharTranspose: 172 r = MetaTranspose 173 case CharBackspace: 174 r = MetaBackspace 175 case 'O': 176 d, _, _ := reader.ReadRune() 177 switch d { 178 case 'H': 179 r = CharLineStart 180 case 'F': 181 r = CharLineEnd 182 default: 183 reader.UnreadRune() 184 } 185 case CharEsc: 186 187 } 188 return r 189} 190 191func SplitByLine(start, screenWidth int, rs []rune) []string { 192 var ret []string 193 buf := bytes.NewBuffer(nil) 194 currentWidth := start 195 for _, r := range rs { 196 w := runes.Width(r) 197 currentWidth += w 198 buf.WriteRune(r) 199 if currentWidth >= screenWidth { 200 ret = append(ret, buf.String()) 201 buf.Reset() 202 currentWidth = 0 203 } 204 } 205 ret = append(ret, buf.String()) 206 return ret 207} 208 209// calculate how many lines for N character 210func LineCount(screenWidth, w int) int { 211 r := w / screenWidth 212 if w%screenWidth != 0 { 213 r++ 214 } 215 return r 216} 217 218func IsWordBreak(i rune) bool { 219 switch { 220 case i >= 'a' && i <= 'z': 221 case i >= 'A' && i <= 'Z': 222 case i >= '0' && i <= '9': 223 default: 224 return true 225 } 226 return false 227} 228 229func GetInt(s []string, def int) int { 230 if len(s) == 0 { 231 return def 232 } 233 c, err := strconv.Atoi(s[0]) 234 if err != nil { 235 return def 236 } 237 return c 238} 239 240type RawMode struct { 241 state *State 242} 243 244func (r *RawMode) Enter() (err error) { 245 r.state, err = MakeRaw(GetStdin()) 246 return err 247} 248 249func (r *RawMode) Exit() error { 250 if r.state == nil { 251 return nil 252 } 253 return Restore(GetStdin(), r.state) 254} 255 256// ----------------------------------------------------------------------------- 257 258func sleep(n int) { 259 Debug(n) 260 time.Sleep(2000 * time.Millisecond) 261} 262 263// print a linked list to Debug() 264func debugList(l *list.List) { 265 idx := 0 266 for e := l.Front(); e != nil; e = e.Next() { 267 Debug(idx, fmt.Sprintf("%+v", e.Value)) 268 idx++ 269 } 270} 271 272// append log info to another file 273func Debug(o ...interface{}) { 274 f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 275 fmt.Fprintln(f, o...) 276 f.Close() 277} 278