ruby-lsp 0.14.6 → 0.16.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +1 -16
  5. data/exe/ruby-lsp-check +13 -22
  6. data/exe/ruby-lsp-doctor +9 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +14 -1
  8. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +11 -23
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +32 -8
  10. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +26 -0
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
  13. data/lib/ruby_indexer/test/configuration_test.rb +2 -11
  14. data/lib/ruby_lsp/addon.rb +18 -9
  15. data/lib/ruby_lsp/base_server.rb +149 -0
  16. data/lib/ruby_lsp/document.rb +6 -11
  17. data/lib/ruby_lsp/global_state.rb +180 -0
  18. data/lib/ruby_lsp/internal.rb +4 -1
  19. data/lib/ruby_lsp/listeners/code_lens.rb +22 -13
  20. data/lib/ruby_lsp/listeners/completion.rb +13 -14
  21. data/lib/ruby_lsp/listeners/definition.rb +14 -6
  22. data/lib/ruby_lsp/listeners/document_symbol.rb +91 -3
  23. data/lib/ruby_lsp/listeners/hover.rb +6 -5
  24. data/lib/ruby_lsp/listeners/signature_help.rb +7 -4
  25. data/lib/ruby_lsp/load_sorbet.rb +62 -0
  26. data/lib/ruby_lsp/requests/code_lens.rb +3 -2
  27. data/lib/ruby_lsp/requests/completion.rb +15 -4
  28. data/lib/ruby_lsp/requests/completion_resolve.rb +56 -0
  29. data/lib/ruby_lsp/requests/definition.rb +11 -4
  30. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  31. data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
  32. data/lib/ruby_lsp/requests/formatting.rb +7 -43
  33. data/lib/ruby_lsp/requests/hover.rb +4 -4
  34. data/lib/ruby_lsp/requests/request.rb +2 -0
  35. data/lib/ruby_lsp/requests/semantic_highlighting.rb +7 -4
  36. data/lib/ruby_lsp/requests/signature_help.rb +4 -3
  37. data/lib/ruby_lsp/requests/support/common.rb +16 -5
  38. data/lib/ruby_lsp/requests/support/formatter.rb +26 -0
  39. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +1 -1
  40. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +47 -0
  41. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
  42. data/lib/ruby_lsp/requests/support/{syntax_tree_formatting_runner.rb → syntax_tree_formatter.rb} +13 -6
  43. data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
  44. data/lib/ruby_lsp/requests.rb +3 -1
  45. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +36 -13
  46. data/lib/ruby_lsp/server.rb +763 -142
  47. data/lib/ruby_lsp/setup_bundler.rb +13 -1
  48. data/lib/ruby_lsp/store.rb +3 -15
  49. data/lib/ruby_lsp/test_helper.rb +52 -0
  50. data/lib/ruby_lsp/utils.rb +68 -33
  51. metadata +16 -13
  52. data/lib/ruby_lsp/executor.rb +0 -614
  53. data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -93
  54. data/lib/ruby_lsp/requests/support/formatter_runner.rb +0 -18
  55. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -34
  56. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -35
@@ -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
@@ -163,7 +163,7 @@ module RubyLsp
163
163
 
164
164
  sig { params(line: String).returns(Integer) }
165
165
  def length_of_line(line)
166
- if @document.encoding == Constant::PositionEncodingKind::UTF16
166
+ if @document.encoding == Encoding::UTF_16LE
167
167
  line_length = 0
168
168
  line.codepoints.each do |codepoint|
169
169
  line_length += 1
@@ -0,0 +1,47 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ return unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
5
+
6
+ module RubyLsp
7
+ module Requests
8
+ module Support
9
+ class RuboCopFormatter
10
+ extend T::Sig
11
+ include Formatter
12
+
13
+ sig { void }
14
+ def initialize
15
+ @diagnostic_runner = T.let(RuboCopRunner.new, RuboCopRunner)
16
+ # -a is for "--auto-correct" (or "--autocorrect" on newer versions of RuboCop)
17
+ @format_runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
18
+ end
19
+
20
+ sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
21
+ def run_formatting(uri, document)
22
+ filename = T.must(uri.to_standardized_path || uri.opaque)
23
+
24
+ # Invoke RuboCop with just this file in `paths`
25
+ @format_runner.run(filename, document.source)
26
+ @format_runner.formatted_source
27
+ end
28
+
29
+ sig do
30
+ override.params(
31
+ uri: URI::Generic,
32
+ document: Document,
33
+ ).returns(T.nilable(T::Array[Interface::Diagnostic]))
34
+ end
35
+ def run_diagnostic(uri, document)
36
+ filename = T.must(uri.to_standardized_path || uri.opaque)
37
+ # Invoke RuboCop with just this file in `paths`
38
+ @diagnostic_runner.run(filename, document.source)
39
+
40
+ @diagnostic_runner.offenses.map do |offense|
41
+ Support::RuboCopDiagnostic.new(document, offense, uri).to_lsp_diagnostic
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ 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
@@ -8,16 +8,13 @@ rescue LoadError
8
8
  return
9
9
  end
10
10
 
11
- require "singleton"
12
-
13
11
  module RubyLsp
14
12
  module Requests
15
13
  module Support
16
14
  # :nodoc:
17
- class SyntaxTreeFormattingRunner
15
+ class SyntaxTreeFormatter
18
16
  extend T::Sig
19
- include Singleton
20
- include Support::FormatterRunner
17
+ include Support::Formatter
21
18
 
22
19
  sig { void }
23
20
  def initialize
@@ -33,12 +30,22 @@ module RubyLsp
33
30
  end
34
31
 
35
32
  sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
36
- def run(uri, document)
33
+ def run_formatting(uri, document)
37
34
  path = uri.to_standardized_path
38
35
  return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
39
36
 
40
37
  SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
41
38
  end
39
+
40
+ sig do
41
+ override.params(
42
+ uri: URI::Generic,
43
+ document: Document,
44
+ ).returns(T.nilable(T::Array[Interface::Diagnostic]))
45
+ end
46
+ def run_diagnostic(uri, document)
47
+ nil
48
+ end
42
49
  end
43
50
  end
44
51
  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
@@ -55,19 +55,21 @@ module RubyLsp
55
55
 
56
56
  ResponseType = type_member { { fixed: Interface::SemanticTokens } }
57
57
 
58
- sig { void }
59
- def initialize
60
- super
58
+ sig { params(encoding: Encoding).void }
59
+ def initialize(encoding)
60
+ super()
61
+ @encoding = encoding
61
62
  @stack = T.let([], T::Array[SemanticToken])
62
63
  end
63
64
 
64
65
  sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
65
66
  def add_token(location, type, modifiers = [])
66
- length = location.end_offset - location.start_offset
67
+ length = location.end_code_units_offset(@encoding) - location.start_code_units_offset(@encoding)
67
68
  modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
68
69
  @stack.push(
69
70
  SemanticToken.new(
70
- location: location,
71
+ start_line: location.start_line,
72
+ start_code_unit_column: location.start_code_units_column(@encoding),
71
73
  length: length,
72
74
  type: T.must(TOKEN_TYPES[type]),
73
75
  modifier: modifiers_indices,
@@ -75,6 +77,15 @@ module RubyLsp
75
77
  )
76
78
  end
77
79
 
80
+ sig { params(location: Prism::Location).returns(T::Boolean) }
81
+ def last_token_matches?(location)
82
+ token = @stack.last
83
+ return false unless token
84
+
85
+ token.start_line == location.start_line &&
86
+ token.start_code_unit_column == location.start_code_units_column(@encoding)
87
+ end
88
+
78
89
  sig { returns(T.nilable(SemanticToken)) }
79
90
  def last
80
91
  @stack.last
@@ -88,8 +99,11 @@ module RubyLsp
88
99
  class SemanticToken
89
100
  extend T::Sig
90
101
 
91
- sig { returns(Prism::Location) }
92
- attr_reader :location
102
+ sig { returns(Integer) }
103
+ attr_reader :start_line
104
+
105
+ sig { returns(Integer) }
106
+ attr_reader :start_code_unit_column
93
107
 
94
108
  sig { returns(Integer) }
95
109
  attr_reader :length
@@ -100,9 +114,18 @@ module RubyLsp
100
114
  sig { returns(T::Array[Integer]) }
101
115
  attr_reader :modifier
102
116
 
103
- sig { params(location: Prism::Location, length: Integer, type: Integer, modifier: T::Array[Integer]).void }
104
- def initialize(location:, length:, type:, modifier:)
105
- @location = location
117
+ sig do
118
+ params(
119
+ start_line: Integer,
120
+ start_code_unit_column: Integer,
121
+ length: Integer,
122
+ type: Integer,
123
+ modifier: T::Array[Integer],
124
+ ).void
125
+ end
126
+ def initialize(start_line:, start_code_unit_column:, length:, type:, modifier:)
127
+ @start_line = start_line
128
+ @start_code_unit_column = start_code_unit_column
106
129
  @length = length
107
130
  @type = type
108
131
  @modifier = modifier
@@ -146,7 +169,7 @@ module RubyLsp
146
169
  # Enumerable#sort_by is not deterministic when the compared values are equal.
147
170
  # When that happens, we need to use the index as a tie breaker to ensure
148
171
  # that the order of the tokens is always the same.
149
- [token.location.start_line, token.location.start_column, index]
172
+ [token.start_line, token.start_code_unit_column, index]
150
173
  end
151
174
 
152
175
  delta = sorted_tokens.flat_map do |token|
@@ -167,8 +190,8 @@ module RubyLsp
167
190
  # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
168
191
  sig { params(token: SemanticToken).returns(T::Array[Integer]) }
169
192
  def compute_delta(token)
170
- row = token.location.start_line - 1
171
- column = token.location.start_column
193
+ row = token.start_line - 1
194
+ column = token.start_code_unit_column
172
195
 
173
196
  begin
174
197
  delta_line = row - @current_row