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, 10 Sep 2016 14:13:13 +0200 Message-ID: <1473509593.26636.0@smtp.gmail.com> References: <1473270025.21515.0@smtp.gmail.com> <1473270025.21515.0@smtp.gmail.com> <"Simen Heggest=?UTF-8?Q?=C3=B8yl"'s_message_of_"Wed, _07_Sep_2016_19:40:25_+0200"> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-/3IEwuAES9zgbF6MxpRn" X-Trace: blaine.gmane.org 1473509668 22547 195.159.176.226 (10 Sep 2016 12:14:28 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 10 Sep 2016 12:14:28 +0000 (UTC) Cc: 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 10 14:14:24 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 1bihAk-0004F2-KQ for geb-bug-gnu-emacs@m.gmane.org; Sat, 10 Sep 2016 14:14:14 +0200 Original-Received: from localhost ([::1]:34367 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bihAi-0004Ow-GX for geb-bug-gnu-emacs@m.gmane.org; Sat, 10 Sep 2016 08:14:12 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:38637) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bihAc-0004Oq-KS for bug-gnu-emacs@gnu.org; Sat, 10 Sep 2016 08:14:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bihAY-0002RY-CK for bug-gnu-emacs@gnu.org; Sat, 10 Sep 2016 08:14:05 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:57468) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bihAY-0002RU-7c for bug-gnu-emacs@gnu.org; Sat, 10 Sep 2016 08:14:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1bihAX-0002n9-RP for bug-gnu-emacs@gnu.org; Sat, 10 Sep 2016 08:14:01 -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, 10 Sep 2016 12:14:01 +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.147350960410678 (code B ref 24389); Sat, 10 Sep 2016 12:14:01 +0000 Original-Received: (at 24389) by debbugs.gnu.org; 10 Sep 2016 12:13:24 +0000 Original-Received: from localhost ([127.0.0.1]:55180 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bih9w-0002m9-Be for submit@debbugs.gnu.org; Sat, 10 Sep 2016 08:13:24 -0400 Original-Received: from mail-lf0-f52.google.com ([209.85.215.52]:34160) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bih9u-0002lt-Cl for 24389@debbugs.gnu.org; Sat, 10 Sep 2016 08:13:23 -0400 Original-Received: by mail-lf0-f52.google.com with SMTP id u14so60586746lfd.1 for <24389@debbugs.gnu.org>; Sat, 10 Sep 2016 05:13:22 -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=u0CAoDpNVMsckROzdXiIn7vHqwc9/1KxyI74Ea7Hmtc=; b=PTJzNlmq9ckwnrZ0OUy6o+gdVS4FEpv94ZCuydPkhoAc3X+tPFt/esuxyHauavxWb6 Dd054jN5EIwLqaMexBKS6q76GFChv5fWwH5kivFxEE3qpRPbzHr5vEW2vugHmbIUTVAa G1nb3QilPTOqXiUt4YP4t1UNv0byP2qkfebALOXZU2OrSzbWWn6Igqvbarj5qZvTJjh3 BO17xx9h5S0Ck84OPz+F25vR7EQO7SpIR+JoA77v7SaK2WBqYBCKTnAOUe3zDcJfhqOu velDN8GCG9sJNTMfoDwT/QhLjZnLabBtK0fz+eo9yWi5UUOOYjMyfzgln4Svh72xRrsv 6x8A== 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=u0CAoDpNVMsckROzdXiIn7vHqwc9/1KxyI74Ea7Hmtc=; b=EHW/vPZb5omP32a54mMZnDeKYMzXNr14Fi4blE1Bi+jnumPLwPR+BPxur7g8TUZ+MV 8dn+/byK5pRJJpV1Z02z8nIvOTQZ5lLRrGfAZfRtHuDmehU0G4VKoK1eMMlLoejW5H0D 3N5laMDkiP63EM6ZTKS68K3g5NZAtDw6ia1icY/MHfbDNhCYiUckq4YHNpqHfYu7e6Cj Z5w/GWiVd1JvkDqSy+toZzGcdcVhLoDgOijrc7q6cK75psTaMm2FvlUfknEF9G0ABAER HqK6QV87QJdAmzpRr3Gdxv5X1MUqHORTeu1YgSO9z82eoX+3n9ZyXTX1BjpEzapDbI5Q AtfQ== X-Gm-Message-State: AE9vXwO6iRCclEvuFNPyH5Zi3hmCL6v3wobv+xBzsRd/yXTCgNY0GyOVlVcbrMRQ2xaoag== X-Received: by 10.46.1.85 with SMTP id 82mr2540831ljb.8.1473509596104; Sat, 10 Sep 2016 05:13:16 -0700 (PDT) Original-Received: from [192.168.100.10] (cm-84.210.143.4.getinternet.no. [84.210.143.4]) by smtp.gmail.com with ESMTPSA id f71sm1466249lfg.36.2016.09.10.05.13.14 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sat, 10 Sep 2016 05:13:15 -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:123143 Archived-At: --=-/3IEwuAES9zgbF6MxpRn Content-Type: text/plain; charset=utf-8; format=flowed Hi Stefan, thanks for the feedback. On Wed, Sep 7, 2016 at 10:01 PM, Stefan Monnier wrote: > I don't see where this is used. Oops, that was left over from an earlier revision. > I urge you to use alists rather than plist whenever you can. I only > recommend plists for those rare cases where they're likely to often be > written down by end-users who'd be bothered by the need to add dots > and parentheses. Noted, thanks. > I think this design won't work well. How 'bout instead just always > calling the function and let the other side handle the caching? > > This way the cache can work correctly even in case the list of > completions depends on sources other than the buffer's content (a case > which even your costly buffer-hash won't catch). Also, it means that > depending on the cost of the extraction itself, the cache may be > flushed > more or less eagerly. True. Considering the things you've mentioned, I also think it will be much better to let the other side (optionally) handle their own cache. Please see the revised patch. > The definition of those global variables seems to be missing. > IIUC they should be in css-mode.el, so their name should start with > "css-" I think (tho I agree that it's debatable). Hm, is it wrong to let them stay void, and then check whether they're bound in each buffer we visit? I ran som benchmarks to assess whether the expensive buffer-hash cache is worth it, results follow below. All the tests were run with 583 HTML mode buffers open, with content totaling 7.5 MB. * No cache: (benchmark 1 '(css--foreign-completions 'css-class-list-function)) "Elapsed time: 1.597629s (0.797973s in 55 GCs)" (benchmark 10 '(css--foreign-completions 'css-class-list-function)) "Elapsed time: 15.322130s (7.589323s in 549 GCs)" * Cold cache: (benchmark 1 '(css--foreign-completions 'css-class-list-function)) "Elapsed time: 1.609650s (0.786968s in 54 GCs)" * Warm cache: (benchmark 1 '(css--foreign-completions 'css-class-list-function)) "Elapsed time: 0.471924s (0.102814s in 6 GCs)" (benchmark 10 '(css--foreign-completions 'css-class-list-function)) "Elapsed time: 4.376787s (0.981541s in 59 GCs)" -- Simen --=-/3IEwuAES9zgbF6MxpRn 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 bcb7d93d69bb8b536baffbe7558a7e89181c9c5b 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 | 44 ++++++++++++++++++++++++++++------ lisp/textmodes/sgml-mode.el | 58 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 82eb2b8..2c1ce3b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -386,8 +386,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..4149a95 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,47 @@ 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." + (seq-uniq + (seq-mapcat + (lambda (buf) + (with-current-buffer buf + (when (boundp extractor) + (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..68ee7d0 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 +hash as produced by `buffer-hash', 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 +hash as produced by `buffer-hash', and the CDR is the list of IDs +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 ((hash (buffer-hash))) + (if (equal (car html--buffer-classes-cache) hash) + (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 hash 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 ((hash (buffer-hash))) + (if (equal (car html--buffer-ids-cache) hash) + (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 hash 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 = --=-/3IEwuAES9zgbF6MxpRn--