tk_inspect 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,2 @@
1
+ require_relative './console/base'
2
+ require_relative './console/root_component'
@@ -0,0 +1,118 @@
1
+ module TkInspect
2
+ module Console
3
+ class Base
4
+ cattr_accessor :main_console
5
+
6
+ attr_accessor :tk_root
7
+ attr_accessor :main_component
8
+ attr_accessor :eval_binding
9
+ attr_accessor :modal
10
+ attr_accessor :root
11
+
12
+ def initialize(options = {})
13
+ @tk_root = nil
14
+ @main_component = nil
15
+ @eval_binding = binding
16
+ @modal = !!options[:modal]
17
+ @root = !!options[:root]
18
+ end
19
+
20
+ def make_main
21
+ self.class.main_console = self
22
+ end
23
+
24
+ def focus
25
+ create_root if @main_component.nil?
26
+ @tk_root.focus
27
+ @tk_root.tk_item.native_item.after(100) { @main_component.focus_on_code }
28
+ end
29
+
30
+ def refresh
31
+ @main_component.regenerate
32
+ @main_component.focus
33
+ end
34
+
35
+ def modal_loop
36
+ @main_component.run_modal_loop
37
+ end
38
+
39
+ def execute(code)
40
+ eval(code, eval_binding)
41
+ end
42
+
43
+ def say(s)
44
+ main_component.show_output(s)
45
+ return nil
46
+ end
47
+
48
+ def open_inspector(expression = nil)
49
+ if expression.present?
50
+ val = eval(expression, eval_binding)
51
+ TkInspect::Inspector::Base
52
+ .inspector_for_object(val)
53
+ .new(expression: expression,
54
+ binding: eval_binding,
55
+ value: val).refresh
56
+ else
57
+ TkInspect::Inspector::Base
58
+ .new(expression: expression,
59
+ binding: eval_binding).refresh
60
+ end
61
+ end
62
+
63
+ def open_class_browser
64
+ TkInspect::ClassBrowser::Base.new.refresh
65
+ end
66
+
67
+ def create_root
68
+ @tk_root = TkComponent::Window.new(title: "Ruby Console", root: @root)
69
+ @main_component = RootComponent.new
70
+ @main_component.console = self
71
+ @tk_root.place_root_component(@main_component)
72
+ create_menu
73
+ @tk_root.tk_item.native_item.after(100) { @main_component.focus_on_code }
74
+ end
75
+
76
+ def create_menu
77
+ @menubar = TkMenu.new(@tk_root.tk_item.native_item)
78
+ @menu = {}
79
+ file = TkMenu.new(@menubar)
80
+ @menu[:file] = file
81
+ edit = TkMenu.new(@menubar)
82
+ @menu[:edit] = edit
83
+ edit.add :command, label: "Undo", accelerator: 'Command+z', command: -> { Tk.event_generate(Tk.focus, "<Undo>") }
84
+ edit.add :command, label: "Redo", accelerator: 'Command+Shift+z', command: -> { Tk.event_generate(Tk.focus, "<Redo>") }
85
+ edit.add :separator
86
+ edit.add :command, label: "Cut", accelerator: 'Command+x', command: -> { Tk.event_generate(Tk.focus, "<Cut>") }
87
+ edit.add :command, label: "Copy", accelerator: 'Command+c', command: -> { Tk.event_generate(Tk.focus, "<Copy>") }
88
+ edit.add :command, label: "Paste", accelerator: 'Command+v', command: -> { Tk.event_generate(Tk.focus, "<Paste>") }
89
+ edit.add :command, label: "Clear", accelerator: 'Delete', command: -> { Tk.event_generate(Tk.focus, "<Clear>") }
90
+ view = TkMenu.new(@menubar)
91
+ @menu[:view] = view
92
+ view.add :command, label: "Bigger font", accelerator: 'Command++', command: -> { main_component.zoom_in(nil) }
93
+ view.add :command, label: "Smaller font", accelerator: 'Command+-', command: -> { main_component.zoom_out(nil) }
94
+ tools = TkMenu.new(@menubar)
95
+ @menu[:tools] = tools
96
+ tools.add :command, label: "Run selection", accelerator: 'Command+r', command: -> { main_component.run_selected(nil) }
97
+ tools.add :command, label: "Inspect selection", accelerator: 'Command+i', command: -> { main_component.inspect_selected(nil) }
98
+ tools.add :separator
99
+ tools.add :command, label: "Clear output", accelerator: 'Command+k', command: -> { main_component.clear_output(nil) }
100
+ tools.add :separator
101
+ tools.add :command, label: "Inspector ...", accelerator: 'Command+Shift+i', command: -> { open_inspector }
102
+ tools.add :command, label: "Class Browser ...", accelerator: 'Command+Shift+b', command: -> { open_class_browser }
103
+ @menubar.add :cascade, menu: file, label: 'File'
104
+ @menubar.add :cascade, menu: edit, label: 'Edit'
105
+ @menubar.add :cascade, menu: view, label: 'View'
106
+ @menubar.add :cascade, menu: tools, label: 'Tools'
107
+ @tk_root.tk_item.native_item['menu'] = @menubar
108
+ @tk_root.tk_item.native_item.bind('Command-r', -> { main_component.run_selected(nil) })
109
+ @tk_root.tk_item.native_item.bind('Command-k', -> { main_component.clear_output(nil) })
110
+ @tk_root.tk_item.native_item.bind('Command-+', -> { main_component.zoom_in(nil) })
111
+ @tk_root.tk_item.native_item.bind('Command-minus', -> { main_component.zoom_out(nil) })
112
+ @tk_root.tk_item.native_item.bind('Command-i', -> { main_component.inspect_selected(nil) })
113
+ @tk_root.tk_item.native_item.bind('Command-Shift-i', -> { open_inspector })
114
+ @tk_root.tk_item.native_item.bind('Command-Shift-b', -> { open_class_browser })
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,104 @@
1
+ module TkInspect
2
+ module Console
3
+ class RootComponent < TkComponent::Base
4
+ attr_accessor :console
5
+
6
+ def generate(parent_component, options = {})
7
+ parse_component(parent_component, options) do |p|
8
+ p.vframe(sticky: 'nsew', x_flex: 1, y_flex: 1) do |r|
9
+ r.hframe(sticky: 'new', padding: '8', x_flex: 0) do |hf|
10
+ hf.label(text: "Write Ruby code here", sticky: 'w')
11
+ hf.hframe(sticky: 'ne', x_flex: 1) do |buttons|
12
+ buttons.button(text: "Run selected", default: "active", on_click: :run_selected)
13
+ buttons.button(text: "Return", on_click: :return_from_modal) if console.modal
14
+ end
15
+ end
16
+ r.vpaned(sticky: 'nswe', x_flex: 1, y_flex: 1) do |vp|
17
+ @input = vp.text(sticky: 'nswe', scrollers: 'y',
18
+ highlightthickness: 0,
19
+ x_flex: 1, y_flex: 1)
20
+ vp.vframe(padding: "0 0 0 0", sticky: 'nswe', x_flex: 1, y_flex: 1) do |vf|
21
+ vf.hframe(sticky: 'new', padding: '8', x_flex: 0) do |hf|
22
+ hf.label(text: "Output", padding: '8', sticky: 'w')
23
+ hf.hframe(sticky: 'ne', x_flex: 1) do |buttons|
24
+ buttons.button(text: "Clear", on_click: :clear_output)
25
+ end
26
+ end
27
+ @output = vf.text(sticky: 'nswe', scrollers: 'y',
28
+ background: 'systemSheetBackgroundOpaque', highlightthickness: 0,
29
+ x_flex: 1, y_flex: 1)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def focus_on_code
37
+ @input.tk_item.focus
38
+ end
39
+
40
+ def run_modal_loop
41
+ @running_modal_loop = true
42
+ while @running_modal_loop
43
+ Tk.update
44
+ sleep(0.019)
45
+ end
46
+ end
47
+
48
+ def run_selected(e)
49
+ code = @input.tk_item.selected_text
50
+ code = @input.tk_item.current_line if code.blank?
51
+ return unless code.present?
52
+
53
+ res = console.execute(code)
54
+ show_output(res)
55
+ end
56
+
57
+ def inspect_selected(e)
58
+ code = @input.tk_item.selected_text
59
+ code = @input.tk_item.current_line if code.blank?
60
+ return unless code.present?
61
+
62
+ res = console.open_inspector(code)
63
+ end
64
+
65
+ def show_output(res)
66
+ @output.tk_item.append_text(res.to_s + "\n");
67
+ end
68
+
69
+ def zoom_in(e)
70
+ zoom_by(1.1, @input)
71
+ zoom_by(1.1, @output)
72
+ end
73
+
74
+ def zoom_out(e)
75
+ zoom_by(0.9091, @input)
76
+ zoom_by(0.9091, @output)
77
+ end
78
+
79
+ def zoom_by(factor, item)
80
+ font = item.native_item.font
81
+ new_font = TkFont.new(family: font.family, weight: font.weight, size: (font.size * factor).ceil)
82
+ geom = console.tk_root.tk_item.native_item['geometry']
83
+ item.native_item.font(new_font)
84
+ console.tk_root.tk_item.native_item['geometry'] = geom
85
+ end
86
+
87
+ def clear_output(e)
88
+ @output.tk_item.value = ''
89
+ end
90
+
91
+ def inspect(e)
92
+ console.open_inspector
93
+ end
94
+
95
+ def open_class_browser(e)
96
+ console.open_class_browser
97
+ end
98
+
99
+ def return_from_modal(e)
100
+ @running_modal_loop = false
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,2 @@
1
+ require_relative './inspector/base'
2
+ require_relative './inspector/root_component'
@@ -0,0 +1,68 @@
1
+ module TkInspect
2
+ module Inspector
3
+ class Base
4
+ cattr_accessor :shared_inspector
5
+ cattr_accessor :custom_inspectors
6
+
7
+ self.custom_inspectors = {}
8
+
9
+ attr_accessor :inspected_binding
10
+ attr_accessor :expression
11
+ attr_accessor :tk_root
12
+ attr_accessor :main_component
13
+
14
+ def self.shared_inspector
15
+ shared_inspector ||= self.new
16
+ end
17
+
18
+ def self.register_shared_inspector(inspector_class, inspected_class)
19
+ custom_inspectors[inspected_class] = inspector_class
20
+ end
21
+
22
+ def self.inspector_for_class(klass)
23
+ while klass && !(inspector = custom_inspectors[klass]) do
24
+ klass = klass.superclass
25
+ end
26
+ inspector || self
27
+ end
28
+
29
+ def self.inspector_for_object(obj)
30
+ self.inspector_for_class(obj.class)
31
+ end
32
+
33
+ def initialize(options = {})
34
+ @expression = options[:expression]
35
+ @inspected_binding = options[:binding] || binding
36
+ @value = options[:value]
37
+ @tk_root = nil
38
+ @main_component = nil
39
+ end
40
+
41
+ def refresh
42
+ @main_component.nil? ? create_root : @main_component.regenerate
43
+ end
44
+
45
+ def create_root
46
+ @tk_root = TkComponent::Window.new(title: window_title)
47
+ create_main_component
48
+ @main_component.inspector = self
49
+ @tk_root.place_root_component(@main_component)
50
+ end
51
+
52
+ def browse_class(class_name)
53
+ class_browser = TkInspect::ClassBrowser::Base.new
54
+ class_browser.select_class_name(class_name)
55
+ class_browser.refresh
56
+ class_browser.show_current_path
57
+ end
58
+
59
+ def create_main_component
60
+ @main_component = RootComponent.new
61
+ end
62
+
63
+ def window_title
64
+ "Inspector"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ module TkInspect
2
+ module Inspector
3
+ class RootComponent < TkComponent::Base
4
+ attr_accessor :inspector
5
+
6
+ def generate(parent_component, options = {})
7
+ parse_component(parent_component, options) do |p|
8
+ p.vframe(padding: "0 0 0 0", sticky: 'nsew', x_flex: 1, y_flex: 1) do |f|
9
+ @table = f.insert_component(TkComponent::TableViewComponent, self,
10
+ data_source: self,
11
+ columns: [
12
+ { key: :var, text: 'Variable', anchor: 'w' },
13
+ { key: :value, text: 'Value', anchor: 'center' },
14
+ { key: :klass, text: 'Class', anchor: 'center' }
15
+ ],
16
+ nested: true,
17
+ lazy: true,
18
+ sticky: 'nsew', x_flex: 1, y_flex: 1)
19
+ f.hframe(sticky: 'se', padding: '8', x_flex: 1) do |hf|
20
+ hf.button(text: "Browse selected class", on_click: :browse_class)
21
+ hf.button(text: "Refresh", on_click: ->(e) { regenerate })
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def items_for_path(path = nil)
28
+ path = [] if path.blank?
29
+ if path.empty?
30
+ self_exp = inspector.expression || 'self'
31
+ self_value = eval(self_exp, inspector.inspected_binding)
32
+ res = [{ var: self_exp, value: self_value.value_for_tk_inspect, klass: self_value.class.to_s, real_value: self_value }]
33
+ if inspector.expression.blank?
34
+ vars = inspector.inspected_binding.local_variables.sort.map do |var_name|
35
+ value = inspector.inspected_binding.local_variable_get(var_name)
36
+ res << { var: var_name, value: value.value_for_tk_inspect, klass: value.class.to_s, real_value: value }
37
+ end
38
+ end
39
+ res
40
+ else
41
+ obj = path.last[:real_value]
42
+ obj.children_for_tk_inspect.map do |child_name, child_value|
43
+ { var: child_name, value: child_value.value_for_tk_inspect, klass: child_value.class.to_s, real_value: child_value }
44
+ end
45
+ end
46
+ end
47
+
48
+ def has_sub_items?(path)
49
+ path = [] if path.blank?
50
+ if path.empty?
51
+ return true # At least we have self
52
+ else
53
+ obj = path.last[:real_value]
54
+ obj.children_for_tk_inspect.any?
55
+ end
56
+ end
57
+
58
+ def browse_class(e)
59
+ item = @table.selected_item
60
+ return unless item && (class_name = item[:klass])
61
+ inspector.browse_class(class_name)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module TkInspect
2
+ VERSION = "0.1.0"
3
+ 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 "tk_inspect/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tk_inspect"
8
+ spec.version = TkInspect::VERSION
9
+ spec.authors = ["Josep Egea"]
10
+ spec.email = ["jes@josepegea.com"]
11
+
12
+ spec.summary = %q{Poor's man Smalltalk-like environment for Ruby using TK}
13
+ spec.description = %q{Poor's man Smalltalk-like environment for Ruby using TK}
14
+ spec.homepage = "https://github.com/josepegea/tk_inspect"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+
22
+ spec.metadata["homepage_uri"] = spec.homepage
23
+ spec.metadata["source_code_uri"] = "https://github.com/josepegea/tk_inspect"
24
+ spec.metadata["changelog_uri"] = "https://github.com/josepegea/tk_inspect"
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against " \
27
+ "public gem pushes."
28
+ end
29
+
30
+ # Specify which files should be added to the gem when it is released.
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
33
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ end
35
+ spec.bindir = "bin"
36
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_dependency "tk_component", "~> 0.1.2"
40
+
41
+ spec.add_dependency "rouge", "~> 3.26"
42
+
43
+ spec.add_development_dependency "bundler", "~> 1.16"
44
+ spec.add_development_dependency "rake", "~> 10.0"
45
+ spec.add_development_dependency "rspec", "~> 3.0"
46
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tk_inspect
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josep Egea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: tk_component
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: rouge
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.26'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.26'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: Poor's man Smalltalk-like environment for Ruby using TK
84
+ email:
85
+ - jes@josepegea.com
86
+ executables:
87
+ - console
88
+ - setup
89
+ - tk_console
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".gitignore"
94
+ - ".rspec"
95
+ - ".travis.yml"
96
+ - CODE_OF_CONDUCT.md
97
+ - Gemfile
98
+ - Gemfile.lock
99
+ - LICENSE.txt
100
+ - README.md
101
+ - Rakefile
102
+ - bin/console
103
+ - bin/setup
104
+ - bin/tk_console
105
+ - lib/base_inspectors.rb
106
+ - lib/cli_support.rb
107
+ - lib/tk_inspect.rb
108
+ - lib/tk_inspect/canvas_window.rb
109
+ - lib/tk_inspect/canvas_window/base.rb
110
+ - lib/tk_inspect/canvas_window/root_component.rb
111
+ - lib/tk_inspect/class_browser.rb
112
+ - lib/tk_inspect/class_browser/base.rb
113
+ - lib/tk_inspect/class_browser/class_namespace_data_source.rb
114
+ - lib/tk_inspect/class_browser/class_tree_data_source.rb
115
+ - lib/tk_inspect/class_browser/code_component.rb
116
+ - lib/tk_inspect/class_browser/module_method_data_source.rb
117
+ - lib/tk_inspect/class_browser/root_component.rb
118
+ - lib/tk_inspect/console.rb
119
+ - lib/tk_inspect/console/base.rb
120
+ - lib/tk_inspect/console/root_component.rb
121
+ - lib/tk_inspect/inspector.rb
122
+ - lib/tk_inspect/inspector/base.rb
123
+ - lib/tk_inspect/inspector/root_component.rb
124
+ - lib/tk_inspect/version.rb
125
+ - tk_inspect.gemspec
126
+ homepage: https://github.com/josepegea/tk_inspect
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ allowed_push_host: https://rubygems.org
131
+ homepage_uri: https://github.com/josepegea/tk_inspect
132
+ source_code_uri: https://github.com/josepegea/tk_inspect
133
+ changelog_uri: https://github.com/josepegea/tk_inspect
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.7.8
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Poor's man Smalltalk-like environment for Ruby using TK
154
+ test_files: []