diff --git a/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_custom_format.txt b/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_custom_format.txt index 814228133..fb1fe0650 100644 --- a/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_custom_format.txt +++ b/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_custom_format.txt @@ -1,7 +1,7 @@ +----------------+---------------+---------------------+ -| ISBN | Title | Author | +| ISBN | Title | Author | +----------------+---------------+---------------------+ -| 978-0521567817 | De Monarchia | Dante Alighieri | -| 978-0804169127 | Divine Comedy | spans multiple rows | -| test | tttt | +| 978-0521567817 | De Monarchia | Dante Alighieri | +| 978-0804169127 | Divine Comedy | spans multiple rows | +| test | tttt | +----------------+---------------+---------------------+ diff --git a/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_fgbg.txt b/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_fgbg.txt index ae33b8a8b..14b045cdf 100644 --- a/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_fgbg.txt +++ b/src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_fgbg.txt @@ -1,7 +1,7 @@ +---------------+---------------+-------------------------------------------+ -| 978 | De Monarchia | Dante Alighieri | -| 99921-58-10-7 | Divine Comedy | spans multiple rows rows Dante Alighieri | -| | | spans multiple rows rows | +| 978 | De Monarchia | Dante Alighieri | +| 99921-58-10-7 | Divine Comedy | spans multiple rows rows Dante Alighieri | +| | | spans multiple rows rows | +---------------+---------------+-------------------------------------------+ -| test | tttt | +| test | tttt | +---------------+---------------+-------------------------------------------+ diff --git a/src/components/console/spec/fixtures/helper/table/default_colspan_and_table_cell_with_comment_style.txt b/src/components/console/spec/fixtures/helper/table/default_colspan_and_table_cell_with_comment_style.txt index 1124a6875..808178f7b 100644 --- a/src/components/console/spec/fixtures/helper/table/default_colspan_and_table_cell_with_comment_style.txt +++ b/src/components/console/spec/fixtures/helper/table/default_colspan_and_table_cell_with_comment_style.txt @@ -1,5 +1,5 @@ +-----------------+------------------+---------+ -| Long Title | +| Long Title | +-----------------+------------------+---------+ | 9971-5-0210-0 | +-----------------+------------------+---------+ diff --git a/src/components/console/spec/fixtures/helper/table/default_formatted_row_with_line_breaks.txt b/src/components/console/spec/fixtures/helper/table/default_formatted_row_with_line_breaks.txt index 9a5f9ea7d..379b578fa 100644 --- a/src/components/console/spec/fixtures/helper/table/default_formatted_row_with_line_breaks.txt +++ b/src/components/console/spec/fixtures/helper/table/default_formatted_row_with_line_breaks.txt @@ -1,7 +1,7 @@ +-------+------------+ -| Dont break | -| here | +| Dont break | +| here | +-------+------------+ -| foo | Dont break | -| bar | here | +| foo | Dont break | +| bar | here | +-------+------------+ diff --git a/src/components/console/spec/fixtures/style/block_padding.txt b/src/components/console/spec/fixtures/style/block_padding.txt index a05d75eec..06b485290 100644 --- a/src/components/console/spec/fixtures/style/block_padding.txt +++ b/src/components/console/spec/fixtures/style/block_padding.txt @@ -1,8 +1,8 @@ -\e\[30;42m \e\[0m -\e\[30;42m \[OK\] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore \e\[0m -\e\[30;42m magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \e\[0m -\e\[30;42m consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur\. \e\[0m -\e\[30;42m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum \e\[0m -\e\[30;42m \e\[0m +\e\[30;42m \e\[39;49m +\e\[30;42m \[OK\] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore \e\[39;49m +\e\[30;42m magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \e\[39;49m +\e\[30;42m consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur\. \e\[39;49m +\e\[30;42m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum \e\[39;49m +\e\[30;42m \e\[39;49m diff --git a/src/components/console/spec/fixtures/style/closing_tag.txt b/src/components/console/spec/fixtures/style/closing_tag.txt index 916e2ade3..a0149760e 100644 --- a/src/components/console/spec/fixtures/style/closing_tag.txt +++ b/src/components/console/spec/fixtures/style/closing_tag.txt @@ -1 +1 @@ -\e\[30;46mdo you want \e\[0m\e\[33msomething\e\[0m\e\[30;46m\?\e\[0m +\e\[30;46mdo you want \e\[39;49m\e\[33msomething\e\[39m\e\[30;46m\?\e\[39;49m diff --git a/src/components/console/spec/fixtures/style/long_line_comment_decorated.txt b/src/components/console/spec/fixtures/style/long_line_comment_decorated.txt index c88040df7..49a5219ae 100644 --- a/src/components/console/spec/fixtures/style/long_line_comment_decorated.txt +++ b/src/components/console/spec/fixtures/style/long_line_comment_decorated.txt @@ -1,7 +1,7 @@ - \/\/ Árvíztűrőtükörfúrógép 🎼 Lorem ipsum dolor sit \e\[33m💕 amet, consectetur adipisicing elit, sed do eiusmod tempor incididu \e\[0m - \/\/ \e\[33mlabore et dolore magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex\e\[0m - \/\/ \e\[33mea commodo consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \e\[0m - \/\/ \e\[33mpariatur\.\e\[0m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + \/\/ Árvíztűrőtükörfúrógép 🎼 Lorem ipsum dolor sit \e\[33m💕 amet, consectetur adipisicing elit, sed do eiusmod tempor incididu \e\[39m + \/\/ \e\[33mlabore et dolore magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex\e\[39m + \/\/ \e\[33mea commodo consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \e\[39m + \/\/ \e\[33mpariatur\.\e\[39m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est \/\/ laborum diff --git a/src/components/console/spec/fixtures/style/nested_tag_prefix.txt b/src/components/console/spec/fixtures/style/nested_tag_prefix.txt index 1eafe7583..0900ef7a0 100644 --- a/src/components/console/spec/fixtures/style/nested_tag_prefix.txt +++ b/src/components/console/spec/fixtures/style/nested_tag_prefix.txt @@ -1,7 +1,7 @@ - ║ \[★\] Árvíztűrőtükörfúrógép Lorem ipsum dolor sit \e\[33mamet, consectetur adipisicing elit, sed do eiusmod tempor incididunt \e\[0m - ║ \e\[33m ut labore et dolore magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut \e\[0m - ║ \e\[33m aliquip ex ea commodo consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \e\[0m - ║ \e\[33m fugiat nulla pariatur\.\e\[0m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit + ║ \[★\] Árvíztűrőtükörfúrógép Lorem ipsum dolor sit \e\[33mamet, consectetur adipisicing elit, sed do eiusmod tempor incididunt \e\[39m + ║ \e\[33m ut labore et dolore magna aliqua\. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut \e\[39m + ║ \e\[33m aliquip ex ea commodo consequat\. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \e\[39m + ║ \e\[33m fugiat nulla pariatur\.\e\[39m Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit ║ anim id est laborum diff --git a/src/components/console/spec/fixtures/style/table.txt b/src/components/console/spec/fixtures/style/table.txt index e50e2dab5..8f7940116 100644 --- a/src/components/console/spec/fixtures/style/table.txt +++ b/src/components/console/spec/fixtures/style/table.txt @@ -1,5 +1,5 @@ ----- ------- - \e\[32m Foo \e\[0m \e\[32m Bar \e\[0m + \e\[32m Foo \e\[39m \e\[32m Bar \e\[39m ----- ------- Biz Baz 12 false diff --git a/src/components/console/spec/fixtures/style/table_horizontal.txt b/src/components/console/spec/fixtures/style/table_horizontal.txt index 37f3d85c8..4a9d90072 100644 --- a/src/components/console/spec/fixtures/style/table_horizontal.txt +++ b/src/components/console/spec/fixtures/style/table_horizontal.txt @@ -1,5 +1,5 @@ ----- ----- ------- - \e\[32m Foo \e\[0m Biz 12 - \e\[32m Bar \e\[0m Baz false + \e\[32m Foo \e\[39m Biz 12 + \e\[32m Bar \e\[39m Baz false ----- ----- ------- diff --git a/src/components/console/spec/fixtures/style/table_vertical.txt b/src/components/console/spec/fixtures/style/table_vertical.txt index ba6a1249c..c83e0ed60 100644 --- a/src/components/console/spec/fixtures/style/table_vertical.txt +++ b/src/components/console/spec/fixtures/style/table_vertical.txt @@ -1,8 +1,8 @@ ------------ - \[33mFoo\[0m: Biz - \[33mBar\[0m: Baz + \[33mFoo\[39m: Biz + \[33mBar\[39m: Baz ------------ - \[33mFoo\[0m: 12 - \[33mBar\[0m: false + \[33mFoo\[39m: 12 + \[33mBar\[39m: false ------------ diff --git a/src/components/console/spec/fixtures/text/application_renderexception3_decorated.txt b/src/components/console/spec/fixtures/text/application_renderexception3_decorated.txt index 6fc5d6612..943729500 100644 --- a/src/components/console/spec/fixtures/text/application_renderexception3_decorated.txt +++ b/src/components/console/spec/fixtures/text/application_renderexception3_decorated.txt @@ -1,18 +1,18 @@ -\e\[33mIn foo3\.cr line \d+:\e\[0m -\e\[97;41m \e\[0m -\e\[97;41m Third exception comment \e\[0m -\e\[97;41m \e\[0m +\e\[33mIn foo3\.cr line \d+:\e\[39m +\e\[97;41m \e\[39;49m +\e\[97;41m Third exception comment \e\[39;49m +\e\[97;41m \e\[39;49m -\e\[33mIn foo3\.cr line \d+:\e\[0m -\e\[97;41m \e\[0m -\e\[97;41m Second exception comment \e\[0m -\e\[97;41m \e\[0m +\e\[33mIn foo3\.cr line \d+:\e\[39m +\e\[97;41m \e\[39;49m +\e\[97;41m Second exception comment \e\[39;49m +\e\[97;41m \e\[39;49m -\e\[33mIn foo3\.cr line \d+:\e\[0m -\e\[97;41m \e\[0m -\e\[97;41m First exception

this is html

\e\[0m -\e\[97;41m \e\[0m +\e\[33mIn foo3\.cr line \d+:\e\[39m +\e\[97;41m \e\[39;49m +\e\[97;41m First exception

this is html

\e\[39;49m +\e\[97;41m \e\[39;49m -\e\[32mfoo3:bar\e\[0m +\e\[32mfoo3:bar\e\[39m diff --git a/src/components/console/spec/formatter/output_formatter_spec.cr b/src/components/console/spec/formatter/output_formatter_spec.cr index 7317d1076..5285cefdc 100644 --- a/src/components/console/spec/formatter/output_formatter_spec.cr +++ b/src/components/console/spec/formatter/output_formatter_spec.cr @@ -15,11 +15,11 @@ struct OutputFormatterTest < ASPEC::TestCase @formatter.format("foo\\bar \\ baz \\").should eq "foo << \e[32mbar \\ baz\e[0m \\" + @formatter.format("foo << bar \\ baz \\").should eq "foo << \e[32mbar \\ baz\e[39m \\" @formatter.format("\\some info\\").should eq "some info" ACON::Formatter::Output.escape("some info").should eq "\\some info\\" - @formatter.format("Some\\Path\\ToFile does work very well!").should eq "\e[33mSome\\Path\\ToFile does work very well!\e[0m" + @formatter.format("Some\\Path\\ToFile does work very well!").should eq "\e[33mSome\\Path\\ToFile does work very well!\e[39m" end def test_format_built_in_styles : Nil @@ -28,33 +28,31 @@ struct OutputFormatterTest < ASPEC::TestCase @formatter.has_style?("comment").should be_true @formatter.has_style?("question").should be_true - @formatter.format("some error").should eq "\e[97;41msome error\e[0m" - @formatter.format("some info").should eq "\e[32msome info\e[0m" - @formatter.format("some comment").should eq "\e[33msome comment\e[0m" - @formatter.format("some question").should eq "\e[30;46msome question\e[0m" + @formatter.format("some error").should eq "\e[97;41msome error\e[39;49m" + @formatter.format("some info").should eq "\e[32msome info\e[39m" + @formatter.format("some comment").should eq "\e[33msome comment\e[39m" + @formatter.format("some question").should eq "\e[30;46msome question\e[39;49m" end - # TODO: Dependent on https://github.com/crystal-lang/crystal/issues/10652. - def ptest_format_nested_styles : Nil - @formatter.format("some some info error").should eq "\e[97;41msome \e[0m\e[32msome info\e[39m\e[97;41m error\e[0m" + def test_format_nested_styles : Nil + @formatter.format("some some info error").should eq "\e[97;41msome \e[39;49m\e[32msome info\e[39m\e[97;41m error\e[39;49m" end - # TODO: Dependent on https://github.com/crystal-lang/crystal/issues/10652. - def ptest_format_deeply_nested_styles : Nil - @formatter.format("errorinfocommenterror").should eq "\e[97;41merror\e[0m\e[32minfo\e[39m\e[33mcomment\e[39m\e[97;41merror\e[0m" + def test_format_deeply_nested_styles : Nil + @formatter.format("errorinfocommenterror").should eq "\e[97;41merror\e[39;49m\e[32minfo\e[39m\e[33mcomment\e[39m\e[97;41merror\e[39;49m" end def test_format_adjacent_styles : Nil - @formatter.format("some errorsome info").should eq "\e[97;41msome error\e[0m\e[32msome info\e[0m" + @formatter.format("some errorsome info").should eq "\e[97;41msome error\e[39;49m\e[32msome info\e[39m" end def test_format_adjacent_styles_not_greedy : Nil - @formatter.format("(>=2.0,<2.3)").should eq "(\e[32m>=2.0,<2.3\e[0m)" + @formatter.format("(>=2.0,<2.3)").should eq "(\e[32m>=2.0,<2.3\e[39m)" end def test_format_style_escaping : Nil - @formatter.format(%((#{@formatter.class.escape "z>=2.0,<\\<))).should eq "(\e[32mz>=2.0,<<#{@formatter.class.escape "some error"})).should eq "\e[32msome error\e[0m" + @formatter.format(%((#{@formatter.class.escape "z>=2.0,<\\<))).should eq "(\e[32mz>=2.0,<<#{@formatter.class.escape "some error"})).should eq "\e[32msome error\e[39m" end def test_format_custom_style : Nil @@ -67,7 +65,7 @@ struct OutputFormatterTest < ASPEC::TestCase style = ACON::Formatter::OutputStyle.new :blue, :white @formatter.set_style "b", style - @formatter.format("some messagecustom").should eq "\e[34;107msome message\e[0m\e[34;107mcustom\e[0m" + @formatter.format("some messagecustom").should eq "\e[34;107msome message\e[39;49m\e[34;107mcustom\e[39;49m" # TODO: Also assert it works when nested. end @@ -75,12 +73,12 @@ struct OutputFormatterTest < ASPEC::TestCase style = ACON::Formatter::OutputStyle.new :blue, :white @formatter.set_style "info", style - @formatter.format("some custom message").should eq "\e[34;107msome custom message\e[0m" + @formatter.format("some custom message").should eq "\e[34;107msome custom message\e[39;49m" end def test_format_inline_style : Nil - @formatter.format("some text").should eq "\e[34;41msome text\e[0m" - @formatter.format("some text").should eq "\e[34;41msome text\e[0m" + @formatter.format("some text").should eq "\e[34;41msome text\e[39;49m" + @formatter.format("some text").should eq "\e[34;41msome text\e[39;49m" end @[DataProvider("inline_style_options_provider")] @@ -108,25 +106,25 @@ struct OutputFormatterTest < ASPEC::TestCase { {"", nil, nil, false}, {"", nil, nil, false}, - {"", "\e[32m[test]\e[0m", "[test]", false}, - {"", "\e[32;44ma\e[0m", "a", false}, - {"", "\e[32;1mb\e[0m", "b", false}, - {"", "\e[32;7m\e[0m", "", false}, - {"", "\e[32;1;4mz\e[0m", "z", false}, - {"", "\e[32;1;4;7md\e[0m", "d", false}, - {"", "\e[38;2;0;255;0;48;2;0;0;255m[test]\e[0m", "[test]", true}, + {"", "\e[32m[test]\e[39m", "[test]", false}, + {"", "\e[32;44ma\e[39;49m", "a", false}, + {"", "\e[32;1mb\e[39;22m", "b", false}, + {"", "\e[32;7m\e[39;27m", "", false}, + {"", "\e[32;1;4mz\e[39;22;24m", "z", false}, + {"", "\e[32;1;4;7md\e[39;22;24;27m", "d", false}, + {"", "\e[38;2;0;255;0;48;2;0;0;255m[test]\e[39;49m", "[test]", true}, } end def test_format_non_style_tag : Nil @formatter .format("some styled

single-char tag

") - .should eq "\e[32msome \e[0m\e[32m\e[0m\e[32m \e[0m\e[32m\e[0m\e[32m styled \e[0m\e[32m

\e[0m\e[32msingle-char tag\e[0m\e[32m

\e[0m" + .should eq "\e[32msome \e[39m\e[32m\e[39m\e[32m \e[39m\e[32m\e[39m\e[32m styled \e[39m\e[32m

\e[39m\e[32msingle-char tag\e[39m\e[32m

\e[39m" end def test_format_long_string : Nil long = "\\" * 14_000 - @formatter.format("some error#{long}").should eq "\e[97;41msome error\e[0m#{long}" + @formatter.format("some error#{long}").should eq "\e[97;41msome error\e[39;49m#{long}" end def test_has_style : Nil @@ -157,33 +155,33 @@ struct OutputFormatterTest < ASPEC::TestCase def decorated_and_non_decorated_output : Tuple { - {"some error", "some error", "\e[97;41msome error\e[0m", "foo"}, - {"some info", "some info", "\e[32msome info\e[0m", "foo"}, - {"some comment", "some comment", "\e[33msome comment\e[0m", "foo"}, - {"some question", "some question", "\e[30;46msome question\e[0m", "foo"}, - {"some text with inline style", "some text with inline style", "\e[31msome text with inline style\e[0m", "foo"}, + {"some error", "some error", "\e[97;41msome error\e[39;49m", "foo"}, + {"some info", "some info", "\e[32msome info\e[39m", "foo"}, + {"some comment", "some comment", "\e[33msome comment\e[39m", "foo"}, + {"some question", "some question", "\e[30;46msome question\e[39;49m", "foo"}, + {"some text with inline style", "some text with inline style", "\e[31msome text with inline style\e[39m", "foo"}, {"some URL", "some URL", "\e]8;;idea://open/?file=/path/SomeFile.php&line=12\e\\some URL\e]8;;\e\\", "foo"}, {"some URL", "some URL", "some URL", "JetBrains-JediTerm"}, } end def test_format_with_line_breaks : Nil - @formatter.format("\nsome text").should eq "\e[32m\nsome text\e[0m" - @formatter.format("some text\n").should eq "\e[32msome text\n\e[0m" - @formatter.format("\nsome text\n").should eq "\e[32m\nsome text\n\e[0m" - @formatter.format("\nsome text\nmore text\n").should eq "\e[32m\nsome text\nmore text\n\e[0m" + @formatter.format("\nsome text").should eq "\e[32m\nsome text\e[39m" + @formatter.format("some text\n").should eq "\e[32msome text\n\e[39m" + @formatter.format("\nsome text\n").should eq "\e[32m\nsome text\n\e[39m" + @formatter.format("\nsome text\nmore text\n").should eq "\e[32m\nsome text\nmore text\n\e[39m" end def test_format_and_wrap : Nil - @formatter.format_and_wrap("ooobar bbz", 2).should eq "oo\no\e[97;41mb\e[0m\n\e[97;41mar\e[0m\nbb\nz" - @formatter.format_and_wrap("pre foo bar baz post", 2).should eq "pr\ne \e[97;41m\e[0m\n\e[97;41mfo\e[0m\n\e[97;41mo \e[0m\n\e[97;41mba\e[0m\n\e[97;41mr \e[0m\n\e[97;41mba\e[0m\n\e[97;41mz\e[0m \npo\nst" - @formatter.format_and_wrap("pre foo bar baz post", 3).should eq "pre\e[97;41m\e[0m\n\e[97;41mfoo\e[0m\n\e[97;41mbar\e[0m\n\e[97;41mbaz\e[0m\npos\nt" - @formatter.format_and_wrap("pre foo bar baz post", 4).should eq "pre \e[97;41m\e[0m\n\e[97;41mfoo \e[0m\n\e[97;41mbar \e[0m\n\e[97;41mbaz\e[0m \npost" - @formatter.format_and_wrap("pre foo bbr baz post", 5).should eq "pre \e[97;41mf\e[0m\n\e[97;41moo bb\e[0m\n\e[97;41mr baz\e[0m\npost" - - @formatter.format_and_wrap("Lorem ipsum dolor sit amet", 4).should eq "Lore\nm \e[97;41mip\e[0m\n\e[97;41msum\e[0m \ndolo\nr \e[32msi\e[0m\n\e[32mt\e[0m am\net" - @formatter.format_and_wrap("Lorem ipsum dolor sit amet", 8).should eq "Lorem \e[97;41mip\e[0m\n\e[97;41msum\e[0m dolo\nr \e[32msit\e[0m am\net" - @formatter.format_and_wrap("Lorem ipsum dolor sit, amet et laudantium architecto", 18).should eq "Lorem \e[97;41mipsum\e[0m dolor \e[32m\e[0m\n\e[32msit\e[0m, \e[97;41mamet\e[0m et \e[32mlauda\e[0m\n\e[32mntium\e[0m architecto" + @formatter.format_and_wrap("ooobar bbz", 2).should eq "oo\no\e[97;41mb\e[39;49m\n\e[97;41mar\e[39;49m\nbb\nz" + @formatter.format_and_wrap("pre foo bar baz post", 2).should eq "pr\ne \e[97;41m\e[39;49m\n\e[97;41mfo\e[39;49m\n\e[97;41mo \e[39;49m\n\e[97;41mba\e[39;49m\n\e[97;41mr \e[39;49m\n\e[97;41mba\e[39;49m\n\e[97;41mz\e[39;49m \npo\nst" + @formatter.format_and_wrap("pre foo bar baz post", 3).should eq "pre\e[97;41m\e[39;49m\n\e[97;41mfoo\e[39;49m\n\e[97;41mbar\e[39;49m\n\e[97;41mbaz\e[39;49m\npos\nt" + @formatter.format_and_wrap("pre foo bar baz post", 4).should eq "pre \e[97;41m\e[39;49m\n\e[97;41mfoo \e[39;49m\n\e[97;41mbar \e[39;49m\n\e[97;41mbaz\e[39;49m \npost" + @formatter.format_and_wrap("pre foo bbr baz post", 5).should eq "pre \e[97;41mf\e[39;49m\n\e[97;41moo bb\e[39;49m\n\e[97;41mr baz\e[39;49m\npost" + + @formatter.format_and_wrap("Lorem ipsum dolor sit amet", 4).should eq "Lore\nm \e[97;41mip\e[39;49m\n\e[97;41msum\e[39;49m \ndolo\nr \e[32msi\e[39m\n\e[32mt\e[39m am\net" + @formatter.format_and_wrap("Lorem ipsum dolor sit amet", 8).should eq "Lorem \e[97;41mip\e[39;49m\n\e[97;41msum\e[39;49m dolo\nr \e[32msit\e[39m am\net" + @formatter.format_and_wrap("Lorem ipsum dolor sit, amet et laudantium architecto", 18).should eq "Lorem \e[97;41mipsum\e[39;49m dolor \e[32m\e[39m\n\e[32msit\e[39m, \e[97;41mamet\e[39;49m et \e[32mlauda\e[39m\n\e[32mntium\e[39m architecto" end def test_format_and_wrap_non_decorated : Nil diff --git a/src/components/console/spec/formatter/output_formatter_style_spec.cr b/src/components/console/spec/formatter/output_formatter_style_spec.cr index bd397ecd3..ee8934a63 100644 --- a/src/components/console/spec/formatter/output_formatter_style_spec.cr +++ b/src/components/console/spec/formatter/output_formatter_style_spec.cr @@ -3,23 +3,23 @@ require "../spec_helper" describe ACON::Formatter::OutputStyle do it ".new" do ACON::Formatter::OutputStyle.new(:green, :black, Colorize::Mode[:bold, :underline]) - .apply("foo").should eq "\e[32;40;1;4mfoo\e[0m" + .apply("foo").should eq "\e[32;40;1;4mfoo\e[39;49;22;24m" ACON::Formatter::OutputStyle.new(:red, options: Colorize::Mode::Blink) - .apply("foo").should eq "\e[31;5mfoo\e[0m" + .apply("foo").should eq "\e[31;5mfoo\e[39;25m" ACON::Formatter::OutputStyle.new(background: :white) - .apply("foo").should eq "\e[107mfoo\e[0m" + .apply("foo").should eq "\e[107mfoo\e[49m" ACON::Formatter::OutputStyle.new("red", "#000000", Colorize::Mode[:bold, :underline]) - .apply("foo").should eq "\e[31;48;2;0;0;0;1;4mfoo\e[0m" + .apply("foo").should eq "\e[31;48;2;0;0;0;1;4mfoo\e[39;49;22;24m" end describe "foreground=" do it "with ANSI color" do style = ACON::Formatter::OutputStyle.new style.foreground = :black - style.apply("foo").should eq "\e[30mfoo\e[0m" + style.apply("foo").should eq "\e[30mfoo\e[39m" end it "with default value" do @@ -31,7 +31,7 @@ describe ACON::Formatter::OutputStyle do it "with HEX RGB value" do style = ACON::Formatter::OutputStyle.new style.foreground = "#aedfff" - style.apply("foo").should eq "\e[38;2;174;223;255mfoo\e[0m" + style.apply("foo").should eq "\e[38;2;174;223;255mfoo\e[39m" end it "with invalid color" do @@ -47,7 +47,7 @@ describe ACON::Formatter::OutputStyle do it "with ANSI color" do style = ACON::Formatter::OutputStyle.new style.background = :black - style.apply("foo").should eq "\e[40mfoo\e[0m" + style.apply("foo").should eq "\e[40mfoo\e[49m" end it "with default value" do @@ -59,7 +59,7 @@ describe ACON::Formatter::OutputStyle do it "with HEX RGB value" do style = ACON::Formatter::OutputStyle.new style.background = "#aedfff" - style.apply("foo").should eq "\e[48;2;174;223;255mfoo\e[0m" + style.apply("foo").should eq "\e[48;2;174;223;255mfoo\e[49m" end it "with invalid color" do @@ -76,19 +76,19 @@ describe ACON::Formatter::OutputStyle do style.add_option "reverse" style.add_option "hidden" - style.apply("foo").should eq "\e[7;8mfoo\e[0m" + style.apply("foo").should eq "\e[7;8mfoo\e[27;28m" style.add_option "bold" - style.apply("foo").should eq "\e[1;7;8mfoo\e[0m" + style.apply("foo").should eq "\e[1;7;8mfoo\e[22;27;28m" style.remove_option "reverse" - style.apply("foo").should eq "\e[1;8mfoo\e[0m" + style.apply("foo").should eq "\e[1;8mfoo\e[22;28m" style.add_option "bold" - style.apply("foo").should eq "\e[1;8mfoo\e[0m" + style.apply("foo").should eq "\e[1;8mfoo\e[22;28m" style.options = Colorize::Mode::Bold - style.apply("foo").should eq "\e[1mfoo\e[0m" + style.apply("foo").should eq "\e[1mfoo\e[22m" end it "href" do diff --git a/src/components/console/spec/formatter/output_formatter_style_stack_spec.cr b/src/components/console/spec/formatter/output_formatter_style_stack_spec.cr index 8ca300799..bbf0b89a9 100644 --- a/src/components/console/spec/formatter/output_formatter_style_stack_spec.cr +++ b/src/components/console/spec/formatter/output_formatter_style_stack_spec.cr @@ -40,6 +40,15 @@ describe ACON::Formatter::OutputStyleStack do stack.pop.should eq s1 end + it "nested styles" do + stack = ACON::Formatter::OutputStyleStack.new + stack << (s1 = ACON::Formatter::OutputStyle.new :white, :red) + stack << (s2 = ACON::Formatter::OutputStyle.new :green, :default) + + stack.pop(s2).should eq s2 + stack.pop(s1).should eq s1 + end + it "invalid pop" do stack = ACON::Formatter::OutputStyleStack.new stack << ACON::Formatter::OutputStyle.new :white, :black diff --git a/src/components/console/spec/helper/progress_bar_spec.cr b/src/components/console/spec/helper/progress_bar_spec.cr index b8e2b54e0..db9b3a668 100644 --- a/src/components/console/spec/helper/progress_bar_spec.cr +++ b/src/components/console/spec/helper/progress_bar_spec.cr @@ -339,7 +339,7 @@ struct ProgressBarTest < ASPEC::TestCase output = ACON::Output::Section.new acon_output.io, sections, verbosity: acon_output.verbosity, decorated: acon_output.decorated?, formatter: ACON::Formatter::Output.new bar = ACON::Helper::ProgressBar.new output, 50, 0 - bar.format = " \033[44;37m%current%/%max%\033[0m [%bar%] %percent:3s%%" + bar.format = " \e[44;37m%current%/%max%\e[0m [%bar%] %percent:3s%%" bar.start bar.display bar.advance @@ -347,9 +347,9 @@ struct ProgressBarTest < ASPEC::TestCase self.assert_output( output, - " \033[44;37m 0/50\033[0m [>---------------------------] 0%#{EOL}", - "\e[1A\e[0J \033[44;37m 1/50\033[0m [>---------------------------] 2%#{EOL}", - "\e[1A\e[0J \033[44;37m 2/50\033[0m [=>--------------------------] 4%#{EOL}", + " \e[44;37m 0/50\e[0m [>---------------------------] 0%#{EOL}", + "\e[1A\e[0J \e[44;37m 1/50\e[0m [>---------------------------] 2%#{EOL}", + "\e[1A\e[0J \e[44;37m 2/50\e[0m [=>--------------------------] 4%#{EOL}", ) end @@ -655,7 +655,7 @@ struct ProgressBarTest < ASPEC::TestCase 1.upto 3 do |idx| # Up two lines - output.print "\033[2A" + output.print "\e[2A" if idx <= 2 bar1.advance @@ -667,7 +667,7 @@ struct ProgressBarTest < ASPEC::TestCase bar3.advance end - output.print "\033[2A" + output.print "\e[2A" output.print "\n" output.print "\n" bar3.finish @@ -678,27 +678,27 @@ struct ProgressBarTest < ASPEC::TestCase " 0/3 [#---------------------------] 0%\n", " 0 [>---------------------------]", - "\033[2A", + "\e[2A", self.generate_output(" 1/2 [==============>-------------] 50%"), "\n", self.generate_output(" 1/3 [=========#------------------] 33%"), "\n", self.generate_output(" 1 [->--------------------------]").rstrip, - "\033[2A", + "\e[2A", self.generate_output(" 2/2 [============================] 100%"), "\n", self.generate_output(" 2/3 [==================#---------] 66%"), "\n", self.generate_output(" 2 [-->-------------------------]").rstrip, - "\033[2A", + "\e[2A", "\n", self.generate_output(" 3/3 [============================] 100%"), "\n", self.generate_output(" 3 [--->------------------------]").rstrip, - "\033[2A", + "\e[2A", "\n", "\n", self.generate_output(" 3 [============================]").rstrip, @@ -1019,7 +1019,7 @@ struct ProgressBarTest < ASPEC::TestCase colors = idx.zero? ? "44;37" : "41;37" idx += 1 - "\033[#{colors}m #{mem.humanize_bytes} \033[0m" + "\e[#{colors}m #{mem.humanize_bytes} \e[0m" end bar = ACON::Helper::ProgressBar.new output = self.output, 15, 0 @@ -1046,9 +1046,9 @@ struct ProgressBarTest < ASPEC::TestCase self.assert_output( output, self.generate_output( - " \033[44;37m Looks good to me... \033[0m\n", + " \e[44;37m Looks good to me... \e[0m\n", " 4/15 #{done * 7}#{progress}#{empty * 19} 26%\n", - " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 98kiB \033[0m", + " \xf0\x9f\x8f\x81 < 1 sec \e[41;37m 98kiB \e[0m", ) ) @@ -1060,9 +1060,9 @@ struct ProgressBarTest < ASPEC::TestCase self.assert_output( output, self.generate_output( - " \033[44;37m Thanks, bye \033[0m\n", + " \e[44;37m Thanks, bye \e[0m\n", " 15/15 #{done * 28} 100%\n", - " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195kiB \033[0m", + " \xf0\x9f\x8f\x81 < 1 sec \e[41;37m 195kiB \e[0m", ) ) @@ -1186,9 +1186,9 @@ struct ProgressBarTest < ASPEC::TestCase self.assert_output( output, - "[>---------------------------] 0%#{EOL}\x1b[33mStart\x1b[0m#{EOL}", + "[>---------------------------] 0%#{EOL}\x1b[33mStart\x1b[39m#{EOL}", "\x1b[2A\x1b[0J[>---------------------------] 2%#{EOL}", - "\x1b[1A\x1b[0J[=>--------------------------] 4%#{EOL}\x1b[33mDoing something...\x1b[0m#{EOL}", + "\x1b[1A\x1b[0J[=>--------------------------] 4%#{EOL}\x1b[33mDoing something...\x1b[39m#{EOL}", ) end diff --git a/src/components/console/spec/helper/table_spec.cr b/src/components/console/spec/helper/table_spec.cr index f7237ffc2..26ec4ec43 100644 --- a/src/components/console/spec/helper/table_spec.cr +++ b/src/components/console/spec/helper/table_spec.cr @@ -834,18 +834,18 @@ struct TableSpec < ASPEC::TestCase self.output_content(output).should eq self.normalize <<-TABLE +---------------+---------------+-----------------+-------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +---------------+---------------+-----------------+-------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 | +---------------+---------------+-----------------+-------+ +---------------+----------------------+-----------------+--------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 | | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 | +---------------+----------------------+-----------------+--------+ +---------------+----------------------+-----------------+--------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 | | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 | @@ -872,7 +872,7 @@ struct TableSpec < ASPEC::TestCase self.output_content(output).should eq self.normalize <<-TABLE +---------------+----------------------+-----------------+--------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 | | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 | @@ -928,10 +928,10 @@ struct TableSpec < ASPEC::TestCase self.output_content(output).should eq self.normalize <<-TABLE +------+-------+--------+-------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +------+-------+--------+-------+ +---------------+----------------------+-----------------+--------+ - | ISBN | Title | Author | Price | + | ISBN | Title | Author | Price | +---------------+----------------------+-----------------+--------+ | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 | +---------------+----------------------+-----------------+--------+ diff --git a/src/components/console/spec/output/output_spec.cr b/src/components/console/spec/output/output_spec.cr index f545549f2..9debc60ed 100644 --- a/src/components/console/spec/output/output_spec.cr +++ b/src/components/console/spec/output/output_spec.cr @@ -55,7 +55,7 @@ struct OutputTest < ASPEC::TestCase output.formatter.has_style?("FOO").should be_true output.decorated = true output.puts "foo" - output.output.should eq "\e[33;41;5mfoo\e[0m\n" + output.output.should eq "\e[33;41;5mfoo\e[39;49;25m\n" end def test_write_decorated_invalid_style : Nil diff --git a/src/components/console/src/formatter/output.cr b/src/components/console/src/formatter/output.cr index 9e0aa4a29..759fbf7ca 100644 --- a/src/components/console/src/formatter/output.cr +++ b/src/components/console/src/formatter/output.cr @@ -69,7 +69,7 @@ class Athena::Console::Formatter::Output @current_line_length = 0 - message.scan(/<(([a-z][^<>]*+) | \/([a-z][^<>]*+)?)>/ix) do |match| + message.scan(/<(([a-z](?:[^\\<>]*+ | \\.)*) | \/([a-z][^<>]*+)?)>/ix) do |match| pos = match.begin.not_nil! text = match[0] @@ -137,7 +137,9 @@ class Athena::Console::Formatter::Output # ameba:disable Metrics/CyclomaticComplexity private def apply_current_style(text : String, current : String, width : Int32) - return "" if text.empty? + if text.empty? + return "" + end if width.zero? return self.decorated? ? @style_stack.current.apply(text) : text diff --git a/src/components/console/src/formatter/output_formatter_style_stack.cr b/src/components/console/src/formatter/output_formatter_style_stack.cr index 7d466472c..a09fd6505 100644 --- a/src/components/console/src/formatter/output_formatter_style_stack.cr +++ b/src/components/console/src/formatter/output_formatter_style_stack.cr @@ -21,12 +21,12 @@ struct Athena::Console::Formatter::OutputStyleStack return @styles.pop if style.nil? - @styles.reverse_each.each_with_index do |stacked_style, idx| - if style.apply("") == stacked_style.apply("") - @styles = @styles[0...idx] + if match_index = @styles.rindex { |stacked_style| style.apply("") == stacked_style.apply("") } + matched_style = @styles[match_index] - return stacked_style - end + @styles = @styles[0...match_index] + + return matched_style end raise ACON::Exception::InvalidArgument.new "Provided style is not present in the stack." diff --git a/src/components/console/src/formatter/output_style.cr b/src/components/console/src/formatter/output_style.cr index 4f56201f4..70c834a6c 100644 --- a/src/components/console/src/formatter/output_style.cr +++ b/src/components/console/src/formatter/output_style.cr @@ -73,17 +73,83 @@ struct Athena::Console::Formatter::OutputStyle text = "\e]8;;#{href}\e\\#{text}\e]8;;\e\\" end - color = Colorize::Object(String) - .new(text) - .fore(@foreground) - .back(@background) - - if options = @options - options.each do |mode| - color.mode mode + return text if self.default? + + apply_color text + end + + # TODO: Remove methods below when/if https://github.com/crystal-lang/crystal/pull/16052 is merged/released. + # Should then bump min crystal version. + + private def apply_color(text : String) : String + String.build do |io| + printed = false + + io << "\e[" + + unless @foreground == Colorize::ColorANSI::Default + @foreground.fore io + printed = true + end + + unless @background == Colorize::ColorANSI::Default + io << ';' if printed + @background.back io + printed = true + end + + each_code(@options) do |flag| + io << ';' if printed + io << flag + printed = true + end + + io << 'm' + + io << text + + printed = false + + io << "\e[" + + unless @foreground == Colorize::ColorANSI::Default + io << ';' if printed + io << 39 + printed = true + end + + unless @background == Colorize::ColorANSI::Default + io << ';' if printed + io << 49 + printed = true end + + each_code(@options, true) do |flag| + io << ';' if printed + io << flag + printed = true + end + + io << 'm' end + end + + private def default? : Bool + @foreground == Colorize::ColorANSI::Default && @background == Colorize::ColorANSI::Default && @options.none? + end - color.to_s + # ameba:disable Metrics/CyclomaticComplexity + private def each_code(mode : Colorize::Mode, unset : Bool = false, &) + yield (unset ? "22" : "1") if mode.bold? + yield (unset ? "22" : "2") if mode.dim? + yield (unset ? "23" : "3") if mode.italic? + yield (unset ? "24" : "4") if mode.underline? + yield (unset ? "25" : "5") if mode.blink? + yield (unset ? "26" : "6") if mode.blink_fast? + yield (unset ? "27" : "7") if mode.reverse? + yield (unset ? "28" : "8") if mode.hidden? + yield (unset ? "29" : "9") if mode.strikethrough? + yield (unset ? "24" : "21") if mode.double_underline? + yield (unset ? "55" : "53") if mode.overline? end end