Add word selection with double click

This commit is contained in:
Zachary Yedidia
2016-03-28 08:43:08 -04:00
parent 4d9e45bfca
commit a6764a04bc
5 changed files with 127 additions and 26 deletions

View File

@@ -42,7 +42,11 @@ type Cursor struct {
x int
y int
curSelection [2]int
// The current selection as a range of character numbers (inclusive)
curSelection [2]int
// 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
}
@@ -97,43 +101,93 @@ func (c *Cursor) SelectLine() {
c.End()
c.curSelection[1] = c.Loc()
c.origSelection[0] = c.curSelection[0]
c.origSelection[1] = c.curSelection[1]
c.origSelection = c.curSelection
}
// AddLineToSelection adds the current line to the selection
func (c *Cursor) AddLineToSelection() {
loc := c.Loc()
if loc < c.origSelection[0] {
c.Start()
c.curSelection[0] = c.Loc()
c.curSelection[1] = c.origSelection[1]
}
if loc > c.origSelection[1] {
c.End()
c.curSelection[1] = c.Loc()
c.curSelection[0] = c.origSelection[0]
}
if loc < c.origSelection[1] && loc > c.origSelection[0] {
c.curSelection = c.origSelection
}
}
// SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() {
if !IsWordChar(string(c.RuneUnder(c.x))) {
return
}
forward, backward := c.x, c.x
for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
backward--
}
c.curSelection[0] = ToCharPos(backward, c.y, c.v.buf)
c.origSelection[0] = c.curSelection[0]
for forward < Count(c.v.buf.lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++
}
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf)
c.origSelection[1] = 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] {
c.curSelection = c.origSelection
return
}
if loc < c.origSelection[0] {
c.Start()
c.curSelection[0] = c.Loc()
} else if loc > c.origSelection[1] {
c.End()
c.curSelection[1] = c.Loc()
backward := c.x
for backward > 0 && IsWordChar(string(c.RuneUnder(backward-1))) {
backward--
}
c.curSelection[0] = ToCharPos(backward, c.y, c.v.buf)
c.curSelection[1] = c.origSelection[1]
}
if loc < c.curSelection[0] {
c.Start()
c.curSelection[0] = c.Loc()
} else if loc > c.curSelection[1] {
c.End()
c.curSelection[1] = c.Loc()
if loc > c.origSelection[1] {
forward := c.x
for forward < Count(c.v.buf.lines[c.y])-1 && IsWordChar(string(c.RuneUnder(forward+1))) {
forward++
}
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf)
c.curSelection[0] = c.origSelection[0]
}
}
// RuneUnder returns the rune under the cursor
func (c *Cursor) RuneUnder() rune {
line := c.v.buf.lines[c.y]
if c.x >= Count(line) {
return ' '
// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
line := []rune(c.v.buf.lines[c.y])
if x >= len(line) {
x = len(line) - 1
} else if x < 0 {
x = 0
}
return []rune(line)[c.x]
return line[x]
}
// Up moves the cursor up one line (if possible)

View File

@@ -48,3 +48,15 @@ func Max(a, b int) int {
}
return b
}
// IsWordChar returns whether or not the string is a 'word character'
// If it is a unicode character, then it does not match
// Word characters are defined as [A-Za-z0-9_]
func IsWordChar(str string) bool {
if len(str) > 1 {
// Unicode
return false
}
c := str[0]
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
}

View File

@@ -33,3 +33,33 @@ func TestSpaces(t *testing.T) {
}
}
}
func TestIsWordChar(t *testing.T) {
if IsWordChar("t") == false {
t.Errorf("IsWordChar(t) = false")
}
if IsWordChar("T") == false {
t.Errorf("IsWordChar(T) = false")
}
if IsWordChar("5") == false {
t.Errorf("IsWordChar(5) = false")
}
if IsWordChar("_") == false {
t.Errorf("IsWordChar(_) = false")
}
if IsWordChar("~") == true {
t.Errorf("IsWordChar(~) = true")
}
if IsWordChar(" ") == true {
t.Errorf("IsWordChar( ) = true")
}
if IsWordChar("ß") == true {
t.Errorf("IsWordChar(ß) = true")
}
if IsWordChar(")") == true {
t.Errorf("IsWordChar()) = true")
}
if IsWordChar("\n") == true {
t.Errorf("IsWordChar(\n)) = true")
}
}

View File

@@ -456,14 +456,19 @@ func (v *View) HandleEvent(event tcell.Event) {
if v.doubleClick {
// Triple click
v.lastClickTime = time.Now()
v.tripleClick = true
v.doubleClick = false
v.cursor.SelectLine()
} else {
// Double click
v.lastClickTime = time.Now()
v.doubleClick = true
v.tripleClick = false
v.lastClickTime = time.Now()
v.cursor.SelectWord()
}
} else {
v.doubleClick = false
@@ -478,7 +483,7 @@ func (v *View) HandleEvent(event tcell.Event) {
if v.tripleClick {
v.cursor.AddLineToSelection()
} else if v.doubleClick {
v.cursor.AddWordToSelection()
} else {
v.cursor.curSelection[1] = v.cursor.Loc()
}

View File

@@ -8,10 +8,6 @@
- [ ] Search and replace
- [ ] Better selection
- [ ] Double click selects current word
- [x] Triple click enables line selection
- [ ] More keybindings
- [x] Page up and page down
- [x] CtrlA for select all
@@ -40,6 +36,10 @@
- [x] Help screen which lists keybindings and commands
- [x] Opened with Ctrl-h
- [x] Better selection
- [x] Double click selects current word
- [x] Triple click enables line selection
- [x] Options
- [x] Colorscheme
- [x] tab size