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