oppen 0.9.7 → 1.0.0
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 +99 -29
- data/lib/oppen/mixins.rb +39 -41
- data/lib/oppen/print_stack.rb +61 -68
- data/lib/oppen/printer.rb +90 -77
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +50 -35
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +174 -74
- data/lib/wadler/print.rb +719 -95
- metadata +8 -8
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(indent: 4) {
|
68
|
+
# out.group(indent: 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(indent: 4) {
|
84
|
+
# out.group(indent: 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:,
|
158
|
+
indent_anchor: :current_offset,
|
159
|
+
trim_trailing_whitespaces:,
|
160
|
+
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
175
|
Token::String.new(value, 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
199
|
Token::Break.new(str, width:, line_continuation:, 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
212
|
Token::LineBreak.new(line_continuation:, 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:)
|
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:)
|
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
|