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 (
8 "github.com/go-errors/errors"
9
10 "github.com/mattn/go-runewidth"
11)
12
13const maxInt = int(^uint(0) >> 1)
14
15// Editor interface must be satisfied by gocui editors.
16type Editor interface {
17 Edit(v *View, key Key, ch rune, mod Modifier)
18}
19
20// The EditorFunc type is an adapter to allow the use of ordinary functions as
21// Editors. If f is a function with the appropriate signature, EditorFunc(f)
22// is an Editor object that calls f.
23type EditorFunc func(v *View, key Key, ch rune, mod Modifier)
24
25// Edit calls f(v, key, ch, mod)
26func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) {
27 f(v, key, ch, mod)
28}
29
30// DefaultEditor is the default editor.
31var DefaultEditor Editor = EditorFunc(simpleEditor)
32
33// simpleEditor is used as the default gocui editor.
34func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
35 switch {
36 case ch != 0 && mod == 0:
37 v.EditWrite(ch)
38 case key == KeySpace:
39 v.EditWrite(' ')
40 case key == KeyBackspace || key == KeyBackspace2:
41 v.EditDelete(true)
42 case key == KeyDelete:
43 v.EditDelete(false)
44 case key == KeyInsert:
45 v.Overwrite = !v.Overwrite
46 case key == KeyEnter:
47 v.EditNewLine()
48 case key == KeyArrowDown:
49 v.MoveCursor(0, 1, false)
50 case key == KeyArrowUp:
51 v.MoveCursor(0, -1, false)
52 case key == KeyArrowLeft:
53 v.MoveCursor(-1, 0, false)
54 case key == KeyArrowRight:
55 v.MoveCursor(1, 0, false)
56 case key == KeyTab:
57 v.EditWrite('\t')
58 case key == KeySpace:
59 v.EditWrite(' ')
60 case key == KeyInsert:
61 v.Overwrite = !v.Overwrite
62 default:
63 v.EditWrite(ch)
64 }
65}
66
67// EditWrite writes a rune at the cursor position.
68func (v *View) EditWrite(ch rune) {
69 w := runewidth.RuneWidth(ch)
70 v.writeRune(v.cx, v.cy, ch)
71 v.moveCursor(w, 0, true)
72}
73
74// EditDeleteToStartOfLine is the equivalent of pressing ctrl+U in your terminal, it deletes to the start of the line. Or if you are already at the start of the line, it deletes the newline character
75func (v *View) EditDeleteToStartOfLine() {
76 x, _ := v.Cursor()
77 if x == 0 {
78 v.EditDelete(true)
79 } else {
80 // delete characters until we are the start of the line
81 for x > 0 {
82 v.EditDelete(true)
83 x, _ = v.Cursor()
84 }
85 }
86}
87
88// EditGotoToStartOfLine takes you to the start of the current line
89func (v *View) EditGotoToStartOfLine() {
90 x, _ := v.Cursor()
91 for x > 0 {
92 v.MoveCursor(-1, 0, false)
93 x, _ = v.Cursor()
94 }
95}
96
97// EditGotoToEndOfLine takes you to the end of the line
98func (v *View) EditGotoToEndOfLine() {
99 _, y := v.Cursor()
100 _ = v.SetCursor(0, y+1)
101 x, newY := v.Cursor()
102 if newY == y {
103 // we must be on the last line, so lets move to the very end
104 prevX := -1
105 for prevX != x {
106 prevX = x
107 v.MoveCursor(1, 0, false)
108 x, _ = v.Cursor()
109 }
110 } else {
111 // most left so now we're at the end of the original line
112 v.MoveCursor(-1, 0, false)
113 }
114}
115
116// EditDelete deletes a rune at the cursor position. back determines the
117// direction.
118func (v *View) EditDelete(back bool) {
119 x, y := v.ox+v.cx, v.oy+v.cy
120 if y < 0 {
121 return
122 } else if y >= len(v.viewLines) {
123 v.MoveCursor(-1, 0, true)
124 return
125 }
126
127 maxX, _ := v.Size()
128 if back {
129 if x == 0 { // start of the line
130 if y < 1 {
131 return
132 }
133
134 var maxPrevWidth int
135 if v.Wrap {
136 maxPrevWidth = maxX
137 } else {
138 maxPrevWidth = maxInt
139 }
140
141 if v.viewLines[y].linesX == 0 { // regular line
142 v.mergeLines(v.cy - 1)
143 if len(v.viewLines[y-1].line) < maxPrevWidth {
144 v.MoveCursor(-1, 0, true)
145 }
146 } else { // wrapped line
147 n, _ := v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
148 v.MoveCursor(-n, 0, true)
149 }
150 } else { // middle/end of the line
151 n, _ := v.deleteRune(v.cx-1, v.cy)
152 v.MoveCursor(-n, 0, true)
153 }
154 } else {
155 if x == len(v.viewLines[y].line) { // end of the line
156 v.mergeLines(v.cy)
157 } else { // start/middle of the line
158 v.deleteRune(v.cx, v.cy)
159 }
160 }
161}
162
163// EditNewLine inserts a new line under the cursor.
164func (v *View) EditNewLine() {
165 v.breakLine(v.cx, v.cy)
166 v.ox = 0
167 v.cy = v.cy + 1
168 v.cx = 0
169}
170
171// MoveCursor moves the cursor taking into account the width of the line/view,
172// displacing the origin if necessary.
173func (v *View) MoveCursor(dx, dy int, writeMode bool) {
174 ox, oy := v.cx+v.ox, v.cy+v.oy
175 x, y := ox+dx, oy+dy
176
177 if y < 0 || y >= len(v.viewLines) {
178 v.moveCursor(dx, dy, writeMode)
179 return
180 }
181
182 // Removing newline.
183 if x < 0 {
184 var prevLen int
185 if y-1 >= 0 && y-1 < len(v.viewLines) {
186 prevLen = lineWidth(v.viewLines[y-1].line)
187 }
188
189 v.MoveCursor(prevLen, -1, writeMode)
190 return
191 }
192
193 line := v.viewLines[y].line
194 var col int
195 var prevCol int
196 for i := range line {
197 prevCol = col
198 col += runewidth.RuneWidth(line[i].chr)
199 if dx > 0 {
200 if x <= col {
201 x = col
202 break
203 }
204 continue
205 }
206
207 if x < col {
208 x = prevCol
209 break
210 }
211 }
212
213 v.moveCursor(x-ox, y-oy, writeMode)
214}
215
216func (v *View) moveCursor(dx, dy int, writeMode bool) {
217 maxX, maxY := v.Size()
218 cx, cy := v.cx+dx, v.cy+dy
219 x, y := v.ox+cx, v.oy+cy
220
221 var curLineWidth, prevLineWidth int
222 // get the width of the current line
223 curLineWidth = maxInt
224 if v.Wrap {
225 curLineWidth = maxX - 1
226 }
227
228 if !writeMode {
229 curLineWidth = 0
230 if y >= 0 && y < len(v.viewLines) {
231 curLineWidth = lineWidth(v.viewLines[y].line)
232 if v.Wrap && curLineWidth >= maxX {
233 curLineWidth = maxX - 1
234 }
235 }
236 }
237 // get the width of the previous line
238 prevLineWidth = 0
239 if y-1 >= 0 && y-1 < len(v.viewLines) {
240 prevLineWidth = lineWidth(v.viewLines[y-1].line)
241 }
242 // adjust cursor's x position and view's x origin
243 if x > curLineWidth { // move to next line
244 if dx > 0 { // horizontal movement
245 cy++
246 if writeMode || v.oy+cy < len(v.viewLines) {
247 if !v.Wrap {
248 v.ox = 0
249 }
250 v.cx = 0
251 }
252 } else { // vertical movement
253 if curLineWidth > 0 { // move cursor to the EOL
254 if v.Wrap {
255 v.cx = curLineWidth
256 } else {
257 ncx := curLineWidth - v.ox
258 if ncx < 0 {
259 v.ox += ncx
260 if v.ox < 0 {
261 v.ox = 0
262 }
263 v.cx = 0
264 } else {
265 v.cx = ncx
266 }
267 }
268 } else {
269 if writeMode || v.oy+cy < len(v.viewLines) {
270 if !v.Wrap {
271 v.ox = 0
272 }
273 v.cx = 0
274 }
275 }
276 }
277 } else if cx < 0 {
278 if !v.Wrap && v.ox > 0 { // move origin to the left
279 v.ox += cx
280 v.cx = 0
281 } else { // move to previous line
282 cy--
283 if prevLineWidth > 0 {
284 if !v.Wrap { // set origin so the EOL is visible
285 nox := prevLineWidth - maxX + 1
286 if nox < 0 {
287 nox = 0
288 }
289 v.ox = nox
290 }
291 v.cx = prevLineWidth
292 } else {
293 if !v.Wrap {
294 v.ox = 0
295 }
296 v.cx = 0
297 }
298 }
299 } else { // stay on the same line
300 if v.Wrap {
301 v.cx = cx
302 } else {
303 if cx >= maxX {
304 v.ox += cx - maxX + 1
305 v.cx = maxX
306 } else {
307 v.cx = cx
308 }
309 }
310 }
311
312 // adjust cursor's y position and view's y origin
313 if cy < 0 {
314 if v.oy > 0 {
315 v.oy--
316 }
317 } else if writeMode || v.oy+cy < len(v.viewLines) {
318 if cy >= maxY {
319 v.oy++
320 } else {
321 v.cy = cy
322 }
323 }
324}
325
326// writeRune writes a rune into the view's internal buffer, at the
327// position corresponding to the point (x, y). The length of the internal
328// buffer is increased if the point is out of bounds. Overwrite mode is
329// governed by the value of View.overwrite.
330func (v *View) writeRune(x, y int, ch rune) error {
331 v.tainted = true
332
333 x, y, err := v.realPosition(x, y)
334 if err != nil {
335 return err
336 }
337
338 if x < 0 || y < 0 {
339 return errors.New("invalid point")
340 }
341
342 if y >= len(v.lines) {
343 s := make([][]cell, y-len(v.lines)+1)
344 v.lines = append(v.lines, s...)
345 }
346
347 olen := len(v.lines[y])
348
349 var s []cell
350 if x >= len(v.lines[y]) {
351 s = make([]cell, x-len(v.lines[y])+1)
352 } else if !v.Overwrite {
353 s = make([]cell, 1)
354 }
355 v.lines[y] = append(v.lines[y], s...)
356
357 if !v.Overwrite || (v.Overwrite && x >= olen-1) {
358 copy(v.lines[y][x+1:], v.lines[y][x:])
359 }
360 v.lines[y][x] = cell{
361 fgColor: v.FgColor,
362 bgColor: v.BgColor,
363 chr: ch,
364 }
365
366 return nil
367}
368
369// deleteRune removes a rune from the view's internal buffer, at the
370// position corresponding to the point (x, y).
371// returns the amount of columns that where removed.
372func (v *View) deleteRune(x, y int) (int, error) {
373 v.tainted = true
374
375 x, y, err := v.realPosition(x, y)
376 if err != nil {
377 return 0, err
378 }
379
380 if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
381 return 0, errors.New("invalid point")
382 }
383
384 var tw int
385 for i := range v.lines[y] {
386 w := runewidth.RuneWidth(v.lines[y][i].chr)
387 tw += w
388 if tw > x {
389 v.lines[y] = append(v.lines[y][:i], v.lines[y][i+1:]...)
390 return w, nil
391 }
392
393 }
394
395 return 0, nil
396}
397
398// mergeLines merges the lines "y" and "y+1" if possible.
399func (v *View) mergeLines(y int) error {
400 v.tainted = true
401
402 _, y, err := v.realPosition(0, y)
403 if err != nil {
404 return err
405 }
406
407 if y < 0 || y >= len(v.lines) {
408 return errors.New("invalid point")
409 }
410
411 if y < len(v.lines)-1 { // otherwise we don't need to merge anything
412 v.lines[y] = append(v.lines[y], v.lines[y+1]...)
413 v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
414 }
415 return nil
416}
417
418// breakLine breaks a line of the internal buffer at the position corresponding
419// to the point (x, y).
420func (v *View) breakLine(x, y int) error {
421 v.tainted = true
422
423 x, y, err := v.realPosition(x, y)
424 if err != nil {
425 return err
426 }
427
428 if y < 0 || y >= len(v.lines) {
429 return errors.New("invalid point")
430 }
431
432 var left, right []cell
433 if x < len(v.lines[y]) { // break line
434 left = make([]cell, len(v.lines[y][:x]))
435 copy(left, v.lines[y][:x])
436 right = make([]cell, len(v.lines[y][x:]))
437 copy(right, v.lines[y][x:])
438 } else { // new empty line
439 left = v.lines[y]
440 }
441
442 lines := make([][]cell, len(v.lines)+1)
443 lines[y] = left
444 lines[y+1] = right
445 copy(lines, v.lines[:y])
446 copy(lines[y+2:], v.lines[y+1:])
447 v.lines = lines
448 return nil
449}