scryglass 0.1.0

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.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scryglass
4
+ class TreePanel < Scryglass::ViewPanel
5
+ using ClipStringRefinement
6
+ using AnsilessStringRefinement
7
+
8
+ def slide_view_to_cursor
9
+ cursor_tracking = Scryglass.config.cursor_tracking
10
+
11
+ current_ro = scry_session.current_ro
12
+
13
+ ## Here we calculate the ro_in_center_of_view:
14
+ visible_ros_from_center = (body_screen_height / 2)
15
+ scanning_ro = top_visible_ro_of_tree_view
16
+ visible_ros_from_center.times do
17
+ next_visible_ro = scanning_ro.next_visible_ro_down
18
+ scanning_ro = next_visible_ro if next_visible_ro
19
+ end
20
+ ro_in_center_of_view = scanning_ro
21
+
22
+ ## We don't need to do anything if current_ro is already in the center:
23
+ relative_index = current_ro.index - ro_in_center_of_view.index
24
+ return if relative_index.zero?
25
+
26
+ ## Establish the number of visible ros between current_ro and center point
27
+ index_span = [ro_in_center_of_view.index, current_ro.index]
28
+ ros_between_them = scry_session.all_ros[index_span.min...index_span.max]
29
+ visible_count_between_them = ros_between_them.count(&:visible?)
30
+
31
+ direction = :up if relative_index.negative?
32
+ direction = :down if relative_index.positive?
33
+
34
+ ## If view movement is needed, and how far, depends on the tracking config
35
+ case cursor_tracking
36
+ when :flexible_range
37
+ flex_range = body_screen_height / 3
38
+ if visible_count_between_them >= flex_range
39
+ distance_to_flex_range = visible_count_between_them - flex_range
40
+ move_view_up(distance_to_flex_range) if direction == :up
41
+ move_view_down(distance_to_flex_range) if direction == :down
42
+ end
43
+ when :dead_center
44
+ move_view_up(visible_count_between_them) if direction == :up
45
+ move_view_down(visible_count_between_them) if direction == :down
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def uncut_body_string
52
+ body_array_from_ro(top_visible_ro_of_tree_view).join("\n")
53
+ end
54
+
55
+ def uncut_header_string
56
+ _screen_height, screen_width = $stdout.winsize
57
+ header_string = "Press '?' for controls\n".rjust(screen_width, ' ') +
58
+ '·' * screen_width
59
+ if scry_session.special_command_targets.any?
60
+ special_targets_message = " (Next command will apply to all selected rows)"
61
+ header_string[0...special_targets_message.length] = special_targets_message
62
+ end
63
+ header_string
64
+ end
65
+
66
+ def visible_body_slice(uncut_body_string)
67
+ _screen_height, screen_width = $stdout.winsize
68
+
69
+ split_lines = uncut_body_string.split("\n")
70
+ sliced_lines = split_lines.map do |string|
71
+ ansi_length = string.length - string.ansiless.length
72
+ slice_length = screen_width + ansi_length
73
+ string[current_view_coords[:x], slice_length] || '' # If I don't want to
74
+ # opacify here, I need to account for nils when the view is fully
75
+ # beyond the shorter lines.
76
+ end
77
+
78
+ sliced_lines.join("\n")
79
+ end
80
+
81
+ def recalculate_y_boundaries
82
+ self.y_boundaries = 0...scry_session.all_ros.select(&:visible?).count
83
+ end
84
+
85
+ def recalculate_x_boundaries
86
+ _screen_height, screen_width = $stdout.winsize
87
+
88
+ split_lines = uncut_body_string.split("\n")
89
+ length_of_longest_line = split_lines.map(&:length).max
90
+ max_line_length = [length_of_longest_line, screen_width].max
91
+ self.x_boundaries = 0...max_line_length
92
+ end
93
+
94
+ def top_visible_ro_of_tree_view
95
+ top_ro = scry_session.top_ro
96
+
97
+ scanning_ro = top_ro
98
+ top_ros = [scanning_ro]
99
+ until top_ros.count > current_view_coords[:y] || scanning_ro.next_visible_ro_down.nil? #I shouldn't need this?
100
+ scanning_ro = scanning_ro.next_visible_ro_down
101
+ top_ros << scanning_ro
102
+ end
103
+ top_ros.last
104
+ end
105
+
106
+ def body_array_from_ro(ro)
107
+ y, _x = $stdout.winsize
108
+ non_header_view_size = y - visible_header_string.split("\n").count
109
+ display_array = []
110
+
111
+ scanning_ro = ro
112
+ display_array << scanning_ro.to_s
113
+
114
+ while scanning_ro.next_visible_ro_down && display_array.count < non_header_view_size
115
+ scanning_ro = scanning_ro.next_visible_ro_down
116
+ display_array << scanning_ro.to_s
117
+ end
118
+
119
+ display_array
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,3 @@
1
+ module Scryglass
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scryglass
4
+ class ViewPanel
5
+ using AnsilessStringRefinement
6
+
7
+ attr_accessor :current_view_coords, :x_boundaries, :y_boundaries
8
+ attr_accessor :scry_session
9
+
10
+ def initialize(scry_session:)
11
+ self.scry_session = scry_session
12
+ self.current_view_coords = { x: 0, y: 0 }
13
+
14
+ recalculate_boundaries
15
+ end
16
+
17
+ def ensure_correct_view_coords
18
+ _screen_height, screen_width = $stdout.winsize
19
+ top_boundary = y_boundaries.min
20
+ bottom_boundary = y_boundaries.max
21
+ left_boundary = x_boundaries.min
22
+ right_boundary = x_boundaries.max
23
+
24
+ ## Snap View, Vertical
25
+ screen_bottom_index = (body_screen_height - 1)
26
+ top_edge_of_view = current_view_coords[:y]
27
+ bottom_edge_of_view = current_view_coords[:y] + screen_bottom_index
28
+ if bottom_edge_of_view > bottom_boundary
29
+ current_view_coords[:y] = [bottom_boundary - screen_bottom_index, 0].max
30
+ elsif top_edge_of_view < top_boundary
31
+ current_view_coords[:y] = top_boundary
32
+ end
33
+
34
+ ## Snap View, Horizontal
35
+ screen_right_edge_index = (screen_width - 1)
36
+ left_edge_of_view = current_view_coords[:x]
37
+ right_edge_of_view = current_view_coords[:x] + screen_right_edge_index
38
+ if !x_boundaries.include?(left_edge_of_view)
39
+ current_view_coords[:x] = left_boundary
40
+ elsif !x_boundaries.include?(right_edge_of_view)
41
+ current_view_coords[:x] = right_boundary - screen_right_edge_index
42
+ end
43
+ end
44
+
45
+ def move_view_up(distance)
46
+ current_view_coords[:y] -= distance
47
+ end
48
+
49
+ def move_view_down(distance)
50
+ current_view_coords[:y] += distance
51
+ end
52
+
53
+ def move_view_left(distance)
54
+ current_view_coords[:x] -= distance
55
+ end
56
+
57
+ def move_view_right(distance)
58
+ current_view_coords[:x] += distance
59
+ end
60
+
61
+ def screen_string
62
+ Hexes.opacify_screen_string(visible_header_string + "\n" + visible_body_string)
63
+ end
64
+
65
+ def recalculate_boundaries
66
+ recalculate_y_boundaries
67
+ recalculate_x_boundaries
68
+ end
69
+
70
+ def visible_header_string
71
+ visible_header_slice(uncut_header_string)
72
+ end
73
+
74
+ def visible_body_string
75
+ visible_body_slice(uncut_body_string)
76
+ end
77
+
78
+ private
79
+
80
+ def visible_header_slice(uncut_header_string)
81
+ Hexes.simple_screen_slice(uncut_header_string)
82
+ end
83
+
84
+ def body_screen_height
85
+ screen_height, _screen_width = $stdout.winsize
86
+ # It would be more efficient, but technically overall less accurate, to
87
+ # avoid recalculating visible_header_string here (and everywhere that calls this)
88
+ screen_height - visible_header_string.split("\n").count
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scryglass
4
+ class ViewWrapper
5
+ attr_accessor :model, :string, :string_lambda
6
+
7
+ def initialize(model, string: nil, string_lambda: nil)
8
+ unless !!string ^ !!string_lambda
9
+ raise ArgumentError, 'Must provide either `string` or `string_lambda`, ' \
10
+ 'but not both.'
11
+ end
12
+
13
+ self.model = model
14
+ self.string = string
15
+ self.string_lambda = string_lambda
16
+ end
17
+
18
+ def to_s
19
+ return string if string
20
+ return string_lambda.call(model) if string_lambda
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "scryglass/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scryglass"
8
+ spec.version = Scryglass::VERSION
9
+ spec.authors = ["Gavin Myers"]
10
+ spec.email = ["gavin.myers@annkissam.com"]
11
+
12
+ spec.summary = 'Scryglass is a ruby console tool for visualizing ' \
13
+ 'and actively exploring objects.'
14
+ spec.description = 'Scryglass is a ruby console tool for visualizing ' \
15
+ 'and actively exploring objects (large, nested, interrelated, ' \
16
+ 'or unfamiliar). You can navigate nested arrays, hashes, instance variables, ' \
17
+ 'ActiveRecord relations, and unknown Enumerable types like an' \
18
+ "expandable/collapsable file tree in an intuitive UI.\n\n" \
19
+ 'Objects and child objects can also be inspected through a variety of ' \
20
+ 'display lenses, returned directly to the console, and more!'
21
+ spec.homepage = "https://github.com/annkissam/scryglass"
22
+
23
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
24
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
25
+ if spec.respond_to?(:metadata)
26
+ spec.metadata["allowed_push_host"] = 'https://rubygems.org/'
27
+ else
28
+ raise "RubyGems 2.0 or newer is required to protect against " \
29
+ "public gem pushes."
30
+ end
31
+
32
+ # Specify which files should be added to the gem when it is released.
33
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
34
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
35
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
36
+ end
37
+ spec.bindir = "exe"
38
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
39
+ spec.require_paths = ["lib"]
40
+
41
+ spec.required_ruby_version = '>= 2.4.4'
42
+
43
+ spec.add_development_dependency 'activesupport', '~> 5.0'
44
+ spec.add_development_dependency 'bundler', '~> 2.1'
45
+ spec.add_development_dependency 'rake', '~> 10.0'
46
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scryglass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gavin Myers
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description: |-
56
+ Scryglass is a ruby console tool for visualizing and actively exploring objects (large, nested, interrelated, or unfamiliar). You can navigate nested arrays, hashes, instance variables, ActiveRecord relations, and unknown Enumerable types like anexpandable/collapsable file tree in an intuitive UI.
57
+
58
+ Objects and child objects can also be inspected through a variety of display lenses, returned directly to the console, and more!
59
+ email:
60
+ - gavin.myers@annkissam.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".tool-versions"
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/console
73
+ - bin/setup
74
+ - example_config.rb
75
+ - lib/example_material.rb
76
+ - lib/hexes.rb
77
+ - lib/prog.rb
78
+ - lib/refinements/ansiless_string_refinement.rb
79
+ - lib/refinements/array_fit_to_refinement.rb
80
+ - lib/refinements/clip_string_refinement.rb
81
+ - lib/refinements/constant_defined_string_refinement.rb
82
+ - lib/scryglass.rb
83
+ - lib/scryglass/config.rb
84
+ - lib/scryglass/lens_helper.rb
85
+ - lib/scryglass/lens_panel.rb
86
+ - lib/scryglass/ro.rb
87
+ - lib/scryglass/ro_builder.rb
88
+ - lib/scryglass/session.rb
89
+ - lib/scryglass/tree_panel.rb
90
+ - lib/scryglass/version.rb
91
+ - lib/scryglass/view_panel.rb
92
+ - lib/scryglass/view_wrapper.rb
93
+ - scryglass.gemspec
94
+ homepage: https://github.com/annkissam/scryglass
95
+ licenses: []
96
+ metadata:
97
+ allowed_push_host: https://rubygems.org/
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 2.4.4
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.1.4
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Scryglass is a ruby console tool for visualizing and actively exploring objects.
117
+ test_files: []