Skip to content

Commit 5dbdf8c

Browse files
committed
Implemented SubWordRight, SubWordLeft, SelectSubWordRight, SelectSubWordLeft and DeleteSubWordRight, DeleteSubWordLeft
1 parent 889a841 commit 5dbdf8c

File tree

5 files changed

+253
-3
lines changed

5 files changed

+253
-3
lines changed

internal/action/actions.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,22 @@ func (h *BufPane) WordLeft() bool {
283283
return true
284284
}
285285

286+
// SubWordRight moves the cursor one sub-word to the right
287+
func (h *BufPane) SubWordRight() bool {
288+
h.Cursor.Deselect(false)
289+
h.Cursor.SubWordRight()
290+
h.Relocate()
291+
return true
292+
}
293+
294+
// SubWordLeft moves the cursor one sub-word to the left
295+
func (h *BufPane) SubWordLeft() bool {
296+
h.Cursor.Deselect(true)
297+
h.Cursor.SubWordLeft()
298+
h.Relocate()
299+
return true
300+
}
301+
286302
// SelectUp selects up one line
287303
func (h *BufPane) SelectUp() bool {
288304
if !h.Cursor.HasSelection() {
@@ -359,6 +375,28 @@ func (h *BufPane) SelectWordLeft() bool {
359375
return true
360376
}
361377

378+
// SelectSubWordRight selects the sub-word to the right of the cursor
379+
func (h *BufPane) SelectSubWordRight() bool {
380+
if !h.Cursor.HasSelection() {
381+
h.Cursor.OrigSelection[0] = h.Cursor.Loc
382+
}
383+
h.Cursor.SubWordRight()
384+
h.Cursor.SelectTo(h.Cursor.Loc)
385+
h.Relocate()
386+
return true
387+
}
388+
389+
// SelectSubWordLeft selects the sub-word to the left of the cursor
390+
func (h *BufPane) SelectSubWordLeft() bool {
391+
if !h.Cursor.HasSelection() {
392+
h.Cursor.OrigSelection[0] = h.Cursor.Loc
393+
}
394+
h.Cursor.SubWordLeft()
395+
h.Cursor.SelectTo(h.Cursor.Loc)
396+
h.Relocate()
397+
return true
398+
}
399+
362400
// StartOfText moves the cursor to the start of the text of the line
363401
func (h *BufPane) StartOfText() bool {
364402
h.Cursor.Deselect(true)
@@ -622,6 +660,28 @@ func (h *BufPane) DeleteWordLeft() bool {
622660
return true
623661
}
624662

663+
// DeleteSubWordRight deletes the sub-word to the right of the cursor
664+
func (h *BufPane) DeleteSubWordRight() bool {
665+
h.SelectSubWordRight()
666+
if h.Cursor.HasSelection() {
667+
h.Cursor.DeleteSelection()
668+
h.Cursor.ResetSelection()
669+
}
670+
h.Relocate()
671+
return true
672+
}
673+
674+
// DeleteSubWordLeft deletes the sub-word to the left of the cursor
675+
func (h *BufPane) DeleteSubWordLeft() bool {
676+
h.SelectSubWordLeft()
677+
if h.Cursor.HasSelection() {
678+
h.Cursor.DeleteSelection()
679+
h.Cursor.ResetSelection()
680+
}
681+
h.Relocate()
682+
return true
683+
}
684+
625685
// Delete deletes the next character
626686
func (h *BufPane) Delete() bool {
627687
if h.Cursor.HasSelection() {

internal/action/bufpane.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,10 +746,16 @@ var BufKeyActions = map[string]BufKeyAction{
746746
"SelectRight": (*BufPane).SelectRight,
747747
"WordRight": (*BufPane).WordRight,
748748
"WordLeft": (*BufPane).WordLeft,
749+
"SubWordRight": (*BufPane).SubWordRight,
750+
"SubWordLeft": (*BufPane).SubWordLeft,
749751
"SelectWordRight": (*BufPane).SelectWordRight,
750752
"SelectWordLeft": (*BufPane).SelectWordLeft,
753+
"SelectSubWordRight": (*BufPane).SelectSubWordRight,
754+
"SelectSubWordLeft": (*BufPane).SelectSubWordLeft,
751755
"DeleteWordRight": (*BufPane).DeleteWordRight,
752756
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
757+
"DeleteSubWordRight": (*BufPane).DeleteSubWordRight,
758+
"DeleteSubWordLeft": (*BufPane).DeleteSubWordLeft,
753759
"SelectLine": (*BufPane).SelectLine,
754760
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
755761
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
@@ -876,10 +882,16 @@ var MultiActions = map[string]bool{
876882
"SelectRight": true,
877883
"WordRight": true,
878884
"WordLeft": true,
885+
"SubWordRight": true,
886+
"SubWordLeft": true,
879887
"SelectWordRight": true,
880888
"SelectWordLeft": true,
889+
"SelectSubWordRight": true,
890+
"SelectSubWordLeft": true,
881891
"DeleteWordRight": true,
882892
"DeleteWordLeft": true,
893+
"DeleteSubWordRight": true,
894+
"DeleteSubWordLeft": true,
883895
"SelectLine": true,
884896
"SelectToStartOfLine": true,
885897
"SelectToStartOfText": true,

internal/buffer/cursor.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,127 @@ func (c *Cursor) WordLeft() {
438438
c.Right()
439439
}
440440

441+
// SubWordRight moves the cursor one sub-word to the right
442+
func (c *Cursor) SubWordRight() {
443+
if util.IsWhitespace(c.RuneUnder(c.X)) {
444+
for util.IsWhitespace(c.RuneUnder(c.X)) {
445+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
446+
c.Right()
447+
return
448+
}
449+
c.Right()
450+
}
451+
return
452+
}
453+
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
454+
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
455+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
456+
c.Right()
457+
return
458+
}
459+
c.Right()
460+
}
461+
return
462+
}
463+
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
464+
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
465+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
466+
c.Right()
467+
return
468+
}
469+
c.Right()
470+
}
471+
if util.IsWhitespace(c.RuneUnder(c.X)) {
472+
return
473+
}
474+
}
475+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
476+
return
477+
}
478+
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
479+
util.IsUpperLetter(c.RuneUnder(c.X+1)) {
480+
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
481+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
482+
return
483+
}
484+
c.Right()
485+
}
486+
if util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
487+
c.Left()
488+
}
489+
} else {
490+
c.Right()
491+
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
492+
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
493+
return
494+
}
495+
c.Right()
496+
}
497+
}
498+
}
499+
500+
// SubWordLeft moves the cursor one sub-word to the left
501+
func (c *Cursor) SubWordLeft() {
502+
c.Left()
503+
if util.IsWhitespace(c.RuneUnder(c.X)) {
504+
for util.IsWhitespace(c.RuneUnder(c.X)) {
505+
if c.X == 0 {
506+
return
507+
}
508+
c.Left()
509+
}
510+
c.Right()
511+
return
512+
}
513+
if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
514+
for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) {
515+
if c.X == 0 {
516+
return
517+
}
518+
c.Left()
519+
}
520+
c.Right()
521+
return
522+
}
523+
if util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
524+
for util.IsSubwordDelimiter(c.RuneUnder(c.X)) {
525+
if c.X == 0 {
526+
return
527+
}
528+
c.Left()
529+
}
530+
if util.IsWhitespace(c.RuneUnder(c.X)) {
531+
c.Right()
532+
return
533+
}
534+
}
535+
if c.X == 0 {
536+
return
537+
}
538+
if util.IsUpperLetter(c.RuneUnder(c.X)) &&
539+
util.IsUpperLetter(c.RuneUnder(c.X-1)) {
540+
for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
541+
if c.X == 0 {
542+
return
543+
}
544+
c.Left()
545+
}
546+
if !util.IsUpperAlphanumeric(c.RuneUnder(c.X)) {
547+
c.Right()
548+
}
549+
} else {
550+
for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) {
551+
if c.X == 0 {
552+
return
553+
}
554+
c.Left()
555+
}
556+
if !util.IsAlphanumeric(c.RuneUnder(c.X)) {
557+
c.Right()
558+
}
559+
}
560+
}
561+
441562
// RuneUnder returns the rune under the given x position
442563
func (c *Cursor) RuneUnder(x int) rune {
443564
line := c.buf.LineBytes(c.Y)

internal/util/util.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,18 +219,69 @@ func FSize(f *os.File) int64 {
219219
}
220220

221221
// IsWordChar returns whether or not a rune is a 'word character'
222-
// Word characters are defined as numbers, letters or '_'
222+
// Word characters are defined as numbers, letters or sub-word delimiters
223223
func IsWordChar(r rune) bool {
224-
return unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_'
224+
return IsAlphanumeric(r) || IsSubwordDelimiter(r)
225225
}
226226

227227
// IsNonWordChar returns whether or not a rune is not a 'word character'
228-
// Non word characters are defined as all characters not being numbers, letters or '_'
228+
// Non word characters are defined as all characters not being numbers, letters or sub-word delimiters
229229
// See IsWordChar()
230230
func IsNonWordChar(r rune) bool {
231231
return !IsWordChar(r)
232232
}
233233

234+
// IsUpperWordChar returns whether or not a rune is an 'upper word character'
235+
// Upper word characters are defined as numbers, upper-case letters or sub-word delimiters
236+
func IsUpperWordChar(r rune) bool {
237+
return IsUpperAlphanumeric(r) || IsSubwordDelimiter(r)
238+
}
239+
240+
// IsLowerWordChar returns whether or not a rune is a 'lower word character'
241+
// Lower word characters are defined as numbers, lower-case letters or sub-word delimiters
242+
func IsLowerWordChar(r rune) bool {
243+
return IsLowerAlphanumeric(r) || IsSubwordDelimiter(r)
244+
}
245+
246+
// IsSubwordDelimiter returns whether or not a rune is a 'sub-word delimiter character'
247+
// i.e. is considered a part of the word and is used as a delimiter between sub-words of the word.
248+
// For now the only sub-word delimiter character is '_'.
249+
func IsSubwordDelimiter(r rune) bool {
250+
return r == '_'
251+
}
252+
253+
// IsAlphanumeric returns whether or not a rune is an 'alphanumeric character'
254+
// Alphanumeric characters are defined as numbers or letters
255+
func IsAlphanumeric(r rune) bool {
256+
return unicode.IsLetter(r) || unicode.IsNumber(r)
257+
}
258+
259+
// IsUpperAlphanumeric returns whether or not a rune is an 'upper alphanumeric character'
260+
// Upper alphanumeric characters are defined as numbers or upper-case letters
261+
func IsUpperAlphanumeric(r rune) bool {
262+
return IsUpperLetter(r) || unicode.IsNumber(r)
263+
}
264+
265+
// IsLowerAlphanumeric returns whether or not a rune is a 'lower alphanumeric character'
266+
// Lower alphanumeric characters are defined as numbers or lower-case letters
267+
func IsLowerAlphanumeric(r rune) bool {
268+
return IsLowerLetter(r) || unicode.IsNumber(r)
269+
}
270+
271+
// IsUpperLetter returns whether or not a rune is an 'upper letter character'
272+
// Upper letter characters are defined as upper-case letters
273+
func IsUpperLetter(r rune) bool {
274+
// unicode.IsUpper() returns true for letters only
275+
return unicode.IsUpper(r)
276+
}
277+
278+
// IsLowerLetter returns whether or not a rune is a 'lower letter character'
279+
// Lower letter characters are defined as lower-case letters
280+
func IsLowerLetter(r rune) bool {
281+
// unicode.IsLower() returns true for letters only
282+
return unicode.IsLower(r)
283+
}
284+
234285
// Spaces returns a string with n spaces
235286
func Spaces(n int) string {
236287
return strings.Repeat(" ", n)

runtime/help/keybindings.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,18 @@ SelectToStartOfText
178178
SelectToStartOfTextToggle
179179
WordRight
180180
WordLeft
181+
SubWordRight
182+
SubWordLeft
181183
SelectWordRight
182184
SelectWordLeft
185+
SelectSubWordRight
186+
SelectSubWordLeft
183187
MoveLinesUp
184188
MoveLinesDown
185189
DeleteWordRight
186190
DeleteWordLeft
191+
DeleteSubWordRight
192+
DeleteSubWordLeft
187193
SelectLine
188194
SelectToStartOfLine
189195
SelectToEndOfLine

0 commit comments

Comments
 (0)