;;; hvar.el --- Variable manipulation routines for GNU Hyperbole  -*- lexical-binding: t; -*-
;;
;; Author:       Bob Weiner
;;
;; Orig-Date:     1-Oct-91 at 14:00:24
;; Last-Mod:     29-May-24 at 01:08:47 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
;; Copyright (C) 1991-2022  Free Software Foundation, Inc.
;; See the "HY-COPY" file for license information.
;;
;; This file is part of GNU Hyperbole.

;;; Commentary:

;;; Code:
;;; ************************************************************************
;;; Other required Elisp libraries
;;; ************************************************************************

(require 'set)

;;; ************************************************************************
;;; Forward declarations
;;; ************************************************************************

(defvar inhibit-hyperbole-messaging) ; ; Defined in `hsettings' required below

;;; ************************************************************************
;;; Private variables
;;; ************************************************************************

(defvar var::append-list nil
  "List of (VAR-SYMBOL . APPENDED-LIST) elements saved from this Emacs session.")

;;; ************************************************************************
;;; Public functions
;;; ************************************************************************

;;;###autoload
(defun var:add-and-run-hook (hook hook-function)
  "Add to mode HOOK the HOOK-FUNCTION; call it in matching major-mode buffers.
HOOK is a symbol whose name begins with a major-mode name and ends with
\"-hook\"."
  (add-hook hook hook-function)
  (let* ((hook-name (symbol-name hook))
	 (mode (when (string-match "-hooks?\\'" hook-name)
		 (intern (substring hook-name 0 (match-beginning 0))))))
    (when mode (var:run-hook-in-matching-buffers mode hook-function))))

(defun var:append-all ()
  "Add back all hook values previously added by var:append in this Emacs session.
The ones that were removed by `var:remove-all' at some point."
  (mapc (lambda (elt) (var:append (car elt) (cdr elt)))
	var::append-list)
  var::append-list)

;;;###autoload
(defun var:append (var-symbol list-to-add)
  "Append to value held by VAR-SYMBOL, LIST-TO-ADD.  Return new value.
If VAR-SYMBOL is unbound, it is set to LIST-TO-ADD.
Use to append to hook variables.  Store all values for later removal.
Do nothing when `inhibit-hyperbole-messaging' is non-nil."
  (unless (symbolp var-symbol)
    (error "(var:append): First argument, `%s', must be a symbol (not a string)" var-symbol))
  (unless (and list-to-add (listp list-to-add))
    (error "(var:append): Second argument, `%s', must be a non-empty list" list-to-add))
  (unless inhibit-hyperbole-messaging
    (let ((val) result)
      (setq result
	    (if (and (boundp var-symbol)
		     (setq val (symbol-value var-symbol))
		     (or (when (symbolp val)
			   (setq val (cons val nil)))
			 (listp val)))
		(progn (when (functionp val)
			 (setq val (list val)))
		       (set var-symbol (set:union val list-to-add)))
	      (set var-symbol list-to-add)))
      (add-to-list 'var::append-list (cons var-symbol result))
      (symbol-value var-symbol))))

(defun var:remove (var-symbol list-to-remove)
  "Remove from VAR-SYMBOL the functions in LIST-TO-REMOVE.
Use to remove from hook variables."
  (unless (symbolp var-symbol)
    (error "(var:remove): First argument, `%s', must be a symbol (not a string)" var-symbol))
  (unless (and list-to-remove (listp list-to-remove))
    (error "(var:remove): Second argument, `%s', must be a non-empty list" list-to-remove))
  (when (eq (car list-to-remove) 'lambda)
    (setq list-to-remove (list list-to-remove)))
  (mapc (lambda (func) (remove-hook var-symbol func))
	list-to-remove)
  (setq var::append-list (delete (cons var-symbol list-to-remove) var::append-list))
  (symbol-value var-symbol))

(defun var:remove-all ()
  "Remove all hook values added by `var:append' from their hook variables.
Affects only those hook values added by `var:append' in this Emacs session.
Keep a copy of these values for future re-use; see `var:append-all'."
  (mapc (lambda (elt) (var:remove (car elt) (cdr elt)))
	var::append-list)
  var::append-list)

(defun var:run-hook-in-matching-buffers (mode hook-function)
  "Within all buffers with a given major MODE, call HOOK-FUNCTION.
This is used after a hook is changed to affect buffers that
existed before the change was made."
  (mapc (lambda (buf) (with-current-buffer buf (funcall hook-function)))
	(delq nil (mapcar (lambda (buf) (when (eq (buffer-local-value 'major-mode buf) mode)
					  buf))
			  (buffer-list)))))

;;; ************************************************************************
;;; Private variables
;;; ************************************************************************

(defvar var::append-list nil
  "List of (var-symbol . appended-list) elements saved from this Emacs session.")

(provide 'hvar)


;; `hsettings' and `hvar' have a cyclic dependency; require this after providing 'hvar
;; to avoid an infinite loop.
(require 'hsettings)

;;; hvar.el ends here
