Replace rope with lineArray

This commit is contained in:
Zachary Yedidia
2016-06-07 11:43:28 -04:00
parent d6307b2718
commit 72f5808025
12 changed files with 453 additions and 275 deletions

View File

@@ -405,7 +405,7 @@ func DefaultBindings() map[string]string {
// CursorUp moves the cursor up
func (v *View) CursorUp() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[0])
v.Cursor.Loc = v.Cursor.CurSelection[0]
v.Cursor.ResetSelection()
}
v.Cursor.Up()
@@ -415,7 +415,7 @@ func (v *View) CursorUp() bool {
// CursorDown moves the cursor down
func (v *View) CursorDown() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[1])
v.Cursor.Loc = v.Cursor.CurSelection[1]
v.Cursor.ResetSelection()
}
v.Cursor.Down()
@@ -425,7 +425,7 @@ func (v *View) CursorDown() bool {
// CursorLeft moves the cursor left
func (v *View) CursorLeft() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[0])
v.Cursor.Loc = v.Cursor.CurSelection[0]
v.Cursor.ResetSelection()
} else {
v.Cursor.Left()
@@ -436,7 +436,7 @@ func (v *View) CursorLeft() bool {
// CursorRight moves the cursor right
func (v *View) CursorRight() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[1] - 1)
v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
v.Cursor.ResetSelection()
} else {
v.Cursor.Right()
@@ -458,75 +458,71 @@ func (v *View) WordLeft() bool {
// SelectUp selects up one line
func (v *View) SelectUp() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.Up()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectDown selects down one line
func (v *View) SelectDown() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.Down()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectLeft selects the character to the left of the cursor
func (v *View) SelectLeft() bool {
loc := v.Cursor.Loc()
count := v.Buf.Len() - 1
if loc > count {
loc := v.Cursor.Loc
count := v.Buf.End().Move(-1, v.Buf)
if loc.GreaterThan(count) {
loc = count
}
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
}
v.Cursor.Left()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectRight selects the character to the right of the cursor
func (v *View) SelectRight() bool {
loc := v.Cursor.Loc()
count := v.Buf.Len() - 1
if loc > count {
loc := v.Cursor.Loc
count := v.Buf.End().Move(-1, v.Buf)
if loc.GreaterThan(count) {
loc = count
}
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
}
v.Cursor.Right()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectWordRight selects the word to the right of the cursor
func (v *View) SelectWordRight() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.WordRight()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectWordLeft selects the word to the left of the cursor
func (v *View) SelectWordLeft() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.WordLeft()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
@@ -544,23 +540,21 @@ func (v *View) EndOfLine() bool {
// SelectToStartOfLine selects to the start of the current line
func (v *View) SelectToStartOfLine() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.Start()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
// SelectToEndOfLine selects to the end of the current line
func (v *View) SelectToEndOfLine() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.Cursor.End()
v.Cursor.SelectTo(v.Cursor.Loc())
v.Cursor.SelectTo(v.Cursor.Loc)
return true
}
@@ -573,29 +567,27 @@ func (v *View) CursorStart() bool {
// CursorEnd moves the cursor to the end of the buffer
func (v *View) CursorEnd() bool {
v.Cursor.SetLoc(v.Buf.Len())
v.Cursor.Loc = v.Buf.End()
return true
}
// SelectToStart selects the text from the cursor to the start of the buffer
func (v *View) SelectToStart() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.CursorStart()
v.Cursor.SelectTo(0)
v.Cursor.SelectTo(v.Buf.Start())
return true
}
// SelectToEnd selects the text from the cursor to the end of the buffer
func (v *View) SelectToEnd() bool {
loc := v.Cursor.Loc()
if !v.Cursor.HasSelection() {
v.Cursor.OrigSelection[0] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
}
v.CursorEnd()
v.Cursor.SelectTo(v.Buf.Len())
v.Cursor.SelectTo(v.Buf.End())
return true
}
@@ -605,7 +597,7 @@ func (v *View) InsertSpace() bool {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc(), " ")
v.Buf.Insert(v.Cursor.Loc, " ")
v.Cursor.Right()
return true
}
@@ -618,12 +610,12 @@ func (v *View) InsertEnter() bool {
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc(), "\n")
ws := GetLeadingWhitespace(v.Buf.Lines[v.Cursor.Y])
v.Buf.Insert(v.Cursor.Loc, "\n")
ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
v.Cursor.Right()
if settings["autoindent"].(bool) {
v.Buf.Insert(v.Cursor.Loc(), ws)
v.Buf.Insert(v.Cursor.Loc, ws)
for i := 0; i < len(ws); i++ {
v.Cursor.Right()
}
@@ -638,7 +630,7 @@ func (v *View) Backspace() bool {
if v.Cursor.HasSelection() {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
} else if v.Cursor.Loc() > 0 {
} else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
// We have to do something a bit hacky here because we want to
// delete the line by first moving left and then deleting backwards
// but the undo redo would place the cursor in the wrong place
@@ -648,21 +640,21 @@ func (v *View) Backspace() bool {
// If the user is using spaces instead of tabs and they are deleting
// whitespace at the start of the line, we should delete as if its a
// tab (tabSize number of spaces)
lineStart := v.Buf.Lines[v.Cursor.Y][:v.Cursor.X]
lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
tabSize := int(settings["tabsize"].(float64))
if settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
loc := v.Cursor.Loc()
v.Cursor.SetLoc(loc - tabSize)
loc := v.Cursor.Loc
v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
cx, cy := v.Cursor.X, v.Cursor.Y
v.Cursor.SetLoc(loc)
v.Buf.Remove(loc-tabSize, loc)
v.Cursor.Loc = loc
v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
v.Cursor.X, v.Cursor.Y = cx, cy
} else {
v.Cursor.Left()
cx, cy := v.Cursor.X, v.Cursor.Y
v.Cursor.Right()
loc := v.Cursor.Loc()
v.Buf.Remove(loc-1, loc)
loc := v.Cursor.Loc
v.Buf.Remove(loc.Move(-1, v.Buf), loc)
v.Cursor.X, v.Cursor.Y = cx, cy
}
}
@@ -696,9 +688,9 @@ func (v *View) Delete() bool {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
} else {
loc := v.Cursor.Loc()
if loc < v.Buf.Len() {
v.Buf.Remove(loc, loc+1)
loc := v.Cursor.Loc
if loc.LessThan(v.Buf.End()) {
v.Buf.Remove(loc, loc.Move(1, v.Buf))
}
}
return true
@@ -713,12 +705,12 @@ func (v *View) InsertTab() bool {
}
if settings["tabstospaces"].(bool) {
tabSize := int(settings["tabsize"].(float64))
v.Buf.Insert(v.Cursor.Loc(), Spaces(tabSize))
v.Buf.Insert(v.Cursor.Loc, Spaces(tabSize))
for i := 0; i < tabSize; i++ {
v.Cursor.Right()
}
} else {
v.Buf.Insert(v.Cursor.Loc(), "\t")
v.Buf.Insert(v.Cursor.Loc, "\t")
v.Cursor.Right()
}
return true
@@ -765,9 +757,9 @@ func (v *View) Save() bool {
// Find opens a prompt and searches forward for the input
func (v *View) Find() bool {
if v.Cursor.HasSelection() {
searchStart = v.Cursor.CurSelection[1]
searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
} else {
searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
}
BeginSearch()
return true
@@ -776,9 +768,9 @@ func (v *View) Find() bool {
// FindNext searches forwards for the last used search term
func (v *View) FindNext() bool {
if v.Cursor.HasSelection() {
searchStart = v.Cursor.CurSelection[1]
searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
} else {
searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
}
messenger.Message("Finding: " + lastSearch)
Search(lastSearch, v, true)
@@ -788,9 +780,9 @@ func (v *View) FindNext() bool {
// FindPrevious searches backwards for the last used search term
func (v *View) FindPrevious() bool {
if v.Cursor.HasSelection() {
searchStart = v.Cursor.CurSelection[0]
searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
} else {
searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
}
messenger.Message("Finding: " + lastSearch)
Search(lastSearch, v, false)
@@ -861,7 +853,7 @@ func (v *View) Cut() bool {
// DuplicateLine duplicates the current line
func (v *View) DuplicateLine() bool {
v.Cursor.End()
v.Buf.Insert(v.Cursor.Loc(), "\n"+v.Buf.Lines[v.Cursor.Y])
v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
v.Cursor.Right()
messenger.Message("Duplicated line")
return true
@@ -875,8 +867,8 @@ func (v *View) Paste() bool {
v.Cursor.ResetSelection()
}
clip, _ := clipboard.ReadAll()
v.Buf.Insert(v.Cursor.Loc(), clip)
v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
v.Buf.Insert(v.Cursor.Loc, clip)
v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
v.freshClip = false
messenger.Message("Pasted clipboard")
return true
@@ -884,8 +876,8 @@ func (v *View) Paste() bool {
// SelectAll selects the entire buffer
func (v *View) SelectAll() bool {
v.Cursor.CurSelection[0] = 0
v.Cursor.CurSelection[1] = v.Buf.Len()
v.Cursor.CurSelection[0] = v.Buf.Start()
v.Cursor.CurSelection[1] = v.Buf.End()
// Put the cursor at the beginning
v.Cursor.X = 0
v.Cursor.Y = 0
@@ -907,7 +899,7 @@ func (v *View) OpenFile() bool {
messenger.Error(err.Error())
return true
}
buf := NewBuffer(string(file), filename)
buf := NewBuffer(file, filename)
v.OpenBuffer(buf)
}
return true
@@ -952,7 +944,7 @@ func (v *View) PageDown() bool {
// CursorPageUp places the cursor a page up
func (v *View) CursorPageUp() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[0])
v.Cursor.Loc = v.Cursor.CurSelection[0]
v.Cursor.ResetSelection()
}
v.Cursor.UpN(v.height)
@@ -962,7 +954,7 @@ func (v *View) CursorPageUp() bool {
// CursorPageDown places the cursor a page up
func (v *View) CursorPageDown() bool {
if v.Cursor.HasSelection() {
v.Cursor.SetLoc(v.Cursor.CurSelection[1])
v.Cursor.Loc = v.Cursor.CurSelection[1]
v.Cursor.ResetSelection()
}
v.Cursor.DownN(v.height)
@@ -1036,7 +1028,7 @@ func (v *View) ClearStatus() bool {
func (v *View) ToggleHelp() bool {
if !v.helpOpen {
v.lastBuffer = v.Buf
helpBuffer := NewBuffer(helpTxt, "help.md")
helpBuffer := NewBuffer([]byte(helpTxt), "help.md")
helpBuffer.Name = "Help"
v.helpOpen = true
v.OpenBuffer(helpBuffer)

View File

@@ -8,10 +8,8 @@ import (
"os/exec"
"os/signal"
"path/filepath"
"strings"
"time"
"github.com/vinzmay/go-rope"
"unicode/utf8"
)
// Buffer stores the text for files that are loaded into the text editor
@@ -20,9 +18,7 @@ import (
type Buffer struct {
// The eventhandler for undo/redo
*EventHandler
// Stores the text of the buffer
r *rope.Rope
*LineArray
Cursor Cursor
@@ -36,10 +32,6 @@ type Buffer struct {
// Stores the last modification time of the file the buffer is pointing to
ModTime time.Time
// Provide efficient and easy access to text and lines so the rope String does not
// need to be constantly recalculated
// These variables are updated in the update() function
Lines []string
NumLines int
// Syntax highlighting rules
@@ -56,13 +48,9 @@ type SerializedBuffer struct {
}
// NewBuffer creates a new buffer from `txt` with path and name `path`
func NewBuffer(txt, path string) *Buffer {
func NewBuffer(txt []byte, path string) *Buffer {
b := new(Buffer)
if txt == "" {
b.r = new(rope.Rope)
} else {
b.r = rope.New(txt)
}
b.LineArray = NewLineArray(txt)
b.Path = path
b.Name = path
@@ -79,8 +67,10 @@ func NewBuffer(txt, path string) *Buffer {
// Put the cursor at the first spot
b.Cursor = Cursor{
Loc: Loc{
X: 0,
Y: 0,
},
buf: b,
}
@@ -121,13 +111,6 @@ func (b *Buffer) UpdateRules() {
b.rules, b.FileType = GetRules(b)
}
func (b *Buffer) String() string {
if b.r.Len() != 0 {
return b.r.String()
}
return ""
}
// CheckModTime makes sure that the file this buffer points to hasn't been updated
// by an external program since it was last read
// If it has, we ask the user if they would like to reload the file
@@ -168,8 +151,7 @@ func (b *Buffer) ReOpen() {
// Update fetches the string from the rope and updates the `text` and `lines` in the buffer
func (b *Buffer) Update() {
b.Lines = strings.Split(b.String(), "\n")
b.NumLines = len(b.Lines)
b.NumLines = len(b.lines)
}
// Save saves the buffer to its default path
@@ -268,42 +250,44 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
return err
}
// This directly inserts value at idx, bypassing all undo/redo
func (b *Buffer) insert(idx int, value string) {
func (b *Buffer) insert(pos Loc, value []byte) {
b.IsModified = true
b.r = b.r.Insert(idx, value)
b.LineArray.insert(pos, value)
b.Update()
}
// Remove a slice of the rope from start to end (exclusive)
// Returns the string that was removed
// This directly removes from start to end from the buffer, bypassing all undo/redo
func (b *Buffer) remove(start, end int) string {
func (b *Buffer) remove(start, end Loc) string {
b.IsModified = true
if start < 0 {
start = 0
}
if end > b.Len() {
end = b.Len()
}
if start == end {
return ""
}
removed := b.Substr(start, end)
// The rope implenentation I am using wants indicies starting at 1 instead of 0
start++
end++
b.r = b.r.Delete(start, end-start)
sub := b.LineArray.remove(start, end)
b.Update()
return removed
return sub
}
// Substr returns the substring of the rope from start to end
func (b *Buffer) Substr(start, end int) string {
return b.r.Substr(start+1, end-start).String()
// Start returns the location of the first character in the buffer
func (b *Buffer) Start() Loc {
return Loc{0, 0}
}
// End returns the location of the last character in the buffer
func (b *Buffer) End() Loc {
return Loc{utf8.RuneCount(b.lines[len(b.lines)-1]), b.NumLines - 1}
}
// Line returns a single line
func (b *Buffer) Line(n int) string {
return string(b.lines[n])
}
// Lines returns an array of strings containing the lines from start to end
func (b *Buffer) Lines(start, end int) []string {
lines := b.lines[start:end]
var slice []string
for _, line := range lines {
slice = append(slice, string(line))
}
return slice
}
// Len gives the length of the buffer
func (b *Buffer) Len() int {
return b.r.Len()
return Count(b.String())
}

View File

@@ -158,7 +158,7 @@ func Replace(args []string) {
choice, canceled := messenger.YesNoPrompt("Perform replacement? (y,n)")
if canceled {
if view.Cursor.HasSelection() {
view.Cursor.SetLoc(view.Cursor.CurSelection[0])
view.Cursor.Loc = view.Cursor.CurSelection[0]
view.Cursor.ResetSelection()
}
messenger.Reset()
@@ -166,19 +166,19 @@ func Replace(args []string) {
}
if choice {
view.Cursor.DeleteSelection()
view.Buf.Insert(match[0], replace)
view.Buf.Insert(FromCharPos(match[0], view.Buf), replace)
view.Cursor.ResetSelection()
messenger.Reset()
} else {
if view.Cursor.HasSelection() {
searchStart = view.Cursor.CurSelection[1]
searchStart = ToCharPos(view.Cursor.CurSelection[1], view.Buf)
} else {
searchStart = ToCharPos(view.Cursor.X, view.Cursor.Y, view.Buf)
searchStart = ToCharPos(view.Cursor.Loc, view.Buf)
}
continue
}
} else {
view.Buf.Replace(match[0], match[1], replace)
view.Buf.Replace(FromCharPos(match[0], view.Buf), FromCharPos(match[1], view.Buf), replace)
}
}
if !found {

View File

@@ -1,40 +1,5 @@
package main
// FromCharPos converts from a character position to an x, y position
func FromCharPos(loc int, buf *Buffer) (int, int) {
return FromCharPosStart(0, 0, 0, loc, buf)
}
// FromCharPosStart converts from a character position to an x, y position, starting at the specified character location
func FromCharPosStart(startLoc, startX, startY, loc int, buf *Buffer) (int, int) {
charNum := startLoc
x, y := startX, startY
lineLen := Count(buf.Lines[y]) + 1
for charNum+lineLen <= loc {
charNum += lineLen
y++
if y >= buf.NumLines {
return 0, 0
}
lineLen = Count(buf.Lines[y]) + 1
}
x = loc - charNum
return x, y
}
// ToCharPos converts from an x, y position to a character position
func ToCharPos(x, y int, buf *Buffer) int {
loc := 0
for i := 0; i < y; i++ {
// + 1 for the newline
loc += Count(buf.Lines[i]) + 1
}
loc += x
return loc
}
// The Cursor struct stores the location of the cursor in the view
// The complicated part about the cursor is storing its location.
// The cursor must be displayed at an x, y location, but since the buffer
@@ -43,20 +8,17 @@ func ToCharPos(x, y int, buf *Buffer) int {
// selection.
type Cursor struct {
buf *Buffer
// The cursor display location
X int
Y int
Loc
// Last cursor x position
LastVisualX int
// The current selection as a range of character numbers (inclusive)
CurSelection [2]int
CurSelection [2]Loc
// The original selection as a range of character numbers
// This is used for line and word selection where it is necessary
// to know what the original selection was
OrigSelection [2]int
OrigSelection [2]Loc
}
// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
@@ -65,25 +27,10 @@ func (c *Cursor) Goto(b Cursor) {
c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
}
// SetLoc sets the location of the cursor in terms of character number
// and not x, y location
// It's just a simple wrapper of FromCharPos
func (c *Cursor) SetLoc(loc int) {
c.X, c.Y = FromCharPos(loc, c.buf)
c.LastVisualX = c.GetVisualX()
}
// Loc gets the cursor location in terms of character number instead
// of x, y location
// It's just a simple wrapper of ToCharPos
func (c *Cursor) Loc() int {
return ToCharPos(c.X, c.Y, c.buf)
}
// ResetSelection resets the user's selection
func (c *Cursor) ResetSelection() {
c.CurSelection[0] = 0
c.CurSelection[1] = 0
c.CurSelection[0] = c.buf.Start()
c.CurSelection[1] = c.buf.Start()
}
// HasSelection returns whether or not the user has selected anything
@@ -93,20 +40,20 @@ func (c *Cursor) HasSelection() bool {
// DeleteSelection deletes the currently selected text
func (c *Cursor) DeleteSelection() {
if c.CurSelection[0] > c.CurSelection[1] {
if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
c.buf.Remove(c.CurSelection[1], c.CurSelection[0])
c.SetLoc(c.CurSelection[1])
c.Loc = c.CurSelection[1]
} else if c.GetSelection() == "" {
return
} else {
c.buf.Remove(c.CurSelection[0], c.CurSelection[1])
c.SetLoc(c.CurSelection[0])
c.Loc = c.CurSelection[0]
}
}
// GetSelection returns the cursor's selection
func (c *Cursor) GetSelection() string {
if c.CurSelection[0] > c.CurSelection[1] {
if c.CurSelection[0].GreaterThan(c.CurSelection[1]) {
return c.buf.Substr(c.CurSelection[1], c.CurSelection[0])
}
return c.buf.Substr(c.CurSelection[0], c.CurSelection[1])
@@ -115,12 +62,12 @@ func (c *Cursor) GetSelection() string {
// SelectLine selects the current line
func (c *Cursor) SelectLine() {
c.Start()
c.CurSelection[0] = c.Loc()
c.CurSelection[0] = c.Loc
c.End()
if c.buf.NumLines-1 > c.Y {
c.CurSelection[1] = c.Loc() + 1
c.CurSelection[1] = c.Loc.Move(1, c.buf)
} else {
c.CurSelection[1] = c.Loc()
c.CurSelection[1] = c.Loc
}
c.OrigSelection = c.CurSelection
@@ -128,34 +75,31 @@ func (c *Cursor) SelectLine() {
// AddLineToSelection adds the current line to the selection
func (c *Cursor) AddLineToSelection() {
loc := c.Loc()
if loc < c.OrigSelection[0] {
if c.Loc.LessThan(c.OrigSelection[0]) {
c.Start()
c.CurSelection[0] = c.Loc()
c.CurSelection[0] = c.Loc
c.CurSelection[1] = c.OrigSelection[1]
}
if loc > c.OrigSelection[1] {
if c.Loc.GreaterThan(c.OrigSelection[1]) {
c.End()
c.CurSelection[1] = c.Loc() + 1
c.CurSelection[1] = c.Loc.Move(1, c.buf)
c.CurSelection[0] = c.OrigSelection[0]
}
if loc < c.OrigSelection[1] && loc > c.OrigSelection[0] {
if c.Loc.LessThan(c.OrigSelection[1]) && c.Loc.GreaterThan(c.OrigSelection[0]) {
c.CurSelection = c.OrigSelection
}
}
// SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() {
if len(c.buf.Lines[c.Y]) == 0 {
if len(c.buf.Line(c.Y)) == 0 {
return
}
if !IsWordChar(string(c.RuneUnder(c.X))) {
loc := c.Loc()
c.CurSelection[0] = loc
c.CurSelection[1] = loc + 1
c.CurSelection[0] = c.Loc
c.CurSelection[1] = c.Loc.Move(1, c.buf)
c.OrigSelection = c.CurSelection
return
}
@@ -166,55 +110,53 @@ func (c *Cursor) SelectWord() {
backward--
}
c.CurSelection[0] = ToCharPos(backward, c.Y, c.buf)
c.CurSelection[0] = Loc{backward, c.Y}
c.OrigSelection[0] = c.CurSelection[0]
for forward < Count(c.buf.Lines[c.Y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++
}
c.CurSelection[1] = ToCharPos(forward, c.Y, c.buf) + 1
c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
c.OrigSelection[1] = c.CurSelection[1]
c.SetLoc(c.CurSelection[1])
c.Loc = c.CurSelection[1]
}
// AddWordToSelection adds the word the cursor is currently on to the selection
func (c *Cursor) AddWordToSelection() {
loc := c.Loc()
if loc > c.OrigSelection[0] && loc < c.OrigSelection[1] {
if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
c.CurSelection = c.OrigSelection
return
}
if loc < c.OrigSelection[0] {
if c.Loc.LessThan(c.OrigSelection[0]) {
backward := c.X
for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
backward--
}
c.CurSelection[0] = ToCharPos(backward, c.Y, c.buf)
c.CurSelection[0] = Loc{backward, c.Y}
c.CurSelection[1] = c.OrigSelection[1]
}
if loc > c.OrigSelection[1] {
if c.Loc.GreaterThan(c.OrigSelection[1]) {
forward := c.X
for forward < Count(c.buf.Lines[c.Y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
for forward < Count(c.buf.Line(c.Y))-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++
}
c.CurSelection[1] = ToCharPos(forward, c.Y, c.buf) + 1
c.CurSelection[1] = Loc{forward, c.Y}.Move(1, c.buf)
c.CurSelection[0] = c.OrigSelection[0]
}
c.SetLoc(c.CurSelection[1])
c.Loc = c.CurSelection[1]
}
// SelectTo selects from the current cursor location to the given location
func (c *Cursor) SelectTo(loc int) {
if loc > c.OrigSelection[0] {
func (c *Cursor) SelectTo(loc Loc) {
if loc.GreaterThan(c.OrigSelection[0]) {
c.CurSelection[0] = c.OrigSelection[0]
c.CurSelection[1] = loc
} else {
@@ -227,13 +169,13 @@ func (c *Cursor) SelectTo(loc int) {
func (c *Cursor) WordRight() {
c.Right()
for IsWhitespace(c.RuneUnder(c.X)) {
if c.X == Count(c.buf.Lines[c.Y]) {
if c.X == Count(c.buf.Line(c.Y)) {
return
}
c.Right()
}
for !IsWhitespace(c.RuneUnder(c.X)) {
if c.X == Count(c.buf.Lines[c.Y]) {
if c.X == Count(c.buf.Line(c.Y)) {
return
}
c.Right()
@@ -260,7 +202,7 @@ func (c *Cursor) WordLeft() {
// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
line := []rune(c.buf.Lines[c.Y])
line := []rune(c.buf.Line(c.Y))
if len(line) == 0 {
return '\n'
}
@@ -285,7 +227,7 @@ func (c *Cursor) UpN(amount int) {
}
c.Y = proposedY
runes := []rune(c.buf.Lines[c.Y])
runes := []rune(c.buf.Line(c.Y))
c.X = c.GetCharPosInLine(c.Y, c.LastVisualX)
if c.X > len(runes) {
c.X = len(runes)
@@ -309,7 +251,7 @@ func (c *Cursor) Down() {
// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
func (c *Cursor) Left() {
if c.Loc() == 0 {
if c.Loc == c.buf.Start() {
return
}
if c.X > 0 {
@@ -323,11 +265,10 @@ func (c *Cursor) Left() {
// Right moves the cursor right one cell (if possible) or to the next line if it is at the end
func (c *Cursor) Right() {
if c.Loc() == c.buf.Len() {
if c.Loc == c.buf.End() {
return
}
// TermMessage(Count(c.buf.Lines[c.Y]))
if c.X < Count(c.buf.Lines[c.Y]) {
if c.X < Count(c.buf.Line(c.Y)) {
c.X++
} else {
c.Down()
@@ -338,7 +279,7 @@ func (c *Cursor) Right() {
// End moves the cursor to the end of the line it is on
func (c *Cursor) End() {
c.X = Count(c.buf.Lines[c.Y])
c.X = Count(c.buf.Line(c.Y))
c.LastVisualX = c.GetVisualX()
}
@@ -353,11 +294,11 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
// Get the tab size
tabSize := int(settings["tabsize"].(float64))
// This is the visual line -- every \t replaced with the correct number of spaces
visualLineLen := StringWidth(c.buf.Lines[lineNum])
visualLineLen := StringWidth(c.buf.Line(lineNum))
if visualPos > visualLineLen {
visualPos = visualLineLen
}
width := WidthOfLargeRunes(c.buf.Lines[lineNum])
width := WidthOfLargeRunes(c.buf.Line(lineNum))
if visualPos >= width {
return visualPos - width
}
@@ -366,7 +307,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
// GetVisualX returns the x value of the cursor in visual spaces
func (c *Cursor) GetVisualX() int {
runes := []rune(c.buf.Lines[c.Y])
runes := []rune(c.buf.Line(c.Y))
return StringWidth(string(runes[:c.X]))
}
@@ -381,7 +322,7 @@ func (c *Cursor) Relocate() {
if c.X < 0 {
c.X = 0
} else if c.X > Count(c.buf.Lines[c.Y]) {
c.X = Count(c.buf.Lines[c.Y])
} else if c.X > Count(c.buf.Line(c.Y)) {
c.X = Count(c.buf.Line(c.Y))
}
}

View File

@@ -21,15 +21,15 @@ type TextEvent struct {
EventType int
Text string
Start int
End int
Start Loc
End Loc
Time time.Time
}
// ExecuteTextEvent runs a text event
func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
if t.EventType == TextEventInsert {
buf.insert(t.Start, t.Text)
buf.insert(t.Start, []byte(t.Text))
} else if t.EventType == TextEventRemove {
t.Text = buf.remove(t.Start, t.End)
}
@@ -64,32 +64,32 @@ func NewEventHandler(buf *Buffer) *EventHandler {
func (eh *EventHandler) ApplyDiff(new string) {
differ := dmp.New()
diff := differ.DiffMain(eh.buf.String(), new, false)
var charNum int
loc := eh.buf.Start()
for _, d := range diff {
if d.Type == dmp.DiffInsert {
eh.Insert(charNum, d.Text)
eh.Insert(loc, d.Text)
} else if d.Type == dmp.DiffDelete {
eh.Remove(charNum, charNum+Count(d.Text))
eh.Remove(loc, loc.Move(Count(d.Text), eh.buf))
}
charNum += Count(d.Text)
loc = loc.Move(Count(d.Text), eh.buf)
}
}
// Insert creates an insert text event and executes it
func (eh *EventHandler) Insert(start int, text string) {
func (eh *EventHandler) Insert(start Loc, text string) {
e := &TextEvent{
C: eh.buf.Cursor,
EventType: TextEventInsert,
Text: text,
Start: start,
End: start + Count(text),
End: start.Move(Count(text), eh.buf),
Time: time.Now(),
}
eh.Execute(e)
}
// Remove creates a remove text event and executes it
func (eh *EventHandler) Remove(start, end int) {
func (eh *EventHandler) Remove(start, end Loc) {
e := &TextEvent{
C: eh.buf.Cursor,
EventType: TextEventRemove,
@@ -101,7 +101,7 @@ func (eh *EventHandler) Remove(start, end int) {
}
// Replace deletes from start to end and replaces it with the given string
func (eh *EventHandler) Replace(start, end int, replace string) {
func (eh *EventHandler) Replace(start, end Loc, replace string) {
eh.Remove(start, end)
eh.Insert(start, replace)
}

View File

@@ -366,7 +366,7 @@ func GetRules(buf *Buffer) ([]SyntaxRule, string) {
if r[0] != nil && r[0].MatchString(buf.Path) {
// Check if the syntax statement matches the extension
return LoadRulesFromFile(syntaxFiles[r].text, syntaxFiles[r].filename), syntaxFiles[r].filetype
} else if r[1] != nil && r[1].MatchString(buf.Lines[0]) {
} else if r[1] != nil && r[1].MatchString(buf.Line(0)) {
// Check if the header statement matches the first line
return LoadRulesFromFile(syntaxFiles[r].text, syntaxFiles[r].filename), syntaxFiles[r].filetype
}
@@ -390,7 +390,7 @@ func Match(v *View) SyntaxMatches {
viewEnd = buf.NumLines
}
lines := buf.Lines[viewStart:viewEnd]
lines := buf.Lines(viewStart, viewEnd)
matches := make(SyntaxMatches, len(lines))
for i, line := range lines {
@@ -407,10 +407,10 @@ func Match(v *View) SyntaxMatches {
totalEnd = buf.NumLines
}
str := strings.Join(buf.Lines[totalStart:totalEnd], "\n")
startNum := ToCharPos(0, totalStart, v.Buf)
str := strings.Join(buf.Lines(totalStart, totalEnd), "\n")
startNum := ToCharPos(Loc{0, totalStart}, v.Buf)
toplineNum := ToCharPos(0, v.Topline, v.Buf)
toplineNum := ToCharPos(Loc{0, v.Topline}, v.Buf)
for _, rule := range rules {
if rule.startend {
@@ -422,7 +422,8 @@ func Match(v *View) SyntaxMatches {
if i < toplineNum {
continue
}
colNum, lineNum := FromCharPosStart(toplineNum, 0, v.Topline, i, buf)
loc := FromCharPos(i, buf)
colNum, lineNum := loc.X, loc.Y
if lineNum == -1 || colNum == -1 {
continue
}

133
cmd/micro/lineArray.go Normal file
View File

@@ -0,0 +1,133 @@
package main
import (
"bytes"
"unicode/utf8"
)
func runeToByteIndex(n int, txt []byte) int {
if n == 0 {
return 0
}
count := 0
i := 0
for len(txt) > 0 {
_, size := utf8.DecodeRune(txt)
txt = txt[size:]
count += size
i++
if i == n {
break
}
}
return count
}
type LineArray struct {
lines [][]byte
}
func NewLineArray(text []byte) *LineArray {
la := new(LineArray)
split := bytes.Split(text, []byte("\n"))
la.lines = make([][]byte, len(split))
for i := range split {
la.lines[i] = make([]byte, len(split[i]))
copy(la.lines[i], split[i])
}
return la
}
func (la *LineArray) String() string {
return string(bytes.Join(la.lines, []byte("\n")))
}
func (la *LineArray) NewlineBelow(y int) {
la.lines = append(la.lines, []byte(" "))
copy(la.lines[y+2:], la.lines[y+1:])
la.lines[y+1] = []byte("")
}
func (la *LineArray) insert(pos Loc, value []byte) {
x, y := runeToByteIndex(pos.X, la.lines[pos.Y]), pos.Y
// x, y := pos.x, pos.y
for i := 0; i < len(value); i++ {
if value[i] == '\n' {
la.Split(Loc{x, y})
x = 0
y++
continue
}
la.insertByte(Loc{x, y}, value[i])
x++
}
}
func (la *LineArray) insertByte(pos Loc, value byte) {
la.lines[pos.Y] = append(la.lines[pos.Y], 0)
copy(la.lines[pos.Y][pos.X+1:], la.lines[pos.Y][pos.X:])
la.lines[pos.Y][pos.X] = value
}
func (la *LineArray) JoinLines(a, b int) {
la.insert(Loc{len(la.lines[a]), a}, la.lines[b])
la.DeleteLine(b)
}
func (la *LineArray) Split(pos Loc) {
la.NewlineBelow(pos.Y)
la.insert(Loc{0, pos.Y + 1}, la.lines[pos.Y][pos.X:])
la.DeleteToEnd(Loc{pos.X, pos.Y})
}
func (la *LineArray) remove(start, end Loc) string {
sub := la.Substr(start, end)
startX := runeToByteIndex(start.X, la.lines[start.Y])
endX := runeToByteIndex(end.X, la.lines[end.Y])
if start.Y == end.Y {
la.lines[start.Y] = append(la.lines[start.Y][:startX], la.lines[start.Y][endX:]...)
} else {
for i := start.Y + 1; i <= end.Y-1; i++ {
la.DeleteLine(i)
}
la.DeleteToEnd(Loc{startX, start.Y})
la.DeleteFromStart(Loc{endX - 1, start.Y + 1})
la.JoinLines(start.Y, start.Y+1)
}
return sub
}
func (la *LineArray) DeleteToEnd(pos Loc) {
la.lines[pos.Y] = la.lines[pos.Y][:pos.X]
}
func (la *LineArray) DeleteFromStart(pos Loc) {
la.lines[pos.Y] = la.lines[pos.Y][pos.X+1:]
}
func (la *LineArray) DeleteLine(y int) {
la.lines = la.lines[:y+copy(la.lines[y:], la.lines[y+1:])]
}
func (la *LineArray) DeleteByte(pos Loc) {
la.lines[pos.Y] = la.lines[pos.Y][:pos.X+copy(la.lines[pos.Y][pos.X:], la.lines[pos.Y][pos.X+1:])]
}
func (la *LineArray) Substr(start, end Loc) string {
startX := runeToByteIndex(start.X, la.lines[start.Y])
endX := runeToByteIndex(end.X, la.lines[end.Y])
if start.Y == end.Y {
return string(la.lines[start.Y][startX:endX])
}
var str string
str += string(la.lines[start.Y][startX:]) + "\n"
for i := start.Y + 1; i <= end.Y-1; i++ {
str += string(la.lines[i]) + "\n"
}
str += string(la.lines[end.Y][:endX])
return str
}

120
cmd/micro/loc.go Normal file
View File

@@ -0,0 +1,120 @@
package main
// FromCharPos converts from a character position to an x, y position
func FromCharPos(loc int, buf *Buffer) Loc {
charNum := 0
x, y := 0, 0
lineLen := Count(buf.Line(y)) + 1
for charNum+lineLen <= loc {
charNum += lineLen
y++
lineLen = Count(buf.Line(y)) + 1
}
x = loc - charNum
return Loc{x, y}
}
// ToCharPos converts from an x, y position to a character position
func ToCharPos(start Loc, buf *Buffer) int {
x, y := start.X, start.Y
loc := 0
for i := 0; i < y; i++ {
// + 1 for the newline
loc += Count(buf.Line(i)) + 1
}
loc += x
return loc
}
// Loc stores a location
type Loc struct {
X, Y int
}
// LessThan returns true if b is smaller
func (l Loc) LessThan(b Loc) bool {
if l.Y < b.Y {
return true
}
if l.Y == b.Y && l.X < b.X {
return true
}
return false
}
// GreaterThan returns true if b is bigger
func (l Loc) GreaterThan(b Loc) bool {
if l.Y > b.Y {
return true
}
if l.Y == b.Y && l.X > b.X {
return true
}
return false
}
// GreaterEqual returns true if b is greater than or equal to b
func (l Loc) GreaterEqual(b Loc) bool {
if l.Y > b.Y {
return true
}
if l.Y == b.Y && l.X > b.X {
return true
}
if l == b {
return true
}
return false
}
// LessEqual returns true if b is less than or equal to b
func (l Loc) LessEqual(b Loc) bool {
if l.Y < b.Y {
return true
}
if l.Y == b.Y && l.X < b.X {
return true
}
if l == b {
return true
}
return false
}
func (l Loc) right(n int, buf *Buffer) Loc {
if l == buf.End() {
return l
}
var res Loc
if l.X < Count(buf.Line(l.Y)) {
res = Loc{l.X + 1, l.Y}
} else {
res = Loc{0, l.Y + 1}
}
return res
}
func (l Loc) left(n int, buf *Buffer) Loc {
if l == buf.Start() {
return l
}
var res Loc
if l.X > 0 {
res = Loc{l.X - 1, l.Y}
} else {
res = Loc{Count(buf.Line(l.Y - 1)), l.Y - 1}
}
return res
}
func (l Loc) Move(n int, buf *Buffer) Loc {
if n > 0 {
return l.right(n, buf)
}
return l.left(Abs(n), buf)
}
// func (l Loc) DistanceTo(b Loc, buf *Buffer) int {
//
// }

View File

@@ -210,7 +210,7 @@ func main() {
// Load the help files
LoadHelp()
buf := NewBuffer(string(input), filename)
buf := NewBuffer(input, filename)
InitScreen()

View File

@@ -125,9 +125,9 @@ func Search(searchStr string, v *View, down bool) {
return
}
v.Cursor.CurSelection[0] = charPos + runePos(match[0], str)
v.Cursor.CurSelection[1] = charPos + runePos(match[1], str)
v.Cursor.X, v.Cursor.Y = FromCharPos(charPos+match[1]-1, v.Buf)
v.Cursor.CurSelection[0] = FromCharPos(charPos+runePos(match[0], str), v.Buf)
v.Cursor.CurSelection[1] = FromCharPos(charPos+runePos(match[1], str), v.Buf)
v.Cursor.Loc = FromCharPos(charPos+match[1]-1, v.Buf)
if v.Relocate() {
v.matches = Match(v)
}

View File

@@ -165,3 +165,11 @@ func WidthOfLargeRunes(str string) int {
func runePos(p int, str string) int {
return utf8.RuneCountInString(str[:p])
}
// Abs is a simple absolute value function for ints
func Abs(n int) int {
if n < 0 {
return -n
}
return n
}

View File

@@ -251,8 +251,8 @@ func (v *View) MoveToMouseClick(x, y int) {
}
x = v.Cursor.GetCharPosInLine(y, x)
if x > Count(v.Buf.Lines[y]) {
x = Count(v.Buf.Lines[y])
if x > Count(v.Buf.Line(y)) {
x = Count(v.Buf.Line(y))
}
v.Cursor.X = x
v.Cursor.Y = y
@@ -278,7 +278,7 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc(), string(e.Rune()))
v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
v.Cursor.Right()
} else {
for key, actions := range bindings {
@@ -310,8 +310,8 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Cursor.ResetSelection()
}
clip := e.Text()
v.Buf.Insert(v.Cursor.Loc(), clip)
v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
v.Buf.Insert(v.Cursor.Loc, clip)
v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
v.freshClip = false
case *tcell.EventMouse:
x, y := e.Position()
@@ -350,10 +350,9 @@ func (v *View) HandleEvent(event tcell.Event) {
v.tripleClick = false
v.lastClickTime = time.Now()
loc := v.Cursor.Loc()
v.Cursor.OrigSelection[0] = loc
v.Cursor.CurSelection[0] = loc
v.Cursor.CurSelection[1] = loc
v.Cursor.OrigSelection[0] = v.Cursor.Loc
v.Cursor.CurSelection[0] = v.Cursor.Loc
v.Cursor.CurSelection[1] = v.Cursor.Loc
}
v.mouseReleased = false
} else if !v.mouseReleased {
@@ -363,7 +362,7 @@ func (v *View) HandleEvent(event tcell.Event) {
} else if v.doubleClick {
v.Cursor.AddWordToSelection()
} else {
v.Cursor.CurSelection[1] = v.Cursor.Loc()
v.Cursor.CurSelection[1] = v.Cursor.Loc
}
}
case tcell.ButtonNone:
@@ -379,7 +378,7 @@ func (v *View) HandleEvent(event tcell.Event) {
if !v.doubleClick && !v.tripleClick {
v.MoveToMouseClick(x, y)
v.Cursor.CurSelection[1] = v.Cursor.Loc()
v.Cursor.CurSelection[1] = v.Cursor.Loc
}
v.mouseReleased = true
}
@@ -436,7 +435,7 @@ func (v *View) ClearAllGutterMessages() {
// DisplayView renders the view to the screen
func (v *View) DisplayView() {
// The character number of the character in the top left of the screen
charNum := ToCharPos(0, v.Topline, v.Buf)
charNum := Loc{0, v.Topline}
// Convert the length of buffer to a string, and get the length of the string
// We are going to have to offset by that amount
@@ -470,7 +469,7 @@ func (v *View) DisplayView() {
continue
}
line := v.Buf.Lines[lineN+v.Topline]
line := v.Buf.Line(lineN + v.Topline)
if hasGutterMessages {
msgOnLine := false
@@ -551,8 +550,8 @@ func (v *View) DisplayView() {
}
if v.Cursor.HasSelection() &&
(charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] ||
charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) {
(charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) ||
charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) {
lineStyle = tcell.StyleDefault.Reverse(true)
@@ -576,8 +575,8 @@ func (v *View) DisplayView() {
lineIndentStyle = style
}
if v.Cursor.HasSelection() &&
(charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] ||
charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) {
(charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) ||
charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) {
lineIndentStyle = tcell.StyleDefault.Reverse(true)
@@ -617,7 +616,7 @@ func (v *View) DisplayView() {
screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle)
}
}
charNum++
charNum = charNum.Move(1, v.Buf)
x++
}
// Here we are at a newline
@@ -625,8 +624,8 @@ func (v *View) DisplayView() {
// The newline may be selected, in which case we should draw the selection style
// with a space to represent it
if v.Cursor.HasSelection() &&
(charNum >= v.Cursor.CurSelection[0] && charNum < v.Cursor.CurSelection[1] ||
charNum < v.Cursor.CurSelection[0] && charNum >= v.Cursor.CurSelection[1]) {
(charNum.GreaterEqual(v.Cursor.CurSelection[0]) && charNum.LessThan(v.Cursor.CurSelection[1]) ||
charNum.LessThan(v.Cursor.CurSelection[0]) && charNum.GreaterEqual(v.Cursor.CurSelection[1])) {
selectStyle := defStyle.Reverse(true)
@@ -637,7 +636,7 @@ func (v *View) DisplayView() {
x++
}
charNum++
charNum = charNum.Move(1, v.Buf)
for i := 0; i < v.width-(x-v.leftCol); i++ {
lineStyle := tcell.StyleDefault