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.
@@ -8,11 +8,20 @@ module ArrayFitToRefinement
8
8
  length_method = ignore_ansi_codes ? :ansiless_length : :length
9
9
  length_result = string_array.join('').send(length_method)
10
10
 
11
+ if string_array.count < 2
12
+ return nonplural_solution(string_length_goal,
13
+ length_result,
14
+ fill: fill,
15
+ ignore_ansi_codes: ignore_ansi_codes)
16
+ end
11
17
 
12
18
  if length_result > string_length_goal
13
- string_array.compress_to(string_length_goal, ignore_ansi_codes: ignore_ansi_codes)
19
+ string_array.compress_to(string_length_goal,
20
+ ignore_ansi_codes: ignore_ansi_codes)
14
21
  elsif length_result < string_length_goal
15
- string_array.expand_to(string_length_goal, ignore_ansi_codes: ignore_ansi_codes, fill: fill)
22
+ string_array.expand_to(string_length_goal,
23
+ ignore_ansi_codes: ignore_ansi_codes,
24
+ fill: fill)
16
25
  else # If it joins to the right length already, we still want to return the expected number of strings.
17
26
  spacers = [''] * (string_array.count - 1)
18
27
  string_array.zip(spacers).flatten.compact
@@ -32,9 +41,12 @@ module ArrayFitToRefinement
32
41
  longest_string_length = working_array.map { |s| s.send(length_method) }.max
33
42
  slider_index = slider % working_array.count
34
43
  if working_array[slider_index].send(length_method) >= longest_string_length
35
- working_array[slider_index] =
36
- working_array[slider_index].clip_at(working_array[slider_index].send(length_method) - 1,
37
- ignore_ansi_codes: ignore_ansi_codes)
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
38
50
  end
39
51
  slider += 1
40
52
  end
@@ -63,5 +75,26 @@ module ArrayFitToRefinement
63
75
 
64
76
  working_array.zip(spacers).flatten.compact
65
77
  end
78
+
79
+ private
80
+
81
+ def nonplural_solution(string_length_goal,
82
+ length_result,
83
+ fill:,
84
+ ignore_ansi_codes:)
85
+ return [fill * string_length_goal] if self.empty?
86
+
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
98
+ end
66
99
  end
67
100
  end
@@ -6,21 +6,36 @@ module ClipStringRefinement
6
6
  def clip_at(clip_length, ignore_ansi_codes: false)
7
7
  length_method = ignore_ansi_codes ? :ansiless_length : :length
8
8
  original_length = send(length_method)
9
- ansi_length = ignore_ansi_codes ? length - ansiless_length : 0
10
- slice_length = clip_length + ansi_length
11
- clipped_string = self[0...slice_length]
9
+
10
+ clipped_string = if ignore_ansi_codes
11
+ self.ansi_slice(0...clip_length)
12
+ else
13
+ self[0...clip_length]
14
+ end
12
15
  if clipped_string.send(length_method) < original_length
13
- clipped_string = clipped_string.mark_as_abbreviated
16
+ clipped_string =
17
+ clipped_string.mark_as_abbreviated(ignore_ansi_codes: ignore_ansi_codes)
14
18
  end
15
19
 
16
20
  clipped_string
17
21
  end
18
22
 
23
+ def ansiless_clip_at(clip_length)
24
+ self.clip_at(clip_length, ignore_ansi_codes: true)
25
+ end
26
+
19
27
  # Warning: Still not going to work nicely if a string ends in an ansi code!
20
- def mark_as_abbreviated
28
+ def mark_as_abbreviated(ignore_ansi_codes: false)
21
29
  self_dup = dup
22
- self_dup[-1] = '…' if self_dup[-1]
23
- self_dup[-2] = '…' if self_dup[-2]
30
+
31
+ if ignore_ansi_codes
32
+ self_dup.ansiless_set!(-1, '…') if self_dup.ansiless_pick(-1)
33
+ self_dup.ansiless_set!(-2, '…') if self_dup.ansiless_pick(-2)
34
+ else
35
+ self_dup[-1] = '…' if self_dup[-1]
36
+ self_dup[-2] = '…' if self_dup[-2]
37
+ end
38
+
24
39
  self_dup
25
40
  end
26
41
  end
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
- ## Bookkeeping and external tools:
3
+ ## Bookkeeping
5
4
  require "scryglass/version"
6
- require 'active_support/core_ext/object/blank' # This gives us `.present?` and `.blank?` # https://stackoverflow.com/questions/4648684/how-to-use-present-in-ruby-projects
5
+
6
+ ## External tools:
7
7
  require 'io/console'
8
+ require 'stringio'
8
9
  require 'pp'
10
+ require 'amazing_print' # For use as a lens
11
+ require 'method_source' # For use in lens_helper
12
+ require 'binding_of_caller'
13
+ require 'timeout'
9
14
 
10
15
  ## Refinements and sub-tools:
11
16
  require 'refinements/ansiless_string_refinement'
12
17
  require 'refinements/clip_string_refinement'
13
18
  require 'refinements/constant_defined_string_refinement'
14
19
  require 'refinements/array_fit_to_refinement'
20
+ require 'scryglass/lens_helper'
15
21
  require 'hexes'
16
22
  require 'prog'
17
23
 
@@ -19,7 +25,9 @@ require 'prog'
19
25
  require 'scryglass/config'
20
26
  require 'scryglass/ro'
21
27
  require 'scryglass/ro_builder'
28
+ require 'scryglass/binding_tracker'
22
29
  require 'scryglass/session'
30
+ require 'scryglass/session_manager'
23
31
  require 'scryglass/view_wrapper'
24
32
  require 'scryglass/view_panel'
25
33
  require 'scryglass/tree_panel'
@@ -27,55 +35,67 @@ require 'scryglass/lens_panel'
27
35
 
28
36
  ## Testing and Demoing:
29
37
  require 'example_material.rb'
30
-
38
+ #test
31
39
  module Scryglass
32
- HELP_SCREEN = <<~'HELPSCREENPAGE'
33
- q : Quit Scry ? : Cycle help panels (1/2)
40
+ HELP_SCREEN = <<~"HELPSCREENPAGE"
41
+ \e[36mq\e[0m : Quit Scry \e[36m?\e[0m : Cycle help panels (1/2)
34
42
 
35
43
  BASIC NAVIGATION: · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
36
44
  · ·
37
- · UP / DOWN : Navigate (You can type a number first) ·
38
- · RIGHT : Expand current or selected row(s) ·
39
- · LEFT : Collapse current or selected row(s) ·
45
+ · \e[36mUP / DOWN\e[0m : Navigate (To move further, type a number first or use \e[36mSHIFT\e[0m) ·
46
+ · \e[36mRIGHT\e[0m : Expand current or selected row(s) ·
47
+ · \e[36mLEFT\e[0m : Collapse current or selected row(s) ·
40
48
  · ·
41
- · ENTER : Close Scry, returning current or selected object(s) (Key or Value) ·
49
+ · (\e[36mh/j/k/l\e[0m on the home row can also serve as arrow keys) ·
50
+ · ·
51
+ · \e[36mENTER\e[0m : Close Scry, returning current or selected object(s) (Key or Value) ·
42
52
  · ·
43
53
  · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
44
54
 
45
55
  INSPECTING WITH LENS VIEW: · · · · · · · · · · · · · ·
46
56
  · ·
47
- · SPACEBAR : Toggle Lens View ·
48
- · l : Cycle through lens types ·
49
- · L : Toggle subject (Key/Value of row) ·
57
+ · \e[36mSPACEBAR\e[0m : Toggle Lens View ·
58
+ · \e[36m > \e[0m : Cycle through lens types ·
59
+ · \e[36m < \e[0m : Toggle subject (Key/Value of row) ·
50
60
  · ·
51
61
  · · · · · · · · · · · · · · · · · · · · · · · · · · ·
52
62
 
53
63
  MORE NAVIGATION: · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
54
64
  · ·
55
- · [w] : Move view window 0 : Reset view location ·
56
- · [a][s][d] (ALT increases speed) (Press again: reset cursor) ·
65
+ · \e[36m [w] \e[0m : Move view window \e[36m0\e[0m : Reset view location ·
66
+ · \e[36m[a][s][d]\e[0m (\e[36mALT\e[0m increases speed) (Press again: reset cursor) ·
57
67
  · ·
58
68
  · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
59
69
  HELPSCREENPAGE
60
70
 
61
- HELP_SCREEN_ADVANCED = <<~'HELPSCREENADVANCEDPAGE'
62
- q : Quit Scry ? : Cycle help panels (2/2)
71
+ HELP_SCREEN_ADVANCED = <<~"HELPSCREENADVANCEDPAGE"
72
+ \e[36mq\e[0m : Quit Scry \e[36m?\e[0m : Cycle help panels (2/2)
63
73
 
64
74
  ADVANCED: · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
65
75
  · DIGGING DEEPER: ·
66
76
  · For current or selected row(s)... ·
67
- · @ : Build instance variable sub-rows ·
68
- · . : Build ActiveRecord association sub-rows ·
69
- · ( : Attempt to smart-build sub-rows, if Enumerable. Usually '@' is preferable. ·
77
+ · \e[36m@\e[0m : Build instance variable sub-rows ·
78
+ · \e[36m.\e[0m : Build ActiveRecord association sub-rows ·
79
+ · \e[36m(\e[0m : Attempt to smart-build sub-rows, if Enumerable. Usually '@' is preferable. ·
80
+ · \e[36mo\e[0m : Quick Open: builds the most likely helpful sub-rows ( '.' || '@' || '(' ) ·
70
81
  · ·
71
82
  · SELECTING ROWS: ·
72
- · * : Select/Deselect ALL rows ·
73
- · | : Select/Deselect every sibling row under the same parent row ·
74
- · - : Select/Deselect current row ·
83
+ · \e[36m*\e[0m : Select/Deselect ALL rows ·
84
+ · \e[36m|\e[0m : Select/Deselect every sibling row under the same parent row ·
85
+ · \e[36m-\e[0m : Select/Deselect current row ·
86
+ · ·
87
+ · MANAGING MULTIPLE SESSION TABS: ·
88
+ · \e[36mTab\e[0m : Change session tab (to the right) (\e[36mShift+Tab\e[0m moves left) ·
89
+ · \e[36mQ\e[0m : Close current session tab ·
75
90
  · ·
76
91
  · TEXT SEARCH: ·
77
- · / : Begin a text search (in tree view) ·
78
- · n : Move to next search result ·
92
+ · \e[36m/\e[0m : Begin a text search (in tree view) ·
93
+ · \e[36mn\e[0m : Move to next search result ·
94
+ · ·
95
+ · ·
96
+ · \e[36m=\e[0m : Open prompt to type a console handle for current or selected row(s) ·
97
+ · ·
98
+ · \e[36mEsc\e[0m : Resets selection, last search, and number-to-move. (or returns to Tree View) ·
79
99
  · ·
80
100
  · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
81
101
  HELPSCREENADVANCEDPAGE
@@ -94,7 +114,8 @@ module Scryglass
94
114
 
95
115
  def self.load_silently
96
116
  begin
97
- add_kernel_methods
117
+ add_kernel_method
118
+ create_scryglass_session_manager
98
119
  { success: true, error: nil }
99
120
  rescue => e
100
121
  { success: false, error: e }
@@ -127,8 +148,7 @@ module Scryglass
127
148
  | > my_object.scry
128
149
  |
129
150
  | To resume the previous session: (in same console session)
130
- | > scry OR
131
- | > scry_resume (if you're in a breakpoint pry)
151
+ | > scry
132
152
  \e[0m
133
153
  CONSOLE_HELP
134
154
 
@@ -137,39 +157,51 @@ module Scryglass
137
157
 
138
158
  private
139
159
 
140
- def self.add_kernel_methods
160
+ def self.create_scryglass_session_manager
161
+ $scry_session_manager = Scryglass::SessionManager.new
162
+ end
163
+
164
+ def self.add_kernel_method
141
165
  Kernel.module_eval do
142
- def scry(arg = nil, actions = nil) # `actions` can't be a keyword arg due
143
- # to this ruby issue: https://bugs.ruby-lang.org/issues/8316
166
+ def scry(arg = nil, _actions = nil)
167
+ # `actions` can't be a keyword arg due to this ruby issue:
168
+ # https://bugs.ruby-lang.org/issues/8316
144
169
 
145
- receiver = self unless to_s == 'main'
170
+ Scryglass.config.validate!
171
+
172
+ current_console_binding = binding.of_caller(1)
173
+
174
+ receiver_is_just_the_console = self == current_console_binding.receiver
175
+ receiver = self unless receiver_is_just_the_console
146
176
  # As in: `receiver.scry`,
147
177
  # and no receiver means scry was called on 'main', (unless self is
148
178
  # different in the because you've pry'd into something!)
149
179
 
150
180
  seed_object = arg || receiver
151
181
 
152
- $scry_session = Scryglass::Session.new(seed_object) if seed_object
153
- # If it's been given an arg or receiver, create new session!
154
- # The global variable is purposeful, and not accessible outside of
155
- # the one particular console instance.
156
-
157
- scry_resume(actions) # Pick up the new or previous session
158
- end
159
-
160
- # For the user, this is mainly just for pry sessions where `self` isn't `main`
161
- def scry_resume(actions = nil)
162
- Scryglass.config.validate!
182
+ if seed_object
183
+ # If it's been given an arg or receiver, create new session!
184
+ # The global variable is purposeful, and not accessible outside of
185
+ # the one particular console instance.
186
+ $scry_session_manager << Scryglass::Session.new(seed_object)
187
+ end
163
188
 
164
- no_previous_session = $scry_session.nil?
165
- if no_previous_session
189
+ unless $scry_session_manager.current_session
166
190
  raise ArgumentError,
167
- '`scry` requires either an argument, a receiver, or a past' \
191
+ '`scry` requires either an argument, a receiver, or a past ' \
168
192
  'session to reopen. try `Scryglass.help`'
169
193
  end
170
194
 
171
- Hexes.stdout_rescue do
172
- $scry_session.run_scry_ui(actions: actions)
195
+ $scry_session_manager.track_binding!(current_console_binding)
196
+
197
+ begin
198
+ Hexes.stdout_rescue do
199
+ $scry_session_manager.run_scry_ui
200
+ end
201
+ rescue => e # Here we ensure good visibility in case of errors
202
+ screen_height, _screen_width = $stdout.winsize
203
+ $stdout.write "\e[#{screen_height};1H\n" # (Moves console cursor to bottom left corner)
204
+ raise e
173
205
  end
174
206
  end
175
207
  end
@@ -0,0 +1,10 @@
1
+ module Scryglass
2
+ class BindingTracker
3
+ attr_accessor :console_binding, :user_named_variables
4
+
5
+ def initialize(console_binding:)
6
+ self.user_named_variables = []
7
+ self.console_binding = console_binding
8
+ end
9
+ end
10
+ end
@@ -26,16 +26,25 @@ module Scryglass
26
26
  self.lenses = [ # Custom lenses can easily be added as name+lambda hashes! Or comment some out to turn them off.
27
27
  { name: 'Pretty Print (`pp`)',
28
28
  lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { pp o } } },
29
+ { name: 'Amazing Print (`ap`)',
30
+ lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { ap o } } }, # This has colors!
29
31
  { name: 'Inspect (`.inspect`)',
30
32
  lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o.inspect } } },
31
33
  { name: 'Yaml Print (`y`)',
32
34
  lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { require 'yaml' ; y o } } }, # OR: `puts o.to_yaml`
33
35
  { name: 'Puts (`puts`)',
34
36
  lambda: ->(o) { Hexes.capture_io(char_limit: 20_000) { puts o } } },
35
- # { name: 'Method Showcase',
36
- # lambda: ->(o) { Scryglass::LensHelper.method_showcase_for(o) } },
37
+ { name: 'Method Showcase',
38
+ lambda: ->(o) { Scryglass::LensHelper.method_showcase_for(o) } },
37
39
  ]
38
40
 
41
+ ## AmazingPrint defaults, if the user has not set their own:
42
+ ::AmazingPrint.defaults ||= {
43
+ index: false, # (Don't display array indices).
44
+ raw: true, # (Recursively format instance variables).
45
+ }
46
+ # See https://github.com/amazing-print/amazing_print
47
+
39
48
  ## Building ActiveRecord association sub-rows:
40
49
  self.include_empty_associations = true
41
50
  self.include_through_associations = false
@@ -2,19 +2,64 @@
2
2
 
3
3
  module Scryglass
4
4
  module LensHelper
5
- def method_showcase_for(object)
5
+ def self.method_showcase_for(object)
6
+ # method_list = object.methods - Object.methods
6
7
  method_list = object.methods - Object.methods
8
+ return '' if method_list.empty?
9
+
7
10
  label_space = [method_list.map(&:length).max, 45].min
8
11
  method_list.sort.map do |method_name|
9
- label = method_name.to_s.ljust(label_space, ' ')
12
+ label = method_name.to_s
13
+ label_padding = ' ' * [(label_space - label.length), 0].max
14
+ label = "\e[1;34m#{label}\e[0m" # make blue and bold
15
+
10
16
  begin
11
17
  method = object.method(method_name)
12
- label + ' : ' +
13
- method.source_location.to_a.join(':') + "\n" +
14
- Hexes.capture_io { puts method.source }
18
+
19
+ method_source_location = method.source_location.to_a.join(':')
20
+ source_location_line =
21
+ unless method_source_location.empty?
22
+ " \e[36m\e[4m#{method_source_location}\e[0m\n" # Cyan, underlined
23
+ end
24
+
25
+ highlighted_space = "\e[7m\s\e[0m"
26
+ method_lines = Hexes.capture_io { puts method.source }.split("\n")
27
+ method_lines.prepend('')
28
+ method_source = method_lines.map do |line|
29
+ ' ' + highlighted_space + line
30
+ end.join("\n")
31
+
32
+ translated_parameters = method.parameters.map do |pair|
33
+ arg_type = pair[0]
34
+ arg_name = pair[1]
35
+
36
+ case arg_type
37
+ when :req
38
+ "#{arg_name}"
39
+ when :opt
40
+ "#{arg_name} = ?"
41
+ when :keyreq
42
+ "#{arg_name}:"
43
+ when :key
44
+ "#{arg_name}: ?"
45
+ when :rest
46
+ "*#{arg_name}"
47
+ when :keyrest
48
+ "**#{arg_name}"
49
+ when :block
50
+ "&#{arg_name}"
51
+ end
52
+ end
53
+
54
+ arg_preview = "(#{translated_parameters.join(', ')})"
55
+
56
+ "#{label} #{arg_preview}\n" +
57
+ source_location_line +
58
+ "#{method_source}\n"
15
59
  rescue => e
16
- label + ' : Error: ' +
17
- e.message + "\n"
60
+ "#{label}#{label_padding} : " \
61
+ "Error: \e[31m#{e.message}\n\e[0m" +
62
+ (source_location_line || '')
18
63
  end
19
64
  end.join("\n")
20
65
  end
@@ -51,44 +51,64 @@ module Scryglass
51
51
 
52
52
  ## Here we cut down the (rectangular) display array in both dimensions (into a smaller rectangle), as needed, to fit the view.
53
53
  sliced_lines = split_lines.map do |string|
54
- ansi_length = string.length - string.ansiless_length # Escape codes make `length` different from display length!
55
- slice_length = screen_width + ansi_length
56
- string[current_view_coords[:x], slice_length] || '' # If I don't want to
57
- # opacify here, I need to account for nils when the view is fully
58
- # beyond the shorter lines.
54
+ string.ansi_slice(current_view_coords[:x], screen_width) || '' # If I
55
+ # don't want to opacify here, I need to account for nils when the view
56
+ # is fully beyond the shorter lines.
59
57
  end
60
58
  sliced_list = sliced_lines[current_view_coords[:y], non_header_view_size]
61
59
 
62
- sliced_list.join("\n")
60
+ sliced_list
63
61
  end
64
62
 
65
63
  def recalculate_y_boundaries
66
- self.y_boundaries = 0...(uncut_body_string.count("\n") + 1)
64
+ number_of_lines = uncut_body_string.count("\n") + 1
65
+ preview_row = 1
66
+ self.y_boundaries = 0...(number_of_lines + preview_row)
67
67
  end
68
68
 
69
69
  def recalculate_x_boundaries
70
70
  _screen_height, screen_width = $stdout.winsize
71
71
 
72
72
  split_lines = uncut_body_string.split("\n")
73
- length_of_longest_line = split_lines.map(&:length).max || 0
73
+ length_of_longest_line = split_lines.map(&:ansiless_length).max || 0
74
74
  max_line_length = [length_of_longest_line, screen_width].max
75
+ preview_column = 1
75
76
 
76
- self.x_boundaries = 0...max_line_length
77
+ self.x_boundaries = 0...(max_line_length + preview_column)
77
78
  end
78
79
 
79
80
  def current_ro_subheader
80
81
  current_ro = scry_session.current_ro
81
- user_input = scry_session.user_input
82
+ last_keypress = scry_session.last_keypress
82
83
 
83
84
  row_above_string =
84
85
  current_ro.next_visible_ro_up.to_s if current_ro.next_visible_ro_up
85
86
  row_below_string =
86
87
  current_ro.next_visible_ro_down.to_s if current_ro.next_visible_ro_down
87
88
 
88
- tree_preview_related_commands = ['A', 'B', 'C', 'D',
89
- '@', '.', '(', '*', '|', '-']
89
+ tree_preview_related_keys = ::Scryglass::Session::KEY_MAP.slice(
90
+ :move_cursor_up,
91
+ :move_cursor_down,
92
+ :homerow_move_cursor_up,
93
+ :homerow_move_cursor_down,
94
+ :homerow_move_cursor_up_fast,
95
+ :homerow_move_cursor_down_fast,
96
+ :open_bucket,
97
+ :close_bucket,
98
+ :homerow_open_bucket,
99
+ :homerow_close_bucket,
100
+ :build_instance_variables,
101
+ :build_ar_relations,
102
+ :build_enum_children,
103
+ :smart_open,
104
+ :select_siblings,
105
+ :select_all,
106
+ :select_current,
107
+ :continue_search,
108
+ ).values
109
+
90
110
  ro_view_label =
91
- if tree_preview_related_commands.include?(user_input)
111
+ if tree_preview_related_keys.include?(last_keypress)
92
112
  "\e[7mVIEWING:\e[00m" # Color reversed
93
113
  else
94
114
  'VIEWING:'
@@ -108,7 +128,8 @@ module Scryglass
108
128
  current_lens = scry_session.current_lens
109
129
  current_subject_type = scry_session.current_subject_type
110
130
  current_subject = scry_session.current_ro.current_subject
111
- user_input = scry_session.user_input
131
+ last_keypress = scry_session.last_keypress
132
+ key_map = ::Scryglass::Session::KEY_MAP
112
133
 
113
134
  lens_count = LensPanel.lenses.count
114
135
  lens_id = current_lens % lens_count
@@ -117,23 +138,26 @@ module Scryglass
117
138
  longest_lens_name_length = LensPanel.lenses.map do |lens|
118
139
  lens[:name].length
119
140
  end.max
120
- lens_type_header_length = 9 + (lens_count.to_s.length * 2)
141
+ lens_type_header_length = 13 + (lens_count.to_s.length * 2)
121
142
  + longest_lens_name_length
122
- subject_type_header = "SUBJECT: #{current_subject_type}".ljust(14, ' ')
143
+ subject_type_header = "[<] SUBJECT: #{current_subject_type}".ljust(18, ' ')
123
144
  subject_class_header = " CLASS: #{current_subject.class}"
124
- lens_type_header = " LENS #{lens_id + 1}/#{lens_count}: #{lens[:name]}"
145
+ lens_type_header = " [>] LENS #{lens_id + 1}/#{lens_count}: #{lens[:name]}"
125
146
  .ljust(lens_type_header_length, ' ')
126
147
 
148
+ user_just_switched_lens = last_keypress == key_map[:switch_lens]
149
+ user_just_switched_subject_type = last_keypress == key_map[:switch_subject_type]
150
+
151
+ if user_just_switched_lens
152
+ lens_type_header = "\e[7m#{lens_type_header}\e[00m" # Color reversed
153
+ elsif user_just_switched_subject_type
154
+ subject_type_header = "\e[7m#{subject_type_header}\e[00m" # Color reversed
155
+ end
156
+
127
157
  fit_lens_header = [
128
158
  subject_type_header, subject_class_header, lens_type_header
129
159
  ].fit_to(screen_width)
130
160
 
131
- if user_input == 'l'
132
- fit_lens_header[4] = "\e[7m#{fit_lens_header[4]}" # Format to be ended by Hexes.opacify_screen_string() (using \e[00m)
133
- elsif user_input == 'L'
134
- fit_lens_header[0] = "\e[7m#{fit_lens_header[0]}\e[00m"
135
- end
136
-
137
161
  fit_lens_header.join('')
138
162
  end
139
163
  end