emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Speed up project-kill-buffers


From: Stephen Leake
Subject: Re: [PATCH] Speed up project-kill-buffers
Date: Wed, 19 May 2021 16:37:25 -0700
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (windows-nt)

Dmitry Gutov <dgutov@yandex.ru> writes:

> On 16.05.2021 23:36, Stephen Leake wrote:
>> Which means you need to provide an option for the times when we want the
>> other behavior: C-u kills root and external-roots, plain kills root only.
>
> I wouldn't mind adding an option, but that presents a difficulty.
>
> At the moment, we call `project-current` in each buffer and compare
> the returned value to the "current" project instance. That wouldn't
> work for external roots no matter what option we add because a given
> file inside "external root" can belong (in an "extended" sense) to
> several projects at once, so there's no logical way to obtain the
> project instance we're looking for based on such external file. Right?

There is in my projects; the currently active global project. Which
means that any buffer will appear to "belong" to that project, unless
project-find-functions is buffer-local, as it is in my elisp buffers.

I view project-current as returning an object useful for project-related
commands. So in a "notes" text file, where I keep track of things to do
for a project, I want project-find-file to use that project (which is
the current globally selected project). project-delete-buffers should
delete that notes file. But in a higher level notes file, opened on
Emacs start, that lists all the projects I'm currently working on,
project-find-file should also use the current global project, but that
buffer should not be deleted with the project.

> A generic like project-contains? 

That's one option, with the current predicate in project--buffer-list as
the default implementation; then I could override that to check
project-files, or all roots, or something. As long as it passes C-u to
the backend, my function could also use that to decide about dependent
libraries.

It might be better to make the predicate more specific;
project-delete-this-buffer-p. That way eglot won't try to abuse
project-contains to pick a server :). Or maybe my delete buffer case
and eglot's "choose the right server" case really are the same?

> I previously mentioned would have a problem that every project
> backend's implementation would have to obey such an option, which
> creates new possibility for bugs and diverging behaviors.

Yes. Things should be as simple as possible, but no simpler.

A default that is useful in many simple projects helps a lot.

> If the current project-kill-buffers doesn't work for you (please
> check; IIRC your backend's peculiar behavior might have an upside in
> this case), 

First, M-x project-kill-buffers dies because my definition of
project-root returns nil. I suppose you'd call that a bug in my project
backend, so I'll pick some arbitrary directory (essentially (car
compilation-search-path)) and call it the "primary root".

Then the prompt is:

    Kill 17 buffers in d:/apps/gnat-gpl_2020/lib/gnat? (yes or no)

That directory is something you would call an external root, I guess, so
you would argue I should pick a different one. But I have lived with
project-root returning nil for several years now, so I don't see why I
should be forced to put effort into choosing some "better" root, just so
project-kill-buffers can detect a remote project. I'd be happy if that
remote project predicate treated nil as "not remote".

In any case, 17 buffers is far too many; there are only three buffers
open related to the current project. (length (buffer-list)) is 24, so at
least it's not deleting everything.

Hmm. I just found project-kill-buffer-conditions; maybe I could
customize that; it allows arbitrary predicate functions. It would be
more efficient if the predicate function were also passed the project
object, so it doesn't have to call project-current again (hmm - shades
of project-delete-this-buffer-p :).

A better prompt would be the name of the project; the ELPA package wisi
defines a name for each project, specified by the user as part of the
project definition. In this case, it is "ada_mode_wisi_parse stephe_6",
which is much more meaningfull to me, and more meaningfull than some
"better" "primary root" directory. In fact, I have two different
projects that share the directory of test files (one for compiling the
parser, one for running the parser tests), so a directory name does not
uniquely identify a project. Similarly, there are elisp and Ada files in
the ada-mode source directory; the elisp files use the elisp project
(named "elisp"), the Ada (and all other) files use the
"ada_mode_wisi_parse stephe_6" project.

I think another generic project-name, or possbly project-prompt, would
be good here. The default could be project-root.    

There should also be a custom boolean to turn off the prompt; it's
helpful the first few times, then it's just annoying. Currently C-u is
'no-confirm'; I hope that will change to 'no-dependencies'. Normal Emacs
style uses a custom variable for suppressing prompts.

Even better would to define the key ? at that prompt to put up a list of
the buffer names; that way the user could learn to trust the command (I
certainly don't trust it now :). I suspect that would be very hard to do
(certainly yes-or-no-p can't do that now). Instead, the custom variable
that turns off the prompt could be tri-valued; nil (no prompt), t (short
prompt), 'verbose (list all buffers, with a hint on how to change the
prompt behavior). Then 'verbose should be the default. dired has similar
behavior when deleting one vs several files.

> what we could more easily do is create an option like
> 'project-list-buffers-function'. The default would delegate to
> project-current. 

I don't see how that is better/easier than a cl-defgeneric; either way
the project has to specify the correct function, possibly on a
per-project basis. Either way you have to provide a useful default
implementation. And they provide the same power to encounter/create
bugs; they are both dispatching methods. I think it's better to stick to
one method of dispatching. In my case, the wisi package will provide the
function.

On the other hand, it appears we already have dispatching via
project-kill-buffer-conditions; I'll see if I can make that work. I'm
not sure I can reliably detect C-u from there for dependent libraries.

I already have a function similar to 'project-kill-buffers', defined in
my vc package; it kills buffers whose files are under a root directory.
That solves the question of whether to delete buffers in dependent
libraries, at the expense of requiring the user to invoke it several
times in order to fully clean up. So a correct solution for
project-kill-buffers, with a simple invocation-time option for dependent
libraries, would be nice.

-- 
-- Stephe



reply via email to

[Prev in Thread] Current Thread [Next in Thread]