1// Copyright 2014 The gocui Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package gocui 6 7import "errors" 8 9const maxInt = int(^uint(0) >> 1) 10 11// Editor interface must be satisfied by gocui editors. 12type Editor interface { 13 Edit(v *View, key Key, ch rune, mod Modifier) 14} 15 16// The EditorFunc type is an adapter to allow the use of ordinary functions as 17// Editors. If f is a function with the appropriate signature, EditorFunc(f) 18// is an Editor object that calls f. 19type EditorFunc func(v *View, key Key, ch rune, mod Modifier) 20 21// Edit calls f(v, key, ch, mod) 22func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) { 23 f(v, key, ch, mod) 24} 25 26// DefaultEditor is the default editor. 27var DefaultEditor Editor = EditorFunc(simpleEditor) 28 29// simpleEditor is used as the default gocui editor. 30func simpleEditor(v *View, key Key, ch rune, mod Modifier) { 31 switch { 32 case ch != 0 && mod == 0: 33 v.EditWrite(ch) 34 case key == KeySpace: 35 v.EditWrite(' ') 36 case key == KeyBackspace || key == KeyBackspace2: 37 v.EditDelete(true) 38 case key == KeyDelete: 39 v.EditDelete(false) 40 case key == KeyInsert: 41 v.Overwrite = !v.Overwrite 42 case key == KeyEnter: 43 v.EditNewLine() 44 case key == KeyArrowDown: 45 v.MoveCursor(0, 1, false) 46 case key == KeyArrowUp: 47 v.MoveCursor(0, -1, false) 48 case key == KeyArrowLeft: 49 v.MoveCursor(-1, 0, false) 50 case key == KeyArrowRight: 51 v.MoveCursor(1, 0, false) 52 } 53} 54 55// EditWrite writes a rune at the cursor position. 56func (v *View) EditWrite(ch rune) { 57 v.writeRune(v.cx, v.cy, ch) 58 v.MoveCursor(1, 0, true) 59} 60 61// EditDelete deletes a rune at the cursor position. back determines the 62// direction. 63func (v *View) EditDelete(back bool) { 64 x, y := v.ox+v.cx, v.oy+v.cy 65 if y < 0 { 66 return 67 } else if y >= len(v.viewLines) { 68 v.MoveCursor(-1, 0, true) 69 return 70 } 71 72 maxX, _ := v.Size() 73 if back { 74 if x == 0 { // start of the line 75 if y < 1 { 76 return 77 } 78 79 var maxPrevWidth int 80 if v.Wrap { 81 maxPrevWidth = maxX 82 } else { 83 maxPrevWidth = maxInt 84 } 85 86 if v.viewLines[y].linesX == 0 { // regular line 87 v.mergeLines(v.cy - 1) 88 if len(v.viewLines[y-1].line) < maxPrevWidth { 89 v.MoveCursor(-1, 0, true) 90 } 91 } else { // wrapped line 92 v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1) 93 v.MoveCursor(-1, 0, true) 94 } 95 } else { // middle/end of the line 96 v.deleteRune(v.cx-1, v.cy) 97 v.MoveCursor(-1, 0, true) 98 } 99 } else { 100 if x == len(v.viewLines[y].line) { // end of the line 101 v.mergeLines(v.cy) 102 } else { // start/middle of the line 103 v.deleteRune(v.cx, v.cy) 104 } 105 } 106} 107 108// EditNewLine inserts a new line under the cursor. 109func (v *View) EditNewLine() { 110 v.breakLine(v.cx, v.cy) 111 v.ox = 0 112 v.cx = 0 113 v.MoveCursor(0, 1, true) 114} 115 116// MoveCursor moves the cursor taking into account the width of the line/view, 117// displacing the origin if necessary. 118func (v *View) MoveCursor(dx, dy int, writeMode bool) { 119 maxX, maxY := v.Size() 120 cx, cy := v.cx+dx, v.cy+dy 121 x, y := v.ox+cx, v.oy+cy 122 123 var curLineWidth, prevLineWidth int 124 // get the width of the current line 125 if writeMode { 126 if v.Wrap { 127 curLineWidth = maxX - 1 128 } else { 129 curLineWidth = maxInt 130 } 131 } else { 132 if y >= 0 && y < len(v.viewLines) { 133 curLineWidth = len(v.viewLines[y].line) 134 if v.Wrap && curLineWidth >= maxX { 135 curLineWidth = maxX - 1 136 } 137 } else { 138 curLineWidth = 0 139 } 140 } 141 // get the width of the previous line 142 if y-1 >= 0 && y-1 < len(v.viewLines) { 143 prevLineWidth = len(v.viewLines[y-1].line) 144 } else { 145 prevLineWidth = 0 146 } 147 148 // adjust cursor's x position and view's x origin 149 if x > curLineWidth { // move to next line 150 if dx > 0 { // horizontal movement 151 cy++ 152 if writeMode || v.oy+cy < len(v.viewLines) { 153 if !v.Wrap { 154 v.ox = 0 155 } 156 v.cx = 0 157 } 158 } else { // vertical movement 159 if curLineWidth > 0 { // move cursor to the EOL 160 if v.Wrap { 161 v.cx = curLineWidth 162 } else { 163 ncx := curLineWidth - v.ox 164 if ncx < 0 { 165 v.ox += ncx 166 if v.ox < 0 { 167 v.ox = 0 168 } 169 v.cx = 0 170 } else { 171 v.cx = ncx 172 } 173 } 174 } else { 175 if writeMode || v.oy+cy < len(v.viewLines) { 176 if !v.Wrap { 177 v.ox = 0 178 } 179 v.cx = 0 180 } 181 } 182 } 183 } else if cx < 0 { 184 if !v.Wrap && v.ox > 0 { // move origin to the left 185 v.ox += cx 186 v.cx = 0 187 } else { // move to previous line 188 cy-- 189 if prevLineWidth > 0 { 190 if !v.Wrap { // set origin so the EOL is visible 191 nox := prevLineWidth - maxX + 1 192 if nox < 0 { 193 v.ox = 0 194 } else { 195 v.ox = nox 196 } 197 } 198 v.cx = prevLineWidth 199 } else { 200 if !v.Wrap { 201 v.ox = 0 202 } 203 v.cx = 0 204 } 205 } 206 } else { // stay on the same line 207 if v.Wrap { 208 v.cx = cx 209 } else { 210 if cx >= maxX { 211 v.ox += cx - maxX + 1 212 v.cx = maxX 213 } else { 214 v.cx = cx 215 } 216 } 217 } 218 219 // adjust cursor's y position and view's y origin 220 if cy < 0 { 221 if v.oy > 0 { 222 v.oy-- 223 } 224 } else if writeMode || v.oy+cy < len(v.viewLines) { 225 if cy >= maxY { 226 v.oy++ 227 } else { 228 v.cy = cy 229 } 230 } 231} 232 233// writeRune writes a rune into the view's internal buffer, at the 234// position corresponding to the point (x, y). The length of the internal 235// buffer is increased if the point is out of bounds. Overwrite mode is 236// governed by the value of View.overwrite. 237func (v *View) writeRune(x, y int, ch rune) error { 238 v.tainted = true 239 240 x, y, err := v.realPosition(x, y) 241 if err != nil { 242 return err 243 } 244 245 if x < 0 || y < 0 { 246 return errors.New("invalid point") 247 } 248 249 if y >= len(v.lines) { 250 s := make([][]cell, y-len(v.lines)+1) 251 v.lines = append(v.lines, s...) 252 } 253 254 olen := len(v.lines[y]) 255 256 var s []cell 257 if x >= len(v.lines[y]) { 258 s = make([]cell, x-len(v.lines[y])+1) 259 } else if !v.Overwrite { 260 s = make([]cell, 1) 261 } 262 v.lines[y] = append(v.lines[y], s...) 263 264 if !v.Overwrite || (v.Overwrite && x >= olen-1) { 265 copy(v.lines[y][x+1:], v.lines[y][x:]) 266 } 267 v.lines[y][x] = cell{ 268 fgColor: v.FgColor, 269 bgColor: v.BgColor, 270 chr: ch, 271 } 272 273 return nil 274} 275 276// deleteRune removes a rune from the view's internal buffer, at the 277// position corresponding to the point (x, y). 278func (v *View) deleteRune(x, y int) error { 279 v.tainted = true 280 281 x, y, err := v.realPosition(x, y) 282 if err != nil { 283 return err 284 } 285 286 if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) { 287 return errors.New("invalid point") 288 } 289 v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...) 290 return nil 291} 292 293// mergeLines merges the lines "y" and "y+1" if possible. 294func (v *View) mergeLines(y int) error { 295 v.tainted = true 296 297 _, y, err := v.realPosition(0, y) 298 if err != nil { 299 return err 300 } 301 302 if y < 0 || y >= len(v.lines) { 303 return errors.New("invalid point") 304 } 305 306 if y < len(v.lines)-1 { // otherwise we don't need to merge anything 307 v.lines[y] = append(v.lines[y], v.lines[y+1]...) 308 v.lines = append(v.lines[:y+1], v.lines[y+2:]...) 309 } 310 return nil 311} 312 313// breakLine breaks a line of the internal buffer at the position corresponding 314// to the point (x, y). 315func (v *View) breakLine(x, y int) error { 316 v.tainted = true 317 318 x, y, err := v.realPosition(x, y) 319 if err != nil { 320 return err 321 } 322 323 if y < 0 || y >= len(v.lines) { 324 return errors.New("invalid point") 325 } 326 327 var left, right []cell 328 if x < len(v.lines[y]) { // break line 329 left = make([]cell, len(v.lines[y][:x])) 330 copy(left, v.lines[y][:x]) 331 right = make([]cell, len(v.lines[y][x:])) 332 copy(right, v.lines[y][x:]) 333 } else { // new empty line 334 left = v.lines[y] 335 } 336 337 lines := make([][]cell, len(v.lines)+1) 338 lines[y] = left 339 lines[y+1] = right 340 copy(lines, v.lines[:y]) 341 copy(lines[y+2:], v.lines[y+1:]) 342 v.lines = lines 343 return nil 344} 345