gocui/edit.go

345 lines
7.5 KiB
Go
Raw Permalink Normal View History

// Copyright 2014 The gocui Authors. All rights reserved.
2014-01-22 23:44:42 +01:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2014-01-22 23:44:08 +01:00
package gocui
2017-08-15 21:13:34 +02:00
import "errors"
const maxInt = int(^uint(0) >> 1)
// Editor interface must be satisfied by gocui editors.
type Editor interface {
Edit(v *View, key Key, ch rune, mod Modifier)
}
// The EditorFunc type is an adapter to allow the use of ordinary functions as
// Editors. If f is a function with the appropriate signature, EditorFunc(f)
// is an Editor object that calls f.
type EditorFunc func(v *View, key Key, ch rune, mod Modifier)
// Edit calls f(v, key, ch, mod)
func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) {
f(v, key, ch, mod)
}
// DefaultEditor is the default editor.
var DefaultEditor Editor = EditorFunc(simpleEditor)
// simpleEditor is used as the default gocui editor.
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
2015-02-13 18:40:45 +01:00
switch {
2015-02-24 00:26:26 +01:00
case ch != 0 && mod == 0:
v.EditWrite(ch)
case key == KeySpace:
v.EditWrite(' ')
case key == KeyBackspace || key == KeyBackspace2:
v.EditDelete(true)
case key == KeyDelete:
v.EditDelete(false)
case key == KeyInsert:
v.Overwrite = !v.Overwrite
case key == KeyEnter:
v.EditNewLine()
case key == KeyArrowDown:
v.MoveCursor(0, 1, false)
case key == KeyArrowUp:
v.MoveCursor(0, -1, false)
case key == KeyArrowLeft:
v.MoveCursor(-1, 0, false)
case key == KeyArrowRight:
v.MoveCursor(1, 0, false)
2015-02-13 18:40:45 +01:00
}
}
2015-02-24 00:26:26 +01:00
// EditWrite writes a rune at the cursor position.
func (v *View) EditWrite(ch rune) {
2014-01-22 23:44:08 +01:00
v.writeRune(v.cx, v.cy, ch)
2015-02-24 00:26:26 +01:00
v.MoveCursor(1, 0, true)
2014-01-22 23:44:08 +01:00
}
2015-02-24 00:26:26 +01:00
// EditDelete deletes a rune at the cursor position. back determines the
// direction.
func (v *View) EditDelete(back bool) {
x, y := v.ox+v.cx, v.oy+v.cy
if y < 0 {
return
} else if y >= len(v.viewLines) {
2015-02-24 00:26:26 +01:00
v.MoveCursor(-1, 0, true)
return
}
maxX, _ := v.Size()
2014-01-22 23:44:08 +01:00
if back {
if x == 0 { // start of the line
if y < 1 {
return
}
var maxPrevWidth int
if v.Wrap {
maxPrevWidth = maxX
} else {
maxPrevWidth = maxInt
}
if v.viewLines[y].linesX == 0 { // regular line
v.mergeLines(v.cy - 1)
if len(v.viewLines[y-1].line) < maxPrevWidth {
2015-02-24 00:26:26 +01:00
v.MoveCursor(-1, 0, true)
}
} else { // wrapped line
v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1)
2015-02-24 00:26:26 +01:00
v.MoveCursor(-1, 0, true)
}
2015-02-23 11:13:25 +01:00
} else { // middle/end of the line
v.deleteRune(v.cx-1, v.cy)
2015-02-24 00:26:26 +01:00
v.MoveCursor(-1, 0, true)
2014-01-22 23:44:08 +01:00
}
2015-02-23 11:09:31 +01:00
} else {
if x == len(v.viewLines[y].line) { // end of the line
v.mergeLines(v.cy)
2015-02-23 11:13:25 +01:00
} else { // start/middle of the line
2015-02-23 11:09:31 +01:00
v.deleteRune(v.cx, v.cy)
}
2014-01-22 23:44:08 +01:00
}
}
2015-02-24 00:26:26 +01:00
// EditNewLine inserts a new line under the cursor.
func (v *View) EditNewLine() {
2015-02-14 20:17:44 +01:00
v.breakLine(v.cx, v.cy)
2017-08-18 00:19:02 +02:00
v.ox = 0
v.cx = 0
v.MoveCursor(0, 1, true)
}
2015-02-24 00:26:26 +01:00
// MoveCursor moves the cursor taking into account the width of the line/view,
// displacing the origin if necessary.
func (v *View) MoveCursor(dx, dy int, writeMode bool) {
maxX, maxY := v.Size()
cx, cy := v.cx+dx, v.cy+dy
x, y := v.ox+cx, v.oy+cy
var curLineWidth, prevLineWidth int
// get the width of the current line
if writeMode {
if v.Wrap {
curLineWidth = maxX - 1
} else {
curLineWidth = maxInt
}
} else {
if y >= 0 && y < len(v.viewLines) {
2016-10-11 10:18:38 +02:00
curLineWidth = len(v.viewLines[y].line)
if v.Wrap && curLineWidth >= maxX {
curLineWidth = maxX - 1
}
} else {
curLineWidth = 0
2014-01-22 23:44:08 +01:00
}
}
// get the width of the previous line
if y-1 >= 0 && y-1 < len(v.viewLines) {
2016-10-11 10:18:38 +02:00
prevLineWidth = len(v.viewLines[y-1].line)
} else {
prevLineWidth = 0
}
// adjust cursor's x position and view's x origin
if x > curLineWidth { // move to next line
if dx > 0 { // horizontal movement
cy++
2017-08-19 00:06:54 +02:00
if writeMode || v.oy+cy < len(v.viewLines) {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
} else { // vertical movement
if curLineWidth > 0 { // move cursor to the EOL
if v.Wrap {
v.cx = curLineWidth
} else {
ncx := curLineWidth - v.ox
if ncx < 0 {
v.ox += ncx
if v.ox < 0 {
v.ox = 0
}
v.cx = 0
} else {
v.cx = ncx
}
}
} else {
2017-08-19 00:06:54 +02:00
if writeMode || v.oy+cy < len(v.viewLines) {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
}
}
} else if cx < 0 {
if !v.Wrap && v.ox > 0 { // move origin to the left
v.ox += cx
v.cx = 0
} else { // move to previous line
2017-08-19 00:06:54 +02:00
cy--
if prevLineWidth > 0 {
if !v.Wrap { // set origin so the EOL is visible
nox := prevLineWidth - maxX + 1
if nox < 0 {
v.ox = 0
} else {
v.ox = nox
}
}
v.cx = prevLineWidth
} else {
if !v.Wrap {
v.ox = 0
}
v.cx = 0
}
}
} else { // stay on the same line
if v.Wrap {
v.cx = cx
} else {
if cx >= maxX {
v.ox += cx - maxX + 1
v.cx = maxX
} else {
v.cx = cx
}
}
2014-01-23 21:26:53 +01:00
}
// adjust cursor's y position and view's y origin
2017-08-19 00:06:54 +02:00
if cy < 0 {
if v.oy > 0 {
v.oy--
}
2017-08-19 00:06:54 +02:00
} else if writeMode || v.oy+cy < len(v.viewLines) {
if cy >= maxY {
v.oy++
} else {
v.cy = cy
}
2014-01-23 21:26:53 +01:00
}
2014-01-22 23:44:08 +01:00
}
// writeRune writes a rune into the view's internal buffer, at the
// position corresponding to the point (x, y). The length of the internal
// buffer is increased if the point is out of bounds. Overwrite mode is
// governed by the value of View.overwrite.
func (v *View) writeRune(x, y int, ch rune) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if x < 0 || y < 0 {
return errors.New("invalid point")
}
if y >= len(v.lines) {
s := make([][]cell, y-len(v.lines)+1)
v.lines = append(v.lines, s...)
}
olen := len(v.lines[y])
2017-08-18 00:19:02 +02:00
2017-08-18 09:21:00 +02:00
var s []cell
if x >= len(v.lines[y]) {
2017-08-18 09:21:00 +02:00
s = make([]cell, x-len(v.lines[y])+1)
2017-08-18 00:19:02 +02:00
} else if !v.Overwrite {
2017-08-18 09:21:00 +02:00
s = make([]cell, 1)
}
2017-08-18 09:21:00 +02:00
v.lines[y] = append(v.lines[y], s...)
if !v.Overwrite || (v.Overwrite && x >= olen-1) {
copy(v.lines[y][x+1:], v.lines[y][x:])
}
2017-08-18 09:21:00 +02:00
v.lines[y][x] = cell{
fgColor: v.FgColor,
bgColor: v.BgColor,
chr: ch,
}
2017-08-18 00:19:02 +02:00
return nil
}
// deleteRune removes a rune from the view's internal buffer, at the
// position corresponding to the point (x, y).
func (v *View) deleteRune(x, y int) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
return errors.New("invalid point")
}
v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
return nil
}
// mergeLines merges the lines "y" and "y+1" if possible.
func (v *View) mergeLines(y int) error {
v.tainted = true
_, y, err := v.realPosition(0, y)
if err != nil {
return err
}
if y < 0 || y >= len(v.lines) {
return errors.New("invalid point")
}
if y < len(v.lines)-1 { // otherwise we don't need to merge anything
v.lines[y] = append(v.lines[y], v.lines[y+1]...)
v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
}
return nil
}
// breakLine breaks a line of the internal buffer at the position corresponding
// to the point (x, y).
func (v *View) breakLine(x, y int) error {
v.tainted = true
x, y, err := v.realPosition(x, y)
if err != nil {
return err
}
if y < 0 || y >= len(v.lines) {
return errors.New("invalid point")
}
var left, right []cell
if x < len(v.lines[y]) { // break line
left = make([]cell, len(v.lines[y][:x]))
copy(left, v.lines[y][:x])
right = make([]cell, len(v.lines[y][x:]))
copy(right, v.lines[y][x:])
} else { // new empty line
left = v.lines[y]
}
lines := make([][]cell, len(v.lines)+1)
lines[y] = left
lines[y+1] = right
copy(lines, v.lines[:y])
copy(lines[y+2:], v.lines[y+1:])
v.lines = lines
return nil
}