# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
  module Lexers
    class Smarty < TemplateLexer
      title "Smarty"
      desc 'Smarty Template Engine'
      tag 'smarty'
      filenames '*.tpl', '*.smarty'
      mimetypes 'application/x-smarty', 'text/x-smarty'

      def self.builtins
        @builtins ||= %w(
          append assign block call capture config_load debug extends
          for foreach foreachelse break continue function if elseif
          else include include_php insert ldelim rdelim literal nocache
          php section sectionelse setfilter strip while
          counter cycle eval fetch html_checkboxes html_image html_options
          html_radios html_select_date html_select_time html_table
          mailto math textformat
          capitalize cat count_characters count_paragraphs
          count_sentences count_words date_format default escape
          from_charset indent lower nl2br regex_replace replace spacify
          string_format strip strip_tags to_charset truncate unescape
          upper wordwrap
        )
      end


      state :root do
        rule(/\{\s+/) { delegate parent }

        # block comments
        rule %r/\{\*.*?\*\}/m, Comment

        rule %r/\{\/?(?![\s*])/ do
          token Keyword
          push :smarty
        end


        rule(/.+?(?={[\/a-zA-Z0-9$#*"'])/m) { delegate parent }
        rule(/.+/m) { delegate parent }
      end

      state :comment do
        rule(/{\*/) { token Comment; push }
        rule(/\*}/) { token Comment; pop! }
        rule(/[^{}]+/m) { token Comment }
      end

      state :smarty do
        # allow nested tags
        rule %r/\{\/?(?![\s*])/ do
          token Keyword
          push :smarty
        end

        rule %r/}/, Keyword, :pop!
        rule %r/\s+/m, Text
        rule %r([~!%^&*()+=|\[\]:;,.<>/@?-]), Operator
        rule %r/#[a-zA-Z_]\w*#/, Name::Variable
        rule %r/\$[a-zA-Z_]\w*(\.\w+)*/, Name::Variable
        rule %r/(true|false|null)\b/, Keyword::Constant
        rule %r/[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?/, Num
        rule %r/"(\\.|.)*?"/, Str::Double
        rule %r/'(\\.|.)*?'/, Str::Single

        rule %r/([a-zA-Z_]\w*)/ do |m|
          if self.class.builtins.include? m[0]
            token Name::Builtin
          else
            token Name::Attribute
          end
        end
      end
    end
  end
end
