1package jid 2 3import ( 4 "io" 5 "strings" 6 7 termbox "github.com/nsf/termbox-go" 8) 9 10const ( 11 DefaultY int = 1 12 FilterPrompt string = "[Filter]> " 13) 14 15type EngineInterface interface { 16 Run() EngineResultInterface 17 GetQuery() QueryInterface 18} 19 20type EngineResultInterface interface { 21 GetQueryString() string 22 GetContent() string 23 GetError() error 24} 25 26type Engine struct { 27 manager *JsonManager 28 query QueryInterface 29 queryCursorIdx int 30 term *Terminal 31 complete []string 32 keymode bool 33 candidates []string 34 candidatemode bool 35 candidateidx int 36 contentOffset int 37 queryConfirm bool 38 prettyResult bool 39} 40 41type EngineAttribute struct { 42 DefaultQuery string 43 Monochrome bool 44 PrettyResult bool 45} 46 47func NewEngine(s io.Reader, ea *EngineAttribute) (EngineInterface, error) { 48 j, err := NewJsonManager(s) 49 if err != nil { 50 return nil, err 51 } 52 e := &Engine{ 53 manager: j, 54 term: NewTerminal(FilterPrompt, DefaultY, ea.Monochrome), 55 query: NewQuery([]rune(ea.DefaultQuery)), 56 complete: []string{"", ""}, 57 keymode: false, 58 candidates: []string{}, 59 candidatemode: false, 60 candidateidx: 0, 61 contentOffset: 0, 62 queryConfirm: false, 63 prettyResult: ea.PrettyResult, 64 } 65 e.queryCursorIdx = e.query.Length() 66 return e, nil 67} 68 69type EngineResult struct { 70 content string 71 qs string 72 err error 73} 74 75func (er *EngineResult) GetQueryString() string { 76 return er.qs 77} 78 79func (er *EngineResult) GetContent() string { 80 return er.content 81} 82func (er *EngineResult) GetError() error { 83 return er.err 84} 85 86func (e *Engine) GetQuery() QueryInterface { 87 return e.query 88} 89 90func (e *Engine) Run() EngineResultInterface { 91 92 err := termbox.Init() 93 if err != nil { 94 panic(err) 95 } 96 defer termbox.Close() 97 98 var contents []string 99 100 for { 101 102 if e.query.StringGet() == "" { 103 e.query.StringSet(".") 104 e.queryCursorIdx = e.query.Length() 105 } 106 107 bl := len(contents) 108 contents = e.getContents() 109 e.setCandidateData() 110 e.queryConfirm = false 111 if bl != len(contents) { 112 e.contentOffset = 0 113 } 114 115 ta := &TerminalDrawAttributes{ 116 Query: e.query.StringGet(), 117 Contents: contents, 118 CandidateIndex: e.candidateidx, 119 ContentsOffsetY: e.contentOffset, 120 Complete: e.complete[0], 121 Candidates: e.candidates, 122 CursorOffset: e.query.IndexOffset(e.queryCursorIdx), 123 } 124 err = e.term.Draw(ta) 125 if err != nil { 126 panic(err) 127 } 128 129 switch ev := termbox.PollEvent(); ev.Type { 130 case termbox.EventKey: 131 switch ev.Key { 132 case 0: 133 e.inputChar(ev.Ch) 134 case termbox.KeyBackspace, termbox.KeyBackspace2: 135 e.deleteChar() 136 case termbox.KeyTab: 137 e.tabAction() 138 case termbox.KeyArrowLeft, termbox.KeyCtrlB: 139 e.moveCursorBackward() 140 case termbox.KeyArrowRight, termbox.KeyCtrlF: 141 e.moveCursorForward() 142 case termbox.KeyHome, termbox.KeyCtrlA: 143 e.moveCursorToTop() 144 case termbox.KeyEnd, termbox.KeyCtrlE: 145 e.moveCursorToEnd() 146 case termbox.KeyCtrlK: 147 e.scrollToAbove() 148 case termbox.KeyCtrlJ: 149 e.scrollToBelow() 150 case termbox.KeyCtrlG: 151 e.scrollToBottom(len(contents)) 152 case termbox.KeyCtrlT: 153 e.scrollToTop() 154 case termbox.KeyCtrlN: 155 _, h := termbox.Size() 156 e.scrollPageDown(len(contents), h) 157 case termbox.KeyCtrlP: 158 _, h := termbox.Size() 159 e.scrollPageUp(h) 160 case termbox.KeyCtrlL: 161 e.toggleKeymode() 162 case termbox.KeyCtrlU: 163 e.deleteLineQuery() 164 case termbox.KeyCtrlW: 165 e.deleteWordBackward() 166 case termbox.KeyEsc: 167 e.escapeCandidateMode() 168 case termbox.KeyEnter: 169 if !e.candidatemode { 170 var cc string 171 var err error 172 if e.prettyResult { 173 cc, _, _, err = e.manager.GetPretty(e.query, true) 174 } else { 175 cc, _, _, err = e.manager.Get(e.query, true) 176 } 177 178 return &EngineResult{ 179 content: cc, 180 qs: e.query.StringGet(), 181 err: err, 182 } 183 } 184 e.confirmCandidate() 185 case termbox.KeyCtrlC: 186 return &EngineResult{} 187 default: 188 } 189 case termbox.EventError: 190 panic(ev.Err) 191 break 192 default: 193 } 194 } 195} 196 197func (e *Engine) getContents() []string { 198 var c string 199 var contents []string 200 c, e.complete, e.candidates, _ = e.manager.GetPretty(e.query, e.queryConfirm) 201 if e.keymode { 202 contents = e.candidates 203 } else { 204 contents = strings.Split(c, "\n") 205 } 206 return contents 207} 208 209func (e *Engine) setCandidateData() { 210 if l := len(e.candidates); e.complete[0] == "" && l > 1 { 211 if e.candidateidx >= l { 212 e.candidateidx = 0 213 } 214 } else { 215 e.candidatemode = false 216 } 217 if !e.candidatemode { 218 e.candidateidx = 0 219 e.candidates = []string{} 220 } 221} 222 223func (e *Engine) confirmCandidate() { 224 _, _ = e.query.PopKeyword() 225 _ = e.query.StringAdd(".") 226 _ = e.query.StringAdd(e.candidates[e.candidateidx]) 227 e.queryCursorIdx = e.query.Length() 228 e.queryConfirm = true 229} 230 231func (e *Engine) deleteChar() { 232 if i := e.queryCursorIdx - 1; i > 0 { 233 _ = e.query.Delete(i) 234 e.queryCursorIdx-- 235 } 236 237} 238 239func (e *Engine) deleteLineQuery() { 240 _ = e.query.StringSet("") 241 e.queryCursorIdx = 0 242} 243 244func (e *Engine) scrollToBelow() { 245 e.contentOffset++ 246} 247 248func (e *Engine) scrollToAbove() { 249 if o := e.contentOffset - 1; o >= 0 { 250 e.contentOffset = o 251 } 252} 253 254func (e *Engine) scrollToBottom(rownum int) { 255 e.contentOffset = rownum - 1 256} 257 258func (e *Engine) scrollToTop() { 259 e.contentOffset = 0 260} 261 262func (e *Engine) scrollPageDown(rownum int, height int) { 263 co := rownum - 1 264 if o := rownum - e.contentOffset; o > height { 265 co = e.contentOffset + (height - DefaultY) 266 } 267 e.contentOffset = co 268} 269 270func (e *Engine) scrollPageUp(height int) { 271 co := 0 272 if o := e.contentOffset - (height - DefaultY); o > 0 { 273 co = o 274 } 275 e.contentOffset = co 276} 277 278func (e *Engine) toggleKeymode() { 279 e.keymode = !e.keymode 280} 281func (e *Engine) deleteWordBackward() { 282 if k, _ := e.query.StringPopKeyword(); k != "" && !strings.Contains(k, "[") { 283 _ = e.query.StringAdd(".") 284 } 285 e.queryCursorIdx = e.query.Length() 286} 287func (e *Engine) tabAction() { 288 if !e.candidatemode { 289 e.candidatemode = true 290 if e.complete[0] != e.complete[1] && e.complete[0] != "" { 291 if k, _ := e.query.StringPopKeyword(); !strings.Contains(k, "[") { 292 _ = e.query.StringAdd(".") 293 } 294 _ = e.query.StringAdd(e.complete[1]) 295 } else { 296 _ = e.query.StringAdd(e.complete[0]) 297 } 298 } else { 299 e.candidateidx = e.candidateidx + 1 300 } 301 e.queryCursorIdx = e.query.Length() 302} 303func (e *Engine) escapeCandidateMode() { 304 e.candidatemode = false 305} 306func (e *Engine) inputChar(ch rune) { 307 _ = e.query.Insert([]rune{ch}, e.queryCursorIdx) 308 e.queryCursorIdx++ 309} 310 311func (e *Engine) moveCursorBackward() { 312 if i := e.queryCursorIdx - 1; i >= 0 { 313 e.queryCursorIdx-- 314 } 315} 316 317func (e *Engine) moveCursorForward() { 318 if e.query.Length() > e.queryCursorIdx { 319 e.queryCursorIdx++ 320 } 321} 322 323func (e *Engine) moveCursorWordBackwark() { 324} 325func (e *Engine) moveCursorWordForward() { 326} 327func (e *Engine) moveCursorToTop() { 328 e.queryCursorIdx = 0 329} 330func (e *Engine) moveCursorToEnd() { 331 e.queryCursorIdx = e.query.Length() 332} 333