9423ef237708b5010b1d669081e20d4fc4d50c1e
[emacs-init.git] / setup / nxml.el
1 (defvar nxml-where-elements-to-id 3)
2 (defvar nxml-where-max-elements 6)
3
4 (require 'cl)
5 (require 'nxml-mode)
6
7 (defun nxml-where ()
8   "Display the hierarchy of XML elements the point is on as a path."
9   (and (eq major-mode 'nxml-mode)
10            (let (path-to-id path-rest)
11                  (save-excursion
12                    (save-restriction
13                          (widen)
14                          (while (and (not (bobp))
15                                      (condition-case nil (progn (nxml-backward-up-element) t)
16                                        (error nil)))
17                            (multiple-value-bind
18                                (has-id step)
19                                (loop with has-id = nil
20                                          with step = (xmltok-start-tag-local-name)
21                                          for att in xmltok-attributes
22                                          if (string= (xmltok-attribute-local-name att) "id")
23                                          return (values t (concat "\""
24                                                                   (xmltok-attribute-value att)
25                                                                   "\""))
26                                          else if (string= (xmltok-attribute-local-name att) "name")
27                                          do (setq has-id t
28                                                   step (concat "\""
29                                                                (xmltok-attribute-value att)
30                                                                "\""))
31                                          finally return (values has-id step ))
32                              (if (or path-to-id has-id)
33                                  (setq path-to-id (cons step path-to-id))
34                                (setq path-rest (cons step path-rest)))))))
35                  (let ((path-to-id-len (length path-to-id))
36                            (path-rest-len (length path-rest)))
37                    (if (> path-to-id-len nxml-where-elements-to-id)
38                            (progn
39                                  (setq path-to-id (nthcdr (- path-to-id-len
40                                                              nxml-where-elements-to-id
41                                                              -1) path-to-id))
42                                  (setq path-to-id (cons "..." path-to-id))
43                                  (setq path-to-id-len nxml-where-elements-to-id))
44                          (setq path-to-id (cons "" path-to-id)))
45                    (when (> (+ path-to-id-len path-rest-len) nxml-where-max-elements)
46                          (setq path-rest (nbutlast path-rest (- path-rest-len
47                                                                 (- nxml-where-max-elements
48                                                                    path-to-id-len)
49                                                                 -1)))
50                          (setq path-rest (nconc path-rest (list "...")))))
51                  (mapconcat 'identity (nconc path-to-id path-rest) "/"))))
52
53 (require 'which-func)
54 (which-func-mode)
55
56 (delete (assoc 'which-func-mode mode-line-format) mode-line-format)
57 (setq which-func-header-line-format
58               '(which-func-mode
59                 ("" which-func-format
60                  )))
61 (defadvice which-func-ff-hook (after header-line activate)
62   (when which-func-mode
63     (delete (assoc 'which-func-mode mode-line-format) mode-line-format)
64     (setq header-line-format which-func-header-line-format)))
65
66 (add-to-list 'which-func-functions 'nxml-where)
67 (add-to-list 'which-func-modes 'nxml-mode)
68 (add-to-list 'which-func-non-auto-modes 'nxml-mode)
69
70 (add-to-list 'auto-mode-alist '("\\.xslt?\\'" . nxml-mode))
71 (add-to-list 'auto-mode-alist '("\\.xsd\\'" . nxml-mode))
72
73 (require 'hideshow)
74
75 (add-to-list 'hs-special-modes-alist '(nxml-mode ("\\(<[^/>]*>\\)$" 1) "</[^/>]*>$"))
76
77 (defun nxml-enable-hs ()
78   (setq nxml-sexp-element-flag t)
79   (hs-minor-mode 1))
80
81 (add-hook 'nxml-mode-hook 'nxml-enable-hs)
82
83 (defun hs-nxml-enter ()
84   (interactive)
85   (when (hs-already-hidden-p)
86         (hs-show-block)
87         (hs-hide-level 1)
88         (nxml-forward-element)
89         (nxml-backward-element)))
90
91 (defun hs-nxml-leave ()
92   (interactive)
93   (nxml-backward-up-element)
94   (hs-hide-block)
95   (nxml-backward-up-element))
96
97 (defun hs-nxml-hide-other ()
98   (interactive)
99   (let ((p (point)))
100     (hs-hide-all)
101     (while (progn (goto-char p) (hs-already-hidden-p))
102       (hs-nxml-enter))
103     (hs-show-block)
104     (goto-char p)
105     (recenter-top-bottom)))
106
107 (define-key nxml-mode-map (kbd "\C-c <left>") 'hs-nxml-leave)
108 (define-key nxml-mode-map (kbd "\C-c <right>") 'hs-nxml-enter)
109 (define-key nxml-mode-map (kbd "\C-c @ o") 'hs-nxml-hide-other)
110
111 (defun nxml-complete-and-autoclose-element (use-region)
112   (interactive "P")
113   (let* ((start (if use-region (setq start (set-marker (make-marker) (region-beginning)))))
114          (end (setq use-region (set-marker (make-marker) (region-end))))
115          (beg (and start (= (point) start))))
116   (save-excursion
117     (insert " "))
118   (nxml-complete)
119   (let ((name (xmltok-start-tag-local-name)))
120     (delete-char 1)
121     (if (and start end)
122         (progn
123           (if (not beg)
124               (progn
125                 (delete-char (- (1+ (length name))))
126                 (goto-char start)
127                 (insert "<" name ">"))
128             (insert ">"))
129           (goto-char end))
130       (insert ">"))
131     (save-excursion
132       (insert "</" name ">")))))
133
134 (define-key nxml-mode-map (kbd "\C-c .") 'nxml-complete-and-autoclose-element)
135 (define-key nxml-mode-map (kbd "\C-c ,") 'nxml-complete)
136
137 (define-key nxml-mode-map (kbd "\C-c\C-c") 'recompile)
138
139 (defconst nxml-docbook-common-elements
140   '(("section" . ("para" "itemizedlist" "variablelist" "section" "bridgehead" "task" "procedure"
141                   "title"))
142     ("refsect1" . ("title" "para" "itemizedlist" "variablelist" "screen" "refsect2"))
143     ("refsect2" . ("title" "para" "itemizedlilst" "variablelist" "screen"))
144     ("para" . ("emphasis" "code" "replaceable" "literal"))
145     ("emphasis" . ("code"))
146     ("itemizedlist" . ("listitem"))
147     ("orderedlist" . ("listitem"))
148     ("variablelist" . ("varlistentry"))
149     ("varlistentry" . ("term" "listitem"))
150     ("term" . ("emphasis" "literal" "replaceable" "option"))
151     ("listitem" . ("para" "itemizedlist"))
152     ("task" . ("tasksummary" "procedure" "title"))
153     ("tasksummary" . ("para" "itemizedlist" "variablelist"))
154     ("procedure" . ("step"))
155     ("step" . ("para" "procedure"))
156     ("mathphrase" . ("replaceable" "superscript" "subscript"))
157     ("title" . ("code" "replaceable"))
158     ("literallayout" . ("replaceable" "emphasis" "code"))
159     ("table" . ("title" . "tgroup"))
160     ("tgroup" . ("colspec" "thead" "tbody"))
161     ("thead" . ("row"))
162     ("tbody" . ("row"))
163     ("row". ("entry"))
164     ("entry" . ("emphasis" "code"))))
165
166 (defvar nxml-docbook-common-elements-next-args nil)
167
168 (defun nxml-docbook-make-common-element (&optional start end kill-tag use-index old-tag valid)
169   (interactive (cond ((and (eq real-last-command 'nxml-docbook-make-common-element)
170                            nxml-docbook-common-elements-next-args)
171                       nxml-docbook-common-elements-next-args)
172                      (current-prefix-arg
173                       (save-excursion
174                         (nxml-backward-up-element)
175                         (let ((tag (xmltok-start-tag-qname)))
176                           (list (save-excursion
177                                   (skip-chars-forward "^>")
178                                   (forward-char 1)
179                                   (point))
180                                 (progn
181                                   (nxml-forward-balanced-item)
182                                   (skip-chars-backward "^<")
183                                   (forward-char -1)
184                                   (point))
185                                 t nil tag))))
186                      ((region-active-p)
187                       (list (region-beginning) (region-end) nil))))
188   (setq nxml-docbook-common-elements-next-args nil)
189   (let ((start (set-marker (make-marker) (or start (point))))
190         (end (set-marker (make-marker) (or end (point)))))
191     (when kill-tag
192       (delete-region (save-excursion
193                        (goto-char start) (skip-chars-backward "^<") (1- (point))) start)
194       (delete-region end (save-excursion
195                            (goto-char end) (skip-chars-forward "^>") (1+ (point)))))
196     (save-excursion
197       (goto-char start)
198       (let* ((token-end (nxml-token-before))
199              (start-tag-end
200               (save-excursion
201                 (when (and (< (point) token-end)
202                            (memq xmltok-type
203                                  '(cdata-section
204                                    processing-instruction
205                                    comment
206                                    start-tag
207                                    end-tag
208                                    empty-element)))
209                   (error "Point is inside a %s"
210                          (nxml-token-type-friendly-name xmltok-type)))
211                 (nxml-scan-element-backward token-end t)))
212              (context (xmltok-start-tag-qname))
213              (common-elements (cdr (assoc context nxml-docbook-common-elements)))
214              (valid-elements (or valid
215                                  (let ((lt-pos (point)))
216                                    (rng-set-state-after lt-pos)
217                                    (loop for (ns . name) in (rng-match-possible-start-tag-names)
218                                          if (not (member name elements)) collect name into elements
219                                          finally return elements))))
220              (elements (loop for element in common-elements
221                              if (member element valid-elements) collect element))
222              (index (or (and elements
223                              (or use-index
224                                  (and old-tag
225                                       (loop for i from 0
226                                             for elt in elements
227                                             if (string= elt old-tag) return (1+ i)
228                                             finally return 0))))
229                         0))
230              (element (and elements (nth index elements))))
231         (when (not elements)
232           (error "No common elements for %s" context))
233         (if element
234             (progn
235               (goto-char start)
236               (insert-before-markers "<" element ">")
237               (goto-char end)
238               (insert "</" element ">")
239               (goto-char end)
240               (setq nxml-docbook-common-elements-next-args (list (marker-position start)
241                                                                  (marker-position end)
242                                                                  t
243                                                                  (1+ index)
244                                                                  valid-elements)))
245           (setq nxml-docbook-common-elements-next-args (list (marker-position start)
246                                                              (marker-position end)
247                                                              nil
248                                                              0
249                                                              valid-elements)))))))
250
251 (defun nxml-just-one-space-or-skip-end ()
252   (interactive)
253   (if (looking-at "</")
254       (progn
255         (skip-chars-forward "^>")
256         (forward-char 1))
257     (just-one-space)))
258
259 (define-key nxml-mode-map (kbd "M-RET") 'nxml-docbook-make-common-element)
260 (define-key nxml-mode-map (kbd "M-SPC") 'nxml-just-one-space-or-skip-end)
261
262 (defun nxml-open-line ()
263   (interactive)
264   (if (region-active-p)
265       (let ((start (region-beginning))
266             (end (region-end))
267             chars)
268         (save-excursion
269           (goto-char end)
270           (newline-and-indent)
271           (goto-char start)
272           (setq chars (- (- (point) (progn (newline-and-indent) (point)))))
273           (indent-region (+ start chars) (+ end chars))))
274     (open-line 1)
275     (save-excursion
276       (forward-line 1)
277       (indent-according-to-mode))
278     (newline-and-indent)))
279
280 (defun nxml-split-element ()
281   (interactive)
282   (let (element block-p)
283     (save-excursion
284       (nxml-backward-up-element)
285       (setq element (xmltok-start-tag-qname)
286             block-p (looking-back "^\s-*" (save-excursion (beginning-of-line) (point)))))
287     (delete-horizontal-space)
288     (insert "</" element ">")
289     (fill-paragraph)
290     (insert "\n")
291     (newline-and-indent)
292     (insert "<" element ">")
293     (fill-paragraph)))
294
295 (defun my-nxml-reformat-region (begin end)
296   (interactive "r")
297   (save-excursion
298       (nxml-mode)
299       (goto-char begin)
300       (while (search-forward-regexp "\>[ \\t]*\<" nil t)
301         (backward-char) (insert "\n"))
302       (indent-region begin end nil)))
303
304 (define-key nxml-mode-map (kbd "M-o") 'nxml-open-line)
305 (define-key nxml-mode-map (kbd "S-<return>") 'nxml-split-element)