scryglass 0.1.0 → 2.0.1
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/.DS_Store +0 -0
- data/.irbrc +9 -0
- data/.tool-versions +1 -1
- data/CHANGELOG.md +119 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +18 -14
- data/README.md +54 -18
- data/example_config.rb +13 -3
- data/lib/example_material.rb +1 -1
- data/lib/hexes.rb +1 -1
- data/lib/refinements/ansiless_string_refinement.rb +145 -0
- data/lib/refinements/array_fit_to_refinement.rb +38 -5
- data/lib/refinements/clip_string_refinement.rb +22 -7
- data/lib/scryglass.rb +80 -48
- 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 +47 -23
- data/lib/scryglass/ro.rb +92 -27
- data/lib/scryglass/ro_builder.rb +96 -17
- data/lib/scryglass/session.rb +407 -157
- data/lib/scryglass/session_manager.rb +154 -0
- data/lib/scryglass/tree_panel.rb +42 -15
- data/lib/scryglass/version.rb +1 -1
- data/lib/scryglass/view_panel.rb +43 -1
- data/scryglass.gemspec +8 -4
- metadata +67 -19
data/lib/scryglass/ro.rb
CHANGED
@@ -43,14 +43,22 @@ module Scryglass
|
|
43
43
|
self.key_string = key.to_s.clip_at(key_clip_length)
|
44
44
|
self.key = key.model
|
45
45
|
else
|
46
|
-
|
46
|
+
# Note: `.inspect` may return *true newlines* for objects with a custom
|
47
|
+
# `.inspect`, which will sabotage scry's display, so we gsub thusly:
|
48
|
+
self.key_string = key.inspect
|
49
|
+
.gsub("\n", "\\n")
|
50
|
+
.clip_at(key_clip_length)
|
47
51
|
self.key = key
|
48
52
|
end
|
49
53
|
if val.class == Scryglass::ViewWrapper
|
50
54
|
self.value_string = val.to_s.clip_at(value_clip_length)
|
51
55
|
self.value = val.model
|
52
56
|
else
|
53
|
-
|
57
|
+
# Note: `.inspect` may return *true newlines* for objects with a custom
|
58
|
+
# `.inspect`, which will sabotage scry's display, so we gsub thusly:
|
59
|
+
self.value_string = val.inspect
|
60
|
+
.gsub("\n", "\\n")
|
61
|
+
.clip_at(value_clip_length)
|
54
62
|
self.value = val
|
55
63
|
end
|
56
64
|
|
@@ -78,9 +86,10 @@ module Scryglass
|
|
78
86
|
|
79
87
|
key_value_spacer =
|
80
88
|
key_value_pair? ? key_string + key_value_relationship_indicator : ''
|
81
|
-
|
89
|
+
dot = '•'
|
90
|
+
dot = "\e[36m#{dot}\e[00m" if Scryglass.config.dot_coloring # cyan then back to *default*
|
82
91
|
special_sub_ro_expansion_indicator =
|
83
|
-
special_sub_ros.any? && !expanded ?
|
92
|
+
special_sub_ros.any? && !expanded ? dot : ' '
|
84
93
|
|
85
94
|
left_fill_string + special_sub_ro_expansion_indicator +
|
86
95
|
key_value_spacer + value_indicator
|
@@ -114,19 +123,15 @@ module Scryglass
|
|
114
123
|
|
115
124
|
# (Used for recalculate_indeces after new Ros have been injected)
|
116
125
|
def next_ro_without_using_index
|
117
|
-
if sub_ros.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
upward_feeler_ro = self
|
125
|
-
until upward_feeler_ro.sibling_down.present? || upward_feeler_ro.top_ro?
|
126
|
-
upward_feeler_ro = upward_feeler_ro.parent_ro
|
127
|
-
end
|
128
|
-
upward_feeler_ro.sibling_down
|
126
|
+
return sub_ros.first if sub_ros.first
|
127
|
+
return nil if top_ro?
|
128
|
+
return sibling_down if sibling_down
|
129
|
+
|
130
|
+
upward_feeler_ro = self
|
131
|
+
until upward_feeler_ro.sibling_down || upward_feeler_ro.top_ro?
|
132
|
+
upward_feeler_ro = upward_feeler_ro.parent_ro
|
129
133
|
end
|
134
|
+
upward_feeler_ro.sibling_down
|
130
135
|
end
|
131
136
|
|
132
137
|
def sibling_down
|
@@ -172,12 +177,12 @@ module Scryglass
|
|
172
177
|
!!key_value_relationship_indicator
|
173
178
|
end
|
174
179
|
|
175
|
-
private
|
176
|
-
|
177
180
|
def special_sub_ros
|
178
181
|
sub_ros.select(&:special_sub_ro_type)
|
179
182
|
end
|
180
183
|
|
184
|
+
private
|
185
|
+
|
181
186
|
def normal_sub_ros
|
182
187
|
sub_ros.reject(&:special_sub_ro_type)
|
183
188
|
end
|
@@ -191,7 +196,9 @@ module Scryglass
|
|
191
196
|
# communicating with a solid preexisting symbol), but keeping the idea here:
|
192
197
|
# sub_ros_order_of_magnitude = normal_sub_ros.count.to_s.length
|
193
198
|
# wrappers.dup.insert(1, '•' * sub_ros_order_of_magnitude)
|
194
|
-
|
199
|
+
dots = '•••'
|
200
|
+
dots = "\e[36m#{dots}\e[00m" if Scryglass.config.dot_coloring # cyan then back to *default*
|
201
|
+
wrappers.dup.insert(1, dots)
|
195
202
|
else
|
196
203
|
wrappers
|
197
204
|
end
|
@@ -218,20 +225,78 @@ module Scryglass
|
|
218
225
|
(tab_length * depth) + consistent_margin
|
219
226
|
end
|
220
227
|
|
228
|
+
def cursor_char
|
229
|
+
Scryglass::Session::CURSOR_CHARACTER
|
230
|
+
end
|
231
|
+
|
221
232
|
def cursor_string
|
222
|
-
cursor =
|
233
|
+
cursor = cursor_char * cursor_length
|
234
|
+
|
235
|
+
cursor[0] = enum_status_char
|
236
|
+
cursor[1] = iv_status_char
|
237
|
+
cursor[2] = ar_status_char
|
223
238
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
239
|
+
cursor
|
240
|
+
end
|
241
|
+
|
242
|
+
def enum_status_char
|
243
|
+
enum_worth_checking = nugget? && value.is_a?(Enumerable)
|
244
|
+
return cursor_char unless enum_worth_checking
|
245
|
+
|
246
|
+
enum_check = Scryglass::Ro.safe_quick_check do
|
247
|
+
# value.any? Can take an eternity for a few specific objects, breaking
|
248
|
+
# the session when the cursor passes over them. Also breaks on read-
|
249
|
+
# locked IO objects.
|
250
|
+
enum_sub_ros.empty? && value.any?
|
228
251
|
end
|
229
252
|
|
230
|
-
|
231
|
-
|
253
|
+
return 'X' if enum_check.nil?
|
254
|
+
|
255
|
+
return '(' if enum_check
|
256
|
+
|
257
|
+
cursor_char
|
258
|
+
end
|
259
|
+
|
260
|
+
def iv_status_char
|
261
|
+
return cursor_char unless iv_sub_ros.empty?
|
262
|
+
|
263
|
+
iv_check = Scryglass::Ro.safe_quick_check do
|
264
|
+
value.instance_variables.any?
|
232
265
|
end
|
233
266
|
|
234
|
-
|
267
|
+
return 'X' if iv_check.nil?
|
268
|
+
|
269
|
+
return '@' if iv_check
|
270
|
+
|
271
|
+
cursor_char
|
272
|
+
end
|
273
|
+
|
274
|
+
def ar_status_char
|
275
|
+
return cursor_char unless ar_sub_ros.empty?
|
276
|
+
|
277
|
+
iv_check = Scryglass::Ro.safe_quick_check do
|
278
|
+
# Currently, this will always indicate hidden secrets if the object, with
|
279
|
+
# the given Scryglass config, doesn't yield any ar_sub_ros upon trying '.'
|
280
|
+
value.class.respond_to?(:reflections) # TODO: maybe dig more here?
|
281
|
+
end
|
282
|
+
|
283
|
+
return 'X' if iv_check.nil?
|
284
|
+
|
285
|
+
return '·' if iv_check
|
286
|
+
|
287
|
+
cursor_char
|
288
|
+
end
|
289
|
+
|
290
|
+
class << self
|
291
|
+
def safe_quick_check
|
292
|
+
begin
|
293
|
+
Timeout.timeout(0.05) do
|
294
|
+
yield
|
295
|
+
end
|
296
|
+
rescue
|
297
|
+
nil
|
298
|
+
end
|
299
|
+
end
|
235
300
|
end
|
236
301
|
end
|
237
302
|
end
|
data/lib/scryglass/ro_builder.rb
CHANGED
@@ -135,8 +135,49 @@ module Scryglass
|
|
135
135
|
new_ro
|
136
136
|
end
|
137
137
|
|
138
|
+
def add_message_no_items_found
|
139
|
+
message = { text: "No new sub-items were found with '#{last_keypress}'",
|
140
|
+
end_time: Time.now + 1.5 }
|
141
|
+
self.current_warning_messages << message
|
142
|
+
end
|
143
|
+
|
144
|
+
def smart_open_target_ros
|
145
|
+
original_ro_count = all_ros.count
|
146
|
+
original_special_sub_ro_count = current_ro.special_sub_ros.count
|
147
|
+
|
148
|
+
if special_command_targets.any?
|
149
|
+
task = Prog::Task.new(max_count: special_command_targets.count)
|
150
|
+
progress_bar << task
|
151
|
+
|
152
|
+
target_ros = special_command_targets.dup # dup because some commands
|
153
|
+
# create ros which are added to all_ros and then this process starts
|
154
|
+
# adding them to the list of things it tries to act on!
|
155
|
+
target_ros.each do |target_ro|
|
156
|
+
smart_open(target_ro)
|
157
|
+
task.tick
|
158
|
+
print_progress_bar
|
159
|
+
end
|
160
|
+
self.special_command_targets = []
|
161
|
+
else
|
162
|
+
smart_open(current_ro)
|
163
|
+
|
164
|
+
new_special_sub_ro_count = current_ro.special_sub_ros.count
|
165
|
+
new_sub_ros_were_added = new_special_sub_ro_count != original_special_sub_ro_count
|
166
|
+
|
167
|
+
if new_sub_ros_were_added
|
168
|
+
expand!(current_ro)
|
169
|
+
else
|
170
|
+
add_message_no_items_found
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
new_ro_count = all_ros.count
|
175
|
+
recalculate_indeces unless new_ro_count == original_ro_count
|
176
|
+
end
|
177
|
+
|
138
178
|
def build_instance_variables_for_target_ros
|
139
|
-
|
179
|
+
original_ro_count = all_ros.count
|
180
|
+
original_iv_sub_ro_count = current_ro.iv_sub_ros.count
|
140
181
|
|
141
182
|
if special_command_targets.any?
|
142
183
|
task = Prog::Task.new(max_count: special_command_targets.count)
|
@@ -153,15 +194,24 @@ module Scryglass
|
|
153
194
|
self.special_command_targets = []
|
154
195
|
else
|
155
196
|
build_iv_sub_ros_for(current_ro)
|
156
|
-
|
197
|
+
|
198
|
+
new_iv_sub_ro_count = current_ro.iv_sub_ros.count
|
199
|
+
new_iv_sub_ros_were_added = new_iv_sub_ro_count != original_iv_sub_ro_count
|
200
|
+
|
201
|
+
if new_iv_sub_ros_were_added
|
202
|
+
expand!(current_ro)
|
203
|
+
else
|
204
|
+
add_message_no_items_found
|
205
|
+
end
|
157
206
|
end
|
158
207
|
|
159
|
-
|
160
|
-
recalculate_indeces unless
|
208
|
+
new_ro_count = all_ros.count
|
209
|
+
recalculate_indeces unless new_ro_count == original_ro_count
|
161
210
|
end
|
162
211
|
|
163
212
|
def build_activerecord_relations_for_target_ros
|
164
|
-
|
213
|
+
original_ro_count = all_ros.count
|
214
|
+
original_ar_sub_ro_count = current_ro.ar_sub_ros.count
|
165
215
|
|
166
216
|
if special_command_targets.any?
|
167
217
|
task = Prog::Task.new(max_count: special_command_targets.count)
|
@@ -178,15 +228,24 @@ module Scryglass
|
|
178
228
|
self.special_command_targets = []
|
179
229
|
else
|
180
230
|
build_ar_sub_ros_for(current_ro)
|
181
|
-
|
231
|
+
|
232
|
+
new_ar_sub_ro_count = current_ro.ar_sub_ros.count
|
233
|
+
new_ar_sub_ros_were_added = new_ar_sub_ro_count != original_ar_sub_ro_count
|
234
|
+
|
235
|
+
if new_ar_sub_ros_were_added
|
236
|
+
expand!(current_ro)
|
237
|
+
else
|
238
|
+
add_message_no_items_found
|
239
|
+
end
|
182
240
|
end
|
183
|
-
|
241
|
+
new_ro_count = all_ros.count
|
184
242
|
|
185
|
-
recalculate_indeces unless
|
243
|
+
recalculate_indeces unless new_ro_count == original_ro_count
|
186
244
|
end
|
187
245
|
|
188
246
|
def build_enum_children_for_target_ros
|
189
|
-
|
247
|
+
original_ro_count = all_ros.count
|
248
|
+
original_enum_sub_ro_count = current_ro.enum_sub_ros.count
|
190
249
|
|
191
250
|
if special_command_targets.any?
|
192
251
|
task = Prog::Task.new(max_count: special_command_targets.count)
|
@@ -196,18 +255,32 @@ module Scryglass
|
|
196
255
|
# create ros which are added to all_ros and then this process starts
|
197
256
|
# adding them to the list of things it tries to act on!
|
198
257
|
target_ros.each do |target_ro|
|
199
|
-
|
258
|
+
build_enum_sub_ros_for(target_ro)
|
200
259
|
task.tick
|
201
260
|
print_progress_bar
|
202
261
|
end
|
203
262
|
self.special_command_targets = []
|
204
263
|
else
|
205
|
-
|
206
|
-
|
264
|
+
build_enum_sub_ros_for(current_ro)
|
265
|
+
|
266
|
+
new_enum_sub_ro_count = current_ro.enum_sub_ros.count
|
267
|
+
new_enum_sub_ros_were_added = new_enum_sub_ro_count != original_enum_sub_ro_count
|
268
|
+
|
269
|
+
if new_enum_sub_ros_were_added
|
270
|
+
expand!(current_ro)
|
271
|
+
else
|
272
|
+
add_message_no_items_found
|
273
|
+
end
|
207
274
|
end
|
208
275
|
|
209
|
-
|
210
|
-
recalculate_indeces unless
|
276
|
+
new_ro_count = all_ros.count
|
277
|
+
recalculate_indeces unless new_ro_count == original_ro_count
|
278
|
+
end
|
279
|
+
|
280
|
+
def smart_open(ro)
|
281
|
+
build_ar_sub_ros_for(ro) ||
|
282
|
+
build_iv_sub_ros_for(ro) ||
|
283
|
+
build_enum_sub_ros_for(ro)
|
211
284
|
end
|
212
285
|
|
213
286
|
def recalculate_indeces
|
@@ -254,6 +327,8 @@ module Scryglass
|
|
254
327
|
prog_task.tick
|
255
328
|
print_progress_bar
|
256
329
|
end
|
330
|
+
|
331
|
+
true
|
257
332
|
end
|
258
333
|
|
259
334
|
def build_ar_sub_ros_for(ro)
|
@@ -310,7 +385,7 @@ module Scryglass
|
|
310
385
|
through_indicator = is_through ? '(t)' : ' '
|
311
386
|
end
|
312
387
|
|
313
|
-
is_scoped = info.scope
|
388
|
+
is_scoped = !!info.scope
|
314
389
|
if include_scoped_associations
|
315
390
|
scoped_indicator = is_scoped ? '(s)' : ' '
|
316
391
|
end
|
@@ -322,7 +397,7 @@ module Scryglass
|
|
322
397
|
relation_name.to_s
|
323
398
|
end
|
324
399
|
|
325
|
-
if ar_value.
|
400
|
+
if (!ar_value || (ar_value.respond_to?(:empty?) && ar_value.empty?)) || include_empty_associations
|
326
401
|
ar_key = Scryglass::ViewWrapper.new(
|
327
402
|
relation_name,
|
328
403
|
string: relation_representation
|
@@ -338,9 +413,11 @@ module Scryglass
|
|
338
413
|
task.tick
|
339
414
|
print_progress_bar
|
340
415
|
end
|
416
|
+
|
417
|
+
true if ro.ar_sub_ros.any?
|
341
418
|
end
|
342
419
|
|
343
|
-
def
|
420
|
+
def build_enum_sub_ros_for(ro)
|
344
421
|
return if ro.enum_sub_ros.any?
|
345
422
|
return if ro.bucket?
|
346
423
|
return unless ro.value.is_a?(Enumerable)
|
@@ -383,6 +460,8 @@ module Scryglass
|
|
383
460
|
print_progress_bar
|
384
461
|
end
|
385
462
|
end
|
463
|
+
|
464
|
+
true
|
386
465
|
end
|
387
466
|
|
388
467
|
def rescue_to_viewwrapped_error
|
data/lib/scryglass/session.rb
CHANGED
@@ -9,15 +9,18 @@ class Scryglass::Session
|
|
9
9
|
|
10
10
|
attr_accessor :current_view_coords, :current_lens, :current_subject_type,
|
11
11
|
:view_panels, :current_panel_type,
|
12
|
-
:progress_bar
|
12
|
+
:progress_bar, :current_warning_messages
|
13
13
|
|
14
|
-
attr_accessor :
|
14
|
+
attr_accessor :user_signals, :last_search, :number_to_move
|
15
|
+
|
16
|
+
attr_accessor :session_manager, :signal_to_manager, :session_is_current,
|
17
|
+
:tab_icon, :session_view_start_time
|
15
18
|
|
16
19
|
CURSOR_CHARACTER = '–' # These are en dashes (alt+dash), not hyphens or em dashes.
|
17
20
|
|
18
|
-
SEARCH_PROMPT = "\e[7mSearch for (regex, case-sensitive):
|
21
|
+
SEARCH_PROMPT = "\e[7mSearch for (regex, case-sensitive): /\e[00m"
|
19
22
|
|
20
|
-
|
23
|
+
VARNAME_PROMPT = "\e[7mName your object(s): @\e[00m"
|
21
24
|
|
22
25
|
SUBJECT_TYPES = [
|
23
26
|
:value,
|
@@ -26,6 +29,66 @@ class Scryglass::Session
|
|
26
29
|
|
27
30
|
CSI = "\e[" # "(C)ontrol (S)equence (I)ntroducer" for ANSI sequences
|
28
31
|
|
32
|
+
KEY_MAP = {
|
33
|
+
escape: 'esc', # Not a normal keystroke, see: genuine_escape_key_press
|
34
|
+
ctrl_c: "\u0003",
|
35
|
+
quit_session: 'q',
|
36
|
+
delete_session_tab: 'Q',
|
37
|
+
change_session_right: "\t", # Tab
|
38
|
+
change_session_left: 'Z', # Shift+Tab (well, one of its signals, after "\e" and "[")
|
39
|
+
digit_1: '1',
|
40
|
+
digit_2: '2',
|
41
|
+
digit_3: '3',
|
42
|
+
digit_4: '4',
|
43
|
+
digit_5: '5',
|
44
|
+
digit_6: '6',
|
45
|
+
digit_7: '7',
|
46
|
+
digit_8: '8',
|
47
|
+
digit_9: '9',
|
48
|
+
digit_0: '0',
|
49
|
+
move_cursor_up: 'A', # Up arrow (well, one of its signals, after "\e" and "[")
|
50
|
+
move_cursor_down: 'B', # Down arrow (well, one of its signals, after "\e" and "[")
|
51
|
+
open_bucket: 'C', # Right arrow (well, one of its signals, after "\e" and "[")
|
52
|
+
close_bucket: 'D', # Left arrow (well, one of its signals, after "\e" and "[")
|
53
|
+
homerow_move_cursor_up: 'k', # To be like VIM arrow keys
|
54
|
+
homerow_move_cursor_up_fast: 'K', # To be like VIM arrow keys
|
55
|
+
homerow_move_cursor_down: 'j', # To be like VIM arrow keys
|
56
|
+
homerow_move_cursor_down_fast: 'J', # To be like VIM arrow keys
|
57
|
+
homerow_open_bucket: 'l', # To be like VIM arrow keys
|
58
|
+
homerow_close_bucket: 'h', # To be like VIM arrow keys
|
59
|
+
# Note, shift-UP and shift-DOWN are not here, as those work very
|
60
|
+
# differently: by virtue of the type-a-number-first functionality.
|
61
|
+
toggle_view_panel: ' ',
|
62
|
+
switch_lens: '>',
|
63
|
+
switch_subject_type: '<',
|
64
|
+
move_view_up: 'w',
|
65
|
+
move_view_down: 's',
|
66
|
+
move_view_left: 'a',
|
67
|
+
move_view_right: 'd',
|
68
|
+
move_view_up_fast: '∑', # Alt+w
|
69
|
+
move_view_down_fast: 'ß', # Alt+s
|
70
|
+
move_view_left_fast: 'å', # Alt+a
|
71
|
+
move_view_right_fast: '∂', # Alt+d
|
72
|
+
control_screen: '?',
|
73
|
+
build_instance_variables: '@',
|
74
|
+
build_ar_relations: '.',
|
75
|
+
build_enum_children: '(',
|
76
|
+
smart_open: 'o',
|
77
|
+
select_siblings: '|',
|
78
|
+
select_all: '*',
|
79
|
+
select_current: '-',
|
80
|
+
start_search: '/',
|
81
|
+
continue_search: 'n',
|
82
|
+
return_objects: "\r", # [ENTER],
|
83
|
+
name_objects: "="
|
84
|
+
}.freeze
|
85
|
+
|
86
|
+
PATIENT_ACTIONS = [
|
87
|
+
:control_screen,
|
88
|
+
:escape,
|
89
|
+
:name_objects,
|
90
|
+
].freeze
|
91
|
+
|
29
92
|
def initialize(seed)
|
30
93
|
self.all_ros = []
|
31
94
|
self.current_lens = 0
|
@@ -33,8 +96,14 @@ class Scryglass::Session
|
|
33
96
|
self.current_panel_type = :tree
|
34
97
|
self.special_command_targets = []
|
35
98
|
self.number_to_move = ''
|
36
|
-
self.
|
99
|
+
self.user_signals = []
|
37
100
|
self.progress_bar = Prog::Pipe.new
|
101
|
+
self.current_warning_messages = []
|
102
|
+
self.session_manager = nil
|
103
|
+
self.signal_to_manager = nil
|
104
|
+
self.tab_icon = nil
|
105
|
+
self.session_is_current = false
|
106
|
+
self.session_view_start_time = nil
|
38
107
|
|
39
108
|
top_ro = roify(seed, parent_ro: nil, depth: 1)
|
40
109
|
top_ro.has_cursor = true
|
@@ -48,150 +117,186 @@ class Scryglass::Session
|
|
48
117
|
}
|
49
118
|
end
|
50
119
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
120
|
+
def top_ro
|
121
|
+
all_ros.first
|
122
|
+
end
|
54
123
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
124
|
+
def last_keypress
|
125
|
+
last_two_signals = user_signals.last(2)
|
126
|
+
last_two_signals.last || last_two_signals.first
|
127
|
+
end
|
128
|
+
|
129
|
+
def run_scry_ui
|
130
|
+
redraw = true
|
131
|
+
signal_to_manager = nil
|
132
|
+
self.session_view_start_time = Time.now # For this particular tab/session
|
133
|
+
|
134
|
+
## On hold: Record/Playback Functionality:
|
135
|
+
# case actions
|
136
|
+
# when :record
|
137
|
+
# $scry_session_actions_performed = []
|
138
|
+
# when :playback
|
139
|
+
# if $scry_session_actions_performed.blank?
|
140
|
+
# raise 'Could not find recording of previous session\'s actions'
|
141
|
+
# end
|
142
|
+
# @input_stack = $scry_session_actions_performed.dup
|
143
|
+
# end
|
64
144
|
|
65
145
|
# We print a full screen of lines so the first call of draw_screen doesn't
|
66
146
|
# write over any previous valuable content the user had in the console.
|
67
147
|
print Hexes.opacify_screen_string(Hexes.simple_screen_slice(boot_screen))
|
68
148
|
|
69
|
-
while
|
149
|
+
while true
|
70
150
|
draw_screen if redraw
|
71
151
|
redraw = true
|
72
152
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
153
|
+
## On hold: Record/Playback Functionality:
|
154
|
+
# case actions
|
155
|
+
# when :record
|
156
|
+
# self.user_input = $stdin.getch
|
157
|
+
# $scry_session_actions_performed << user_input
|
158
|
+
# when :playback
|
159
|
+
# if @input_stack.any? # (IV to be easily accessible for debugging)
|
160
|
+
# self.user_input = @input_stack.shift
|
161
|
+
# sleep 0.05
|
162
|
+
# else
|
163
|
+
# self.user_input = $stdin.getch
|
164
|
+
# end
|
165
|
+
# else
|
166
|
+
# self.user_input = $stdin.getch
|
167
|
+
# end
|
168
|
+
|
169
|
+
new_signal = fetch_user_signal
|
87
170
|
|
88
171
|
wait_start_time = Time.now
|
89
172
|
|
90
|
-
case
|
91
|
-
when
|
173
|
+
case new_signal
|
174
|
+
when nil
|
175
|
+
when KEY_MAP[:escape]
|
176
|
+
case current_panel_type
|
177
|
+
when :lens
|
178
|
+
self.current_panel_type = :tree
|
179
|
+
when :tree
|
180
|
+
clear_tracked_values
|
181
|
+
end
|
182
|
+
when KEY_MAP[:ctrl_c]
|
92
183
|
set_console_cursor_below_content
|
93
184
|
raise IRB::Abort, 'Ctrl+C Detected'
|
94
|
-
when
|
95
|
-
|
96
|
-
|
97
|
-
when
|
185
|
+
when KEY_MAP[:quit_session]
|
186
|
+
self.signal_to_manager = :quit
|
187
|
+
return
|
188
|
+
when KEY_MAP[:delete_session_tab]
|
189
|
+
self.signal_to_manager = :delete
|
190
|
+
return
|
191
|
+
when KEY_MAP[:control_screen]
|
192
|
+
remain_in_scry_session = run_help_screen_ui
|
193
|
+
unless remain_in_scry_session
|
194
|
+
self.signal_to_manager = :quit_from_help
|
195
|
+
return
|
196
|
+
end
|
197
|
+
when KEY_MAP[:digit_1]
|
98
198
|
self.number_to_move += '1'
|
99
|
-
|
100
|
-
# quickly and still have it process all the digits
|
101
|
-
|
199
|
+
# This allows you to type multi-digit number very
|
200
|
+
# quickly and still have it process all the digits:
|
201
|
+
redraw = false
|
202
|
+
when KEY_MAP[:digit_2]
|
102
203
|
self.number_to_move += '2'
|
103
204
|
redraw = false
|
104
|
-
when
|
205
|
+
when KEY_MAP[:digit_3]
|
105
206
|
self.number_to_move += '3'
|
106
207
|
redraw = false
|
107
|
-
when
|
208
|
+
when KEY_MAP[:digit_4]
|
108
209
|
self.number_to_move += '4'
|
109
210
|
redraw = false
|
110
|
-
when
|
211
|
+
when KEY_MAP[:digit_5]
|
111
212
|
self.number_to_move += '5'
|
112
213
|
redraw = false
|
113
|
-
when
|
214
|
+
when KEY_MAP[:digit_6]
|
114
215
|
self.number_to_move += '6'
|
115
216
|
redraw = false
|
116
|
-
when
|
217
|
+
when KEY_MAP[:digit_7]
|
117
218
|
self.number_to_move += '7'
|
118
219
|
redraw = false
|
119
|
-
when
|
220
|
+
when KEY_MAP[:digit_8]
|
120
221
|
self.number_to_move += '8'
|
121
222
|
redraw = false
|
122
|
-
when
|
223
|
+
when KEY_MAP[:digit_9]
|
123
224
|
self.number_to_move += '9'
|
124
225
|
redraw = false
|
125
|
-
when
|
126
|
-
if number_to_move
|
226
|
+
when KEY_MAP[:digit_0]
|
227
|
+
if number_to_move[0] # You can append zeros to existing number_to_move...
|
127
228
|
self.number_to_move += '0'
|
128
229
|
redraw = false
|
129
230
|
else # ...but otherwise it's understood to be a view||cursor reset.
|
130
231
|
reset_the_view_or_cursor
|
131
232
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
tree_view.slide_view_to_cursor
|
139
|
-
when 'B' # Down arrow
|
140
|
-
action_count = number_to_move.present? ? number_to_move.to_i : 1
|
141
|
-
navigate_down_multiple(action_count)
|
142
|
-
|
143
|
-
self.number_to_move = ''
|
144
|
-
lens_view.recalculate_boundaries if current_panel_type == :lens
|
145
|
-
tree_view.slide_view_to_cursor
|
146
|
-
when 'C' # Right arrow
|
233
|
+
|
234
|
+
when KEY_MAP[:move_cursor_up]
|
235
|
+
move_cursor_up_action
|
236
|
+
when KEY_MAP[:move_cursor_down]
|
237
|
+
move_cursor_down_action
|
238
|
+
when KEY_MAP[:open_bucket]
|
147
239
|
expand_targets
|
148
|
-
when
|
240
|
+
when KEY_MAP[:close_bucket]
|
149
241
|
collapse_targets
|
150
|
-
|
151
|
-
when
|
242
|
+
|
243
|
+
when KEY_MAP[:homerow_move_cursor_up]
|
244
|
+
move_cursor_up_action
|
245
|
+
when KEY_MAP[:homerow_move_cursor_up_fast]
|
246
|
+
move_cursor_up_action(12) # 12 matches the digits provided by shift+up
|
247
|
+
when KEY_MAP[:homerow_move_cursor_down]
|
248
|
+
move_cursor_down_action
|
249
|
+
when KEY_MAP[:homerow_move_cursor_down_fast]
|
250
|
+
move_cursor_down_action(12) # 12 matches the digits provided by shift+down
|
251
|
+
when KEY_MAP[:homerow_open_bucket]
|
252
|
+
expand_targets
|
253
|
+
when KEY_MAP[:homerow_close_bucket]
|
254
|
+
collapse_targets
|
255
|
+
|
256
|
+
when KEY_MAP[:toggle_view_panel]
|
152
257
|
toggle_view_panel
|
153
|
-
|
154
|
-
when 'l'
|
258
|
+
when KEY_MAP[:switch_lens]
|
155
259
|
scroll_lens_type
|
156
|
-
|
157
|
-
when 'L'
|
260
|
+
when KEY_MAP[:switch_subject_type]
|
158
261
|
toggle_current_subject_type
|
159
|
-
|
160
|
-
when
|
262
|
+
|
263
|
+
when KEY_MAP[:move_view_up]
|
161
264
|
current_view_panel.move_view_up(5)
|
162
|
-
when
|
265
|
+
when KEY_MAP[:move_view_down]
|
163
266
|
current_view_panel.move_view_down(5)
|
164
|
-
when
|
267
|
+
when KEY_MAP[:move_view_left]
|
165
268
|
current_view_panel.move_view_left(5)
|
166
|
-
when
|
269
|
+
when KEY_MAP[:move_view_right]
|
167
270
|
current_view_panel.move_view_right(5)
|
168
|
-
|
271
|
+
|
272
|
+
when KEY_MAP[:move_view_up_fast]
|
169
273
|
current_view_panel.move_view_up(50)
|
170
|
-
when
|
274
|
+
when KEY_MAP[:move_view_down_fast]
|
171
275
|
current_view_panel.move_view_down(50)
|
172
|
-
when
|
276
|
+
when KEY_MAP[:move_view_left_fast]
|
173
277
|
current_view_panel.move_view_left(50)
|
174
|
-
when
|
278
|
+
when KEY_MAP[:move_view_right_fast]
|
175
279
|
current_view_panel.move_view_right(50)
|
176
|
-
|
177
|
-
|
178
|
-
when '@'
|
280
|
+
|
281
|
+
when KEY_MAP[:build_instance_variables]
|
179
282
|
build_instance_variables_for_target_ros
|
180
|
-
tree_view.recalculate_boundaries
|
181
283
|
tree_view.slide_view_to_cursor # Just a nice-to-have
|
182
|
-
when
|
284
|
+
when KEY_MAP[:build_ar_relations]
|
183
285
|
build_activerecord_relations_for_target_ros
|
184
|
-
tree_view.recalculate_boundaries
|
185
286
|
tree_view.slide_view_to_cursor # Just a nice-to-have
|
186
|
-
when
|
287
|
+
when KEY_MAP[:build_enum_children]
|
187
288
|
build_enum_children_for_target_ros
|
188
|
-
tree_view.recalculate_boundaries
|
189
289
|
tree_view.slide_view_to_cursor # Just a nice-to-have
|
190
|
-
when
|
290
|
+
when KEY_MAP[:smart_open]
|
291
|
+
smart_open_target_ros
|
292
|
+
tree_view.slide_view_to_cursor # Just a nice-to-have
|
293
|
+
|
294
|
+
when KEY_MAP[:select_siblings]
|
191
295
|
sibling_ros = if current_ro.top_ro?
|
192
296
|
[top_ro]
|
193
297
|
else
|
194
|
-
current_ro.parent_ro.sub_ros.dup
|
298
|
+
current_ro.parent_ro.sub_ros.dup
|
299
|
+
# ^If we don't dup,
|
195
300
|
# then '-' can remove ros from `sub_ros`.
|
196
301
|
end
|
197
302
|
if special_command_targets.sort == sibling_ros.sort
|
@@ -199,58 +304,158 @@ class Scryglass::Session
|
|
199
304
|
else
|
200
305
|
self.special_command_targets = sibling_ros
|
201
306
|
end
|
202
|
-
when
|
203
|
-
all_the_ros = all_ros.dup
|
307
|
+
when KEY_MAP[:select_all]
|
308
|
+
all_the_ros = all_ros.dup
|
309
|
+
# ^If we don't dup,
|
204
310
|
# then '-' can remove ros from all_ros.
|
205
311
|
if special_command_targets.sort == all_the_ros.sort
|
206
312
|
self.special_command_targets = []
|
207
313
|
else
|
208
314
|
self.special_command_targets = all_the_ros
|
209
315
|
end
|
210
|
-
when
|
316
|
+
when KEY_MAP[:select_current]
|
211
317
|
if special_command_targets.include?(current_ro)
|
212
318
|
special_command_targets.delete(current_ro)
|
213
319
|
else
|
214
320
|
special_command_targets << current_ro
|
215
321
|
end
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
# console cursor to just after the search prompt, before user types)
|
221
|
-
query = $stdin.gets.chomp
|
222
|
-
if query.present?
|
223
|
-
self.last_search = query
|
224
|
-
go_to_next_search_result
|
225
|
-
end
|
226
|
-
when 'n'
|
322
|
+
|
323
|
+
when KEY_MAP[:start_search]
|
324
|
+
initiate_search
|
325
|
+
when KEY_MAP[:continue_search]
|
227
326
|
if last_search
|
228
327
|
go_to_next_search_result
|
229
328
|
else
|
230
|
-
|
231
|
-
|
232
|
-
sleep 2
|
329
|
+
message = { text: 'No Search has been entered', end_time: Time.now + 2 }
|
330
|
+
self.current_warning_messages << message
|
233
331
|
end
|
234
|
-
|
235
|
-
|
236
|
-
|
332
|
+
|
333
|
+
when KEY_MAP[:change_session_right]
|
334
|
+
self.signal_to_manager = :change_session_right
|
335
|
+
return
|
336
|
+
when KEY_MAP[:change_session_left]
|
337
|
+
self.signal_to_manager = :change_session_left
|
338
|
+
return
|
339
|
+
when KEY_MAP[:name_objects]
|
340
|
+
name_subjects_of_target_ros
|
341
|
+
when KEY_MAP[:return_objects]
|
342
|
+
self.signal_to_manager = :return
|
343
|
+
subjects = subjects_of_target_ros
|
344
|
+
self.special_command_targets = []
|
345
|
+
return subjects
|
237
346
|
end
|
238
347
|
|
239
|
-
|
348
|
+
beep_if_user_had_to_wait(wait_start_time)
|
240
349
|
end
|
241
350
|
end
|
242
351
|
|
243
|
-
def
|
244
|
-
|
352
|
+
def set_console_cursor_below_content(floor_the_cursor:)
|
353
|
+
if floor_the_cursor
|
354
|
+
screen_height, _screen_width = $stdout.winsize
|
355
|
+
$stdout.write "#{CSI}#{screen_height};1H\n" # (Moves console cursor to bottom left corner, then one more)
|
356
|
+
return
|
357
|
+
end
|
358
|
+
|
359
|
+
bare_screen_string =
|
360
|
+
current_view_panel.visible_header_string + "\n" +
|
361
|
+
current_view_panel.visible_body_string
|
362
|
+
split_lines = bare_screen_string.split("\n")
|
363
|
+
rows_filled = split_lines.count
|
364
|
+
$stdout.write "#{CSI}#{rows_filled};1H\n" # Moves console cursor to bottom
|
365
|
+
# of *content*, then one more.
|
366
|
+
end
|
367
|
+
|
368
|
+
def tab_string
|
369
|
+
top_ro_preview = top_ro.value_string
|
370
|
+
tab = if session_is_current
|
371
|
+
"\e[7m #{tab_icon}: #{top_ro_preview} \e[00m"
|
372
|
+
else
|
373
|
+
" \e[7m#{tab_icon}:\e[00m #{top_ro_preview} "
|
374
|
+
end
|
375
|
+
tab
|
376
|
+
end
|
377
|
+
|
378
|
+
def subjects_of_target_ros
|
379
|
+
if special_command_targets.any?
|
380
|
+
return special_command_targets.map(&:current_subject)
|
381
|
+
end
|
382
|
+
|
383
|
+
current_ro.current_subject
|
245
384
|
end
|
246
385
|
|
247
386
|
private
|
248
387
|
|
388
|
+
def beep_if_user_had_to_wait(wait_start_time)
|
389
|
+
patient_keys = KEY_MAP.slice(*PATIENT_ACTIONS).values
|
390
|
+
user_has_waited_at_least_four_seconds =
|
391
|
+
Time.now - wait_start_time > 4 &&
|
392
|
+
!patient_keys.include?(last_keypress)
|
393
|
+
print "\a" if user_has_waited_at_least_four_seconds # (Audio 'beep')
|
394
|
+
end
|
395
|
+
|
396
|
+
def initiate_search
|
397
|
+
_screen_height, screen_width = $stdout.winsize
|
398
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
399
|
+
$stdout.print ' ' * screen_width
|
400
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
401
|
+
$stdout.print SEARCH_PROMPT
|
402
|
+
$stdout.write "#{CSI}1;#{SEARCH_PROMPT.ansiless_length + 1}H" # (Moves
|
403
|
+
# console cursor to just after the search prompt, before user types)
|
404
|
+
query = $stdin.gets.chomp
|
405
|
+
unless query.empty?
|
406
|
+
self.last_search = query
|
407
|
+
go_to_next_search_result
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def move_cursor_up_action(action_count = nil)
|
412
|
+
action_count ||= !number_to_move.empty? ? number_to_move.to_i : 1
|
413
|
+
navigate_up_multiple(action_count)
|
414
|
+
|
415
|
+
self.number_to_move = ''
|
416
|
+
tree_view.slide_view_to_cursor
|
417
|
+
end
|
418
|
+
|
419
|
+
def move_cursor_down_action(action_count = nil)
|
420
|
+
action_count ||= !number_to_move.empty? ? number_to_move.to_i : 1
|
421
|
+
navigate_down_multiple(action_count)
|
422
|
+
|
423
|
+
self.number_to_move = ''
|
424
|
+
tree_view.slide_view_to_cursor
|
425
|
+
end
|
426
|
+
|
427
|
+
def clear_tracked_values
|
428
|
+
self.special_command_targets = []
|
429
|
+
self.last_search = nil
|
430
|
+
self.number_to_move = ''
|
431
|
+
end
|
432
|
+
|
249
433
|
def print_progress_bar
|
250
434
|
screen_height, _screen_width = $stdout.winsize
|
251
435
|
bar = progress_bar.to_s
|
252
436
|
$stdout.write "#{CSI}#{screen_height};1H" # (Moves console cursor to bottom left corner)
|
253
|
-
print bar
|
437
|
+
print bar unless bar.tr(' ', '').empty?
|
438
|
+
end
|
439
|
+
|
440
|
+
def print_current_warning_messages
|
441
|
+
return if current_warning_messages.empty?
|
442
|
+
|
443
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
444
|
+
wing = ' ' * 3
|
445
|
+
|
446
|
+
self.current_warning_messages.reject! { |message| Time.now > message[:end_time] }
|
447
|
+
messages = current_warning_messages.map { |message| message[:text] }
|
448
|
+
print messages.map { |message| "\e[7m#{wing + message + wing}\e[00m" }.join("\n")
|
449
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
450
|
+
end
|
451
|
+
|
452
|
+
def print_session_tabs_bar_if_changed
|
453
|
+
seconds_in_tab = Time.now - session_view_start_time
|
454
|
+
if seconds_in_tab < 2
|
455
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
456
|
+
print session_manager.session_tabs_bar
|
457
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
458
|
+
end
|
254
459
|
end
|
255
460
|
|
256
461
|
def current_view_panel
|
@@ -265,14 +470,6 @@ class Scryglass::Session
|
|
265
470
|
view_panels[:lens]
|
266
471
|
end
|
267
472
|
|
268
|
-
def colorize(screen_string)
|
269
|
-
dot = '•'
|
270
|
-
cyan_dot = "\e[36m#{dot}\e[00m" # cyan then back to *default*
|
271
|
-
screen_string.gsub!('•', cyan_dot)
|
272
|
-
|
273
|
-
screen_string
|
274
|
-
end
|
275
|
-
|
276
473
|
def display_active_searching_indicator
|
277
474
|
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
278
475
|
message = ' Searching... '
|
@@ -315,14 +512,32 @@ class Scryglass::Session
|
|
315
512
|
tree_view.current_view_coords = { y: 0, x: 0 }
|
316
513
|
tree_view.slide_view_to_cursor
|
317
514
|
else
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
515
|
+
message = { text: 'No Match Found', end_time: Time.now + 2 }
|
516
|
+
self.current_warning_messages << message
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def fetch_user_signal
|
521
|
+
previous_signal = user_signals.last
|
522
|
+
new_signal =
|
523
|
+
begin
|
524
|
+
Timeout.timeout(0.3) { $stdin.getch }
|
525
|
+
rescue Timeout::Error
|
526
|
+
nil
|
527
|
+
end
|
322
528
|
|
323
|
-
|
324
|
-
|
529
|
+
## Since many keys, including arrow keys, result in several signals being
|
530
|
+
## sent (e.g. DOWN: "\e" then "[" then "B" in RAPID succession), the
|
531
|
+
## *pause* after a genuine escape key press (also "\e") is the only way
|
532
|
+
## to distinguish it precisely.
|
533
|
+
genuine_escape_key_press = new_signal.nil? && previous_signal == "\e"
|
534
|
+
if genuine_escape_key_press
|
535
|
+
new_signal = 'esc'
|
325
536
|
end
|
537
|
+
|
538
|
+
user_signals << new_signal unless new_signal.nil? && previous_signal.nil?
|
539
|
+
|
540
|
+
new_signal
|
326
541
|
end
|
327
542
|
|
328
543
|
def run_help_screen_ui
|
@@ -337,17 +552,21 @@ class Scryglass::Session
|
|
337
552
|
sliced_help_screen = Hexes.simple_screen_slice(current_help_screen)
|
338
553
|
help_screen_string = Hexes.opacify_screen_string(sliced_help_screen)
|
339
554
|
Hexes.overwrite_screen(help_screen_string)
|
340
|
-
help_screen_user_input = $stdin.getch
|
341
555
|
|
342
|
-
|
343
|
-
|
556
|
+
new_signal = fetch_user_signal
|
557
|
+
|
558
|
+
case new_signal
|
559
|
+
when nil
|
560
|
+
when KEY_MAP[:escape]
|
561
|
+
return true
|
562
|
+
when KEY_MAP[:control_screen]
|
344
563
|
current_help_screen_index += 1
|
345
|
-
when
|
564
|
+
when KEY_MAP[:quit_session]
|
346
565
|
$stdout.write "#{CSI}#{screen_height};1H" # (Moves console cursor to
|
347
566
|
# bottom left corner). This helps 'q' not print the console prompt at
|
348
567
|
# the top of the screen, overlapping with the old display.
|
349
568
|
return false
|
350
|
-
when
|
569
|
+
when KEY_MAP[:ctrl_c]
|
351
570
|
screen_height, _screen_width = $stdout.winsize
|
352
571
|
puts "\n" * screen_height
|
353
572
|
raise IRB::Abort, 'Ctrl+C Detected'
|
@@ -375,7 +594,6 @@ class Scryglass::Session
|
|
375
594
|
|
376
595
|
move_cursor_to(current_ro.parent_ro) until current_ro.visible?
|
377
596
|
tree_view.slide_view_to_cursor
|
378
|
-
tree_view.recalculate_boundaries # TODO: should these be conditional? If they are, I might need a potential tree view recalc after toggling lens view to tree view.
|
379
597
|
end
|
380
598
|
|
381
599
|
def expand_targets
|
@@ -388,7 +606,6 @@ class Scryglass::Session
|
|
388
606
|
else
|
389
607
|
expand!(current_ro)
|
390
608
|
end
|
391
|
-
tree_view.recalculate_boundaries
|
392
609
|
end
|
393
610
|
|
394
611
|
def reset_the_view_or_cursor
|
@@ -400,40 +617,73 @@ class Scryglass::Session
|
|
400
617
|
end
|
401
618
|
|
402
619
|
def draw_screen
|
620
|
+
current_view_panel.recalculate_boundaries # This now happens at every screen
|
621
|
+
# draw to account for the user changing the screen size. Otherwise glitch.
|
403
622
|
current_view_panel.ensure_correct_view_coords
|
404
623
|
screen_string = current_view_panel.screen_string
|
405
624
|
|
406
|
-
screen_string = colorize(screen_string) if Scryglass.config.dot_coloring
|
407
625
|
Hexes.overwrite_screen(screen_string)
|
408
626
|
$stdout.write "#{CSI}1;1H" # Moves terminal cursor to top left corner,
|
409
627
|
# mostly for consistency.
|
628
|
+
print_current_warning_messages
|
629
|
+
print_session_tabs_bar_if_changed
|
410
630
|
end
|
411
631
|
|
412
|
-
def
|
413
|
-
bare_screen_string =
|
414
|
-
current_view_panel.visible_header_string + "\n" +
|
415
|
-
current_view_panel.visible_body_string
|
416
|
-
split_lines = bare_screen_string.split("\n")
|
417
|
-
rows_filled = split_lines.count
|
418
|
-
$stdout.write "#{CSI}#{rows_filled};1H\n" # Moves console cursor to bottom
|
419
|
-
# of *content*, then one more.
|
420
|
-
end
|
421
|
-
|
422
|
-
def visually_close_ui
|
632
|
+
def get_subject_name_from_user
|
423
633
|
_screen_height, screen_width = $stdout.winsize
|
424
|
-
|
425
|
-
|
426
|
-
|
634
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
635
|
+
$stdout.print ' ' * screen_width
|
636
|
+
$stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner)
|
637
|
+
$stdout.print VARNAME_PROMPT
|
638
|
+
$stdout.write "#{CSI}1;#{VARNAME_PROMPT.ansiless_length + 1}H" # (Moves
|
639
|
+
# console cursor to just after the varname prompt, before user types)
|
640
|
+
$stdin.gets.chomp
|
427
641
|
end
|
428
642
|
|
429
|
-
def
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
643
|
+
def name_subjects_of_target_ros
|
644
|
+
typed_name = get_subject_name_from_user
|
645
|
+
typed_name = typed_name.tr(' ', '')
|
646
|
+
|
647
|
+
if typed_name.empty?
|
648
|
+
message = { text: 'Instance Variable name cannot be blank',
|
649
|
+
end_time: Time.now + 2 }
|
650
|
+
self.current_warning_messages << message
|
651
|
+
print "\a" # (Audio 'beep')
|
652
|
+
return
|
434
653
|
end
|
435
654
|
|
436
|
-
|
655
|
+
current_console_binding = session_manager.current_console_binding
|
656
|
+
preexisting_iv_names = current_console_binding
|
657
|
+
.eval('instance_variables') # Different than just `.instance_variables`
|
658
|
+
.map { |iv| iv.to_s.tr('@', '') }
|
659
|
+
all_method_names = preexisting_iv_names |
|
660
|
+
current_console_binding.methods |
|
661
|
+
current_console_binding.singleton_methods |
|
662
|
+
current_console_binding.private_methods
|
663
|
+
conflicting_method_name = all_method_names.find do |method_name|
|
664
|
+
pure_method_name = method_name.to_s.tr('=', '')
|
665
|
+
typed_name == pure_method_name
|
666
|
+
end
|
667
|
+
|
668
|
+
if conflicting_method_name
|
669
|
+
message = { text: 'Instance Variable name conflict',
|
670
|
+
end_time: Time.now + 2 }
|
671
|
+
self.current_warning_messages << message
|
672
|
+
print "\a" # (Audio 'beep')
|
673
|
+
return
|
674
|
+
end
|
675
|
+
|
676
|
+
set_iv_name_in_console =
|
677
|
+
"@#{typed_name} = " \
|
678
|
+
"$scry_session_manager.current_session.subjects_of_target_ros"
|
679
|
+
current_console_binding.eval(set_iv_name_in_console)
|
680
|
+
session_manager.current_binding_tracker.user_named_variables << "@#{typed_name}"
|
681
|
+
|
682
|
+
message = { text: "#{subjects_of_target_ros.class} assigned to: @#{typed_name}",
|
683
|
+
end_time: Time.now + 3 }
|
684
|
+
self.current_warning_messages << message
|
685
|
+
|
686
|
+
self.special_command_targets = []
|
437
687
|
end
|
438
688
|
|
439
689
|
def navigate_up_multiple(action_count)
|