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 "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 }
57}
58
59// EditWrite writes a rune at the cursor position.
60func (v *View) EditWrite(ch rune) {
61 v.writeRune(v.cx, v.cy, ch)
62 v.MoveCursor(runewidth.RuneWidth(ch), 0, true)
63}
64
65// EditDelete deletes a rune at the cursor position. back determines the
66// direction.
67func (v *View) EditDelete(back bool) {
68 x, y := v.ox+v.cx, v.oy+v.cy
69 if y < 0 {
70 return
71 } else if y >= len(v.viewLines) {
72 v.MoveCursor(-1, 0, true)
73 return
74 }
75
76 maxX, _ := v.Size()
77 if back {
78 if x == 0 { // start of the line
79 if y < 1 {
80 return
81 }
82
83 var maxPrevWidth int
84 if v.Wrap {
85 maxPrevWidth = maxX
86 } else {
87 maxPrevWidth = maxInt
88 }
89
90 if v.viewLines[y].linesX == 0 { // regular line
91 v.mergeLines(v.cy - 1)
92 if len(v.viewLines[y-1].line) < maxPrevWidth {
93 v.MoveCursor(-1, 0, true)
94 }
95 } else { // wrapped line
96 ch, _ := v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
97 v.MoveCursor(0-runewidth.RuneWidth(ch), 0, true)
98 }
99 } else { // middle/end of the line
100 ch, _ := v.deleteRune(v.cx-1, v.cy)
101 v.MoveCursor(0-runewidth.RuneWidth(ch), 0, true)
102 }
103 } else {
104 if x == len(v.viewLines[y].line) { // end of the line
105 v.mergeLines(v.cy)
106 } else { // start/middle of the line
107 v.deleteRune(v.cx, v.cy)
108 }
109 }
110}
111
112// EditNewLine inserts a new line under the cursor.
113func (v *View) EditNewLine() {
114 v.breakLine(v.cx, v.cy)
115 v.ox = 0
116 v.cx = 0
117 v.MoveCursor(0, 1, true)
118}
119
120// MoveCursor moves the cursor taking into account the width of the line/view,
121// displacing the origin if necessary.
122func (v *View) MoveCursor(dx, dy int, writeMode bool) {
123 maxX, maxY := v.Size()
124 cx, cy := v.cx+dx, v.cy+dy
125 x, y := v.ox+cx, v.oy+cy
126
127 var curLineWidth, prevLineWidth int
128 // get the width of the current line
129 if writeMode {
130 if v.Wrap {
131 curLineWidth = maxX - 1
132 } else {
133 curLineWidth = maxInt
134 }
135 } else {
136 if y >= 0 && y < len(v.viewLines) {
137 curLineWidth = len(v.viewLines[y].line)
138 if v.Wrap && curLineWidth >= maxX {
139 curLineWidth = maxX - 1
140 }
141 } else {
142 curLineWidth = 0
143 }
144 }
145 // get the width of the previous line
146 if y-1 >= 0 && y-1 < len(v.viewLines) {
147 prevLineWidth = len(v.viewLines[y-1].line)
148 } else {
149 prevLineWidth = 0
150 }
151
152 // adjust cursor's x position and view's x origin
153 if x > curLineWidth { // move to next line
154 if dx > 0 { // horizontal movement
155 cy++
156 if writeMode || v.oy+cy < len(v.viewLines) {
157 if !v.Wrap {
158 v.ox = 0
159 }
160 v.cx = 0
161 }
162 } else { // vertical movement
163 if curLineWidth > 0 { // move cursor to the EOL
164 if v.Wrap {
165 v.cx = curLineWidth
166 } else {
167 ncx := curLineWidth - v.ox
168 if ncx < 0 {
169 v.ox += ncx
170 if v.ox < 0 {
171 v.ox = 0
172 }
173 v.cx = 0
174 } else {
175 v.cx = ncx
176 }
177 }
178 } else {
179 if writeMode || v.oy+cy < len(v.viewLines) {
180 if !v.Wrap {
181 v.ox = 0
182 }
183 v.cx = 0
184 }
185 }
186 }
187 } else if cx < 0 {
188 if !v.Wrap && v.ox > 0 { // move origin to the left
189 v.ox += cx
190 v.cx = 0
191 } else { // move to previous line
192 cy--
193 if prevLineWidth > 0 {
194 if !v.Wrap { // set origin so the EOL is visible
195 nox := prevLineWidth - maxX + 1
196 if nox < 0 {
197 v.ox = 0
198 } else {
199 v.ox = nox
200 }
201 }
202 v.cx = prevLineWidth
203 } else {
204 if !v.Wrap {
205 v.ox = 0
206 }
207 v.cx = 0
208 }
209 }
210 } else { // stay on the same line
211 if v.Wrap {
212 v.cx = cx
213 } else {
214 if cx >= maxX {
215 v.ox += cx - maxX + 1
216 v.cx = maxX
217 } else {
218 v.cx = cx
219 }
220 }
221 }
222
223 // adjust cursor's y position and view's y origin
224 if cy < 0 {
225 if v.oy > 0 {
226 v.oy--
227 }
228 } else if writeMode || v.oy+cy < len(v.viewLines) {
229 if cy >= maxY {
230 v.oy++
231 } else {
232 v.cy = cy
233 }
234 }
235}
236
237// writeRune writes a rune into the view's internal buffer, at the
238// position corresponding to the point (x, y). The length of the internal
239// buffer is increased if the point is out of bounds. Overwrite mode is
240// governed by the value of View.overwrite.
241func (v *View) writeRune(x, y int, ch rune) error {
242 v.tainted = true
243
244 x, y, err := v.realPosition(x, y)
245 if err != nil {
246 return err
247 }
248
249 if x < 0 || y < 0 {
250 return errors.New("invalid point")
251 }
252
253 if y >= len(v.lines) {
254 s := make([][]cell, y-len(v.lines)+1)
255 v.lines = append(v.lines, s...)
256 }
257
258 olen := len(v.lines[y])
259
260 var s []cell
261 if x >= len(v.lines[y]) {
262 s = make([]cell, x-len(v.lines[y])+1)
263 } else if !v.Overwrite {
264 s = make([]cell, 1)
265 }
266 v.lines[y] = append(v.lines[y], s...)
267
268 if !v.Overwrite || (v.Overwrite && x >= olen-1) {
269 copy(v.lines[y][x+1:], v.lines[y][x:])
270 }
271 v.lines[y][x] = cell{
272 fgColor: v.FgColor,
273 bgColor: v.BgColor,
274 chr: ch,
275 }
276
277 return nil
278}
279
280// deleteRune removes a rune from the view's internal buffer, at the
281// position corresponding to the point (x, y).
282func (v *View) deleteRune(x, y int) (ch rune, err error) {
283 v.tainted = true
284
285 x, y, err = v.realPosition(x, y)
286 if err != nil {
287 return 0, err
288 }
289
290 if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
291 return 0, errors.New("invalid point")
292 }
293 chx := v.lines[y][x]
294 v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
295 return chx.chr, nil
296}
297
298// mergeLines merges the lines "y" and "y+1" if possible.
299func (v *View) mergeLines(y int) error {
300 v.tainted = true
301
302 _, y, err := v.realPosition(0, y)
303 if err != nil {
304 return err
305 }
306
307 if y < 0 || y >= len(v.lines) {
308 return errors.New("invalid point")
309 }
310
311 if y < len(v.lines)-1 { // otherwise we don't need to merge anything
312 v.lines[y] = append(v.lines[y], v.lines[y+1]...)
313 v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
314 }
315 return nil
316}
317
318// breakLine breaks a line of the internal buffer at the position corresponding
319// to the point (x, y).
320func (v *View) breakLine(x, y int) error {
321 v.tainted = true
322
323 x, y, err := v.realPosition(x, y)
324 if err != nil {
325 return err
326 }
327
328 if y < 0 || y >= len(v.lines) {
329 return errors.New("invalid point")
330 }
331
332 var left, right []cell
333 if x < len(v.lines[y]) { // break line
334 left = make([]cell, len(v.lines[y][:x]))
335 copy(left, v.lines[y][:x])
336 right = make([]cell, len(v.lines[y][x:]))
337 copy(right, v.lines[y][x:])
338 } else { // new empty line
339 left = v.lines[y]
340 }
341
342 lines := make([][]cell, len(v.lines)+1)
343 lines[y] = left
344 lines[y+1] = right
345 copy(lines, v.lines[:y])
346 copy(lines[y+2:], v.lines[y+1:])
347 v.lines = lines
348 return nil
349}