]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/lineArray.go
Minor optimizations
[micro.git] / cmd / micro / lineArray.go
index 9b6c0751004bafd1f03c39baadb2ab1bef1c51a6..248603782d9a7de1c55c2bc91869c16f955abeec 100644 (file)
@@ -2,32 +2,12 @@ package main
 
 import (
        "bufio"
-       "bytes"
        "io"
        "unicode/utf8"
 
        "github.com/zyedidia/micro/cmd/micro/highlight"
 )
 
-func lineCounter(r io.Reader) (int, error) {
-       buf := make([]byte, 32*1024)
-       count := 0
-       lineSep := []byte{'\n'}
-
-       for {
-               c, err := r.Read(buf)
-               count += bytes.Count(buf[:c], lineSep)
-
-               switch {
-               case err == io.EOF:
-                       return count, nil
-
-               case err != nil:
-                       return count, err
-               }
-       }
-}
-
 func runeToByteIndex(n int, txt []byte) int {
        if n == 0 {
                return 0
@@ -49,6 +29,8 @@ func runeToByteIndex(n int, txt []byte) int {
        return count
 }
 
+// A Line contains the data in bytes as well as a highlight state, match
+// and a flag for whether the highlighting needs to be updated
 type Line struct {
        data []byte
 
@@ -63,34 +45,71 @@ type LineArray struct {
        lines []Line
 }
 
+// Append efficiently appends lines together
+// It allocates an additional 10000 lines if the original estimate
+// is incorrect
+func Append(slice []Line, data ...Line) []Line {
+       l := len(slice)
+       if l+len(data) > cap(slice) { // reallocate
+               newSlice := make([]Line, (l+len(data))+10000)
+               // The copy function is predeclared and works for any slice type.
+               copy(newSlice, slice)
+               slice = newSlice
+       }
+       slice = slice[0 : l+len(data)]
+       for i, c := range data {
+               slice[l+i] = c
+       }
+       return slice
+}
+
 // NewLineArray returns a new line array from an array of bytes
-func NewLineArray(reader io.Reader) *LineArray {
+func NewLineArray(size int64, reader io.Reader) *LineArray {
        la := new(LineArray)
 
-       var buf bytes.Buffer
-       tee := io.TeeReader(reader, &buf)
-       numlines, _ := lineCounter(tee)
-       if numlines == 0 {
-               numlines = 1
-       }
+       la.lines = make([]Line, 0, 1000)
 
-       la.lines = make([]Line, numlines)
+       br := bufio.NewReader(reader)
+       var loaded int
 
-       br := bufio.NewReader(&buf)
-
-       for i := 0; i < numlines; i++ {
+       n := 0
+       for {
                data, err := br.ReadBytes('\n')
+               if len(data) > 1 && data[len(data)-2] == '\r' {
+                       data = append(data[:len(data)-2], '\n')
+                       if fileformat == 0 {
+                               fileformat = 2
+                       }
+               } else if len(data) > 0 {
+                       if fileformat == 0 {
+                               fileformat = 1
+                       }
+               }
+
+               if n >= 1000 && loaded >= 0 {
+                       totalLinesNum := int(float64(size) * (float64(n) / float64(loaded)))
+                       newSlice := make([]Line, len(la.lines), totalLinesNum+10000)
+                       copy(newSlice, la.lines)
+                       la.lines = newSlice
+                       loaded = -1
+               }
+
+               if loaded >= 0 {
+                       loaded += len(data)
+               }
+
                if err != nil {
                        if err == io.EOF {
-                               // la.lines[i] = Line{data[:len(data)], nil, nil, false}
-                               la.lines[i].data = data
+                               la.lines = Append(la.lines, Line{data[:], nil, nil, false})
+                               // la.lines = Append(la.lines, Line{data[:len(data)]})
                        }
                        // Last line was read
                        break
                } else {
-                       la.lines[i].data = data[:len(data)-1]
-                       // la.lines[i] = Line{data[:len(data)-1], nil, nil, false}
+                       // la.lines = Append(la.lines, Line{data[:len(data)-1]})
+                       la.lines = Append(la.lines, Line{data[:len(data)-1], nil, nil, false})
                }
+               n++
        }
 
        return la
@@ -108,11 +127,28 @@ func (la *LineArray) String() string {
        return str
 }
 
+// SaveString returns the string that should be written to disk when
+// the line array is saved
+// It is the same as string but uses crlf or lf line endings depending
+func (la *LineArray) SaveString(useCrlf bool) string {
+       str := ""
+       for i, l := range la.lines {
+               str += string(l.data)
+               if i != len(la.lines)-1 {
+                       if useCrlf {
+                               str += "\r"
+                       }
+                       str += "\n"
+               }
+       }
+       return str
+}
+
 // NewlineBelow adds a newline below the given line number
 func (la *LineArray) NewlineBelow(y int) {
-       la.lines = append(la.lines, Line{[]byte(" "), nil, nil, false})
+       la.lines = append(la.lines, Line{[]byte{' '}, nil, nil, false})
        copy(la.lines[y+2:], la.lines[y+1:])
-       la.lines[y+1] = Line{[]byte(""), la.lines[y].state, nil, false}
+       la.lines[y+1] = Line{[]byte{}, la.lines[y].state, nil, false}
 }
 
 // inserts a byte array at a given location
@@ -210,18 +246,22 @@ func (la *LineArray) Substr(start, end Loc) string {
        return str
 }
 
+// State gets the highlight state for the given line number
 func (la *LineArray) State(lineN int) highlight.State {
        return la.lines[lineN].state
 }
 
+// SetState sets the highlight state at the given line number
 func (la *LineArray) SetState(lineN int, s highlight.State) {
        la.lines[lineN].state = s
 }
 
+// SetMatch sets the match at the given line number
 func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) {
        la.lines[lineN].match = m
 }
 
+// Match retrieves the match for the given line number
 func (la *LineArray) Match(lineN int) highlight.LineMatch {
        return la.lines[lineN].match
 }