diff --git a/CHANGES.md b/CHANGES.md index 7975bf4f..90f3ed54 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ ## Unreleased -* ErbAst-parser now only used for `*.html.erb`-files, not e.g. `*.js.erb` +* ERB AST-scanner now uses regex instead of better_html to allow + parsing files other than HTML. ## v1.0.13 diff --git a/i18n-tasks.gemspec b/i18n-tasks.gemspec index 875c89c9..114793a7 100644 --- a/i18n-tasks.gemspec +++ b/i18n-tasks.gemspec @@ -39,7 +39,6 @@ Gem::Specification.new do |s| s.add_dependency 'activesupport', '>= 4.0.2' s.add_dependency 'ast', '>= 2.1.0' - s.add_dependency 'better_html', '>= 1.0', '< 3.0' s.add_dependency 'erubi' s.add_dependency 'highline', '>= 2.0.0' s.add_dependency 'i18n' diff --git a/lib/i18n/tasks/scanners/erb_ast_processor.rb b/lib/i18n/tasks/scanners/erb_ast_processor.rb deleted file mode 100644 index 1ccd2a5b..00000000 --- a/lib/i18n/tasks/scanners/erb_ast_processor.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'ast' -require 'set' -require 'i18n/tasks/scanners/local_ruby_parser' - -module I18n::Tasks::Scanners - class ErbAstProcessor - include AST::Processor::Mixin - def initialize - super() - @ruby_parser = LocalRubyParser.new(ignore_blocks: true) - @comments = [] - end - - def process_and_extract_comments(ast) - result = process(ast) - [result, @comments] - end - - def on_code(node) - parsed, comments = @ruby_parser.parse( - node.children[0], - location: node.location - ) - @comments.concat(comments) - - unless parsed.nil? - parsed = parsed.updated( - nil, - parsed.children.map { |child| node?(child) ? process(child) : child } - ) - node = node.updated(:send, parsed) - end - node - end - - # @param node [::Parser::AST::Node] - # @return [::Parser::AST::Node] - def handler_missing(node) - node = handle_comment(node) - return if node.nil? - - node.updated( - nil, - node.children.map { |child| node?(child) ? process(child) : child } - ) - end - - private - - # Convert ERB-comments to ::Parser::Source::Comment and skip processing node - # - # @param node Parser::AST::Node Potential comment node - # @return Parser::AST::Node or nil - def handle_comment(node) - if node.type == :erb && node.children.size == 4 && - node.children[0]&.type == :indicator && node.children[0].children[0] == '#' && - node.children[2]&.type == :code - - # Do not continue parsing this node - comment = node.children[2] - @comments << ::Parser::Source::Comment.new(comment.location.expression) - return - end - - node - end - - def node?(node) - node.is_a?(::Parser::AST::Node) - end - end -end diff --git a/lib/i18n/tasks/scanners/erb_ast_scanner.rb b/lib/i18n/tasks/scanners/erb_ast_scanner.rb index 5d7a096d..c6d5120f 100644 --- a/lib/i18n/tasks/scanners/erb_ast_scanner.rb +++ b/lib/i18n/tasks/scanners/erb_ast_scanner.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true require 'i18n/tasks/scanners/ruby_ast_scanner' -require 'i18n/tasks/scanners/erb_ast_processor' -require 'better_html/errors' -require 'better_html/parser' +require 'i18n/tasks/scanners/local_ruby_parser' module I18n::Tasks::Scanners # Scan for I18n.translate calls in ERB-file better-html and ASTs class ErbAstScanner < RubyAstScanner + DEFAULT_REGEXP = /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>/m.freeze + def initialize(**args) super(**args) - @erb_ast_processor = ErbAstProcessor.new + @ruby_parser = LocalRubyParser.new(ignore_blocks: true) end private @@ -20,29 +20,59 @@ def initialize(**args) # @param path Path to file to parse # @return [{Parser::AST::Node}, [Parser::Source::Comment]] def path_to_ast_and_comments(path) - parser = BetterHtml::Parser.new(make_buffer(path)) - ast = convert_better_html(parser.ast) - @erb_ast_processor.process_and_extract_comments(ast) + comments = [] + buffer = make_buffer(path) + + children = [] + buffer + .source + .scan(DEFAULT_REGEXP) do |indicator, code, tailch, _rspace| + match = Regexp.last_match + character = indicator ? indicator[0] : nil + + start = match.begin(0) + 2 + (character&.size || 0) + stop = match.end(0) - 2 - (tailch&.size || 0) + + case character + when '=', nil, '-' + parsed, parsed_comments = handle_code(buffer, code, start, stop) + comments.concat(parsed_comments) + children << parsed unless parsed.nil? + when '#', '#-' + comments << handle_comment(buffer, start, stop) + end + end + + [root_node(children, buffer), comments] end - # Convert BetterHtml nodes to Parser::AST::Node - # - # @param node BetterHtml::Parser::AST::Node - # @return Parser::AST::Node - def convert_better_html(node) - definition = Parser::Source::Map::Definition.new( - node.location.begin, - node.location.begin, - node.location.begin, - node.location.end - ) - Parser::AST::Node.new( - node.type, - node.children.map { |child| child.is_a?(BetterHtml::AST::Node) ? convert_better_html(child) : child }, - { - location: definition - } - ) + def handle_code(buffer, code, start, stop) + range = ::Parser::Source::Range.new(buffer, start, stop) + location = + Parser::Source::Map::Definition.new( + range.begin, + range.begin, + range.begin, + range.end + ) + @ruby_parser.parse(code, location: location) + end + + def handle_comment(buffer, start, stop) + range = ::Parser::Source::Range.new(buffer, start, stop) + ::Parser::Source::Comment.new(range) + end + + def root_node(children, buffer) + range = ::Parser::Source::Range.new(buffer, 0, buffer.source.size) + location = + Parser::Source::Map::Definition.new( + range.begin, + range.begin, + range.begin, + range.end + ) + ::Parser::AST::Node.new(:erb, children, location: location) end end end diff --git a/spec/used_keys_erb_spec.rb b/spec/used_keys_erb_spec.rb index 2f881f46..7a4c959a 100644 --- a/spec/used_keys_erb_spec.rb +++ b/spec/used_keys_erb_spec.rb @@ -33,8 +33,8 @@ [ { path: 'app/views/application/show.html.erb', - pos: 17, - line_num: 1, line_pos: 17, + pos: 18, + line_num: 1, line_pos: 18, line: "