oppen 0.9.6 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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