diff --git a/.changelog_entries/00650ce1adae0a3f1e59f3d6f009360c.json b/.changelog_entries/00650ce1adae0a3f1e59f3d6f009360c.json new file mode 100644 index 0000000..4babb05 --- /dev/null +++ b/.changelog_entries/00650ce1adae0a3f1e59f3d6f009360c.json @@ -0,0 +1 @@ +{"type":"Fixed","tickets":[],"description":"fixed missing end span edge case","tags":["bug"]} \ No newline at end of file diff --git a/spec/oho/converter_spec.cr b/spec/oho/converter_spec.cr index f9c0e85..510de01 100644 --- a/spec/oho/converter_spec.cr +++ b/spec/oho/converter_spec.cr @@ -20,6 +20,19 @@ describe Oho::Converter do response, escape_code = c.process(test_string, nil) response.should(eq("foo\n
bar
baz")) end + it "handles escape codes that terminate on subsequent lines with non-display codes in between" do + c = Oho::Converter.new(default_options) + test_string = "\033[36mfoo\033[K\nbar\033[0m" + response, escape_code = c.process(test_string, nil) + response.should(eq("foo\n
bar
")) + end + it "handles escape codes with non-display ones in between" do + c = Oho::Converter.new(default_options) + test_string = "\033[36mfoo\033[Kbar\033[0m" + response, escape_code = c.process(test_string, nil) + response.should(eq("foobar")) + + end describe "#extract_next_escape_code" do # there are too damn many options to do a unit test for each one # looping over grouped arrays of them to make sure all are tested @@ -43,7 +56,7 @@ describe Oho::Converter do code.as(Oho::EscapeCode).to_span(nil).should(eq("")) end - it "returs ColorEscapeCode for color codes" do + it "returns ColorEscapeCode for color codes" do seqs = [ "[m", # same as 0n "[0m",# reset styling and colors @@ -60,6 +73,11 @@ describe Oho::Converter do code.class.should(eq(Oho::ColorEscapeCode)) end end + it "doesn't get confused by non-display codes immediately following" do + reader = Char::Reader.new("[32m\033[K") + code, ignore = c.extract_next_escape_code('[', reader) + code.class.should(eq(Oho::ColorEscapeCode)) + end # The ITU's T.416 Information technology - # Open Document Architecture (ODA) and interchange format: # Character content architectures[20] uses ':' as separator diff --git a/src/oho/converter.cr b/src/oho/converter.cr index 7de2dd1..2b54ce7 100644 --- a/src/oho/converter.cr +++ b/src/oho/converter.cr @@ -12,6 +12,7 @@ module Oho def initialize(@options : Hash(Symbol, String)) @options[:background_color] = "initial" unless @options.has_key? :background_color @options[:foreground_color] = "initial" unless @options.has_key? :foreground_color + @last_display_escape_code = nil.as( EscapeCode? ) end def process(string : String, escape_code : EscapeCode?) : Tuple(String, EscapeCode?) reader = Char::Reader.new(string) @@ -35,7 +36,14 @@ module Oho # TODO: handle bad input better char, reader = get_next_char(reader) # if we're here there should be more following if ( char == '[' ) - escape_code, reader = handle_left_square_bracket( str, char, reader, escape_code ) + ec_for_handler = escape_code + if !escape_code.nil? && ! escape_code.as(EscapeCode).affects_display? + ec_for_handler = @last_display_escape_code + end + escape_code, reader = handle_left_square_bracket( str, + char, + reader, + ec_for_handler) next elsif char == ']' # Operating System Command (OSC), ignoring for now char, reader = handle_right_square_bracket(char, reader) @@ -52,7 +60,10 @@ module Oho handle_non_escape_chars(str, char, reader) end # end if not backspace end # END if char == '\e' || char.hash == 27 - end + if escape_code.as(EscapeCode).affects_display? + @last_display_escape_code = escape_code + end + end # end while reader.has_next? str << "" if ! escape_code.nil? && escape_code.as(EscapeCode).affects_display? end @@ -67,8 +78,6 @@ module Oho counter = Int8.new(1) raw_escape_seq = String.build do | str2 | str2 << char.to_s # this is [ most of the time - # if first_char == [ - # then while ! last_char_in_escape?( first_char, char, counter) begin char, reader = get_next_char(reader) @@ -80,11 +89,11 @@ module Oho break end end - # buffer[counter -1] = 0 # '\u{0}' not needed here end # end constructing escape_seq + begin if raw_escape_seq.starts_with?("[") && raw_escape_seq.ends_with?("m") - if ! raw_escape_seq.includes? ":" + if ! raw_escape_seq.includes? ":" # if it's normal return {ColorEscapeCode.new(raw_escape_seq, @options), reader} else return {T416ColorEscapeCode.new(raw_escape_seq, @options), reader} @@ -98,7 +107,7 @@ module Oho # [D - cursor backwards (val is num columns) # [s & [u - save and restore cursor position # [2j - erase display & move cursor to 0,0 - # [K - erase line + # [K - erase to end of line # [=h [=l - set & reset mode # [;;...p - redefine keyboard key to specified string # AND more from http://ascii-table.com/ansi-escape-sequences-vt-100.php @@ -235,10 +244,12 @@ module Oho {char, reader} end - private def handle_left_square_bracket(str : String::Builder, - char : Char, - reader : Char::Reader, - escape_code : EscapeCode?) : Tuple(EscapeCode?, Char::Reader) + private def handle_left_square_bracket( + str : String::Builder, + char : Char, + reader : Char::Reader, + escape_code : EscapeCode?) : Tuple(EscapeCode?, Char::Reader) + new_escape_code, reader = extract_next_escape_code(char, reader) unless new_escape_code.nil? str << new_escape_code.to_span(escape_code)