Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement remove newlines in brackets before toggle block to on… #3005

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
75 changes: 63 additions & 12 deletions lib/ruby_lsp/requests/code_action_resolve.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,62 @@ def create_text_edit(range, new_text)
)
end

sig do
params(
body_content: String,
body_loc: Prism::Location,
node: Prism::Node,
indentation: T.nilable(String),
).returns(String)
end
def handle_nested_structure(body_content, body_loc, node, indentation)
location = node.location
correction_start = location.start_offset - body_loc.start_offset
correction_end = location.end_offset - body_loc.start_offset
next_indentation = indentation ? "#{indentation} " : nil

transformed_content = if node.is_a?(Prism::HashNode)
transform_hash_node(node, next_indentation)
elsif node.is_a?(Prism::ArrayNode)
transform_array_node(node, next_indentation)
elsif node.is_a?(Prism::BlockNode)
recursively_switch_nested_block_styles(node, next_indentation)
else
raise "Unsupported node type: #{node.class.name}"
end

body_content[correction_start...correction_end] = transformed_content
body_content
end

sig { params(node: Prism::HashNode, indentation: T.nilable(String)).returns(String) }
def transform_hash_node(node, indentation)
elements = node.elements.map do |elem|
if elem.is_a?(Prism::AssocNode) && !elem.operator
"#{elem.key.slice} #{switch_block_body(elem.value, indentation).strip}"
elsif elem.is_a?(Prism::AssocNode) && elem.operator
"#{elem.key.slice} => #{switch_block_body(elem.value, indentation).strip}"
elsif elem.is_a?(Prism::AssocSplatNode)
"**#{elem.value&.slice}"
end
end
if indentation
"{\n#{indentation} #{elements.join(",\n#{indentation} ")},\n#{indentation}}"
else
"{ #{elements.join(", ")} } "
end
end

sig { params(node: Prism::ArrayNode, indentation: T.nilable(String)).returns(String) }
def transform_array_node(node, indentation)
elements = node.elements.map { |elem| switch_block_body(elem, indentation).strip }
if indentation
"[\n#{indentation} #{elements.join(",\n#{indentation} ")},\n#{indentation}]"
else
"[#{elements.join(", ")}]"
end
end

sig { params(node: Prism::BlockNode, indentation: T.nilable(String)).returns(String) }
def recursively_switch_nested_block_styles(node, indentation)
parameters = node.parameters
Expand All @@ -295,7 +351,8 @@ def recursively_switch_nested_block_styles(node, indentation)
source << "{ "
source << "#{parameters.slice} " if parameters
source << switch_block_body(body, nil) if body
source << "}"
source << " }"
source = source.squeeze(" ").gsub("\n", ";")
end

source
Expand All @@ -310,24 +367,18 @@ def switch_block_body(body, indentation)
start: { line: body_loc.start_line - 1, character: body_loc.start_column },
end: { line: body_loc.end_line - 1, character: body_loc.end_column },
},
node_types: [Prism::BlockNode],
node_types: [Prism::BlockNode, Prism::HashNode, Prism::ArrayNode],
)

body_content = body.slice.dup

# If there are nested blocks, then we change their style too and we have to mutate the string using the
# relative position in respect to the beginning of the body
if nested_block.is_a?(Prism::BlockNode)
location = nested_block.location
correction_start = location.start_offset - body_loc.start_offset
correction_end = location.end_offset - body_loc.start_offset
next_indentation = indentation ? "#{indentation} " : nil

body_content[correction_start...correction_end] =
recursively_switch_nested_block_styles(nested_block, next_indentation)
if nested_block.is_a?(Prism::HashNode) || nested_block.is_a?(Prism::ArrayNode) || nested_block.is_a?(Prism::BlockNode) # rubocop:disable Layout/LineLength
handle_nested_structure(body_content, body_loc, nested_block, indentation)
else
indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
end

indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"params": {
"kind": "refactor.rewrite",
"title": "Refactor: Toggle block style",
"data": {
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 8,
"character": 3
}
},
"uri": "file:///fake"
}
},
"result": {
"title": "Refactor: Toggle block style",
"edit": {
"documentChanges": [
{
"textDocument": {
"uri": "file:///fake",
"version": null
},
"edits": [
{
"range": {
"start": {
"line": 0,
"character": 12
},
"end": {
"line": 8,
"character": 3
}
},
"newText": "{ |group| users.map { |user| [group.id, user.id, Time.now] } }"
}
]
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"params": {
"kind": "refactor.rewrite",
"title": "Refactor: Toggle block style",
"data": {
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 8,
"character": 3
}
},
"uri": "file:///fake"
}
},
"result": {
"title": "Refactor: Toggle block style",
"edit": {
"documentChanges": [
{
"textDocument": {
"uri": "file:///fake",
"version": null
},
"edits": [
{
"range": {
"start": {
"line": 0,
"character": 10
},
"end": {
"line": 8,
"character": 3
}
},
"newText": "{ |user| items.each { |item| { :user_id => user.id, item_id: item.id, **status } } }"
}
]
}
]
}
}
}
9 changes: 9 additions & 0 deletions test/fixtures/nested_block_with_array_do_end.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
groups.each do |group|
users.map do |user|
[
group.id,
user.id,
Time.now
]
end
end
9 changes: 9 additions & 0 deletions test/fixtures/nested_block_with_hash_do_end.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
users.map do |user|
items.each do |item|
{
:user_id => user.id,
item_id: item.id,
**status
}
end
end
Loading