From: "Simen Heggestøyl" <simenheg@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Nicolas Petton <nicolas@petton.fr>,
24389@debbugs.gnu.org, dgutov@yandex.ru
Subject: bug#24389: [PATCH] Support completion of classes and IDs in CSS mode
Date: Sat, 17 Sep 2016 09:10:52 +0200 [thread overview]
Message-ID: <1474096252.26266.1@smtp.gmail.com> (raw)
In-Reply-To: <jwv4m5nzgjm.fsf-monnier+emacsbugs@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 1603 bytes --]
Thanks again for your feedback.
On Sat, Sep 10, 2016 at 9:55 PM, Stefan Monnier
<monnier@iro.umontreal.ca> wrote:
> What'd be the benefit? You do want to `defvar' them in order to give
> them a doctring (which means that if you want them void you have to do
> extra gymnastic), and giving them a good default value means you can
> call them without having to check the value beforehand (and that also
> means you can use `add-function' on it).
Nothing, but I couldn't think of any downsides either before you
mentioned them. I see now that a `defvar' is better.
> `buffer-hash` is only a problem in large buffers. But you can use
> buffer-text-modified-tick to get a much quicker test (only important
> in
> large buffers). And you might not even need to test
> buffer-text-modified-tick because you can often just flush the
> (relevant
> part of) the cache(s) from an after-change-functions or from
> syntax-propertize or ...
OK, I changed it to use the buffer's tick counter instead. I also
changed `seq-uniq' to `delete-dups', which resulted in a massive
speedup. Some benchmarks using the same test files as before follow.
* With `seq-uniq':
(benchmark 10 '(css--foreign-completions 'css-class-list-function))
"Elapsed time: 4.198944s (0.911449s in 60 GCs)"
* With `delete-dups':
(benchmark 10 '(css--foreign-completions 'css-class-list-function))
"Elapsed time: 0.282890s (0.188205s in 10 GCs)"
As a side note, maybe a hashing strategy like the one `delete-dups'
uses would be good for `seq-uniq' too?
> You don't need the boundp test here, AFAICT.
Right, removed.
-- Simen
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Support-completion-of-classes-and-IDs-in-CSS-mode.patch --]
[-- Type: text/x-patch, Size: 7869 bytes --]
From e4aff3d72925be558f9278353d09b691edf39845 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Sat, 25 Jun 2016 15:12:09 +0200
Subject: [PATCH] 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.
---
etc/NEWS | 6 +++--
lisp/textmodes/css-mode.el | 43 +++++++++++++++++++++++++++------
lisp/textmodes/sgml-mode.el | 58 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 9 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index 9b992d0..ee49136 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -393,8 +393,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
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 4d8170e..53b3fa5 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -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 @@ css--nested-selectors-allowed
"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.
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 990c09b..43effef 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -32,6 +32,9 @@
;;; Code:
+(require 'dom)
+(require 'seq)
+(require 'subr-x)
(eval-when-compile
(require 'skeleton)
(require 'cl-lib))
@@ -2168,6 +2171,55 @@ html-current-defun-name
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))))
+
\f
;;;###autoload
(define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML")
@@ -2218,6 +2270,12 @@ html-mode
(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
--
2.9.3
next prev parent reply other threads:[~2016-09-17 7:10 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-07 17:40 bug#24389: [PATCH] Support completion of classes and IDs in CSS mode Simen Heggestøyl
2016-09-07 20:01 ` Stefan Monnier
2016-09-10 12:13 ` Simen Heggestøyl
2016-09-10 19:55 ` Stefan Monnier
2016-09-17 7:10 ` Simen Heggestøyl [this message]
2016-09-17 12:35 ` Stefan Monnier
2016-09-24 11:58 ` Simen Heggestøyl
2016-09-24 15:00 ` Nicolas Petton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1474096252.26266.1@smtp.gmail.com \
--to=simenheg@gmail.com \
--cc=24389@debbugs.gnu.org \
--cc=dgutov@yandex.ru \
--cc=monnier@iro.umontreal.ca \
--cc=nicolas@petton.fr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.