From 4ce464827cc90a435f0546da30297a1342f955cf Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 11:57:23 +0100 Subject: [PATCH 01/23] wadler: accept indent kwarg use by default in nest and group --- CHANGELOG.md | 4 + .../new_line.rb | 4 +- .../oppen_and_wadler_customization/space.rb | 6 +- .../whitespace.rb | 4 +- .../oppen_and_wadler_customization/width.rb | 6 +- .../show_print_commands.rb | 4 +- lib/oppen.rb | 8 +- lib/oppen/mixins.rb | 15 ++-- lib/wadler/print.rb | 33 +++---- test/customization_test.rb | 30 +++---- test/indent_anchor_test.rb | 83 ++++++++---------- test/token_to_wadler_test.rb | 84 +++++++++--------- test/wadler_test.rb | 87 +++++++++---------- 13 files changed, 178 insertions(+), 190 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f044c..ce36ce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [unreleased] +- Deprarture from the `ruby/pretty_print` API: + - Wadler: accept base `indent` which will apply to `nest` and `group` + implicitly if no `indent:` kwarg is passed. + ## v0.9.8 (30-12-2024) - Oppen now supports Ruby 3.0+. It used to be restricted to Ruby 3.2+. diff --git a/examples/oppen_and_wadler_customization/new_line.rb b/examples/oppen_and_wadler_customization/new_line.rb index 4412031..b1ba64f 100644 --- a/examples/oppen_and_wadler_customization/new_line.rb +++ b/examples/oppen_and_wadler_customization/new_line.rb @@ -5,9 +5,9 @@ # The new line String can be specified using the `new_line` parameter. new_line = '
' -printer = Oppen::Wadler.new(new_line: new_line) +printer = Oppen::Wadler.new(indent: 2, new_line: new_line) -printer.group(2) { +printer.group { printer.text 'Hello, World!' printer.break printer.text 'How are you doing?' diff --git a/examples/oppen_and_wadler_customization/space.rb b/examples/oppen_and_wadler_customization/space.rb index 37e30bb..b805cdf 100644 --- a/examples/oppen_and_wadler_customization/space.rb +++ b/examples/oppen_and_wadler_customization/space.rb @@ -9,13 +9,13 @@ # By using a callable: space = ->(n) { '---' * n } -printer = Oppen::Wadler.new(space: space) +printer = Oppen::Wadler.new(indent: 2, space: space) -printer.group(2) { +printer.group { printer.text 'Hello, World!' printer.break printer.text 'How are you doing?' - printer.group(2) { + printer.group { printer.break printer.text 'I am fine' } diff --git a/examples/oppen_and_wadler_customization/whitespace.rb b/examples/oppen_and_wadler_customization/whitespace.rb index f21734c..2348028 100644 --- a/examples/oppen_and_wadler_customization/whitespace.rb +++ b/examples/oppen_and_wadler_customization/whitespace.rb @@ -9,9 +9,9 @@ # to prevent trailing whitespaces can be specified using the `whitespace` parameter. whitespace = '**' -printer = Oppen::Wadler.new(whitespace: whitespace) +printer = Oppen::Wadler.new(indent: 2, whitespace: whitespace) -printer.group(2) { +printer.group { printer.text '******Hello, World!******' printer.break } diff --git a/examples/oppen_and_wadler_customization/width.rb b/examples/oppen_and_wadler_customization/width.rb index 440e914..8c4814f 100644 --- a/examples/oppen_and_wadler_customization/width.rb +++ b/examples/oppen_and_wadler_customization/width.rb @@ -5,10 +5,10 @@ # The maximum width can be specified using the `width` parameter. width = 5 -printer_width_default = Oppen::Wadler.new -printer_width_narrow = Oppen::Wadler.new(width: width) +printer_width_default = Oppen::Wadler.new(indent: 2) +printer_width_narrow = Oppen::Wadler.new(indent: 2, width: width) test_block = ->(printer) { - printer.group(2) { + printer.group { printer.text 'Hello, World!' printer.breakable printer.text 'How are you doing?' diff --git a/examples/show_print_commands/show_print_commands.rb b/examples/show_print_commands/show_print_commands.rb index 2a42568..9a966fe 100644 --- a/examples/show_print_commands/show_print_commands.rb +++ b/examples/show_print_commands/show_print_commands.rb @@ -13,9 +13,9 @@ } puts printer.show_print_commands -# out.group(2, "", "", :consistent) { +# out.group("", "", :consistent, indent: 2) { # out.text("Hello, World!", width: 13) -# out.nest(4, "", "") { +# out.nest("", "", indent: 4) { # out.break(line_continuation: "") # } # out.text("GoodBye, World!", width: 15) diff --git a/lib/oppen.rb b/lib/oppen.rb index 5897ce3..60a64eb 100644 --- a/lib/oppen.rb +++ b/lib/oppen.rb @@ -64,8 +64,8 @@ class Config # config = Oppen::Config.new(indent_anchor: :end_of_previous_line) # out = Oppen::Wadler.new config:, width: 13 # out.text 'And she said:' - # out.group(4) { - # out.group(4) { + # out.group(indent: 4) { + # out.group(indent: 4) { # out.break # out.text 'Hello, World!' # } @@ -80,8 +80,8 @@ class Config # config = Oppen::Config.new(indent_anchor: :current_offset) # out = Oppen::Wadler.new config:, width: 13 # out.text 'And she said:' - # out.group(4) { - # out.group(4) { + # out.group(indent: 4) { + # out.group(indent: 4) { # out.break # out.text 'Hello, World!' # } diff --git a/lib/oppen/mixins.rb b/lib/oppen/mixins.rb index 33550b6..9384aa4 100644 --- a/lib/oppen/mixins.rb +++ b/lib/oppen/mixins.rb @@ -24,14 +24,13 @@ def upsize_circular_array(arr, offset) # @return [String] def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens.length * 3) - printer = Oppen::Wadler.new(width: width) + printer = Oppen::Wadler.new(width: width, indent: 2) printer.base_indent(base_indent) - indent = 2 handle_break_token = ->(token) { if token.offset.positive? - printer.text "#{printer_name}.nest(#{token.offset}, '', '') {" - printer.nest_open indent + printer.text "#{printer_name}.nest('', '', indent: #{token.offset}) {" + printer.nest_open printer.break end @@ -46,7 +45,7 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. ) if token.offset.positive? - printer.nest_close indent + printer.nest_close printer.break printer.text '}' end @@ -59,10 +58,10 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. in Token::Break handle_break_token.(token) in Token::Begin - printer.text "#{printer_name}.group(#{token.offset}, '', '', #{token.break_type.inspect}) {" - printer.nest_open indent + printer.text "#{printer_name}.group('', '', #{token.break_type.inspect}, indent: #{token.offset}) {" + printer.nest_open in Token::End - printer.nest_close indent + printer.nest_close printer.break printer.text '}' in Token::EOF diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index de28274..c6895f2 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -31,6 +31,8 @@ class Wadler # @param config [Config] # to customize the printer's behavior. + # @param indent [Integer] + # the default indentation amount for {group} and {nest}. # @param new_line [String] # the new line String. # @param out [Object] @@ -44,17 +46,18 @@ class Wadler # @param width [Integer] maximum line width desired. # # @see Token::Whitespace - def initialize(config: Config.wadler, new_line: "\n", + def initialize(config: Config.wadler, indent: 0, new_line: "\n", out: StringIO.new, space: ' ', whitespace: ' ', width: 80) @config = config @current_indent = 0 - @space = space - @width = width + @indent = indent @new_line = new_line @out = out + @space = space @tokens = [] @whitespace = whitespace + @width = width end # Add missing {Token::Begin}, {Token::End} or {Token::EOF}. @@ -102,7 +105,7 @@ def output # out.show_print_commands(out_name: 'out') # # # => - # # out.group(0, "", "", :consistent) { + # # out.group("", "", :consistent, indent: 0) { # # out.text("Hello World!", width: 12) # # } # @@ -129,7 +132,7 @@ def show_print_commands(**kwargs) # @example # out = Oppen::Wadler.new # out.text 'a' - # out.group(2, '{', '}') { + # out.group('{', '}', indent: 2) { # out.break # out.text 'b' # } @@ -143,7 +146,7 @@ def show_print_commands(**kwargs) # # @example Consistent Breaking # out = Oppen::Wadler.new - # out.group(0, '', '', :consistent) { + # out.group('', '', :consistent) { # out.text 'a' # out.break # out.text 'b' @@ -159,7 +162,7 @@ def show_print_commands(**kwargs) # # @example Inconsistent Breaking # out = Oppen::Wadler.new - # out.group(0, '', '', :inconsistent) { + # out.group('', '', :inconsistent) { # out.text 'a' # out.break # out.text 'b' @@ -176,8 +179,8 @@ def show_print_commands(**kwargs) # # @see Oppen.begin_consistent # @see Oppen.begin_inconsistent - def group(indent = 0, open_obj = '', close_obj = '', - break_type = :consistent) + def group(open_obj = '', close_obj = '', + break_type = :consistent, indent: @indent) raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \ if open_obj.nil? || close_obj.nil? @@ -230,7 +233,7 @@ def group(indent = 0, open_obj = '', close_obj = '', # # @example # out = Oppen::Wadler.new - # out.nest(2, '{', '}') { + # out.nest('{', '}', indent: 2) { # out.text 'a' # out.break # out.text 'b' @@ -244,7 +247,7 @@ def group(indent = 0, open_obj = '', close_obj = '', # # } # # @return [Nil] - def nest(indent, open_obj = '', close_obj = '') + def nest(open_obj = '', close_obj = '', indent: @indent) raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \ if open_obj.nil? || close_obj.nil? @@ -371,7 +374,7 @@ def group_close(_) # the amount of indentation of the group. # # @return [Nil] - def indent_open(indent) + def indent_open(indent: @indent) @current_indent += indent group_open end @@ -382,7 +385,7 @@ def indent_open(indent) # the amount of indentation of the group. # # @return [Nil] - def indent_close(group, indent) + def indent_close(group, indent: @indent) @current_indent -= indent group_close(group) end @@ -393,7 +396,7 @@ def indent_close(group, indent) # the amount of indentation of the nest. # # @return [Nil] - def nest_open(indent) + def nest_open(indent: @indent) @current_indent += indent end @@ -403,7 +406,7 @@ def nest_open(indent) # the amount of indentation of the nest. # # @return [Nil] - def nest_close(indent) + def nest_close(indent: @indent) @current_indent -= indent end diff --git a/test/customization_test.rb b/test/customization_test.rb index 39c832c..7a8bb66 100644 --- a/test/customization_test.rb +++ b/test/customization_test.rb @@ -3,11 +3,11 @@ require_relative 'lib' def customization_build_output(printer) - printer.group(2) { + printer.group(indent: 2) { printer.text 'function' printer.breakable printer.text 'test(' - printer.group(2, '', '', :inconsistent) { + printer.group('', '', :inconsistent, indent: 2) { printer.break printer.text 'int index,' printer.breakable @@ -25,11 +25,11 @@ def customization_build_output(printer) describe 'Inconsistent break tests' do it 'must work in a group if the line fits' do printer = Oppen::Wadler.new(width: 100) - printer.group(2) { + printer.group(indent: 2) { printer.text 'function' printer.breakable printer.text 'test(' - printer.group(2, '', '', :inconsistent) { + printer.group('', '', :inconsistent, indent: 2) { printer.breakable printer.text 'int index,' printer.breakable @@ -160,7 +160,7 @@ def customization_build_output(printer) # In these cases, the fixed sized string should not be displayed if the indentation is 0. it 'calls space only when indent is positive' do printer = Oppen::Wadler.new(width: 10, space: ->(_n) { '*******' }) - printer.group(0) { + printer.group { printer.text 'Hello' printer.break printer.text 'World' @@ -217,15 +217,15 @@ def customization_build_output(printer) describe 'Nest delimiter tests' do def nest_delimiter_build_output(printer, open_obj, close_obj) - printer.group(0) { - printer.group(2) { + printer.group { + printer.group(indent: 2) { printer.text 'function' printer.breakable printer.text 'foo()' } printer.break - printer.nest(2, open_obj, close_obj) { - printer.group(0) { + printer.nest(open_obj, close_obj, indent: 2) { + printer.group { printer.text 'Hello' printer.breakable(', ') printer.text 'World!' @@ -308,14 +308,14 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) describe 'Group delimiter tests' do def group_delimiter_build_output(printer, open_obj, close_obj) - printer.group(0) { - printer.group(2) { + printer.group { + printer.group(indent: 2) { printer.text 'function' printer.breakable printer.text 'foo()' } - printer.group(2, open_obj, close_obj) { - printer.group(0) { + printer.group(open_obj, close_obj, indent: 2) { + printer.group { printer.break printer.text 'Hello' printer.breakable(', ') @@ -400,9 +400,9 @@ def group_delimiter_build_output(printer, open_obj, close_obj) describe 'Line continuation tests' do def line_continuation_build_output(printer, break_type = :consistent, line_continuation = ',') - printer.group(0) { + printer.group { printer.text('[') - printer.group(2, '', '', break_type) { + printer.group('', '', break_type, indent: 2) { printer.breakable('') printer.text('1') printer.breakable(', ', line_continuation: line_continuation) diff --git a/test/indent_anchor_test.rb b/test/indent_anchor_test.rb index b357550..e9929ac 100644 --- a/test/indent_anchor_test.rb +++ b/test/indent_anchor_test.rb @@ -12,11 +12,6 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde printer = Oppen::Wadler.new(width: width, config: Oppen::Config.wadler) builder_block.(printer) _(printer.output).must_equal expected_wadler, 'Wadler failed the test' - - printer = PrettyPrint.new(''.dup, width) - builder_block.(printer) - printer.flush - _(printer.output).must_equal expected_wadler, 'PrettyPrint failed the test' end describe 'Indent anchor tests' do @@ -24,7 +19,7 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple group of indentation 0', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' out.break out.text 'How are you?' @@ -44,7 +39,7 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple group of indentation 1', proc { |out| - out.group(1) { + out.group(indent: 1) { out.text 'Hello, World!' out.break out.text 'How are you?' @@ -64,7 +59,7 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple group of indentation 2', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' out.break out.text 'How are you?' @@ -84,9 +79,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case of indentation 2-0', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(0) { + out.group { out.break out.text 'How are you?' out.break @@ -106,9 +101,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case of indentation 2-1', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(1) { + out.group(indent: 1) { out.break out.text 'How are you?' out.break @@ -128,9 +123,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case of indentation 2-2', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(2) { + out.group(indent: 2) { out.break out.text 'How are you?' out.break @@ -150,9 +145,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case of indentation 0-0', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' - out.group(0) { + out.group { out.break out.text 'How are you?' out.break @@ -172,9 +167,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case of indentation 0-1', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' - out.group(1) { + out.group(indent: 1) { out.break out.text 'How are you?' out.break @@ -194,13 +189,13 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a multiple groups of same indentation nested in a group', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(1) { + out.group(indent: 1) { out.break out.text 'How are you?' } - out.group(1) { + out.group(indent: 1) { out.break out.text 'I am fine and you?' } @@ -218,13 +213,13 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a multiple groups of different increasing indentation nested in a group', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(1) { + out.group(indent: 1) { out.break out.text 'How are you?' } - out.group(2) { + out.group(indent: 2) { out.break out.text 'I am fine and you?' } @@ -242,13 +237,13 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a multiple groups of different decreasing indentation nested in a group', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(2) { + out.group(indent: 2) { out.break out.text 'How are you?' } - out.group(1) { + out.group(indent: 1) { out.break out.text 'I am fine and you?' } @@ -266,12 +261,12 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with 3 nested blocks of same indentation', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(2) { + out.group(indent: 2) { out.break out.text 'How' - out.group(2) { + out.group(indent: 2) { out.break out.text 'are' out.break @@ -294,12 +289,12 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with 3 nested blocks of different indentation', proc { |out| - out.group(2) { + out.group(indent: 2) { out.text 'Hello, World!' - out.group(3) { + out.group(indent: 3) { out.break out.text 'How' - out.group(4) { + out.group(indent: 4) { out.break out.text 'are' out.break @@ -322,10 +317,10 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case break outside nested', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' out.break - out.group(1) { + out.group(indent: 1) { out.text 'How are you?' out.break out.text 'I am fine and you?' @@ -344,9 +339,9 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case text outside nested', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' - out.group(1) { + out.group(indent: 1) { out.break out.text 'How are you?' out.break @@ -366,10 +361,10 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde [ 'must work with a simple nested case text and break outside nested', proc { |out| - out.group(0) { + out.group { out.text 'Hello, World!' out.break - out.group(1) { + out.group(indent: 1) { out.text 'How are you?' out.break } @@ -396,13 +391,7 @@ def check_difference_oppen_wadler(width, expected_oppen, expected_wadler, builde describe 'Indent anchor error tests' do it 'must raise a LocalJumpError if no block is given to group' do - width = 30 - printer = PrettyPrint.new ''.dup, width - - _ { printer.group(2) }.must_raise LocalJumpError - - printer = Oppen::Wadler.new(width: width) - - _ { printer.group(2) }.must_raise LocalJumpError + printer = Oppen::Wadler.new(width: 20) + _ { printer.group(indent: 2) }.must_raise LocalJumpError end end diff --git a/test/token_to_wadler_test.rb b/test/token_to_wadler_test.rb index 41cb6af..729ea67 100644 --- a/test/token_to_wadler_test.rb +++ b/test/token_to_wadler_test.rb @@ -10,7 +10,7 @@ printer }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { } LANG @@ -21,7 +21,7 @@ printer.text('Hello World!') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 12) } @@ -33,7 +33,7 @@ printer.text('"\'Hello World!\'"') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("\\"'Hello World!'\\"", width: 16) } @@ -45,7 +45,7 @@ printer.text('Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ", width: 17) } @@ -57,7 +57,7 @@ printer.text('Hello World!', width: 42) }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 42) } @@ -69,7 +69,7 @@ printer.text('Hello World! ') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 12) printer.text(" ", width: 2) } @@ -80,7 +80,7 @@ title: 'displays a simple break token', block: proc(&:break), expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.break(line_continuation: "") } @@ -92,7 +92,7 @@ printer.break(line_continuation: '##') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.break(line_continuation: "##") } @@ -102,7 +102,7 @@ title: 'displays a simple breakable token', block: proc(&:breakable), expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.breakable(" ", width: 1, line_continuation: "") } @@ -114,7 +114,7 @@ printer.breakable('**', width: 42, line_continuation: '##') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.breakable("**", width: 42, line_continuation: "##") } @@ -128,7 +128,7 @@ } }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 12) } @@ -137,12 +137,12 @@ { title: 'displays a group token with arguments', block: proc { |printer| - printer.group(2, '{', '}', :inconsistent) { + printer.group('{', '}', :inconsistent, indent: 2) { printer.text('Hello World!') } }, expected: <<~LANG, - printer.group(2, '', '', :inconsistent) { + printer.group('', '', :inconsistent, indent: 2) { printer.break(line_continuation: "") printer.text("{", width: 1) printer.text("Hello World!", width: 12) @@ -166,7 +166,7 @@ printer.text('Hello World!') }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 12) printer.break(line_continuation: "") printer.breakable(" ", width: 1, line_continuation: "") @@ -196,11 +196,11 @@ } }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { - printer.group(0, '', '', :consistent) { - printer.group(0, '', '', :consistent) { - printer.group(0, '', '', :consistent) { - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { + printer.group('', '', :consistent, indent: 0) { + printer.group('', '', :consistent, indent: 0) { + printer.group('', '', :consistent, indent: 0) { + printer.group('', '', :consistent, indent: 0) { printer.text("Hello World!", width: 12) } } @@ -213,19 +213,19 @@ { title: 'displays a simple nest token', block: proc { |printer| - printer.nest(2) { + printer.nest(indent: 2) { printer.breakable printer.text('Hello World!') printer.break } }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { - printer.nest(2, '', '') { + printer.group('', '', :consistent, indent: 0) { + printer.nest('', '', indent: 2) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest(2, '', '') { + printer.nest('', '', indent: 2) { printer.break(line_continuation: "") } } @@ -235,23 +235,23 @@ { title: 'displays a nest token with arguments', block: proc { |printer| - printer.nest(2, '{', '}') { + printer.nest('{', '}', indent: 2) { printer.breakable printer.text('Hello World!') printer.break } }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { + printer.group('', '', :consistent, indent: 0) { printer.text("{", width: 1) - printer.nest(2, '', '') { + printer.nest('', '', indent: 2) { printer.break(line_continuation: "") } - printer.nest(2, '', '') { + printer.nest('', '', indent: 2) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest(2, '', '') { + printer.nest('', '', indent: 2) { printer.break(line_continuation: "") } printer.break(line_continuation: "") @@ -263,9 +263,9 @@ { title: 'displays nested nest tokens', block: proc { |printer| - printer.nest(2) { - printer.nest(2) { - printer.nest(2) { + printer.nest(indent: 2) { + printer.nest(indent: 2) { + printer.nest(indent: 2) { printer.breakable printer.text('Hello World!') printer.break @@ -274,12 +274,12 @@ } }, expected: <<~LANG, - printer.group(0, '', '', :consistent) { - printer.nest(6, '', '') { + printer.group('', '', :consistent, indent: 0) { + printer.nest('', '', indent: 6) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest(6, '', '') { + printer.nest('', '', indent: 6) { printer.break(line_continuation: "") } } @@ -289,10 +289,10 @@ { title: 'displays nested nest and group tokens', block: proc { |printer| - printer.group(2) { - printer.nest(2) { - printer.group(2) { - printer.nest(2) { + printer.group(indent: 2) { + printer.nest(indent: 2) { + printer.group(indent: 2) { + printer.nest(indent: 2) { printer.breakable printer.text('Hello World!') printer.break @@ -302,13 +302,13 @@ } }, expected: <<~LANG, - printer.group(2, '', '', :consistent) { - printer.group(2, '', '', :consistent) { - printer.nest(4, '', '') { + printer.group('', '', :consistent, indent: 2) { + printer.group('', '', :consistent, indent: 2) { + printer.nest('', '', indent: 4) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest(4, '', '') { + printer.nest('', '', indent: 4) { printer.break(line_continuation: "") } } diff --git a/test/wadler_test.rb b/test/wadler_test.rb index 9ed5222..01e12de 100644 --- a/test/wadler_test.rb +++ b/test/wadler_test.rb @@ -1,18 +1,11 @@ # frozen_string_literal: true -require 'prettyprint' - require_relative 'lib' -def check_roundtrip(width, expected, builder_block) +def assert_wadler(width, expected, builder_block) printer = Oppen::Wadler.new(width: width) builder_block.(printer) _(printer.output).must_equal expected, 'Oppen failed the test' - - printer = PrettyPrint.new(''.dup, width) - builder_block.(printer) - printer.flush - _(printer.output).must_equal expected, 'PrettyPrint failed the test' end describe 'Wadler tests' do @@ -76,7 +69,7 @@ def check_roundtrip(width, expected, builder_block) ].each do |expected, vals| vals.each do |width| it "must work with line width: #{width}" do - check_roundtrip(width, expected, builder_block) + assert_wadler(width, expected, builder_block) end end end @@ -95,10 +88,10 @@ def initialize(string, *children) def show(out) out.group { out.text string - out.nest(string.length) { + out.nest(indent: string.length) { unless children.empty? out.text '[' - out.nest(1) { + out.nest(indent: 1) { first = true children.each { |t| if first @@ -159,7 +152,7 @@ def show(out) ].each do |expected, vals| vals.each do |width| it "must work with line width: #{width}" do - check_roundtrip(width, expected, builder_block) + assert_wadler(width, expected, builder_block) end end end @@ -172,7 +165,7 @@ def altshow(out) out.text @string unless @children.empty? out.text '[' - out.nest(2) { + out.nest(indent: 2) { out.breakable first = true @children.each { |t| @@ -246,7 +239,7 @@ def altshow(out) ].each do |expected, vals| vals.each do |width| it "must work with line width: #{width}" do - check_roundtrip(width, expected, builder_block_altshow) + assert_wadler(width, expected, builder_block_altshow) end end end @@ -256,11 +249,11 @@ def altshow(out) builder_block = proc { |out| out.group { out.group { - out.nest(2) { + out.nest(indent: 2) { out.text 'if' out.breakable out.group { - out.nest(2) { + out.nest(indent: 2) { out.group { out.text 'a' out.breakable @@ -274,11 +267,11 @@ def altshow(out) } out.breakable out.group { - out.nest(2) { + out.nest(indent: 2) { out.text 'then' out.breakable out.group { - out.nest(2) { + out.nest(indent: 2) { out.group { out.text 'a' out.breakable @@ -292,11 +285,11 @@ def altshow(out) } out.breakable out.group { - out.nest(2) { + out.nest(indent: 2) { out.text 'else' out.breakable out.group { - out.nest(2) { + out.nest(indent: 2) { out.group { out.text 'a' out.breakable @@ -410,7 +403,7 @@ def altshow(out) ].each do |expected, vals| vals.each do |width| it "must work with line width: #{width}" do - check_roundtrip(width, expected, builder_block) + assert_wadler(width, expected, builder_block) end end end @@ -442,7 +435,7 @@ def altshow(out) ].each do |expected, vals| vals.each do |width| it "must work with line width: #{width}" do - check_roundtrip(width, expected, builder_block) + assert_wadler(width, expected, builder_block) end end end @@ -451,22 +444,22 @@ def altshow(out) describe 'must not break if stack is full' do it 'must work with a big token list' do wadler = Oppen::Wadler.new(width: 3) - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { - wadler.group(1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { + wadler.group(indent: 1) { wadler.text '1 +' } wadler.breakable @@ -563,7 +556,7 @@ def altshow(out) } } expected = 'This is a long sentence This is another long sentence.' - check_roundtrip(10, expected, builder_block) + assert_wadler(10, expected, builder_block) end it 'must work with a width bigger than text length' do @@ -584,7 +577,7 @@ def altshow(out) This is a small sentence This is another small sentence. LANG - check_roundtrip(100, expected, builder_block) + assert_wadler(100, expected, builder_block) end it 'must work with a width smaller than break length' do @@ -600,7 +593,7 @@ def altshow(out) } } expected = 'axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxb' - check_roundtrip(5, expected, builder_block) + assert_wadler(5, expected, builder_block) end it 'must work with a width bigger than break length' do @@ -619,7 +612,7 @@ def altshow(out) This is a small sentence This is another small sentence. LANG - check_roundtrip(100, expected, builder_block) + assert_wadler(100, expected, builder_block) end end @@ -638,7 +631,7 @@ def altshow(out) it 'works with only a nest' do out = Oppen::Wadler.new - out.nest(0) { out.text 'Hello World' } + out.nest { out.text 'Hello World' } _(out.output).must_equal 'Hello World' end @@ -660,7 +653,7 @@ def altshow(out) describe 'handling empty lines' do it 'does not indent by default' do out = Oppen::Wadler.new(width: 10) - out.group(2) { + out.group(indent: 2) { out.text 'a' out.break out.break @@ -676,7 +669,7 @@ def altshow(out) it 'does not indent if empty first line' do out = Oppen::Wadler.new(width: 10) - out.group(8) { + out.group(indent: 8) { out.break out.break out.text 'b' @@ -691,7 +684,7 @@ def altshow(out) it 'does not indent if empty last line' do out = Oppen::Wadler.new(width: 10) - out.group(2) { + out.group(indent: 2) { out.text 'a' out.break out.break @@ -706,7 +699,7 @@ def altshow(out) it 'does not indent if empty first and last line' do out = Oppen::Wadler.new(width: 10) - out.group(8) { + out.group(indent: 8) { out.break out.break out.break From fc325ceec2d77d8de96eb9f2e8bf975198e9b413 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 12:06:22 +0100 Subject: [PATCH 02/23] wadler: accept base_indent kwarg --- CHANGELOG.md | 8 +++++--- lib/oppen/mixins.rb | 3 +-- lib/wadler/print.rb | 16 ++++------------ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce36ce9..b88155d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ ## [unreleased] -- Deprarture from the `ruby/pretty_print` API: - - Wadler: accept base `indent` which will apply to `nest` and `group` - implicitly if no `indent:` kwarg is passed. +- Deprarture from the `ruby/pretty_print` API with Wadler: + - accept `indent` which will apply to `nest` and `group` implicitly if no + `indent:` kwarg is passed. + - accept `base_indent` which sets the base global indentation level for + the whole printer. ## v0.9.8 (30-12-2024) diff --git a/lib/oppen/mixins.rb b/lib/oppen/mixins.rb index 9384aa4..447b6d4 100644 --- a/lib/oppen/mixins.rb +++ b/lib/oppen/mixins.rb @@ -24,8 +24,7 @@ def upsize_circular_array(arr, offset) # @return [String] def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens.length * 3) - printer = Oppen::Wadler.new(width: width, indent: 2) - printer.base_indent(base_indent) + printer = Oppen::Wadler.new(base_indent: base_indent, indent: 2, width: width) handle_break_token = ->(token) { if token.offset.positive? diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index c6895f2..e8246c0 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -29,6 +29,8 @@ class Wadler # maximum line width. attr_reader :width + # @param base_indent [Integer] + # the starting indentation level for the whole printer. # @param config [Config] # to customize the printer's behavior. # @param indent [Integer] @@ -46,11 +48,11 @@ class Wadler # @param width [Integer] maximum line width desired. # # @see Token::Whitespace - def initialize(config: Config.wadler, indent: 0, new_line: "\n", + def initialize(base_indent: 0, config: Config.wadler, indent: 0, new_line: "\n", out: StringIO.new, space: ' ', whitespace: ' ', width: 80) @config = config - @current_indent = 0 + @current_indent = base_indent @indent = indent @new_line = new_line @out = out @@ -331,16 +333,6 @@ def break(line_continuation: '') # @!group Helpers - # Set a base indenetaion level for the printer. - # - # @param indent [Integer] - # the amount of indentation. - # - # @return [Nil] - def base_indent(indent = 0) - @current_indent = indent if !indent.nil? - end - # Open a consistent group. # # @param inconsistent [Boolean] From f5ec8f25e764f822c062b9836a706c5aa536f2f9 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 14:44:51 +0100 Subject: [PATCH 03/23] wadler: use delim kwarg instead of positional args Makes for a simpler, more declarative API. --- .../show_print_commands.rb | 4 +- lib/oppen/mixins.rb | 4 +- lib/wadler/print.rb | 108 ++++++++++++------ test/customization_test.rb | 82 +++++-------- test/token_to_wadler_test.rb | 68 +++++------ 5 files changed, 143 insertions(+), 123 deletions(-) diff --git a/examples/show_print_commands/show_print_commands.rb b/examples/show_print_commands/show_print_commands.rb index 9a966fe..1eaa69e 100644 --- a/examples/show_print_commands/show_print_commands.rb +++ b/examples/show_print_commands/show_print_commands.rb @@ -13,9 +13,9 @@ } puts printer.show_print_commands -# out.group("", "", :consistent, indent: 2) { +# out.group(:consistent, indent: 2) { # out.text("Hello, World!", width: 13) -# out.nest("", "", indent: 4) { +# out.nest(indent: 4) { # out.break(line_continuation: "") # } # out.text("GoodBye, World!", width: 15) diff --git a/lib/oppen/mixins.rb b/lib/oppen/mixins.rb index 447b6d4..cad521f 100644 --- a/lib/oppen/mixins.rb +++ b/lib/oppen/mixins.rb @@ -28,7 +28,7 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. handle_break_token = ->(token) { if token.offset.positive? - printer.text "#{printer_name}.nest('', '', indent: #{token.offset}) {" + printer.text "#{printer_name}.nest(indent: #{token.offset}) {" printer.nest_open printer.break end @@ -57,7 +57,7 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. in Token::Break handle_break_token.(token) in Token::Begin - printer.text "#{printer_name}.group('', '', #{token.break_type.inspect}, indent: #{token.offset}) {" + printer.text "#{printer_name}.group(#{token.break_type.inspect}, indent: #{token.offset}) {" printer.nest_open in Token::End printer.nest_close diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index e8246c0..e308ed8 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -107,7 +107,7 @@ def output # out.show_print_commands(out_name: 'out') # # # => - # # out.group("", "", :consistent, indent: 0) { + # # out.group(:consistent, indent: 0) { # # out.text("Hello World!", width: 12) # # } # @@ -119,22 +119,53 @@ def show_print_commands(**kwargs) # Create a new group. # - # @param indent [Integer] + # @param indent [Integer] # indentation. - # @param open_obj [String] - # opening delimiter. - # @param close_obj [String] - # closing delimiter. + # @param delim [Nil|String|Symbol|Array] + # delimiters, to be printed at the start and the end of the group: + # - If it's nil, nothing will be printed + # - If it's a Strings or a Symbol, it will be printed at both positions. + # - If it's an Array of many items, the first two elements will be used + # for the start and end of the group. # @param break_type [Token::BreakType] # break type. # # @yield # the block of text in a group. # - # @example + # @example 1 String Delimiter + # out = Oppen::Wadler.new + # out.text 'a' + # out.group(indent: 2, delim: '|') { + # out.break + # out.text 'b' + # } + # puts out.output + # + # # => + # # a + # # | + # # b + # # | + # + # @example 1 Delimiter in Array + # out = Oppen::Wadler.new + # out.text 'a' + # out.group(indent: 2, delim: ['|']) { + # out.break + # out.text 'b' + # } + # puts out.output + # + # # => + # # a + # # | + # # b + # + # @example 2 Delimiters # out = Oppen::Wadler.new # out.text 'a' - # out.group('{', '}', indent: 2) { + # out.group(indent: 2, delim: %i[{ }]) { # out.break # out.text 'b' # } @@ -148,14 +179,14 @@ def show_print_commands(**kwargs) # # @example Consistent Breaking # out = Oppen::Wadler.new - # out.group('', '', :consistent) { + # out.group(:consistent) { # out.text 'a' # out.break # out.text 'b' # out.breakable # out.text 'c' # } - # out.output + # puts out.output # # # => # # a @@ -164,14 +195,14 @@ def show_print_commands(**kwargs) # # @example Inconsistent Breaking # out = Oppen::Wadler.new - # out.group('', '', :inconsistent) { + # out.group(:inconsistent) { # out.text 'a' # out.break # out.text 'b' # out.breakable # out.text 'c' # } - # out.output + # puts out.output # # # => # # a @@ -181,10 +212,13 @@ def show_print_commands(**kwargs) # # @see Oppen.begin_consistent # @see Oppen.begin_inconsistent - def group(open_obj = '', close_obj = '', - break_type = :consistent, indent: @indent) - raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \ - if open_obj.nil? || close_obj.nil? + def group(break_type = :consistent, delim: nil, indent: @indent) + lft, rgt = + case delim + in nil then ['', ''] + in String | Symbol then [delim, delim] + in Array then delim.values_at(0, 1).map(&:to_s) + end tokens << case break_type @@ -194,16 +228,16 @@ def group(open_obj = '', close_obj = '', Oppen.begin_inconsistent(offset: indent) end - if !open_obj.empty? + if !lft.empty? self.break - text(open_obj) + text lft end yield - if !close_obj.empty? + if !rgt.empty? self.break - text(close_obj) + text rgt end tokens << Oppen.end @@ -223,19 +257,21 @@ def group(open_obj = '', close_obj = '', # @note a {nest} will not forcibly indent its content if the break type of # the enclosing {group} is `:inconsistent`. # - # @param indent [Integer] + # @param delim [Nil|String|Symbol|Array] + # delimiters, to be printed at the start and the end of the group: + # - `nil` is always the empty string. + # - If it's a Strings or a Symbol, it will be printed at both positions. + # - If it's an Array of many items, the first two elements will be used + # for the start and end of the group. + # @param indent [Integer] # indentation. - # @param open_obj [String] - # opening delimiter. A {break} is implicitly slipped after it if it's not empty. - # @param close_obj [String] - # closing delimiter. A {break} is implicitly slipped before it if it's not empty. # # @yield # the block of text in a nest. # # @example # out = Oppen::Wadler.new - # out.nest('{', '}', indent: 2) { + # out.nest(delim: %i[{ }], indent: 2) { # out.text 'a' # out.break # out.text 'b' @@ -249,14 +285,18 @@ def group(open_obj = '', close_obj = '', # # } # # @return [Nil] - def nest(open_obj = '', close_obj = '', indent: @indent) - raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \ - if open_obj.nil? || close_obj.nil? + def nest(delim: nil, indent: @indent) + lft, rgt = + case delim + in nil then ['', ''] + in String | Symbol then [delim, delim] + in Array then delim.values_at(0, 1).map(&:to_s) + end @current_indent += indent - if !open_obj.empty? - text(open_obj) + if !lft.empty? + text lft self.break end @@ -266,10 +306,10 @@ def nest(open_obj = '', close_obj = '', indent: @indent) @current_indent -= indent end - return if close_obj.empty? + return if rgt.empty? self.break - text(close_obj) + text rgt end # Create a new text element. @@ -319,7 +359,7 @@ def breakable(str = ' ', line_continuation: '', width: str.length) # out.text 'b' # out.break line_continuation: '#' # out.text 'c' - # out.output + # puts out.output # # # => # # a diff --git a/test/customization_test.rb b/test/customization_test.rb index 7a8bb66..0737042 100644 --- a/test/customization_test.rb +++ b/test/customization_test.rb @@ -7,7 +7,7 @@ def customization_build_output(printer) printer.text 'function' printer.breakable printer.text 'test(' - printer.group('', '', :inconsistent, indent: 2) { + printer.group(:inconsistent, indent: 2) { printer.break printer.text 'int index,' printer.breakable @@ -29,7 +29,7 @@ def customization_build_output(printer) printer.text 'function' printer.breakable printer.text 'test(' - printer.group('', '', :inconsistent, indent: 2) { + printer.group(:inconsistent, indent: 2) { printer.breakable printer.text 'int index,' printer.breakable @@ -195,9 +195,9 @@ def customization_build_output(printer) it 'must work with a different delimiter' do printer = Oppen::Wadler.new(width: 45) printer.group { - printer.breakable('') + printer.breakable '' printer.text 'Hello' - printer.breakable(', ') + printer.breakable ', ' printer.text 'World!' } _(printer.output).must_equal 'Hello, World!' @@ -206,9 +206,9 @@ def customization_build_output(printer) it 'must work with an UTF-8 delimiter' do printer = Oppen::Wadler.new(width: 45) printer.group { - printer.breakable('') + printer.breakable '' printer.text 'Hello' - printer.breakable(' ʃ ') + printer.breakable ' ʃ ' printer.text 'World!' } _(printer.output).must_equal 'Hello ʃ World!' @@ -216,7 +216,7 @@ def customization_build_output(printer) end describe 'Nest delimiter tests' do - def nest_delimiter_build_output(printer, open_obj, close_obj) + def nest_delimiter_build_output(printer, delim) printer.group { printer.group(indent: 2) { printer.text 'function' @@ -224,7 +224,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) printer.text 'foo()' } printer.break - printer.nest(open_obj, close_obj, indent: 2) { + printer.nest(delim: delim, indent: 2) { printer.group { printer.text 'Hello' printer.breakable(', ') @@ -236,7 +236,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) it 'must work with an open and close object' do printer = Oppen::Wadler.new(width: 5) - nest_delimiter_build_output(printer, '{', '}') + nest_delimiter_build_output(printer, %w[{ }]) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -249,7 +249,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) it 'must work with no open and close object' do printer = Oppen::Wadler.new(width: 5) - nest_delimiter_build_output(printer, '', '') + nest_delimiter_build_output(printer, []) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -260,7 +260,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) it 'must work with only an open object' do printer = Oppen::Wadler.new(width: 5) - nest_delimiter_build_output(printer, '{', '') + nest_delimiter_build_output(printer, ['{', '']) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -272,7 +272,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) it 'must work with only a close object' do printer = Oppen::Wadler.new(width: 5) - nest_delimiter_build_output(printer, '', '}') + nest_delimiter_build_output(printer, ['', '}']) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -284,7 +284,7 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) it 'must work with an UTF-8 string' do printer = Oppen::Wadler.new(width: 5) - nest_delimiter_build_output(printer, 'Ϯ', 'Ϯ') + nest_delimiter_build_output(printer, %w[Ϯ Ϯ]) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -294,31 +294,21 @@ def nest_delimiter_build_output(printer, open_obj, close_obj) Ϯ LANG end - - it 'must raise an error when open_obj is nil' do - printer = Oppen::Wadler.new(width: 5) - _ { nest_delimiter_build_output(printer, nil, '}') }.must_raise ArgumentError - end - - it 'must raise an error when close_obj is nil' do - printer = Oppen::Wadler.new(width: 5) - _ { nest_delimiter_build_output(printer, '{', nil) }.must_raise ArgumentError - end end describe 'Group delimiter tests' do - def group_delimiter_build_output(printer, open_obj, close_obj) + def group_delimiter_build_output(printer, delim) printer.group { printer.group(indent: 2) { printer.text 'function' printer.breakable printer.text 'foo()' } - printer.group(open_obj, close_obj, indent: 2) { + printer.group(delim: delim, indent: 2) { printer.group { printer.break printer.text 'Hello' - printer.breakable(', ') + printer.breakable ', ' printer.text 'World!' } } @@ -327,7 +317,7 @@ def group_delimiter_build_output(printer, open_obj, close_obj) it 'must work with an open and close object' do printer = Oppen::Wadler.new(width: 5) - group_delimiter_build_output(printer, '{', '}') + group_delimiter_build_output(printer, %i[{ }]) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -340,7 +330,7 @@ def group_delimiter_build_output(printer, open_obj, close_obj) it 'must work with no open and close object' do printer = Oppen::Wadler.new(width: 5) - group_delimiter_build_output(printer, '', '') + group_delimiter_build_output(printer, nil) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -351,7 +341,7 @@ def group_delimiter_build_output(printer, open_obj, close_obj) it 'must work with only an open object' do printer = Oppen::Wadler.new(width: 5) - group_delimiter_build_output(printer, '{', '') + group_delimiter_build_output(printer, ['{', '']) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -363,7 +353,7 @@ def group_delimiter_build_output(printer, open_obj, close_obj) it 'must work with only a close object' do printer = Oppen::Wadler.new(width: 5) - group_delimiter_build_output(printer, '', '}') + group_delimiter_build_output(printer, ['', '}']) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -375,7 +365,7 @@ def group_delimiter_build_output(printer, open_obj, close_obj) it 'must work with an UTF-8 string' do printer = Oppen::Wadler.new(width: 5) - group_delimiter_build_output(printer, 'Ϯ', 'Ϯ') + group_delimiter_build_output(printer, %w[Ϯ Ϯ]) _(printer.output).must_equal <<~LANG.chomp function foo() @@ -385,33 +375,23 @@ def group_delimiter_build_output(printer, open_obj, close_obj) Ϯ LANG end - - it 'must raise an error when open_obj is nil' do - printer = Oppen::Wadler.new(width: 5) - _ { group_delimiter_build_output(printer, nil, '}') }.must_raise ArgumentError - end - - it 'must raise an error when close_obj is nil' do - printer = Oppen::Wadler.new(width: 5) - _ { group_delimiter_build_output(printer, '{', nil) }.must_raise ArgumentError - end end describe 'Line continuation tests' do def line_continuation_build_output(printer, break_type = :consistent, line_continuation = ',') printer.group { - printer.text('[') - printer.group('', '', break_type, indent: 2) { - printer.breakable('') - printer.text('1') - printer.breakable(', ', line_continuation: line_continuation) - printer.text('2') - printer.breakable(', ', line_continuation: line_continuation) - printer.text('3') + printer.text '[' + printer.group(break_type, indent: 2) { + printer.breakable '' + printer.text '1' + printer.breakable ', ', line_continuation: line_continuation + printer.text '2' + printer.breakable ', ', line_continuation: line_continuation + printer.text '3' } - printer.breakable('', line_continuation: line_continuation) - printer.text(']') + printer.breakable '', line_continuation: line_continuation + printer.text ']' } end it 'must not display line continuation if line fits' do diff --git a/test/token_to_wadler_test.rb b/test/token_to_wadler_test.rb index 729ea67..3abc096 100644 --- a/test/token_to_wadler_test.rb +++ b/test/token_to_wadler_test.rb @@ -10,7 +10,7 @@ printer }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { } LANG @@ -21,7 +21,7 @@ printer.text('Hello World!') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 12) } @@ -33,7 +33,7 @@ printer.text('"\'Hello World!\'"') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("\\"'Hello World!'\\"", width: 16) } @@ -45,7 +45,7 @@ printer.text('Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ", width: 17) } @@ -57,7 +57,7 @@ printer.text('Hello World!', width: 42) }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 42) } @@ -69,7 +69,7 @@ printer.text('Hello World! ') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 12) printer.text(" ", width: 2) } @@ -80,7 +80,7 @@ title: 'displays a simple break token', block: proc(&:break), expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.break(line_continuation: "") } @@ -92,7 +92,7 @@ printer.break(line_continuation: '##') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.break(line_continuation: "##") } @@ -102,7 +102,7 @@ title: 'displays a simple breakable token', block: proc(&:breakable), expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.breakable(" ", width: 1, line_continuation: "") } @@ -114,7 +114,7 @@ printer.breakable('**', width: 42, line_continuation: '##') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.breakable("**", width: 42, line_continuation: "##") } @@ -128,7 +128,7 @@ } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 12) } @@ -137,12 +137,12 @@ { title: 'displays a group token with arguments', block: proc { |printer| - printer.group('{', '}', :inconsistent, indent: 2) { + printer.group(:inconsistent, delim: %w[{ }], indent: 2) { printer.text('Hello World!') } }, expected: <<~LANG, - printer.group('', '', :inconsistent, indent: 2) { + printer.group(:inconsistent, indent: 2) { printer.break(line_continuation: "") printer.text("{", width: 1) printer.text("Hello World!", width: 12) @@ -166,7 +166,7 @@ printer.text('Hello World!') }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 12) printer.break(line_continuation: "") printer.breakable(" ", width: 1, line_continuation: "") @@ -196,11 +196,11 @@ } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { - printer.group('', '', :consistent, indent: 0) { - printer.group('', '', :consistent, indent: 0) { - printer.group('', '', :consistent, indent: 0) { - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { + printer.group(:consistent, indent: 0) { + printer.group(:consistent, indent: 0) { + printer.group(:consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("Hello World!", width: 12) } } @@ -220,12 +220,12 @@ } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { - printer.nest('', '', indent: 2) { + printer.group(:consistent, indent: 0) { + printer.nest(indent: 2) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest('', '', indent: 2) { + printer.nest(indent: 2) { printer.break(line_continuation: "") } } @@ -235,23 +235,23 @@ { title: 'displays a nest token with arguments', block: proc { |printer| - printer.nest('{', '}', indent: 2) { + printer.nest(delim: %w[{ }], indent: 2) { printer.breakable printer.text('Hello World!') printer.break } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { + printer.group(:consistent, indent: 0) { printer.text("{", width: 1) - printer.nest('', '', indent: 2) { + printer.nest(indent: 2) { printer.break(line_continuation: "") } - printer.nest('', '', indent: 2) { + printer.nest(indent: 2) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest('', '', indent: 2) { + printer.nest(indent: 2) { printer.break(line_continuation: "") } printer.break(line_continuation: "") @@ -274,12 +274,12 @@ } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 0) { - printer.nest('', '', indent: 6) { + printer.group(:consistent, indent: 0) { + printer.nest(indent: 6) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest('', '', indent: 6) { + printer.nest(indent: 6) { printer.break(line_continuation: "") } } @@ -302,13 +302,13 @@ } }, expected: <<~LANG, - printer.group('', '', :consistent, indent: 2) { - printer.group('', '', :consistent, indent: 2) { - printer.nest('', '', indent: 4) { + printer.group(:consistent, indent: 2) { + printer.group(:consistent, indent: 2) { + printer.nest(indent: 4) { printer.breakable(" ", width: 1, line_continuation: "") } printer.text("Hello World!", width: 12) - printer.nest('', '', indent: 4) { + printer.nest(indent: 4) { printer.break(line_continuation: "") } } From b2fa1d5e9c07b8cec6cab2c5119942d2d0c59ce5 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 14:45:42 +0100 Subject: [PATCH 04/23] bin: repl: change ascii art It's still there since project initialization. --- bin/repl.rb | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/bin/repl.rb b/bin/repl.rb index 955df9e..d7f4609 100644 --- a/bin/repl.rb +++ b/bin/repl.rb @@ -3,17 +3,12 @@ require 'colored' # Fancy -WELCOME = <<-'YGM' - __ __ ___ __ __ ____ ____ ___ ___ ___ ____ ____ ___ ___ ___ - | | | / \ | | || \ / | / _]| | || \ / || | | / _] - | | || || | || D )| __| / [_ | _ _ || _ || o || _ _ | / [_ - | ~ || O || | || / | | || _]| \_/ || | || || \_/ || _] - |___, || || : || \ | |_ || [_ | | || | || _ || | || [_ - | || || || . \| ______ ______ ______ __ || | - |____/ \___/ \__,_||__|\_||___ /\ == \ /\ ___\ /\ == \ /\ \ ___||_____| - \ \ __< \ \ __\ \ \ _-/ \ \ \____ - \ \_\ \_\ \ \_____\ \ \_\ \ \_____\ - \/_/ /_/ \/_____/ \/_/ \/_____/ +WELCOME = <<~YGM + ___ ___ ___ ___ ___ + | . | . | . | -_| | + |___| _| _|___|_|_| + |_| |_| + YGM puts WELCOME.green From 7d3b32f9cf303f035e44e4f6fa58f3eae52b00da Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 14:49:02 +0100 Subject: [PATCH 05/23] wadler: introduce consistent and inconsistent calls They're _"aliases"_ to `group`. --- CHANGELOG.md | 2 ++ lib/wadler/print.rb | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b88155d..20dc53f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ `indent:` kwarg is passed. - accept `base_indent` which sets the base global indentation level for the whole printer. + - Wadler: add `#consistent` and `#inconsistent` as shorthands to + `group(:consistent)` and `group(:inconsistent)`, respectively. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index e308ed8..cda523c 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -243,6 +243,16 @@ def group(break_type = :consistent, delim: nil, indent: @indent) tokens << Oppen.end end + # An alias for `group(:consistent, ...)` + def consistent(**kwargs) + group(:consistent, **kwargs) + end + + # An alias for `group(:inconsistent, ...)` + def inconsistent(**kwargs) + group(:inconsistent, **kwargs) + end + # Create a new non-strict {group}. # # {group}s isolate breaking decisions, and in that sense they're considered From 2dae1e7a7a19b90a81fbe4f647de3fbce8559014 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 14:50:55 +0100 Subject: [PATCH 06/23] wadler: remove unnecessary args for group_close --- CHANGELOG.md | 4 +++- lib/wadler/print.rb | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20dc53f..f10f212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ `indent:` kwarg is passed. - accept `base_indent` which sets the base global indentation level for the whole printer. - - Wadler: add `#consistent` and `#inconsistent` as shorthands to +- Wadler: + - add `#consistent` and `#inconsistent` as shorthands to `group(:consistent)` and `group(:inconsistent)`, respectively. + - remove args of `#group_close`. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index cda523c..0249e0a 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -406,7 +406,7 @@ def group_open(inconsistent: false, indent: 0) # Close a group. # # @return [Nil] - def group_close(_) + def group_close tokens << Oppen.end end @@ -429,7 +429,7 @@ def indent_open(indent: @indent) # @return [Nil] def indent_close(group, indent: @indent) @current_indent -= indent - group_close(group) + group_close end # Open a nest by adding indent. From 361f4a37ad189b87b9769f923e63dab40473e2e1 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 15:04:53 +0100 Subject: [PATCH 07/23] wadler: make all printing methods return self Which makes chaining calls possible => better looking programs --- CHANGELOG.md | 1 + lib/oppen/mixins.rb | 23 +++++------- lib/wadler/print.rb | 89 +++++++++++++++++++++++---------------------- test/wadler_test.rb | 61 ++++++++++++++++--------------- 4 files changed, 87 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f10f212..3a40eb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ `indent:` kwarg is passed. - accept `base_indent` which sets the base global indentation level for the whole printer. + - all printing methods return `self` for call chaining. - Wadler: - add `#consistent` and `#inconsistent` as shorthands to `group(:consistent)` and `group(:inconsistent)`, respectively. diff --git a/lib/oppen/mixins.rb b/lib/oppen/mixins.rb index cad521f..2f30bb5 100644 --- a/lib/oppen/mixins.rb +++ b/lib/oppen/mixins.rb @@ -28,9 +28,10 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. handle_break_token = ->(token) { if token.offset.positive? - printer.text "#{printer_name}.nest(indent: #{token.offset}) {" - printer.nest_open - printer.break + printer + .text("#{printer_name}.nest(indent: #{token.offset}) {") + .nest_open + .break end printer.text( @@ -43,11 +44,7 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. end, ) - if token.offset.positive? - printer.nest_close - printer.break - printer.text '}' - end + printer.nest_close.break.text '}' if token.offset.positive? } tokens.each_with_index do |token, idx| @@ -57,17 +54,17 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens. in Token::Break handle_break_token.(token) in Token::Begin - printer.text "#{printer_name}.group(#{token.break_type.inspect}, indent: #{token.offset}) {" - printer.nest_open + printer + .text("#{printer_name}.group(#{token.break_type.inspect}, indent: #{token.offset}) {") + .nest_open in Token::End - printer.nest_close - printer.break - printer.text '}' + printer.nest_close.break.text '}' in Token::EOF nil end printer.break if !tokens[idx + 1].is_a?(Token::End) end + printer.output end end diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 0249e0a..d3daccc 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -135,11 +135,11 @@ def show_print_commands(**kwargs) # # @example 1 String Delimiter # out = Oppen::Wadler.new - # out.text 'a' - # out.group(indent: 2, delim: '|') { - # out.break - # out.text 'b' - # } + # out + # .text('a') + # .group(indent: 2, delim: '|') { + # out.break.text 'b' + # } # puts out.output # # # => @@ -150,11 +150,11 @@ def show_print_commands(**kwargs) # # @example 1 Delimiter in Array # out = Oppen::Wadler.new - # out.text 'a' - # out.group(indent: 2, delim: ['|']) { - # out.break - # out.text 'b' - # } + # out + # .text('a') + # .group(indent: 2, delim: ['|']) { + # out.break.text 'b' + # } # puts out.output # # # => @@ -164,12 +164,12 @@ def show_print_commands(**kwargs) # # @example 2 Delimiters # out = Oppen::Wadler.new - # out.text 'a' - # out.group(indent: 2, delim: %i[{ }]) { - # out.break - # out.text 'b' - # } - # out.output + # out + # .text('a') + # .group(indent: 2, delim: %i[{ }]) { + # out.break.text 'b' + # } + # puts out.output # # # => # # a @@ -180,11 +180,7 @@ def show_print_commands(**kwargs) # @example Consistent Breaking # out = Oppen::Wadler.new # out.group(:consistent) { - # out.text 'a' - # out.break - # out.text 'b' - # out.breakable - # out.text 'c' + # out.text('a').break.text('b').breakable.text('c') # } # puts out.output # @@ -196,11 +192,7 @@ def show_print_commands(**kwargs) # @example Inconsistent Breaking # out = Oppen::Wadler.new # out.group(:inconsistent) { - # out.text 'a' - # out.break - # out.text 'b' - # out.breakable - # out.text 'c' + # out.text('a').break.text('b').breakable.text('c') # } # puts out.output # @@ -208,7 +200,7 @@ def show_print_commands(**kwargs) # # a # # b c # - # @return [Nil] + # @return [self] # # @see Oppen.begin_consistent # @see Oppen.begin_inconsistent @@ -241,6 +233,8 @@ def group(break_type = :consistent, delim: nil, indent: @indent) end tokens << Oppen.end + + self end # An alias for `group(:consistent, ...)` @@ -282,11 +276,9 @@ def inconsistent(**kwargs) # @example # out = Oppen::Wadler.new # out.nest(delim: %i[{ }], indent: 2) { - # out.text 'a' - # out.break - # out.text 'b' + # out.text('a').break.text('b') # } - # out.output + # puts out.output # # # => # # { @@ -294,7 +286,7 @@ def inconsistent(**kwargs) # # b # # } # - # @return [Nil] + # @return [self] def nest(delim: nil, indent: @indent) lft, rgt = case delim @@ -316,10 +308,12 @@ def nest(delim: nil, indent: @indent) @current_indent -= indent end - return if rgt.empty? + if !rgt.empty? + self.break + text rgt + end - self.break - text rgt + self end # Create a new text element. @@ -327,7 +321,7 @@ def nest(delim: nil, indent: @indent) # @param value [String] # the value of the token. # - # @return [Nil] + # @return [self] def text(value, width: value.length) if config.trim_trailing_whitespaces? && value.match(/((?:#{Regexp.escape(whitespace)})+)\z/) match = Regexp.last_match(1) @@ -339,6 +333,7 @@ def text(value, width: value.length) else tokens << Oppen.string(value, width: width) end + self end # Create a new breakable element. @@ -350,11 +345,12 @@ def text(value, width: value.length) # @param width [Integer] # the width of the token. # - # @return [Nil] + # @return [self] # # @see Wadler#break example on `line_continuation`. def breakable(str = ' ', line_continuation: '', width: str.length) tokens << Oppen.break(str, width: width, line_continuation: line_continuation, offset: current_indent) + self end # Create a new break element. @@ -376,9 +372,10 @@ def breakable(str = ' ', line_continuation: '', width: str.length) # # b# # # c # - # @return [Nil] + # @return [self] def break(line_continuation: '') tokens << Oppen.line_break(line_continuation: line_continuation, offset: current_indent) + self end # @!group Helpers @@ -390,7 +387,7 @@ def break(line_continuation: '') # @param indent [Integer] # the amount of indentation of the group. # - # @return [Nil] + # @return [self] # # @see Oppen.begin_consistent # @see Oppen.begin_inconsistent @@ -401,13 +398,15 @@ def group_open(inconsistent: false, indent: 0) else Oppen.begin_consistent(offset: indent) end + self end # Close a group. # - # @return [Nil] + # @return [self] def group_close tokens << Oppen.end + self end # Open a consistent group and add indent amount. @@ -415,7 +414,7 @@ def group_close # @param indent [Integer] # the amount of indentation of the group. # - # @return [Nil] + # @return [self] def indent_open(indent: @indent) @current_indent += indent group_open @@ -426,7 +425,7 @@ def indent_open(indent: @indent) # @param indent [Integer] # the amount of indentation of the group. # - # @return [Nil] + # @return [self] def indent_close(group, indent: @indent) @current_indent -= indent group_close @@ -437,9 +436,10 @@ def indent_close(group, indent: @indent) # @param indent [Integer] # the amount of indentation of the nest. # - # @return [Nil] + # @return [self] def nest_open(indent: @indent) @current_indent += indent + self end # Close a nest by subtracting indent. @@ -447,9 +447,10 @@ def nest_open(indent: @indent) # @param indent [Integer] # the amount of indentation of the nest. # - # @return [Nil] + # @return [self] def nest_close(indent: @indent) @current_indent -= indent + self end # @!endgroup diff --git a/test/wadler_test.rb b/test/wadler_test.rb index 01e12de..68c7618 100644 --- a/test/wadler_test.rb +++ b/test/wadler_test.rb @@ -14,19 +14,19 @@ def assert_wadler(width, expected, builder_block) out.group { out.group { out.group { - out.group { - out.text 'hello' - out.breakable - out.text 'a' - } - out.breakable - out.text 'b' + out + .group { + out + .text('hello') + .breakable + .text('a') + } + .breakable + .text('b') } - out.breakable - out.text 'c' + out.breakable.text 'c' } - out.breakable - out.text 'd' + out.breakable.text 'd' } } @@ -87,25 +87,26 @@ def initialize(string, *children) def show(out) out.group { - out.text string - out.nest(indent: string.length) { - unless children.empty? - out.text '[' - out.nest(indent: 1) { - first = true - children.each { |t| - if first - first = false - else - out.text ',' - out.breakable - end - t.show(out) - } - } - out.text ']' - end - } + out + .text(string) + .nest(indent: string.length) { + unless children.empty? + out + .text('[') + .nest(indent: 1) { + first = true + children.each { |t| + if first + first = false + else + out.text(',').breakable + end + t.show(out) + } + } + .text ']' + end + } } end end From d3e0ef34e89cba083b98e2625a25bef6e446c26e Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 16:26:10 +0100 Subject: [PATCH 08/23] wdaler: add do method to avoid breaking call chains --- CHANGELOG.md | 1 + lib/wadler/print.rb | 18 ++++++++++++++++++ test/wadler_test.rb | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a40eb0..f4ec1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - add `#consistent` and `#inconsistent` as shorthands to `group(:consistent)` and `group(:inconsistent)`, respectively. - remove args of `#group_close`. + - add `do` to avoid braking call chains. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index d3daccc..74c3542 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -378,6 +378,24 @@ def break(line_continuation: '') self end + # A convenient way to avoid breaking chains of calls. + # + # @example + # out + # .do { fn_call(fn_arg) } + # .breakable + # .text('=') + # .breakable + # .do { fn_call(fn_arg) } + # + # @yield to execute the passed block + # + # @return [self] + def do + yield + self + end + # @!group Helpers # Open a consistent group. diff --git a/test/wadler_test.rb b/test/wadler_test.rb index 68c7618..c292ff6 100644 --- a/test/wadler_test.rb +++ b/test/wadler_test.rb @@ -709,4 +709,23 @@ def altshow(out) _(out.output).must_equal "\n\n\n" end end + + describe 'Convenience methods' do + it 'chains with calls of do' do + out = Oppen::Wadler.new(width: 10) + lam = -> { out.text 'from lambda' } + out.group(indent: 8) { + out + .do { lam.() } + .break + .do { lam.() } + .break + } + + _(out.output).must_equal <<~OUT + from lambda + from lambda + OUT + end + end end From 2a8ffe8afb9800c67c20b294fe78c10fa8864924 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 16:41:45 +0100 Subject: [PATCH 09/23] wadler: normalize group_open and accept a symbol break_type --- CHANGELOG.md | 5 +++-- lib/wadler/print.rb | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4ec1b0..7af75bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,11 @@ the whole printer. - all printing methods return `self` for call chaining. - Wadler: - - add `#consistent` and `#inconsistent` as shorthands to + - add `consistent` and `inconsistent` as shorthands to `group(:consistent)` and `group(:inconsistent)`, respectively. - - remove args of `#group_close`. + - remove args of `group_close`. - add `do` to avoid braking call chains. + - `group_open` accepts a symbol `break_type` instead of a boolean. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 74c3542..9777c99 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -400,22 +400,21 @@ def do # Open a consistent group. # - # @param inconsistent [Boolean] - # whether the break type of the group should be inconsistent. - # @param indent [Integer] + # @param break_type [Symbol] + # `:consistent` or `:inconsistent` + # @param indent [Integer] # the amount of indentation of the group. # # @return [self] # # @see Oppen.begin_consistent # @see Oppen.begin_inconsistent - def group_open(inconsistent: false, indent: 0) - tokens << - if inconsistent - Oppen.begin_inconsistent(offset: indent) - else - Oppen.begin_consistent(offset: indent) - end + def group_open(break_type: :consistent, indent: 0) + if %i[consistent inconsistent].none?(break_type) + raise ArgumentError, '%s is not a valid type. Choose one: :consistent or :inconsistent' + end + + tokens << Oppen.send(:"begin_#{break_type}", offset: indent) self end @@ -444,7 +443,7 @@ def indent_open(indent: @indent) # the amount of indentation of the group. # # @return [self] - def indent_close(group, indent: @indent) + def indent_close(indent: @indent) @current_indent -= indent group_close end From 51c1532c9cf0fd06342a8750aac7d1974b23652f Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 17:40:25 +0100 Subject: [PATCH 10/23] wadler: add {#separate} --- .rubocop.yml | 3 + CHANGELOG.md | 1 + lib/wadler/print.rb | 148 +++++++++++++++++++++++++++++++++++ test/lib.rb | 6 ++ test/wadler_separate_test.rb | 100 +++++++++++++++++++++++ test/wadler_test.rb | 6 -- 6 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 test/wadler_separate_test.rb diff --git a/.rubocop.yml b/.rubocop.yml index ad7c4e2..815ad1c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -79,6 +79,9 @@ Style/Lambda: Style/LambdaCall: EnforcedStyle: braces +Style/MultilineBlockChain: + Enabled: false + Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af75bd..6daff96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - remove args of `group_close`. - add `do` to avoid braking call chains. - `group_open` accepts a symbol `break_type` instead of a boolean. + - add `separate` to separate a list of items. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 9777c99..627e4ef 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -396,6 +396,123 @@ def do self end + # A means to wrap a piece of code in several ways. + # + # @example + # out + # .wrap { + # # all printing instructions here will be deferred. + # # they will be executed in `when` blocks by calling the `wrapped`. + # out.text(...) + # # ... + # } # This is "wrapped". + # .when(cond1){ |wrapped| + # # when cond1 is true you execute this block. + # out.text("before wrapped") + # # call the wrapped + # wrapped.call + # # and continue printing + # out.text("after wrapped) + # } + # .when(cond2){ |wrapped| + # # and you cand define many conditions. + # } + # .end + # + # @example Calling `end` is not needed if there's another call after the last `when`: + # out + # .wrap{...} # This is "wrapped". + # .when(cond1){ |wrapped| ... } + # .when(cond2){ |wrapped| ... } + # .text('foo') + # + # @return [Wrap] + def wrap(&blk) + Wrap.new(blk) + end + + # Produce a separated list. + # + # @example Consistent Breaking + # puts out.separate((1..3).map(&:to_s), ',') { |i| out.text i} + # + # # => + # # 1, + # # 2, + # # 3 + # + # @example Inconsistent Breaking + # puts out.separate((1..3).map(&:to_s), ',', break_type: :inconsistent) { |i| out.text i} + # + # # => + # # 1, 2, + # # 3 + # + # @param args [String] + # a list of values. + # @param sep [String] + # a separator. + # @param breakable [String|Nil] + # adds a `breakable` after the separator. + # @param break_pos [Symbol] + # whether to break :before or :after the seraparator. + # @param break_type [Symbol|Nil] + # whether the break is :consistent or :inconsistent. + # If nil is given, the tokens will not be surrounded by a group. + # @param indent [Boolean] + # whether to indent. + # @param force_break [Boolean] + # adds a `break` after the separator. + # @param line_continuation [String] + # string to display before new line. + # + # @yield to execute the passed block. + # + # @return [self] + def separate(args, sep, breakable: ' ', break_pos: :after, + break_type: nil, indent: false, + force_break: false, line_continuation: '') + if args.is_a?(Enumerator) ? args.count == 1 : args.length == 1 + yield(*args[0]) + return self + end + + first = true + wrap { + wrap { + args&.each do |*as| + if first + breakable '' if !line_continuation.empty? && break_pos == :after + first = false + elsif break_pos == :after + text sep + breakable(breakable, line_continuation: line_continuation) if breakable && !force_break + self.break(line_continuation: line_continuation) if force_break + else + breakable(breakable, line_continuation: line_continuation) if breakable && !force_break + self.break(line_continuation: line_continuation) if force_break + text sep + end + yield(*as) + end + } + .when(break_type) { |body| + group(break_type) { + body.() + } + } + .end + } + .when(indent) { |body| + nest { + body.() + } + }.end + breakable('', line_continuation: line_continuation) if !line_continuation.empty? && !break_type + + self + end + # @!group Helpers # Open a consistent group. @@ -471,5 +588,36 @@ def nest_close(indent: @indent) end # @!endgroup + + # Helper class to allow conditional printing. + class Wrap + def initialize(blk) + @wrapped = blk + @wrapper = nil + end + + # Conditional. + def when(cond, &blk) + if cond + @wrapper = blk + end + self + end + + # Flush. + def end + @wrapper ? @wrapper.(@wrapped) : @wrapped.() + end + + # To re-enable chaining. + def method_missing(meth, ...) + self.end.send(meth, ...) + end + + # To re-enable chaining. + def respond_to_missing?(meth, include_private) + self.end.respond_to_missing?(meth, include_private) + end + end end end diff --git a/test/lib.rb b/test/lib.rb index dd32943..4a1257f 100644 --- a/test/lib.rb +++ b/test/lib.rb @@ -39,3 +39,9 @@ def break current_group.break end end + +def assert_wadler(width, expected, builder_block) + printer = Oppen::Wadler.new(width: width) + builder_block.(printer) + _(printer.output).must_equal expected, 'Oppen failed the test' +end diff --git a/test/wadler_separate_test.rb b/test/wadler_separate_test.rb new file mode 100644 index 0000000..b0652d4 --- /dev/null +++ b/test/wadler_separate_test.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require_relative 'lib' + +describe 'separate' do + it 'does nothing for singletons' do + width = 10 + block = proc { |out| + out.separate(%w[1], ',') { |i| + out.text i + } + } + assert_wadler width, '1', block + end + + it 'adds separator for non-sigletons' do + width = 10 + block = proc { |out| + out.separate(%w[1 2 3], ',') { |i| + out.text i + } + } + assert_wadler width, '1, 2, 3', block + end + + it 'breaks consistently by default' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',') { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + OUT + end + + it 'breaks inconsistently' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent) { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, + 10 + OUT + end + + it 'breaks consistently before the separator' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',', break_pos: :before) { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1 + ,2 + ,3 + ,4 + ,5 + ,6 + ,7 + ,8 + ,9 + ,10 + OUT + end + + it 'breaks inconsistently before the separator' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',', break_pos: :before, break_type: :inconsistent) { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1 ,2 ,3 ,4 + ,5 ,6 ,7 + ,8 ,9 ,10 + OUT + end + + # TODO: the rest of the params + # 1. indent doesn't work + # 1. breaking inconsistently :before deos not make sense. +end diff --git a/test/wadler_test.rb b/test/wadler_test.rb index c292ff6..c742964 100644 --- a/test/wadler_test.rb +++ b/test/wadler_test.rb @@ -2,12 +2,6 @@ require_relative 'lib' -def assert_wadler(width, expected, builder_block) - printer = Oppen::Wadler.new(width: width) - builder_block.(printer) - _(printer.output).must_equal expected, 'Oppen failed the test' -end - describe 'Wadler tests' do describe 'must work like ruby\'s PrettyPrint library' do builder_block = proc { |out| From 272da6258d3625d1ef724de1fcd23e6f3d9d7687 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 17:49:13 +0100 Subject: [PATCH 11/23] wadler: rename ctor param space to space_gen This is in preparation for the next commit which introduces the `space` method to actually print a space. Besides, it's technically a space generator. --- CHANGELOG.md | 1 + .../oppen_and_wadler_customization/space.rb | 2 +- lib/wadler/print.rb | 10 ++++---- test/customization_test.rb | 24 +++++++++---------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6daff96..a4ce8ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ the whole printer. - all printing methods return `self` for call chaining. - Wadler: + - rename ctor param `space` to `space_gen`. - add `consistent` and `inconsistent` as shorthands to `group(:consistent)` and `group(:inconsistent)`, respectively. - remove args of `group_close`. diff --git a/examples/oppen_and_wadler_customization/space.rb b/examples/oppen_and_wadler_customization/space.rb index b805cdf..0bd8a5e 100644 --- a/examples/oppen_and_wadler_customization/space.rb +++ b/examples/oppen_and_wadler_customization/space.rb @@ -9,7 +9,7 @@ # By using a callable: space = ->(n) { '---' * n } -printer = Oppen::Wadler.new(indent: 2, space: space) +printer = Oppen::Wadler.new(indent: 2, space_gen: space) printer.group { printer.text 'Hello, World!' diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 627e4ef..118b02f 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -18,7 +18,7 @@ class Wadler attr_reader :out # @return [Proc] # space generator, a callable. - attr_reader :space + attr_reader :space_gen # @return [Array] # the tokens list that is being built. attr_reader :tokens @@ -39,7 +39,7 @@ class Wadler # the new line String. # @param out [Object] # the output string buffer. It should have a `write` and `string` methods. - # @param space [String, Proc] + # @param space_gen [String, Proc] # indentation string or a string generator. # - If a `String`, spaces will be generated with the the lambda # `->(n){ space * n }`, where `n` is the number of columns to indent. @@ -49,14 +49,14 @@ class Wadler # # @see Token::Whitespace def initialize(base_indent: 0, config: Config.wadler, indent: 0, new_line: "\n", - out: StringIO.new, space: ' ', + out: StringIO.new, space_gen: ' ', whitespace: ' ', width: 80) @config = config @current_indent = base_indent @indent = indent @new_line = new_line @out = out - @space = space + @space_gen = space_gen @tokens = [] @whitespace = whitespace @width = width @@ -82,7 +82,7 @@ def output tokens: tokens, new_line: new_line, config: config, - space: space, + space: space_gen, out: out, width: width, ) diff --git a/test/customization_test.rb b/test/customization_test.rb index 0737042..33400bf 100644 --- a/test/customization_test.rb +++ b/test/customization_test.rb @@ -62,7 +62,7 @@ def customization_build_output(printer) describe 'Spaces tests' do it 'must work with a string of length 1' do - printer = Oppen::Wadler.new(width: 45, space: '*') + printer = Oppen::Wadler.new(width: 45, space_gen: '*') customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -74,7 +74,7 @@ def customization_build_output(printer) end it 'must work with a string of length greater than 1' do - printer = Oppen::Wadler.new(width: 45, space: '**') + printer = Oppen::Wadler.new(width: 45, space_gen: '**') customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -86,7 +86,7 @@ def customization_build_output(printer) end it 'must work with an UTF-8 string' do - printer = Oppen::Wadler.new(width: 45, space: 'ω') + printer = Oppen::Wadler.new(width: 45, space_gen: 'ω') customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -98,7 +98,7 @@ def customization_build_output(printer) end it 'must work with a lambda' do - printer = Oppen::Wadler.new(width: 45, space: ->(n) { '*' * n }) + printer = Oppen::Wadler.new(width: 45, space_gen: ->(n) { '*' * n }) customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -110,7 +110,7 @@ def customization_build_output(printer) end it 'must work with an UTF-8 string lambda' do - printer = Oppen::Wadler.new(width: 45, space: ->(n) { 'ω' * n }) + printer = Oppen::Wadler.new(width: 45, space_gen: ->(n) { 'ω' * n }) customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -122,7 +122,7 @@ def customization_build_output(printer) end it 'must work with a lambda different from default' do - printer = Oppen::Wadler.new(width: 45, space: ->(n) { '*' * n * 2 }) + printer = Oppen::Wadler.new(width: 45, space_gen: ->(n) { '*' * n * 2 }) customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -134,7 +134,7 @@ def customization_build_output(printer) end it 'must work with a proc' do - printer = Oppen::Wadler.new(width: 45, space: proc { |n| '*' * n }) + printer = Oppen::Wadler.new(width: 45, space_gen: proc { |n| '*' * n }) customization_build_output(printer) _(printer.output).must_equal <<~LANG.chomp function @@ -146,20 +146,20 @@ def customization_build_output(printer) end it 'must raise an error for a lambda of arity different than one' do - printer = Oppen::Wadler.new(width: 45, space: ->(n, _o) { '*' * n }) + printer = Oppen::Wadler.new(width: 45, space_gen: ->(n, _o) { '*' * n }) _ { printer.output }.must_raise ArgumentError - printer = Oppen::Wadler.new(width: 45, space: -> { '*' }) + printer = Oppen::Wadler.new(width: 45, space_gen: -> { '*' }) _ { printer.output }.must_raise ArgumentError - printer = Oppen::Wadler.new(width: 45, space: ->(*_args) { '*' }) + printer = Oppen::Wadler.new(width: 45, space_gen: ->(*_args) { '*' }) _ { printer.output }.must_raise ArgumentError end # The indentation lambda can yield a fixed size string indepedant of the indentation level. # In these cases, the fixed sized string should not be displayed if the indentation is 0. - it 'calls space only when indent is positive' do - printer = Oppen::Wadler.new(width: 10, space: ->(_n) { '*******' }) + it 'calls space_gen only when indent is positive' do + printer = Oppen::Wadler.new(width: 10, space_gen: ->(_n) { '*******' }) printer.group { printer.text 'Hello' printer.break From 695244cd0f46c44ad2e2694e2c25acf709773ccb Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Tue, 31 Dec 2024 17:53:28 +0100 Subject: [PATCH 12/23] wadler: add {#space} to generate a literal space --- CHANGELOG.md | 1 + lib/wadler/print.rb | 7 +++++++ test/wadler_test.rb | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ce8ee..675f8e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - add `do` to avoid braking call chains. - `group_open` accepts a symbol `break_type` instead of a boolean. - add `separate` to separate a list of items. + - add `space` to generate a literal space. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 118b02f..bb8186c 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -513,6 +513,13 @@ def separate(args, sep, breakable: ' ', break_pos: :after, self end + # A shorhand for `text ' '`. + # + # @return [self] + def space + text ' ' + end + # @!group Helpers # Open a consistent group. diff --git a/test/wadler_test.rb b/test/wadler_test.rb index c742964..daa9206 100644 --- a/test/wadler_test.rb +++ b/test/wadler_test.rb @@ -721,5 +721,12 @@ def altshow(out) from lambda OUT end + + it 'produces an actual space' do + out = Oppen::Wadler.new(width: 10) + out.text('Hello').space.text('World!') + + _(out.output).must_equal 'Hello World!' + end end end From 1629d5ba8b5d2dc4662f524063f2d389821c70b3 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Fri, 3 Jan 2025 13:06:38 +0100 Subject: [PATCH 13/23] wadler: add {#surround} as a glorified group It adds breakables next to delimiters, and gives more control to the caller. Will be useful in the next commits when we add more vocab. --- CHANGELOG.md | 1 + lib/wadler/print.rb | 74 +++++++++++++++ test/wadler_surround_test.rb | 175 +++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 test/wadler_surround_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 675f8e4..f679e6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - `group_open` accepts a symbol `break_type` instead of a boolean. - add `separate` to separate a list of items. - add `space` to generate a literal space. + - add `surround` as a glorified `group` that add breakables next to delimiters. ## v0.9.8 (30-12-2024) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index bb8186c..42cba9f 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -520,6 +520,80 @@ def space text ' ' end + # Surround a block with +lft+ and +rgt+ + # + # @param lft [String] lft + # left surrounding string. + # @param rgt [String] rgt + # right surrounding string. + # + # @yield the passed block to be surrounded with `lft` and `rgt`. + # + # @option opts [Boolean] :group (true) + # whether to create a group enclosing `lft`, `rgt`, and the passed block. + # @option opts [Boolean] :indent (@indent) + # whether to indent the passed block. + # @option opts [String] :lft_breakable ('') + # left breakable string. + # @option opts [Boolean] :lft_can_break (true) + # injects `break` or `breakable` only if true; + # i.e. `lft_breakable` will be ignored if false. + # @option opts [Boolean] :lft_force_break (false) + # force break instead of using `lft_breakable`. + # @option opts [String] :rgt_breakable ('') + # right breakable string. + # @option opts [Boolean] :rgt_can_break (true) + # injects `break` or `breakable` only if true. + # i.e. `rgt_breakable` will be ignored if false. + # @option opts [Boolean] :rgt_force_break (false) + # force break instead of using `rgt_breakable`. + # + # @return [self] + def surround(lft, rgt, **opts) + group = opts.fetch(:group, true) + group_open(break_type: :inconsistent) if group + + text lft if lft + + indent = opts.fetch(:indent, @indent) + nest_open(indent: indent) + + lft_breakable = opts.fetch(:lft_breakable, '') + lft_can_break = opts.fetch(:lft_can_break, true) + lft_force_break = opts.fetch(:lft_force_break, false) + if lft && lft_can_break + if lft_force_break + self.break + else + breakable lft_breakable + end + end + + if block_given? + yield + end + + nest_close + + rgt_breakable = opts.fetch(:rgt_breakable, '') + rgt_can_break = opts.fetch(:rgt_can_break, true) + rgt_force_break = opts.fetch(:rgt_force_break, false) + if rgt + if rgt_can_break + if rgt_force_break + self.break + else + breakable rgt_breakable + end + end + text rgt + end + + group_close if group + + self + end + # @!group Helpers # Open a consistent group. diff --git a/test/wadler_surround_test.rb b/test/wadler_surround_test.rb new file mode 100644 index 0000000..63c338f --- /dev/null +++ b/test/wadler_surround_test.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +require_relative 'lib' + +describe 'surround' do + it 'prints lft and rg only' do + width = 10 + block = proc { |out| + out.surround('{', '}').text('!') + } + assert_wadler width, '{}!', block + end + + it 'accepts a block' do + width = 10 + block = proc { |out| + out.surround('{', '}') { + out.text '1' + } + } + assert_wadler width, '{1}', block + end + + it 'breaks on left and right inconsistently by default' do + width = 10 + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}') { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + end + + it 'force breaks on left' do + width = 10 + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_force_break: true) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, lft_force_break: true) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + end + + it 'force breaks on right' do + width = 10 + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', rgt_force_break: true) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1 + } + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, rgt_force_break: true) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1 + } + OUT + end + + it 'prevents breaks on left' do + width = 10 + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_can_break: false) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{1 + } + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, lft_can_break: false) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{1 + } + OUT + end + + it 'prevents breaks on right' do + width = 10 + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', rgt_can_break: false) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, rgt_can_break: false) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + end + + it 'can change breakables' do + width = 10 + block = proc { |out| + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { + out.text '1' + } + } + assert_wadler width, '{<1>}', block + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1>} + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', lft_can_break: false, rgt_breakable: '>') { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{1 + } + OUT + block = proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>', rgt_can_break: false) { + out.text '1' + } + } + assert_wadler width, <<~OUT.chomp, block + A long long string{ + 1} + OUT + end +end From 470a06078e77ddbd81f1d472e1bbcadc477a23f6 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Mon, 6 Jan 2025 10:50:38 +0100 Subject: [PATCH 14/23] wadler: add {#lines} and {#concat} as wrappers for {#surround} --- lib/wadler/print.rb | 18 ++++++++++++++++++ test/wadler_separate_test.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 42cba9f..d21beae 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -594,6 +594,24 @@ def surround(lft, rgt, **opts) self end + # Separate args into lines. + # + # This is a wrapper around {separate} where `breakable: true`. + # + # @see [separate] + def lines(*args, **kwargs, &block) + separate(*args, **kwargs.merge(force_break: true), &block) + end + + # Concatenates args. + # + # This is a wrapper around {separate} where `breakable: false`. + # + # @see [separate] + def concat *args, **kwargs, &block + separate(*args, **kwargs.merge(breakable: false), &block) + end + # @!group Helpers # Open a consistent group. diff --git a/test/wadler_separate_test.rb b/test/wadler_separate_test.rb index b0652d4..3ee3d48 100644 --- a/test/wadler_separate_test.rb +++ b/test/wadler_separate_test.rb @@ -98,3 +98,38 @@ # 1. indent doesn't work # 1. breaking inconsistently :before deos not make sense. end + +describe 'helpers built on separate' do + it 'creates lines from a list' do + width = 10 + block = proc { |out| + out.lines((1..10).map(&:to_s), ',') { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + OUT + end + + it 'concatenates args from a list' do + width = 10 + block = proc { |out| + out.concat((1..10).map(&:to_s), ',') { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1,2,3,4,5,6,7,8,9,10 + OUT + end +end From ca62905ae2b9ba939ca82bd08e10bee0e7cd3f25 Mon Sep 17 00:00:00 2001 From: Firas al-Khalil Date: Mon, 6 Jan 2025 17:40:48 +0100 Subject: [PATCH 15/23] wadler: add some helpers based on surround --- lib/wadler/print.rb | 144 ++++++++++++++++++++++++++++++++++- test/wadler_surround_test.rb | 66 ++++++++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index d21beae..9e8032f 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -594,6 +594,8 @@ def surround(lft, rgt, **opts) self end + # @!group Convenience Methods Built On {separate} + # Separate args into lines. # # This is a wrapper around {separate} where `breakable: true`. @@ -612,7 +614,147 @@ def concat *args, **kwargs, &block separate(*args, **kwargs.merge(breakable: false), &block) end - # @!group Helpers + # @!endgroup + # @!group Convenience Methods Built On {surround} + + # YARD doesn't drop into blocks, so we can't use metaprogramming + # to generate all these functions, so we're copy-pastring. + + # {surround} with `< >`. New lines can appear after and before the delimiters. + # + # @param padding [String] ('') + # Passed to `lft_breakable` and `rgt_breakable`. + # + # @return [self] + def angles(padding: '', **kwargs, &block) + surround( + '<', '>', + **kwargs.merge(lft_breakable: padding, rgt_breakable: padding), + &block + ) + end + + # {surround} with `< >`. New lines cannot appear after and before the delimiters. + # + # @return [self] + def angles_break_both(**kwargs, &block) + angles(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &block) + end + + # {surround} with `< >`. New lines will appear after and before the delimiters. + # + # @return [self] + def angles_break_none(**kwargs, &block) + angles(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &block) + end + + # {surround} with `{ }`. New lines can appear after and before the delimiters. + # + # @param padding [String] ('') + # Passed to `lft_breakable` and `rgt_breakable`. + # + # @return [self] + def braces(padding: '', **kwargs, &block) + surround( + '{', '}', + **kwargs.merge(lft_breakable: padding, rgt_breakable: padding), + &block + ) + end + + # {surround} with `{ }`. New lines cannot appear after and before the delimiters. + # + # @return [self] + def braces_break_both(**kwargs, &block) + braces(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &block) + end + + # {surround} with `{ }`. New lines will appear after and before the delimiters. + # + # @return [self] + def braces_break_none(**kwargs, &block) + braces(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &block) + end + + # {surround} with `[ ]`. New lines can appear after and before the delimiters. + # + # @param padding [String] ('') + # Passed to `lft_breakable` and `rgt_breakable`. + # + # @return [self] + def brackets(padding: '', **kwargs, &block) + surround( + '[', ']', + **kwargs.merge(lft_breakable: padding, rgt_breakable: padding), + &block + ) + end + + # {surround} with `[ ]`. New lines cannot appear after and before the delimiters. + # + # @return [self] + def brackets_break_both(**kwargs, &block) + brackets(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &block) + end + + # {surround} with `[ ]`. New lines will appear after and before the delimiters. + # + # @return [self] + def brackets_break_none(**kwargs, &block) + brackets(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &block) + end + + # {surround} with `( )`. New lines can appear after and before the delimiters. + # + # @param padding [String] ('') + # Passed to `lft_breakable` and `rgt_breakable`. + # + # @return [self] + def parens(padding: '', **kwargs, &block) + surround( + '(', ')', + **kwargs.merge(lft_breakable: padding, rgt_breakable: padding), + &block + ) + end + + # {surround} with `( )`. New lines cannot appear after and before the delimiters. + # + # @return [self] + def parens_break_both(**kwargs, &block) + parens(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &block) + end + + # {surround} with `( )`. New lines will appear after and before the delimiters. + # + # @return [self] + def parens_break_none(**kwargs, &block) + parens(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &block) + end + + # {surround} with `` ` ` ``. New lines cannot appear after and before the delimiters + # unless you specify it with `rgt_can_break` and `lft_can_break`. + # + # @return [self] + def backticks(**kwargs, &block) + surround('`', '`', lft_can_break: false, rgt_can_break: false, **kwargs, &block) + end + + # {surround} with `" "`. New lines cannot appear after and before the delimiters + # unless you specify it with `rgt_can_break` and `lft_can_break`. + # + # @return [self] + def quote_double(**kwargs, &block) + surround('"', '"', lft_can_break: false, rgt_can_break: false, **kwargs, &block) + end + + # {surround} with `' '`. New lines cannot appear after and before the delimiters + # unless you specify it with `rgt_can_break` and `lft_can_break`. + # + # @return [self] + def quote_single(**kwargs, &block) + surround("'", "'", lft_can_break: false, rgt_can_break: false, **kwargs, &block) + end # Open a consistent group. # diff --git a/test/wadler_surround_test.rb b/test/wadler_surround_test.rb index 63c338f..f9dafec 100644 --- a/test/wadler_surround_test.rb +++ b/test/wadler_surround_test.rb @@ -173,3 +173,69 @@ OUT end end + +describe 'helpers for surround' do + width = 10 + padding_no = ->(name) { + proc { |out| + out.send(name) { + out.text '1' + } + } + } + padding_yes = ->(name) { + proc { |out| + out.send(name, padding: '~') { + out.text '1' + } + } + } + [ + [:angles, %w[< >]], + [:braces, %w[{ }]], + [:brackets, %w{[ ]}], + [:parens, %w[( )]], + ].each do |name, chars| + it "prints #{name} w/o padding" do + assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_no.(name) + end + + it "prints #{name} w padding" do + assert_wadler width, "#{chars[0]}~1~#{chars[1]}", padding_yes.(name) + end + + it "prints #{name}_break_both w/o padding" do + assert_wadler width, <<~OUT.chomp, padding_no.("#{name}_break_both") + #{chars[0]} + 1 + #{chars[1]} + OUT + end + + it "prints #{name}_break_both w padding but removes them" do + assert_wadler width, <<~OUT.chomp, padding_yes.("#{name}_break_both") + #{chars[0]} + 1 + #{chars[1]} + OUT + end + + it "prints #{name}_break_non w/o padding but removes them" do + assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_no.("#{name}_break_none") + end + + it "prints #{name}_break_non w padding but removes them" do + assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_yes.("#{name}_break_none") + end + end + + [ + [:backticks, '`'], + [:quote_double, '"'], + [:quote_single, "'"], + ].each do |name, char| + it "prints #{name}" do + assert_wadler width, "#{char}1#{char}", padding_no.(name) + end + end +end From 8af37ae9cfc91829aaa2c9ac6b170de9ef67f226 Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 11:10:16 +0100 Subject: [PATCH 16/23] wadler: allow {#separate}'s indent param to be an Integer --- lib/wadler/print.rb | 9 +++++---- test/lib.rb | 4 ++-- test/wadler_separate_test.rb | 32 +++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 9e8032f..515c26f 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -459,8 +459,9 @@ def wrap(&blk) # @param break_type [Symbol|Nil] # whether the break is :consistent or :inconsistent. # If nil is given, the tokens will not be surrounded by a group. - # @param indent [Boolean] - # whether to indent. + # @param indent [Boolean|Integer] + # - If `true`, indent by @indent. + # - If an 'Integer', indent by its value. # @param force_break [Boolean] # adds a `break` after the separator. # @param line_continuation [String] @@ -497,14 +498,14 @@ def separate(args, sep, breakable: ' ', break_pos: :after, end } .when(break_type) { |body| - group(break_type) { + group(break_type, indent: 0) { body.() } } .end } .when(indent) { |body| - nest { + nest(indent: indent.is_a?(Integer) ? indent : @indent) { body.() } }.end diff --git a/test/lib.rb b/test/lib.rb index 4a1257f..6cda1dc 100644 --- a/test/lib.rb +++ b/test/lib.rb @@ -40,8 +40,8 @@ def break end end -def assert_wadler(width, expected, builder_block) - printer = Oppen::Wadler.new(width: width) +def assert_wadler(width, expected, builder_block, indent: 0) + printer = Oppen::Wadler.new(indent: indent, width: width) builder_block.(printer) _(printer.output).must_equal expected, 'Oppen failed the test' end diff --git a/test/wadler_separate_test.rb b/test/wadler_separate_test.rb index 3ee3d48..cb191d8 100644 --- a/test/wadler_separate_test.rb +++ b/test/wadler_separate_test.rb @@ -94,8 +94,38 @@ OUT end + it 'indents when using a Boolean' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: true) { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block, indent: 4 + 1, 2, 3, + 4, 5, + 6, 7, + 8, 9, + 10 + OUT + end + + it 'indents when using an Integer' do + width = 10 + block = proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: 2) { |i| + out.text i + } + } + assert_wadler width, <<~OUT.chomp, block + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, + 10 + OUT + end + # TODO: the rest of the params - # 1. indent doesn't work # 1. breaking inconsistently :before deos not make sense. end From 4f911d7fb698e3a138f9b60f7de02e7659db4316 Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 13:33:02 +0100 Subject: [PATCH 17/23] test: refactor surround and separate tests --- test/wadler_separate_test.rb | 304 ++++++++++++------------ test/wadler_surround_test.rb | 432 +++++++++++++++++++---------------- 2 files changed, 389 insertions(+), 347 deletions(-) diff --git a/test/wadler_separate_test.rb b/test/wadler_separate_test.rb index cb191d8..3b09c25 100644 --- a/test/wadler_separate_test.rb +++ b/test/wadler_separate_test.rb @@ -3,163 +3,165 @@ require_relative 'lib' describe 'separate' do - it 'does nothing for singletons' do - width = 10 - block = proc { |out| - out.separate(%w[1], ',') { |i| - out.text i - } - } - assert_wadler width, '1', block - end - - it 'adds separator for non-sigletons' do - width = 10 - block = proc { |out| - out.separate(%w[1 2 3], ',') { |i| - out.text i - } - } - assert_wadler width, '1, 2, 3', block - end - - it 'breaks consistently by default' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',') { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - OUT - end - - it 'breaks inconsistently' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent) { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1, 2, 3, - 4, 5, 6, - 7, 8, 9, - 10 - OUT - end - - it 'breaks consistently before the separator' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',', break_pos: :before) { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1 - ,2 - ,3 - ,4 - ,5 - ,6 - ,7 - ,8 - ,9 - ,10 - OUT - end - - it 'breaks inconsistently before the separator' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',', break_pos: :before, break_type: :inconsistent) { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1 ,2 ,3 ,4 - ,5 ,6 ,7 - ,8 ,9 ,10 - OUT - end - - it 'indents when using a Boolean' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: true) { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block, indent: 4 - 1, 2, 3, - 4, 5, - 6, 7, - 8, 9, - 10 - OUT - end - - it 'indents when using an Integer' do - width = 10 - block = proc { |out| - out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: 2) { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1, 2, 3, + [ + { + title: 'does nothing for singletons', + block: proc { |out| + out.separate(%w[1], ',') { |i| + out.text i + } + }, + expected: '1', + }, + { + title: 'adds separator for non-sigletons', + block: proc { |out| + out.separate(%w[1 2 3], ',') { |i| + out.text i + } + }, + expected: '1, 2, 3', + }, + { + title: 'breaks consistently by default', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',') { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + OUT + }, + { + title: 'breaks inconsistently', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent) { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - OUT + OUT + }, + { + title: 'breaks consistently before the separator', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',', break_pos: :before) { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1 + ,2 + ,3 + ,4 + ,5 + ,6 + ,7 + ,8 + ,9 + ,10 + OUT + }, + { + title: 'breaks inconsistently before the separator', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',', break_pos: :before, break_type: :inconsistent) { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1 ,2 ,3 ,4 + ,5 ,6 ,7 + ,8 ,9 ,10 + OUT + }, + { + title: 'indents when using a Boolean', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: true) { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1, 2, 3, + 4, 5, + 6, 7, + 8, 9, + 10 + OUT + indent: 4, + }, + { + title: 'indents when using an Integer', + block: proc { |out| + out.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: 2) { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1, 2, 3, + 4, 5, 6, + 7, 8, 9, + 10 + OUT + }, + ].each do |test| + it test[:title] do + assert_wadler 10, test[:expected], test[:block], indent: test[:indent] || 0 + end end - - # TODO: the rest of the params - # 1. breaking inconsistently :before deos not make sense. end describe 'helpers built on separate' do - it 'creates lines from a list' do - width = 10 - block = proc { |out| - out.lines((1..10).map(&:to_s), ',') { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - OUT - end - - it 'concatenates args from a list' do - width = 10 - block = proc { |out| - out.concat((1..10).map(&:to_s), ',') { |i| - out.text i - } - } - assert_wadler width, <<~OUT.chomp, block - 1,2,3,4,5,6,7,8,9,10 - OUT + [ + { + title: 'creates lines from a list', + block: proc { |out| + out.lines((1..10).map(&:to_s), ',') { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + OUT + }, + { + title: 'concatenates args from a list', + block: proc { |out| + out.concat((1..10).map(&:to_s), ',') { |i| + out.text i + } + }, + expected: <<~OUT.chomp, + 1,2,3,4,5,6,7,8,9,10 + OUT + }, + ].each do |test| + it test[:title] do + assert_wadler 10, test[:expected], test[:block], indent: test[:indent] || 0 + end end end diff --git a/test/wadler_surround_test.rb b/test/wadler_surround_test.rb index f9dafec..843e20d 100644 --- a/test/wadler_surround_test.rb +++ b/test/wadler_surround_test.rb @@ -3,179 +3,198 @@ require_relative 'lib' describe 'surround' do - it 'prints lft and rg only' do - width = 10 - block = proc { |out| - out.surround('{', '}').text('!') - } - assert_wadler width, '{}!', block - end - - it 'accepts a block' do - width = 10 - block = proc { |out| - out.surround('{', '}') { - out.text '1' - } - } - assert_wadler width, '{1}', block - end - - it 'breaks on left and right inconsistently by default' do - width = 10 - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}') { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1} - OUT - end - - it 'force breaks on left' do - width = 10 - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', lft_force_break: true) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1} - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', indent: 2, lft_force_break: true) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ + [ + { + title: 'prints lft and rg only', + block: proc { |out| + out.surround('{', '}').text('!') + }, + expected: '{}!', + }, + { + title: 'accepts a block', + block: proc { |out| + out.surround('{', '}') { + out.text '1' + } + }, + expected: '{1}', + }, + { + title: 'breaks on left and right inconsistently by default', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}') { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ 1} - OUT - end - - it 'force breaks on right' do - width = 10 - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', rgt_force_break: true) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1 - } - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', indent: 2, rgt_force_break: true) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ + OUT + }, + { + title: 'force breaks on left', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_force_break: true) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ + 1} + OUT + }, + { + title: 'force breaks on left with indent', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, lft_force_break: true) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ + 1} + OUT + }, + { + title: 'force breaks on right', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', rgt_force_break: true) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ 1 } - OUT - end - - it 'prevents breaks on left' do - width = 10 - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', lft_can_break: false) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{1 - } - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', indent: 2, lft_can_break: false) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{1 + OUT + }, + { + title: 'force breaks on right with indent', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, rgt_force_break: true) { + out.text '1' } - OUT - end - - it 'prevents breaks on right' do - width = 10 - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', rgt_can_break: false) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1} - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', indent: 2, rgt_can_break: false) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ + }, + expected: <<~OUT.chomp, + A long long string{ + 1 + } + OUT + }, + { + title: 'prevents breaks on left', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_can_break: false) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{1 + } + OUT + }, + { + title: 'prevents breaks on left with indent', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, lft_can_break: false) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{1 + } + OUT + }, + { + title: 'prevents breaks on right', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', rgt_can_break: false) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ 1} - OUT - end - - it 'can change breakables' do - width = 10 - block = proc { |out| - out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { - out.text '1' - } - } - assert_wadler width, '{<1>}', block - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1>} - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', lft_breakable: '<', lft_can_break: false, rgt_breakable: '>') { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{1 - } - OUT - block = proc { |out| - out.text 'A long long string' - out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>', rgt_can_break: false) { - out.text '1' - } - } - assert_wadler width, <<~OUT.chomp, block - A long long string{ - 1} - OUT + OUT + }, + { + title: 'prevents breaks on right with indent', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', indent: 2, rgt_can_break: false) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ + 1} + OUT + }, + { + title: 'can change breakable', + block: proc { |out| + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { + out.text '1' + } + }, + expected: '{<1>}', + }, + { + title: 'can change breakable and breaks on left and right inconsistently by default', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>') { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ + 1>} + OUT + }, + { + title: 'can change breakable and prevent left break', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', lft_can_break: false, rgt_breakable: '>') { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{1 + } + OUT + }, + { + title: 'can change breakable and prevent right break', + block: proc { |out| + out.text 'A long long string' + out.surround('{', '}', lft_breakable: '<', rgt_breakable: '>', rgt_can_break: false) { + out.text '1' + } + }, + expected: <<~OUT.chomp, + A long long string{ + 1} + OUT + }, + ].each do |test| + it test[:title] do + assert_wadler 10, test[:expected], test[:block] + end end end describe 'helpers for surround' do - width = 10 padding_no = ->(name) { proc { |out| out.send(name) { @@ -196,36 +215,49 @@ [:brackets, %w{[ ]}], [:parens, %w[( )]], ].each do |name, chars| - it "prints #{name} w/o padding" do - assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_no.(name) - end - - it "prints #{name} w padding" do - assert_wadler width, "#{chars[0]}~1~#{chars[1]}", padding_yes.(name) - end - - it "prints #{name}_break_both w/o padding" do - assert_wadler width, <<~OUT.chomp, padding_no.("#{name}_break_both") - #{chars[0]} - 1 - #{chars[1]} - OUT - end - - it "prints #{name}_break_both w padding but removes them" do - assert_wadler width, <<~OUT.chomp, padding_yes.("#{name}_break_both") - #{chars[0]} - 1 - #{chars[1]} - OUT - end - - it "prints #{name}_break_non w/o padding but removes them" do - assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_no.("#{name}_break_none") - end - - it "prints #{name}_break_non w padding but removes them" do - assert_wadler width, "#{chars[0]}1#{chars[1]}", padding_yes.("#{name}_break_none") + [ + { + title: "prints #{name} w/o padding", + block: padding_no.(name), + expected: "#{chars[0]}1#{chars[1]}", + }, + { + title: "prints #{name} w padding", + block: padding_yes.(name), + expected: "#{chars[0]}~1~#{chars[1]}", + }, + { + title: "prints #{name}_break_both w/o padding", + block: padding_no.("#{name}_break_both"), + expected: <<~OUT.chomp, + #{chars[0]} + 1 + #{chars[1]} + OUT + }, + { + title: "prints #{name}_break_both w padding but removes them", + block: padding_yes.("#{name}_break_both"), + expected: <<~OUT.chomp, + #{chars[0]} + 1 + #{chars[1]} + OUT + }, + { + title: "prints #{name}_break_non w/o padding but removes them", + block: padding_no.("#{name}_break_none"), + expected: "#{chars[0]}1#{chars[1]}", + }, + { + title: "prints #{name}_break_non w padding but removes them", + block: padding_yes.("#{name}_break_none"), + expected: "#{chars[0]}1#{chars[1]}", + }, + ].each do |test| + it test[:title] do + assert_wadler 10, test[:expected], test[:block] + end end end @@ -234,8 +266,16 @@ [:quote_double, '"'], [:quote_single, "'"], ].each do |name, char| - it "prints #{name}" do - assert_wadler width, "#{char}1#{char}", padding_no.(name) + [ + { + title: "prints #{name}", + block: padding_no.(name), + expected: "#{char}1#{char}", + }, + ].each do |test| + it test[:title] do + assert_wadler 10, test[:expected], test[:block] + end end end end From 42bc83caa0608bcc691bc745f67f3dc790e22482 Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 15:11:08 +0100 Subject: [PATCH 18/23] wadler: always add parent group before starting print --- lib/wadler/print.rb | 6 ++---- test/token_to_wadler_test.rb | 38 ++++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 515c26f..e5dc0fe 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -66,10 +66,8 @@ def initialize(base_indent: 0, config: Config.wadler, indent: 0, new_line: "\n", # # @return [Nil] def add_missing_begin_and_end - if !tokens.first.is_a? Token::Begin - tokens.unshift Oppen.begin_consistent(offset: 0) - tokens << Oppen.end - end + tokens.unshift Oppen.begin_consistent(offset: 0) + tokens << Oppen.end tokens << Oppen.eof if !tokens.last.is_a?(Oppen::Token::EOF) end diff --git a/test/token_to_wadler_test.rb b/test/token_to_wadler_test.rb index 3abc096..9ac3327 100644 --- a/test/token_to_wadler_test.rb +++ b/test/token_to_wadler_test.rb @@ -129,7 +129,9 @@ }, expected: <<~LANG, printer.group(:consistent, indent: 0) { - printer.text("Hello World!", width: 12) + printer.group(:consistent, indent: 0) { + printer.text("Hello World!", width: 12) + } } LANG @@ -142,12 +144,14 @@ } }, expected: <<~LANG, - printer.group(:inconsistent, indent: 2) { - printer.break(line_continuation: "") - printer.text("{", width: 1) - printer.text("Hello World!", width: 12) - printer.break(line_continuation: "") - printer.text("}", width: 1) + printer.group(:consistent, indent: 0) { + printer.group(:inconsistent, indent: 2) { + printer.break(line_continuation: "") + printer.text("{", width: 1) + printer.text("Hello World!", width: 12) + printer.break(line_continuation: "") + printer.text("}", width: 1) + } } LANG @@ -201,7 +205,9 @@ printer.group(:consistent, indent: 0) { printer.group(:consistent, indent: 0) { printer.group(:consistent, indent: 0) { - printer.text("Hello World!", width: 12) + printer.group(:consistent, indent: 0) { + printer.text("Hello World!", width: 12) + } } } } @@ -302,14 +308,16 @@ } }, expected: <<~LANG, - printer.group(:consistent, indent: 2) { + printer.group(:consistent, indent: 0) { printer.group(:consistent, indent: 2) { - printer.nest(indent: 4) { - printer.breakable(" ", width: 1, line_continuation: "") - } - printer.text("Hello World!", width: 12) - printer.nest(indent: 4) { - printer.break(line_continuation: "") + printer.group(:consistent, indent: 2) { + printer.nest(indent: 4) { + printer.breakable(" ", width: 1, line_continuation: "") + } + printer.text("Hello World!", width: 12) + printer.nest(indent: 4) { + printer.break(line_continuation: "") + } } } } From 30cf12267e16e595f9514f3154657bbe2fec3ce0 Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 15:47:32 +0100 Subject: [PATCH 19/23] fix: wadler groups aliases --- lib/wadler/print.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index e5dc0fe..92f1d0b 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -236,13 +236,13 @@ def group(break_type = :consistent, delim: nil, indent: @indent) end # An alias for `group(:consistent, ...)` - def consistent(**kwargs) - group(:consistent, **kwargs) + def consistent(...) + group(:consistent, ...) end # An alias for `group(:inconsistent, ...)` - def inconsistent(**kwargs) - group(:inconsistent, **kwargs) + def inconsistent(...) + group(:inconsistent, ...) end # Create a new non-strict {group}. From a10bec70ef331478208c19ba30a825e2e81c1b9c Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 16:41:58 +0100 Subject: [PATCH 20/23] doc: fix examples results --- lib/oppen.rb | 2 +- lib/wadler/print.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/oppen.rb b/lib/oppen.rb index 60a64eb..1a0c3c5 100644 --- a/lib/oppen.rb +++ b/lib/oppen.rb @@ -123,7 +123,7 @@ def initialize(eager_print: false, indent_anchor: :end_of_previous_line, # # # # eager_print: true => # # abc defghi - # # jkl + # # jkl # # @return [Boolean] def eager_print? = @eager_print diff --git a/lib/wadler/print.rb b/lib/wadler/print.rb index 92f1d0b..2ead099 100644 --- a/lib/wadler/print.rb +++ b/lib/wadler/print.rb @@ -99,9 +99,7 @@ def output # # @example # out = Oppen::Wadler.new - # out.group { - # out.text('Hello World!') - # } + # out.text('Hello World!') # out.show_print_commands(out_name: 'out') # # # => From 9a48652cb710e725a60acea7eedee1321985738a Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 16:16:37 +0100 Subject: [PATCH 21/23] examples: make compliant to new api --- examples/configs/eager_print.rb | 2 +- examples/configs/indent_anchor.rb | 6 ++++-- examples/configs/trim_trailing_whitespaces.rb | 0 examples/configs/upsize_stack.rb | 0 examples/oppen/oppen.rb | 0 .../oppen_and_wadler_customization/new_line.rb | 0 examples/oppen_and_wadler_customization/space.rb | 0 .../oppen_and_wadler_customization/whitespace.rb | 0 examples/oppen_and_wadler_customization/width.rb | 0 .../show_print_commands/show_print_commands.rb | 16 +++++++++------- examples/wadler_break_and_breakable/break.rb | 0 examples/wadler_break_and_breakable/breakable.rb | 2 +- .../line_continuation.rb | 0 examples/wadler_group/consistent.rb | 2 +- examples/wadler_group/delimiters.rb | 5 +++-- examples/wadler_group/inconsistent.rb | 2 +- examples/wadler_group/indentation.rb | 4 ++-- examples/wadler_nest/delimiters.rb | 4 ++-- examples/wadler_nest/indentation.rb | 4 ++-- 19 files changed, 26 insertions(+), 21 deletions(-) mode change 100644 => 100755 examples/configs/eager_print.rb mode change 100644 => 100755 examples/configs/indent_anchor.rb mode change 100644 => 100755 examples/configs/trim_trailing_whitespaces.rb mode change 100644 => 100755 examples/configs/upsize_stack.rb mode change 100644 => 100755 examples/oppen/oppen.rb mode change 100644 => 100755 examples/oppen_and_wadler_customization/new_line.rb mode change 100644 => 100755 examples/oppen_and_wadler_customization/space.rb mode change 100644 => 100755 examples/oppen_and_wadler_customization/whitespace.rb mode change 100644 => 100755 examples/oppen_and_wadler_customization/width.rb mode change 100644 => 100755 examples/show_print_commands/show_print_commands.rb mode change 100644 => 100755 examples/wadler_break_and_breakable/break.rb mode change 100644 => 100755 examples/wadler_break_and_breakable/breakable.rb mode change 100644 => 100755 examples/wadler_break_and_breakable/line_continuation.rb mode change 100644 => 100755 examples/wadler_group/consistent.rb mode change 100644 => 100755 examples/wadler_group/delimiters.rb mode change 100644 => 100755 examples/wadler_group/inconsistent.rb mode change 100644 => 100755 examples/wadler_group/indentation.rb mode change 100644 => 100755 examples/wadler_nest/delimiters.rb mode change 100644 => 100755 examples/wadler_nest/indentation.rb diff --git a/examples/configs/eager_print.rb b/examples/configs/eager_print.rb old mode 100644 new mode 100755 index d2334a3..877b535 --- a/examples/configs/eager_print.rb +++ b/examples/configs/eager_print.rb @@ -24,7 +24,7 @@ title 'With eager printing:' puts printer_with_config.output # abc defghi -# jkl +# jkl puts '' diff --git a/examples/configs/indent_anchor.rb b/examples/configs/indent_anchor.rb old mode 100644 new mode 100755 index 09c9fc7..6eb2b84 --- a/examples/configs/indent_anchor.rb +++ b/examples/configs/indent_anchor.rb @@ -5,17 +5,19 @@ printer_with_config = Oppen::Wadler.new( config: Oppen::Config.new(indent_anchor: :current_offset), + indent: 4, width: 13, ) printer_no_config = Oppen::Wadler.new( config: Oppen::Config.new(indent_anchor: :end_of_previous_line), + indent: 4, width: 13, ) test_block = ->(printer) { printer.text 'And she said:' - printer.group(4) { - printer.group(4) { + printer.group { + printer.group { printer.break printer.text 'Hello, World!' } diff --git a/examples/configs/trim_trailing_whitespaces.rb b/examples/configs/trim_trailing_whitespaces.rb old mode 100644 new mode 100755 diff --git a/examples/configs/upsize_stack.rb b/examples/configs/upsize_stack.rb old mode 100644 new mode 100755 diff --git a/examples/oppen/oppen.rb b/examples/oppen/oppen.rb old mode 100644 new mode 100755 diff --git a/examples/oppen_and_wadler_customization/new_line.rb b/examples/oppen_and_wadler_customization/new_line.rb old mode 100644 new mode 100755 diff --git a/examples/oppen_and_wadler_customization/space.rb b/examples/oppen_and_wadler_customization/space.rb old mode 100644 new mode 100755 diff --git a/examples/oppen_and_wadler_customization/whitespace.rb b/examples/oppen_and_wadler_customization/whitespace.rb old mode 100644 new mode 100755 diff --git a/examples/oppen_and_wadler_customization/width.rb b/examples/oppen_and_wadler_customization/width.rb old mode 100644 new mode 100755 diff --git a/examples/show_print_commands/show_print_commands.rb b/examples/show_print_commands/show_print_commands.rb old mode 100644 new mode 100755 index 1eaa69e..3217df1 --- a/examples/show_print_commands/show_print_commands.rb +++ b/examples/show_print_commands/show_print_commands.rb @@ -4,19 +4,21 @@ printer = Oppen::Wadler.new -printer.group(2) { +printer.group(indent: 2) { printer.text 'Hello, World!' - printer.nest(4) { + printer.nest(indent: 4) { printer.break printer.text 'GoodBye, World!' } } puts printer.show_print_commands -# out.group(:consistent, indent: 2) { -# out.text("Hello, World!", width: 13) -# out.nest(indent: 4) { -# out.break(line_continuation: "") +# out.group(:consistent, indent: 0) { +# out.group(:consistent, indent: 2) { +# out.text("Hello, World!", width: 13) +# out.nest(indent: 4) { +# out.break(line_continuation: "") +# } +# out.text("GoodBye, World!", width: 15) # } -# out.text("GoodBye, World!", width: 15) # } diff --git a/examples/wadler_break_and_breakable/break.rb b/examples/wadler_break_and_breakable/break.rb old mode 100644 new mode 100755 diff --git a/examples/wadler_break_and_breakable/breakable.rb b/examples/wadler_break_and_breakable/breakable.rb old mode 100644 new mode 100755 index 07830f5..2197592 --- a/examples/wadler_break_and_breakable/breakable.rb +++ b/examples/wadler_break_and_breakable/breakable.rb @@ -8,7 +8,7 @@ printer = Oppen::Wadler.new(width: 40) # See `examples/wadler_group/inconsistent.rb` for more infos about `:inconsistent` -printer.group(0, '', '', :inconsistent) { +printer.group(:inconsistent) { printer.text 'Hello, World!' printer.breakable printer.text '(still fits on the line)' diff --git a/examples/wadler_break_and_breakable/line_continuation.rb b/examples/wadler_break_and_breakable/line_continuation.rb old mode 100644 new mode 100755 diff --git a/examples/wadler_group/consistent.rb b/examples/wadler_group/consistent.rb old mode 100644 new mode 100755 index a946b66..6607a80 --- a/examples/wadler_group/consistent.rb +++ b/examples/wadler_group/consistent.rb @@ -8,7 +8,7 @@ printer = Oppen::Wadler.new(width: 999_999) # Groups are consistent by default. -printer.group { +printer.consistent { # alias for printer.group(:consistent) and same as printer.group printer.text 'Hello, World!' printer.breakable printer.text 'How are you doing?' diff --git a/examples/wadler_group/delimiters.rb b/examples/wadler_group/delimiters.rb old mode 100644 new mode 100755 index fc2c22a..a31caf9 --- a/examples/wadler_group/delimiters.rb +++ b/examples/wadler_group/delimiters.rb @@ -2,10 +2,10 @@ require_relative '../helper' -printer = Oppen::Wadler.new(width: 999_999) +printer = Oppen::Wadler.new(indent: 2, width: 999_999) # Groups have no delimiters by default. -printer.group(2, '<<', '>>') { +printer.group(delim: %w[<< >>]) { printer.text 'Hello, World!' printer.break printer.text 'How are you doing?' @@ -16,6 +16,7 @@ } puts printer.output +# # <>') { +printer.nest(delim: %w[<< >>]) { printer.text 'Hello, World!' printer.break printer.text 'How are you doing?' diff --git a/examples/wadler_nest/indentation.rb b/examples/wadler_nest/indentation.rb old mode 100644 new mode 100755 index 525e45d..5e0f9e0 --- a/examples/wadler_nest/indentation.rb +++ b/examples/wadler_nest/indentation.rb @@ -2,9 +2,9 @@ require_relative '../helper' -printer = Oppen::Wadler.new(width: 999_999) +printer = Oppen::Wadler.new(indent: 4, width: 999_999) -printer.nest(4) { +printer.nest { printer.text 'Hello, World!' printer.break printer.text 'How are you doing?' From be00905a16fbbc3b39cbab7a38fe1cf1b12f80fe Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Tue, 7 Jan 2025 15:39:58 +0100 Subject: [PATCH 22/23] ci: run examples to check if they break --- .github/workflows/test.yml | 2 ++ justfile | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2624ef..9982905 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,6 +37,8 @@ jobs: run: just lint - name: test run: just test + - name: examples + run: just examples - name: doc run: just doc - name: package gem diff --git a/justfile b/justfile index f2105a0..f7b5eb8 100644 --- a/justfile +++ b/justfile @@ -30,6 +30,12 @@ doc: doc-stats: bundle exec yard stats --list-undoc +[group('test')] +examples: + find examples/ -mindepth 2 -type f -name "*.rb" | while IFS= read -r file; do \ + bundle exec ruby "$file" > /dev/null || exit 1; \ + done + [group('publish')] gem: mkdir -p {{PKG_OUT}} From 60a44e3aec68a62380d75098bb30db2be953dac2 Mon Sep 17 00:00:00 2001 From: Amine Mike Date: Wed, 8 Jan 2025 10:44:36 +0100 Subject: [PATCH 23/23] examples: add entries for surround and separate --- examples/wadler_utils/separate.rb | 17 +++++++++++++++++ examples/wadler_utils/surround.rb | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100755 examples/wadler_utils/separate.rb create mode 100755 examples/wadler_utils/surround.rb diff --git a/examples/wadler_utils/separate.rb b/examples/wadler_utils/separate.rb new file mode 100755 index 0000000..8d52ef4 --- /dev/null +++ b/examples/wadler_utils/separate.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative '../helper' + +# You might want to also take a look at the `lines` and `concat` methods. + +printer = Oppen::Wadler.new(width: 10) + +printer.separate((1..10).map(&:to_s), ',', break_type: :inconsistent, indent: 2) { |i| + printer.text i +} + +puts printer.output +# 1, 2, 3, +# 4, 5, 6, +# 7, 8, 9, +# 10 diff --git a/examples/wadler_utils/surround.rb b/examples/wadler_utils/surround.rb new file mode 100755 index 0000000..c06a713 --- /dev/null +++ b/examples/wadler_utils/surround.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative '../helper' + +# You might want to also take a look at the `parens`, `parens_break_both`, `angles`, ... methods. + +printer = Oppen::Wadler.new(width: 10) + +printer.surround('<<', '>>', indent: 2, lft_force_break: true, rgt_force_break: true) { + printer.text '42' +} + +puts printer.output +# << +# 42 +# >>