]> git.lizzy.rs Git - micro.git/blob - cmd/micro/buffer.go
0879c0ec1f39c6b27c06b47d26b380c9a4fdc037
[micro.git] / cmd / micro / buffer.go
1 package main
2
3 import (
4         "crypto/md5"
5         "github.com/vinzmay/go-rope"
6         "io/ioutil"
7         "strings"
8 )
9
10 // Buffer stores the text for files that are loaded into the text editor
11 // It uses a rope to efficiently store the string and contains some
12 // simple functions for saving and wrapper functions for modifying the rope
13 type Buffer struct {
14         // Stores the text of the buffer
15         r *rope.Rope
16
17         // Path to the file on disk
18         path string
19         // Name of the buffer on the status line
20         name string
21
22         // This is the text stored every time the buffer is saved to check if the buffer is modified
23         savedText           [16]byte
24         netInsertions       int
25         dirtySinceLastCheck bool
26
27         // Provide efficient and easy access to text and lines so the rope String does not
28         // need to be constantly recalculated
29         // These variables are updated in the update() function
30         text  string
31         lines []string
32
33         // Syntax highlighting rules
34         rules []SyntaxRule
35         // The buffer's filetype
36         filetype string
37 }
38
39 // NewBuffer creates a new buffer from `txt` with path and name `path`
40 func NewBuffer(txt, path string) *Buffer {
41         b := new(Buffer)
42         if txt == "" {
43                 b.r = new(rope.Rope)
44         } else {
45                 b.r = rope.New(txt)
46         }
47         b.path = path
48         b.name = path
49         b.savedText = md5.Sum([]byte(txt))
50
51         b.Update()
52         b.UpdateRules()
53
54         return b
55 }
56
57 // UpdateRules updates the syntax rules and filetype for this buffer
58 // This is called when the colorscheme changes
59 func (b *Buffer) UpdateRules() {
60         b.rules, b.filetype = GetRules(b)
61 }
62
63 // Update fetches the string from the rope and updates the `text` and `lines` in the buffer
64 func (b *Buffer) Update() {
65         if b.r.Len() == 0 {
66                 b.text = ""
67         } else {
68                 b.text = b.r.String()
69         }
70         b.lines = strings.Split(b.text, "\n")
71 }
72
73 // Save saves the buffer to its default path
74 func (b *Buffer) Save() error {
75         return b.SaveAs(b.path)
76 }
77
78 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
79 func (b *Buffer) SaveAs(filename string) error {
80         b.UpdateRules()
81         data := []byte(b.text)
82         err := ioutil.WriteFile(filename, data, 0644)
83         if err == nil {
84                 b.savedText = md5.Sum(data)
85                 b.netInsertions = 0
86         }
87         return err
88 }
89
90 // IsDirty returns whether or not the buffer has been modified compared to the one on disk
91 func (b *Buffer) IsDirty() bool {
92         if !b.dirtySinceLastCheck {
93                 return false
94         }
95         if b.netInsertions == 0 {
96                 isDirty := b.savedText != md5.Sum([]byte(b.text))
97                 b.dirtySinceLastCheck = isDirty
98                 return isDirty
99         }
100         return true
101 }
102
103 func (b *Buffer) Lines() []string {
104         return strings.Split(b.text, "\n")
105 }
106
107 // Insert a string into the rope
108 func (b *Buffer) Insert(idx int, value string) {
109         b.dirtySinceLastCheck = true
110         b.netInsertions += len(value)
111         b.r = b.r.Insert(idx, value)
112         b.Update()
113 }
114
115 // Remove a slice of the rope from start to end (exclusive)
116 // Returns the string that was removed
117 func (b *Buffer) Remove(start, end int) string {
118         b.dirtySinceLastCheck = true
119         b.netInsertions -= end - start
120         if start < 0 {
121                 start = 0
122         }
123         if end > b.Len() {
124                 end = b.Len()
125         }
126         removed := b.text[start:end]
127         // The rope implenentation I am using wants indicies starting at 1 instead of 0
128         start++
129         end++
130         b.r = b.r.Delete(start, end-start)
131         b.Update()
132         return removed
133 }
134
135 // Len gives the length of the buffer
136 func (b *Buffer) Len() int {
137         return b.r.Len()
138 }