]> git.lizzy.rs Git - micro.git/blob - cmd/micro/buffer.go
Rewrite gofmt and goimports as plugins
[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         text := ""
65         if b.r.Len() != 0 {
66                 text = b.r.String()
67         }
68         return text
69 }
70
71 // Update fetches the string from the rope and updates the `text` and `lines` in the buffer
72 func (b *Buffer) Update() {
73         b.Lines = strings.Split(b.String(), "\n")
74         b.NumLines = len(b.Lines)
75 }
76
77 // Save saves the buffer to its default path
78 func (b *Buffer) Save() error {
79         return b.SaveAs(b.Path)
80 }
81
82 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
83 func (b *Buffer) SaveAs(filename string) error {
84         b.UpdateRules()
85         data := []byte(b.String())
86         err := ioutil.WriteFile(filename, data, 0644)
87         if err == nil {
88                 b.savedText = md5.Sum(data)
89                 b.netInsertions = 0
90         }
91         return err
92 }
93
94 // IsDirty returns whether or not the buffer has been modified compared to the one on disk
95 func (b *Buffer) IsDirty() bool {
96         if !b.dirtySinceLastCheck {
97                 return false
98         }
99         if b.netInsertions == 0 {
100                 isDirty := b.savedText != md5.Sum([]byte(b.String()))
101                 b.dirtySinceLastCheck = isDirty
102                 return isDirty
103         }
104         return true
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.r.Report(start+1, end-start)
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 }