prettier_print 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/main.yml +42 -0
- data/.gitignore +8 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +24 -0
- data/LICENSE +21 -0
- data/README.md +446 -0
- data/Rakefile +12 -0
- data/bin/console +8 -0
- data/lib/prettier_print/version.rb +5 -0
- data/lib/prettier_print.rb +1274 -0
- data/prettier_print.gemspec +25 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 907080c33747b11f43d7165ca49678637e042a6f72e49af0f07ada0c8a41326f
|
4
|
+
data.tar.gz: fce6553a15d281741ba58aa27572824457ff74546c5bb4d5ef0e5ba6a4dc67c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: afadb2cc911c46b55a2abf07818481fc41472f050ac18bfb6a08016a4586222832d937a816a47b1ce313dbfbfe9c1577cf37b646ebbc6dbff4b2a4f9b57c78e1
|
7
|
+
data.tar.gz: 31076563a9afa16bd4ba137c13ac27eaa3e45cb392923d02a9097260b945bb133829e02258284210ac1661ebb87fa5db0cd2f7803197e40780d116202e42ce41
|
@@ -0,0 +1,42 @@
|
|
1
|
+
name: Main
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request_target
|
5
|
+
jobs:
|
6
|
+
ci:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
ruby:
|
11
|
+
- '2.7'
|
12
|
+
- '3.0'
|
13
|
+
- '3.1'
|
14
|
+
- head
|
15
|
+
name: CI
|
16
|
+
runs-on: ubuntu-latest
|
17
|
+
env:
|
18
|
+
CI: true
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@master
|
21
|
+
- uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
bundler-cache: true
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
- name: Test
|
26
|
+
run: bundle exec rake test
|
27
|
+
|
28
|
+
automerge:
|
29
|
+
name: AutoMerge
|
30
|
+
needs:
|
31
|
+
- ci
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]'
|
34
|
+
steps:
|
35
|
+
- uses: actions/github-script@v3
|
36
|
+
with:
|
37
|
+
script: |
|
38
|
+
github.pulls.merge({
|
39
|
+
owner: context.payload.repository.owner.login,
|
40
|
+
repo: context.payload.repository.name,
|
41
|
+
pull_number: context.payload.pull_request.number
|
42
|
+
})
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [0.1.0] - 2022-05-13
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- 🎉 Initial release! 🎉
|
14
|
+
|
15
|
+
[unreleased]: https://github.com/ruby-syntax-tree/prettier_print/compare/v0.1.0...HEAD
|
16
|
+
[0.1.0]: https://github.com/ruby-syntax-tree/prettier_print/compare/...v0.1.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
prettier_print (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
power_assert (2.0.1)
|
10
|
+
rake (13.0.6)
|
11
|
+
test-unit (3.5.3)
|
12
|
+
power_assert
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
x86_64-darwin-21
|
16
|
+
x86_64-linux
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
prettier_print!
|
20
|
+
rake
|
21
|
+
test-unit
|
22
|
+
|
23
|
+
BUNDLED WITH
|
24
|
+
2.3.6
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022-present Kevin Newton
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,446 @@
|
|
1
|
+
# PrettierPrint
|
2
|
+
|
3
|
+
A drop-in replacement for the `prettyprint` gem with more functionality.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "prettier_print"
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install prettier_print
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To use `PrettierPrint`, you're going to construct a tree that encodes information about how to best print your data, then give the tree a maximum width within which to print. The tree will contain various nodes like `Text` (which wraps actual content to be printed), `Breakable` (a place where a line break could be inserted), `Group` (a set of nodes that the printer should attempt to print on one line), and others.
|
24
|
+
|
25
|
+
### Building the printer
|
26
|
+
|
27
|
+
To construct the tree, you're going to instantiate a `PrettierPrint` object, like so:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
q = PrettierPrint.new(+"", 80, "\n") { |n| " " * n }
|
31
|
+
```
|
32
|
+
|
33
|
+
By convention, the `PrettierPrint` object is called `q`. The arguments are detailed below.
|
34
|
+
|
35
|
+
* This first argument (and the only required argument) is the output object. It can be anything that responds to `<<`, provided that method accepts strings. Usually this is an unfrozen empty string (`+""`). It's also common to see an empty array (`[]`).
|
36
|
+
* The optional second argument is the print width. This defaults to `80`. For more information about this see the section below on [print width](#print-width).
|
37
|
+
* The optional third argument is the newline to use. This defaults to `"\n"`. In some special circumstances, you might want something else like `"\r\n"` or any other newline marker.
|
38
|
+
* The final optional argument is the block that specifies how to build spaces. It receives a single argument which is the number of spaces to generate. This defaults to printing the specified number of space characters. You would modify this only in special circumstances.
|
39
|
+
|
40
|
+
### Print width
|
41
|
+
|
42
|
+
It's important to note that this is different than a maximum line width on a linter. When linting, you want to enforce that nothing exceeds a certain width. In a printer, you're saying that this width is what makes things most readable. So for example, if you were printing some Ruby code like:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
if some_very_long_condition.some_very_long_method_name(some_very_long_argument)
|
46
|
+
do_something
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
In this case you wouldn't want your print width to be set much more than `80`, since it would attempt to print this all on one line, which is much less readable.
|
51
|
+
|
52
|
+
### Building the tree
|
53
|
+
|
54
|
+
Now that you have a printer created, you can start to build out the tree. Each of the nodes of the tree is a small object that you can inspect manually. They each have convenience methods on the printer object that should be used to create them. We'll start by talking about the most foundational nodes, then move on to the less commonly-used ones.
|
55
|
+
|
56
|
+
#### `Text`
|
57
|
+
|
58
|
+
This node contains literal text to be printed. It can wrap any object, but by convention it is normally a string. These objects will never be broken up. For the printing algorithm to work properly, they shouldn't contain newline characters. To instantiate one and add it to the tree, you call the `text` command:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
q.text("my content")
|
62
|
+
```
|
63
|
+
|
64
|
+
If you're using an object that isn't a string and doesn't respond to `#length`, you will need to additionally specify the width of the object, which is the optional second argument, as in:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
q.text(content, 1)
|
68
|
+
```
|
69
|
+
|
70
|
+
#### `Breakable`
|
71
|
+
|
72
|
+
This node specifies where in an expression a line break could be inserted. If the expression fits on one line, the line break will be replaced by its separator. Line breaks by default indent the next line with the current level of indentation. To instantiate one and add it to the tree, you call the `breakable` command:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
q.breakable
|
76
|
+
```
|
77
|
+
|
78
|
+
When it fits on one line, that will be replaced by a space. If you want to change that behavior, you can specify the first argument to be whatever you like. Commonly it will be an empty string, as in:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
q.breakable("")
|
82
|
+
```
|
83
|
+
|
84
|
+
As with `Text`, if you're using an object that isn't a string and doesn't respond to `#length`, you will need to additionally specify the width of the object, which is the optional second argument, as in:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
q.breakable(newline, 1)
|
88
|
+
```
|
89
|
+
|
90
|
+
By default, breakables will indent the next line to the current level of indentation. This is desirable in most cases since if you're inside a parent node that has indented by - for instance, 3 levels - you wouldn't want the next content to start at the beginning of the next line. However, in some circumstances you want control over this behavior, which you can control through the optional `indent` keyword, as in:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
q.breakable(indent: false)
|
94
|
+
```
|
95
|
+
|
96
|
+
There are some times when you want to force a newline into the output and not check whether or not it fits on the current line. You need this behavior if, for instance, you're printing Ruby code and you need to put a separator between two statements. To force the newline into the output, you can use the optional `force` keyword, as in:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
q.breakable(force: true)
|
100
|
+
```
|
101
|
+
|
102
|
+
There are a few circumstances where you'll want to force the newline into the output but not insert a break parent (because you don't want to necessarily force the groups to break unless they need to). In this case you can pass `force: :skip_break_parent` to breakable and it will not insert a break parent.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
q.breakable(force: :skip_break_parent)
|
106
|
+
```
|
107
|
+
|
108
|
+
### `Group`
|
109
|
+
|
110
|
+
This node marks a group of items which the printer should try to fit on one line. Groups are usually nested, and the printer will try to fit everything on one line, but if it doesn't fit it will break the outermost group first and try again. It will continue breaking groups until everything fits (or there are no more groups to break).
|
111
|
+
|
112
|
+
Breaks are propagated to all parent groups, so if a deeply nested expression has a forced break, everything will break. This only matters for forced breaks, i.e. newlines that are printed no matter what and can be statically analyzed.
|
113
|
+
|
114
|
+
To instantiate a group and add it to the tree, you call the `group` method, as in:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
q.group {}
|
118
|
+
```
|
119
|
+
|
120
|
+
It accepts a block that specifies the contents of the group. Within that block you would continue to call other node building methods. By default, this is all you need to specify, as it will group its contents automatically. You can optionally specify open and close segments that should be printed before and after the group, as well as specify how indented the contents of the group should be printed, as in:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
q.group(2, "[", "]") {}
|
124
|
+
```
|
125
|
+
|
126
|
+
In the above example, `"["` will always be printed before the group contents and `"]"` will always be printed after. If the group breaks, its contents will be indented by 2 spaces. As with `Text`, if you're using an object for the open or close segment that isn't a string and doesn't respond to `#length`, you will need to additionally specify the width of the objects, as in:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
q.group(2, opening, closing, 1, 1) {}
|
130
|
+
```
|
131
|
+
|
132
|
+
#### `Align`
|
133
|
+
|
134
|
+
This node increases the indentation by a fixed number of spaces or a string. It is automatically created within `Group` nodes if a width is specified. To instantiate one and add it to the tree, you call the `nest` method, as in:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
q.nest(2) {}
|
138
|
+
```
|
139
|
+
|
140
|
+
It accepts a block that specifies the contents of the alignment node. The value that you're indenting by can be positive or negative. It can also be a string, in which case that value will be used at the beginning of each line, as in:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
q.nest("-->") {}
|
144
|
+
```
|
145
|
+
|
146
|
+
#### `BreakParent`
|
147
|
+
|
148
|
+
This node forces all parent groups up to this point in the tree to break. It's useful if you have some condition under which you must force all of the newlines into the output buffer. To instantiate one and add it to the tree, you call the `break_parent` method, as in:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
q.break_parent
|
152
|
+
```
|
153
|
+
|
154
|
+
#### `IfBreak`
|
155
|
+
|
156
|
+
This node allows you to represent the same content in two different ways: one for if the parent group breaks, one for if it doesn't. For example, if you were writing a formatter for Ruby code, you could use this node to print an `if` statement in the modifier form _only_ if it fits on one line. Otherwise, you could provide the multi-line form. To instantiate one and add it to the tree, you call the `if_break` method, as in:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
q.if_break {}
|
160
|
+
```
|
161
|
+
|
162
|
+
It accepts a block that specifies the contents that should be printed in the event that the parent group is broken. It returns an object that responds to `if_flat`, which you can use to specify the contents that should be printed in the event that the parent group is unbroken, as in:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
q.if_break {}.if_flat {}
|
166
|
+
```
|
167
|
+
|
168
|
+
If you have contents that should _only_ be printed in the case that the parent is group is unbroken (like a `then` keyword in Ruby after a `when` inside a `case` statement), you can just call `if_flat` directly on the printer, as in:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
q.if_flat {}
|
172
|
+
```
|
173
|
+
|
174
|
+
#### `Indent`
|
175
|
+
|
176
|
+
This node is a variant on the `Align` node that always indents by exactly one level of indentation. It's basically a shortcut for calling `nest(2)`. To instantiate one and add it to the tree, you call the `indent` method, as in:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
q.indent {}
|
180
|
+
```
|
181
|
+
|
182
|
+
It accepts a block that specifies the contents that should be indented.
|
183
|
+
|
184
|
+
#### `LineSuffix`
|
185
|
+
|
186
|
+
There are times when you want something to be printed, but only just before the subsequent newline. It's not practical to constantly check where the line ends to avoid accidentally printing something in the middle of the line. This node instead buffers other nodes passed to it and flushes them before any newline. It can be used to implement trailing comments, for example, that should be printed after all source code has been flushed. To instantiate one and add it to the tree, you call the `line_suffix` method, as in:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
q.line_suffix {}
|
190
|
+
```
|
191
|
+
|
192
|
+
It accepts a block that specifies the contents that should be printed before the next newline.
|
193
|
+
|
194
|
+
#### `Trim`
|
195
|
+
|
196
|
+
This node trims all the indentation on the current line. It's a very niche use case, but necessary in specific circumstances. For example, if you're in the middle of a deeply indented node, but absolutely have to print the next content at the beginning of the next line (think something like `=begin` comments in Ruby). To instantiate one and add it to the tree, you call the `trim` method, as in:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
q.trim
|
200
|
+
```
|
201
|
+
|
202
|
+
Note that trim will only work if the output buffer supports modifying its contents, e.g., an array that we can call `pop` on.
|
203
|
+
|
204
|
+
### Helpers
|
205
|
+
|
206
|
+
When you're determining how to build your print tree, there are a couple of utilities that are provided to address some common use cases. They are listed below.
|
207
|
+
|
208
|
+
#### `current_group`
|
209
|
+
|
210
|
+
`current_group` returns the most-recently created group being built (i.e., the group whose block is being executed). Usually you won't need to access this information, as it's mostly here as a reflection API.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
q.current_group
|
214
|
+
```
|
215
|
+
|
216
|
+
#### `comma_breakable`
|
217
|
+
|
218
|
+
`comma_breakable` is a shortcut for calling `q.text(",")` and then `q.breakable` immediately after. It's relatively common when printing lists.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
q.comma_breakable
|
222
|
+
```
|
223
|
+
|
224
|
+
#### `fill_breakable`
|
225
|
+
|
226
|
+
Similar to `breakable`, except wrapped in a group. This is useful if you're trying to fill a line of contents as opposed to breaking every item up individually. This can transform output from:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
item1
|
230
|
+
item2
|
231
|
+
item3
|
232
|
+
item4
|
233
|
+
item5
|
234
|
+
```
|
235
|
+
|
236
|
+
to
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
item1 item2 item3
|
240
|
+
item4 item5
|
241
|
+
```
|
242
|
+
|
243
|
+
Contrast that will `breakable`, where everything would be forced onto its own line if it were in the same group.
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
q.fill_breakable
|
247
|
+
```
|
248
|
+
|
249
|
+
This method accepts the same arguments as the [breakable](#breakable) method.
|
250
|
+
|
251
|
+
#### `seplist`
|
252
|
+
|
253
|
+
Creates a separated list of elements, by default separated by the `comma_breakable` method. It will yield each element to a block that can be customized printing behavior for each one. For example, to print a separated array:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
q.seplist(%w[one two three]) { |element| q.text(element) }
|
257
|
+
```
|
258
|
+
|
259
|
+
This will result in commas and breakables being inserted between each element. To customize that separator, pass a proc as the second argument, as in:
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
separator = -> { q.text(" - ") }
|
263
|
+
q.seplist(%w[one two three], separator) { |element| q.text(element) }
|
264
|
+
```
|
265
|
+
|
266
|
+
If you're printing a list of elements and want to specify which method is called to create the iterator, you can pass an optional third argument that defaults to `:each`, as in:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
pairs = { one: "a", two: "b", three: "c" }
|
270
|
+
separator = -> { q.comma_breakable }
|
271
|
+
|
272
|
+
q.seplist(pairs, separator, :each_pair) do |(key, value)|
|
273
|
+
q.text(key)
|
274
|
+
q.text("=")
|
275
|
+
q.text(value)
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
#### `target`
|
280
|
+
|
281
|
+
`target` returns the current array that is being used to capture calls to node builder methods. It is always the contents of the most recently built node. For example, if you create a group and are inside the block specifying the contents, `target` will return the group's contents array. Usually you won't need to access this information, as it's mostly here as a reflection API.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
q.target
|
285
|
+
```
|
286
|
+
|
287
|
+
#### `with_target`
|
288
|
+
|
289
|
+
This method is used internally to control which node is currently capturing content from the node builder methods. You can optionally use it if, for some reason, you need the printer to put all of its contents into a specific array.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
target = []
|
293
|
+
q.with_target(target) {}
|
294
|
+
```
|
295
|
+
|
296
|
+
### Printing the tree
|
297
|
+
|
298
|
+
Now that the tree has been built, you can print its contents using the `flush` method. This will flush all of the contents of the printer to the output buffer specified when the printer was created. For example:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
q.flush
|
302
|
+
```
|
303
|
+
|
304
|
+
When `flush` is called, the output buffer receives the `<<` method for however many text segments ended up getting printed. For convenience, since creating a printer, building a tree, and printing a tree is so common, you can use the `PrettierPrinter.format` method, as in:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
PrettierPrinter.format(+"") do |q|
|
308
|
+
q.text("content")
|
309
|
+
end
|
310
|
+
```
|
311
|
+
|
312
|
+
This method will automatically call `flush` after the block has been run and return the output buffer.
|
313
|
+
|
314
|
+
### Examples
|
315
|
+
|
316
|
+
All of these APIs are made much more clear by a couple of examples. Below are a couple that should help elucidate how these methods fit together.
|
317
|
+
|
318
|
+
#### Printing an array
|
319
|
+
|
320
|
+
Let's say you wanted to pretty-print an array of strings. You would:
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
def print_array(array)
|
324
|
+
PrettierPrinter.format(+"") do |q|
|
325
|
+
q.text("[")
|
326
|
+
q.indent do
|
327
|
+
q.breakable("")
|
328
|
+
q.seplist(array) { |element| q.text(element) }
|
329
|
+
end
|
330
|
+
|
331
|
+
q.breakable("")
|
332
|
+
q.text("]")
|
333
|
+
end
|
334
|
+
end
|
335
|
+
```
|
336
|
+
|
337
|
+
#### Printing a hash
|
338
|
+
|
339
|
+
Let's say you wanted to pretty-print a hash with symbol keys and string values. You would:
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
def print_hash(hash)
|
343
|
+
PrettierPrinter.format(+"") do |q|
|
344
|
+
q.text("{")
|
345
|
+
q.indent do
|
346
|
+
q.breakable
|
347
|
+
|
348
|
+
q.seplist(hash, -> { q.comma_breakable }) do |(key, value)|
|
349
|
+
q.group do
|
350
|
+
q.text(key)
|
351
|
+
q.text(":")
|
352
|
+
|
353
|
+
q.indent do
|
354
|
+
q.breakable
|
355
|
+
q.text(value)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
q.breakable
|
362
|
+
q.text("}")
|
363
|
+
end
|
364
|
+
end
|
365
|
+
```
|
366
|
+
|
367
|
+
#### Printing arithmetic
|
368
|
+
|
369
|
+
Let's say you had some arithmetic nodes that you wanted to print out recursively. You would:
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
Binary = Struct.new(:left, :operator, :right, keyword_init: true)
|
373
|
+
|
374
|
+
def print_binary(q, node)
|
375
|
+
case node
|
376
|
+
in Binary[left:, operator:, right:]
|
377
|
+
q.group do
|
378
|
+
print_binary(q, left)
|
379
|
+
q.text(" ")
|
380
|
+
q.text(operator)
|
381
|
+
|
382
|
+
q.indent do
|
383
|
+
q.breakable
|
384
|
+
print_binary(q, right)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
else
|
388
|
+
q.text(node)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
node =
|
393
|
+
Binary.new(
|
394
|
+
left: Binary.new(left: "1", operator: "+", right: "2"),
|
395
|
+
operator: "*",
|
396
|
+
right:
|
397
|
+
Binary.new(
|
398
|
+
left: "3",
|
399
|
+
operator: "-",
|
400
|
+
right: Binary.new(left: "5", operator: "*", right: "6")
|
401
|
+
)
|
402
|
+
)
|
403
|
+
|
404
|
+
puts PrettierPrint.format(+"") { |q| print_binary(q, node) }
|
405
|
+
```
|
406
|
+
|
407
|
+
#### Printing a file system
|
408
|
+
|
409
|
+
Let's say you wanted to print out a file system like the `tree` command. You would:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
def print_directory(q, entries)
|
413
|
+
grouped = entries.group_by { _1.include?("/") ? _1[0..._1.index("/")] : "." }
|
414
|
+
|
415
|
+
q.seplist(grouped["."], -> { q.breakable }) do |entry|
|
416
|
+
if grouped.key?(entry)
|
417
|
+
q.text(entry)
|
418
|
+
q.indent do
|
419
|
+
q.breakable
|
420
|
+
print_directory(q, grouped[entry].map! { _1[(entry.length + 1)..] })
|
421
|
+
end
|
422
|
+
else
|
423
|
+
q.text(entry)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
puts PrettierPrint.format(+"") { |q| print_directory(q, Dir["**/*"]) }
|
429
|
+
```
|
430
|
+
|
431
|
+
#### Other examples
|
432
|
+
|
433
|
+
There are lots of other examples that you can look at in other gems and files. Those include:
|
434
|
+
|
435
|
+
* [test/prettier_print_test.rb](test/prettier_print_test.rb) - the test file for this gem
|
436
|
+
* [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) - a formatter for Ruby code
|
437
|
+
* [Syntax Tree HAML plugin](https://github.com/ruby-syntax-tree/syntax_tree-haml) - a formatter for the HAML template language
|
438
|
+
* [Syntax Tree RBS plugin](https://github.com/ruby-syntax-tree/syntax_tree-rbs) - a formatter the RBS type specification language
|
439
|
+
|
440
|
+
## Contributing
|
441
|
+
|
442
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby-syntax-tree/prettier_print.
|
443
|
+
|
444
|
+
## License
|
445
|
+
|
446
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED