ruby-lsp 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,14 +17,14 @@ module RubyLsp
17
17
  # require "some_gem/file" # <- Request go to definition on this string will take you to the file
18
18
  # Product.new # <- Request go to definition on this class name will take you to its declaration.
19
19
  # ```
20
- class Definition < Listener
20
+ class Definition < ExtensibleListener
21
21
  extend T::Sig
22
22
  extend T::Generic
23
23
 
24
24
  ResponseType = type_member { { fixed: T.nilable(T.any(T::Array[Interface::Location], Interface::Location)) } }
25
25
 
26
26
  sig { override.returns(ResponseType) }
27
- attr_reader :response
27
+ attr_reader :_response
28
28
 
29
29
  sig do
30
30
  params(
@@ -36,15 +36,37 @@ module RubyLsp
36
36
  ).void
37
37
  end
38
38
  def initialize(uri, nesting, index, emitter, message_queue)
39
- super(emitter, message_queue)
40
-
41
39
  @uri = uri
42
40
  @nesting = nesting
43
41
  @index = index
44
- @response = T.let(nil, ResponseType)
42
+ @_response = T.let(nil, ResponseType)
43
+
44
+ super(emitter, message_queue)
45
+
45
46
  emitter.register(self, :on_command, :on_const, :on_const_path_ref)
46
47
  end
47
48
 
49
+ sig { override.params(ext: Extension).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
50
+ def initialize_external_listener(ext)
51
+ ext.create_definition_listener(@uri, @nesting, @index, @emitter, @message_queue)
52
+ end
53
+
54
+ sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
55
+ def merge_response!(other)
56
+ other_response = other._response
57
+
58
+ case @_response
59
+ when Interface::Location
60
+ @_response = [@_response, *other_response]
61
+ when Array
62
+ @_response.concat(Array(other_response))
63
+ when nil
64
+ @_response = other_response
65
+ end
66
+
67
+ self
68
+ end
69
+
48
70
  sig { params(node: SyntaxTree::ConstPathRef).void }
49
71
  def on_const_path_ref(node)
50
72
  name = full_constant_name(node)
@@ -67,14 +89,16 @@ module RubyLsp
67
89
  string = argument.parts.first
68
90
  return unless string.is_a?(SyntaxTree::TStringContent)
69
91
 
70
- required_file = "#{string.value}.rb"
71
-
72
92
  case message
73
93
  when "require"
74
- candidate = find_file_in_load_path(required_file)
94
+ entry = @index.search_require_paths(string.value).find do |indexable_path|
95
+ indexable_path.require_path == string.value
96
+ end
97
+
98
+ if entry
99
+ candidate = entry.full_path
75
100
 
76
- if candidate
77
- @response = Interface::Location.new(
101
+ @_response = Interface::Location.new(
78
102
  uri: URI::Generic.from_path(path: candidate).to_s,
79
103
  range: Interface::Range.new(
80
104
  start: Interface::Position.new(line: 0, character: 0),
@@ -83,19 +107,18 @@ module RubyLsp
83
107
  )
84
108
  end
85
109
  when "require_relative"
110
+ required_file = "#{string.value}.rb"
86
111
  path = @uri.to_standardized_path
87
112
  current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
88
113
  candidate = File.expand_path(File.join(current_folder, required_file))
89
114
 
90
- if candidate
91
- @response = Interface::Location.new(
92
- uri: URI::Generic.from_path(path: candidate).to_s,
93
- range: Interface::Range.new(
94
- start: Interface::Position.new(line: 0, character: 0),
95
- end: Interface::Position.new(line: 0, character: 0),
96
- ),
97
- )
98
- end
115
+ @_response = Interface::Location.new(
116
+ uri: URI::Generic.from_path(path: candidate).to_s,
117
+ range: Interface::Range.new(
118
+ start: Interface::Position.new(line: 0, character: 0),
119
+ end: Interface::Position.new(line: 0, character: 0),
120
+ ),
121
+ )
99
122
  end
100
123
  end
101
124
 
@@ -112,7 +135,7 @@ module RubyLsp
112
135
  nil
113
136
  end
114
137
 
115
- @response = entries.filter_map do |entry|
138
+ @_response = entries.filter_map do |entry|
116
139
  location = entry.location
117
140
  # If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
118
141
  # additional behavior on top of jumping to RBIs. Sorbet can already handle go to definition for all constants
@@ -133,18 +156,6 @@ module RubyLsp
133
156
  )
134
157
  end
135
158
  end
136
-
137
- sig { params(file: String).returns(T.nilable(String)) }
138
- def find_file_in_load_path(file)
139
- return unless file.include?("/")
140
-
141
- $LOAD_PATH.each do |p|
142
- found = Dir.glob("**/#{file}", base: p).first
143
- return "#{p}/#{found}" if found
144
- end
145
-
146
- nil
147
- end
148
159
  end
149
160
  end
150
161
  end
@@ -28,7 +28,7 @@ module RubyLsp
28
28
  ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } }
29
29
 
30
30
  sig { override.returns(ResponseType) }
31
- attr_reader :response
31
+ attr_reader :_response
32
32
 
33
33
  sig do
34
34
  params(
@@ -41,7 +41,7 @@ module RubyLsp
41
41
  def initialize(target, parent, emitter, message_queue)
42
42
  super(emitter, message_queue)
43
43
 
44
- @response = T.let([], T::Array[Interface::DocumentHighlight])
44
+ @_response = T.let([], T::Array[Interface::DocumentHighlight])
45
45
 
46
46
  return unless target && parent
47
47
 
@@ -83,7 +83,7 @@ module RubyLsp
83
83
  sig { params(match: Support::HighlightTarget::HighlightMatch).void }
84
84
  def add_highlight(match)
85
85
  range = range_from_syntax_tree_node(match.node)
86
- @response << Interface::DocumentHighlight.new(range: range, kind: match.type)
86
+ @_response << Interface::DocumentHighlight.new(range: range, kind: match.type)
87
87
  end
88
88
  end
89
89
  end
@@ -73,7 +73,7 @@ module RubyLsp
73
73
  end
74
74
 
75
75
  sig { override.returns(ResponseType) }
76
- attr_reader :response
76
+ attr_reader :_response
77
77
 
78
78
  sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue).void }
79
79
  def initialize(uri, emitter, message_queue)
@@ -84,7 +84,7 @@ module RubyLsp
84
84
  path = uri.to_standardized_path
85
85
  version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil
86
86
  @gem_version = T.let(version_match && version_match[0], T.nilable(String))
87
- @response = T.let([], T::Array[Interface::DocumentLink])
87
+ @_response = T.let([], T::Array[Interface::DocumentLink])
88
88
 
89
89
  emitter.register(self, :on_comment)
90
90
  end
@@ -99,7 +99,7 @@ module RubyLsp
99
99
  file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
100
100
  return if file_path.nil?
101
101
 
102
- @response << Interface::DocumentLink.new(
102
+ @_response << Interface::DocumentLink.new(
103
103
  range: range_from_syntax_tree_node(node),
104
104
  target: "file://#{file_path}##{uri.line_number}",
105
105
  tooltip: "Jump to #{file_path}##{uri.line_number}",
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  # end
27
27
  # end
28
28
  # ```
29
- class DocumentSymbol < Listener
29
+ class DocumentSymbol < ExtensibleListener
30
30
  extend T::Sig
31
31
  extend T::Generic
32
32
 
@@ -47,22 +47,18 @@ module RubyLsp
47
47
  end
48
48
 
49
49
  sig { override.returns(T::Array[Interface::DocumentSymbol]) }
50
- attr_reader :response
50
+ attr_reader :_response
51
51
 
52
52
  sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
53
53
  def initialize(emitter, message_queue)
54
- super
55
-
56
54
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
57
- @response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
55
+ @_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
58
56
  @stack = T.let(
59
57
  [@root],
60
58
  T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
61
59
  )
62
60
 
63
- @external_listeners.concat(
64
- Extension.extensions.filter_map { |ext| ext.create_document_symbol_listener(emitter, message_queue) },
65
- )
61
+ super
66
62
 
67
63
  emitter.register(
68
64
  self,
@@ -79,10 +75,15 @@ module RubyLsp
79
75
  )
80
76
  end
81
77
 
78
+ sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) }
79
+ def initialize_external_listener(extension)
80
+ extension.create_document_symbol_listener(@emitter, @message_queue)
81
+ end
82
+
82
83
  # Merges responses from other listeners
83
84
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
84
85
  def merge_response!(other)
85
- @response.concat(other.response)
86
+ @_response.concat(other.response)
86
87
  self
87
88
  end
88
89
 
@@ -13,7 +13,7 @@ module RubyLsp
13
13
  # ```ruby
14
14
  # String # -> Hovering over the class reference will show all declaration locations and the documentation
15
15
  # ```
16
- class Hover < Listener
16
+ class Hover < ExtensibleListener
17
17
  extend T::Sig
18
18
  extend T::Generic
19
19
 
@@ -30,7 +30,7 @@ module RubyLsp
30
30
  )
31
31
 
32
32
  sig { override.returns(ResponseType) }
33
- attr_reader :response
33
+ attr_reader :_response
34
34
 
35
35
  sig do
36
36
  params(
@@ -41,27 +41,29 @@ module RubyLsp
41
41
  ).void
42
42
  end
43
43
  def initialize(index, nesting, emitter, message_queue)
44
- super(emitter, message_queue)
45
-
46
44
  @nesting = nesting
47
45
  @index = index
48
- @external_listeners.concat(
49
- Extension.extensions.filter_map { |ext| ext.create_hover_listener(emitter, message_queue) },
50
- )
51
- @response = T.let(nil, ResponseType)
46
+ @_response = T.let(nil, ResponseType)
47
+
48
+ super(emitter, message_queue)
52
49
  emitter.register(self, :on_const_path_ref, :on_const)
53
50
  end
54
51
 
52
+ sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) }
53
+ def initialize_external_listener(extension)
54
+ extension.create_hover_listener(@emitter, @message_queue)
55
+ end
56
+
55
57
  # Merges responses from other hover listeners
56
58
  sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
57
59
  def merge_response!(other)
58
60
  other_response = other.response
59
61
  return self unless other_response
60
62
 
61
- if @response.nil?
62
- @response = other.response
63
+ if @_response.nil?
64
+ @_response = other.response
63
65
  else
64
- @response.contents.value << "\n\n" << other_response.contents.value
66
+ @_response.contents.value << "\n\n" << other_response.contents.value
65
67
  end
66
68
 
67
69
  self
@@ -89,29 +91,10 @@ module RubyLsp
89
91
  entries = @index.resolve(name, @nesting)
90
92
  return unless entries
91
93
 
92
- title = +"```ruby\n#{name}\n```"
93
- definitions = []
94
- content = +""
95
- entries.each do |entry|
96
- loc = entry.location
97
-
98
- # We always handle locations as zero based. However, for file links in Markdown we need them to be one based,
99
- # which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to columns. The
100
- # format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
101
- uri = URI::Generic.from_path(
102
- path: entry.file_path,
103
- fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
104
- )
105
-
106
- definitions << "[#{entry.file_name}](#{uri})"
107
- content << "\n\n#{entry.comments.join("\n")}" unless entry.comments.empty?
108
- end
109
-
110
- contents = Interface::MarkupContent.new(
111
- kind: "markdown",
112
- value: "#{title}\n\n**Definitions**: #{definitions.join(" | ")}\n\n#{content}",
94
+ @_response = Interface::Hover.new(
95
+ range: range_from_syntax_tree_node(node),
96
+ contents: markdown_from_index_entries(name, entries),
113
97
  )
114
- @response = Interface::Hover.new(range: range_from_syntax_tree_node(node), contents: contents)
115
98
  end
116
99
  end
117
100
  end
@@ -27,13 +27,13 @@ module RubyLsp
27
27
  RESCUE_STRING_LENGTH = T.let("rescue".length, Integer)
28
28
 
29
29
  sig { override.returns(ResponseType) }
30
- attr_reader :response
30
+ attr_reader :_response
31
31
 
32
32
  sig { params(range: T::Range[Integer], emitter: EventEmitter, message_queue: Thread::Queue).void }
33
33
  def initialize(range, emitter, message_queue)
34
34
  super(emitter, message_queue)
35
35
 
36
- @response = T.let([], ResponseType)
36
+ @_response = T.let([], ResponseType)
37
37
  @range = range
38
38
 
39
39
  emitter.register(self, :on_rescue)
@@ -47,7 +47,7 @@ module RubyLsp
47
47
  loc = node.location
48
48
  return unless visible?(node, @range)
49
49
 
50
- @response << Interface::InlayHint.new(
50
+ @_response << Interface::InlayHint.new(
51
51
  position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
52
52
  label: "StandardError",
53
53
  padding_left: true,
@@ -105,7 +105,7 @@ module RubyLsp
105
105
  end
106
106
 
107
107
  sig { override.returns(ResponseType) }
108
- attr_reader :response
108
+ attr_reader :_response
109
109
 
110
110
  sig do
111
111
  params(
@@ -117,7 +117,7 @@ module RubyLsp
117
117
  def initialize(emitter, message_queue, range: nil)
118
118
  super(emitter, message_queue)
119
119
 
120
- @response = T.let([], ResponseType)
120
+ @_response = T.let([], ResponseType)
121
121
  @range = range
122
122
  @special_methods = T.let(nil, T.nilable(T::Array[String]))
123
123
 
@@ -174,7 +174,7 @@ module RubyLsp
174
174
  # When finding a module or class definition, we will have already pushed a token related to this constant. We
175
175
  # need to look at the previous two tokens and if they match this locatione exactly, avoid pushing another token
176
176
  # on top of the previous one
177
- return if @response.last(2).any? { |token| token.location == node.location }
177
+ return if @_response.last(2).any? { |token| token.location == node.location }
178
178
 
179
179
  add_token(node.location, :namespace)
180
180
  end
@@ -327,7 +327,7 @@ module RubyLsp
327
327
  def add_token(location, type, modifiers = [])
328
328
  length = location.end_char - location.start_char
329
329
  modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
330
- @response.push(
330
+ @_response.push(
331
331
  SemanticToken.new(
332
332
  location: location,
333
333
  length: length,
@@ -74,6 +74,39 @@ module RubyLsp
74
74
  data: data,
75
75
  )
76
76
  end
77
+
78
+ sig { params(title: String, entries: T::Array[RubyIndexer::Index::Entry]).returns(Interface::MarkupContent) }
79
+ def markdown_from_index_entries(title, entries)
80
+ markdown_title = "```ruby\n#{title}\n```"
81
+ definitions = []
82
+ content = +""
83
+ entries.each do |entry|
84
+ loc = entry.location
85
+
86
+ # We always handle locations as zero based. However, for file links in Markdown we need them to be one
87
+ # based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
88
+ # columns. The format for VS Code file URIs is
89
+ # `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
90
+ uri = URI::Generic.from_path(
91
+ path: entry.file_path,
92
+ fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
93
+ )
94
+
95
+ definitions << "[#{entry.file_name}](#{uri})"
96
+ content << "\n\n#{entry.comments.join("\n")}" unless entry.comments.empty?
97
+ end
98
+
99
+ Interface::MarkupContent.new(
100
+ kind: "markdown",
101
+ value: <<~MARKDOWN.chomp,
102
+ #{markdown_title}
103
+
104
+ **Definitions**: #{definitions.join(" | ")}
105
+
106
+ #{content}
107
+ MARKDOWN
108
+ )
109
+ end
77
110
  end
78
111
  end
79
112
  end
@@ -17,7 +17,7 @@ module RubyLsp
17
17
  # - [CodeActionResolve](rdoc-ref:RubyLsp::Requests::CodeActionResolve)
18
18
  # - [DocumentHighlight](rdoc-ref:RubyLsp::Requests::DocumentHighlight)
19
19
  # - [InlayHint](rdoc-ref:RubyLsp::Requests::InlayHints)
20
- # - [PathCompletion](rdoc-ref:RubyLsp::Requests::PathCompletion)
20
+ # - [Completion](rdoc-ref:RubyLsp::Requests::Completion)
21
21
  # - [CodeLens](rdoc-ref:RubyLsp::Requests::CodeLens)
22
22
  # - [Definition](rdoc-ref:RubyLsp::Requests::Definition)
23
23
  # - [ShowSyntaxTree](rdoc-ref:RubyLsp::Requests::ShowSyntaxTree)
@@ -38,7 +38,7 @@ module RubyLsp
38
38
  autoload :CodeActionResolve, "ruby_lsp/requests/code_action_resolve"
39
39
  autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
40
40
  autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
41
- autoload :PathCompletion, "ruby_lsp/requests/path_completion"
41
+ autoload :Completion, "ruby_lsp/requests/completion"
42
42
  autoload :CodeLens, "ruby_lsp/requests/code_lens"
43
43
  autoload :Definition, "ruby_lsp/requests/definition"
44
44
  autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
@@ -53,7 +53,6 @@ module RubyLsp
53
53
  autoload :Sorbet, "ruby_lsp/requests/support/sorbet"
54
54
  autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
55
55
  autoload :RailsDocumentClient, "ruby_lsp/requests/support/rails_document_client"
56
- autoload :PrefixTree, "ruby_lsp/requests/support/prefix_tree"
57
56
  autoload :Common, "ruby_lsp/requests/support/common"
58
57
  autoload :FormatterRunner, "ruby_lsp/requests/support/formatter_runner"
59
58
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-01 00:00:00.000000000 Z
11
+ date: 2023-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -67,7 +67,7 @@ dependencies:
67
67
  version: '0.9'
68
68
  - - "<"
69
69
  - !ruby/object:Gem::Version
70
- version: '0.10'
70
+ version: '0.11'
71
71
  type: :runtime
72
72
  prerelease: false
73
73
  version_requirements: !ruby/object:Gem::Requirement
@@ -77,7 +77,7 @@ dependencies:
77
77
  version: '0.9'
78
78
  - - "<"
79
79
  - !ruby/object:Gem::Version
80
- version: '0.10'
80
+ version: '0.11'
81
81
  description: An opinionated language server for Ruby
82
82
  email:
83
83
  - ruby@shopify.com
@@ -97,12 +97,15 @@ files:
97
97
  - lib/ruby-lsp.rb
98
98
  - lib/ruby_indexer/lib/ruby_indexer/configuration.rb
99
99
  - lib/ruby_indexer/lib/ruby_indexer/index.rb
100
+ - lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
101
+ - lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
100
102
  - lib/ruby_indexer/lib/ruby_indexer/visitor.rb
101
103
  - lib/ruby_indexer/ruby_indexer.rb
102
104
  - lib/ruby_indexer/test/classes_and_modules_test.rb
103
105
  - lib/ruby_indexer/test/configuration_test.rb
104
106
  - lib/ruby_indexer/test/constant_test.rb
105
107
  - lib/ruby_indexer/test/index_test.rb
108
+ - lib/ruby_indexer/test/prefix_tree_test.rb
106
109
  - lib/ruby_indexer/test/test_case.rb
107
110
  - lib/ruby_lsp/check_docs.rb
108
111
  - lib/ruby_lsp/document.rb
@@ -116,6 +119,7 @@ files:
116
119
  - lib/ruby_lsp/requests/code_action_resolve.rb
117
120
  - lib/ruby_lsp/requests/code_actions.rb
118
121
  - lib/ruby_lsp/requests/code_lens.rb
122
+ - lib/ruby_lsp/requests/completion.rb
119
123
  - lib/ruby_lsp/requests/definition.rb
120
124
  - lib/ruby_lsp/requests/diagnostics.rb
121
125
  - lib/ruby_lsp/requests/document_highlight.rb
@@ -126,7 +130,6 @@ files:
126
130
  - lib/ruby_lsp/requests/hover.rb
127
131
  - lib/ruby_lsp/requests/inlay_hints.rb
128
132
  - lib/ruby_lsp/requests/on_type_formatting.rb
129
- - lib/ruby_lsp/requests/path_completion.rb
130
133
  - lib/ruby_lsp/requests/selection_ranges.rb
131
134
  - lib/ruby_lsp/requests/semantic_highlighting.rb
132
135
  - lib/ruby_lsp/requests/show_syntax_tree.rb
@@ -135,7 +138,6 @@ files:
135
138
  - lib/ruby_lsp/requests/support/dependency_detector.rb
136
139
  - lib/ruby_lsp/requests/support/formatter_runner.rb
137
140
  - lib/ruby_lsp/requests/support/highlight_target.rb
138
- - lib/ruby_lsp/requests/support/prefix_tree.rb
139
141
  - lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
140
142
  - lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
141
143
  - lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
@@ -170,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
172
  - !ruby/object:Gem::Version
171
173
  version: '0'
172
174
  requirements: []
173
- rubygems_version: 3.4.18
175
+ rubygems_version: 3.4.19
174
176
  signing_key:
175
177
  specification_version: 4
176
178
  summary: An opinionated language server for Ruby
@@ -1,65 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- module Requests
6
- # ![Path completion demo](../../path_completion.gif)
7
- #
8
- # The [completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
9
- # request looks up Ruby files in the $LOAD_PATH to suggest path completion inside `require` statements.
10
- #
11
- # # Example
12
- #
13
- # ```ruby
14
- # require "ruby_lsp/requests" # --> completion: suggests `base_request`, `code_actions`, ...
15
- # ```
16
- class PathCompletion < Listener
17
- extend T::Sig
18
- extend T::Generic
19
-
20
- ResponseType = type_member { { fixed: T::Array[Interface::CompletionItem] } }
21
-
22
- sig { override.returns(ResponseType) }
23
- attr_reader :response
24
-
25
- sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
26
- def initialize(emitter, message_queue)
27
- super
28
- @response = T.let([], ResponseType)
29
- @tree = T.let(Support::PrefixTree.new(collect_load_path_files), Support::PrefixTree)
30
-
31
- emitter.register(self, :on_tstring_content)
32
- end
33
-
34
- sig { params(node: SyntaxTree::TStringContent).void }
35
- def on_tstring_content(node)
36
- @tree.search(node.value).sort.each do |path|
37
- @response << build_completion(path, node)
38
- end
39
- end
40
-
41
- private
42
-
43
- sig { returns(T::Array[String]) }
44
- def collect_load_path_files
45
- $LOAD_PATH.flat_map do |p|
46
- Dir.glob("**/*.rb", base: p)
47
- end.map! do |result|
48
- result.delete_suffix!(".rb")
49
- end
50
- end
51
-
52
- sig { params(label: String, node: SyntaxTree::TStringContent).returns(Interface::CompletionItem) }
53
- def build_completion(label, node)
54
- Interface::CompletionItem.new(
55
- label: label,
56
- text_edit: Interface::TextEdit.new(
57
- range: range_from_syntax_tree_node(node),
58
- new_text: label,
59
- ),
60
- kind: Constant::CompletionItemKind::REFERENCE,
61
- )
62
- end
63
- end
64
- end
65
- end
@@ -1,80 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- module Requests
6
- module Support
7
- class PrefixTree
8
- extend T::Sig
9
-
10
- sig { params(items: T::Array[String]).void }
11
- def initialize(items)
12
- @root = T.let(Node.new(""), Node)
13
-
14
- items.each do |item|
15
- insert(item)
16
- end
17
- end
18
-
19
- sig { params(prefix: String).returns(T::Array[String]) }
20
- def search(prefix)
21
- node = T.let(@root, Node)
22
-
23
- prefix.each_char do |char|
24
- snode = node.children[char]
25
- return [] unless snode
26
-
27
- node = snode
28
- end
29
-
30
- node.collect
31
- end
32
-
33
- private
34
-
35
- sig { params(item: String).void }
36
- def insert(item)
37
- node = T.let(@root, Node)
38
-
39
- item.each_char do |char|
40
- node = node.children[char] ||= Node.new(node.value + char)
41
- end
42
-
43
- node.leaf = true
44
- end
45
-
46
- class Node
47
- extend T::Sig
48
-
49
- sig { returns(T::Hash[String, Node]) }
50
- attr_reader :children
51
-
52
- sig { returns(String) }
53
- attr_reader :value
54
-
55
- sig { returns(T::Boolean) }
56
- attr_accessor :leaf
57
-
58
- sig { params(value: String).void }
59
- def initialize(value)
60
- @children = T.let({}, T::Hash[String, Node])
61
- @value = T.let(value, String)
62
- @leaf = T.let(false, T::Boolean)
63
- end
64
-
65
- sig { returns(T::Array[String]) }
66
- def collect
67
- result = T.let([], T::Array[String])
68
- result << value if leaf
69
-
70
- children.each_value do |node|
71
- result.concat(node.collect)
72
- end
73
-
74
- result
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end