Class: Scryglass::Session

Inherits:
Object
  • Object
show all
Includes:
RoBuilder
Defined in:
lib/scryglass/session.rb

Constant Summary collapse

CURSOR_CHARACTER =

These are en dashes (alt+dash), not hyphens or em dashes.

''
SEARCH_PROMPT =
"\e[7mSearch for (regex, case-sensitive):  /\e[00m"
VARNAME_PROMPT =
"\e[7mName your object(s):  @\e[00m"
METHOD_NAME_PROMPT =
"\e[7mMethod(s) to call on object(s):  object\e[00m"
SUBJECT_TYPES =
[
  :value,
  :key
].freeze
CSI =

“©ontrol (S)equence (I)ntroducer” for ANSI sequences

"\e["
KEY_MAP =
{
  escape: 'esc', # Not a normal keystroke, see: genuine_escape_key_press
  ctrl_c: "\u0003",
  quit_session: 'q',
  delete_session_tab: 'Q',
  change_session_right: "\t", # Tab
  change_session_left: 'Z', # Shift+Tab (well, one of its signals, after "\e" and "[")
  start_new_session_from_target: 't',
  restart_session_from_target: 'T',
  digit_1: '1',
  digit_2: '2',
  digit_3: '3',
  digit_4: '4',
  digit_5: '5',
  digit_6: '6',
  digit_7: '7',
  digit_8: '8',
  digit_9: '9',
  digit_0: '0',
  move_cursor_up: 'A',     # Up arrow (well, one of its signals, after "\e" and "[")
  move_cursor_down: 'B', # Down arrow (well, one of its signals, after "\e" and "[")
  open_bucket: 'C',     # Right arrow (well, one of its signals, after "\e" and "[")
  close_bucket: 'D',     # Left arrow (well, one of its signals, after "\e" and "[")
  homerow_move_cursor_up: 'k',        # To be like VIM arrow keys
  homerow_move_cursor_up_fast: 'K',   # To be like VIM arrow keys
  homerow_move_cursor_down: 'j',      # To be like VIM arrow keys
  homerow_move_cursor_down_fast: 'J', # To be like VIM arrow keys
  homerow_open_bucket: 'l',           # To be like VIM arrow keys
  homerow_close_bucket: 'h',          # To be like VIM arrow keys
  # Note, shift-UP and shift-DOWN are not here, as those work very
  #   differently: by virtue of the type-a-number-first functionality.
  toggle_view_panel: ' ',
  switch_lens: '>',
  switch_subject_type: '<',
  move_view_up: 'w',
  move_view_down: 's',
  move_view_left: 'a',
  move_view_right: 'd',
  move_view_up_fast: '', # Alt+w
  move_view_down_fast: 'ß', # Alt+s
  move_view_left_fast: 'å', # Alt+a
  move_view_right_fast: '', # Alt+d
  control_screen: '?',
  build_instance_variables: '@',
  build_ar_relations: '.',
  build_enum_children: '(',
  smart_open: 'o',
  build_method_results: 'c',
  select_siblings: '|',
  select_all: '*',
  select_current: '-',
  start_search: '/',
  continue_search: 'n',
  return_objects: "\r", # [ENTER],
  name_objects: "="
}.freeze
PATIENT_ACTIONS =
[
  :control_screen,
  :escape,
  :name_objects,
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seed) ⇒ Session

Returns a new instance of Session.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/scryglass/session.rb', line 94

def initialize(seed)
  self.all_ros = []
  self.current_lens = 0
  self.current_subject_type = :value
  self.current_panel_type = :tree
  self.special_command_targets = []
  self.number_to_move = ''
  self.user_signals = []
  self.progress_bar = Prog::Pipe.new
  self.current_warning_messages = []
  self.session_manager = nil
  self.signal_to_manager = nil
  self.tab_icon = nil
  self.session_is_current = false
  self.session_view_start_time = nil
  self.content_shape_changed = true
  self.previous_screen_dimensions = $stdout.winsize

  top_ro = roify(seed, parent_ro: nil, depth: 1)
  top_ro.has_cursor = true
  self.current_ro = top_ro

  expand!(top_ro)

  self.view_panels = {
    tree: Scryglass::TreePanel.new(scry_session: self),
    lens: Scryglass::LensPanel.new(scry_session: self),
  }
end

Instance Attribute Details

#all_rosObject

Returns the value of attribute all_ros.



8
9
10
# File 'lib/scryglass/session.rb', line 8

def all_ros
  @all_ros
end

#content_shape_changedObject

Returns the value of attribute content_shape_changed.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def content_shape_changed
  @content_shape_changed
end

#current_lensObject

Returns the value of attribute current_lens.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def current_lens
  @current_lens
end

#current_panel_typeObject

Returns the value of attribute current_panel_type.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def current_panel_type
  @current_panel_type
end

#current_roObject

Returns the value of attribute current_ro.



8
9
10
# File 'lib/scryglass/session.rb', line 8

def current_ro
  @current_ro
end

#current_subject_typeObject

Returns the value of attribute current_subject_type.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def current_subject_type
  @current_subject_type
end

#current_view_coordsObject

Returns the value of attribute current_view_coords.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def current_view_coords
  @current_view_coords
end

#current_warning_messagesObject

Returns the value of attribute current_warning_messages.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def current_warning_messages
  @current_warning_messages
end

#last_searchObject

Returns the value of attribute last_search.



16
17
18
# File 'lib/scryglass/session.rb', line 16

def last_search
  @last_search
end

#number_to_moveObject

Returns the value of attribute number_to_move.



16
17
18
# File 'lib/scryglass/session.rb', line 16

def number_to_move
  @number_to_move
end

#previous_screen_dimensionsObject

Returns the value of attribute previous_screen_dimensions.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def previous_screen_dimensions
  @previous_screen_dimensions
end

#progress_barObject

Returns the value of attribute progress_bar.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def progress_bar
  @progress_bar
end

#session_is_currentObject

Returns the value of attribute session_is_current.



18
19
20
# File 'lib/scryglass/session.rb', line 18

def session_is_current
  @session_is_current
end

#session_managerObject

Returns the value of attribute session_manager.



18
19
20
# File 'lib/scryglass/session.rb', line 18

def session_manager
  @session_manager
end

#session_view_start_timeObject

Returns the value of attribute session_view_start_time.



18
19
20
# File 'lib/scryglass/session.rb', line 18

def session_view_start_time
  @session_view_start_time
end

#signal_to_managerObject

Returns the value of attribute signal_to_manager.



18
19
20
# File 'lib/scryglass/session.rb', line 18

def signal_to_manager
  @signal_to_manager
end

#special_command_targetsObject

Returns the value of attribute special_command_targets.



8
9
10
# File 'lib/scryglass/session.rb', line 8

def special_command_targets
  @special_command_targets
end

#tab_iconObject

Returns the value of attribute tab_icon.



18
19
20
# File 'lib/scryglass/session.rb', line 18

def tab_icon
  @tab_icon
end

#user_signalsObject

Returns the value of attribute user_signals.



16
17
18
# File 'lib/scryglass/session.rb', line 16

def user_signals
  @user_signals
end

#view_panelsObject

Returns the value of attribute view_panels.



10
11
12
# File 'lib/scryglass/session.rb', line 10

def view_panels
  @view_panels
end

Instance Method Details

#last_keypressObject



128
129
130
131
# File 'lib/scryglass/session.rb', line 128

def last_keypress
  last_two_signals = user_signals.last(2)
  last_two_signals.last || last_two_signals.first
end

#run_scry_uiObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/scryglass/session.rb', line 133

def run_scry_ui
  redraw = true
  self.signal_to_manager = nil
  self.session_view_start_time = Time.now # For this particular tab/session

  ## On hold: Record/Playback Functionality:
  # case actions
  # when :record
  #   $scry_session_actions_performed = []
  # when :playback
  #   if $scry_session_actions_performed.blank?
  #     raise 'Could not find recording of previous session\'s actions'
  #   end
  #   @input_stack = $scry_session_actions_performed.dup
  # end

  # We print a full screen of lines so the first call of draw_screen doesn't
  #   write over any previous valuable content the user had in the console.
  print Hexes.opacify_screen_string(Hexes.simple_screen_slice(boot_screen))

  while true
    draw_screen if redraw
    redraw = true

    ## On hold: Record/Playback Functionality:
    # case actions
    # when :record
    #   self.user_input = $stdin.getch
    #   $scry_session_actions_performed << user_input
    # when :playback
    #   if @input_stack.any? # (IV to be easily accessible for debugging)
    #     self.user_input = @input_stack.shift
    #     sleep 0.05
    #   else
    #     self.user_input = $stdin.getch
    #   end
    # else
    #   self.user_input = $stdin.getch
    # end

    new_signal = fetch_user_signal

    wait_start_time = Time.now

    case new_signal
    when nil
    when KEY_MAP[:escape]
      case current_panel_type
      when :lens
        self.current_panel_type = :tree
      when :tree
        clear_tracked_values
      end
    when KEY_MAP[:ctrl_c]
      set_console_cursor_below_content
      raise IRB::Abort, 'Ctrl+C Detected'
    when KEY_MAP[:quit_session]
      self.signal_to_manager = :quit
      return
    when KEY_MAP[:delete_session_tab]
      self.signal_to_manager = :delete
      return
    when KEY_MAP[:control_screen]
      remain_in_scry_session = run_help_screen_ui
      unless remain_in_scry_session
        self.signal_to_manager = :quit_from_help
        return
      end
    when KEY_MAP[:digit_1]
      self.number_to_move += '1'
      # This allows you to type multi-digit number very
      #   quickly and still have it process all the digits:
      redraw = false
    when KEY_MAP[:digit_2]
      self.number_to_move += '2'
      redraw = false
    when KEY_MAP[:digit_3]
      self.number_to_move += '3'
      redraw = false
    when KEY_MAP[:digit_4]
      self.number_to_move += '4'
      redraw = false
    when KEY_MAP[:digit_5]
      self.number_to_move += '5'
      redraw = false
    when KEY_MAP[:digit_6]
      self.number_to_move += '6'
      redraw = false
    when KEY_MAP[:digit_7]
      self.number_to_move += '7'
      redraw = false
    when KEY_MAP[:digit_8]
      self.number_to_move += '8'
      redraw = false
    when KEY_MAP[:digit_9]
      self.number_to_move += '9'
      redraw = false
    when KEY_MAP[:digit_0]
      if number_to_move[0] # You can append zeros to existing number_to_move...
        self.number_to_move += '0'
        redraw = false
      else # ...but otherwise it's understood to be a view||cursor reset.
        reset_the_view_or_cursor
      end

    when KEY_MAP[:move_cursor_up]
      move_cursor_up_action
    when KEY_MAP[:move_cursor_down]
      move_cursor_down_action
    when KEY_MAP[:open_bucket]
      expand_targets
    when KEY_MAP[:close_bucket]
      collapse_targets

    when KEY_MAP[:homerow_move_cursor_up]
      move_cursor_up_action
    when KEY_MAP[:homerow_move_cursor_up_fast]
      move_cursor_up_action(12) # 12 matches the digits provided by shift+up
    when KEY_MAP[:homerow_move_cursor_down]
      move_cursor_down_action
    when KEY_MAP[:homerow_move_cursor_down_fast]
      move_cursor_down_action(12) # 12 matches the digits provided by shift+down
    when KEY_MAP[:homerow_open_bucket]
      expand_targets
    when KEY_MAP[:homerow_close_bucket]
      collapse_targets

    when KEY_MAP[:toggle_view_panel]
      toggle_view_panel
    when KEY_MAP[:switch_lens]
      scroll_lens_type
    when KEY_MAP[:switch_subject_type]
      toggle_current_subject_type

    when KEY_MAP[:move_view_up]
      current_view_panel.move_view_up(5)
    when KEY_MAP[:move_view_down]
      current_view_panel.move_view_down(5)
    when KEY_MAP[:move_view_left]
      current_view_panel.move_view_left(5)
    when KEY_MAP[:move_view_right]
      current_view_panel.move_view_right(5)

    when KEY_MAP[:move_view_up_fast]
      current_view_panel.move_view_up(50)
    when KEY_MAP[:move_view_down_fast]
      current_view_panel.move_view_down(50)
    when KEY_MAP[:move_view_left_fast]
      current_view_panel.move_view_left(50)
    when KEY_MAP[:move_view_right_fast]
      current_view_panel.move_view_right(50)

    when KEY_MAP[:build_instance_variables]
      build_instance_variables_for_target_ros
      self.content_shape_changed = true
      tree_view.slide_view_to_cursor # Just a nice-to-have
    when KEY_MAP[:build_ar_relations]
      build_activerecord_relations_for_target_ros
      self.content_shape_changed = true
      tree_view.slide_view_to_cursor # Just a nice-to-have
    when KEY_MAP[:build_enum_children]
      build_enum_children_for_target_ros
      self.content_shape_changed = true
      tree_view.slide_view_to_cursor # Just a nice-to-have
    when KEY_MAP[:smart_open]
      smart_open_target_ros
      self.content_shape_changed = true
      tree_view.slide_view_to_cursor # Just a nice-to-have
    when KEY_MAP[:build_method_results]
      build_method_result_ros
      self.content_shape_changed = true
      tree_view.slide_view_to_cursor # Just a nice-to-have

    when KEY_MAP[:select_siblings]
      sibling_ros = if current_ro.top_ro?
                      [top_ro]
                    else
                      current_ro.parent_ro.sub_ros.dup
                      # ^If we don't dup,
                      #   then '-' can remove ros from `sub_ros`.
                    end
      if special_command_targets.sort == sibling_ros.sort
        self.special_command_targets = []
      else
        self.special_command_targets = sibling_ros
      end
    when KEY_MAP[:select_all]
      all_the_ros = all_ros.dup
      # ^If we don't dup,
      #   then '-' can remove ros from all_ros.
      if special_command_targets.sort == all_the_ros.sort
        self.special_command_targets = []
      else
        self.special_command_targets = all_the_ros
      end
    when KEY_MAP[:select_current]
      if special_command_targets.include?(current_ro)
        special_command_targets.delete(current_ro)
      else
        special_command_targets << current_ro
      end

    when KEY_MAP[:start_search]
      initiate_search
    when KEY_MAP[:continue_search]
      # TODO: extract in separate commit
      if last_search
        go_to_next_search_result
      else
        message = { text: 'No Search has been entered', end_time: Time.now + 2 }
        self.current_warning_messages << message
      end
    when KEY_MAP[:start_new_session_from_target]
      self.signal_to_manager = :start_new_session_from_target
      return subjects_of_target_ros
    when KEY_MAP[:restart_session_from_target]
      self.signal_to_manager = :restart_session_from_target
      return subjects_of_target_ros
    when KEY_MAP[:change_session_right]
      self.signal_to_manager = :change_session_right
      return
    when KEY_MAP[:change_session_left]
      self.signal_to_manager = :change_session_left
      return
    when KEY_MAP[:name_objects]
      name_subjects_of_target_ros
    when KEY_MAP[:return_objects]
      self.signal_to_manager = :return
      return subjects_of_target_ros
    end

    beep_if_user_had_to_wait(wait_start_time)
  end
end

#set_console_cursor_below_content(floor_the_cursor:) ⇒ Object



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/scryglass/session.rb', line 368

def set_console_cursor_below_content(floor_the_cursor:)
  if floor_the_cursor
    screen_height, _screen_width = $stdout.winsize
    $stdout.write "#{CSI}#{screen_height};1H\n" # (Moves console cursor to bottom left corner, then one more)
    return
  end

  bare_screen_string =
    current_view_panel.visible_header_string + "\n" +
    current_view_panel.visible_body_string
  split_lines = bare_screen_string.split("\n")
  rows_filled = split_lines.count
  $stdout.write "#{CSI}#{rows_filled};1H\n" # Moves console cursor to bottom
                                            #   of *content*, then one more.
end

#subjects_of_target_rosObject



394
395
396
397
398
399
400
# File 'lib/scryglass/session.rb', line 394

def subjects_of_target_ros
  if special_command_targets.any?
    return special_command_targets.map(&:current_subject)
  end

  current_ro.current_subject
end

#tab_stringObject



384
385
386
387
388
389
390
391
392
# File 'lib/scryglass/session.rb', line 384

def tab_string
  top_ro_preview = top_ro.value_string
  tab = if session_is_current
          "\e[7m #{tab_icon}: #{top_ro_preview} \e[00m"
        else
          " \e[7m#{tab_icon}:\e[00m #{top_ro_preview} "
        end
  tab
end

#top_roObject



124
125
126
# File 'lib/scryglass/session.rb', line 124

def top_ro
  all_ros.first
end