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