" Language: C++
" Description: Override default C++ syntax file.

" Quit when a syntax file was already loaded
if exists('b:current_syntax')
  finish
endif

" Disable C syntax options to improve performance
let b:c_no_curly_error = 1
let b:c_no_bracket_error = 1
let b:c_no_cformat = 1
let b:c_no_c11 = 1
let b:c_no_ansi = 1
let b:c_no_bsd = 1
let b:c_no_operators = 1

" Read the C syntax to start with
runtime! syntax/c.vim
unlet b:current_syntax

" C++98
syn keyword cppStatement this friend using
syn keyword cppAccess public protected private
syn keyword cppModifier inline virtual explicit export
syn keyword cppType bool wchar_t
syn keyword cppExceptions throw try catch
syn keyword cppOperator new delete typeid and bitor or xor compl bitand and_eq or_eq xor_eq not not_eq
if exists('c_no_operators')
  syn keyword cppOperator operator
endif
syn keyword cppCast const_cast static_cast dynamic_cast reinterpret_cast
syn keyword cppStorageClass mutable
syn keyword cppStructure class typename template namespace
syn keyword cppBoolean true false
syn keyword cppConstant __cplusplus

" C++11
if !exists('cpp_no_cpp11')
  " Identifiers with special meaning: override final
  " TODO: These should not be keyword's, instead highlight based on context
  " since these can be used as regular identifiers outside of those contexts.
  syn keyword cppModifier override final
  syn keyword cppType char16_t char32_t nullptr_t
  syn keyword cppExceptions noexcept
  syn keyword cppStorageClass constexpr decltype thread_local
  syn keyword cppConstant nullptr
  syn keyword cppOperator alignas alignof decltype static_assert
  syn region cppRawString matchgroup=cppRawStringDelimiter start=+\%(u8\|[uLU]\)\=R"\z([[:alnum:]_{}[\]#<>%:;.?*\+\-/\^&|~!=,"']\{,16}\)(+ end=+)\z1"+ contains=@Spell display

  " Match: = delete; and = default;
  "          ^^^^^^        ^^^^^^^
  " Both `delete` and `default` are keywords which normally take precidence,
  " however if the match starts before the beginning of the keyword it will be
  " chosen instead so we can specialze for this case.
  syn match cppStatement '=\s*\(delete\|default\)\>\ze\s*;\?' contains=cOperator display

  " Attribute Specifier Sequence
  " Match: [[attribute-list]]
  syn region cppAttributeDelimiter matchgroup=cppDelimiter start='\[\[' skip='\[\[' end='\]\]' transparent contains=cppAttribute,cppNamespace,cppAttributeUsing,cppAttributeUsingNamespace,cDelimiter,cString,cOperator,cNumber,cppFunction,cFunction

  " Standard Attributes
  syn keyword cppAttribute noreturn carries_dependency contained
endif

" C++14
if !exists('cpp_no_cpp14')
  syn match cppNumber display '\<0b[01]\+\(u\=l\{0,2}\|ll\=u\)\>'
  " Standard Attributes
  syn keyword cppAttribute deprecated contained
endif

" C++17
if exists('cpp_experimental') && !exists('cpp_no_cpp17')
  " Attribute Specifier Sequence
  " FIXME: This matches range-for and switch case statements
  " Match: [[using attribute-namespace: attribute-list]]
  syn match cppAttributeUsingNamespace '\s\+\zs\w\+\ze\s*:' contained contains=cppAttributeUsing
  syn keyword cppAttributeUsing using contained

  " Standard Attributes
  syn keyword cppAttribute fallthrough nodiscard maybe_unused contained

  hi def link cppAttributeUsing cppStatement
  hi def link cppAttributeUsingNamespace cInclude
  hi def link cppAttributeUsingDelimiter cppDelimiter
endif

" C++20
if exists('cpp_experimental') && !exists('cpp_no_cpp20')
  syn keyword cppStatement concept requires

  " TODO: Attribute Specifier Sequence
  " Match: [[contract-attribute-token contract-level(opt) identifier(opt): expression]]
  "
  " Standard Attributes
  syn keyword cppAttribute likely unlikely no_unique_address contained
  syn keyword cppAttribute expects ensures assert contained

  " Contract Levels
  syn keyword cppAttribute default audit axiom contained
  "
  " TODO: These should not be keyword's, instead highlight based on context
  " since these can be used as regular identifiers outside of those contexts.
  " These only occur inside an contracts generalised attribute.
  syn keyword cppModifier audit axiom
endif

if !exists('cpp_no_function')
  " Match function expressions: expr<T>()
  "                             ^^^^
  " TODO: change .* to a not be greedy
  syn match cppFunction '\h\w*\ze<.*>\s*(' display

  hi default link cppFunction Function
endif

if !exists('cpp_no_delimiters')
  " Match: delimiter expressions: <expr>, ex::pr
  "                               ^    ^    ^^
  syn match cppDelimiter '\(<\|>\|::\)' display

  " Match: nested namespace expressions: expr::
  "                                      ^^^^
  syn match cppNamespace '\w\+::' display contains=cppDelimiter

  " Match: label: as a Label and public: protected: private: as a Statement
  "        ^^^^^                 ^^^^^^  ^^^^^^^^^  ^^^^^^^
  syn match cUserCont "^\s*\I\i*\s*:$" contains=cUserLabel,cppAccess display

  hi default link cppDelimiter cDelimiter
  hi default link cppNamespace cInclude
endif

if !exists('cpp_no_operators') && !exists('cpp_no_delimiters')
  " Match: single character operators if they are surrounded by white space: / < >
  "                                                                          ^ ^ ^
  syn match cppOperator '\(\s\zs\/\s\ze\|\s\zs<\s\ze\|\s\zs>\s\ze\)' display

  " Match: <= >= -> << >> <<= >>= ->*
  "        ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^
  syn match cppOperator '\(<=\|>=\|->\|<<\ze\s\|\s\zs>>\|>>=\|<<=\|->\*\)' display

  " Match: operator<< operator>> operator< operator> operator[]
  "        ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^
  "        operator new operator delete operator new[] operator delete[]
  "        ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
  syn match cppOperator '\<operator\>\s*\(\/\|<<\|>>\|<\|>\|\(new\|delete\)\?\s*\[\s*\]\)\?' display
endif

" Default highlighting
hi def link cppAccess cppStatement
hi def link cppCast cppOperator
hi def link cppExceptions Exception
hi def link cppOperator Operator
hi def link cppStatement Statement
hi def link cppModifier cppStatement
hi def link cppType Type
hi def link cppStorageClass StorageClass
hi def link cppStructure Structure
hi def link cppBoolean Boolean
hi def link cppConstant Constant
hi def link cppRawStringDelimiter Delimiter
hi def link cppRawString String
hi def link cppNumber Number
hi def link cppAttribute Keyword

let b:current_syntax = 'cpp'