aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimen Heggestøyl2016-09-24 13:55:36 +0200
committerSimen Heggestøyl2016-09-24 13:55:36 +0200
commit6ddcb0f10fb2b3c6c6a31733b28f7fbb30637ac2 (patch)
tree0090d6f3b5b0a1d94d19d103d59c664886469144
parent05ed68a25d3c81cc20314c42a43aeb23d6c2d8f1 (diff)
downloademacs-6ddcb0f10fb2b3c6c6a31733b28f7fbb30637ac2.tar.gz
emacs-6ddcb0f10fb2b3c6c6a31733b28f7fbb30637ac2.zip
Support completion of classes and IDs in CSS mode
* lisp/textmodes/css-mode.el (css-class-list-function): New variable holding the function to call for retrieving completions of class names. (css-id-list-function): New variable holding the function to call for retrieving completions of IDs. (css--foreign-completions): New function for retrieving completions from other buffers. (css--complete-selector): Support completing HTML class names and IDs from other buffers in addition to completing HTML tags. * lisp/textmodes/sgml-mode.el (html--buffer-classes-cache): New variable holding a cache for `html-current-buffer-classes'. (html--buffer-ids-cache): New variable holding a cache for `html-current-buffer-ids'. (html-current-buffer-classes): New function returning a list of class names used in the current buffer. (html-current-buffer-ids): New function returning a list of IDs used in the current buffer. (html-mode): Set `css-class-list-function' and `css-id-list-function' to `html-current-buffer-classes' and `html-current-buffer-ids' respectively.
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/textmodes/css-mode.el43
-rw-r--r--lisp/textmodes/sgml-mode.el58
3 files changed, 98 insertions, 9 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 3149d918b56..c3f4cf01b26 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -392,8 +392,10 @@ enables reading of shell initialization files.
392** CSS mode 392** CSS mode
393 393
394--- 394---
395*** Support for completing attribute values, at-rules, bang-rules, and 395*** Support for completing attribute values, at-rules, bang-rules,
396HTML tags using the 'completion-at-point' command. 396HTML tags, classes and IDs using the 'completion-at-point' command.
397Completion candidates for HTML classes and IDs are retrieved from open
398HTML mode buffers.
397 399
398+++ 400+++
399** Emacs now supports character name escape sequences in character and 401** Emacs now supports character name escape sequences in character and
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 4d8170eda06..53b3fa55c6b 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -30,7 +30,6 @@
30;; - electric ; and } 30;; - electric ; and }
31;; - filling code with auto-fill-mode 31;; - filling code with auto-fill-mode
32;; - fix font-lock errors with multi-line selectors 32;; - fix font-lock errors with multi-line selectors
33;; - support completion of user-defined classes names and IDs
34 33
35;;; Code: 34;;; Code:
36 35
@@ -864,16 +863,46 @@ Used to provide completion of HTML tags in selectors.")
864 "Non-nil if nested selectors are allowed in the current mode.") 863 "Non-nil if nested selectors are allowed in the current mode.")
865(make-variable-buffer-local 'css--nested-selectors-allowed) 864(make-variable-buffer-local 'css--nested-selectors-allowed)
866 865
867;; TODO: Currently only supports completion of HTML tags. By looking 866(defvar css-class-list-function #'ignore
868;; at open HTML mode buffers we should be able to provide completion 867 "Called to provide completions of class names.
869;; of user-defined classes and IDs too. 868This can be bound by buffers that are able to suggest class name
869completions, such as HTML mode buffers.")
870
871(defvar css-id-list-function #'ignore
872 "Called to provide completions of IDs.
873This can be bound by buffers that are able to suggest ID
874completions, such as HTML mode buffers.")
875
876(defun css--foreign-completions (extractor)
877 "Return a list of completions provided by other buffers.
878EXTRACTOR should be the name of a function that may be defined in
879one or more buffers. In each of the buffers where EXTRACTOR is
880defined, EXTRACTOR is called and the results are accumulated into
881a list of completions."
882 (delete-dups
883 (seq-mapcat
884 (lambda (buf)
885 (with-current-buffer buf
886 (funcall (symbol-value extractor))))
887 (buffer-list))))
888
870(defun css--complete-selector () 889(defun css--complete-selector ()
871 "Complete part of a CSS selector at point." 890 "Complete part of a CSS selector at point."
872 (when (or (= (nth 0 (syntax-ppss)) 0) css--nested-selectors-allowed) 891 (when (or (= (nth 0 (syntax-ppss)) 0) css--nested-selectors-allowed)
873 (save-excursion 892 (let ((end (point)))
874 (let ((end (point))) 893 (save-excursion
875 (skip-chars-backward "-[:alnum:]") 894 (skip-chars-backward "-[:alnum:]")
876 (list (point) end css--html-tags))))) 895 (let ((start-char (char-before)))
896 (list
897 (point) end
898 (completion-table-dynamic
899 (lambda (_)
900 (cond
901 ((eq start-char ?.)
902 (css--foreign-completions 'css-class-list-function))
903 ((eq start-char ?#)
904 (css--foreign-completions 'css-id-list-function))
905 (t css--html-tags))))))))))
877 906
878(defun css-completion-at-point () 907(defun css-completion-at-point ()
879 "Complete current symbol at point. 908 "Complete current symbol at point.
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 990c09bfda7..43effefdecd 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -32,6 +32,9 @@
32 32
33;;; Code: 33;;; Code:
34 34
35(require 'dom)
36(require 'seq)
37(require 'subr-x)
35(eval-when-compile 38(eval-when-compile
36 (require 'skeleton) 39 (require 'skeleton)
37 (require 'cl-lib)) 40 (require 'cl-lib))
@@ -2168,6 +2171,55 @@ This takes effect when first loading the library.")
2168 nil t) 2171 nil t)
2169 (match-string-no-properties 1)))) 2172 (match-string-no-properties 1))))
2170 2173
2174(defvar html--buffer-classes-cache nil
2175 "Cache for `html-current-buffer-classes'.
2176When set, this should be a cons cell where the CAR is the
2177buffer's tick counter (as produced by `buffer-modified-tick'),
2178and the CDR is the list of class names found in the buffer.")
2179(make-variable-buffer-local 'html--buffer-classes-cache)
2180
2181(defvar html--buffer-ids-cache nil
2182 "Cache for `html-current-buffer-ids'.
2183When set, this should be a cons cell where the CAR is the
2184buffer's tick counter (as produced by `buffer-modified-tick'),
2185and the CDR is the list of class names found in the buffer.")
2186(make-variable-buffer-local 'html--buffer-ids-cache)
2187
2188(defun html-current-buffer-classes ()
2189 "Return a list of class names used in the current buffer.
2190The result is cached in `html--buffer-classes-cache'."
2191 (let ((tick (buffer-modified-tick)))
2192 (if (eq (car html--buffer-classes-cache) tick)
2193 (cdr html--buffer-classes-cache)
2194 (let* ((dom (libxml-parse-html-region (point-min) (point-max)))
2195 (classes
2196 (seq-mapcat
2197 (lambda (el)
2198 (when-let (class-list
2199 (cdr (assq 'class (dom-attributes el))))
2200 (split-string class-list)))
2201 (dom-by-class dom ""))))
2202 (setq-local html--buffer-classes-cache (cons tick classes))
2203 classes))))
2204
2205(defun html-current-buffer-ids ()
2206 "Return a list of IDs used in the current buffer.
2207The result is cached in `html--buffer-ids-cache'."
2208 (let ((tick (buffer-modified-tick)))
2209 (if (eq (car html--buffer-ids-cache) tick)
2210 (cdr html--buffer-ids-cache)
2211 (let* ((dom
2212 (libxml-parse-html-region (point-min) (point-max)))
2213 (ids
2214 (seq-mapcat
2215 (lambda (el)
2216 (when-let (id-list
2217 (cdr (assq 'id (dom-attributes el))))
2218 (split-string id-list)))
2219 (dom-by-id dom ""))))
2220 (setq-local html--buffer-ids-cache (cons tick ids))
2221 ids))))
2222
2171 2223
2172;;;###autoload 2224;;;###autoload
2173(define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML") 2225(define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML")
@@ -2218,6 +2270,12 @@ To work around that, do:
2218 (setq-local add-log-current-defun-function #'html-current-defun-name) 2270 (setq-local add-log-current-defun-function #'html-current-defun-name)
2219 (setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*") 2271 (setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*")
2220 2272
2273 (when (fboundp 'libxml-parse-html-region)
2274 (defvar css-class-list-function)
2275 (setq-local css-class-list-function #'html-current-buffer-classes)
2276 (defvar css-id-list-function)
2277 (setq-local css-id-list-function #'html-current-buffer-ids))
2278
2221 (setq imenu-create-index-function 'html-imenu-index) 2279 (setq imenu-create-index-function 'html-imenu-index)
2222 2280
2223 (setq-local sgml-empty-tags 2281 (setq-local sgml-empty-tags