Project

General

Profile

Feature #14787 ยป show_doc_when_completion.patch

aycabta (aycabta .), 05/25/2018 07:43 PM

View differences:

lib/irb/completion.rb
8 8
#
9 9

  
10 10
require "readline"
11
require "rdoc"
11 12

  
12 13
module IRB
13 14
  module InputCompletor # :nodoc:
14 15

  
16
    RDocRIDriver = RDoc::RI::Driver.new
15 17

  
16 18
    # Set of reserved words used by Ruby, you should not use these for
17 19
    # constants or variables
......
36 38
    ]
37 39

  
38 40
    CompletionProc = proc { |input|
39
      bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
41
      workspace = IRB.conf[:MAIN_CONTEXT].workspace
42
      if workspace.completion_data[:prev] == input
43
        workspace.completion_data[:repeat_count] += 1
44
      else
45
        workspace.completion_data[:repeat_count] = 0
46
      end
47
      workspace.completion_data[:prev] = input
40 48

  
49
      candidates = complement(input)
50
      if workspace.completion_data[:display_name]
51
        display_name = workspace.completion_data[:display_name]
52
        workspace.completion_data[:display_name] = nil
53
        begin
54
          RDocRIDriver.display_name(display_name)
55
        rescue RDoc::RI::Driver::NotFoundError
56
          candidates
57
        else
58
          []
59
        end
60
      else
61
        candidates
62
      end
63
    }
64

  
65
    def self.check_display_name(candidates, on_input, receiver = nil)
66
      ws = IRB.conf[:MAIN_CONTEXT].workspace
67
      if ws.completion_data[:repeat_count] > 1 and candidates.find{|m| m == on_input}
68
        if block_given?
69
          ws.completion_data[:display_name] = yield
70
        else
71
          ws.completion_data[:display_name] = receiver ? "#{receiver}##{on_input}" : on_input
72
        end
73
      end
74
      candidates
75
    end
76

  
77
    def self.complement(input)
78
      bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
41 79
      case input
42 80
      when /^((["'`]).*\2)\.([^.]*)$/
43 81
        # String
44 82
        receiver = $1
83
        on_input = $3
45 84
        message = Regexp.quote($3)
46 85

  
47 86
        candidates = String.instance_methods.collect{|m| m.to_s}
48
        select_message(receiver, message, candidates)
87
        candidates = select_message(receiver, message, candidates)
88
        check_display_name(candidates, input) do
89
          "String##{on_input}"
90
        end
49 91

  
50 92
      when /^(\/[^\/]*\/)\.([^.]*)$/
51 93
        # Regexp
52 94
        receiver = $1
95
        on_input = $2
53 96
        message = Regexp.quote($2)
54 97

  
55 98
        candidates = Regexp.instance_methods.collect{|m| m.to_s}
56
        select_message(receiver, message, candidates)
99
        candidates = select_message(receiver, message, candidates)
100
        check_display_name(candidates, input) do
101
          "Regexp##{on_input}"
102
        end
57 103

  
58 104
      when /^([^\]]*\])\.([^.]*)$/
59 105
        # Array
60 106
        receiver = $1
107
        on_input = $2
61 108
        message = Regexp.quote($2)
62 109

  
63 110
        candidates = Array.instance_methods.collect{|m| m.to_s}
64
        select_message(receiver, message, candidates)
111
        candidates = select_message(receiver, message, candidates)
112
        check_display_name(candidates, input) do
113
          "Array##{on_input}"
114
        end
65 115

  
66 116
      when /^([^\}]*\})\.([^.]*)$/
67 117
        # Proc or Hash
68 118
        receiver = $1
119
        on_input = $2
69 120
        message = Regexp.quote($2)
70 121

  
71 122
        candidates = Proc.instance_methods.collect{|m| m.to_s}
72 123
        candidates |= Hash.instance_methods.collect{|m| m.to_s}
73
        select_message(receiver, message, candidates)
124
        candidates = select_message(receiver, message, candidates)
125
        check_display_name(candidates, input) do
126
          if Proc.instance_methods.find{|m| m.to_s == on_input}
127
            "Proc##{on_input}"
128
          elsif Hash.instance_methods.find{|m| m.to_s == on_input}
129
            "Hash##{on_input}"
130
          end
131
        end
74 132

  
75 133
      when /^(:[^:.]*)$/
76 134
        # Symbol
......
86 144
        # Absolute Constant or class methods
87 145
        receiver = $1
88 146
        candidates = Object.constants.collect{|m| m.to_s}
89
        candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
147
        candidates = candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
148
        check_display_name(candidates, input) do
149
          receiver
150
        end
90 151

  
91 152
      when /^([A-Z].*)::([^:.]*)$/
92 153
        # Constant or class methods
93 154
        receiver = $1
155
        on_input = $2
94 156
        message = Regexp.quote($2)
95 157
        begin
96 158
          candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
......
98 160
        rescue Exception
99 161
          candidates = []
100 162
        end
101
        select_message(receiver, message, candidates, "::")
163
        candidates = select_message(receiver, message, candidates, "::")
164
        check_display_name(candidates, input) do
165
          input
166
        end
102 167

  
103 168
      when /^(:[^:.]+)(\.|::)([^.]*)$/
104 169
        # Symbol
105 170
        receiver = $1
106 171
        sep = $2
172
        on_input = $3
107 173
        message = Regexp.quote($3)
108 174

  
109 175
        candidates = Symbol.instance_methods.collect{|m| m.to_s}
110
        select_message(receiver, message, candidates, sep)
176
        candidates = select_message(receiver, message, candidates, sep)
177
        check_display_name(candidates, on_input, "Symbol")
111 178

  
112 179
      when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)(\.|::)([^.]*)$/
113 180
        # Numeric
114 181
        receiver = $1
115 182
        sep = $5
183
        on_input = $6
116 184
        message = Regexp.quote($6)
117 185

  
118 186
        begin
......
120 188
        rescue Exception
121 189
          candidates = []
122 190
        end
123
        select_message(receiver, message, candidates, sep)
191
        candidates = select_message(receiver, message, candidates, sep)
192
        check_display_name(candidates, input) do
193
          rec = eval(receiver, bind)
194
          "#{rec.class.name}##{on_input}"
195
        end
124 196

  
125 197
      when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/
126 198
        # Numeric(0xFFFF)
127 199
        receiver = $1
128 200
        sep = $2
201
        on_input = $3
129 202
        message = Regexp.quote($3)
130 203

  
131 204
        begin
......
133 206
        rescue Exception
134 207
          candidates = []
135 208
        end
136
        select_message(receiver, message, candidates, sep)
209
        candidates = select_message(receiver, message, candidates, sep)
210
        check_display_name(candidates, input) do
211
          rec = eval(receiver, bind)
212
          display_name = "#{rec.class.name}##{on_input}"
213
        end
137 214

  
138 215
      when /^(\$[^.]*)$/
139 216
        # global var
......
144 221
        # variable.func or func.func
145 222
        receiver = $1
146 223
        sep = $2
224
        on_input = $3
147 225
        message = Regexp.quote($3)
148 226

  
149 227
        gv = eval("global_variables", bind).collect{|m| m.to_s}
......
163 241
              candidates = rec.constants.collect{|m| m.to_s}
164 242
            end
165 243
            candidates |= rec.methods.collect{|m| m.to_s}
244
            check_display_name(candidates, on_input) do
245
              if sep == "::" or rec.kind_of?(Module)
246
                "#{rec.name}::#{on_input}"
247
              else
248
                "#{rec.class.name}##{on_input}"
249
              end
250
            end
166 251
          rescue Exception
167 252
            candidates = []
168 253
          end
......
176 261
          }
177 262
          candidates.sort!
178 263
          candidates.uniq!
264
          check_display_name(candidates, on_input) do
265
            "#{sep}#{on_input}"
266
          end
179 267
        end
180 268
        select_message(receiver, message, candidates, sep)
181 269

  
......
183 271
        # unknown(maybe String)
184 272

  
185 273
        receiver = ""
274
        on_input = $1
186 275
        message = Regexp.quote($1)
187 276

  
188 277
        candidates = String.instance_methods(true).collect{|m| m.to_s}
189
        select_message(receiver, message, candidates)
278
        candidates = select_message(receiver, message, candidates)
279
        check_display_name(candidates, on_input, "String")
190 280

  
191 281
      else
192 282
        candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
193 283

  
194
        (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
284
        candidates = (candidates | ReservedWords).grep(/^#{Regexp.quote(input)}/)
285
        check_display_name(candidates, input) do
286
          "Kernel##{input}"
287
        end
195 288
      end
196
    }
289
    end
197 290

  
198 291
    # Set of available operators in Ruby
199 292
    Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
lib/irb/workspace.rb
71 71
          end
72 72
        end
73 73
      end
74
      @completion_data = {}
74 75
      @binding.local_variable_set(:_, nil)
75 76
    end
76 77

  
......
80 81
    # <code>IRB.conf[:__MAIN__]</code>
81 82
    attr_reader :main
82 83

  
84
    # Data for showing document in completion
85
    attr_accessor :completion_data
86

  
83 87
    # Evaluate the given +statements+ within the  context of this workspace.
84 88
    def evaluate(context, statements, file = __FILE__, line = __LINE__)
85 89
      eval(statements, @binding, file, line)