Subject: bug#35419: [Proposal] Buffer Lenses and the Case of Org-Mode (also, Jupyter)

From: Ihor Radchenko <[email-masked]>
Date: Sat, 01 Jun 2019 22:49:51 +0800

Dear Dmitrii,

Regarding the question about buffer-lens interaction. Let's take even
more complicated example:  To run the command, the user hits some key
combination, which happens to be bound to different commands in the main
buffer and in the lense buffer (i.e. the main buffer is in org-mode, the
lense is in mingus-mode, and the key is C-d). What should be the
behaviour in such a case? run the commands in both the buffers? decide
depending on the point position? It is easy to make up similar
complicated examples if you consider some exotic major modes in the
lense buffer.

I think that it would be more effective if someone decide on some basic
approach for the low-level implementation of the lense-mode (which
probably involves modifying emacs C-level source code) and continue the
discussion according to the benefits/limitations of that kind of
implementation.

Best,
Ihor

Dmitrii Korobeinikov <[email-masked]> writes:

> Dear Ihor,
>
>> Note that indirect buffers always share *all* the contents with the master
>> buffer. As a result, it may not be easy to make things like flyspell
>> work on code blocks in org-mode, if these code blocks are treated as
>> lenses.
>
> I tried flyspell w/ different dictionaries on 2 buffers.
> The dictionary is switched every time I switch into one of the buffers.
> You are right, ispell and the like working w/ a file directly would have to
> learn to work w/ indirect buffers by managing multiple simultaneous
> processes.
> Fortunately, that doesn't seem like a big hurdle.
>
>>> (1) A question: when an indirect buffer is created and some region is
>>> narrowed to, is the rest of the buffer duplicated in memory somewhere? If
>>> this is so, there could be a useful efficiency-related modification to
>>> indirect buffers, which would allow "hard-narrowing": not duplicating the
>>> rest of the base buffer.
>>
>> There is no duplication of the buffer content in indirect buffers.
>> Internally, indirect buffer's content is a pointer to the main buffer
>> content. If you modify text in any of the indirect buffers or in the
>> main buffer, the text is modified in all of them and in the main buffer.
>> Only the buffer-local variables are duplicated.
>> You can refer to "27.11 Indirect Buffers" in the elisp manual for
>> details.
>
> Bad choice of wording on my side, I didn't mean duplication, but rather
> keeping unnecessary info, like text properties in the newly created
> indirect buffer, in the regions which were "permanently" chosen to be
> narrowed-out.
> Anyway, this is a premature optimization for now.
>
>> > The next immediately outstanding question is:
>> > (2) how can "embedding" (of a buffer as a part of another buffer as an
>> > area) be done efficiently? This could possibly be approached as two
>> > problems: (i) displaying the area and (ii) interacting with it.
>> > Any ideas?
>>
>> These issues have been discussed in
>> https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00863.html.
>> As I remember, the discussion stopped without a clear conclusion. It was
>> not clear how to separate the main buffer contents from the nested
>> buffer (I treat them as analogue of the buffer lenses). Another issue
>> was how the keymaps and buffer-local variables would interact when the
>> point is within a lense. It was not clear what should be the priority.
>
> The short answer is probably that lens-mode looks at the changes to the
> buffer and decides what's what.
> Here is my vision for this.
>
> Say, you have an indirect buffer, call it A, it's base has contents:
>
>> line 1
>> line 2
>> line 3
>
> Also, there is a buffer, call it B, where we want to embed A, with contents:
>
>> word 1
>> instruction: lens that displays A
>> word 2
>
> Lens-mode decides to identify the second line as a lens and constructs
> layout of the file.
>
>> [text]
>> [lens#A]
>> [text]
>
> Now, construct and display the final buffer as:
>
>> word 1
>> line 1
>> line 2
>> line 3
>> word 2
>
> The core question: how is this "displaying" done.
> In part, somewhat like how indirect buffers function.
> A displayed piece of text is a pointer/reference to the text in the
> indirect buffer.
> Of course, this should be done in a way so that the modes running in B
> don't change the properties of the text (following the layout constructed
> by lens-mode as in the example above). Though, this might better&easier be
> done at the display unit level.
>
> What about interaction?
> Well, when the cursor is inside the lens, the controller decides what to do
> w/ each keybinding, whether to redirect it to the indirect buffer or not.
> And what about the case when borders are crossed?
> As I see it, any code that executes - does so as is.
> For instance, consider a buffer with a lens (contents: "lens"), which looks
> like this: "word1 lens word2".
> Place the cursor on the first word, run a command to remove 2 words.
> Now there are to possibilities here, depending on the implementation of the
> function which does the removal:
> 1. Implementation identifies the boundaries of deletion and removes the
> words as contents of the main buffer. Lens-mode identifies the removal and
> deletes the lens altogether.
> 2. Delete "word1" -> the cursor is on "lens" -> next deletion command is
> redirected to the indirect buffer, which does the removal. Lens-mode
> identifies the lens as empty and removes it.
>
> The second way might not be always desirable, so whenever a function is
> called with the cursor outside a lens, as an option, decide to never
> redirect the input to a lens while the function runs.
>
> For another case, consider the first example with lines and, cursor being
> in the beginning of the file, remove three lines.
> To make this interesting, assume the first kind of implementation runs,
> identifying the bounds, never redirecting a command to the lens.
> Well, if the implementation of the lens is inspired by how indirect buffers
> work, I imagine, the necessary changes in the embedded buffer take place
> directly, in which case everything works as desirable.
>
> Afterwards, lens-mode processes the changes and decides if some lens was
> removed or added, updating the file layout.
>
> Best regards,
> Dmitrii.