summaryrefslogtreecommitdiff
path: root/trunk/lib/rdoc/ri/cache.rb
blob: 2e267d95fb665dd423f5f94fe58de806c6d68be0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
require 'rdoc/ri'

class RDoc::RI::ClassEntry

  attr_reader :name
  attr_reader :path_names

  def initialize(path_name, name, in_class)
    @path_names = [ path_name ]
    @name = name
    @in_class = in_class
    @class_methods    = []
    @instance_methods = []
    @inferior_classes = []
  end

  # We found this class in more tha one place, so add
  # in the name from there.
  def add_path(path)
    @path_names << path
  end

  # read in our methods and any classes
  # and modules in our namespace. Methods are
  # stored in files called name-c|i.yaml,
  # where the 'name' portion is the external
  # form of the method name and the c|i is a class|instance
  # flag

  def load_from(dir)
    Dir.foreach(dir) do |name|
      next if name =~ /^\./

      # convert from external to internal form, and
      # extract the instance/class flag

      if name =~ /^(.*?)-(c|i).yaml$/
        external_name = $1
        is_class_method = $2 == "c"
        internal_name = RiWriter.external_to_internal(external_name)
        list = is_class_method ? @class_methods : @instance_methods
        path = File.join(dir, name)
        list << MethodEntry.new(path, internal_name, is_class_method, self)
      else
        full_name = File.join(dir, name)
        if File.directory?(full_name)
          inf_class = @inferior_classes.find {|c| c.name == name }
          if inf_class
            inf_class.add_path(full_name)
          else
            inf_class = ClassEntry.new(full_name, name, self)
            @inferior_classes << inf_class
          end
          inf_class.load_from(full_name)
        end
      end
    end
  end

  # Return a list of any classes or modules that we contain
  # that match a given string

  def contained_modules_matching(name)
    @inferior_classes.find_all {|c| c.name[name]}
  end

  def classes_and_modules
    @inferior_classes
  end

  # Return an exact match to a particular name
  def contained_class_named(name)
    @inferior_classes.find {|c| c.name == name}
  end

  # return the list of local methods matching name
  # We're split into two because we need distinct behavior
  # when called from the _toplevel_
  def methods_matching(name, is_class_method)
    local_methods_matching(name, is_class_method)
  end

  # Find methods matching 'name' in ourselves and in
  # any classes we contain
  def recursively_find_methods_matching(name, is_class_method)
    res = local_methods_matching(name, is_class_method)
    @inferior_classes.each do |c|
      res.concat(c.recursively_find_methods_matching(name, is_class_method))
    end
    res
  end


  # Return our full name
  def full_name
    res = @in_class.full_name
    res << "::" unless res.empty?
    res << @name
  end

  # Return a list of all out method names
  def all_method_names
    res = @class_methods.map {|m| m.full_name }
    @instance_methods.each {|m| res << m.full_name}
    res
  end

  private

  # Return a list of all our methods matching a given string.
  # Is +is_class_methods+ if 'nil', we don't care if the method
  # is a class method or not, otherwise we only return
  # those methods that match
  def local_methods_matching(name, is_class_method)

    list = case is_class_method
           when nil then  @class_methods + @instance_methods
           when true then @class_methods
           when false then @instance_methods
           else fail "Unknown is_class_method: #{is_class_method.inspect}"
           end

    list.find_all {|m| m.name;  m.name[name]}
  end
end

##
# A TopLevelEntry is like a class entry, but when asked to search for methods
# searches all classes, not just itself

class RDoc::RI::TopLevelEntry < RDoc::RI::ClassEntry
  def methods_matching(name, is_class_method)
    res = recursively_find_methods_matching(name, is_class_method)
  end

  def full_name
      ""
  end

  def module_named(name)

  end

end

class RDoc::RI::MethodEntry
  attr_reader :name
  attr_reader :path_name

  def initialize(path_name, name, is_class_method, in_class)
    @path_name = path_name
    @name = name
    @is_class_method = is_class_method
    @in_class = in_class
  end

  def full_name
    res = @in_class.full_name
    unless res.empty?
      if @is_class_method
        res << "::"
      else
        res << "#"
      end
    end
    res << @name
  end
end

##
# We represent everything know about all 'ri' files accessible to this program

class RDoc::RI::Cache

  attr_reader :toplevel

  def initialize(dirs)
    # At the top level we have a dummy module holding the
    # overall namespace
    @toplevel = RDoc::RI::TopLevelEntry.new('', '::', nil)

    dirs.each do |dir|
      @toplevel.load_from(dir)
    end
  end

end