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
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
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
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
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
}