Subject: [PATCH] Support completion of HTML tags in CSS selectors

From: Simen Heggestøyl <[email-masked]>
Date: Thu, 05 May 2016 14:32:45 +0200

The attached patch adds support for completion of HTML tags in CSS
selectors for CSS- and SCSS mode.

-- Simen
>From 4af469d4a67c020eef90e3c19ca9094bf7b59696 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <[email-masked]>
Date: Sun, 1 May 2016 14:59:11 +0200
Subject: [PATCH] Support completion of HTML tags in CSS selectors

* lisp/textmodes/css-mode.el (css--complete-property): Make completion
non-exclusive to allow mixing with selector completions.
(css--html-tags): New variable holding a list of HTML tags for
completion.
(css--nested-selectors-allowed): New variable for determining whether
nested selectors are allowed in the current mode.
(css--complete-selector): New function for completing part of a CSS
selector.
(css-completion-at-point): Removed in favor of adding several
completion functions to `completion-at-point-functions'.
(css-mode): Add each completion function to
`completion-at-point-functions' instead of using
`css-completion-at-point'.
(scss-mode): Allow nested selectors.
---
 etc/NEWS                   |  4 ++--
 lisp/textmodes/css-mode.el | 41 +++++++++++++++++++++++++++++------------
 2 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 21602ff..e202612 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -282,8 +282,8 @@ different group ID.
 ** CSS mode
 
 ---
-*** Support for completing attribute values and bang-rules using the
-'completion-at-point' command.
+*** Support for completing attribute values, at-rules, bang-rules, and
+HTML tags using the 'completion-at-point' command.
 
 +++
 ** 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 e30fb3e..77e2e77 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -30,10 +30,12 @@
 ;; - 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:
 
 (require 'seq)
+(require 'sgml-mode)
 (require 'smie)
 
 (defgroup css nil
@@ -747,7 +749,7 @@ css--complete-property
       (let ((start (point)))
         (skip-chars-backward " \t\r\n")
         (when (memq (char-before) '(?\{ ?\;))
-          (list start pos css-property-ids))))))
+          (list start pos css-property-ids :exclusive 'no))))))
 
 (defun css--complete-bang-rule ()
   "Complete bang-rule at point."
@@ -824,15 +826,24 @@ css--complete-property-value
           (list (point) end
                 (cons "inherit" (css--property-values property))))))))
 
-(defun css-completion-at-point ()
-  "Complete current symbol at point.
-Currently supports completion of CSS properties, property values,
-pseudo-elements, pseudo-classes, at-rules, and bang-rules."
-  (or (css--complete-property)
-      (css--complete-bang-rule)
-      (css--complete-property-value)
-      (css--complete-pseudo-element-or-class)
-      (css--complete-at-rule)))
+(defvar css--html-tags (mapcar #'car html-tag-alist)
+  "List of HTML tags.
+Used to provide completion of HTML tags in selectors.")
+
+(defvar css--nested-selectors-allowed nil
+  "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-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)))
+        (skip-chars-backward "-[:alnum:]")
+        (list (point) end css--html-tags)))))
 
 ;;;###autoload
 (define-derived-mode css-mode prog-mode "CSS"
@@ -852,8 +863,13 @@ css-mode
               :backward-token #'css-smie--backward-token)
   (setq-local electric-indent-chars
               (append css-electric-keys electric-indent-chars))
-  (add-hook 'completion-at-point-functions
-            #'css-completion-at-point nil 'local))
+  (dolist (func (list #'css--complete-bang-rule
+                      #'css--complete-property-value
+                      #'css--complete-pseudo-element-or-class
+                      #'css--complete-at-rule
+                      #'css--complete-property
+                      #'css--complete-selector))
+    (add-hook 'completion-at-point-functions func t 'local)))
 
 (defvar comment-continue)
 
@@ -990,6 +1006,7 @@ scss-mode
   (setq-local comment-end-skip "[ \t]*\\(?:\n\\|\\*+/\\)")
   (setq-local css--at-ids (append css-at-ids scss-at-ids))
   (setq-local css--bang-ids (append css-bang-ids scss-bang-ids))
+  (setq-local css--nested-selectors-allowed t)
   (setq-local font-lock-defaults
               (list (scss-font-lock-keywords) nil t)))
 
-- 
2.8.1