]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/buffer.go
Make tab views array public
[micro.git] / cmd / micro / buffer.go
index c791deaa753dab4cf5e641c7133ddd973fba2bbe..b255fc00648c538ee513f37d45bb0ca44cfba25c 100644 (file)
@@ -4,6 +4,7 @@ import (
        "bytes"
        "crypto/md5"
        "encoding/gob"
+       "errors"
        "io"
        "io/ioutil"
        "os"
@@ -73,6 +74,33 @@ type SerializedBuffer struct {
        ModTime      time.Time
 }
 
+// NewBufferFromFile opens a new buffer using the given filepath
+// It will also automatically handle `~`, and line/column with filename:l:c
+// It will return an empty buffer if the filepath does not exist
+// and an error if the file is a directory
+func NewBufferFromFile(path string) (*Buffer, error) {
+       filename := GetPath(path)
+       filename = ReplaceHome(filename)
+       file, err := os.Open(filename)
+       fileInfo, _ := os.Stat(filename)
+
+       if err == nil && fileInfo.IsDir() {
+               return nil, errors.New(filename + " is a directory")
+       }
+
+       defer file.Close()
+
+       var buf *Buffer
+       if err != nil {
+               // File does not exist -- create an empty buffer with that name
+               buf = NewBufferFromString("", path)
+       } else {
+               buf = NewBuffer(file, FSize(file), path)
+       }
+
+       return buf, nil
+}
+
 // NewBufferFromString creates a new buffer containing the given
 // string
 func NewBufferFromString(text, path string) *Buffer {
@@ -81,9 +109,29 @@ func NewBufferFromString(text, path string) *Buffer {
 
 // NewBuffer creates a new buffer from a given reader with a given path
 func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
+       startpos := Loc{0, 0}
+       startposErr := true
+       if strings.Contains(path, ":") {
+               var err error
+               split := strings.Split(path, ":")
+               path = split[0]
+               startpos.Y, err = strconv.Atoi(split[1])
+               if err != nil {
+                       messenger.Error("Error opening file: ", err)
+               } else {
+                       startposErr = false
+                       if len(split) > 2 {
+                               startpos.X, err = strconv.Atoi(split[2])
+                               if err != nil {
+                                       messenger.Error("Error opening file: ", err)
+                               }
+                       }
+               }
+       }
+
        if path != "" {
                for _, tab := range tabs {
-                       for _, view := range tab.views {
+                       for _, view := range tab.Views {
                                if view.Buf.Path == path {
                                        return view.Buf
                                }
@@ -128,11 +176,18 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
        cursorStartX := 0
        cursorStartY := 0
        // If -startpos LINE,COL was passed, use start position LINE,COL
-       if len(*flagStartPos) > 0 {
+       if len(*flagStartPos) > 0 || !startposErr {
                positions := strings.Split(*flagStartPos, ",")
-               if len(positions) == 2 {
-                       lineNum, errPos1 := strconv.Atoi(positions[0])
-                       colNum, errPos2 := strconv.Atoi(positions[1])
+               if len(positions) == 2 || !startposErr {
+                       var lineNum, colNum int
+                       var errPos1, errPos2 error
+                       if !startposErr {
+                               lineNum = startpos.Y
+                               colNum = startpos.X
+                       } else {
+                               lineNum, errPos1 = strconv.Atoi(positions[0])
+                               colNum, errPos2 = strconv.Atoi(positions[1])
+                       }
                        if errPos1 == nil && errPos2 == nil {
                                cursorStartX = colNum
                                cursorStartY = lineNum - 1
@@ -161,7 +216,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
 
        InitLocalSettings(b)
 
-       if len(*flagStartPos) == 0 && (b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool)) {
+       if startposErr && len(*flagStartPos) == 0 && (b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool)) {
                // If either savecursor or saveundo is turned on, we need to load the serialized information
                // from ~/.config/micro/buffers
                file, err := os.Open(configDir + "/buffers/" + EscapePath(b.AbsPath))
@@ -428,7 +483,28 @@ func (b *Buffer) SaveAs(filename string) error {
                b.ModTime, _ = GetModTime(filename)
        }()
 
-       f, err := os.OpenFile(ReplaceHome(filename), os.O_WRONLY|os.O_CREATE, 0644)
+       // Removes any tilde and replaces with the absolute path to home
+       var absFilename string = ReplaceHome(filename)
+
+       // Get the leading path to the file | "." is returned if there's no leading path provided
+       if dirname := filepath.Dir(absFilename); dirname != "." {
+               // Check if the parent dirs don't exist
+               if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) {
+                       // Prompt to make sure they want to create the dirs that are missing
+                       if yes, canceled := messenger.YesNoPrompt("Parent folders \"" + dirname + "\" do not exist. Create them? (y,n)"); yes && !canceled {
+                               // Create all leading dir(s) since they don't exist
+                               if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil {
+                                       // If there was an error creating the dirs
+                                       return mkdirallErr
+                               }
+                       } else {
+                               // If they canceled the creation of leading dirs
+                               return errors.New("Save aborted")
+                       }
+               }
+       }
+
+       f, err := os.OpenFile(absFilename, os.O_WRONLY|os.O_CREATE, 0644)
        if err != nil {
                return err
        }
@@ -534,13 +610,29 @@ func (b *Buffer) End() Loc {
 
 // RuneAt returns the rune at a given location in the buffer
 func (b *Buffer) RuneAt(loc Loc) rune {
-       line := []rune(b.Line(loc.Y))
+       line := b.LineRunes(loc.Y)
        if len(line) > 0 {
                return line[loc.X]
        }
        return '\n'
 }
 
+// Line returns a single line as an array of runes
+func (b *Buffer) LineBytes(n int) []byte {
+       if n >= len(b.lines) {
+               return []byte{}
+       }
+       return b.lines[n].data
+}
+
+// Line returns a single line as an array of runes
+func (b *Buffer) LineRunes(n int) []rune {
+       if n >= len(b.lines) {
+               return []rune{}
+       }
+       return toRunes(b.lines[n].data)
+}
+
 // Line returns a single line
 func (b *Buffer) Line(n int) string {
        if n >= len(b.lines) {
@@ -630,3 +722,58 @@ func (b *Buffer) clearCursors() {
        b.UpdateCursors()
        b.Cursor.ResetSelection()
 }
+
+var bracePairs = [][2]rune{
+       {'(', ')'},
+       {'{', '}'},
+       {'[', ']'},
+}
+
+// FindMatchingBrace returns the location in the buffer of the matching bracket
+// It is given a brace type containing the open and closing character, (for example
+// '{' and '}') as well as the location to match from
+func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) Loc {
+       curLine := b.LineRunes(start.Y)
+       startChar := curLine[start.X]
+       var i int
+       if startChar == braceType[0] {
+               for y := start.Y; y < b.NumLines; y++ {
+                       l := b.LineRunes(y)
+                       xInit := 0
+                       if y == start.Y {
+                               xInit = start.X
+                       }
+                       for x := xInit; x < len(l); x++ {
+                               r := l[x]
+                               if r == braceType[0] {
+                                       i++
+                               } else if r == braceType[1] {
+                                       i--
+                                       if i == 0 {
+                                               return Loc{x, y}
+                                       }
+                               }
+                       }
+               }
+       } else if startChar == braceType[1] {
+               for y := start.Y; y >= 0; y-- {
+                       l := []rune(string(b.lines[y].data))
+                       xInit := len(l) - 1
+                       if y == start.Y {
+                               xInit = start.X
+                       }
+                       for x := xInit; x >= 0; x-- {
+                               r := l[x]
+                               if r == braceType[0] {
+                                       i--
+                                       if i == 0 {
+                                               return Loc{x, y}
+                                       }
+                               } else if r == braceType[1] {
+                                       i++
+                               }
+                       }
+               }
+       }
+       return start
+}