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
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}