ruby-lsp 0.4.0 → 0.4.1
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ruby_lsp/document.rb +5 -3
- data/lib/ruby_lsp/executor.rb +32 -4
- data/lib/ruby_lsp/requests/code_action_resolve.rb +100 -0
- data/lib/ruby_lsp/requests/code_actions.rb +20 -4
- data/lib/ruby_lsp/requests/formatting.rb +2 -0
- data/lib/ruby_lsp/requests/path_completion.rb +20 -13
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0812eed9a874fa78fbe75cf2e8dc2bd58c2dfb67a61353f732aaba965ae9d66f'
|
4
|
+
data.tar.gz: 6d9a567aa54c95f5fd7ada3756d4b5575f068bee90f19f99f83f485d91de83a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53311978b0c6d32a34e1e69ddeea1d4b154ee0e2bc1cd426fd7e4db6ab46eeb7a5dff7d0516b5c2c722f95f05a0c5051fe7caf874e14740b5023b5f0e79da962
|
7
|
+
data.tar.gz: ce04150602707622dd0d463a35a2a9c94fe7b24b1e376dbeeae4e45f4487438219f8ab48056658946e9f7cc56136714496fdfbddf7513b77fea334aac2687ae3
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.1
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -21,9 +21,10 @@ module RubyLsp
|
|
21
21
|
@encoding = T.let(encoding, String)
|
22
22
|
@source = T.let(source, String)
|
23
23
|
@unparsed_edits = T.let([], T::Array[EditShape])
|
24
|
+
@syntax_error = T.let(false, T::Boolean)
|
24
25
|
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|
25
26
|
rescue SyntaxTree::Parser::ParseError
|
26
|
-
|
27
|
+
@syntax_error = true
|
27
28
|
end
|
28
29
|
|
29
30
|
sig { params(other: Document).returns(T::Boolean) }
|
@@ -68,14 +69,15 @@ module RubyLsp
|
|
68
69
|
return if @unparsed_edits.empty?
|
69
70
|
|
70
71
|
@tree = SyntaxTree.parse(@source)
|
72
|
+
@syntax_error = false
|
71
73
|
@unparsed_edits.clear
|
72
74
|
rescue SyntaxTree::Parser::ParseError
|
73
|
-
|
75
|
+
@syntax_error = true
|
74
76
|
end
|
75
77
|
|
76
78
|
sig { returns(T::Boolean) }
|
77
79
|
def syntax_error?
|
78
|
-
@
|
80
|
+
@syntax_error
|
79
81
|
end
|
80
82
|
|
81
83
|
sig { returns(T::Boolean) }
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -37,6 +37,9 @@ module RubyLsp
|
|
37
37
|
case request[:method]
|
38
38
|
when "initialize"
|
39
39
|
initialize_request(request.dig(:params))
|
40
|
+
when "initialized"
|
41
|
+
warn("Ruby LSP is ready")
|
42
|
+
VOID
|
40
43
|
when "textDocument/didOpen"
|
41
44
|
text_document_did_open(uri, request.dig(:params, :textDocument, :text))
|
42
45
|
when "textDocument/didClose"
|
@@ -84,6 +87,8 @@ module RubyLsp
|
|
84
87
|
inlay_hint(uri, request.dig(:params, :range))
|
85
88
|
when "textDocument/codeAction"
|
86
89
|
code_action(uri, request.dig(:params, :range), request.dig(:params, :context))
|
90
|
+
when "codeAction/resolve"
|
91
|
+
code_action_resolve(request.dig(:params))
|
87
92
|
when "textDocument/diagnostic"
|
88
93
|
begin
|
89
94
|
diagnostic(uri)
|
@@ -233,11 +238,30 @@ module RubyLsp
|
|
233
238
|
).returns(T.nilable(T::Array[Interface::CodeAction]))
|
234
239
|
end
|
235
240
|
def code_action(uri, range, context)
|
236
|
-
start_line = range.dig(:start, :line)
|
237
|
-
end_line = range.dig(:end, :line)
|
238
241
|
document = @store.get(uri)
|
239
242
|
|
240
|
-
Requests::CodeActions.new(uri, document,
|
243
|
+
Requests::CodeActions.new(uri, document, range, context).run
|
244
|
+
end
|
245
|
+
|
246
|
+
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Interface::CodeAction) }
|
247
|
+
def code_action_resolve(params)
|
248
|
+
uri = params.dig(:data, :uri)
|
249
|
+
document = @store.get(uri)
|
250
|
+
result = Requests::CodeActionResolve.new(document, params).run
|
251
|
+
|
252
|
+
case result
|
253
|
+
when Requests::CodeActionResolve::Error::EmptySelection
|
254
|
+
@notifications << Notification.new(
|
255
|
+
message: "window/showMessage",
|
256
|
+
params: Interface::ShowMessageParams.new(
|
257
|
+
type: Constant::MessageType::ERROR,
|
258
|
+
message: "Invalid selection for Extract Variable refactor",
|
259
|
+
),
|
260
|
+
)
|
261
|
+
raise Requests::CodeActionResolve::CodeActionError
|
262
|
+
else
|
263
|
+
result
|
264
|
+
end
|
241
265
|
end
|
242
266
|
|
243
267
|
sig { params(uri: String).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
|
@@ -325,6 +349,10 @@ module RubyLsp
|
|
325
349
|
)
|
326
350
|
end
|
327
351
|
|
352
|
+
code_action_provider = if enabled_features.include?("codeActions")
|
353
|
+
Interface::CodeActionOptions.new(resolve_provider: true)
|
354
|
+
end
|
355
|
+
|
328
356
|
inlay_hint_provider = if enabled_features.include?("inlayHint")
|
329
357
|
Interface::InlayHintOptions.new(resolve_provider: false)
|
330
358
|
end
|
@@ -350,7 +378,7 @@ module RubyLsp
|
|
350
378
|
semantic_tokens_provider: semantic_tokens_provider,
|
351
379
|
document_formatting_provider: enabled_features.include?("formatting"),
|
352
380
|
document_highlight_provider: enabled_features.include?("documentHighlights"),
|
353
|
-
code_action_provider:
|
381
|
+
code_action_provider: code_action_provider,
|
354
382
|
document_on_type_formatting_provider: on_type_formatting_provider,
|
355
383
|
diagnostic_provider: diagnostics_provider,
|
356
384
|
inlay_hint_provider: inlay_hint_provider,
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
|
9
|
+
# request is used to to resolve the edit field for a given code action, if it is not already provided in the
|
10
|
+
# textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
|
11
|
+
#
|
12
|
+
# # Example: Extract to variable
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# # Before:
|
16
|
+
# 1 + 1 # Select the text and use Refactor: Extract Variable
|
17
|
+
#
|
18
|
+
# # After:
|
19
|
+
# new_variable = 1 + 1
|
20
|
+
# new_variable
|
21
|
+
#
|
22
|
+
# ```
|
23
|
+
#
|
24
|
+
class CodeActionResolve < BaseRequest
|
25
|
+
extend T::Sig
|
26
|
+
NEW_VARIABLE_NAME = "new_variable"
|
27
|
+
|
28
|
+
class CodeActionError < StandardError; end
|
29
|
+
|
30
|
+
class Error < ::T::Enum
|
31
|
+
enums do
|
32
|
+
EmptySelection = new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { params(document: Document, code_action: T::Hash[Symbol, T.untyped]).void }
|
37
|
+
def initialize(document, code_action)
|
38
|
+
super(document)
|
39
|
+
|
40
|
+
@code_action = code_action
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { override.returns(T.any(Interface::CodeAction, Error)) }
|
44
|
+
def run
|
45
|
+
source_range = @code_action.dig(:data, :range)
|
46
|
+
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
47
|
+
|
48
|
+
scanner = @document.create_scanner
|
49
|
+
start_index = scanner.find_char_position(source_range[:start])
|
50
|
+
end_index = scanner.find_char_position(source_range[:end])
|
51
|
+
extraction_source = T.must(@document.source[start_index...end_index])
|
52
|
+
source_line_indentation = T.must(T.must(@document.source.lines[source_range.dig(:start, :line)])[/\A */]).size
|
53
|
+
|
54
|
+
Interface::CodeAction.new(
|
55
|
+
title: "Refactor: Extract Variable",
|
56
|
+
edit: Interface::WorkspaceEdit.new(
|
57
|
+
document_changes: [
|
58
|
+
Interface::TextDocumentEdit.new(
|
59
|
+
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
60
|
+
uri: @code_action.dig(:data, :uri),
|
61
|
+
version: nil,
|
62
|
+
),
|
63
|
+
edits: edits_to_extract_variable(source_range, extraction_source, source_line_indentation),
|
64
|
+
),
|
65
|
+
],
|
66
|
+
),
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
sig do
|
73
|
+
params(range: Document::RangeShape, source: String, indentation: Integer)
|
74
|
+
.returns(T::Array[Interface::TextEdit])
|
75
|
+
end
|
76
|
+
def edits_to_extract_variable(range, source, indentation)
|
77
|
+
target_range = {
|
78
|
+
start: { line: range.dig(:start, :line), character: indentation },
|
79
|
+
end: { line: range.dig(:start, :line), character: indentation },
|
80
|
+
}
|
81
|
+
|
82
|
+
[
|
83
|
+
create_text_edit(range, NEW_VARIABLE_NAME),
|
84
|
+
create_text_edit(target_range, "#{NEW_VARIABLE_NAME} = #{source}\n#{" " * indentation}"),
|
85
|
+
]
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { params(range: Document::RangeShape, new_text: String).returns(Interface::TextEdit) }
|
89
|
+
def create_text_edit(range, new_text)
|
90
|
+
Interface::TextEdit.new(
|
91
|
+
range: Interface::Range.new(
|
92
|
+
start: Interface::Position.new(line: range.dig(:start, :line), character: range.dig(:start, :character)),
|
93
|
+
end: Interface::Position.new(line: range.dig(:end, :line), character: range.dig(:end, :character)),
|
94
|
+
),
|
95
|
+
new_text: new_text,
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -23,7 +23,7 @@ module RubyLsp
|
|
23
23
|
params(
|
24
24
|
uri: String,
|
25
25
|
document: Document,
|
26
|
-
range:
|
26
|
+
range: Document::RangeShape,
|
27
27
|
context: T::Hash[Symbol, T.untyped],
|
28
28
|
).void
|
29
29
|
end
|
@@ -38,9 +38,8 @@ module RubyLsp
|
|
38
38
|
sig { override.returns(T.nilable(T.all(T::Array[Interface::CodeAction], Object))) }
|
39
39
|
def run
|
40
40
|
diagnostics = @context[:diagnostics]
|
41
|
-
return if diagnostics.nil? || diagnostics.empty?
|
42
41
|
|
43
|
-
diagnostics.filter_map do |diagnostic|
|
42
|
+
code_actions = diagnostics.filter_map do |diagnostic|
|
44
43
|
code_action = diagnostic.dig(:data, :code_action)
|
45
44
|
next if code_action.nil?
|
46
45
|
|
@@ -49,13 +48,30 @@ module RubyLsp
|
|
49
48
|
range = code_action.dig(:edit, :documentChanges, 0, :edits, 0, :range)
|
50
49
|
code_action if diagnostic.dig(:data, :correctable) && cover?(range)
|
51
50
|
end
|
51
|
+
|
52
|
+
code_actions << refactor_code_action(@range, @uri)
|
52
53
|
end
|
53
54
|
|
54
55
|
private
|
55
56
|
|
56
57
|
sig { params(range: T.nilable(Document::RangeShape)).returns(T::Boolean) }
|
57
58
|
def cover?(range)
|
58
|
-
range.nil? ||
|
59
|
+
range.nil? ||
|
60
|
+
((@range.dig(:start, :line))..(@range.dig(:end, :line))).cover?(
|
61
|
+
(range.dig(:start, :line))..(range.dig(:end, :line)),
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { params(range: Document::RangeShape, uri: String).returns(Interface::CodeAction) }
|
66
|
+
def refactor_code_action(range, uri)
|
67
|
+
Interface::CodeAction.new(
|
68
|
+
title: "Refactor: Extract Variable",
|
69
|
+
kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
|
70
|
+
data: {
|
71
|
+
range: range,
|
72
|
+
uri: uri,
|
73
|
+
},
|
74
|
+
)
|
59
75
|
end
|
60
76
|
end
|
61
77
|
end
|
@@ -21,17 +21,20 @@ module RubyLsp
|
|
21
21
|
super(document)
|
22
22
|
|
23
23
|
@tree = T.let(Support::PrefixTree.new(collect_load_path_files), Support::PrefixTree)
|
24
|
-
|
25
|
-
char_position = document.create_scanner.find_char_position(position)
|
26
|
-
@target = T.let(find(char_position), T.nilable(SyntaxTree::TStringContent))
|
24
|
+
@position = position
|
27
25
|
end
|
28
26
|
|
29
|
-
sig { override.returns(T.all(T::Array[
|
27
|
+
sig { override.returns(T.all(T::Array[Interface::CompletionItem], Object)) }
|
30
28
|
def run
|
31
|
-
#
|
32
|
-
return []
|
29
|
+
# We can't verify if we're inside a require when there are syntax errors
|
30
|
+
return [] if @document.syntax_error?
|
31
|
+
|
32
|
+
char_position = @document.create_scanner.find_char_position(@position)
|
33
|
+
target = T.let(find(char_position), T.nilable(SyntaxTree::TStringContent))
|
34
|
+
# no target means the we are not inside a `require` call
|
35
|
+
return [] unless target
|
33
36
|
|
34
|
-
text =
|
37
|
+
text = target.value
|
35
38
|
@tree.search(text).sort.map! do |path|
|
36
39
|
build_completion(path, path.delete_prefix(text))
|
37
40
|
end
|
@@ -73,14 +76,18 @@ module RubyLsp
|
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
76
|
-
sig
|
77
|
-
params(label: String, insert_text: String).returns(LanguageServer::Protocol::Interface::CompletionItem)
|
78
|
-
end
|
79
|
+
sig { params(label: String, insert_text: String).returns(Interface::CompletionItem) }
|
79
80
|
def build_completion(label, insert_text)
|
80
|
-
|
81
|
+
Interface::CompletionItem.new(
|
81
82
|
label: label,
|
82
|
-
|
83
|
-
|
83
|
+
text_edit: Interface::TextEdit.new(
|
84
|
+
range: Interface::Range.new(
|
85
|
+
start: @position,
|
86
|
+
end: @position,
|
87
|
+
),
|
88
|
+
new_text: insert_text,
|
89
|
+
),
|
90
|
+
kind: Constant::CompletionItemKind::REFERENCE,
|
84
91
|
)
|
85
92
|
end
|
86
93
|
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -14,6 +14,7 @@ module RubyLsp
|
|
14
14
|
# - {RubyLsp::Requests::OnTypeFormatting}
|
15
15
|
# - {RubyLsp::Requests::Diagnostics}
|
16
16
|
# - {RubyLsp::Requests::CodeActions}
|
17
|
+
# - {RubyLsp::Requests::CodeActionResolve}
|
17
18
|
# - {RubyLsp::Requests::DocumentHighlight}
|
18
19
|
# - {RubyLsp::Requests::InlayHints}
|
19
20
|
# - {RubyLsp::Requests::PathCompletion}
|
@@ -30,6 +31,7 @@ module RubyLsp
|
|
30
31
|
autoload :OnTypeFormatting, "ruby_lsp/requests/on_type_formatting"
|
31
32
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
32
33
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
34
|
+
autoload :CodeActionResolve, "ruby_lsp/requests/code_action_resolve"
|
33
35
|
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
34
36
|
autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
|
35
37
|
autoload :PathCompletion, "ruby_lsp/requests/path_completion"
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -33,7 +33,7 @@ module RubyLsp
|
|
33
33
|
# fall under the else branch which just pushes requests to the queue
|
34
34
|
@reader.read do |request|
|
35
35
|
case request[:method]
|
36
|
-
when "initialize", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
36
|
+
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
37
37
|
result = Executor.new(@store).execute(request)
|
38
38
|
finalize_request(result, request)
|
39
39
|
when "$/cancelRequest"
|
@@ -56,6 +56,7 @@ module RubyLsp
|
|
56
56
|
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
57
57
|
# https://microsoft.github.io/language-server-protocol/specification/#exit
|
58
58
|
status = @store.empty? ? 0 : 1
|
59
|
+
warn("Shutdown complete with status #{status}")
|
59
60
|
exit(status)
|
60
61
|
else
|
61
62
|
# Default case: push the request to the queue to be executed by the worker
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- lib/ruby_lsp/internal.rb
|
77
77
|
- lib/ruby_lsp/requests.rb
|
78
78
|
- lib/ruby_lsp/requests/base_request.rb
|
79
|
+
- lib/ruby_lsp/requests/code_action_resolve.rb
|
79
80
|
- lib/ruby_lsp/requests/code_actions.rb
|
80
81
|
- lib/ruby_lsp/requests/diagnostics.rb
|
81
82
|
- lib/ruby_lsp/requests/document_highlight.rb
|