There's a couple of emacs features, such as flyspell-mode
,
highlight-beyond-fill-column
, or auto-fill-mode
, I find so useful that I
want them enabled almost all the time. However, there's always certain
conditions in which they don't make much sense.
highlight-beyond-fill-column
, for example, I tend to want for pretty much
everything I edit myself, but for reading things others wrote, like in Gnus or
when reading the built-in documentation, it's actually quite annoying.
Similarly auto-fill-mode
is incredibly handy when writing just Text. However,
it's entirely unhelpful when programming.
For those reasons I can't just enable features like that globally. Always enabling them manually isn't very practical as well, but so is having to write hooks for each and every mode or application I'm using within emacs, obviously not being able to cover all of them, and still ending up enabling those features manually.
What I believe I'm looking for is a way to globally enable some feat开发者_Go百科ures, but selectively turn them off again, based on various conditions such as which major or minor modes are being used, if the buffer is read-only or writable, or depending on the buffer containing text or source code. I do realize that at least the last thing might not be easy for emacs to answer, but at least for that I believe I'd be fine with a hard-coded list of "programming-modes" i use regularly.
So you want total control over what's executed when a particular mode is opened or a particular type of file... OK here is what you need :
;; The function where you could put all your customization
(defun my-func ()
(turn-on-auto-fill))
;; This is an example, customize it like you need it.
(defvar functions-to-call
`(((c-mode c++-mode) ".h$" (my-func))
((cperl-mode perl-mode) nil (my-func)))
"A list of triples, used for storing functions.
A triplet is composed of a symbol for the major mode (or a list of symbols),
a regular expression to match against the buffer's file name,
and the functions to call when both the major mode and regular expr match.")
(defun call-mode-functions ()
"call functions, based on major mode and buffer name regexp matching"
(interactive)
(let ((l functions-to-call))
(while l
(let* ((elt (car l))
(modes (if (listp (car elt)) (car elt) (list (car elt))))
(re (cadr elt))
(fcts (caddr elt)))
(when (and (member major-mode modes)
(or (null re)
(string-match re (buffer-file-name))))
(while fcts
(funcall (car fcts))
(setq fcts (cdr fcts)))
(setq l nil)))
(setq l (cdr l)))))
(add-hook 'after-change-major-mode-hook 'call-mode-functions)
With this code, you can can do the fine-grained customization you require. This is just an example, you can adapt it to your needs.
Interesting idea. I recommend using the espect extension from your github.
It sounds like you basically want to turn specific minor-modes on or off for "specific buffers". Usually, the "specific buffers" can be distinguished by their major mode, which is how I usually look at this type of problem. How to turn minor modes on or off depends on the implementation of both the minor mode you're trying to turn on/off and the major mode you're trying to turn it on/off in.
The usual way to enable/disable things based on major-mode is via the major-mode-hook
variable. This is where you stick things to customize the mode:
(add-hook 'text-mode-hook 'auto-fill-mode)
I usually write my own function, even if it's a simple one-liner, because I almost always will add stuff later on:
(defun my-text-mode-hook ()
"Stuff to do when `text-mode' is invoked."
(auto-fill-mode 1))
(add-hook 'text-mode-hook 'my-text-mode-hook)
You can also make things within the hook conditional:
(defun my-text-mode-hook ()
"Stuff to do when `text-mode' is invoked."
;; skip modes based on text-mode
(when (eq major-mode 'text-mode)
(auto-fill-mode 1))
)
(add-hook 'text-mode-hook 'my-text-mode-hook)
I usually do all of this in a major-mode-load-hook
, so that it only happens when the major-mode's code is loaded:
(defun my-tnt-load-hook ()
(defun my-tnt-im-mode-hook ()
"Hook for TNT's im-mode hook."
(flyspell-mode 1)
(setq fill-column (- (frame-width) 5)))
(add-hook 'tnt-im-mode-hook 'my-tnt-im-mode-hook)
(add-hook 'tnt-chat-mode-hook 'my-tnt-im-mode-hook))
(add-hook 'tnt-load-hook 'my-tnt-load-hook)
A well-written major-mode will have a load-hook
variable defined (I usually look at the mode's source code to find out). If it doesn't have a load-hook
, you can simulate one with the eval-after-load
function:
(defun my-view-mode-after-load-hook ()
"Stuff to do after view mode loads."
(defun my-view-mode-hook ()
"Stuff to run in `view-mode'."
(flyspell-mode 0))
(add-hook 'view-mode-hook 'my-view-mode-hook)
(define-key view-mode-map "b" 'View-scroll-page-backward)
(define-key view-mode-map [(delete)] 'View-scroll-page-backward)
(define-key view-mode-map "q" 'View-kill-and-leave)
(define-key view-mode-map "Q" 'View-quit))
(eval-after-load 'view '(my-view-mode-after-load-hook))
If you don't do this in a load-hook
then you have to make sure the mode-hook
is customizable, and then add in your my-mode-hook
via customize; I'd rather have all of the stuff in one place in my .emacs, so I don't usually customize my hooks this way.
If you ever find a major-mode that does not have a major-mode-hook
you can create your own major-mode based off of it using define-derived-mode
. You'll then have to get the newly defined mode invoked whenever the old mode was.
(defun replace-alist-mode (alist oldmode newmode)
(dolist (aitem alist)
(if (eq (cdr aitem) oldmode)
(setcdr aitem newmode))))
(define-derived-mode hooked-foobar-mode foobar-mode "Foobar")
(replace-alist-mode auto-mode-alist 'foobar-mode 'hooked-foobar-mode)
(defun my-hooked-foobar-mode-hook ()
"Hook to run when `hooked-foobar-mode' is called."
(flyspell-mode 0))
(add-hook 'hooked-foobar-mode-hook 'my-hooked-foobar-mode-hook)
Some minor modes can be enabled globally. If you want them on most of the time and it supports it, you can turn it on globally and then turn it off for specific major modes.
(global-font-lock-mode 1)
;; example of how to do it without a defun
(add-hook 'text-mode-hook (function
(lambda () ""
(interactive)
(font-lock-mode 0))))
If the minor mode can't be enabled globally, or you don't want it enabled globally, just turn it on for specific modes, as shown above.
So here's what I came up with after reading [Jérôme Radix][1]'s excellent
reply. Especially the pointer to after-change-major-mode-hook
has helped a
lot.
I now define my buffer-specific settings in a list like this:
;; no `highlight-beyond-fill-column' for w3m and gnus
'((((:not ((:mode "^gnus") (:mode w3m-mode))))
(lambda () (highlight-beyond-fill-column)))
;; `flyspell-mode` and `auto-fill-mode` for text-ish buffers
(((:mode message-mode)
(:mode org-mode)
(:mode pod-mode)
(:mode markdown-mode)
(:name "\\.\\(txt\\|mkn\\)$"))
(lambda ()
(flyspell-mode)
(auto-fill-mode)))
;; indenting with tabs for certain projects
(((:name t :fun (lambda () (and (not eproject-root)
(eproject-maybe-turn-on)))))
(lambda () (setq indent-tabs-mode t)))
When the major mode changes, I then iterate over all those settings, evaluate the defined conditions in the buffer, and call the appropriate lambda if a condition matches:
(add-hook 'after-change-major-mode-hook
(lambda () (rafl:apply-buffer-settings rafl:buffer-settings)))
(defun rafl:apply-buffer-settings (settings)
(dolist (setting rafl:buffer-settings)
(let ((condition (car setting))
(action (cadr setting)))
(when (rafl:evaluate-buffer-condition condition)
(funcall action)))))
Evaluating those conditions is a little messy, but works rather well for me.
(defun rafl:evaluate-buffer-condition (con)
(cond
((functionp con)
(funcall con))
((listp con)
(cond
((listp (car con))
(reduce
(lambda (a b) (or a b))
(cons nil (mapcar #'rafl:evaluate-buffer-condition con))))
(t
(reduce
(lambda (a b) (and a b))
(cons
t
(let (ret)
(while con
(let ((k (pop con))
(v (pop con)))
(push (cond
((eq k :fun)
(funcall v))
((eq k :not)
(when (not (listp v))
(error ":not requires a list"))
(not (rafl:evaluate-buffer-condition v)))
((eq k :mode)
(if (stringp v)
(string-match-p v (symbol-name major-mode))
(eq v major-mode)))
((eq k :name)
(cond
((and (buffer-file-name) (stringp v))
(string-match-p v (buffer-file-name)))
((buffer-file-name)
v)
(t
(not v))))
(t
(error "unknown cond")))
ret)))
ret))))))
(t
(error "invalid condition"))))
It also turns out that I could do all my per-project setting, which I did quite differently before, using this mechanism. I'm very happy about that.
1: Enabling certain emacs modes or features *almost* always
I do this
(require 'linum)
;(global-linum-mode t)
(add-hook 'find-file-hook (lambda ()
(if (not(equal major-mode 'term-mode))
(linum-mode nil))))
精彩评论