mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-23 16:58:09 +02:00
Port code to truncate strings to visual width to Go
This commit is contained in:
93
tools/wcswidth/truncate.go
Normal file
93
tools/wcswidth/truncate.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
|
||||
package wcswidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"kitty/tools/utils"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
type truncate_error struct {
|
||||
pos, width int
|
||||
}
|
||||
|
||||
func (self *truncate_error) Error() string {
|
||||
return fmt.Sprint("Truncation at:", self.pos, " with width:", self.width)
|
||||
}
|
||||
|
||||
type truncate_iterator struct {
|
||||
w WCWidthIterator
|
||||
|
||||
pos, limit int
|
||||
limit_exceeded_at *truncate_error
|
||||
}
|
||||
|
||||
func (self *truncate_iterator) handle_csi(body []byte) error {
|
||||
self.pos += len(body) + 2
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *truncate_iterator) handle_st_terminated_escape_code(body []byte) error {
|
||||
self.pos += len(body) + 4
|
||||
return nil
|
||||
}
|
||||
|
||||
func create_truncate_iterator() *truncate_iterator {
|
||||
var ans truncate_iterator
|
||||
ans.w.parser.HandleRune = ans.handle_rune
|
||||
ans.w.parser.HandleCSI = ans.handle_csi
|
||||
ans.w.parser.HandleOSC = ans.handle_st_terminated_escape_code
|
||||
ans.w.parser.HandleAPC = ans.handle_st_terminated_escape_code
|
||||
ans.w.parser.HandleDCS = ans.handle_st_terminated_escape_code
|
||||
ans.w.parser.HandlePM = ans.handle_st_terminated_escape_code
|
||||
ans.w.parser.HandleSOS = ans.handle_st_terminated_escape_code
|
||||
return &ans
|
||||
}
|
||||
|
||||
func (self *truncate_iterator) handle_rune(ch rune) error {
|
||||
width := self.w.current_width
|
||||
self.w.handle_rune(ch)
|
||||
if self.limit_exceeded_at != nil {
|
||||
if self.w.current_width <= self.limit { // emoji variation selectors can cause width to decrease
|
||||
return &truncate_error{pos: self.pos + len(string(ch)), width: self.w.current_width}
|
||||
}
|
||||
return self.limit_exceeded_at
|
||||
}
|
||||
if self.w.current_width > self.limit {
|
||||
self.limit_exceeded_at = &truncate_error{pos: self.pos, width: width}
|
||||
}
|
||||
self.pos += len(string(ch))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *truncate_iterator) parse(b []byte) (ans int, width int) {
|
||||
err := self.w.parser.Parse(b)
|
||||
var te *truncate_error
|
||||
if err != nil && errors.As(err, &te) {
|
||||
return te.pos, te.width
|
||||
}
|
||||
if self.limit_exceeded_at != nil {
|
||||
return self.limit_exceeded_at.pos, self.limit_exceeded_at.width
|
||||
}
|
||||
return len(b), self.w.current_width
|
||||
}
|
||||
|
||||
func TruncateToVisualLengthWithWidth(text string, length int) (truncated string, width_of_truncated int) {
|
||||
if length < 1 {
|
||||
return text[:0], 0
|
||||
}
|
||||
t := create_truncate_iterator()
|
||||
t.limit = length
|
||||
t.limit_exceeded_at = nil
|
||||
t.w.current_width = 0
|
||||
truncate_point, width := t.parse(utils.UnsafeStringToBytes(text))
|
||||
return text[:truncate_point], width
|
||||
}
|
||||
|
||||
func TruncateToVisualLength(text string, length int) string {
|
||||
ans, _ := TruncateToVisualLengthWithWidth(text, length)
|
||||
return ans
|
||||
}
|
||||
Reference in New Issue
Block a user