hl-defined.el

Download Git Homepage

;;; hl-defined.el --- Highlight defined or undefined symbols in Emacs-Lisp.
;;
;; Filename: hl-defined.el
;; Description: Highlight defined or undefined symbols in Emacs-Lisp.
;; Author: Drew Adams
;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com")
;; Copyright (C) 2013-2021, Drew Adams, all rights reserved.
;; Created: Sat Aug 17 13:59:36 2013 (-0700)
;; Version: 0
;; Package-Requires: ()
;; Last-Updated: Sun Aug 15 10:10:03 2021 (-0700)
;;           By: dradams
;;     Update #: 316
;; URL: https://www.emacswiki.org/emacs/download/hl-defined.el
;; Doc URL: https://emacswiki.org/emacs/HighlightLispFunctions
;; Keywords: highlight, lisp, functions
;; Compatibility: GNU Emacs: 22.x, 23.x, 24.x, 25.x, 26.x, 27.x
;;
;; Features that might be required by this library:
;;
;;   None
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;;    Highlight defined or undefined symbols in Emacs-Lisp.
;;
;;  `hdefd-highlight-mode' is a minor mode that highlights, in the
;;  current buffer, symbols that are known to be defined as Emacs-Lisp
;;  functions or variables or both.  Alternatively, it can highlight
;;  symbols that are not known to be defined as functions or
;;  variables.
;;
;;  The current buffer should be in Emacs-Lisp mode.
;;
;;  Command `hdefd-highlight-mode' toggles highlighting on/off.  The
;;  highlighting respects option `hdefd-highlight-type'.
;;
;;  Command `hdefd-cycle' cycles highlighting among the available
;;  types and off, as follows: functions & variables > functions >
;;  variables > undefined > off .  It does this by changing the
;;  current value of option `hdefd-highlight-type'.
;;
;;  You can of course customize the faces used for highlighting.  You
;;  might want, for instance, to have face `hdefd-functions' inherit
;;  from face `font-lock-function-name-face', and `hdefd-variables'
;;  inherit from `font-lock-variable-name-face'.  This is not the
;;  default because I don't find it so useful.
;;
;;
;;  Put this in your init file:
;;
;;    (require 'hl-defined)
;;
;;  If you want to turn on this highlighting automatically whenever
;;  you enter Emacs-Lisp mode then you can do this in your init file:
;;
;;    (require 'hl-defined)
;;    (add-hook 'emacs-lisp-mode-hook 'hdefd-highlight-mode 'APPEND)
;;
;;  User option `hdefd-highlight-type' controls what gets highlighted.
;;
;;
;;  Faces defined here:
;;
;;    `hdefd-functions', `hdefd-variables', `hdefd-undefined'.
;;
;;  User options defined here:
;;
;;    `hdefd-highlight-type'.
;;
;;  Commands defined here:
;;
;;    `hdefd-highlight-mode'.
;;
;;  Non-interactive functions defined here:
;;
;;    `hdefd-highlight'.
;;
;;  Internal variables defined here:
;;
;;    `hdefd--face'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change Log:
;;
;; 2021/08/15 dadams
;;     Renamed hdefd-face to hdefd--face.
;; 2021/08/14 dadams
;;     hdefd-highlight: Skip over whitespace also, not just quote.
;; 2013/08/19 dadams
;;     Created from highlight-fns.el (obsolete - replaced by this).
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 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, 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 this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:

(eval-when-compile (require 'cl)) ;; case

(defgroup Highlight-Defined nil
  "Highlight defined or undefined symbols in Emacs-Lisp."
  :prefix "hdefd-" :group 'matching :group 'font-lock :group 'programming
  :link `(url-link :tag "Send Bug Report"
          ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
hl-defined.el bug: \
&body=Describe bug here, starting with `emacs -q'.  \
Don't forget to mention your Emacs and library versions."))
  :link '(url-link :tag "Other Libraries by Drew"
          "https://www.emacswiki.org/emacs/DrewsElispLibraries")
  :link '(url-link :tag "Download"
          "https://www.emacswiki.org/emacs/download/hl-defined.el")
  :link '(url-link :tag "Description" "https://emacswiki.org/emacs/HighlightLispFunctions")
  :link '(emacs-commentary-link :tag "Commentary" "hl-defined"))

(defface hdefd-functions
    '((t (:foreground "#00006DE06DE0"))) ; Like `font-lock-constant-face'.  Do not inherit.
  "Face used to highlight Emacs-Lisp functions."
  :group 'Highlight-Defined :group 'faces)

(defface hdefd-variables
    '((t (:foreground "Orchid"))) ; Like `font-lock-builtin-face'.  Do not inherit.
  "Face used to highlight Emacs-Lisp variables."
  :group 'Highlight-Defined :group 'faces)

(defface hdefd-undefined
    '((t (:foreground "Orange")))
  "Face used to highlight undefined Emacs-Lisp symbols."
  :group 'Highlight-Defined :group 'faces)

(defcustom hdefd-highlight-type 'fns-and-vars
  "Type of highlighting to be done by `hdefd-highlight-mode'.
If the value is `undefined', highlight symbols not known to be defined
as a function or a variable, using face `hdefd-undefined'.
Otherwise, highlight defined symbols.

If highlighting defined symbols and a function and a variable have the
same name then:
* The name is highlighted.
* If the option value means that function names are highlighted then
  the name is highlighted with face `hdefd-functions' (even if the
  occurrence is in fact used as a variable).
* If the value means that only variable names are highlighted then the
  name is highlighted with face `hdefd-variables' (even if it is used
  as a function)."
  :type '(choice
          (const :tag "Functions and variables" fns-and-vars)
          (const :tag "Functions"               functions)
          (const :tag "Variables"               variables)
          (const :tag "Undefined symbols"       undefined))
  :group 'Highlight-Defined)

(defvar hdefd--face nil)

(define-minor-mode hdefd-highlight-mode
    "Toggle highlighting defined or undefined symbols in the buffer.
The current buffer should be in Emacs-Lisp mode.
With prefix ARG, turn the mode on if ARG is positive, off otherwise.

Highlighting is governed by option `hdefd-highlight-type': either
undefined symbols or defined symbols: functions or variables or both."
  :group 'Highlight-Defined
  (if hdefd-highlight-mode
      (font-lock-add-keywords nil '((hdefd-highlight . hdefd--face)) 'APPEND)
    (font-lock-remove-keywords nil '((hdefd-highlight . hdefd--face))))
  (when font-lock-mode (font-lock-mode -1))
  (font-lock-mode 1)
  (when (if (> emacs-major-version 22)
            (called-interactively-p 'interactive)
          (called-interactively-p))
    (message "Highlighting Emacs-Lisp %s is now %s."
             (case hdefd-highlight-type
               (fns-and-vars "FUNCTIONS and VARIABLES")
               (functions    "FUNCTIONS")
               (variables    "VARIABLES")
               (undefined    "UNDEFINED symbols"))
             (if hdefd-highlight-mode "ON" "OFF"))))

(defun hdefd-cycle ()
  "Cycle highlighting via `hdefd-highlight-mode'.
Cycle among the possible values of option `hdefd-highlight-type' and off."
  (interactive)
  (setq hdefd-highlight-type  (if (not hdefd-highlight-mode)
                                  'fns-and-vars
                                (case hdefd-highlight-type
                                  (fns-and-vars  'functions)
                                  (functions     'variables)
                                  (variables     'undefined)
                                  (undefined     nil)
                                  ((nil)         'fns-and-vars))))
  (if hdefd-highlight-type
      (unless hdefd-highlight-mode (hdefd-highlight-mode 1))
    (setq hdefd-highlight-type  'fns-and-vars)
    (hdefd-highlight-mode -1))
  (message "Highlighting Emacs-Lisp %s is now %s."
           (case hdefd-highlight-type
             (fns-and-vars (if hdefd-highlight-mode "FUNCTIONS and VARIABLES" "symbols"))
             (functions    "FUNCTIONS")
             (variables    "VARIABLES")
             (undefined    "UNDEFINED symbols"))
           (if hdefd-highlight-mode "ON" "OFF"))
  (font-lock-fontify-buffer))

(defun hdefd-highlight (_limit)
  "Highlight Emacs-Lisp functions and/or variables.
Use as a font-lock MATCHER function for `hdefd-highlight-mode'."
  ;; If your code uses any of these variable names then too bad - they will be highlighted:
  ;; `hdefd-found', `hdefd-highlight-type', `hdefd-obj', and `hdefd-opoint'.
  (let ((hdefd-opoint  (point))
        (hdefd-found   nil))
    (with-syntax-table emacs-lisp-mode-syntax-table
      (while (and (not hdefd-found)  (not (eobp)))
        (cond ((condition-case ()
                   (save-excursion
                     (skip-chars-forward "' \t\n")
                     (setq hdefd-opoint  (point))
                     (let ((hdefd-obj  (read (current-buffer))))
                       (and (symbolp hdefd-obj)
                            (not (memq hdefd-obj '(nil t)))
                            (not (keywordp hdefd-obj))
                            (or (and (memq hdefd-highlight-type '(fns-and-vars functions))
                                     (fboundp hdefd-obj))
                                (and (memq hdefd-highlight-type '(fns-and-vars variables))
                                     (boundp hdefd-obj))
                                (and (eq hdefd-highlight-type 'undefined)
                                     (not (fboundp hdefd-obj))
                                     (not (boundp  hdefd-obj))))
                            (progn (set-match-data (list hdefd-opoint (point)))
                                   (setq hdefd--face
                                         (if (and (fboundp hdefd-obj)
                                                  (memq hdefd-highlight-type
                                                        '(fns-and-vars functions)))
                                             'hdefd-functions
                                           (if (eq hdefd-highlight-type 'undefined)
                                               'hdefd-undefined
                                             'hdefd-variables)))
                                   t))))
                 (error nil))
               (forward-sexp 1)
               (setq hdefd-opoint  (point)
                     hdefd-found   t))
              (t
               (if (looking-at "\\(\\sw\\|\\s_\\)")
                   (forward-sexp 1)
                 (forward-char 1)))))
      hdefd-found)))

;;;;;;;;;;;;;;;;;;;;;;;

(provide 'hl-defined)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; hl-defined.el ends here