Skip to content

Commit

Permalink
liner, word separators: add configurable word separators
Browse files Browse the repository at this point in the history
  • Loading branch information
reinerRubin committed Sep 23, 2018
1 parent 162544a commit b84b4fd
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 32 deletions.
42 changes: 26 additions & 16 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,23 @@ import (
)

type commonState struct {
terminalSupported bool
outputRedirected bool
inputRedirected bool
history []string
historyMutex sync.RWMutex
completer WordCompleter
columns int
killRing *ring.Ring
ctrlCAborts bool
r *bufio.Reader
tabStyle TabStyle
multiLineMode bool
cursorRows int
maxRows int
shouldRestart ShouldRestart
needRefresh bool
terminalSupported bool
outputRedirected bool
inputRedirected bool
history []string
historyMutex sync.RWMutex
completer WordCompleter
columns int
killRing *ring.Ring
ctrlCAborts bool
r *bufio.Reader
tabStyle TabStyle
multiLineMode bool
cursorRows int
maxRows int
shouldRestart ShouldRestart
needRefresh bool
wordSeparatorChecker WordSeparatorChecker
}

// TabStyle is used to select how tab completions are displayed.
Expand Down Expand Up @@ -253,3 +254,12 @@ func (s *State) promptUnsupported(p string) (string, error) {
}
return string(linebuf), nil
}

// WordSeparatorChecker returns true if a rune should be consider as a word separator
type WordSeparatorChecker func(r rune) bool

// SetWordSeparatorChecker sets a function that Liner will call to determine
// the end/beginning of a word
func (s *State) SetWordSeparatorChecker(ch WordSeparatorChecker) {
s.wordSeparatorChecker = ch
}
24 changes: 16 additions & 8 deletions line.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ mainLoop:
// Remove word separators to the left
var buf []rune // Store the deleted chars in a buffer
for {
if pos == 0 || !isWordSeparator(line[pos-1]) {
if pos == 0 || !s.isWordSeparator(line[pos-1]) {
break
}
buf = append(buf, line[pos-1])
Expand All @@ -828,7 +828,7 @@ mainLoop:
}
// Remove non-word separators to the left
for {
if pos == 0 || isWordSeparator(line[pos-1]) {
if pos == 0 || s.isWordSeparator(line[pos-1]) {
break
}
buf = append(buf, line[pos-1])
Expand Down Expand Up @@ -906,10 +906,10 @@ mainLoop:
if leftKnown {
atWordSeparator = wordSeparatorLeft
} else {
atWordSeparator = isWordSeparator(line[pos])
atWordSeparator = s.isWordSeparator(line[pos])
}

wordSeparatorLeft = isWordSeparator(line[pos-1])
wordSeparatorLeft = s.isWordSeparator(line[pos-1])
leftKnown = true

if !atWordSeparator && wordSeparatorLeft {
Expand All @@ -936,10 +936,10 @@ mainLoop:
if hereKnown {
wordSeparatorLeft = atWordSeparator
} else {
wordSeparatorLeft = isWordSeparator(line[pos-1])
wordSeparatorLeft = s.isWordSeparator(line[pos-1])
}

atWordSeparator = isWordSeparator(line[pos])
atWordSeparator = s.isWordSeparator(line[pos])
hereKnown = true

if atWordSeparator && !wordSeparatorLeft {
Expand Down Expand Up @@ -996,15 +996,15 @@ mainLoop:
// Remove word separators to the right
var buf []rune // Store the deleted chars in a buffer
for {
if pos == len(line) || !isWordSeparator(line[pos]) {
if pos == len(line) || !s.isWordSeparator(line[pos]) {
break
}
buf = append(buf, line[pos])
line = append(line[:pos], line[pos+1:]...)
}
// Remove non-word separators to the right
for {
if pos == len(line) || isWordSeparator(line[pos]) {
if pos == len(line) || s.isWordSeparator(line[pos]) {
break
}
buf = append(buf, line[pos])
Expand Down Expand Up @@ -1175,3 +1175,11 @@ func (s *State) tooNarrow(prompt string) (string, error) {
}
return s.promptUnsupported(prompt)
}

func (s *State) isWordSeparator(r rune) bool {
if s.wordSeparatorChecker == nil {
s.wordSeparatorChecker = SpaceWordSeparatorChecker
}

return s.wordSeparatorChecker(r)
}
8 changes: 0 additions & 8 deletions rune.go

This file was deleted.

30 changes: 30 additions & 0 deletions word_separator_checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package liner

import "unicode"

// SpaceWordSeparatorChecker (default) returns true if r is a unicode-space
func SpaceWordSeparatorChecker(r rune) bool {
return unicode.IsSpace(r)
}

// PunctWordSeparatorChecker returns true if r is a unicode punctuation character
func PunctWordSeparatorChecker(r rune) bool {
return unicode.IsPunct(r)
}

// CombineWordSeparatorChecker combines checkers
// eg. line.SetWordSeparatorChecker(liner.CombineWordSeparatorChecker(
// liner.SpaceWordSeparatorChecker,
// liner.PunctWordSeparatorChecker,
// ))
func CombineWordSeparatorChecker(checkers ...WordSeparatorChecker) WordSeparatorChecker {
return func(r rune) bool {
for _, isSeparator := range checkers {
if isSeparator(r) {
return true
}
}

return false
}
}
42 changes: 42 additions & 0 deletions word_separator_checker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package liner

import (
"testing"
)

func TestSeparators(t *testing.T) {
// test spaces
spaces := []rune{' ', ' '}
for _, r := range spaces {
if !SpaceWordSeparatorChecker(r) {
t.Errorf("'%s' was not recognized by the space separator", string(r))
}
}

// test punctuation character
puncts := []rune{'(', ')', '{', '}'} // etc
for _, r := range puncts {
if !PunctWordSeparatorChecker(r) {
t.Errorf(`'%s' was not recognized by the "punctuation" separator`, string(r))
}
}

// test combination of them
combinedChecker := CombineWordSeparatorChecker(
SpaceWordSeparatorChecker,
PunctWordSeparatorChecker,
)
for _, r := range append(puncts, spaces...) {
if !combinedChecker(r) {
t.Errorf("'%s' was not recognized by the combined separator", string(r))
}
}

// test some letters
justLetters := []rune{'a', 'b', 'c'}
for _, r := range justLetters {
if combinedChecker(r) {
t.Errorf("'%s' was recognized as a space or a punctuation char", string(r))
}
}
}

0 comments on commit b84b4fd

Please sign in to comment.