Implement the trim_whitespace option
Needed for help text formatting
This commit is contained in:
parent
34526517de
commit
266746c96e
@ -263,12 +263,24 @@ func (self hyperlink_state) as_escape_codes(for_close bool) string {
|
||||
type line_builder struct {
|
||||
buf []byte
|
||||
cursor_pos int
|
||||
seen_non_space_chars bool
|
||||
pos_of_trailing_whitespace int
|
||||
}
|
||||
|
||||
func (self *line_builder) reset() string {
|
||||
func (self *line_builder) reset(trim_whitespace bool) string {
|
||||
ans := string(self.buf)
|
||||
if trim_whitespace && self.pos_of_trailing_whitespace > -1 {
|
||||
prefix := ans[:self.pos_of_trailing_whitespace]
|
||||
tp := strings.TrimRightFunc(prefix, is_space)
|
||||
if len(tp) != len(prefix) {
|
||||
ans = tp + ans[self.pos_of_trailing_whitespace:]
|
||||
}
|
||||
|
||||
}
|
||||
self.buf = self.buf[:0]
|
||||
self.cursor_pos = 0
|
||||
self.seen_non_space_chars = false
|
||||
self.pos_of_trailing_whitespace = -1
|
||||
return ans
|
||||
}
|
||||
|
||||
@ -277,15 +289,34 @@ func (self *line_builder) has_space_for_width(w, max_width int) bool {
|
||||
}
|
||||
|
||||
func (self *line_builder) add_char(ch rune) {
|
||||
self.seen_non_space_chars = true
|
||||
self.buf = utf8.AppendRune(self.buf, ch)
|
||||
self.cursor_pos += wcswidth.Runewidth(ch)
|
||||
self.pos_of_trailing_whitespace = -1
|
||||
}
|
||||
|
||||
func (self *line_builder) add_space(ch rune, trim_whitespace bool) {
|
||||
if !trim_whitespace || self.seen_non_space_chars {
|
||||
self.buf = utf8.AppendRune(self.buf, ch)
|
||||
self.pos_of_trailing_whitespace = len(self.buf)
|
||||
self.cursor_pos += wcswidth.Runewidth(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *line_builder) add_word(word []byte, width int) {
|
||||
self.seen_non_space_chars = true
|
||||
self.pos_of_trailing_whitespace = -1
|
||||
self.buf = append(self.buf, word...)
|
||||
self.cursor_pos += width
|
||||
}
|
||||
|
||||
func (self *line_builder) add_indent(word string, width int) {
|
||||
if word != "" {
|
||||
self.buf = append(self.buf, word...)
|
||||
self.cursor_pos += width
|
||||
}
|
||||
}
|
||||
|
||||
func (self *line_builder) add_escape_code(code string) {
|
||||
self.buf = append(self.buf, code...)
|
||||
}
|
||||
@ -360,6 +391,7 @@ type wrapper struct {
|
||||
ep wcswidth.EscapeCodeParser
|
||||
indent string
|
||||
width, indent_width int
|
||||
trim_whitespace bool
|
||||
|
||||
sgr sgr_state
|
||||
hyperlink hyperlink_state
|
||||
@ -372,7 +404,7 @@ type wrapper struct {
|
||||
func (self *wrapper) newline_prefix() {
|
||||
self.current_line.add_escape_code(self.sgr.as_escape_codes(true))
|
||||
self.current_line.add_escape_code(self.hyperlink.as_escape_codes(true))
|
||||
self.current_line.add_word(utils.UnsafeStringToBytes(self.indent), self.indent_width)
|
||||
self.current_line.add_indent(self.indent, self.indent_width)
|
||||
self.current_line.add_escape_code(self.sgr.as_escape_codes(false))
|
||||
self.current_line.add_escape_code(self.hyperlink.as_escape_codes(false))
|
||||
}
|
||||
@ -387,7 +419,7 @@ func (self *wrapper) append_line(line string) {
|
||||
}
|
||||
|
||||
func (self *wrapper) end_current_line() {
|
||||
line := self.current_line.reset()
|
||||
line := self.current_line.reset(self.trim_whitespace)
|
||||
if strings.HasSuffix(line, self.indent) && wcswidth.Stringwidth(line) == self.indent_width {
|
||||
line = line[:len(line)-len(self.indent)]
|
||||
}
|
||||
@ -413,16 +445,22 @@ func (self *wrapper) print_word() {
|
||||
})
|
||||
}
|
||||
|
||||
func is_space(ch rune) bool {
|
||||
return ch != 0xa0 && unicode.IsSpace(ch)
|
||||
}
|
||||
|
||||
func (self *wrapper) handle_rune(ch rune) error {
|
||||
if ch == '\n' {
|
||||
self.print_word()
|
||||
self.end_current_line()
|
||||
} else if self.current_word.has_text() && ch != 0xa0 && unicode.IsSpace(ch) {
|
||||
} else if is_space(ch) {
|
||||
if self.current_word.has_text() {
|
||||
self.print_word()
|
||||
}
|
||||
if self.current_line.cursor_pos >= self.width {
|
||||
self.end_current_line()
|
||||
}
|
||||
self.current_line.add_char(ch)
|
||||
self.current_line.add_space(ch, self.trim_whitespace)
|
||||
} else {
|
||||
num_of_bytes_written := self.current_word.add_rune(ch)
|
||||
if self.current_word.width() > self.width {
|
||||
@ -448,18 +486,18 @@ func (self *wrapper) wrap_text(text string) []string {
|
||||
if text == "" {
|
||||
return []string{""}
|
||||
}
|
||||
self.current_line.reset()
|
||||
self.current_line.reset(self.trim_whitespace)
|
||||
self.current_word.reset(func([]byte) {})
|
||||
self.lines = self.lines[:0]
|
||||
self.current_line.add_word(utils.UnsafeStringToBytes(self.indent), self.indent_width)
|
||||
self.current_line.add_indent(self.indent, self.indent_width)
|
||||
self.ep.ParseString(text)
|
||||
if !self.current_word.is_empty() {
|
||||
self.print_word()
|
||||
}
|
||||
self.end_current_line()
|
||||
last_line := self.current_line.reset()
|
||||
last_line := self.current_line.reset(self.trim_whitespace)
|
||||
self.newline_prefix()
|
||||
if last_line == self.current_line.reset() {
|
||||
if last_line == self.current_line.reset(self.trim_whitespace) {
|
||||
last_line = ""
|
||||
}
|
||||
if last_line != "" {
|
||||
@ -470,7 +508,7 @@ func (self *wrapper) wrap_text(text string) []string {
|
||||
|
||||
func new_wrapper(opts WrapOptions, width int) *wrapper {
|
||||
width = utils.Max(2, width)
|
||||
ans := wrapper{indent: opts.Indent, width: width, indent_width: wcswidth.Stringwidth(opts.Indent)}
|
||||
ans := wrapper{indent: opts.Indent, width: width, trim_whitespace: opts.Trim_whitespace, indent_width: wcswidth.Stringwidth(opts.Indent)}
|
||||
if opts.Ignore_lines_containing != "" {
|
||||
ans.ignore_lines_containing = utils.Splitlines(opts.Ignore_lines_containing)
|
||||
}
|
||||
@ -485,8 +523,8 @@ func new_wrapper(opts WrapOptions, width int) *wrapper {
|
||||
|
||||
type WrapOptions struct {
|
||||
Ignore_lines_containing string
|
||||
Trim_whitespace bool // trim whitespace at the start and end of lines (start is after indent)
|
||||
Indent string
|
||||
Trim_whitespace bool // trim whitespace at the start and end of lines (start is after indent).
|
||||
Indent string // indent to add at the start of every line all formatting is cleared for the indent.
|
||||
}
|
||||
|
||||
func WrapTextAsLines(text string, width int, opts WrapOptions) []string {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -16,7 +17,8 @@ func TestFormatWithIndent(t *testing.T) {
|
||||
q := opts.Indent + strings.Join(expected, "")
|
||||
actual := WrapText(text, screen_width, opts)
|
||||
if actual != q {
|
||||
t.Fatalf("\nFailed for: %#v\nexpected: %#v\nactual: %#v", text, q, actual)
|
||||
os, _ := json.Marshal(opts)
|
||||
t.Fatalf("\nFailed for: %#v\nOptions: %s\nexpected: %#v\nactual: %#v", text, os, q, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +29,16 @@ func TestFormatWithIndent(t *testing.T) {
|
||||
screen_width = 3
|
||||
tx("one tw", "one\n tw")
|
||||
|
||||
screen_width = 4
|
||||
opts.Trim_whitespace = true
|
||||
opts.Indent = "X"
|
||||
tx("one two", "one\nXtwo")
|
||||
tx("\x1b[2mone \x1b[mtwo", "\x1b[2mone\n\x1b[222mX\x1b[2m\x1b[mtwo")
|
||||
screen_width = 3
|
||||
tx("on tw", "on\nXtw")
|
||||
opts.Indent = ""
|
||||
opts.Trim_whitespace = false
|
||||
|
||||
opts.Indent = "__"
|
||||
screen_width = 11
|
||||
tx("testing\n\ntwo", "testing\n\n__two")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user