What I'm trying to do is very similar to what make-local-variable does except that I don't want variables to be declared for every buffer created (only for those which belong to a particular mode). This is mostly a performance concern, the objects created may be big. AND more importantly I'd need a function like (get-buffer-property buffer property-name) to figure out what is the state of the particular buffer.
Essentially, I'm after something similar to get-buffer-proccess except that I need something other than a process.
Will it make sense to create such objects in the declaration of the major mode and destructing them in a hook for when such buffer is killed, or is there a better way for that?
This is what I have so far:
(defun haxe-get-buffer-property (buffer property)
"Pops to BUFFER, reads the value of the PROPERTY and returns it."
(let ((result
(save-excursion
(pop-to-buffer buffer)
(symbol-value property))))
result))
(defmacro haxe-buffer-property (buffer property)
`(haxe-get-buffer-property ,buffer ',property))
(defmacro deflocal (var &rest body)
(let ((symb var)
(val (car body))
(doc (cadr body)))
`(progn
(defvar ,symb nil ,doc)
(unless ,symb (setq ,symb ,val))
(make-local-variable ',symb))))
But I don't like that I have to visit buffers because I'm not sure of side effects.
EDIT: Some more info.
What happens is like so, there are multiple buffers which can interact with a network connection process. This process can be shared by groups of buffers (it is important that buffers share this process), but it also may happen that there exist simultaneously multiple buffers that have different processes assigned to each other. Besides the process itself, there's a lot of info to store about the state of the process (how much data was received, what was sent, errors etc.) Similarly this data has to be shared by groups of buffers.
EDIT2:
This is what become of the code above, just in case anyone will need it.
(defmacro deflocal (var &rest body)
(let ((symb var)
(val (car body))
(doc (cadr body)))
`(progn
(set (make-local-variable ',symb) ,val)
(put ',symb 'variable-documentation ,doc))))
(defun haxe-get-buffer-property (buffer property)
"Pops to BUFFER, reads the value of the PROPERTY and returns it."
(let ((result
(with-current-buffer buffer
(symbol-value property))))
result))
(defun haxe-set-buffer-property (buffer &rest proplist)
"Pops to BUFFER and sets properties in parallel, similar to `pset'."
(let ((result
(with-current-buffer buffer
(loop for (property value) on proplist by #'cddr
do (set property value)
finally (return value)))))
result))
(defmacro haxe-buffer-pset-property (buffer &rest proplist)
`(haxe-set-buffer-property
,buffer
,#(loop for (key value) on proplist by #'cddr
nconc (list (list 'quote key) value))))
(defalias 'haxe-pbset #'haxe-buffer-pset-property)
(defmacro haxe-buffer-setf-property (buffer &rest proplist)
`(with-current-buffer ,buffer
,#(list (append '(setf) proplist))))
(defalias 'haxe-pbsetf #'haxe-buffer-setf-property)
On an emacs -q session I executed the following:
;; set up a hook to create buffer local variable
(add-hook 'html-mode-hook
(lambda ()
(set (make-local-variable 'my-var) "abcd")))
;; find two files, one html and another java
(find-file-noselect "X.java")
(find-file-noselect "X.html")
;; see the buffer value in html buffer
(progn
(set-buffer "X.html")
(message "html:%s" my-var))
;; in java buffer I get error for a void variable
(progn
(set-buffer "X.java")
(message "java:%s" my-var))
Related
I have two functions that return an anonymous function, and almost all the content in the functions are the same except for a few lines.
(defun foo ()
(interactive)
(lambda (arg)
(when (not (string-empty-p arg))
(message "foo")
(message arg))))
(defun bar ()
(interactive)
(lambda (arg)
(when (not (string-empty-p arg))
(message "bar")
(message arg))))
Therefore, I created a function that received lists as argument and returned an anonymous function as follows:
(defun make-anonymous-func (&rest body)
(interactive)
(lambda (arg)
`(when (not (string-empty-p arg))
,#body
(message arg))))
However, when I executed the function returned by make-anonymous-func, the list in lambda was recognized as list literal and wasn't properly evaluated.
(funcall (make-anonymous-func '(message "foo")) "bar")
; ===> (when (not (string-empty-p arg)) (message "foo") (message arg))
Are there some good ways to make it work?
Thanks.
A form like
`(when (not (string-empty-p arg))
,#body
(message arg))
is equivalent to something like this:
(append '(when (not (string-empty-p arg)))
body
((message arg))))
So your function can be rewritten like this:
(defun make-anonymous-func (&rest body)
(lambda (arg)
(append '(when (not (string-empty-p arg)))
body
`((message arg)))))
And I think it's now very clear why this can't work. More generally, backquote forms provide a concise way of describing ways of constructing s-expressions by filling in templates, which is useful in macros, since s-expressions represent Lisp source code and macros are essentially functions whose values are source code, ad well as in other places where you want to construct s-expressions from templates.
(One horrible approach to do what you want is eval which evaluates source code. There are many reasons why this is a very bad answer however, so I won't go into it here.)
The general way to do what you're after is to rely on lexical scope. Conveniently elisp now has lexical scope, so you actually can do this. The following two examples assume that lexical-binding is true.
First of all if you merely want to capture the value of some variable bindings then you can do this:
(defun make-anonymous-func (msg)
(lambda (arg)
(when (not (string-empty-p arg))
(message msg)
(message arg))))
The second approach is if you want the function to run some general code: the way to do this is to pass it a function to call:
(defun make-anonymous-func (f)
(lambda (arg)
(when (not (string-empty-p arg))
(funcall f arg)
(message arg))))
And now
(funcall (make-anonymous-func
(lambda (m)
(message "foo")
(sit-for 1)))
"x")
Will cause two messages to appear, but this time there will be a pause between them so you'll see the first.
A final possibility, if what you want to do is create a bunch of similar functions statically – at compile time or definition time rather than dynamically at runtime – is to use a macro.
(defmacro define-messaging-function (name args &rest forms)
;; elisp has no destructuring, so check
(unless (and (listp args) (= (length args) 1)
(symbolp (car args)))
(error "bad arguments"))
(let ((arg (car args)))
`(defun ,name (,arg)
(when (not (string-empty-p ,arg))
,#forms
(message ,arg)))))
And now
(define-messaging-function foo (x)
(message "foo"))
Defines foo as a function which, when called, will do what the function returned by your original foo did.
Under Linux, eshell-autojump will do case sensitive matching which I just find a nuisance. I've tried to circumvent this by advising eshell/j with a eshell-under-windows-p that always returns t but to my chagrin eshell-under-windows-p invoked in eshell/j is unaffected by cl-letf. I've modified my eshell/j a bit to give me some debug info:
;; Modified eshell/j inside eshell-autojump.el to this
(defun eshell/j (&rest args) ; all but first ignored
"Jump to a directory you often cd to.
This compares the argument with the list of directories you usually jump to.
Without an argument, list the ten most common directories.
With a positive integer argument, list the n most common directories.
Otherwise, call `eshell/cd' with the result."
(setq args (eshell-flatten-list args))
(let ((path (car args))
(candidates (eshell-autojump-candidates))
(case-fold-search (eshell-under-windows-p))
result)
(when (not path)
(setq path 10))
(message "case-fold-search = %S" case-fold-search)
(message "eshell-under-windows-p returns %s from inside eshell/j" (eshell-under-windows-p))
(if (and (integerp path) (> path 0))
(progn
(let ((n (nthcdr (1- path) candidates)))
(when n
(setcdr n nil)))
(eshell-lisp-command (mapconcat 'identity candidates "\n")))
(while (and candidates (not result))
(if (string-match path (car candidates))
(setq result (car candidates))
(setq candidates (cdr candidates))))
(eshell/cd result))))
My init.el adds the advice to attempt to make eshell/j caseless by trying to trick it to think we are on Windows:
;; Added to init.el
(require 'eshell-autojump)
(advice-add 'eshell/j :around
(lambda (orig-fun &rest xs)
(cl-letf (((symbol-function 'eshell-under-windows-p) (lambda () t)))
(progn (message "eshell-under-windows-p returns %s from lambda" (eshell-under-windows-p)) (apply orig-fun xs)))))
But all I get in Messages buffer when I try to jump in eshell is:
;; I get in *Messages*
eshell-under-windows-p returns t from lambda
case-fold-search = nil
eshell-under-windows-p returns nil from inside eshell/j
My rookie knowledge of elisp is not enough to wrestle with probable scoping issues here. Can anyone decode why eshell-under-window-p is unaffected when called from eshell/j here?
I've found the answer. cl-letf does not work for byte compiled functions. As eshell-autojump is a package it gets byte compiled upon installation and cl-letf cannot be used to modify it's internal behavior. I had to resort to redefining the eshell/j which is a suboptimal solution.
When pressing TAB (org-agenda-goto) in org-agenda I want to open the related org-file in a new frame instead of splitting the existing frame.
I could create a modified function of org-agenda-goto replacing switch-to-buffer-other-window with switch-to-buffer-other-frame and rebinding the TAB-key but I assume there is a more elegant way to do so?
The quick solution would be as below modifying line 8:
(defun sk/org-agenda-goto (&optional highlight)
"Go to the entry at point in the corresponding Org file."
(interactive)
(let* ((marker (or (org-get-at-bol 'org-marker)
(org-agenda-error)))
(buffer (marker-buffer marker))
(pos (marker-position marker)))
(switch-to-buffer-other-frame buffer)
(widen)
(push-mark)
(goto-char pos)
(when (derived-mode-p 'org-mode)
(org-show-context 'agenda)
(recenter (/ (window-height) 2))
(org-back-to-heading t)
(let ((case-fold-search nil))
(when (re-search-forward org-complex-heading-regexp nil t)
(goto-char (match-beginning 4)))))
(run-hooks 'org-agenda-after-show-hook)
(and highlight (org-highlight (point-at-bol) (point-at-eol)))))
I assume it may be done more elegantly with advice but I'm not so experienced in emacs-lisp and would not know how exactly this could be achived or if using advice would be the right approach.
I found out in override prefered method are hints for using advice-add like this in order to replace the original function with my own:
(advice-add 'org-agenda-goto :override #'sk/org-agenda-goto)
You can use advice to temporarily redefine switch-to-buffer-other-window using cl-letf. Assuming your on at least emacs 25.1 you can use define-advice, eg.
(define-advice org-agenda-goto (:around (orig-fn &rest args) "new-frame")
(cl-letf (((symbol-function 'switch-to-buffer-other-window)
(symbol-function 'switch-to-buffer-other-frame)))
(apply orig-fn args)))
In the advice orig-fn is a placeholder to org-agenda-goto. Alternatively, you could temporarily override display-buffer's function (there are a number of options you could use here -- see help for display-buffer), eg.
(define-advice org-agenda-goto (:around (orig-fn &rest args) "new-frame")
(let ((display-buffer-overriding-action '(display-buffer-pop-up-frame)))
(apply orig-fn args)))
Is there a list of commands to choose Emacs modes? How can I know which modes are available on my platform? I mean the list of modes names you type after M-x.
type M-x *-mode <Tab> and emacs will list all interactive commands ending in -mode that are currently loaded.
I'm not sure you can easily see what modes are available after a require without first having loaded all the elisp files in your load path.
A function for listing major modes with some guess-work to avoid the listing of minor-modes and other functions that end in -mode:
(defun list-major-modes ()
"Returns list of potential major mode names (without the final -mode).
Note, that this is guess work."
(interactive)
(let (l)
(mapatoms #'(lambda (f) (and
(commandp f)
(string-match "-mode$" (symbol-name f))
;; auto-loaded
(or (and (autoloadp (symbol-function f))
(let ((doc (documentation f)))
(when doc
(and
(let ((docSplit (help-split-fundoc doc f)))
(and docSplit ;; car is argument list
(null (cdr (read (car docSplit)))))) ;; major mode starters have no arguments
(if (string-match "[mM]inor" doc) ;; If the doc contains "minor"...
(string-match "[mM]ajor" doc) ;; it should also contain "major".
t) ;; else we cannot decide therefrom
))))
(null (help-function-arglist f)))
(setq l (cons (substring (symbol-name f) 0 -5) l)))))
(when (called-interactively-p 'any)
(with-current-buffer (get-buffer-create "*Major Modes*")
(clear-buffer-delete)
(let ((standard-output (current-buffer)))
(display-completion-list l)
(display-buffer (current-buffer)))))
l))
C-h a mode
displays a summary of all modes
Here is the list : http://www.emacswiki.org/CategoryModes
I use the Emacs desktop module to save my open buffers between sessions. However I found that this accumulates more buffers than I want, so I wrote a small function to clean up the buffer list immediately before saving to the desktop file. This works as expected, but for strange reasons the .emacs.desktop gets scrambled occasionally, i.e. it contains a part of another buffer at its start, then the intended contents and then the result of the other buffer. I don't have the slightest idea, why this happens. Here is an excerpt from my .emacs file:
(defun kill-old-buffers ()
"Kill buffers from end of buffer list (not used recently) until no more than 50 buffers are left. Remove temporary buffers first"
(interactive)
(let* (desktop-buffer (current-buffer))
(dolist (buffer (buffer-list))
(if (or (string-match "^\*" (buffer-name buffer)) (string-match "\.hpp$" (buffer-name buffer)))
(kill-buffer buffer)
)
)
(setq all-buffers (reverse (buffer-list)))
(while (> (safe-length all-buffers) 50)
(setq buffer (pop all-buffers))
(if (not (string-equal (buffer-name buffer) (buffer-name (current-buffer))))
(kill-buffer buffer)
)
)
(switch-to-buffer desktop-buffer)
))
;; Kill old rarely-used buffers before saving
(add-hook 'desktop-save-hook
'(lambda () (kill-old-buffers)))
Any help would be appreciated.
I'm not sure if your function is really the cause of your problem. If it should be the case, the wrong usage of the let* that scottfrazer pointed out might be the cause. But you don't even need that let* (and switch-to-buffer) at all, because
what you're trying to do is better done with Emacs' built in save-excursion, and
you aren't actually switching the buffer ever.
OTOH, you should have used let instead of the setqs in the lower half of your function, because setq will otherwise change a variable from an enclosing lexical scope. In this case you might very well have stomped over a buffer variable from the function that's executing the desktop-save-hook which is another potential cause of your problem.
But you don't need those lets either because you can do the second loop with another dolist. You can get rid of those first 50 buffers that you don't want to loop over with nthcdr.
Here's my improved version of kill-old-buffers:
(defun kill-old-buffers ()
"Kill buffers from end of buffer list (not used recently) until
no more than 50 buffers are left. Remove temporary buffers first."
(interactive)
(save-excursion
(dolist (buffer (buffer-list))
(if (or (string-match "^\*" (buffer-name buffer))
(string-match "\.hpp$" (buffer-name buffer)))
(kill-buffer buffer)))
(dolist (buffer (reverse (nthcdr 50 (buffer-list))))
(unless (eq buffer (current-buffer))
(kill-buffer buffer)))))
It may not fix all your problems, but for starters you need another set of parens around the variable in your let* statement
(let* ((desktop-buffer (current-buffer)))