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}