Store cursor and eventhandler in buffer instead of view

This is better design because the cursor and eventhandler are things the
buffer should know about directly but the view shouldn't. This should
make it easier to add persistent undo or location saving between
sessions (see #107).
This commit is contained in:
Zachary Yedidia
2016-05-22 15:01:02 -04:00
parent 6eeda5d21f
commit df684ec505
6 changed files with 92 additions and 87 deletions

View File

@@ -506,7 +506,7 @@ func (v *View) InsertSpace() bool {
v.Cursor.DeleteSelection() v.Cursor.DeleteSelection()
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
} }
v.eh.Insert(v.Cursor.Loc(), " ") v.Buf.Insert(v.Cursor.Loc(), " ")
v.Cursor.Right() v.Cursor.Right()
return true return true
} }
@@ -519,12 +519,12 @@ func (v *View) InsertEnter() bool {
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
} }
v.eh.Insert(v.Cursor.Loc(), "\n") v.Buf.Insert(v.Cursor.Loc(), "\n")
ws := GetLeadingWhitespace(v.Buf.Lines[v.Cursor.y]) ws := GetLeadingWhitespace(v.Buf.Lines[v.Cursor.y])
v.Cursor.Right() v.Cursor.Right()
if settings["autoindent"].(bool) { if settings["autoindent"].(bool) {
v.eh.Insert(v.Cursor.Loc(), ws) v.Buf.Insert(v.Cursor.Loc(), ws)
for i := 0; i < len(ws); i++ { for i := 0; i < len(ws); i++ {
v.Cursor.Right() v.Cursor.Right()
} }
@@ -556,14 +556,14 @@ func (v *View) Backspace() bool {
v.Cursor.SetLoc(loc - tabSize) v.Cursor.SetLoc(loc - tabSize)
cx, cy := v.Cursor.x, v.Cursor.y cx, cy := v.Cursor.x, v.Cursor.y
v.Cursor.SetLoc(loc) v.Cursor.SetLoc(loc)
v.eh.Remove(loc-tabSize, loc) v.Buf.Remove(loc-tabSize, loc)
v.Cursor.x, v.Cursor.y = cx, cy v.Cursor.x, v.Cursor.y = cx, cy
} else { } else {
v.Cursor.Left() v.Cursor.Left()
cx, cy := v.Cursor.x, v.Cursor.y cx, cy := v.Cursor.x, v.Cursor.y
v.Cursor.Right() v.Cursor.Right()
loc := v.Cursor.Loc() loc := v.Cursor.Loc()
v.eh.Remove(loc-1, loc) v.Buf.Remove(loc-1, loc)
v.Cursor.x, v.Cursor.y = cx, cy v.Cursor.x, v.Cursor.y = cx, cy
} }
} }
@@ -579,7 +579,7 @@ func (v *View) Delete() bool {
} else { } else {
loc := v.Cursor.Loc() loc := v.Cursor.Loc()
if loc < v.Buf.Len() { if loc < v.Buf.Len() {
v.eh.Remove(loc, loc+1) v.Buf.Remove(loc, loc+1)
} }
} }
return true return true
@@ -594,12 +594,12 @@ func (v *View) InsertTab() bool {
} }
if settings["tabsToSpaces"].(bool) { if settings["tabsToSpaces"].(bool) {
tabSize := int(settings["tabsize"].(float64)) tabSize := int(settings["tabsize"].(float64))
v.eh.Insert(v.Cursor.Loc(), Spaces(tabSize)) v.Buf.Insert(v.Cursor.Loc(), Spaces(tabSize))
for i := 0; i < tabSize; i++ { for i := 0; i < tabSize; i++ {
v.Cursor.Right() v.Cursor.Right()
} }
} else { } else {
v.eh.Insert(v.Cursor.Loc(), "\t") v.Buf.Insert(v.Cursor.Loc(), "\t")
v.Cursor.Right() v.Cursor.Right()
} }
return true return true
@@ -663,14 +663,14 @@ func (v *View) FindPrevious() bool {
// Undo undoes the last action // Undo undoes the last action
func (v *View) Undo() bool { func (v *View) Undo() bool {
v.eh.Undo() v.Buf.Undo()
messenger.Message("Undid action") messenger.Message("Undid action")
return true return true
} }
// Redo redoes the last action // Redo redoes the last action
func (v *View) Redo() bool { func (v *View) Redo() bool {
v.eh.Redo() v.Buf.Redo()
messenger.Message("Redid action") messenger.Message("Redid action")
return true return true
} }
@@ -722,7 +722,7 @@ func (v *View) Cut() bool {
// DuplicateLine duplicates the current line // DuplicateLine duplicates the current line
func (v *View) DuplicateLine() bool { func (v *View) DuplicateLine() bool {
v.Cursor.End() v.Cursor.End()
v.eh.Insert(v.Cursor.Loc(), "\n"+v.Buf.Lines[v.Cursor.y]) v.Buf.Insert(v.Cursor.Loc(), "\n"+v.Buf.Lines[v.Cursor.y])
v.Cursor.Right() v.Cursor.Right()
messenger.Message("Duplicated line") messenger.Message("Duplicated line")
return true return true
@@ -736,7 +736,7 @@ func (v *View) Paste() bool {
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
} }
clip, _ := clipboard.ReadAll() clip, _ := clipboard.ReadAll()
v.eh.Insert(v.Cursor.Loc(), clip) v.Buf.Insert(v.Cursor.Loc(), clip)
v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip)) v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
v.freshClip = false v.freshClip = false
messenger.Message("Pasted clipboard") messenger.Message("Pasted clipboard")

View File

@@ -10,9 +10,14 @@ import (
// It uses a rope to efficiently store the string and contains some // It uses a rope to efficiently store the string and contains some
// simple functions for saving and wrapper functions for modifying the rope // simple functions for saving and wrapper functions for modifying the rope
type Buffer struct { type Buffer struct {
// The eventhandler for undo/redo
*EventHandler
// Stores the text of the buffer // Stores the text of the buffer
r *rope.Rope r *rope.Rope
Cursor Cursor
// Path to the file on disk // Path to the file on disk
Path string Path string
// Name of the buffer on the status line // Name of the buffer on the status line
@@ -43,6 +48,15 @@ func NewBuffer(txt, path string) *Buffer {
b.Path = path b.Path = path
b.Name = path b.Name = path
// Put the cursor at the first spot
b.Cursor = Cursor{
x: 0,
y: 0,
Buf: b,
}
b.EventHandler = NewEventHandler(b)
b.Update() b.Update()
b.UpdateRules() b.UpdateRules()
@@ -84,8 +98,8 @@ func (b *Buffer) SaveAs(filename string) error {
return err return err
} }
// Insert a string into the rope // This directly inserts value at idx, bypassing all undo/redo
func (b *Buffer) Insert(idx int, value string) { func (b *Buffer) insert(idx int, value string) {
b.IsModified = true b.IsModified = true
b.r = b.r.Insert(idx, value) b.r = b.r.Insert(idx, value)
b.Update() b.Update()
@@ -93,7 +107,8 @@ func (b *Buffer) Insert(idx int, value string) {
// Remove a slice of the rope from start to end (exclusive) // Remove a slice of the rope from start to end (exclusive)
// Returns the string that was removed // Returns the string that was removed
func (b *Buffer) Remove(start, end int) string { // This directly removes from start to end from the buffer, bypassing all undo/redo
func (b *Buffer) remove(start, end int) string {
b.IsModified = true b.IsModified = true
if start < 0 { if start < 0 {
start = 0 start = 0

View File

@@ -162,7 +162,7 @@ func HandleCommand(input string, view *View) {
} }
if choice { if choice {
view.Cursor.DeleteSelection() view.Cursor.DeleteSelection()
view.eh.Insert(match[0], replace) view.Buf.Insert(match[0], replace)
view.Cursor.ResetSelection() view.Cursor.ResetSelection()
messenger.Reset() messenger.Reset()
} else { } else {
@@ -174,7 +174,7 @@ func HandleCommand(input string, view *View) {
continue continue
} }
} else { } else {
view.eh.Replace(match[0], match[1], replace) view.Buf.Replace(match[0], match[1], replace)
} }
} }
if !found { if !found {

View File

@@ -43,7 +43,7 @@ func ToCharPos(x, y int, buf *Buffer) int {
// is also simpler to use character indicies for other tasks such as // is also simpler to use character indicies for other tasks such as
// selection. // selection.
type Cursor struct { type Cursor struct {
v *View Buf *Buffer
// The cursor display location // The cursor display location
x int x int
@@ -64,7 +64,7 @@ type Cursor struct {
// and not x, y location // and not x, y location
// It's just a simple wrapper of FromCharPos // It's just a simple wrapper of FromCharPos
func (c *Cursor) SetLoc(loc int) { func (c *Cursor) SetLoc(loc int) {
c.x, c.y = FromCharPos(loc, c.v.Buf) c.x, c.y = FromCharPos(loc, c.Buf)
c.lastVisualX = c.GetVisualX() c.lastVisualX = c.GetVisualX()
} }
@@ -72,7 +72,7 @@ func (c *Cursor) SetLoc(loc int) {
// of x, y location // of x, y location
// It's just a simple wrapper of ToCharPos // It's just a simple wrapper of ToCharPos
func (c *Cursor) Loc() int { func (c *Cursor) Loc() int {
return ToCharPos(c.x, c.y, c.v.Buf) return ToCharPos(c.x, c.y, c.Buf)
} }
// ResetSelection resets the user's selection // ResetSelection resets the user's selection
@@ -89,12 +89,12 @@ func (c *Cursor) HasSelection() bool {
// DeleteSelection deletes the currently selected text // DeleteSelection deletes the currently selected text
func (c *Cursor) DeleteSelection() { func (c *Cursor) DeleteSelection() {
if c.curSelection[0] > c.curSelection[1] { if c.curSelection[0] > c.curSelection[1] {
c.v.eh.Remove(c.curSelection[1], c.curSelection[0]) c.Buf.Remove(c.curSelection[1], c.curSelection[0])
c.SetLoc(c.curSelection[1]) c.SetLoc(c.curSelection[1])
} else if c.GetSelection() == "" { } else if c.GetSelection() == "" {
return return
} else { } else {
c.v.eh.Remove(c.curSelection[0], c.curSelection[1]) c.Buf.Remove(c.curSelection[0], c.curSelection[1])
c.SetLoc(c.curSelection[0]) c.SetLoc(c.curSelection[0])
} }
} }
@@ -102,9 +102,9 @@ func (c *Cursor) DeleteSelection() {
// GetSelection returns the cursor's selection // GetSelection returns the cursor's selection
func (c *Cursor) GetSelection() string { func (c *Cursor) GetSelection() string {
if c.curSelection[0] > c.curSelection[1] { if c.curSelection[0] > c.curSelection[1] {
return c.v.Buf.Substr(c.curSelection[1], c.curSelection[0]) return c.Buf.Substr(c.curSelection[1], c.curSelection[0])
} }
return c.v.Buf.Substr(c.curSelection[0], c.curSelection[1]) return c.Buf.Substr(c.curSelection[0], c.curSelection[1])
} }
// SelectLine selects the current line // SelectLine selects the current line
@@ -112,7 +112,7 @@ func (c *Cursor) SelectLine() {
c.Start() c.Start()
c.curSelection[0] = c.Loc() c.curSelection[0] = c.Loc()
c.End() c.End()
if c.v.Buf.NumLines-1 > c.y { if c.Buf.NumLines-1 > c.y {
c.curSelection[1] = c.Loc() + 1 c.curSelection[1] = c.Loc() + 1
} else { } else {
c.curSelection[1] = c.Loc() c.curSelection[1] = c.Loc()
@@ -143,7 +143,7 @@ func (c *Cursor) AddLineToSelection() {
// SelectWord selects the word the cursor is currently on // SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() { func (c *Cursor) SelectWord() {
if len(c.v.Buf.Lines[c.y]) == 0 { if len(c.Buf.Lines[c.y]) == 0 {
return return
} }
@@ -161,14 +161,14 @@ func (c *Cursor) SelectWord() {
backward-- backward--
} }
c.curSelection[0] = ToCharPos(backward, c.y, c.v.Buf) c.curSelection[0] = ToCharPos(backward, c.y, c.Buf)
c.origSelection[0] = c.curSelection[0] c.origSelection[0] = c.curSelection[0]
for forward < Count(c.v.Buf.Lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) { for forward < Count(c.Buf.Lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++ forward++
} }
c.curSelection[1] = ToCharPos(forward, c.y, c.v.Buf) + 1 c.curSelection[1] = ToCharPos(forward, c.y, c.Buf) + 1
c.origSelection[1] = c.curSelection[1] c.origSelection[1] = c.curSelection[1]
c.SetLoc(c.curSelection[1]) c.SetLoc(c.curSelection[1])
} }
@@ -189,18 +189,18 @@ func (c *Cursor) AddWordToSelection() {
backward-- backward--
} }
c.curSelection[0] = ToCharPos(backward, c.y, c.v.Buf) c.curSelection[0] = ToCharPos(backward, c.y, c.Buf)
c.curSelection[1] = c.origSelection[1] c.curSelection[1] = c.origSelection[1]
} }
if loc > c.origSelection[1] { if loc > c.origSelection[1] {
forward := c.x forward := c.x
for forward < Count(c.v.Buf.Lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) { for forward < Count(c.Buf.Lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++ forward++
} }
c.curSelection[1] = ToCharPos(forward, c.y, c.v.Buf) + 1 c.curSelection[1] = ToCharPos(forward, c.y, c.Buf) + 1
c.curSelection[0] = c.origSelection[0] c.curSelection[0] = c.origSelection[0]
} }
@@ -222,13 +222,13 @@ func (c *Cursor) SelectTo(loc int) {
func (c *Cursor) WordRight() { func (c *Cursor) WordRight() {
c.Right() c.Right()
for IsWhitespace(c.RuneUnder(c.x)) { for IsWhitespace(c.RuneUnder(c.x)) {
if c.x == Count(c.v.Buf.Lines[c.y]) { if c.x == Count(c.Buf.Lines[c.y]) {
return return
} }
c.Right() c.Right()
} }
for !IsWhitespace(c.RuneUnder(c.x)) { for !IsWhitespace(c.RuneUnder(c.x)) {
if c.x == Count(c.v.Buf.Lines[c.y]) { if c.x == Count(c.Buf.Lines[c.y]) {
return return
} }
c.Right() c.Right()
@@ -255,7 +255,7 @@ func (c *Cursor) WordLeft() {
// RuneUnder returns the rune under the given x position // RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune { func (c *Cursor) RuneUnder(x int) rune {
line := []rune(c.v.Buf.Lines[c.y]) line := []rune(c.Buf.Lines[c.y])
if len(line) == 0 { if len(line) == 0 {
return '\n' return '\n'
} }
@@ -272,7 +272,7 @@ func (c *Cursor) Up() {
if c.y > 0 { if c.y > 0 {
c.y-- c.y--
runes := []rune(c.v.Buf.Lines[c.y]) runes := []rune(c.Buf.Lines[c.y])
c.x = c.GetCharPosInLine(c.y, c.lastVisualX) c.x = c.GetCharPosInLine(c.y, c.lastVisualX)
if c.x > len(runes) { if c.x > len(runes) {
c.x = len(runes) c.x = len(runes)
@@ -282,10 +282,10 @@ func (c *Cursor) Up() {
// Down moves the cursor down one line (if possible) // Down moves the cursor down one line (if possible)
func (c *Cursor) Down() { func (c *Cursor) Down() {
if c.y < c.v.Buf.NumLines-1 { if c.y < c.Buf.NumLines-1 {
c.y++ c.y++
runes := []rune(c.v.Buf.Lines[c.y]) runes := []rune(c.Buf.Lines[c.y])
c.x = c.GetCharPosInLine(c.y, c.lastVisualX) c.x = c.GetCharPosInLine(c.y, c.lastVisualX)
if c.x > len(runes) { if c.x > len(runes) {
c.x = len(runes) c.x = len(runes)
@@ -309,10 +309,10 @@ func (c *Cursor) Left() {
// Right moves the cursor right one cell (if possible) or to the next line if it is at the end // Right moves the cursor right one cell (if possible) or to the next line if it is at the end
func (c *Cursor) Right() { func (c *Cursor) Right() {
if c.Loc() == c.v.Buf.Len() { if c.Loc() == c.Buf.Len() {
return return
} }
if c.x < Count(c.v.Buf.Lines[c.y]) { if c.x < Count(c.Buf.Lines[c.y]) {
c.x++ c.x++
} else { } else {
c.Down() c.Down()
@@ -323,7 +323,7 @@ func (c *Cursor) Right() {
// End moves the cursor to the end of the line it is on // End moves the cursor to the end of the line it is on
func (c *Cursor) End() { func (c *Cursor) End() {
c.x = Count(c.v.Buf.Lines[c.y]) c.x = Count(c.Buf.Lines[c.y])
c.lastVisualX = c.GetVisualX() c.lastVisualX = c.GetVisualX()
} }
@@ -338,7 +338,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
// Get the tab size // Get the tab size
tabSize := int(settings["tabsize"].(float64)) tabSize := int(settings["tabsize"].(float64))
// This is the visual line -- every \t replaced with the correct number of spaces // This is the visual line -- every \t replaced with the correct number of spaces
visualLine := strings.Replace(c.v.Buf.Lines[lineNum], "\t", "\t"+Spaces(tabSize-1), -1) visualLine := strings.Replace(c.Buf.Lines[lineNum], "\t", "\t"+Spaces(tabSize-1), -1)
if visualPos > Count(visualLine) { if visualPos > Count(visualLine) {
visualPos = Count(visualLine) visualPos = Count(visualLine)
} }
@@ -351,7 +351,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
// GetVisualX returns the x value of the cursor in visual spaces // GetVisualX returns the x value of the cursor in visual spaces
func (c *Cursor) GetVisualX() int { func (c *Cursor) GetVisualX() int {
runes := []rune(c.v.Buf.Lines[c.y]) runes := []rune(c.Buf.Lines[c.y])
tabSize := int(settings["tabsize"].(float64)) tabSize := int(settings["tabsize"].(float64))
return c.x + NumOccurences(string(runes[:c.x]), '\t')*(tabSize-1) return c.x + NumOccurences(string(runes[:c.x]), '\t')*(tabSize-1)
} }
@@ -361,23 +361,13 @@ func (c *Cursor) GetVisualX() int {
func (c *Cursor) Relocate() { func (c *Cursor) Relocate() {
if c.y < 0 { if c.y < 0 {
c.y = 0 c.y = 0
} else if c.y >= c.v.Buf.NumLines { } else if c.y >= c.Buf.NumLines {
c.y = c.v.Buf.NumLines - 1 c.y = c.Buf.NumLines - 1
} }
if c.x < 0 { if c.x < 0 {
c.x = 0 c.x = 0
} else if c.x > Count(c.v.Buf.Lines[c.y]) { } else if c.x > Count(c.Buf.Lines[c.y]) {
c.x = Count(c.v.Buf.Lines[c.y]) c.x = Count(c.Buf.Lines[c.y])
}
}
// Display draws the cursor to the screen at the correct position
func (c *Cursor) Display() {
// Don't draw the cursor if it is out of the viewport or if it has a selection
if (c.y-c.v.Topline < 0 || c.y-c.v.Topline > c.v.height-1) || c.HasSelection() {
screen.HideCursor()
} else {
screen.ShowCursor(c.GetVisualX()+c.v.lineNumOffset-c.v.leftCol, c.y-c.v.Topline)
} }
} }

View File

@@ -27,9 +27,9 @@ type TextEvent struct {
// ExecuteTextEvent runs a text event // ExecuteTextEvent runs a text event
func ExecuteTextEvent(t *TextEvent, buf *Buffer) { func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
if t.eventType == TextEventInsert { if t.eventType == TextEventInsert {
buf.Insert(t.start, t.text) buf.insert(t.start, t.text)
} else if t.eventType == TextEventRemove { } else if t.eventType == TextEventRemove {
t.text = buf.Remove(t.start, t.end) t.text = buf.remove(t.start, t.end)
} }
} }
@@ -41,24 +41,24 @@ func UndoTextEvent(t *TextEvent, buf *Buffer) {
// EventHandler executes text manipulations and allows undoing and redoing // EventHandler executes text manipulations and allows undoing and redoing
type EventHandler struct { type EventHandler struct {
v *View buf *Buffer
undo *Stack undo *Stack
redo *Stack redo *Stack
} }
// NewEventHandler returns a new EventHandler // NewEventHandler returns a new EventHandler
func NewEventHandler(v *View) *EventHandler { func NewEventHandler(buf *Buffer) *EventHandler {
eh := new(EventHandler) eh := new(EventHandler)
eh.undo = new(Stack) eh.undo = new(Stack)
eh.redo = new(Stack) eh.redo = new(Stack)
eh.v = v eh.buf = buf
return eh return eh
} }
// Insert creates an insert text event and executes it // Insert creates an insert text event and executes it
func (eh *EventHandler) Insert(start int, text string) { func (eh *EventHandler) Insert(start int, text string) {
e := &TextEvent{ e := &TextEvent{
c: eh.v.Cursor, c: eh.buf.Cursor,
eventType: TextEventInsert, eventType: TextEventInsert,
text: text, text: text,
start: start, start: start,
@@ -71,7 +71,7 @@ func (eh *EventHandler) Insert(start int, text string) {
// Remove creates a remove text event and executes it // Remove creates a remove text event and executes it
func (eh *EventHandler) Remove(start, end int) { func (eh *EventHandler) Remove(start, end int) {
e := &TextEvent{ e := &TextEvent{
c: eh.v.Cursor, c: eh.buf.Cursor,
eventType: TextEventRemove, eventType: TextEventRemove,
start: start, start: start,
end: end, end: end,
@@ -92,7 +92,7 @@ func (eh *EventHandler) Execute(t *TextEvent) {
eh.redo = new(Stack) eh.redo = new(Stack)
} }
eh.undo.Push(t) eh.undo.Push(t)
ExecuteTextEvent(t, eh.v.Buf) ExecuteTextEvent(t, eh.buf)
} }
// Undo the first event in the undo stack // Undo the first event in the undo stack
@@ -135,12 +135,12 @@ func (eh *EventHandler) UndoOneEvent() {
te := t.(*TextEvent) te := t.(*TextEvent)
// Undo it // Undo it
// Modifies the text event // Modifies the text event
UndoTextEvent(te, eh.v.Buf) UndoTextEvent(te, eh.buf)
// Set the cursor in the right place // Set the cursor in the right place
teCursor := te.c teCursor := te.c
te.c = eh.v.Cursor te.c = eh.buf.Cursor
eh.v.Cursor = teCursor eh.buf.Cursor = teCursor
// Push it to the redo stack // Push it to the redo stack
eh.redo.Push(te) eh.redo.Push(te)
@@ -183,11 +183,11 @@ func (eh *EventHandler) RedoOneEvent() {
te := t.(*TextEvent) te := t.(*TextEvent)
// Modifies the text event // Modifies the text event
UndoTextEvent(te, eh.v.Buf) UndoTextEvent(te, eh.buf)
teCursor := te.c teCursor := te.c
te.c = eh.v.Cursor te.c = eh.buf.Cursor
eh.v.Cursor = teCursor eh.buf.Cursor = teCursor
eh.undo.Push(te) eh.undo.Push(te)
} }

View File

@@ -15,7 +15,8 @@ import (
// It stores information about the cursor, and the viewport // It stores information about the cursor, and the viewport
// that the user sees the buffer from. // that the user sees the buffer from.
type View struct { type View struct {
Cursor Cursor // A pointer to the buffer's cursor for ease of access
Cursor *Cursor
// The topmost line, used for vertical scrolling // The topmost line, used for vertical scrolling
Topline int Topline int
@@ -33,9 +34,6 @@ type View struct {
// How much to offset because of line numbers // How much to offset because of line numbers
lineNumOffset int lineNumOffset int
// The eventhandler for undo/redo
eh *EventHandler
// Holds the list of gutter messages // Holds the list of gutter messages
messages map[string][]GutterMessage messages map[string][]GutterMessage
@@ -90,8 +88,6 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
v.OpenBuffer(buf) v.OpenBuffer(buf)
v.eh = NewEventHandler(v)
v.messages = make(map[string][]GutterMessage) v.messages = make(map[string][]GutterMessage)
v.sline = Statusline{ v.sline = Statusline{
@@ -162,18 +158,12 @@ func (v *View) CanClose(msg string) bool {
// This resets the topline, event handler and cursor. // This resets the topline, event handler and cursor.
func (v *View) OpenBuffer(buf *Buffer) { func (v *View) OpenBuffer(buf *Buffer) {
v.Buf = buf v.Buf = buf
v.Cursor = &buf.Cursor
v.Topline = 0 v.Topline = 0
v.leftCol = 0 v.leftCol = 0
// Put the cursor at the first spot
v.Cursor = Cursor{
x: 0,
y: 0,
v: v,
}
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
v.messages = make(map[string][]GutterMessage) v.messages = make(map[string][]GutterMessage)
v.eh = NewEventHandler(v)
v.matches = Match(v) v.matches = Match(v)
// Set mouseReleased to true because we assume the mouse is not being pressed when // Set mouseReleased to true because we assume the mouse is not being pressed when
@@ -269,7 +259,7 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Cursor.DeleteSelection() v.Cursor.DeleteSelection()
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
} }
v.eh.Insert(v.Cursor.Loc(), string(e.Rune())) v.Buf.Insert(v.Cursor.Loc(), string(e.Rune()))
v.Cursor.Right() v.Cursor.Right()
} else { } else {
for key, action := range bindings { for key, action := range bindings {
@@ -293,7 +283,7 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Cursor.ResetSelection() v.Cursor.ResetSelection()
} }
clip := e.Text() clip := e.Text()
v.eh.Insert(v.Cursor.Loc(), clip) v.Buf.Insert(v.Cursor.Loc(), clip)
v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip)) v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
v.freshClip = false v.freshClip = false
case *tcell.EventMouse: case *tcell.EventMouse:
@@ -595,10 +585,20 @@ func (v *View) DisplayView() {
} }
} }
// DisplayCursor draws the current buffer's cursor to the screen
func (v *View) DisplayCursor() {
// Don't draw the cursor if it is out of the viewport or if it has a selection
if (v.Cursor.y-v.Topline < 0 || v.Cursor.y-v.Topline > v.height-1) || v.Cursor.HasSelection() {
screen.HideCursor()
} else {
screen.ShowCursor(v.Cursor.GetVisualX()+v.lineNumOffset-v.leftCol, v.Cursor.y-v.Topline)
}
}
// Display renders the view, the cursor, and statusline // Display renders the view, the cursor, and statusline
func (v *View) Display() { func (v *View) Display() {
v.DisplayView() v.DisplayView()
v.Cursor.Display() v.DisplayCursor()
if settings["statusline"].(bool) { if settings["statusline"].(bool) {
v.sline.Display() v.sline.Display()
} }