1 ;;; majmodpri.el --- Major mode priorities handling
3 ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
5 (defconst majmodpri:version "0.62") ;;Version:
6 ;; Last-Updated: 2009-04-30 Thu
11 ;; Features that might be required by this library:
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19 ;; Different elisp libraries may try to handle the same type of files.
20 ;; They normally do that by entering their major mode for a file type
21 ;; in `auto-mode-alist' or the other lists affecting `normal-mode'.
22 ;; Since the libraries may be loaded in different orders in different
23 ;; Emacs sessions this can lead to rather stochastic choices of major
26 ;; This library tries to give the control of which major modes will be
27 ;; used back to the user. It does that by letting the user set up
28 ;; priorities among the major modes. This priorities are used to sort
29 ;; the lists used by `normal-mode'.
31 ;; To setup this libray and get more information do
33 ;; M-x customize-group RET majmodpri RET
35 ;; Or, see the commands `majmodpri-sort-lists'.
37 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
42 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44 ;; This program is free software; you can redistribute it and/or
45 ;; modify it under the terms of the GNU General Public License as
46 ;; published by the Free Software Foundation; either version 2, or
47 ;; (at your option) any later version.
49 ;; This program is distributed in the hope that it will be useful,
50 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
51 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
52 ;; General Public License for more details.
54 ;; You should have received a copy of the GNU General Public License
55 ;; along with this program; see the file COPYING. If not, write to
56 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
57 ;; Floor, Boston, MA 02110-1301, USA.
59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
63 (eval-when-compile (require 'mumamo nil t))
64 (eval-when-compile (require 'ourcomments-indirect-fun nil t))
68 (defvar majmodpri-idle-sort-timer nil)
70 (defun majmodpri-cancel-idle-sort ()
71 "Cancel idle sorting request."
72 (when majmodpri-idle-sort-timer
73 (cancel-timer majmodpri-idle-sort-timer)
74 (setq majmodpri-idle-sort-timer nil)))
76 (defun majmodpri-start-idle-sort ()
77 "Request idle sorting."
78 (majmodpri-cancel-idle-sort)
79 (setq majmodpri-idle-sort-timer
80 (run-with-idle-timer 0 nil 'majmodpri-sort-lists-in-timer)))
82 (defun majmodpri-sort-lists-in-timer ()
84 (save-match-data ;; runs in timer
85 (majmodpri-sort-lists))
86 (error (message "(majmodpri-sort-lists): %s" err))))
91 (defvar majmodpri-schwarzian-ordnum nil)
92 (defun majmodpri-schwarzian-in (rec)
93 "Transform REC before sorting."
94 (setq majmodpri-schwarzian-ordnum (1+ majmodpri-schwarzian-ordnum))
95 (let ((mode (cdr rec)))
97 (list mode majmodpri-schwarzian-ordnum)
100 (defun majmodpri-schwarzian-out (rec)
101 "Get original value of REC after sorting."
104 ;; Fix-me: default for Emacs 22??
105 (defcustom majmodpri-no-nxml (< emacs-major-version 23)
106 "Don't use multi major modes with nxml if non-nil.
107 The default for Emacs prior to version 23 is to not use this
108 multi major modes by default since there are some problems.
110 This gives those multi major mode lower priority, but it does not
111 prevent use of them."
115 ;; (majmodpri-priority 'html-mumamo-mode)
116 ;; (majmodpri-priority 'nxhtml-mumamo-mode)
117 (defsubst majmodpri-priority (mode)
118 "Return major mode MODE priority."
119 (if (and majmodpri-no-nxml
122 ;; (string-match "nxhtml-mumamo" (symbol-name mode))))
123 (let* ((real (or (ourcomments-indirect-fun mode)
125 (chunk (when real (get real 'mumamo-chunk-family)))
126 (major-mode (when chunk
129 (derived-mode-p 'nxml-mode))))
131 (length (memq mode majmodpri-mode-priorities))))
133 (defun majmodpri-compare-auto-modes (rec1 rec2)
134 "Compare record REC1 and record REC2.
137 - First check `majmodpri-mode-priorities'.
138 - Then use old order in list."
139 (let* ((schw1 (car rec1))
145 (pri1 (majmodpri-priority mod1))
146 (pri2 (majmodpri-priority mod2)))
148 ((/= pri1 pri2) (> pri1 pri2))
151 ;;(benchmark 100 (quote (majmodpri-sort-lists)))
152 ;;(defvar my-auto-mode-alist nil)
153 (defun majmodpri-sort-auto-mode-alist ()
154 "Sort `auto-mode-alist' after users priorities."
155 (setq majmodpri-schwarzian-ordnum 0)
156 ;; Do not reorder function part, but put it first.
159 (dolist (rec auto-mode-alist)
160 (if (listp (cdr rec))
161 (setq fun-list (cons rec fun-list))
162 (setq mod-list (cons rec mod-list))))
163 (setq fun-list (nreverse fun-list))
164 (setq auto-mode-alist
167 (mapcar 'majmodpri-schwarzian-out
169 (mapcar 'majmodpri-schwarzian-in mod-list)
170 'majmodpri-compare-auto-modes))))))
172 (defun majmodpri-sort-magic-list (magic-mode-list-sym)
173 "Sort list MAGIC-MODE-LIST-SYM after users priorities."
174 (let ((orig-ordnum 0))
175 (set magic-mode-list-sym
177 (mapcar (lambda (rec)
182 (mapcar (lambda (rec)
183 (setq orig-ordnum (1+ orig-ordnum))
184 (let ((mode (cdr rec)))
186 (list mode orig-ordnum)
188 (symbol-value magic-mode-list-sym))
190 (let* ((schw1 (car rec1))
196 (pri1 (majmodpri-priority mod1))
197 (pri2 (majmodpri-priority mod2)))
199 ((/= pri1 pri2) (> pri1 pri2))
200 (t (> ord1 ord2))))))))))
203 (defun majmodpri-sort-lists ()
204 "Sort the list used when selecting major mode.
205 Only sort those lists choosen in `majmodpri-lists-to-sort'.
206 Sort according to priorities in `majmodpri-mode-priorities'.
207 Keep the old order in the list otherwise.
209 The lists can be sorted when loading elisp libraries, see
210 `majmodpri-sort-after-load'.
212 See also `majmodpri-apply-priorities'."
214 ;;(message "majmodpri-sort-lists running ...")
215 (majmodpri-cancel-idle-sort)
216 (when (memq 'magic-mode-alist majmodpri-lists-to-sort)
217 (majmodpri-sort-magic-list 'magic-mode-alist))
218 (when (memq 'auto-mode-alist majmodpri-lists-to-sort)
219 (majmodpri-sort-auto-mode-alist))
220 (when (memq 'magic-fallback-mode-alist majmodpri-lists-to-sort)
221 (majmodpri-sort-magic-list 'magic-fallback-mode-alist))
222 ;;(message "majmodpri-sort-lists running ... (done)")
227 (defun majmodpri-apply ()
228 "Sort major mode lists and apply to existing buffers.
229 Note: This function is suitable to add to
230 `desktop-after-read-hook'. It will restore the multi major modes
232 (majmodpri-apply-priorities t))
234 (defun majmodpri-sort-apply-to-current ()
235 "Sort lists and apply to current buffer."
236 (majmodpri-sort-lists)
237 (add-hook 'find-file-hook 'normal-mode t t))
239 (defun majmodpri-check-normal-mode ()
240 "Like `normal-mode', but keep major mode if same."
241 (let ((keep-mode-if-same t)
242 (old-major-mode major-mode)
243 (old-mumamo-multi-major-mode (when (boundp 'mumamo-multi-major-mode)
244 mumamo-multi-major-mode)))
245 (report-errors "File mode specification error: %s"
247 ;;(msgtrc "majmodpri-check %s %s %s" (current-buffer) major-mode mumamo-multi-major-mode)
248 (unless (and (eq old-major-mode major-mode)
249 (or (not old-mumamo-multi-major-mode)
250 (eq old-mumamo-multi-major-mode mumamo-multi-major-mode)))
251 (msgtrc "majmodpri-check changing")
252 (report-errors "File local-variables error: %s"
253 (hack-local-variables))
254 ;; Turn font lock off and on, to make sure it takes account of
255 ;; whatever file local variables are relevant to it.
256 (when (and font-lock-mode
257 ;; Font-lock-mode (now in font-core.el) can be ON when
258 ;; font-lock.el still hasn't been loaded.
259 (boundp 'font-lock-keywords)
260 (eq (car font-lock-keywords) t))
261 (setq font-lock-keywords (cadr font-lock-keywords))
263 (message "majmodpri-apply-priorities: buffer=%s, %s,%s => %s,%s"
266 old-mumamo-multi-major-mode
268 (when (boundp 'mumamo-multi-major-mode)
269 mumamo-multi-major-mode)))))
272 (defun majmodpri-apply-priorities (change-modes)
273 "Apply major mode priorities.
274 First run `majmodpri-sort-lists' and then if CHANGE-MODES is
275 non-nil apply to existing file buffers. If interactive ask
278 (message "majmodpri-apply-priorities running ...")
279 (majmodpri-sort-lists)
280 (when (or change-modes
281 (with-no-warnings (called-interactively-p)))
283 (dolist (buffer (buffer-list))
284 (with-current-buffer buffer
285 (let ((name (buffer-name))
286 (file buffer-file-name))
287 (or (string= (substring name 0 1) " ") ;; Internal
289 (setq file-buffers (cons buffer file-buffers))))))
290 (if (not file-buffers)
292 ;;(message "majmodpri-apply-priorities: No file buffers to change modes in")
294 (when (with-no-warnings (called-interactively-p))
296 (y-or-n-p "Check major mode in all file visiting buffers? ")))
298 (dolist (buffer file-buffers)
299 (with-current-buffer buffer
300 (let ((old-major major-mode))
301 (majmodpri-check-normal-mode)
303 (message "majmodpri-apply-priorities running ... (done)"))
309 (defgroup majmodpri nil
310 "Customization group for majmodpri.el"
314 (defcustom majmodpri-mode-priorities
317 csound-sgml-mumamo-mode
318 django-nxhtml-mumamo-mode
319 django-html-mumamo-mode
320 embperl-nxhtml-mumamo-mode
321 embperl-html-mumamo-mode
322 eruby-nxhtml-mumamo-mode
323 eruby-html-mumamo-mode
324 genshi-nxhtml-mumamo-mode
325 genshi-html-mumamo-mode
326 jsp-nxhtml-mumamo-mode
328 laszlo-nxml-mumamo-mode
330 mjt-nxhtml-mumamo-mode
335 smarty-nxhtml-mumamo-mode
336 smarty-html-mumamo-mode
337 ;;tt-html-mumamo-mode
348 "Priority list for major modes.
349 Modes that comes first have higher priority.
350 See `majmodpri-sort-lists' for more information."
351 :type '(repeat symbol)
352 :set (lambda (sym val)
353 (set-default sym val)
354 (when (and (boundp 'majmodpri-sort-after-load)
355 majmodpri-sort-after-load)
356 (majmodpri-start-idle-sort)))
359 (defcustom majmodpri-lists-to-sort
360 '(magic-mode-alist auto-mode-alist magic-fallback-mode-alist)
362 "Which major mode lists to sort.
363 See `majmodpri-sort-lists' for more information."
364 :type '(set (const magic-mode-alist)
365 (const auto-mode-alist)
366 (const magic-fallback-mode-alist))
367 :set (lambda (sym val)
368 (set-default sym val)
369 (when (and (boundp 'majmodpri-sort-after-load)
370 majmodpri-sort-after-load)
371 (majmodpri-start-idle-sort)))
374 (defcustom majmodpri-sort-after-load
383 "Sort major mode lists after loading elisp libraries if non-nil.
384 This should not really be needed since just loading a library
385 should not change how Emacs behaves. There are however quite a
386 few thirt party libraries that does change `auto-mode-alist'
387 \(including some of my own) since that sometimes seems
388 reasonable. Some of them are in the default value of this
391 There are two possibilities for sorting here:
393 - Value=list of features (default). Sort immediately after loading a
394 library in the list. Apply to current buffer.
396 - Value=t. Sort after loading any library. Sorting is then not
397 done immediately. Instead it runs in an idle timer. This
398 means that if several elisp libraries are loaded in a command
399 then the sorting will only be done once, after the command has
400 finished. After sorting apply to all buffers.
402 Note that the default does break Emacs rule that loading a
403 library should not change how Emacs behave. On the other hand
404 the default tries to compensate for that the loaded libraries
405 breaks this rule by changing `auto-mode-alist'.
407 See `majmodpri-sort-lists' for more information."
408 :type '(choice (const :tag "Never" nil)
409 (const :tag "After loading any elisp library" t)
410 (repeat :tag "After loading specified features" symbol))
411 :set (lambda (sym val)
412 (set-default sym val)
413 ;; Clean up `after-load-alist' first.
414 (setq after-load-alist
416 (mapcar (lambda (rec)
417 (unless (member (cadr rec)
418 '((majmodpri-start-idle-sort)
419 (majmodpri-sort-lists)))
423 ;;(message "majmodpri-sort-after-load: val=%s" val)
424 (let ((sort-and-apply nil))
425 (if (not (listp val))
426 (add-to-list 'after-load-alist
428 '(".*" (majmodpri-start-idle-sort))
429 '("." (majmodpri-sort-lists))))
431 ;;(message "feat=%s" feat)
433 (setq sort-and-apply t)
435 (eval-after-load feat '(majmodpri-start-idle-sort))
436 (eval-after-load feat '(majmodpri-sort-apply-to-current))))))
438 ;;(message "majmodpri-sort-after-load: sort-and-apply")
439 (majmodpri-apply-priorities t))
441 (majmodpri-start-idle-sort)
442 (majmodpri-apply-priorities t)))))
447 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
448 ;;; majmodpri.el ends here