Skip to content

Commit

Permalink
fixed missing end span edge case
Browse files Browse the repository at this point in the history
a non-display escape code after a display one started
but before it was cleared would result in
no </span> being inserted.

solution was to maintain the last display affecting
code and pass that in instead to EscapeCode#to_span
  • Loading branch information
masukomi committed Feb 5, 2021
1 parent 4395b60 commit da561ca
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 12 deletions.
1 change: 1 addition & 0 deletions .changelog_entries/00650ce1adae0a3f1e59f3d6f009360c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type":"Fixed","tickets":[],"description":"fixed missing end span edge case","tags":["bug"]}
20 changes: 19 additions & 1 deletion spec/oho/converter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ describe Oho::Converter do
response, escape_code = c.process(test_string, nil)
response.should(eq("<span style=\"color: aqua; \">foo\n<br />bar</span><span style=\"\"> baz</span>"))
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("<span style=\"color: aqua; \">foo\n<br />bar</span>"))
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("<span style=\"color: aqua; \">foobar</span>"))

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
Expand All @@ -43,7 +56,7 @@ describe Oho::Converter do
code.as(Oho::EscapeCode).to_span(nil).should(eq("<span style=\"color: red; \">"))

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
Expand All @@ -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
Expand Down
33 changes: 22 additions & 11 deletions src/oho/converter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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 << "</span>" if ! escape_code.nil? && escape_code.as(EscapeCode).affects_display?
end

Expand All @@ -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)
Expand All @@ -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}
Expand All @@ -98,7 +107,7 @@ module Oho
# [<val>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
# [=<val>h [=<val>l - set & reset mode
# [<code>;<string>;...p - redefine keyboard key to specified string
# AND more from http://ascii-table.com/ansi-escape-sequences-vt-100.php
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit da561ca

Please sign in to comment.