]> git.lizzy.rs Git - micro.git/blob - cmd/micro/buffer.go
517896a8ad490140b8ba1a3e169cf233dd555b43
[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         NumLines int
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 func (b *Buffer) String() string {
64         if b.r.Len() != 0 {
65                 return b.r.String()
66         }
67         return ""
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         b.NumLines = len(b.Lines)
74 }
75
76 // Save saves the buffer to its default path
77 func (b *Buffer) Save() error {
78         return b.SaveAs(b.Path)
79 }
80
81 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
82 func (b *Buffer) SaveAs(filename string) error {
83         b.UpdateRules()
84         data := []byte(b.String())
85         err := ioutil.WriteFile(filename, data, 0644)
86         if err == nil {
87                 b.savedText = md5.Sum(data)
88                 b.netInsertions = 0
89         }
90         return err
91 }
92
93 // IsDirty returns whether or not the buffer has been modified compared to the one on disk
94 func (b *Buffer) IsDirty() bool {
95         if !b.dirtySinceLastCheck {
96                 return false
97         }
98         if b.netInsertions == 0 {
99                 isDirty := b.savedText != md5.Sum([]byte(b.String()))
100                 b.dirtySinceLastCheck = isDirty
101                 return isDirty
102         }
103         return true
104 }
105
106 // Insert a string into the rope
107 func (b *Buffer) Insert(idx int, value string) {
108         b.dirtySinceLastCheck = true
109         b.netInsertions += len(value)
110         b.r = b.r.Insert(idx, value)
111         b.Update()
112 }
113
114 // Remove a slice of the rope from start to end (exclusive)
115 // Returns the string that was removed
116 func (b *Buffer) Remove(start, end int) string {
117         b.dirtySinceLastCheck = true
118         b.netInsertions -= end - start
119         if start < 0 {
120                 start = 0
121         }
122         if end > b.Len() {
123                 end = b.Len()
124         }
125         removed := b.Substr(start, end)
126         // The rope implenentation I am using wants indicies starting at 1 instead of 0
127         start++
128         end++
129         b.r = b.r.Delete(start, end-start)
130         b.Update()
131         return removed
132 }
133
134 func (b *Buffer) Substr(start, end int) string {
135         return b.r.Substr(start+1, end-start).String()
136 }
137
138 // Len gives the length of the buffer
139 func (b *Buffer) Len() int {
140         return b.r.Len()
141 }