looksee 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGELOG +14 -0
  2. data/LICENSE +22 -0
  3. data/README.markdown +161 -0
  4. data/Rakefile +9 -36
  5. data/ext/extconf.rb +9 -0
  6. data/ext/mri/1.9.2/debug.h +36 -0
  7. data/ext/mri/1.9.2/id.h +170 -0
  8. data/ext/mri/1.9.2/method.h +103 -0
  9. data/ext/mri/1.9.2/node.h +483 -0
  10. data/ext/mri/1.9.2/thread_pthread.h +27 -0
  11. data/ext/mri/1.9.2/vm_core.h +707 -0
  12. data/ext/mri/1.9.2/vm_opts.h +51 -0
  13. data/ext/mri/env-1.8.h +27 -0
  14. data/ext/mri/eval_c-1.8.h +27 -0
  15. data/ext/mri/mri.c +269 -0
  16. data/ext/{looksee → mri}/node-1.9.h +0 -0
  17. data/ext/rbx/rbx.c +13 -0
  18. data/lib/looksee.rb +4 -385
  19. data/lib/looksee/adapter.rb +10 -0
  20. data/lib/looksee/adapter/base.rb +100 -0
  21. data/lib/looksee/adapter/rubinius.rb +73 -0
  22. data/lib/looksee/clean.rb +122 -0
  23. data/lib/looksee/columnizer.rb +73 -0
  24. data/lib/looksee/core_ext.rb +59 -0
  25. data/lib/looksee/editor.rb +58 -0
  26. data/lib/looksee/help.rb +54 -0
  27. data/lib/looksee/inspector.rb +55 -0
  28. data/lib/looksee/lookup_path.rb +95 -0
  29. data/lib/looksee/rbx.bundle +0 -0
  30. data/lib/looksee/shortcuts.rb +1 -105
  31. data/lib/looksee/version.rb +9 -1
  32. data/lib/looksee/wirble_compatibility.rb +2 -3
  33. data/spec/adapter_spec.rb +546 -0
  34. data/spec/columnizer_spec.rb +52 -0
  35. data/spec/core_ext_spec.rb +41 -0
  36. data/spec/editor_spec.rb +128 -0
  37. data/spec/inspector_spec.rb +178 -0
  38. data/spec/lookup_path_spec.rb +84 -0
  39. data/spec/spec_helper.rb +13 -127
  40. data/spec/support/core_ext.rb +25 -0
  41. data/spec/support/temporary_classes.rb +102 -0
  42. data/spec/support/test_adapter.rb +72 -0
  43. data/spec/wirble_compatibility_spec.rb +20 -23
  44. metadata +91 -57
  45. data/.autotest +0 -9
  46. data/History.txt +0 -22
  47. data/Manifest.txt +0 -21
  48. data/README.rdoc +0 -129
  49. data/ext/looksee/extconf.rb +0 -6
  50. data/ext/looksee/looksee.c +0 -144
  51. data/looksee.gemspec +0 -44
  52. data/script/console +0 -7
  53. data/script/destroy +0 -14
  54. data/script/generate +0 -14
  55. data/spec/looksee_spec.rb +0 -425
  56. data/tasks/extconf.rake +0 -13
  57. data/tasks/extconf/looksee.rake +0 -43
@@ -0,0 +1,10 @@
1
+ require 'rbconfig'
2
+
3
+ module Looksee
4
+ module Adapter
5
+ autoload :Base, 'looksee/adapter/base'
6
+ autoload :MRI, "looksee/mri.#{Config::CONFIG['DLEXT']}"
7
+ autoload :JRuby, 'looksee/JRuby.jar'
8
+ autoload :Rubinius, "looksee/adapter/rubinius"
9
+ end
10
+ end
@@ -0,0 +1,100 @@
1
+ module Looksee
2
+ module Adapter
3
+ class Base
4
+ #
5
+ # Return the chain of classes and modules which comprise the
6
+ # object's method lookup path.
7
+ #
8
+ def lookup_modules(object)
9
+ modules = []
10
+ klass = internal_class(object)
11
+ while klass
12
+ modules << internal_class_to_module(klass)
13
+ klass = internal_superclass(klass)
14
+ end
15
+ modules
16
+ end
17
+
18
+ #
19
+ # Return a description of the given module.
20
+ #
21
+ # This is used for the module labels in the Inspector output.
22
+ #
23
+ def describe_module(mod)
24
+ num_brackets = 0
25
+ object = mod
26
+ while singleton_class?(object)
27
+ num_brackets += 1
28
+ object = singleton_instance(object)
29
+ end
30
+
31
+ if object.is_a?(Module)
32
+ description = module_name(object)
33
+ if description.empty?
34
+ description = "unnamed #{object.is_a?(Class) ? 'Class' : 'Module'}"
35
+ end
36
+ else
37
+ description = "#{module_name(object.class)} instance"
38
+ end
39
+
40
+ if num_brackets == 0
41
+ description
42
+ else
43
+ "#{'['*num_brackets}#{description}#{']'*num_brackets}"
44
+ end
45
+ end
46
+
47
+ def internal_superclass(klass)
48
+ raise NotImplementedError, "abstract"
49
+ end
50
+
51
+ def internal_class(object)
52
+ raise NotImplementedError, "abstract"
53
+ end
54
+
55
+ def internal_class_to_module(internal_class)
56
+ raise NotImplementedError, "abstract"
57
+ end
58
+
59
+ def internal_public_instance_methods(mod)
60
+ raise NotImplementedError, "abstract"
61
+ end
62
+
63
+ def internal_protected_instance_methods(mod)
64
+ raise NotImplementedError, "abstract"
65
+ end
66
+
67
+ def internal_private_instance_methods(mod)
68
+ raise NotImplementedError, "abstract"
69
+ end
70
+
71
+ def internal_undefined_instance_methods(mod)
72
+ raise NotImplementedError, "abstract"
73
+ end
74
+
75
+ def singleton_class?(object)
76
+ raise NotImplementedError, "abstract"
77
+ end
78
+
79
+ def singleton_instance(singleton_class)
80
+ raise NotImplementedError, "abstract"
81
+ end
82
+
83
+ def module_name(mod)
84
+ raise NotImplementedError, "abstract"
85
+ end
86
+
87
+ if RUBY_VERSION >= '1.9.0' || Looksee.ruby_engine == 'rbx'
88
+ def source_location(method)
89
+ method.is_a?(UnboundMethod) or
90
+ raise TypeError, "expected UnboundMethod, got #{method.class}"
91
+ method.source_location
92
+ end
93
+ else
94
+ def source_location(method)
95
+ raise NotImplementedError, 'abstract'
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,73 @@
1
+ require 'looksee/adapter/base'
2
+ require 'looksee/rbx'
3
+
4
+ module Looksee
5
+ module Adapter
6
+ class Rubinius < Base
7
+ def internal_superclass(klass)
8
+ klass.direct_superclass
9
+ end
10
+
11
+ def internal_class_to_module(internal_class)
12
+ if internal_class.is_a?(::Rubinius::IncludedModule)
13
+ internal_class.module
14
+ else
15
+ internal_class
16
+ end
17
+ end
18
+
19
+ def internal_public_instance_methods(mod)
20
+ mod.method_table.public_names
21
+ end
22
+
23
+ def internal_protected_instance_methods(mod)
24
+ mod.method_table.protected_names
25
+ end
26
+
27
+ def internal_private_instance_methods(mod)
28
+ mod.method_table.private_names
29
+ end
30
+
31
+ def internal_undefined_instance_methods(mod)
32
+ names = []
33
+ mod.method_table.each_entry do |entry|
34
+ names << entry.name if entry.visibility.equal?(:undef)
35
+ end
36
+ names
37
+ end
38
+
39
+ def singleton_class?(object)
40
+ object.is_a?(Class) && object.__metaclass_object__
41
+ end
42
+
43
+ def singleton_instance(singleton_class)
44
+ singleton_class?(singleton_class) or
45
+ raise TypeError, "expected singleton class, got #{singleton_class.class}"
46
+ singleton_class.__metaclass_object__
47
+ end
48
+
49
+ def module_name(mod)
50
+ mod.is_a?(Module) or
51
+ raise TypeError, "expected module, got #{mod.class}"
52
+ mod.__name__
53
+ end
54
+
55
+ def source_location(method)
56
+ method.is_a?(UnboundMethod) or
57
+ raise TypeError, "expected UnboundMethod, got #{method.class}"
58
+ source_location = method.source_location and
59
+ return source_location
60
+
61
+ # #source_location doesn't always work. If it returns nil, try
62
+ # a little harder.
63
+ case (executable = method.executable)
64
+ when ::Rubinius::BlockEnvironment::AsMethod
65
+ method = executable.instance_variable_get(:@block_env).method
66
+ [method.file.to_s, method.lines[1]]
67
+ when ::Rubinius::DelegatedMethod
68
+ executable.instance_variable_get(:@receiver).source_location
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,122 @@
1
+ require "rbconfig"
2
+
3
+ require 'set'
4
+
5
+ module Looksee
6
+ autoload :VERSION, 'looksee/version'
7
+ autoload :Adapter, 'looksee/adapter'
8
+ autoload :Columnizer, 'looksee/columnizer'
9
+ autoload :Editor, 'looksee/editor'
10
+ autoload :Help, 'looksee/help'
11
+ autoload :Inspector, 'looksee/inspector'
12
+ autoload :LookupPath, 'looksee/lookup_path'
13
+ autoload :WirbleCompatibility, 'looksee/wirble_compatibility'
14
+
15
+ class << self
16
+ #
17
+ # The default options passed to #ls.
18
+ #
19
+ # Default: <tt>[:public, :protected, :private, :undefined,
20
+ # :overridden]</tt>
21
+ #
22
+ attr_accessor :default_specifiers
23
+
24
+ #
25
+ # The width to use for displaying output, when not available in
26
+ # the COLUMNS environment variable.
27
+ #
28
+ # Default: 80
29
+ #
30
+ attr_accessor :default_width
31
+
32
+ #
33
+ # The default styles to use for the +inspect+ strings.
34
+ #
35
+ # This is a hash with keys:
36
+ #
37
+ # * :module
38
+ # * :public
39
+ # * :protected
40
+ # * :private
41
+ # * :undefined
42
+ # * :overridden
43
+ #
44
+ # The values are format strings. They should all contain a single
45
+ # "%s", which is where the name is inserted.
46
+ #
47
+ # Default:
48
+ #
49
+ # {
50
+ # :module => "\e[1;37m%s\e[0m", # white
51
+ # :public => "\e[1;32m%s\e[0m", # green
52
+ # :protected => "\e[1;33m%s\e[0m", # yellow
53
+ # :private => "\e[1;31m%s\e[0m", # red
54
+ # :undefined => "\e[1;34m%s\e[0m", # blue
55
+ # :overridden => "\e[1;30m%s\e[0m", # black
56
+ # }
57
+ #
58
+ attr_accessor :styles
59
+
60
+ #
61
+ # The editor command, used for Object#edit.
62
+ #
63
+ # This string should contain a "%f", which is replaced with the
64
+ # file name, and/or "%l" which is replaced with the line number. A
65
+ # "%%" is replaced with "%".
66
+ #
67
+ # If the LOOKSEE_EDITOR environment variable is set, it is used as
68
+ # the default. Otherwise, we use the following heuristic:
69
+ #
70
+ # If EDITOR is set, we use that. If it looks like vi, emacs, or
71
+ # textmate, we also append options to position the cursor on the
72
+ # appropriate line. If EDITOR is not set, we use "vi +%l %f".
73
+ #
74
+ attr_accessor :editor
75
+
76
+ #
77
+ # The interpreter adapter.
78
+ #
79
+ # Encapsulates the interpreter-specific functionality.
80
+ #
81
+ attr_accessor :adapter
82
+
83
+ #
84
+ # Wrapper around RUBY_ENGINE that's always defined.
85
+ #
86
+ attr_accessor :ruby_engine
87
+
88
+ #
89
+ # Show a quick reference.
90
+ #
91
+ def help
92
+ Help.new
93
+ end
94
+ end
95
+
96
+ self.default_specifiers = [:public, :protected, :private, :undefined, :overridden]
97
+ self.default_width = 80
98
+ self.styles = {
99
+ :module => "\e[1;37m%s\e[0m", # white
100
+ :public => "\e[1;32m%s\e[0m", # green
101
+ :protected => "\e[1;33m%s\e[0m", # yellow
102
+ :private => "\e[1;31m%s\e[0m", # red
103
+ :undefined => "\e[1;34m%s\e[0m", # blue
104
+ :overridden => "\e[1;30m%s\e[0m", # black
105
+ }
106
+ self.editor = ENV['LOOKSEE_EDITOR'] || ENV['EDITOR'] || 'vi'
107
+
108
+ if Object.const_defined?(:RUBY_ENGINE)
109
+ self.ruby_engine = RUBY_ENGINE
110
+ else
111
+ self.ruby_engine = 'ruby'
112
+ end
113
+
114
+ case ruby_engine
115
+ when 'jruby'
116
+ self.adapter = Adapter::JRuby.new
117
+ when 'rbx'
118
+ self.adapter = Adapter::Rubinius.new
119
+ else
120
+ self.adapter = Adapter::MRI.new
121
+ end
122
+ end
@@ -0,0 +1,73 @@
1
+ module Looksee
2
+ module Columnizer
3
+ class << self
4
+ #
5
+ # Arrange the given strings in columns, restricted to the given
6
+ # width. Smart enough to ignore content in terminal control
7
+ # sequences.
8
+ #
9
+ def columnize(strings, width)
10
+ return '' if strings.empty?
11
+
12
+ num_columns = 1
13
+ layout = [strings]
14
+ loop do
15
+ break if layout.first.length <= 1
16
+ next_layout = layout_in_columns(strings, num_columns + 1)
17
+ break if layout_width(next_layout) > width
18
+ layout = next_layout
19
+ num_columns += 1
20
+ end
21
+
22
+ pad_strings(layout)
23
+ rectangularize_layout(layout)
24
+ layout.transpose.map do |row|
25
+ ' ' + row.compact.join(' ')
26
+ end.join("\n") << "\n"
27
+ end
28
+
29
+ private # -----------------------------------------------------
30
+
31
+ def layout_in_columns(strings, num_columns)
32
+ strings_per_column = (strings.length / num_columns.to_f).ceil
33
+ (0...num_columns).map{|i| strings[i*strings_per_column...(i+1)*strings_per_column] || []}
34
+ end
35
+
36
+ def layout_width(layout)
37
+ widths = layout_column_widths(layout)
38
+ widths.inject(0){|sum, w| sum + w} + 2*layout.length
39
+ end
40
+
41
+ def layout_column_widths(layout)
42
+ layout.map do |column|
43
+ column.map{|string| display_width(string)}.max || 0
44
+ end
45
+ end
46
+
47
+ def display_width(string)
48
+ # remove terminal control sequences
49
+ string.gsub(/\e\[.*?m/, '').length
50
+ end
51
+
52
+ def pad_strings(layout)
53
+ widths = layout_column_widths(layout)
54
+ layout.each_with_index do |column, i|
55
+ column_width = widths[i]
56
+ column.each do |string|
57
+ padding = column_width - display_width(string)
58
+ string << ' '*padding
59
+ end
60
+ end
61
+ end
62
+
63
+ def rectangularize_layout(layout)
64
+ return if layout.length == 1
65
+ height = layout[0].length
66
+ layout[1..-1].each do |column|
67
+ column.length == height or
68
+ column[height - 1] = nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,59 @@
1
+ module Looksee
2
+ module ObjectMixin
3
+ #
4
+ # Return a Looksee::Inspector for this object.
5
+ #
6
+ # +args+ is an optional list of specifiers.
7
+ #
8
+ # * +:public+ - include public methods
9
+ # * +:protected+ - include public methods
10
+ # * +:private+ - include public methods
11
+ # * +:undefined+ - include public methods (see Module#undef_method)
12
+ # * +:overridden+ - include public methods
13
+ # * +:nopublic+ - include public methods
14
+ # * +:noprotected+ - include public methods
15
+ # * +:noprivate+ - include public methods
16
+ # * +:noundefined+ - include public methods (see Module#undef_method)
17
+ # * +:nooverridden+ - include public methods
18
+ # * a string - only include methods containing this string (may
19
+ # be used multiple times)
20
+ # * a regexp - only include methods matching this regexp (may
21
+ # be used multiple times)
22
+ #
23
+ # The default (if options is nil or omitted) is given by
24
+ # #default_lookup_path_options.
25
+ #
26
+ def ls(*args)
27
+ options = {:visibilities => Set[], :filters => Set[]}
28
+ (Looksee.default_specifiers + args).each do |arg|
29
+ case arg
30
+ when String, Regexp
31
+ options[:filters] << arg
32
+ when :public, :protected, :private, :undefined, :overridden
33
+ options[:visibilities].add(arg)
34
+ when :nopublic, :noprotected, :noprivate, :noundefined, :nooverridden
35
+ visibility = arg.to_s.sub(/\Ano/, '').to_sym
36
+ options[:visibilities].delete(visibility)
37
+ else
38
+ raise ArgumentError, "invalid specifier: #{arg.inspect}"
39
+ end
40
+ end
41
+ lookup_path = LookupPath.new(self)
42
+ Inspector.new(lookup_path, options)
43
+ end
44
+
45
+ #
46
+ # Open an editor at the named method's definition.
47
+ #
48
+ # Uses Looksee.editor to determine the editor command to run.
49
+ #
50
+ # Only works for methods for which file and line numbers are
51
+ # accessible.
52
+ #
53
+ def edit(name)
54
+ Editor.new(Looksee.editor).edit(self, name)
55
+ end
56
+ end
57
+
58
+ Object.send :include, ObjectMixin
59
+ end
@@ -0,0 +1,58 @@
1
+ require 'shellwords'
2
+
3
+ module Looksee
4
+ class Editor
5
+ def initialize(command)
6
+ @command = command.dup
7
+ infer_arguments
8
+ end
9
+
10
+ attr_reader :command
11
+
12
+ #
13
+ # Run the editor command for the +method_name+ of +object+.
14
+ #
15
+ def edit(object, method_name)
16
+ method = LookupPath.new(object).find(method_name.to_s) or
17
+ return
18
+ file, line = Looksee.adapter.source_location(method)
19
+ run(file, line) unless line.nil?
20
+ end
21
+
22
+ #
23
+ # Run the editor command for the given file and line.
24
+ #
25
+ def run(file, line)
26
+ system *command_for(file, line)
27
+ end
28
+
29
+ #
30
+ # Return the editor command for the given file and line.
31
+ #
32
+ # This is an array of the command with its arguments.
33
+ #
34
+ def command_for(file, line)
35
+ line = line.to_s
36
+ words = Shellwords.shellwords(command)
37
+ words.map! do |word|
38
+ word.gsub!(/%f/, file)
39
+ word.gsub!(/%l/, line)
40
+ word.gsub!(/%%/, '%')
41
+ word
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def infer_arguments
48
+ return if command =~ /%[fl]/
49
+
50
+ case command[/\S+/]
51
+ when /\A(?:g?vim?|.*macs|pico|nano)\z/
52
+ command << " +%l %f"
53
+ when 'mate'
54
+ command << " -l%l %f"
55
+ end
56
+ end
57
+ end
58
+ end