From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Newsgroups: gmane.emacs.bugs Subject: bug#24389: [PATCH] Support completion of classes and IDs in CSS mode Date: Sat, 17 Sep 2016 09:10:52 +0200 Message-ID: <1474096252.26266.1@smtp.gmail.com> References: <1473270025.21515.0@smtp.gmail.com> <=?UTF-8?Q?1473270025.21515.0@smtp.gmail.com> <1473509593.26636.0@smtp.gmail.com> <1473509593.26636.0@smtp.gmail.com> <"Simen Heggest=?UTF-8?Q?=C3=B8yl"'s_message_of_"Sat, _10_Sep_2016_14:13:13_+0200"> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-kigjVUi/DS7RpuswlymL" X-Trace: blaine.gmane.org 1474096351 3863 195.159.176.226 (17 Sep 2016 07:12:31 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 17 Sep 2016 07:12:31 +0000 (UTC) Cc: Nicolas Petton , 24389@debbugs.gnu.org, dgutov@yandex.ru To: Stefan Monnier Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sat Sep 17 09:12:26 2016 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bl9nP-0007sE-5K for geb-bug-gnu-emacs@m.gmane.org; Sat, 17 Sep 2016 09:12:19 +0200 Original-Received: from localhost ([::1]:45039 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bl9nN-0000F7-8u for geb-bug-gnu-emacs@m.gmane.org; Sat, 17 Sep 2016 03:12:17 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:52268) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bl9nC-0000E9-PY for bug-gnu-emacs@gnu.org; Sat, 17 Sep 2016 03:12:12 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bl9n8-00063N-Fh for bug-gnu-emacs@gnu.org; Sat, 17 Sep 2016 03:12:05 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:35972) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bl9n8-00063A-BI for bug-gnu-emacs@gnu.org; Sat, 17 Sep 2016 03:12:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1bl9n8-0008E2-4U for bug-gnu-emacs@gnu.org; Sat, 17 Sep 2016 03:12:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Simen =?UTF-8?Q?Heggest=C3=B8yl?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 17 Sep 2016 07:12:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 24389 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 24389-submit@debbugs.gnu.org id=B24389.147409626331526 (code B ref 24389); Sat, 17 Sep 2016 07:12:02 +0000 Original-Received: (at 24389) by debbugs.gnu.org; 17 Sep 2016 07:11:03 +0000 Original-Received: from localhost ([127.0.0.1]:33684 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bl9mA-0008CP-Tk for submit@debbugs.gnu.org; Sat, 17 Sep 2016 03:11:03 -0400 Original-Received: from mail-lf0-f42.google.com ([209.85.215.42]:36769) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bl9m9-0008Br-2E for 24389@debbugs.gnu.org; Sat, 17 Sep 2016 03:11:01 -0400 Original-Received: by mail-lf0-f42.google.com with SMTP id g62so75407364lfe.3 for <24389@debbugs.gnu.org>; Sat, 17 Sep 2016 00:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:subject:to:cc:message-id:in-reply-to:references :mime-version; bh=1LcRRpobgsWdtwktoysR7JlSxAKALsDdUBGZZVkgCjQ=; b=NwUOYUZuPxdm55o+puDDRpoQcH0VB7Imcyh31+Tu4C/lMoexN/oFnO10JwdliRj9wd 7HqVPR0czMNw9F4l9MWk7Q1f0YMc3zaOMv4jLlIHCOopecH0M5kegA354FBXC1OzH4li 3m9yTcdWS4NAbcil7QZ5oe/wjFYW+qUIP1wZpMG4J31zkLtiHzs4YYwYEI/fMS1eGTWy Wqgk/hZFBfa3hfAeMSWYV/BTL0KbC+tyjpg2tkthiERwJgcffUwiQvKNV0vpDDVnfiQQ f/UXdt91njdrrC/+kDVq1U6OXk8bnUj51FK5TlHUzj/glLo8TIg7N6GDJIvuWnyrzVYW wk0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:subject:to:cc:message-id:in-reply-to :references:mime-version; bh=1LcRRpobgsWdtwktoysR7JlSxAKALsDdUBGZZVkgCjQ=; b=CR/u6z5h8+PrahrkBWn6C4v7tUrDOg0Bj1E3FKZitK4L3AQjARYQw7MUU0BMs7jyik CQ9fnOGuwLkrC6VROSalVJHTAOFFP2g+xZ2vrFh5TOlTNdiniScqtxAIKT2iXzOEPtev eFTZZIXWR/i5x2DiA1vv1FpNe0ROZ9nrDM0+po2vq/GZ1zmEWEMLecfpxnzX+zIGVmkk JmFmoRB2Yoexhhw/w3sr78+43Ru4lyc4Ba/U98RZWtmg7oQNLMa6uSdpPGjLcwu5I1ps wkzD6XXTGyxzcRGIKQAtVF5xAQSdf4HnSTruRMZJAjwyDb1nW+Xx3jFvqqcSL+cixkG8 DRtQ== X-Gm-Message-State: AE9vXwOWqpfVejAJJZl1BtRb2katxfWT09KWEVKDr64jOZs1AXn5k7rJJkJhuuw8A0ZwmA== X-Received: by 10.25.190.201 with SMTP id o192mr2063240lff.23.1474096255024; Sat, 17 Sep 2016 00:10:55 -0700 (PDT) Original-Received: from [10.0.0.38] (ti0117a400-5592.bb.online.no. [85.166.148.235]) by smtp.gmail.com with ESMTPSA id e1sm3013408lfe.46.2016.09.17.00.10.53 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 17 Sep 2016 00:10:54 -0700 (PDT) In-Reply-To: X-Mailer: geary/0.11.2 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:123393 Archived-At: --=-kigjVUi/DS7RpuswlymL Content-Type: text/plain; charset=utf-8; format=flowed Thanks again for your feedback. On Sat, Sep 10, 2016 at 9:55 PM, Stefan Monnier 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 --=-kigjVUi/DS7RpuswlymL Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Support-completion-of-classes-and-IDs-in-CSS-mode.patch Content-Transfer-Encoding: quoted-printable >From e4aff3d72925be558f9278353d09b691edf39845 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Simen=3D20Heggest=3DC3=3DB8yl?=3D 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 =20 --- -*** 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. =20 +++ ** 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 =20 ;;; Code: =20 @@ -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) =20 -;; 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 (=3D (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)))))))))) =20 (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 @@ =20 ;;; Code: =20 +(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)))) =20 +(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)))) + =0C ;;;###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 "[.?!][]\"'=E2=80=9D)}]*\\(<[^>]*>\\)*") =20 + (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) =20 (setq-local sgml-empty-tags --=20 2.9.3 = --=-kigjVUi/DS7RpuswlymL--