ruby-lsp 0.14.6 → 0.16.0

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