emacs.d/elpa/racket-mode-20191204.205/racket-font-lock.el

355 lines
13 KiB
EmacsLisp
Raw Normal View History

2019-11-23 09:10:03 +01:00
;;; racket-font-lock.el
;; Copyright (c) 2013-2018 by Greg Hendershott.
;; Author: Greg Hendershott
;; URL: https://github.com/greghendershott/racket-mode
;; License:
;; This is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version. This is distributed in the hope that it will be
;; useful, but without any warranty; without even the implied warranty
;; of merchantability or fitness for a particular purpose. See the GNU
;; General Public License for more details. See
;; http://www.gnu.org/licenses/ for details.
(require 'cl-lib)
(require 'racket-custom)
(require 'racket-keywords-and-builtins)
(require 'racket-ppss)
(require 'racket-util)
;; Define 3 levels of font-lock, as documented in 23.6.5 "Levels of
;; Font Lock". User may control using `font-lock-maximum-decoration'.
;; Note: font-lock iterates by matcher, doing an re-search-forward
;; over the entire region. As a result, it's faster to consolidate
;; matchers that will yield the same result (unless they need to be
;; tried in a certain order).
;; Note: This relies on our character syntax already having been
;; applied. For example a Racket identifier like `|name with spaces|`
;; will already have word/symbol syntax on everything including the
;; pipe and space chars.
(defconst racket-font-lock-keywords-0
(eval-when-compile
`(
;; #shebang
(,(rx bol "#!" (+ nonl) eol) . font-lock-comment-face)
;; #lang
(,(rx (group (group "#lang")
(1+ " ")
(group (1+ not-newline))))
(2 font-lock-keyword-face nil t)
(3 font-lock-variable-name-face nil t))
;; #; sexp comments
;;
;; We don't put any comment syntax on these -- that way things
;; like indent and nav work within the sexp. They are solely
;; font-locked as comments, here.
(,#'racket--font-lock-sexp-comments
(1 font-lock-comment-delimiter-face t)
(2 font-lock-comment-face t))
;; #<< here strings
;;
;; We only handle the opening #<<ID here. The remainder is
;; handled in `racket-font-lock-syntatic-face-function'.
(,(rx (group "#<<" (+? (not (any blank ?\n)))) ?\n)
(1 racket-here-string-face nil t))
))
"Strings, comments, #lang.")
(defconst racket-font-lock-keywords-1
(eval-when-compile
`(
;; keyword argument
(,(rx "#:" (1+ (or (syntax word) (syntax symbol))))
. racket-keyword-argument-face)
;; Various things for racket-selfeval-face
(,(rx (or
;; symbol
(seq ?' ?| (+ any) ?|)
(seq ?' (1+ (or (syntax word) (syntax symbol))))
(seq "#\\" (1+ (or (syntax word) (syntax symbol))))))
. racket-selfeval-face)
;; #rx #px
(,(rx (group (or "#rx" "#px")) ?\")
1 racket-selfeval-face)
;; Some self-eval constants
(,(regexp-opt '("#t" "#true" "#f" "#false" "+inf.0" "-inf.0" "+nan.0") 'symbols)
. racket-selfeval-face)
;; Numeric literals including Racket reader hash prefixes.
(,(rx
(seq symbol-start
(or
;; #d #e #i or no hash prefix
(seq (? "#" (any "dei"))
(or (seq (? (any "-+"))
(1+ digit)
(? (any "./") (1+ digit)))
(seq (1+ digit)
?e
(? (any "-+"))
(1+ digit))))
;; #x
(seq "#x"
(? (any "-+"))
(1+ hex-digit)
(? (any "./") (1+ hex-digit)))
;; #b
(seq "#b"
(or (seq (? (any "-+"))
(1+ (any "01"))
(? (any "./") (1+ (any "01"))))
(seq (1+ (any "01"))
?e
(? (any "-+"))
(1+ (any "01")))))
;; #o
(seq "#o"
(or (seq (? (any "-+"))
(1+ (any "0-7"))
(? (any "./") (1+ (any "0-7"))))
(seq (1+ (any "0-7"))
?e
(? (any "-+"))
(1+ (any "0-7"))))))
symbol-end))
. racket-selfeval-face)
))
"Self-evals")
(defconst racket-font-lock-keywords-2
(eval-when-compile
`(
;; def* -- variables
(,(rx (syntax open-parenthesis)
"def" (0+ (or (syntax word) (syntax symbol)))
(1+ space)
(group (1+ (or (syntax word) (syntax symbol)))))
1 font-lock-variable-name-face)
(,(rx (syntax open-parenthesis)
"define-values"
(1+ space)
(syntax open-parenthesis)
(group (1+ (or (syntax word) (syntax symbol) space)))
(syntax close-parenthesis))
1 font-lock-variable-name-face)
;; def* -- functions
(,(rx (syntax open-parenthesis)
"def" (0+ (or (syntax word) (syntax symbol)))
(1+ space)
(1+ (syntax open-parenthesis)) ;1+ b/c curried define
(group (1+ (or (syntax word) (syntax symbol)))))
1 font-lock-function-name-face)
;; let identifiers
(,#'racket--font-lock-let-identifiers . font-lock-variable-name-face)
;; module and module*
(,(rx (syntax open-parenthesis)
(group "module" (? "*"))
(1+ space)
(group (1+ (or (syntax word) (syntax symbol))))
(1+ space)
(group (1+ (or (syntax word) (syntax symbol)))))
(1 font-lock-keyword-face nil t)
(2 font-lock-function-name-face nil t)
(3 font-lock-variable-name-face nil t))
;; module+
(,(rx (syntax open-parenthesis)
(group "module+")
(1+ space)
(group (1+ (or (syntax word) (syntax symbol)))))
(1 font-lock-keyword-face nil t)
(2 font-lock-function-name-face nil t))
))
"Parens, modules, function/variable identifiers, syntax-")
(defconst racket-font-lock-keywords-3
(eval-when-compile
`(
(,(regexp-opt racket-keywords 'symbols) . font-lock-keyword-face)
(,(regexp-opt racket-builtins-1-of-2 'symbols) . font-lock-builtin-face)
(,(regexp-opt racket-builtins-2-of-2 'symbols) . font-lock-builtin-face)
(,(regexp-opt racket-type-list 'symbols) . font-lock-type-face)
;; pretty lambda (deprecated)
(,(rx (syntax open-parenthesis)
(? (or "case-" "match-" "opt-"))
(group "lambda")
(or word-end symbol-end))
1
(ignore
(when racket-pretty-lambda
(compose-region (match-beginning 1)
(match-end 1)
racket-lambda-char)))
nil t)
))
"Function/variable identifiers, Typed Racket types.
Note: To the extent you use #lang racket or #typed/racket, this
may be handy. But Racket is also a tool to make #lang's, and this
doesn't really fit that.")
(defconst racket-font-lock-keywords-level-0
(append racket-font-lock-keywords-0))
(defconst racket-font-lock-keywords-level-1
(append racket-font-lock-keywords-0
racket-font-lock-keywords-1))
(defconst racket-font-lock-keywords-level-2
(append racket-font-lock-keywords-0
racket-font-lock-keywords-1
racket-font-lock-keywords-2))
(defconst racket-font-lock-keywords-level-3
(append racket-font-lock-keywords-0
racket-font-lock-keywords-1
racket-font-lock-keywords-2
racket-font-lock-keywords-3))
(defconst racket-font-lock-keywords
'(racket-font-lock-keywords-level-0
racket-font-lock-keywords-level-1
racket-font-lock-keywords-level-2
racket-font-lock-keywords-level-3))
(defun racket-font-lock-syntactic-face-function (state)
(let ((q (racket--ppss-string-p state)))
(if q
(let ((startpos (racket--ppss-string/comment-start state)))
(if (eq (char-after startpos) ?|)
nil ;a |...| symbol
(if (characterp q)
font-lock-string-face
racket-here-string-face)))
font-lock-comment-face)))
;;; sexp comments
(defun racket--font-lock-sexp-comments (limit)
"Font-lock sexp comments.
2019-12-06 19:15:46 +01:00
Note that our syntax table intentionally does not mark these as
comments. As a result, indent and nav work within the sexp.
Instead we merely font-lock them to look like comments."
(when (re-search-forward (rx (group-n 1 "#;" (* " "))
(group-n 2 (not (any " "))))
limit t)
(unless (racket--string-or-comment-p ;issues 388, 408
(match-beginning 1))
(ignore-errors
(let ((md (match-data)))
(goto-char (match-beginning 2))
(forward-sexp 1)
(setf (elt md 5) (point)) ;set (match-end 2)
(set-match-data md)
t)))))
(defun racket--string-or-comment-p (pos)
(let ((state (syntax-ppss pos)))
(or (racket--ppss-string-p state)
(racket--ppss-comment-p state))))
2019-11-23 09:10:03 +01:00
;;; let forms
(defun racket--font-lock-let-identifiers (limit)
"In let forms give identifiers `font-lock-variable-name-face'.
This handles both let and let-values style forms (bindings with
with single identifiers or identifier lists).
Note: This works only when the let form has a closing paren.
\(Otherwise, when you type an incomplete let form before existing
code, this would mistakenly treat the existing code as part of
the let form.) The font-lock will kick in after you type the
closing paren. Or if you use electric-pair-mode, paredit, or
similar, it will already be there."
(while (re-search-forward
(rx (syntax open-parenthesis)
(* (syntax whitespace))
(group-n 1 "let" (* (or (syntax word) (syntax symbol)))))
limit
t)
(ignore-errors
(when (and (not (member (match-string-no-properties 1) '("let/ec" "let/cc")))
(racket--inside-complete-sexp))
;; Resume search before this let's bindings list, so we can
;; check rhs of bindings for more lets.
(save-excursion
;; Check for named let
(when (looking-at (rx (+ space) (+ (or (syntax word) (syntax symbol)))))
(forward-sexp 1)
(backward-sexp 1)
(racket--sexp-set-face font-lock-function-name-face))
;; Set font-lock-multiline property on entire identifier
;; list. Avoids need for font-lock-extend-region function.
(put-text-property (point)
(save-excursion (forward-sexp 1) (point))
'font-lock-multiline t)
(down-list 1) ;to the open paren of the first binding form
(while (ignore-errors
(down-list 1) ;to the id or list of id's
(if (not (looking-at "[([{]"))
(racket--sexp-set-face font-lock-variable-name-face)
;; list of ids, e.g. let-values
(down-list 1) ;to first id
(cl-loop
do (racket--sexp-set-face font-lock-variable-name-face)
while (ignore-errors (forward-sexp 1) (backward-sexp 1) t))
(backward-up-list))
(backward-up-list) ;to open paren of this binding form
(forward-sexp 1) ;to open paren of next binding form
t))))))
nil)
;;; misc
(defun racket--inside-complete-sexp ()
"Return whether point is inside a complete sexp."
(condition-case ()
(save-excursion (backward-up-list) (forward-sexp 1) t)
(error nil)))
(defun racket--sexp-set-face (face &optional forcep)
"Set 'face prop to FACE, rear-nonsticky, for the sexp starting at point.
Unless FORCEP is t, does so only if not already set in the
region.
Moves point to the end of the sexp."
(racket--region-set-face (point)
(progn (forward-sexp 1) (point))
face
forcep))
(defun racket--region-set-face (beg end face &optional forcep)
"Set 'face prop to FACE, rear-nonsticky, in the region BEG..END.
Unless FORCEP is t, does so only if not already set in the
region."
(when (or forcep (not (text-property-not-all beg end 'face nil)))
(add-text-properties beg end
`(face ,face
;;rear-nonsticky (face)
))))
(provide 'racket-font-lock)
;; racket-font-lock.el ends here