diff options
| author | Simen Heggestøyl | 2016-09-24 13:55:36 +0200 |
|---|---|---|
| committer | Simen Heggestøyl | 2016-09-24 13:55:36 +0200 |
| commit | 6ddcb0f10fb2b3c6c6a31733b28f7fbb30637ac2 (patch) | |
| tree | 0090d6f3b5b0a1d94d19d103d59c664886469144 | |
| parent | 05ed68a25d3c81cc20314c42a43aeb23d6c2d8f1 (diff) | |
| download | emacs-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/NEWS | 6 | ||||
| -rw-r--r-- | lisp/textmodes/css-mode.el | 43 | ||||
| -rw-r--r-- | lisp/textmodes/sgml-mode.el | 58 |
3 files changed, 98 insertions, 9 deletions
| @@ -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, |
| 396 | HTML tags using the 'completion-at-point' command. | 396 | HTML tags, classes and IDs using the 'completion-at-point' command. |
| 397 | Completion candidates for HTML classes and IDs are retrieved from open | ||
| 398 | HTML 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. | 868 | This can be bound by buffers that are able to suggest class name |
| 869 | completions, such as HTML mode buffers.") | ||
| 870 | |||
| 871 | (defvar css-id-list-function #'ignore | ||
| 872 | "Called to provide completions of IDs. | ||
| 873 | This can be bound by buffers that are able to suggest ID | ||
| 874 | completions, such as HTML mode buffers.") | ||
| 875 | |||
| 876 | (defun css--foreign-completions (extractor) | ||
| 877 | "Return a list of completions provided by other buffers. | ||
| 878 | EXTRACTOR should be the name of a function that may be defined in | ||
| 879 | one or more buffers. In each of the buffers where EXTRACTOR is | ||
| 880 | defined, EXTRACTOR is called and the results are accumulated into | ||
| 881 | a 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'. | ||
| 2176 | When set, this should be a cons cell where the CAR is the | ||
| 2177 | buffer's tick counter (as produced by `buffer-modified-tick'), | ||
| 2178 | and 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'. | ||
| 2183 | When set, this should be a cons cell where the CAR is the | ||
| 2184 | buffer's tick counter (as produced by `buffer-modified-tick'), | ||
| 2185 | and 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. | ||
| 2190 | The 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. | ||
| 2207 | The 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 |