ruby-lsp 0.17.4 → 0.17.13

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +26 -1
  5. data/exe/ruby-lsp-check +1 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +74 -43
  7. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +26 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +147 -29
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +383 -79
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +195 -61
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -8
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +71 -3
  13. data/lib/ruby_indexer/test/configuration_test.rb +1 -1
  14. data/lib/ruby_indexer/test/constant_test.rb +17 -17
  15. data/lib/ruby_indexer/test/enhancements_test.rb +197 -0
  16. data/lib/ruby_indexer/test/index_test.rb +367 -17
  17. data/lib/ruby_indexer/test/method_test.rb +58 -25
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +297 -0
  19. data/lib/ruby_indexer/test/test_case.rb +1 -5
  20. data/lib/ruby_lsp/addon.rb +22 -5
  21. data/lib/ruby_lsp/base_server.rb +8 -3
  22. data/lib/ruby_lsp/document.rb +27 -46
  23. data/lib/ruby_lsp/erb_document.rb +125 -0
  24. data/lib/ruby_lsp/global_state.rb +47 -19
  25. data/lib/ruby_lsp/internal.rb +2 -0
  26. data/lib/ruby_lsp/listeners/completion.rb +161 -57
  27. data/lib/ruby_lsp/listeners/definition.rb +91 -27
  28. data/lib/ruby_lsp/listeners/document_highlight.rb +5 -1
  29. data/lib/ruby_lsp/listeners/hover.rb +61 -19
  30. data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
  31. data/lib/ruby_lsp/node_context.rb +65 -5
  32. data/lib/ruby_lsp/requests/code_action_resolve.rb +107 -9
  33. data/lib/ruby_lsp/requests/code_actions.rb +11 -2
  34. data/lib/ruby_lsp/requests/completion.rb +4 -4
  35. data/lib/ruby_lsp/requests/completion_resolve.rb +14 -9
  36. data/lib/ruby_lsp/requests/definition.rb +18 -8
  37. data/lib/ruby_lsp/requests/diagnostics.rb +6 -5
  38. data/lib/ruby_lsp/requests/document_symbol.rb +2 -7
  39. data/lib/ruby_lsp/requests/folding_ranges.rb +6 -2
  40. data/lib/ruby_lsp/requests/formatting.rb +15 -0
  41. data/lib/ruby_lsp/requests/hover.rb +5 -5
  42. data/lib/ruby_lsp/requests/on_type_formatting.rb +6 -4
  43. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  44. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
  45. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  46. data/lib/ruby_lsp/requests/support/common.rb +11 -2
  47. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +2 -6
  48. data/lib/ruby_lsp/ruby_document.rb +74 -0
  49. data/lib/ruby_lsp/server.rb +129 -54
  50. data/lib/ruby_lsp/store.rb +33 -9
  51. data/lib/ruby_lsp/test_helper.rb +3 -1
  52. data/lib/ruby_lsp/type_inferrer.rb +61 -25
  53. data/lib/ruby_lsp/utils.rb +13 -0
  54. metadata +9 -8
  55. data/exe/ruby-lsp-doctor +0 -23
@@ -34,14 +34,9 @@ module RubyLsp
34
34
  class << self
35
35
  extend T::Sig
36
36
 
37
- sig { returns(Interface::DocumentSymbolClientCapabilities) }
37
+ sig { returns(Interface::DocumentSymbolOptions) }
38
38
  def provider
39
- Interface::DocumentSymbolClientCapabilities.new(
40
- hierarchical_document_symbol_support: true,
41
- symbol_kind: {
42
- value_set: (Constant::SymbolKind::FILE..Constant::SymbolKind::TYPE_PARAMETER).to_a,
43
- },
44
- )
39
+ Interface::DocumentSymbolOptions.new
45
40
  end
46
41
  end
47
42
 
@@ -23,9 +23,13 @@ module RubyLsp
23
23
  class << self
24
24
  extend T::Sig
25
25
 
26
- sig { returns(Interface::FoldingRangeClientCapabilities) }
26
+ sig { returns(Interface::FoldingRangeRegistrationOptions) }
27
27
  def provider
28
- Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
28
+ Interface::FoldingRangeRegistrationOptions.new(
29
+ document_selector: [
30
+ Interface::DocumentFilter.new(language: "ruby"),
31
+ ],
32
+ )
29
33
  end
30
34
  end
31
35
 
@@ -27,6 +27,19 @@ module RubyLsp
27
27
 
28
28
  class Error < StandardError; end
29
29
 
30
+ class << self
31
+ extend T::Sig
32
+
33
+ sig { returns(Interface::DocumentFormattingRegistrationOptions) }
34
+ def provider
35
+ Interface::DocumentFormattingRegistrationOptions.new(
36
+ document_selector: [
37
+ Interface::DocumentFilter.new(language: "ruby"),
38
+ ],
39
+ )
40
+ end
41
+ end
42
+
30
43
  sig { params(global_state: GlobalState, document: Document).void }
31
44
  def initialize(global_state, document)
32
45
  super()
@@ -40,6 +53,8 @@ module RubyLsp
40
53
  return unless @active_formatter
41
54
  return if @document.syntax_error?
42
55
 
56
+ # We don't format erb documents yet
57
+
43
58
  formatted_text = @active_formatter.run_formatting(@uri, @document)
44
59
  return unless formatted_text
45
60
 
@@ -22,9 +22,9 @@ module RubyLsp
22
22
  class << self
23
23
  extend T::Sig
24
24
 
25
- sig { returns(Interface::HoverClientCapabilities) }
25
+ sig { returns(Interface::HoverOptions) }
26
26
  def provider
27
- Interface::HoverClientCapabilities.new(dynamic_registration: false)
27
+ Interface::HoverOptions.new
28
28
  end
29
29
  end
30
30
 
@@ -36,10 +36,10 @@ module RubyLsp
36
36
  global_state: GlobalState,
37
37
  position: T::Hash[Symbol, T.untyped],
38
38
  dispatcher: Prism::Dispatcher,
39
- typechecker_enabled: T::Boolean,
39
+ sorbet_level: RubyDocument::SorbetLevel,
40
40
  ).void
41
41
  end
42
- def initialize(document, global_state, position, dispatcher, typechecker_enabled)
42
+ def initialize(document, global_state, position, dispatcher, sorbet_level)
43
43
  super()
44
44
  node_context = document.locate_node(position, node_types: Listeners::Hover::ALLOWED_TARGETS)
45
45
  target = node_context.node
@@ -65,7 +65,7 @@ module RubyLsp
65
65
  @target = T.let(target, T.nilable(Prism::Node))
66
66
  uri = document.uri
67
67
  @response_builder = T.let(ResponseBuilders::Hover.new, ResponseBuilders::Hover)
68
- Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, typechecker_enabled)
68
+ Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level)
69
69
  Addon.addons.each do |addon|
70
70
  addon.create_hover_listener(@response_builder, node_context, dispatcher)
71
71
  end
@@ -21,9 +21,10 @@ module RubyLsp
21
21
  class << self
22
22
  extend T::Sig
23
23
 
24
- sig { returns(Interface::DocumentOnTypeFormattingOptions) }
24
+ sig { returns(Interface::DocumentOnTypeFormattingRegistrationOptions) }
25
25
  def provider
26
- Interface::DocumentOnTypeFormattingOptions.new(
26
+ Interface::DocumentOnTypeFormattingRegistrationOptions.new(
27
+ document_selector: [Interface::DocumentFilter.new(language: "ruby")],
27
28
  first_trigger_character: "{",
28
29
  more_trigger_character: ["\n", "|", "d"],
29
30
  )
@@ -32,8 +33,9 @@ module RubyLsp
32
33
 
33
34
  END_REGEXES = T.let(
34
35
  [
35
- /\b(if|unless|for|while|class|module|until|def|case)\b.*/,
36
- /.*\s\bdo\b/,
36
+ /\b(if|unless|for|while|until)\b($|\s|\()/,
37
+ /\b(class|module|def|case)\b($|\s)/,
38
+ /.*\s\bdo\b($|\s)/,
37
39
  ],
38
40
  T::Array[Regexp],
39
41
  )
@@ -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?
@@ -46,10 +46,10 @@ module RubyLsp
46
46
  position: T::Hash[Symbol, T.untyped],
47
47
  context: T.nilable(T::Hash[Symbol, T.untyped]),
48
48
  dispatcher: Prism::Dispatcher,
49
- typechecker_enabled: T::Boolean,
49
+ sorbet_level: RubyDocument::SorbetLevel,
50
50
  ).void
51
51
  end
52
- def initialize(document, global_state, position, context, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
52
+ def initialize(document, global_state, position, context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
53
53
  super()
54
54
  node_context = document.locate_node(
55
55
  { line: position[:line], character: position[:character] },
@@ -61,7 +61,7 @@ module RubyLsp
61
61
  @target = T.let(target, T.nilable(Prism::Node))
62
62
  @dispatcher = dispatcher
63
63
  @response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp)
64
- Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher, typechecker_enabled)
64
+ Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher, sorbet_level)
65
65
  end
66
66
 
67
67
  sig { override.returns(T.nilable(Interface::SignatureHelp)) }
@@ -130,13 +130,17 @@ module RubyLsp
130
130
  title: String,
131
131
  entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
132
132
  max_entries: T.nilable(Integer),
133
+ extra_links: T.nilable(String),
133
134
  ).returns(String)
134
135
  end
135
- def markdown_from_index_entries(title, entries, max_entries = nil)
136
+ def markdown_from_index_entries(title, entries, max_entries = nil, extra_links: nil)
136
137
  categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
137
138
 
139
+ markdown = +(categorized_markdown[:title] || "")
140
+ markdown << "\n\n#{extra_links}" if extra_links
141
+
138
142
  <<~MARKDOWN.chomp
139
- #{categorized_markdown[:title]}
143
+ #{markdown}
140
144
 
141
145
  #{categorized_markdown[:links]}
142
146
 
@@ -204,6 +208,11 @@ module RubyLsp
204
208
  Constant::SymbolKind::FIELD
205
209
  end
206
210
  end
211
+
212
+ sig { params(sorbet_level: RubyDocument::SorbetLevel).returns(T::Boolean) }
213
+ def sorbet_level_true_or_higher?(sorbet_level)
214
+ sorbet_level == RubyDocument::SorbetLevel::True || sorbet_level == RubyDocument::SorbetLevel::Strict
215
+ end
207
216
  end
208
217
  end
209
218
  end
@@ -54,8 +54,6 @@ module RubyLsp
54
54
  end
55
55
 
56
56
  entry.mixin_operations.each do |mixin_operation|
57
- next if mixin_operation.is_a?(RubyIndexer::Entry::Extend)
58
-
59
57
  mixin_name = mixin_operation.module_name
60
58
  resolved_mixin_entries = @index.resolve(mixin_name, entry.nesting)
61
59
  next unless resolved_mixin_entries
@@ -75,14 +73,12 @@ module RubyLsp
75
73
 
76
74
  sig { params(entry: RubyIndexer::Entry).returns(Interface::TypeHierarchyItem) }
77
75
  def hierarchy_item(entry)
78
- range = range_from_location(entry.location)
79
-
80
76
  Interface::TypeHierarchyItem.new(
81
77
  name: entry.name,
82
78
  kind: kind_for_entry(entry),
83
79
  uri: URI::Generic.from_path(path: entry.file_path).to_s,
84
- range: range,
85
- selection_range: range,
80
+ range: range_from_location(entry.location),
81
+ selection_range: range_from_location(entry.name_location),
86
82
  detail: entry.file_name,
87
83
  )
88
84
  end
@@ -3,6 +3,16 @@
3
3
 
4
4
  module RubyLsp
5
5
  class RubyDocument < Document
6
+ class SorbetLevel < T::Enum
7
+ enums do
8
+ None = new("none")
9
+ Ignore = new("ignore")
10
+ False = new("false")
11
+ True = new("true")
12
+ Strict = new("strict")
13
+ end
14
+ end
15
+
6
16
  sig { override.returns(Prism::ParseResult) }
7
17
  def parse
8
18
  return @parse_result unless @needs_parsing
@@ -10,5 +20,69 @@ module RubyLsp
10
20
  @needs_parsing = false
11
21
  @parse_result = Prism.parse(@source)
12
22
  end
23
+
24
+ sig { override.returns(T::Boolean) }
25
+ def syntax_error?
26
+ @parse_result.failure?
27
+ end
28
+
29
+ sig { override.returns(LanguageId) }
30
+ def language_id
31
+ LanguageId::Ruby
32
+ end
33
+
34
+ sig { returns(SorbetLevel) }
35
+ def sorbet_level
36
+ sigil = parse_result.magic_comments.find do |comment|
37
+ comment.key == "typed"
38
+ end&.value
39
+
40
+ case sigil
41
+ when "ignore"
42
+ SorbetLevel::Ignore
43
+ when "false"
44
+ SorbetLevel::False
45
+ when "true"
46
+ SorbetLevel::True
47
+ when "strict", "strong"
48
+ SorbetLevel::Strict
49
+ else
50
+ SorbetLevel::None
51
+ end
52
+ end
53
+
54
+ sig do
55
+ params(
56
+ range: T::Hash[Symbol, T.untyped],
57
+ node_types: T::Array[T.class_of(Prism::Node)],
58
+ ).returns(T.nilable(Prism::Node))
59
+ end
60
+ def locate_first_within_range(range, node_types: [])
61
+ scanner = create_scanner
62
+ start_position = scanner.find_char_position(range[:start])
63
+ end_position = scanner.find_char_position(range[:end])
64
+ desired_range = (start_position...end_position)
65
+ queue = T.let(@parse_result.value.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
66
+
67
+ until queue.empty?
68
+ candidate = queue.shift
69
+
70
+ # Skip nil child nodes
71
+ next if candidate.nil?
72
+
73
+ # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
74
+ # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
75
+ # sibling
76
+ T.unsafe(queue).unshift(*candidate.child_nodes)
77
+
78
+ # Skip if the current node doesn't cover the desired position
79
+ loc = candidate.location
80
+
81
+ if desired_range.cover?(loc.start_offset...loc.end_offset) &&
82
+ (node_types.empty? || node_types.any? { |type| candidate.class == type })
83
+ return candidate
84
+ end
85
+ end
86
+ end
13
87
  end
14
88
  end