Move some strings utilities to internal/strings
This commit is contained in:
parent
24941a6503
commit
6916c5f66d
96
document.go
96
document.go
@ -5,6 +5,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/c-bata/go-prompt/internal/bisect"
|
"github.com/c-bata/go-prompt/internal/bisect"
|
||||||
|
istrings "github.com/c-bata/go-prompt/internal/strings"
|
||||||
runewidth "github.com/mattn/go-runewidth"
|
runewidth "github.com/mattn/go-runewidth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ func (d *Document) FindStartOfPreviousWord() int {
|
|||||||
// The only difference is to ignore contiguous spaces.
|
// The only difference is to ignore contiguous spaces.
|
||||||
func (d *Document) FindStartOfPreviousWordWithSpace() int {
|
func (d *Document) FindStartOfPreviousWordWithSpace() int {
|
||||||
x := d.TextBeforeCursor()
|
x := d.TextBeforeCursor()
|
||||||
end := lastIndexByteNot(x, ' ')
|
end := istrings.LastIndexNotByte(x, ' ')
|
||||||
if end == -1 {
|
if end == -1 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -168,7 +169,7 @@ func (d *Document) FindStartOfPreviousWordUntilSeparatorIgnoreNextToCursor(sep s
|
|||||||
}
|
}
|
||||||
|
|
||||||
x := d.TextBeforeCursor()
|
x := d.TextBeforeCursor()
|
||||||
end := lastIndexAnyNot(x, sep)
|
end := istrings.LastIndexNotAny(x, sep)
|
||||||
if end == -1 {
|
if end == -1 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -195,7 +196,7 @@ func (d *Document) FindEndOfCurrentWord() int {
|
|||||||
func (d *Document) FindEndOfCurrentWordWithSpace() int {
|
func (d *Document) FindEndOfCurrentWordWithSpace() int {
|
||||||
x := d.TextAfterCursor()
|
x := d.TextAfterCursor()
|
||||||
|
|
||||||
start := indexByteNot(x, ' ')
|
start := istrings.IndexNotByte(x, ' ')
|
||||||
if start == -1 {
|
if start == -1 {
|
||||||
return len(x)
|
return len(x)
|
||||||
}
|
}
|
||||||
@ -232,7 +233,7 @@ func (d *Document) FindEndOfCurrentWordUntilSeparatorIgnoreNextToCursor(sep stri
|
|||||||
|
|
||||||
x := d.TextAfterCursor()
|
x := d.TextAfterCursor()
|
||||||
|
|
||||||
start := indexAnyNot(x, sep)
|
start := istrings.IndexNotAny(x, sep)
|
||||||
if start == -1 {
|
if start == -1 {
|
||||||
return len(x)
|
return len(x)
|
||||||
}
|
}
|
||||||
@ -432,90 +433,3 @@ func (d *Document) leadingWhitespaceInCurrentLine() (margin string) {
|
|||||||
margin = d.CurrentLine()[:len(d.CurrentLine())-len(trimmed)]
|
margin = d.CurrentLine()[:len(d.CurrentLine())-len(trimmed)]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexByteNot(s string, c byte) int {
|
|
||||||
n := len(s)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if s[i] != c {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func lastIndexByteNot(s string, c byte) int {
|
|
||||||
for i := len(s) - 1; i >= 0; i-- {
|
|
||||||
if s[i] != c {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
type asciiSet [8]uint32
|
|
||||||
|
|
||||||
func (as *asciiSet) notContains(c byte) bool {
|
|
||||||
return (as[c>>5] & (1 << uint(c&31))) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeASCIISet(chars string) (as asciiSet, ok bool) {
|
|
||||||
for i := 0; i < len(chars); i++ {
|
|
||||||
c := chars[i]
|
|
||||||
if c >= utf8.RuneSelf {
|
|
||||||
return as, false
|
|
||||||
}
|
|
||||||
as[c>>5] |= 1 << uint(c&31)
|
|
||||||
}
|
|
||||||
return as, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexAnyNot(s, chars string) int {
|
|
||||||
if len(chars) > 0 {
|
|
||||||
if len(s) > 8 {
|
|
||||||
if as, isASCII := makeASCIISet(chars); isASCII {
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if as.notContains(s[i]) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < len(s); {
|
|
||||||
// I don't know why strings.IndexAny doesn't add rune count here.
|
|
||||||
r, size := utf8.DecodeRuneInString(s[i:])
|
|
||||||
i += size
|
|
||||||
for _, c := range chars {
|
|
||||||
if r != c {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func lastIndexAnyNot(s, chars string) int {
|
|
||||||
if len(chars) > 0 {
|
|
||||||
if len(s) > 8 {
|
|
||||||
if as, isASCII := makeASCIISet(chars); isASCII {
|
|
||||||
for i := len(s) - 1; i >= 0; i-- {
|
|
||||||
if as.notContains(s[i]) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := len(s); i > 0; {
|
|
||||||
r, size := utf8.DecodeLastRuneInString(s[:i])
|
|
||||||
i -= size
|
|
||||||
for _, c := range chars {
|
|
||||||
if r != c {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
110
internal/strings/strings.go
Normal file
110
internal/strings/strings.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package strings
|
||||||
|
|
||||||
|
import "unicode/utf8"
|
||||||
|
|
||||||
|
// IndexNotByte is similar with strings.IndexByte but returns
|
||||||
|
// the index of the first instance of character except c in s.
|
||||||
|
// or -1 if s only contains c.
|
||||||
|
func IndexNotByte(s string, c byte) int {
|
||||||
|
n := len(s)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if s[i] != c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastIndexByte is similar with strings.IndexByte but returns
|
||||||
|
// the index of the last instance of character except c in s,
|
||||||
|
// or -1 if s only contains c.
|
||||||
|
func LastIndexNotByte(s string, c byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] != c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
type asciiSet [8]uint32
|
||||||
|
|
||||||
|
func (as *asciiSet) notContains(c byte) bool {
|
||||||
|
return (as[c>>5] & (1 << uint(c&31))) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeASCIISet(chars string) (as asciiSet, ok bool) {
|
||||||
|
for i := 0; i < len(chars); i++ {
|
||||||
|
c := chars[i]
|
||||||
|
if c >= utf8.RuneSelf {
|
||||||
|
return as, false
|
||||||
|
}
|
||||||
|
as[c>>5] |= 1 << uint(c&31)
|
||||||
|
}
|
||||||
|
return as, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexNotAny is similar with strings.IndexAny but returns
|
||||||
|
// the index of the first instance of any Unicode code point from chars in s,
|
||||||
|
// or -1 if no Unicode code point from chars is present in s.
|
||||||
|
func IndexNotAny(s, chars string) int {
|
||||||
|
if len(chars) > 0 {
|
||||||
|
if len(s) > 8 {
|
||||||
|
if as, isASCII := makeASCIISet(chars); isASCII {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if as.notContains(s[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelFirstLoop:
|
||||||
|
for i, c := range s {
|
||||||
|
for j, m := range chars {
|
||||||
|
if c != m && j == len(chars)-1 {
|
||||||
|
return i
|
||||||
|
} else if c != m {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
continue LabelFirstLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastIndexAny returns the index of the last instance of any Unicode code
|
||||||
|
// point from chars in s, or -1 if no Unicode code point from chars is
|
||||||
|
// present in s.
|
||||||
|
func LastIndexNotAny(s, chars string) int {
|
||||||
|
if len(chars) > 0 {
|
||||||
|
if len(s) > 8 {
|
||||||
|
if as, isASCII := makeASCIISet(chars); isASCII {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if as.notContains(s[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LabelFirstLoop:
|
||||||
|
for i := len(s); i > 0; {
|
||||||
|
r, size := utf8.DecodeLastRuneInString(s[:i])
|
||||||
|
i -= size
|
||||||
|
for j, m := range chars {
|
||||||
|
if r != m && j == len(chars)-1 {
|
||||||
|
return i
|
||||||
|
} else if r != m {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
continue LabelFirstLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
47
internal/strings/strings_test.go
Normal file
47
internal/strings/strings_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package strings_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/c-bata/go-prompt/internal/strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleIndexNotByte() {
|
||||||
|
fmt.Println(strings.IndexNotByte("golang", 'g'))
|
||||||
|
fmt.Println(strings.IndexNotByte("golang", 'x'))
|
||||||
|
fmt.Println(strings.IndexNotByte("gggggg", 'g'))
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
// 0
|
||||||
|
// -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLastIndexNotByte() {
|
||||||
|
fmt.Println(strings.LastIndexNotByte("golang", 'g'))
|
||||||
|
fmt.Println(strings.LastIndexNotByte("golang", 'x'))
|
||||||
|
fmt.Println(strings.LastIndexNotByte("gggggg", 'g'))
|
||||||
|
// Output:
|
||||||
|
// 4
|
||||||
|
// 5
|
||||||
|
// -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleIndexNotAny() {
|
||||||
|
fmt.Println(strings.IndexNotAny("golang", "glo"))
|
||||||
|
fmt.Println(strings.IndexNotAny("golang", "gl"))
|
||||||
|
fmt.Println(strings.IndexNotAny("golang", "golang"))
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
// 1
|
||||||
|
// -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLastIndexNotAny() {
|
||||||
|
fmt.Println(strings.LastIndexNotAny("golang", "agn"))
|
||||||
|
fmt.Println(strings.LastIndexNotAny("golang", "an"))
|
||||||
|
fmt.Println(strings.LastIndexNotAny("golang", "golang"))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
// 5
|
||||||
|
// -1
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user