oppen 0.9.7 → 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,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(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,
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: eager_print,
158
+ indent_anchor: :current_offset,
159
+ trim_trailing_whitespaces: trim_trailing_whitespaces,
160
+ upsize_stack: 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
- Token::String.new(value, width:)
175
+ Token::String.new(value, width: 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
- Token::Break.new(str, width:, line_continuation:, offset:)
199
+ Token::Break.new(str, width: width, line_continuation: line_continuation, offset: 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
- Token::LineBreak.new(line_continuation:, offset:)
212
+ Token::LineBreak.new(line_continuation: line_continuation, offset: 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: 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: 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