Skip to content

Commit

Permalink
Add the switch block style refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Aug 7, 2024
1 parent 21139ab commit 31215f1
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 3 deletions.
63 changes: 61 additions & 2 deletions lib/ruby_lsp/requests/code_action_resolve.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ module Requests
#
class CodeActionResolve < Request
extend T::Sig
include Support::Common

NEW_VARIABLE_NAME = "new_variable"
NEW_METHOD_NAME = "new_method"

Expand Down Expand Up @@ -50,11 +52,70 @@ def perform
refactor_variable
when CodeActions::EXTRACT_TO_METHOD_TITLE
refactor_method
when CodeActions::SWITCH_BLOCK_STYLE_TITLE
switch_block_style
else
Error::UnknownCodeAction
end
end

private

sig { returns(T.any(Interface::CodeAction, Error)) }
def switch_block_style
return Error::EmptySelection if @document.source.empty?

source_range = @code_action.dig(:data, :range)
return Error::EmptySelection if source_range[:start] == source_range[:end]

target = @document.locate_first_within_range(
@code_action.dig(:data, :range),
node_types: [Prism::CallNode],
)

return Error::InvalidTargetRange unless target.is_a?(Prism::CallNode)

node = target.block
return Error::InvalidTargetRange unless node.is_a?(Prism::BlockNode)

parameters = node.parameters
body = node.body

# If the block is using `do...end` style, we change it to a single line brace block. Newlines are turned into
# semi colons, so that the result is valid Ruby code and still a one liner. If the block is using brace style,
# we do the opposite and turn it into a `do...end` block, making all semi colons into newlines.
new_source = if node.opening_loc.slice == "do"
source = +"{ "
source << "#{parameters.slice} " if parameters
source << "#{body.slice.gsub("\n", ";")} " if body
source << "}"
else
indentation = " " * target.location.start_column
source = +"do"
source << " #{parameters.slice}" if parameters
source << "\n#{indentation} "
source << body.slice.gsub(";", "\n") if body
source << "\n#{indentation}end"
end

Interface::CodeAction.new(
title: CodeActions::SWITCH_BLOCK_STYLE_TITLE,
edit: Interface::WorkspaceEdit.new(
document_changes: [
Interface::TextDocumentEdit.new(
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
uri: @code_action.dig(:data, :uri),
version: nil,
),
edits: [
Interface::TextEdit.new(range: range_from_location(node.location), new_text: new_source),
],
),
],
),
)
end

sig { returns(T.any(Interface::CodeAction, Error)) }
def refactor_variable
return Error::EmptySelection if @document.source.empty?
Expand Down Expand Up @@ -206,8 +267,6 @@ def refactor_method
)
end

private

sig { params(range: T::Hash[Symbol, T.untyped], new_text: String).returns(Interface::TextEdit) }
def create_text_edit(range, new_text)
Interface::TextEdit.new(
Expand Down
6 changes: 6 additions & 0 deletions lib/ruby_lsp/requests/code_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CodeActions < Request

EXTRACT_TO_VARIABLE_TITLE = "Refactor: Extract Variable"
EXTRACT_TO_METHOD_TITLE = "Refactor: Extract Method"
SWITCH_BLOCK_STYLE_TITLE = "Refactor: Switch block style"

class << self
extend T::Sig
Expand Down Expand Up @@ -69,6 +70,11 @@ def perform
kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
data: { range: @range, uri: @uri.to_s },
)
code_actions << Interface::CodeAction.new(
title: SWITCH_BLOCK_STYLE_TITLE,
kind: Constant::CodeActionKind::REFACTOR_REWRITE,
data: { range: @range, uri: @uri.to_s },
)
end

code_actions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"params": {
"kind": "refactor.rewrite",
"title": "Refactor: Switch block style",
"data": {
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 58
}
},
"uri": "file:///fake"
}
},
"result": {
"title": "Refactor: Switch block style",
"edit": {
"documentChanges": [
{
"textDocument": {
"uri": "file:///fake",
"version": null
},
"edits": [
{
"range": {
"start": {
"line": 0,
"character": 26
},
"end": {
"line": 0,
"character": 58
}
},
"newText": "do |a|\n a[\"field\"] == \"expected\"\nend"
}
]
}
]
}
}
}
47 changes: 47 additions & 0 deletions test/expectations/code_action_resolve/nested_block_calls.exp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"params": {
"kind": "refactor.rewrite",
"title": "Refactor: Switch block style",
"data": {
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 3,
"character": 3
}
},
"uri": "file:///fake"
}
},
"result": {
"title": "Refactor: Switch block style",
"edit": {
"documentChanges": [
{
"textDocument": {
"uri": "file:///fake",
"version": null
},
"edits": [
{
"range": {
"start": {
"line": 0,
"character": 29
},
"end": {
"line": 3,
"character": 3
}
},
"newText": "{ |a| nested_call(fourth_call).each do |b|; end }"
}
]
}
]
}
}
}
17 changes: 17 additions & 0 deletions test/expectations/code_actions/aref_field.exp.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@
},
"uri": "file:///fake"
}
},
{
"title": "Refactor: Switch block style",
"kind": "refactor.rewrite",
"data": {
"range": {
"start": {
"line": 2,
"character": 9
},
"end": {
"line": 2,
"character": 13
}
},
"uri": "file:///fake"
}
}
]
}
4 changes: 4 additions & 0 deletions test/fixtures/nested_block_calls.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
method_call(other_call).each do |a|
nested_call(fourth_call).each do |b|
end
end
2 changes: 1 addition & 1 deletion test/requests/code_actions_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def code_action_for_diagnostic(diagnostic)
def code_action_for_refactor(refactor)
LanguageServer::Protocol::Interface::CodeAction.new(
title: refactor["title"],
kind: LanguageServer::Protocol::Constant::CodeActionKind::REFACTOR_EXTRACT,
kind: refactor["kind"],
data: {
range: refactor.dig("data", "range"),
uri: refactor.dig("data", "uri"),
Expand Down

0 comments on commit 31215f1

Please sign in to comment.