// 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
+ Path string
// Name of the buffer on the status line
- name string
+ Name string
- // This is the text stored every time the buffer is saved to check if the buffer is modified
- savedText string
- netInsertions int
- dirtySinceLastCheck bool
+ 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
- text string
- lines []string
+ Lines []string
+ NumLines int
// Syntax highlighting rules
rules []SyntaxRule
// The buffer's filetype
- filetype string
+ FileType string
}
// NewBuffer creates a new buffer from `txt` with path and name `path`
} else {
b.r = rope.New(txt)
}
- b.path = path
- b.name = path
- b.savedText = 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()
// 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)
+ 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() {
- if b.r.Len() == 0 {
- b.text = ""
- } else {
- b.text = b.r.String()
- }
- b.lines = strings.Split(b.text, "\n")
+ 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)
+ 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()
- err := ioutil.WriteFile(filename, []byte(b.text), 0644)
+ data := []byte(b.String())
+ err := ioutil.WriteFile(filename, data, 0644)
if err == nil {
- b.savedText = b.text
- b.netInsertions = 0
+ b.IsModified = false
}
return err
}
-// IsDirty returns whether or not the buffer has been modified compared to the one on disk
-func (b *Buffer) IsDirty() bool {
- if !b.dirtySinceLastCheck {
- return false
- }
- if b.netInsertions == 0 {
- isDirty := b.savedText != b.text
- b.dirtySinceLastCheck = isDirty
- return isDirty
- }
- return true
-}
-
-// Insert a string into the rope
-func (b *Buffer) Insert(idx int, value string) {
- b.dirtySinceLastCheck = true
- b.netInsertions += len(value)
+// 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
-func (b *Buffer) Remove(start, end int) string {
- b.dirtySinceLastCheck = true
- b.netInsertions -= end - start
+// 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()
}
- removed := b.text[start:end]
+ 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++
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()