11 "github.com/vinzmay/go-rope"
14 // Buffer stores the text for files that are loaded into the text editor
15 // It uses a rope to efficiently store the string and contains some
16 // simple functions for saving and wrapper functions for modifying the rope
18 // The eventhandler for undo/redo
21 // Stores the text of the buffer
26 // Path to the file on disk
28 // Name of the buffer on the status line
33 // Stores the last modification time of the file the buffer is pointing to
36 // Provide efficient and easy access to text and lines so the rope String does not
37 // need to be constantly recalculated
38 // These variables are updated in the update() function
42 // Syntax highlighting rules
44 // The buffer's filetype
48 // The SerializedBuffer holds the types that get serialized when a buffer is saved
49 type SerializedBuffer struct {
50 EventHandler *EventHandler
55 // NewBuffer creates a new buffer from `txt` with path and name `path`
56 func NewBuffer(txt, path string) *Buffer {
66 b.ModTime, _ = GetModTime(b.Path)
68 b.EventHandler = NewEventHandler(b)
73 if _, err := os.Stat(configDir + "/buffers/"); os.IsNotExist(err) {
74 os.Mkdir(configDir+"/buffers/", os.ModePerm)
77 // Put the cursor at the first spot
84 if settings["savecursor"].(bool) || settings["saveundo"].(bool) {
85 absPath, _ := filepath.Abs(b.Path)
86 file, err := os.Open(configDir + "/buffers/" + EscapePath(absPath))
88 var buffer SerializedBuffer
89 decoder := gob.NewDecoder(file)
90 gob.Register(TextEvent{})
91 err = decoder.Decode(&buffer)
93 TermMessage(err.Error())
95 if settings["savecursor"].(bool) {
96 b.Cursor = buffer.Cursor
101 if settings["saveundo"].(bool) {
102 // We should only use last time's eventhandler if the file wasn't by someone else in the meantime
103 if b.ModTime == buffer.ModTime {
104 b.EventHandler = buffer.EventHandler
105 b.EventHandler.buf = b
115 // UpdateRules updates the syntax rules and filetype for this buffer
116 // This is called when the colorscheme changes
117 func (b *Buffer) UpdateRules() {
118 b.rules, b.FileType = GetRules(b)
121 func (b *Buffer) String() string {
128 // CheckModTime makes sure that the file this buffer points to hasn't been updated
129 // by an external program since it was last read
130 // If it has, we ask the user if they would like to reload the file
131 func (b *Buffer) CheckModTime() {
132 modTime, ok := GetModTime(b.Path)
134 if modTime != b.ModTime {
135 choice, canceled := messenger.YesNoPrompt("The file has changed since it was last read. Reload file? (y,n)")
138 if !choice || canceled {
139 // Don't load new changes -- do nothing
140 b.ModTime, _ = GetModTime(b.Path)
149 // ReOpen reloads the current buffer from disk
150 func (b *Buffer) ReOpen() {
151 data, err := ioutil.ReadFile(b.Path)
155 messenger.Error(err.Error())
164 b.ModTime, _ = GetModTime(b.Path)
170 // Update fetches the string from the rope and updates the `text` and `lines` in the buffer
171 func (b *Buffer) Update() {
172 b.Lines = strings.Split(b.String(), "\n")
173 b.NumLines = len(b.Lines)
176 // Save saves the buffer to its default path
177 func (b *Buffer) Save() error {
178 return b.SaveAs(b.Path)
181 // Serialize serializes the buffer to configDir/buffers
182 func (b *Buffer) Serialize() error {
183 if settings["savecursor"].(bool) || settings["saveundo"].(bool) {
184 absPath, _ := filepath.Abs(b.Path)
185 file, err := os.Create(configDir + "/buffers/" + EscapePath(absPath))
187 enc := gob.NewEncoder(file)
188 gob.Register(TextEvent{})
189 err = enc.Encode(SerializedBuffer{
194 // err = enc.Encode(b.Cursor)
202 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
203 func (b *Buffer) SaveAs(filename string) error {
207 data := []byte(b.String())
208 err := ioutil.WriteFile(filename, data, 0644)
211 b.ModTime, _ = GetModTime(filename)
217 // This directly inserts value at idx, bypassing all undo/redo
218 func (b *Buffer) insert(idx int, value string) {
220 b.r = b.r.Insert(idx, value)
224 // Remove a slice of the rope from start to end (exclusive)
225 // Returns the string that was removed
226 // This directly removes from start to end from the buffer, bypassing all undo/redo
227 func (b *Buffer) remove(start, end int) string {
238 removed := b.Substr(start, end)
239 // The rope implenentation I am using wants indicies starting at 1 instead of 0
242 b.r = b.r.Delete(start, end-start)
247 // Substr returns the substring of the rope from start to end
248 func (b *Buffer) Substr(start, end int) string {
249 return b.r.Substr(start+1, end-start).String()
252 // Len gives the length of the buffer
253 func (b *Buffer) Len() int {