First elisp attempt - minor mode for tab key not being invoked on tab? - emacs

I've decided to get my toes wet with a bit of lisp, since I want to make emacs behave a little better when I hit TAB. My command works fine. It just performs indent-for-tab-command and if nothing happens, it performs tab-to-tab-stop, on the assumption that it's unlikely I was hitting TAB just to have the point refuse to budge when I'm inside a multi-line string or some such. After the first TAB press, it continues to do tab-to-tab-stop until either editing resumes, or the point is moved elsewhere. AFAIK, my logic is ok, though my lisp code probably isn't!
Originally I just hacked this into my emacs dot files by doing (local-set-key (kbd "TAB") 'tab-dwim) for major modes where I wanted this behaviour. That worked as expected.
Then I decided that what I was doing was basically a minor mode, so I tried to move the key-binding into a minor-mode. For some reason, even though the minor mode is enabled (as indicated in the mode line, and just from toggling it on and off), my tab-dwim function isn't being invoked when I hit the TAB key. I can still invoke it with M-x as expected.
What am I doing wrong with my minor mode's :keymap?
;;;
;; TAB DWIM
; buffer-local before/after point tracking
(setq point-before-tab nil)
(setq point-after-tab nil)
(make-local-variable 'point-before-tab)
(make-local-variable 'point-after-tab)
(defun tab-dwim ()
"Indents normally once, then switches to tab-to-tab-stop if invoked again.
tab-dwim will always perform tab-to-tab-stop if the first TAB press does not
cause the point to move."
(interactive)
(print "in tab-dwim now") ; THIS LINE IS NEVER INVOKED ON TAB?
(setq point-before-tab (point))
(if (eq point-before-tab point-after-tab) ; pressed TAB again
(tab-to-tab-stop)
(indent-for-tab-command))
(if (eq (point) point-before-tab) ; point didn't move
(tab-to-tab-stop))
(setq point-after-tab (point)))
(define-minor-mode tab-dwim-mode
"Toggle tab-dwim-mode.
With a non-nil argument, turns on tab-dwim-mode. With a nil argument, turns it
off.
When tab-dwim-mode is enabled, pressing the TAB key once will behave as normal,
but pressing it subsequent times, will continue to indent, using
tab-to-tab-stop.
If tab-dwim determines that the first TAB key press resulted in no movement of
the point, it will indent according to tab-to-tab-stop instead."
:init-value nil
:lighter " DWIM"
:keymap
'(([TAB] . tab-dwim)))
(provide 'tab-dwim)
Cheers,
Chris

I think you are very close.
Try this for your keymap:
'(("\t" . tab-dwim)))

Yes, use "\t" or the vector format "[(tab)]".
Some additional notes for your elisp development:
Avoid making global variables as much as possible. In this case, I think dynamically binding with let is appropriate. Also have a look at let*.
Understand the difference between make-local-variable and make-variable-buffer-local. The way you've written your code, the buffer-local variable would only exist in the buffer that loads your package.
As Nemo mentioned, it's extemely extremely extremely recommended that you use a common prefix for all variables/functions related to each package. Emacs has only one namespace, this is the "hacky" way to keep it somewhat organized.

Related

Different results when elisp function is run different ways; why?

EDIT: Perhaps (in original post) I used the term "transient" incorrectly (I'm not familiar enough with the jargon yet). What I really mean is that the highlighted region will disappear immediately when the user presses a navigation keys eg. arrow-keys... (2nd EDIT: I've removed the word "transient")
The particular issue of selecting a region so the user gets "cursor-key movement will make highlighting disappear" has been the bane of my existance recently. I get differnt results depending on how I run the following script.
Why does it give different results, and more specifically, is there a way to make it produce "cursor-keys make highlighting disappear" regardless of which mode is running, or whether it is being evaluated while testing? .. CUA mode has this behaviour, but I really need that non CUA mode does it too (and eval, if possible)...
Here are the results, followed by the code. (GNU Emacs 23.1.1)
CUA mode enabled
Evaluate via C-x C-e — both (call-trans-hi) and (trans-hi)
NO-GO: Both set mark and move point to EOL, but nothing is highlighted.
Execute M-x call-trans-hi
ok: Works fine; the region is highlighted and then disappears the first time a key is pressed.
Via key binding C-f1
ok: Works fine; the region is highlighted and then disappears the first time a key is pressed.
no CUA mode (pretty much std emacs)
Evaluate via C-x C-e
NO-GO: Same as 1. when CUA is enabled.
Execute M-x call-trans-hi
NO-GO: The line is highlighted, but it is sticky! and requires C-g (keyboard-quit) to clear it.
Via key binding C-f1
NO-GO: The line is highlighted, but it is sticky! and requires C-g (keyboard-quit) to clear it.
;test (trans-hi) EOL
(defun trans-hi ()
"transient highlight"
(beginning-of-line)
(push-mark (point))
(end-of-line)
(activate-mark))
;test (call-trans-hi) EOL
(defun call-trans-hi ()
"call transient highlight"
(interactive)
(trans-hi))
(global-set-key [C-f1] 'call-trans-hi)
When you look at the source of activate-mark, you can see that it's just setting some variables. I suppose that's why you don't see the mark in both 1., because the actual highlighting happens in some stuff that's done when executing functions interactively instead of just calling them.
In the other cases of no CUA-mode, that just how transient highlighting works outside of CUA-mode. If you want the CUA-mode behaviour, use CUA-mode resp. that part of it.
EDIT:
Does this change (the addition of the setq line) to trans-hi make the highlighting work the way you want?
(defun trans-hi ()
"transient highlight"
(beginning-of-line)
(push-mark (point))
(end-of-line)
(setq transient-mark-mode (cons 'only transient-mark-mode))
(activate-mark))
If you want to see the region highlighted when you mark it, you need
to activate the minor mode transient-mark-mode.
When a region is highlighted and a character insterted the default is
to disable the highlighting and insert the character at the cursor.
If you wish you can delete the selected region by activating the minor
mode delete-selection-mode.

How to make emacs behave closer to the regular editors?

I'm using Emacs 23.1.1 on Ubuntu with Emacs starter kit. I primarily work in the lua-mode.
Is there a way to stop Emacs being so smart about indentation? I'm used to the dumb editors, and press all the required keys manually.
I want to use two spaces per indent, tabs-to-spaces.
When I press RETURN, the new line indentation must match the previous line.
When I press TAB on the leading whitespace, the line contents must be indented by one indentation unit.
When I press TAB on the beginning of empty line, the cursor must move one indentation unit to the right.
Oh, and I'd like to get soft word wrap on 80th column and trim-trailing-spaces on save as well.
Update:
(Would put this in a comment, but it needs formatting)
If I use Thomas's solution, auto-indent on RETURN is "fixed", but TAB still indents weirdly:
local run = function(...)
x
"x" marks the spot where cursor appears after I type the first line and hit RETURN, TAB.
Emacs has a concept of modes, which means that depending on what type of file you're editing it provides special functionality that is useful for that file. Every buffer has one major mode associated and optionally a number of minor modes.
Indentation is one of the things that is typically mode-dependent. That is, you may have to configure indentation separately for every major-mode, because otherwise when you load a new file, its associated major mode may override your indentation settings. It's possible though to write a function that configures indentation and set up Emacs in a way that the function is invoked whenever a new major-mode is started.
In order to realize the settings you want, you'll need to run a few lines of elisp code. (Unfortunately your description of what should happen when you hit TAB leaves out some details, I've implemented the simplest version I could think of below -- if it's not what you want, that can be changed, of course.)
Put the following code in the file named .emacs in your home directory (~):
(setq-default indent-tabs-mode nil) ; use spaces for indentation
(defvar my-indentation-width 2
"The number of spaces I prefer for line indentation.")
(defun my-enter ()
"Inserts a newline character then indents the new line just
like the previous line"
(interactive)
(newline)
(indent-relative-maybe))
(defun my-indent ()
"When point is on leading white-space of a non-empty line, the
line is indented `my-indentation-width' spaces. If point is at
the beginning of an empty line, inserts `my-indentation-width'
spaces."
(interactive)
(insert (make-string my-indentation-width ? )))
(defun my-indentation-setup ()
"Binds RETURN to the function `my-enter' and TAB to call
`my-indent'"
(local-set-key "\r" 'my-enter)
(setq indent-line-function 'my-indent))
(defun delete-trailing-whitespace-and-blank-lines ()
"Deletes all whitespace at the end of a buffer (or, rather, a
buffer's accessible portion, see `Narrowing'), including blank
lines."
(interactive)
(let ((point (point)))
(delete-trailing-whitespace)
(goto-char (point-max))
(delete-blank-lines)
(goto-char (min point (point-max)))))
;; make sure trailing whitespace is removed every time a buffer is saved.
(add-hook 'before-save-hook 'delete-trailing-whitespace-and-blank-lines)
;; globally install my indentation setup
(global-set-key "\r" 'my-enter)
(setq indent-line-function 'my-indent)
;; also override key setting of major-modes, if any
(add-hook 'after-change-major-mode-hook 'my-indentation-setup)
This works for me in Emacs 23, although I may have missed some edge cases. However, these changes are so fundamental that I predict you will run into incompatibilities sooner or later with some major-modes that expect indentation to work they set it up. If you really want to get into Emacs it's worthwhile adapting the habits you inherited from other editors to the way Emacs does things.
For soft word-wrap there is a minor-mode called "longlines" which you can download from here: http://www.emacswiki.org/cgi-bin/emacs/download/longlines.el I haven't used it so I can't tell you how well it works.
Fixing TAB and RETURN:
(global-set-key "\t" 'self-insert-command)
(global-set-key "\r" 'newline-and-indent)
Fill column (haven't tried): say ESC x customize-var, enter fill-column, set to 80.

How can I easily reload Emacs lisp code as I am editing it?

As an Emacs beginner, I am working on writing a minor mode. My current (naive) method of programming elisp consists of making a change, closing out Emacs, restarting Emacs, and observing the change. How can I streamline this process? Is there a command to refresh everything?
You might try using M-C-x (eval-defun), which will re-evaluate the top-level form around point. Unlike M-x eval-buffer or C-x C-e (exal-last-sexp), this will reset variables declared with defvar and defcustom to their initial values, which might be what's tripping you up.
Also try out C-u C-M-x which evaluates the definition at point and sets a breakpoint there, so you get dropped into the debugger when you hit that function.
M-x ielm is also very useful as a more feature-rich Lisp REPL when developing Emacs code.
M-x eval-buffer should do it.
What Sean said. In addition, I have (eval-defun) bound to a key, along with a test. The development loop then becomes: 1) edit function, 2) press eval-and-test key, 3) observe results, 4) repeat. This is extremely fast.
During development I write a test, bind it to jmc-test, then use the above key to run it on my just-edited function. I edit more, then press key again, testing it again. When the function works, I zap jmc-test, edit another function, and write another jmc-test function. They're nearly always one line of code, so easy to just bang out.
(defun jmc-eval-and-test ()
(interactive)
(eval-defun nil)
(jmc-test))
(define-key emacs-lisp-mode-map (kbd "<kp-enter>") 'jmc-eval-and-test)
(when t
(defun myfunc (beer yum)
(+ beer yum))
(defun jmc-test () (message "out: %s" (myfunc 1 2))))
When editing "myfunc", if I hit keypad enter, it prints "out: 3".
It all depends on what you're writing and how you've written it. Toggling the mode should get you the new behavior. If you're using [define-minor-mode][1], you can add code in the body of the macro that keys off the mode variable:
(define-minor-mode my-minor-mode
"doc string"
nil
""
nil
(if my-minor-mode
(progn
;; do something when minor mode is on
)
;; do something when minor mode is off
)
But, another way to check it quickly would be to spawn a new Emacs from your existing one:
M-x shell-command emacs&
I just define a function called ldf (short for load-file) in my .emacs file,
like this:
(defun ldf (arg) (interactive "P") (load-file (buffer-file-name)))
As you can see, this little function looks up the filename of the current buffer and then loads the file. Whenever I need to reload the current buffer elisp file, just type "M-x ldf"

Emacs key binding fallback

I have a minor mode. If that mode is active and the user hits DEL, I
want to do some action, but only if some condition holds. If the
condition holds and the action is executed I want to do nothing more
after that. But if the condition fails, I don't want to do anything
and let the default DEL action execute.
Not sure how I could solve this. But I guess I could do it in two ways:
1)
I could rebind the DEL key to a function in the minor mode and then
check if the conditions holds ot not. But then how do I know what the
default command to DEL is?
2)
I could add a pre command hook like this. Execute the command and then
break the chain. But how do I break the chain?
(add-hook 'pre-command-hook
(lambda()
(when (equal last-input-event 'backspace)
;; Do something and then stop (do not execute the
;; command that backspace is bound to)
)))
In what way would you solve it? Thanks!
The way to do this is to temporarily disable your minor mode, then look up the key binding.
Pretend that you've bound 'do-thingy to DEL. Then this would do the trick (assuming the condition you want to trigger off is (equal last-input-event 'backspace):
(defun do-thingy ()
"Do something, unless last event was backspace."
(interactive)
(if (equal last-input-event 'backspace)
(let* ((my-minor-mode nil)
(original-func (key-binding (kbd "DEL"))))
;; original-func is whatever DEL would be if
;; my-minor-mode were disabled
(call-interactively original-func))
(message "Here's my minor mode behavior!")))
Note: This behavior assumes you have set up your key bindings the standard way a minor-mode would. Specifically, you should add your keymap to the variable minor-mode-map-alist by adding an element (my-minor-mode . my-minor-mode-keymap). That's how the above let statement works, it looks up the binding you want with your mode temporarily disabled.
If you use define-minor-mode to define your minor mode, the keymap gets set up the "right way" automatically.
This is what I use for my smart-tab package which does exactly that.
(defun smart-tab-default ()
"Indents region if mark is active, or current line otherwise."
(interactive)
(if mark-active
(indent-region (region-beginning)
(region-end))
(call-interactively
(or
;; Minor mode maps for tab (without smart-tab-mode)
(cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding "\t")))
(cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding [(tab)])))
(local-key-binding "\t")
(local-key-binding [(tab)])
(global-key-binding "\t")
(global-key-binding [(tab)])))))
And in the command smart-tab (which is the one bound to tab in the minor
mode), it has the following:
(if (smart-tab-must-expand prefix)
;; use smart tab
(smart-tab-default))
It first checks if there are any minor mode bindings for tab (not including
smart-tab-mode), then local, and finally global keybindings.
There doesn't seem to be a way to do what you want reliably. If your new command is bound to DEL, then whatever was bound to DEL before in the current keymap isn't there anymore. The other approach you proposed won't work because pre-command-hooks don't prevent the following action from taking place. You might also think to interrupt further execution with ^G (Keyboard-Quit), but that's an uncontrolled interrupt that might stop more things than you want.
Even if you make the process of setting up the new binding a little more sophisticated than just re-bind, and remember what was bound there before, so you can call it afterwards, you don't really have what your looking for. If someone wants to rebind the "default" action, they have to do it by modifying to your function rather than replacing the key binding.
What you want to do doesn't fit Emacs' model of how key binding works.

viper-auto-indent breaks inferior modes

As a vim convert, I've gotten fairly used to viper mode. One issue that I've discovered, however, is that viper-auto-indent breaks all inferior modes. What happens is when I enter any sort of inferior mode (sql-mode, ess-mode, etc.) and hit Enter, the Enter key doesn't actually send the command off to the inferior process and gives the appearance of the process just hanging.
Without setting viper-auto-indent I have the problem that the Enter key doesn't automatically indent when writing code, meaning that I need to always hit tab after entering a new line, which is annoying. The workaround I've been using is to have viper-auto-indent enabled by default (since I spend most of my time programming), and then disabling it when I enter an inferior-mode buffer.
Does anyone know how to fix this problem? Alternatively, can anyone help supply me with the elisp to disable viper-auto-indent when switching to an interior mode buffer, and enabling it when in a non-inferior mode buffer? Thanks.
I think Emacs' intent is to have you use "C-j" for newline-and-indent, and let Enter be left alone.
If that is not yet acceptable to you, then this untested code may work:
(add-hook 'inferior-ess-mode-hook
'(lambda () (set (make-local-variable 'viper-auto-indent) nil))
I'm not able to reproduce your problem. I tried every level of viper-mode (1-5), and a number of inferior processes. That said, from your actual question, this code appears like it should fit the bill. If/when 'viper-autoindent is called, if the current buffer has a process, it calls the original binding for the keys just pressed. If there's no process, the original viper-autoindent is called.
(defadvice viper-autoindent (around viper-autoindent-but-not-when-buffer-has-process activate)
"work around reported user problem"
(if (and (this-command-keys)
(get-buffer-process (current-buffer)))
(let* ((viper-mode nil)
(thiskey (key-binding (this-command-keys))))
(when thiskey
(call-interactively thiskey)))
ad-do-it))