Ensure wrapping never results in lines longer than the specified word
This commit is contained in:
parent
6c503985ce
commit
18b58c5cf9
@ -7,7 +7,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
"kitty/tools/wcswidth"
|
"kitty/tools/wcswidth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -305,47 +307,50 @@ type escape_code_ struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type word_builder struct {
|
type word_builder struct {
|
||||||
buf strings.Builder
|
buf []byte
|
||||||
escape_codes []escape_code_
|
escape_codes []escape_code_
|
||||||
text_start_position int
|
text_start_position int
|
||||||
|
wcswidth *wcswidth.WCWidthIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) reset() string {
|
func (self *word_builder) reset() string {
|
||||||
ans := self.buf.String()
|
ans := utils.UnsafeBytesToString(self.buf)
|
||||||
sz := self.buf.Len()
|
sz := utils.Min(utils.Max(64, len(ans)), 4096)
|
||||||
if sz < 64 {
|
self.buf = make([]byte, 0, sz)
|
||||||
sz = 64
|
|
||||||
}
|
|
||||||
self.buf.Reset()
|
|
||||||
self.buf.Grow(sz)
|
|
||||||
self.escape_codes = self.escape_codes[:0]
|
self.escape_codes = self.escape_codes[:0]
|
||||||
self.text_start_position = 0
|
self.text_start_position = 0
|
||||||
|
self.wcswidth.Reset()
|
||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) is_empty() bool {
|
func (self *word_builder) is_empty() bool {
|
||||||
return self.buf.Len() == 0
|
return len(self.buf) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) width() int {
|
func (self *word_builder) width() int {
|
||||||
return wcswidth.Stringwidth(self.buf.String())
|
return self.wcswidth.CurrentWidth()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) add_escape_code(prefix string, body []byte, suffix string) {
|
func (self *word_builder) add_escape_code(prefix string, body []byte, suffix string) {
|
||||||
e := escape_code_{prefix: prefix, body: string(body), suffix: suffix}
|
e := escape_code_{prefix: prefix, body: string(body), suffix: suffix}
|
||||||
self.escape_codes = append(self.escape_codes, e)
|
self.escape_codes = append(self.escape_codes, e)
|
||||||
self.buf.WriteString(prefix)
|
self.buf = append(self.buf, prefix...)
|
||||||
self.buf.Write(body)
|
self.buf = append(self.buf, body...)
|
||||||
self.buf.WriteString(suffix)
|
self.buf = append(self.buf, suffix...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) has_text() bool { return self.text_start_position != 0 }
|
func (self *word_builder) has_text() bool { return self.text_start_position != 0 }
|
||||||
|
|
||||||
|
func (self *word_builder) recalculate_width() {
|
||||||
|
self.wcswidth.Reset()
|
||||||
|
self.wcswidth.Parse(self.buf)
|
||||||
|
}
|
||||||
|
|
||||||
func (self *word_builder) trim_leading_spaces() {
|
func (self *word_builder) trim_leading_spaces() {
|
||||||
if self.buf.Len() == 0 {
|
if self.is_empty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s := self.buf.String()
|
s := utils.UnsafeBytesToString(self.buf)
|
||||||
var before, after string
|
var before, after string
|
||||||
if self.text_start_position != 0 {
|
if self.text_start_position != 0 {
|
||||||
before, after = s[:self.text_start_position-1], s[self.text_start_position-1:]
|
before, after = s[:self.text_start_position-1], s[self.text_start_position-1:]
|
||||||
@ -354,19 +359,30 @@ func (self *word_builder) trim_leading_spaces() {
|
|||||||
}
|
}
|
||||||
q := strings.TrimLeftFunc(after, unicode.IsSpace)
|
q := strings.TrimLeftFunc(after, unicode.IsSpace)
|
||||||
if q != after {
|
if q != after {
|
||||||
self.buf.Reset()
|
self.buf = make([]byte, 0, len(s))
|
||||||
self.buf.Grow(len(before) + len(q))
|
self.buf = append(self.buf, before...)
|
||||||
self.buf.WriteString(before)
|
self.buf = append(self.buf, q...)
|
||||||
self.buf.WriteString(q)
|
|
||||||
self.text_start_position = len(before) + 1
|
self.text_start_position = len(before) + 1
|
||||||
|
self.recalculate_width()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *word_builder) add_rune(ch rune) {
|
func (self *word_builder) add_rune(ch rune) (num_bytes_written int) {
|
||||||
self.buf.WriteRune(ch)
|
before := len(self.buf)
|
||||||
if self.text_start_position == 0 {
|
self.buf = utf8.AppendRune(self.buf, ch)
|
||||||
self.text_start_position = self.buf.Len()
|
num_bytes_written = len(self.buf) - before
|
||||||
|
for _, b := range self.buf[before:] {
|
||||||
|
self.wcswidth.ParseByte(b)
|
||||||
}
|
}
|
||||||
|
if self.text_start_position == 0 {
|
||||||
|
self.text_start_position = len(self.buf)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *word_builder) remove_trailing_bytes(n int) {
|
||||||
|
self.buf = self.buf[:len(self.buf)-n]
|
||||||
|
self.recalculate_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
type wrapper struct {
|
type wrapper struct {
|
||||||
@ -433,7 +449,12 @@ func (self *wrapper) handle_rune(ch rune) error {
|
|||||||
self.print_word()
|
self.print_word()
|
||||||
self.current_line.add_char(ch)
|
self.current_line.add_char(ch)
|
||||||
} else {
|
} else {
|
||||||
self.current_word.add_rune(ch)
|
num_of_bytes_written := self.current_word.add_rune(ch)
|
||||||
|
if self.current_word.width() > self.width {
|
||||||
|
self.current_word.remove_trailing_bytes(num_of_bytes_written)
|
||||||
|
self.print_word()
|
||||||
|
return self.handle_rune(ch)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -471,12 +492,14 @@ func (self *wrapper) wrap_text(text string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func new_wrapper(indent string, width int) *wrapper {
|
func new_wrapper(indent string, width int) *wrapper {
|
||||||
|
width = utils.Max(2, width)
|
||||||
ans := wrapper{indent: indent, width: width, indent_width: wcswidth.Stringwidth(indent)}
|
ans := wrapper{indent: indent, width: width, indent_width: wcswidth.Stringwidth(indent)}
|
||||||
ans.ep.HandleRune = ans.handle_rune
|
ans.ep.HandleRune = ans.handle_rune
|
||||||
ans.ep.HandleCSI = ans.handle_csi
|
ans.ep.HandleCSI = ans.handle_csi
|
||||||
ans.ep.HandleOSC = ans.handle_osc
|
ans.ep.HandleOSC = ans.handle_osc
|
||||||
ans.lines = make([]string, 0, 32)
|
ans.lines = make([]string, 0, 32)
|
||||||
ans.current_word.escape_codes = make([]escape_code_, 0, 8)
|
ans.current_word.escape_codes = make([]escape_code_, 0, 8)
|
||||||
|
ans.current_word.wcswidth = wcswidth.CreateWCWidthIterator()
|
||||||
return &ans
|
return &ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,5 +31,8 @@ func TestFormatWithIndent(t *testing.T) {
|
|||||||
tx(
|
tx(
|
||||||
"\x1b[31;4:3m\x1b]8;;XXX\x1b\\combined using\x1b]8;;\x1b\\ operators",
|
"\x1b[31;4:3m\x1b]8;;XXX\x1b\\combined using\x1b]8;;\x1b\\ operators",
|
||||||
"\x1b[31;4:3m\x1b]8;;XXX\x1b\\combined\n\x1b[4:0;39m\x1b]8;;\x1b\\__\x1b[4:3;31m\x1b]8;;XXX\x1b\\using\x1b]8;;\x1b\\\n\x1b[4:0;39m__\x1b[4:3;31moperators")
|
"\x1b[31;4:3m\x1b]8;;XXX\x1b\\combined\n\x1b[4:0;39m\x1b]8;;\x1b\\__\x1b[4:3;31m\x1b]8;;XXX\x1b\\using\x1b]8;;\x1b\\\n\x1b[4:0;39m__\x1b[4:3;31moperators")
|
||||||
|
indent = ""
|
||||||
|
screen_width = 3
|
||||||
|
tx("one", "one")
|
||||||
|
tx("four", "fou\nr")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user