]> git.rkrishnan.org Git - .emacs.d.git/blob - emacs/nxhtml/util/fold-dwim.el
remove toolbar and menubar
[.emacs.d.git] / emacs / nxhtml / util / fold-dwim.el
1 ;;; fold-dwim.el -- Unified user interface for Emacs folding modes
2 ;;
3 ;; Copyright (C) 2004 P J Heslin
4 ;;
5 ;; Author: Peter Heslin <p.j.heslin@dur.ac.uk>
6 ;; URL: http://www.dur.ac.uk/p.j.heslin/Software/Emacs/Download/fold-dwim.el
7 (defconst fold-dwim:version "1.4")
8 ;;
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13 ;;
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; If you do not have a copy of the GNU General Public License, you
20 ;; can obtain one by writing to the Free Software Foundation, Inc., 59
21 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 ;;; Overview:
24 ;;
25 ;; DWIM stands for "do what I mean", as in the idea that one keystroke
26 ;; can do different things depending on the context. In this package,
27 ;; it means that, if the cursor is in a currently hidden folded
28 ;; construction, we want to show it; if it's not, we want to hide
29 ;; whatever fold the cursor is in.
30 ;;
31 ;; Some editors other than Emacs provide a single mechanism for
32 ;; folding text which various file types can exploit.  The advantage
33 ;; of this arrangement is that the user only has to know one set of
34 ;; folding commands; the disadvantage is that the various file types
35 ;; are limited to using whatever functionality is provided centrally.
36 ;; Emacs by contrast provides a very general and powerful framework
37 ;; for hiding text, which major modes can use as they see fit.  The
38 ;; advantage of this is that each major mode can deal with folding in
39 ;; the way that is suitable for that type of file; the disadvantage is
40 ;; that different major modes have different styles of folding, and
41 ;; provide different key bindings.
42 ;;
43 ;; In practice, matters are simpler than that, since most major modes
44 ;; delegate the task of folding to packages like outline.el and
45 ;; hideshow.el.  The key bindings for these two packages alone,
46 ;; however, are numerous and for some people hard to type.  Another
47 ;; usability complication arises when a package like AucTeX uses
48 ;; outline-minor-mode for some folds, and provides its own
49 ;; key-bindings for other kinds of folds.  Likewise, nXML-mode
50 ;; provides its own style of folding for certain types of files, but
51 ;; for files that don't fit that paradigm (such as XHTML), you may
52 ;; want to use outline-minor-mode instead.
53 ;;
54 ;; The goal of this package is to reduce this complexity to three
55 ;; globally-defined keystrokes: one to toggle the state of the fold at
56 ;; point, whatever its type may be, one to hide all folds of all types
57 ;; in the buffer, and one to show all folds.
58 ;;
59 ;; This package currently knows about folding-mode (from folding.el),
60 ;; hs-minor-mode (from hideshow.el), outline-minor-mode (from
61 ;; outline.el), TeX-fold-mode (from AUCTeX), and nXML-mode outlining.
62 ;; More could be added.  It is not necessary to have folding.el,
63 ;; AUCTeX or nXML-mode installed, if you just want to use it with the
64 ;; built-in modes.
65
66 ;;; Usage:
67 ;;
68 ;; You will need to have one or more of following minor modes switched
69 ;; on: hs-minor-mode, outline-minor-mode, TeX-fold-mode, folding-mode.
70 ;; Otherwise no folds may be found. There are three functions to try:
71 ;;
72 ;; fold-dwim-toggle: try to show any hidden text at the cursor; if no
73 ;; hidden text is found, try to hide the text at the cursor.
74 ;;
75 ;; fold-dwim-hide-all: hide all folds in the buffer.
76 ;;
77 ;; fold-dwim-show-all: show all folds in the buffer.
78
79 ;;; Configuration
80 ;;
81 ;; This package binds no keys by default, so you need to find three
82 ;; free and convenient key-bindings.  This is what I use:
83 ;;
84 ;;  (global-set-key (kbd "<f7>")      'fold-dwim-toggle)
85 ;;  (global-set-key (kbd "<M-f7>")    'fold-dwim-hide-all)
86 ;;  (global-set-key (kbd "<S-M-f7>")  'fold-dwim-show-all)
87 ;;
88
89 ;;; Advanced Configuration
90 ;;
91 ;; With respect to outline-minor-mode (or outline-mode), dwim-fold
92 ;; provides two different styles of usage.  The first is a "nested"
93 ;; style which only shows top-level headings when you fold the whole
94 ;; buffer, and then allows you to drill down progressively through the
95 ;; other levels.  The other is a "flat" style, whereby folding the
96 ;; entire buffer shows all headings at every level.
97 ;;
98 ;; The default is "flat", but if you want to change the default, you
99 ;; can set the value of fold-dwim-outline-style-default to be 'flat or
100 ;; 'nested.  If you wish to override the default for a particular
101 ;; major mode, put a value of either 'flat or 'nested for the
102 ;; fold-dwim-outline-style property of the major-mode symbol, like so:
103 ;;
104 ;;   (put 'org-mode 'fold-dwim-outline-style 'nested)
105 ;;
106 ;; At present, there is no way to customize nXML-mode outlining to use
107 ;; the nested style, since it is not really supported by that mode
108 ;; (there is no function to hide all text and subheadings in the
109 ;; buffer).
110
111 ;;; Compatibility
112 ;;
113 ;; Tested with GNU Emacs CVS (from Sept. 10, 2004), AUCTeX version
114 ;; 11.53, nxml-mode version 20041004, folding.el version 2.97.
115 ;;
116 ;; If there are any other important major or minor modes that do
117 ;; folding and that could usefully be handled in this package, please
118 ;; let me know.
119
120 ;;; Bugs
121 ;;
122 ;; It is possible that some of the various folding modes may interact
123 ;; badly if used together; I have not tested all permutations.
124 ;;
125 ;; The function fold-dwim-hide tries various folding modes in
126 ;; succession, and stops when it finds one that successfully makes a
127 ;; fold at point.  This means that the order in which those modes are
128 ;; tried is significant.  I have not spent a lot of time thinking
129 ;; about what the optimal order would be; all I care about is that
130 ;; hideshow and TeX-fold have priority over outline-minor-mode (since
131 ;; for me they usually fold smaller chunks of the file).
132 ;;
133 ;; I don't use folding.el myself, so that functionality is not well
134 ;; tested.
135
136 ;;; Changes
137 ;;
138 ;; 1.0 Initial release
139 ;; 1.1 Bugfix: test if folding-mode is bound
140 ;; 1.2 fold-dwim-hide-all and -show-all operate only on active region
141 ;;     in transient-mark-mode.
142 ;; 1.3 Added outline-mode (Lennart Borgman)
143 ;; 1.4 Removed nxml-mode style folding (Lennart Borgman)
144 ;;     + some functions used by nXhtml.
145
146 (require 'outline)
147 (require 'hideshow)
148
149 ;;;###autoload
150 (defgroup fold-dwim nil
151   "Unified interface to folding commands"
152   :prefix "fold-dwim-"
153   :group 'editing)
154
155 (defcustom fold-dwim-outline-style-default 'flat
156   "Default style in which to fold in outline-minor-mode: 'nested or
157   'flat."
158   :type '(choice (const :tag "Flat (show all headings)" flat)
159                  (const :tag "Nested (nest headings hierarchically)" nested))
160   :group 'fold-dwim)
161
162 (defvar fold-dwim-toggle-selective-display 'nil
163   "Set this non-nil to make fold-dwim functions use selective
164   display (folding of all lines indented as much or more than the
165   current line).  Probably only useful for minor modes like
166   makefile-mode that don't provide a more intelligent way of
167   folding.")
168
169 (make-variable-buffer-local
170  'fold-dwim-toggle-selective-display)
171
172 (defun fold-dwim-maybe-recenter ()
173   "It's annoyingly frequent that hiding a fold will leave you
174 with point on the top or bottom line of the screen, looking at
175 nothing but an ellipsis.  TODO: only recenter if we end up near
176 the top or bottom of the screen"
177   (recenter))
178
179 (defun fold-dwim-toggle-selective-display ()
180   "Set selective display to indentation of current line"
181   (interactive)
182   (if (numberp selective-display)
183       (set-selective-display nil)
184     (save-excursion
185       (beginning-of-line)
186       (skip-chars-forward " \t")
187       (let ((col (current-column)))
188         (if (zerop col)
189             (set-selective-display nil)
190           (set-selective-display col))))))
191
192 (defun fold-dwim-hide-all ()
193   "Hide all folds of various kinds in the buffer or region"
194   (interactive)
195   (save-excursion
196     (save-restriction
197       (when (and transient-mark-mode mark-active)
198         (narrow-to-region (region-beginning) (region-end)))
199       (when (and (boundp 'TeX-fold-mode) TeX-fold-mode)
200         (TeX-fold-buffer))
201       (when hs-minor-mode
202         (hs-hide-all))
203       (when (or outline-minor-mode (eq major-mode 'outline-mode))
204         (if (fold-dwim-outline-nested-p)
205             (hide-sublevels 1)
206           (hide-body)))
207       ;; (when (derived-mode-p 'nxml-mode)
208       ;;   (nxml-hide-all-text-content))
209       (when (and (boundp 'folding-mode) folding-mode)
210         (folding-whole-buffer))))
211   (fold-dwim-maybe-recenter))
212
213 (defun fold-dwim-show-all ()
214   "Show all folds of various kinds in the buffer or region"
215   (interactive)
216   (save-excursion
217     (save-restriction
218       (when (and transient-mark-mode mark-active)
219         (narrow-to-region (region-beginning) (region-end)))
220       (when (and (boundp 'TeX-fold-mode) TeX-fold-mode)
221         (TeX-fold-clearout-buffer))
222       (when hs-minor-mode
223         (hs-show-all))
224       ;; (when (derived-mode-p 'nxml-mode)
225       ;;   (nxml-show-all))
226       (when (or outline-minor-mode (eq major-mode 'outline-mode))
227         (show-all))
228       (when (and (boundp 'folding-mode) folding-mode)
229         (folding-open-buffer))
230       (when fold-dwim-toggle-selective-display
231         (set-selective-display 'nil)))))
232
233 (defun fold-dwim-hide ()
234   "Hide one item"
235   (or (and (boundp 'TeX-fold-mode)
236            TeX-fold-mode
237            (let ((type (fold-dwim-auctex-env-or-macro)))
238              (when type
239                (TeX-fold-item type))))
240       ;; Look for html headers.
241       (when (and (derived-mode-p 'nxml-mode 'html-mode)
242                  outline-minor-mode)
243         (when (save-excursion
244                 (save-match-data
245                   (looking-back (rx "<" (optional "/")
246                                     "h" (any "1-6")
247                                     (0+ (not (any "<")))))))
248           (hide-entry)
249           t))
250       (and hs-minor-mode
251            (when (save-excursion
252                    (or (hs-find-block-beginning) (hs-inside-comment-p)))
253              (hs-hide-block)
254              (hs-already-hidden-p)))
255       ;; (and (derived-mode-p 'nxml-mode)
256       ;;      (condition-case nil
257       ;;          (save-excursion
258       ;;            (nxml-back-to-section-start))
259       ;;        (error nil))
260       ;;      (nxml-hide-text-content))
261       (and (boundp 'folding-mode)
262            folding-mode
263            (condition-case nil
264                (save-excursion
265                  (folding-hide-current-entry)
266                  t)
267              (error nil)))
268       (when (or outline-minor-mode (eq major-mode 'outline-mode))
269         (if (fold-dwim-outline-nested-p)
270             (hide-subtree)
271           (hide-entry))))
272   (fold-dwim-maybe-recenter))
273
274
275 (defun fold-dwim-show ()
276   "If point is in a closed or temporarily open fold,
277   open it.  Returns nil if nothing was done"
278   (save-excursion
279     (let ((stop))
280       (when (and (or outline-minor-mode (eq major-mode 'outline-mode))
281                  (or (fold-dwim-outline-invisible-p (line-end-position))
282                      (and (bolp)
283                           (not (bobp))
284                           (fold-dwim-outline-invisible-p (1- (point))))))
285         (if (not (fold-dwim-outline-nested-p))
286             (show-entry)
287           (show-children)
288           (show-entry))
289         (setq stop "outline-minor-mode"))
290       (when (and (not stop)
291                  hs-minor-mode
292                  (hs-already-hidden-p))
293         (hs-show-block)
294         (setq stop "hs-minor-mode"))
295       (when (and (not stop)
296                  (boundp 'TeX-fold-mode)
297                  TeX-fold-mode)
298         (let ((overlays (overlays-at (point))))
299           (while overlays
300             (when (eq (overlay-get (car overlays) 'category) 'TeX-fold)
301               (delete-overlay (car overlays))
302               (setq stop "Tex-fold-mode"))
303             (setq overlays (cdr overlays)))))
304       ;; (when (and (not stop)
305       ;;            (derived-mode-p 'nxml-mode))
306       ;;   (let ((overlays (overlays-at (point))))
307       ;;     (while (and overlays (not stop))
308       ;;       (when (overlay-get (car overlays) 'nxml-outline-display)
309       ;;         (setq stop "nxml folding"))
310       ;;       (setq overlays (cdr overlays))))
311       ;;   (when stop
312       ;;       (nxml-show)))
313       (when (and (not stop)
314                  (boundp 'folding-mode)
315                  folding-mode
316                  (save-excursion
317                    (beginning-of-line)
318                    (let ((current-line-mark (folding-mark-look-at)))
319                      (when (and (numberp current-line-mark)
320                                 (= current-line-mark 0))
321                        (folding-show-current-entry)
322                        (setq stop "folding-mode"))))))
323       stop)))
324
325 ;;;###autoload
326 (defun fold-dwim-toggle ()
327   "Toggle visibility or some other visual things.
328 Try toggling different visual things in this order:
329
330 - Images shown at point with `inlimg-mode'
331 - Text at point prettified by `html-write-mode'.
332
333 For the rest it unhides if possible, otherwise hides in this
334 order:
335
336 - `org-mode' header or something else using that outlines.
337 - Maybe `fold-dwim-toggle-selective-display'.
338 - `Tex-fold-mode' things.
339 - In html if `outline-minor-mode' and after heading hide content.
340 - `hs-minor-mode' things.
341 - `outline-minor-mode' things. (Turns maybe on this.)
342
343 It uses `fold-dwim-show' to show any hidden text at point; if no
344 hidden fold is found, try `fold-dwim-hide' to hide the
345 construction at the cursor.
346
347 Note: Also first turn on `fold-dwim-mode' to get the keybinding
348 for this function from it."
349   (interactive)
350   (fold-dwim-mode 1)
351   (cond
352    ((get-char-property (point) 'html-write)
353     (html-write-toggle-current-tag))
354    ((get-char-property (point) 'inlimg-img)
355     (inlimg-toggle-display (point)))
356    ((eq major-mode 'org-mode)
357     (org-cycle))
358    ((and (fboundp 'outline-cycle)
359          outline-minor-mode)
360     (outline-cycle))
361    (t
362     (unless (or outline-minor-mode hs-minor-mode)
363       (outline-minor-mode 1))
364     (if fold-dwim-toggle-selective-display
365         (fold-dwim-toggle-selective-display)
366         (let ((unfolded (fold-dwim-show)))
367           (if unfolded
368               (message "Fold DWIM showed: %s" unfolded)
369             (fold-dwim-hide)))))))
370
371 ;;;###autoload
372 (define-minor-mode fold-dwim-mode
373   "Key binding for `fold-dwim-toggle'."
374   :global t
375   :group 'nxhtml
376   :group 'foldit
377   nil)
378
379 ;; Fix-me: Maybe move to fold-dwim and rethink?
380 (defvar fold-dwim-mode-map
381   (let ((map (make-sparse-keymap)))
382     (define-key map [(control ?c) ?+] 'fold-dwim-toggle)
383     map))
384
385 ;;;###autoload
386 (defun fold-dwim-unhide-hs-and-outline ()
387   "Unhide everything hidden by Hide/Show and Outline.
388 Ie everything hidden by `hs-minor-mode' and
389 `outline-minor-mode'."
390   (interactive)
391   (hs-show-all)
392   (show-all))
393
394 ;;;###autoload
395 (defun fold-dwim-turn-on-hs-and-hide ()
396   "Turn on minor mode `hs-minor-mode' and hide.
397 If major mode is derived from `nxml-mode' call `hs-hide-block'
398 else call `hs-hide-all'."
399   (interactive)
400   (hs-minor-mode 1)
401   (foldit-mode 1)
402   (if (derived-mode-p 'nxml-mode)
403       (hs-hide-block)
404     (hs-hide-all)))
405
406 ;;;###autoload
407 (defun fold-dwim-turn-on-outline-and-hide-all ()
408   "Turn on `outline-minor-mode' and call `hide-body'."
409   (interactive)
410   (outline-minor-mode 1)
411   (foldit-mode 1)
412   (hide-body))
413
414 (defun fold-dwim-auctex-env-or-macro ()
415   (let ((type (cond
416                ;; Fold macro before env, unless it's begin or end
417                ((save-excursion
418                   (let ((macro-start (TeX-find-macro-start)))
419                     (and macro-start
420                          (not (= macro-start (point)))
421                          (goto-char macro-start)
422                          (not (looking-at
423                                (concat (regexp-quote TeX-esc)
424                                        "\\(begin\\|end\\)[ \t]*{"))))))
425                 'macro)
426                ((and (eq major-mode 'context-mode)
427                      (save-excursion
428                        (ConTeXt-find-matching-start) (point)))
429                 'env)
430                ((and (eq major-mode 'texinfo-mode)
431                      (save-excursion
432                        (Texinfo-find-env-start) (point)))
433                 'env)
434                ((and (eq major-mode 'latex-mode)
435                      (condition-case nil
436                          (save-excursion
437                            (LaTeX-find-matching-begin) (point)
438                            (not (looking-at "\\\\begin[ \t]*{document}")))
439                        (error nil)))
440                 'env)
441                (t
442                 nil))))
443     type))
444
445 (defun fold-dwim-outline-invisible-p (pos)
446   "The version of this function in outline.el doesn't work so
447   well for our purposes, because it doesn't distinguish between
448   invisibility caused by outline, and that of other modes."
449   (save-excursion
450     (goto-char pos)
451     (let ((overlays (overlays-at (point)))
452           (found-one))
453       (while overlays
454         (when (eq (overlay-get (car overlays) 'invisible) 'outline)
455           (setq found-one t))
456         (setq overlays (cdr overlays)))
457       found-one)))
458
459 (defun fold-dwim-outline-nested-p ()
460   "Are we using the flat or nested style for outline-minor-mode?"
461   (let ((style (get major-mode 'fold-dwim-outline-style)))
462     (if style
463         (eq style 'nested)
464       (eq fold-dwim-outline-style-default 'nested))))
465
466 (provide 'fold-dwim)