ruby-lsp 0.17.3 → 0.17.5

Sign up to get free protection for your applications and to get access to all the features.
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