aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2002-03-28 16:06:38 +0000
committerStefan Monnier2002-03-28 16:06:38 +0000
commit1c1d2eb667186e204e220c623b8ac7ebcea65a7b (patch)
treecb7c57034fb499cca8f7bd89134b94fa125374f0
parent3f270c8a1b726f8c0348e4268648cb38704f130f (diff)
downloademacs-1c1d2eb667186e204e220c623b8ac7ebcea65a7b.tar.gz
emacs-1c1d2eb667186e204e220c623b8ac7ebcea65a7b.zip
(sgml-make-syntax-table): New fun.
(sgml-mode-syntax-table): Use it. (sgml-tag-syntax-table, sgml-tag-name-re): New const. (sgml-tags-invisible): Use it. (sgml-lexical-context): New fun. (sgml-maybe-end-tag, sgml-beginning-of-tag): Use it. (sgml-quote): Accept \n as entity reference terminator. (sgml-calculate-indent, sgml-indent-line): New funs.
-rw-r--r--lisp/textmodes/sgml-mode.el190
1 files changed, 150 insertions, 40 deletions
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index ad5df701171..415d69eb800 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -132,19 +132,31 @@ This takes effect when first loading the `sgml-mode' library.")
132 "Keymap for SGML mode. See also `sgml-specials'.") 132 "Keymap for SGML mode. See also `sgml-specials'.")
133 133
134 134
135(defvar sgml-mode-syntax-table 135(defun sgml-make-syntax-table (specials)
136 (let ((table (copy-syntax-table text-mode-syntax-table))) 136 (let ((table (make-syntax-table text-mode-syntax-table)))
137 (modify-syntax-entry ?< "(>" table) 137 (modify-syntax-entry ?< "(>" table)
138 (modify-syntax-entry ?> ")<" table) 138 (modify-syntax-entry ?> ")<" table)
139 (if (memq ?- sgml-specials) 139 (modify-syntax-entry ?: "_" table)
140 (modify-syntax-entry ?_ "_" table)
141 (modify-syntax-entry ?. "_" table)
142 (if (memq ?- specials)
140 (modify-syntax-entry ?- "_ 1234" table)) 143 (modify-syntax-entry ?- "_ 1234" table))
141 (if (memq ?\" sgml-specials) 144 (if (memq ?\" specials)
142 (modify-syntax-entry ?\" "\"\"" table)) 145 (modify-syntax-entry ?\" "\"\"" table))
143 (if (memq ?' sgml-specials) 146 (if (memq ?' specials)
144 (modify-syntax-entry ?\' "\"'" table)) 147 (modify-syntax-entry ?\' "\"'" table))
145 table) 148 table))
149
150(defvar sgml-mode-syntax-table (sgml-make-syntax-table sgml-specials)
146 "Syntax table used in SGML mode. See also `sgml-specials'.") 151 "Syntax table used in SGML mode. See also `sgml-specials'.")
147 152
153(defconst sgml-tag-syntax-table
154 (let ((table (sgml-make-syntax-table '(?- ?\" ?\'))))
155 (dolist (char '(?\( ?\) ?\{ ?\} ?\[ ?\] ?$ ?% ?& ?* ?+ ?/))
156 (modify-syntax-entry char "." table))
157 table)
158 "Syntax table used to parse SGML tags.")
159
148 160
149(defcustom sgml-name-8bit-mode nil 161(defcustom sgml-name-8bit-mode nil
150 "*When non-nil, insert non-ASCII characters as named entities." 162 "*When non-nil, insert non-ASCII characters as named entities."
@@ -225,6 +237,7 @@ separated by a space."
225 :type '(choice (const nil) integer) 237 :type '(choice (const nil) integer)
226 :group 'sgml) 238 :group 'sgml)
227 239
240(defconst sgml-tag-name-re "<\\([!/?]?[[:alpha:]][-_.:[:alnum:]]*\\)")
228(defconst sgml-start-tag-regex 241(defconst sgml-start-tag-regex
229 "<[[:alpha:]]\\([-_.:[:alnum:]= \n\t]\\|\"[^\"]*\"\\|'[^']*'\\)*" 242 "<[[:alpha:]]\\([-_.:[:alnum:]= \n\t]\\|\"[^\"]*\"\\|'[^']*'\\)*"
230 "Regular expression that matches a non-empty start tag. 243 "Regular expression that matches a non-empty start tag.
@@ -235,7 +248,7 @@ Any terminating `>' or `/' is not matched.")
235(defconst sgml-font-lock-keywords-1 248(defconst sgml-font-lock-keywords-1
236 '(("<\\([!?][[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-keyword-face) 249 '(("<\\([!?][[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-keyword-face)
237 ("<\\(/?[[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-function-name-face) 250 ("<\\(/?[[:alpha:]][-_.:[:alnum:]]*\\)" 1 font-lock-function-name-face)
238 ;; FIXME: this doesn't cover the variable using a default value. 251 ;; FIXME: this doesn't cover the variables using a default value.
239 ("\\([[:alpha:]][-_.:[:alnum:]]*\\)=[\"']" 1 font-lock-variable-name-face) 252 ("\\([[:alpha:]][-_.:[:alnum:]]*\\)=[\"']" 1 font-lock-variable-name-face)
240 ("[&%][[:alpha:]][-_.:[:alnum:]]*;?" . font-lock-variable-name-face))) 253 ("[&%][[:alpha:]][-_.:[:alnum:]]*;?" . font-lock-variable-name-face)))
241 254
@@ -634,20 +647,12 @@ With prefix argument, only self insert."
634 "No description available"))) 647 "No description available")))
635 648
636 649
637(defun sgml-maybe-end-tag () 650(defun sgml-maybe-end-tag (&optional arg)
638 "Name self unless in position to end a tag." 651 "Name self unless in position to end a tag or a prefix ARG is given."
639 (interactive) 652 (interactive "P")
640 (or (condition-case nil 653 (if (or arg (eq (car (sgml-lexical-context)) 'tag))
641 (save-excursion (up-list -1)) 654 (self-insert-command (prefix-numeric-value arg))
642 (error 655 (sgml-name-self)))
643 (sgml-name-self)
644 t))
645 (condition-case nil
646 (progn
647 (save-excursion (up-list 1))
648 (sgml-name-self))
649 (error (self-insert-command 1)))))
650
651 656
652(defun sgml-skip-tag-backward (arg) 657(defun sgml-skip-tag-backward (arg)
653 "Skip to beginning of tag or matching opening tag if present. 658 "Skip to beginning of tag or matching opening tag if present.
@@ -769,8 +774,7 @@ With prefix argument ARG, repeat this ARG times."
769 (if arg 774 (if arg
770 (>= (prefix-numeric-value arg) 0) 775 (>= (prefix-numeric-value arg) 0)
771 (not sgml-tags-invisible))) 776 (not sgml-tags-invisible)))
772 (while (re-search-forward "<\\([!/?]?[[:alpha:]][-_.:[:alnum:]]*\\)" 777 (while (re-search-forward sgml-tag-name-re nil t)
773 nil t)
774 (setq string 778 (setq string
775 (cdr (assq (intern-soft (downcase (match-string 1))) 779 (cdr (assq (intern-soft (downcase (match-string 1)))
776 sgml-display-text))) 780 sgml-display-text)))
@@ -829,24 +833,49 @@ and move to the line in the SGML document that caused it."
829 (compile-internal command "No more errors")) 833 (compile-internal command "No more errors"))
830 834
831 835
836(defun sgml-lexical-context (&optional limit)
837 "Return the lexical context at point as (TYPE . START).
838START is the location of the start of the lexical element.
839TYPE is one of `string', `comment', `tag', `cdata', ....
840Return nil if we are inside text (i.e. outside of any kind of tag).
841
842If non-nil LIMIT is a nearby position before point outside of any tag."
843 ;; As usual, it's difficult to get a reliable answer without parsing the
844 ;; whole buffer. We'll assume that a tag at indentation is outside of
845 ;; any string or tag or comment or ...
846 (save-excursion
847 (let ((pos (point))
848 (state nil))
849 ;; Hopefully this regexp will match something that's not inside
850 ;; a tag and also hopefully the match is nearby.
851 (when (or (and limit (goto-char limit))
852 (re-search-backward "^[ \t]*<" nil t))
853 (with-syntax-table sgml-tag-syntax-table
854 (while (< (point) pos)
855 ;; When entering this loop we're inside text.
856 (skip-chars-forward "^<" pos)
857 ;; We skipped text and reached a tag. Parse it.
858 ;; FIXME: this does not handle CDATA and funny stuff yet.
859 (setq state (parse-partial-sexp (point) pos 0)))
860 (cond
861 ((nth 3 state) (cons 'string (nth 8 state)))
862 ((nth 4 state) (cons 'comment (nth 8 state)))
863 ((and state (> (nth 0 state) 0)) (cons 'tag (nth 1 state)))
864 (t nil)))))))
865
832(defun sgml-beginning-of-tag (&optional top-level) 866(defun sgml-beginning-of-tag (&optional top-level)
833 "Skip to beginning of tag and return its name. 867 "Skip to beginning of tag and return its name.
834If this can't be done, return t." 868If this can't be done, return nil."
835 (or (if top-level 869 (let ((context (sgml-lexical-context)))
836 (condition-case nil 870 (if (eq (car context) 'tag)
837 (up-list -1) 871 (progn
838 (error t)) 872 (goto-char (cdr context))
839 (>= (point) 873 (when (looking-at sgml-tag-name-re)
840 (if (search-backward "<" nil t) 874 (match-string-no-properties 1)))
841 (save-excursion 875 (if top-level nil
842 (forward-list) 876 (when context
843 (point)) 877 (goto-char (cdr context))
844 0))) 878 (sgml-beginning-of-tag t))))))
845 (if (looking-at "<[!/?]?[[:alpha:]][-_.:[:alnum:]]*")
846 (buffer-substring-no-properties
847 (1+ (point))
848 (match-end 0))
849 t)))
850 879
851(defun sgml-value (alist) 880(defun sgml-value (alist)
852 "Interactively insert value taken from attributerule ALIST. 881 "Interactively insert value taken from attributerule ALIST.
@@ -875,7 +904,7 @@ With prefix argument, unquote the region."
875 (goto-char end) 904 (goto-char end)
876 (setq end start)) 905 (setq end start))
877 (if unquotep 906 (if unquotep
878 (while (re-search-forward "&\\(amp\\|\\(l\\|\\(g\\)\\)t\\);" end t) 907 (while (re-search-forward "&\\(amp\\|\\(l\\|\\(g\\)\\)t\\)[;\n]" end t)
879 (replace-match (if (match-end 3) ">" (if (match-end 2) "<" "&")))) 908 (replace-match (if (match-end 3) ">" (if (match-end 2) "<" "&"))))
880 (while (re-search-forward "[&<>]" end t) 909 (while (re-search-forward "[&<>]" end t)
881 (replace-match (cdr (assq (char-before) '((?& . "&amp;") 910 (replace-match (cdr (assq (char-before) '((?& . "&amp;")
@@ -883,6 +912,87 @@ With prefix argument, unquote the region."
883 (?> . "&gt;")))))))) 912 (?> . "&gt;"))))))))
884 913
885 914
915(defun sgml-calculate-indent ()
916 "Calculate the column to which this line should be indented."
917 (let ((lcon (sgml-lexical-context)))
918 ;; Indent comment-start markers inside <!-- just like comment-end markers.
919 (if (and (eq (car lcon) 'tag)
920 (looking-at "--")
921 (save-excursion (goto-char (cdr lcon)) (looking-at "<!--")))
922 (setq lcon (cons 'comment (+ (cdr lcon) 2))))
923
924 (case (car lcon)
925 (string
926 ;; Go back to previous non-empty line.
927 (while (and (> (point) (cdr lcon))
928 (zerop (forward-line -1))
929 (looking-at "[ \t]*$")))
930 (if (> (point) (cdr lcon))
931 ;; Previous line is inside the string.
932 (current-indentation)
933 (goto-char (cdr lcon))
934 (1+ (current-column))))
935
936 (comment
937 (let ((mark (looking-at "--")))
938 ;; Go back to previous non-empty line.
939 (while (and (> (point) (cdr lcon))
940 (zerop (forward-line -1))
941 (or (looking-at "[ \t]*$")
942 (if mark (not (looking-at "[ \t]*--"))))))
943 (if (> (point) (cdr lcon))
944 ;; Previous line is inside the comment.
945 (skip-chars-forward " \t")
946 (goto-char (cdr lcon)))
947 (when (and (not mark) (looking-at "--"))
948 (forward-char 2) (skip-chars-forward " \t"))
949 (current-column)))
950
951 (tag
952 (goto-char (1+ (cdr lcon)))
953 (skip-chars-forward "^ \t\n") ;Skip tag name.
954 (skip-chars-forward " \t")
955 (if (not (eolp))
956 (current-column)
957 ;; This is the first attribute: indent.
958 (goto-char (1+ (cdr lcon)))
959 (+ (current-column) sgml-basic-offset)))
960
961 (t
962 (while (looking-at "</")
963 (forward-sexp 1)
964 (skip-chars-forward " \t"))
965 (let ((context (xml-lite-get-context)))
966 (cond
967 ((null context) 0) ; no context
968 ;; Align closing tag with the opening one.
969 ;; ((and (eq (length context) 1) (looking-at "</"))
970 ;; (goto-char (xml-lite-tag-start (car context)))
971 ;; (current-column))
972 (t
973 (let ((here (point)))
974 (goto-char (xml-lite-tag-end (car context)))
975 (skip-chars-forward " \t\n")
976 (if (< (point) here)
977 (current-column)
978 (goto-char (xml-lite-tag-start (car context)))
979 (+ (current-column) sgml-basic-offset))))))))))
980
981(defun sgml-indent-line ()
982 "Indent the current line as SGML."
983 (interactive)
984 (let* ((savep (point))
985 (indent-col
986 (save-excursion
987 (beginning-of-line)
988 (skip-chars-forward " \t")
989 (if (>= (point) savep) (setq savep nil))
990 ;; calculate basic indent
991 (sgml-calculate-indent))))
992 (if savep
993 (save-excursion (indent-line-to indent-col))
994 (indent-line-to indent-col))))
995
886;;; HTML mode 996;;; HTML mode
887 997
888(defcustom html-mode-hook nil 998(defcustom html-mode-hook nil