X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/eadf313c72edcc3a11b9d03032699416efebfe1a..595195a10e5dd568bf249f5fb6778ae3d7037cd5:/lisp/textmodes/sgml-mode.el diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el index 12d98c8238..990c09bfda 100644 --- a/lisp/textmodes/sgml-mode.el +++ b/lisp/textmodes/sgml-mode.el @@ -1,6 +1,6 @@ -;;; sgml-mode.el --- SGML- and HTML-editing modes -*- coding: utf-8 -*- +;;; sgml-mode.el --- SGML- and HTML-editing modes -*- lexical-binding:t -*- -;; Copyright (C) 1992, 1995-1996, 1998, 2001-2015 Free Software +;; Copyright (C) 1992, 1995-1996, 1998, 2001-2016 Free Software ;; Foundation, Inc. ;; Author: James Clark @@ -46,6 +46,25 @@ :type 'integer :group 'sgml) +(defcustom sgml-attribute-offset 0 + "Specifies a delta for attribute indentation in `sgml-indent-line'. + +When 0, attribute indentation looks like this: + + + + +When 2, attribute indentation looks like this: + + + " + :version "25.1" + :type 'integer + :safe 'integerp + :group 'sgml) + (defcustom sgml-xml-mode nil "When non-nil, tag insertion functions will be XML-compliant. It is set to be buffer-local when the file has @@ -87,10 +106,10 @@ This list is used when first loading the `sgml-mode' library. The supported characters and potential disadvantages are: ?\\\" Makes \" in text start a string. - ?' Makes ' in text start a string. + ?\\=' Makes \\=' in text start a string. ?- Makes -- in text start a comment. -When only one of ?\\\" or ?' are included, \"'\" or '\"', as can be found in +When only one of ?\\\" or ?\\=' are included, \"\\='\" or \\='\"\\=', as can be found in DTDs, start a string. To partially avoid this problem this also makes these self insert as named entities depending on `sgml-quick-keys'. @@ -423,7 +442,7 @@ an optional alist of possible values." (comment-style 'plain)) (comment-indent-new-line soft))) -(defun sgml-mode-facemenu-add-face-function (face end) +(defun sgml-mode-facemenu-add-face-function (face _end) (let ((tag-face (cdr (assq face sgml-face-tag-alist)))) (cond (tag-face (setq tag-face (funcall skeleton-transformation-function tag-face)) @@ -463,14 +482,14 @@ This function is designed for use in `fill-nobreak-predicate'. (define-derived-mode sgml-mode text-mode '(sgml-xml-mode "XML" "SGML") "Major mode for editing SGML documents. Makes > match <. -Keys <, &, SPC within <>, \", / and ' can be electric depending on +Keys <, &, SPC within <>, \", / and \\=' can be electric depending on `sgml-quick-keys'. An argument of N to a tag-inserting command means to wrap it around the next N words. In Transient Mark mode, when the mark is active, N defaults to -1, which means to wrap it around the current region. -If you like upcased tags, put (setq sgml-transformation-function 'upcase) +If you like upcased tags, put (setq sgml-transformation-function \\='upcase) in your init file. Use \\[sgml-validate] to validate your document with an SGML parser. @@ -506,7 +525,7 @@ Do \\[describe-key] on the following bindings to discover what they do. ;; This is desirable because SGML discards a newline that appears ;; immediately after a start tag or immediately before an end tag. (setq-local paragraph-start (concat "[ \t]*$\\|\ -\[ \t]*")) +[ \t]*")) (setq-local paragraph-separate (concat paragraph-start "$")) (setq-local adaptive-fill-regexp "[ \t]*") (add-hook 'fill-nobreak-predicate 'sgml-fill-nobreak nil t) @@ -678,7 +697,7 @@ This only works for Latin-1 input." "Prompt for a tag and insert it, optionally with attributes. Completion and configuration are done according to `sgml-tag-alist'. If you like tags and attributes in uppercase, customize -`sgml-transformation-function' to 'upcase." +`sgml-transformation-function' to `upcase'." (funcall (or skeleton-transformation-function 'identity) (setq sgml-tag-last (completing-read @@ -740,9 +759,10 @@ If QUIET, do not print a message when there are no attributes for TAG." (insert ?\s) (insert (funcall skeleton-transformation-function (setq attribute - (skeleton-read '(completing-read - "Attribute: " - alist))))) + (skeleton-read (lambda () + (completing-read + "Attribute: " + alist)))))) (if (string= "" attribute) (setq i 0) (sgml-value (assoc (downcase attribute) alist)) @@ -822,10 +842,29 @@ Return non-nil if we skipped over matched tags." (setq arg (1- arg))) return)) +(defun sgml-forward-sexp (n) + ;; This function is needed in major-modes such as nxml-mode where + ;; forward-sexp-function is used to give a more dwimish behavior to + ;; the `forward-sexp' command. + ;; Without it, we can end up with backtraces like: + ;; "get-text-property" (0xffffc0f0) + ;; "nxml-token-after" (0xffffc2ac) + ;; "nxml-forward-single-balanced-item" (0xffffc46c) + ;; "nxml-forward-balanced-item" (0xffffc61c) + ;; "forward-sexp" (0xffffc7f8) + ;; "sgml-parse-tag-backward" (0xffffc9c8) + ;; "sgml-lexical-context" (0xffffcba8) + ;; "sgml-mode-flyspell-verify" (0xffffcd74) + ;; "flyspell-word" (0xffffcf3c) + ;; "flyspell-post-command-hook" (0xffffd108) + ;; FIXME: should we also set the sgml-tag-syntax-table? + (let ((forward-sexp-function nil)) + (forward-sexp n))) + (defvar sgml-electric-tag-pair-overlays nil) (defvar sgml-electric-tag-pair-timer nil) -(defun sgml-electric-tag-pair-before-change-function (beg end) +(defun sgml-electric-tag-pair-before-change-function (_beg end) (condition-case err (save-excursion (goto-char end) @@ -842,11 +881,12 @@ Return non-nil if we skipped over matched tags." (if endp (when (sgml-skip-tag-backward 1) (forward-char 1) t) (with-syntax-table sgml-tag-syntax-table - (up-list -1) - (when (sgml-skip-tag-forward 1) - (backward-sexp 1) - (forward-char 2) - t)))) + (let ((forward-sexp-function nil)) + (up-list -1) + (when (sgml-skip-tag-forward 1) + (backward-sexp 1) + (forward-char 2) + t))))) (clones (get-char-property (point) 'text-clones))) (when (and match (/= cl-end cl-start) @@ -993,7 +1033,7 @@ With prefix argument ARG, repeat this ARG times." (or (get 'sgml-tag 'invisible) (setplist 'sgml-tag (append '(invisible t - point-entered sgml-point-entered + cursor-sensor-functions (sgml-cursor-sensor) rear-nonsticky t read-only t) (symbol-plist 'sgml-tag)))) @@ -1001,63 +1041,59 @@ With prefix argument ARG, repeat this ARG times." (defun sgml-tags-invisible (arg) "Toggle visibility of existing tags." (interactive "P") - (let ((modified (buffer-modified-p)) - (inhibit-read-only t) - (inhibit-modification-hooks t) - ;; Avoid spurious the `file-locked' checks. - (buffer-file-name nil) - ;; This is needed in case font lock gets called, - ;; since it moves point and might call sgml-point-entered. - ;; How could it get called? -stef - (inhibit-point-motion-hooks t) + (let ((inhibit-read-only t) string) - (unwind-protect - (save-excursion - (goto-char (point-min)) - (if (setq-local sgml-tags-invisible - (if arg - (>= (prefix-numeric-value arg) 0) - (not sgml-tags-invisible))) - (while (re-search-forward sgml-tag-name-re nil t) - (setq string - (cdr (assq (intern-soft (downcase (match-string 1))) - sgml-display-text))) - (goto-char (match-beginning 0)) - (and (stringp string) - (not (overlays-at (point))) - (let ((ol (make-overlay (point) (match-beginning 1)))) - (overlay-put ol 'before-string string) - (overlay-put ol 'sgml-tag t))) - (put-text-property (point) - (progn (forward-list) (point)) - 'category 'sgml-tag)) - (let ((pos (point-min))) - (while (< (setq pos (next-overlay-change pos)) (point-max)) - (dolist (ol (overlays-at pos)) - (if (overlay-get ol 'sgml-tag) - (delete-overlay ol))))) - (remove-text-properties (point-min) (point-max) '(category nil)))) - (restore-buffer-modified-p modified)) + (with-silent-modifications + (save-excursion + (goto-char (point-min)) + (if (setq-local sgml-tags-invisible + (if arg + (>= (prefix-numeric-value arg) 0) + (not sgml-tags-invisible))) + (while (re-search-forward sgml-tag-name-re nil t) + (setq string + (cdr (assq (intern-soft (downcase (match-string 1))) + sgml-display-text))) + (goto-char (match-beginning 0)) + (and (stringp string) + (not (overlays-at (point))) + (let ((ol (make-overlay (point) (match-beginning 1)))) + (overlay-put ol 'before-string string) + (overlay-put ol 'sgml-tag t))) + (put-text-property (point) + (progn (forward-list) (point)) + 'category 'sgml-tag)) + (let ((pos (point-min))) + (while (< (setq pos (next-overlay-change pos)) (point-max)) + (dolist (ol (overlays-at pos)) + (if (overlay-get ol 'sgml-tag) + (delete-overlay ol))))) + (remove-text-properties (point-min) (point-max) '(category nil))))) + (cursor-sensor-mode (if sgml-tags-invisible 1 -1)) (run-hooks 'sgml-tags-invisible-hook) (message ""))) -(defun sgml-point-entered (x y) - ;; Show preceding or following hidden tag, depending of cursor direction. - (let ((inhibit-point-motion-hooks t)) - (save-excursion - (condition-case nil - (message "Invisible tag: %s" - ;; Strip properties, otherwise, the text is invisible. - (buffer-substring-no-properties - (point) - (if (or (and (> x y) - (not (eq (following-char) ?<))) - (and (< x y) - (eq (preceding-char) ?>))) - (backward-list) - (forward-list)))) - (error nil))))) - +(defun sgml-cursor-sensor (window x dir) + ;; Show preceding or following hidden tag, depending of cursor direction (and + ;; `dir' is not the direction in this sense). + (when (eq dir 'entered) + (ignore-errors + (let* ((y (window-point window)) + (otherend + (save-excursion + (goto-char y) + (cond + ((and (eq (char-before) ?>) + (or (not (eq (char-after) ?<)) + (> x y))) + (sgml-forward-sexp -1)) + ((eq (char-after y) ?<) + (sgml-forward-sexp 1))) + (point)))) + (message "Invisible tag: %s" + ;; Strip properties, otherwise, the text is invisible. + (buffer-substring-no-properties + y otherend)))))) (defun sgml-validate (command) @@ -1139,7 +1175,7 @@ If nil, start from a preceding tag at indentation." ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state))) (t (cons 'text text-start)))))) -(defun sgml-beginning-of-tag (&optional top-level) +(defun sgml-beginning-of-tag (&optional only-immediate) "Skip to beginning of tag and return its name. If this can't be done, return nil." (let ((context (sgml-lexical-context))) @@ -1148,7 +1184,7 @@ If this can't be done, return nil." (goto-char (cdr context)) (when (looking-at sgml-tag-name-re) (match-string-no-properties 1))) - (if top-level nil + (if only-immediate nil (when (not (eq (car context) 'text)) (goto-char (cdr context)) (sgml-beginning-of-tag t)))))) @@ -1162,13 +1198,16 @@ See `sgml-tag-alist' for info about attribute rules." (if (and (eq (car alist) t) (not sgml-xml-mode)) (when (cdr alist) (insert "=\"") - (setq alist (skeleton-read '(completing-read "Value: " (cdr alist)))) + (setq alist (skeleton-read (lambda () + (completing-read + "Value: " (cdr alist))))) (if (string< "" alist) (insert alist ?\") (delete-char -2))) (insert "=\"") (if (cdr alist) - (insert (skeleton-read '(completing-read "Value: " alist))) + (insert (skeleton-read (lambda () + (completing-read "Value: " alist)))) (when (null alist) (insert (skeleton-read '(read-string "Value: "))))) (insert ?\")))) @@ -1216,7 +1255,7 @@ You might want to turn on `auto-fill-mode' to get better results." (unless (or ;;(looking-at " isn't really the end of a tag. Skip it. (goto-char (1- tag-end)) @@ -1510,18 +1549,18 @@ LCON is the lexical context, if any." (`pi nil) (`tag - (goto-char (1+ (cdr lcon))) + (goto-char (+ (cdr lcon) sgml-attribute-offset)) (skip-chars-forward "^ \t\n") ;Skip tag name. (skip-chars-forward " \t") (if (not (eolp)) (current-column) ;; This is the first attribute: indent. - (goto-char (1+ (cdr lcon))) + (goto-char (+ (cdr lcon) sgml-attribute-offset)) (+ (current-column) sgml-basic-offset))) (`text (while (looking-at "") + ;; (re-search-backward "