Defun with optional args inside the lambda? - emacs

I want to call defun with optional args inside the lambda:
(global-set-key (kbd "M-S-v")
(lambda ()
(interactive) (bk-cleans-text nil 2)))
The function works as standalone:
(bk-cleans-text nil 2)
But when called inside the lambda it complains on cond:
(defun bk-cleans-text (&optional killIt ParsSepBy)
(interactive)
(cond
((= 1 ParsSepBy)
(setq Find "\n+")
(setq Replace "\n"))
((= 2 ParsSepBy)
(setq Find "\n\n+")
(setq Replace "\n\n"))
(t
(setq Find "\n+")
(setq Replace "")))
(message "F: %s, R: %s" Find Replace))

The problem is in the key combination that you have chosen. I used another one and it worked.

Related

Can't assign keys programmatically to bookmarks in a loop

Suppose, we have n bookmarks in the list (n <= 9)
% Bookmark
- --------
1 mark-1 ~/work/file-1.txt
2 mark-2 ~/work/file-2.txt
...
9 mark-9 ~/work/file-9.txt
I wanted to programmatically attach key <n> to jump to n-th bookmark.
Code
;get a sorted list of bookmarks
(let ((bookmarks (sort (bookmark-all-names) 'string<))
(i 1))
(while bookmarks
(setq key (number-to-string i))
;try to assign key i -> jump-to-bookmark(bookmarks[i])
(define-key bookmark-bmenu-mode-map
(kbd key)
(lambda () (interactive) (bookmark-jump (car bookmarks))))
;next bookmark
(setq bookmarks (cdr bookmarks))
(setq i (+ i 1))
))
Unfortunately, it gives an error:
(wrong-type-argument integer-or-marker-p key)
The issue here is related to the fact that you are using dynamic binding, but trying to access from a lambda a variable that is not in scope anymore (bookmarks). You can fix it by defining the lambda this way:
`(lambda () (interactive) (bookmark-jump (car ',bookmarks))))
You can see an explanation here.
(btw, code will fail when you have more than 9 bookmarks)
This is the modified code that works fine for me:
(let ((bookmarks (sort (bookmark-all-names) 'string<))
(i 1)
key)
(while bookmarks
(setq key (number-to-string i))
(define-key bookmark-bmenu-mode-map
(kbd key)
`(lambda () (interactive) (bookmark-jump (car ',bookmarks))))
(setq bookmarks (cdr bookmarks))
(setq i (+ i 1))))

Is is possible to bind a key plus a variable in Emacs?

I have buffers named *terminal<1>*, *terminal<2>*, *terminal<3>*, etc. Is there a way to bind a key combination that will take an argument for the number? That is, I want to bind C-c 1 to switch to *terminal<1>* and C-c 2 to switch to *terminal<2>*, and so on. If I can't do this directly, is it possible to do metaprogramming in Elisp that will define all the functions for me?
In this suggestion, the interactive switch-to-terminal will take either a prefix argument C-u 2 for example, or prompt the user.
The macro then makes setting up the key bindings a little easier.
Finally I bind C-c 1 to C-c 4 to switch to *terminal<1>* to *terminal<4>*.
(defun switch-to-terminal (buf-num)
(interactive "NNumber of buffer to visist: ")
(let* ((buf-name (format "*terminal<%d>*" buf-num))
(buf (get-buffer buf-name)))
(unless buf
(error "No buffer %s" buf-name))
(switch-to-buffer buf)))
(defmacro bind-switch-to-terminal (num)
`(global-set-key
,(kbd (format "C-c %d" num))
(lambda ()
(interactive)
(switch-to-terminal ,num))))
(bind-switch-to-terminal 1)
(bind-switch-to-terminal 2)
(bind-switch-to-terminal 3)
(bind-switch-to-terminal 4)
This change uses the same switch-to-terminal function, but replaces bind-switch-to-terminal with a function. The lexical-let* allows for the creation of a closure to create unique terminal switching functions, the dotimes loop then binds for C-c 1 to C-c 9.
(defun bind-switch-to-terminal (num)
(lexical-let* ((buf-num num)
(switch-func
(lambda ()
(interactive)
(switch-to-terminal buf-num))))
(global-set-key
(kbd (format "C-c %d" buf-num))
switch-func)))
(dotimes (num 9)
(bind-switch-to-terminal (1+ num)))
You can bind keys as usual:
(global-set-key (kbd "C-c 1") (lambda ()
(interactive)
(switch-to-buffer "*terminal<1>*")))
To create all the shortcuts from 1 to 9 we would use macros.
edit: This buggy version may put you on tracks. I give up :(
(defmacro gototerminal (count)
`(global-set-key (kbd ,(concat "C-c " (number-to-string count)))
;; with the comma I want to evaluate what is inside concat
(lambda () (interactive)
(switch-to-buffer (concat "*terminal<" ,count ">*"))))
)
(progn (setq count 1)
(while (< count 10)
(gototerminal count)
(setq count (1+ count))
))
ps: an elisp debugger is edebug. Set it with C-u C-M-x
I would write a function that calls interactive with an n argument which indicates that the function reads a number from the mini buffer:
(defun test (x)
(interactive "nNumber of buffer to visit: ")
(message (concat "received number: " (number-to-string x))))
Binding this to a key will let you enter a number in the mini buffer.
Another way is to use a numerical argument:
(defun test (x)
(interactive "P")
(message (concat "received number: " (number-to-string x))))
Say you bind this function to C-c c, you can then pass it the number 2 as an argument by pressing C-u 2 C-c c.
If you avoid the usage of an existing prefix key like C-c you can trigger a command with one keypress e.g. F9.
This command can have a single key as input.
Example:
(defun test (k)
(interactive "K")
(message "Pressed key: %d" (- (aref k 0) ?0)))
(local-set-key [f9] 'test)

Convert command line arguments to list in Emacs Lisp

In an automated Emacs Lisp --batch/--script script I need to process the command line arguments given to the script.
I've gotten as far as getting the arguments into a list of the the form:
("--aaa=bbb" "--ccc=ddd=eee" "--blah")
Now, I need to convert them to a list of the form:
(("aaa" "bbb") ("ccc" "ddd=eee") ("blah"))
In Python I'd write something like;
output = []
for v in input:
output.append(v[2:].split("=", 1))
But have been unable to convert that code to Emacs Lisp. I found Elisp split-string function to split a string by . character but wasn't able to figure out how to make it only split on the first equals.
I was heading down a route of using (substring "abcdefg" x x) with (search) from the cl package but it felt like there should be a better way? I think also want to use (mapc '<function> input) where function does the v[2:].split("=",1) part.
You can use split-string. See the following code example.
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (argstr)
(when (string-match "^--" argstr)
(split-string (substring argstr 2) "=")))
cmd-line))
The output is (("aaa" "bbb") ("ccc" "ddd" "eee") ("blah")).
That is not exactly what you want because of "eee". Maybe you can use that and just neglect "eee".
If the "eee" is really a problem a small modification helps:
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (arg)
(when (string-match "^--" arg)
(setq arg (split-string (substring arg 2) "="))
(if (cdr arg)
(setcdr (cdr arg) nil))
arg))
cmd-line))
The output is:
(("aaa" "bbb") ("ccc" "ddd") ("blah"))
Variant for the new requirement in the question:
(setq cmd-line '("--aaa=bbb" "--ccc=ddd=eee" "--blah"))
(setq cmd-line (mapcar (lambda (arg)
(when (string-match "^--\\([^=]*\\)\\(?:=\\(.*\\)\\)?" arg)
(let ((opt (match-string 1 arg))
(val (match-string 2 arg)))
(if val
(list opt val)
(list opt)))))
cmd-line))
The output is:
(("aaa" "bbb") ("ccc" "ddd=eee") ("blah"))

Generate quoted symbol in Emacs Lisp macro

I am creating a simple macro to disable show trailing whitespace in certain major modes (I will loop this macro in a dolist loop later):
(defmacro non-trailing-whitespaces-modes (mode)
(let ((hook (intern (concat mode "-mode-hook"))))
`(add-hook ,hook (lambda () (setq show-trailing-whitespace nil)))))
After this macroexpand: (macroexpand '(non-trailing-whitespaces-modes "eshell")), the result is
(add-hook eshell-mode-hook
(lambda nil
(setq show-trailing-whitespace nil)))
I want eshell-mode-hook to be 'eshell-mode-hook, otherwise it will cause error.
Simply change the body of the macro to begin (add-hook ',hook ...)
No need for a macro, here:
(defun non-trailing-whitespaces-modes (mode)
(let ((hook (intern (concat mode "-mode-hook"))))
(add-hook hook (lambda () (setq show-trailing-whitespace nil)))))

emacs/openwith how to pass arguments to program?

I am using the openwith package in emacs. I would like to open .fig files with xfig with some additional options, for example:
xfig -specialtext -latexfont -startlatexFont default file.fig
openwith is working for me with other file associations where I don't need to pass additional options. I tried the following in my .emacs file
(setq
openwith-associations
'(("\\.fig\\'" "xfig" (file))))
which works, but
(setq
openwith-associations
'(("\\.fig\\'" "xfig -specialtext -latexfont -startlatexFont default" (file))))
does not work (error: Wrong type argument: arrayp, nil), also
(setq
openwith-associations
'(("\\.fig\\'" "xfig" (" -specialtext -latexfont -startlatexFont default " file))))
does not work, although here I don't get any error. It says "Opened file.fig in external program" but nothing happens. In this case, I notice that there is an xfig process running with all these options.
Could someone let me know how to fix this?
Thanks for the help.
I have no clue how this works, so I just document how one can figure it by reading the code:
The important code in openwith.el is the call to start-process in:
(dolist (oa openwith-associations)
(let (match)
(save-match-data
(setq match (string-match (car oa) (car args))))
(when match
(let ((params (mapcar (lambda (x)
(if (eq x 'file)
(car args)
(format "%s" x))) (nth 2 oa))))
(apply #'start-process "openwith-process" nil
(cadr oa) params))
(kill-buffer nil)
(throw 'openwith-done t))))
The in your case oa would have the following structure, and the cadr is "xfig":
(cadr '("\.fig\'" "xfig" (file))) ;; expands to => xfig
This is the definition and doc of start-process:
Function: start-process name buffer-or-name program &rest args
http://www.gnu.org/software/emacs/elisp/html_node/Asynchronous-Processes.html
args, are strings that specify command line arguments for the program.
An example:
(start-process "my-process" "foo" "ls" "-l" "/user/lewis/bin")
Now we need to figure out how params is constructed. With your example the argument to the mapcar is:
(nth 2 '("\.fig\'" "xfig" (file))) ;=> (file)
By the way you can write such lines in the scratch buffer in emacs and run them with C-M-x.
The (car args) refers to the parameter you give to openwith-association, note how the occurance of 'file in (nth 2 oa) is replaced by that. I'll just replace it with "here.txt" for now:
(mapcar (lambda (x)
(if (eq x 'file)
"here.txt"
(format "%s" x))) (nth 2 '("\.fig\'" "xfig" (file)))) ;=> ("here.txt")
Okay, now we see how the argument should be constructed:
(mapcar (lambda (x)
(if (eq x 'file)
"here.txt"
(format "%s" x)))
(nth 2 '("\.fig\'" "xfig"
("-specialtext" "-latexfont" "-startlatexFont" "default" file))))
; => ("-specialtext" "-latexfont" "-startlatexFont" "default" "here.txt")
Try this:
(setq openwith-associations
'(("\\.fig\\'" "xfig" ("-specialtext" "-latexfont" "-startlatexFont" "default" file))))
You have to supply each word as a single string in the list of parameters.