mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-15 21:37:09 +09:00
Replace rope with lineArray
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
133
cmd/micro/lineArray.go
Normal 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
120
cmd/micro/loc.go
Normal 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 {
|
||||
//
|
||||
// }
|
||||
@@ -210,7 +210,7 @@ func main() {
|
||||
// Load the help files
|
||||
LoadHelp()
|
||||
|
||||
buf := NewBuffer(string(input), filename)
|
||||
buf := NewBuffer(input, filename)
|
||||
|
||||
InitScreen()
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user