<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language
[
  <!ENTITY tab               "&#x9;">
  <!ENTITY white             "&tab; ">
  <!ENTITY separator         "&white;|&amp;;&lt;&gt;()'&quot;">  <!-- token separator as char list -->
  <!ENTITY sym_sep           "*?$\\{}&separator;">
  <!ENTITY path_sym_sep      "/&sym_sep;">
  <!ENTITY blocksep          "|;&amp;#)}&lt;&gt;">

  <!ENTITY inbrace_text      "[^,\\{}()'&quot;$]*+">
  <!-- fake recursive, only supports 2 levels of brace e.g. {ab{cd}ef} -->
  <!ENTITY inbrace_text2     "(&inbrace_text;|\{&inbrace_text;\})*+">
  <!ENTITY nobrace_text      "\{&inbrace_text2;\}">

  <!-- see ampersand-nobg-in-token feature
  echo a&b
        ~ no special
  echo a&
        ~ special
  -->
  <!ENTITY amp_sym_sep       ";|&white;&amp;&lt;&gt;">
  <!ENTITY amp_sep           "([&amp_sym_sep;]|$)">
  <!ENTITY text_amp          "(&amp;(?=[^&amp_sym_sep;]))">

  <!ENTITY text_sym          "(&text_amp;|&nobrace_text;)">

  <!ENTITY end_of_text_base  "$|[&white;|;&lt;&gt;]|&amp;&amp_sep;">
  <!ENTITY is_end_of_text    "(?=&end_of_text_base;)">

  <!ENTITY varname           "\w+">
  <!-- https://fishshell.com/docs/current/language.html#escaping-characters
  note: \b is missing -->
  <!ENTITY escape_chars      "\\([abefnrtv $\\*?~#(){}[\]&lt;&gt;&amp;|;&quot;']|[0-7]{1,3}|[xX][A-Fa-f0-9]{1,2}|u[A-Fa-f0-9]{1,4}|U[A-Fa-f0-9]{1,8}|c[a-zA-Z])|\\(?![xXuUc])">
  <!ENTITY simplevar         "\$+(\w*+|\(([-\w\s$]|\\.)*+\))(\[[-+&white;0-9]\])*+">

  <!ENTITY text              "([^&sym_sep;]++|&text_sym;)*+">
  <!ENTITY text_in_brace     "([^,&sym_sep;]++|&text_sym;)*+">
  <!ENTITY option_name       "[-A-Za-z0-9]+">

  <!ENTITY homedir           "~[-A-Za-z0-9._]*(?=/|&end_of_text_base;)">

  <!-- consume string, stop at / -->
  <!ENTITY path_dir_sq       "'([^/'\\]*|\\.)*+'?+">
  <!ENTITY path_dir_dq       '"([^/"\\$]*|\\.|&simplevar;)*+"?+'>
  <!ENTITY path_prefix       "([^&path_sym_sep;]++|&text_sym;])*+">
  <!ENTITY path_syms         "([^&path_sym_sep;]++|&path_dir_sq;|&path_dir_dq;|&text_sym;|&simplevar;|\\.)*+">
  <!ENTITY path_dot          "(^|(?&lt;=[&white;]))[.]{1,2}(?=&is_end_of_text;)">
  <!ENTITY path              "&path_dot;|(&path_prefix;(/+|(?=&path_syms;[/*?])))++">

  <!ENTITY command_sep_sym   "/$\\{}&white;|;&lt;&gt;()'&quot;">
  <!ENTITY command_dir_prefix "([^*?&command_sep_sym;]++|\\ |&nobrace_text;])*+">
  <!ENTITY command_dir_syms  "([^&command_sep_sym;]++|&path_dir_sq;|&path_dir_dq;|&nobrace_text;|&simplevar;|\\.)*+">
  <!ENTITY command_dir       "(&command_dir_prefix;(/+|(?=&command_dir_syms;/)))++">

  <!-- all unix commands and builtins are ascii with '-' and '+' (e.g. g++) -->
  <!ENTITY simple_command    "[-+\w]++">
  <!ENTITY is_end_of_simple_cmd "(?=[&white;|;&lt;&gt;)}]|&amp;&amp_sep;|$)">
]>

<language name="Fish" version="12" kateversion="6.22" section="Scripts" extensions="*.fish;fishd.*" mimetype="application/x-fish" casesensitive="1" author="Arnd Diestelhorst (adiestelhorst@m-xchg.de)" license="LGPL">

  <!-- Based on Fish 4.6
  https://fishshell.com/docs/current/relnotes.html
  https://fishshell.com/docs/current/language.html
  https://fishshell.com/docs/current/commands.html

  Assume features:
  - ampersand-nobg-in-token on ('&' in 'echo a&b' is a regular character)
  - qmark-noglob off ('?' is a globbing character)
  -->

  <highlighting>

    <list name="commands">
      <item>abbr</item>
      <item>alias</item>
      <item>argparse</item>
      <item>bg</item>
      <item>bind</item>
      <item>block</item>
      <item>builtin</item>
      <item>cd</item>
      <item>cdh</item>
      <item>command</item>
      <item>commandline</item>
      <item>complete</item>
      <item>contains</item>
      <item>count</item>
      <item>dirs</item>
      <item>disown</item>
      <item>echo</item>
      <item>emit</item>
      <item>end</item>
      <item>eval</item>
      <item>exec</item>
      <item>exit</item>
      <item>fg</item>
      <item>fish</item>
      <item>fish_config</item>
      <item>fish_indent</item>
      <item>fish_key_reader</item>
      <item>fish_mode_prompt</item>
      <item>fish_opt</item>
      <item>fish_prompt</item>
      <item>fish_right_prompt</item>
      <item>fish_update_completions</item>
      <item>fish_vi_mode</item>
      <item>funced</item>
      <item>funcsave</item>
      <item>functions</item>
      <item>help</item>
      <item>history</item>
      <item>isatty</item>
      <item>jobs</item>
      <item>math</item>
      <item>nextd</item>
      <item>open</item>
      <item>prevd</item>
      <item>printf</item>
      <item>psub</item>
      <item>pushd</item>
      <item>random</item>
      <item>read</item>
      <item>realpath</item>
      <item>return</item>
      <item>set</item>
      <item>set_color</item>
      <item>source</item>
      <item>status</item>
      <item>string</item>
      <item>suspend</item>
      <item>test</item>
      <item>trap</item>
      <item>type</item>
      <item>ulimit</item>
      <item>umask</item>
      <item>vared</item>
    </list>

    <list name="commandsnoargs">
      <item>break</item>
      <item>breakpoint</item>
      <item>continue</item>
      <item>dirh</item>
      <item>false</item> 
      <item>popd</item>
      <item>prompt_pwd</item>
      <item>pwd</item>
      <item>true</item>
    </list>

    <list name="commandsstay">
      <item>and</item>
      <item>not</item>
      <item>or</item>
    </list>

    <list name="stringsubcommands">
      <item>collect</item>
      <item>escape</item>
      <item>join</item>
      <item>join0</item>
      <item>length</item>
      <item>lower</item>
      <item>match</item>
      <item>pad</item>
      <item>repeat</item>
      <item>replace</item>
      <item>shorten</item>
      <item>split</item>
      <item>split0</item>
      <item>sub</item>
      <item>trim</item>
      <item>unescape</item>
      <item>upper</item>
    </list>

    <list name="unixcommands">
      <include>unixcommands##Bash</include>
      <item>kill</item>
    </list>

    <contexts>
      <context name="Root" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <DetectSpaces attribute="Normal Text"/>
        <AnyChar attribute="Separator" String="|&amp;;"/>
        <IncludeRules context="FindComment"/>

        <StringDetect String="[&tab;" attribute="Builtin" context="ArgumentsTestBrack"/>
        <StringDetect String="[ " attribute="Builtin" context="ArgumentsTestBrack"/>
        <StringDetect String="!" lookAhead="1" context="NotOrCommand"/>
        <StringDetect String="{" attribute="Keyword" context="Block" beginRegion="block"/>
        <!-- \ newline is a no op in this context -->
        <LineContinue attribute="Escape"/>

        <AnyChar String="*?'&quot;$" lookAhead="1" context="Arguments!FieldSep!CommandPart"/>
        <RegExpr String="&simple_command;&is_end_of_simple_cmd;" lookAhead="1" context="CommandSimple"/>
        <StringDetect String=". " attribute="Builtin" context="Arguments"/>
        <RegExpr String="&varname;(?==)" attribute="Variable" context="SetEnv"/>
        <RegExpr String="&command_dir;" attribute="CommandPath" context="Arguments!FieldSep!CommandPart"/>

        <AnyChar String=")}&lt;&gt;" attribute="Error" context="Arguments!FieldSep"/>
        <AnyChar String="(" attribute="Error" context="#pop"/>
      </context>

      <!-- ! cmd -->
      <context name="NotOrCommand" attribute="Normal Text" fallthroughContext="#pop!Arguments!FieldSep!CommandPart">
        <RegExpr String="!&is_end_of_text;" attribute="Operator" context="#pop"/>
      </context>

      <!-- { cmd } -->
      <context name="Block" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <StringDetect String="}" attribute="Keyword" context="#pop!ArgumentsNone!FieldSep" endRegion="block"/>
        <IncludeRules context="Root"/>
      </context>


      <!--
      @{ Set env command
      -->
      <!-- var=value cmd -->
      <context name="SetEnv" attribute="Normal Text">
        <DetectChar char="=" attribute="Builtin" context="#pop!VarValue"/>
      </context>
      <context name="VarValue" attribute="Normal Text">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindFishExpr"/>
      </context>
      <!--
      @} Set env command
      -->


      <!--
      @{ Comment
      -->
      <context name="FindComment" attribute="Comment">
        <DetectChar char="#" attribute="Comment" context="Comment"/>
      </context>

      <context name="Comment" attribute="Comment" lineEndContext="#pop">
        <DetectSpaces/>
        <IncludeRules context="##Comments"/>
        <DetectIdentifier/>
      </context>
      <!--
      @} Comment
      -->


      <!--
      @{ Command
      -->

      <context name="CommandSimple" attribute="Normal Text">
        <WordDetect attribute="Builtin" String="test" context="#pop!test!FieldSep"/>
        <WordDetect attribute="Builtin" String="set" context="#pop!set!FieldSep"/>
        <WordDetect attribute="Control Flow" String="if" context="#pop!if" beginRegion="if"/>
        <WordDetect attribute="Control Flow" String="for" context="#pop!forbody!forin!FieldSep!VarNameWithoutBraceOrError!BlockSep4" beginRegion="for"/>
        <WordDetect attribute="Keyword" String="function" context="#pop!funcbody!funcopts!FieldSep!funcname!BlockSep4" beginRegion="function"/>
        <WordDetect attribute="Control Flow" String="switch" context="#pop!switchbody!switch!BlockSep2" beginRegion="switch"/>
        <WordDetect attribute="Control Flow" String="while" context="#pop!while" beginRegion="while"/>
        <WordDetect attribute="Control Flow" String="return" context="#pop!Arguments!FieldSep"/>
        <WordDetect attribute="Control Flow" String="break" context="#pop!ArgumentsNone!FieldSep"/>
        <WordDetect attribute="Control Flow" String="continue" context="#pop!ArgumentsNone!FieldSep"/>
        <WordDetect attribute="Builtin" String="string" context="#pop!string!FieldSep"/>
        <WordDetect attribute="Builtin" String="read" context="#pop!read_vared!FieldSep"/>
        <WordDetect attribute="Builtin" String="vared" context="#pop!read_vared!FieldSep"/>
        <WordDetect attribute="Keyword" String="begin" context="#pop!beginbody" beginRegion="begin"/>
        <WordDetect attribute="Builtin" String="command" context="#pop!command_builtin!FieldSep"/>
        <WordDetect attribute="Builtin" String="builtin" context="#pop!command_builtin!FieldSep"/>
        <WordDetect attribute="Builtin" String="eval" context="#pop!eval!FieldSep"/>
        <WordDetect attribute="Builtin" String="time" context="#pop"/>
        <WordDetect attribute="Builtin" String="exec" context="#pop"/>
        <keyword String="commands" attribute="Builtin" context="#pop!Arguments!FieldSep"/>
        <keyword String="commandsnoargs" attribute="Builtin" context="#pop!ArgumentsNone!FieldSep"/>
        <keyword String="commandsstay" attribute="Control Flow" context="#pop"/>
        <keyword String="unixcommands" attribute="UnixCommand" context="#pop!Arguments!FieldSep"/>
        <RegExpr String="&simple_command;" attribute="Command" context="#pop!Arguments!FieldSep"/>
      </context>

      <context name="CommandPart" attribute="CommandPath" lineEndContext="#pop#pop#pop" fallthroughContext="#pop!CommandName">
        <IncludeRules context="CommandName"/>
        <RegExpr String="&command_dir;" attribute="CommandPath"/>
        <RegExpr String="(?&lt;=^|/['&quot;]?)&simple_command;&is_end_of_simple_cmd;" lookAhead="1" context="CommandPartCommand"/>
      </context>
      <context name="CommandPartCommand" attribute="Error">
        <keyword String="unixcommands" attribute="UnixCommand" context="#pop#pop"/>
        <RegExpr String="&simple_command;" attribute="Command" context="#pop#pop"/>
      </context>

      <context name="CommandName" attribute="Command" lineEndContext="#pop#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindStrings"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindCommandEscape"/>
        <IncludeRules context="FindGlob"/>
        <IncludeRules context="FindBraceExpansion"/>
        <DetectChar char="(" attribute="Error" context="#pop!CommandSubstitution"/>
      </context>

      <!--
      @} Command
      -->


      <!--
      @{ Builtin
      -->

      <!--
      command -opt cmd
              ~~~~~~~~
      -->
      <context name="command_builtin" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
        <IncludeRules context="FindOption_then_FieldSep"/>
      </context>

      <!--
      eval (command) ...
      eval command ...
           ~~~~~~~~~~~~~
      -->
      <context name="eval" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
        <StringDetect String="(" attribute="Keychar" context="#pop!Arguments!CommandSubstitution"/>
        <StringDetect String="{" lookAhead="1" context="#pop!Arguments!FieldSep!CommandName"/>
      </context>


      <!--
      set name values...
      -->
      <context name="set" attribute="Error" lineEndContext="#pop"
               fallthroughContext="#pop!Arguments!FieldSep!VarNameWithSubscriptOrError">
        <IncludeRules context="FindOption_then_FieldSep"/>
      </context>


      <!--
      string func params
      -->
      <context name="string" attribute="Error" lineEndContext="#pop" fallthroughContext="#pop!Arguments">
        <keyword String="stringsubcommands" attribute="Builtin" context="#pop!Arguments!FieldSep"/>
        <DetectIdentifier attribute="Error" context="#pop!Arguments!FieldSep"/>
        <Int attribute="Error" context="#pop!Arguments!FieldSep"/>
      </context>


      <!--
      read -xy var
      -->
      <context name="read_vared" attribute="Error" lineEndContext="#pop" fallthroughContext="FieldSep!VarNameOrError">
        <IncludeRules context="FindOption_then_FieldSep"/>
      </context>


      <!--
      test $var -eq abc
      -->
      <context name="test" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="FieldSep!ArgMaybePath">
        <AnyChar String="*?" attribute="Glob" context="FieldSep!ArgPath"/>
        <DetectChar char="~" lookAhead="1" context="FieldSep!ArgMaybeHome"/>
        <RegExpr String="-[bcdefgGkLOprsStuwx]&is_end_of_text;" attribute="Operator"
                 context="DirectPath_then_FieldSep"/>
        <RegExpr String="(-[nzao]|!|!?=|-eq|-ne|-gt|-ge|-lt|-le|-nt|-ot|-ef)&is_end_of_text;"
                 attribute="Operator" context="FieldSep"/>
      </context>


      <!--
      [ $var -eq abc ]
      -->
      <context name="ArgumentsTestBrack" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="FieldSep!ArgMaybePath">
        <IncludeRules context="test"/>
        <RegExpr String="(^\]|(?&lt;=[&white;])\])(?=\\?$|[&white;|&amp;;&lt;&gt;])" attribute="Builtin" context="#pop!ArgumentsNone!FieldSep"/>
      </context>


      <!--
      begin cmd ; end
      -->
      <context name="beginbody" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="end" attribute="Keyword" context="#pop" endRegion="begin"/>
        <IncludeRules context="Root"/>
      </context>


      <!--
      if CONDITION; COMMANDS_TRUE ...;
      [else if CONDITION2; COMMANDS_TRUE2 ...;]
      [else; COMMANDS_FALSE ...;]
      end
      -->
      <context name="if" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect attribute="Control Flow" String="else" context="else" endRegion="if" beginRegion="if"/>
        <WordDetect String="end" attribute="Control Flow" context="#pop" endRegion="if"/>
        <IncludeRules context="Root"/>
      </context>

      <context name="else" attribute="Normal Text" fallthroughContext="#pop">
        <IncludeRules context="FindEmpty"/>
        <WordDetect String="if" attribute="Control Flow" context="#pop"/>
      </context>


      <!--
      for VARNAME in [VALUES ...]; COMMANDS ...; end
      -->
      <context name="forin" attribute="Error"
               lineEndContext="#pop!Arguments!BlockSep2"
               fallthroughContext="#pop!Arguments!BlockSep2">
        <WordDetect attribute="Keyword" String="in" context="#pop!Arguments!BlockSep2"/>
        <DetectIdentifier attribute="Error" context="#pop!Arguments!BlockSep2"/>
        <Int attribute="Error" context="#pop!Arguments!BlockSep2"/>
      </context>
      <context name="forbody" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="end" attribute="Control Flow" context="#pop" endRegion="for"/>
        <IncludeRules context="Root"/>
      </context>


      <!--
      function NAME [OPTIONS]; BODY; end
      -->
      <context name="funcname" attribute="Command" lineEndContext="#pop#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <DetectChar char="/" attribute="Error"/>
        <IncludeRules context="FindFishExpr"/>
      </context>

      <context name="funcopts" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="FieldSep!func_arg">
        <IncludeRules context="PopWhenSep"/>
        <DetectChar char="-" lookAhead="1" context="FieldSep!func_Option"/>
      </context>
      <context name="func_arg" attribute="Normal Text" lineEndContext="#pop#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindFishExpr"/>
      </context>

      <context name="func_Option" attribute="Normal Text">
        <RegExpr attribute="Option" String="-d[&white;]*(?=[&quot;']|\\$)|--description(?=[=&white;])" context="funcdesc" beginRegion="doc"/>
        <DetectChar char="-" attribute="Option" context="#pop!Option"/>
      </context>
      <!-- -d "..." / - -description="..." / - -description "..." -->
      <context name="funcdesc" attribute="Function Doc">
        <DetectChar context="DocstringSQ" char="'"/>
        <DetectChar context="DocstringDQ" char="&quot;"/>
        <DetectChar attribute="Option" context="funcdescLong" char="="/>
        <AnyChar context="funcdescSpacing" String="&white;"/>
        <LineContinue attribute="Escape" context="funcdescSpacing"/>
      </context>
      <context name="funcdescLong" attribute="Function Doc" fallthroughContext="#pop#pop#pop!ArgMaybePath">
        <DetectChar context="#pop!DocstringSQ" char="'"/>
        <DetectChar context="#pop!DocstringDQ" char="&quot;"/>
      </context>
      <context name="funcdescSpacing" attribute="Function Doc" fallthroughContext="funcdescClose" lineEndContext="funcdescClose">
        <IncludeRules context="funcdescLong"/>
        <DetectSpaces/>
        <LineContinue attribute="Escape"/>
      </context>
      <context name="funcdescClose" attribute="Function Doc" fallthroughContext="#pop#pop#pop#pop">
        <RegExpr String="." lookAhead="1" context="#pop#pop#pop#pop" endRegion="doc"/>
      </context>
      <context name="DocstringSQ" attribute="Function Doc">
        <DetectChar attribute="Function Doc" context="#pop#pop#pop" char="'" endRegion="doc"/>
        <IncludeRules context="StringSQ"/>
      </context>
      <context name="DocstringDQ" attribute="Function Doc">
        <DetectChar attribute="Function Doc" context="#pop#pop#pop" char="&quot;" endRegion="doc"/>
        <IncludeRules context="StringDQ"/>
      </context>

      <context name="funcbody" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="end" attribute="Keyword" context="#pop" endRegion="function"/>
        <IncludeRules context="Root"/>
      </context>


      <!--
      switch VALUE; [case [GLOB ...]; [COMMANDS ...]; ...] end
      -->
      <context name="switch" attribute="Normal Text" lineEndContext="#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindFishExpr"/>
      </context>

      <context name="switchbody" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="case" attribute="Control Flow" context="#pop!casebody!Arguments!FieldSep" beginRegion="case"/>
        <WordDetect String="end" attribute="Control Flow" context="#pop" endRegion="switch"/>
        <IncludeRules context="Root"/>
      </context>

      <context name="casebody" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="case" attribute="Control Flow" context="Arguments!FieldSep" endRegion="case" beginRegion="case"/>
        <WordDetect String="end" lookAhead="1" context="switchend" endRegion="case"/>
        <IncludeRules context="Root"/>
      </context>

      <context name="switchend" attribute="Normal Text">
        <WordDetect String="end" attribute="Control Flow" context="#pop#pop" endRegion="switch"/>
      </context>


      <!--
      while CONDITION ; ... ; end
      -->
      <context name="while" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <WordDetect String="end" attribute="Control Flow" context="#pop" endRegion="while"/>
        <IncludeRules context="Root"/>
      </context>

      <!--
      @} Builtin
      -->


      <!--
      @{ Option (-xy)
      -->
      <context name="FindOption_then_FieldSep" attribute="Normal Text">
        <DetectChar char="-" attribute="Option" context="FieldSep!Option"/>
      </context>
      <context name="Option" attribute="Option" lineEndContext="#pop#pop" fallthroughContext="#pop!ArgMaybePath">
        <IncludeRules context="PopWhenSep"/>
        <DetectChar char="=" attribute="Operator" context="#pop!ArgMaybePath"/>
        <RegExpr String="&option_name;" attribute="Option"/>
        <IncludeRules context="FindFishExpr"/>
      </context>
      <!--
      @} Option (-xy)
      -->


      <!--
      @{ Strings
      https://fishshell.com/docs/current/language.html#quotes
      -->

      <!-- FindStrings looks for single and double quoted strings -->
      <context name="FindStrings" attribute="Normal Text">
        <DetectChar char="'" attribute="String SingleQ" context="StringSQ"/>
        <DetectChar char='"' attribute="String DoubleQ" context="StringDQ"/>
      </context>

      <!-- StringSQ consumes anything till ' -->
      <context name="StringSQ" attribute="String SingleQ">
        <DetectChar char="'" attribute="String SingleQ" context="#pop"/>
        <!--no line continuation here-->
        <StringDetect String="\'" attribute="Escape"/>
        <StringDetect String="\\" attribute="Escape"/>
      </context>

      <!-- StringDQ consumes anything till ", substitutes vars and expressions -->
      <context name="StringDQ" attribute="String DoubleQ">
        <DetectChar char='"' attribute="String DoubleQ" context="#pop"/>
        <StringDetect String='\"' attribute="Escape"/>
        <StringDetect String="\$" attribute="Escape"/>
        <StringDetect String="\\" attribute="Escape"/>
        <LineContinue attribute="Escape"/>
        <IncludeRules context="FindVarExpansion"/>
      </context>

      <!--
      @} Strings
      -->


      <!--
      @{ $var and $(command)
      -->

      <context name="FindVarExpansion" attribute="Normal Text">
        <StringDetect String="$" lookAhead="1" context="Variable"/>
      </context>

      <!-- $(command) -->
      <context name="DollarCommandSubstitution" attribute="Variable">
        <StringDetect String="$" attribute="Variable"/>
        <StringDetect String="(" attribute="Keychar" context="#pop#pop!CommandSubstitution"/>
      </context>

      <!-- $var -->
      <context name="Variable" attribute="Variable" lineEndContext="#pop" fallthroughContext="#pop">
        <StringDetect String="$(" lookAhead="1" context="DollarCommandSubstitution"/>
        <RegExpr String="(\$+&varname;|\$(?=\\$))++" attribute="Variable" context="#pop!VarName"/>
        <StringDetect String="$$" lookAhead="1" context="PrefixDollar!PrefixDollar2"/>
        <StringDetect String="$" attribute="Error" context="#pop!VarName"/>
      </context>
      <context name="VarName" attribute="Error" lineEndContext="#pop" fallthroughContext="#pop">
        <DetectChar char="[" attribute="Index" context="#pop!InSubscript"/>
        <LineContinue attribute="Escape" context="VarNameContinue"/>
      </context>
      <context name="VarNameContinue" attribute="Error" lineEndContext="#pop#pop" fallthroughContext="#pop#pop">
        <RegExpr String="\w+" attribute="Variable" context="#pop"/>
      </context>
      <!-- $$$
           ~~  Variable
             ~ Error
      -->
      <context name="PrefixDollar" attribute="Variable">
        <StringDetect String="$$" lookAhead="1" context="PrefixDollar2"/>
        <StringDetect String="$" attribute="Error" context="#pop"/>
      </context>
      <context name="PrefixDollar2" attribute="Variable">
        <StringDetect String="$" attribute="Variable" context="#pop"/>
      </context>

      <!-- var[1..2+3-1]
              ~~~~~~~~~~
      -->
      <context name="FindSubscript" attribute="Index">
        <DetectChar char="[" attribute="Index" context="InSubscript"/>
      </context>
      <context name="Subscript" attribute="Index" lineEndContext="#pop" fallthroughContext="#pop">
        <LineContinue attribute="Escape"/>
        <DetectChar attribute="Index" context="#pop!InSubscript" char="["/>
      </context>
      <context name="InSubscript" attribute="Error" lineEndContext="#pop" fallthroughContext="#pop">
        <StringDetect String="]" attribute="Index" context="#pop!Subscript"/>
        <StringDetect String="..." attribute="Error"/>
        <RegExpr String="(\.\.(?=[^.]|$)|[-+]?[0-9]++|\s)*+" attribute="Index"/>
        <AnyChar String="-+" attribute="Error"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindSubstitutions"/>
        <IncludeRules context="FindStrings"/>
        <LineContinue attribute="Escape"/>
        <RegExpr String="[^&path_sym_sep;]" attribute="Error" context="#pop"/>
      </context>

      <!--
      @} $var and $(command)
      -->


      <!--
      @{ Variable name
      -->
      <context name="VarNameOrError" attribute="Error" lineEndContext="#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <RegExpr String="&varname;" attribute="Variable"/>
        <IncludeRules context="FindFishExpr"/>
      </context>

      <context name="VarNameWithoutBraceOrError" attribute="Error" lineEndContext="#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <RegExpr String="&varname;" attribute="Variable"/>
        <IncludeRules context="FindFishExprWithoutBrace"/>
      </context>

      <context name="VarNameWithSubscriptOrError" attribute="Error" lineEndContext="#pop#pop#pop">
        <IncludeRules context="FindSubscript"/>
        <IncludeRules context="VarNameOrError"/>
      </context>
      <!--
      @} Variable name
      -->


      <!--
      @{ Spaces and separators
      -->
      <!-- #pop#pop when end command, otherwise #pop -->
      <context name="FieldSep" attribute="Normal Text"
               lineEndContext="#pop#pop" fallthroughContext="#pop">
        <IncludeRules context="FindEmpty"/>
        <DetectChar char="#" attribute="Comment" context="#pop#pop!Comment"/>
        <AnyChar String="&amp;&lt;&gt;0123456789" lookAhead="1" context="MaybeRedirection"/>
        <AnyChar String="|;" attribute="Separator" context="#pop#pop"/>
        <AnyChar String=")}" lookAhead="1" context="#pop#pop"/>
      </context>

      <!-- #pop(2) when special chars or end command, otherwise #pop -->
      <context name="BlockSep2" attribute="Normal Text"
               lineEndContext="#pop#pop" fallthroughContext="#pop">
        <IncludeRules context="FindEmpty"/>
        <AnyChar String="&blocksep;" lookAhead="1" context="#pop#pop"/>
      </context>
      <!-- #pop(4) when special chars or end command, otherwise #pop -->
      <context name="BlockSep4" attribute="Normal Text"
               lineEndContext="#pop#pop#pop#pop" fallthroughContext="#pop">
        <IncludeRules context="FindEmpty"/>
        <AnyChar String="&blocksep;" lookAhead="1" context="#pop#pop#pop#pop"/>
      </context>

      <context name="FindEmpty" attribute="Normal Text">
        <DetectSpaces attribute="Normal Text"/>
        <LineContinue attribute="Escape" context="CommentSep"/>
      </context>
      <!--
      echo before comment \
        # comment
        # comment 2
        after comment # just another argument -->
      <context name="CommentSep" attribute="Normal Text" fallthroughContext="#pop">
        <DetectSpaces attribute="Normal Text"/>
        <LineContinue attribute="Escape"/>
        <IncludeRules context="FindComment"/>
      </context>

      <!-- #pop when cmd separator -->
      <context name="PopWhenSep" attribute="Normal Text">
        <AnyChar String="&white;|;&lt;&gt;)}" lookAhead="1" context="#pop"/>
        <AnyChar String="&amp;" lookAhead="1" context="PopWhenSep_amp"/>
      </context>
      <context name="PopWhenSep_amp" attribute="Normal Text" fallthroughContext="#pop#pop">
        <RegExpr String="&text_amp;|" context="#pop"/>
      </context>
      <!--
      @} Spaces and separators
      -->


      <!--
      @{ Redirection
      -->
      <context name="MaybeRedirection" attribute="Normal Text" lineEndContext="#pop#pop#pop" fallthroughContext="#pop#pop">
        <StringDetect String="&amp;&amp;" attribute="Separator" context="#pop#pop#pop"/>
        <StringDetect String="&amp;|" attribute="Redirection" context="#pop#pop#pop"/>
        <StringDetect String="&amp;>>" attribute="Separator" context="Redirect"/>
        <StringDetect String="&amp;>" attribute="Separator" context="Redirect"/>
        <StringDetect String="&amp;" attribute="Separator" context="#pop#pop#pop"/>
        <RegExpr String="[0-9]*(>>?|&lt;)" attribute="Redirection" context="Redirect"/>
      </context>

      <context name="Redirect" attribute="Redirection" lineEndContext="#pop#pop#pop#pop" fallthroughContext="#pop#pop#pop!DirectPath_then_FieldSep">
        <DetectChar char="?" attribute="Redirection"
                    context="#pop#pop#pop!DirectPath_then_FieldSep"/>
        <DetectChar char="|" attribute="Redirection" context="#pop#pop#pop#pop"/>
        <DetectChar char="&amp;" lookAhead="1" context="RedirectMaybeFd"/>
      </context>
      <!-- cmd >&N
               ~~~ Redirection
           cmd >&
                ~ Error
      -->
      <context name="RedirectMaybeFd" attribute="Redirection" lineEndContext="#pop#pop#pop#pop#pop" fallthroughContext="#pop#pop#pop#pop">
        <RegExpr String="&amp;(?:[0-9]+|-|(?=['&quot;$(]|\\$))" attribute="Redirection" context="#pop#pop#pop#pop!FieldSep!Fd"/>
        <DetectChar char="&amp;" attribute="Error"
                    context="#pop#pop#pop#pop!DirectPath_then_FieldSep"/>
      </context>
      <context name="Fd" attribute="Redirection" lineEndContext="#pop#pop#pop" fallthroughContext="#pop">
        <AnyChar String="&white;|;&lt;&gt;)}&amp;" lookAhead="1" context="#pop"/>
        <LineContinue attribute="Escape"/>
        <DetectIdentifier attribute="Error" context="#pop"/>
        <AnyChar String="'&quot;$(\" lookAhead="1" context="#pop!FdDigit"/>
      </context>
      <context name="FdDigit" attribute="Error" lineEndContext="#pop#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <AnyChar String="0123456789" attribute="Redirection"/>
        <IncludeRules context="FindFishExprWithoutBrace"/>
        <RegExpr String="." attribute="Error" context="#pop"/>
      </context>
      <!--
      @} Redirection
      -->


      <!--
      @{ Path
      -->
      <!-- DirectPath is used at places where a path is expected (e.g. test -f file) -->
      <context name="DirectPath_then_FieldSep" attribute="Path" lineEndContext="#pop#pop" fallthroughContext="#pop!FieldSep!DirectPathTail!Home">
        <DetectSpaces attribute="Normal Text"/>
        <DetectChar char="#" attribute="Error" context="#pop#pop!Comment"/>
        <AnyChar String="&amp;|;" attribute="Error" context="#pop#pop"/>
        <AnyChar String="&lt;&gt;" attribute="Error" context="#pop!FieldSep"/>
        <LineContinue attribute="Escape"/>
      </context>

      <context name="Home" attribute="Path" lineEndContext="#pop#pop" fallthroughContext="#pop">
        <RegExpr String="&homedir;" attribute="HomeDir" context="#pop"/>
      </context>

      <!-- Path with error for brace ({abc,def}) -->
      <context name="DirectPathTail" attribute="Path" lineEndContext="#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindStrings"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindSubstitutions"/>
        <IncludeRules context="FindEscape"/>
        <IncludeRules context="FindGlob"/>
        <IncludeRules context="FindNoBraceOrError"/>
      </context>
      <!--
      @} Path
      -->


      <!--
      @{ Escape
      https://fishshell.com/docs/current/language.html#escaping-characters
      -->
      <context name="FindEscape" attribute="Normal Text">
        <DetectChar char="\" lookAhead="1" context="Escape"/>
      </context>
      <context name="Escape" attribute="Escape">
        <LineContinue attribute="Escape" context="#pop"/>
        <RegExpr String="&escape_chars;" attribute="Escape" context="#pop"/>
        <RegExpr String="\\." attribute="Error" context="#pop"/>
      </context>

      <context name="FindCommandEscape" attribute="Normal Text">
        <DetectChar char="\" lookAhead="1" context="CommandEscape"/>
      </context>
      <context name="CommandEscape" attribute="Escape">
        <StringDetect String="\ " attribute="Command" context="#pop"/>
        <IncludeRules context="Escape"/>
      </context>

      <context name="FindBraceEscape" attribute="Normal Text">
        <DetectChar char="\" lookAhead="1" context="BraceEscape"/>
      </context>
      <context name="BraceEscape" attribute="Escape">
        <LineContinue attribute="Escape" context="#pop"/>
        <RegExpr String="\\,|&escape_chars;" attribute="Escape" context="#pop"/>
        <RegExpr String="\\." attribute="Error" context="#pop"/>
      </context>
      <!--
      @} Escape
      -->


      <!--
      @{ Substitution
      -->
      <context name="FindSubstitutions" attribute="Normal Text">
        <DetectChar char="(" attribute="Keychar" context="CommandSubstitution"/>
      </context>

      <context name="CommandSubstitution" attribute="Normal Text" fallthroughContext="Arguments!FieldSep!CommandName">
        <DetectChar char=")" attribute="Keychar" context="#pop!Subscript"/>
        <IncludeRules context="Root"/>
      </context>
      <!--
      @} Substitution
      -->


      <!--
      @{ Brace
      -->
      <!--	Brace Expansion consumes anything till } -->
      <context name="FindBraceExpansion" attribute="Normal Text">
        <DetectChar char="{" lookAhead="1" context="MaybeBraceList"/>
      </context>
      <context name="FindNoBraceOrError" attribute="Normal Text">
        <DetectChar char="{" lookAhead="1" context="NoBraceOrError"/>
      </context>
      <context name="FindRealBraceExpansion" attribute="Normal Text">
        <DetectChar char="{" attribute="Keychar" context="BraceList"/>
      </context>
      <context name="FindPathBraceExpansion" attribute="Normal Text">
        <DetectChar char="{" attribute="Keychar" context="PathBraceList"/>
      </context>

      <context name="MaybeBraceList" attribute="Normal Text">
        <IncludeRules context="NoBrace"/>
        <DetectChar char="{" attribute="Keychar" context="#pop!BraceList"/>
      </context>
      <context name="NoBraceOrError" attribute="Normal Text">
        <IncludeRules context="NoBrace"/>
        <DetectChar char="{" attribute="Error" context="#pop!BraceList"/>
      </context>
      <context name="NoBrace" attribute="Normal Text">
        <RegExpr String="&nobrace_text;|" context="#pop"/>
      </context>

      <!--Note:
      Multiple lines are allowed in brace,
      but this is so rare that the syntax does not permit it,
      to prevent the entire document from changing color when } is missing.-->
      <context name="BraceList" attribute="Normal Text" lineEndContext="#pop#pop">
        <DetectChar char="}" attribute="Keychar" context="#pop"/>
        <DetectChar char="," attribute="Separator"/>
        <IncludeRules context="FindStrings"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindSubstitutions"/>
        <IncludeRules context="FindBraceEscape"/>
        <IncludeRules context="FindGlob"/>
        <IncludeRules context="FindBraceExpansion"/>
        <DetectChar char=")" lookAhead="1" context="#pop#pop"/>
      </context>

      <context name="PathBraceList" attribute="Path" lineEndContext="#pop#pop">
        <IncludeRules context="BraceList"/>
      </context>
      <!--
      @} Brace
      -->


      <!--
      @} Glob
      -->
      <context name="FindGlob" attribute="Error">
        <AnyChar String="*?" attribute="Glob"/>
      </context>
      <!--
      @} Glob
      -->


      <!--
      @} Expression
      -->
      <context name="FindFishExpr" attribute="Error">
        <IncludeRules context="FindFishExprWithoutBrace"/>
        <IncludeRules context="FindBraceExpansion"/>
      </context>

      <context name="FindFishExprWithoutBrace" attribute="Error">
        <IncludeRules context="FindStrings"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindSubstitutions"/>
        <IncludeRules context="FindEscape"/>
        <IncludeRules context="FindGlob"/>
      </context>
      <!--
      @{ Expression
      -->


      <!--
      @{ Arguments
      -->
      <!--Argument Rules match the items after a command
            this one is a collection with the common part for inclusion-->
      <context name="Arguments" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="FieldSep!ArgMaybePath">
        <IncludeRules context="FindOption_then_FieldSep"/>
        <AnyChar String="*?" attribute="Glob" context="FieldSep!ArgPath"/>
        <DetectChar char="~" lookAhead="1" context="FieldSep!ArgMaybeHome"/>
      </context>
      <context name="ArgMaybeHome" attribute="Path">
        <RegExpr String="&homedir;" attribute="HomeDir" context="#pop!ArgPath"/>
        <DetectChar char="~" attribute="Normal Text" context="#pop!ArgMaybePath"/>
      </context>

      <context name="ArgMaybePath" attribute="Normal Text" lineEndContext="#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindStrings"/>
        <IncludeRules context="FindVarExpansion"/>
        <IncludeRules context="FindSubstitutions"/>
        <IncludeRules context="FindEscape"/>
        <AnyChar String="*?" attribute="Glob" context="#pop!ArgPath"/>
        <RegExpr String="&path;" attribute="Path" context="#pop!ArgPath"/>
        <RegExpr String="&text;" attribute="Normal Text"/>
        <IncludeRules context="FindRealBraceExpansion"/>
      </context>

      <context name="ArgPath" attribute="Path" lineEndContext="#pop#pop#pop">
        <IncludeRules context="PopWhenSep"/>
        <IncludeRules context="FindFishExprWithoutBrace"/>
        <IncludeRules context="FindPathBraceExpansion"/>
      </context>

      <context name="ArgumentsNone" attribute="Error" lineEndContext="#pop">
        <IncludeRules context="PopWhenSep"/>
        <DetectIdentifier attribute="Error" context="#pop!Arguments!FieldSep"/>
        <Int attribute="Error" context="#pop!Arguments!FieldSep"/>
      </context>
      <!--
      @} Arguments
      -->

    </contexts>

    <itemDatas>
      <itemData name="Normal Text"    defStyleNum="dsNormal"/>
      <itemData name="Path"           defStyleNum="dsNormal"/>
      <itemData name="Option"         defStyleNum="dsNormal"/>
      <itemData name="Escape"         defStyleNum="dsKeyword"/>
      <itemData name="Separator"      defStyleNum="dsKeyword"/>
      <itemData name="Glob"           defStyleNum="dsKeyword"/>
      <itemData name="HomeDir"        defStyleNum="dsKeyword"/>
      <itemData name="Keychar"        defStyleNum="dsSpecialChar"/>
      <itemData name="Command"        defStyleNum="dsFunction"/>
      <itemData name="UnixCommand"    defStyleNum="dsFunction"/>
      <itemData name="CommandPath"    defStyleNum="dsExtension"/>
      <itemData name="Variable"       defStyleNum="dsVariable"/>
      <itemData name="Keyword"        defStyleNum="dsKeyword"/>
      <itemData name="Control Flow"   defStyleNum="dsControlFlow"/>
      <itemData name="Operator"       defStyleNum="dsOperator"/>
      <itemData name="Redirection"    defStyleNum="dsOperator"/>
      <itemData name="Builtin"        defStyleNum="dsBuiltIn"/>
      <itemData name="Index"          defStyleNum="dsAttribute"/>

      <itemData name="String SingleQ" defStyleNum="dsString"/>
      <itemData name="String DoubleQ" defStyleNum="dsString"/>

      <itemData name="Comment"        defStyleNum="dsComment"/>
      <itemData name="Function Doc"   defStyleNum="dsDocumentation"/>

      <itemData name="Error"          defStyleNum="dsError"/>
    </itemDatas>
  </highlighting>
  <general>
    <keywords casesensitive="1" weakDeliminator="\+,-:=!.@#&#37;^_`~/"  wordWrapDeliminator="&separator;"/>
    <comments>
      <comment name="singleLine" start="#" position="afterwhitespace"/>
    </comments>
  </general>
</language>
