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

Add Node#left_sibling, right_sibling, left_siblings and right_siblings #93

Merged
merged 1 commit into from Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@
* [#92](https://github.com/rubocop-hq/rubocop-ast/pull/92): Add `ProcessedSource#tokens_within`, `ProcessedSource#first_token_of` and `ProcessedSource#last_token_of`. ([@fatkodima][])
* [#88](https://github.com/rubocop-hq/rubocop-ast/pull/88): Add `RescueNode`. Add `ResbodyNode#exceptions` and `ResbodyNode#branch_index`. ([@fatkodima][])
* [#89](https://github.com/rubocop-hq/rubocop-ast/pull/89): Support right hand assignment for Ruby 2.8 (3.0) parser. ([@koic][])
* [#93](https://github.com/rubocop-hq/rubocop-ast/pull/93): Add `Node#{left|right}_sibling{s}` ([@marcandre][])

## 0.3.0 (2020-08-01)

Expand Down
31 changes: 30 additions & 1 deletion lib/rubocop/ast/node.rb
Expand Up @@ -117,11 +117,40 @@ def updated(type = nil, children = nil, properties = {})
# Returns the index of the receiver node in its siblings. (Sibling index
# uses zero based numbering.)
#
# @return [Integer] the index of the receiver node in its siblings
# @return [Integer, nil] the index of the receiver node in its siblings
def sibling_index
parent&.children&.index { |sibling| sibling.equal?(self) }
end

# @return [Node, nil] the right (aka next) sibling
def right_sibling
return unless parent

parent.children[sibling_index + 1].freeze
end

# @return [Node, nil] the left (aka previous) sibling
def left_sibling
i = sibling_index
return if i.nil? || i.zero?

parent.children[i - 1].freeze
end

# @return [Array<Node>] the left (aka previous) siblings
def left_siblings
return [].freeze unless parent

parent.children[0...sibling_index].freeze
end

# @return [Array<Node>] the right (aka next) siblings
def right_siblings
return [].freeze unless parent

parent.children[sibling_index + 1..-1].freeze
end

# Common destructuring method. This can be used to normalize
# destructuring for different variations of the node.
# Some node types override this with their own custom
Expand Down
44 changes: 33 additions & 11 deletions spec/rubocop/ast/node_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true

RSpec.describe RuboCop::AST::Node do
let(:node) { RuboCop::AST::ProcessedSource.new(src, ruby_version).ast }
let(:ast) { RuboCop::AST::ProcessedSource.new(src, ruby_version).ast }
let(:node) { ast }

describe '#value_used?' do
before :all do
Expand Down Expand Up @@ -333,18 +334,39 @@ def used?
end
end

describe '#sibling_index' do
let(:src) do
[
'def foo; end',
'def bar; end',
'def baz; end'
].join("\n")
describe 'sibling_access' do
let(:src) { '[0, 1, 2, 3, 4, 5]' }

it 'returns trivial values for a root node' do
expect(node.sibling_index).to eq nil
expect(node.left_sibling).to eq nil
expect(node.right_sibling).to eq nil
expect(node.left_siblings).to eq []
expect(node.right_siblings).to eq []
end

context 'for a node with siblings' do
let(:node) { ast.children[2] }

it 'returns the expected values' do
expect(node.sibling_index).to eq 2
expect(node.left_sibling.value).to eq 1
expect(node.right_sibling.value).to eq 3
expect(node.left_siblings.map(&:value)).to eq [0, 1]
expect(node.right_siblings.map(&:value)).to eq [3, 4, 5]
end
end

it 'returns its sibling index' do
(0..2).each do |n|
expect(node.children[n].sibling_index).to eq(n)
context 'for a single child' do
let(:src) { '[0]' }
let(:node) { ast.children[0] }

it 'returns the expected values' do
expect(node.sibling_index).to eq 0
expect(node.left_sibling).to eq nil
expect(node.right_sibling).to eq nil
expect(node.left_siblings.map(&:value)).to eq []
expect(node.right_siblings.map(&:value)).to eq []
end
end
end
Expand Down