Files
zyedidia.micro/cmd/micro/buffer.go
Zachary Yedidia df684ec505 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).
2016-05-22 15:01:02 -04:00

140 lines
3.1 KiB
Go

package main
import (
"github.com/vinzmay/go-rope"
"io/ioutil"
"strings"
)
// Buffer stores the text for files that are loaded into the text editor
// It uses a rope to efficiently store the string and contains some
// simple functions for saving and wrapper functions for modifying the rope
type Buffer struct {
// The eventhandler for undo/redo
*EventHandler
// Stores the text of the buffer
r *rope.Rope
Cursor Cursor
// Path to the file on disk
Path string
// Name of the buffer on the status line
Name string
IsModified bool
// Provide efficient and easy access to text and lines so the rope String does not
// need to be constantly recalculated
// These variables are updated in the update() function
Lines []string
NumLines int
// Syntax highlighting rules
rules []SyntaxRule
// The buffer's filetype
FileType string
}
// NewBuffer creates a new buffer from `txt` with path and name `path`
func NewBuffer(txt, path string) *Buffer {
b := new(Buffer)
if txt == "" {
b.r = new(rope.Rope)
} else {
b.r = rope.New(txt)
}
b.Path = 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.UpdateRules()
return b
}
// UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes
func (b *Buffer) UpdateRules() {
b.rules, b.FileType = GetRules(b)
}
func (b *Buffer) String() string {
if b.r.Len() != 0 {
return b.r.String()
}
return ""
}
// Update fetches the string from the rope and updates the `text` and `lines` in the buffer
func (b *Buffer) Update() {
b.Lines = strings.Split(b.String(), "\n")
b.NumLines = len(b.Lines)
}
// Save saves the buffer to its default path
func (b *Buffer) Save() error {
return b.SaveAs(b.Path)
}
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
func (b *Buffer) SaveAs(filename string) error {
b.UpdateRules()
data := []byte(b.String())
err := ioutil.WriteFile(filename, data, 0644)
if err == nil {
b.IsModified = false
}
return err
}
// This directly inserts value at idx, bypassing all undo/redo
func (b *Buffer) insert(idx int, value string) {
b.IsModified = true
b.r = b.r.Insert(idx, value)
b.Update()
}
// Remove a slice of the rope from start to end (exclusive)
// Returns the string that was removed
// 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
if start < 0 {
start = 0
}
if end > b.Len() {
end = b.Len()
}
if start == end {
return ""
}
removed := b.Substr(start, end)
// The rope implenentation I am using wants indicies starting at 1 instead of 0
start++
end++
b.r = b.r.Delete(start, end-start)
b.Update()
return removed
}
// Substr returns the substring of the rope from start to end
func (b *Buffer) Substr(start, end int) string {
return b.r.Substr(start+1, end-start).String()
}
// Len gives the length of the buffer
func (b *Buffer) Len() int {
return b.r.Len()
}