lib/scryglass/session.rb in scryglass-1.0.1 vs lib/scryglass/session.rb in scryglass-1.1.0

- old
+ new

@@ -9,11 +9,11 @@ attr_accessor :current_view_coords, :current_lens, :current_subject_type, :view_panels, :current_panel_type, :progress_bar - attr_accessor :user_input, :last_search, :number_to_move + attr_accessor :user_signals, :last_search, :number_to_move CURSOR_CHARACTER = '–' # These are en dashes (alt+dash), not hyphens or em dashes. SEARCH_PROMPT = "\e[7mSearch for (regex, case-sensitive): /\e[00m" @@ -31,11 +31,11 @@ self.current_lens = 0 self.current_subject_type = :value self.current_panel_type = :tree self.special_command_targets = [] self.number_to_move = '' - self.user_input = nil + self.user_signals = [] self.progress_bar = Prog::Pipe.new top_ro = roify(seed, parent_ro: nil, depth: 1) top_ro.has_cursor = true self.current_ro = top_ro @@ -50,46 +50,58 @@ def run_scry_ui(actions:) in_scry_session = true redraw = true - 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 + ## 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 in_scry_session draw_screen if redraw redraw = true - 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 + ## 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 user_input + case new_signal + when nil + when 'esc' + case current_panel_type + when :lens + self.current_panel_type = :tree + when :tree + clear_tracked_values + end when "\u0003" set_console_cursor_below_content raise IRB::Abort, 'Ctrl+C Detected' when 'q' in_scry_session = false @@ -121,44 +133,38 @@ redraw = false when '9' self.number_to_move += '9' redraw = false when '0' - if number_to_move.present? # You can append zeros to number_to_move... + 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 'A' # Up arrow - action_count = number_to_move.present? ? number_to_move.to_i : 1 + action_count = !number_to_move.empty? ? number_to_move.to_i : 1 navigate_up_multiple(action_count) self.number_to_move = '' - lens_view.recalculate_boundaries if current_panel_type == :lens tree_view.slide_view_to_cursor when 'B' # Down arrow - action_count = number_to_move.present? ? number_to_move.to_i : 1 + action_count = !number_to_move.empty? ? number_to_move.to_i : 1 navigate_down_multiple(action_count) self.number_to_move = '' - lens_view.recalculate_boundaries if current_panel_type == :lens tree_view.slide_view_to_cursor when 'C' # Right arrow expand_targets when 'D' # Left arrow collapse_targets - lens_view.recalculate_boundaries if current_panel_type == :lens when ' ' toggle_view_panel - lens_view.recalculate_boundaries if current_panel_type == :lens when 'l' scroll_lens_type - lens_view.recalculate_boundaries if current_panel_type == :lens when 'L' toggle_current_subject_type - lens_view.recalculate_boundaries if current_panel_type == :lens when 'w' current_view_panel.move_view_up(5) when 's' current_view_panel.move_view_down(5) when 'a' @@ -175,19 +181,16 @@ current_view_panel.move_view_right(50) when '?' in_scry_session = run_help_screen_ui when '@' build_instance_variables_for_target_ros - tree_view.recalculate_boundaries tree_view.slide_view_to_cursor # Just a nice-to-have when '.' build_activerecord_relations_for_target_ros - tree_view.recalculate_boundaries tree_view.slide_view_to_cursor # Just a nice-to-have when '(' build_enum_children_for_target_ros - tree_view.recalculate_boundaries tree_view.slide_view_to_cursor # Just a nice-to-have when '|' sibling_ros = if current_ro.top_ro? [top_ro] else @@ -212,16 +215,19 @@ special_command_targets.delete(current_ro) else special_command_targets << current_ro end when '/' + _screen_height, screen_width = $stdout.winsize $stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner) - $stdout.write SEARCH_PROMPT + $stdout.print ' ' * screen_width + $stdout.write "#{CSI}1;1H" # (Moves console cursor to top left corner) + $stdout.print SEARCH_PROMPT $stdout.write "#{CSI}1;#{SEARCH_PROMPT.ansiless_length + 1}H" # (Moves # console cursor to just after the search prompt, before user types) query = $stdin.gets.chomp - if query.present? + unless query.empty? self.last_search = query go_to_next_search_result end when 'n' if last_search @@ -234,25 +240,36 @@ when "\r" # [ENTER] visually_close_ui return subjects_of_target_ros end - print "\a" if Time.now - wait_start_time > 4 && user_input != '?' # (Audio 'beep') + print "\a" if Time.now - wait_start_time > 4 && last_keypress != '?' # (Audio 'beep') end end def top_ro all_ros.first end + def last_keypress + last_two_signals = user_signals.last(2) + last_two_signals.last || last_two_signals.first + end + private + def clear_tracked_values + self.special_command_targets = [] + self.last_search = nil + self.number_to_move = '' + end + def print_progress_bar screen_height, _screen_width = $stdout.winsize bar = progress_bar.to_s $stdout.write "#{CSI}#{screen_height};1H" # (Moves console cursor to bottom left corner) - print bar if bar.present? + print bar unless bar.tr(' ', '').empty? end def current_view_panel view_panels[current_panel_type] end @@ -323,10 +340,33 @@ $stdout.write "\e[7m#{wing + message + wing}\e[00m" sleep 2 end end + def fetch_user_signal + previous_signal = user_signals.last + new_signal = + begin + Timeout.timeout(0.1) { $stdin.getch } + rescue Timeout::Error + nil + end + + ## Since many keys, including arrow keys, result in several signals being + ## sent (e.g. DOWN: "\e" then "[" then "B" in RAPID succession), the + ## *pause* after a genuine escape key press (also "\e") is the only way + ## to distinguish it precisely. + genuine_escape_key_press = new_signal.nil? && previous_signal == "\e" + if genuine_escape_key_press + new_signal = 'esc' + end + + user_signals << new_signal unless new_signal.nil? && previous_signal.nil? + + new_signal + end + def run_help_screen_ui screen_height, _screen_width = $stdout.winsize in_help_screen = true current_help_screen_index = 0 @@ -335,13 +375,16 @@ while in_help_screen current_help_screen = help_screens[current_help_screen_index] sliced_help_screen = Hexes.simple_screen_slice(current_help_screen) help_screen_string = Hexes.opacify_screen_string(sliced_help_screen) Hexes.overwrite_screen(help_screen_string) - help_screen_user_input = $stdin.getch - case help_screen_user_input + new_signal = fetch_user_signal + + case new_signal + when 'esc' + return true when '?' current_help_screen_index += 1 when 'q' $stdout.write "#{CSI}#{screen_height};1H" # (Moves console cursor to # bottom left corner). This helps 'q' not print the console prompt at @@ -373,11 +416,10 @@ collapse!(current_ro.parent_ro) end move_cursor_to(current_ro.parent_ro) until current_ro.visible? tree_view.slide_view_to_cursor - 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. end def expand_targets if special_command_targets.any? target_ros = special_command_targets.dup # dup because some commands @@ -386,11 +428,10 @@ target_ros.each { |target_ro| expand!(target_ro) } self.special_command_targets = [] else expand!(current_ro) end - tree_view.recalculate_boundaries end def reset_the_view_or_cursor if current_view_panel.current_view_coords != { x: 0, y: 0 } current_view_panel.current_view_coords = { x: 0, y: 0 } @@ -398,9 +439,11 @@ move_cursor_to(top_ro) end end def draw_screen + current_view_panel.recalculate_boundaries # This now happens at every screen + # draw to account for the user changing the screen size. Otherwise glitch. current_view_panel.ensure_correct_view_coords screen_string = current_view_panel.screen_string screen_string = colorize(screen_string) if Scryglass.config.dot_coloring Hexes.overwrite_screen(screen_string)