edit.go

  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}