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.
This commit is contained in:
parent
05ed68a25d
commit
6ddcb0f10f
3 changed files with 98 additions and 9 deletions
6
etc/NEWS
6
etc/NEWS
|
@ -392,8 +392,10 @@ enables reading of shell initialization files.
|
|||
** CSS mode
|
||||
|
||||
---
|
||||
*** Support for completing attribute values, at-rules, bang-rules, and
|
||||
HTML tags using the 'completion-at-point' command.
|
||||
*** Support for completing attribute values, at-rules, bang-rules,
|
||||
HTML tags, classes and IDs using the 'completion-at-point' command.
|
||||
Completion candidates for HTML classes and IDs are retrieved from open
|
||||
HTML mode buffers.
|
||||
|
||||
+++
|
||||
** Emacs now supports character name escape sequences in character and
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
;; - electric ; and }
|
||||
;; - filling code with auto-fill-mode
|
||||
;; - fix font-lock errors with multi-line selectors
|
||||
;; - support completion of user-defined classes names and IDs
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
@ -864,16 +863,46 @@ Used to provide completion of HTML tags in selectors.")
|
|||
"Non-nil if nested selectors are allowed in the current mode.")
|
||||
(make-variable-buffer-local 'css--nested-selectors-allowed)
|
||||
|
||||
;; TODO: Currently only supports completion of HTML tags. By looking
|
||||
;; at open HTML mode buffers we should be able to provide completion
|
||||
;; of user-defined classes and IDs too.
|
||||
(defvar css-class-list-function #'ignore
|
||||
"Called to provide completions of class names.
|
||||
This can be bound by buffers that are able to suggest class name
|
||||
completions, such as HTML mode buffers.")
|
||||
|
||||
(defvar css-id-list-function #'ignore
|
||||
"Called to provide completions of IDs.
|
||||
This can be bound by buffers that are able to suggest ID
|
||||
completions, such as HTML mode buffers.")
|
||||
|
||||
(defun css--foreign-completions (extractor)
|
||||
"Return a list of completions provided by other buffers.
|
||||
EXTRACTOR should be the name of a function that may be defined in
|
||||
one or more buffers. In each of the buffers where EXTRACTOR is
|
||||
defined, EXTRACTOR is called and the results are accumulated into
|
||||
a list of completions."
|
||||
(delete-dups
|
||||
(seq-mapcat
|
||||
(lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(funcall (symbol-value extractor))))
|
||||
(buffer-list))))
|
||||
|
||||
(defun css--complete-selector ()
|
||||
"Complete part of a CSS selector at point."
|
||||
(when (or (= (nth 0 (syntax-ppss)) 0) css--nested-selectors-allowed)
|
||||
(save-excursion
|
||||
(let ((end (point)))
|
||||
(let ((end (point)))
|
||||
(save-excursion
|
||||
(skip-chars-backward "-[:alnum:]")
|
||||
(list (point) end css--html-tags)))))
|
||||
(let ((start-char (char-before)))
|
||||
(list
|
||||
(point) end
|
||||
(completion-table-dynamic
|
||||
(lambda (_)
|
||||
(cond
|
||||
((eq start-char ?.)
|
||||
(css--foreign-completions 'css-class-list-function))
|
||||
((eq start-char ?#)
|
||||
(css--foreign-completions 'css-id-list-function))
|
||||
(t css--html-tags))))))))))
|
||||
|
||||
(defun css-completion-at-point ()
|
||||
"Complete current symbol at point.
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
|
||||
;;; Code:
|
||||
|
||||
(require 'dom)
|
||||
(require 'seq)
|
||||
(require 'subr-x)
|
||||
(eval-when-compile
|
||||
(require 'skeleton)
|
||||
(require 'cl-lib))
|
||||
|
@ -2168,6 +2171,55 @@ This takes effect when first loading the library.")
|
|||
nil t)
|
||||
(match-string-no-properties 1))))
|
||||
|
||||
(defvar html--buffer-classes-cache nil
|
||||
"Cache for `html-current-buffer-classes'.
|
||||
When set, this should be a cons cell where the CAR is the
|
||||
buffer's tick counter (as produced by `buffer-modified-tick'),
|
||||
and the CDR is the list of class names found in the buffer.")
|
||||
(make-variable-buffer-local 'html--buffer-classes-cache)
|
||||
|
||||
(defvar html--buffer-ids-cache nil
|
||||
"Cache for `html-current-buffer-ids'.
|
||||
When set, this should be a cons cell where the CAR is the
|
||||
buffer's tick counter (as produced by `buffer-modified-tick'),
|
||||
and the CDR is the list of class names found in the buffer.")
|
||||
(make-variable-buffer-local 'html--buffer-ids-cache)
|
||||
|
||||
(defun html-current-buffer-classes ()
|
||||
"Return a list of class names used in the current buffer.
|
||||
The result is cached in `html--buffer-classes-cache'."
|
||||
(let ((tick (buffer-modified-tick)))
|
||||
(if (eq (car html--buffer-classes-cache) tick)
|
||||
(cdr html--buffer-classes-cache)
|
||||
(let* ((dom (libxml-parse-html-region (point-min) (point-max)))
|
||||
(classes
|
||||
(seq-mapcat
|
||||
(lambda (el)
|
||||
(when-let (class-list
|
||||
(cdr (assq 'class (dom-attributes el))))
|
||||
(split-string class-list)))
|
||||
(dom-by-class dom ""))))
|
||||
(setq-local html--buffer-classes-cache (cons tick classes))
|
||||
classes))))
|
||||
|
||||
(defun html-current-buffer-ids ()
|
||||
"Return a list of IDs used in the current buffer.
|
||||
The result is cached in `html--buffer-ids-cache'."
|
||||
(let ((tick (buffer-modified-tick)))
|
||||
(if (eq (car html--buffer-ids-cache) tick)
|
||||
(cdr html--buffer-ids-cache)
|
||||
(let* ((dom
|
||||
(libxml-parse-html-region (point-min) (point-max)))
|
||||
(ids
|
||||
(seq-mapcat
|
||||
(lambda (el)
|
||||
(when-let (id-list
|
||||
(cdr (assq 'id (dom-attributes el))))
|
||||
(split-string id-list)))
|
||||
(dom-by-id dom ""))))
|
||||
(setq-local html--buffer-ids-cache (cons tick ids))
|
||||
ids))))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML")
|
||||
|
@ -2218,6 +2270,12 @@ To work around that, do:
|
|||
(setq-local add-log-current-defun-function #'html-current-defun-name)
|
||||
(setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*")
|
||||
|
||||
(when (fboundp 'libxml-parse-html-region)
|
||||
(defvar css-class-list-function)
|
||||
(setq-local css-class-list-function #'html-current-buffer-classes)
|
||||
(defvar css-id-list-function)
|
||||
(setq-local css-id-list-function #'html-current-buffer-ids))
|
||||
|
||||
(setq imenu-create-index-function 'html-imenu-index)
|
||||
|
||||
(setq-local sgml-empty-tags
|
||||
|
|
Loading…
Add table
Reference in a new issue