Reimplement simple selection

This commit is contained in:
Luke Wilson 2021-04-12 18:57:59 -05:00
parent b0ca65a613
commit 8d775804cb
2 changed files with 86 additions and 78 deletions

View File

@ -17,27 +17,19 @@ type position struct {
// The start and end are inclusive. If the EndCol of a Region is one more than the // The start and end are inclusive. If the EndCol of a Region is one more than the
// last column of a line, then it points to the line delimiter at the end of that // last column of a line, then it points to the line delimiter at the end of that
// line. It is understood that as a Region spans multiple lines, those connecting // line. It is understood that as a Region spans multiple lines, those connecting
// line-delimiters are included, as well. // line-delimiters are included in the selection, as well.
type Region struct { type Region struct {
buffer *Buffer Start Cursor
start position End Cursor
end position
} }
func NewRegion(in *Buffer) Region { func NewRegion(in *Buffer) Region {
return Region{ return Region{
buffer: in, NewCursor(in),
NewCursor(in),
} }
} }
func (r Region) Start() (line, col int) {
return r.start.line, r.start.col
}
func (r Region) End() (line, col int) {
return r.end.line, r.end.col
}
// A Cursor's functions emulate common cursor actions. To have a Cursor be // A Cursor's functions emulate common cursor actions. To have a Cursor be
// automatically updated when the buffer has text prepended or appended -- one // automatically updated when the buffer has text prepended or appended -- one
// should register the Cursor with the Buffer's function `RegisterCursor()` // should register the Cursor with the Buffer's function `RegisterCursor()`
@ -105,3 +97,7 @@ func (c Cursor) SetLineCol(line, col int) Cursor {
c.line, c.col = (*c.buffer).ClampLineCol(line, col) c.line, c.col = (*c.buffer).ClampLineCol(line, col)
return c return c
} }
func (c Cursor) Eq(other Cursor) bool {
return c.buffer == other.buffer && c.line == other.line && c.col == other.col
}

View File

@ -77,6 +77,7 @@ loop:
t.Buffer = buffer.NewRopeBuffer(contents) t.Buffer = buffer.NewRopeBuffer(contents)
t.cursor = buffer.NewCursor(&t.Buffer) t.cursor = buffer.NewCursor(&t.Buffer)
t.selection = buffer.NewRegion(&t.Buffer)
// TODO: replace with automatic determination of language via filetype // TODO: replace with automatic determination of language via filetype
lang := &buffer.Language{ lang := &buffer.Language{
@ -151,8 +152,8 @@ func (t *TextEdit) Delete(forwards bool) {
if t.selectMode { // If text is selected, delete the whole selection if t.selectMode { // If text is selected, delete the whole selection
t.selectMode = false // Disable selection and prevent infinite loop t.selectMode = false // Disable selection and prevent infinite loop
startLine, startCol := t.selection.Start() startLine, startCol := t.selection.Start.GetLineCol()
endLine, endCol := t.selection.End() endLine, endCol := t.selection.End.GetLineCol()
// Delete the region // Delete the region
t.Buffer.Remove(startLine, startCol, endLine, endCol) t.Buffer.Remove(startLine, startCol, endLine, endCol)
@ -319,8 +320,8 @@ func (t *TextEdit) getColumnWidth() int {
func (t *TextEdit) GetSelectedBytes() []byte { func (t *TextEdit) GetSelectedBytes() []byte {
// TODO: there's a bug with copying text // TODO: there's a bug with copying text
if t.selectMode { if t.selectMode {
startLine, startCol := t.selection.Start() startLine, startCol := t.selection.Start.GetLineCol()
endLine, endCol := t.selection.End() endLine, endCol := t.selection.End.GetLineCol()
return t.Buffer.Slice(startLine, startCol, endLine, endCol) return t.Buffer.Slice(startLine, startCol, endLine, endCol)
} }
return []byte{} return []byte{}
@ -421,8 +422,8 @@ func (t *TextEdit) Draw(s tcell.Screen) {
r = ' ' r = ' '
} }
startLine, startCol := t.selection.Start() startLine, startCol := t.selection.Start.GetLineCol()
endLine, endCol := t.selection.End() endLine, endCol := t.selection.End.GetLineCol()
// Determine whether we select the current rune. Also only select runes within // Determine whether we select the current rune. Also only select runes within
// the line bytes range. // the line bytes range.
@ -508,87 +509,98 @@ func (t *TextEdit) HandleEvent(event tcell.Event) bool {
// Cursor movement // Cursor movement
case tcell.KeyUp: case tcell.KeyUp:
if ev.Modifiers() == tcell.ModShift { if ev.Modifiers() == tcell.ModShift {
// if !t.selectMode { if !t.selectMode {
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx var endCursor buffer.Cursor
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx if cursLine, _ := t.cursor.GetLineCol(); cursLine != 0 {
// t.selectMode = true endCursor = t.cursor.Left()
// } else { } else {
// prevCurX, prevCurY := t.curx, t.cury endCursor = t.cursor
// t.CursorUp() }
// // Grow the selection in the correct direction t.selection.End = endCursor
// if prevCurY <= t.selection.StartLine && prevCurX <= t.selection.StartCol { t.SetCursor(t.cursor.Up())
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.selection.Start = t.cursor
// } else { t.selectMode = true
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx t.ScrollToCursor()
// } break // Select only a single character at start
// } }
if t.selection.Start.Eq(t.cursor) {
t.SetCursor(t.cursor.Up())
t.selection.Start = t.cursor
} else {
t.SetCursor(t.cursor.Up())
t.selection.End = t.cursor
}
} else { } else {
t.selectMode = false t.selectMode = false
t.SetCursor(t.cursor.Up()) t.SetCursor(t.cursor.Up())
t.ScrollToCursor()
} }
t.ScrollToCursor()
case tcell.KeyDown: case tcell.KeyDown:
if ev.Modifiers() == tcell.ModShift { if ev.Modifiers() == tcell.ModShift {
// if !t.selectMode { if !t.selectMode {
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.selection.Start = t.cursor
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx t.SetCursor(t.cursor.Down())
// t.selectMode = true t.selection.End = t.cursor
// } else { t.selectMode = true
// prevCurX, prevCurY := t.curx, t.cury t.ScrollToCursor()
// t.CursorDown() break
// if prevCurY >= t.selection.EndLine && prevCurX >= t.selection.EndCol { }
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx
// } else { if t.selection.End.Eq(t.cursor) {
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.SetCursor(t.cursor.Down())
// } t.selection.End = t.cursor
// } } else {
t.SetCursor(t.cursor.Down())
t.selection.Start = t.cursor
}
} else { } else {
t.selectMode = false t.selectMode = false
t.SetCursor(t.cursor.Down()) t.SetCursor(t.cursor.Down())
t.ScrollToCursor()
} }
t.ScrollToCursor()
case tcell.KeyLeft: case tcell.KeyLeft:
if ev.Modifiers() == tcell.ModShift { if ev.Modifiers() == tcell.ModShift {
// if !t.selectMode { if !t.selectMode {
// t.CursorLeft() // We want the character to the left to be selected only (think insert) t.SetCursor(t.cursor.Left())
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.selection.Start, t.selection.End = t.cursor, t.cursor
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx t.selectMode = true
// t.selectMode = true t.ScrollToCursor()
// } else { break // Select only a single character at start
// prevCurX, prevCurY := t.curx, t.cury }
// t.CursorLeft()
// if prevCurY == t.selection.StartLine && prevCurX == t.selection.StartCol { // We are moving the start... if t.selection.Start.Eq(t.cursor) {
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.SetCursor(t.cursor.Left())
// } else { t.selection.Start = t.cursor
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx } else {
// } t.SetCursor(t.cursor.Left())
// } t.selection.End = t.cursor
}
} else { } else {
t.selectMode = false t.selectMode = false
t.SetCursor(t.cursor.Left()) t.SetCursor(t.cursor.Left())
t.ScrollToCursor()
} }
t.ScrollToCursor()
case tcell.KeyRight: case tcell.KeyRight:
if ev.Modifiers() == tcell.ModShift { if ev.Modifiers() == tcell.ModShift {
// if !t.selectMode { // If we are not already selecting... if !t.selectMode {
// // Reset the selection to cursor pos t.selection.Start, t.selection.End = t.cursor, t.cursor
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.selectMode = true
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx break
// t.selectMode = true }
// } else {
// prevCurX, prevCurY := t.curx, t.cury if t.selection.End.Eq(t.cursor) {
// t.CursorRight() // Advance the cursor t.SetCursor(t.cursor.Right())
// if prevCurY == t.selection.EndLine && prevCurX == t.selection.EndCol { t.selection.End = t.cursor
// t.selection.EndLine, t.selection.EndCol = t.cury, t.curx } else {
// } else { t.SetCursor(t.cursor.Right())
// t.selection.StartLine, t.selection.StartCol = t.cury, t.curx t.selection.Start = t.cursor
// } }
// }
} else { } else {
t.selectMode = false t.selectMode = false
t.SetCursor(t.cursor.Right()) t.SetCursor(t.cursor.Right())
t.ScrollToCursor()
} }
t.ScrollToCursor()
case tcell.KeyHome: case tcell.KeyHome:
cursLine, _ := t.cursor.GetLineCol() cursLine, _ := t.cursor.GetLineCol()
// TODO: go to first (non-whitespace) character on current line, if we are not already there // TODO: go to first (non-whitespace) character on current line, if we are not already there