@@ -123,6 +123,12 @@ font faces assigned as if respective mode was enabled."
123123 " Face used to highlight Haskell keywords."
124124 :group 'haskell-appearance )
125125
126+ ;;;### autoload
127+ (defface haskell-type-face
128+ '((t :inherit font-lock-function-name-face ))
129+ " Face used to highlight Haskell types"
130+ :group 'haskell-appearance )
131+
126132;;;### autoload
127133(defface haskell-constructor-face
128134 '((t :inherit font-lock-type-face ))
@@ -206,6 +212,117 @@ Regexp match data 0 points to the chars."
206212 ; ; no face. So force evaluation by using `keep' .
207213 keep)))))
208214
215+ (defconst haskell-font-lock--reverved-ids
216+ ; ; `as' , `hiding' , and `qualified' are part of the import
217+ ; ; spec syntax, but they are not reserved.
218+ ; ; `_' can go in here since it has temporary word syntax.
219+ '(" case" " class" " data" " default" " deriving" " do"
220+ " else" " if" " import" " in" " infix" " infixl"
221+ " infixr" " instance" " let" " module" " mdo" " newtype" " of"
222+ " rec" " proc" " then" " type" " where" " _" )
223+ " Identifiers treated as reserved keywords in Haskell." )
224+
225+ (defun haskell-font-lock--forward-type (&optional ignore )
226+ " Find where does this type declaration end.
227+
228+ Moves the point to the end of type declaration. It should be
229+ invoked with point just after one of type introducing keywords
230+ like ::, class, instance, data, newtype, type."
231+ (interactive )
232+ (let ((cont t )
233+ (end (point ))
234+ (token nil )
235+ ; ; we are starting right after ::
236+ (last-token-was-operator t )
237+ (last-token-was-newline nil ))
238+ (while cont
239+ (setq token (haskell-lexeme-looking-at-token 'newline ))
240+
241+ (cond
242+ ((null token)
243+ (setq cont nil ))
244+ ((member token '(newline ))
245+ (setq last-token-was-newline (not last-token-was-operator))
246+ (setq end (match-end 0 ))
247+ (goto-char (match-end 0 )))
248+ ((and (or (member (match-string-no-properties 0 )
249+ '(" <-" " =" " <-" " ←" " ," " ;"
250+ " )" " ]" " }" " |" ))
251+ (member (match-string-no-properties 0 ) haskell-font-lock--reverved-ids))
252+ (not (member (match-string-no-properties 0 ) ignore)))
253+ (setq cont nil )
254+ (setq last-token-was-newline nil ))
255+ ((member (match-string-no-properties 0 )
256+ '(" (" " [" " {" ))
257+ (if last-token-was-newline
258+ (setq cont nil )
259+ (goto-char (match-beginning 0 ))
260+ (condition-case err
261+ (forward-sexp )
262+ (scan-error (goto-char (nth 3 err))))
263+ (setq end (point ))
264+ (setq last-token-was-newline nil )))
265+ ((member token '(qsymid char string number template-haskell-quote template-haskell-quasi-quote))
266+ (setq last-token-was-operator (member (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1 )))
267+ '(varsym consym)))
268+ (if (and (not last-token-was-operator) last-token-was-newline)
269+ (setq cont nil )
270+
271+ (goto-char (match-end 0 ))
272+ (setq end (point )))
273+ (setq last-token-was-newline nil ))
274+ ((member token '(comment nested-comment literate-comment))
275+ (goto-char (match-end 0 ))
276+ (setq end (point )))
277+ (t
278+ (goto-char (match-end 0 ))
279+ (setq end (point ))
280+ (setq last-token-was-newline nil ))))
281+ (goto-char end)))
282+
283+
284+ (defun haskell-font-lock--put-face-on-type-or-constructor ()
285+ " Private function used to put either type or constructor face
286+ on an uppercase identifier."
287+ (cl-case (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1 )))
288+ (varid (when (member (match-string 0 ) haskell-font-lock--reverved-ids)
289+ ; ; Note: keywords parse as keywords only when not qualified.
290+ ; ; GHC parses Control.let as a single but illegal lexeme.
291+ (when (member (match-string 0 ) '(" class" " instance" " type" " data" " newtype" ))
292+ (save-excursion
293+ (goto-char (match-end 0 ))
294+ (save-match-data
295+ (haskell-font-lock--forward-type
296+ (cond
297+ ((member (match-string 0 ) '(" class" " instance" ))
298+ '(" |" ))
299+ ((member (match-string 0 ) '(" type" ))
300+ ; ; Need to support 'type instance'
301+ '(" =" " instance" )))))
302+ (add-text-properties (match-end 0 ) (point ) '(font-lock-multiline t haskell-type t ))))
303+ 'haskell-keyword-face ))
304+ (conid (if (get-text-property (match-beginning 0 ) 'haskell-type )
305+ 'haskell-type-face
306+ 'haskell-constructor-face ))
307+ (varsym (when (and (not (member (match-string 0 ) '(" -" " +" " ." )))
308+ (not (save-excursion
309+ (goto-char (match-beginning 1 ))
310+ (looking-at-p " \\ sw" ))))
311+ ; ; We need to protect against the case of
312+ ; ; plus, minus or dot inside a floating
313+ ; ; point number.
314+ 'haskell-operator-face ))
315+ (consym (if (not (member (match-string 1 ) '(" ::" " ∷" )))
316+ (if (get-text-property (match-beginning 0 ) 'haskell-type )
317+ 'haskell-type-face
318+ 'haskell-constructor-face )
319+ (save-excursion
320+ (goto-char (match-end 0 ))
321+ (save-match-data
322+ (haskell-font-lock--forward-type))
323+ (add-text-properties (match-end 0 ) (point ) '(font-lock-multiline t haskell-type t )))
324+ 'haskell-operator-face ))))
325+
209326(defun haskell-font-lock-keywords ()
210327 ; ; this has to be a function because it depends on global value of
211328 ; ; `haskell-font-lock-symbols'
@@ -218,14 +335,6 @@ Regexp match data 0 points to the chars."
218335 ; ; We allow ' preceding conids because of DataKinds/PolyKinds
219336 (conid " \\ b'?[[:upper:]][[:alnum:]'_]*\\ b" )
220337 (sym " \\ s.+" )
221- (reservedids
222- ; ; `as' , `hiding' , and `qualified' are part of the import
223- ; ; spec syntax, but they are not reserved.
224- ; ; `_' can go in here since it has temporary word syntax.
225- '(" case" " class" " data" " default" " deriving" " do"
226- " else" " if" " import" " in" " infix" " infixl"
227- " infixr" " instance" " let" " module" " mdo" " newtype" " of"
228- " rec" " proc" " then" " type" " where" " _" ))
229338
230339 ; ; Top-level declarations
231340 (topdecl-var
@@ -291,11 +400,11 @@ Regexp match data 0 points to the chars."
291400
292401 ; ; Toplevel Declarations.
293402 ; ; Place them *before* generic id-and-op highlighting.
294- (, topdecl-var (1 (unless (member (match-string 1 ) ' , reservedids )
403+ (, topdecl-var (1 (unless (member (match-string 1 ) haskell-font-lock--reverved-ids )
295404 'haskell-definition-face )))
296- (, topdecl-var2 (2 (unless (member (match-string 2 ) ' , reservedids )
405+ (, topdecl-var2 (2 (unless (member (match-string 2 ) haskell-font-lock--reverved-ids )
297406 'haskell-definition-face )))
298- (, topdecl-bangpat (1 (unless (member (match-string 1 ) ' , reservedids )
407+ (, topdecl-bangpat (1 (unless (member (match-string 1 ) haskell-font-lock--reverved-ids )
299408 'haskell-definition-face )))
300409 (, topdecl-sym (2 (unless (member (match-string 2 ) '(" \\ " " =" " ->" " →" " <-" " ←" " ::" " ∷" " ," " ;" " `" ))
301410 'haskell-definition-face )))
@@ -309,23 +418,7 @@ Regexp match data 0 points to the chars."
309418 (,(concat " `" haskell-lexeme-qid-or-qsym " `" ) 0 'haskell-operator-face )
310419
311420 (, haskell-lexeme-qid-or-qsym
312- 0 (cl-case (haskell-lexeme-classify-by-first-char (char-after (match-beginning 1 )))
313- (varid (when (member (match-string 0 ) ', reservedids )
314- ; ; Note: keywords parse as keywords only when not qualified.
315- ; ; GHC parses Control.let as a single but illegal lexeme.
316- 'haskell-keyword-face ))
317- (conid 'haskell-constructor-face )
318- (varsym (when (and (not (member (match-string 0 ) '(" -" " +" " ." )))
319- (not (save-excursion
320- (goto-char (match-beginning 1 ))
321- (looking-at-p " \\ sw" ))))
322- ; ; We need to protect against the case of
323- ; ; plus, minus or dot inside a floating
324- ; ; point number.
325- 'haskell-operator-face ))
326- (consym (if (not (member (match-string 1 ) '(" ::" " ∷" )))
327- 'haskell-constructor-face
328- 'haskell-operator-face ))))))
421+ (0 (haskell-font-lock--put-face-on-type-or-constructor)))))
329422 keywords))
330423
331424
0 commit comments