X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fbuffer.go;h=b255fc00648c538ee513f37d45bb0ca44cfba25c;hb=cb45481526ab893f092ae27b6868731cb428b935;hp=c791deaa753dab4cf5e641c7133ddd973fba2bbe;hpb=0a500be3ba5f29bae5f0976ac7ecbc0f80e20446;p=micro.git diff --git a/cmd/micro/buffer.go b/cmd/micro/buffer.go index c791deaa..b255fc00 100644 --- a/cmd/micro/buffer.go +++ b/cmd/micro/buffer.go @@ -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 +}