<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd" [
  <!ENTITY a_valid_char_in_macro_names "A-Za-z0-9_">
]>
<language name="RPM Spec" version="1.4" kateversion="2.4" section="Other" extensions="*.spec" mimetype="" license="public domain">

  <highlighting>
    
    <contexts>
      <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
        <!-- Handle tags: -->
        <RegExpr attribute="Tag" context="tag_line" String="((Name|Summary|Summary\(.*\)|Version|Epoch|Serial|Release|Group|Copyright|License|Icon|Vendor|Distribution|Packager|Url|URL|Prefix|Conflicts|Obsoletes|Provides|AutoReqProv|PreReq|Requires|Requires\(.*\)|Enhances|Suggests|Recommends|BuildArch|BuildArchitectures|BuildConflicts|BuildRequires|BuildRoot|ExclusiveArch|ExclusiveOs|ExcludeArch|ExcludeOs|Source\d*|Nosource|Patch\d*|Nopatch)[ \t]*:)" column="0" />
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
        <!-- Handle comments. "# norootforbuild" is displayed like a tag. -->
        <DetectChar context="Comment" char="#" column="0" lookAhead="true" />
      </context>
      
      <context attribute="Normal Text" lineEndContext="#stay" name="command section">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
        <!-- Handle comments. "# norootforbuild" is displayed like a tag. -->
        <DetectChar context="Comment" char="#" column="0" lookAhead="true" />
      </context>
      
      <context attribute="Normal Text" lineEndContext="#stay" name="changelog section">
        <Detect2Chars attribute="Value" context="tag_line" char="-" char1=" " column="0" />
        <Detect2Chars attribute="Tag" context="changelog_header" char="*" char1=" " column="0" />
        <!-- Handle comments. "# norootforbuild" is displayed like a tag. -->
        <DetectChar context="Comment" char="#" column="0" lookAhead="true" />
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>
      
      <context attribute="Normal" lineEndContext="description section" name="description section start line">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>
      
      <context attribute="Value" lineEndContext="#stay" name="description section">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
        <!-- Handle comments. "# norootforbuild" is displayed like a tag. -->
        <DetectChar context="Comment" char="#" column="0" lookAhead="true" />
      </context>

      <context attribute="Comment" lineEndContext="#pop" name="Comment">
        <StringDetect attribute="Tag" context="every_non_whitespace_is_error" String="# norootforbuild" column="0" />
        <!-- Error on single percent ("%"), but not on double percent ("%%"): -->
        <Detect2Chars attribute="Comment" char="%" char1="%"/>
        <DetectChar attribute="Error" char="%"/>
        <!-- Alert on "TODO" -->
        <StringDetect attribute="Alert" String="TODO" insensitive="true" />
        <StringDetect attribute="Alert" String="FIXME" insensitive="true" />
      </context>
      
      <context attribute="Normal Text" lineEndContext="#pop" name="every_non_whitespace_is_error">
        <RegExpr attribute="Error" context="#stay" String="[^\s]" />
      </context>
      
      <context attribute="Value" lineEndContext="#pop" name="tag_line">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>
      
      <context attribute="Tag" lineEndContext="#pop" name="changelog_header">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>
      
      <context attribute="Normal Text" lineEndContext="#pop" name="parameters after keyword">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>
      
      <!-- Expects a string starting with % or $ (otherwise, this context will fail!). -->
      <context lineEndContext="#pop" name="handle_percent">
        <Detect2Chars attribute="Escaped character" context="#pop" char="%" char1="%"/>
        <!-- Keywords: -->
        <RegExpr attribute="Keyword" context="parameters after keyword" String="%(if|ifarch|ifnarch|ifos|ifnos)(?=($| |\t))" column="0" beginRegion="if_block" />
        <RegExpr attribute="Keyword" context="every_non_whitespace_is_error" String="%else(?=($| |\t))" column="0" />
        <RegExpr attribute="Keyword" context="every_non_whitespace_is_error" String="%endif(?=($| |\t))" column="0" endRegion="if_block" />
        <RegExpr attribute="Keyword" context="parameters after keyword" String="%(define|global|undefine)(?=($| |\t))" column="0" />
        <!-- If a command section starts (=section macro at begin of the line), we switch
             to the corresponding context. There will never be a return to THIS context...: -->
        <RegExpr attribute="Section" context="Normal" String="%package(?=($| |\t))" column="0" />
        <RegExpr attribute="Section" context="description section start line" String="%description(?=($| |\t))" column="0" />
        <RegExpr attribute="Section" context="command section" String="%(prep|build|pre|preun|install|post|postun|clean|files|trigger|triggerin|triggerun|triggerpostun|verifyscript)(?=($| |\t))" column="0" />
        <RegExpr attribute="Section" context="changelog section" String="%changelog(?=($| |\t))" column="0" />
        <!-- Handle normal macros -->
        <RegExpr attribute="Error" context="#pop" String="%([\{\(][ \t]{0,}){0,1}(if|ifarch|ifnarch|ifos|ifnos|else|endif|define|global|undefine|package|description|prep|build|pre|preun|install|post|postun|clean|files|trigger|triggerin|triggerun|triggerpostun|verifyscript|changelog)(?=($|[^&a_valid_char_in_macro_names;]))" />
        <RegExpr attribute="Macro call" context="macro content in parenthesis" String="%[&a_valid_char_in_macro_names;]*\(" />
        <RegExpr attribute="Macro call" context="macro content in braces" String="(%|\$)\{" />
        <RegExpr attribute="Macro call" context="#pop" String="(%|\$)([&a_valid_char_in_macro_names;]{1,}|\*|\#)(?=($|[^&a_valid_char_in_macro_names;]))" /> <!-- ')' and '}' are only allowed as macro terminator in the lookahead because when you do something like %__make %{?jobs:-j %jobs}, then the "%jobs" is a valid macro. However, the disadvantage is that a line like "%abc} isn't marked as error. But it is to complicate to distinguish this properly. -->
        <RegExpr attribute="Error" context="#pop" String="(%|\$)([&a_valid_char_in_macro_names;]{1,}|\*|\#)" />
        <AnyChar attribute="Error" context="#pop" String="%$" /> <!-- a single % or $ is also an error -->
      </context>
      
      <context attribute="Macro call" lineEndContext="#pop#pop" name="macro content in parenthesis">
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
        <DetectChar attribute="Macro call" context="#pop#pop" char=")" />
        <AnyChar attribute="Error" context="#stay" String="({}" />
      </context>
      
      <context attribute="Macro call" lineEndContext="#pop#pop" name="macro content in braces">
        <DetectChar attribute="Macro call" context="#pop#pop" char="}" />
        <AnyChar attribute="Error" context="#stay" String="({)" />
        <!-- Handle % -->
        <AnyChar context="handle_percent" String="%$" lookAhead="true" />
      </context>

    </contexts>
    
    <itemDatas>
      <itemData name="Normal Text" defStyleNum="dsNormal"/>
      <itemData name="Keyword"  defStyleNum="dsKeyword"/>
      <itemData name="Comment"  defStyleNum="dsComment"/>
      <itemData name="Tag" defStyleNum="dsDataType"/>
      <itemData name="Value"  defStyleNum="dsDecVal"/>
      <itemData name="Error" defStyleNum="dsError"/>
      <itemData name="Macro call" defStyleNum="dsFunction"/>
      <itemData name="Section" defStyleNum="dsRegionMarker"/>
      <itemData name="Alert" defStyleNum="dsAlert"/>
      <itemData name="Escaped character" defStyleNum="dsChar"/>
    </itemDatas>
    
  </highlighting>
  
  <general>
    <keywords casesensitive="1" />
  </general>
  
</language>
