In emacs 24, set-temporary-overlay-map makes active a keymap which becomes inactivated as soon as the user presses a key which is not defined in that keymap.
I need to inactivate the overlay keymap manually, but no function is provided to do this in particular. I've peeked into the source code:
(defun set-temporary-overlay-map (map &optional keep-pred)
"Set MAP as a temporary keymap taking precedence over most other keymaps.
Note that this does NOT take precedence over the \"overriding\" maps
`overriding-terminal-local-map' and `overriding-local-map' (or the
`keymap' text property). Unlike those maps, if no match for a key is
found in MAP, the normal key lookup sequence then continues.
Normally, MAP is used only once. If the optional argument
KEEP-PRED is t, MAP stays active if a key from MAP is used.
KEEP-PRED can also be a function of no arguments: if it returns
non-nil then MAP stays active."
(let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
(overlaysym (make-symbol "t"))
(alist (list (cons overlaysym map)))
(clearfun
;; FIXME: Use lexical-binding.
`(lambda ()
(unless ,(cond ((null keep-pred) nil)
((eq t keep-pred)
`(eq this-command
(lookup-key ',map
(this-command-keys-vector))))
(t `(funcall ',keep-pred)))
(set ',overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook ',clearfunsym)
(setq emulation-mode-map-alists
(delq ',alist emulation-mode-map-alists))))))
(set overlaysym overlaysym)
(fset clearfunsym clearfun)
(add-hook 'pre-command-hook clearfunsym)
;; FIXME: That's the keymaps with highest precedence, except for
;; the `keymap' text-property ;-(
(push alist emulation-mode-map-alists)))
I gather that the mechanism to inactivate the current overlay keymap is as follows:
A function clearfun is defined to run before every command, checking if the previous command invoked was in the map.
If it was not in the map, the following code is executed:
(Why doesn't this format correctly? Ok, now it does)
(set ',overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook ',clearfunsym)
(setq emulation-mode-map-alists
(delq ',alist emulation-mode-map-alists))
Thus, what I really want is to execute the code above with the appropriate variables. But this code is part of a closure, and I'm having trouble determining values like overlaysym, clearfunsym, alist inside the closure. I tried looking for clearfunsym by eval-ing pre-command-hook, but strangely nothing is there (except for another unrelated hook).
I tried re-evaluating the function defintion and edebugging it, and I notcied after the (add-hook 'pre-command-hook clearfunsym), pre-command-hook is still nil, which puzzles me. I will continue digging deeper into the source code, and maybe I will just rewrite my own version of this function to additionally produce a force-clear function that I can call later, but maybe someone can see a cleaner solution.
You wrote: "I'm having trouble determining values like overlaysym"
But, overlaysym is evaluated. It has the value (make-symbol "t").
It is a symbol with name t. This makes it hard to access it but not impossible.
Evaluation of the following lines gives the out-commented results:
(setq mysym (make-symbol "t"))
;; t
(set mysym 'test)
;; test
(symbol-value mysym)
;; test
The same applies to clearfunsym which evaluates to clear-temporary-overlay-map.
One more comment: When you debug set-temporary-overlay-map you are hitting keys. Might it be that these keystrokes call clear-temporary-overlay-map and clear pre-command-hook?
Try that:
(defadvice set-temporary-overlay-map (after test activate)
(setq test-pre-command-hook pre-command-hook))
Then enter text-scale-mode (C-+) and look at test-pre-command-hook. For an istance evaluating test-pre-command-hook on my computer gave me the following list:
(clear-temporary-overlay-map tooltip-hide).
Let us do the same with emulation-mode-map-alists. Then we get:
(((t keymap (67108912 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
...
(fn)" nil]) (45 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
(fn)" nil]))))
Especially, note the t at the beginning. That means you find the overlay map by searching for the list with symbol t at the beginning.
Something like the following code fragment should be sufficient to delete the overlay map:
(when (assoc-string "t" (car emulation-mode-map-alists))
(setq emulation-mode-map-alists (cdr emulation-mode-map-alists)))
The when is just a protection. (Maybe, something else has killed the map before?) The temporary map should always be at the front (because of the push in set-temporary-overlay-map). Does anything have a chance to put another keymap in front of it? Maybe, something time-controlled? Then you would need to search emulation-mode-map-alists for the alist with the (make-symbol "t") keymap.
The original set-temporary-overlay-map is very confusing, unreadable, and relies on a lot of unnecessary dirty hacks and tricks. Using lexical binding, I have rewritten the function in a way that is more clear and modular.
The revised function uses lexical-binding to replace the eager-lazy evaluation hacks (in this case they are completely unnecessary).
The original function unnecessarily creates two symbols for each function, (clearfunsym and clear-fun are basically the same thing, the later being used only as the function cell of the former)
The revised function provides an force-overlay-clear, which is called by the clear-temporary-overlay-map pre-command-hook when the conditions are met (ie, that the last key was not in the overlay map). It can also be called by the user if he wants to manually clear this map. The function voids force-overlay-clear's own function cell, so it will err if called twice.
Code to test whether clear is applicable simplified.
I was not able to do away with the extremely weird (overlaysym (make-symbol "t")), fearing that some other code might rely on this t symbol. Thus, the revised version is almost certainly equivalent to the original version. I have tested this, and it works nicely.
(defun set-temporary-overlay-map (map &optional keep-pred)
(lexical-let* (
(map map)
(keep-pred keep-pred)
(clear-temporary-overlay-map nil)
(force-overlay-clear nil)
(overlaysym (make-symbol "t"))
(alist (list (cons overlaysym map))))
(fset 'force-overlay-clear (lambda ()
(message "clearing overlay")
;this is a copy of the original code to clear
(set overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook 'clear-temporary-overlay-map)
(setq emulation-mode-map-alists
(delq alist emulation-mode-map-alists))
;void the function cell of 'force-overlay-clear', an attempt to clear overlay twice will err
(fset 'force-overlay-clear nil)
))
(fset 'clear-temporary-overlay-map (lambda ()
(unless (cond
((null keep-pred) nil)
(keep-pred
(lookup-key map (this-command-keys-vector)))
(t (funcall keep-pred)))
(force-overlay-clear)
)))
(set overlaysym overlaysym)
(add-hook 'pre-command-hook 'clear-temporary-overlay-map)
;; FIXME: That's the keymaps with highest precedence, except for
;; the `keymap' text-property ;-
(push alist emulation-mode-map-alists))
)
You can do the following:
(defun my-exit-command ()
(do-what-the-q-key-should-do))
....(set-temporary-overlay-map
my-overlay-map
(lambda ()
(and (eq this-command (lookup-key my-overlay-map
(this-single-command-keys)))
(not (eq this-command 'my-exit-command)))))
....
Related
Is there a test to determine what type of hook is running -- e.g., window-configuration-change-hook?
For example, I have a function that contains conditions used determine whether it should be run -- it is used in conjunction with post-command-hook. I would like to use the same function for the window-configuration-change-hook, without triggering the first set of conditions:
(when
(or
(and
(not window-configuration-change-hook) ;; illustrative example only
(memq this-command this-last-command-inclusions)
(not multiple-cursors-mode))
window-configuration-change-hook) ;; illustrative example only
. . .
AFAIK there is no standardized method to determine which hook is running.
If you want to use advice or something like that to store this information you have to be aware that hooks can run within hooks. See the following example.
That means you need a stack-like structure to store that information.
EDIT: The example includes now a hook-stack storing the currently running hooks.
Note, I do not recommend to use this method since it is quite critical and does not work in all cases. Better, advice the mode/function or whatever what you want to identify.
(defvar hook-stack nil)
(defadvice run-hooks (around register-hooks activate)
"Store current hook into `hook-stack'."
(let ((hooks (ad-get-args 0)))
(loop for h in hooks do
(unwind-protect
(progn
(push h hook-stack)
(ad-set-args 0 (list h))
ad-do-it))
(pop hook-stack)
)))
(ad-remove-advice 'run-hooks 'around 'register-hooks)
(setq hooks1 nil)
(setq hooks2 nil)
(add-hook 'hooks1 (lambda () (message "Running hooks1, hook-stack: %S" hook-stack)))
(add-hook 'hooks2 (lambda () (message "Running hooks2") (run-hooks 'hooks1)
(message "Finishing hooks2, hook-stack: %S" hook-stack)))
(run-hooks 'hooks2)
Note: This does not work if run-hooks is called from C instaead of lisp. Furthermore, there are other functions like run-hook-with-args-until-success.
Once in a while I manually set the font-family and size different from the default, and I use buffer-face-mode to do it. (To be exact I use the mouse & pick one from the dialog box.) Once I set it, I'd like it to stay set for that buffer, even if I change modes, so I tried a customization. The idea was to add a change-major-mode-hook (which runs just before buffer-locals get killed) that would save the buffer face, if it is set, in a function to be called later- that much seems to work. But then that function seems to be called too soon, and when the mode change is over, buffer-face-mode is not active.
Here's the customization I cam up with so far
(defun my-preserve-bufface-cmmh ()
"Keep the state of buffer-face-mode between major-mode changes"
(if (and (local-variable-p 'buffer-face-mode) buffer-face-mode)
(delay-mode-hooks
(message "face is %s" buffer-face-mode-face) ; Just to show me it has the right face
(let ((my-inner-face buffer-face-mode-face))
(run-mode-hooks
(message "inner %s" my-inner-face) ; it still has the right face here
(setq buffer-face-mode-face my-inner-face)
(buffer-face-mode))))))
(add-hook 'change-major-mode-hook
'my-preserve-bufface-cmmh)
The messages both run and show a custom face, as they should, when I'm changing major-mode in a buffer with the minor-mode buffer-face-mode set. I had thought the combination of delay-mode-hooks ... run-mode-hooks would make setq buffer-face-mode-face ... (buffer-face-mode) run after the new mode was set up, but apparently not.
Is this customization "close"/salvageable for my wants? Is there a cleaner way?
The first thing is that delayed-mode-hooks is itself a local variable.
That means, if you set it by (delay-mode-hooks (run-mode-hooks ...)) in change-major-mode-hook
this will have no effect since it is killed instantaneously.
The second thing is that the stuff within your run-mode-hooks is
evaluated within my-preserve-bufface-cmmh. It should be defined as a back-quoted hook function
`(lambda () ...) where you splice in the values you want to keep.
The alternative would be to use lexical binding (which google).
The 2nd thing is demonstrated in the following example (to be evaluated step-by-step):
(defun test (str)
(let ((mytest (concat "hello " str)))
(add-hook 'my-own-hook `(lambda () (message "mytest:%S" ,mytest)))))
(test "you")
(run-hooks 'my-own-hook)
(test "world")
(run-hooks 'my-own-hook)
(put :myface 'test)
If you want to keep the font buffer-local you have to use a local variable that survives kill-all-local-variables such as buffer-file-name. You can hook a property there:
Edit: Even if the symbol has a buffer local value its properties are not buffer local. Thus, the previous approach did not work. Better: Create your own permanent buffer-local variable:
(defvar-local my-preserve-bufface nil
"Keep the state of buffer-face-mode between major-mode changes")
(put 'my-preserve-bufface 'permanent-local t)
(defun my-preserve-bufface-put ()
"Keep the state of buffer-face-mode between major-mode changes"
(and (local-variable-p 'buffer-face-mode)
buffer-face-mode
(setq my-preserve-bufface buffer-face-mode-face)))
(defun my-preserve-bufface-get ()
"Keep the state of buffer-face-mode between major-mode changes"
(and my-preserve-bufface
(setq buffer-face-mode-face my-preserve-bufface)
(buffer-face-mode)))
(add-hook 'change-major-mode-hook 'my-preserve-bufface-put)
(add-hook 'after-change-major-mode-hook 'my-preserve-bufface-get)
Thanks for the educational comments and especially the answer/example from #user2708138, which I am going to accept because it does answer the question.
Yet I am also going to answer my own question, since I did come up with working code that is a more general solution. I went down this path after finding I also wanted my font-size changes maintained and that they were from text-scale-mode, one more minor-mode to keep. This code reads a list of minor-modes to preserve, without my having to figure out which variables they use. (It isn't too hard for a human to figure them out, but I wanted to try having emacs do it).
Alas there is no function I know of to retrieve the variables used by a minor-mode, but the modes I'm interested in use the convention minor-mode-var-name, so this code just filters buffer-local variables for that pattern.
; Save & restore minor modes I wish to be "permanent" if set
(setq my-preserve-minor-modes '(buffer-face-mode text-scale-mode))
(defun my-preserve-minor-modes-cmmh ()
"Keep the state of desired-permanent minor modes between major-mode changes. Assumes that associated buffer-local minor-mode variables to save begin with `minor-mode-'"
(setq my-restore-minor-modes-acmmh nil)
(dolist (mm my-preserve-minor-modes)
(when (and (local-variable-p mm) (symbol-value mm))
(push mm my-restore-minor-modes-acmmh)))
(when my-restore-minor-modes-acmmh
(add-hook 'after-change-major-mode-hook 'my-restore-minor-modes-acmmh)
; Predicate-list showing if symbol starts with a preserved mode
(let ((mm-p-l `(lambda (locvar-nm)
(or ,#(mapcar (lambda (mm)
`(and (< ,(length (symbol-name mm))
(length locvar-nm))
(string-prefix-p ,(symbol-name mm)
locvar-nm)))
my-restore-minor-modes-acmmh)))))
; For each found minor mode, create fn to restore its buf-local variables
(dolist (locvar (buffer-local-variables))
(if (and (listp locvar) (funcall mm-p-l (symbol-name (car locvar))))
(push `(lambda()(setq ,(car locvar) ',(cdr locvar)))
my-restore-minor-modes-acmmh))))))
(defun my-restore-minor-modes-acmmh ()
"After major-mode change, restore minor-mode state, and remove self from hook. It restores state by calling the function stored in the variable my-restore-minor-modes-acmmh."
(remove-hook 'after-change-major-mode-hook 'my-restore-minor-modes-acmmh)
(dolist (restore-f my-restore-minor-modes-acmmh) (funcall restore-f)))
(add-hook 'change-major-mode-hook 'my-preserve-minor-modes-cmmh)
I did know about the permanent-local property but I wasn't sure about any unwanted side-effects... probably unwarranted paranoia on my part!
My answer could be improved if there is ever a way to get a list of variables for a minor mode, or by having the user specify an alist of variables per minor mode- in either case we wouldn't have to loop over buffer-local-variables anymore, and maybe simply making those all permanent-local would be all we need, simplifying the code quite a bit. Anyway, figuring all this out (with your help & looking at the fine manual) was quite educational.
I have most of my bookmarks prefixed by a letter in a way that
the first letter almost always uniquely determines the bookmark.
This way I can, for instance,
jump to my source folder (bookmarked as "s: source") with M-x bookmark-jump RET s RET.
I have it on a shortcut, so it's actually ~ s RET.
I'd like to get rid of RET in the end,
i.e. get M-x bookmark-quick-jump RET s or ~ s
to do the aforementioned job.
I'd also like it to fall back to the default behavior: to show me all bookmarks
that start with the given letter, in case there's not just one variant.
So far, I've got:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(bookmark-jump
(if (eq 1 (length completions))
(car completions)
(completing-read "Jump to bookmark: " bookmark-alist nil t str)))))
There's still two hiccups:
Firstly, I need to jump into minibuffer somehow and stick in there this map (don't know how to do this):
(setq bookmark-quick-jump-map
(let ((map (make-sparse-keymap)))
(mapcar (lambda (key)
(define-key map key
(lambda()
(interactive)
(bookmark-do-quick-jump key))))
(loop for c from ?a to ?z
collect (string c)))
map))
Secondly, when I do a call
(bookmark-do-quick-jump "o")
It comes back with 3 variants (org-capture-last-stored, org-capture-last-stored-marker...).
I'm in minibuffer now, but I still need to press RET RET
to see these 3 variants. I'd like this to be done automatically.
I'd appreciate any responses that either directly answer my two sub-problems,
or an altogether different approach, as long as I can get the behavior and usability
that I described.
UPD:
I've solved the second thing by switching from completing-read to ido-completing-read:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(bookmark-jump
(if (eq 1 (length completions))
(car completions)
(ido-completing-read "Jump to bookmark: " completions nil t str)))))
Btw, I forgot to mention that I use bookmark+. I'm not sure if jumping to dired
is supported by the default bookmark-jump.
We can remap self-insert-command during the completing-read to trigger the auto-completion and auto-acceptance behaviour.
I originally used (or (minibuffer-complete-and-exit) (minibuffer-completion-help)) which at first glance worked very nicely but, as noted in the comments, is less than ideal when one bookmark's name is the prefix of another, as it will immediately accept the shorter name, hence making the longer one inaccessible.
Calling minibuffer-complete and minibuffer-completion-help together breaks the completion functionality, however, so instead I've copied the relevant part of minibuffer-complete-and-exit to a new function. Using this resolves all of the earlier problems.
(require 'bookmark)
(defvar bookmark-do-quick-jump-map (copy-keymap minibuffer-local-must-match-map)
"Keymap for `bookmark-do-quick-jump'.
`minibuffer-local-must-match-map' is used by `completing-read' when its
REQUIRE-MATCH argument is t.
In `bookmark-do-quick-jump' we bind this modified copy to use in its place.")
(define-key bookmark-do-quick-jump-map
[remap self-insert-command] 'my-self-insert-complete-and-exit)
(defun bookmark-do-quick-jump ()
"Jump to specified bookmark with auto-completion and auto-acceptance."
(interactive)
(bookmark-maybe-load-default-file)
(let ((minibuffer-local-must-match-map bookmark-do-quick-jump-map))
(bookmark-jump
(completing-read "Jump to bookmark: " bookmark-alist nil t))))
(defun my-self-insert-complete-and-exit (n)
"Insert the character, then attempt to complete the current string,
automatically exiting when only one option remains, and displaying the
completion options otherwise."
(interactive "p")
(self-insert-command n)
(my-minibuffer-complete)
(let ((my-completions (completion-all-sorted-completions)))
(if (and my-completions (eq 0 (cdr my-completions)))
(exit-minibuffer)
(minibuffer-completion-help))))
(defun my-minibuffer-complete ()
"Copied from `minibuffer-complete-and-exit'."
(interactive)
(condition-case nil
(completion--do-completion nil 'expect-exact)
(error 1)))
Edit:
I took another stab at this using ido. It's a little unfortunate that you don't get the next 'important character' highlighted the way that you do with the regular minibuffer completion (as that was a nice indicator of what to type next), but this seems to work nicely in other respects.
(require 'bookmark)
(require 'ido)
(defvar bookmark-ido-quick-jump-map (copy-keymap minibuffer-local-map)
"Keymap for `bookmark-ido-quick-jump'.
Every time `ido-completing-read' is called it re-initializes
`ido-common-completion-map' and sets its parent to be `minibuffer-local-map'.
In `bookmark-ido-quick-jump' we provide this modified copy as a replacement
parent.")
(define-key bookmark-ido-quick-jump-map
[remap self-insert-command] 'my-self-insert-and-ido-complete)
(defun bookmark-ido-quick-jump ()
"Jump to selected bookmark, using auto-completion and auto-acceptance."
(interactive)
(bookmark-maybe-load-default-file)
(let ((minibuffer-local-map bookmark-ido-quick-jump-map)
(ido-enable-prefix t))
(bookmark-jump
(ido-completing-read "Jump to bookmark: "
(loop for b in bookmark-alist collect (car b))))))
(defun my-self-insert-and-ido-complete (n)
"Insert the character, then attempt to complete the current string,
automatically exiting when only one option remains."
(interactive "p")
(self-insert-command n)
;; ido uses buffer-local pre- and post-command hooks, so we need to
;; co-operate with those. We append our post-command function so that
;; it executes after ido has finished processing our self-insert.
(add-hook 'post-command-hook
'my-self-insert-and-ido-complete-post-command t t))
(defun my-self-insert-and-ido-complete-post-command ()
(remove-hook 'post-command-hook
'my-self-insert-and-ido-complete-post-command t)
;; Now that ido has finished its normal processing for the current
;; command, we simulate a subsequent `ido-complete' command.
(ido-tidy) ;; pre-command-hook
(ido-complete)
(ido-exhibit)) ;; post-command-hook
Here's another take:
(defun bookmark-do-quick-jump (str)
(let ((completions (all-completions str bookmark-alist)))
(if (null (cdr completions))
(bookmark-jump (car completions))
(minibuffer-with-setup-hook
(lambda () (insert str)
(minibuffer-completion-help))
(call-interactively 'bookmark-jump)))))
Or yet another (even more guaranteed untested):
(defadvice bookmark-jump (around quick-bookmarks activate)
(minibuffer-with-setup-hook
(lambda ()
(add-hook 'post-self-insert-hook
(lambda ()
(let ((completions
(all-completions (minibuffer-contents)
bookmark-alist)))
(if (cdr completions)
(minibuffer-completion-help)
(minibuffer-complete-and-exit))))
nil t))
ad-do-it))
Sounds like you're doing a lot of extra work. Just use Icicles.
User option icicle-incremental-completion non-nil and non-t means show all matches as soon as you type input.
Option icicle-top-level-when-sole-completion-flag non-nil means accept a solitary match without your needing to hit a key (e.g. RET).
Instead of customizing the options to have these values in general, you can just bind them to the values in your own command.
I'm using folding-mode in emacs and was trying to make a function to insert the appropriate folding marker (start or end) depending on mode. So far I have
(defun insert-folding-mode-mark ()
(interactive)
(let ((st "##{{{")
(en "##}}}")
string-to-insert)
(save-excursion
(setq string-to-insert
(let ((here (point))
sp ep)
(setq sp (search-backward st))
(goto-char here)
(setq ep (search-backward en))
(if (< sp ep) st en))))
(insert string-to-insert)))
This inserts "##{{{" at (point) unless "##{{{" precedes it, in which case it inserts "##}}}".
I'd like to replace the first (let) assignment with something that determines the start and end markers with something like
(let* ((match (assoc (intern mode-name) folding-mode-marks-alist))
(st (nth 1 match))
(en (nth 2 match)))
[is (intern) meant to be called in this way?] A truncated version of my folding-mode-marks-alist looks something like
((ess-mode "##{{{" "##}}}")
(tex-mode "%{{{" "%}}}")
(python-mode "# {{{" "# }}}")
(emacs-lisp-mode ";;{{{" ";;}}}")
(TeX-mode "%{{{" "%}}}")
(LaTeX-mode "%{{{" "%}}}"))
while the mode-name returned from various modes are {"Emacs-Lisp", "ESS[S]", "PDFLaTeX", "Python", ...}. Seems like I might want to do some partial matching with strings using (downcase), (concat x "-mode"), and so on, but was wondering if there was an idiomatic way in emacs lisp to do this sort of matching with keys of an alist, or do I just have to have a separate block of code by which I extract the keys with (mapcar 'car folding-mode-marks-alist) and convert each symbol to string (how?) to do the matching?
Thanks much!
Emacs Lisp has a destructuring-bind facility which may be helpful here. Also taking advantage of the fact that the symbol naming the current major mode is available via the variable major-mode, you can write something like this:
(destructuring-bind (st en) (cdr (assoc major-mode folding-mode-marks-alist))
; do stuff
)
Note that this won't work if (assoc major-mode folding-mode-marks-alist) returns nil, so better replace that with a call to some custom function capable of returning a sensible default.
In addition to major-mode being more appropriate than mode-name here, the function insert-folding-mode-mark as listed above would throw an error if there were no folding marker between cursor and beginning of buffer. Here is a revision without that quirk:
(require 'cl)
(defun insert-folding-mode-mark ()
(interactive)
(flet ((fn (s) (save-excursion (or (search-backward s () t) 0))))
(destructuring-bind (mode st en)
(or (assoc major-mode folding-mode-marks-alist) '(nil "" ""))
(insert (if (<= (fn st) (fn en)) st en)))))
Edit: fix problem pointed out in comment.
You may be interested to know there are comment-start and comment-end variables which should already contain the information you need based on major-mode. Something like
(search-backward (concat comment-start "{{{"))
...
(insert comment-start "{{{" comment-end)
should be sufficient. Of course, for lisp modes comment-start is ";" so you may want to do what you are doing to get ";;" but fall back on comment-start for other modes. You can also (setq comment-start ";;") though I'm not entirely sure what difference that makes for the lisp modes.
I've been using Emacs for a couple months now, and I want to get started in elisp programming. Specifically, I'd like to write my own interactive function. However, I'm more than a bit lost. (interactive ...) has tons of options and I'm not sure which one I want. Then, I don't really know the names of the functions I need. If someone could kindly help me turn my pseudocode into real code, I would be mighty appreciative! (And as always, any links to informative places would be good. Right now I've just been reading this.)
Here is pseudocode for what I'd like to do:
(defun my-func (buffer) ; I think I need the buffer as an arg?
"does some replacements"
(interactive ???) ; ?
(let (replacements (list
'("a-regexp-string" . "a-replacement-string-with-backreferences")
...)) ; more of the above
(while replacements
(let (current (car replacements)) ; get a regexp-replacement pair
(some-regexp-replace-func buffer (car current) (cdr current)) ; do the replacement
(setq replacements (cdr replacements))))))
First, from the looks of your function you would probably be doing it in the current buffer, so no, you don't need to have a 'buffer' argument. If that's a bad assumption, I can change the code. Next, in a 'let' if you are assigning to variables you need another set of parens around each pair of var/value. Finally, when looping through a list I prefer to use functional-programming-like functions (mapcar, mapc, etc.). I'll try to inline some comments here:
(defun my-func ()
"Do some replacements"
(interactive)
(let ((replacements (list '("foo" . "bar")
'("baz" . "quux"))))
(save-excursion ; So point isn't moved after this function
(mapc (lambda (x) ; Go through the list, with this 'inline' function
; being called with each element as the variable 'x'
(goto-char (point-min)) ; Start at the beginning of the buffer
(while (re-search-forward (car x) nil t) ; Search for the car of the replacement
(replace-match (cdr x)))) ; And replace it with the cdr
replacements)))) ; The list we're mapc'ing through
As for what to read, I'd suggest the Elisp manual that comes with Emacs.