-
Notifications
You must be signed in to change notification settings - Fork 183
/
Copy pathcompletion.rb
117 lines (106 loc) · 3.53 KB
/
completion.rb
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
# typed: strict
# frozen_string_literal: true
require "ruby_lsp/listeners/completion"
module RubyLsp
module Requests
# 
#
# The [completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
# suggests possible completions according to what the developer is typing.
#
# Currently supported targets:
#
# - Classes
# - Modules
# - Constants
# - Require paths
# - Methods invoked on self only
# - Instance variables
#
# # Example
#
# ```ruby
# require "ruby_lsp/requests" # --> completion: suggests `base_request`, `code_actions`, ...
#
# RubyLsp::Requests:: # --> completion: suggests `Completion`, `Hover`, ...
# ```
class Completion < Request
extend T::Sig
class << self
extend T::Sig
sig { returns(Interface::CompletionOptions) }
def provider
Interface::CompletionOptions.new(
resolve_provider: true,
trigger_characters: ["/", "\"", "'", ":", "@", "."],
completion_item: {
labelDetailsSupport: true,
},
)
end
end
sig do
params(
document: T.any(RubyDocument, ERBDocument),
global_state: GlobalState,
params: T::Hash[Symbol, T.untyped],
sorbet_level: RubyDocument::SorbetLevel,
dispatcher: Prism::Dispatcher,
).void
end
def initialize(document, global_state, params, sorbet_level, dispatcher)
super()
@target = T.let(nil, T.nilable(Prism::Node))
@dispatcher = dispatcher
# Completion always receives the position immediately after the character that was just typed. Here we adjust it
# back by 1, so that we find the right node
char_position = document.create_scanner.find_char_position(params[:position]) - 1
node_context = RubyDocument.locate(
document.parse_result.value,
char_position,
node_types: [
Prism::CallNode,
Prism::ConstantReadNode,
Prism::ConstantPathNode,
Prism::InstanceVariableReadNode,
Prism::InstanceVariableAndWriteNode,
Prism::InstanceVariableOperatorWriteNode,
Prism::InstanceVariableOrWriteNode,
Prism::InstanceVariableTargetNode,
Prism::InstanceVariableWriteNode,
],
)
@response_builder = T.let(
ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem].new,
ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
)
Listeners::Completion.new(
@response_builder,
global_state,
node_context,
sorbet_level,
dispatcher,
document.uri,
params.dig(:context, :triggerCharacter),
)
Addon.addons.each do |addon|
addon.create_completion_listener(@response_builder, node_context, dispatcher, document.uri)
end
matched = node_context.node
parent = node_context.parent
return unless matched && parent
@target = if parent.is_a?(Prism::ConstantPathNode) && matched.is_a?(Prism::ConstantReadNode)
parent
else
matched
end
end
sig { override.returns(T::Array[Interface::CompletionItem]) }
def perform
return [] unless @target
@dispatcher.dispatch_once(@target)
@response_builder.response
end
end
end
end