diff options
Diffstat (limited to 'vendor/github.com/jroimartin/gocui/edit.go')
-rw-r--r-- | vendor/github.com/jroimartin/gocui/edit.go | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/vendor/github.com/jroimartin/gocui/edit.go b/vendor/github.com/jroimartin/gocui/edit.go new file mode 100644 index 00000000..66d84c8d --- /dev/null +++ b/vendor/github.com/jroimartin/gocui/edit.go @@ -0,0 +1,341 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocui + +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) { + switch { + 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) + } +} + +// EditWrite writes a rune at the cursor position. +func (v *View) EditWrite(ch rune) { + v.writeRune(v.cx, v.cy, ch) + v.MoveCursor(1, 0, true) +} + +// 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) { + v.MoveCursor(-1, 0, true) + return + } + + maxX, _ := v.Size() + 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 { + v.MoveCursor(-1, 0, true) + } + } else { // wrapped line + v.deleteRune(len(v.viewLines[y-1].line)-1, v.cy-1) + v.MoveCursor(-1, 0, true) + } + } else { // middle/end of the line + v.deleteRune(v.cx-1, v.cy) + v.MoveCursor(-1, 0, true) + } + } else { + if x == len(v.viewLines[y].line) { // end of the line + v.mergeLines(v.cy) + } else { // start/middle of the line + v.deleteRune(v.cx, v.cy) + } + } +} + +// EditNewLine inserts a new line under the cursor. +func (v *View) EditNewLine() { + v.breakLine(v.cx, v.cy) + + y := v.oy + v.cy + if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) && + !(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) { + // new line at the end of the buffer or + // cursor is not at the beginning of a wrapped line + v.ox = 0 + v.cx = 0 + v.MoveCursor(0, 1, true) + } +} + +// 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) { + curLineWidth = len(v.viewLines[y].line) + if v.Wrap && curLineWidth >= maxX { + curLineWidth = maxX - 1 + } + } else { + curLineWidth = 0 + } + } + // get the width of the previous line + if y-1 >= 0 && y-1 < len(v.viewLines) { + 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 + if !v.Wrap { + v.ox = 0 + } + v.cx = 0 + cy++ + } 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 { + 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-- + } else { // move to previous line + 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 + } + cy-- + } + } else { // stay on the same line + if v.Wrap { + v.cx = cx + } else { + if cx >= maxX { + v.ox++ + } else { + v.cx = cx + } + } + } + + // adjust cursor's y position and view's y origin + if cy >= maxY { + v.oy++ + } else if cy < 0 { + if v.oy > 0 { + v.oy-- + } + } else { + v.cy = cy + } +} + +// 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]) + if x >= len(v.lines[y]) { + s := make([]cell, x-len(v.lines[y])+1) + v.lines[y] = append(v.lines[y], s...) + } + + c := cell{ + fgColor: v.FgColor, + bgColor: v.BgColor, + } + if !v.Overwrite || (v.Overwrite && x >= olen-1) { + c.chr = '\x00' + v.lines[y] = append(v.lines[y], c) + copy(v.lines[y][x+1:], v.lines[y][x:]) + } + c.chr = ch + v.lines[y][x] = c + 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 +} |