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/.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 75f044c..f679e6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,23 @@
## [unreleased]
+- 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.
+ - 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`.
+ - 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.
+ - add `surround` as a glorified `group` that add breakables next to delimiters.
+
## v0.9.8 (30-12-2024)
- Oppen now supports Ruby 3.0+. It used to be restricted to Ruby 3.2+.
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
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
index 4412031..b1ba64f
--- 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
old mode 100644
new mode 100755
index 37e30bb..0bd8a5e
--- 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_gen: 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
old mode 100644
new mode 100755
index f21734c..2348028
--- 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
old mode 100644
new mode 100755
index 440e914..8c4814f
--- 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
old mode 100644
new mode 100755
index 2a42568..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(2, "", "", :consistent) {
-# out.text("Hello, World!", width: 13)
-# out.nest(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?'
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
+# >>
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}}
diff --git a/lib/oppen.rb b/lib/oppen.rb
index 5897ce3..1a0c3c5 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!'
# }
@@ -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/oppen/mixins.rb b/lib/oppen/mixins.rb
index 33550b6..2f30bb5 100644
--- a/lib/oppen/mixins.rb
+++ b/lib/oppen/mixins.rb
@@ -24,15 +24,14 @@ 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.base_indent(base_indent)
- indent = 2
+ printer = Oppen::Wadler.new(base_indent: base_indent, indent: 2, width: width)
handle_break_token = ->(token) {
if token.offset.positive?
- printer.text "#{printer_name}.nest(#{token.offset}, '', '') {"
- printer.nest_open indent
- printer.break
+ printer
+ .text("#{printer_name}.nest(indent: #{token.offset}) {")
+ .nest_open
+ .break
end
printer.text(
@@ -45,11 +44,7 @@ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens.
end,
)
- if token.offset.positive?
- printer.nest_close indent
- printer.break
- printer.text '}'
- end
+ printer.nest_close.break.text '}' if token.offset.positive?
}
tokens.each_with_index do |token, idx|
@@ -59,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.offset}, '', '', #{token.break_type.inspect}) {"
- printer.nest_open indent
+ printer
+ .text("#{printer_name}.group(#{token.break_type.inspect}, indent: #{token.offset}) {")
+ .nest_open
in Token::End
- printer.nest_close indent
- 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 de28274..2ead099 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
@@ -29,13 +29,17 @@ 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]
+ # the default indentation amount for {group} and {nest}.
# @param new_line [String]
# 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.
@@ -44,27 +48,26 @@ class Wadler
# @param width [Integer] maximum line width desired.
#
# @see Token::Whitespace
- def initialize(config: Config.wadler, new_line: "\n",
- out: StringIO.new, space: ' ',
+ def initialize(base_indent: 0, config: Config.wadler, indent: 0, new_line: "\n",
+ out: StringIO.new, space_gen: ' ',
whitespace: ' ', width: 80)
@config = config
- @current_indent = 0
- @space = space
- @width = width
+ @current_indent = base_indent
+ @indent = indent
@new_line = new_line
@out = out
+ @space_gen = space_gen
@tokens = []
@whitespace = whitespace
+ @width = width
end
# Add missing {Token::Begin}, {Token::End} or {Token::EOF}.
#
# @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
@@ -77,7 +80,7 @@ def output
tokens: tokens,
new_line: new_line,
config: config,
- space: space,
+ space: space_gen,
out: out,
width: width,
)
@@ -96,13 +99,11 @@ def output
#
# @example
# out = Oppen::Wadler.new
- # out.group {
- # out.text('Hello World!')
- # }
+ # out.text('Hello World!')
# out.show_print_commands(out_name: 'out')
#
# # =>
- # # out.group(0, "", "", :consistent) {
+ # # out.group(:consistent, indent: 0) {
# # out.text("Hello World!", width: 12)
# # }
#
@@ -114,26 +115,57 @@ 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(2, '{', '}') {
- # out.break
- # out.text 'b'
- # }
- # out.output
+ # out
+ # .text('a')
+ # .group(indent: 2, delim: '|') {
+ # out.break.text 'b'
+ # }
+ # puts out.output
+ #
+ # # =>
+ # # a
+ # # |
+ # # b
+ # # |
+ #
+ # @example 1 Delimiter in Array
+ # out = Oppen::Wadler.new
+ # out
+ # .text('a')
+ # .group(indent: 2, delim: ['|']) {
+ # out.break.text 'b'
+ # }
+ # puts out.output
+ #
+ # # =>
+ # # a
+ # # |
+ # # b
+ #
+ # @example 2 Delimiters
+ # out = Oppen::Wadler.new
+ # out
+ # .text('a')
+ # .group(indent: 2, delim: %i[{ }]) {
+ # out.break.text 'b'
+ # }
+ # puts out.output
#
# # =>
# # a
@@ -143,14 +175,10 @@ def show_print_commands(**kwargs)
#
# @example Consistent Breaking
# out = Oppen::Wadler.new
- # out.group(0, '', '', :consistent) {
- # out.text 'a'
- # out.break
- # out.text 'b'
- # out.breakable
- # out.text 'c'
+ # out.group(:consistent) {
+ # out.text('a').break.text('b').breakable.text('c')
# }
- # out.output
+ # puts out.output
#
# # =>
# # a
@@ -159,27 +187,26 @@ def show_print_commands(**kwargs)
#
# @example Inconsistent Breaking
# out = Oppen::Wadler.new
- # out.group(0, '', '', :inconsistent) {
- # out.text 'a'
- # out.break
- # out.text 'b'
- # out.breakable
- # out.text 'c'
+ # out.group(:inconsistent) {
+ # out.text('a').break.text('b').breakable.text('c')
# }
- # out.output
+ # puts out.output
#
# # =>
# # a
# # b c
#
- # @return [Nil]
+ # @return [self]
#
# @see Oppen.begin_consistent
# @see Oppen.begin_inconsistent
- def group(indent = 0, open_obj = '', close_obj = '',
- break_type = :consistent)
- 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
@@ -189,19 +216,31 @@ def group(indent = 0, 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
+
+ self
+ end
+
+ # An alias for `group(:consistent, ...)`
+ def consistent(...)
+ group(:consistent, ...)
+ end
+
+ # An alias for `group(:inconsistent, ...)`
+ def inconsistent(...)
+ group(:inconsistent, ...)
end
# Create a new non-strict {group}.
@@ -218,24 +257,24 @@ def group(indent = 0, 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(2, '{', '}') {
- # out.text 'a'
- # out.break
- # out.text 'b'
+ # out.nest(delim: %i[{ }], indent: 2) {
+ # out.text('a').break.text('b')
# }
- # out.output
+ # puts out.output
#
# # =>
# # {
@@ -243,15 +282,19 @@ def group(indent = 0, open_obj = '', close_obj = '',
# # b
# # }
#
- # @return [Nil]
- def nest(indent, open_obj = '', close_obj = '')
- raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
- if open_obj.nil? || close_obj.nil?
+ # @return [self]
+ 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
@@ -261,10 +304,12 @@ def nest(indent, open_obj = '', close_obj = '')
@current_indent -= indent
end
- return if close_obj.empty?
+ if !rgt.empty?
+ self.break
+ text rgt
+ end
- self.break
- text(close_obj)
+ self
end
# Create a new text element.
@@ -272,7 +317,7 @@ def nest(indent, open_obj = '', close_obj = '')
# @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)
@@ -284,6 +329,7 @@ def text(value, width: value.length)
else
tokens << Oppen.string(value, width: width)
end
+ self
end
# Create a new breakable element.
@@ -295,11 +341,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.
@@ -314,55 +361,424 @@ def breakable(str = ' ', line_continuation: '', width: str.length)
# out.text 'b'
# out.break line_continuation: '#'
# out.text 'c'
- # out.output
+ # puts out.output
#
# # =>
# # a
# # b#
# # c
#
- # @return [Nil]
+ # @return [self]
def break(line_continuation: '')
tokens << Oppen.line_break(line_continuation: line_continuation, offset: current_indent)
+ 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
+ # 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
- # Set a base indenetaion level for the printer.
+ # Produce a separated list.
#
- # @param indent [Integer]
- # the amount of indentation.
+ # @example Consistent Breaking
+ # puts out.separate((1..3).map(&:to_s), ',') { |i| out.text i}
#
- # @return [Nil]
- def base_indent(indent = 0)
- @current_indent = indent if !indent.nil?
+ # # =>
+ # # 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|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]
+ # 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, indent: 0) {
+ body.()
+ }
+ }
+ .end
+ }
+ .when(indent) { |body|
+ nest(indent: indent.is_a?(Integer) ? indent : @indent) {
+ body.()
+ }
+ }.end
+ breakable('', line_continuation: line_continuation) if !line_continuation.empty? && !break_type
+
+ self
+ end
+
+ # A shorhand for `text ' '`.
+ #
+ # @return [self]
+ 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 Convenience Methods Built On {separate}
+
+ # 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
+
+ # @!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.
#
- # @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 [Nil]
+ # @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
# Close a group.
#
- # @return [Nil]
- def group_close(_)
+ # @return [self]
+ def group_close
tokens << Oppen.end
+ self
end
# Open a consistent group and add indent amount.
@@ -370,8 +786,8 @@ def group_close(_)
# @param indent [Integer]
# the amount of indentation of the group.
#
- # @return [Nil]
- def indent_open(indent)
+ # @return [self]
+ def indent_open(indent: @indent)
@current_indent += indent
group_open
end
@@ -381,10 +797,10 @@ def indent_open(indent)
# @param indent [Integer]
# the amount of indentation of the group.
#
- # @return [Nil]
- def indent_close(group, indent)
+ # @return [self]
+ def indent_close(indent: @indent)
@current_indent -= indent
- group_close(group)
+ group_close
end
# Open a nest by adding indent.
@@ -392,9 +808,10 @@ def indent_close(group, indent)
# @param indent [Integer]
# the amount of indentation of the nest.
#
- # @return [Nil]
- def nest_open(indent)
+ # @return [self]
+ def nest_open(indent: @indent)
@current_indent += indent
+ self
end
# Close a nest by subtracting indent.
@@ -402,11 +819,43 @@ def nest_open(indent)
# @param indent [Integer]
# the amount of indentation of the nest.
#
- # @return [Nil]
- def nest_close(indent)
+ # @return [self]
+ def nest_close(indent: @indent)
@current_indent -= indent
+ self
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/customization_test.rb b/test/customization_test.rb
index 39c832c..33400bf 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
@@ -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,21 +146,21 @@ 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) { '*******' })
- printer.group(0) {
+ 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
printer.text 'World'
@@ -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,16 +216,16 @@ def customization_build_output(printer)
end
describe 'Nest delimiter tests' do
- def nest_delimiter_build_output(printer, open_obj, close_obj)
- printer.group(0) {
- printer.group(2) {
+ def nest_delimiter_build_output(printer, delim)
+ 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(delim: delim, indent: 2) {
+ printer.group {
printer.text 'Hello'
printer.breakable(', ')
printer.text 'World!'
@@ -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)
- printer.group(0) {
- printer.group(2) {
+ def group_delimiter_build_output(printer, delim)
+ 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(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(0) {
- printer.text('[')
- printer.group(2, '', '', break_type) {
- printer.breakable('')
- printer.text('1')
- printer.breakable(', ', line_continuation: line_continuation)
- printer.text('2')
- printer.breakable(', ', line_continuation: line_continuation)
- printer.text('3')
+ 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.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/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/lib.rb b/test/lib.rb
index dd32943..6cda1dc 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, 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/token_to_wadler_test.rb b/test/token_to_wadler_test.rb
index 41cb6af..9ac3327 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,8 +128,10 @@
}
},
expected: <<~LANG,
- printer.group(0, '', '', :consistent) {
- printer.text("Hello World!", width: 12)
+ printer.group(:consistent, indent: 0) {
+ printer.group(:consistent, indent: 0) {
+ printer.text("Hello World!", width: 12)
+ }
}
LANG
@@ -137,17 +139,19 @@
{
title: 'displays a group token with arguments',
block: proc { |printer|
- printer.group(2, '{', '}', :inconsistent) {
+ printer.group(:inconsistent, delim: %w[{ }], indent: 2) {
printer.text('Hello World!')
}
},
expected: <<~LANG,
- printer.group(2, '', '', :inconsistent) {
- 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
@@ -166,7 +170,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,12 +200,14 @@
}
},
expected: <<~LANG,
- printer.group(0, '', '', :consistent) {
- printer.group(0, '', '', :consistent) {
- printer.group(0, '', '', :consistent) {
- printer.group(0, '', '', :consistent) {
- printer.group(0, '', '', :consistent) {
- printer.text("Hello World!", width: 12)
+ 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)
+ }
}
}
}
@@ -213,19 +219,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 +241,23 @@
{
title: 'displays a nest token with arguments',
block: proc { |printer|
- printer.nest(2, '{', '}') {
+ printer.nest(delim: %w[{ }], 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 +269,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 +280,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 +295,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,14 +308,16 @@
}
},
expected: <<~LANG,
- printer.group(2, '', '', :consistent) {
- printer.group(2, '', '', :consistent) {
- printer.nest(4, '', '') {
- printer.breakable(" ", width: 1, line_continuation: "")
- }
- printer.text("Hello World!", width: 12)
- printer.nest(4, '', '') {
- printer.break(line_continuation: "")
+ printer.group(:consistent, indent: 0) {
+ 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.break(line_continuation: "")
+ }
}
}
}
diff --git a/test/wadler_separate_test.rb b/test/wadler_separate_test.rb
new file mode 100644
index 0000000..3b09c25
--- /dev/null
+++ b/test/wadler_separate_test.rb
@@ -0,0 +1,167 @@
+# frozen_string_literal: true
+
+require_relative 'lib'
+
+describe 'separate' do
+ [
+ {
+ 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
+ },
+ {
+ 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
+end
+
+describe 'helpers built on separate' do
+ [
+ {
+ 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
new file mode 100644
index 0000000..843e20d
--- /dev/null
+++ b/test/wadler_surround_test.rb
@@ -0,0 +1,281 @@
+# frozen_string_literal: true
+
+require_relative 'lib'
+
+describe 'surround' do
+ [
+ {
+ 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
+ },
+ {
+ 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
+ },
+ {
+ 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'
+ }
+ },
+ 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
+ },
+ {
+ 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
+ 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|
+ [
+ {
+ 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
+
+ [
+ [:backticks, '`'],
+ [:quote_double, '"'],
+ [:quote_single, "'"],
+ ].each do |name, char|
+ [
+ {
+ 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
diff --git a/test/wadler_test.rb b/test/wadler_test.rb
index 9ed5222..daa9206 100644
--- a/test/wadler_test.rb
+++ b/test/wadler_test.rb
@@ -1,39 +1,26 @@
# frozen_string_literal: true
-require 'prettyprint'
-
require_relative 'lib'
-def check_roundtrip(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
describe 'must work like ruby\'s PrettyPrint library' do
builder_block = proc { |out|
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'
}
}
@@ -76,7 +63,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
@@ -94,25 +81,26 @@ def initialize(string, *children)
def show(out)
out.group {
- out.text string
- out.nest(string.length) {
- unless children.empty?
- out.text '['
- out.nest(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
@@ -159,7 +147,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 +160,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 +234,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 +244,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 +262,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 +280,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 +398,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 +430,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 +439,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 +551,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 +572,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 +588,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 +607,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 +626,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 +648,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 +664,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 +679,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 +694,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
@@ -715,4 +703,30 @@ 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
+
+ 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