oppen 0.9.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +99 -28
- data/lib/oppen/mixins.rb +11 -16
- data/lib/oppen/print_stack.rb +1 -1
- data/lib/oppen/printer.rb +1 -1
- data/lib/oppen/token.rb +1 -1
- data/lib/oppen/version.rb +1 -1
- data/lib/oppen.rb +13 -13
- data/lib/wadler/print.rb +560 -111
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e53fd2f0875228c91da4aaa2259d7b1bf18e0561284513e57181cb6c260ba610
|
|
4
|
+
data.tar.gz: 82c1776d841d6eef2d2071fc923b4864e23e2e68e079fa6caec36b7cca23ffbc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 87f3293a723b93c82532045aafe0dd699b6d7ac754893d51ff338448550187f27d75d7bea12fd4035be3f765de660605c56ac431460785387b47b6955b2805bb
|
|
7
|
+
data.tar.gz: 9928e9536e2fb193150fb32d000abf5e5b0017cbbecab050b2b064f14847f72b24ac573ea4779d2fe0bd2f5ac4f5b1ef04a7b1341bdc36e9b74bd652aa3b43ce
|
data/README.md
CHANGED
|
@@ -22,16 +22,12 @@ transition from `ruby/prettyprint` to this gem.
|
|
|
22
22
|
|
|
23
23
|
`Wadler` is implemented on top of `Oppen`, and it provides more options than
|
|
24
24
|
`ruby/prettyprint`, notably:
|
|
25
|
-
1. Consistent and inconsistent breaking.
|
|
26
|
-
1. Explicit breaking, which is achievable in `ruby/prettyprint` with some
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
|
|
34
|
-
A few examples of the API usage can be found in [examples/](examples/README.md).
|
|
25
|
+
1. [Consistent](examples/wadler_group/consistent.rb) and [inconsistent](examples/wadler_group/inconsistent.rb) breaking.
|
|
26
|
+
1. [Explicit breaking](examples/wadler_break_and_breakable/break.rb), which is achievable in `ruby/prettyprint` with some monkeypatching.
|
|
27
|
+
1. [Trimming of trailing whitespaces](examples/oppen_and_wadler_customization/whitespace.rb).
|
|
28
|
+
1. [Display a `String` on line break](examples/wadler_break_and_breakable/line_continuation.rb).
|
|
29
|
+
1. A bunch of helper methods to simplify common patterns like [surrounding](examples/wadler_utils/surround.rb) or
|
|
30
|
+
[separating](examples/wadler_utils/surround.rb) tokens.
|
|
35
31
|
|
|
36
32
|
## Oppen vs Wadler
|
|
37
33
|
|
|
@@ -39,34 +35,109 @@ A few examples of the API usage can be found in [examples/](examples/README.md).
|
|
|
39
35
|
and it's not calling ruby's `prettyprint`.
|
|
40
36
|
|
|
41
37
|
Both implementations have their use cases:
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
- Oppen gives more control over tokens sent to the printer.
|
|
39
|
+
- Wadler gives a more _"functional"_ API, which is far nicer to work with.
|
|
44
40
|
|
|
45
41
|
That being said, both APIs in this gem can achieve the same results, especially
|
|
46
42
|
on consistent and inconsistent breaking.
|
|
47
43
|
|
|
48
|
-
##
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
## Oppen's API Example
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
tokens = [
|
|
48
|
+
Oppen.begin_inconsistent,
|
|
49
|
+
Oppen.string('Hello'),
|
|
50
|
+
Oppen.break(', '),
|
|
51
|
+
Oppen.string('World!'),
|
|
52
|
+
Oppen.line_break,
|
|
53
|
+
Oppen.string('How are you doing?'),
|
|
54
|
+
Oppen.end,
|
|
55
|
+
Oppen.eof,
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
puts Oppen.print(tokens:)
|
|
59
|
+
# Hello, World!
|
|
60
|
+
# How are you doing?
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Wadler's API Example
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
out = Oppen::Wadler.new(width: 20)
|
|
67
|
+
|
|
68
|
+
out.group(indent: 2) {
|
|
69
|
+
out.group {
|
|
70
|
+
out.text('def').breakable.text('foo')
|
|
71
|
+
}
|
|
72
|
+
out.parens_break_none {
|
|
73
|
+
out.separate(%w[bar baz bat qux], ',', break_type: :inconsistent) { |param|
|
|
74
|
+
out.text(param)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
out.group(indent: 2) {
|
|
79
|
+
out
|
|
80
|
+
.break
|
|
81
|
+
.nest(indent: 2) {
|
|
82
|
+
out
|
|
83
|
+
.text('puts')
|
|
84
|
+
.breakable(line_continuation: ' \\')
|
|
85
|
+
.text('42')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
out.break.text('end')
|
|
89
|
+
|
|
90
|
+
puts out.output
|
|
91
|
+
# def foo(bar, baz,
|
|
92
|
+
# bat, qux)
|
|
93
|
+
# puts \
|
|
94
|
+
# 42
|
|
95
|
+
# end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## More Examples
|
|
99
|
+
|
|
100
|
+
An easy way to add colors to the output on the terminal is wrap `oppen` and expose your own vocabulary:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
require 'colored'
|
|
104
|
+
class ColoredTty
|
|
105
|
+
KW_PALETTE = { Hello: :red, World: :green }.freeze
|
|
106
|
+
def initialize(...) = @out = Oppen::Wadler.new(...)
|
|
107
|
+
def breakable(...) = @out.breakable(...) && self
|
|
108
|
+
def keyword(value, width: value.length) = @out.text(value.send(KW_PALETTE[value.to_sym] || :white), width:) && self
|
|
109
|
+
def output = @out.output
|
|
110
|
+
def text(...) = @out.text(...) && self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
out = ColoredTty.new(width: 12)
|
|
114
|
+
out.keyword('Hello').breakable.text('World')
|
|
115
|
+
|
|
116
|
+
puts out.output
|
|
117
|
+
# \e[31mHello\e[0m World
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The same idea can be applied an adapted to make an HTML printer; all you need to take care of is the correct width of the text to preserve the width of the text and get an output identical to that of the tty colored printer.
|
|
121
|
+
|
|
122
|
+
Check out the [examples/](examples/README.md) folder for more details on how to use the Oppen and Wadler APIs.
|
|
123
|
+
|
|
124
|
+
## Difference With Oppen's Original Algorithm
|
|
125
|
+
|
|
126
|
+
1. We took the liberty to rename functions to make the API more modern and closer to
|
|
53
127
|
what we expect when writing Ruby code. All correspondences with the algorithm
|
|
54
128
|
as described in Oppen's paper are noted in the comments of classes and methods.
|
|
55
129
|
1. We do not raise exceptions when we overflow the margin. The only exceptions
|
|
56
130
|
that we raise indicate a bug in the implementation. Please report them.
|
|
131
|
+
1. The stacks described by the algorithm do not have a fixed size in our
|
|
132
|
+
implementation: we upsize them when they are full.
|
|
133
|
+
1. We can optionally trim trailing whitespaces (this feature is on by default for the `Wadler` API).
|
|
134
|
+
1. We added support for an additional new line anchors, see [examples/configs/indent_anchor.rb](examples/configs/indent_anchor.rb).
|
|
135
|
+
1. We added support for eager printing of `groups`; see [examples/configs/eager_print.rb](examples/configs/eager_print.rb).
|
|
136
|
+
1. We introduced a new token (`Whitespace`) and added more customizations to one of the originals (`Break`).
|
|
57
137
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Oppen's algorithm and `ruby/prettyprint` do not have the same starting positions
|
|
61
|
-
for a group's indentation. That's why you need to pay particular attention to
|
|
62
|
-
calls for `nest`; you might want to decrease them by `1` if you care about keeping
|
|
63
|
-
the same behavior.
|
|
64
|
-
|
|
65
|
-
This is what we do in our test suite to verify the correspondence of the `Wadler`
|
|
66
|
-
API and the `ruby/prettyprint`. We decided to shift the burden to the user because
|
|
67
|
-
we think that the deicision taken by `ruby/prettyprint` does not suit us.
|
|
138
|
+
For more insight on how Oppen's algorithm works, check out [docs/oppen_algorithm.md](docs/oppen_algorithm.md).
|
|
68
139
|
|
|
69
|
-
## Related
|
|
140
|
+
## Related Projects
|
|
70
141
|
|
|
71
142
|
1. [`ruby/prettyprint`](https://github.com/ruby/prettyprint)
|
|
72
143
|
1. [rustc implementation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_pretty/pp/index.html)
|
data/lib/oppen/mixins.rb
CHANGED
|
@@ -24,15 +24,14 @@ module Oppen
|
|
|
24
24
|
|
|
25
25
|
# @return [String]
|
|
26
26
|
def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens.length * 3)
|
|
27
|
-
printer = Oppen::Wadler.new(
|
|
28
|
-
printer.base_indent(base_indent)
|
|
29
|
-
indent = 2
|
|
27
|
+
printer = Oppen::Wadler.new(base_indent:, indent: 2, width:)
|
|
30
28
|
|
|
31
29
|
handle_break_token = ->(token) {
|
|
32
30
|
if token.offset.positive?
|
|
33
|
-
printer
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
printer
|
|
32
|
+
.text("#{printer_name}.nest(indent: #{token.offset}) {")
|
|
33
|
+
.nest_open
|
|
34
|
+
.break
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
printer.text(
|
|
@@ -45,11 +44,7 @@ module Oppen
|
|
|
45
44
|
end,
|
|
46
45
|
)
|
|
47
46
|
|
|
48
|
-
if token.offset.positive?
|
|
49
|
-
printer.nest_close indent
|
|
50
|
-
printer.break
|
|
51
|
-
printer.text '}'
|
|
52
|
-
end
|
|
47
|
+
printer.nest_close.break.text '}' if token.offset.positive?
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
tokens.each_with_index do |token, idx|
|
|
@@ -59,17 +54,17 @@ module Oppen
|
|
|
59
54
|
in Token::Break
|
|
60
55
|
handle_break_token.(token)
|
|
61
56
|
in Token::Begin
|
|
62
|
-
printer
|
|
63
|
-
|
|
57
|
+
printer
|
|
58
|
+
.text("#{printer_name}.group(#{token.break_type.inspect}, indent: #{token.offset}) {")
|
|
59
|
+
.nest_open
|
|
64
60
|
in Token::End
|
|
65
|
-
printer.nest_close
|
|
66
|
-
printer.break
|
|
67
|
-
printer.text '}'
|
|
61
|
+
printer.nest_close.break.text '}'
|
|
68
62
|
in Token::EOF
|
|
69
63
|
nil
|
|
70
64
|
end
|
|
71
65
|
printer.break if !tokens[idx + 1].is_a?(Token::End)
|
|
72
66
|
end
|
|
67
|
+
|
|
73
68
|
printer.output
|
|
74
69
|
end
|
|
75
70
|
end
|
data/lib/oppen/print_stack.rb
CHANGED
data/lib/oppen/printer.rb
CHANGED
data/lib/oppen/token.rb
CHANGED
data/lib/oppen/version.rb
CHANGED
data/lib/oppen.rb
CHANGED
|
@@ -64,8 +64,8 @@ module Oppen
|
|
|
64
64
|
# config = Oppen::Config.new(indent_anchor: :end_of_previous_line)
|
|
65
65
|
# out = Oppen::Wadler.new config:, width: 13
|
|
66
66
|
# out.text 'And she said:'
|
|
67
|
-
# out.group(4) {
|
|
68
|
-
# out.group(4) {
|
|
67
|
+
# out.group(indent: 4) {
|
|
68
|
+
# out.group(indent: 4) {
|
|
69
69
|
# out.break
|
|
70
70
|
# out.text 'Hello, World!'
|
|
71
71
|
# }
|
|
@@ -80,8 +80,8 @@ module Oppen
|
|
|
80
80
|
# config = Oppen::Config.new(indent_anchor: :current_offset)
|
|
81
81
|
# out = Oppen::Wadler.new config:, width: 13
|
|
82
82
|
# out.text 'And she said:'
|
|
83
|
-
# out.group(4) {
|
|
84
|
-
# out.group(4) {
|
|
83
|
+
# out.group(indent: 4) {
|
|
84
|
+
# out.group(indent: 4) {
|
|
85
85
|
# out.break
|
|
86
86
|
# out.text 'Hello, World!'
|
|
87
87
|
# }
|
|
@@ -123,7 +123,7 @@ module Oppen
|
|
|
123
123
|
# #
|
|
124
124
|
# # eager_print: true =>
|
|
125
125
|
# # abc defghi
|
|
126
|
-
# #
|
|
126
|
+
# # jkl
|
|
127
127
|
#
|
|
128
128
|
# @return [Boolean]
|
|
129
129
|
def eager_print? = @eager_print
|
|
@@ -154,10 +154,10 @@ module Oppen
|
|
|
154
154
|
# @return [Config]
|
|
155
155
|
def self.wadler(eager_print: true, trim_trailing_whitespaces: true, upsize_stack: true)
|
|
156
156
|
new(
|
|
157
|
-
eager_print
|
|
157
|
+
eager_print:,
|
|
158
158
|
indent_anchor: :current_offset,
|
|
159
|
-
trim_trailing_whitespaces
|
|
160
|
-
upsize_stack
|
|
159
|
+
trim_trailing_whitespaces:,
|
|
160
|
+
upsize_stack:,
|
|
161
161
|
)
|
|
162
162
|
end
|
|
163
163
|
end
|
|
@@ -172,7 +172,7 @@ module Oppen
|
|
|
172
172
|
# @return [Token::String]
|
|
173
173
|
# a new String token.
|
|
174
174
|
def self.string(value, width: value.length)
|
|
175
|
-
Token::String.new(value, width:
|
|
175
|
+
Token::String.new(value, width:)
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
# @return [Token::Whitespace] a new Whitespace token.
|
|
@@ -196,7 +196,7 @@ module Oppen
|
|
|
196
196
|
#
|
|
197
197
|
# @see Wadler#break example on `line_continuation`.
|
|
198
198
|
def self.break(str = ' ', line_continuation: '', offset: 0, width: str.length)
|
|
199
|
-
Token::Break.new(str, width
|
|
199
|
+
Token::Break.new(str, width:, line_continuation:, offset:)
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
# @param line_continuation [String]
|
|
@@ -209,7 +209,7 @@ module Oppen
|
|
|
209
209
|
#
|
|
210
210
|
# @see Wadler#break example on `line_continuation`.
|
|
211
211
|
def self.line_break(line_continuation: '', offset: 0)
|
|
212
|
-
Token::LineBreak.new(line_continuation
|
|
212
|
+
Token::LineBreak.new(line_continuation:, offset:)
|
|
213
213
|
end
|
|
214
214
|
|
|
215
215
|
# In a consistent group, the presence of a new line inside the group will
|
|
@@ -232,7 +232,7 @@ module Oppen
|
|
|
232
232
|
#
|
|
233
233
|
# @see Wadler#group
|
|
234
234
|
def self.begin_consistent(offset: 2)
|
|
235
|
-
Token::Begin.new(break_type: :consistent, offset:
|
|
235
|
+
Token::Begin.new(break_type: :consistent, offset:)
|
|
236
236
|
end
|
|
237
237
|
|
|
238
238
|
# In an inconsistent group, the presence of a new line inside the group will
|
|
@@ -251,7 +251,7 @@ module Oppen
|
|
|
251
251
|
#
|
|
252
252
|
# @see Wadler#group
|
|
253
253
|
def self.begin_inconsistent(offset: 2)
|
|
254
|
-
Token::Begin.new(break_type: :inconsistent, offset:
|
|
254
|
+
Token::Begin.new(break_type: :inconsistent, offset:)
|
|
255
255
|
end
|
|
256
256
|
|
|
257
257
|
# @return [Token::End] a new End token.
|
data/lib/wadler/print.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Oppen
|
|
|
18
18
|
attr_reader :out
|
|
19
19
|
# @return [Proc]
|
|
20
20
|
# space generator, a callable.
|
|
21
|
-
attr_reader :
|
|
21
|
+
attr_reader :space_gen
|
|
22
22
|
# @return [Array<Token>]
|
|
23
23
|
# the tokens list that is being built.
|
|
24
24
|
attr_reader :tokens
|
|
@@ -29,13 +29,17 @@ module Oppen
|
|
|
29
29
|
# maximum line width.
|
|
30
30
|
attr_reader :width
|
|
31
31
|
|
|
32
|
+
# @param base_indent [Integer]
|
|
33
|
+
# the starting indentation level for the whole printer.
|
|
32
34
|
# @param config [Config]
|
|
33
35
|
# to customize the printer's behavior.
|
|
36
|
+
# @param indent [Integer]
|
|
37
|
+
# the default indentation amount for {group} and {nest}.
|
|
34
38
|
# @param new_line [String]
|
|
35
39
|
# the new line String.
|
|
36
40
|
# @param out [Object]
|
|
37
41
|
# the output string buffer. It should have a `write` and `string` methods.
|
|
38
|
-
# @param
|
|
42
|
+
# @param space_gen [String, Proc]
|
|
39
43
|
# indentation string or a string generator.
|
|
40
44
|
# - If a `String`, spaces will be generated with the the lambda
|
|
41
45
|
# `->(n){ space * n }`, where `n` is the number of columns to indent.
|
|
@@ -44,27 +48,26 @@ module Oppen
|
|
|
44
48
|
# @param width [Integer] maximum line width desired.
|
|
45
49
|
#
|
|
46
50
|
# @see Token::Whitespace
|
|
47
|
-
def initialize(config: Config.wadler, new_line: "\n",
|
|
48
|
-
out: StringIO.new,
|
|
51
|
+
def initialize(base_indent: 0, config: Config.wadler, indent: 0, new_line: "\n",
|
|
52
|
+
out: StringIO.new, space_gen: ' ',
|
|
49
53
|
whitespace: ' ', width: 80)
|
|
50
54
|
@config = config
|
|
51
|
-
@current_indent =
|
|
52
|
-
@
|
|
53
|
-
@width = width
|
|
55
|
+
@current_indent = base_indent
|
|
56
|
+
@indent = indent
|
|
54
57
|
@new_line = new_line
|
|
55
58
|
@out = out
|
|
59
|
+
@space_gen = space_gen
|
|
56
60
|
@tokens = []
|
|
57
61
|
@whitespace = whitespace
|
|
62
|
+
@width = width
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
# Add missing {Token::Begin}, {Token::End} or {Token::EOF}.
|
|
61
66
|
#
|
|
62
67
|
# @return [Nil]
|
|
63
68
|
def add_missing_begin_and_end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
tokens << Oppen.end
|
|
67
|
-
end
|
|
69
|
+
tokens.unshift Oppen.begin_consistent(offset: 0)
|
|
70
|
+
tokens << Oppen.end
|
|
68
71
|
tokens << Oppen.eof if !tokens.last.is_a?(Oppen::Token::EOF)
|
|
69
72
|
end
|
|
70
73
|
|
|
@@ -74,12 +77,12 @@ module Oppen
|
|
|
74
77
|
def output
|
|
75
78
|
add_missing_begin_and_end
|
|
76
79
|
Oppen.print(
|
|
77
|
-
tokens
|
|
78
|
-
new_line
|
|
79
|
-
config
|
|
80
|
-
space:
|
|
81
|
-
out
|
|
82
|
-
width
|
|
80
|
+
tokens:,
|
|
81
|
+
new_line:,
|
|
82
|
+
config:,
|
|
83
|
+
space: space_gen,
|
|
84
|
+
out:,
|
|
85
|
+
width:,
|
|
83
86
|
)
|
|
84
87
|
end
|
|
85
88
|
|
|
@@ -96,13 +99,11 @@ module Oppen
|
|
|
96
99
|
#
|
|
97
100
|
# @example
|
|
98
101
|
# out = Oppen::Wadler.new
|
|
99
|
-
# out.
|
|
100
|
-
# out.text('Hello World!')
|
|
101
|
-
# }
|
|
102
|
+
# out.text('Hello World!')
|
|
102
103
|
# out.show_print_commands(out_name: 'out')
|
|
103
104
|
#
|
|
104
105
|
# # =>
|
|
105
|
-
# # out.group(
|
|
106
|
+
# # out.group(:consistent, indent: 0) {
|
|
106
107
|
# # out.text("Hello World!", width: 12)
|
|
107
108
|
# # }
|
|
108
109
|
#
|
|
@@ -114,26 +115,57 @@ module Oppen
|
|
|
114
115
|
|
|
115
116
|
# Create a new group.
|
|
116
117
|
#
|
|
117
|
-
# @param indent
|
|
118
|
+
# @param indent [Integer]
|
|
118
119
|
# indentation.
|
|
119
|
-
# @param
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
120
|
+
# @param delim [Nil|String|Symbol|Array<Nil, String, Symbol>]
|
|
121
|
+
# delimiters, to be printed at the start and the end of the group:
|
|
122
|
+
# - If it's nil, nothing will be printed
|
|
123
|
+
# - If it's a Strings or a Symbol, it will be printed at both positions.
|
|
124
|
+
# - If it's an Array of many items, the first two elements will be used
|
|
125
|
+
# for the start and end of the group.
|
|
123
126
|
# @param break_type [Token::BreakType]
|
|
124
127
|
# break type.
|
|
125
128
|
#
|
|
126
129
|
# @yield
|
|
127
130
|
# the block of text in a group.
|
|
128
131
|
#
|
|
129
|
-
# @example
|
|
132
|
+
# @example 1 String Delimiter
|
|
130
133
|
# out = Oppen::Wadler.new
|
|
131
|
-
# out
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# out.output
|
|
134
|
+
# out
|
|
135
|
+
# .text('a')
|
|
136
|
+
# .group(indent: 2, delim: '|') {
|
|
137
|
+
# out.break.text 'b'
|
|
138
|
+
# }
|
|
139
|
+
# puts out.output
|
|
140
|
+
#
|
|
141
|
+
# # =>
|
|
142
|
+
# # a
|
|
143
|
+
# # |
|
|
144
|
+
# # b
|
|
145
|
+
# # |
|
|
146
|
+
#
|
|
147
|
+
# @example 1 Delimiter in Array
|
|
148
|
+
# out = Oppen::Wadler.new
|
|
149
|
+
# out
|
|
150
|
+
# .text('a')
|
|
151
|
+
# .group(indent: 2, delim: ['|']) {
|
|
152
|
+
# out.break.text 'b'
|
|
153
|
+
# }
|
|
154
|
+
# puts out.output
|
|
155
|
+
#
|
|
156
|
+
# # =>
|
|
157
|
+
# # a
|
|
158
|
+
# # |
|
|
159
|
+
# # b
|
|
160
|
+
#
|
|
161
|
+
# @example 2 Delimiters
|
|
162
|
+
# out = Oppen::Wadler.new
|
|
163
|
+
# out
|
|
164
|
+
# .text('a')
|
|
165
|
+
# .group(indent: 2, delim: %i[{ }]) {
|
|
166
|
+
# out.break.text 'b'
|
|
167
|
+
# }
|
|
168
|
+
# puts out.output
|
|
137
169
|
#
|
|
138
170
|
# # =>
|
|
139
171
|
# # a
|
|
@@ -143,14 +175,10 @@ module Oppen
|
|
|
143
175
|
#
|
|
144
176
|
# @example Consistent Breaking
|
|
145
177
|
# out = Oppen::Wadler.new
|
|
146
|
-
# out.group(
|
|
147
|
-
# out.text
|
|
148
|
-
# out.break
|
|
149
|
-
# out.text 'b'
|
|
150
|
-
# out.breakable
|
|
151
|
-
# out.text 'c'
|
|
178
|
+
# out.group(:consistent) {
|
|
179
|
+
# out.text('a').break.text('b').breakable.text('c')
|
|
152
180
|
# }
|
|
153
|
-
# out.output
|
|
181
|
+
# puts out.output
|
|
154
182
|
#
|
|
155
183
|
# # =>
|
|
156
184
|
# # a
|
|
@@ -159,27 +187,26 @@ module Oppen
|
|
|
159
187
|
#
|
|
160
188
|
# @example Inconsistent Breaking
|
|
161
189
|
# out = Oppen::Wadler.new
|
|
162
|
-
# out.group(
|
|
163
|
-
# out.text
|
|
164
|
-
# out.break
|
|
165
|
-
# out.text 'b'
|
|
166
|
-
# out.breakable
|
|
167
|
-
# out.text 'c'
|
|
190
|
+
# out.group(:inconsistent) {
|
|
191
|
+
# out.text('a').break.text('b').breakable.text('c')
|
|
168
192
|
# }
|
|
169
|
-
# out.output
|
|
193
|
+
# puts out.output
|
|
170
194
|
#
|
|
171
195
|
# # =>
|
|
172
196
|
# # a
|
|
173
197
|
# # b c
|
|
174
198
|
#
|
|
175
|
-
# @return [
|
|
199
|
+
# @return [self]
|
|
176
200
|
#
|
|
177
201
|
# @see Oppen.begin_consistent
|
|
178
202
|
# @see Oppen.begin_inconsistent
|
|
179
|
-
def group(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
203
|
+
def group(break_type = :consistent, delim: nil, indent: @indent)
|
|
204
|
+
lft, rgt =
|
|
205
|
+
case delim
|
|
206
|
+
in nil then ['', '']
|
|
207
|
+
in String | Symbol then [delim, delim]
|
|
208
|
+
in Array then delim.values_at(0, 1).map(&:to_s)
|
|
209
|
+
end
|
|
183
210
|
|
|
184
211
|
tokens <<
|
|
185
212
|
case break_type
|
|
@@ -189,19 +216,31 @@ module Oppen
|
|
|
189
216
|
Oppen.begin_inconsistent(offset: indent)
|
|
190
217
|
end
|
|
191
218
|
|
|
192
|
-
if !
|
|
219
|
+
if !lft.empty?
|
|
193
220
|
self.break
|
|
194
|
-
text
|
|
221
|
+
text lft
|
|
195
222
|
end
|
|
196
223
|
|
|
197
224
|
yield
|
|
198
225
|
|
|
199
|
-
if !
|
|
226
|
+
if !rgt.empty?
|
|
200
227
|
self.break
|
|
201
|
-
text
|
|
228
|
+
text rgt
|
|
202
229
|
end
|
|
203
230
|
|
|
204
231
|
tokens << Oppen.end
|
|
232
|
+
|
|
233
|
+
self
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# An alias for `group(:consistent, ...)`
|
|
237
|
+
def consistent(...)
|
|
238
|
+
group(:consistent, ...)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# An alias for `group(:inconsistent, ...)`
|
|
242
|
+
def inconsistent(...)
|
|
243
|
+
group(:inconsistent, ...)
|
|
205
244
|
end
|
|
206
245
|
|
|
207
246
|
# Create a new non-strict {group}.
|
|
@@ -218,24 +257,24 @@ module Oppen
|
|
|
218
257
|
# @note a {nest} will not forcibly indent its content if the break type of
|
|
219
258
|
# the enclosing {group} is `:inconsistent`.
|
|
220
259
|
#
|
|
221
|
-
# @param
|
|
260
|
+
# @param delim [Nil|String|Symbol|Array<Nil, String, Symbol>]
|
|
261
|
+
# delimiters, to be printed at the start and the end of the group:
|
|
262
|
+
# - `nil` is always the empty string.
|
|
263
|
+
# - If it's a Strings or a Symbol, it will be printed at both positions.
|
|
264
|
+
# - If it's an Array of many items, the first two elements will be used
|
|
265
|
+
# for the start and end of the group.
|
|
266
|
+
# @param indent [Integer]
|
|
222
267
|
# indentation.
|
|
223
|
-
# @param open_obj [String]
|
|
224
|
-
# opening delimiter. A {break} is implicitly slipped after it if it's not empty.
|
|
225
|
-
# @param close_obj [String]
|
|
226
|
-
# closing delimiter. A {break} is implicitly slipped before it if it's not empty.
|
|
227
268
|
#
|
|
228
269
|
# @yield
|
|
229
270
|
# the block of text in a nest.
|
|
230
271
|
#
|
|
231
272
|
# @example
|
|
232
273
|
# out = Oppen::Wadler.new
|
|
233
|
-
# out.nest(
|
|
234
|
-
# out.text
|
|
235
|
-
# out.break
|
|
236
|
-
# out.text 'b'
|
|
274
|
+
# out.nest(delim: %i[{ }], indent: 2) {
|
|
275
|
+
# out.text('a').break.text('b')
|
|
237
276
|
# }
|
|
238
|
-
# out.output
|
|
277
|
+
# puts out.output
|
|
239
278
|
#
|
|
240
279
|
# # =>
|
|
241
280
|
# # {
|
|
@@ -243,15 +282,19 @@ module Oppen
|
|
|
243
282
|
# # b
|
|
244
283
|
# # }
|
|
245
284
|
#
|
|
246
|
-
# @return [
|
|
247
|
-
def nest(
|
|
248
|
-
|
|
249
|
-
|
|
285
|
+
# @return [self]
|
|
286
|
+
def nest(delim: nil, indent: @indent)
|
|
287
|
+
lft, rgt =
|
|
288
|
+
case delim
|
|
289
|
+
in nil then ['', '']
|
|
290
|
+
in String | Symbol then [delim, delim]
|
|
291
|
+
in Array then delim.values_at(0, 1).map(&:to_s)
|
|
292
|
+
end
|
|
250
293
|
|
|
251
294
|
@current_indent += indent
|
|
252
295
|
|
|
253
|
-
if !
|
|
254
|
-
text
|
|
296
|
+
if !lft.empty?
|
|
297
|
+
text lft
|
|
255
298
|
self.break
|
|
256
299
|
end
|
|
257
300
|
|
|
@@ -261,10 +304,12 @@ module Oppen
|
|
|
261
304
|
@current_indent -= indent
|
|
262
305
|
end
|
|
263
306
|
|
|
264
|
-
|
|
307
|
+
if !rgt.empty?
|
|
308
|
+
self.break
|
|
309
|
+
text rgt
|
|
310
|
+
end
|
|
265
311
|
|
|
266
|
-
self
|
|
267
|
-
text(close_obj)
|
|
312
|
+
self
|
|
268
313
|
end
|
|
269
314
|
|
|
270
315
|
# Create a new text element.
|
|
@@ -272,7 +317,7 @@ module Oppen
|
|
|
272
317
|
# @param value [String]
|
|
273
318
|
# the value of the token.
|
|
274
319
|
#
|
|
275
|
-
# @return [
|
|
320
|
+
# @return [self]
|
|
276
321
|
def text(value, width: value.length)
|
|
277
322
|
if config.trim_trailing_whitespaces? && value.match(/((?:#{Regexp.escape(whitespace)})+)\z/)
|
|
278
323
|
match = Regexp.last_match(1)
|
|
@@ -282,8 +327,9 @@ module Oppen
|
|
|
282
327
|
end
|
|
283
328
|
tokens << Oppen.whitespace(match)
|
|
284
329
|
else
|
|
285
|
-
tokens << Oppen.string(value, width:
|
|
330
|
+
tokens << Oppen.string(value, width:)
|
|
286
331
|
end
|
|
332
|
+
self
|
|
287
333
|
end
|
|
288
334
|
|
|
289
335
|
# Create a new breakable element.
|
|
@@ -295,11 +341,12 @@ module Oppen
|
|
|
295
341
|
# @param width [Integer]
|
|
296
342
|
# the width of the token.
|
|
297
343
|
#
|
|
298
|
-
# @return [
|
|
344
|
+
# @return [self]
|
|
299
345
|
#
|
|
300
346
|
# @see Wadler#break example on `line_continuation`.
|
|
301
347
|
def breakable(str = ' ', line_continuation: '', width: str.length)
|
|
302
|
-
tokens << Oppen.break(str, width
|
|
348
|
+
tokens << Oppen.break(str, width:, line_continuation:, offset: current_indent)
|
|
349
|
+
self
|
|
303
350
|
end
|
|
304
351
|
|
|
305
352
|
# Create a new break element.
|
|
@@ -314,55 +361,424 @@ module Oppen
|
|
|
314
361
|
# out.text 'b'
|
|
315
362
|
# out.break line_continuation: '#'
|
|
316
363
|
# out.text 'c'
|
|
317
|
-
# out.output
|
|
364
|
+
# puts out.output
|
|
318
365
|
#
|
|
319
366
|
# # =>
|
|
320
367
|
# # a
|
|
321
368
|
# # b#
|
|
322
369
|
# # c
|
|
323
370
|
#
|
|
324
|
-
# @return [
|
|
371
|
+
# @return [self]
|
|
325
372
|
def break(line_continuation: '')
|
|
326
|
-
tokens << Oppen.line_break(line_continuation
|
|
373
|
+
tokens << Oppen.line_break(line_continuation:, offset: current_indent)
|
|
374
|
+
self
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# A convenient way to avoid breaking chains of calls.
|
|
378
|
+
#
|
|
379
|
+
# @example
|
|
380
|
+
# out
|
|
381
|
+
# .do { fn_call(fn_arg) }
|
|
382
|
+
# .breakable
|
|
383
|
+
# .text('=')
|
|
384
|
+
# .breakable
|
|
385
|
+
# .do { fn_call(fn_arg) }
|
|
386
|
+
#
|
|
387
|
+
# @yield to execute the passed block
|
|
388
|
+
#
|
|
389
|
+
# @return [self]
|
|
390
|
+
def do
|
|
391
|
+
yield
|
|
392
|
+
self
|
|
327
393
|
end
|
|
328
394
|
|
|
329
|
-
#
|
|
395
|
+
# A means to wrap a piece of code in several ways.
|
|
396
|
+
#
|
|
397
|
+
# @example
|
|
398
|
+
# out
|
|
399
|
+
# .wrap {
|
|
400
|
+
# # all printing instructions here will be deferred.
|
|
401
|
+
# # they will be executed in `when` blocks by calling the `wrapped`.
|
|
402
|
+
# out.text(...)
|
|
403
|
+
# # ...
|
|
404
|
+
# } # This is "wrapped".
|
|
405
|
+
# .when(cond1){ |wrapped|
|
|
406
|
+
# # when cond1 is true you execute this block.
|
|
407
|
+
# out.text("before wrapped")
|
|
408
|
+
# # call the wrapped
|
|
409
|
+
# wrapped.call
|
|
410
|
+
# # and continue printing
|
|
411
|
+
# out.text("after wrapped)
|
|
412
|
+
# }
|
|
413
|
+
# .when(cond2){ |wrapped|
|
|
414
|
+
# # and you cand define many conditions.
|
|
415
|
+
# }
|
|
416
|
+
# .end
|
|
417
|
+
#
|
|
418
|
+
# @example Calling `end` is not needed if there's another call after the last `when`:
|
|
419
|
+
# out
|
|
420
|
+
# .wrap{...} # This is "wrapped".
|
|
421
|
+
# .when(cond1){ |wrapped| ... }
|
|
422
|
+
# .when(cond2){ |wrapped| ... }
|
|
423
|
+
# .text('foo')
|
|
424
|
+
#
|
|
425
|
+
# @return [Wrap]
|
|
426
|
+
def wrap(&blk)
|
|
427
|
+
Wrap.new(blk)
|
|
428
|
+
end
|
|
330
429
|
|
|
331
|
-
#
|
|
430
|
+
# Produce a separated list.
|
|
332
431
|
#
|
|
333
|
-
# @
|
|
334
|
-
#
|
|
432
|
+
# @example Consistent Breaking
|
|
433
|
+
# puts out.separate((1..3).map(&:to_s), ',') { |i| out.text i}
|
|
335
434
|
#
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
435
|
+
# # =>
|
|
436
|
+
# # 1,
|
|
437
|
+
# # 2,
|
|
438
|
+
# # 3
|
|
439
|
+
#
|
|
440
|
+
# @example Inconsistent Breaking
|
|
441
|
+
# puts out.separate((1..3).map(&:to_s), ',', break_type: :inconsistent) { |i| out.text i}
|
|
442
|
+
#
|
|
443
|
+
# # =>
|
|
444
|
+
# # 1, 2,
|
|
445
|
+
# # 3
|
|
446
|
+
#
|
|
447
|
+
# @param args [String]
|
|
448
|
+
# a list of values.
|
|
449
|
+
# @param sep [String]
|
|
450
|
+
# a separator.
|
|
451
|
+
# @param breakable [String|Nil]
|
|
452
|
+
# adds a `breakable` after the separator.
|
|
453
|
+
# @param break_pos [Symbol]
|
|
454
|
+
# whether to break :before or :after the seraparator.
|
|
455
|
+
# @param break_type [Symbol|Nil]
|
|
456
|
+
# whether the break is :consistent or :inconsistent.
|
|
457
|
+
# If nil is given, the tokens will not be surrounded by a group.
|
|
458
|
+
# @param indent [Boolean|Integer]
|
|
459
|
+
# - If `true`, indent by @indent.
|
|
460
|
+
# - If an 'Integer', indent by its value.
|
|
461
|
+
# @param force_break [Boolean]
|
|
462
|
+
# adds a `break` after the separator.
|
|
463
|
+
# @param line_continuation [String]
|
|
464
|
+
# string to display before new line.
|
|
465
|
+
#
|
|
466
|
+
# @yield to execute the passed block.
|
|
467
|
+
#
|
|
468
|
+
# @return [self]
|
|
469
|
+
def separate(args, sep, breakable: ' ', break_pos: :after,
|
|
470
|
+
break_type: nil, indent: false,
|
|
471
|
+
force_break: false, line_continuation: '')
|
|
472
|
+
if args.is_a?(Enumerator) ? args.count == 1 : args.length == 1
|
|
473
|
+
yield(*args[0])
|
|
474
|
+
return self
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
first = true
|
|
478
|
+
wrap {
|
|
479
|
+
wrap {
|
|
480
|
+
args&.each do |*as|
|
|
481
|
+
if first
|
|
482
|
+
breakable '' if !line_continuation.empty? && break_pos == :after
|
|
483
|
+
first = false
|
|
484
|
+
elsif break_pos == :after
|
|
485
|
+
text sep
|
|
486
|
+
breakable(breakable, line_continuation:) if breakable && !force_break
|
|
487
|
+
self.break(line_continuation:) if force_break
|
|
488
|
+
else
|
|
489
|
+
breakable(breakable, line_continuation:) if breakable && !force_break
|
|
490
|
+
self.break(line_continuation:) if force_break
|
|
491
|
+
text sep
|
|
492
|
+
end
|
|
493
|
+
yield(*as)
|
|
494
|
+
end
|
|
495
|
+
}
|
|
496
|
+
.when(break_type) { |body|
|
|
497
|
+
group(break_type, indent: 0) {
|
|
498
|
+
body.()
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
.end
|
|
502
|
+
}
|
|
503
|
+
.when(indent) { |body|
|
|
504
|
+
nest(indent: indent.is_a?(Integer) ? indent : @indent) {
|
|
505
|
+
body.()
|
|
506
|
+
}
|
|
507
|
+
}.end
|
|
508
|
+
breakable('', line_continuation:) if !line_continuation.empty? && !break_type
|
|
509
|
+
|
|
510
|
+
self
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# A shorhand for `text ' '`.
|
|
514
|
+
#
|
|
515
|
+
# @return [self]
|
|
516
|
+
def space
|
|
517
|
+
text ' '
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Surround a block with +lft+ and +rgt+
|
|
521
|
+
#
|
|
522
|
+
# @param lft [String] lft
|
|
523
|
+
# left surrounding string.
|
|
524
|
+
# @param rgt [String] rgt
|
|
525
|
+
# right surrounding string.
|
|
526
|
+
#
|
|
527
|
+
# @yield the passed block to be surrounded with `lft` and `rgt`.
|
|
528
|
+
#
|
|
529
|
+
# @option opts [Boolean] :group (true)
|
|
530
|
+
# whether to create a group enclosing `lft`, `rgt`, and the passed block.
|
|
531
|
+
# @option opts [Boolean] :indent (@indent)
|
|
532
|
+
# whether to indent the passed block.
|
|
533
|
+
# @option opts [String] :lft_breakable ('')
|
|
534
|
+
# left breakable string.
|
|
535
|
+
# @option opts [Boolean] :lft_can_break (true)
|
|
536
|
+
# injects `break` or `breakable` only if true;
|
|
537
|
+
# i.e. `lft_breakable` will be ignored if false.
|
|
538
|
+
# @option opts [Boolean] :lft_force_break (false)
|
|
539
|
+
# force break instead of using `lft_breakable`.
|
|
540
|
+
# @option opts [String] :rgt_breakable ('')
|
|
541
|
+
# right breakable string.
|
|
542
|
+
# @option opts [Boolean] :rgt_can_break (true)
|
|
543
|
+
# injects `break` or `breakable` only if true.
|
|
544
|
+
# i.e. `rgt_breakable` will be ignored if false.
|
|
545
|
+
# @option opts [Boolean] :rgt_force_break (false)
|
|
546
|
+
# force break instead of using `rgt_breakable`.
|
|
547
|
+
#
|
|
548
|
+
# @return [self]
|
|
549
|
+
def surround(lft, rgt, **opts)
|
|
550
|
+
group = opts.fetch(:group, true)
|
|
551
|
+
group_open(break_type: :inconsistent) if group
|
|
552
|
+
|
|
553
|
+
text lft if lft
|
|
554
|
+
|
|
555
|
+
indent = opts.fetch(:indent, @indent)
|
|
556
|
+
nest_open(indent:)
|
|
557
|
+
|
|
558
|
+
lft_breakable = opts.fetch(:lft_breakable, '')
|
|
559
|
+
lft_can_break = opts.fetch(:lft_can_break, true)
|
|
560
|
+
lft_force_break = opts.fetch(:lft_force_break, false)
|
|
561
|
+
if lft && lft_can_break
|
|
562
|
+
if lft_force_break
|
|
563
|
+
self.break
|
|
564
|
+
else
|
|
565
|
+
breakable lft_breakable
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
if block_given?
|
|
570
|
+
yield
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
nest_close
|
|
574
|
+
|
|
575
|
+
rgt_breakable = opts.fetch(:rgt_breakable, '')
|
|
576
|
+
rgt_can_break = opts.fetch(:rgt_can_break, true)
|
|
577
|
+
rgt_force_break = opts.fetch(:rgt_force_break, false)
|
|
578
|
+
if rgt
|
|
579
|
+
if rgt_can_break
|
|
580
|
+
if rgt_force_break
|
|
581
|
+
self.break
|
|
582
|
+
else
|
|
583
|
+
breakable rgt_breakable
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
text rgt
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
group_close if group
|
|
590
|
+
|
|
591
|
+
self
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# @!group Convenience Methods Built On {separate}
|
|
595
|
+
|
|
596
|
+
# Separate args into lines.
|
|
597
|
+
#
|
|
598
|
+
# This is a wrapper around {separate} where `breakable: true`.
|
|
599
|
+
#
|
|
600
|
+
# @see [separate]
|
|
601
|
+
def lines(*args, **kwargs, &)
|
|
602
|
+
separate(*args, **kwargs.merge(force_break: true), &)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Concatenates args.
|
|
606
|
+
#
|
|
607
|
+
# This is a wrapper around {separate} where `breakable: false`.
|
|
608
|
+
#
|
|
609
|
+
# @see [separate]
|
|
610
|
+
def concat(*args, **kwargs, &)
|
|
611
|
+
separate(*args, **kwargs.merge(breakable: false), &)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# @!endgroup
|
|
615
|
+
# @!group Convenience Methods Built On {surround}
|
|
616
|
+
|
|
617
|
+
# YARD doesn't drop into blocks, so we can't use metaprogramming
|
|
618
|
+
# to generate all these functions, so we're copy-pastring.
|
|
619
|
+
|
|
620
|
+
# {surround} with `< >`. New lines can appear after and before the delimiters.
|
|
621
|
+
#
|
|
622
|
+
# @param padding [String] ('')
|
|
623
|
+
# Passed to `lft_breakable` and `rgt_breakable`.
|
|
624
|
+
#
|
|
625
|
+
# @return [self]
|
|
626
|
+
def angles(padding: '', **kwargs, &block)
|
|
627
|
+
surround(
|
|
628
|
+
'<', '>',
|
|
629
|
+
**kwargs.merge(lft_breakable: padding, rgt_breakable: padding),
|
|
630
|
+
&block
|
|
631
|
+
)
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
# {surround} with `< >`. New lines cannot appear after and before the delimiters.
|
|
635
|
+
#
|
|
636
|
+
# @return [self]
|
|
637
|
+
def angles_break_both(**kwargs, &)
|
|
638
|
+
angles(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# {surround} with `< >`. New lines will appear after and before the delimiters.
|
|
642
|
+
#
|
|
643
|
+
# @return [self]
|
|
644
|
+
def angles_break_none(**kwargs, &)
|
|
645
|
+
angles(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &)
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# {surround} with `{ }`. New lines can appear after and before the delimiters.
|
|
649
|
+
#
|
|
650
|
+
# @param padding [String] ('')
|
|
651
|
+
# Passed to `lft_breakable` and `rgt_breakable`.
|
|
652
|
+
#
|
|
653
|
+
# @return [self]
|
|
654
|
+
def braces(padding: '', **kwargs, &block)
|
|
655
|
+
surround(
|
|
656
|
+
'{', '}',
|
|
657
|
+
**kwargs.merge(lft_breakable: padding, rgt_breakable: padding),
|
|
658
|
+
&block
|
|
659
|
+
)
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# {surround} with `{ }`. New lines cannot appear after and before the delimiters.
|
|
663
|
+
#
|
|
664
|
+
# @return [self]
|
|
665
|
+
def braces_break_both(**kwargs, &)
|
|
666
|
+
braces(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
# {surround} with `{ }`. New lines will appear after and before the delimiters.
|
|
670
|
+
#
|
|
671
|
+
# @return [self]
|
|
672
|
+
def braces_break_none(**kwargs, &)
|
|
673
|
+
braces(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &)
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# {surround} with `[ ]`. New lines can appear after and before the delimiters.
|
|
677
|
+
#
|
|
678
|
+
# @param padding [String] ('')
|
|
679
|
+
# Passed to `lft_breakable` and `rgt_breakable`.
|
|
680
|
+
#
|
|
681
|
+
# @return [self]
|
|
682
|
+
def brackets(padding: '', **kwargs, &block)
|
|
683
|
+
surround(
|
|
684
|
+
'[', ']',
|
|
685
|
+
**kwargs.merge(lft_breakable: padding, rgt_breakable: padding),
|
|
686
|
+
&block
|
|
687
|
+
)
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# {surround} with `[ ]`. New lines cannot appear after and before the delimiters.
|
|
691
|
+
#
|
|
692
|
+
# @return [self]
|
|
693
|
+
def brackets_break_both(**kwargs, &)
|
|
694
|
+
brackets(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
# {surround} with `[ ]`. New lines will appear after and before the delimiters.
|
|
698
|
+
#
|
|
699
|
+
# @return [self]
|
|
700
|
+
def brackets_break_none(**kwargs, &)
|
|
701
|
+
brackets(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &)
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
# {surround} with `( )`. New lines can appear after and before the delimiters.
|
|
705
|
+
#
|
|
706
|
+
# @param padding [String] ('')
|
|
707
|
+
# Passed to `lft_breakable` and `rgt_breakable`.
|
|
708
|
+
#
|
|
709
|
+
# @return [self]
|
|
710
|
+
def parens(padding: '', **kwargs, &block)
|
|
711
|
+
surround(
|
|
712
|
+
'(', ')',
|
|
713
|
+
**kwargs.merge(lft_breakable: padding, rgt_breakable: padding),
|
|
714
|
+
&block
|
|
715
|
+
)
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
# {surround} with `( )`. New lines cannot appear after and before the delimiters.
|
|
719
|
+
#
|
|
720
|
+
# @return [self]
|
|
721
|
+
def parens_break_both(**kwargs, &)
|
|
722
|
+
parens(**kwargs.merge(lft_force_break: true, rgt_force_break: true), &)
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
# {surround} with `( )`. New lines will appear after and before the delimiters.
|
|
726
|
+
#
|
|
727
|
+
# @return [self]
|
|
728
|
+
def parens_break_none(**kwargs, &)
|
|
729
|
+
parens(**kwargs.merge(lft_can_break: false, rgt_can_break: false), &)
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
# {surround} with `` ` ` ``. New lines cannot appear after and before the delimiters
|
|
733
|
+
# unless you specify it with `rgt_can_break` and `lft_can_break`.
|
|
734
|
+
#
|
|
735
|
+
# @return [self]
|
|
736
|
+
def backticks(**kwargs, &)
|
|
737
|
+
surround('`', '`', lft_can_break: false, rgt_can_break: false, **kwargs, &)
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# {surround} with `" "`. New lines cannot appear after and before the delimiters
|
|
741
|
+
# unless you specify it with `rgt_can_break` and `lft_can_break`.
|
|
742
|
+
#
|
|
743
|
+
# @return [self]
|
|
744
|
+
def quote_double(**kwargs, &)
|
|
745
|
+
surround('"', '"', lft_can_break: false, rgt_can_break: false, **kwargs, &)
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
# {surround} with `' '`. New lines cannot appear after and before the delimiters
|
|
749
|
+
# unless you specify it with `rgt_can_break` and `lft_can_break`.
|
|
750
|
+
#
|
|
751
|
+
# @return [self]
|
|
752
|
+
def quote_single(**kwargs, &)
|
|
753
|
+
surround("'", "'", lft_can_break: false, rgt_can_break: false, **kwargs, &)
|
|
339
754
|
end
|
|
340
755
|
|
|
341
756
|
# Open a consistent group.
|
|
342
757
|
#
|
|
343
|
-
# @param
|
|
344
|
-
#
|
|
345
|
-
# @param indent
|
|
758
|
+
# @param break_type [Symbol]
|
|
759
|
+
# `:consistent` or `:inconsistent`
|
|
760
|
+
# @param indent [Integer]
|
|
346
761
|
# the amount of indentation of the group.
|
|
347
762
|
#
|
|
348
|
-
# @return [
|
|
763
|
+
# @return [self]
|
|
349
764
|
#
|
|
350
765
|
# @see Oppen.begin_consistent
|
|
351
766
|
# @see Oppen.begin_inconsistent
|
|
352
|
-
def group_open(
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
767
|
+
def group_open(break_type: :consistent, indent: 0)
|
|
768
|
+
if %i[consistent inconsistent].none?(break_type)
|
|
769
|
+
raise ArgumentError, '%s is not a valid type. Choose one: :consistent or :inconsistent'
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
tokens << Oppen.send(:"begin_#{break_type}", offset: indent)
|
|
773
|
+
self
|
|
359
774
|
end
|
|
360
775
|
|
|
361
776
|
# Close a group.
|
|
362
777
|
#
|
|
363
|
-
# @return [
|
|
364
|
-
def group_close
|
|
778
|
+
# @return [self]
|
|
779
|
+
def group_close
|
|
365
780
|
tokens << Oppen.end
|
|
781
|
+
self
|
|
366
782
|
end
|
|
367
783
|
|
|
368
784
|
# Open a consistent group and add indent amount.
|
|
@@ -370,8 +786,8 @@ module Oppen
|
|
|
370
786
|
# @param indent [Integer]
|
|
371
787
|
# the amount of indentation of the group.
|
|
372
788
|
#
|
|
373
|
-
# @return [
|
|
374
|
-
def indent_open(indent)
|
|
789
|
+
# @return [self]
|
|
790
|
+
def indent_open(indent: @indent)
|
|
375
791
|
@current_indent += indent
|
|
376
792
|
group_open
|
|
377
793
|
end
|
|
@@ -381,10 +797,10 @@ module Oppen
|
|
|
381
797
|
# @param indent [Integer]
|
|
382
798
|
# the amount of indentation of the group.
|
|
383
799
|
#
|
|
384
|
-
# @return [
|
|
385
|
-
def indent_close(
|
|
800
|
+
# @return [self]
|
|
801
|
+
def indent_close(indent: @indent)
|
|
386
802
|
@current_indent -= indent
|
|
387
|
-
group_close
|
|
803
|
+
group_close
|
|
388
804
|
end
|
|
389
805
|
|
|
390
806
|
# Open a nest by adding indent.
|
|
@@ -392,9 +808,10 @@ module Oppen
|
|
|
392
808
|
# @param indent [Integer]
|
|
393
809
|
# the amount of indentation of the nest.
|
|
394
810
|
#
|
|
395
|
-
# @return [
|
|
396
|
-
def nest_open(indent)
|
|
811
|
+
# @return [self]
|
|
812
|
+
def nest_open(indent: @indent)
|
|
397
813
|
@current_indent += indent
|
|
814
|
+
self
|
|
398
815
|
end
|
|
399
816
|
|
|
400
817
|
# Close a nest by subtracting indent.
|
|
@@ -402,11 +819,43 @@ module Oppen
|
|
|
402
819
|
# @param indent [Integer]
|
|
403
820
|
# the amount of indentation of the nest.
|
|
404
821
|
#
|
|
405
|
-
# @return [
|
|
406
|
-
def nest_close(indent)
|
|
822
|
+
# @return [self]
|
|
823
|
+
def nest_close(indent: @indent)
|
|
407
824
|
@current_indent -= indent
|
|
825
|
+
self
|
|
408
826
|
end
|
|
409
827
|
|
|
410
828
|
# @!endgroup
|
|
829
|
+
|
|
830
|
+
# Helper class to allow conditional printing.
|
|
831
|
+
class Wrap
|
|
832
|
+
def initialize(blk)
|
|
833
|
+
@wrapped = blk
|
|
834
|
+
@wrapper = nil
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# Conditional.
|
|
838
|
+
def when(cond, &blk)
|
|
839
|
+
if cond
|
|
840
|
+
@wrapper = blk
|
|
841
|
+
end
|
|
842
|
+
self
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
# Flush.
|
|
846
|
+
def end
|
|
847
|
+
@wrapper ? @wrapper.(@wrapped) : @wrapped.()
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
# To re-enable chaining.
|
|
851
|
+
def method_missing(meth, ...)
|
|
852
|
+
self.end.send(meth, ...)
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
# To re-enable chaining.
|
|
856
|
+
def respond_to_missing?(meth, include_private)
|
|
857
|
+
self.end.respond_to_missing?(meth, include_private)
|
|
858
|
+
end
|
|
859
|
+
end
|
|
411
860
|
end
|
|
412
861
|
end
|
metadata
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oppen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Amine Mike El Maalouf <amine.el-maalouf@epita.fr>
|
|
8
8
|
- Firas al-Khalil <firas.alkhalil@faveod.com>
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2025-01-21 00:00:00.000000000 Z
|
|
13
13
|
dependencies: []
|
|
14
14
|
description: Implementation of the Oppen's pretty printing algorithm
|
|
15
|
-
email:
|
|
15
|
+
email:
|
|
16
16
|
executables: []
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
@@ -31,7 +31,7 @@ homepage: http://github.com/Faveod/oppen-ruby
|
|
|
31
31
|
licenses:
|
|
32
32
|
- MIT
|
|
33
33
|
metadata: {}
|
|
34
|
-
post_install_message:
|
|
34
|
+
post_install_message:
|
|
35
35
|
rdoc_options: []
|
|
36
36
|
require_paths:
|
|
37
37
|
- lib
|
|
@@ -39,7 +39,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
39
39
|
requirements:
|
|
40
40
|
- - ">="
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: '3.
|
|
42
|
+
version: '3.1'
|
|
43
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
47
47
|
version: '0'
|
|
48
48
|
requirements: []
|
|
49
49
|
rubygems_version: 3.4.19
|
|
50
|
-
signing_key:
|
|
50
|
+
signing_key:
|
|
51
51
|
specification_version: 4
|
|
52
52
|
summary: Pretty-printing library
|
|
53
53
|
test_files: []
|