help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is there a way of setting a variable only when it exists?


From: Emanuel Berg
Subject: Re: Is there a way of setting a variable only when it exists?
Date: Tue, 15 Mar 2022 12:29:32 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux)

Jean Louis wrote:

>> Understood. Perhaps that was a lame attempt at humour on
>> my part.
>
> I totally understand it, people introducing their own
> understanding of definitions will have troubles
> communicating with others, it causes conflicts.

...

> First we have to agree on what is the accepted definition.
> There is nothing wrong in changing such definitions, though
> it goes gradually again with agreements among people.

Haha :)

Anyway, here is some custom code I found by doing
C-h f defcustom RET TAB RET ...
(I don't have to do the TAB LOL.
  https://dataswamp.org/~incal/emacs-init/help-incal.el )

OK, here is the custom code. Does it look like anything you
want to hand over your data to for safe-and-sound handling?

Probably the best interface in the world?

We came out of a crazy mind - and walked out on a piece of
paper ...

(defun custom-declare-variable (symbol default doc &rest args)
  "Like `defcustom', but SYMBOL and DEFAULT are evaluated as normal arguments.
DEFAULT should be an expression to evaluate to compute the default value,
not the default value itself.

DEFAULT is stored as SYMBOL's standard value, in SYMBOL's property
`standard-value'.  At the same time, SYMBOL's property `force-value' is
set to nil, as the value is no longer rogue."
  (put symbol 'standard-value (purecopy (list default)))
  ;; Maybe this option was rogue in an earlier version.  It no longer is.
  (when (get symbol 'force-value)
    (put symbol 'force-value nil))
  (if (keywordp doc)
      (error "Doc string is missing"))
  (let ((initialize #'custom-initialize-reset)
        (requests nil)
        ;; Whether automatically buffer-local.
        buffer-local)
    (unless (memq :group args)
      (let ((cg (custom-current-group)))
        (when cg
          (custom-add-to-group cg symbol 'custom-variable))))
    (while args
      (let ((keyword (pop args)))
        (unless (symbolp keyword)
          (error "Junk in args %S" args))
        (unless args
          (error "Keyword %s is missing an argument" keyword))
        (let ((value (pop args)))
          ;; Can't use `pcase' because it is loaded after `custom.el'
          ;; during bootstrap.  See `loadup.el'.
          (cond ((eq keyword :initialize)
                 (setq initialize value))
                ((eq keyword :set)
                 (put symbol 'custom-set value))
                ((eq keyword :get)
                 (put symbol 'custom-get value))
                ((eq keyword :require)
                 (push value requests))
                ((eq keyword :risky)
                 (put symbol 'risky-local-variable value))
                ((eq keyword :safe)
                 (put symbol 'safe-local-variable value))
                ((eq keyword :local)
                 (when (memq value '(t permanent))
                   (setq buffer-local t))
                 (when (eq value 'permanent)
                   (put symbol 'permanent-local t)))
                ((eq keyword :type)
                 (put symbol 'custom-type (purecopy value)))
                ((eq keyword :options)
                 (if (get symbol 'custom-options)
                     ;; Slow safe code to avoid duplicates.
                     (mapc (lambda (option)
                             (custom-add-option symbol option))
                           value)
                   ;; Fast code for the common case.
                   (put symbol 'custom-options (copy-sequence value))))
                (t
                 (custom-handle-keyword symbol keyword value
                                        'custom-variable))))))
    ;; Set the docstring, record the var on load-history, as well
    ;; as set the special-variable-p flag.
    (internal--define-uninitialized-variable symbol doc)
    (put symbol 'custom-requests requests)
    ;; Do the actual initialization.
    (unless custom-dont-initialize
      (funcall initialize symbol default)
      ;; If there is a value under saved-value that wasn't saved by the user,
      ;; reset it: we used that property to stash the value, but we don't need
      ;; it anymore.
      ;; This can happen given the following:
      ;; 1. The user loaded a theme that had a setting for an unbound
      ;; variable, so we stashed the theme setting under the saved-value
      ;; property in `custom-theme-recalc-variable'.
      ;; 2. Then, Emacs evaluated the defcustom for the option
      ;; (e.g., something required the file where the option is defined).
      ;; If we don't reset it and the user later sets this variable via
      ;; Customize, we might end up saving the theme setting in the custom-file.
      ;; See the test `custom-test-no-saved-value-after-customizing-option'.
      (let ((theme (caar (get symbol 'theme-value))))
        (when (and theme (not (eq theme 'user)) (get symbol 'saved-value))
          (put symbol 'saved-value nil))))
    (when buffer-local
      (make-variable-buffer-local symbol)))
  (run-hooks 'custom-define-hook)
  symbol)

(defmacro defcustom (symbol standard doc &rest args)
  "Declare SYMBOL as a customizable variable.
SYMBOL is the variable name; it should not be quoted.
STANDARD is an expression specifying the variable's standard
value.  It should not be quoted.  It is evaluated once by
`defcustom', and the value is assigned to SYMBOL if the variable
is unbound.  The expression itself is also stored, so that
Customize can re-evaluate it later to get the standard value.
DOC is the variable documentation.

This macro uses `defvar' as a subroutine, which also marks the
variable as \"special\", so that it is always dynamically bound
even when `lexical-binding' is t.

The remaining arguments to `defcustom' should have the form

   [KEYWORD VALUE]...

The following keywords are meaningful:

:type   VALUE should be a widget type for editing the symbol's value.
        Every `defcustom' should specify a value for this keyword.
        See Info node `(elisp) Customization Types' for a list of
        base types and useful composite types.
:options VALUE should be a list of valid members of the widget type.
:initialize
        VALUE should be a function used to initialize the
        variable.  It takes two arguments, the symbol and value
        given in the `defcustom' call.  The default is
        `custom-initialize-reset'.
:set    VALUE should be a function to set the value of the symbol
        when using the Customize user interface.  It takes two arguments,
        the symbol to set and the value to give it.  The function should
        not modify its value argument destructively.  The default choice
        of function is `set-default'.
:get    VALUE should be a function to extract the value of symbol.
        The function takes one argument, a symbol, and should return
        the current value for that symbol.  The default choice of function
        is `default-value'.
:require
        VALUE should be a feature symbol.  If you save a value
        for this option, then when your init file loads the value,
        it does (require VALUE) first.
:set-after VARIABLES
        Specifies that SYMBOL should be set after the list of variables
        VARIABLES when both have been customized.
:risky  Set SYMBOL's `risky-local-variable' property to VALUE.
:safe   Set SYMBOL's `safe-local-variable' property to VALUE.
        See Info node `(elisp) File Local Variables'.
:local  If VALUE is t, mark SYMBOL as automatically buffer-local.
        If VALUE is `permanent', also set SYMBOL's `permanent-local'
        property to t.

The following common keywords are also meaningful.

:group  VALUE should be a customization group.
        Add SYMBOL (or FACE with `defface') to that group.
:link LINK-DATA
        Include an external link after the documentation string for this
        item.  This is a sentence containing an active field which
        references some other documentation.

        There are several alternatives you can use for LINK-DATA:

        (custom-manual INFO-NODE)
             Link to an Info node; INFO-NODE is a string which specifies
             the node name, as in \"(emacs)Top\".

        (info-link INFO-NODE)
             Like `custom-manual' except that the link appears in the
             customization buffer with the Info node name.

        (url-link URL)
             Link to a web page; URL is a string which specifies the URL.

        (emacs-commentary-link LIBRARY)
             Link to the commentary section of LIBRARY.

        (emacs-library-link LIBRARY)
             Link to an Emacs Lisp LIBRARY file.

        (file-link FILE)
             Link to FILE.

        (function-link FUNCTION)
             Link to the documentation of FUNCTION.

        (variable-link VARIABLE)
             Link to the documentation of VARIABLE.

        (custom-group-link GROUP)
             Link to another customization GROUP.

        You can specify the text to use in the customization buffer by
        adding `:tag NAME' after the first element of the LINK-DATA; for
        example, (info-link :tag \"foo\" \"(emacs)Top\") makes a link to the
        Emacs manual which appears in the buffer as `foo'.

        An item can have more than one external link; however, most items
        have none at all.
:version
        VALUE should be a string specifying that the variable was
        first introduced, or its default value was changed, in Emacs
        version VERSION.
:package-version
        VALUE should be a list with the form (PACKAGE . VERSION)
        specifying that the variable was first introduced, or its
        default value was changed, in PACKAGE version VERSION.  This
        keyword takes priority over :version.  For packages which
        are bundled with Emacs releases, the PACKAGE and VERSION
        must appear in the alist `customize-package-emacs-version-alist'.
        Since PACKAGE must be unique and the user might see it in an
        error message, a good choice is the official name of the
        package, such as MH-E or Gnus.
:tag LABEL
        Use LABEL, a string, instead of the item's name, to label the item
        in customization menus and buffers.
:load FILE
        Load file FILE (a string) before displaying this customization
        item.  Loading is done with `load', and only if the file is
        not already loaded.

If SYMBOL has a local binding, then this form affects the local
binding.  This is normally not what you want.  Thus, if you need
to load a file defining variables with this form, or with
`defvar' or `defconst', you should always load that file
_outside_ any bindings for these variables.  (`defvar' and
`defconst' behave similarly in this respect.)

This macro calls `custom-declare-variable'.  If you want to
programmatically alter a customizable variable (for instance, to
write a package that extends the syntax of a variable), you can
call that function directly.

See Info node `(elisp) Customization' in the Emacs Lisp manual
for more information."
  (declare (doc-string 3) (debug (name body))
           (indent defun))
  ;; It is better not to use backquote in this file,
  ;; because that makes a bootstrapping problem
  ;; if you need to recompile all the Lisp files using interpreted code.
  `(custom-declare-variable
    ',symbol
    ,(if lexical-binding
         ;; The STANDARD arg should be an expression that evaluates to
         ;; the standard value.  The use of `eval' for it is spread
         ;; over many different places and hence difficult to
         ;; eliminate, yet we want to make sure that the `standard'
         ;; expression is checked by the byte-compiler, and that
         ;; lexical-binding is obeyed, so quote the expression with
         ;; `lambda' rather than with `quote'.
         ``(funcall #',(lambda () "" ,standard))
       `',standard)
    ,doc
    ,@args))

-- 
underground experts united
https://dataswamp.org/~incal




reply via email to

[Prev in Thread] Current Thread [Next in Thread]