]> git.lizzy.rs Git - micro.git/blob - cmd/micro/buffer.go
1f3e8686b90ab6619ea0dda9f3c7c37497ad9d27
[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         lines []string
31
32         // Syntax highlighting rules
33         rules []SyntaxRule
34         // The buffer's filetype
35         filetype string
36 }
37
38 // NewBuffer creates a new buffer from `txt` with path and name `path`
39 func NewBuffer(txt, path string) *Buffer {
40         b := new(Buffer)
41         if txt == "" {
42                 b.r = new(rope.Rope)
43         } else {
44                 b.r = rope.New(txt)
45         }
46         b.path = path
47         b.name = path
48         b.savedText = md5.Sum([]byte(txt))
49
50         b.Update()
51         b.UpdateRules()
52
53         return b
54 }
55
56 // UpdateRules updates the syntax rules and filetype for this buffer
57 // This is called when the colorscheme changes
58 func (b *Buffer) UpdateRules() {
59         b.rules, b.filetype = GetRules(b)
60 }
61
62 func (b *Buffer) String() string {
63         text := ""
64         if b.r.Len() != 0 {
65                 text = b.r.String()
66         }
67         return text
68 }
69
70 // Update fetches the string from the rope and updates the `text` and `lines` in the buffer
71 func (b *Buffer) Update() {
72         b.lines = strings.Split(b.String(), "\n")
73 }
74
75 // Save saves the buffer to its default path
76 func (b *Buffer) Save() error {
77         return b.SaveAs(b.path)
78 }
79
80 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
81 func (b *Buffer) SaveAs(filename string) error {
82         b.UpdateRules()
83         data := []byte(b.String())
84         err := ioutil.WriteFile(filename, data, 0644)
85         if err == nil {
86                 b.savedText = md5.Sum(data)
87                 b.netInsertions = 0
88         }
89         return err
90 }
91
92 // IsDirty returns whether or not the buffer has been modified compared to the one on disk
93 func (b *Buffer) IsDirty() bool {
94         if !b.dirtySinceLastCheck {
95                 return false
96         }
97         if b.netInsertions == 0 {
98                 isDirty := b.savedText != md5.Sum([]byte(b.String()))
99                 b.dirtySinceLastCheck = isDirty
100                 return isDirty
101         }
102         return true
103 }
104
105 // Insert a string into the rope
106 func (b *Buffer) Insert(idx int, value string) {
107         b.dirtySinceLastCheck = true
108         b.netInsertions += len(value)
109         b.r = b.r.Insert(idx, value)
110         b.Update()
111 }
112
113 // Remove a slice of the rope from start to end (exclusive)
114 // Returns the string that was removed
115 func (b *Buffer) Remove(start, end int) string {
116         b.dirtySinceLastCheck = true
117         b.netInsertions -= end - start
118         if start < 0 {
119                 start = 0
120         }
121         if end > b.Len() {
122                 end = b.Len()
123         }
124         removed := b.r.Report(start+1, end-start)
125         // The rope implenentation I am using wants indicies starting at 1 instead of 0
126         start++
127         end++
128         b.r = b.r.Delete(start, end-start)
129         b.Update()
130         return removed
131 }
132
133 // Len gives the length of the buffer
134 func (b *Buffer) Len() int {
135         return b.r.Len()
136 }