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 {
|
type line_builder struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
cursor_pos int
|
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)
|
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.buf = self.buf[:0]
|
||||||
self.cursor_pos = 0
|
self.cursor_pos = 0
|
||||||
|
self.seen_non_space_chars = false
|
||||||
|
self.pos_of_trailing_whitespace = -1
|
||||||
return ans
|
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) {
|
func (self *line_builder) add_char(ch rune) {
|
||||||
|
self.seen_non_space_chars = true
|
||||||
self.buf = utf8.AppendRune(self.buf, ch)
|
self.buf = utf8.AppendRune(self.buf, ch)
|
||||||
self.cursor_pos += wcswidth.Runewidth(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) {
|
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.buf = append(self.buf, word...)
|
||||||
self.cursor_pos += width
|
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) {
|
func (self *line_builder) add_escape_code(code string) {
|
||||||
self.buf = append(self.buf, code...)
|
self.buf = append(self.buf, code...)
|
||||||
}
|
}
|
||||||
@ -360,6 +391,7 @@ type wrapper struct {
|
|||||||
ep wcswidth.EscapeCodeParser
|
ep wcswidth.EscapeCodeParser
|
||||||
indent string
|
indent string
|
||||||
width, indent_width int
|
width, indent_width int
|
||||||
|
trim_whitespace bool
|
||||||
|
|
||||||
sgr sgr_state
|
sgr sgr_state
|
||||||
hyperlink hyperlink_state
|
hyperlink hyperlink_state
|
||||||
@ -372,7 +404,7 @@ type wrapper struct {
|
|||||||
func (self *wrapper) newline_prefix() {
|
func (self *wrapper) newline_prefix() {
|
||||||
self.current_line.add_escape_code(self.sgr.as_escape_codes(true))
|
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_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.sgr.as_escape_codes(false))
|
||||||
self.current_line.add_escape_code(self.hyperlink.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() {
|
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 {
|
if strings.HasSuffix(line, self.indent) && wcswidth.Stringwidth(line) == self.indent_width {
|
||||||
line = line[:len(line)-len(self.indent)]
|
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 {
|
func (self *wrapper) handle_rune(ch rune) error {
|
||||||
if ch == '\n' {
|
if ch == '\n' {
|
||||||
self.print_word()
|
self.print_word()
|
||||||
self.end_current_line()
|
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()
|
self.print_word()
|
||||||
|
}
|
||||||
if self.current_line.cursor_pos >= self.width {
|
if self.current_line.cursor_pos >= self.width {
|
||||||
self.end_current_line()
|
self.end_current_line()
|
||||||
}
|
}
|
||||||
self.current_line.add_char(ch)
|
self.current_line.add_space(ch, self.trim_whitespace)
|
||||||
} else {
|
} else {
|
||||||
num_of_bytes_written := self.current_word.add_rune(ch)
|
num_of_bytes_written := self.current_word.add_rune(ch)
|
||||||
if self.current_word.width() > self.width {
|
if self.current_word.width() > self.width {
|
||||||
@ -448,18 +486,18 @@ func (self *wrapper) wrap_text(text string) []string {
|
|||||||
if text == "" {
|
if text == "" {
|
||||||
return []string{""}
|
return []string{""}
|
||||||
}
|
}
|
||||||
self.current_line.reset()
|
self.current_line.reset(self.trim_whitespace)
|
||||||
self.current_word.reset(func([]byte) {})
|
self.current_word.reset(func([]byte) {})
|
||||||
self.lines = self.lines[:0]
|
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)
|
self.ep.ParseString(text)
|
||||||
if !self.current_word.is_empty() {
|
if !self.current_word.is_empty() {
|
||||||
self.print_word()
|
self.print_word()
|
||||||
}
|
}
|
||||||
self.end_current_line()
|
self.end_current_line()
|
||||||
last_line := self.current_line.reset()
|
last_line := self.current_line.reset(self.trim_whitespace)
|
||||||
self.newline_prefix()
|
self.newline_prefix()
|
||||||
if last_line == self.current_line.reset() {
|
if last_line == self.current_line.reset(self.trim_whitespace) {
|
||||||
last_line = ""
|
last_line = ""
|
||||||
}
|
}
|
||||||
if 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 {
|
func new_wrapper(opts WrapOptions, width int) *wrapper {
|
||||||
width = utils.Max(2, width)
|
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 != "" {
|
if opts.Ignore_lines_containing != "" {
|
||||||
ans.ignore_lines_containing = utils.Splitlines(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 {
|
type WrapOptions struct {
|
||||||
Ignore_lines_containing string
|
Ignore_lines_containing string
|
||||||
Trim_whitespace bool // trim whitespace at the start and end of lines (start is after indent)
|
Trim_whitespace bool // trim whitespace at the start and end of lines (start is after indent).
|
||||||
Indent string
|
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 {
|
func WrapTextAsLines(text string, width int, opts WrapOptions) []string {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
package style
|
package style
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -16,7 +17,8 @@ func TestFormatWithIndent(t *testing.T) {
|
|||||||
q := opts.Indent + strings.Join(expected, "")
|
q := opts.Indent + strings.Join(expected, "")
|
||||||
actual := WrapText(text, screen_width, opts)
|
actual := WrapText(text, screen_width, opts)
|
||||||
if actual != q {
|
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
|
screen_width = 3
|
||||||
tx("one tw", "one\n tw")
|
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 = "__"
|
opts.Indent = "__"
|
||||||
screen_width = 11
|
screen_width = 11
|
||||||
tx("testing\n\ntwo", "testing\n\n__two")
|
tx("testing\n\ntwo", "testing\n\n__two")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user