ruby-lsp 0.14.6 → 0.16.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +1 -16
  4. data/exe/ruby-lsp-check +13 -22
  5. data/exe/ruby-lsp-doctor +9 -0
  6. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +14 -1
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +11 -23
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -0
  9. data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
  10. data/lib/ruby_indexer/test/configuration_test.rb +2 -11
  11. data/lib/ruby_lsp/addon.rb +18 -9
  12. data/lib/ruby_lsp/base_server.rb +147 -0
  13. data/lib/ruby_lsp/document.rb +0 -5
  14. data/lib/ruby_lsp/{requests/support/dependency_detector.rb → global_state.rb} +41 -9
  15. data/lib/ruby_lsp/internal.rb +4 -1
  16. data/lib/ruby_lsp/listeners/code_lens.rb +13 -9
  17. data/lib/ruby_lsp/listeners/completion.rb +13 -14
  18. data/lib/ruby_lsp/listeners/definition.rb +4 -3
  19. data/lib/ruby_lsp/listeners/document_symbol.rb +91 -3
  20. data/lib/ruby_lsp/listeners/hover.rb +6 -5
  21. data/lib/ruby_lsp/listeners/signature_help.rb +7 -4
  22. data/lib/ruby_lsp/load_sorbet.rb +62 -0
  23. data/lib/ruby_lsp/requests/code_lens.rb +3 -2
  24. data/lib/ruby_lsp/requests/completion.rb +15 -4
  25. data/lib/ruby_lsp/requests/completion_resolve.rb +56 -0
  26. data/lib/ruby_lsp/requests/definition.rb +11 -4
  27. data/lib/ruby_lsp/requests/diagnostics.rb +6 -12
  28. data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
  29. data/lib/ruby_lsp/requests/formatting.rb +7 -43
  30. data/lib/ruby_lsp/requests/hover.rb +4 -4
  31. data/lib/ruby_lsp/requests/request.rb +2 -0
  32. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  33. data/lib/ruby_lsp/requests/signature_help.rb +4 -3
  34. data/lib/ruby_lsp/requests/support/common.rb +16 -5
  35. data/lib/ruby_lsp/requests/support/formatter.rb +26 -0
  36. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +50 -0
  37. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
  38. data/lib/ruby_lsp/requests/support/{syntax_tree_formatting_runner.rb → syntax_tree_formatter.rb} +13 -3
  39. data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
  40. data/lib/ruby_lsp/requests.rb +3 -1
  41. data/lib/ruby_lsp/server.rb +770 -142
  42. data/lib/ruby_lsp/store.rb +0 -8
  43. data/lib/ruby_lsp/test_helper.rb +52 -0
  44. data/lib/ruby_lsp/utils.rb +68 -33
  45. metadata +11 -9
  46. data/lib/ruby_lsp/executor.rb +0 -614
  47. data/lib/ruby_lsp/requests/support/formatter_runner.rb +0 -18
  48. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -34
  49. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -35
@@ -1,9 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "ruby_lsp/requests/support/rubocop_formatting_runner"
5
- require "ruby_lsp/requests/support/syntax_tree_formatting_runner"
6
-
7
4
  module RubyLsp
8
5
  module Requests
9
6
  # ![Formatting symbol demo](../../formatting.gif)
@@ -26,47 +23,24 @@ module RubyLsp
26
23
  # end
27
24
  # ```
28
25
  class Formatting < Request
29
- class Error < StandardError; end
30
- class InvalidFormatter < StandardError; end
31
-
32
- @formatters = T.let({}, T::Hash[String, Support::FormatterRunner])
33
-
34
- class << self
35
- extend T::Sig
36
-
37
- sig { returns(T::Hash[String, Support::FormatterRunner]) }
38
- attr_reader :formatters
39
-
40
- sig { params(identifier: String, instance: Support::FormatterRunner).void }
41
- def register_formatter(identifier, instance)
42
- @formatters[identifier] = instance
43
- end
44
- end
45
-
46
- if defined?(Support::RuboCopFormattingRunner)
47
- register_formatter("rubocop", Support::RuboCopFormattingRunner.instance)
48
- end
49
-
50
- if defined?(Support::SyntaxTreeFormattingRunner)
51
- register_formatter("syntax_tree", Support::SyntaxTreeFormattingRunner.instance)
52
- end
53
-
54
26
  extend T::Sig
55
27
 
56
- sig { params(document: Document, formatter: String).void }
57
- def initialize(document, formatter: "auto")
28
+ class Error < StandardError; end
29
+
30
+ sig { params(global_state: GlobalState, document: Document).void }
31
+ def initialize(global_state, document)
58
32
  super()
59
33
  @document = document
34
+ @active_formatter = T.let(global_state.active_formatter, T.nilable(Support::Formatter))
60
35
  @uri = T.let(document.uri, URI::Generic)
61
- @formatter = formatter
62
36
  end
63
37
 
64
38
  sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
65
39
  def perform
66
- return if @formatter == "none"
40
+ return unless @active_formatter
67
41
  return if @document.syntax_error?
68
42
 
69
- formatted_text = formatted_file
43
+ formatted_text = @active_formatter.run_formatting(@uri, @document)
70
44
  return unless formatted_text
71
45
 
72
46
  size = @document.source.size
@@ -82,16 +56,6 @@ module RubyLsp
82
56
  ),
83
57
  ]
84
58
  end
85
-
86
- private
87
-
88
- sig { returns(T.nilable(String)) }
89
- def formatted_file
90
- formatter_runner = Formatting.formatters[@formatter]
91
- raise InvalidFormatter, "Formatter is not available: #{@formatter}" unless formatter_runner
92
-
93
- formatter_runner.run(@uri, @document)
94
- end
95
59
  end
96
60
  end
97
61
  end
@@ -33,13 +33,13 @@ module RubyLsp
33
33
  sig do
34
34
  params(
35
35
  document: Document,
36
- index: RubyIndexer::Index,
36
+ global_state: GlobalState,
37
37
  position: T::Hash[Symbol, T.untyped],
38
38
  dispatcher: Prism::Dispatcher,
39
39
  typechecker_enabled: T::Boolean,
40
40
  ).void
41
41
  end
42
- def initialize(document, index, position, dispatcher, typechecker_enabled)
42
+ def initialize(document, global_state, position, dispatcher, typechecker_enabled)
43
43
  super()
44
44
  @target = T.let(nil, T.nilable(Prism::Node))
45
45
  @target, parent, nesting = document.locate_node(
@@ -62,9 +62,9 @@ module RubyLsp
62
62
 
63
63
  uri = document.uri
64
64
  @response_builder = T.let(ResponseBuilders::Hover.new, ResponseBuilders::Hover)
65
- Listeners::Hover.new(@response_builder, uri, nesting, index, dispatcher, typechecker_enabled)
65
+ Listeners::Hover.new(@response_builder, global_state, uri, nesting, dispatcher, typechecker_enabled)
66
66
  Addon.addons.each do |addon|
67
- addon.create_hover_listener(@response_builder, nesting, index, dispatcher)
67
+ addon.create_hover_listener(@response_builder, nesting, dispatcher)
68
68
  end
69
69
 
70
70
  @dispatcher = dispatcher
@@ -8,6 +8,8 @@ module RubyLsp
8
8
  extend T::Sig
9
9
  extend T::Generic
10
10
 
11
+ class InvalidFormatter < StandardError; end
12
+
11
13
  abstract!
12
14
 
13
15
  sig { abstract.returns(T.anything) }
@@ -29,7 +29,7 @@ module RubyLsp
29
29
  sig { returns(Interface::SemanticTokensRegistrationOptions) }
30
30
  def provider
31
31
  Interface::SemanticTokensRegistrationOptions.new(
32
- document_selector: { scheme: "file", language: "ruby" },
32
+ document_selector: [{ language: "ruby" }],
33
33
  legend: Interface::SemanticTokensLegend.new(
34
34
  token_types: ResponseBuilders::SemanticHighlighting::TOKEN_TYPES.keys,
35
35
  token_modifiers: ResponseBuilders::SemanticHighlighting::TOKEN_MODIFIERS.keys,
@@ -42,13 +42,14 @@ module RubyLsp
42
42
  sig do
43
43
  params(
44
44
  document: Document,
45
- index: RubyIndexer::Index,
45
+ global_state: GlobalState,
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
50
  ).void
50
51
  end
51
- def initialize(document, index, position, context, dispatcher)
52
+ def initialize(document, global_state, position, context, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
52
53
  super()
53
54
  target, parent, nesting = document.locate_node(
54
55
  { line: position[:line], character: position[:character] },
@@ -60,7 +61,7 @@ module RubyLsp
60
61
  @target = T.let(target, T.nilable(Prism::Node))
61
62
  @dispatcher = dispatcher
62
63
  @response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp)
63
- Listeners::SignatureHelp.new(@response_builder, nesting, index, dispatcher)
64
+ Listeners::SignatureHelp.new(@response_builder, global_state, nesting, dispatcher, typechecker_enabled)
64
65
  end
65
66
 
66
67
  sig { override.returns(T.nilable(Interface::SignatureHelp)) }
@@ -86,13 +86,16 @@ module RubyLsp
86
86
  params(
87
87
  title: String,
88
88
  entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
89
+ max_entries: T.nilable(Integer),
89
90
  ).returns(T::Hash[Symbol, String])
90
91
  end
91
- def categorized_markdown_from_index_entries(title, entries)
92
+ def categorized_markdown_from_index_entries(title, entries, max_entries = nil)
92
93
  markdown_title = "```ruby\n#{title}\n```"
93
94
  definitions = []
94
95
  content = +""
95
- Array(entries).each do |entry|
96
+ entries = Array(entries)
97
+ entries_to_format = max_entries ? entries.take(max_entries) : entries
98
+ entries_to_format.each do |entry|
96
99
  loc = entry.location
97
100
 
98
101
  # We always handle locations as zero based. However, for file links in Markdown we need them to be one
@@ -108,9 +111,16 @@ module RubyLsp
108
111
  content << "\n\n#{entry.comments.join("\n")}" unless entry.comments.empty?
109
112
  end
110
113
 
114
+ additional_entries_text = if max_entries && entries.length > max_entries
115
+ additional = entries.length - max_entries
116
+ " | #{additional} other#{additional > 1 ? "s" : ""}"
117
+ else
118
+ ""
119
+ end
120
+
111
121
  {
112
122
  title: markdown_title,
113
- links: "**Definitions**: #{definitions.join(" | ")}",
123
+ links: "**Definitions**: #{definitions.join(" | ")}#{additional_entries_text}",
114
124
  documentation: content,
115
125
  }
116
126
  end
@@ -119,10 +129,11 @@ module RubyLsp
119
129
  params(
120
130
  title: String,
121
131
  entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
132
+ max_entries: T.nilable(Integer),
122
133
  ).returns(String)
123
134
  end
124
- def markdown_from_index_entries(title, entries)
125
- categorized_markdown = categorized_markdown_from_index_entries(title, entries)
135
+ def markdown_from_index_entries(title, entries, max_entries = nil)
136
+ categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
126
137
 
127
138
  <<~MARKDOWN.chomp
128
139
  #{categorized_markdown[:title]}
@@ -0,0 +1,26 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ module Support
7
+ module Formatter
8
+ extend T::Sig
9
+ extend T::Helpers
10
+
11
+ interface!
12
+
13
+ sig { abstract.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
14
+ def run_formatting(uri, document); end
15
+
16
+ sig do
17
+ abstract.params(
18
+ uri: URI::Generic,
19
+ document: Document,
20
+ ).returns(T.nilable(T::Array[Interface::Diagnostic]))
21
+ end
22
+ def run_diagnostic(uri, document); end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ return unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
5
+
6
+ require "singleton"
7
+
8
+ module RubyLsp
9
+ module Requests
10
+ module Support
11
+ class RuboCopFormatter
12
+ extend T::Sig
13
+ include Formatter
14
+ include Singleton
15
+
16
+ sig { void }
17
+ def initialize
18
+ @diagnostic_runner = T.let(RuboCopRunner.new, RuboCopRunner)
19
+ # -a is for "--auto-correct" (or "--autocorrect" on newer versions of RuboCop)
20
+ @format_runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
21
+ end
22
+
23
+ sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
24
+ def run_formatting(uri, document)
25
+ filename = T.must(uri.to_standardized_path || uri.opaque)
26
+
27
+ # Invoke RuboCop with just this file in `paths`
28
+ @format_runner.run(filename, document.source)
29
+ @format_runner.formatted_source
30
+ end
31
+
32
+ sig do
33
+ override.params(
34
+ uri: URI::Generic,
35
+ document: Document,
36
+ ).returns(T.nilable(T::Array[Interface::Diagnostic]))
37
+ end
38
+ def run_diagnostic(uri, document)
39
+ filename = T.must(uri.to_standardized_path || uri.opaque)
40
+ # Invoke RuboCop with just this file in `paths`
41
+ @diagnostic_runner.run(filename, document.source)
42
+
43
+ @diagnostic_runner.offenses.map do |offense|
44
+ Support::RuboCopDiagnostic.new(document, offense, uri).to_lsp_diagnostic
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -92,6 +92,10 @@ module RubyLsp
92
92
  @options[:stdin] = contents
93
93
 
94
94
  super([path])
95
+
96
+ # RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
97
+ # so here we re-raise in case RuboCop received an interrupt.
98
+ raise Interrupt if aborting?
95
99
  rescue RuboCop::Runner::InfiniteCorrectionLoop => error
96
100
  raise Formatting::Error, error.message
97
101
  rescue RuboCop::ValidationError => error
@@ -14,10 +14,10 @@ module RubyLsp
14
14
  module Requests
15
15
  module Support
16
16
  # :nodoc:
17
- class SyntaxTreeFormattingRunner
17
+ class SyntaxTreeFormatter
18
18
  extend T::Sig
19
19
  include Singleton
20
- include Support::FormatterRunner
20
+ include Support::Formatter
21
21
 
22
22
  sig { void }
23
23
  def initialize
@@ -33,12 +33,22 @@ module RubyLsp
33
33
  end
34
34
 
35
35
  sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
36
- def run(uri, document)
36
+ def run_formatting(uri, document)
37
37
  path = uri.to_standardized_path
38
38
  return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
39
39
 
40
40
  SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
41
41
  end
42
+
43
+ sig do
44
+ override.params(
45
+ uri: URI::Generic,
46
+ document: Document,
47
+ ).returns(T.nilable(T::Array[Interface::Diagnostic]))
48
+ end
49
+ def run_diagnostic(uri, document)
50
+ nil
51
+ end
42
52
  end
43
53
  end
44
54
  end
@@ -22,11 +22,12 @@ module RubyLsp
22
22
  extend T::Sig
23
23
  include Support::Common
24
24
 
25
- sig { params(query: T.nilable(String), index: RubyIndexer::Index).void }
26
- def initialize(query, index)
25
+ sig { params(global_state: GlobalState, query: T.nilable(String)).void }
26
+ def initialize(global_state, query)
27
27
  super()
28
+ @global_state = global_state
28
29
  @query = query
29
- @index = index
30
+ @index = T.let(global_state.index, RubyIndexer::Index)
30
31
  end
31
32
 
32
33
  sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
@@ -35,7 +36,7 @@ module RubyLsp
35
36
  # If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
36
37
  # we still return entries defined in gems to allow developers to jump directly to the source
37
38
  file_path = entry.file_path
38
- next if DependencyDetector.instance.typechecker && not_in_dependencies?(file_path)
39
+ next if @global_state.typechecker && not_in_dependencies?(file_path)
39
40
 
40
41
  # We should never show private symbols when searching the entire workspace
41
42
  next if entry.visibility == :private
@@ -18,6 +18,7 @@ module RubyLsp
18
18
  # - [DocumentHighlight](rdoc-ref:RubyLsp::Requests::DocumentHighlight)
19
19
  # - [InlayHint](rdoc-ref:RubyLsp::Requests::InlayHints)
20
20
  # - [Completion](rdoc-ref:RubyLsp::Requests::Completion)
21
+ # - [CompletionResolve](rdoc-ref:RubyLsp::Requests::CompletionResolve)
21
22
  # - [CodeLens](rdoc-ref:RubyLsp::Requests::CodeLens)
22
23
  # - [Definition](rdoc-ref:RubyLsp::Requests::Definition)
23
24
  # - [ShowSyntaxTree](rdoc-ref:RubyLsp::Requests::ShowSyntaxTree)
@@ -40,6 +41,7 @@ module RubyLsp
40
41
  autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
41
42
  autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
42
43
  autoload :Completion, "ruby_lsp/requests/completion"
44
+ autoload :CompletionResolve, "ruby_lsp/requests/completion_resolve"
43
45
  autoload :CodeLens, "ruby_lsp/requests/code_lens"
44
46
  autoload :Definition, "ruby_lsp/requests/definition"
45
47
  autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
@@ -54,7 +56,7 @@ module RubyLsp
54
56
  autoload :Sorbet, "ruby_lsp/requests/support/sorbet"
55
57
  autoload :RailsDocumentClient, "ruby_lsp/requests/support/rails_document_client"
56
58
  autoload :Common, "ruby_lsp/requests/support/common"
57
- autoload :FormatterRunner, "ruby_lsp/requests/support/formatter_runner"
59
+ autoload :Formatter, "ruby_lsp/requests/support/formatter"
58
60
  end
59
61
  end
60
62
  end