5

Currently I have variables defined with setq-local, this is useful for modes, so I can define variables only for a particular mode.

How can this be done for functions?

I'd like to have a function, eg:
generic-lookup-reference-at-point, bind this to a key, then define it for each mode which has the ability to lookup an item under the cursor.

1
  • It's the same as for anything. Just assign the function to the variable.
    – quincyKirk
    Jun 25, 2019 at 12:36

3 Answers 3

8
(defvar my-local-variable nil
"Variable used on a bufer-local basis to indicate ...")

(make-variable-buffer-local 'my-local-variable)

USAGE:

(setq my-local-variable (lambda () (message "major-mode:  %s" major-mode)))

(define-key name-of-major-mode-keymap [f5]
  (lambda () (interactive)
             (funcall my-local-variable)))
5
  • 1
    Is there a reason to use defvar instead of defvar-local?
    – ideasman42
    Jun 25, 2019 at 3:42
  • 1
    It is possible that defvar-local came into being after my introduction to Emacs ... If it has the same effect as defvar followed by make-variable-buffer-local, then by all means consolidate the two into one ... :) I still have several libraries that use the defvar / make-variable-buffer-local combination and I've never tried defvar-local.
    – lawlist
    Jun 25, 2019 at 3:46
  • That is indeed precisely what it does. Introduced (along with setq-local) in 24.3, so both macros are pretty safe to make use of in general at this point.
    – phils
    Jun 25, 2019 at 6:08
  • Just nil as default looks wrong. The old way is to replace (funcall my-local-variable) by something like (funcall (or my-local-variable #'my-default-function)). The new way is to use #'my-default-function as default value for my-local-variable.
    – Tobias
    Jun 25, 2019 at 13:04
  • 1
    Agree nil as default isn't great, wrote a macro that sets a placeholder function, here: emacs.stackexchange.com/a/51244/2418
    – ideasman42
    Jun 26, 2019 at 1:29
2

If a library developer is responsible for definining the mode dependent behavior and you do not plan to allow the user to change it per defcustom you can use mode dependent overloads.

The code below is for playing around with mode local overrides. In buffers derived from text-mode [f9] prints "action in text-mode." in other buffers it prints "default action."

(require 'mode-local)

(define-overload lookup-reference-at-point (point) ;
  "Default lookup for reference at POINT."
  (interactive "d")
  (:override
   (message "This is the")
   (message "default action.")))

(define-mode-local-override lookup-reference-at-point text-mode (point)
  "Lookup for reference at POINT in `text-mode'."
  (message "This is the")
  (message "action in text-mode."))

(global-set-key [f9] #'lookup-reference-at-point)
1

Posting answer to own question, including an example.

As @lawlist points out this can be done using defvar-local, this example uses macros to avoid writing boiler plate code and prints a message if the function isn't defined, runs a fallback function.

(defmacro my-generic-fn-with-fallback (var description &rest body)
  `
  (progn
    (defun ,var ()
      ,description
      (interactive)
      (call-interactively ,var))
    (defvar-local ,var ,@body "Value storing a function, called by a function of the same name.")))

(defmacro my-generic-fn (var description)
  `
  (my-generic-fn-with-fallback ,var ,description
    (lambda ()
      (interactive)
      (message "Mode <%s> doesn't define <%s>, doing nothing!" major-mode ',var))))

(my-generic-fn my-generic-run "Run the current buffer")
(my-generic-fn my-generic-goto-thing-at-point "Go to symbol at the point")
(my-generic-fn my-generic-usage-of-thing-at-point "References to symbol point")

(my-generic-fn-with-fallback my-generic-jump-next "Jump forward"
                             'evil-jump-forward)
(my-generic-fn-with-fallback my-generic-jump-prev "Jump backward"
                             'evil-jump-backward)

(my-generic-fn-with-fallback my-generic-doc-jump-section "Jump to sections"
                             'imenu)

Then each mode can set functions:

(add-hook
 'diff-mode-hook
 (lambda ()

   ;; --- snip

   ;; Generic functions
   ;; 'my-generic-run (no need)
   (setq my-generic-goto-thing-at-point 'diff-goto-source-and-close)
   (setq my-generic-jump-next 'diff-goto-source-and-close)
   (setq my-generic-jump-prev 'kill-buffer-and-window)
   ;; Lambda's work too.
   (setq my-generic-doc-jump-section (lambda () (interactive) (doc-jump-section-diff)))

   ;; --- snip

   )
 )

Bind the keys:


;; global key example.
(global-set-key (kbd "<f5>") (lambda () (interactive) (funcall my-generic-run)))

;; using evil-mode.
(define-key evil-normal-state-map (kbd "M-l") 'my-generic-jump-next)
(define-key evil-normal-state-map (kbd "M-h") 'my-generic-jump-prev)
(define-key evil-normal-state-map (kbd "M-RET") 'my-generic-goto-thing-at-point)
(define-key evil-normal-state-map (kbd "C-S-u") 'my-generic-usage-of-thing-at-point)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.