ruby-lsp 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
# ![Code action resolve demo](../../misc/code_action_resolve.gif)
|
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
|