diff --git a/pkg/gocui/text_area.go b/pkg/gocui/text_area.go index 7aeb6220a37..a1f01012bcd 100644 --- a/pkg/gocui/text_area.go +++ b/pkg/gocui/text_area.go @@ -310,6 +310,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 @@ -328,8 +332,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-- + } } } @@ -358,8 +368,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 f0bc2fca81f..a1d70d7bf02 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`)