diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..9b28abf --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,22 @@ +name: Dependabot auto-merge +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1.3.3 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c48bd8a..bcd3384 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,19 +24,3 @@ jobs: ruby-version: ${{ matrix.ruby }} - name: Test run: bundle exec rake test - - automerge: - name: AutoMerge - needs: - - ci - runs-on: ubuntu-latest - if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]' - steps: - - uses: actions/github-script@v3 - with: - script: | - github.pulls.merge({ - owner: context.payload.repository.owner.login, - repo: context.payload.repository.name, - pull_number: context.payload.pull_request.number - }) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ff878..42bd93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +## [1.1.0] - 2022-11-08 + +### Added + +- `prettier_print` now works with Ractors. + ## [1.0.2] - 2022-10-19 ### Changed @@ -43,7 +49,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - 🎉 Initial release! 🎉 -[unreleased]: https://github.com/ruby-syntax-tree/prettier_print/compare/v1.0.2...HEAD +[unreleased]: https://github.com/ruby-syntax-tree/prettier_print/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/ruby-syntax-tree/prettier_print/compare/v1.0.2...v1.1.0 [1.0.2]: https://github.com/ruby-syntax-tree/prettier_print/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/ruby-syntax-tree/prettier_print/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/ruby-syntax-tree/prettier_print/compare/v0.1.0...v1.0.0 diff --git a/Gemfile b/Gemfile index b6e59d0..7459803 100644 --- a/Gemfile +++ b/Gemfile @@ -4,5 +4,6 @@ source "https://rubygems.org" gemspec +gem "simplecov" gem "rake" gem "test-unit" diff --git a/Gemfile.lock b/Gemfile.lock index 357299d..a6dd75f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,20 @@ PATH remote: . specs: - prettier_print (1.0.2) + prettier_print (1.1.0) GEM remote: https://rubygems.org/ specs: + docile (1.4.0) power_assert (2.0.1) rake (13.0.6) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) test-unit (3.5.5) power_assert @@ -19,6 +26,7 @@ PLATFORMS DEPENDENCIES prettier_print! rake + simplecov test-unit BUNDLED WITH diff --git a/lib/prettier_print.rb b/lib/prettier_print.rb index 423b8de..537f64a 100644 --- a/lib/prettier_print.rb +++ b/lib/prettier_print.rb @@ -122,10 +122,10 @@ def pretty_print(q) # Below here are the most common combination of options that are created when # creating new breakables. They are here to cut down on some allocations. - BREAKABLE_SPACE = Breakable.new(" ", 1, indent: true, force: false) - BREAKABLE_EMPTY = Breakable.new("", 0, indent: true, force: false) - BREAKABLE_FORCE = Breakable.new(" ", 1, indent: true, force: true) - BREAKABLE_RETURN = Breakable.new(" ", 1, indent: false, force: true) + BREAKABLE_SPACE = Breakable.new(" ", 1, indent: true, force: false).freeze + BREAKABLE_EMPTY = Breakable.new("", 0, indent: true, force: false).freeze + BREAKABLE_FORCE = Breakable.new(" ", 1, indent: true, force: true).freeze + BREAKABLE_RETURN = Breakable.new(" ", 1, indent: false, force: true).freeze # A node in the print tree that forces the surrounding group to print out in # the "break" mode as opposed to the "flat" mode. Useful for when you need to @@ -138,7 +138,7 @@ def pretty_print(q) # Since there's really no difference in these instances, just using the same # one saves on some allocations. - BREAK_PARENT = BreakParent.new + BREAK_PARENT = BreakParent.new.freeze # A node in the print tree that represents a group of items which the printer # should try to fit onto one line. This is the basic command to tell the @@ -267,7 +267,7 @@ def pretty_print(q) # Since all of the instances here are the same, we can reuse the same one to # cut down on allocations. - TRIM = Trim.new + TRIM = Trim.new.freeze # When building up the contents in the output buffer, it's convenient to be # able to trim trailing whitespace before newlines. If the output object is a @@ -338,151 +338,6 @@ def self.for(output) end end - # PrettierPrint::SingleLine is used by PrettierPrint.singleline_format - # - # It is passed to be similar to a PrettierPrint object itself, by responding to - # all of the same print tree node builder methods, as well as the #flush - # method. - # - # The significant difference here is that there are no line breaks in the - # output. If an IfBreak node is used, only the flat contents are printed. - # LineSuffix nodes are printed at the end of the buffer when #flush is called. - class SingleLine - # The output object. It stores rendered text and should respond to <<. - attr_reader :output - - # The current array of contents that the print tree builder methods should - # append to. - attr_reader :target - - # A buffer output that wraps any calls to line_suffix that will be flushed - # at the end of printing. - attr_reader :line_suffixes - - # Create a PrettierPrint::SingleLine object - # - # Arguments: - # * +output+ - String (or similar) to store rendered text. Needs to respond - # to '<<'. - # * +maxwidth+ - Argument position expected to be here for compatibility. - # This argument is a noop. - # * +newline+ - Argument position expected to be here for compatibility. - # This argument is a noop. - def initialize(output, _maxwidth = nil, _newline = nil) - @output = Buffer.for(output) - @target = @output - @line_suffixes = Buffer::ArrayBuffer.new - end - - # Flushes the line suffixes onto the output buffer. - def flush - line_suffixes.output.each { |doc| output << doc } - end - - # -------------------------------------------------------------------------- - # Markers node builders - # -------------------------------------------------------------------------- - - # Appends +separator+ to the text to be output. By default +separator+ is - # ' ' - # - # The +width+, +indent+, and +force+ arguments are here for compatibility. - # They are all noop arguments. - def breakable( - separator = " ", - _width = separator.length, - indent: nil, - force: nil - ) - target << separator - end - - # Here for compatibility, does nothing. - def break_parent - end - - # Appends +separator+ to the output buffer. +width+ is a noop here for - # compatibility. - def fill_breakable(separator = " ", _width = separator.length) - target << separator - end - - # Immediately trims the output buffer. - def trim - target.trim! - end - - # -------------------------------------------------------------------------- - # Container node builders - # -------------------------------------------------------------------------- - - # Opens a block for grouping objects to be pretty printed. - # - # Arguments: - # * +indent+ - noop argument. Present for compatibility. - # * +open_obj+ - text appended before the &block. Default is '' - # * +close_obj+ - text appended after the &block. Default is '' - # * +open_width+ - noop argument. Present for compatibility. - # * +close_width+ - noop argument. Present for compatibility. - def group( - _indent = nil, - open_object = "", - close_object = "", - _open_width = nil, - _close_width = nil - ) - target << open_object - yield - target << close_object - end - - # A class that wraps the ability to call #if_flat. The contents of the - # #if_flat block are executed immediately, so effectively this class and the - # #if_break method that triggers it are unnecessary, but they're here to - # maintain compatibility. - class IfBreakBuilder - def if_flat - yield - end - end - - # Effectively unnecessary, but here for compatibility. - def if_break - IfBreakBuilder.new - end - - # Also effectively unnecessary, but here for compatibility. - def if_flat - end - - # A noop that immediately yields. - def indent - yield - end - - # Changes the target output buffer to the line suffix output buffer which - # will get flushed at the end of printing. - def line_suffix - previous_target, @target = @target, line_suffixes - yield - @target = previous_target - end - - # Takes +indent+ arg, but does nothing with it. - # - # Yields to a block. - def nest(_indent) - yield - end - - # Add +object+ to the text to be output. - # - # +width+ argument is here for compatibility. It is a noop argument. - def text(object = "", _width = nil) - target << object - end - end - # When printing, you can optionally specify the value that should be used # whenever a group needs to be broken onto multiple lines. In this case the # default is \n. @@ -493,6 +348,7 @@ def text(object = "", _width = nil) # behavior (for instance to use tabs) by passing a different genspace # procedure. DEFAULT_GENSPACE = ->(n) { " " * n } + Ractor.make_shareable(DEFAULT_GENSPACE) if defined?(Ractor) # There are two modes in printing, break and flat. When we're in break mode, # any lines will use their newline, any if-breaks will use their break @@ -525,24 +381,6 @@ def self.format( output end - # This is similar to PrettierPrint::format but the result has no breaks. - # - # +maxwidth+, +newline+ and +genspace+ are ignored. - # - # The invocation of +breakable+ in the block doesn't break a line and is - # treated as just an invocation of +text+. - # - def self.singleline_format( - output = +"", - _maxwidth = nil, - _newline = nil, - _genspace = nil - ) - q = SingleLine.new(output) - yield q - output - end - # The output object. It represents the final destination of the contents of # the print tree. It should respond to <<. # @@ -1251,3 +1089,5 @@ def remove_breaks_with(doc, replace) end end end + +require_relative "prettier_print/single_line" diff --git a/lib/prettier_print/single_line.rb b/lib/prettier_print/single_line.rb new file mode 100644 index 0000000..e9d89fa --- /dev/null +++ b/lib/prettier_print/single_line.rb @@ -0,0 +1,166 @@ +# frozen_string_literal: true + +class PrettierPrint + # PrettierPrint::SingleLine is used by PrettierPrint.singleline_format + # + # It is passed to be similar to a PrettierPrint object itself, by responding to + # all of the same print tree node builder methods, as well as the #flush + # method. + # + # The significant difference here is that there are no line breaks in the + # output. If an IfBreak node is used, only the flat contents are printed. + # LineSuffix nodes are printed at the end of the buffer when #flush is called. + class SingleLine + # The output object. It stores rendered text and should respond to <<. + attr_reader :output + + # The current array of contents that the print tree builder methods should + # append to. + attr_reader :target + + # A buffer output that wraps any calls to line_suffix that will be flushed + # at the end of printing. + attr_reader :line_suffixes + + # Create a PrettierPrint::SingleLine object + # + # Arguments: + # * +output+ - String (or similar) to store rendered text. Needs to respond + # to '<<'. + # * +maxwidth+ - Argument position expected to be here for compatibility. + # This argument is a noop. + # * +newline+ - Argument position expected to be here for compatibility. + # This argument is a noop. + def initialize(output, _maxwidth = nil, _newline = nil) + @output = Buffer.for(output) + @target = @output + @line_suffixes = Buffer::ArrayBuffer.new + end + + # Flushes the line suffixes onto the output buffer. + def flush + line_suffixes.output.each { |doc| output << doc } + end + + # -------------------------------------------------------------------------- + # Markers node builders + # -------------------------------------------------------------------------- + + # Appends +separator+ to the text to be output. By default +separator+ is + # ' ' + # + # The +width+, +indent+, and +force+ arguments are here for compatibility. + # They are all noop arguments. + def breakable( + separator = " ", + _width = separator.length, + indent: nil, + force: nil + ) + target << separator + end + + # Here for compatibility, does nothing. + def break_parent + end + + # Appends +separator+ to the output buffer. +width+ is a noop here for + # compatibility. + def fill_breakable(separator = " ", _width = separator.length) + target << separator + end + + # Immediately trims the output buffer. + def trim + target.trim! + end + + # -------------------------------------------------------------------------- + # Container node builders + # -------------------------------------------------------------------------- + + # Opens a block for grouping objects to be pretty printed. + # + # Arguments: + # * +indent+ - noop argument. Present for compatibility. + # * +open_obj+ - text appended before the &block. Default is '' + # * +close_obj+ - text appended after the &block. Default is '' + # * +open_width+ - noop argument. Present for compatibility. + # * +close_width+ - noop argument. Present for compatibility. + def group( + _indent = nil, + open_object = "", + close_object = "", + _open_width = nil, + _close_width = nil + ) + target << open_object + yield + target << close_object + end + + # A class that wraps the ability to call #if_flat. The contents of the + # #if_flat block are executed immediately, so effectively this class and the + # #if_break method that triggers it are unnecessary, but they're here to + # maintain compatibility. + class IfBreakBuilder + def if_flat + yield + end + end + + # Effectively unnecessary, but here for compatibility. + def if_break + IfBreakBuilder.new + end + + # Also effectively unnecessary, but here for compatibility. + def if_flat + end + + # A noop that immediately yields. + def indent + yield + end + + # Changes the target output buffer to the line suffix output buffer which + # will get flushed at the end of printing. + def line_suffix + previous_target, @target = @target, line_suffixes + yield + @target = previous_target + end + + # Takes +indent+ arg, but does nothing with it. + # + # Yields to a block. + def nest(_indent) + yield + end + + # Add +object+ to the text to be output. + # + # +width+ argument is here for compatibility. It is a noop argument. + def text(object = "", _width = nil) + target << object + end + end + + # This is similar to PrettierPrint::format but the result has no breaks. + # + # +maxwidth+, +newline+ and +genspace+ are ignored. + # + # The invocation of +breakable+ in the block doesn't break a line and is + # treated as just an invocation of +text+. + # + def self.singleline_format( + output = +"", + _maxwidth = nil, + _newline = nil, + _genspace = nil + ) + q = SingleLine.new(output) + yield q + output + end +end diff --git a/lib/prettier_print/version.rb b/lib/prettier_print/version.rb index 4a46852..9194059 100644 --- a/lib/prettier_print/version.rb +++ b/lib/prettier_print/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class PrettierPrint - VERSION = "1.0.2" + VERSION = "1.1.0" end diff --git a/test/prettier_print_test.rb b/test/prettier_print_test.rb index 7bca9d8..51c2faa 100644 --- a/test/prettier_print_test.rb +++ b/test/prettier_print_test.rb @@ -1,520 +1,217 @@ # frozen_string_literal: true require "test_helper" +require "pp" -module PrettierPrintTest - -class WadlerExample < Test::Unit::TestCase # :nodoc: - def setup - @tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"), - Tree.new("dd")), - Tree.new("eee"), - Tree.new("ffff", Tree.new("gg"), - Tree.new("hhh"), - Tree.new("ii"))) - end - - def hello(width) - PrettierPrint.format(''.dup, width) {|hello| - hello.group { - hello.group { - hello.group { - hello.group { - hello.text 'hello' - hello.breakable; hello.text 'a' - } - hello.breakable; hello.text 'b' - } - hello.breakable; hello.text 'c' - } - hello.breakable; hello.text 'd' - } - } - end - - def test_hello_00_06 - expected = <<'End'.chomp -hello -a -b -c -d -End - assert_equal(expected, hello(0)) - assert_equal(expected, hello(6)) - end +class PrettierPrint + class PrettierPrintTest < Test::Unit::TestCase + test "Align#pretty_print" do + assert_equal "align0([])\n", PP.pp(Align.new(indent: 0), +"") + end - def test_hello_07_08 - expected = <<'End'.chomp -hello a -b -c -d -End - assert_equal(expected, hello(7)) - assert_equal(expected, hello(8)) - end + test "Breakable#pretty_print" do + assert_equal "breakable\n", PP.pp(Breakable.new, +"") + end - def test_hello_09_10 - expected = <<'End'.chomp -hello a b -c -d -End - out = hello(9); assert_equal(expected, out) - out = hello(10); assert_equal(expected, out) - end + test "Breakable#pretty_print force=true" do + assert_equal "breakable(force=true)\n", PP.pp(Breakable.new(force: true), +"") + end - def test_hello_11_12 - expected = <<'End'.chomp -hello a b c -d -End - assert_equal(expected, hello(11)) - assert_equal(expected, hello(12)) - end + test "Breakable#pretty_print indent=false" do + assert_equal "breakable(indent=false)\n", PP.pp(Breakable.new(indent: false), +"") + end - def test_hello_13 - expected = <<'End'.chomp -hello a b c d -End - assert_equal(expected, hello(13)) - end + test "BreakParent#pretty_print" do + assert_equal "break-parent\n", PP.pp(BreakParent.new, +"") + end - def tree(width) - PrettierPrint.format(''.dup, width) {|q| @tree.show(q)} - end + test "Group#pretty_print" do + assert_equal "group([])\n", PP.pp(Group.new(0), +"") + end - def test_tree_00_19 - expected = <<'End'.chomp -aaaa[bbbbb[ccc, - dd], - eee, - ffff[gg, - hhh, - ii]] -End - assert_equal(expected, tree(0)) - assert_equal(expected, tree(19)) - end + test "Group#pretty_print break" do + assert_equal "breakGroup([])\n", PP.pp(Group.new(0).tap(&:break), +"") + end - def test_tree_20_22 - expected = <<'End'.chomp -aaaa[bbbbb[ccc, dd], - eee, - ffff[gg, - hhh, - ii]] -End - assert_equal(expected, tree(20)) - assert_equal(expected, tree(22)) - end + test "IfBreak#pretty_print" do + assert_equal "if-break([], [])\n", PP.pp(IfBreak.new, +"") + end - def test_tree_23_43 - expected = <<'End'.chomp -aaaa[bbbbb[ccc, dd], - eee, - ffff[gg, hhh, ii]] -End - assert_equal(expected, tree(23)) - assert_equal(expected, tree(43)) - end + test "Indent#pretty_print" do + assert_equal "indent([])\n", PP.pp(Indent.new, +"") + end - def test_tree_44 - assert_equal(<<'End'.chomp, tree(44)) -aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]] -End - end + test "LineSuffix#pretty_print" do + assert_equal "line-suffix([])\n", PP.pp(LineSuffix.new, +"") + end - def tree_alt(width) - PrettierPrint.format(''.dup, width) {|q| @tree.altshow(q)} - end + test "Text#pretty_print" do + assert_equal "text([])\n", PP.pp(Text.new, +"") + end - def test_tree_alt_00_18 - expected = <<'End'.chomp -aaaa[ - bbbbb[ - ccc, - dd - ], - eee, - ffff[ - gg, - hhh, - ii - ] -] -End - assert_equal(expected, tree_alt(0)) - assert_equal(expected, tree_alt(18)) - end + test "Trim#pretty_print" do + assert_equal "trim\n", PP.pp(Trim.new, +"") + end - def test_tree_alt_19_20 - expected = <<'End'.chomp -aaaa[ - bbbbb[ ccc, dd ], - eee, - ffff[ - gg, - hhh, - ii - ] -] -End - assert_equal(expected, tree_alt(19)) - assert_equal(expected, tree_alt(20)) - end + test "Buffer::StrinfBuffer#trim!" do + buffer = Buffer::StringBuffer.new + buffer << "......" + buffer << +"... " + buffer << " " + buffer << "\t\t\t" + buffer << "\t \t " - def test_tree_alt_20_49 - expected = <<'End'.chomp -aaaa[ - bbbbb[ ccc, dd ], - eee, - ffff[ gg, hhh, ii ] -] -End - assert_equal(expected, tree_alt(21)) - assert_equal(expected, tree_alt(49)) - end + buffer.trim! + assert_equal ".........", buffer.output + end - def test_tree_alt_50 - expected = <<'End'.chomp -aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ] -End - assert_equal(expected, tree_alt(50)) - end + test "Buffer::ArrayBuffer#trim!" do + buffer = Buffer::ArrayBuffer.new + buffer << "......" + buffer << +"... " + buffer << " " + buffer << "\t\t\t" + buffer << "\t \t " - class Tree # :nodoc: - def initialize(string, *children) - @string = string - @children = children + buffer.trim! + assert_equal ".........", buffer.output.join end - def show(q) - q.group { - q.text @string - q.nest(@string.length) { - unless @children.empty? - q.text '[' - q.nest(1) { - first = true - @children.each {|t| - if first - first = false - else - q.text ',' - q.breakable - end - t.show(q) - } - } - q.text ']' + test "PrettierPrint#nest" do + result = + PrettierPrint.format do |q| + q.nest(4) do + q.breakable(force: true) + q.text("content") end - } - } - end - - def altshow(q) - q.group { - q.text @string - unless @children.empty? - q.text '[' - q.nest(2) { - q.breakable - first = true - @children.each {|t| - if first - first = false - else - q.text ',' - q.breakable - end - t.altshow(q) - } - } - q.breakable - q.text ']' end - } - end - end -end + assert_equal "\n content", result + end -class StrictPrettyExample < Test::Unit::TestCase # :nodoc: - def prog(width) - PrettierPrint.format(''.dup, width) {|q| - q.group { - q.group {q.nest(2) { - q.text "if"; q.breakable; - q.group { - q.nest(2) { - q.group {q.text "a"; q.breakable; q.text "=="} - q.breakable; q.text "b"}}}} - q.breakable - q.group {q.nest(2) { - q.text "then"; q.breakable; - q.group { - q.nest(2) { - q.group {q.text "a"; q.breakable; q.text "<<"} - q.breakable; q.text "2"}}}} - q.breakable - q.group {q.nest(2) { - q.text "else"; q.breakable; - q.group { - q.nest(2) { - q.group {q.text "a"; q.breakable; q.text "+"} - q.breakable; q.text "b"}}}}} - } - end + test "PrettierPrint#breakable(indent: false)" do + result = + PrettierPrint.format do |q| + q.nest(4) do + q.breakable_return + q.text("content") + end + end - def test_00_04 - expected = <<'End'.chomp -if - a - == - b -then - a - << - 2 -else - a - + - b -End - assert_equal(expected, prog(0)) - assert_equal(expected, prog(4)) - end + assert_equal "\ncontent", result + end - def test_05 - expected = <<'End'.chomp -if - a - == - b -then - a - << - 2 -else - a + - b -End - assert_equal(expected, prog(5)) - end + test "PrettierPrint#break_parent" do + result = + PrettierPrint.format do |q| + q.if_break { q.text("break") }.if_flat { q.text("flat") } + q.break_parent + end - def test_06 - expected = <<'End'.chomp -if - a == - b -then - a << - 2 -else - a + - b -End - assert_equal(expected, prog(6)) - end + assert_equal "break", result + end - def test_07 - expected = <<'End'.chomp -if - a == - b -then - a << - 2 -else - a + b -End - assert_equal(expected, prog(7)) - end + test "PrettierPrint#if_break" do + result = + PrettierPrint.format do |q| + q.if_break { q.text("break") }.if_flat { q.text("flat") } + end - def test_08 - expected = <<'End'.chomp -if - a == b -then - a << 2 -else - a + b -End - assert_equal(expected, prog(8)) - end + assert_equal "flat", result + end - def test_09 - expected = <<'End'.chomp -if a == b -then - a << 2 -else - a + b -End - assert_equal(expected, prog(9)) - end + test "PrettierPrint#if_flat" do + result = + PrettierPrint.format do |q| + q.if_flat { q.text("flat") } + end - def test_10 - expected = <<'End'.chomp -if a == b -then - a << 2 -else a + b -End - assert_equal(expected, prog(10)) - end + assert_equal "flat", result + end - def test_11_31 - expected = <<'End'.chomp -if a == b -then a << 2 -else a + b -End - assert_equal(expected, prog(11)) - assert_equal(expected, prog(15)) - assert_equal(expected, prog(31)) - end + test "PrettierPrint#indent" do + result = + PrettierPrint.format do |q| + q.indent do + q.breakable_force + q.text("content") + end + end - def test_32 - expected = <<'End'.chomp -if a == b then a << 2 else a + b -End - assert_equal(expected, prog(32)) - end + assert_equal "\n content", result + end -end + test "PrettierPrint#line_suffix" do + result = + PrettierPrint.format do |q| + q.line_suffix { q.text(" # suffix") } + q.text("content") + q.breakable_force + end -class TailGroup < Test::Unit::TestCase # :nodoc: - def test_1 - out = PrettierPrint.format(''.dup, 10) {|q| - q.group { - q.group { - q.text "abc" - q.breakable - q.text "def" - } - q.group { - q.text "ghi" - q.breakable - q.text "jkl" - } - } - } - assert_equal("abc defghi\njkl", out) - end -end + assert_equal "content # suffix\n", result + end -class NonString < Test::Unit::TestCase # :nodoc: - def format(width) - PrettierPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|q| - q.text(3, 3) - q.breakable(1, 1) - q.text(3, 3) - } - end + test "PrettierPrint#trim" do + result = + PrettierPrint.format do |q| + q.indent do + q.breakable_force + q.text("first content") + q.breakable_force + q.trim + q.text("second content") + end + end - def test_6 - assert_equal([3, "newline", "0 spaces", 3], format(6)) - end + assert_equal "\n first content\nsecond content", result + end - def test_7 - assert_equal([3, 1, 3], format(7)) - end + test "PrettierPrint pushing strings" do + result = + PrettierPrint.format do |q| + q.target << "content" + end -end + assert_equal "content", result + end -class Fill < Test::Unit::TestCase # :nodoc: - def format(width) - PrettierPrint.format(''.dup, width) {|q| - q.group { - q.text 'abc' - q.fill_breakable - q.text 'def' - q.fill_breakable - q.text 'ghi' - q.fill_breakable - q.text 'jkl' - q.fill_breakable - q.text 'mno' - q.fill_breakable - q.text 'pqr' - q.fill_breakable - q.text 'stu' - } - } - end + test "PrettierPrint pushing unknown objects" do + q = PrettierPrint.new([]) + q.target << Object.new + q.target << "content" + q.flush + q.output.shift + assert_equal "content", q.output.join + end - def test_00_06 - expected = <<'End'.chomp -abc -def -ghi -jkl -mno -pqr -stu -End - assert_equal(expected, format(0)) - assert_equal(expected, format(6)) - end + test "PrettierPrint#last_position string" do + q = PrettierPrint.new + assert_equal 5, q.last_position(".....") + end - def test_07_10 - expected = <<'End'.chomp -abc def -ghi jkl -mno pqr -stu -End - assert_equal(expected, format(7)) - assert_equal(expected, format(10)) - end + test "PrettierPrint#last_position group" do + q = PrettierPrint.new + q.text(".....") - def test_11_14 - expected = <<'End'.chomp -abc def ghi -jkl mno pqr -stu -End - assert_equal(expected, format(11)) - assert_equal(expected, format(14)) - end + assert_equal 5, q.last_position(q.groups.first) + end - def test_15_18 - expected = <<'End'.chomp -abc def ghi jkl -mno pqr stu -End - assert_equal(expected, format(15)) - assert_equal(expected, format(18)) - end + test "PrettierPrint#last_position breakable" do + q = PrettierPrint.new + q.text(".....") + q.breakable + q.text("...") - def test_19_22 - expected = <<'End'.chomp -abc def ghi jkl mno -pqr stu -End - assert_equal(expected, format(19)) - assert_equal(expected, format(22)) - end + assert_equal 3, q.last_position(q.groups.first) + end - def test_23_26 - expected = <<'End'.chomp -abc def ghi jkl mno pqr -stu -End - assert_equal(expected, format(23)) - assert_equal(expected, format(26)) - end + test "PrettierPrint#last_position if break" do + q = PrettierPrint.new + q.text(".....") + q.if_break { q.text("...") } - def test_27 - expected = <<'End'.chomp -abc def ghi jkl mno pqr stu -End - assert_equal(expected, format(27)) + assert_equal 8, q.last_position(q.groups.first) + end end - -end - end diff --git a/test/prettyprint_test.rb b/test/prettyprint_test.rb new file mode 100644 index 0000000..cc61041 --- /dev/null +++ b/test/prettyprint_test.rb @@ -0,0 +1,520 @@ +# frozen_string_literal: true + +require "test_helper" + +module PrettyPrintTest + +class WadlerExample < Test::Unit::TestCase # :nodoc: + def setup + @tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"), + Tree.new("dd")), + Tree.new("eee"), + Tree.new("ffff", Tree.new("gg"), + Tree.new("hhh"), + Tree.new("ii"))) + end + + def hello(width) + PrettierPrint.format(''.dup, width) {|hello| + hello.group { + hello.group { + hello.group { + hello.group { + hello.text 'hello' + hello.breakable; hello.text 'a' + } + hello.breakable; hello.text 'b' + } + hello.breakable; hello.text 'c' + } + hello.breakable; hello.text 'd' + } + } + end + + def test_hello_00_06 + expected = <<'End'.chomp +hello +a +b +c +d +End + assert_equal(expected, hello(0)) + assert_equal(expected, hello(6)) + end + + def test_hello_07_08 + expected = <<'End'.chomp +hello a +b +c +d +End + assert_equal(expected, hello(7)) + assert_equal(expected, hello(8)) + end + + def test_hello_09_10 + expected = <<'End'.chomp +hello a b +c +d +End + out = hello(9); assert_equal(expected, out) + out = hello(10); assert_equal(expected, out) + end + + def test_hello_11_12 + expected = <<'End'.chomp +hello a b c +d +End + assert_equal(expected, hello(11)) + assert_equal(expected, hello(12)) + end + + def test_hello_13 + expected = <<'End'.chomp +hello a b c d +End + assert_equal(expected, hello(13)) + end + + def tree(width) + PrettierPrint.format(''.dup, width) {|q| @tree.show(q)} + end + + def test_tree_00_19 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, + dd], + eee, + ffff[gg, + hhh, + ii]] +End + assert_equal(expected, tree(0)) + assert_equal(expected, tree(19)) + end + + def test_tree_20_22 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, dd], + eee, + ffff[gg, + hhh, + ii]] +End + assert_equal(expected, tree(20)) + assert_equal(expected, tree(22)) + end + + def test_tree_23_43 + expected = <<'End'.chomp +aaaa[bbbbb[ccc, dd], + eee, + ffff[gg, hhh, ii]] +End + assert_equal(expected, tree(23)) + assert_equal(expected, tree(43)) + end + + def test_tree_44 + assert_equal(<<'End'.chomp, tree(44)) +aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]] +End + end + + def tree_alt(width) + PrettierPrint.format(''.dup, width) {|q| @tree.altshow(q)} + end + + def test_tree_alt_00_18 + expected = <<'End'.chomp +aaaa[ + bbbbb[ + ccc, + dd + ], + eee, + ffff[ + gg, + hhh, + ii + ] +] +End + assert_equal(expected, tree_alt(0)) + assert_equal(expected, tree_alt(18)) + end + + def test_tree_alt_19_20 + expected = <<'End'.chomp +aaaa[ + bbbbb[ ccc, dd ], + eee, + ffff[ + gg, + hhh, + ii + ] +] +End + assert_equal(expected, tree_alt(19)) + assert_equal(expected, tree_alt(20)) + end + + def test_tree_alt_20_49 + expected = <<'End'.chomp +aaaa[ + bbbbb[ ccc, dd ], + eee, + ffff[ gg, hhh, ii ] +] +End + assert_equal(expected, tree_alt(21)) + assert_equal(expected, tree_alt(49)) + end + + def test_tree_alt_50 + expected = <<'End'.chomp +aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ] +End + assert_equal(expected, tree_alt(50)) + end + + class Tree # :nodoc: + def initialize(string, *children) + @string = string + @children = children + end + + def show(q) + q.group { + q.text @string + q.nest(@string.length) { + unless @children.empty? + q.text '[' + q.nest(1) { + first = true + @children.each {|t| + if first + first = false + else + q.text ',' + q.breakable + end + t.show(q) + } + } + q.text ']' + end + } + } + end + + def altshow(q) + q.group { + q.text @string + unless @children.empty? + q.text '[' + q.nest(2) { + q.breakable + first = true + @children.each {|t| + if first + first = false + else + q.text ',' + q.breakable + end + t.altshow(q) + } + } + q.breakable + q.text ']' + end + } + end + + end +end + +class StrictPrettyExample < Test::Unit::TestCase # :nodoc: + def prog(width) + PrettierPrint.format(''.dup, width) {|q| + q.group { + q.group {q.nest(2) { + q.text "if"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "=="} + q.breakable; q.text "b"}}}} + q.breakable + q.group {q.nest(2) { + q.text "then"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "<<"} + q.breakable; q.text "2"}}}} + q.breakable + q.group {q.nest(2) { + q.text "else"; q.breakable; + q.group { + q.nest(2) { + q.group {q.text "a"; q.breakable; q.text "+"} + q.breakable; q.text "b"}}}}} + } + end + + def test_00_04 + expected = <<'End'.chomp +if + a + == + b +then + a + << + 2 +else + a + + + b +End + assert_equal(expected, prog(0)) + assert_equal(expected, prog(4)) + end + + def test_05 + expected = <<'End'.chomp +if + a + == + b +then + a + << + 2 +else + a + + b +End + assert_equal(expected, prog(5)) + end + + def test_06 + expected = <<'End'.chomp +if + a == + b +then + a << + 2 +else + a + + b +End + assert_equal(expected, prog(6)) + end + + def test_07 + expected = <<'End'.chomp +if + a == + b +then + a << + 2 +else + a + b +End + assert_equal(expected, prog(7)) + end + + def test_08 + expected = <<'End'.chomp +if + a == b +then + a << 2 +else + a + b +End + assert_equal(expected, prog(8)) + end + + def test_09 + expected = <<'End'.chomp +if a == b +then + a << 2 +else + a + b +End + assert_equal(expected, prog(9)) + end + + def test_10 + expected = <<'End'.chomp +if a == b +then + a << 2 +else a + b +End + assert_equal(expected, prog(10)) + end + + def test_11_31 + expected = <<'End'.chomp +if a == b +then a << 2 +else a + b +End + assert_equal(expected, prog(11)) + assert_equal(expected, prog(15)) + assert_equal(expected, prog(31)) + end + + def test_32 + expected = <<'End'.chomp +if a == b then a << 2 else a + b +End + assert_equal(expected, prog(32)) + end + +end + +class TailGroup < Test::Unit::TestCase # :nodoc: + def test_1 + out = PrettierPrint.format(''.dup, 10) {|q| + q.group { + q.group { + q.text "abc" + q.breakable + q.text "def" + } + q.group { + q.text "ghi" + q.breakable + q.text "jkl" + } + } + } + assert_equal("abc defghi\njkl", out) + end +end + +class NonString < Test::Unit::TestCase # :nodoc: + def format(width) + PrettierPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|q| + q.text(3, 3) + q.breakable(1, 1) + q.text(3, 3) + } + end + + def test_6 + assert_equal([3, "newline", "0 spaces", 3], format(6)) + end + + def test_7 + assert_equal([3, 1, 3], format(7)) + end + +end + +class Fill < Test::Unit::TestCase # :nodoc: + def format(width) + PrettierPrint.format(''.dup, width) {|q| + q.group { + q.text 'abc' + q.fill_breakable + q.text 'def' + q.fill_breakable + q.text 'ghi' + q.fill_breakable + q.text 'jkl' + q.fill_breakable + q.text 'mno' + q.fill_breakable + q.text 'pqr' + q.fill_breakable + q.text 'stu' + } + } + end + + def test_00_06 + expected = <<'End'.chomp +abc +def +ghi +jkl +mno +pqr +stu +End + assert_equal(expected, format(0)) + assert_equal(expected, format(6)) + end + + def test_07_10 + expected = <<'End'.chomp +abc def +ghi jkl +mno pqr +stu +End + assert_equal(expected, format(7)) + assert_equal(expected, format(10)) + end + + def test_11_14 + expected = <<'End'.chomp +abc def ghi +jkl mno pqr +stu +End + assert_equal(expected, format(11)) + assert_equal(expected, format(14)) + end + + def test_15_18 + expected = <<'End'.chomp +abc def ghi jkl +mno pqr stu +End + assert_equal(expected, format(15)) + assert_equal(expected, format(18)) + end + + def test_19_22 + expected = <<'End'.chomp +abc def ghi jkl mno +pqr stu +End + assert_equal(expected, format(19)) + assert_equal(expected, format(22)) + end + + def test_23_26 + expected = <<'End'.chomp +abc def ghi jkl mno pqr +stu +End + assert_equal(expected, format(23)) + assert_equal(expected, format(26)) + end + + def test_27 + expected = <<'End'.chomp +abc def ghi jkl mno pqr stu +End + assert_equal(expected, format(27)) + end + +end + +end diff --git a/test/ractor_test.rb b/test/ractor_test.rb new file mode 100644 index 0000000..757dd19 --- /dev/null +++ b/test/ractor_test.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +return unless defined?(Ractor) +require "test_helper" + +class PrettierPrint + class RactorTest < Test::Unit::TestCase + test "ractor safe" do + result = + Ractor.new do + PrettierPrint.format([]) do |q| + q.group do + q.text("# This is the main class for the PrettierPrint gem.") + q.breakable_force + + q.text("class ") + q.group { q.text("PrettierPrint") } + + q.indent do + q.breakable_force + + q.trim + q.text("=begin") + q.breakable_return + q.text("This is embedded documentation.") + q.breakable_return + q.text("=end") + q.breakable_force + + q.group do + q.text("def ") + q.text("format(") + + q.group do + q.seplist(%w[foo bar baz]) do |item| + q.text(item) + end + end + + q.text(")") + q.breakable_force + q.text("end") + end + end + + q.breakable_force + q.text("end") + end + + q.breakable_force + end + end + + expected = <<~RUBY + # This is the main class for the PrettierPrint gem. + class PrettierPrint + =begin + This is embedded documentation. + =end + def format(foo, bar, baz) + end + end + RUBY + + assert_equal expected, result.take.join + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 9763a1f..e200acb 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require "simplecov" +SimpleCov.start + $LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) require "prettier_print" require "test-unit"