# frozen_string_literal: truemoduleHamlLint::Tree# Represents a tag node in a HAML document.classTagNode<Node# Computed set of attribute hashes code.## This is a combination of all dynamically calculated attributes from the# different attribute setting syntaxes (`{...}`/`(...)`), converted into# Ruby code.## @note This has to be memoized because of a design decision in Haml 5. When# calling `DynamicAttributes#to_literal`, they mutate the "old" parameter using# `String#sub!` instead of returning a new string. This means that any subsequent# calls can return a nil instead of a string for that attribute, which causes# any subsequent calls to the method to raise an error.## @return [Array<String>]defdynamic_attributes_sources@dynamic_attributes_sources||=ifGem::Version.new(Haml::VERSION)<Gem::Version.new('5')@value[:attributes_hashes]elseArray(@value[:dynamic_attributes].to_literal).reject(&:empty?)endend# Returns whether this tag contains executable script (e.g. is followed by a# `=`).## @return [true,false]defcontains_script?@value[:parse]&&!@value[:value].strip.empty?end# Returns whether this tag has a specified attribute.## @return [true,false]defhas_hash_attribute?(attribute)hash_attributes?&&existing_attributes.include?(attribute)end# List of classes statically defined for this tag.## @example For `%tag.button.button-info{ class: status }`, this returns:# ['button', 'button-info']## @return [Array<String>] list of statically defined classes with leading# dot removeddefstatic_classes@static_classes||=static_attributes_source.scan(/\.([-:\w]+)/)end# List of ids statically defined for this tag.## @example For `%tag.button#start-button{ id: special_id }`, this returns:# ['start-button']## @return [Array<String>] list of statically defined ids with leading `#`# removeddefstatic_ids@static_ids||=static_attributes_source.scan(/#([-:\w]+)/)end# Static element attributes defined after the tag name.## @example For `%tag.button#start-button`, this returns:# '.button#start-button'## @return [String]defstatic_attributes_sourceattributes_source[:static]||''end# Returns the source code for the dynamic attributes defined in `{...}`,# `(...)`, or `[...]` after a tag name.## @example For `%tag.class{ id: 'hello' }(lang=en)`, this returns:# { :hash => " id: 'hello' ", :html => "lang=en" }## @return [Hash]defdynamic_attributes_source@dynamic_attributes_source||=attributes_source.reject{|key|key==:static}end# Returns the source code for the static and dynamic attributes# of a tag.## @example For `%tag.class{ id: 'hello' }(lang=en)`, this returns:# { :static => '.class', :hash => " id: 'hello' ", :html => "lang=en" }## @return [Hash]defattributes_source@attributes_source||=begin_explicit_tag,static_attrs,rest=source_code.scan(/\A\s*(%[-:\w]+)?([-:\w.\#]*)(.*)/m)[0]attr_types={'{'=>[:hash,%w[{ }]],'('=>[:html,%w[( )]],'['=>[:object_ref,%w[[ ]]],}attr_source={static: static_attrs}whileresttype,chars=attr_types[rest[0]]breakunlesstype# Not an attribute opening character, so we're done# Can't define multiple of the same attribute type (e.g. two {...})breakifattr_source[type]attr_source[type],rest=Haml::Util.balance(rest,*chars)endattr_sourceendend# Whether this tag node has a set of hash attributes defined via the# curly brace syntax (e.g. `%tag{ lang: 'en' }`).## @return [true,false]defhash_attributes?!dynamic_attributes_source[:hash].nil?end# Attributes defined after the tag name in Ruby hash brackets (`{}`).## @example For `%tag.class{ lang: 'en' }`, this returns:# " lang: 'en' "## @return [String] source without the surrounding curly bracesdefhash_attributes_sourcedynamic_attributes_source[:hash]end# Whether this tag node has a set of HTML attributes defined via the# parentheses syntax (e.g. `%tag(lang=en)`).## @return [true,false]defhtml_attributes?!dynamic_attributes_source[:html].nil?end# Attributes defined after the tag name in parentheses (`()`).## @example For `%tag.class(lang=en)`, this returns:# "lang=en"## @return [String,nil] source without the surrounding parentheses, or `nil`# if it has not been defineddefhtml_attributes_sourcedynamic_attributes_source[:html][/\A\((.*)\)\z/,1]ifhtml_attributes?end# ID of the HTML tag.## @return [String]deftag_id@value[:attributes]['id']end# Name of the HTML tag.## @return [String]deftag_name@value[:name]end# Whether this tag node has a set of square brackets (e.g. `%tag[...]`)# following it that indicates its class and ID will be to the value of the# given object's {#to_key} or {#id} method (in that order).## @return [true,false]defobject_reference?@value[:object_ref].to_s!='nil'end# Source code for the contents of the node's object reference.## @see http://haml.info/docs/yardoc/file.REFERENCE.html#object_reference_# @return [String,nil] string source of object reference or `nil` if it has# not been defineddefobject_reference_source@value[:object_ref][/\A\[(.*)\]\z/,1]ifobject_reference?end# The attributes given to the tag parsed into a Ruby syntax tree.## @return [ParsedRuby] syntax tree in the form returned by Parser gemdefparsed_attributesHamlLint::ParsedRuby.new(HamlLint::RubyParser.new.parse(hash_attributes_source||''))end# The Ruby script contents of a tag parsed into a syntax tree.## @return [ParsedRuby] syntax tree in the form returned by Parser gemdefparsed_scriptHamlLint::ParsedRuby.new(HamlLint::RubyParser.new.parse(script||''))end# Whether this node had a `<` after it signifying that outer whitespace# should be removed.## @return [true,false]defremove_inner_whitespace?@value[:nuke_inner_whitespace]end# Whether this node had a `>` after it signifying that outer whitespace# should be removed.## @return [true,false]defremove_outer_whitespace?!!@value[:nuke_outer_whitespace]end# Returns the script source that will be evaluated to produce this tag's# inner content, if any.## @return [String]defscript(@value[:value]if@value[:parse])||''endprivatedefexisting_attributesparsed_attrs=parsed_attributesreturn{}unlessparsed_attrs.respond_to?(:children)parsed_attrs.children.collectdo|parsed_attribute|parsed_attribute.children.first.to_a.firstendendendend