ruby-lsp 0.17.3 → 0.17.5

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/VERSION +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +251 -100
  5. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +173 -114
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +337 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +43 -14
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +79 -3
  9. data/lib/ruby_indexer/test/index_test.rb +563 -29
  10. data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
  11. data/lib/ruby_indexer/test/method_test.rb +75 -25
  12. data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -2
  13. data/lib/ruby_indexer/test/test_case.rb +1 -5
  14. data/lib/ruby_lsp/addon.rb +13 -1
  15. data/lib/ruby_lsp/document.rb +50 -23
  16. data/lib/ruby_lsp/erb_document.rb +125 -0
  17. data/lib/ruby_lsp/global_state.rb +11 -4
  18. data/lib/ruby_lsp/internal.rb +3 -0
  19. data/lib/ruby_lsp/listeners/completion.rb +69 -34
  20. data/lib/ruby_lsp/listeners/definition.rb +34 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +14 -7
  22. data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
  23. data/lib/ruby_lsp/node_context.rb +6 -1
  24. data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -2
  25. data/lib/ruby_lsp/requests/completion.rb +6 -5
  26. data/lib/ruby_lsp/requests/completion_resolve.rb +7 -4
  27. data/lib/ruby_lsp/requests/definition.rb +4 -3
  28. data/lib/ruby_lsp/requests/formatting.rb +2 -0
  29. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
  30. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  31. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
  32. data/lib/ruby_lsp/requests/support/common.rb +19 -1
  33. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
  34. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +87 -0
  35. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
  36. data/lib/ruby_lsp/requests.rb +2 -0
  37. data/lib/ruby_lsp/ruby_document.rb +10 -0
  38. data/lib/ruby_lsp/server.rb +95 -26
  39. data/lib/ruby_lsp/store.rb +23 -8
  40. data/lib/ruby_lsp/test_helper.rb +3 -1
  41. data/lib/ruby_lsp/type_inferrer.rb +86 -0
  42. metadata +10 -6
@@ -40,7 +40,8 @@ module RubyLsp
40
40
  def perform
41
41
  # Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
42
42
  # a completion resolve request must always return the original completion item without modifying ANY fields
43
- # other than label details and documentation. If we modify anything, the completion behaviour might be broken.
43
+ # other than detail and documentation (NOT labelDetails). If we modify anything, the completion behaviour might
44
+ # be broken.
44
45
  #
45
46
  # For example, forgetting to return the `insertText` included in the original item will make the editor use the
46
47
  # `label` for the text edit instead
@@ -56,9 +57,11 @@ module RubyLsp
56
57
  end
57
58
  end
58
59
 
59
- @item[:labelDetails] = Interface::CompletionItemLabelDetails.new(
60
- description: entries.take(MAX_DOCUMENTATION_ENTRIES).map(&:file_name).join(","),
61
- )
60
+ first_entry = T.must(entries.first)
61
+
62
+ if first_entry.is_a?(RubyIndexer::Entry::Member)
63
+ label = "#{label}#{first_entry.decorated_parameters}"
64
+ end
62
65
 
63
66
  @item[:documentation] = Interface::MarkupContent.new(
64
67
  kind: "markdown",
@@ -42,8 +42,8 @@ module RubyLsp
42
42
  def initialize(document, global_state, position, dispatcher, typechecker_enabled)
43
43
  super()
44
44
  @response_builder = T.let(
45
- ResponseBuilders::CollectionResponseBuilder[Interface::Location].new,
46
- ResponseBuilders::CollectionResponseBuilder[Interface::Location],
45
+ ResponseBuilders::CollectionResponseBuilder[T.any(Interface::Location, Interface::LocationLink)].new,
46
+ ResponseBuilders::CollectionResponseBuilder[T.any(Interface::Location, Interface::LocationLink)],
47
47
  )
48
48
  @dispatcher = dispatcher
49
49
 
@@ -90,6 +90,7 @@ module RubyLsp
90
90
  Listeners::Definition.new(
91
91
  @response_builder,
92
92
  global_state,
93
+ document.language_id,
93
94
  document.uri,
94
95
  node_context,
95
96
  dispatcher,
@@ -104,7 +105,7 @@ module RubyLsp
104
105
  @target = T.let(target, T.nilable(Prism::Node))
105
106
  end
106
107
 
107
- sig { override.returns(T::Array[Interface::Location]) }
108
+ sig { override.returns(T::Array[T.any(Interface::Location, Interface::LocationLink)]) }
108
109
  def perform
109
110
  @dispatcher.dispatch_once(@target) if @target
110
111
  @response_builder.response
@@ -40,6 +40,8 @@ module RubyLsp
40
40
  return unless @active_formatter
41
41
  return if @document.syntax_error?
42
42
 
43
+ # We don't format erb documents yet
44
+
43
45
  formatted_text = @active_formatter.run_formatting(@uri, @document)
44
46
  return unless formatted_text
45
47
 
@@ -0,0 +1,88 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Prepare type hierarchy demo](../../prepare_type_hierarchy.gif)
7
+ #
8
+ # The [prepare type hierarchy
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareTypeHierarchy)
10
+ # displays the list of ancestors (supertypes) and descendants (subtypes) for the selected type.
11
+ #
12
+ # Currently only supports supertypes due to a limitation of the index.
13
+ #
14
+ # # Example
15
+ #
16
+ # ```ruby
17
+ # class Foo; end
18
+ # class Bar < Foo; end
19
+ #
20
+ # puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
21
+ # ```
22
+ class PrepareTypeHierarchy < Request
23
+ extend T::Sig
24
+
25
+ include Support::Common
26
+
27
+ class << self
28
+ extend T::Sig
29
+
30
+ sig { returns(Interface::TypeHierarchyOptions) }
31
+ def provider
32
+ Interface::TypeHierarchyOptions.new
33
+ end
34
+ end
35
+
36
+ sig do
37
+ params(
38
+ document: Document,
39
+ index: RubyIndexer::Index,
40
+ position: T::Hash[Symbol, T.untyped],
41
+ ).void
42
+ end
43
+ def initialize(document, index, position)
44
+ super()
45
+
46
+ @document = document
47
+ @index = index
48
+ @position = position
49
+ end
50
+
51
+ sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
52
+ def perform
53
+ context = @document.locate_node(
54
+ @position,
55
+ node_types: [
56
+ Prism::ConstantReadNode,
57
+ Prism::ConstantWriteNode,
58
+ Prism::ConstantPathNode,
59
+ ],
60
+ )
61
+
62
+ node = context.node
63
+ parent = context.parent
64
+ return unless node && parent
65
+
66
+ target = determine_target(node, parent, @position)
67
+ entries = @index.resolve(target.slice, context.nesting)
68
+ return unless entries
69
+
70
+ # While the spec allows for multiple entries, VSCode seems to only support one
71
+ # We'll just return the first one for now
72
+ first_entry = T.must(entries.first)
73
+
74
+ range = range_from_location(first_entry.location)
75
+
76
+ [
77
+ Interface::TypeHierarchyItem.new(
78
+ name: first_entry.name,
79
+ kind: kind_for_entry(first_entry),
80
+ uri: URI::Generic.from_path(path: first_entry.file_path).to_s,
81
+ range: range,
82
+ selection_range: range,
83
+ ),
84
+ ]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -34,7 +34,7 @@ module RubyLsp
34
34
  sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
35
35
  def perform
36
36
  # [node, parent]
37
- queue = [[@document.tree, nil]]
37
+ queue = [[@document.parse_result.value, nil]]
38
38
 
39
39
  until queue.empty?
40
40
  node, parent = queue.shift
@@ -25,6 +25,7 @@ module RubyLsp
25
25
  super()
26
26
  @document = document
27
27
  @range = range
28
+ @tree = T.let(document.parse_result.value, Prism::ProgramNode)
28
29
  end
29
30
 
30
31
  sig { override.returns(String) }
@@ -32,7 +33,7 @@ module RubyLsp
32
33
  return ast_for_range if @range
33
34
 
34
35
  output_string = +""
35
- PP.pp(@document.tree, output_string)
36
+ PP.pp(@tree, output_string)
36
37
  output_string
37
38
  end
38
39
 
@@ -46,7 +47,7 @@ module RubyLsp
46
47
  start_char = scanner.find_char_position(range[:start])
47
48
  end_char = scanner.find_char_position(range[:end])
48
49
 
49
- queue = @document.tree.statements.body.dup
50
+ queue = @tree.statements.body.dup
50
51
  found_nodes = []
51
52
 
52
53
  until queue.empty?
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  )
27
27
  end
28
28
 
29
- sig { params(location: Prism::Location).returns(Interface::Range) }
29
+ sig { params(location: T.any(Prism::Location, RubyIndexer::Location)).returns(Interface::Range) }
30
30
  def range_from_location(location)
31
31
  Interface::Range.new(
32
32
  start: Interface::Position.new(
@@ -186,6 +186,24 @@ module RubyLsp
186
186
  current = current.parent
187
187
  end
188
188
  end
189
+
190
+ sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
191
+ def kind_for_entry(entry)
192
+ case entry
193
+ when RubyIndexer::Entry::Class
194
+ Constant::SymbolKind::CLASS
195
+ when RubyIndexer::Entry::Module
196
+ Constant::SymbolKind::NAMESPACE
197
+ when RubyIndexer::Entry::Constant
198
+ Constant::SymbolKind::CONSTANT
199
+ when RubyIndexer::Entry::Method
200
+ entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
201
+ when RubyIndexer::Entry::Accessor
202
+ Constant::SymbolKind::PROPERTY
203
+ when RubyIndexer::Entry::InstanceVariable
204
+ Constant::SymbolKind::FIELD
205
+ end
206
+ end
189
207
  end
190
208
  end
191
209
  end
@@ -42,7 +42,7 @@ module RubyLsp
42
42
  def to_lsp_code_actions
43
43
  code_actions = []
44
44
 
45
- code_actions << autocorrect_action if @offense.correctable?
45
+ code_actions << autocorrect_action if correctable?
46
46
  code_actions << disable_line_action
47
47
 
48
48
  code_actions
@@ -70,7 +70,7 @@ module RubyLsp
70
70
  ),
71
71
  ),
72
72
  data: {
73
- correctable: @offense.correctable?,
73
+ correctable: correctable?,
74
74
  code_actions: to_lsp_code_actions,
75
75
  },
76
76
  )
@@ -81,7 +81,7 @@ module RubyLsp
81
81
  sig { returns(String) }
82
82
  def message
83
83
  message = @offense.message
84
- message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
84
+ message += "\n\nThis offense is not auto-correctable.\n" unless correctable?
85
85
  message
86
86
  end
87
87
 
@@ -115,7 +115,7 @@ module RubyLsp
115
115
  uri: @uri.to_s,
116
116
  version: nil,
117
117
  ),
118
- edits: @offense.correctable? ? offense_replacements : [],
118
+ edits: correctable? ? offense_replacements : [],
119
119
  ),
120
120
  ],
121
121
  ),
@@ -193,6 +193,14 @@ module RubyLsp
193
193
  line.length
194
194
  end
195
195
  end
196
+
197
+ # When `RuboCop::LSP.enable` is called, contextual autocorrect will not offer itself
198
+ # as `correctable?` to prevent annoying changes while typing. Instead check if
199
+ # a corrector is present. If it is, then that means some code transformation can be applied.
200
+ sig { returns(T::Boolean) }
201
+ def correctable?
202
+ !@offense.corrector.nil?
203
+ end
196
204
  end
197
205
  end
198
206
  end
@@ -0,0 +1,87 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Type hierarchy supertypes demo](../../type_hierarchy_supertypes.gif)
7
+ #
8
+ # The [type hierarchy supertypes
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#typeHierarchy_supertypes)
10
+ # displays the list of ancestors (supertypes) for the selected type.
11
+ #
12
+ # # Example
13
+ #
14
+ # ```ruby
15
+ # class Foo; end
16
+ # class Bar < Foo; end
17
+ #
18
+ # puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
19
+ # ```
20
+ class TypeHierarchySupertypes < Request
21
+ extend T::Sig
22
+
23
+ include Support::Common
24
+
25
+ sig { params(index: RubyIndexer::Index, item: T::Hash[Symbol, T.untyped]).void }
26
+ def initialize(index, item)
27
+ super()
28
+
29
+ @index = index
30
+ @item = item
31
+ end
32
+
33
+ sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
34
+ def perform
35
+ name = @item[:name]
36
+ entries = @index[name]
37
+
38
+ parents = T.let(Set.new, T::Set[RubyIndexer::Entry::Namespace])
39
+ return unless entries&.any?
40
+
41
+ entries.each do |entry|
42
+ next unless entry.is_a?(RubyIndexer::Entry::Namespace)
43
+
44
+ if entry.is_a?(RubyIndexer::Entry::Class)
45
+ parent_class_name = entry.parent_class
46
+ if parent_class_name
47
+ resolved_parent_entries = @index.resolve(parent_class_name, entry.nesting)
48
+ resolved_parent_entries&.each do |entry|
49
+ next unless entry.is_a?(RubyIndexer::Entry::Class)
50
+
51
+ parents << entry
52
+ end
53
+ end
54
+ end
55
+
56
+ entry.mixin_operations.each do |mixin_operation|
57
+ mixin_name = mixin_operation.module_name
58
+ resolved_mixin_entries = @index.resolve(mixin_name, entry.nesting)
59
+ next unless resolved_mixin_entries
60
+
61
+ resolved_mixin_entries.each do |mixin_entry|
62
+ next unless mixin_entry.is_a?(RubyIndexer::Entry::Module)
63
+
64
+ parents << mixin_entry
65
+ end
66
+ end
67
+ end
68
+
69
+ parents.map { |entry| hierarchy_item(entry) }
70
+ end
71
+
72
+ private
73
+
74
+ sig { params(entry: RubyIndexer::Entry).returns(Interface::TypeHierarchyItem) }
75
+ def hierarchy_item(entry)
76
+ Interface::TypeHierarchyItem.new(
77
+ name: entry.name,
78
+ kind: kind_for_entry(entry),
79
+ uri: URI::Generic.from_path(path: entry.file_path).to_s,
80
+ range: range_from_location(entry.location),
81
+ selection_range: range_from_location(entry.name_location),
82
+ detail: entry.file_name,
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
@@ -52,7 +52,7 @@ module RubyLsp
52
52
 
53
53
  Interface::WorkspaceSymbol.new(
54
54
  name: entry.name,
55
- container_name: T.must(container).join("::"),
55
+ container_name: container.join("::"),
56
56
  kind: kind,
57
57
  location: Interface::Location.new(
58
58
  uri: URI::Generic.from_path(path: file_path).to_s,
@@ -64,26 +64,6 @@ module RubyLsp
64
64
  )
65
65
  end
66
66
  end
67
-
68
- private
69
-
70
- sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
71
- def kind_for_entry(entry)
72
- case entry
73
- when RubyIndexer::Entry::Class
74
- Constant::SymbolKind::CLASS
75
- when RubyIndexer::Entry::Module
76
- Constant::SymbolKind::NAMESPACE
77
- when RubyIndexer::Entry::Constant
78
- Constant::SymbolKind::CONSTANT
79
- when RubyIndexer::Entry::Method
80
- entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
81
- when RubyIndexer::Entry::Accessor
82
- Constant::SymbolKind::PROPERTY
83
- when RubyIndexer::Entry::InstanceVariable
84
- Constant::SymbolKind::FIELD
85
- end
86
- end
87
67
  end
88
68
  end
89
69
  end
@@ -47,6 +47,8 @@ module RubyLsp
47
47
  autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
48
48
  autoload :WorkspaceSymbol, "ruby_lsp/requests/workspace_symbol"
49
49
  autoload :SignatureHelp, "ruby_lsp/requests/signature_help"
50
+ autoload :PrepareTypeHierarchy, "ruby_lsp/requests/prepare_type_hierarchy"
51
+ autoload :TypeHierarchySupertypes, "ruby_lsp/requests/type_hierarchy_supertypes"
50
52
 
51
53
  # :nodoc:
52
54
  module Support
@@ -10,5 +10,15 @@ module RubyLsp
10
10
  @needs_parsing = false
11
11
  @parse_result = Prism.parse(@source)
12
12
  end
13
+
14
+ sig { override.returns(T::Boolean) }
15
+ def syntax_error?
16
+ @parse_result.failure?
17
+ end
18
+
19
+ sig { override.returns(LanguageId) }
20
+ def language_id
21
+ LanguageId::Ruby
22
+ end
13
23
  end
14
24
  end