looksee 1.1.0 → 2.0.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,142 @@
1
+ /**********************************************************************
2
+
3
+ method.h -
4
+
5
+ $Author: tmm1 $
6
+ created at: Wed Jul 15 20:02:33 2009
7
+
8
+ Copyright (C) 2009 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+ #ifndef METHOD_H
12
+ #define METHOD_H
13
+
14
+ #include "internal.h"
15
+
16
+ #ifndef END_OF_ENUMERATION
17
+ # if defined(__GNUC__) &&! defined(__STRICT_ANSI__)
18
+ # define END_OF_ENUMERATION(key)
19
+ # else
20
+ # define END_OF_ENUMERATION(key) END_OF_##key##_PLACEHOLDER = 0
21
+ # endif
22
+ #endif
23
+
24
+ typedef enum {
25
+ NOEX_PUBLIC = 0x00,
26
+ NOEX_NOSUPER = 0x01,
27
+ NOEX_PRIVATE = 0x02,
28
+ NOEX_PROTECTED = 0x04,
29
+ NOEX_MASK = 0x06,
30
+ NOEX_BASIC = 0x08,
31
+ NOEX_UNDEF = NOEX_NOSUPER,
32
+ NOEX_MODFUNC = 0x12,
33
+ NOEX_SUPER = 0x20,
34
+ NOEX_VCALL = 0x40,
35
+ NOEX_RESPONDS = 0x80,
36
+
37
+ NOEX_BIT_WIDTH = 8,
38
+ NOEX_SAFE_SHIFT_OFFSET = ((NOEX_BIT_WIDTH+3)/4)*4 /* round up to nibble */
39
+ } rb_method_flag_t;
40
+
41
+ #define NOEX_SAFE(n) ((int)((n) >> NOEX_SAFE_SHIFT_OFFSET) & 0x0F)
42
+ #define NOEX_WITH(n, s) (((s) << NOEX_SAFE_SHIFT_OFFSET) | (n) | (ruby_running ? 0 : NOEX_BASIC))
43
+ #define NOEX_WITH_SAFE(n) NOEX_WITH((n), rb_safe_level())
44
+
45
+ /* method data type */
46
+
47
+ typedef enum {
48
+ VM_METHOD_TYPE_ISEQ,
49
+ VM_METHOD_TYPE_CFUNC,
50
+ VM_METHOD_TYPE_ATTRSET,
51
+ VM_METHOD_TYPE_IVAR,
52
+ VM_METHOD_TYPE_BMETHOD,
53
+ VM_METHOD_TYPE_ZSUPER,
54
+ VM_METHOD_TYPE_UNDEF,
55
+ VM_METHOD_TYPE_NOTIMPLEMENTED,
56
+ VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
57
+ VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */
58
+ VM_METHOD_TYPE_REFINED,
59
+
60
+ END_OF_ENUMERATION(VM_METHOD_TYPE)
61
+ } rb_method_type_t;
62
+
63
+ struct rb_call_info_struct;
64
+
65
+ typedef struct rb_method_cfunc_struct {
66
+ VALUE (*func)(ANYARGS);
67
+ VALUE (*invoker)(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv);
68
+ int argc;
69
+ } rb_method_cfunc_t;
70
+
71
+ typedef struct rb_method_attr_struct {
72
+ ID id;
73
+ const VALUE location;
74
+ } rb_method_attr_t;
75
+
76
+ typedef struct rb_iseq_struct rb_iseq_t;
77
+
78
+ typedef struct rb_method_definition_struct {
79
+ rb_method_type_t type; /* method type */
80
+ ID original_id;
81
+ union {
82
+ rb_iseq_t * const iseq; /* should be mark */
83
+ rb_method_cfunc_t cfunc;
84
+ rb_method_attr_t attr;
85
+ const VALUE proc; /* should be mark */
86
+ enum method_optimized_type {
87
+ OPTIMIZED_METHOD_TYPE_SEND,
88
+ OPTIMIZED_METHOD_TYPE_CALL,
89
+
90
+ OPTIMIZED_METHOD_TYPE__MAX
91
+ } optimize_type;
92
+ struct rb_method_entry_struct *orig_me;
93
+ } body;
94
+ int alias_count;
95
+ } rb_method_definition_t;
96
+
97
+ typedef struct rb_method_entry_struct {
98
+ rb_method_flag_t flag;
99
+ char mark;
100
+ rb_method_definition_t *def;
101
+ ID called_id;
102
+ VALUE klass; /* should be mark */
103
+ } rb_method_entry_t;
104
+
105
+ struct unlinked_method_entry_list_entry {
106
+ struct unlinked_method_entry_list_entry *next;
107
+ rb_method_entry_t *me;
108
+ };
109
+
110
+ #define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF)
111
+
112
+ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
113
+ rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
114
+ rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr);
115
+ rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
116
+ void rb_add_refined_method_entry(VALUE refined_class, ID mid);
117
+ rb_method_entry_t *rb_resolve_refined_method(VALUE refinements,
118
+ const rb_method_entry_t *me,
119
+ VALUE *defined_class_ptr);
120
+ rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id,
121
+ VALUE *defined_class_ptr);
122
+ rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id,
123
+ VALUE *defined_class_ptr);
124
+
125
+ rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr);
126
+ rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
127
+
128
+ int rb_method_entry_arity(const rb_method_entry_t *me);
129
+ int rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2);
130
+ st_index_t rb_hash_method_entry(st_index_t hash, const rb_method_entry_t *me);
131
+
132
+ VALUE rb_method_entry_location(rb_method_entry_t *me);
133
+ VALUE rb_mod_method_location(VALUE mod, ID id);
134
+ VALUE rb_obj_method_location(VALUE obj, ID id);
135
+
136
+ void rb_mark_method_entry(const rb_method_entry_t *me);
137
+ void rb_free_method_entry(rb_method_entry_t *me);
138
+ void rb_sweep_method_entry(void *vm);
139
+ void rb_free_m_tbl(st_table *tbl);
140
+ void rb_free_m_tbl_wrapper(struct method_table_wrapper *wrapper);
141
+
142
+ #endif /* METHOD_H */
Binary file
data/lib/looksee/clean.rb CHANGED
@@ -4,6 +4,10 @@ require 'set'
4
4
  module Looksee
5
5
  Config = Object.const_defined?(:RbConfig) ? ::RbConfig : ::Config
6
6
 
7
+ NoMethodError = Class.new(RuntimeError)
8
+ NoSourceLocationError = Class.new(RuntimeError)
9
+ NoSourceFileError = Class.new(RuntimeError)
10
+
7
11
  autoload :VERSION, 'looksee/version'
8
12
  autoload :Adapter, 'looksee/adapter'
9
13
  autoload :Columnizer, 'looksee/columnizer'
@@ -42,38 +42,31 @@ module Looksee
42
42
  Inspector.new(lookup_path, options)
43
43
  end
44
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
-
57
- def self.rename(renamings) # :nodoc:
58
- renamings.each do |old_name, new_name|
59
- alias_method new_name, old_name
60
- remove_method old_name
61
- end
45
+ def self.rename(name) # :nodoc:
46
+ name = name[:ls] if name.is_a?(Hash)
47
+ alias_method name, :ls
48
+ remove_method :ls
62
49
  end
63
50
  end
64
51
 
65
52
  #
66
- # Rename the methods added to every object. Example:
53
+ # Rename the #ls method, added to every object. Example:
54
+ #
55
+ # rename :_ls
56
+ #
57
+ # This renames Looksee's #ls method to #_ls.
67
58
  #
68
- # rename :ls => :_ls, :edit => :_edit
59
+ # For backward compatibility, the old-style invocation is also
60
+ # supported. Please note this is deprecated.
69
61
  #
70
- def self.rename(renamings)
71
- ObjectMixin.rename(renamings)
62
+ # rename :ls => :_ls
63
+ #
64
+ def self.rename(name)
65
+ ObjectMixin.rename(name)
72
66
  end
73
67
 
74
- (ENV['LOOKSEE_METHODS'] || '').scan(/([\w_]+)=([\w_]+)/) do
75
- rename $1.to_sym => $2.to_sym
76
- end
68
+ name = ENV['LOOKSEE_METHOD'] and
69
+ rename name
77
70
 
78
71
  Object.send :include, ObjectMixin
79
72
  end
@@ -14,9 +14,15 @@ module Looksee
14
14
  #
15
15
  def edit(object, method_name)
16
16
  method = LookupPath.new(object).find(method_name.to_s) or
17
- return
17
+ raise NoMethodError, "no method `#{method_name}' in lookup path of #{object.class} instance"
18
18
  file, line = Looksee.adapter.source_location(method)
19
- run(file, line) unless line.nil?
19
+ if !file
20
+ raise NoSourceLocationError, "no source location for #{method.owner}##{method.name}"
21
+ elsif !File.exist?(file)
22
+ raise NoSourceFileError, "cannot find source file: #{file}"
23
+ else
24
+ run(file, line)
25
+ end
20
26
  end
21
27
 
22
28
  #
data/lib/looksee/help.rb CHANGED
@@ -40,7 +40,7 @@ module Looksee
40
40
  | ...
41
41
  | }
42
42
  |
43
- | object.edit(method)
43
+ | object.ls.edit(method)
44
44
  |
45
45
  | Jump to the source of the given method. Set your editor
46
46
  | with Looksee.editor or the LOOKSEE_EDITOR environment
@@ -11,12 +11,27 @@ module Looksee
11
11
  attr_reader :visibilities
12
12
  attr_reader :filters
13
13
 
14
+ #
15
+ # Print the method lookup path of self. See the README for details.
16
+ #
14
17
  def inspect
15
18
  lookup_path.entries.reverse.map do |entry|
16
19
  inspect_entry(entry)
17
20
  end.join("\n")
18
21
  end
19
22
 
23
+ #
24
+ # Open an editor at the named method's definition.
25
+ #
26
+ # Uses Looksee.editor to determine the editor command to run.
27
+ #
28
+ # Only works for methods for which file and line numbers are
29
+ # accessible.
30
+ #
31
+ def edit(name)
32
+ Editor.new(Looksee.editor).edit(lookup_path.object, name)
33
+ end
34
+
20
35
  private
21
36
 
22
37
  def inspect_entry(entry)
@@ -1,5 +1,5 @@
1
1
  module Looksee
2
- VERSION = [1, 1, 0]
2
+ VERSION = [2, 0, 0]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
data/spec/adapter_spec.rb CHANGED
@@ -95,33 +95,33 @@ describe "Looksee.adapter" do
95
95
  def self.it_should_list_methods_with_visibility(visibility)
96
96
  it "should return the list of #{visibility} instance methods defined directly on a class" do
97
97
  temporary_class :C
98
- replace_methods C, visibility => [:one, :two]
98
+ add_methods C, visibility => [:one, :two]
99
99
  @adapter.send(target_method, C).to_set.should == Set[:one, :two]
100
100
  end
101
101
 
102
102
  it "should return the list of #{visibility} instance methods defined directly on a module" do
103
103
  temporary_module :M
104
- replace_methods M, visibility => [:one, :two]
104
+ add_methods M, visibility => [:one, :two]
105
105
  @adapter.send(target_method, M).to_set.should == Set[:one, :two]
106
106
  end
107
107
 
108
108
  it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
109
109
  temporary_class :C
110
110
  c = C.new
111
- replace_methods c.singleton_class, visibility => [:one, :two]
111
+ add_methods c.singleton_class, visibility => [:one, :two]
112
112
  @adapter.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
113
113
  end
114
114
 
115
115
  it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
116
116
  temporary_class :C
117
- replace_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
117
+ add_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
118
118
  @adapter.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
119
119
  end
120
120
 
121
121
  # Worth checking as ruby keeps undef'd methods in method tables.
122
122
  it "should not return undefined methods" do
123
123
  temporary_class :C
124
- replace_methods C, visibility => [:removed]
124
+ add_methods C, visibility => [:removed]
125
125
  C.send(:undef_method, :removed)
126
126
  @adapter.send(target_method, C).to_set.should == Set[]
127
127
  end
@@ -130,7 +130,7 @@ describe "Looksee.adapter" do
130
130
  def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
131
131
  it "should not return any #{visibility1} or #{visibility2} instance methods" do
132
132
  temporary_class :C
133
- replace_methods C, {visibility1 => [:a], visibility2 => [:b]}
133
+ add_methods C, {visibility1 => [:a], visibility2 => [:b]}
134
134
  @adapter.send(target_method, C).to_set.should == Set[]
135
135
  end
136
136
  end
data/spec/editor_spec.rb CHANGED
@@ -1,128 +1,110 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Looksee::Editor do
4
- def editor_command(command)
5
- Looksee::Editor.new(command).command_for('FILE', 'LINE')
6
- end
4
+ describe "#command_for" do
5
+ def editor_command(command)
6
+ Looksee::Editor.new(command).command_for('FILE', 'LINE')
7
+ end
7
8
 
8
- it "should infer the file and line arguments for 'vi'" do
9
- editor_command('vi').should == ['vi', '+LINE', 'FILE']
10
- end
9
+ it "should infer the file and line arguments for 'vi'" do
10
+ editor_command('vi').should == ['vi', '+LINE', 'FILE']
11
+ end
11
12
 
12
- it "should infer the file and line arguments for 'vim'" do
13
- editor_command('vim').should == ['vim', '+LINE', 'FILE']
14
- end
13
+ it "should infer the file and line arguments for 'vim'" do
14
+ editor_command('vim').should == ['vim', '+LINE', 'FILE']
15
+ end
15
16
 
16
- it "should infer the file and line arguments for 'gvim'" do
17
- editor_command('gvim').should == ['gvim', '+LINE', 'FILE']
18
- end
17
+ it "should infer the file and line arguments for 'gvim'" do
18
+ editor_command('gvim').should == ['gvim', '+LINE', 'FILE']
19
+ end
19
20
 
20
- it "should infer the file and line arguments for 'emacs'" do
21
- editor_command('emacs').should == ['emacs', '+LINE', 'FILE']
22
- end
21
+ it "should infer the file and line arguments for 'emacs'" do
22
+ editor_command('emacs').should == ['emacs', '+LINE', 'FILE']
23
+ end
23
24
 
24
- it "should infer the file and line arguments for 'xemacs'" do
25
- editor_command('xemacs').should == ['xemacs', '+LINE', 'FILE']
26
- end
25
+ it "should infer the file and line arguments for 'xemacs'" do
26
+ editor_command('xemacs').should == ['xemacs', '+LINE', 'FILE']
27
+ end
27
28
 
28
- it "should infer the file and line arguments for 'aquamacs'" do
29
- editor_command('aquamacs').should == ['aquamacs', '+LINE', 'FILE']
30
- end
29
+ it "should infer the file and line arguments for 'aquamacs'" do
30
+ editor_command('aquamacs').should == ['aquamacs', '+LINE', 'FILE']
31
+ end
31
32
 
32
- it "should infer the file and line arguments for 'pico'" do
33
- editor_command('pico').should == ['pico', '+LINE', 'FILE']
34
- end
33
+ it "should infer the file and line arguments for 'pico'" do
34
+ editor_command('pico').should == ['pico', '+LINE', 'FILE']
35
+ end
35
36
 
36
- it "should infer the file and line arguments for 'nano'" do
37
- editor_command('nano').should == ['nano', '+LINE', 'FILE']
38
- end
37
+ it "should infer the file and line arguments for 'nano'" do
38
+ editor_command('nano').should == ['nano', '+LINE', 'FILE']
39
+ end
39
40
 
40
- it "should infer the file and line arguments for 'mate'" do
41
- editor_command('mate').should == ['mate', '-lLINE', 'FILE']
42
- end
41
+ it "should infer the file and line arguments for 'mate'" do
42
+ editor_command('mate').should == ['mate', '-lLINE', 'FILE']
43
+ end
43
44
 
44
- it "should support escaped '%'-signs" do
45
- editor_command('%% %f %l').should == ['%', 'FILE', 'LINE']
46
- end
45
+ it "should support escaped '%'-signs" do
46
+ editor_command('%% %f %l').should == ['%', 'FILE', 'LINE']
47
+ end
47
48
 
48
- it "should not infer file and line arguments for unknown editors" do
49
- editor_command('wtfbbq').should == ['wtfbbq']
49
+ it "should not infer file and line arguments for unknown editors" do
50
+ editor_command('wtfbbq').should == ['wtfbbq']
51
+ end
50
52
  end
51
53
 
52
54
  describe "#edit" do
53
- TMP = "#{ROOT}/spec/tmp"
55
+ let(:tmp) { "#{ROOT}/spec/tmp" }
56
+ let(:editor_path) { "#{tmp}/edit" }
57
+ let(:editor_output) { "#{tmp}/edit.out" }
58
+ let(:editor) { Looksee::Editor.new("#{editor_path} %f %l") }
59
+ let(:editor_invocation) { File.exist?(editor_output) ? File.read(editor_output) : nil }
60
+ let(:source_location) { ["#{tmp}/c.rb", 2] }
61
+ let(:object) { C.new }
54
62
 
55
63
  before do
56
- FileUtils.mkdir_p TMP
57
- make_editor "#{TMP}/edit"
58
- @editor = Looksee::Editor.new("#{TMP}/edit %f %l")
64
+ FileUtils.mkdir_p tmp
65
+ set_up_editor
66
+
67
+ file, line = *source_location
68
+ FileUtils.touch file
69
+ Object.const_set(:C, Class.new { def f; end })
70
+ Looksee.adapter.set_methods(C, [:f], [], [], [])
71
+ Looksee.adapter.set_source_location(C, :f, source_location)
72
+ Looksee.adapter.ancestors[object] = [C, Object]
59
73
  end
60
74
 
61
75
  after do
62
- FileUtils.rm_rf TMP
76
+ Object.send(:remove_const, :C)
77
+ FileUtils.rm_rf tmp
63
78
  end
64
79
 
65
- def make_editor(path)
66
- open(path, 'w') { |f| f.puts <<-EOS.demargin }
80
+ def set_up_editor
81
+ open(editor_path, 'w') { |f| f.puts <<-EOS.demargin }
67
82
  |#!/bin/sh
68
- |echo $# $1 $2 > "#{TMP}/editor.out"
83
+ |echo $# $1 $2 > "#{editor_output}"
69
84
  EOS
70
- File.chmod 0755, path
85
+ File.chmod 0755, editor_path
71
86
  end
72
87
 
73
- def with_source_file
74
- path = "#{TMP}/c.rb"
75
- open(path, 'w') { |f| f.puts <<-EOS.demargin }
76
- |class C
77
- | def f
78
- | end
79
- |end
80
- EOS
81
- begin
82
- load path
83
- Looksee.adapter.set_methods(C, [:f], [], [], [])
84
- yield path
85
- ensure
86
- Object.send(:remove_const, :C)
87
- end
88
+ it "should run the editor on the method's source location if available" do
89
+ editor.edit(object, :f)
90
+ editor_invocation.should == "2 #{source_location.join(' ')}\n"
88
91
  end
89
92
 
90
- it "should run the editor on the method's source location if available" do
91
- with_source_file do |path|
92
- c = C.new
93
- Looksee.adapter.ancestors[c] = [C, Object]
94
- Looksee.adapter.set_source_location(C, :f, [path, 2])
95
- @editor.edit(c, :f)
96
- File.read("#{TMP}/editor.out").should == "2 #{path} 2\n"
97
- end
98
- end
99
-
100
- it "should not run the editor if the source file does not exist" do
101
- with_source_file do |path|
102
- FileUtils.rm_f path
103
- @editor.edit(C.new, :f)
104
- File.should_not exist("#{TMP}/editor.out")
105
- end
106
- end
107
-
108
- it "should not run the editor for methods defined with eval where no source file specified" do
109
- eval <<-EOS.demargin
110
- |class ::C
111
- | def f
112
- | end
113
- |end
114
- EOS
115
- begin
116
- @editor.edit(C.new, :f)
117
- File.should_not exist("#{TMP}/editor.out")
118
- ensure
119
- Object.send(:remove_const, :C)
120
- end
93
+ it "should raise NoMethodError and not run the editor if the method does not exist" do
94
+ expect { editor.edit(object, :x) }.to raise_error(Looksee::NoMethodError)
95
+ editor_invocation.should be_nil
96
+ end
97
+
98
+ it "should raise NoSourceFileError and not run the editor if the source file does not exist" do
99
+ FileUtils.rm_f source_location.first
100
+ expect { editor.edit(object, :f) }.to raise_error(Looksee::NoSourceFileError)
101
+ editor_invocation.should be_nil
121
102
  end
122
103
 
123
- it "should not run the editor for primitives" do
124
- @editor.edit('', :size)
125
- File.should_not exist("#{TMP}/editor.out")
104
+ it "should raise NoSourceLocationError and not run the editor if no source location is available" do
105
+ Looksee.adapter.set_source_location(C, :f, nil)
106
+ expect { editor.edit(object, :f) }.to raise_error(Looksee::NoSourceLocationError)
107
+ editor_invocation.should be_nil
126
108
  end
127
109
  end
128
110
  end