oppen 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/lib/oppen/mixins.rb +42 -39
- data/lib/oppen/print_stack.rb +62 -69
- data/lib/oppen/printer.rb +91 -78
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +51 -36
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +177 -77
- data/lib/wadler/print.rb +221 -46
- metadata +4 -4
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,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
|
-
|
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
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
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
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
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
|
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
|
-
#
|
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(
|
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
|
-
#
|
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 [
|
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
|
-
# @
|
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
|
126
|
-
#
|
127
|
-
# @param
|
128
|
-
#
|
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
|
-
# @
|
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
|
199
|
+
Token::Break.new(str, width: width, line_continuation: line_continuation, offset: offset)
|
133
200
|
end
|
134
201
|
|
135
|
-
# @param line_continuation [String]
|
136
|
-
#
|
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
|
-
# @
|
210
|
+
# @see Wadler#break example on `line_continuation`.
|
139
211
|
def self.line_break(line_continuation: '', offset: 0)
|
140
|
-
Token::LineBreak.new(line_continuation
|
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 [
|
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:
|
235
|
+
Token::Begin.new(break_type: :consistent, offset: offset)
|
148
236
|
end
|
149
237
|
|
150
|
-
#
|
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
|
-
# @
|
252
|
+
# @see Wadler#group
|
153
253
|
def self.begin_inconsistent(offset: 2)
|
154
|
-
Token::Begin.new(break_type:
|
254
|
+
Token::Begin.new(break_type: :inconsistent, offset: offset)
|
155
255
|
end
|
156
256
|
|
157
|
-
# @return [
|
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 [
|
262
|
+
# @return [Token::EOF] a new EOF token.
|
163
263
|
def self.eof
|
164
264
|
Token::EOF.new
|
165
265
|
end
|