]> git.rkrishnan.org Git - .emacs.d.git/blob - emacs/nxhtml/util/majmodpri.el
7bdbea6dd5a4380df664745e8c24c6df118f7062
[.emacs.d.git] / emacs / nxhtml / util / majmodpri.el
1 ;;; majmodpri.el --- Major mode priorities handling
2 ;;
3 ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
4 ;; Created: 2008-08-26
5 (defconst majmodpri:version "0.62") ;;Version:
6 ;; Last-Updated: 2009-04-30 Thu
7 ;; URL:
8 ;; Keywords:
9 ;; Compatibility:
10 ;;
11 ;; Features that might be required by this library:
12 ;;
13 ;;   None
14 ;;
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16 ;;
17 ;;; Commentary:
18 ;;
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
24 ;; mode.
25 ;;
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'.
30 ;;
31 ;; To setup this libray and get more information do
32 ;;
33 ;;   M-x customize-group RET majmodpri RET
34 ;;
35 ;; Or, see the commands `majmodpri-sort-lists'.
36 ;;
37 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
38 ;;
39 ;;; Change log:
40 ;;
41 ;;
42 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
43 ;;
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.
48 ;;
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.
53 ;;
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.
58 ;;
59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60 ;;
61 ;;; Code:
62
63 (eval-when-compile (require 'mumamo nil t))
64 (eval-when-compile (require 'ourcomments-indirect-fun nil t))
65
66 ;;;; Idle sorting
67
68 (defvar majmodpri-idle-sort-timer nil)
69
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)))
75
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)))
81
82 (defun majmodpri-sort-lists-in-timer ()
83   (condition-case err
84       (save-match-data ;; runs in timer
85         (majmodpri-sort-lists))
86     (error (message "(majmodpri-sort-lists): %s" err))))
87
88
89 ;;;; Sorting
90
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)))
96     (list
97      (list mode majmodpri-schwarzian-ordnum)
98      rec)))
99
100 (defun majmodpri-schwarzian-out (rec)
101   "Get original value of REC after sorting."
102   (cadr rec))
103
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.
109
110 This gives those multi major mode lower priority, but it does not
111 prevent use of them."
112   :type 'boolean
113   :group 'majmodpri)
114
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
120            ;; (symbolp mode)
121            ;; (save-match-data
122            ;;   (string-match "nxhtml-mumamo" (symbol-name mode))))
123            (let* ((real (or (ourcomments-indirect-fun mode)
124                             mode))
125                   (chunk (when real (get real 'mumamo-chunk-family)))
126                   (major-mode (when chunk
127                                 (cadr chunk))))
128              (when major-mode
129                (derived-mode-p 'nxml-mode))))
130       0
131     (length (memq mode majmodpri-mode-priorities))))
132
133 (defun majmodpri-compare-auto-modes (rec1 rec2)
134   "Compare record REC1 and record REC2.
135 Comparision:
136
137 - First check `majmodpri-mode-priorities'.
138 - Then use old order in list."
139   (let* ((schw1 (car rec1))
140          (schw2 (car rec2))
141          (mod1     (nth 0 schw1))
142          (mod2     (nth 0 schw2))
143          (ord1     (nth 1 schw1))
144          (ord2     (nth 1 schw2))
145          (pri1 (majmodpri-priority mod1))
146          (pri2 (majmodpri-priority mod2)))
147     (cond
148      ((/= pri1 pri2) (> pri1 pri2))
149      (t (> ord1 ord2)))))
150
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.
157   (let (fun-list
158         mod-list)
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
165           (append
166            fun-list
167            (mapcar 'majmodpri-schwarzian-out
168                    (sort
169                     (mapcar 'majmodpri-schwarzian-in mod-list)
170                     'majmodpri-compare-auto-modes))))))
171
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
176          ;; S out
177          (mapcar (lambda (rec)
178                    (cadr rec))
179                  ;; Sort
180                  (sort
181                   ;; S in
182                   (mapcar (lambda (rec)
183                             (setq orig-ordnum (1+ orig-ordnum))
184                             (let ((mode (cdr rec)))
185                               (list
186                                (list mode orig-ordnum)
187                                rec)))
188                           (symbol-value magic-mode-list-sym))
189                   (lambda (rec1 rec2)
190                     (let* ((schw1 (car rec1))
191                            (schw2 (car rec2))
192                            (mod1 (nth 0 schw1))
193                            (mod2 (nth 0 schw2))
194                            (ord1 (nth 1 schw1))
195                            (ord2 (nth 1 schw2))
196                            (pri1 (majmodpri-priority mod1))
197                            (pri2 (majmodpri-priority mod2)))
198                       (cond
199                        ((/= pri1 pri2) (> pri1 pri2))
200                        (t (> ord1 ord2))))))))))
201
202 ;;;###autoload
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.
208
209 The lists can be sorted when loading elisp libraries, see
210 `majmodpri-sort-after-load'.
211
212 See also `majmodpri-apply-priorities'."
213   (interactive)
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)")
223   )
224
225
226 ;;;###autoload
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
231 in buffers."
232   (majmodpri-apply-priorities t))
233
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))
238
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"
246       (set-auto-mode t))
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))
262         (font-lock-mode 1))
263       (message "majmodpri-apply-priorities: buffer=%s, %s,%s => %s,%s"
264                (current-buffer)
265                old-major-mode
266                old-mumamo-multi-major-mode
267                major-mode
268                (when (boundp 'mumamo-multi-major-mode)
269                  mumamo-multi-major-mode)))))
270
271 ;;;###autoload
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
276 before applying."
277   (interactive '(nil))
278   (message "majmodpri-apply-priorities running ...")
279   (majmodpri-sort-lists)
280   (when (or change-modes
281             (with-no-warnings (called-interactively-p)))
282     (let (file-buffers)
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
288                 (not file)
289                 (setq file-buffers (cons buffer file-buffers))))))
290       (if (not file-buffers)
291           (when change-modes
292             ;;(message "majmodpri-apply-priorities: No file buffers to change modes in")
293             )
294         (when (with-no-warnings (called-interactively-p))
295           (setq change-modes
296                 (y-or-n-p "Check major mode in all file visiting buffers? ")))
297         (when change-modes
298           (dolist (buffer file-buffers)
299             (with-current-buffer buffer
300               (let ((old-major major-mode))
301                 (majmodpri-check-normal-mode)
302                 )))))))
303   (message "majmodpri-apply-priorities running ... (done)"))
304
305
306 ;;;; Custom
307
308 ;;;###autoload
309 (defgroup majmodpri nil
310   "Customization group for majmodpri.el"
311   :group 'nxhtml
312   )
313
314 (defcustom majmodpri-mode-priorities
315   '(
316     cperl-mumamo-mode
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
327     jsp-html-mumamo-mode
328     laszlo-nxml-mumamo-mode
329     metapost-mumamo-mode
330     mjt-nxhtml-mumamo-mode
331     mjt-html-mumamo-mode
332     noweb2-mumamo-mode
333     ;;org-mumamo-mode
334     perl-mumamo-mode
335     smarty-nxhtml-mumamo-mode
336     smarty-html-mumamo-mode
337     ;;tt-html-mumamo-mode
338
339     nxhtml-mumamo-mode
340     html-mumamo-mode
341     nxml-mumamo-mode
342     nxml-mode
343
344     javascript-mode
345     ;;espresso-mode
346     rhtml-mode
347     )
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)))
357   :group 'majmodpri)
358
359 (defcustom majmodpri-lists-to-sort
360   '(magic-mode-alist auto-mode-alist magic-fallback-mode-alist)
361   ;;nil
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)))
372   :group 'majmodpri)
373
374 (defcustom majmodpri-sort-after-load
375   '(
376     chart
377     gpl
378     ;;nxhtml-autoload
379     php-mode
380     rnc-mode
381     ruby-mode
382     )
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
389 variable.
390
391 There are two possibilities for sorting here:
392
393 - Value=list of features (default). Sort immediately after loading a
394   library in the list.  Apply to current buffer.
395
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.
401
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'.
406
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
415                (delq nil
416                      (mapcar (lambda (rec)
417                                (unless (member (cadr rec)
418                                                '((majmodpri-start-idle-sort)
419                                                  (majmodpri-sort-lists)))
420                                  rec))
421                              after-load-alist)))
422          (when val
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
427                               (if (eq val t)
428                                   '(".*" (majmodpri-start-idle-sort))
429                                 '("." (majmodpri-sort-lists))))
430                (dolist (feat val)
431                  ;;(message "feat=%s" feat)
432                  (if (featurep feat)
433                      (setq sort-and-apply t)
434                    (if (eq val t)
435                        (eval-after-load feat '(majmodpri-start-idle-sort))
436                      (eval-after-load feat '(majmodpri-sort-apply-to-current))))))
437              (when sort-and-apply
438                ;;(message "majmodpri-sort-after-load: sort-and-apply")
439                (majmodpri-apply-priorities t))
440              (if (eq val t)
441                  (majmodpri-start-idle-sort)
442                (majmodpri-apply-priorities t)))))
443   :group 'majmodpri)
444
445
446 (provide 'majmodpri)
447 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
448 ;;; majmodpri.el ends here