# -*- coding: utf-8 -*- # # frozen_string_literal: true module Rouge module Lexers class Gherkin < RegexLexer tag 'gherkin' aliases 'cucumber', 'behat' title "Gherkin" desc 'A business-readable spec DSL (github.com/cucumber/cucumber/wiki/Gherkin)' filenames '*.feature' mimetypes 'text/x-gherkin' def self.detect?(text) return true if text.shebang? 'cucumber' end # self-modifying method that loads the keywords file def self.keywords load File.join(__dir__, 'gherkin/keywords.rb') keywords end def self.step_regex # in Gherkin's config, keywords that end in < don't # need word boundaries at the ends - all others do. @step_regex ||= Regexp.new( keywords[:step].map do |w| if w.end_with? '<' Regexp.escape(w.chop) elsif w.end_with?(' ') Regexp.escape(w) else "#{Regexp.escape(w)}\\b" end end.join('|') ) end rest_of_line = /.*?(?=[#\n])/ state :basic do rule %r(#.*$), Comment rule %r/[ \r\t]+/, Text end state :root do mixin :basic rule %r(\n), Text rule %r(""".*?""")m, Str rule %r(@[^\s@]+), Name::Tag mixin :has_table mixin :has_examples end state :has_scenarios do rule %r((.*?)(:)) do |m| reset_stack keyword = m[1] keyword_tok = if self.class.keywords[:element].include? keyword push :description; Keyword::Namespace elsif self.class.keywords[:feature].include? keyword push :feature_description; Keyword::Declaration elsif self.class.keywords[:examples].include? keyword push :example_description; Name::Namespace else Error end groups keyword_tok, Punctuation end end state :has_examples do mixin :has_scenarios rule Gherkin.step_regex, Name::Function do token Name::Function reset_stack; push :step end end state :has_table do rule(/(?=[|])/) { push :table_header } end state :table_header do rule %r/[^|\s]+/, Name::Variable rule %r/\n/ do token Text goto :table end mixin :table end state :table do mixin :basic rule %r/\n/, Text, :table_bol rule %r/[|]/, Punctuation rule %r/[^|\s]+/, Name end state :table_bol do rule(/(?=\s*[^\s|])/) { reset_stack } rule(//) { pop! } end state :description do mixin :basic mixin :has_examples rule %r/\n/, Text rule rest_of_line, Text end state :feature_description do mixin :basic mixin :has_scenarios rule %r/\n/, Text rule rest_of_line, Text end state :example_description do mixin :basic mixin :has_table rule %r/\n/, Text rule rest_of_line, Text end state :step do mixin :basic rule %r/<.*?>/, Name::Variable rule %r/".*?"/, Str rule %r/\S[^\s<]*/, Text rule rest_of_line, Text, :pop! end end end end