Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax: Don't highlight the LHS of type decl as types #81

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
70 changes: 49 additions & 21 deletions syntax/ocaml.vim
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
" 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)
" 2022 Jul 20 - Improved highlighting of type decl (Jules Aguillon)
Julow marked this conversation as resolved.
Show resolved Hide resolved
" 2022 Jul 18 - Accurate type highlighting (Maëlan)
" 2019 Feb 21 - 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")
" 2017 Apr 11 - Improved matching of negative numbers (MM)
Expand Down Expand Up @@ -36,6 +37,8 @@ setlocal iskeyword+=`
" OCaml is case sensitive.
syn case match

syn match ocamlWhite /[ \t\n\r]*/

" Access to the method of an object
syn match ocamlMethod "#"

Expand All @@ -44,6 +47,8 @@ syn match ocamlScript "^#\<\(quit\|labels\|warnings\|warn_error\|directory\|r

" lowercase identifier - the standard way to match
syn match ocamlLCIdentifier /\<\(\l\|_\)\(\w\|'\)*\>/
syn match ocamlTypeIdentifier /\<\(\l\|_\)\(\w\|'\)*\>/
syn cluster ocamlTypeContained add=ocamlTypeIdentifier

" Errors
syn match ocamlBraceErr "}"
Expand Down Expand Up @@ -339,10 +344,6 @@ 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\|'\)*\>"
Expand Down Expand Up @@ -466,25 +467,54 @@ 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\>\)\@="
\ matchgroup=NONE end="\(|\|\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<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)
" RHS of a ocamlTypeDef
syn region ocamlTypeDefImpl
\ matchgroup=ocamlKeyword start="\<of\>"
\ matchgroup=ocamlKeyChar start=":=\|+=\|:\|="
\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|\<and\>\|)\|]\|}\|;\|;;\|=\)\@="
\ contained skipwhite skipempty
\ contains=@ocamlTypeExpr,ocamlTypePrivate,ocamlTypeDefDots,ocamlTypeRecordDecl,ocamlTypeSumDecl,ocamlComment,ocamlPpx
hi link ocamlTypeDefImpl ocamlTypeCatchAll
syn cluster ocamlContained add=ocamlTypeDefImpl

" Type context opened by “type” (type definition) and “constraint” (type
" constraint).
" Match the opening keyword and the identifier then jump into
" ocamlTypeDefImpl.
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
\ matchgroup=ocamlKeyword start="\<type\>\(\_s\+\<nonrec\>\)\?\|\<constraint\>"
\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|\<and\>\)\@="
\ contains=@ocamlTypeExpr,@ocamlAllErrs,ocamlComment,ocamlTypeVariance,ocamlTypeVar,ocamlPpx,ocamlWhite,ocamlTypeIdentifier,ocamlTypeDefImpl
\ skipwhite skipempty
\ nextgroup=ocamlTypeDefAnd

" Type context opened by “type” (type definition) and “constraint” (type
" constraint).
" Match the opening keyword and the identifier then jump into
" ocamlTypeDefImpl.
syn region ocamlTypeDefAnd
\ matchgroup=ocamlKeyword start="\<and\>"
\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|\<and\>\)\@="
\ contains=@ocamlTypeExpr,@ocamlAllErrs,ocamlComment,ocamlTypeVariance,ocamlTypeVar,ocamlPpx,ocamlWhite,ocamlTypeIdentifier,ocamlTypeDefImpl
\ skipwhite skipempty
\ nextgroup=ocamlTypeDefAnd
syn cluster ocamlTypeContained add=ocamlTypeDefAnd

" Exception definitions. Like ocamlTypeDef, jump into ocamlTypeDefImpl.
syn region ocamlExceptionDef
\ matchgroup=ocamlKeyword start="\<exception\>"
\ matchgroup=ocamlConstructor end="\u\(\w\|'\)*\>"
\ contains=@ocamlAllErrs,ocamlComment,ocamlTypeVariance,ocamlTypeVar,ocamlPpx
\ skipwhite skipempty
\ nextgroup=ocamlTypeDefImpl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I note that with this technique, there must be no comment (* *) between the defined type name and the equal sign =, which may be acceptable (we already have that limitation elsewhere).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this. Perhaps because the end regex don't contain \@= ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t actually tested your code, but from what I understand about nextgroup= and contained, the ocamlTypeDefImpl group will never be tried except immediately after the end of the ocamlTypeDef group.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what skipwhite is doing, from the :h:

These arguments are only used in combination with "nextgroup".	They can be
used to allow the next group to match after skipping some text:
	skipwhite	skip over space and tab characters
	skipnl		skip over the end of a line
	skipempty	skip over empty lines (implies a "skipnl")

I took thought this was doing something between start and end and nothing outside but it's actually the opposite, they take effect after end and do nothing inside the region (whitespace are skipped by default it seems).


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
Expand All @@ -500,9 +530,7 @@ 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="\(=\|:>\)\@="
\ matchgroup=NONE end="\(\<type\>\|\<exception\>\|\<val\>\|\<module\>\|\<class\>\|\<method\>\|\<constraint\>\|\<inherit\>\|\<object\>\|\<struct\>\|\<open\>\|\<include\>\|\<let\>\|\<external\>\|\<in\>\|\<end\>\|\<and\>\|)\|]\|}\|;\|;;\|=\|:>\)\@="
\ contains=@ocamlTypeExpr,ocamlComment,ocamlPpx
hi link ocamlTypeAnnot ocamlTypeCatchAll

Expand Down
25 changes: 25 additions & 0 deletions type-linter-test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
type u = char
type v = string
type w = bytes
type abstract
type !+_ abstract'

(* type expressions with arrows, tuples, 0-ary type constructors *)
type t = t0 * t0 -> t0
Expand Down Expand Up @@ -166,6 +168,24 @@
(* definition of an empty type *)
type t = |

(* Constraints *)
type 'a foo := 'a bar

(* RECURSION *)

type foo = bar
and bar
and baz = foo

;;
let foo = 1
and bar = 2 in
()

(* FIXME: 'and' part not matched by module decl (maybe matched by types decl ?). *)
module rec Foo : sig end = struct end
and Bar : sig end = struct end

(* TYPE ANNOTATIONS *)

(* annotations on let binders *)
Expand Down Expand Up @@ -318,6 +338,9 @@
end
end

(* FIXME: ':=' not recognized and RHS highlighted as constructor. *)
module Foo := Bar

(* ATTRIBUTES AND COMMENTS *)

exception[@my.attr "payld"] (*c*) E [@my.attr "payld"] (*c*)
Expand All @@ -330,6 +353,8 @@
type t = [ `A of int [@my.attr "payld"] (*c*) | (*c*) `B (*c*) of (*c*) int (*c*) ]
type t = | A of int [@my.attr "payld"] (*c*) | (*c*) B (*c*) of (*c*) int (*c*)
let _ : unit [@my.attr "payld"] (*c*) = ()
type t (*c*) = int
and u (*c*) = float

(* VARIOUS TRAPS *)

Expand Down