scryglass 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.irbrc +9 -0
- data/CHANGELOG.md +46 -0
- data/Gemfile.lock +18 -1
- data/README.md +28 -15
- data/example_config.rb +12 -2
- data/lib/refinements/ansiless_string_refinement.rb +145 -0
- data/lib/refinements/array_fit_to_refinement.rb +30 -9
- data/lib/refinements/clip_string_refinement.rb +22 -7
- data/lib/scryglass.rb +78 -49
- data/lib/scryglass/binding_tracker.rb +10 -0
- data/lib/scryglass/config.rb +11 -2
- data/lib/scryglass/lens_helper.rb +52 -7
- data/lib/scryglass/lens_panel.rb +45 -21
- data/lib/scryglass/ro.rb +84 -15
- data/lib/scryglass/ro_builder.rb +95 -16
- data/lib/scryglass/session.rb +333 -126
- data/lib/scryglass/session_manager.rb +154 -0
- data/lib/scryglass/tree_panel.rb +14 -10
- data/lib/scryglass/version.rb +1 -1
- data/lib/scryglass/view_panel.rb +43 -1
- data/scryglass.gemspec +4 -1
- metadata +61 -3
- data/lib/refinements/ansi_slice_string_refinement.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b02a7943a8abec8175e8585b55127b197fdb7543c9015a2fbef908f29c99fa3
|
4
|
+
data.tar.gz: 1bdc489ea5afe33ddc5ffea4cc5fed26980fb158815d483d4a0f8c56a808cf75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a79a7e60525eb2c99a15d581c09c1f13e92b26dd7f4330c0c24ba974012a6a6b2e4605d6e45bec00ab0099dc84eae28e37007bca43bc105d23f278a92d40dcc
|
7
|
+
data.tar.gz: fab64fe8ad6d75a1e3d46753be7868ae40107fc9d5a1299291b6d4b03988cb612ce05087ccb35932b31cf75aeb9e20966392f4e34ae20f4a75962b882ff83072
|
data/.irbrc
ADDED
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## Added
|
11
|
+
|
12
|
+
## Changed
|
13
|
+
|
14
|
+
## Fixed
|
15
|
+
|
16
|
+
## [2.0.0] - 2021-01-13
|
17
|
+
|
18
|
+
## Added
|
19
|
+
|
20
|
+
- Turned on ANSI formatted/colored support with AnsiSliceStringRefinement.
|
21
|
+
- Added 'AmazingPrint' lens (colored) and gem.
|
22
|
+
- Added color formatting to (beta) method_showcase_for.
|
23
|
+
- Added "Smart Open" command 'o', which attempts to create sub-rows of the (next) most helpful type.
|
24
|
+
- README and help screen now point out that holding SHIFT will increase up/down step distance.
|
25
|
+
- Bottom and right edges of screen now indicate, with dots, when there is more beyond the view's edge.
|
26
|
+
- Added the VIM home row keybindings `h`/`j`/`k`/`l` as optional arrow keys.
|
27
|
+
- Now if the scry session hits an error, it will first ensure the error and console prompt appear below the present screen.
|
28
|
+
- Can now press `=` to give a console instance variable name to current objects without leaving scry session.
|
29
|
+
- Added popup messages for when the user attempts to create sub-rows for the current row and no sub-items are found.
|
30
|
+
- Added tab functionality to manage multiple scry session tabs for easy reference and comparison.
|
31
|
+
- Scryglass version now shows up in top right corner of the tree view when the header has no values to track.
|
32
|
+
- Improved and enabled 'Method Showcase' lens by default.
|
33
|
+
- Added `[<]`/`[>]` key reminders to Lens View.
|
34
|
+
|
35
|
+
## Changed
|
36
|
+
|
37
|
+
- Changed user_signal timeout period from 0.1sec to 0.3sec to reduce number of coincidentally dropped inputs.
|
38
|
+
- Changed AnsiSliceStringRefinement syntax even closer to 'string'[args] (supporting [i, l] syntax).
|
39
|
+
- Changed the keys for switching subject type and lens from `L`/`l` to `<`/`>` (To make room for vim h/j/k/l keybindings)
|
40
|
+
- Expanded list of "Patient Actions" which won't beep even if that procedure (sometimes user input) took longer than 4 seconds.
|
41
|
+
- Improved popup messages QOL (they now stack properly and don't make the user wait for them to disappear).
|
42
|
+
- Removed `scry_resume` command; bare `scry` now always resumes last session even if the current console receiver isn't `main`.
|
43
|
+
- Made help screen key text blue.
|
44
|
+
|
45
|
+
## Fixed
|
46
|
+
|
47
|
+
- Some more fixes to support (BETA) method_showcase_for:
|
48
|
+
- Added method_source gem.
|
49
|
+
- Now requiring lens_helper.
|
50
|
+
- Changed method to be callable externally
|
51
|
+
- Extra view margin no longer producible at far end of ANSI strings
|
52
|
+
- Escape true newlines returned by objects with unexpected `.inspect` results, which otherwise messes up the display.
|
53
|
+
- Cursor indicators ( `(`/`@`/`·` ) can no longer stall or error on exceptional objects; they now show up as `X` if they error or take too long (0.05s).
|
54
|
+
- When quitting from the help screen, cursor and prompt are now set all the way at the bottom of the display, rather than where the content ends on the current *non-help* panel.
|
55
|
+
|
10
56
|
## [1.1.0] - 2020-09-21
|
11
57
|
|
12
58
|
## Added
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
scryglass (
|
4
|
+
scryglass (2.0.0)
|
5
|
+
amazing_print
|
6
|
+
binding_of_caller
|
7
|
+
method_source
|
5
8
|
|
6
9
|
GEM
|
7
10
|
remote: https://rubygems.org/
|
8
11
|
specs:
|
12
|
+
amazing_print (1.2.1)
|
13
|
+
binding_of_caller (0.8.0)
|
14
|
+
debug_inspector (>= 0.0.1)
|
15
|
+
coderay (1.1.3)
|
16
|
+
debug_inspector (0.0.3)
|
17
|
+
interception (0.5)
|
18
|
+
method_source (1.0.0)
|
19
|
+
pry (0.13.1)
|
20
|
+
coderay (~> 1.1)
|
21
|
+
method_source (~> 1.0)
|
22
|
+
pry-rescue (1.5.2)
|
23
|
+
interception (>= 0.5)
|
24
|
+
pry (>= 0.12.0)
|
9
25
|
rake (12.3.3)
|
10
26
|
|
11
27
|
PLATFORMS
|
@@ -13,6 +29,7 @@ PLATFORMS
|
|
13
29
|
|
14
30
|
DEPENDENCIES
|
15
31
|
bundler (~> 2.1)
|
32
|
+
pry-rescue
|
16
33
|
rake (~> 12.0)
|
17
34
|
scryglass!
|
18
35
|
|
data/README.md
CHANGED
@@ -87,9 +87,6 @@ To start a Scry Session, call:
|
|
87
87
|
**A note about passing an argument without parentheses:**
|
88
88
|
> The arg syntax (`scry my_object`) will get confused if it's given a hash direcly (`scry {a: [1, 2] }`), thinking you're trying to pass a block, unless you use parentheses (`scry({a: [1, 2] })`).
|
89
89
|
|
90
|
-
**A note about using the resume session command, while in a pry:**
|
91
|
-
> The straight resume command, the bare `scry`, relies on the assumption that the method receiver is `main`. When your console is a pry in some other code, `self` is no longer `main`, but some other object, and so you are actually calling `scry` on that, overwriting your previous session. If you still want to resume session in that context, you can use `scry_resume`.
|
92
|
-
|
93
90
|
## Basic Usage
|
94
91
|
|
95
92
|
Use the arrow keys to move around and open/close known Enumerable types! Hit `'?'` to view all the controls and learn how to do much much more.
|
@@ -130,40 +127,46 @@ The cursor, movable by arrow keys, is represented by a straight line (`–––
|
|
130
127
|
| `––––` | ...no secret contents |
|
131
128
|
| `(–––` | ...a non-empty Enumerable of an unknown type (openable with `'('`) |
|
132
129
|
| `–@––` | ...instance variables on the object (openable with `'@'`) |
|
133
|
-
|
|
130
|
+
| `––·–` | ...ActiveRecord associations (openable with `'.'`) |
|
131
|
+
| `(@·–` | ...all three! (Note: ***Generally* IVs yield more sub-items with more info than using `'('`).** |
|
134
132
|
|
135
|
-
|
133
|
+
An `X` in place of any of these characters indicates an error or a timeout (if the "counting" process takes longer than 0.05 seconds)
|
136
134
|
|
137
135
|
A single `•` will mark the presence of user-added rows when they are hidden.
|
138
136
|
|
139
137
|
### Waiting!
|
140
138
|
Scryglass has two features to make wait time a little easier:
|
141
|
-
- If any process takes longer than 4 seconds between you pressing a key and the process completing, it **makes a beep sound!** This means if something seems like it might take a bit, you can switch to another tab or window without worry, and it will tell you when to check back.
|
139
|
+
- If any process (with a couple exceptions for user input) takes longer than 4 seconds between you pressing a key and the process completing, it **makes a beep sound!** This means if something seems like it might take a bit, you can switch to another tab or window without worry, and it will tell you when to check back.
|
142
140
|
- While there are no time estimates (for a number of reasons), many subprocesses are linked to a **progress bar**, which will display at the bottom of the screen. If multiple nested processes are running one within another, the progress bar will divide itself into parts to show each process. The leftmost bar is the base level iteration task.
|
143
141
|
|
144
142
|
## In-Depth Control Rundown
|
145
143
|
|
146
|
-
| Key |
|
144
|
+
| Key | Help_Screen_Snippet | Verbose Description |
|
147
145
|
|:---:|---------------------|---------------------|
|
148
146
|
| `?` | Press '?' for controls | `?` will cycle through the help panels, then back to the scry session. |
|
149
147
|
| `q` | Quit Scry | Exits the scry session, returning nil. The cursor (and exit message) is then placed below the last line console line with content in order to take up no more space than needed. |
|
150
|
-
| `UP`/`DOWN` | Navigate (
|
151
|
-
| `RIGHT` | Expand current or selected row(s) | If any rows are *selected* this attempts to expand all of them, and will expand the ones it can. If none are selected, then it will attempt to expand the current row where the cursor is. If the current row has preexisting sub-items, but they are hidden because the current
|
148
|
+
| `UP`/`DOWN` | Navigate (To move further, type a number first or use SHIFT) | Moves the cursor one step upward or downward in the tree view (this can be done while in lens view). If a number (of any number of digits) is typed out before pressing `UP` or `DOWN`, then the cursor will move that many steps in that direction. If `SHIFT` is held while pressing, then the cursor will move 12 steps in that direction. If the number of steps goes past the edge of the list, the cursor will sit safely at that edge. |
|
149
|
+
| `RIGHT` | Expand current or selected row(s) | If any rows are *selected* this attempts to expand all of them, and will expand the ones it can. If none are selected, then it will attempt to expand the current row where the cursor is. If the current row has preexisting sub-items, but they are hidden because the current row is collapsed, this will reveal them in the tree view. |
|
152
150
|
| `LEFT` | Collapse current or selected row(s) | If any rows are *selected* this attempts to collapse all of them, and will collapse the ones it can. If none are selected, then it will attempt to collapse the current row where the cursor is. If the current row either has no sub-items or is already collapsed, this action will collapse its parent row instead and place the cursor there. |
|
151
|
+
| `h`/`j`/`k`/`l` | (These keys on the home row can also serve as arrow keys) | This is for those familiar with VIM keybindings! Shift speeds up k/j to 12 steps just the same as UP/DOWN. |
|
153
152
|
| `ENTER` | Close Scry, returning current or selected object(s) (Key or Value) | Returns the subject object (based on current subject type, :value or :key) of the current item, or, if any items are selected (`->`), it returns all of those in an array. The order matches the order in which they were marked as selected. In the case of \| and \*, the order of the array will be top to bottom. If the current Subject Type (toggled by `L`) is :key, rows without "keys" will return `nil`. |
|
154
153
|
| `SPACEBAR` | Toggle Lens View | Switches between Tree View and Lens View. This will not change the view position of the lens view, but the view position of the tree view will still follow the cursor if the cursor moves while in lens view. |
|
155
|
-
|
|
156
|
-
|
|
157
|
-
| `w`/`a`/`s`/`d` | Move view window (ALT increases speed) | The W/A/S/D keys form a second set of arrow keys for moving around the "screen" through which you view the tree view and the lens view, when the contents don't all fit on the screen at once. They move 5 cells in the
|
154
|
+
| `>` | Cycle through lens types | Cycles through the different lens types in the lens view. These all take the current row, at either the "key" or the "value" object depending on the current subject type (toggled by `L`), and display a string of it, transformed through that particular lens. New lenses can be written in the config. |
|
155
|
+
| `<` | Toggle subject (Key/Value of row) | This change is only perceptible in the lens view, but does also change which objects are returned by `ENTER`. Any objects without "keys" will return nil for their :key if they don't have one. |
|
156
|
+
| `w`/`a`/`s`/`d` | Move view window (ALT increases speed) | The W/A/S/D keys form a second set of arrow keys for moving around the "screen" through which you view the tree view and the lens view, when the contents don't all fit on the screen at once. They move 5 cells in the specified direction, or 50 if ALT is held before pressing. Can be held down for continuous movement. |
|
158
157
|
| `0` | Reset view location (Press again: reset cursor) | This resets/zeros the current view (tree or lens). If you are in the tree view, and the view is in the zero (top left) position, then this will instead move the cursor there. |
|
159
158
|
| `@` | Build instance variable sub-rows for current or selected row(s) | Identifies all instance variables on the object (or value of a key-value pair) of the current or selected rows. Then these instance variables are turned into a list of keys, called on the original object, and then paired with the resulting objects. Known Enumerables are recursively navigable as always. |
|
160
|
-
| `.` | Build ActiveRecord association sub-rows for current or selected row(s) | If the `ActiveRecord` constant is not defined by the system, this will do nothing. If it is, this will navigate the reflections of the the class of the object (or value of a key-value pair) in order to find its AR Associations and turn them into key-value sub-items. Note: With the default configuration, the way it uses reflections *purposefully ignores `:through` relations and `scope`d relations (e.g. the extraneous `CURRENT_phone_numbers`).* |
|
159
|
+
| `.` | Build ActiveRecord association sub-rows for current or selected row(s) | If the `ActiveRecord` constant is not defined by the system, this will do nothing. If it is, this will navigate the reflections of the the class of the object (or value of a key-value pair) in order to find its AR Associations and turn them into key-value sub-items. Note: With the default configuration, the way it uses reflections *purposefully ignores `:through` relations and `scope`d relations (e.g. the extraneous `CURRENT_phone_numbers`).* Note: The `·` cursor indicator does not take the time to traverse all reflections, nor account for all configured filters. It's possible that `.` will produce no sub-rows despite the indicator. |
|
161
160
|
| `(` | Attempt to smart-build sub-rows for current or selected row(s), if Enumerable. Usually '@' is preferable | Attempts a "smart reading" of the object (or value of a key-value pair) of the current or selected rows. If the object is an Enumerable, it will attempt to parse it into sub-items. if the object has keys, it will be parsed as key-value pairs like a hash, otherwise singular objects like an array. This can sometimes create sub-items that would otherwise be more neatly accesible under a single instance variable if instance variables are built instead, so default to trying that first. |
|
161
|
+
| `o` | Quick Open: builds the most likely helpful sub-rows ( '.' \|\| '@' \|\| '(' ) | "Quick Open" first tries opening AR Associations. If none are produced, it tries for instance variables. If none are produced, it attempts to open as it would an unknown Enumerable type. Can be pressed repeatedly to produce all types of sub-rows. |
|
162
162
|
| `*` | Select/Deselect ALL rows | This includes hidden rows. If all rows are already selected, they will be unselected, regardless of how they became selected. |
|
163
163
|
| `\|` | Select/Deselect every sibling row under the same parent row | If all siblings under that parent row are already selected, they will be unselected, regardless of how they became selected. |
|
164
164
|
| `-` | Select/Deselect current row | If these objects are later returned, the order in which they were selected will determine their order in the returned array. |
|
165
|
+
| `Tab` | Change session tab (to the right) (`Shift`+`Tab` moves left) | Changes which scry session is the current one, and brings up the tab bar for a couple seconds. |
|
166
|
+
| `Q` | Close current session tab | Permanently exits the current session tab, but keeps scry running if another tab remains. Switches one tab to left if there is one.
|
165
167
|
| `/` | Begin a text search (in tree view) | Begins a case-sensitive regex search of all items, in a loop, starting with just below the current row. For a matching object to be found, the search must match its *truncated sample string in tree view* (regardless of what is on or off screen) (it must match either the key or the value, not the full line they create) (known enumerable types, like `[•••]`, may count as a match if they contain the string in the backend). |
|
166
168
|
| `n` | Move to next search result | Will, using the most recent search entry, move the cursor on to the next match downward, cycling through all rows. This follows the same matching rules as the original search. |
|
169
|
+
| `=` | Open prompt to type a console handle for current or selected row(s) | Gets text from user (not including the '@') and saves the current subject, or array of selected row(s) subjects, under that instance variable name. These variables live in the console itself: the binding of wherever `scry` was called. Care is taken not to allow them to conflict with preexisting IV names or method names. Note: But still, if you are prying inside an object/context, your IVs will be defined on that object. Note: If you switch from one pry context to another and then back, your first pry's instance variables will be there despite not being listed in the IV outro message. |
|
167
170
|
| `Esc` | Resets selection, last search, and number-to-move. (or returns to Tree View) | (Essentially, clears the values represented in the Tree View header if you're in the Tree View; otherwise it returns you to the Tree View) |
|
168
171
|
|
169
172
|
## Configuration / Customization (all optional)
|
@@ -190,6 +193,8 @@ Scryglass.configure do |config|
|
|
190
193
|
## UX
|
191
194
|
# config.cursor_tracking = [:flexible_range, :dead_center][0] # Default: [0]
|
192
195
|
# config.lenses = [ # Custom lenses can easily be added as name+lambda hashes! Or comment some out to turn them off.
|
196
|
+
# { name: 'Amazing Print (`ap`)',
|
197
|
+
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { ap o } } }, # This has colors!
|
193
198
|
# { name: 'Pretty Print (`pp`)',
|
194
199
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { pp o } } },
|
195
200
|
# { name: 'Inspect (`.inspect`)',
|
@@ -198,10 +203,18 @@ Scryglass.configure do |config|
|
|
198
203
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { require 'yaml' ; y o } } }, # OR: `puts o.to_yaml`
|
199
204
|
# { name: 'Puts (`puts`)',
|
200
205
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o } } },
|
201
|
-
#
|
202
|
-
#
|
206
|
+
# { name: 'Method Showcase',
|
207
|
+
# lambda: ->(o) { Scryglass::LensHelper.method_showcase_for(o) } },
|
203
208
|
# ]
|
204
209
|
|
210
|
+
## AmazingPrint defaults, if the user has not set their own:
|
211
|
+
# AmazingPrint.defaults ||= {
|
212
|
+
# index: false, # (Don't display array indices).
|
213
|
+
# raw: true, # (Recursively format instance variables).
|
214
|
+
# }
|
215
|
+
# See https://github.com/amazing-print/amazing_print
|
216
|
+
|
217
|
+
|
205
218
|
## Building ActiveRecord association sub-rows:
|
206
219
|
# config.include_empty_associations = true # Default: true
|
207
220
|
# config.include_through_associations = false # Default: false
|
data/example_config.rb
CHANGED
@@ -12,16 +12,26 @@ Scryglass.configure do |config|
|
|
12
12
|
# config.lenses = [ # Custom lenses can easily be added as name+lambda hashes! Or comment some out to turn them off.
|
13
13
|
# { name: 'Pretty Print (`pp`)',
|
14
14
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { pp o } } },
|
15
|
+
# { name: 'Amazing Print (`ap`)',
|
16
|
+
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { ap o } } }, # This has colors!
|
15
17
|
# { name: 'Inspect (`.inspect`)',
|
16
18
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o.inspect } } },
|
17
19
|
# { name: 'Yaml Print (`y`)',
|
18
20
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { require 'yaml' ; y o } } }, # OR: `puts o.to_yaml`
|
19
21
|
# { name: 'Puts (`puts`)',
|
20
22
|
# lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o } } },
|
21
|
-
#
|
22
|
-
#
|
23
|
+
# { name: 'Method Showcase',
|
24
|
+
# lambda: ->(o) { Scryglass::LensHelper.method_showcase_for(o) } },
|
23
25
|
# ]
|
24
26
|
|
27
|
+
## AmazingPrint defaults, if the user has not set their own:
|
28
|
+
# AmazingPrint.defaults ||= {
|
29
|
+
# index: false, # (Don't display array indices).
|
30
|
+
# raw: true, # (Recursively format instance variables).
|
31
|
+
# }
|
32
|
+
# See https://github.com/amazing-print/amazing_print
|
33
|
+
|
34
|
+
|
25
35
|
## Building ActiveRecord association sub-rows:
|
26
36
|
# config.include_empty_associations = true # Default: true
|
27
37
|
# config.include_through_associations = false # Default: false
|
@@ -7,5 +7,150 @@ module AnsilessStringRefinement
|
|
7
7
|
def ansiless_length
|
8
8
|
ansiless.length
|
9
9
|
end
|
10
|
+
|
11
|
+
## Splits string into characters, with each ANSI escape code being its own
|
12
|
+
## grouped item, like so:
|
13
|
+
## irb> "PLAIN\e[32mCOLOR\e[0mPLAIN".ansi_string_breakout
|
14
|
+
## => ["P", "L", "A", "I", "N", "\e[32m", "C", "O", "L", "O", "R",
|
15
|
+
## "\e[0m", "P", "L", "A", "I", "N"]
|
16
|
+
def ansi_string_breakout
|
17
|
+
breakout_array = []
|
18
|
+
working_self = self.dup
|
19
|
+
|
20
|
+
while working_self[0]
|
21
|
+
if (working_self =~ /\e\[[\d\;]*m/) == 0 # if begins with
|
22
|
+
end_of_escape_code = (working_self.index('m'))
|
23
|
+
leading_escape_code = working_self[0..end_of_escape_code]
|
24
|
+
breakout_array << leading_escape_code
|
25
|
+
working_self = working_self[(end_of_escape_code + 1)..-1]
|
26
|
+
else
|
27
|
+
leading_character = working_self[0]
|
28
|
+
breakout_array << leading_character
|
29
|
+
working_self = working_self[1..-1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
breakout_array
|
34
|
+
end
|
35
|
+
|
36
|
+
def is_ansi_escape_code?
|
37
|
+
(self =~ /^\e\[[\d\;]*m$/) == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
## Returns the indexed character of the real string, determined by the index
|
41
|
+
## given in reference to its ANSIless display form. e.g.:
|
42
|
+
## > s = "\e[31mTEST\e[00m"
|
43
|
+
## > puts s
|
44
|
+
## TEST
|
45
|
+
## > s.ansiless_pick(1) = 'y'
|
46
|
+
## > s
|
47
|
+
## => "\e[31mTyST\e[00m"
|
48
|
+
def ansiless_pick(given_index)
|
49
|
+
ansiless_self = self.ansiless
|
50
|
+
|
51
|
+
return nil if ansiless_self[given_index].nil?
|
52
|
+
|
53
|
+
given_index = (given_index + ansiless_self.length) if given_index.negative?
|
54
|
+
|
55
|
+
mock_index = 0 # A scanning index that *doesn't* count ANSI codes
|
56
|
+
real_index = 0 # A scanning index that *does* count ANSI codes
|
57
|
+
|
58
|
+
ansi_string_array = ansi_string_breakout
|
59
|
+
|
60
|
+
until mock_index == given_index
|
61
|
+
while ansi_string_array.first.is_ansi_escape_code?
|
62
|
+
real_index += (ansi_string_array.shift.length)
|
63
|
+
end
|
64
|
+
|
65
|
+
ansi_string_array.shift
|
66
|
+
|
67
|
+
while ansi_string_array.first.is_ansi_escape_code?
|
68
|
+
real_index += (ansi_string_array.shift.length)
|
69
|
+
end
|
70
|
+
|
71
|
+
mock_index += 1
|
72
|
+
real_index += 1
|
73
|
+
end
|
74
|
+
|
75
|
+
self[real_index]
|
76
|
+
end
|
77
|
+
|
78
|
+
## Like ansiless_pick, but it can set that found string character instead
|
79
|
+
def ansiless_set!(given_index, string)
|
80
|
+
raise ArgumentError, 'First argument must be an Integer' unless given_index.is_a?(Integer)
|
81
|
+
raise ArgumentError, 'Second argument must be a String' unless string.is_a?(String)
|
82
|
+
|
83
|
+
ansiless_self = self.ansiless
|
84
|
+
|
85
|
+
return nil if ansiless_self[given_index].nil?
|
86
|
+
|
87
|
+
given_index = (given_index + ansiless_self.length) if given_index.negative?
|
88
|
+
|
89
|
+
|
90
|
+
new_string = string.to_s
|
91
|
+
|
92
|
+
mock_index = 0 # A scanning index that *doesn't* count ANSI codes
|
93
|
+
real_index = 0 # A scanning index that *does* count ANSI codes
|
94
|
+
|
95
|
+
ansi_string_array = ansi_string_breakout
|
96
|
+
|
97
|
+
until mock_index == given_index
|
98
|
+
while ansi_string_array.first.is_ansi_escape_code?
|
99
|
+
real_index += (ansi_string_array.shift.length)
|
100
|
+
end
|
101
|
+
|
102
|
+
ansi_string_array.shift
|
103
|
+
|
104
|
+
while ansi_string_array.first.is_ansi_escape_code?
|
105
|
+
real_index += (ansi_string_array.shift.length)
|
106
|
+
end
|
107
|
+
|
108
|
+
mock_index += 1
|
109
|
+
real_index += 1
|
110
|
+
end
|
111
|
+
|
112
|
+
self[real_index] = new_string
|
113
|
+
end
|
114
|
+
|
115
|
+
def ansi_slice(arg1, arg2 = nil) # i.e. like 'test'[0..2] and 'test'[2, 1]
|
116
|
+
unless (arg1.is_a?(Range) && arg2.nil?) ||
|
117
|
+
(arg1.is_a?(Integer) && arg2.is_a?(Integer))
|
118
|
+
raise ArgumentError, 'ansi_slice takes either a single Range ' \
|
119
|
+
'or two integers (index and length) as its arguments.'
|
120
|
+
end
|
121
|
+
|
122
|
+
target_range = arg1.is_a?(Range) ? arg1 : (arg1...(arg1 + arg2))
|
123
|
+
|
124
|
+
if target_range.min.negative? || target_range.max.negative?
|
125
|
+
raise ArgumentError, 'Range must be entirely positive!'
|
126
|
+
end
|
127
|
+
|
128
|
+
args = [arg1, arg2].compact
|
129
|
+
return self[*args] if (self =~ /\e\[[\d\;]*m/).nil? # No work need be done
|
130
|
+
|
131
|
+
## And here we match the normal `:[]` behavior outside of boundaries, e.g:
|
132
|
+
## irb> 'TEST'[4..9]
|
133
|
+
## => ""
|
134
|
+
## irb> 'TEST'[5..9]
|
135
|
+
## => nil
|
136
|
+
return nil if target_range.min > self.ansiless_length
|
137
|
+
|
138
|
+
mock_index = 0 # A scanning index that *doesn't* count ANSI codes
|
139
|
+
result_string_array = []
|
140
|
+
|
141
|
+
ansi_string_breakout.each do |char|
|
142
|
+
char_is_ansi_escape_code = char.is_ansi_escape_code?
|
143
|
+
within_target_range =
|
144
|
+
target_range.include?(mock_index)
|
145
|
+
|
146
|
+
if within_target_range || char_is_ansi_escape_code
|
147
|
+
result_string_array << char
|
148
|
+
end
|
149
|
+
|
150
|
+
mock_index += 1 unless char_is_ansi_escape_code
|
151
|
+
end
|
152
|
+
|
153
|
+
result_string_array.join('')
|
154
|
+
end
|
10
155
|
end
|
11
156
|
end
|
@@ -9,13 +9,19 @@ module ArrayFitToRefinement
|
|
9
9
|
length_result = string_array.join('').send(length_method)
|
10
10
|
|
11
11
|
if string_array.count < 2
|
12
|
-
return nonplural_solution(string_length_goal,
|
12
|
+
return nonplural_solution(string_length_goal,
|
13
|
+
length_result,
|
14
|
+
fill: fill,
|
15
|
+
ignore_ansi_codes: ignore_ansi_codes)
|
13
16
|
end
|
14
17
|
|
15
18
|
if length_result > string_length_goal
|
16
|
-
string_array.compress_to(string_length_goal,
|
19
|
+
string_array.compress_to(string_length_goal,
|
20
|
+
ignore_ansi_codes: ignore_ansi_codes)
|
17
21
|
elsif length_result < string_length_goal
|
18
|
-
string_array.expand_to(string_length_goal,
|
22
|
+
string_array.expand_to(string_length_goal,
|
23
|
+
ignore_ansi_codes: ignore_ansi_codes,
|
24
|
+
fill: fill)
|
19
25
|
else # If it joins to the right length already, we still want to return the expected number of strings.
|
20
26
|
spacers = [''] * (string_array.count - 1)
|
21
27
|
string_array.zip(spacers).flatten.compact
|
@@ -35,9 +41,12 @@ module ArrayFitToRefinement
|
|
35
41
|
longest_string_length = working_array.map { |s| s.send(length_method) }.max
|
36
42
|
slider_index = slider % working_array.count
|
37
43
|
if working_array[slider_index].send(length_method) >= longest_string_length
|
38
|
-
working_array[slider_index]
|
39
|
-
|
40
|
-
|
44
|
+
length_less_one = working_array[slider_index].send(length_method) - 1
|
45
|
+
clipped_by_one = working_array[slider_index].clip_at(
|
46
|
+
length_less_one,
|
47
|
+
ignore_ansi_codes: ignore_ansi_codes
|
48
|
+
)
|
49
|
+
working_array[slider_index] = clipped_by_one
|
41
50
|
end
|
42
51
|
slider += 1
|
43
52
|
end
|
@@ -69,11 +78,23 @@ module ArrayFitToRefinement
|
|
69
78
|
|
70
79
|
private
|
71
80
|
|
72
|
-
def nonplural_solution(string_length_goal,
|
81
|
+
def nonplural_solution(string_length_goal,
|
82
|
+
length_result,
|
83
|
+
fill:,
|
84
|
+
ignore_ansi_codes:)
|
73
85
|
return [fill * string_length_goal] if self.empty?
|
74
86
|
|
75
|
-
|
76
|
-
|
87
|
+
string_array = self.map(&:to_s) # This also acts to dup
|
88
|
+
|
89
|
+
if length_result > string_length_goal
|
90
|
+
string_array.compress_to(string_length_goal,
|
91
|
+
ignore_ansi_codes: ignore_ansi_codes)
|
92
|
+
elsif length_result < string_length_goal
|
93
|
+
remaining_space = string_length_goal - length_result
|
94
|
+
string_array.append(fill * remaining_space)
|
95
|
+
else # If it joins to the right length already, we still want to return the expected number of strings.
|
96
|
+
self + ['']
|
97
|
+
end
|
77
98
|
end
|
78
99
|
end
|
79
100
|
end
|