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