Class: RubyLsp::Listeners::Completion

Inherits:
Object
  • Object
show all
Includes:
Requests::Support::Common
Defined in:
lib/ruby_lsp/listeners/completion.rb

Constant Summary collapse

KEYWORDS =
[
  "alias",
  "and",
  "begin",
  "BEGIN",
  "break",
  "case",
  "class",
  "def",
  "defined?",
  "do",
  "else",
  "elsif",
  "end",
  "END",
  "ensure",
  "false",
  "for",
  "if",
  "in",
  "module",
  "next",
  "nil",
  "not",
  "or",
  "redo",
  "rescue",
  "retry",
  "return",
  "self",
  "super",
  "then",
  "true",
  "undef",
  "unless",
  "until",
  "when",
  "while",
  "yield",
  "__ENCODING__",
  "__FILE__",
  "__LINE__",
].freeze

Instance Method Summary collapse

Methods included from Requests::Support::Common

#categorized_markdown_from_index_entries, #constant_name, #create_code_lens, #each_constant_path_part, #kind_for_entry, #markdown_from_index_entries, #namespace_constant_name, #not_in_dependencies?, #range_from_location, #range_from_node, #self_receiver?

Constructor Details

#initialize(response_builder, global_state, node_context, sorbet_level, dispatcher, uri, trigger_character) ⇒ Completion

: (ResponseBuilders::CollectionResponseBuilder response_builder, GlobalState global_state, NodeContext node_context, SorbetLevel sorbet_level, Prism::Dispatcher dispatcher, URI::Generic uri, String? trigger_character) -> void



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
# File 'lib/ruby_lsp/listeners/completion.rb', line 54

def initialize( # rubocop:disable Metrics/ParameterLists
  response_builder,
  global_state,
  node_context,
  sorbet_level,
  dispatcher,
  uri,
  trigger_character
)
  @response_builder = response_builder
  @global_state = global_state
  @index = global_state.index #: RubyIndexer::Index
  @type_inferrer = global_state.type_inferrer #: TypeInferrer
  @node_context = node_context
  @sorbet_level = sorbet_level
  @uri = uri
  @trigger_character = trigger_character

  dispatcher.register(
    self,
    :on_constant_path_node_enter,
    :on_constant_read_node_enter,
    :on_call_node_enter,
    :on_global_variable_and_write_node_enter,
    :on_global_variable_operator_write_node_enter,
    :on_global_variable_or_write_node_enter,
    :on_global_variable_read_node_enter,
    :on_global_variable_target_node_enter,
    :on_global_variable_write_node_enter,
    :on_instance_variable_read_node_enter,
    :on_instance_variable_write_node_enter,
    :on_instance_variable_and_write_node_enter,
    :on_instance_variable_operator_write_node_enter,
    :on_instance_variable_or_write_node_enter,
    :on_instance_variable_target_node_enter,
    :on_class_variable_and_write_node_enter,
    :on_class_variable_operator_write_node_enter,
    :on_class_variable_or_write_node_enter,
    :on_class_variable_read_node_enter,
    :on_class_variable_target_node_enter,
    :on_class_variable_write_node_enter,
  )
end

Instance Method Details

#on_call_node_enter(node) ⇒ Object

: (Prism::CallNode node) -> void



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
# File 'lib/ruby_lsp/listeners/completion.rb', line 143

def on_call_node_enter(node)
  # The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
  # no sigil, Sorbet will still provide completion for constants
  if @sorbet_level.ignore?
    receiver = node.receiver

    # When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke
    # singleton methods). However, in addition to providing method completion, we also need to show possible
    # constant completions
    if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
        node.call_operator == "::"

      name = RubyIndexer::Index.constant_name(receiver)

      if name
        start_loc = node.location
        end_loc = node.call_operator_loc #: as !nil

        constant_path_completion(
          "#{name}::",
          Interface::Range.new(
            start: Interface::Position.new(line: start_loc.start_line - 1, character: start_loc.start_column),
            end: Interface::Position.new(line: end_loc.end_line - 1, character: end_loc.end_column),
          ),
        )
        return
      end
    end
  end

  name = node.message
  return unless name

  case name
  when "require"
    complete_require(node)
  when "require_relative"
    complete_require_relative(node)
  else
    complete_methods(node, name)
  end
end

#on_class_variable_and_write_node_enter(node) ⇒ Object

: (Prism::ClassVariableAndWriteNode node) -> void



247
248
249
# File 'lib/ruby_lsp/listeners/completion.rb', line 247

def on_class_variable_and_write_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.name_loc)
end

#on_class_variable_operator_write_node_enter(node) ⇒ Object

: (Prism::ClassVariableOperatorWriteNode node) -> void



252
253
254
# File 'lib/ruby_lsp/listeners/completion.rb', line 252

def on_class_variable_operator_write_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.name_loc)
end

#on_class_variable_or_write_node_enter(node) ⇒ Object

: (Prism::ClassVariableOrWriteNode node) -> void



257
258
259
# File 'lib/ruby_lsp/listeners/completion.rb', line 257

def on_class_variable_or_write_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.name_loc)
end

#on_class_variable_read_node_enter(node) ⇒ Object

: (Prism::ClassVariableReadNode node) -> void



267
268
269
# File 'lib/ruby_lsp/listeners/completion.rb', line 267

def on_class_variable_read_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.location)
end

#on_class_variable_target_node_enter(node) ⇒ Object

: (Prism::ClassVariableTargetNode node) -> void



262
263
264
# File 'lib/ruby_lsp/listeners/completion.rb', line 262

def on_class_variable_target_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.location)
end

#on_class_variable_write_node_enter(node) ⇒ Object

: (Prism::ClassVariableWriteNode node) -> void



272
273
274
# File 'lib/ruby_lsp/listeners/completion.rb', line 272

def on_class_variable_write_node_enter(node)
  handle_class_variable_completion(node.name.to_s, node.name_loc)
end

#on_constant_path_node_enter(node) ⇒ Object

Handle completion on namespaced constant references (e.g. ‘Foo::Bar`) : (Prism::ConstantPathNode node) -> void



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ruby_lsp/listeners/completion.rb', line 125

def on_constant_path_node_enter(node)
  # The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
  # no sigil, Sorbet will still provide completion for constants
  return unless @sorbet_level.ignore?

  name = begin
    node.full_name
  rescue Prism::ConstantPathNode::MissingNodesInConstantPathError
    node.slice
  rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError
    nil
  end
  return if name.nil?

  constant_path_completion(name, range_from_location(node.location))
end

#on_constant_read_node_enter(node) ⇒ Object

Handle completion on regular constant references (e.g. ‘Bar`) : (Prism::ConstantReadNode node) -> void



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/ruby_lsp/listeners/completion.rb', line 100

def on_constant_read_node_enter(node)
  # The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
  # no sigil, Sorbet will still provide completion for constants
  return unless @sorbet_level.ignore?

  name = RubyIndexer::Index.constant_name(node)
  return if name.nil?

  range = range_from_location(node.location)
  candidates = @index.constant_completion_candidates(name, @node_context.nesting)
  candidates.each do |entries|
    complete_name = entries.first #: as !nil
      .name
    @response_builder << build_entry_completion(
      complete_name,
      name,
      range,
      entries,
      top_level?(complete_name),
    )
  end
end

#on_global_variable_and_write_node_enter(node) ⇒ Object

: (Prism::GlobalVariableAndWriteNode node) -> void



187
188
189
# File 'lib/ruby_lsp/listeners/completion.rb', line 187

def on_global_variable_and_write_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.name_loc)
end

#on_global_variable_operator_write_node_enter(node) ⇒ Object

: (Prism::GlobalVariableOperatorWriteNode node) -> void



192
193
194
# File 'lib/ruby_lsp/listeners/completion.rb', line 192

def on_global_variable_operator_write_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.name_loc)
end

#on_global_variable_or_write_node_enter(node) ⇒ Object

: (Prism::GlobalVariableOrWriteNode node) -> void



197
198
199
# File 'lib/ruby_lsp/listeners/completion.rb', line 197

def on_global_variable_or_write_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.name_loc)
end

#on_global_variable_read_node_enter(node) ⇒ Object

: (Prism::GlobalVariableReadNode node) -> void



202
203
204
# File 'lib/ruby_lsp/listeners/completion.rb', line 202

def on_global_variable_read_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.location)
end

#on_global_variable_target_node_enter(node) ⇒ Object

: (Prism::GlobalVariableTargetNode node) -> void



207
208
209
# File 'lib/ruby_lsp/listeners/completion.rb', line 207

def on_global_variable_target_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.location)
end

#on_global_variable_write_node_enter(node) ⇒ Object

: (Prism::GlobalVariableWriteNode node) -> void



212
213
214
# File 'lib/ruby_lsp/listeners/completion.rb', line 212

def on_global_variable_write_node_enter(node)
  handle_global_variable_completion(node.name.to_s, node.name_loc)
end

#on_instance_variable_and_write_node_enter(node) ⇒ Object

: (Prism::InstanceVariableAndWriteNode node) -> void



227
228
229
# File 'lib/ruby_lsp/listeners/completion.rb', line 227

def on_instance_variable_and_write_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.name_loc)
end

#on_instance_variable_operator_write_node_enter(node) ⇒ Object

: (Prism::InstanceVariableOperatorWriteNode node) -> void



232
233
234
# File 'lib/ruby_lsp/listeners/completion.rb', line 232

def on_instance_variable_operator_write_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.name_loc)
end

#on_instance_variable_or_write_node_enter(node) ⇒ Object

: (Prism::InstanceVariableOrWriteNode node) -> void



237
238
239
# File 'lib/ruby_lsp/listeners/completion.rb', line 237

def on_instance_variable_or_write_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.name_loc)
end

#on_instance_variable_read_node_enter(node) ⇒ Object

: (Prism::InstanceVariableReadNode node) -> void



217
218
219
# File 'lib/ruby_lsp/listeners/completion.rb', line 217

def on_instance_variable_read_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.location)
end

#on_instance_variable_target_node_enter(node) ⇒ Object

: (Prism::InstanceVariableTargetNode node) -> void



242
243
244
# File 'lib/ruby_lsp/listeners/completion.rb', line 242

def on_instance_variable_target_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.location)
end

#on_instance_variable_write_node_enter(node) ⇒ Object

: (Prism::InstanceVariableWriteNode node) -> void



222
223
224
# File 'lib/ruby_lsp/listeners/completion.rb', line 222

def on_instance_variable_write_node_enter(node)
  handle_instance_variable_completion(node.name.to_s, node.name_loc)
end