4

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.

  • It's the same as for anything. Just assign the function to the variable. – quincyKirk Jun 25 at 12:36
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)))
  • 1
    Is there a reason to use defvar instead of defvar-local? – ideasman42 Jun 25 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 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 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 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 at 1:29
1

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 (var description)
  `(defvar-local ,var
     (lambda () (interactive)
       (message "Mode <%s> doesn't define <%s>, passing!" major-mode ',var))
     ,description))

(defmacro my-generic-fn-with-fallback (var description &rest body)
  `(defvar-local ,var
     (lambda () (interactive)
       ,@body)
     ,description))

(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"
                             (call-interactively 'evil-jump-forward))
(my-generic-fn-with-fallback my-generic-jump-prev "Jump backward"
                             (call-interactively 'evil-jump-backward))

(my-generic-fn-with-fallback my-generic-doc-jump-section "Jump to sections"
                             (call-interactively '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
     (lambda () (interactive) (diff-goto-source-and-close)))
   (setq my-generic-jump-next
     (lambda () (interactive) (diff-goto-source-and-close)))
   (setq my-generic-jump-prev
     (lambda () (interactive) (kill-buffer-and-window)))
   (setq my-generic-doc-jump-section
     (lambda () (interactive) (doc-jump-section-diff)))

   )
 )

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")
  (lambda () (interactive) (funcall my-generic-jump-next)))
(define-key evil-normal-state-map (kbd "M-h")
  (lambda () (interactive) (funcall my-generic-jump-prev)))
(define-key evil-normal-state-map (kbd "M-RET")
  (lambda () (interactive) (funcall my-generic-goto-thing-at-point)))
(define-key evil-normal-state-map (kbd "C-S-u")
  (lambda () (interactive) (funcall my-generic-usage-of-thing-at-point)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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