From 96b0656ef8e8e337c2b156d79cf40fcabe0a57de Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Mon, 10 Aug 2020 14:45:19 -0400 Subject: [PATCH] Add Node#left_sibling, right_sibling, left_siblings and right_siblings --- CHANGELOG.md | 1 + lib/rubocop/ast/node.rb | 31 +++++++++++++++++++++++- spec/rubocop/ast/node_spec.rb | 44 ++++++++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8975b148b..ede2be76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/lib/rubocop/ast/node.rb b/lib/rubocop/ast/node.rb index 9a1497aac..3193190a1 100644 --- a/lib/rubocop/ast/node.rb +++ b/lib/rubocop/ast/node.rb @@ -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] the left (aka previous) siblings + def left_siblings + return [].freeze unless parent + + parent.children[0...sibling_index].freeze + end + + # @return [Array] 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 diff --git a/spec/rubocop/ast/node_spec.rb b/spec/rubocop/ast/node_spec.rb index 68d09076b..0127cd512 100644 --- a/spec/rubocop/ast/node_spec.rb +++ b/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 @@ -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