all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Philip Kaludercic <philipk@posteo.net>
To: "João Távora" <joaotavora@gmail.com>
Cc: Eli Zaretskii <eliz@gnu.org>,
	manuel.uberti@inventati.org, 58839@debbugs.gnu.org,
	Dmitry Gutov <dgutov@yandex.ru>
Subject: bug#58839: [Patch] Re: bug#58839: 29.0.50; project-kill-buffer fails when Eglot is running
Date: Tue, 01 Nov 2022 13:03:36 +0000	[thread overview]
Message-ID: <877d0ehlnb.fsf@posteo.net> (raw)
In-Reply-To: <CALDnm51Xv9HG06VVzMZbmHc_rG_ugpZQyFh3rsc1oK1jQcEdtA@mail.gmail.com> ("João Távora"'s message of "Tue, 1 Nov 2022 11:59:12 +0000")

João Távora <joaotavora@gmail.com> writes:

> On Tue, Nov 1, 2022 at 11:27 AM Philip Kaludercic <philipk@posteo.net>
> wrote:
>
>
>> E.g. `display-buffer-alist' makes use of it to associate display-buffer
>> rules with buffers.  Now you can add
>>
>>       ((major-mode . help-mode) display-buffer-in-side-window)
>>
>> instead of trying to match being a regular expression to catch all
>> *Help* buffer names of a function along the lines of
>>
>>       (lambda (buf _alist)
>>         (with-current-buffer buf
>>           (derived-mode-p 'help-mode)))
>>
>
> If you really want to save up on this typing, it's better to define
> a reusable helper function, or even a higher order function.
>
>   (defun buffer-mode-matcher (mode)
>     (lambda (b _alist)
>       (with-current-buffer b (derived-mode-p 'help-mode))))
>
> You can add buffer-mode-matcher to the library if it becomes
> useful enough.  Then you add:
>
>   `(,(buffer-mode-matcher 'help-mode) display-buffer-in-side-window)
>
> to display-buffer-alist.
>
> But if you really want a new language your language, then I suggest
> a simple adapter buffer-matcher utility that merges the two.  That way one
> doesn't couple existing utilities to the new mini-language and
> simultaneously
> the new mini-language become useful in a much wider setting for those who
> appreciate such things.
>
>   (defun buffer-matcher (condition)
>      "Return unary predicate of a buffer matching the CONDITION
> mini-language."
>     (lambda (buf &rest _whatever) ; make it even more lax
>        (buffer-match-p condition)))
>
> Later on, you might even pass an (... &optional compiled) so that the
> return value
> is syntax checked and optimized in some way at compile time.
>
> IOW, (E)Lisp already gives you the tools for these composition without
> needing to invent new languages with the drawbacks I listed.

I was curious to try this out, and implemented something along the lines
of your suggestion.  The bad news is that it is at least 10 times slower
than the current implementation, that isn't even really optimised.
Perhaps I did something native and didn't see what is wrong, but here
are my notes:

--8<---------------cut here---------------start------------->8---
(defun translate-buffer-condition (condition)
  "Compile a CONDITION into a predicate function."
  (pcase-exhaustive condition
    ((or 't 'nil)
     (lambda (_buffer _arg)
       condition))
    ((pred stringp)
     (lambda (buffer _arg)
       (string-match-p condition (buffer-name buffer))))
    ((pred functionp)
     (if (eq 1 (cdr (func-arity condition)))
	 (lambda (buffer _arg)
	   (funcall condition buffer))
       condition))
    (`(major-mode . ,mode)
     (lambda (buffer _arg)
       (eq
	(buffer-local-value 'major-mode buffer)
	mode)))
    (`(derived-mode . ,mode)
     (lambda (buffer _arg)
       (provided-mode-derived-p
	(buffer-local-value 'major-mode buffer)
	mode)))
    (`(not . ,cond)
     (lambda (buffer arg)
       (not (funcall (translate-buffer-condition cond) buffer arg))))
    (`(or . ,conds)
     (lambda (buffer arg)
       (catch 'match
	 (dolist (cond conds)
	   (when (funcall (translate-buffer-condition cond) buffer arg)
	     (throw 'match t))))))
    (`(and . ,conds)
     (lambda (buffer arg)
       (catch 'match
	 (dolist (cond conds t)
	   (when (funcall (translate-buffer-condition cond) buffer arg)
	     (throw 'match nil))))))))

(defvar buffer-match-p-cache (make-hash-table :test 'eq))

(defun buffer-match-p/compiled (condition buffer-or-name &optional arg)
  "Return non-nil if BUFFER-OR-NAME matches CONDITION.
CONDITION is either:
- the symbol t, to always match,
- the symbol nil, which never matches,
- a regular expression, to match a buffer name,
- a predicate function that takes a buffer object and ARG as
  arguments, and returns non-nil if the buffer matches,
- a cons-cell, where the car describes how to interpret the cdr.
  The car can be one of the following:
  * `derived-mode': the buffer matches if the buffer's major mode
    is derived from the major mode in the cons-cell's cdr.
  * `major-mode': the buffer matches if the buffer's major mode
    is eq to the cons-cell's cdr.  Prefer using `derived-mode'
    instead when both can work.
  * `not': the cdr is interpreted as a negation of a condition.
  * `and': the cdr is a list of recursive conditions, that all have
    to be met.
  * `or': the cdr is a list of recursive condition, of which at
    least one has to be met."
  (funcall (or (gethash condition buffer-match-p-cache)
	       (puthash condition
			(byte-compile (translate-buffer-condition condition))
			buffer-match-p-cache))
	   (get-buffer buffer-or-name)
	   arg))

(defun match-buffers/compiled (condition &optional buffers arg)
  "Return a list of buffers that match CONDITION.
See `buffer-match' for details on CONDITION.  By default all
buffers are checked, this can be restricted by passing an
optional argument BUFFERS, set to a list of buffers to check.
ARG is passed to `buffer-match', for predicate conditions in
CONDITION."
  (let (bufs)
    (dolist (buf (or buffers (buffer-list)))
      (when (buffer-match-p/compiled condition (get-buffer buf) arg)
        (push buf bufs)))
    bufs))

;; Here we will test a moderately complicated condition and time how
;; long it takes with the current implementation and with the proposed
;; alternative.

(defvar sample-condition
  '(and (or buffer-file-name
	    (derived-mode . compilation-mode)
	    (derived-mode . dired-mode)
	    (derived-mode . diff-mode)
	    (derived-mode . comint-mode)
	    (derived-mode . eshell-mode)
	    (derived-mode . change-log-mode))
	"\\*.+\\*"
	(not . "\\` ")))

(benchmark-run 100
  (match-buffers sample-condition pr))
;; => (1.7045469830000002 20 1.1418286690000023)


(benchmark-run 1000
  (match-buffers/compiled project-buffer-conditions pr))
;; => (17.646938126000002 219 12.428946030999999)
--8<---------------cut here---------------end--------------->8---

I guess this just goes to show that one shouldn't underestimate the cost
of a function call...

    LISP programmers know the value of everything and the cost of nothing.
         --  Alan Perlis 





  reply	other threads:[~2022-11-01 13:03 UTC|newest]

Thread overview: 86+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-28 12:56 bug#58839: 29.0.50; project-kill-buffer fails when Eglot is running Philip Kaludercic
2022-10-28 17:17 ` bug#58839: [Patch] " João Távora
2022-10-28 17:28   ` Philip Kaludercic
2022-10-28 17:36     ` João Távora
2022-10-28 18:14     ` Dmitry Gutov
2022-10-28 18:20       ` Philip Kaludercic
2022-10-28 18:30         ` João Távora
2022-10-28 18:40         ` Dmitry Gutov
2022-10-29  0:15           ` João Távora
2022-10-29  1:09             ` Dmitry Gutov
2022-10-29  1:39               ` João Távora
2022-10-29 11:27                 ` Dmitry Gutov
2022-10-29 12:16                   ` João Távora
2022-10-29 14:32                     ` Philip Kaludercic
2022-10-29 20:38                       ` João Távora
2022-10-29 22:01                         ` Philip Kaludercic
2022-10-29 22:49                           ` João Távora
2022-10-30  6:28                             ` Eli Zaretskii
2022-10-30 12:40                               ` João Távora
2022-10-30 15:58                               ` Dmitry Gutov
2022-10-30 16:39                                 ` Eli Zaretskii
2022-10-30 19:13                                   ` Dmitry Gutov
2022-10-30 19:54                                     ` Eli Zaretskii
2022-10-30 21:15                                       ` Dmitry Gutov
2022-10-31  9:53                                 ` João Távora
2022-10-31 11:56                                   ` João Távora
2022-10-31 17:11                                     ` Dmitry Gutov
2022-10-31 20:36                                       ` João Távora
2022-10-31 22:26                                         ` Dmitry Gutov
2022-10-31 22:51                                           ` João Távora
2022-10-31 14:35                                   ` Philip Kaludercic
2022-10-31 17:33                                     ` Dmitry Gutov
2022-10-31 23:19                                     ` João Távora
2022-11-01 10:51                                       ` Philip Kaludercic
2022-11-01 13:22                                       ` Dmitry Gutov
2022-11-01 13:39                                         ` João Távora
2022-10-31 17:24                                   ` Dmitry Gutov
2022-10-31 20:58                                     ` João Távora
2022-10-31 22:51                                       ` Dmitry Gutov
2022-11-01 10:48                                         ` Philip Kaludercic
2022-11-01 10:59                                           ` João Távora
2022-11-01 11:23                                             ` Dmitry Gutov
2022-11-01 11:39                                               ` João Távora
2022-11-01 15:27                                                 ` Dmitry Gutov
2022-11-01 16:23                                                   ` João Távora
2022-11-01 22:24                                                     ` Dmitry Gutov
2022-11-02  7:40                                                       ` João Távora
2022-11-01 11:27                                             ` Philip Kaludercic
2022-11-01 11:59                                               ` João Távora
2022-11-01 13:03                                                 ` Philip Kaludercic [this message]
2022-11-01 13:37                                                   ` João Távora
2022-11-01 14:00                                                     ` Philip Kaludercic
2022-11-01 14:11                                                       ` João Távora
2022-11-01 14:36                                                         ` Philip Kaludercic
2022-11-02  7:19                                                           ` João Távora
2022-11-02  7:29                                                             ` Philip Kaludercic
2022-11-02  7:48                                                               ` João Távora
2022-11-02  8:21                                                                 ` Philip Kaludercic
2022-11-02  8:41                                                                   ` João Távora
2022-11-02  9:06                                                                     ` Philip Kaludercic
2022-11-02  9:52                                                                       ` João Távora
2022-11-02 11:31                                                                         ` Philip Kaludercic
2022-11-01 15:26                                               ` Dmitry Gutov
2022-11-01 18:44                                                 ` Philip Kaludercic
2022-11-01 19:50                                                   ` Dmitry Gutov
2022-11-01 20:10                                                     ` Philip Kaludercic
2022-11-01 22:40                                                       ` Dmitry Gutov
2022-11-01 11:36                                         ` João Távora
2022-11-01 22:23                                           ` Dmitry Gutov
2022-11-02  7:34                                             ` João Távora
2022-11-02  8:36                                               ` Philip Kaludercic
2022-11-02  8:50                                                 ` João Távora
2022-11-02  9:13                                                   ` Philip Kaludercic
2022-11-02 14:00                                                     ` João Távora
2022-11-02 14:42                                                       ` Philip Kaludercic
2022-11-02 17:32                                                         ` Juri Linkov
2022-11-03 17:30                                                           ` Juri Linkov
2022-11-03 18:19                                                             ` João Távora
2022-11-02 18:16                                                         ` João Távora
2022-11-04  1:13                                           ` Dmitry Gutov
2022-11-04 11:21                                     ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-05  0:53                                       ` Dmitry Gutov
2022-10-29  6:38               ` Philip Kaludercic
2022-10-29 10:59                 ` Dmitry Gutov
2022-10-29 11:12                   ` João Távora
2022-10-29 11:05                 ` João Távora

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=877d0ehlnb.fsf@posteo.net \
    --to=philipk@posteo.net \
    --cc=58839@debbugs.gnu.org \
    --cc=dgutov@yandex.ru \
    --cc=eliz@gnu.org \
    --cc=joaotavora@gmail.com \
    --cc=manuel.uberti@inventati.org \
    /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.