diff --git a/pkg/gocui/text_area.go b/pkg/gocui/text_area.go index 98a5af4da3b..1b314d900bd 100644 --- a/pkg/gocui/text_area.go +++ b/pkg/gocui/text_area.go @@ -315,6 +315,10 @@ func (self *TextArea) MoveCursorRight() { self.cursor += len(s) } +func isASCIIChar(ch string) bool { + return len(ch) == 1 && ch[0] <= 127 +} + func (self *TextArea) newCursorForMoveLeftWord() int { if self.cursor == 0 { return 0 @@ -333,8 +337,14 @@ func (self *TextArea) newCursorForMoveLeftWord() int { separators = true } if !separators { - for cellCursor > 0 && self.cells[cellCursor-1].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor-1].char) { + if cellCursor > 0 && self.cells[cellCursor-1].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor-1].char) { + isASCII := isASCIIChar(self.cells[cellCursor-1].char) + // skip the first char regardless of isAscii. cellCursor-- + for cellCursor > 0 && self.cells[cellCursor-1].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor-1].char) && isASCIIChar(self.cells[cellCursor-1].char) == isASCII { + // skip consecutive characters of the same kind (ASCII or non-ASCII) as the first character. + cellCursor-- + } } } @@ -363,8 +373,14 @@ func (self *TextArea) newCursorForMoveRightWord() int { separators = true } if !separators { - for cellCursor < len(self.cells) && self.cells[cellCursor].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor].char) { + if cellCursor < len(self.cells) && self.cells[cellCursor].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor].char) { + isASCII := isASCIIChar(self.cells[cellCursor].char) + // skip the first char regardless of isAscii. cellCursor++ + for cellCursor < len(self.cells) && self.cells[cellCursor].char != "\n" && !strings.Contains(WHITESPACES+WORD_SEPARATORS, self.cells[cellCursor].char) && isASCIIChar(self.cells[cellCursor].char) == isASCII { + // skip consecutive characters of the same kind (ASCII or non-ASCII) as the first character. + cellCursor++ + } } } diff --git a/pkg/gocui/text_area_test.go b/pkg/gocui/text_area_test.go index f66876d2433..d2d82e0efff 100644 --- a/pkg/gocui/text_area_test.go +++ b/pkg/gocui/text_area_test.go @@ -642,6 +642,123 @@ func TestTextArea(t *testing.T) { expectedCursor: 0, expectedClipboard: "", }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.BackSpaceWord() + }, + expectedContent: "字", + expectedCursor: 3, + expectedClipboard: "abc", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc字") + textarea.BackSpaceWord() + }, + expectedContent: "abc", + expectedCursor: 3, + expectedClipboard: "字", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.BackSpaceWord() + textarea.BackSpaceWord() + }, + expectedContent: "", + expectedCursor: 0, + expectedClipboard: "字", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc 字abc") + textarea.BackSpaceWord() + }, + expectedContent: "abc 字", + expectedCursor: 7, + expectedClipboard: "abc", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.MoveLeftWord() + }, + expectedContent: "字abc", + expectedCursor: 3, + expectedClipboard: "", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc字") + textarea.MoveLeftWord() + }, + expectedContent: "abc字", + expectedCursor: 3, + expectedClipboard: "", + }, + // + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.GoToStartOfLine() + textarea.ForwardDeleteWord() + }, + expectedContent: "abc", + expectedCursor: 0, + expectedClipboard: "字", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc字") + textarea.GoToStartOfLine() + textarea.ForwardDeleteWord() + }, + expectedContent: "字", + expectedCursor: 0, + expectedClipboard: "abc", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.GoToStartOfLine() + textarea.ForwardDeleteWord() + textarea.ForwardDeleteWord() + }, + expectedContent: "", + expectedCursor: 0, + expectedClipboard: "abc", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc 字abc") + textarea.GoToStartOfLine() + textarea.ForwardDeleteWord() + }, + expectedContent: " 字abc", + expectedCursor: 0, + expectedClipboard: "abc", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("字abc") + textarea.GoToStartOfLine() + textarea.MoveRightWord() + }, + expectedContent: "字abc", + expectedCursor: 3, + expectedClipboard: "", + }, + { + actions: func(textarea *TextArea) { + textarea.TypeString("abc字") + textarea.GoToStartOfLine() + textarea.MoveRightWord() + }, + expectedContent: "abc字", + expectedCursor: 3, + expectedClipboard: "", + }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc`)