whirled_peas 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +116 -88
- data/Rakefile +1 -20
- data/bin/reset_cursor +11 -0
- data/bin/screen_test +68 -0
- data/examples/intro.rb +3 -3
- data/examples/scrolling.rb +5 -4
- data/lib/whirled_peas.rb +2 -4
- data/lib/whirled_peas/animator.rb +5 -0
- data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
- data/lib/whirled_peas/animator/easing.rb +72 -0
- data/lib/whirled_peas/animator/frame.rb +5 -0
- data/lib/whirled_peas/animator/frameset.rb +33 -0
- data/lib/whirled_peas/animator/producer.rb +35 -0
- data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
- data/lib/whirled_peas/command.rb +5 -0
- data/lib/whirled_peas/command/base.rb +86 -0
- data/lib/whirled_peas/command/config_command.rb +44 -0
- data/lib/whirled_peas/command/debug.rb +21 -0
- data/lib/whirled_peas/command/fonts.rb +22 -0
- data/lib/whirled_peas/command/frame_command.rb +34 -0
- data/lib/whirled_peas/command/frames.rb +24 -0
- data/lib/whirled_peas/command/help.rb +38 -0
- data/lib/whirled_peas/command/play.rb +108 -0
- data/lib/whirled_peas/command/record.rb +57 -0
- data/lib/whirled_peas/command/still.rb +29 -0
- data/lib/whirled_peas/command_line.rb +22 -212
- data/lib/whirled_peas/config.rb +56 -6
- data/lib/whirled_peas/device.rb +5 -0
- data/lib/whirled_peas/device/null_device.rb +8 -0
- data/lib/whirled_peas/device/output_file.rb +19 -0
- data/lib/whirled_peas/device/screen.rb +26 -0
- data/lib/whirled_peas/graphics/container_painter.rb +91 -0
- data/lib/whirled_peas/graphics/painter.rb +10 -0
- data/lib/whirled_peas/graphics/renderer.rb +8 -2
- data/lib/whirled_peas/utils/ansi.rb +13 -0
- data/lib/whirled_peas/utils/file_handler.rb +57 -0
- data/lib/whirled_peas/version.rb +1 -1
- data/tools/whirled_peas/tools/screen_tester.rb +117 -65
- metadata +27 -8
- data/lib/whirled_peas/frame.rb +0 -6
- data/lib/whirled_peas/frame/consumer.rb +0 -30
- data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
- data/lib/whirled_peas/frame/event_loop.rb +0 -90
- data/lib/whirled_peas/frame/producer.rb +0 -67
- data/lib/whirled_peas/graphics/screen.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9652a887fc6a68ba5367864a25b4a211674433eae86a2f68db3923e873ab49e8
|
4
|
+
data.tar.gz: 72926cd288adc3c30393990d88dcc0bca53e2fbfe79d56768e438ffaee2cf3ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e0124bd724ece36b76e1d527ffb3ccd5a06e9cd7f00c1ae5d0c39445d0c4ed4804d8c2cdc0c3df250f0d927b9927efd97d118fd0fd76bbc42277fda68a674cb
|
7
|
+
data.tar.gz: '09edc4873598d1363d7d3ef2c548699bcbfa229a8821e458c201608ec9f7e3aba8cf2f081f559edc51c04244267f569e8444b7952b21ab41bfea8a7807c0432a'
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.8.0 - 2021-01-29
|
4
|
+
|
5
|
+
BREAKING: `Producer` now responds to `#add_frame` instead of `#send_frame`
|
6
|
+
|
7
|
+
- [67fdc17](https://github.com/tcollier/whirled_peas/tree/67fdc172434cb97f31ca20a674f28aefec6babb3): Add easing functions for frame transitions
|
8
|
+
- [b888f2e](https://github.com/tcollier/whirled_peas/tree/b888f2e7a3b7c3341300326b417641cc8cf2e89b): Add record and playback functionality to command line tool
|
9
|
+
|
3
10
|
## v0.7.1 - 2021-01-27
|
4
11
|
|
5
12
|
- [b98781d](https://github.com/tcollier/whirled_peas/tree/b98781de23a24b596955f25cfc48936c0cc1efac): Fix num_cols bug for vertical grid flow
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
╚══╝╚══╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝
|
19
19
|
```
|
20
20
|
|
21
|
-
Easily create terminal-based graphics to
|
21
|
+
Easily create terminal-based graphics to visualize the execution of your code. Whirled Peas offers templating inspired by HTML and CSS and provides a lightweight tie-in for your code to produce visual animations with these templates.
|
22
22
|
|
23
23
|
## Installation
|
24
24
|
|
@@ -40,9 +40,8 @@ Or install it yourself as:
|
|
40
40
|
|
41
41
|
A Whirled Peas application consists of the following pieces
|
42
42
|
|
43
|
-
- The
|
44
|
-
- The main template factory (required) - builds templates to convert frame events from the
|
45
|
-
- A loading screen template factory (optional) - builds templates to display while content is loading
|
43
|
+
- The application (required) - the code that is to be visualized, it emits lightweight frame events through a producer
|
44
|
+
- The main template factory (required) - builds templates to convert frame events from the application into terminal graphics
|
46
45
|
|
47
46
|
These pieces are configured as following
|
48
47
|
|
@@ -62,15 +61,15 @@ class TemplateFactory
|
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
65
|
-
class
|
64
|
+
class Application
|
66
65
|
def start(producer)
|
67
|
-
producer.
|
66
|
+
producer.add_frame('starting', args: { name: 'World' })
|
68
67
|
# ...
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
72
71
|
WhirledPeas.configure do |config|
|
73
|
-
config.
|
72
|
+
config.application = Application.new
|
74
73
|
config.template_factory = TemplateFactory.new
|
75
74
|
end
|
76
75
|
```
|
@@ -78,34 +77,12 @@ end
|
|
78
77
|
Then the visualizer is started on the command line with
|
79
78
|
|
80
79
|
```
|
81
|
-
$ whirled_peas
|
80
|
+
$ whirled_peas play visualize.rb
|
82
81
|
```
|
83
82
|
|
84
|
-
|
83
|
+
### Application
|
85
84
|
|
86
|
-
|
87
|
-
class LoadingTemplateFactory
|
88
|
-
def build
|
89
|
-
WhirledPeas.template do |composer|
|
90
|
-
composer.add_box('Loading') do |_, settings|
|
91
|
-
settings.set_margin(top: 15)
|
92
|
-
settings.align = :center
|
93
|
-
settings.full_border(color: :blue, style: :double)
|
94
|
-
"Loading..."
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
WhirledPeas.configure do |config|
|
101
|
-
# ...
|
102
|
-
config.loading_template_factory = LoadingTemplateFactory.new
|
103
|
-
end
|
104
|
-
```
|
105
|
-
|
106
|
-
### Driver
|
107
|
-
|
108
|
-
The driver is the application code to be visualized. This is typically a lightweight wrapper around an existing application that conforms to the signature below.
|
85
|
+
The application is code to be visualized that integrates with Whirled Peas by providing the signature below
|
109
86
|
|
110
87
|
```ruby
|
111
88
|
# Start the application and pass frame events to the producer to be rendered by the UI
|
@@ -124,7 +101,7 @@ The producer provides a single method
|
|
124
101
|
# @param name [String] application defined name for the frame. The template factory will be provided this name
|
125
102
|
# @param duration [Number] time in seconds this frame should be displayed for (defaults to 1 frame)
|
126
103
|
# @param args [Hash<Symbol, Object>] key value pairs to send as arguments to the template factory
|
127
|
-
def
|
104
|
+
def add_frame(name, duration:, args:)
|
128
105
|
# implementation
|
129
106
|
end
|
130
107
|
```
|
@@ -136,39 +113,35 @@ end
|
|
136
113
|
Simple application that loads a set of numbers and looks for a pair that adds up to 1,000
|
137
114
|
|
138
115
|
```ruby
|
139
|
-
class
|
116
|
+
class Application
|
140
117
|
def start(producer)
|
141
118
|
numbers = File.readlines('/path/to/numbers.txt').map(&:to_i)
|
142
|
-
producer.
|
119
|
+
producer.add_frame('load-numbers', duration: 3, args: { numbers: numbers })
|
143
120
|
numbers.sort!
|
144
|
-
producer.
|
121
|
+
producer.add_frame('sort-numbers', duration: 3, args: { numbers: numbers })
|
145
122
|
low = 0
|
146
123
|
high = numbers.length - 1
|
147
124
|
while low < high
|
148
125
|
sum = numbers[low] + numbers[high]
|
149
126
|
if sum == 1000
|
150
|
-
producer.
|
127
|
+
producer.add_frame('found-pair', duration: 5, args: { low: low, high: high, sum: sum })
|
151
128
|
return
|
152
129
|
elsif sum < 1000
|
153
|
-
producer.
|
130
|
+
producer.add_frame('too-low', args: { low: low, high: high, sum: sum })
|
154
131
|
low += 1
|
155
132
|
else
|
156
|
-
producer.
|
133
|
+
producer.add_frame('too-high', args: { low: low, high: high, sum: sum })
|
157
134
|
high -= 1
|
158
135
|
end
|
159
136
|
end
|
160
|
-
producer.
|
137
|
+
producer.add_frame('no-solution', duration: 5)
|
161
138
|
end
|
162
139
|
end
|
163
140
|
```
|
164
141
|
|
165
142
|
### Template Factory
|
166
143
|
|
167
|
-
To render the frame events sent by the
|
168
|
-
|
169
|
-
#### Loading Template Factory
|
170
|
-
|
171
|
-
`WhirledPeas.configure` takes an optional template factory to build a loading screen. This instance must implement `#build` (taking no arguments). The template returned by that method will be painted while the event loop is waiting for frames. The factory method will be called once per refresh cycle, so it's possible to implement animation.
|
144
|
+
To render the frame events sent by the application, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the application. A template factory can be an instance of ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs.
|
172
145
|
|
173
146
|
#### Building Blocks
|
174
147
|
|
@@ -258,7 +231,7 @@ The available settigs are
|
|
258
231
|
| `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | No |
|
259
232
|
| `position` | Set the (left, top) position of the element relative to parent content area | `0` | `Box`, `Grid` | No |
|
260
233
|
| `scrollbar` | Display a scroll bar for vertical or horizontal scrolling | | `Box` | No |
|
261
|
-
| `sizing` | Sizing model (`:content` or `:border`) used in conjunction with `width`/`
|
234
|
+
| `sizing` | Sizing model (`:content` or `:border`) used in conjunction with `width`/`height` | `:content` | `Box` | No |
|
262
235
|
| `title_font` | Font used for "large" text (see [Large Text](#large-text), ignores `underline`) | | `Text` | No |
|
263
236
|
| `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Text` | Yes |
|
264
237
|
| `width` | Override the calculated width of an element's content area | | `Box`, `Grid` | No |
|
@@ -746,64 +719,106 @@ class TemplateFactory
|
|
746
719
|
end
|
747
720
|
```
|
748
721
|
|
749
|
-
###
|
722
|
+
### Full usage
|
723
|
+
|
724
|
+
```
|
725
|
+
Usage: whirled_peas <command> [command options]
|
750
726
|
|
751
|
-
|
727
|
+
Available commands:
|
752
728
|
|
753
|
-
|
729
|
+
debug Print template tree for specified frame
|
730
|
+
fonts List installed title fonts with sample text
|
731
|
+
frames Print out list of frames generated by application
|
732
|
+
help Show detailed help for a command
|
733
|
+
play Play an animation from an application or prerecorded file
|
734
|
+
record Record animation to a file
|
735
|
+
still Show the specified still frame
|
736
|
+
```
|
754
737
|
|
755
|
-
|
738
|
+
#### `debug`
|
739
|
+
|
740
|
+
Print the template tree for specified frame.
|
756
741
|
|
757
742
|
```
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
743
|
+
# Usage: whirled_peas debug <config file> <frame> [args as a JSON string]
|
744
|
+
% whirled_peas debug my_app.rb greeting '{"name":"World"}'
|
745
|
+
* WhirledPeas::Graphics::BoxPainter(TEMPLATE)
|
746
|
+
- Dimensions(outer=140x27, content=120x15, grid=1x1)
|
747
|
+
- Settings
|
748
|
+
WhirledPeas::Settings::BoxSettings
|
749
|
+
padding: Padding(left: 10, top: 6, right: 10, bottom: 6)
|
750
|
+
align: :center
|
751
|
+
width: 120
|
752
|
+
flow: :t2b
|
753
|
+
bold: true
|
754
|
+
bg_color: BgColor(code=107, bright=true)
|
755
|
+
- Children
|
756
|
+
* WhirledPeas::Graphics::BoxPainter(Element-1)
|
757
|
+
- Dimensions(outer=64x6, content=64x6, grid=1x1)
|
763
758
|
```
|
764
759
|
|
765
|
-
####
|
760
|
+
#### `fonts`
|
766
761
|
|
767
|
-
|
762
|
+
List all installed title fonts with sample text.
|
768
763
|
|
769
764
|
```
|
770
|
-
|
765
|
+
# Usage: whirled_peas fonts
|
771
766
|
```
|
772
767
|
|
773
|
-
|
768
|
+
#### `frames`
|
769
|
+
|
770
|
+
Print out list of frames generated by application.
|
774
771
|
|
775
772
|
```
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
<default>
|
781
|
-
- Children
|
782
|
-
+ TitleContainer [WhirledPeas::Graphics::BoxPainter]
|
773
|
+
# Usage: whirled_peas frames <config file>
|
774
|
+
% whirled_peas frames my_app.rb
|
775
|
+
Frame 'intro' displayed for 3 second(s) '{"title":"Foo"}'
|
776
|
+
Frame 'greet' displayed for 0.3 second(s)
|
783
777
|
...
|
784
778
|
```
|
785
779
|
|
786
|
-
|
780
|
+
#### `help`
|
787
781
|
|
788
|
-
|
782
|
+
Print out command-specific help message
|
789
783
|
|
790
|
-
|
784
|
+
```
|
785
|
+
Usage: whirled_peas help <command>
|
786
|
+
```
|
787
|
+
|
788
|
+
#### `play`
|
789
|
+
|
790
|
+
Play an animation from an application or prerecorded file
|
791
791
|
|
792
792
|
```
|
793
|
-
|
793
|
+
# Usage: whirled_peas play <config/wpz file>
|
794
|
+
|
795
|
+
# Play animation directly from app
|
796
|
+
% whirled_peas play my_app.rb
|
797
|
+
# Animation plays
|
798
|
+
|
799
|
+
# Play animation from previously recorded file
|
800
|
+
% whirled_peas play my_animation.wpz
|
801
|
+
# Animation plays
|
794
802
|
```
|
795
803
|
|
796
|
-
|
804
|
+
#### `record`
|
805
|
+
|
806
|
+
Record animation to a file
|
797
807
|
|
798
808
|
```
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
809
|
+
# Usage: whirled_peas record <config file> <output file>
|
810
|
+
% whirled_peas record my_app.rb my_animation.wpz
|
811
|
+
# Record animation to my_animation.wpz
|
812
|
+
```
|
813
|
+
|
814
|
+
#### `still`
|
815
|
+
|
816
|
+
Show the specified still frame
|
817
|
+
|
818
|
+
```
|
819
|
+
# Usage: whirled_peas still <config file> <frame> [args as a JSON string]
|
820
|
+
% whirled_peas still my_app.rb greeting '{"name":"World"}'
|
821
|
+
# Still frame is displayed
|
807
822
|
```
|
808
823
|
|
809
824
|
## Development
|
@@ -814,19 +829,28 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
814
829
|
|
815
830
|
### Testing
|
816
831
|
|
817
|
-
In addition to standard RSpec tests, WhirledPeas has custom tests for rendered templates. These files live in `screen_test
|
832
|
+
In addition to standard RSpec tests, WhirledPeas has custom tests for rendered templates. These files live in `screen_test/`. Each ruby file is expected to define a class named `TemplateFactory` that responds to `#build(name, args)` returning a template (the standard template factory role). Each file should also be accompanied by a `.frame` file with the same base name. This file will contain the output of the rendered screen and is considered the correct output when running tests.
|
818
833
|
|
819
834
|
Note: viewing `.frame` files with `cat` works better than most other text editors.
|
820
835
|
|
821
|
-
|
836
|
+
```
|
837
|
+
|
838
|
+
Usage: screen_test [file] [options]
|
839
|
+
|
840
|
+
If not file or options are provide, all tests are run
|
841
|
+
|
842
|
+
If no file is provided, the supported options are
|
843
|
+
--help print this usage statement and exit
|
844
|
+
--view-pending interactively display and optionally save rendered output for each pending test
|
845
|
+
--view-failed interactively display and optionally save rendered output for each faiing test
|
822
846
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
847
|
+
If the file provide is a screen test, the supported options are
|
848
|
+
--run run screen test for given file
|
849
|
+
--view interactively display and optionally save the file's test output
|
850
|
+
--template print out template tree for the test template
|
851
|
+
--debug render the test template without displying it, printing out debug information
|
852
|
+
|
853
|
+
```
|
830
854
|
|
831
855
|
## Contributing
|
832
856
|
|
@@ -839,3 +863,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
839
863
|
## Code of Conduct
|
840
864
|
|
841
865
|
Everyone interacting in the WhirledPeas project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/whirled_peas/blob/master/CODE_OF_CONDUCT.md).
|
866
|
+
|
867
|
+
```
|
868
|
+
|
869
|
+
```
|
data/Rakefile
CHANGED
@@ -3,29 +3,10 @@ require 'rspec/core/rake_task'
|
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
|
-
def screen_test(file, method)
|
7
|
-
raise ArgumentError, 'Missing argument: file' unless file
|
8
|
-
require_relative 'tools/whirled_peas/tools/screen_tester'
|
9
|
-
WhirledPeas::Tools::ScreenTester.new(file).send(method)
|
10
|
-
end
|
11
|
-
|
12
|
-
namespace :screen_test do
|
13
|
-
%i[template view run save update debug].each do |t|
|
14
|
-
task t, [:file] do |_, args|
|
15
|
-
screen_test(args[:file], t)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
task :update_all do
|
20
|
-
require_relative 'tools/whirled_peas/tools/screen_tester'
|
21
|
-
WhirledPeas::Tools::ScreenTester.update_all
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
6
|
task :screen_test do
|
26
7
|
require_relative 'tools/whirled_peas/tools/screen_tester'
|
27
8
|
WhirledPeas::Tools::ScreenTester.run_all
|
28
9
|
end
|
29
10
|
|
30
|
-
task default: :ci
|
31
11
|
task ci: %i[spec screen_test]
|
12
|
+
task default: :ci
|
data/bin/reset_cursor
ADDED
data/bin/screen_test
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'whirled_peas'
|
5
|
+
require 'whirled_peas/utils/formatted_string'
|
6
|
+
|
7
|
+
require_relative '../tools/whirled_peas/tools/screen_tester'
|
8
|
+
|
9
|
+
def print_usage(exit_code=0)
|
10
|
+
puts "Usage: #{$0} [file] [options]"
|
11
|
+
puts
|
12
|
+
puts 'If not file or options are provide, all tests are run'
|
13
|
+
puts
|
14
|
+
puts 'If no file is provided, the supported options are'
|
15
|
+
puts ' --help print this usage statement and exit'
|
16
|
+
puts ' --view-pending interactively display and optionally save rendered output for each pending test'
|
17
|
+
puts ' --view-failed interactively display and optionally save rendered output for each faiing test'
|
18
|
+
puts
|
19
|
+
puts 'If a screen test file is provided as the first argument, the supported options are'
|
20
|
+
puts ' --run run screen test for given file'
|
21
|
+
puts ' --view interactively display and optionally save the file\'s test output'
|
22
|
+
puts ' --template print out template tree for the test template'
|
23
|
+
puts ' --debug render the test template without displying it, printing out debug information'
|
24
|
+
exit(exit_code)
|
25
|
+
end
|
26
|
+
|
27
|
+
module WhirledPeas
|
28
|
+
if ARGV.length == 0
|
29
|
+
Tools::ScreenTester.run_all
|
30
|
+
elsif ARGV.length == 1
|
31
|
+
option = ARGV[0]
|
32
|
+
case option
|
33
|
+
when '--help'
|
34
|
+
print_usage
|
35
|
+
when '--view-pending'
|
36
|
+
Tools::ScreenTester.view_pending
|
37
|
+
when '--view-failed'
|
38
|
+
Tools::ScreenTester.view_failed
|
39
|
+
else
|
40
|
+
print_usage(1)
|
41
|
+
end
|
42
|
+
elsif ARGV.length == 2
|
43
|
+
file, option = ARGV
|
44
|
+
case option
|
45
|
+
when '--run'
|
46
|
+
tester = Tools::ScreenTester.new(file)
|
47
|
+
if tester.pending?
|
48
|
+
puts Utils::FormattedString.new('Pending', Settings::TextColor::YELLOW)
|
49
|
+
puts "Run `#{$0} #{file} --view` to view and optionally save ouput."
|
50
|
+
elsif tester.failed?
|
51
|
+
puts Utils::FormattedString.new('Failed', Settings::TextColor::RED)
|
52
|
+
puts "Run `#{$0} #{file} --view` to compare and optionally save ouput."
|
53
|
+
else
|
54
|
+
puts Utils::FormattedString.new('Pass', Settings::TextColor::GREEN)
|
55
|
+
end
|
56
|
+
when '--view'
|
57
|
+
Tools::ScreenTester.new(file).view
|
58
|
+
when '--template'
|
59
|
+
Tools::ScreenTester.new(file).template
|
60
|
+
when '--debug'
|
61
|
+
Tools::ScreenTester.new(file).debug
|
62
|
+
else
|
63
|
+
print_usage(1)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
print_usage(1)
|
67
|
+
end
|
68
|
+
end
|