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.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/lib/oppen/mixins.rb +50 -27
- data/lib/oppen/print_stack.rb +77 -67
- data/lib/oppen/printer.rb +122 -77
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +54 -22
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +186 -75
- data/lib/wadler/print.rb +248 -45
- metadata +5 -5
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
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
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,
|
31
|
-
|
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
|
-
|
55
|
-
|
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
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
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
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
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
|
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
|
-
#
|
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(
|
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
|
-
#
|
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 [
|
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
|
-
# @
|
115
|
-
|
116
|
-
|
117
|
-
|
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 [
|
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
|
199
|
+
Token::Break.new(str, width: width, line_continuation: line_continuation, offset: offset)
|
122
200
|
end
|
123
201
|
|
124
|
-
# @param line_continuation [String]
|
125
|
-
#
|
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
|
-
# @
|
210
|
+
# @see Wadler#break example on `line_continuation`.
|
128
211
|
def self.line_break(line_continuation: '', offset: 0)
|
129
|
-
Token::LineBreak.new(line_continuation
|
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
|
-
# @
|
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:
|
235
|
+
Token::Begin.new(break_type: :consistent, offset: offset)
|
137
236
|
end
|
138
237
|
|
139
|
-
#
|
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
|
-
# @
|
252
|
+
# @see Wadler#group
|
142
253
|
def self.begin_inconsistent(offset: 2)
|
143
|
-
Token::Begin.new(break_type:
|
254
|
+
Token::Begin.new(break_type: :inconsistent, offset: offset)
|
144
255
|
end
|
145
256
|
|
146
|
-
# @return [
|
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 [
|
262
|
+
# @return [Token::EOF] a new EOF token.
|
152
263
|
def self.eof
|
153
264
|
Token::EOF.new
|
154
265
|
end
|