From 41306ed98d17ab4daed616deec7df483300699e8 Mon Sep 17 00:00:00 2001 From: Owen Stephens Date: Mon, 22 Jun 2020 10:41:34 +0100 Subject: [PATCH] Add `delimiters' and related predicates for `RegexpNode` (#41) As suggested by @bbatsov in https://github.com/rubocop-hq/rubocop/pull/8138#discussion_r439268175 --- CHANGELOG.md | 1 + lib/rubocop/ast/node/regexp_node.rb | 20 +++ spec/rubocop/ast/regexp_node_spec.rb | 188 +++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4a8d5a72..aada835f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * [#20](https://github.com/rubocop-hq/rubocop-ast/pull/20): Add option predicates for `RegexpNode`. ([@owst][]) * [#11](https://github.com/rubocop-hq/rubocop-ast/issues/11): Add `argument_type?` method to make it easy to recognize argument nodes. ([@tejasbubane][]) * [#31](https://github.com/rubocop-hq/rubocop-ast/pull/31): Use `param === node` to match params, which allows Regexp, Proc, Set, etc. ([@marcandre][]) +* [#41](https://github.com/rubocop-hq/rubocop-ast/pull/41): Add `delimiters` and related predicates for `RegexpNode`. ([@owst][]) ## 0.0.3 (2020-05-15) diff --git a/lib/rubocop/ast/node/regexp_node.rb b/lib/rubocop/ast/node/regexp_node.rb index 14c33b104..2b621ad90 100644 --- a/lib/rubocop/ast/node/regexp_node.rb +++ b/lib/rubocop/ast/node/regexp_node.rb @@ -32,6 +32,26 @@ def content children.select(&:str_type?).map(&:str_content).join end + # @return [Bool] if the regexp is a /.../ literal + def slash_literal? + loc.begin.source == '/' + end + + # @return [Bool] if the regexp is a %r{...} literal (using any delimiters) + def percent_r_literal? + !slash_literal? + end + + # @return [String] the regexp delimiters (without %r) + def delimiters + [loc.begin.source[-1], loc.end.source[0]] + end + + # @return [Bool] if char is one of the delimiters + def delimiter?(char) + delimiters.include?(char) + end + # @return [Bool] if regexp contains interpolation def interpolation? children.any?(&:begin_type?) diff --git a/spec/rubocop/ast/regexp_node_spec.rb b/spec/rubocop/ast/regexp_node_spec.rb index 580a98287..8a9a0bbb3 100644 --- a/spec/rubocop/ast/regexp_node_spec.rb +++ b/spec/rubocop/ast/regexp_node_spec.rb @@ -141,6 +141,194 @@ end end + describe '#slash_literal?' do + context 'with /-delimiters' do + let(:source) { '/abc/' } + + it { expect(regexp_node.slash_literal?).to eq(true) } + end + + context 'with %r/-delimiters' do + let(:source) { '%r/abc/' } + + it { expect(regexp_node.slash_literal?).to eq(false) } + end + + context 'with %r{-delimiters' do + let(:source) { '%r{abc}' } + + it { expect(regexp_node.slash_literal?).to eq(false) } + end + + context 'with multi-line %r{-delimiters' do + let(:source) do + <<~SRC + %r{ + abc + }x + SRC + end + + it { expect(regexp_node.slash_literal?).to eq(false) } + end + + context 'with %r<-delimiters' do + let(:source) { '%rx' } + + it { expect(regexp_node.slash_literal?).to eq(false) } + end + end + + describe '#percent_r_literal?' do + context 'with /-delimiters' do + let(:source) { '/abc/' } + + it { expect(regexp_node.percent_r_literal?).to eq(false) } + end + + context 'with %r/-delimiters' do + let(:source) { '%r/abc/' } + + it { expect(regexp_node.percent_r_literal?).to eq(true) } + end + + context 'with %r{-delimiters' do + let(:source) { '%r{abc}' } + + it { expect(regexp_node.percent_r_literal?).to eq(true) } + end + + context 'with multi-line %r{-delimiters' do + let(:source) do + <<~SRC + %r{ + abc + }x + SRC + end + + it { expect(regexp_node.percent_r_literal?).to eq(true) } + end + + context 'with %r<-delimiters' do + let(:source) { '%rx' } + + it { expect(regexp_node.percent_r_literal?).to eq(true) } + end + end + + describe '#delimiters' do + context 'with /-delimiters' do + let(:source) { '/abc/' } + + it { expect(regexp_node.delimiters).to eq(['/', '/']) } + end + + context 'with %r/-delimiters' do + let(:source) { '%r/abc/' } + + it { expect(regexp_node.delimiters).to eq(['/', '/']) } + end + + context 'with %r{-delimiters' do + let(:source) { '%r{abc}' } + + it { expect(regexp_node.delimiters).to eq(['{', '}']) } + end + + context 'with multi-line %r{-delimiters' do + let(:source) do + <<~SRC + %r{ + abc + }x + SRC + end + + it { expect(regexp_node.delimiters).to eq(['{', '}']) } + end + + context 'with %r<-delimiters' do + let(:source) { '%rx' } + + it { expect(regexp_node.delimiters).to eq(['<', '>']) } + end + end + + describe '#delimiter?' do + context 'with /-delimiters' do + let(:source) { '/abc/' } + + it { expect(regexp_node.delimiter?('/')).to eq(true) } + + it { expect(regexp_node.delimiter?('{')).to eq(false) } + end + + context 'with %r/-delimiters' do + let(:source) { '%r/abc/' } + + it { expect(regexp_node.delimiter?('/')).to eq(true) } + + it { expect(regexp_node.delimiter?('{')).to eq(false) } + it { expect(regexp_node.delimiter?('}')).to eq(false) } + it { expect(regexp_node.delimiter?('%')).to eq(false) } + it { expect(regexp_node.delimiter?('r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r/')).to eq(false) } + end + + context 'with %r{-delimiters' do + let(:source) { '%r{abc}' } + + it { expect(regexp_node.delimiter?('{')).to eq(true) } + it { expect(regexp_node.delimiter?('}')).to eq(true) } + + it { expect(regexp_node.delimiter?('/')).to eq(false) } + it { expect(regexp_node.delimiter?('%')).to eq(false) } + it { expect(regexp_node.delimiter?('r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r/')).to eq(false) } + it { expect(regexp_node.delimiter?('%r{')).to eq(false) } + end + + context 'with multi-line %r{-delimiters' do + let(:source) do + <<~SRC + %r{ + abc + }x + SRC + end + + it { expect(regexp_node.delimiter?('{')).to eq(true) } + it { expect(regexp_node.delimiter?('}')).to eq(true) } + + it { expect(regexp_node.delimiter?('/')).to eq(false) } + it { expect(regexp_node.delimiter?('%')).to eq(false) } + it { expect(regexp_node.delimiter?('r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r/')).to eq(false) } + it { expect(regexp_node.delimiter?('%r{')).to eq(false) } + end + + context 'with %r<-delimiters' do + let(:source) { '%rx' } + + it { expect(regexp_node.delimiter?('<')).to eq(true) } + it { expect(regexp_node.delimiter?('>')).to eq(true) } + + it { expect(regexp_node.delimiter?('{')).to eq(false) } + it { expect(regexp_node.delimiter?('}')).to eq(false) } + it { expect(regexp_node.delimiter?('/')).to eq(false) } + it { expect(regexp_node.delimiter?('%')).to eq(false) } + it { expect(regexp_node.delimiter?('r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r')).to eq(false) } + it { expect(regexp_node.delimiter?('%r/')).to eq(false) } + it { expect(regexp_node.delimiter?('%r{')).to eq(false) } + it { expect(regexp_node.delimiter?('%r<')).to eq(false) } + end + end + describe '#interpolation?' do context 'with direct variable interpoation' do let(:source) { '/\n\n#{foo}(abc)+/' }