oppen 0.9.7 → 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.
data/lib/oppen.rb CHANGED
@@ -14,21 +14,23 @@ module Oppen
14
14
 
15
15
  # Entry point of the pretty printer.
16
16
  #
17
- # @param config [Config]
18
- # @param space [String, Proc] could be a String or a callable.
19
- # If it's a string, spaces will be generated with the the
20
- # lambda `->(n){ n * space }`, where `n` is the number of columns
21
- # to indent.
22
- # If it's a callable, it will receive `n` and it needs to return
23
- # a string.
24
- # @param new_line [String] the delimiter between lines
25
- # @param out [Object] should have a write and string method
26
- # @param tokens [Array[Token]] the list of tokens to be printed
27
- # @param width [Integer] maximum line width desired
17
+ # @param config [Config]
18
+ # to customize the printer's behavior.
19
+ # @param new_line [String]
20
+ # the delimiter between lines.
21
+ # @param out [Object]
22
+ # the output string buffer. It should have a `write` and `string` methods.
23
+ # @param space [String, Proc]
24
+ # indentation string or a string generator.
25
+ # - If a `String`, spaces will be generated with the the lambda
26
+ # `->(n){ space * n }`, where `n` is the number of columns to indent.
27
+ # - If a `Proc`, it will receive `n` and it needs to return a `String`.
28
+ # @param tokens [Array<Token>] the list of tokens to be printed.
29
+ # @param width [Integer] maximum line width desired.
28
30
  #
29
- # @return [String] output of the pretty printer
30
- def self.print(config: Config.oppen, space: ' ',
31
- new_line: "\n", out: StringIO.new, tokens: [], width: 80)
31
+ # @return [String] output of the pretty printer.
32
+ def self.print(config: Config.oppen, new_line: "\n",
33
+ out: StringIO.new, space: ' ', tokens: [], width: 80)
32
34
  printer = Printer.new width, new_line, config, space, out
33
35
  tokens.each do |token|
34
36
  printer.print token
@@ -38,54 +40,90 @@ module Oppen
38
40
 
39
41
  # Config.
40
42
  class Config
41
- # IndentAnchor.
42
- #
43
- # ON_BREAK => anchor on break position (as in Oppen's original paper)
44
- # ON_BEGIN => anchor on begin block position
45
- module IndentAnchor
46
- # @return [Integer]
47
- ON_BREAK = 0
48
- # @return [Integer]
49
- ON_BEGIN = 1
50
- end
51
-
52
43
  attr_accessor :indent_anchor
53
44
 
54
- def initialize(indent_anchor: IndentAnchor::ON_BREAK, eager_print: false,
45
+ # @param eager_print [Boolean]
46
+ # whether to eagerly print.
47
+ # @param indent_anchor [Symbol]
48
+ # the different ways of handling the indentation of nested groups.
49
+ # - `:end_of_previous_line`: In the case of a new line in a nested group,
50
+ # the next string token will be displayed with indentation = previous
51
+ # line width + last group indentation. Defined in Oppen's paper.
52
+ #
53
+ # - `:current_offset`: When printing a new line in a nested group, the
54
+ # next string token will be displayed with an indentation equal to the
55
+ # sum of the indentations of all its parent groups. This is an
56
+ # extension to Oppen's work.
57
+ #
58
+ # @param trim_trailing_whitespaces [Boolean]
59
+ # whether to trim trailing whitespaces.
60
+ # @param upsize_stack [Boolean]
61
+ # whether to upsize stack when needed.
62
+ #
63
+ # @example `:end_of_previous_line` anchor
64
+ # config = Oppen::Config.new(indent_anchor: :end_of_previous_line)
65
+ # out = Oppen::Wadler.new config:, width: 13
66
+ # out.text 'And she said:'
67
+ # out.group(indent: 4) {
68
+ # out.group(indent: 4) {
69
+ # out.break
70
+ # out.text 'Hello, World!'
71
+ # }
72
+ # }
73
+ # out.output
74
+ #
75
+ # # =>
76
+ # # And she said:
77
+ # # Hello, World!
78
+ #
79
+ # @example `:current_offset anchor`
80
+ # config = Oppen::Config.new(indent_anchor: :current_offset)
81
+ # out = Oppen::Wadler.new config:, width: 13
82
+ # out.text 'And she said:'
83
+ # out.group(indent: 4) {
84
+ # out.group(indent: 4) {
85
+ # out.break
86
+ # out.text 'Hello, World!'
87
+ # }
88
+ # }
89
+ # out.output
90
+ #
91
+ # # =>
92
+ # # And she said:
93
+ # # Hello, World!
94
+ def initialize(eager_print: false, indent_anchor: :end_of_previous_line,
55
95
  trim_trailing_whitespaces: false, upsize_stack: false)
56
- @indent_anchor = indent_anchor
57
96
  @eager_print = eager_print
97
+ @indent_anchor = indent_anchor
58
98
  @trim_trailing_whitespaces = trim_trailing_whitespaces
59
99
  @upsize_stack = upsize_stack
60
100
  end
61
101
 
62
- # Print groups eagerly
102
+ # Print groups eagerly.
63
103
  #
64
104
  # @example
65
- # out = Oppen::Wadler.new (width: 13)
66
- # out.group {
67
- # out.group {
68
- # out.text 'abc'
69
- # out.breakable
70
- # out.text 'def'
71
- # }
72
- # out.group {
73
- # out.text 'ghi'
74
- # out.breakable
75
- # out.text 'jkl'
76
- # }
77
- # }
78
- # out.output
105
+ # out = Oppen::Wadler.new(width: 13)
106
+ # out.group {
107
+ # out.group {
108
+ # out.text 'abc'
109
+ # out.breakable
110
+ # out.text 'def'
111
+ # }
112
+ # out.group {
113
+ # out.text 'ghi'
114
+ # out.breakable
115
+ # out.text 'jkl'
116
+ # }
117
+ # }
118
+ # out.output
79
119
  #
80
- # # eager_print: false
81
- # # =>
82
- # # abc
83
- # # defghi jkl
84
- # #
85
- # # eager_print: true
86
- # # =>
87
- # # abc defghi
88
- # # jkl
120
+ # # eager_print: false =>
121
+ # # abc
122
+ # # defghi jkl
123
+ # #
124
+ # # eager_print: true =>
125
+ # # abc defghi
126
+ # # jkl
89
127
  #
90
128
  # @return [Boolean]
91
129
  def eager_print? = @eager_print
@@ -94,72 +132,134 @@ module Oppen
94
132
 
95
133
  def upsize_stack? = @upsize_stack
96
134
 
97
- # Default config for Oppen usage
135
+ # Default configuration that provides printing behaviour identical to what's
136
+ # been described by Oppen.
137
+ #
98
138
  # @return [Config]
99
139
  def self.oppen
100
140
  new
101
141
  end
102
142
 
103
- # Default config for Wadler usage
143
+ # Configure the printer to behave more like
144
+ # [ruby/prettyprint](https://github.com/ruby/prettyprint):
145
+ #
146
+ # 1. groups are printed eagerly (we try to flush on a group's close).
147
+ # 2. The indentation is anchored on the left margin.
148
+ # 3. Trailing whitespaces are removed.
149
+ #
150
+ # The name was amusingly chosen in reference to
151
+ # [Wadler](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)'s
152
+ # work on pretty printing.
153
+ #
104
154
  # @return [Config]
105
155
  def self.wadler(eager_print: true, trim_trailing_whitespaces: true, upsize_stack: true)
106
- new(indent_anchor: IndentAnchor::ON_BEGIN, eager_print:, trim_trailing_whitespaces:, upsize_stack:)
156
+ new(
157
+ eager_print:,
158
+ indent_anchor: :current_offset,
159
+ trim_trailing_whitespaces:,
160
+ upsize_stack:,
161
+ )
107
162
  end
108
163
  end
109
164
 
110
165
  # @param value [String]
111
- # @param width [Integer] token width that defaults to value.length
166
+ # the string to print.
167
+ # @param width [Integer]
168
+ # the string's effective width. Useful when printing HTML, e.g.
169
+ # `<span>value</span>`, where the effective width is that of the inner
170
+ # text.
112
171
  #
113
- # @return [Oppen::Token::String] a new String token
172
+ # @return [Token::String]
173
+ # a new String token.
114
174
  def self.string(value, width: value.length)
115
175
  Token::String.new(value, width:)
116
176
  end
117
177
 
118
- # @see Token::Whitespace
119
- #
120
- # @return [Oppen::Token::Whitespace] a new Whitespace token.
178
+ # @return [Token::Whitespace] a new Whitespace token.
121
179
  def self.whitespace(value)
122
180
  Token::Whitespace.new(value, width: value.bytesize)
123
181
  end
124
182
 
125
- # @param str [String]
126
- # @param line_continuation [String] If a new line is needed display this string before the new line
127
- # @param offset [Integer]
128
- # @param width [Integer] token width that defaults to str.length
183
+ # @param str [String]
184
+ # value shown if no new line is needed.
185
+ # @param line_continuation [String]
186
+ # printed before the line break.
187
+ # @param offset [Integer]
188
+ # additional indentation to be added to the current indentation level.
189
+ # @param width [Integer]
190
+ # the string's effective width. Useful when printing HTML, e.g.
191
+ # `<span>value</span>`, where the effective width is that of the inner
192
+ # text.
193
+ #
194
+ # @return [Token::Break]
195
+ # a new Break token.
129
196
  #
130
- # @return [Oppen::Token::Break] a new Break token
197
+ # @see Wadler#break example on `line_continuation`.
131
198
  def self.break(str = ' ', line_continuation: '', offset: 0, width: str.length)
132
199
  Token::Break.new(str, width:, line_continuation:, offset:)
133
200
  end
134
201
 
135
- # @param line_continuation [String] If a new line is needed display this string before the new line
136
- # @param offset [Integer]
202
+ # @param line_continuation [String]
203
+ # printed before the line break.
204
+ # @param offset [Integer]
205
+ # additional indentation to be added to the current indentation level.
206
+ #
207
+ # @return [Token::LineBreak]
208
+ # a new LineBreak token.
137
209
  #
138
- # @return [Oppen::Token::LineBreak] a new LineBreak token
210
+ # @see Wadler#break example on `line_continuation`.
139
211
  def self.line_break(line_continuation: '', offset: 0)
140
212
  Token::LineBreak.new(line_continuation:, offset:)
141
213
  end
142
214
 
215
+ # In a consistent group, the presence of a new line inside the group will
216
+ # propagate to the other Break tokens in the group causing them all to act as
217
+ # a new line.
218
+ #
143
219
  # @param offset [Integer]
220
+ # the additional indentation of the group.
144
221
  #
145
- # @return [Oppen::Token::Begin] a new consistent Begin token
222
+ # @return [Token::Begin]
223
+ # a new consistent Begin token.
224
+ #
225
+ # @example Function Arguments
226
+ # fun(
227
+ # arg1,
228
+ # arg2,
229
+ # arg3,
230
+ # arg4,
231
+ # )
232
+ #
233
+ # @see Wadler#group
146
234
  def self.begin_consistent(offset: 2)
147
- Token::Begin.new(break_type: Token::BreakType::CONSISTENT, offset:)
235
+ Token::Begin.new(break_type: :consistent, offset:)
148
236
  end
149
237
 
150
- # @param offset [Integer]
238
+ # In an inconsistent group, the presence of a new line inside the group will
239
+ # not propagate to the other Break tokens in the group letting them decide if
240
+ # they need to act as a new line or not.
241
+ #
242
+ # @param offset [Integer] the additional indentation of the group.
243
+ #
244
+ # @return [Token::Begin] a new inconsistent Begin token.
245
+ #
246
+ # @example when used for the display of a function's arguments.
247
+ # fun(
248
+ # arg1, arg2,
249
+ # arg3, arg4,
250
+ # )
151
251
  #
152
- # @return [Oppen::Token::Begin] a new inconsistent Begin token
252
+ # @see Wadler#group
153
253
  def self.begin_inconsistent(offset: 2)
154
- Token::Begin.new(break_type: Token::BreakType::INCONSISTENT, offset:)
254
+ Token::Begin.new(break_type: :inconsistent, offset:)
155
255
  end
156
256
 
157
- # @return [Oppen::Token::End] a new End token
257
+ # @return [Token::End] a new End token.
158
258
  def self.end
159
259
  Token::End.new
160
260
  end
161
261
 
162
- # @return [Oppen::Token::EOF] a new EOF token
262
+ # @return [Token::EOF] a new EOF token.
163
263
  def self.eof
164
264
  Token::EOF.new
165
265
  end