emacs-devel
[Top][All Lists]
Advanced

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

Re: display-buffer-alist simplifications


From: Stefan Monnier
Subject: Re: display-buffer-alist simplifications
Date: Thu, 04 Aug 2011 14:16:43 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.50 (gnu/linux)

>> I'm not yet concerned about the ease of customizability.  I'm concerned
>> about the complexity of the semantics.
> When we are concerned about frightening inexperienced users we should be
> concerned about the ease of customizability just as about anything else.

But I'm not concerned about either of them.  I first want to have
something that's simple and powerful, and then I'll worry about making
in usable via customize.

>> And I'm concerned about the ease with which someone will be able to
>> replace display-buffer-alist with something better 10 years from now
>> while preserving the compatibility with your (then legacy) code.
> `display-buffer-alist' has no built-in dependencies wrt `display-buffer'
> like the Emacs 23 buffer display options had.

I don't understand what you're saying here.

> So you would have to tell me why you're concerned: Is it the number of
> options the variable offers or the "inheritance" rules?

Some mix of those.  The number in and of itself is not necessarily
problematic, except that there's a lot of coupling between different
kinds of elements, so it's hard to split things.

>> Ah, so that's what labels are for, now that makes more sense.  But then
>> `switch-to-buffer' should not be a label.  The labels I want are about
>> the "kind of buffer", not "the command used to display it".  The reason
>> for such a label is that buffer-names aren't good enough.
>> Maybe we can rely on major and minor modes instead, BTW.
> We can rely on anything here.  And we can omit them.

First we need to remove the switch-to-buffer' label (and other similar
labels, if any).

>> No.  Any reason it's not in the trunk yet?
> Yes.  Whenever I start writing the ChangeLog I have to think about
> changing the design of `display-buffer-alist'.

Fair enough.

>> I had the window-pub moved to old-branches because I thought it had
>> been merged.
> It had been merged but for the manual and the NEWS entries.

Makes sense, actually.

> It contains rationales for atomic windows and side window.  It's more
> difficult to describe rationales for the remaining features.

Hmm...

>> Note that I generally try to design things to be simpler and more
>> expressive (I don't see a contradiction here, because the
>> expressiveness is obtained by adding a few "hooks" where random elisp
>> code can be plugged).
> We have to care for people who are not able or not willing to write such
> random code.

Of course, but that's trivial to do so: just provide a set of functions
they can use, with clear enough names.  They end up working just as well
as constants like `reuse-window' (they can even look identical in
Customize thanks to :tag).

Note also that there are many more people able to write Elisp code than
people who are able to combine various specifiers to get
a specific behavior.  And Elisp code is supported by various tools like
eldoc, edebug, ...

>> - the same-frame and the same-window parameters are one needed
>> complexity.
> These parameters are ill-suited for a number of reasons:

I'm not saying that their Emacs-23 shape and implementation is needed.
I'm saying that the corresponding functionality is needed.
Obviously you agree, you even provided a lot more fine-grained control
over such things in your code.

> (1) They are presented as special cases within a context of
>     "Alternatively, an element of this list can be specified as
>     \(BUFFER-NAME FRAME-PARAMETERS), where BUFFER-NAME is a buffer ...".
>     Calling them frame parameters is misleading.

Yes the name "frame parameter" is not right any more.  The doc reflects
only the original design which only considered "pop up a frame" as the
possible behaviors.

> (2) They are interpreted exclusively by a function ironically called
>     `special-display-popup-frame'.

Yes, same problem of naming and documentation as above.

>     If the buffer is already displayed in some window, these options
>     are ignored by that function.

That's often the right thing to do, so it's just a lack of flexibility
in the old code.

> (3) Users devising their own `special-display-function' have to ignore
>     them or write their own code to handle them.

I don't know of any such user, so I don't worry about them too much.

>> But these should be consolidated into a single `where'
>> parameter which could specify same-frame, same-window, and many other
>> things (e.g. near-minibuffer).
> This should happen at one level above that handled by
> `display-buffer-alist', for example, via macro specifiers.

We already have 2 ways to name particular values (via either their value
or their function cell), so hopefully we can use one of the two as
"macro specifier" without having to introduce this new concept and its
attending code.

>> - I don't see how you can specify "reusing a window on another frame" in
>> special-display-buffer-names.
> But it is done by `special-display-popup-frame' when it finds such a
> window on a visible frame.  And you can turn that off and on by using
> either another function via the \(BUFFER-NAME FUNCTION OTHER-ARGS) or
> another `special-display-function'.

So you're saying that the FUNCTION hook lets you specify endless things
including "reusing a window on another frame".  Yes, that's true.
And the great thing about it is that it's all done by just one simple
hook: there is no special code designed to see whether the user
specified "reusing a window on another frame" or not.

>> So I don't see the enormous complexity.  It's basically just:
>> - pop up a new frame and mark it dedicated.
>> - except when `where' says otherwise.
>> - and except for the hook case (where you can say "use this function to
>> display the buffer").
>> The hook case gives you a lot of expressiveness since you get complete
>> control, but it adds very little complexity to the design.

> The complexity is built into the code of `special-display-popup-frame'.

I don't find it particularly complex.  It just tries each one of
the 5 different cases in turn: use FUNCTION, already-displayed,
same-window, same-frame, pop-up-frame.

And we should remove the `same-window' and `same-frame' parameters of
special-display-regexps (these were errors and would have been better
handled as predefined functions to use as FUNCTION hooks).  Then the
resulting code is even more straightforward.

> You don't know what `special-display-buffer-names' does unless you read
> the code of that function.

I don't know what makes you think so.

> The correct doc-string of `special-display-buffer-names' would have to
> explain in full what the code of `special-display-popup-frame' does.

What's missing?

>>> Atomic windows were discussed in a thread on how to implement tabs in a
>>> separate window since you were opposed to using the headerline for them.
>> I don't think we should try and provide support for atomic windows with
>> display-buffer.  At least not yet.  I expect that any place where we
>> might want to use atomic windows will need to set things up
>> manually anyway.
> OK.  Let's remove that part.

Good.

>> BTW, I don't see any use of this feature in the window-pub branch.
>> Do you have some sample code showing how it works (the minimap package
>> in GNU ELPA seems like a good candidate)?
> I was writing such code when you announced the feature freeze ;-)

Whenever you find the time, please continue your experiment.  Until we
have actual experience with that feature, it should be strongly marked
as experimental so we can easily change it.
IOW I urge you to hurry up with "writing such code" so we may have
a chance to tweak the design if needed.  Note also that minimap is not
affected by the freeze.

>> Same remark (don't integrate support for it in display-buffer, yet) and
>> same question (do you have sample code using it, ideally some attempt at
>> making ECB use that feature?) for side-windows.
> I'm stronlgy for keeping this in `display-buffer-alist'.  Personally, I
> think that side windows should be used in two ways only: Either by the
> user via `display-buffer' - so she can display a window showing a
> specific buffer always in the same place of the frame - or by special
> window managing code like that of ECB.

I can somewhat imagine using it from display-buffer-alist as
you describe.  So if that works OK, I guess we can keep support for it
in display-buffer-alist.  Tho I'd really like for it to be
more modularized.

> I proceeded as follows:
> - Base the design on that of `same-window-regexps' and
>   `same-window-buffer-names'.
> - Use one variable instead of the twenty or so options we had.
> - Use a "what to do" paradigm instead of "what to avoid".
> - Try to make the customization interface readable.
> - Add things that have been asked for in our forums.
> - Make it compatible with Emacs 23.

There's a crucial element missing: keep the design simple and modular.

>> To take a concrete example: what use-cases did you have in mind that
>> required going from the 4 different cases of the old code
>> (i.e. `same-frame', `same-window', `new-frame-non-dedicated' and
>> `new-frame-dedicated), which in my mind should be something like
>> 4 different values of the `where' parameter, to:
>> - `reuse-window' with 3 extra parameters.
> There are five basic methods: reuse-window, pop-up-window, pop-up-frame,
> use-side-window and specifying a function.

I suggest to bring this down to 1: specifying a function.
We can provide the other 4 as predefined functions to use there.

> `same-window' is provided as a macro specifier.

Using function names is just as good as a "macro specifier" I think here.

> The difference between `new-frame-non-dedicated' and
> `new-frame-dedicated' is not clear to me.

Trivial: one marks the frame's only window as dedicated, the other
doesn't.

There's really nothing more to it, tho in the old code, those two cases
are handled differently (one controlled by pop-up-frames, making
non-dedicated frames with default-frame-alist, the other controlled by
special-display-regexps making dedicated frames with
pop-up-frame-alist).  But in your code, this is more cleanly unified by
just giving a (dedicate . t) param.

> I suppose it stems mainly from the fact that you see dedication as
> a way to specify what to do with the window or frame when it's no
> more used.

It's also to force display-buffer (and switch-to-buffer when called
from Lisp packages) to use some other window/frame.  I don't want any
other buffer ever shown in my *Completions* window (which I carefully
size and place next to my minibuffer-only frame), same for my other
strongly-dedicated windows like *compilation*.

> I specially installed the quit-restore parameter for this
> purpose but it's currently mostly overridden by the dedicated status
> of the window.  Couldn't you try in your private code whether you
> could use that parameter instead and leave window dedication to what
> it should stand for according to its name: Avoid that `display-buffer'
> and/or `set-window-buffer' reuse a window for showing another buffer.

It might be an OK replacement for the soft dedication, yes.

>> - `reuse-window-dedicated' (AFAIK this is only used in switch-to-buffer
>> so it's not clear why it needs to be supported in display-buffer).
> It can (and maybe should) be used for side windows where the IDE people
> have that idea of windows that should always show the same buffer or one
> of the same "group".

Hmmm... yeah, I guess I can see how it might be useful there.
But then I'd rather keep it within the `side-window' (i.e. make it
a parameter of that display function).

>> - `pop-up-window' with a host of different values.
> The `selected' window case is obvious, I think: It allows users a basic
> control where to show the buffer (largest and lru are not very suitable
> for that).  `root' allows to show the buffer in a very predictable
> position on some side of the frame.
> Combining `selected' and `above' you can, for example, to display the
> ispell window without all the tribulations of the current code plus make
> it possible to show that window on a separate frame instead.
> Combining `root' and `left' allows, for example, to display the speedbar
> in a window on the right of the frame.

Thanks.

>> - `pop-up-window-split-unsplittable'.
> A silly leftover from the old code, IIUC.  I'd do away with it at any
> time.

You mean it's inherited from Emacs-23 or from your old code?  If it's
from your old code, then please remove it.  If it's from Emacs-23, maybe
as well, tho I'd first like to see where that feature was.

>> IIUC the reason why you don't have just a single `where' parameter is so
>> that the merge between the specifiers given in display-buffer and
>> display-buffer-alist can be more fine-grained, right?  What were the
>> use-cases where you thought that was important?
> You understand correctly.  Once for compatibility with Emacs 23,

Where does compatibility require fine-grained merging?

> once for applications,

Do you have concrete use-cases?

> and once for people who want fine-grained control.

I don't think that's a good enough justification for the complexity
(tho of course it's good to provide it if the complexity is needed for
other reasons anyway).

> Abstractions (like near-minibuffer and same-frame) would be
> built on top of that.

But you're saying that you're not satisfied with the way same-frame is
built on top of that, right?  And I don't see near-minibuffer so
I suspect you don't have experience with it either.  I think writing
a display-buffer-near-minibuffer function will be simpler/cleaner than
trying to come up with some clever way to combine various specifiers.

>> The problem I have with it is that the specifiers thingy acts in
>> 2 different ways:
>> - an alist for some kinds of parameters, like the old
>>   special-display-regexps.
>> - an `or' that tries elements one after the other until one succeeds.
>> That adds conceptual complexity.
> I can't deny that.  But that's how `display-buffer' alway worked, in a
> hard-coded form.

I don't find the two comparable because one is a piece of code which
provides one complex behavior via a simple API, whereas
display-buffer-alist provides a complex API.

> Even if you use a rule based system, the rules must permit to specify
> that I want to pop up a window first and if that fails try reusing
> another one.

No.  You just have to provide one rule which reproduces the old complex
behavior (and could use that old code to do that).  If someone wants
something else, she can write another rule with another behavior, but we
don't have to care about it nor about the interaction between different
rules as long as you restrict each rule to be used in isolation.

>> Along the same idea, some parameters that refine the way `reuse-window'
>> works are passed as "args" to `reuse-window' while others are provided
>> separately as specifiers.  That also adds conceptual complexity.
> True.  I was too silly to do that better.  And I'm still too silly to
> find a better solution.

Then let's try together.

>> If we're willing to use a single `where' and give up on the finer
>> grained merging, then the specifiers can really be an alist, the
>> `where' parameter can be defined to always be a function (i.e. it's
>> somewhat like your `function' specifier) and we can cut down the
>> docstring of display-buffer-alist by a crap load, it just needs to
>> mention typically useful values of this parameter, such as
>> `display-buffer-same-window', or `display-buffer-same-frame' and those
>> functions can then document that they understand params such as
>> min-height.
> Wouldn't this move options away from the user?

Can't think of any, no.

> I think we know now how many people passed their own function to
> `special-display-popup-frame', namely two.

I know my setup was copied by a few people back in the day when
I started using it, so I'm pretty sure there are more than 2 ;-)

> I'd like to know how many people wrote their own
> `special-display-function'.  And most uses of
> `display-buffer-function' were intended to avoid some special cases
> calling `display-buffer' with `display-buffer-function' bound to
> nil again.

Agreed.  Both should be marked obsolete.

>> In my experience when the preferred method fails, it's sufficient to
>> fallback on the default.  Do you have use-cases where this is not true?
> In Emacs 23 the preferred method is to try some five methods in the
> worst case.  There's no single default fallback.

I think we're miscommunicating here.
I'm just saying that if the caller says `same-window' and the user says
`same-frame', it's OK to just ignore the caller's specification and just
use `same-frame'.
There's no need to merge to two specs: if the user has no preference,
then obey the caller's preference, but if the user has a preference then
it's OK to just drop the caller's preference (so if the user's
preference needs to "fallback" it can fall back on the global default
rather than first trying the caller's preference).

Here's a new, minimalist starting point for display-buffer-alist:

(defvar display-buffer-alist nil
  "Specifications of how to display which buffer.
This is list where each element has the form (CONDITION . RULE) where
CONDITION is either a regular expression which is matched
  against the buffer's name or a function called with the buffer
  to be displayed (so the function can check the major-mode, ...) which
  should return non-nil if the RULE should be used.
RULE has the form (FUNCTION . ARGS) such that if CONDITION matches,
  FUNCTION is called with the buffer as first argument and with ARGS
  as subsequent arguments.  ARGS is usually an alist, but it really
  depends on FUNCTION.  FUNCTION should return the window it used.")

And display-buffer has only two args: BUFFER and RULE such that if none
of display-buffer-alist conditions applies to the buffer, then RULE is
used, otherwise it's ignored.

Then we provide a few standard functions to use in RULE to reproduce
the old Emacs-23 features:
- display-buffer-default (the good ol' code doing all the complex choice
  and used when no RULE is used).
- display-buffer-same-window (which calls display-buffer-same-frame if the
  selected window can't be used).
- display-buffer-other-window.
- display-buffer-same-frame (which calls display-buffer-default if the
  same-frame can't be used).
- display-buffer-pop-up-frame (which does what setting pop-up-frames to
  t used to do, more or less, i.e. create a non-dedicated frame with
  default-frame-alist).
- display-buffer-dedicated-frame (which pops up a new frame with
  a dedicated window obeying pop-up-frame-alist).
Plus a few new functions for new features:
- display-buffer-side-window.
- display-buffer-<something> for things like ispell or speedbar.

Such a design is at least as flexible as what we had in Emacs-23.
I think it's flexible enough to cover the cases you've mentioned.
And it's a lot simpler than the current design (maybe the total
complexity may not seem less if you consider the docstring of
display-buffer-alist together with the docstrings of all those new
standard functions, but the modularity is the key).

I'm not sure it's good enough, tho:
- does it really allow us to get rid of all the let-bindings of
  pop-up-frames and friends around calls to display-buffer?
- not sure where the behavior of "reuse existing window showing the same
  buffer" should be implemented.  In Emacs-23 this is done before
  checking any of special-display-*, which means it can only be
  controlled via display-buffer-function (which I want to obsolete), but
  I think there's a case to be made for letting RULE override it.
  One way to do that is to move this "reuse window" behavior into (all?)
  the standard functions.
- the "pop-up-window-set-height" and friends apply to several of the
  standard functions, so those functions would need to share some code,
  via something like a display-buffer-postprocess-window function.
  But I think that's not a real problem.

What problems do you guys (fore)see with such a setup?


        Stefan



reply via email to

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