changeset 35698:7270e1122962 v9.1.0586

patch 9.1.0586: ocaml runtime files are outdated Commit: https://github.com/vim/vim/commit/700cf8cfa1e926e2ba676203b3ad90c2c2083f1d Author: Yinzuo Jiang <jiangyinzuo@foxmail.com> Date: Sun Jul 14 17:02:33 2024 +0200 patch 9.1.0586: ocaml runtime files are outdated Problem: ocaml runtime files are outdated Solution: sync those files with the upstream repo, detect a few more ocaml files (Yinzuo Jiang) closes: #15260 Signed-off-by: Yinzuo Jiang <jiangyinzuo@foxmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Wed, 17 Jul 2024 08:13:44 +0200
parents ac0e8c423589
children 2efe6e4bf623
files runtime/filetype.vim runtime/indent/ocaml.vim runtime/syntax/dune.vim runtime/syntax/ocaml.vim runtime/syntax/opam.vim src/testdir/test_filetype.vim src/version.c
diffstat 7 files changed, 396 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -688,7 +688,7 @@ au BufNewFile,BufRead *.com			call dist#
 au BufNewFile,BufRead *.dot,*.gv		setf dot
 
 " Dune
-au BufNewFile,BufRead jbuild,dune,dune-project,dune-workspace setf dune
+au BufNewFile,BufRead jbuild,dune,dune-project,dune-workspace,dune-file setf dune
 
 " Dylan - lid files
 au BufNewFile,BufRead *.lid			setf dylanlid
@@ -1629,7 +1629,7 @@ au BufNewFile,BufRead *.xom,*.xin		setf 
 au BufNewFile,BufRead .ondirrc			setf ondir
 
 " OPAM
-au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam
+au BufNewFile,BufRead opam,*.opam,*.opam.template,opam.locked,*.opam.locked setf opam
 
 " OpenFOAM
 au BufNewFile,BufRead [a-zA-Z0-9]*Dict\(.*\)\=,[a-zA-Z]*Properties\(.*\)\=,*Transport\(.*\),fvSchemes,fvSolution,fvConstrains,fvModels,*/constant/g,*/0\(\.orig\)\=/* call dist#ft#FTfoam()
--- a/runtime/indent/ocaml.vim
+++ b/runtime/indent/ocaml.vim
@@ -4,8 +4,7 @@
 "               Mike Leary           <leary@nwlink.com>
 "               Markus Mottl         <markus.mottl@gmail.com>
 " URL:          https://github.com/ocaml/vim-ocaml
-" Last Change:  2023 Aug 28 - Add undo_indent (Vim Project)
-"               2017 Jun 13
+" Last Change:  2017 Jun 13
 "               2005 Jun 25 - Fixed multiple bugs due to 'else\nreturn ind' working
 "               2005 May 09 - Added an option to not indent OCaml-indents specially (MM)
 "               2013 June   - commented textwidth (Marc Weber)
@@ -36,6 +35,7 @@ if !exists("no_ocaml_comments")
    setlocal comments=sr:(*\ ,mb:\ ,ex:*)
    setlocal comments^=sr:(**,mb:\ \ ,ex:*)
    setlocal fo=cqort
+  let b:undo_indent .= " | setl com< fo<"
  endif
 endif
 
--- a/runtime/syntax/dune.vim
+++ b/runtime/syntax/dune.vim
@@ -4,6 +4,7 @@
 "              Anton Kochkov       <anton.kochkov@gmail.com>
 " URL:         https://github.com/ocaml/vim-ocaml
 " Last Change:
+"              2023 Nov 24 - Add end-of-line strings (Samuel Hym)
 "              2019 Feb 27 - Add newer keywords to the syntax (Simon Cruanes)
 "              2018 May 8 - Check current_syntax (Kawahara Satoru)
 "              2018 Mar 29 - Extend jbuild syntax with more keywords (Petter A. Urkedal)
@@ -38,6 +39,8 @@ syn keyword lispFunc ignore-stdout ignor
 syn keyword lispFunc with-stdout-to with-stderr-to with-outputs-to
 syn keyword lispFunc write-file system bash
 
+syn region lispString start=+"\\[>|]+ end=+$+ contains=@Spell
+
 syn cluster lispBaseListCluster add=duneVar
 syn match duneVar '\${[@<^]}' containedin=lispSymbol
 syn match duneVar '\${\k\+\(:\k\+\)\?}' containedin=lispSymbol
--- a/runtime/syntax/ocaml.vim
+++ b/runtime/syntax/ocaml.vim
@@ -6,6 +6,7 @@
 "               Issac Trotts      <ijtrotts@ucdavis.edu>
 " URL:          https://github.com/ocaml/vim-ocaml
 " Last Change:
+"               2019 Nov 05 - Accurate type highlighting (Maëlan)
 "               2018 Nov 08 - Improved highlighting of operators (Maëlan)
 "               2018 Apr 22 - Improved support for PPX (Andrey Popp)
 "               2018 Mar 16 - Remove raise, lnot and not from keywords (Étienne Millon, "copy")
@@ -38,25 +39,18 @@ syn case match
 " Access to the method of an object
 syn match    ocamlMethod       "#"
 
-" Script headers highlighted like comments
-syn match    ocamlComment   "^#!.*" contains=@Spell
-
 " Scripting directives
 syn match    ocamlScript "^#\<\(quit\|labels\|warnings\|warn_error\|directory\|remove_directory\|cd\|load\|load_rec\|use\|mod_use\|install_printer\|remove_printer\|require\|list\|ppx\|principal\|predicates\|rectypes\|thread\|trace\|untrace\|untrace_all\|print_depth\|print_length\|camlp4o\|camlp4r\|topfind_log\|topfind_verbose\)\>"
 
 " lowercase identifier - the standard way to match
 syn match    ocamlLCIdentifier /\<\(\l\|_\)\(\w\|'\)*\>/
 
-syn match    ocamlKeyChar    "|"
-
 " Errors
 syn match    ocamlBraceErr   "}"
 syn match    ocamlBrackErr   "\]"
 syn match    ocamlParenErr   ")"
 syn match    ocamlArrErr     "|]"
 
-syn match    ocamlCommentErr "\*)"
-
 syn match    ocamlCountErr   "\<downto\>"
 syn match    ocamlCountErr   "\<to\>"
 
@@ -75,19 +69,22 @@ else
   syn match    ocamlEndErr     "\<end\>"
 endif
 
-" Some convenient clusters
-syn cluster  ocamlAllErrs contains=ocamlBraceErr,ocamlBrackErr,ocamlParenErr,ocamlCommentErr,ocamlCountErr,ocamlDoErr,ocamlDoneErr,ocamlEndErr,ocamlThenErr
+" These keywords are only expected nested in constructions that are handled by
+" the type linter, so outside of type contexts we highlight them as errors:
+syn match    ocamlKwErr  "\<\(mutable\|nonrec\|of\|private\)\>"
 
-syn cluster  ocamlAENoParen contains=ocamlBraceErr,ocamlBrackErr,ocamlCommentErr,ocamlCountErr,ocamlDoErr,ocamlDoneErr,ocamlEndErr,ocamlThenErr
+" Some convenient clusters
+syn cluster  ocamlAllErrs contains=@ocamlAENoParen,ocamlParenErr
+syn cluster  ocamlAENoParen contains=ocamlBraceErr,ocamlBrackErr,ocamlCountErr,ocamlDoErr,ocamlDoneErr,ocamlEndErr,ocamlThenErr,ocamlKwErr
 
-syn cluster  ocamlContained contains=ocamlTodo,ocamlPreDef,ocamlModParam,ocamlModParam1,ocamlMPRestr,ocamlMPRestr1,ocamlMPRestr2,ocamlMPRestr3,ocamlModRHS,ocamlFuncWith,ocamlFuncStruct,ocamlModTypeRestr,ocamlModTRWith,ocamlWith,ocamlWithRest,ocamlModType,ocamlFullMod,ocamlVal
+syn cluster  ocamlContained contains=ocamlTodo,ocamlPreDef,ocamlModParam,ocamlModParam1,ocamlModTypePre,ocamlModRHS,ocamlFuncWith,ocamlModTypeRestr,ocamlModTRWith,ocamlWith,ocamlWithRest,ocamlFullMod,ocamlVal
 
 
 " Enclosing delimiters
-syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="(" matchgroup=ocamlKeyword end=")" contains=ALLBUT,@ocamlContained,ocamlParenErr
-syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="{" matchgroup=ocamlKeyword end="}"  contains=ALLBUT,@ocamlContained,ocamlBraceErr
-syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="\[" matchgroup=ocamlKeyword end="\]" contains=ALLBUT,@ocamlContained,ocamlBrackErr
-syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="\[|" matchgroup=ocamlKeyword end="|\]" contains=ALLBUT,@ocamlContained,ocamlArrErr
+syn region   ocamlNone transparent matchgroup=ocamlEncl start="(" matchgroup=ocamlEncl end=")" contains=ALLBUT,@ocamlContained,ocamlParenErr
+syn region   ocamlNone transparent matchgroup=ocamlEncl start="{" matchgroup=ocamlEncl end="}"  contains=ALLBUT,@ocamlContained,ocamlBraceErr
+syn region   ocamlNone transparent matchgroup=ocamlEncl start="\[" matchgroup=ocamlEncl end="\]" contains=ALLBUT,@ocamlContained,ocamlBrackErr
+syn region   ocamlNone transparent matchgroup=ocamlEncl start="\[|" matchgroup=ocamlEncl end="|\]" contains=ALLBUT,@ocamlContained,ocamlArrErr
 
 
 " Comments
@@ -124,10 +121,6 @@ syn region ocamlPpx matchgroup=ocamlPpxE
 
 "" Modules
 
-" "sig"
-syn region   ocamlSig matchgroup=ocamlSigEncl start="\<sig\>" matchgroup=ocamlSigEncl end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr,ocamlModule
-syn region   ocamlModSpec matchgroup=ocamlKeyword start="\<module\>" matchgroup=ocamlModule end="\<\u\(\w\|'\)*\>" contained contains=@ocamlAllErrs,ocamlComment skipwhite skipempty nextgroup=ocamlModTRWith,ocamlMPRestr
-
 " "open"
 syn match   ocamlKeyword "\<open\>" skipwhite skipempty nextgroup=ocamlFullMod
 
@@ -135,51 +128,66 @@ syn match   ocamlKeyword "\<open\>" skip
 syn match    ocamlKeyword "\<include\>" skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
 
 " "module" - somewhat complicated stuff ;-)
-syn region   ocamlModule matchgroup=ocamlKeyword start="\<module\>" matchgroup=ocamlModule end="\<\u\(\w\|'\)*\>" contains=@ocamlAllErrs,ocamlComment skipwhite skipempty nextgroup=ocamlPreDef
-syn region   ocamlPreDef start="."me=e-1 matchgroup=ocamlKeyword end="\l\|=\|)"me=e-1 contained contains=@ocamlAllErrs,ocamlComment,ocamlModParam,ocamlGenMod,ocamlModTypeRestr,ocamlModTRWith nextgroup=ocamlModPreRHS
-syn region   ocamlModParam start="([^*]" end=")" contained contains=ocamlGenMod,ocamlModParam1,ocamlSig,ocamlVal
+" 2022-10: please document it?
+syn region   ocamlModule matchgroup=ocamlKeyword start="\<module\>" matchgroup=ocamlModule end="\<_\|\u\(\w\|'\)*\>" contains=@ocamlAllErrs,ocamlComment skipwhite skipempty nextgroup=ocamlPreDef
+syn region   ocamlPreDef start="."me=e-1 end="[a-z:=)]\@=" contained contains=@ocamlAllErrs,ocamlComment,ocamlModParam,ocamlGenMod,ocamlModTypeRestr nextgroup=ocamlModTypePre,ocamlModPreRHS
+syn region   ocamlModParam start="(\*\@!" end=")" contained contains=ocamlGenMod,ocamlModParam,ocamlModParam1,ocamlSig,ocamlVal
 syn match    ocamlModParam1 "\<\u\(\w\|'\)*\>" contained skipwhite skipempty
 syn match    ocamlGenMod "()" contained skipwhite skipempty
 
-syn region   ocamlMPRestr start=":" end="."me=e-1 contained contains=@ocamlComment skipwhite skipempty nextgroup=ocamlMPRestr1,ocamlMPRestr2,ocamlMPRestr3
-syn region   ocamlMPRestr1 matchgroup=ocamlSigEncl start="\ssig\s\=" matchgroup=ocamlSigEncl end="\<end\>" contained contains=ALLBUT,@ocamlContained,ocamlEndErr,ocamlModule
-syn region   ocamlMPRestr2 start="\sfunctor\(\s\|(\)\="me=e-1 matchgroup=ocamlKeyword end="->" contained contains=@ocamlAllErrs,ocamlComment,ocamlModParam,ocamlGenMod skipwhite skipempty nextgroup=ocamlFuncWith,ocamlMPRestr2
-syn match    ocamlMPRestr3 "\w\(\w\|'\)*\( *\. *\w\(\w\|'\)*\)*" contained
+syn match    ocamlModTypePre ":" contained skipwhite skipempty nextgroup=ocamlModTRWith,ocamlSig,ocamlFunctor,ocamlModTypeRestr,ocamlModTypeOf
+syn match    ocamlModTypeRestr "\<\w\(\w\|'\)*\( *\. *\w\(\w\|'\)*\)*\>" contained
+
 syn match    ocamlModPreRHS "=" contained skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
 syn keyword  ocamlKeyword val
-syn region   ocamlVal matchgroup=ocamlKeyword start="\<val\>" matchgroup=ocamlLCIdentifier end="\<\l\(\w\|'\)*\>" contains=@ocamlAllErrs,ocamlComment,ocamlFullMod skipwhite skipempty nextgroup=ocamlMPRestr
+syn region   ocamlVal matchgroup=ocamlKeyword start="\<val\>" matchgroup=ocamlLCIdentifier end="\<\l\(\w\|'\)*\>" contains=@ocamlAllErrs,ocamlComment,ocamlFullMod skipwhite skipempty nextgroup=ocamlModTypePre
 syn region   ocamlModRHS start="." end=". *\w\|([^*]"me=e-2 contained contains=ocamlComment skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
 syn match    ocamlFullMod "\<\u\(\w\|'\)*\( *\. *\u\(\w\|'\)*\)*" contained skipwhite skipempty nextgroup=ocamlFuncWith
 
-syn region   ocamlFuncWith start="([^*)]"me=e-1 end=")" contained contains=ocamlComment,ocamlWith,ocamlFuncStruct skipwhite skipempty nextgroup=ocamlFuncWith
-syn region   ocamlFuncStruct matchgroup=ocamlStructEncl start="[^a-zA-Z]struct\>"hs=s+1 matchgroup=ocamlStructEncl end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+syn region   ocamlFuncWith start="([*)]\@!" end=")" contained contains=ocamlComment,ocamlWith,ocamlStruct skipwhite skipempty nextgroup=ocamlFuncWith
 
-syn match    ocamlModTypeRestr "\<\w\(\w\|'\)*\( *\. *\w\(\w\|'\)*\)*\>" contained
-syn region   ocamlModTRWith start=":\s*("hs=s+1 end=")" contained contains=@ocamlAENoParen,ocamlWith
+syn region   ocamlModTRWith start="(\*\@!" end=")" contained contains=@ocamlAENoParen,ocamlWith
 syn match    ocamlWith "\<\(\u\(\w\|'\)* *\. *\)*\w\(\w\|'\)*\>" contained skipwhite skipempty nextgroup=ocamlWithRest
 syn region   ocamlWithRest start="[^)]" end=")"me=e-1 contained contains=ALLBUT,@ocamlContained
 
 " "struct"
 syn region   ocamlStruct matchgroup=ocamlStructEncl start="\<\(module\s\+\)\=struct\>" matchgroup=ocamlStructEncl end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
 
+" "sig"
+syn region   ocamlSig matchgroup=ocamlSigEncl start="\<sig\>" matchgroup=ocamlSigEncl end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+
+" "functor"
+syn region   ocamlFunctor start="\<functor\>" matchgroup=ocamlKeyword end="->" contains=@ocamlAllErrs,ocamlComment,ocamlModParam,ocamlGenMod skipwhite skipempty nextgroup=ocamlStruct,ocamlSig,ocamlFuncWith,ocamlFunctor
+
 " "module type"
-syn region   ocamlKeyword start="\<module\>\s*\<type\>\(\s*\<of\>\)\=" matchgroup=ocamlModule end="\<\w\(\w\|'\)*\>" contains=ocamlComment skipwhite skipempty nextgroup=ocamlMTDef
+syn region   ocamlModTypeOf start="\<module\s\+type\(\s\+of\)\=\>" matchgroup=ocamlModule end="\<\w\(\w\|'\)*\>" contains=ocamlComment skipwhite skipempty nextgroup=ocamlMTDef
 syn match    ocamlMTDef "=\s*\w\(\w\|'\)*\>"hs=s+1,me=s+1 skipwhite skipempty nextgroup=ocamlFullMod
 
 " Quoted strings
 syn region ocamlString matchgroup=ocamlQuotedStringDelim start="{\z\([a-z_]*\)|" end="|\z1}" contains=@Spell
+syn region ocamlString matchgroup=ocamlQuotedStringDelim start="{%[a-z_]\+\(\.[a-z_]\+\)\?\( \z\([a-z_]\+\)\)\?|" end="|\z1}" contains=@Spell
 
 syn keyword  ocamlKeyword  and as assert class
-syn keyword  ocamlKeyword  constraint else
-syn keyword  ocamlKeyword  exception external fun
-
+syn keyword  ocamlKeyword  else
+syn keyword  ocamlKeyword  external
 syn keyword  ocamlKeyword  in inherit initializer
 syn keyword  ocamlKeyword  lazy let match
-syn keyword  ocamlKeyword  method mutable new nonrec of
-syn keyword  ocamlKeyword  parser private rec
-syn keyword  ocamlKeyword  try type
+syn keyword  ocamlKeyword  method new
+syn keyword  ocamlKeyword  parser rec
+syn keyword  ocamlKeyword  try
 syn keyword  ocamlKeyword  virtual when while with
 
+" Keywords which are handled by the type linter:
+"     as (within a type equation)
+"     constraint exception mutable nonrec of private type
+
+" The `fun` keyword has special treatment because of the syntax `fun … : t -> e`
+" where `->` ends the type context rather than being part of it; to handle that,
+" we blacklist the ocamlTypeAnnot matchgroup, and we plug ocamlFunTypeAnnot
+" instead (later in this file, by using containedin=ocamlFun):
+syn region ocamlFun matchgroup=ocamlKeyword start='\<fun\>' matchgroup=ocamlArrow end='->'
+\ contains=ALLBUT,@ocamlContained,ocamlArrow,ocamlInfixOp,ocamlTypeAnnot
+
 if exists("ocaml_revised")
   syn keyword  ocamlKeyword  do value
   syn keyword  ocamlBoolean  True False
@@ -188,14 +196,10 @@ else
   syn keyword  ocamlBoolean  true false
 endif
 
-syn keyword  ocamlType     array bool char exn float format format4
-syn keyword  ocamlType     int int32 int64 lazy_t list nativeint option
-syn keyword  ocamlType     bytes string unit
-
-syn match    ocamlConstructor  "(\s*)"
-syn match    ocamlConstructor  "\[\s*\]"
-syn match    ocamlConstructor  "\[|\s*>|]"
-syn match    ocamlConstructor  "\[<\s*>\]"
+syn match    ocamlEmptyConstructor  "(\s*)"
+syn match    ocamlEmptyConstructor  "\[\s*\]"
+syn match    ocamlEmptyConstructor  "\[|\s*>|]"
+syn match    ocamlEmptyConstructor  "\[<\s*>\]"
 syn match    ocamlConstructor  "\u\(\w\|'\)*\>"
 
 " Polymorphic variants
@@ -210,26 +214,24 @@ syn match    ocamlCharErr      "'\\\d\d'
 syn match    ocamlCharErr      "'\\[^\'ntbr]'"
 syn region   ocamlString       start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell
 
-syn match    ocamlTopStop      ";;"
-
 syn match    ocamlAnyVar       "\<_\>"
-syn match    ocamlKeyChar      "|[^\]]"me=e-1
+syn match    ocamlKeyChar      "|]\@!"
 syn match    ocamlKeyChar      ";"
 syn match    ocamlKeyChar      "\~"
 syn match    ocamlKeyChar      "?"
 
+" NOTE: for correct precedence, the rule for ";;" must come after that for ";"
+syn match    ocamlTopStop      ";;"
+
 "" Operators
 
 " The grammar of operators is found there:
 "     https://caml.inria.fr/pub/docs/manual-ocaml/names.html#operator-name
 "     https://caml.inria.fr/pub/docs/manual-ocaml/extn.html#s:ext-ops
 "     https://caml.inria.fr/pub/docs/manual-ocaml/extn.html#s:index-operators
-" =, *, < and > are both operator names and keywords, we let the user choose how
-" to display them (has to be declared before regular infix operators):
+" = is both an operator name and a keyword, we let the user choose how
+" to display it (has to be declared before regular infix operators):
 syn match    ocamlEqual        "="
-syn match    ocamlStar         "*"
-syn match    ocamlAngle        "<"
-syn match    ocamlAngle        ">"
 " Custom indexing operators:
 syn region   ocamlIndexing matchgroup=ocamlIndexingOp
   \ start="\.[~?!:|&$%=>@^/*+-][~?!.:|&$%<=>@^*/+-]*\_s*("
@@ -248,8 +250,8 @@ syn match    ocamlExtensionOp          "
 " Infix and prefix operators:
 syn match    ocamlPrefixOp              "![~?!.:|&$%<=>@^*/+-]*"
 syn match    ocamlPrefixOp           "[~?][~?!.:|&$%<=>@^*/+-]\+"
-syn match    ocamlInfixOp      "[&$%@^/+-][~?!.:|&$%<=>@^*/+-]*"
-syn match    ocamlInfixOp         "[|<=>*][~?!.:|&$%<=>@^*/+-]\+"
+syn match    ocamlInfixOp   "[&$%<>@^*/+-][~?!.:|&$%<=>@^*/+-]*"
+syn match    ocamlInfixOp            "[|=][~?!.:|&$%<=>@^*/+-]\+"
 syn match    ocamlInfixOp               "#[~?!.:|&$%<=>@^*/+-]\+#\@!"
 syn match    ocamlInfixOp              "!=[~?!.:|&$%<=>@^*/+-]\@!"
 syn keyword  ocamlInfixOpKeyword      asr land lor lsl lsr lxor mod or
@@ -266,6 +268,9 @@ else
   syn match    ocamlKeyChar    "<-[~?!.:|&$%<=>@^*/+-]\@!"
 endif
 
+" Script shebang (has to be declared after operators)
+syn match    ocamlShebang       "\%1l^#!.*$"
+
 syn match    ocamlNumber        "-\=\<\d\(_\|\d\)*[l|L|n]\?\>"
 syn match    ocamlNumber        "-\=\<0[x|X]\(\x\|_\)\+[l|L|n]\?\>"
 syn match    ocamlNumber        "-\=\<0[o|O]\(\o\|_\)\+[l|L|n]\?\>"
@@ -273,10 +278,264 @@ syn match    ocamlNumber        "-\=\<0[
 syn match    ocamlFloat         "-\=\<\d\(_\|\d\)*\.\?\(_\|\d\)*\([eE][-+]\=\d\(_\|\d\)*\)\=\>"
 
 " Labels
-syn match    ocamlLabel        "\~\(\l\|_\)\(\w\|'\)*"lc=1
-syn match    ocamlLabel        "?\(\l\|_\)\(\w\|'\)*"lc=1
+syn match    ocamlLabel        "[~?]\(\l\|_\)\(\w\|'\)*:\?"
 syn region   ocamlLabel transparent matchgroup=ocamlLabel start="[~?](\(\l\|_\)\(\w\|'\)*"lc=2 end=")"me=e-1 contains=ALLBUT,@ocamlContained,ocamlParenErr
 
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+"" Type contexts
+
+" How we recognize type contexts is explained in `type-linter-notes.md`
+" and a test suite is found in `type-linter-test.ml`.
+"
+" ocamlTypeExpr is the cluster of things that can make up a type expression
+" (in a loose sense, e.g. the “as” keyword and universal quantification are
+" included). Regions containing a type expression use it like this:
+"
+"     contains=@ocamlTypeExpr,...
+"
+" ocamlTypeContained is the cluster of things that can be found in a type
+" expression or a type definition. It is not expected to be used in any region,
+" it exists solely for throwing things in it that should not pollute the main
+" linter.
+"
+" Both clusters are filled in incrementally. Every match group that is not to be
+" found at the main level must be declared as “contained” and added to either
+" ocamlTypeExpr or ocamlTypeContained.
+"
+" In these clusters we don’t put generic things that can also be found elswhere,
+" i.e. ocamlComment and ocamlPpx, because everything that is in these clusters
+" is also put in ocamlContained and thus ignored by the main linter.
+
+"syn cluster ocamlTypeExpr contains=
+syn cluster ocamlTypeContained contains=@ocamlTypeExpr
+syn cluster ocamlContained add=@ocamlTypeContained
+
+" We’ll use a “catch-all” highlighting group to show as error anything that is
+" not matched more specifically; we don’t want spaces to be reported as errors
+" (different background color), so we just catch them here:
+syn cluster ocamlTypeExpr add=ocamlTypeBlank
+syn match    ocamlTypeBlank    contained  "\_s\+"
+hi link ocamlTypeBlank NONE
+
+" NOTE: Carefully avoid catching "(*" here.
+syn cluster ocamlTypeExpr add=ocamlTypeParen
+syn region ocamlTypeParen contained transparent
+\ matchgroup=ocamlEncl start="(\*\@!"
+\ matchgroup=ocamlEncl end=")"
+\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
+
+syn cluster ocamlTypeExpr add=ocamlTypeKeyChar,ocamlTypeAs
+syn match    ocamlTypeKeyChar  contained  "->"
+syn match    ocamlTypeKeyChar  contained  "\*"
+syn match    ocamlTypeKeyChar  contained  "#"
+syn match    ocamlTypeKeyChar  contained  ","
+syn match    ocamlTypeKeyChar  contained  "\."
+syn keyword  ocamlTypeAs       contained  as
+hi link ocamlTypeAs ocamlKeyword
+
+syn cluster ocamlTypeExpr add=ocamlTypeVariance
+syn match ocamlTypeVariance contained "[-+!]\ze *\('\|\<_\>\)"
+syn match ocamlTypeVariance contained "[-+] *!\+\ze *\('\|\<_\>\)"
+syn match ocamlTypeVariance contained "! *[-+]\+\ze *\('\|\<_\>\)"
+
+syn cluster ocamlTypeContained add=ocamlTypeEq
+syn match    ocamlTypeEq       contained  "[+:]\?="
+hi link ocamlTypeEq ocamlKeyChar
+
+syn cluster ocamlTypeExpr add=ocamlTypeVar,ocamlTypeConstr,ocamlTypeAnyVar,ocamlTypeBuiltin
+syn match    ocamlTypeVar      contained   "'\(\l\|_\)\(\w\|'\)*\>"
+syn match    ocamlTypeConstr   contained  "\<\(\l\|_\)\(\w\|'\)*\>"
+" NOTE: for correct precedence, the rule for the wildcard (ocamlTypeAnyVar)
+" must come after the rule for type constructors (ocamlTypeConstr).
+syn match    ocamlTypeAnyVar   contained  "\<_\>"
+" NOTE: For correct precedence, these builtin names must occur after the rule
+" for type constructors (ocamlTypeConstr) but before the rule for non-optional
+" labeled arguments (ocamlTypeLabel). For the latter to take precedence over
+" these builtin names, we use “syn match” here instead of “syn keyword”.
+syn match    ocamlTypeBuiltin  contained  "\<array\>"
+syn match    ocamlTypeBuiltin  contained  "\<bool\>"
+syn match    ocamlTypeBuiltin  contained  "\<bytes\>"
+syn match    ocamlTypeBuiltin  contained  "\<char\>"
+syn match    ocamlTypeBuiltin  contained  "\<exn\>"
+syn match    ocamlTypeBuiltin  contained  "\<float\>"
+syn match    ocamlTypeBuiltin  contained  "\<format\>"
+syn match    ocamlTypeBuiltin  contained  "\<format4\>"
+syn match    ocamlTypeBuiltin  contained  "\<format6\>"
+syn match    ocamlTypeBuiltin  contained  "\<in_channel\>"
+syn match    ocamlTypeBuiltin  contained  "\<int\>"
+syn match    ocamlTypeBuiltin  contained  "\<int32\>"
+syn match    ocamlTypeBuiltin  contained  "\<int64\>"
+syn match    ocamlTypeBuiltin  contained  "\<lazy_t\>"
+syn match    ocamlTypeBuiltin  contained  "\<list\>"
+syn match    ocamlTypeBuiltin  contained  "\<nativeint\>"
+syn match    ocamlTypeBuiltin  contained  "\<option\>"
+syn match    ocamlTypeBuiltin  contained  "\<out_channel\>"
+syn match    ocamlTypeBuiltin  contained  "\<ref\>"
+syn match    ocamlTypeBuiltin  contained  "\<result\>"
+syn match    ocamlTypeBuiltin  contained  "\<scanner\>"
+syn match    ocamlTypeBuiltin  contained  "\<string\>"
+syn match    ocamlTypeBuiltin  contained  "\<unit\>"
+
+syn cluster ocamlTypeExpr add=ocamlTypeLabel
+syn match    ocamlTypeLabel    contained  "?\?\(\l\|_\)\(\w\|'\)*\_s*:[>=]\@!"
+hi link ocamlTypeLabel ocamlLabel
+
+" Object type
+syn cluster ocamlTypeExpr add=ocamlTypeObject
+syn region ocamlTypeObject contained
+\ matchgroup=ocamlEncl start="<"
+\ matchgroup=ocamlEncl end=">"
+\ contains=ocamlTypeObjectDots,ocamlLCIdentifier,ocamlTypeObjectAnnot,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeObject ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypeObjectDots
+syn match ocamlTypeObjectDots contained "\.\."
+hi link ocamlTypeObjectDots ocamlKeyChar
+syn cluster ocamlTypeContained add=ocamlTypeObjectAnnot
+syn region ocamlTypeObjectAnnot contained
+\ matchgroup=ocamlKeyChar start=":"
+\ matchgroup=ocamlKeyChar end=";\|>\@="
+\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
+hi link ocamlTypeObjectAnnot ocamlTypeCatchAll
+
+" Record type definition
+syn cluster ocamlTypeContained add=ocamlTypeRecordDecl
+syn region ocamlTypeRecordDecl contained
+\ matchgroup=ocamlEncl start="{"
+\ matchgroup=ocamlEncl end="}"
+\ contains=ocamlTypeMutable,ocamlLCIdentifier,ocamlTypeRecordAnnot,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeRecordDecl ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypeMutable
+syn keyword ocamlTypeMutable contained mutable
+hi link ocamlTypeMutable ocamlKeyword
+syn cluster ocamlTypeContained add=ocamlTypeRecordAnnot
+syn region ocamlTypeRecordAnnot contained
+\ matchgroup=ocamlKeyChar start=":"
+\ matchgroup=ocamlKeyChar end=";\|}\@="
+\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
+hi link ocamlTypeRecordAnnot ocamlTypeCatchAll
+
+" Polymorphic variant types
+" NOTE: Carefully avoid catching "[@" here.
+syn cluster ocamlTypeExpr add=ocamlTypeVariant
+syn region ocamlTypeVariant contained
+\ matchgroup=ocamlEncl start="\[>" start="\[<" start="\[@\@!"
+\ matchgroup=ocamlEncl end="\]"
+\ contains=ocamlTypeVariantKeyChar,ocamlTypeVariantConstr,ocamlTypeVariantAnnot,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeVariant ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypeVariantKeyChar
+syn match ocamlTypeVariantKeyChar contained "|"
+syn match ocamlTypeVariantKeyChar contained ">"
+hi link ocamlTypeVariantKeyChar ocamlKeyChar
+syn cluster ocamlTypeContained add=ocamlTypeVariantConstr
+syn match ocamlTypeVariantConstr contained "`\w\(\w\|'\)*\>"
+hi link ocamlTypeVariantConstr ocamlConstructor
+syn cluster ocamlTypeContained add=ocamlTypeVariantAnnot
+syn region ocamlTypeVariantAnnot contained
+\ matchgroup=ocamlKeyword start="\<of\>"
+\ matchgroup=ocamlKeyChar end="|\|>\|\]\@="
+\ contains=@ocamlTypeExpr,ocamlTypeAmp,ocamlComment,ocamlPpx
+hi link ocamlTypeVariantAnnot ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypeAmp
+syn match ocamlTypeAmp contained "&"
+hi link ocamlTypeAmp ocamlTypeKeyChar
+
+" Sum type definition
+syn cluster ocamlTypeContained add=ocamlTypeSumDecl
+syn region ocamlTypeSumDecl contained
+\ matchgroup=ocamlTypeSumBar    start="|"
+\ matchgroup=ocamlTypeSumConstr start="\<\u\(\w\|'\)*\>"
+\ matchgroup=ocamlTypeSumConstr start="\<false\>" start="\<true\>"
+\ matchgroup=ocamlTypeSumConstr start="(\_s*)" start="\[\_s*]" start="(\_s*::\_s*)"
+\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|)\|]\|}\|;\|;;\|=\)\@="
+\ matchgroup=NONE end="\(\<and\>\)\@="
+\ contains=ocamlTypeSumBar,ocamlTypeSumConstr,ocamlTypeSumAnnot,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeSumDecl ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypeSumBar
+syn match ocamlTypeSumBar contained "|"
+hi link ocamlTypeSumBar ocamlKeyChar
+syn cluster ocamlTypeContained add=ocamlTypeSumConstr
+syn match ocamlTypeSumConstr contained "\<\u\(\w\|'\)*\>"
+syn match ocamlTypeSumConstr contained "\<false\>"
+syn match ocamlTypeSumConstr contained "\<true\>"
+syn match ocamlTypeSumConstr contained "(\_s*)"
+syn match ocamlTypeSumConstr contained "\[\_s*]"
+syn match ocamlTypeSumConstr contained "(\_s*::\_s*)"
+hi link ocamlTypeSumConstr ocamlConstructor
+syn cluster ocamlTypeContained add=ocamlTypeSumAnnot
+syn region ocamlTypeSumAnnot contained
+\ matchgroup=ocamlKeyword start="\<of\>"
+\ matchgroup=ocamlKeyChar start=":"
+\ matchgroup=NONE end="|\@="
+\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|)\|]\|}\|;\|;;\)\@="
+\ matchgroup=NONE end="\(\<and\>\)\@="
+\ contains=@ocamlTypeExpr,ocamlTypeRecordDecl,ocamlComment,ocamlPpx
+hi link ocamlTypeSumAnnot ocamlTypeCatchAll
+
+" Type context opened by “type” (type definition), “constraint” (type
+" constraint) and “exception” (exception definition)
+syn region ocamlTypeDef
+\ matchgroup=ocamlKeyword start="\<type\>\(\_s\+\<nonrec\>\)\?\|\<constraint\>\|\<exception\>"
+\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|)\|]\|}\|;\|;;\)\@="
+\ contains=@ocamlTypeExpr,ocamlTypeEq,ocamlTypePrivate,ocamlTypeDefDots,ocamlTypeRecordDecl,ocamlTypeSumDecl,ocamlTypeDefAnd,ocamlComment,ocamlPpx
+hi link ocamlTypeDef ocamlTypeCatchAll
+syn cluster ocamlTypeContained add=ocamlTypePrivate
+syn keyword ocamlTypePrivate contained private
+hi link ocamlTypePrivate ocamlKeyword
+syn cluster ocamlTypeContained add=ocamlTypeDefAnd
+syn keyword ocamlTypeDefAnd contained and
+hi link ocamlTypeDefAnd ocamlKeyword
+syn cluster ocamlTypeContained add=ocamlTypeDefDots
+syn match ocamlTypeDefDots contained "\.\."
+hi link ocamlTypeDefDots ocamlKeyChar
+
+" When "exception" is preceded by "with", "|" or "(", that’s not an exception
+" definition but an exception pattern; we simply highlight the keyword without
+" starting a type context.
+" NOTE: These rules must occur after that for "exception".
+syn match ocamlKeyword "\<with\_s\+exception\>"lc=4
+syn match ocamlKeyword "|\_s*exception\>"lc=1
+syn match ocamlKeyword "(\_s*exception\>"lc=1
+
+" Type context opened by “:” (countless kinds of type annotations) and “:>”
+" (type coercions)
+syn region ocamlTypeAnnot matchgroup=ocamlKeyChar start=":\(>\|\_s*type\>\|[>:=]\@!\)"
+\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|)\|]\|}\|;\|;;\)\@="
+\ matchgroup=NONE end="\(;\|}\)\@="
+\ matchgroup=NONE end="\(=\|:>\)\@="
+\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
+hi link ocamlTypeAnnot ocamlTypeCatchAll
+
+" Type annotation that gives the return type of a `fun` keyword
+" (the type context is ended by `->`)
+syn cluster ocamlTypeContained add=ocamlFunTypeAnnot
+syn region ocamlFunTypeAnnot contained containedin=ocamlFun
+\ matchgroup=ocamlKeyChar start=":"
+\ matchgroup=NONE end="\(->\)\@="
+\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
+hi link ocamlFunTypeAnnot ocamlTypeCatchAll
+
+" Module paths (including functors) in types.
+" NOTE: This rule must occur after the rule for ocamlTypeSumDecl as it must take
+" precedence over it (otherwise the module name would be mistakenly highlighted
+" as a constructor).
+" NOTE: Carefully avoid catching "(*" here.
+syn cluster ocamlTypeExpr add=ocamlTypeModPath
+syn match ocamlTypeModPath contained "\<\u\(\w\|'\)*\_s*\."
+syn region ocamlTypeModPath contained transparent
+\ matchgroup=ocamlModPath start="\<\u\(\w\|'\)*\_s*(\*\@!"
+\ matchgroup=ocamlModPath end=")\_s*\."
+\ contains=ocamlTypeDotlessModPath,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeModPath ocamlModPath
+syn cluster ocamlTypeContained add=ocamlTypeDotlessModPath
+syn match ocamlTypeDotlessModPath contained "\<\u\(\w\|'\)*\_s*\.\?"
+syn region ocamlTypeDotlessModPath contained transparent
+\ matchgroup=ocamlModPath start="\<\u\(\w\|'\)*\_s*(\*\@!"
+\ matchgroup=ocamlModPath end=")\_s*\.\?"
+\ contains=ocamlTypeDotlessModPath,ocamlTypeBlank,ocamlComment,ocamlPpx
+hi link ocamlTypeDotlessModPath ocamlTypeModPath
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
 " Synchronization
 syn sync minlines=50
@@ -306,27 +565,25 @@ hi def link ocamlBrackErr	   Error
 hi def link ocamlParenErr	   Error
 hi def link ocamlArrErr	   Error
 
-hi def link ocamlCommentErr   Error
-
 hi def link ocamlCountErr	   Error
 hi def link ocamlDoErr	   Error
 hi def link ocamlDoneErr	   Error
 hi def link ocamlEndErr	   Error
 hi def link ocamlThenErr	   Error
+hi def link ocamlKwErr	   Error
 
 hi def link ocamlCharErr	   Error
 
 hi def link ocamlErr	   Error
 
 hi def link ocamlComment	   Comment
+hi def link ocamlShebang    ocamlComment
 
 hi def link ocamlModPath	   Include
 hi def link ocamlObject	   Include
 hi def link ocamlModule	   Include
 hi def link ocamlModParam1    Include
 hi def link ocamlGenMod       Include
-hi def link ocamlModType	   Include
-hi def link ocamlMPRestr3	   Include
 hi def link ocamlFullMod	   Include
 hi def link ocamlFuncWith	   Include
 hi def link ocamlModParam     Include
@@ -339,10 +596,13 @@ hi def link ocamlStructEncl	   ocamlModu
 hi def link ocamlScript	   Include
 
 hi def link ocamlConstructor  Constant
+hi def link ocamlEmptyConstructor  ocamlConstructor
 
 hi def link ocamlVal          Keyword
+hi def link ocamlModTypePre   Keyword
 hi def link ocamlModPreRHS    Keyword
-hi def link ocamlMPRestr2	   Keyword
+hi def link ocamlFunctor	   Keyword
+hi def link ocamlModTypeOf  Keyword
 hi def link ocamlKeyword	   Keyword
 hi def link ocamlMethod	   Include
 hi def link ocamlArrow	   Keyword
@@ -352,8 +612,6 @@ hi def link ocamlTopStop	   Keyword
 
 hi def link ocamlRefAssign    ocamlKeyChar
 hi def link ocamlEqual        ocamlKeyChar
-hi def link ocamlStar         ocamlInfixOp
-hi def link ocamlAngle        ocamlInfixOp
 hi def link ocamlCons         ocamlInfixOp
 
 hi def link ocamlPrefixOp       ocamlOperator
@@ -377,7 +635,24 @@ hi def link ocamlQuotedStringDelim Ident
 
 hi def link ocamlLabel	   Identifier
 
-hi def link ocamlType	   Type
+" Type linting groups that the user can customize:
+" - ocamlTypeCatchAll: anything in a type context that is not caught by more
+"   specific rules (in principle, this should only match syntax errors)
+" - ocamlTypeConstr: type constructors
+" - ocamlTypeBuiltin: builtin type constructors (like int or list)
+" - ocamlTypeVar: type variables ('a)
+" - ocamlTypeAnyVar: wildcard (_)
+" - ocamlTypeVariance: variance and injectivity indications (+'a, !'a)
+" - ocamlTypeKeyChar: symbols such as -> and *
+" Default values below mimick the behavior before the type linter was
+" implemented, but now we can do better. :-)
+hi def link ocamlTypeCatchAll Error
+hi def link ocamlTypeConstr   NONE
+hi def link ocamlTypeBuiltin  Type
+hi def link ocamlTypeVar      NONE
+hi def link ocamlTypeAnyVar   NONE
+hi def link ocamlTypeVariance ocamlKeyChar
+hi def link ocamlTypeKeyChar  ocamlKeyChar
 
 hi def link ocamlTodo	   Todo
 
--- a/runtime/syntax/opam.vim
+++ b/runtime/syntax/opam.vim
@@ -1,5 +1,5 @@
 " Vim syntax file
-" Language:    OPAM - OCaml package manager
+" Language:    opam - OCaml package manager
 " Maintainer:  Markus Mottl        <markus.mottl@gmail.com>
 " URL:         https://github.com/ocaml/vim-ocaml
 " Last Change:
@@ -11,20 +11,55 @@ endif
 
 " need %{vars}%
 " env: [[CAML_LD_LIBRARY_PATH = "%{lib}%/stublibs"]]
-syn keyword opamKeyword1 remove depends pin-depends depopts conflicts env packages patches version maintainer tags license homepage authors doc install author available name depexts substs synopsis description
-syn match opamKeyword2 "\v(bug-reports|post-messages|ocaml-version|opam-version|dev-repo|build-test|build-doc|build)"
+syn iskeyword a-z,A-Z,-
+syn keyword opamKeyword1 author
+syn keyword opamKeyword1 authors
+syn keyword opamKeyword1 available
+syn keyword opamKeyword1 bug-reports
+syn keyword opamKeyword1 build
+syn keyword opamKeyword1 build-env
+syn keyword opamKeyword1 conflict-class
+syn keyword opamKeyword1 conflicts
+syn keyword opamKeyword1 depends
+syn keyword opamKeyword1 depexts
+syn keyword opamKeyword1 depopts
+syn keyword opamKeyword1 description
+syn keyword opamKeyword1 dev-repo
+syn keyword opamKeyword1 doc
+syn keyword opamKeyword1 extra-files
+syn keyword opamKeyword1 features
+syn keyword opamKeyword1 flags
+syn keyword opamKeyword1 homepage
+syn keyword opamKeyword1 install
+syn keyword opamKeyword1 libraries
+syn keyword opamKeyword1 license
+syn keyword opamKeyword1 maintainer
+syn keyword opamKeyword1 messages
+syn keyword opamKeyword1 name
+syn keyword opamKeyword1 opam-version
+syn keyword opamKeyword1 patches
+syn keyword opamKeyword1 pin-depends
+syn keyword opamKeyword1 post-messages
+syn keyword opamKeyword1 remove
+syn keyword opamKeyword1 run-test
+syn keyword opamKeyword1 setenv
+syn keyword opamKeyword1 substs
+syn keyword opamKeyword1 synopsis
+syn keyword opamKeyword1 syntax
+syn keyword opamKeyword1 tags
+syn keyword opamKeyword1 version
 
 syn keyword opamTodo FIXME NOTE NOTES TODO XXX contained
 syn match opamComment "#.*$" contains=opamTodo,@Spell
 syn match opamOperator ">\|<\|=\|<=\|>="
 
-syn region opamInterpolate start=/%{/ end=/}%/ contained
-syn region opamString start=/"/ end=/"/ contains=opamInterpolate
-syn region opamSeq start=/\[/ end=/\]/ contains=ALLBUT,opamKeyword1,opamKeyword2
-syn region opamExp start=/{/ end=/}/ contains=ALLBUT,opamKeyword1,opamKeyword2
+syn match opamUnclosedInterpolate "%{[^ "]*" contained
+syn match opamInterpolate         "%{[^ "]\+}%" contained
+syn region opamString start=/"/ end=/"/ contains=opamInterpolate,OpamUnclosedInterpolate
+syn region opamSeq start=/\[/ end=/\]/ contains=ALLBUT,opamKeyword1
+syn region opamExp start=/{/ end=/}/ contains=ALLBUT,opamKeyword1
 
 hi link opamKeyword1 Keyword
-hi link opamKeyword2 Keyword
 
 hi link opamString String
 hi link opamExp Function
@@ -32,6 +67,7 @@ hi link opamSeq Statement
 hi link opamOperator Operator
 hi link opamComment Comment
 hi link opamInterpolate Identifier
+hi link opamUnclosedInterpolate Error
 
 let b:current_syntax = "opam"
 
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -228,7 +228,7 @@ def s:GetFilenameChecks(): dict<list<str
     dtd: ['file.dtd'],
     dtrace: ['/usr/lib/dtrace/io.d'],
     dts: ['file.dts', 'file.dtsi', 'file.dtso', 'file.its', 'file.keymap'],
-    dune: ['jbuild', 'dune', 'dune-project', 'dune-workspace'],
+    dune: ['jbuild', 'dune', 'dune-project', 'dune-workspace', 'dune-file'],
     dylan: ['file.dylan'],
     dylanintr: ['file.intr'],
     dylanlid: ['file.lid'],
@@ -527,7 +527,7 @@ def s:GetFilenameChecks(): dict<list<str
     odin: ['file.odin'],
     omnimark: ['file.xom', 'file.xin'],
     ondir: ['.ondirrc'],
-    opam: ['opam', 'file.opam', 'file.opam.template'],
+    opam: ['opam', 'file.opam', 'file.opam.template', 'opam.locked', 'file.opam.locked'],
     openroad: ['file.or'],
     openscad: ['file.scad'],
     openvpn: ['file.ovpn', '/etc/openvpn/client/client.conf', '/usr/share/openvpn/examples/server.conf'],
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    586,
+/**/
     585,
 /**/
     584,