oppen 0.9.6 → 0.9.8

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,117 +40,226 @@ 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, upsize_stack: false)
55
- @indent_anchor = indent_anchor
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(4) {
68
+ # out.group(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(4) {
84
+ # out.group(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,
95
+ trim_trailing_whitespaces: false, upsize_stack: false)
56
96
  @eager_print = eager_print
97
+ @indent_anchor = indent_anchor
98
+ @trim_trailing_whitespaces = trim_trailing_whitespaces
57
99
  @upsize_stack = upsize_stack
58
100
  end
59
101
 
60
- # Print groups eagerly
102
+ # Print groups eagerly.
61
103
  #
62
104
  # @example
63
- # out = Oppen::Wadler.new (width: 13)
64
- # out.group {
65
- # out.group {
66
- # out.text 'abc'
67
- # out.breakable
68
- # out.text 'def'
69
- # }
70
- # out.group {
71
- # out.text 'ghi'
72
- # out.breakable
73
- # out.text 'jkl'
74
- # }
75
- # }
76
- # 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
77
119
  #
78
- # # eager_print: false
79
- # # =>
80
- # # abc
81
- # # defghi jkl
82
- # #
83
- # # eager_print: true
84
- # # =>
85
- # # abc defghi
86
- # # jkl
120
+ # # eager_print: false =>
121
+ # # abc
122
+ # # defghi jkl
123
+ # #
124
+ # # eager_print: true =>
125
+ # # abc defghi
126
+ # # jkl
87
127
  #
88
128
  # @return [Boolean]
89
129
  def eager_print? = @eager_print
90
130
 
131
+ def trim_trailing_whitespaces? = @trim_trailing_whitespaces
132
+
91
133
  def upsize_stack? = @upsize_stack
92
134
 
93
- # Default config for Oppen usage
135
+ # Default configuration that provides printing behaviour identical to what's
136
+ # been described by Oppen.
137
+ #
94
138
  # @return [Config]
95
139
  def self.oppen
96
140
  new
97
141
  end
98
142
 
99
- # 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
+ #
100
154
  # @return [Config]
101
- def self.wadler(eager_print: true, upsize_stack: true)
102
- new(indent_anchor: IndentAnchor::ON_BEGIN, eager_print:, upsize_stack:)
155
+ def self.wadler(eager_print: true, trim_trailing_whitespaces: true, upsize_stack: true)
156
+ new(
157
+ eager_print: eager_print,
158
+ indent_anchor: :current_offset,
159
+ trim_trailing_whitespaces: trim_trailing_whitespaces,
160
+ upsize_stack: upsize_stack,
161
+ )
103
162
  end
104
163
  end
105
164
 
106
165
  # @param value [String]
107
- # @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.
108
171
  #
109
- # @return [Oppen::Token::String] a new String token
172
+ # @return [Token::String]
173
+ # a new String token.
110
174
  def self.string(value, width: value.length)
111
- Token::String.new(value, width:)
175
+ Token::String.new(value, width: width)
112
176
  end
113
177
 
114
- # @param str [String]
115
- # @param line_continuation [String] If a new line is needed display this string before the new line
116
- # @param offset [Integer]
117
- # @param width [Integer] token width that defaults to str.length
178
+ # @return [Token::Whitespace] a new Whitespace token.
179
+ def self.whitespace(value)
180
+ Token::Whitespace.new(value, width: value.bytesize)
181
+ end
182
+
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.
118
193
  #
119
- # @return [Oppen::Token::Break] a new Break token
194
+ # @return [Token::Break]
195
+ # a new Break token.
196
+ #
197
+ # @see Wadler#break example on `line_continuation`.
120
198
  def self.break(str = ' ', line_continuation: '', offset: 0, width: str.length)
121
- Token::Break.new(str, width:, line_continuation:, offset:)
199
+ Token::Break.new(str, width: width, line_continuation: line_continuation, offset: offset)
122
200
  end
123
201
 
124
- # @param line_continuation [String] If a new line is needed display this string before the new line
125
- # @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.
126
209
  #
127
- # @return [Oppen::Token::LineBreak] a new LineBreak token
210
+ # @see Wadler#break example on `line_continuation`.
128
211
  def self.line_break(line_continuation: '', offset: 0)
129
- Token::LineBreak.new(line_continuation:, offset:)
212
+ Token::LineBreak.new(line_continuation: line_continuation, offset: offset)
130
213
  end
131
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
+ #
132
219
  # @param offset [Integer]
220
+ # the additional indentation of the group.
221
+ #
222
+ # @return [Token::Begin]
223
+ # a new consistent Begin token.
133
224
  #
134
- # @return [Oppen::Token::Begin] a new consistent Begin token
225
+ # @example Function Arguments
226
+ # fun(
227
+ # arg1,
228
+ # arg2,
229
+ # arg3,
230
+ # arg4,
231
+ # )
232
+ #
233
+ # @see Wadler#group
135
234
  def self.begin_consistent(offset: 2)
136
- Token::Begin.new(break_type: Token::BreakType::CONSISTENT, offset:)
235
+ Token::Begin.new(break_type: :consistent, offset: offset)
137
236
  end
138
237
 
139
- # @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
+ # )
140
251
  #
141
- # @return [Oppen::Token::Begin] a new inconsistent Begin token
252
+ # @see Wadler#group
142
253
  def self.begin_inconsistent(offset: 2)
143
- Token::Begin.new(break_type: Token::BreakType::INCONSISTENT, offset:)
254
+ Token::Begin.new(break_type: :inconsistent, offset: offset)
144
255
  end
145
256
 
146
- # @return [Oppen::Token::End] a new End token
257
+ # @return [Token::End] a new End token.
147
258
  def self.end
148
259
  Token::End.new
149
260
  end
150
261
 
151
- # @return [Oppen::Token::EOF] a new EOF token
262
+ # @return [Token::EOF] a new EOF token.
152
263
  def self.eof
153
264
  Token::EOF.new
154
265
  end