ruby-lsp 0.14.6 → 0.16.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -16
- data/exe/ruby-lsp-check +13 -22
- data/exe/ruby-lsp-doctor +9 -0
- data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +14 -1
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +11 -23
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +32 -8
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +26 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
- data/lib/ruby_indexer/test/configuration_test.rb +2 -11
- data/lib/ruby_lsp/addon.rb +18 -9
- data/lib/ruby_lsp/base_server.rb +149 -0
- data/lib/ruby_lsp/document.rb +6 -11
- data/lib/ruby_lsp/global_state.rb +180 -0
- data/lib/ruby_lsp/internal.rb +4 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +22 -13
- data/lib/ruby_lsp/listeners/completion.rb +13 -14
- data/lib/ruby_lsp/listeners/definition.rb +14 -6
- data/lib/ruby_lsp/listeners/document_symbol.rb +91 -3
- data/lib/ruby_lsp/listeners/hover.rb +6 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +7 -4
- data/lib/ruby_lsp/load_sorbet.rb +62 -0
- data/lib/ruby_lsp/requests/code_lens.rb +3 -2
- data/lib/ruby_lsp/requests/completion.rb +15 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +56 -0
- data/lib/ruby_lsp/requests/definition.rb +11 -4
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/formatting.rb +7 -43
- data/lib/ruby_lsp/requests/hover.rb +4 -4
- data/lib/ruby_lsp/requests/request.rb +2 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +7 -4
- data/lib/ruby_lsp/requests/signature_help.rb +4 -3
- data/lib/ruby_lsp/requests/support/common.rb +16 -5
- data/lib/ruby_lsp/requests/support/formatter.rb +26 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +47 -0
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
- data/lib/ruby_lsp/requests/support/{syntax_tree_formatting_runner.rb → syntax_tree_formatter.rb} +13 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
- data/lib/ruby_lsp/requests.rb +3 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +36 -13
- data/lib/ruby_lsp/server.rb +763 -142
- data/lib/ruby_lsp/setup_bundler.rb +13 -1
- data/lib/ruby_lsp/store.rb +3 -15
- data/lib/ruby_lsp/test_helper.rb +52 -0
- data/lib/ruby_lsp/utils.rb +68 -33
- metadata +16 -13
- data/lib/ruby_lsp/executor.rb +0 -614
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -93
- data/lib/ruby_lsp/requests/support/formatter_runner.rb +0 -18
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -34
- 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)
|
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 ==
|
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
|
data/lib/ruby_lsp/requests/support/{syntax_tree_formatting_runner.rb → syntax_tree_formatter.rb}
RENAMED
@@ -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
|
15
|
+
class SyntaxTreeFormatter
|
18
16
|
extend T::Sig
|
19
|
-
include
|
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
|
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)
|
26
|
-
def initialize(
|
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
|
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
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -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 :
|
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.
|
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
|
-
|
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(
|
92
|
-
attr_reader :
|
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
|
104
|
-
|
105
|
-
|
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.
|
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.
|
171
|
-
column = token.
|
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
|