Class: RubyLsp::Requests::Completion

Inherits:
Listener
  • Object
show all
Extended by:
T::Generic, T::Sig
Defined in:
lib/ruby_lsp/requests/completion.rb

Overview

![Completion demo](../../completion.gif)

The [completion](microsoft.github.io/language-server-protocol/specification#textDocument_completion) suggests possible completions according to what the developer is typing. Currently, completion is support for

  • require paths

  • classes, modules and constant names

# Example

“‘ruby require “ruby_lsp/requests” # –> completion: suggests `base_request`, `code_actions`, …

RubyLsp::Requests

# –> completion: suggests ‘Completion`, `Hover`, …

“‘

Constant Summary collapse

ResponseType =
type_member { { fixed: T::Array[Interface::CompletionItem] } }

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Listener

#response

Methods included from Support::Common

#create_code_lens, #markdown_from_index_entries, #range_from_location, #range_from_node, #visible?

Constructor Details

#initialize(index, nesting, dispatcher, message_queue) ⇒ Completion

Returns a new instance of Completion.



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ruby_lsp/requests/completion.rb', line 37

def initialize(index, nesting, dispatcher, message_queue)
  super(dispatcher, message_queue)
  @_response = T.let([], ResponseType)
  @index = index
  @nesting = nesting

  dispatcher.register(
    self,
    :on_string_node_enter,
    :on_constant_path_node_enter,
    :on_constant_read_node_enter,
  )
end

Instance Attribute Details

#_responseObject (readonly)

Returns the value of attribute _response.



27
28
29
# File 'lib/ruby_lsp/requests/completion.rb', line 27

def _response
  @_response
end

Instance Method Details

#on_constant_path_node_enter(node) ⇒ Object



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

def on_constant_path_node_enter(node)
  return if DependencyDetector.instance.typechecker

  name = node.slice

  top_level_reference = if name.start_with?("::")
    name = name.delete_prefix("::")
    true
  else
    false
  end

  # If we're trying to provide completion for an aliased namespace, we need to first discover it's real name in
  # order to find which possible constants match the desired search
  *namespace, incomplete_name = name.split("::")
  aliased_namespace = T.must(namespace).join("::")
  namespace_entries = @index.resolve(aliased_namespace, @nesting)
  return unless namespace_entries

  real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)

  candidates = @index.prefix_search("#{real_namespace}::#{incomplete_name}", top_level_reference ? [] : @nesting)
  candidates.each do |entries|
    # The only time we may have a private constant reference from outside of the namespace is if we're dealing
    # with ConstantPath and the entry name doesn't start with the current nesting
    first_entry = T.must(entries.first)
    next if first_entry.visibility == :private && !first_entry.name.start_with?("#{@nesting}::")

    constant_name = T.must(first_entry.name.split("::").last)

    full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"

    @_response << build_entry_completion(
      full_name,
      name,
      node,
      entries,
      top_level_reference || top_level?(T.must(entries.first).name),
    )
  end
end

#on_constant_read_node_enter(node) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruby_lsp/requests/completion.rb', line 60

def on_constant_read_node_enter(node)
  return if DependencyDetector.instance.typechecker

  name = node.slice
  candidates = @index.prefix_search(name, @nesting)
  candidates.each do |entries|
    complete_name = T.must(entries.first).name
    @_response << build_entry_completion(
      complete_name,
      name,
      node,
      entries,
      top_level?(complete_name),
    )
  end
end

#on_string_node_enter(node) ⇒ Object



52
53
54
55
56
# File 'lib/ruby_lsp/requests/completion.rb', line 52

def on_string_node_enter(node)
  @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path|
    @_response << build_completion(T.must(path), node)
  end
end