Skip to content

Commit

Permalink
Merge pull request #2665 from masmu/feature/sub-words
Browse files Browse the repository at this point in the history
Implemented sub-word cursor movement
  • Loading branch information
JoeKar committed May 22, 2024
2 parents 9176508 + 78fcf2f commit 35630aa
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 15 deletions.
64 changes: 62 additions & 2 deletions internal/action/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,22 @@ func (h *BufPane) WordLeft() bool {
return true
}

// SubWordRight moves the cursor one sub-word to the right
func (h *BufPane) SubWordRight() bool {
h.Cursor.Deselect(false)
h.Cursor.SubWordRight()
h.Relocate()
return true
}

// SubWordLeft moves the cursor one sub-word to the left
func (h *BufPane) SubWordLeft() bool {
h.Cursor.Deselect(true)
h.Cursor.SubWordLeft()
h.Relocate()
return true
}

// SelectUp selects up one line
func (h *BufPane) SelectUp() bool {
if !h.Cursor.HasSelection() {
Expand Down Expand Up @@ -359,6 +375,28 @@ func (h *BufPane) SelectWordLeft() bool {
return true
}

// SelectSubWordRight selects the sub-word to the right of the cursor
func (h *BufPane) SelectSubWordRight() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.SubWordRight()
h.Cursor.SelectTo(h.Cursor.Loc)
h.Relocate()
return true
}

// SelectSubWordLeft selects the sub-word to the left of the cursor
func (h *BufPane) SelectSubWordLeft() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.SubWordLeft()
h.Cursor.SelectTo(h.Cursor.Loc)
h.Relocate()
return true
}

// StartOfText moves the cursor to the start of the text of the line
func (h *BufPane) StartOfText() bool {
h.Cursor.Deselect(true)
Expand Down Expand Up @@ -622,6 +660,28 @@ func (h *BufPane) DeleteWordLeft() bool {
return true
}

// DeleteSubWordRight deletes the sub-word to the right of the cursor
func (h *BufPane) DeleteSubWordRight() bool {
h.SelectSubWordRight()
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
h.Relocate()
return true
}

// DeleteSubWordLeft deletes the sub-word to the left of the cursor
func (h *BufPane) DeleteSubWordLeft() bool {
h.SelectSubWordLeft()
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
h.Relocate()
return true
}

// Delete deletes the next character
func (h *BufPane) Delete() bool {
if h.Cursor.HasSelection() {
Expand Down Expand Up @@ -745,8 +805,8 @@ func (h *BufPane) Autocomplete() bool {
}
r := h.Cursor.RuneUnder(h.Cursor.X)
prev := h.Cursor.RuneUnder(h.Cursor.X - 1)
if !util.IsAutocomplete(prev) || !util.IsNonAlphaNumeric(r) {
// don't autocomplete if cursor is on alpha numeric character (middle of a word)
if !util.IsAutocomplete(prev) || util.IsWordChar(r) {
// don't autocomplete if cursor is within a word
return false
}

Expand Down
12 changes: 12 additions & 0 deletions internal/action/bufpane.go
Original file line number Diff line number Diff line change
Expand Up @@ -746,10 +746,16 @@ var BufKeyActions = map[string]BufKeyAction{
"SelectRight": (*BufPane).SelectRight,
"WordRight": (*BufPane).WordRight,
"WordLeft": (*BufPane).WordLeft,
"SubWordRight": (*BufPane).SubWordRight,
"SubWordLeft": (*BufPane).SubWordLeft,
"SelectWordRight": (*BufPane).SelectWordRight,
"SelectWordLeft": (*BufPane).SelectWordLeft,
"SelectSubWordRight": (*BufPane).SelectSubWordRight,
"SelectSubWordLeft": (*BufPane).SelectSubWordLeft,
"DeleteWordRight": (*BufPane).DeleteWordRight,
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
"DeleteSubWordRight": (*BufPane).DeleteSubWordRight,
"DeleteSubWordLeft": (*BufPane).DeleteSubWordLeft,
"SelectLine": (*BufPane).SelectLine,
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
Expand Down Expand Up @@ -876,10 +882,16 @@ var MultiActions = map[string]bool{
"SelectRight": true,
"WordRight": true,
"WordLeft": true,
"SubWordRight": true,
"SubWordLeft": true,
"SelectWordRight": true,
"SelectWordLeft": true,
"SelectSubWordRight": true,
"SelectSubWordLeft": true,
"DeleteWordRight": true,
"DeleteWordLeft": true,
"DeleteSubWordRight": true,
"DeleteSubWordLeft": true,
"SelectLine": true,
"SelectToStartOfLine": true,
"SelectToStartOfText": true,
Expand Down
8 changes: 4 additions & 4 deletions internal/buffer/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ func (b *Buffer) GetWord() ([]byte, int) {
return []byte{}, -1
}

if util.IsNonAlphaNumeric(b.RuneAt(c.Loc.Move(-1, b))) {
if util.IsNonWordChar(b.RuneAt(c.Loc.Move(-1, b))) {
return []byte{}, c.X
}

args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
args := bytes.FieldsFunc(l, util.IsNonWordChar)
input := args[len(args)-1]
return input, c.X - util.CharacterCount(input)
}
Expand Down Expand Up @@ -166,7 +166,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
var suggestions []string
for i := c.Y; i >= 0; i-- {
l := b.LineBytes(i)
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
words := bytes.FieldsFunc(l, util.IsNonWordChar)
for _, w := range words {
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
strw := string(w)
Expand All @@ -179,7 +179,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
}
for i := c.Y + 1; i < b.LinesNum(); i++ {
l := b.LineBytes(i)
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
words := bytes.FieldsFunc(l, util.IsNonWordChar)
for _, w := range words {
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
strw := string(w)
Expand Down
143 changes: 143 additions & 0 deletions internal/buffer/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,17 @@ func (c *Cursor) WordRight() {
}
c.Right()
}
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) &&
util.IsNonWordChar(c.RuneUnder(c.X+1)) {
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
c.Right()
}
return
}
c.Right()
for util.IsWordChar(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
Expand All @@ -428,6 +439,17 @@ func (c *Cursor) WordLeft() {
}
c.Left()
}
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) &&
util.IsNonWordChar(c.RuneUnder(c.X-1)) {
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
c.Right()
return
}
c.Left()
for util.IsWordChar(c.RuneUnder(c.X)) {
if c.X == 0 {
Expand All @@ -438,6 +460,127 @@ func (c *Cursor) WordLeft() {
c.Right()
}

// SubWordRight moves the cursor one sub-word to the right
func (c *Cursor) SubWordRight() {
if util.IsWhitespace(c.RuneUnder(c.X)) {
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
c.Right()
}
return
}
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
c.Right()
}
return
}
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
c.Right()
}
if util.IsWhitespace(c.RuneUnder(c.X)) {
return
}
}
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
return
}
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
util.IsUpperLetter(c.RuneUnder(c.X+1)) {
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
return
}
c.Right()
}
if util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
c.Left()
}
} else {
c.Right()
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
return
}
c.Right()
}
}
}

// SubWordLeft moves the cursor one sub-word to the left
func (c *Cursor) SubWordLeft() {
c.Left()
if util.IsWhitespace(c.RuneUnder(c.X)) {
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
c.Right()
return
}
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
c.Right()
return
}
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
if util.IsWhitespace(c.RuneUnder(c.X)) {
c.Right()
return
}
}
if c.X == 0 {
return
}
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
util.IsUpperLetter(c.RuneUnder(c.X-1)) {
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
if !util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
c.Right()
}
} else {
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
if !util.IsAlphanumeric(c.RuneUnder(c.X)) {
c.Right()
}
}
}

// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
line := c.buf.LineBytes(c.Y)
Expand Down

0 comments on commit 35630aa

Please sign in to comment.