Skip to content

Commit

Permalink
Add Node#left_sibling, right_sibling, left_siblings and right_siblings
Browse files Browse the repository at this point in the history
  • Loading branch information
marcandre committed Aug 10, 2020
1 parent ff710b5 commit b33aba3
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### New features

* [#x](https://github.com/rubocop-hq/rubocop-ast/pull/x): Add `Node#{left|right}_sibling{s}` ([@marcandre][])
* [#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][])
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

0 comments on commit b33aba3

Please sign in to comment.