;;; clojure-mode-extra-font-locking.el --- Extra font-locking for Clojure mode  -*- lexical-binding: t; -*-

;; Copyright © 2014-2026 Bozhidar Batsov
;;
;; Author: Bozhidar Batsov <bozhidar@batsov.dev>
;; URL: https://github.com/clojure-emacs/clojure-mode
;; Version: 5.23.0
;; Keywords: languages, lisp
;; Package-Requires: ((clojure-mode "5.23.0"))

;; This file is not part of GNU Emacs.

;;; Commentary:

;; Provides additional font-locking for clojure-mode.  This font-locking
;; used to be part of clojure-mode up to version 3.0, but it was removed
;; due to its unreliable nature (the implementation is quite primitive
;; and font-locks symbols without any regard for what they resolve to).
;; CIDER provides much more reliable font-locking, that's based on the runtime
;; state of your Clojure application.

;;; License:

;; This program 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 3
;; of the License, or (at your option) any later version.
;;
;; This program 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.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Code:

(require 'clojure-mode)

(defvar clojure-built-in-vars
  '(
    ;; clojure.core
    "abs"
    "accessor"
    "aclone"
    "add-tap"
    "agent"
    "agent-errors"
    "aget"
    "alength"
    "alias"
    "all-ns"
    "alter"
    "alter-meta!"
    "alter-var-root"
    "ancestors"
    "any?"
    "apply"
    "array-map"
    "aset"
    "aset-boolean"
    "aset-byte"
    "aset-char"
    "aset-double"
    "aset-float"
    "aset-int"
    "aset-long"
    "aset-short"
    "assoc"
    "assoc!"
    "assoc-in"
    "associative?"
    "atom"
    "await"
    "await-for"
    "await1"
    "bases"
    "bean"
    "bigdec"
    "bigint"
    "bit-and"
    "bit-and-not"
    "bit-clear"
    "bit-flip"
    "bit-not"
    "bit-or"
    "bit-set"
    "bit-shift-left"
    "bit-shift-right"
    "bit-test"
    "bit-xor"
    "boolean"
    "boolean?"
    "boolean-array"
    "booleans"
    "bounded-count"
    "bound-fn*"
    "bound?"
    "butlast"
    "byte"
    "byte-array"
    "bytes"
    "bytes?"
    "cast"
    "char"
    "char-array"
    "char-escape-string"
    "char-name-string"
    "char?"
    "chars"
    "chunk"
    "chunk-append"
    "chunk-buffer"
    "chunk-cons"
    "chunk-first"
    "chunk-next"
    "chunk-rest"
    "chunked-seq?"
    "class"
    "class?"
    "clear-agent-errors"
    "clojure-version"
    "coll?"
    "commute"
    "comp"
    "comparator"
    "compare"
    "compare-and-set!"
    "compile"
    "complement"
    "concat"
    "conj"
    "conj!"
    "cons"
    "constantly"
    "construct-proxy"
    "contains?"
    "count"
    "counted?"
    "create-ns"
    "create-struct"
    "cycle"
    "dec"
    "decimal?"
    "delay?"
    "deliver"
    "denominator"
    "deref"
    "derive"
    "descendants"
    "destructure"
    "disj"
    "disj!"
    "dissoc"
    "dissoc!"
    "distinct"
    "distinct?"
    "double"
    "double?"
    "double-array"
    "doubles"
    "drop"
    "drop-last"
    "drop-while"
    "empty"
    "empty?"
    "ensure"
    "enumeration-seq"
    "error-handler"
    "error-mode"
    "eval"
    "even?"
    "every?"
    "every-pred"
    "extend"
    "extends?"
    "extenders"
    "ex-info"
    "ex-data"
    "false?"
    "ffirst"
    "file-seq"
    "filter"
    "filterv"
    "find"
    "find-ns"
    "find-keyword"
    "find-var"
    "first"
    "flatten"
    "float"
    "float-array"
    "float?"
    "floats"
    "flush"
    "fn?"
    "fnext"
    "force"
    "format"
    "frequencies"
    "future-call"
    "future-cancel"
    "future-cancelled?"
    "future-done?"
    "future?"
    "gensym"
    "get"
    "get-in"
    "get-method"
    "get-proxy-class"
    "get-thread-bindings"
    "get-validator"
    "group-by"
    "halt-when"
    "hash"
    "hash-map"
    "hash-ordered-coll"
    "hash-set"
    "hash-unordered-coll"
    "ident?"
    "identical?"
    "identity"
    "indexed?"
    "ifn?"
    "inc"
    "infinite?"
    "init-proxy"
    "instance?"
    "inst-ms"
    "inst?"
    "int"
    "int?"
    "int-array"
    "integer?"
    "interleave"
    "intern"
    "interpose"
    "into"
    "into-array"
    "ints"
    "isa?"
    "iteration"
    "iterate"
    "iterator-seq"
    "juxt"
    "keep"
    "keep-indexed"
    "key"
    "keys"
    "keyword"
    "keyword?"
    "last"
    "line-seq"
    "list"
    "list*"
    "list?"
    "load-file"
    "load-reader"
    "load-string"
    "loaded-libs"
    "long"
    "long-array"
    "longs"
    "macroexpand"
    "macroexpand-1"
    "make-array"
    "make-hierarchy"
    "map"
    "mapv"
    "map?"
    "map-indexed"
    "mapcat"
    "max"
    "max-key"
    "memoize"
    "merge"
    "merge-with"
    "meta"
    "method-sig"
    "methods"
    "min"
    "min-key"
    "mix-collection-hash"
    "mod"
    "name"
    "NaN?"
    "namespace"
    "nat-int?"
    "neg-int?"
    "neg?"
    "newline"
    "next"
    "nfirst"
    "nil?"
    "nnext"
    "not"
    "not-any?"
    "not-empty"
    "not-every?"
    "not="
    "ns-aliases"
    "ns-imports"
    "ns-interns"
    "ns-map"
    "ns-name"
    "ns-publics"
    "ns-refers"
    "ns-resolve"
    "ns-unalias"
    "ns-unmap"
    "nth"
    "nthnext"
    "nthrest"
    "num"
    "number?"
    "numerator"
    "object-array"
    "odd?"
    "parents"
    "parse-boolean"
    "parse-double"
    "parse-long"
    "parse-uuid"
    "partial"
    "partition"
    "partition-all"
    "partition-by"
    "partitionv"
    "partitionv-all"
    "pcalls"
    "peek"
    "persistent!"
    "pmap"
    "pop"
    "pop!"
    "pop-thread-bindings"
    "pos?"
    "pos-int?"
    "pr"
    "pr-str"
    "prefer-method"
    "prefers"
    "primitives-classnames"
    "print"
    "print-ctor"
    "print-doc"
    "print-dup"
    "print-method"
    "print-namespace-doc"
    "print-simple"
    "print-special-doc"
    "print-str"
    "printf"
    "println"
    "println-str"
    "prn"
    "prn-str"
    "promise"
    "proxy-call-with-super"
    "proxy-mappings"
    "proxy-name"
    "push-thread-bindings"
    "qualified-ident?"
    "qualified-keyword?"
    "qualified-symbol?"
    "quot"
    "rand"
    "rand-int"
    "rand-nth"
    "random-uuid"
    "range"
    "ratio?"
    "rational?"
    "rationalize"
    "re-find"
    "re-groups"
    "re-matcher"
    "re-matches"
    "re-pattern"
    "re-seq"
    "read"
    "read-line"
    "read-string"
    "realized?"
    "record?"
    "reduce"
    "reduce-kv"
    "reduced"
    "reduced?"
    "reductions"
    "ref"
    "ref-history-count"
    "ref-max-history"
    "ref-min-history"
    "ref-set"
    "release-pending-sends"
    "rem"
    "remove"
    "remove-all-methods"
    "remove-method"
    "remove-ns"
    "remove-tap"
    "remove-watch"
    "repeat"
    "repeatedly"
    "replace"
    "replicate"
    "require"
    "restart-agent"
    "reset!"
    "reset-meta!"
    "reset-vals!"
    "resolve"
    "rest"
    "resultset-seq"
    "reverse"
    "reversible?"
    "rseq"
    "rsubseq"
    "satisfies?"
    "second"
    "select-keys"
    "send"
    "send-off"
    "send-via"
    "seq"
    "seq?"
    "seqable?"
    "seque"
    "sequence"
    "sequential?"
    "set"
    "set-agent-send-executor!"
    "set-agent-send-off-executor!"
    "set-error-handler!"
    "set-error-mode!"
    "set-validator!"
    "set?"
    "short"
    "short-array"
    "shorts"
    "shuffle"
    "shutdown-agents"
    "simple-ident?"
    "simple-keyword?"
    "simple-symbol?"
    "slurp"
    "some"
    "some-fn"
    "some?"
    "sort"
    "sort-by"
    "sorted-map"
    "sorted-map-by"
    "sorted-set"
    "sorted-set-by"
    "sorted?"
    "special-symbol?"
    "spit"
    "split-at"
    "split-with"
    "splitv-at"
    "str"
    "stream-into!"
    "stream-reduce!"
    "stream-seq!"
    "stream-transduce!"
    "string?"
    "struct"
    "struct-map"
    "subs"
    "subseq"
    "subvec"
    "supers"
    "swap!"
    "swap-vals!"
    "symbol"
    "symbol?"
    "take"
    "take-last"
    "take-nth"
    "take-while"
    "tap>"
    "test"
    "the-ns"
    "thread-bound?"
    "to-array"
    "to-array-2d"
    "trampoline"
    "transient"
    "tree-seq"
    "true?"
    "type"
    "unchecked-add"
    "unchecked-add-int"
    "unchecked-byte"
    "unchecked-char"
    "unchecked-dec"
    "unchecked-dec-int"
    "unchecked-divide"
    "unchecked-divide-int"
    "unchecked-double"
    "unchecked-float"
    "unchecked-inc"
    "unchecked-inc-int"
    "unchecked-long"
    "unchecked-multiply"
    "unchecked-multiply-int"
    "unchecked-negate"
    "unchecked-negate-int"
    "unchecked-remainder"
    "unchecked-remainder-int"
    "unchecked-short"
    "unchecked-subtract"
    "unchecked-subtract-int"
    "underive"
    "unsigned-bit-shift-right"
    "unquote"
    "unquote-splicing"
    "update"
    "update-in"
    "update-keys"
    "update-proxy"
    "update-vals"
    "uri?"
    "use"
    "uuid?"
    "val"
    "vals"
    "var-get"
    "var-set"
    "var?"
    "vary-meta"
    "vec"
    "vector"
    "vector?"
    "vector-of"
    "with-bindings*"
    "with-meta"
    "xml-seq"
    "zero?"
    "zipmap"

    ;; clojure.inspector
    "atom?"
    "collection-tag"
    "get-child"
    "get-child-count"
    "inspect"
    "inspect-table"
    "inspect-tree"
    "is-leaf"
    "list-model"
    "list-provider"
    "old-table-model"
    "table-model"
    "tree-model"

    ;; clojure.main
    "demunge"
    "err->msg"
    "ex-str"
    "ex-triage"
    "load-script"
    "main"
    "renumbering-read"
    "repl"
    "repl-caught"
    "repl-exception"
    "repl-prompt"
    "repl-read"
    "repl-requires"
    "report-error"
    "root-cause"
    "skip-if-eol"
    "skip-whitespace"
    "stack-element-str"
    "with-read-known"

    ;; clojure.repl
    "apropos"
    "dir"
    "dir-fn"
    "doc"
    "find-doc"
    "pst"
    "set-break-handler!"
    "source"
    "source-fn"
    "thread-stopper"

    ;; clojure.set
    "difference"
    "index"
    "intersection"
    "join"
    "map-invert"
    "project"
    "rename"
    "rename-keys"
    "select"
    "subset?"
    "superset?"
    "union"

    ;; clojure.stacktrace
    "e"
    "print-cause-trace"
    "print-stack-trace"
    "print-throwable"
    "print-trace-element"
    "root-cause"

    ;; clojure.template
    "do-template"
    "apply-template"

    ;; clojure.test
    "are"
    "assert-any"
    "assert-expr"
    "assert-predicate"
    "compose-fixtures"
    "do-report"
    "file-position"
    "function?"
    "get-possibly-unbound-var"
    "inc-report-counter"
    "is"
    "join-fixtures"
    "print-tap-diagnostic"
    "print-tap-fail"
    "print-tap-pass"
    "print-tap-plan"
    "report"
    "run-all-tests"
    "run-test"
    "run-test-var"
    "run-tests"
    "set-test"
    "successful?"
    "test-all-vars"
    "test-ns"
    "test-var"
    "test-vars"
    "testing"
    "testing-contexts-str"
    "testing-vars-str"
    "try-expr"
    "use-fixtures"
    "with-junit-output"
    "with-tap-output"
    "with-test"
    "with-test-out"

    ;; clojure.walk
    "keywordize-keys"
    "macroexpand-all"
    "postwalk"
    "postwalk-demo"
    "postwalk-replace"
    "prewalk"
    "prewalk-demo"
    "prewalk-replace"
    "stringify-keys"
    "walk"

    ;; clojure.xml
    "attrs"
    "content"
    "content-handler"
    "disable-external-entities"
    "element"
    "emit"
    "emit-element"
    "parse"
    "sax-parser"
    "startparse-sax"
    "startparse-sax-safe"
    "tag"

    ;; clojure.zip
    "append-child"
    "branch?"
    "children"
    "down"
    "edit"
    "end?"
    "insert-child"
    "insert-left"
    "insert-right"
    "left"
    "leftmost"
    "lefts"
    "make-node"
    "next"
    "node"
    "path"
    "prev"
    "remove"
    "replace"
    "right"
    "rightmost"
    "rights"
    "root"
    "seq-zip"
    "up"
    "vector-zip"
    "xml-zip"
    "zipper"
    )
  )

(defvar clojure-built-in-dynamic-vars
  '(;; clojure.test
    "*initial-report-counters*" "*load-tests*" "*report-counters*"
    "*stack-trace-depth*" "*test-out*" "*testing-contexts*" "*testing-vars*"
    ;; clojure.xml
    "*current*" "*sb*" "*stack*" "*state*"
    ))

(font-lock-add-keywords 'clojure-mode
                        `((,(concat "(\\(?:\.*/\\)?"
                                    (regexp-opt clojure-built-in-vars t)
                                    "\\>")
                           1 font-lock-builtin-face)))

(font-lock-add-keywords 'clojure-mode
                        `((,(concat "\\<"
                                    (regexp-opt clojure-built-in-dynamic-vars t)
                                    "\\>")
                           0 font-lock-builtin-face)))

(provide 'clojure-mode-extra-font-locking)

;;; clojure-mode-extra-font-locking.el ends here
