/.emacs.d/el-get/nxhtml/nxhtml/rngalt.el

https://bitbucket.org/shuangxinyu/emacspack · Lisp · 828 lines · 629 code · 65 blank · 134 comment · 15 complexity · d52541cd41f19acbd09da2354f6c3f02 MD5 · raw file

  1. ;;; rngalt.el --- Tools for making completion addition to nxml mode
  2. ;;
  3. ;; Author: Lennart Borgman
  4. ;; Created: Wed Jan 10 17:17:18 2007
  5. (defconst rngalt:version "0.51") ;;Version:
  6. ;; Last-Updated: 2008-03-08T03:33:56+0100 Sat
  7. ;; Keywords:
  8. ;; Compatibility:
  9. ;;
  10. ;; Features that might be required by this library:
  11. ;;
  12. ;; `nxml-enc', `nxml-ns', `nxml-parse', `nxml-util',
  13. ;; `ourcomments-util', `rng-dt', `rng-loc', `rng-match',
  14. ;; `rng-parse', `rng-pttrn', `rng-uri', `rng-util', `rng-valid',
  15. ;; `xmltok'.
  16. ;;
  17. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  18. ;;
  19. ;;; Commentary:
  20. ;;
  21. ;;
  22. ;;
  23. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  24. ;;
  25. ;;; Change log:
  26. ;;
  27. ;;
  28. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  29. ;;
  30. ;; This program is free software; you can redistribute it and/or modify
  31. ;; it under the terms of the GNU General Public License as published by
  32. ;; the Free Software Foundation; either version 2, or (at your option)
  33. ;; any later version.
  34. ;;
  35. ;; This program is distributed in the hope that it will be useful,
  36. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  37. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  38. ;; GNU General Public License for more details.
  39. ;;
  40. ;; You should have received a copy of the GNU General Public License
  41. ;; along with this program; see the file COPYING. If not, write to the
  42. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  43. ;; Boston, MA 02111-1307, USA.
  44. ;;
  45. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  46. ;;
  47. ;;; Code:
  48. (eval-and-compile (require 'rng-valid))
  49. (eval-when-compile (require 'rng-nxml))
  50. (eval-when-compile (unless load-file-name (require 'nxhtml-mode nil t)))
  51. (eval-when-compile
  52. (let* ((this-file (or load-file-name
  53. (when (boundp 'bytecomp-filename) bytecomp-filename)
  54. buffer-file-name))
  55. (this-dir (file-name-directory this-file))
  56. (util-dir (expand-file-name "../util/" this-dir))
  57. (load-path (cons util-dir load-path)))
  58. (require 'ourcomments-util)))
  59. ;;(require 'ourcomments-util)
  60. ;; (setq x (macroexpand '(defcustom my-temp-opt t "doc" :type 'boolean)))
  61. ;; (setq x (macroexpand '(define-minor-mode my-temp-mode "doc")))
  62. ;; (setq x (macroexpand '(define-toggle my-temp-toggle t "doc")))
  63. ;;(define-toggle rngalt-display-validation-header t
  64. (define-minor-mode rngalt-display-validation-header
  65. "Display XML validation headers at the top of buffer when t.
  66. The validation header is only displayed in buffers where the main
  67. major mode is derived from `nxml-mode'."
  68. :global t
  69. :init-value t
  70. :group 'relax-ng
  71. :group 'nxhtml
  72. (when (fboundp 'rngalt-update-validation-header-overlay-everywhere)
  73. (rngalt-update-validation-header-overlay-everywhere)))
  74. (defun rngalt-display-validation-header-toggle ()
  75. "Toggle `rngalt-display-validation-header'."
  76. (interactive)
  77. (rngalt-display-validation-header (if rngalt-display-validation-header -1 1)))
  78. ;;(define-toggle rngalt-minimal-validation-header t
  79. (define-minor-mode rngalt-minimal-validation-header
  80. "If non-nil display only a short informaion about the XML validation header.
  81. See also `rngalt-display-validation-header'."
  82. :global t
  83. :init-value t
  84. :group 'relax-ng
  85. :group 'nxhtml
  86. (when (fboundp 'rngalt-update-validation-header-overlay-everywhere)
  87. (rngalt-update-validation-header-overlay-everywhere)))
  88. (defun rngalt-minimal-validation-header-toggle ()
  89. "Toggle `rngalt-minimal-validation-header'."
  90. (interactive)
  91. (rngalt-minimal-validation-header (if rngalt-minimal-validation-header -1 1)))
  92. (defface rngalt-validation-header-top
  93. '((t (:foreground "RGB:87/CE/FA" :background "white")))
  94. "Face first line of validation header."
  95. :group 'nxhtml)
  96. (defface rngalt-validation-header-bottom
  97. '((t (:foreground "white" :background "RGB:87/CE/FA")))
  98. "Face first line of validation header."
  99. :group 'nxhtml)
  100. ;; FIX-ME: remember to clear these variable, but where?
  101. (defvar rngalt-validation-header nil)
  102. (make-variable-buffer-local 'rngalt-validation-header)
  103. (put 'rngalt-validation-header 'permanent-local t)
  104. (defvar rngalt-current-schema-file-name nil)
  105. (make-variable-buffer-local 'rngalt-current-schema-file-name)
  106. (put 'rngalt-current-schema-file-name 'permanent-local t)
  107. (defvar rngalt-validation-header-overlay nil)
  108. (make-variable-buffer-local 'rngalt-validation-header-overlay)
  109. (put 'rngalt-validation-header-overlay 'permanent-local t)
  110. (defvar rngalt-major-mode nil)
  111. (make-variable-buffer-local 'rngalt-major-mode)
  112. (put 'rngalt-major-mode 'permanent-local t)
  113. (defvar rngalt-complete-first-try nil
  114. "First function to try for completion.
  115. If non-nil should be a function with no parameters. Used by
  116. `rngalt-complete'.")
  117. (defvar rngalt-complete-last-try nil
  118. "Last function to try for completion.
  119. If non-nil should be a function with no parameters. Used by
  120. `rngalt-complete'.")
  121. (defvar rngalt-completing-read-tag nil
  122. "Alternate function for completing tag name.
  123. If non-nil should be a function with the same parameters as
  124. `completing-read'. Used by `rngalt-complete'.")
  125. (defvar rngalt-completing-read-attribute-name nil
  126. "Alternate function for completing attribute name.
  127. If non-nil should be a function with the same parameters as
  128. `completing-read'. Used by `rngalt-complete'.")
  129. (defvar rngalt-completing-read-attribute-value nil
  130. "Alternate function for completing attribute value.
  131. If non-nil should be a function with the same parameters as
  132. `completing-read'. Used by `rngalt-complete'.")
  133. (defun rngalt-finish-element ()
  134. "Finish the current element by inserting an end-tag.
  135. Like `nxml-finish-element' but takes `rngalt-validation-header'
  136. into account."
  137. (interactive "*")
  138. (rngalt-finish-element-1 nil))
  139. ;; Fix-me: Check the other uses of `nxml-finish-element-1'. But this
  140. ;; is maybe not necessary since the only other use is in
  141. ;; `nxml-split-element' and that will anyway work - I believe ...
  142. (defun rngalt-finish-element-1 (startp)
  143. "Insert an end-tag for the current element and optionally a start-tag.
  144. The start-tag is inserted if STARTP is non-nil. Return the position
  145. of the inserted start-tag or nil if none was inserted.
  146. This is like `nxml-finish-element-1' but takes
  147. `rngalt-validation-header' into account."
  148. (interactive "*")
  149. (let (token-end
  150. start-tag-end
  151. starts-line
  152. ends-line
  153. start-tag-indent
  154. qname
  155. inserted-start-tag-pos)
  156. ;; Temporary insert the fictive validation header if any.
  157. (let ((buffer-undo-list nil)
  158. (here (point-marker)))
  159. (when rngalt-validation-header
  160. (let ((vh (nth 2 rngalt-validation-header)))
  161. (set-marker-insertion-type here t)
  162. (save-restriction
  163. (widen)
  164. (goto-char (point-min))
  165. (insert vh)))
  166. (goto-char here))
  167. (setq token-end (nxml-token-before))
  168. (setq start-tag-end
  169. (save-excursion
  170. (when (and (< (point) token-end)
  171. (memq xmltok-type
  172. '(cdata-section
  173. processing-instruction
  174. comment
  175. start-tag
  176. end-tag
  177. empty-element)))
  178. (error "Point is inside a %s"
  179. (nxml-token-type-friendly-name xmltok-type)))
  180. (nxml-scan-element-backward token-end t)))
  181. (when start-tag-end
  182. (setq starts-line
  183. (save-excursion
  184. (unless (eq xmltok-type 'start-tag)
  185. (error "No matching start-tag"))
  186. (goto-char xmltok-start)
  187. (back-to-indentation)
  188. (eq (point) xmltok-start)))
  189. (setq ends-line
  190. (save-excursion
  191. (goto-char start-tag-end)
  192. (looking-at "[ \t\r\n]*$")))
  193. (setq start-tag-indent (save-excursion
  194. (goto-char xmltok-start)
  195. (current-column)))
  196. (setq qname (xmltok-start-tag-qname)))
  197. ;; Undo the insertion of the fictive header:
  198. (undo-start)
  199. (while (and (not (eq t pending-undo-list))
  200. pending-undo-list)
  201. (undo-more 1))
  202. (goto-char here))
  203. (unless start-tag-end (error "No more start tags"))
  204. (when (and starts-line ends-line)
  205. ;; start-tag is on a line by itself
  206. ;; => put the end-tag on a line by itself
  207. (unless (<= (point)
  208. (save-excursion
  209. (back-to-indentation)
  210. (point)))
  211. (insert "\n"))
  212. (indent-line-to start-tag-indent))
  213. (insert "</" qname ">")
  214. (when startp
  215. (when starts-line
  216. (insert "\n")
  217. (indent-line-to start-tag-indent))
  218. (setq inserted-start-tag-pos (point))
  219. (insert "<" qname ">")
  220. (when (and starts-line ends-line)
  221. (insert "\n")
  222. (indent-line-to (save-excursion
  223. (goto-char xmltok-start)
  224. (forward-line 1)
  225. (back-to-indentation)
  226. (if (= (current-column)
  227. (+ start-tag-indent nxml-child-indent))
  228. (+ start-tag-indent nxml-child-indent)
  229. start-tag-indent)))))
  230. inserted-start-tag-pos))
  231. (defun rngalt-complete ()
  232. "Complete the string before point using the current schema.
  233. Return non-nil if in a context it understands.
  234. This function should be added to `nxml-completion-hook' before
  235. `rng-complete'. By default it works just like this function, but
  236. you can add your own completion by setting the variables
  237. `rngalt-complete-first-try', `rngalt-completing-read-tag',
  238. `rngalt-completing-read-attribute-name',
  239. `rngalt-completing-read-attribute-value' and
  240. `rngalt-complete-last-try'."
  241. (interactive)
  242. (unless rng-validate-mode
  243. (when (y-or-n-p
  244. "XML Validation is not on. Do you want to turn it on? ")
  245. (rng-validate-mode 1)))
  246. (when rng-validate-mode
  247. ;; schema file may mismatch if user sets it explicitly:
  248. (rngalt-reapply-validation-header)
  249. (when rng-current-schema-file-name
  250. (rngalt-validate))
  251. (or (when rngalt-complete-first-try
  252. (funcall rngalt-complete-first-try))
  253. (progn
  254. (unless rng-current-schema-file-name
  255. (when (eq major-mode 'nxhtml-mode)
  256. (when (y-or-n-p
  257. "There is currently no DTD specified for the buffer.
  258. This makes XHTML completion impossible. You can add a fictive
  259. XHTML validation header that sets the DTD to XHTML. This will
  260. not be inserted in the buffer but completion and XHTML validation
  261. will assume it is there so both error checking and completion
  262. will work.
  263. Do you want to add a fictive XHTML validation header? ")
  264. (message "") ;; Get rid of the large minibuffer message window
  265. (nxhtml-validation-header-mode)
  266. )))
  267. (let ((lt-pos (save-excursion (search-backward "<" nil t)))
  268. xmltok-dtd)
  269. (or (and lt-pos
  270. (= (rng-set-state-after lt-pos) lt-pos)
  271. (or (rngalt-complete-tag lt-pos)
  272. (rng-complete-end-tag lt-pos)
  273. (rngalt-complete-attribute-name lt-pos)
  274. (rngalt-complete-attribute-value lt-pos)))
  275. (when rngalt-complete-last-try
  276. (funcall rngalt-complete-last-try))))))))
  277. (defun rngalt-validate ()
  278. (unless (= (buffer-size) 0)
  279. (let ((while-n1 0)
  280. (maxn1 20))
  281. (condition-case err
  282. (while (and (> maxn1 (setq while-n1 (1+ while-n1)))
  283. (rng-do-some-validation))
  284. nil)
  285. (error
  286. ;; FIX-ME: for debugging:
  287. ;;(lwarn 'rngalt-validate :error "%s" (error-message-string err))
  288. (message "rngalt-validate: %s" (error-message-string err))
  289. nil))
  290. (when (>= while-n1 maxn1)
  291. (error "rngalt-validate: Could not validate")))
  292. (rng-validate-done)))
  293. (defvar rngalt-region-ovl nil)
  294. (defvar rngalt-region-prepared nil)
  295. (defun rngalt-complete-tag-region-prepare ()
  296. (unless rngalt-region-prepared
  297. (when rngalt-region-ovl
  298. (when (overlayp rngalt-region-ovl)
  299. (delete-overlay rngalt-region-ovl))
  300. (setq rngalt-region-ovl nil))
  301. (when (and mark-active
  302. transient-mark-mode)
  303. (let ((beginning (region-beginning))
  304. (end (region-end)))
  305. (unless (= (point) (region-beginning))
  306. (goto-char beginning))
  307. (when (save-excursion
  308. (when (re-search-forward "\\=[^<]*\\(?:<[^<]*>\\)*[^>]*" end t)
  309. (= end (point))))
  310. (setq rngalt-region-ovl (make-overlay beginning end))
  311. (overlay-put rngalt-region-ovl 'face 'region)
  312. )))
  313. (setq rngalt-region-prepared t)))
  314. (defun rngalt-complete-tag-region-cleanup ()
  315. (when rngalt-region-prepared
  316. (when (overlayp rngalt-region-ovl)
  317. (delete-overlay rngalt-region-ovl))
  318. (deactivate-mark)
  319. (setq rngalt-region-prepared nil)))
  320. (defun rngalt-complete-tag-region-finish ()
  321. (when (and rngalt-region-prepared
  322. (overlayp rngalt-region-ovl))
  323. (let ((here (point)))
  324. (insert ">")
  325. (goto-char (overlay-end rngalt-region-ovl))
  326. (nxml-finish-element)
  327. (rngalt-validate)
  328. (goto-char here)))
  329. (rngalt-complete-tag-region-cleanup))
  330. (defun rngalt-complete-tag (lt-pos)
  331. "Like `rng-complete-tag' but with some additions.
  332. The additions are:
  333. - Alternate completion.
  334. - Complete around highlighted region.
  335. See also the variable `rngalt-completing-read-tag'."
  336. (let (rng-complete-extra-strings)
  337. (when (and (= lt-pos (1- (point)))
  338. rng-complete-end-tags-after-<
  339. rng-open-elements
  340. (not (eq (car rng-open-elements) t))
  341. (or rng-collecting-text
  342. (rng-match-save
  343. (rng-match-end-tag))))
  344. (setq rng-complete-extra-strings
  345. (cons (concat "/"
  346. (if (caar rng-open-elements)
  347. (concat (caar rng-open-elements)
  348. ":"
  349. (cdar rng-open-elements))
  350. (cdar rng-open-elements)))
  351. rng-complete-extra-strings)))
  352. (when (save-excursion
  353. (re-search-backward rng-in-start-tag-name-regex
  354. lt-pos
  355. t))
  356. (and rng-collecting-text (rng-flush-text))
  357. (rngalt-complete-tag-region-prepare)
  358. (let ((completion
  359. (let ((rng-complete-target-names
  360. (rng-match-possible-start-tag-names))
  361. (rng-complete-name-attribute-flag nil))
  362. (rngalt-complete-before-point (1+ lt-pos)
  363. 'rng-complete-qname-function
  364. "Insert tag: "
  365. nil
  366. 'rng-tag-history
  367. rngalt-completing-read-tag)))
  368. name)
  369. (when completion
  370. (cond ((rng-qname-p completion)
  371. (setq name (rng-expand-qname completion
  372. t
  373. 'rng-start-tag-expand-recover))
  374. (when (and name
  375. (rng-match-start-tag-open name)
  376. (or (not (rng-match-start-tag-close))
  377. ;; need a namespace decl on the root element
  378. (and (car name)
  379. (not rng-open-elements))))
  380. ;; attributes are required
  381. (insert " "))
  382. (rngalt-complete-tag-region-finish)
  383. (run-hook-with-args 'rngalt-complete-tag-hooks completion)
  384. )
  385. ((member completion rng-complete-extra-strings)
  386. (insert ">")))))
  387. (rngalt-complete-tag-region-finish)
  388. t)))
  389. (defvar rngalt-complete-tag-hooks nil
  390. "Hook run after completing a tag.
  391. Each function is called with the last name of the last tag
  392. completed.")
  393. (defun rngalt-complete-attribute-name (lt-pos)
  394. "Like `rng-complete-attribute-name' but with alternate completion.
  395. See the variable `rngalt-completing-read-attribute-name'."
  396. (when (save-excursion
  397. (re-search-backward rng-in-attribute-regex lt-pos t))
  398. (let ((attribute-start (match-beginning 1))
  399. rng-undeclared-prefixes)
  400. (and (rng-adjust-state-for-attribute lt-pos
  401. attribute-start)
  402. (let ((rng-complete-target-names
  403. (rng-match-possible-attribute-names))
  404. (rng-complete-extra-strings
  405. (mapcar (lambda (prefix)
  406. (if prefix
  407. (concat "xmlns:" prefix)
  408. "xmlns"))
  409. rng-undeclared-prefixes))
  410. (rng-complete-name-attribute-flag t)
  411. completion)
  412. (setq completion
  413. (rngalt-complete-before-point attribute-start
  414. 'rng-complete-qname-function
  415. "Attribute: "
  416. nil
  417. 'rng-attribute-name-history
  418. rngalt-completing-read-attribute-name))
  419. (when (and completion
  420. (< 0 (length completion)))
  421. (insert "=\"")))))
  422. t))
  423. (defun rngalt-complete-attribute-value (lt-pos)
  424. "Like `rng-complete-attribute-value' but with alternate completion.
  425. See the variable `rngalt-completing-read-attribute-value'."
  426. (when (save-excursion
  427. (re-search-backward rng-in-attribute-value-regex lt-pos t))
  428. (let ((name-start (match-beginning 1))
  429. (name-end (match-end 1))
  430. (colon (match-beginning 2))
  431. (value-start (1+ (match-beginning 3))))
  432. (and (rng-adjust-state-for-attribute lt-pos
  433. name-start)
  434. (if (string= (buffer-substring-no-properties name-start
  435. (or colon name-end))
  436. "xmlns")
  437. (rngalt-complete-before-point
  438. value-start
  439. (rng-strings-to-completion-alist
  440. (rng-possible-namespace-uris
  441. (and colon
  442. (buffer-substring-no-properties (1+ colon) name-end))))
  443. "Namespace URI: "
  444. nil
  445. 'rng-namespace-uri-history
  446. rngalt-completing-read-attribute-value) ;; fix-me
  447. (rng-adjust-state-for-attribute-value name-start
  448. colon
  449. name-end)
  450. (rngalt-complete-before-point
  451. value-start
  452. (rng-strings-to-completion-alist
  453. (rng-match-possible-value-strings))
  454. "Value: "
  455. nil
  456. 'rng-attribute-value-history
  457. rngalt-completing-read-attribute-value))
  458. (unless (eq (char-after) (char-before value-start))
  459. (insert (char-before value-start)))))
  460. t))
  461. (defun rngalt-complete-before-point (start table prompt &optional predicate hist altcompl)
  462. "Complete text between START and point.
  463. Works like `rng-complete-before-point' if ALTCOMPL is nil. When
  464. ALTCOMPL is a function symbol and no completion alternative is
  465. available from table then this is called instead of
  466. `compleating-read' with the same parameters."
  467. (let* ((orig (buffer-substring-no-properties start (point)))
  468. (completion (try-completion orig table predicate))
  469. (completing-fun (if altcompl altcompl 'completing-read))
  470. (completion-ignore-case t))
  471. (cond ((not (or completion completing-fun))
  472. (if (string= orig "")
  473. (message "No completions available")
  474. (message "No completion for %s" (rng-quote-string orig)))
  475. (ding)
  476. nil)
  477. ((eq completion t) orig)
  478. ((and completion
  479. (not (string= completion orig)))
  480. (delete-region start (point))
  481. (insert completion)
  482. (cond ((not (rng-completion-exact-p completion table predicate))
  483. (message "Incomplete")
  484. nil)
  485. ((eq (try-completion completion table predicate) t)
  486. completion)
  487. (t
  488. (message "Complete but not unique")
  489. nil)))
  490. (t
  491. (setq completion
  492. (let ((saved-minibuffer-setup-hook
  493. (default-value 'minibuffer-setup-hook)))
  494. (add-hook 'minibuffer-setup-hook
  495. 'minibuffer-completion-help
  496. t)
  497. (unwind-protect
  498. (funcall completing-fun
  499. prompt
  500. table
  501. predicate
  502. nil
  503. orig
  504. hist)
  505. (setq-default minibuffer-setup-hook
  506. saved-minibuffer-setup-hook))))
  507. (when completion
  508. (delete-region start (point))
  509. (insert completion))
  510. completion))))
  511. (defun rngalt-get-missing-required-attr (single-tag)
  512. "Get a list of missing required attributes.
  513. This is to be used when completing attribute names.
  514. SINGLE-TAG should be non-nil if the tag has no end tag.
  515. For a typical use see `nxhtml-completing-read-attribute-name' in
  516. nxhtml.el.
  517. "
  518. ;; FIX-ME: This is a terrible cludge. One day I hope I will
  519. ;; understand how to write this ;-)
  520. ;;
  521. ;; I currently fetch the missing tags from the error message in the
  522. ;; error overlay set by rng validate.
  523. (let ((here (point)))
  524. (unless (save-match-data (looking-at "[^<]\\{,200\\}>"))
  525. ;; We can probably add a >, so let us do it:
  526. (when single-tag
  527. (insert "/"))
  528. (insert ">")
  529. (rngalt-validate))
  530. (goto-char here))
  531. (let ((ovl (rng-error-overlay-message (or (rng-error-overlay-after (point))
  532. (rng-error-overlay-after (1- (point)))))))
  533. ;;(message "ovl=%s" ovl)(sit-for 1)
  534. ;;(message "prop ovl=%s" (overlay-properties ovl))(sit-for 1)
  535. (when (and ovl
  536. (eq (overlay-get ovl 'category) 'rng-error))
  537. ;;(message "rng-error")(sit-for 1)
  538. (let ((msg (overlay-get ovl 'help-echo)))
  539. ;;(message "msg=%s" msg);(sit-for 1)
  540. (when (string-match "Missing attributes? \\(.*\\)" msg)
  541. ;;(message "0=%s" (match-string 0 msg));(sit-for 1)
  542. ;;(message "1=%s" (match-string 1 msg));(sit-for 1)
  543. (let* ((matches (match-string 1 msg))
  544. (lst (split-string (substring matches 1 (- (length matches) 1)) "\", \"")))
  545. ;;(message "matches=%s" matches);(sit-for 2)
  546. ;;(message "lst=%s" lst);(sit-for 1)
  547. lst))))))
  548. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  549. ;;; Validation start state
  550. (defun rngalt-after-change-major ()
  551. (unless (and (boundp 'mumamo-set-major-running)
  552. mumamo-set-major-running)
  553. (setq rngalt-major-mode major-mode)
  554. (when (and (derived-mode-p 'nxml-mode)
  555. rngalt-validation-header)
  556. (rngalt-reapply-validation-header))
  557. (rngalt-update-validation-header-overlay)))
  558. (defvar rngalt-validation-header-keymap
  559. (let ((map (make-sparse-keymap)))
  560. (define-key map [mouse-1] 'rngalt-minimal-validation-header-toggle)
  561. map))
  562. (defun rngalt-update-validation-header-overlay ()
  563. (if (and (boundp 'rngalt-display-validation-header)
  564. rngalt-display-validation-header
  565. rngalt-validation-header
  566. (or (derived-mode-p 'nxml-mode)
  567. (let ((major-mode rngalt-major-mode))
  568. (and major-mode
  569. (derived-mode-p 'nxml-mode))))
  570. )
  571. (progn
  572. (if rngalt-validation-header-overlay
  573. (move-overlay rngalt-validation-header-overlay 1 1)
  574. (setq rngalt-validation-header-overlay (make-overlay 1 1)))
  575. (overlay-put rngalt-validation-header-overlay
  576. 'priority 1000)
  577. ;; Other properties should go to the 'before-string
  578. (let* ((validation-header (nth 2 rngalt-validation-header))
  579. (header
  580. (if rngalt-minimal-validation-header
  581. (propertize
  582. (concat
  583. "*** Fictive XHTML/XML Validation Header: ... "
  584. (save-match-data
  585. (if (string-match "\\(<[^[:space:]>]+\\)[^>]*>[^<>]*\\'"
  586. validation-header)
  587. (concat (match-string 1 validation-header) ">")
  588. "Error"))
  589. "\n")
  590. 'face 'rngalt-validation-header-bottom)
  591. (concat
  592. (propertize "*** Fictive XHTML/XML Validation Header:\n"
  593. 'face 'rngalt-validation-header-top)
  594. (propertize (concat validation-header "\n")
  595. 'face 'rngalt-validation-header-bottom)))))
  596. (setq header
  597. (propertize
  598. header
  599. 'help-echo
  600. "Click to toggle full/minimal display of header"
  601. 'keymap rngalt-validation-header-keymap))
  602. (overlay-put rngalt-validation-header-overlay
  603. 'before-string header)))
  604. (when rngalt-validation-header-overlay
  605. (delete-overlay rngalt-validation-header-overlay))))
  606. (defun rngalt-update-validation-header-overlay-everywhere ()
  607. (dolist (b (buffer-list))
  608. (when (buffer-live-p b)
  609. (with-current-buffer b
  610. (when rngalt-validation-header
  611. (rngalt-update-validation-header-overlay))))))
  612. ;; This is exactly the same as the original `rng-set-initial-state'
  613. ;; except when `rngalt-validation-header' is non-nil."
  614. (defadvice rng-set-initial-state (around
  615. rngalt-set-initial-state
  616. activate
  617. compile
  618. )
  619. (nxml-ns-init)
  620. (rng-match-start-document)
  621. (setq rng-open-elements nil)
  622. (setq rng-pending-contents nil)
  623. (when rngalt-validation-header
  624. (let ((state (car rngalt-validation-header)))
  625. (rng-restore-state state)))
  626. (setq ad-return-value (goto-char (point-min))))
  627. ;; (defun rng-new-validate-prepare ()
  628. ;; "Prepare to do some validation, initializing point and the state.
  629. ;; Return t if there is work to do, nil otherwise.
  630. ;; This is exactly the same as the original-insert-directory
  631. ;; `rng-validate-prepare' with the difference that the state at
  632. ;; point 1 is set differently if `rngalt-validation-header' is
  633. ;; non-nil.
  634. ;; See also `rng-set-initial-state'."
  635. ;; (cond ((= rng-validate-up-to-date-end 1)
  636. ;; (rng-set-initial-state)
  637. ;; t)
  638. ;; ((= rng-validate-up-to-date-end (point-max))
  639. ;; nil)
  640. ;; (t (let ((state
  641. ;; (if (and rngalt-validation-header
  642. ;; (= rng-validate-up-to-date-end 1))
  643. ;; (car rngalt-validation-header)
  644. ;; (get-text-property (1- rng-validate-up-to-date-end)
  645. ;; 'rng-state))))
  646. ;; (cond (state
  647. ;; (rng-restore-state state)
  648. ;; (goto-char rng-validate-up-to-date-end))
  649. ;; (t
  650. ;; (let ((pos (previous-single-property-change
  651. ;; rng-validate-up-to-date-end
  652. ;; 'rng-state)))
  653. ;; (cond (pos
  654. ;; (rng-restore-state
  655. ;; (or (get-text-property (1- pos) 'rng-state)
  656. ;; (error "Internal error: state null")))
  657. ;; (goto-char pos))
  658. ;; (t (rng-set-initial-state))))))))))
  659. ;; For as-external.el
  660. ;;;###autoload
  661. (defun rngalt-set-validation-header (start-of-doc)
  662. (let ((old-rvm rng-validate-mode))
  663. (when old-rvm (rng-validate-mode -1))
  664. (if start-of-doc
  665. (progn
  666. (add-hook 'after-change-major-mode-hook 'rngalt-after-change-major nil t)
  667. (setq rngalt-validation-header (rngalt-get-state-after start-of-doc))
  668. (rng-set-schema-file-1 (cadr rngalt-validation-header))
  669. (setq rngalt-current-schema-file-name rng-current-schema-file-name)
  670. (setq rng-compile-table nil)
  671. (setq rng-ipattern-table nil)
  672. (setq rng-last-ipattern-index nil))
  673. (remove-hook 'after-change-major-mode-hook 'rngalt-after-change-major t)
  674. (setq rngalt-validation-header nil)
  675. (when old-rvm
  676. (rng-set-vacuous-schema)
  677. (rng-auto-set-schema)))
  678. (when old-rvm
  679. (rng-validate-mode 1)
  680. (rngalt-update-validation-header-overlay)
  681. (rngalt-update-validation-header-buffer))))
  682. (defun rngalt-reapply-validation-header ()
  683. (when rngalt-validation-header
  684. (when (or (not rng-current-schema-file-name)
  685. (unless (string= rngalt-current-schema-file-name rng-current-schema-file-name)
  686. (lwarn 'schema-mismatch :warning
  687. "XHTML validation header schema %s reapplied (replaces %s)"
  688. (file-name-nondirectory rngalt-current-schema-file-name)
  689. (file-name-nondirectory rng-current-schema-file-name))
  690. t))
  691. (rngalt-set-validation-header (nth 2 rngalt-validation-header)))))
  692. ;; (defun rngalt-clear-validation-header ()
  693. ;; "Remove XML validation header from current buffer.
  694. ;; For more information see `rngalt-show-validation-header'."
  695. ;; (interactive)
  696. ;; (rngalt-set-validation-header nil)
  697. ;; (rng-auto-set-schema t))
  698. ;; FIX-ME: Add edit header?
  699. (defun rngalt-get-validation-header-buffer ()
  700. (let ((b (get-buffer " *XML Validation Header*")))
  701. (unless b
  702. (setq b (get-buffer-create " *XML Validation Header*"))
  703. (with-current-buffer b
  704. ;;(fundamental-mode)
  705. (nxml-mode)))
  706. b))
  707. (defun rngalt-get-state-after (start-of-doc)
  708. ;; FIX-ME: better buffer name?
  709. (let ((statebuf (rngalt-get-validation-header-buffer)))
  710. (with-current-buffer statebuf
  711. (when rng-validate-mode (rng-validate-mode -1))
  712. (erase-buffer)
  713. (insert start-of-doc)
  714. ;; From rng-get-state
  715. (setq rng-match-state nil)
  716. (setq nxml-ns-state nil)
  717. (setq rng-open-elements nil)
  718. ;; From rng-match-init-buffer
  719. (setq rng-compile-table nil)
  720. (setq rng-ipattern-table nil)
  721. (setq rng-last-ipattern-index nil)
  722. (nxml-mode)
  723. (rng-validate-mode 1)
  724. (rngalt-validate)
  725. (let* ((state (rng-get-state))
  726. (cp-state (copy-tree state)))
  727. ;;(if (equal state cp-state) (message "(equal state cp-state)=t") (message "(equal state cp-state)=nil"))
  728. ;; Fix-me: is the copy-tree necessary here?
  729. (list
  730. cp-state
  731. (rng-locate-schema-file)
  732. start-of-doc)))))
  733. (defun rngalt-show-validation-header ()
  734. "Show XML validation header used in current buffer.
  735. The XML validation header is used in `nxhtml-mode' to set a state
  736. for XML validation at the start of the buffer.
  737. The purpose is to make it possible to use `nxml-mode' completion
  738. in buffers where you do not actually have a full XML file. This
  739. could for example be a buffer with PHP code or a buffer with a
  740. blog entry.
  741. More techhnical info: This can be used by any mode derived from
  742. `nxml-mode'. To use it in other modes than `nxhtml-mode' replace
  743. `rng-complete' by `rngalt-complete' in `nxml-completion-hook'."
  744. (interactive)
  745. (unless (derived-mode-p 'nxml-mode)
  746. (error "Buffer mode is not an nXml type major mode: %s" major-mode))
  747. (rngalt-update-validation-header-buffer)
  748. (display-buffer (rngalt-get-validation-header-buffer) t))
  749. (defun rngalt-update-validation-header-buffer ()
  750. (let ((vh (nth 2 rngalt-validation-header))
  751. (cb (current-buffer)))
  752. (with-current-buffer (rngalt-get-validation-header-buffer)
  753. (erase-buffer)
  754. (if (not vh)
  755. (setq header-line-format (concat " No XML validation header in buffer "
  756. (buffer-name cb)))
  757. (insert vh)
  758. (setq header-line-format (concat " XML validation header in buffer "
  759. (buffer-name cb)))))))
  760. ;; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  761. (provide 'rngalt)
  762. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  763. ;;; rngalt.el ends here