Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

buffer-local function in elisp

I would like to redefine an existing function foo, but for a specific buffer only.

(defun foo ()
  (message "Not done:("))

I was hopping this will do:

(make-local-variable 'foo)
(fset 'foo #'(lambda () (message "Done!")))

But it does not. Any ideas?

[EDIT: Alternatively, because the function is bounded to a key, it would suffice to modify the binding just for the current buffer. But I don't see how to do it. Local keymap is shared by the all buffers in the major mode. Modifying it modifies the bindings in all the buffers with this major mode.

The only ugly solution which I am able to think of, is is to set a keymap text-property for the whole buffer. Is this the only way to proceed?]

like image 285
VitoshKa Avatar asked Nov 04 '11 10:11

VitoshKa


People also ask

What is a buffer in EMAC?

Buffers in Emacs editing are objects that have distinct names and hold text that can be edited. Buffers appear to Lisp programs as a special data type. You can think of the contents of a buffer as a string that you can extend; insertions and deletions may occur in any part of the buffer. See Text.

What is a buffer variable?

A buffer-local variable has a buffer-local binding associated with a particular buffer. The binding is in effect when that buffer is current; otherwise, it is not in effect. If you set the variable while a buffer-local binding is in effect, the new value goes in that binding, so its other bindings are unchanged.

What is scratch buffer Emacs?

At startup, the *scratch* buffer contains a short message, in the form of a Lisp comment, that explains what it is for. This message is controlled by the variable initial-scratch-message , which should be either a documentation string, or nil (which means to suppress the message).

What is a buffer and how do you switch from one to the other?

To move between the buffers, type C-x b. Emacs shows you a default buffer name. Press Enter if that's the buffer you want, or type the first few characters of the correct buffer name and press Tab. Emacs fills in the rest of the name.


3 Answers

You can make a function which does the overriding for you, something along the lines of this:

(defun override-the-keymap ()
  (let ((my-overriding-keymap (make-sparse-keymap)))
(set-keymap-parent my-overriding-keymap (current-local-map))
(use-local-map my-overriding-keymap)
(define-key my-overriding-keymap (kbd "C-M-x") 
      '(lambda () (interactive) (message "Done!")))))

Obviously customize the key binding appropriately. This has the effect only in the current buffer.

like image 58
Trey Jackson Avatar answered Nov 10 '22 01:11

Trey Jackson


The value and function properties of a given symbol are separate, and so presumably make-local-variable will only affect the value, whereas fset operates on the function property.

You are probably better off describing in more detail what it is that you want to do, but one generic solution would be to use "around advice" to wrap the original function with your own code.

(defadvice foo (around my-foo-wrapper)
  (if (not (and (boundp 'use-my-foo) 'use-my-foo))
      ad-do-it
    (message "Not done:(")))
(ad-activate 'foo)

;; in special buffer
(set (make-local-variable 'use-my-foo) t)

EDIT: (regarding the additional key-map comments)

Perhaps then you want to define a minor-mode for use in your special buffer. Minor mode key-maps take precedence over those of the major mode, so you would simply need to define that same binding in the minor mode's map. See define-minor-mode.

like image 34
phils Avatar answered Nov 10 '22 02:11

phils


How about writing your symbol-functions to symbol-values, then evaluate them by some other function?

(defvar my-buffer-local-function
  (lambda ()
    (interactive)
    (message "Default message"))
  "This variable contains buffer local function")

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

(defun run-my-buffer-local-function (&rest args)
  "This function run buffer-local function"
  (interactive)
  (if (called-interactively-p 'any)   ;To call interactively AND to
                                      ;be able to have elisp-calls
    (call-interactively my-buffer-local-function)
    (apply my-buffer-local-function args)))

(setq my-buffer-local-function
  (lambda (&optional arg)
    (interactive "sinsert message: ")
    (message (concat "Not so default message: " arg))))

The visible bad side, is that it works well if my-buffer-local-function is interactive. If not, run-my-buffer-local-function will still be interactive, and visible in M-x list. I think you cannot make sometimes-interactive functions, because interactive should be top-level call.

Btw, you can name function and value with the same name.

like image 40
desudesudesu Avatar answered Nov 10 '22 01:11

desudesudesu