unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#24389: [PATCH] Support completion of classes and IDs in CSS mode
@ 2016-09-07 17:40 Simen Heggestøyl
  2016-09-07 20:01 ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Simen Heggestøyl @ 2016-09-07 17:40 UTC (permalink / raw)
  To: 24389; +Cc: Stefan Monnier, Dmitry Gutov

[-- Attachment #1: Type: text/plain, Size: 580 bytes --]

Hello.

I've got a seemingly working version of completion of HTML class names
and IDs in CSS mode buffers.

The idea is that CSS mode asks other open buffers if they are able to
produce completion candidates for HTML classes and IDs by checking
whether the functions `html-class-extractor-function' or
`html-id-extractor-function' are bound. I've included implementations
for these in HTML mode using libxml. My hope is that other modes, such
as Web mode [1], will be be able to implement their own extractor
functions.

What do you think?

-- Simen


[1] http://web-mode.org/



[-- 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: 6458 bytes --]

From 45503ade03b277e2f66f2259b0436aa35a162bf2 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--complete-html-tag): New function
for completing HTML tags, which was previously the responsibility of
`css--complete-selector'.
(css--foreign-completion-cache): New variable holding a cache for
completions provided by other buffers.
(css--foreign-completions): New function for retrieving completions
from other buffers.
(css--complete-selector): Support completing HTML IDs and classes from
other buffers in addition to completing HTML tags.

* lisp/textmodes/sgml-mode.el (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-class-extractor-function): New variable holding the function to
use for class name extraction.
(html-id-extractor-function): New variable holding the function to use
for ID extraction.
(html-mode): Set `html-class-extractor-function' and
`html-id-extractor-function' to `html-current-buffer-classes' and
`html-current-buffer-ids' respectively.
---
 lisp/textmodes/css-mode.el  | 56 +++++++++++++++++++++++++++++++++++++++------
 lisp/textmodes/sgml-mode.el | 29 +++++++++++++++++++++++
 2 files changed, 78 insertions(+), 7 deletions(-)

diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 4d8170e..20f3ff2 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,59 @@ 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.
+(defun css--complete-html-tag ()
+  "Complete HTML tag at point."
+  (save-excursion
+    (let ((end (point)))
+      (skip-chars-backward "-[:alnum:]")
+      (list (point) end css--html-tags))))
+
+(defvar css--foreign-completion-cache
+  (list 'html-class-extractor-function (make-hash-table :test 'equal)
+        'html-id-extractor-function (make-hash-table :test 'equal))
+  "Cache of completions provided by other buffers.
+This is a property list where each property is the name of an
+extractor function and the associated value is a hash table
+serving as a cache for that function.")
+
+(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."
+  (seq-uniq
+   (seq-mapcat
+    (lambda (buf)
+      (with-current-buffer buf
+        (when (boundp extractor)
+          (let ((cache
+                 (plist-get css--foreign-completion-cache extractor)))
+            (if cache
+                (let ((hash (buffer-hash buf)))
+                  (or (gethash hash cache)
+                      (puthash hash (funcall (symbol-value extractor))
+                               cache)))
+              (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
+                                    'html-class-extractor-function))
+               ((eq start-char ?#) (css--foreign-completions
+                                    'html-id-extractor-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..5e943de 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,27 @@ html-current-defun-name
 	 nil t)
 	(match-string-no-properties 1))))
 
+(defun html-current-buffer-classes ()
+  "Return a list of class names used in the current buffer."
+  (let ((dom (libxml-parse-html-region (point-min) (point-max))))
+    (seq-mapcat
+     (lambda (el)
+       (when-let (class-list (cdr (assq 'class (dom-attributes el))))
+         (split-string class-list)))
+     (dom-by-class dom ""))))
+
+(defun html-current-buffer-ids ()
+  "Return a list of IDs used in the current buffer."
+  (let ((dom (libxml-parse-html-region (point-min) (point-max))))
+    (seq-mapcat
+     (lambda (el)
+       (when-let (id-list (cdr (assq 'id (dom-attributes el))))
+         (split-string id-list)))
+     (dom-by-id dom ""))))
+
+(defvar html-class-extractor-function)
+(defvar html-id-extractor-function)
+
 \f
 ;;;###autoload
 (define-derived-mode html-mode sgml-mode '(sgml-xml-mode "XHTML" "HTML")
@@ -2218,6 +2242,11 @@ html-mode
   (setq-local add-log-current-defun-function #'html-current-defun-name)
   (setq-local sentence-end-base "[.?!][]\"'”)}]*\\(<[^>]*>\\)*")
 
+  (when (fboundp 'libxml-parse-html-region)
+    (setq-local html-class-extractor-function
+                #'html-current-buffer-classes)
+    (setq-local html-id-extractor-function #'html-current-buffer-ids))
+
   (setq imenu-create-index-function 'html-imenu-index)
 
   (setq-local sgml-empty-tags
-- 
2.9.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2016-09-24 15:00 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
2016-09-17 12:35         ` Stefan Monnier
2016-09-24 11:58           ` Simen Heggestøyl
2016-09-24 15:00         ` Nicolas Petton

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).