Adding comment-end character to Emacs-Lisp - emacs

Would it be possible to add a comment-end character to emacs?
I'll take the first code I have and apply what I would like as example:
(defun smart-tab ()
(interactive)
\1\ (if (minibufferp)
\1a\ (minibuffer-complete)
\2\ (if (eq major-mode 'emacs-lisp-mode)
(progn
(save-excursion
(search-backward "(def")
(while (not (looking-at "\\s-*)"))
(beginning-of-line 1)
(indent-for-tab-command)
(beginning-of-line 1)
(next-line)
(when (looking-at (concat ".*" comment-start))
(next-line))))
(indent-for-tab-command))
(yas-expand)))
)
I would like to add some information in the indentation area before the functions, indicating where the logical parts start.
Would this be possible for emacs-lisp, would there be an easy way to use some little trick to consider the evaluater to skip certain text?

Emacs Lisp doesn't have reader macros (or any other way of modifying the reader). But you can do something close to what you want by writing your own macro and using it instead of defun. For example, with this macro definition:
(defmacro mydefun (name args &rest body)
"Define NAME as a function.
Like normal `defun', except BODY may contain |comments|."
(labels ((uncomment (form)
(cond ((not (consp form)) form)
((and (symbolp (car form))
(string-match "|.*|$" (symbol-name (car form))))
(uncomment (cdr form)))
(t (cons (uncomment (car form))
(uncomment (cdr form)))))))
`(defun ,name ,args ,#(uncomment body))))
you can write:
(mydefun smart-tab ()
(interactive)
|1| (if (minibufferp)
|1a| (minibuffer-complete)
|2| (if (eq major-mode 'emacs-lisp-mode)
(progn
(indent-for-tab-command)))))
(It's not possible to use \ for this because that character already has a meaning for the Emacs Lisp reader.)
I have to say, though, that this doesn't seem like a particularly good idea to me. It would be much better to put your section headings in comments to the right of the source:
(defun smart-tab ()
(interactive)
(if (minibufferp) ; 1
(minibuffer-complete) ; 1a
(if (eq major-mode 'emacs-lisp-mode) ; 2
(progn
(indent-for-tab-command)))))
This seems just as clear as your proposal, and much easier for other Emacs Lisp programmers to understand.

Related

How to change the behaviour of org-agenda-goto to open org-file in a new frame?

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)))

internal link searching via org-open-at-point not working for me

[[search for me]]
doesn't end up finding the text 'search for me' in the same org file (internal link), instead, it always says 'no match' and asks if I'd like to create a new headline. If the text 'search for me' is instead enclosed in double brackets, e.g.,
<<search for me>>
then the link above works. It shouldn't need double brackets to work. Perhaps I have something configured to interfere with the normal behavior? Not sure how to debug this. Any ideas what is happening? Does it work for you?
I created a few versions that work for me. Hopefully it will help someone else:
(defun my-org-search-link-regexp ()
"Search an org link by text in the same file."
(interactive)
(if (org-in-regexp org-bracket-link-regexp 1)
(let ((link-text (if (match-end 1)
(org-match-string-no-properties 1)
nil)))
(goto-char (point-min))
(re-search-forward link-text nil t))))
or
(defun my-org-search-link-regexp ()
(interactive)
(when (equal major-mode 'org-mode)
(let ((object (org-element-context)))
(when (eq (car object) 'link)
(let ((link (org-element-property :raw-link object)))
(goto-char (point-min))
(re-search-forward link nil t))))))
or
(defun my-org-search-link-regexp ()
"If link under point is org link and of link type `regexp:',
e.g., regexp:search for this text, then search for the regexp supplied,
otherwise invoke org-open-at-point."
(interactive)
(when (equal major-mode 'org-mode)
(let ((link-type "regexp:")
(command-prefix-value)
(object (org-element-context)))
(when (eq (car object) 'link)
(let ((link (org-element-property :raw-link object)))
(when (>= (length link) (length link-type))
(setf command-prefix-value (substring link 0 (length link-type))))
(if (and command-prefix-value
(equal link-type command-prefix-value))
(let ((link-regexp (substring link (1+ (length link-type)) nil)))
(org-mark-ring-push)
(goto-char (point-min))
(re-search-forward link-regexp nil t))
(call-interactively 'org-open-at-point)))))))
(bind-key "C-c C-o" #'my-org-search-link-regexp org-mode-map)
My preference is the third one as it, combined with the keybinding, allows me to use my regular keybinding for org-open-at-point. There is probably a better way of doing this using an actual org link type datatype of something, but it works nicely for me. I don't want double brackets all over the place in my org files. I call org-mark-ring-push so that I can later call org-mark-ring-goto which I have mapped to evil-leader m B. This way I can reliably jump back to the originating link.

Could Emacs fontify elisp string constants?

The Dilemma: readability or maintainability?
Let's look at the following function.
It doesn't really matter what it does, the important part is that
it's using twice the string "(let\\*?[ \t]*":
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back "(let\\*?[ \t]*")
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back "(let\\*?[ \t]*"))
(cons 'setq sexp))
(t
sexp))))))
Since it's a headache having to update the string in two (or more) locations,
I'd have to defconst it like so:
(defconst regex-let-form "(let\\*?[ \t]*")
Although the code became more maintainable, it became less readable as well,
because it's hard to see at a glance what regex-let-form really is:
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back regex-let-form)
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back regex-let-form))
(cons 'setq sexp))
(t
sexp))))))
The idea: why not both?
Since it's a constant anyway, why not font-lock it
and make regex-let-form appear as if it's "(let\\*?[ \t]*"?
It's a feasable job, since:
It's possible to font-lock identifiers like so: http://www.emacswiki.org/emacs/PrettyLambda,
or even so: rainbow-mode.
And it's possible to font-lock constants. It's already done for c++-mode,
but not yet for emacs-lisp-mode, as far as I know.
Then it remains only to connect the two. Unfortunately, I don't know
enough of font-lock innards to do it, but maybe someone else does?
Or is there already a package that does this?
Tweaking the code from this answer,
I've solved the problem:
(font-lock-add-keywords
'emacs-lisp-mode
'((fl-string-constant . 'font-lock-constant-face)) 'append)
(defun fl-string-constant (_limit)
(while (not
(ignore-errors
(save-excursion
(skip-chars-forward "'")
(let ((opoint (point))
(obj (read (current-buffer)))
obj-val)
(and (symbolp obj)
(risky-local-variable-p obj)
(special-variable-p obj)
(stringp (setq obj-val (eval obj)))
(progn
(put-text-property
(1- (point)) (point) 'display
(format "%c\"%s\"" (char-before) obj-val))
(set-match-data (list opoint (point)))
t))))))
(if (looking-at "\\(\\sw\\|\\s_\\)")
(forward-sexp 1)
(forward-char 1)))
t)
This displays the value of a string constant right after the constant name.
It works quite nicely with fontified string constants as well.
Speed is a bit of an issue - suggestions to improve are welcome.
Also, I couldn't find anything better than risky-local-variable-p to determine
that it's a constant. The doc says that defconst marks the variable
as special and risky, but nothing else.
hl-defined.el (updated today, 2013-10-20) can highlight constant Emacs-Lisp symbols as such, that is, variables whose current value is the symbol itself. If your defconst has been evaluated then this will do what you are requesting.
This seems to work (source: http://www.emacswiki.org/emacs/PrettyLambda):
(font-lock-add-keywords 'emacs-lisp-mode
`(("\\<\\(regex-let-form\\)\\>" (0 (prog1 nil
(compose-region (match-beginning 1)
(match-end 1)
"\"(let\\\\*?[ \\t]*\""))))))
Although I think adding regex-let-form into the existing let block would be a cleaner solution:
(let ((sexp (preceding-sexp))
(regex-let-form "(let\\*?[ \t]*"))
...
Perhaps your example is not indicative of the real problem, and you really do want to do some display replacement or font-locking, as you say.
But I will answer wrt your example and the problem as posed, regarding maintainability vs readability: Just let-bind your regexp. The binding, unlike a defconst will be nearby and clearly related to the occurrences of the bound variable.
This is typically what people do. Again, you might have had another use case in mind --- I am responding only to the problem as posed narrowly.

in org-mode, how to fold/hide footnotes?

In Emacs org-mode, is there a way to get inline footnote definitions to appear as collapsed?
So that for instance, a line like this:
This effect is due to the strength of weak ties[fn:: Newman, Mark, Albert-László Barabási, and Duncan J. Watts. 2006. The Structure and Dynamics of Networks. Princeton, NJ: Princeton University Press].
might simply appear like this:
This effect is due to the strength of weak ties[✭].
I would also need a command to show the footnotes when necessary. So maybe what is needed are two commands: org-hide-footnotes and org-show-footnotes.
I don't believe this is possible at the moment. Also using TAB as the expansion key within for it would likely cause even more overloading of the key.
On the other hand, is there any particular reason not to use a footnote section for the footnotes?
C-c C-x f will create/interact with any footnotes you have.
(org-footnote-action &optional SPECIAL)
Do the right thing for footnotes.
When at a footnote reference, jump to the definition.
When at a definition, jump to the references if they exist, offer to
create them otherwise.
When neither at definition or reference, create a new footnote,
interactively.
With prefix arg SPECIAL, offer additional commands in a menu.
The additional commands being:
s Sort the footnote definitions by reference sequence. During editing,
Org makes no effort to sort footnote definitions into a particular
sequence. If you want them sorted, use this command, which will
also move entries according to org-footnote-section. Automatic
sorting after each insertion/deletion can be configured using the
variable org-footnote-auto-adjust.
r Renumber the simple fn:N footnotes. Automatic renumbering
after each insertion/deletion can be configured using the variable
org-footnote-auto-adjust.
S Short for first r, then s action.
n Normalize the footnotes by collecting all definitions (including
inline definitions) into a special section, and then numbering them
in sequence. The references will then also be numbers. This is
meant to be the final step before finishing a document (e.g., sending
off an email). The exporters do this automatically, and so could
something like message-send-hook.
d Delete the footnote at point, and all definitions of and references
to it.
INITIAL (February 6, 2014):   First working draft.
EDIT February 18, 2014:  Revised the function lawlist-toggle-block-visibility so that it contains a proper if/then/else statement -- i.e., if the line contains the requisite beginning region regexp, then the block visibility will be toggled, else a message saying sorry . . . . Added a citation to a related thread for code folding. Revised the error message to refer to a point rather than a line.
The source code for the answer below is also stored on Github: https://github.com/lawlist/lawlist-org-block-toggle/blob/master/lawlist-org-block-toggle.el
On a related issue (i.e., to completely hide the properties drawer including the word :PROPERTIES:), please refer to the following thread: Completely hide the :PROPERTIES: drawer in org-mode
On a semi-related issue (i.e., to create a custom block to be code-folded), see also: https://tex.stackexchange.com/a/161196/26911
This solution was tested with a fairly recent version of Emacs Trunk (built on January 19, 2014), which contains org-mode version 8.2.5c. Inasmuch as the :PROPERTIES: drawer through its :END: gets folded separately from the footnotes and the html blocks, this solution contemplates that the footnote and/or html code blocks will not be anywhere inside that properties drawer. The footnote can appear anywhere in the text paragraph, but cannot have another pair of square brackets inside the footnote -- since this code looks for the first ending square bracket in order to mark the end of the folded region. This code contemplates that #+BEGIN_HTML and #+END_HTML will both be flush-left with the left-hand margin. The startup views still work the same way -- e.g., the variables org-startup-folded and org-hide-block-startup.
The modification of org-cycle-internal-local enables tab cycling for all forms of folding in org-mode. All we did was fix the pagination of the function to make it more readable, and added the following condition: ((eq org-cycle-subtree-status 'subtree) (org-show-subtree) (message "ALL") (setq org-cycle-subtree-status 'all)). The interactive function org-cycle is still used to tab cycle between all of the various folded / unfolded views. The function lawlist-block-org-cycle-internal-local is a non-interactive supporting function that is used by org-cycle. The two defalias portions of the code in this solution are needed for everything to work properly. Rather than using tab cycling, the user can also call the interactive function directly at the beginning of the headings or subheadings with: M-x org-cycle RET
To toggle visibility of the footnote or html block directly, we use the interactive function lawlist-toggle-block-visibility. Place the cursor anywhere on the line containing the beginning of the footnote or, the beginning of the html block, and type: M-x lawlist-toggle-block-visibility RET
(require 'org)
(defalias 'org-cycle-hide-drawers 'lawlist-block-org-cycle-hide-drawers)
(defun lawlist-block-org-cycle-hide-drawers (state)
"Re-hide all drawers, footnotes or html blocks after a visibility state change."
(when
(and
(derived-mode-p 'org-mode)
(not (memq state '(overview folded contents))))
(save-excursion
(let* (
(globalp (memq state '(contents all)))
(beg (if globalp (point-min) (point)))
(end
(cond
(globalp
(point-max))
((eq state 'children)
(save-excursion (outline-next-heading) (point)))
(t (org-end-of-subtree t)) )))
(goto-char beg)
(while
(re-search-forward
".*\\[fn\\|^\\#\\+BEGIN_HTML.*$\\|^[ \t]*:PROPERTIES:[ \t]*$" end t)
(lawlist-org-flag t))))))
(defalias 'org-cycle-internal-local 'lawlist-block-org-cycle-internal-local)
(defun lawlist-block-org-cycle-internal-local ()
"Do the local cycling action."
(let ((goal-column 0) eoh eol eos has-children children-skipped struct)
(save-excursion
(if (org-at-item-p)
(progn
(beginning-of-line)
(setq struct (org-list-struct))
(setq eoh (point-at-eol))
(setq eos (org-list-get-item-end-before-blank (point) struct))
(setq has-children (org-list-has-child-p (point) struct)))
(org-back-to-heading)
(setq eoh (save-excursion (outline-end-of-heading) (point)))
(setq eos (save-excursion (1- (org-end-of-subtree t t))))
(setq has-children
(or
(save-excursion
(let ((level (funcall outline-level)))
(outline-next-heading)
(and
(org-at-heading-p t)
(> (funcall outline-level) level))))
(save-excursion
(org-list-search-forward (org-item-beginning-re) eos t)))))
(beginning-of-line 2)
(if (featurep 'xemacs)
(while
(and
(not (eobp))
(get-char-property (1- (point)) 'invisible))
(beginning-of-line 2))
(while
(and
(not (eobp))
(get-char-property (1- (point)) 'invisible))
(goto-char (next-single-char-property-change (point) 'invisible))
(and
(eolp)
(beginning-of-line 2))))
(setq eol (point)))
(cond
((= eos eoh)
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-pre-cycle-hook 'empty))
(org-unlogged-message "EMPTY ENTRY")
(setq org-cycle-subtree-status nil)
(save-excursion
(goto-char eos)
(outline-next-heading)
(if (outline-invisible-p)
(org-flag-heading nil))))
((and
(or
(>= eol eos)
(not (string-match "\\S-" (buffer-substring eol eos))))
(or
has-children
(not (setq children-skipped
org-cycle-skip-children-state-if-no-children))))
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-pre-cycle-hook 'children))
(if (org-at-item-p)
;; then
(org-list-set-item-visibility (point-at-bol) struct 'children)
;; else
(org-show-entry)
(org-with-limited-levels (show-children))
(when (eq org-cycle-include-plain-lists 'integrate)
(save-excursion
(org-back-to-heading)
(while (org-list-search-forward (org-item-beginning-re) eos t)
(beginning-of-line 1)
(let* (
(struct (org-list-struct))
(prevs (org-list-prevs-alist struct))
(end (org-list-get-bottom-point struct)))
(mapc (lambda (e) (org-list-set-item-visibility e struct 'folded))
(org-list-get-all-items (point) struct prevs))
(goto-char (if (< end eos) end eos)))))))
(org-unlogged-message "CHILDREN")
(save-excursion
(goto-char eos)
(outline-next-heading)
(if (outline-invisible-p)
(org-flag-heading nil)))
(setq org-cycle-subtree-status 'children)
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-cycle-hook 'children)))
((or
children-skipped
(and
(eq last-command this-command)
(eq org-cycle-subtree-status 'children)))
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-pre-cycle-hook 'subtree))
(outline-flag-region eoh eos nil)
(org-unlogged-message
(if children-skipped
"SUBTREE (NO CHILDREN)"
"SUBTREE"))
(setq org-cycle-subtree-status 'subtree)
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-cycle-hook 'subtree)))
((eq org-cycle-subtree-status 'subtree)
(org-show-subtree)
(message "ALL")
(setq org-cycle-subtree-status 'all))
(t
(run-hook-with-args 'org-pre-cycle-hook 'folded)
(outline-flag-region eoh eos t)
(org-unlogged-message "FOLDED")
(setq org-cycle-subtree-status 'folded)
(unless (org-before-first-heading-p)
(run-hook-with-args 'org-cycle-hook 'folded))))))
(defun lawlist-org-flag (flag)
"When FLAG is non-nil, hide any of the following: html code block;
footnote; or, the properties drawer. Otherwise make it visible."
(save-excursion
(beginning-of-line 1)
(cond
((looking-at ".*\\[fn")
(let* (
(begin (match-end 0))
end-footnote)
(if (re-search-forward "\\]"
(save-excursion (outline-next-heading) (point)) t)
(progn
(setq end-footnote (point))
(outline-flag-region begin end-footnote flag))
(user-error "Error beginning at point %s." begin))))
((looking-at "^\\#\\+BEGIN_HTML.*$\\|^[ \t]*:PROPERTIES:[ \t]*$")
(let* ((begin (match-end 0)))
(if (re-search-forward "^\\#\\+END_HTML.*$\\|^[ \t]*:END:"
(save-excursion (outline-next-heading) (point)) t)
(outline-flag-region begin (point-at-eol) flag)
(user-error "Error beginning at point %s." begin)))))))
(defun lawlist-toggle-block-visibility ()
"For this function to work, the cursor must be on the same line as the regexp."
(interactive)
(if
(save-excursion
(beginning-of-line 1)
(looking-at
".*\\[fn\\|^\\#\\+BEGIN_HTML.*$\\|^[ \t]*:PROPERTIES:[ \t]*$"))
(lawlist-org-flag (not (get-char-property (match-end 0) 'invisible)))
(message "Sorry, you are not on a line containing the beginning regexp.")))

Enabling certain emacs modes or features *almost* always

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 features, 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))))