-
-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Weird behavior when org-adapt-indentation
is set
#144
Comments
Thanks. I can reproduce the problem. It seems that |
I do not think that Org indentation is doing anything wrong here. |
@yantar92 Any suggestion how this should be fixed? I think the expectation is that the indentation function is based on the underlying text (in the sense that we treat Org as a text format) and not on the way the text is displayed. However somewhere in the indentation process the displayed representation is taken into account for the computations. Where does this happen, in |
@yantar92 Nevermind. I saw that you already mentioned that indentation is based on "visual alignment" by convention.
My opinion is that the convention does not make much sense, but the convention is surely not going to change. For variable pitch fonts one can throw the convention out of the window right away, since there one will never achieve perfect indentation. Indentation via spaces only really works well for fixed pitch fonts and as such one could as well base it on the underlying text representation only. @hammerfunctor I don't see a way to fix this. Therefore I suggest that you avoid |
Emacs does it. As for suggestions, you can maybe try |
It has little to do with |
Daniel Mendler ***@***.***> writes:
@hammerfunctor I don't see a way to fix this. Therefore I suggest that you avoid `org-adapt-indentation`. You can disable `org-modern-block-name` prettification or try use `org-indent-mode`.
Note that `org-indent-mode` will also fight with org-modern once you
start editing the buffer.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
Ok. I looked into org-modern, and I do see that it is what you do already. |
Understood. Thanks.
Not sure how this is going to work if I want to preserve the visual style of org-modern. I am not saying that org-modern is without downsides but so far it works well for me. Despite the styling, most elements stay editable for example (in contrast to svg tags). There are certainly problems in edge cases like this indentation issue. In the end it is the choice of the user if they prefer the plain text display or if they want some styling, same with settings like
My assumption is that Org is mostly not an indented format and
Where do you observe this fighting? The only styling which is incompatible with (org-indent-mode)
(global-org-modern-mode)
(setq org-modern-hide-stars ".") |
Right. I don't observe this issue with either one or both of the following settings, since then the spaces before the ;; Disable problematic setting 1: Do not style fringe
(setq org-modern-block-fringe nil)
;; Disable problematic setting 2: Do not style block name
(setq org-modern-block-name nil) The problem seems to be that we cannot fix the indentation issue without changing the styling at the same time. So you are right that the block styling fights with both indentation and also But given that |
Daniel Mendler ***@***.***> writes:
> Note that `org-indent-mode` will also fight with org-modern once you
> start editing the buffer.
Where do you observe this fighting? The only styling which is incompatible with `org-indent-mode` is `org-modern-block-fringe`, but `org-modern` disables this setting if `org-indent-mode` is enabled. The other settings should not lead to bigger issues. Prettification of the stars doesn't look nice in the presence of indentation due to misalignment, but I would not call this "fighting". One can try the following settings:
Try the recipe from
https://list.orgmode.org/als4q47p4dt4iva4s73sihe75qjdz4ni554h55e63o6izogzst@vs3olesw6da3/T/#m84ce8f02c2bbf107b108b3470777c2d3609cef00
And then press C-j (or <RET>), inserting newline into the quote block.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
Maybe https:/minad/org-modern/blob/main/org-modern.el#L741 Also, it is not necessary to set fringes via line-prefix/wrap-prefix. See "41.16.5 Displaying in the Margins" in Elisp manual:
|
I am not sure I understand. You introduce some misalignment using On the other hand if I turn on Yes, exactly. This is the line which hides the spaces if both
Sure, but then we have to use overlays - something I try to avoid here for performance issues. |
Daniel Mendler ***@***.***> writes:
> Try the recipe from
***@***.***/T/#m84ce8f02c2bbf107b108b3470777c2d3609cef00
I am not sure I understand.
Let me elaborate.
In the linked recipe, I meant that in (4) you instead move point to the
end of #+begin_... line and then in (5) press <RET>.
> Also, it is not necessary to set fringes via line-prefix/wrap-prefix. See "41.16.5 Displaying in the Margins" in Elisp manual:
Sure, but then we have to use overlays - something I try to avoid here for performance issues.
This is no longer necessary starting from Emacs 29, where overlays are
re-implemented using proper segment tree and actually scale even better
compared to text properties (what an irony...).
That said, overlays are buffer-local, so font-lock approach will not
work reliably.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
I tried the following steps:
So far so good. I tried this both in my setup and with emacs -Q. I am on a very recent Emacs 29, compiled a few days ago. What did I do wrong or which bad effect should I observe precisely?
I am aware of the overlay improvements but I have my doubts about the better scaling behavior of overlays. Can you explain this in more detail? Text properties are commonly put on every line, e.g., all the faces by font locking. If I open a large source file and force font locking on every line it still works reasonably well (not sure about the scaling, I don't have any numbers). Would you argue that it is more efficient if faces are instead applied via overlays instead of via properties? |
Daniel Mendler ***@***.***> writes:
2. Enable org-indent-mode and org-modern-mode
Do this in reverse order: enable org-modern-mode first.
I am aware of the overlay improvements but I have my doubts about the
better scaling behavior of overlays. Can you explain this in more
detail? Text properties are commonly put on every line, e.g., all the
faces by font locking. If I open a large source file and force font
locking on every line it still works reasonably well (not sure about
the scaling, I don't have any numbers). Would you argue that it would
be more efficient if faces are instead applied via overlays instead of
via properties?
First let me explain a bit how Emacs redisplay works wrt text properties
and overlays.
Redisplay repeatedly scans for certain text properties like
'composition, 'face, 'display, 'line-prefix, 'wrap-prefix, etc. Each
scan is searching whether each of the necessary text properties is
present in the displayed part of the buffer.
Searching for text properties involves calling
`next-single-char-property-change', which works as the following:
1. It computes the given property value at point, by searching
overlays at point and searching text interval at point.
Then, property lists stored in each overlay at point and in the
interval are scanned for the required text property, in order.
The cost of computing property value at point is
SEARCH-OVERLAY * OVERLAY-PLIST-LENGTH +
SEARCH-TEXT-INTERVAL * INTERVAL-PLIST-LENGTH
If the overlay contains property, it will instead be
SEARCH-OVERLAY * OVERLAY-PLIST-LENGTH
2. It finds next point where any text property changes, by calling
`next-property-change' and does it repeatedly until property value
actually changes.
The upper cost of searching is
(N_OVERLAYS + N_INTERVALS in region) * SEARCH-COST-AT-POINT
For further analysis, we need to understand how overlays and text
properties are stored in C level:
- Overlays in Emacs 29 are stored using segment tree with O(logN) search
time (pre-Emacs 29, it was O(N)). Where N is the number of overlays.
Overlays can intersect each other.
Overlays tend to hold a fairly small number of properties.
- Text properties in Emacs are stored using interval tree with O(logN)
search time. Where N is the number of contiguous text intervals where
_all_ the text properties and values are `eq'.
Intervals cannot intersect. Any change in any text property and/or
property value starts a new interval. So, there tend to be a fairly
large number of text property intervals in practice.
As an illustration, try to open emacs -Q /path/to/org-element.el and
force-fontify it (M-: (font-lock-ensure) <RET>).
Then, try M-: (length (object-intervals (current-buffer))) <RET>.
You will see ~15k intervals just from fontification.
Similar exercise on a reasonably-sides Org buffer (100k) yields
9k intervals from emacs -Q, 11k intervals with M-x org-indent-mode,
and 16k with M-x org-modern-mode + org-indent-mode.
The number of text properties stored in intervals is usually a
handful:
M-: (let ((N 0) (sum 0)) (while (not (eobp)) (cl-incf N) (cl-incf sum (length (text-properties-at (point)))) (goto-char (next-property-change (point) nil (point-max)))) (/ sum N))
emacs -Q: 9 (average number of text properties per interval)
M-x org-indent-mode: 13
+ M-x org-modern-mode: 14
…---
Thus, the cost of querying text property value at point is
SEARCH-OVERLAY * OVERLAY-PLIST-LENGTH +
SEARCH-TEXT-INTERVAL * INTERVAL-PLIST-LENGTH
is
O(logN_overlays) * ~1 +
O(logN_intervals) * ~10
Now, consider two scenarios: (a) set line-prefix in zero-length overlays;
(b) set line-prefix in text properties. In both cases, we want to add N
property values.
For (a), the total cost will become
(N_OVERLAYS + N_INTERVALS in region + N) *
O(log N_overlays + N) * ~1 +
O(log N_intervals) * ~10
For (b), each extra text property will contribute twice because it will
split intervals:
before: <text interval> (1 interval) =>
after: <text ><new interval>< interval> (becomes 3)
(N_OVERLAYS + N_INTERVALS in region + 2N) *
O(log N_overlays) * ~1
O(log N_intervals + 2N) * ~10
We can simplify assuming N << N_intervals and N >> N_overlays.
(a)
(N_INTERVALS + N) *
O(log N_intervals) * ~10
(b)
(N_INTERVALS + 2N) *
O(log N_intervals) * ~10
Conclusion:
Using overlays is slightly faster, but not much faster.
And only when using zero-length overlays.
Otherwise, we would have +2N in both cases.
--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
It is not correct to do that.
Thank you very much for the precise explanation! This is pretty much what I expected. In Emacs, 29 for both overlays and text properties we get O(logN) search time, where N is the number of overlays or number of text property intervals. If I would use overlays instead of text properties I would also need separate overlays for intervals with differing text properties. Therefore we get the same scaling for both if they are used for the same purpose, e.g., if we would do all the font locking via overlays instead of text properties. Now the question is how the overhead is beyond the scaling behavior, the constants of the operations, the memory usage and so on. My expectation is that overlays are less efficient in that regard, but I may wrong about this. I think one should still prefer text properties where applicable. An interesting aspect of your analysis is that org-modern adds quite a bit of overhead since it creates more unique intervals. I tested this in a fairly small buffer and it seems that org-modern roughly doubles the number of intervals, which is not too bad given the logarithmic access complexity. The memory usage is another issue however. It probably makes sense to analyze which parts of org-modern causes the most intervals and check if there are ways to reduce this. Another aspect are markers - I've observed that markers are quite inefficient too if one has many. It severely slows down editing, since all the markers need updating. Maybe this issue has also been addressed in Emacs 29 by storing the markers in a more efficient tree data structure? |
Daniel Mendler ***@***.***> writes:
> Do this in reverse order: enable org-modern-mode first.
It is not correct to do that. `org-modern-mode` relies on `org-indent-mode` to be enabled first. This is a simple order dependency, but not a bigger issue.
You could add an `org-indent-mode-hook` that will handle the conflict.
Now the question is how the overhead is beyond the scaling behavior, the constants of the operations, the memory usage and so on. My expectation is that overlays are less efficient in that regard, but I may wrong about this. I think one should still prefer text properties where applicable.
Overlays take several times more space compared to text properties.
An interesting aspect of your analysis is that org-modern adds quite a
bit of overhead since it creates more unique intervals. I tested this
in a fairly small buffer and it seems that org-modern roughly doubles
the number of intervals, which is not too bad given the logarithmic
access complexity.
Not correct. Redisplay scales with NlogN because it has to traverse all
the intervals in visible region, spending logN on each stop.
So, doubling the number of intervals will double redisplay time (at
large N, when text property lookup dominates).
This can get pretty bad quickly. See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62780
Another aspect are markers - I've observed that markers are quite
inefficient too if one has many. It severely slows down editing, since
all the markers need updating. Maybe this issue has also been
addressed in Emacs 29 by storing the markers in a more efficient tree
data structure?
No. Markers are still horribly slow.
And Emacs sometimes scans all the markers in buffer in certain common
scenarios, like when moving point. See
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=63040
The most promising recent idea I recall was storing markers as overlays.
(The new overlay data structure is very efficient when updating overlay
boundaries).
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
Yes, as expected. Therefore one should still keep on using text properties? Or is the scaling different for the redisplay?
Why NlogN and not something like VlogN where V is bounded by the size of the visible region? In Org it may be particularly bad since everything is visible if the entire tree is folded? Is the scaling different for overlays when redisplaying?
Yes, one should ideally reuse the data structure for markers too. |
Daniel Mendler ***@***.***> writes:
> Overlays take several times more space compared to text properties.
Yes, as expected. Therefore one should still keep on using text properties? Or is the scaling different for the redisplay?
AFAIU, scaling should be almost the same, probably with slightly worse
constants for text properties.
However, text properties have some room for future optimization, unlike
overlays.
So, maybe keeping text properties is not a bad idea after all.
We might try to write a feature request for Emacs to allow other ways to
set fringe bitmap though. So that there is no need to override
line-prefix/wrap-prefix unnecessarily.
> Not correct. Redisplay scales with NlogN because it has to traverse all
the intervals in visible region, spending logN on each stop.
So, doubling the number of intervals will double redisplay time (at
large N, when text property lookup dominates).
Why NlogN and not something like VlogN where V is bounded by the size of the visible region?
Sure, VlogN.
Is the scaling different for overlays when redisplaying?
Is it also NlogN?
N includes both overlays and text properties.
So, it is just bad for Emacs redisplay to set extra display-contributing
properties. Alas.
In Org it may be particularly bad since *everything* is visible if the
entire tree is folded?
Yup. And things get worse as you work with Org file because font-lock
adds more and more text properties as more and more text regions get
displayed and fontified.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
Okay, thank you again very much for your explanations, Ihor. The scaling behavior is mostly aligned with what I had expected. It would be great to make the fringe properties more flexible. There are other properties which could be improved to allow even more advanced styling, e.g. vertical text alignment or rounded borders. But these are controversial topics I suspect. Some users seem to like org-modernish styling but there are certainly many more purists. :) And of course optimizations would be welcome as always. Regarding org-modern, I will probably add such an indent mode hook to avoid the order dependency. Then I will investigate if I can reduce the amount of intervals somehow. But the factor of two I mentioned before is likely a worst case estimate which only applies to buffers with many tags, todo states, tables and so on. |
Daniel Mendler ***@***.***> writes:
It would be great to make the fringe properties more flexible. There are other properties which could be improved to allow even more advanced styling, e.g. vertical text alignment or rounded borders. But these are controversial topics I suspect. Some users seem to like org-modernish styling but there are certainly many more purists. :) And of course optimizations would be welcome as always.
By "more flexible", do you mean improvements in Emacs?
Note that Org itself uses vertical text alignment in `org-use-sub-superscripts`.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
Yes, I meant improvements in Emacs, but I don't have concrete proposals. As example I would like to have an option to adjust vertical text alignment buffer locally if line-spacing>0. |
Very interesting conversation. I am using I have been presuming that font-lock is so well optimized in terms of drawing/computing "only that which is necessary" just-in-time for redisplay that it would be challenging to beat it using a custom overlay algorithm. So I tend to prefer leveraging font-lock machinery to add text properties (although with a more refined approach than just 1 subexp = 1 face). I suppose I could use font-lock to "identify the regions to update" and then, instead of applying a custom |
Daniel Mendler ***@***.***> writes:
> By "more flexible", do you mean improvements in Emacs?
Note that Org itself uses vertical text alignment in `org-use-sub-superscripts`.
Yes, I meant improvements in Emacs, but I don't have concrete proposals. As example I would like to have an option to adjust vertical text alignment buffer locally if line-spacing>0.
Given that RMS encouraged to improve visuals in Emacs, I do not see why
such a feature request would not be acceptable.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
JD Smith ***@***.***> writes:
... But all this discussion of text-property interval explosion gives me pause.
As we just found, using overlays is not much different.
So, if you want visuals, you have to work with the performance Emacs
provides and hope that it can be improved if enough people complain.
I have been presuming that font-lock is so well optimized in terms of
drawing/computing "only that which is necessary" just-in-time for
redisplay that it would be challenging to beat it using a custom
overlay algorithm.
Redisplay is optimized actually, but sometimes it has to redraw things.
This redrawing is when all the performance hits start to show up.
Or, like in one of the referenced bug reports, some customizations
disable redisplay optimizations.
Font-lock is also largely optimized. Not to the limit though, and there
have been some improvements in the latest Emacs:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57447
However, font-lock is conceptually synchronous - fontification must
finish when it is requested.
See https://yhetil.org/emacs-devel/[email protected]/
That's why org-indent is using agent timers that do not compute
indentation "in time" but sometimes "late". And never (or, at least,
less) block.
So I tend to prefer leveraging font-lock machinery to add text
properties (although with a more refined approach than just 1 subexp =
1 face). I suppose I could use font-lock to "identify the regions to
update" and then, instead of applying a custom `font-lock-face`
directly to the text, add _overlays_ (with the same face). Just
sounds _very_ roundabout... Thoughts?
I will not make much difference redisplay-wise. Both overlays and text
properties contribute there.
Also, overlays are buffer-local and will not reach indirect buffers.
…--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
|
@jdtsmith I don't think performance is such a big problem for reasonably sized Org files. I am quite sensitive to performance and I usually avoid adding slow minor modes to my setup. org-modern hasn't so far made problems for me, but usually my Org files are also not gigantic. As another example, in your shakespeare.org I get only a 20% increase in additional text property intervals due to org-modern. The 100% increase I mentioned before really is a worst case scenario. |
Thanks for your work on this great package! I found some weird things on the cooperation between
org
,indent-region
andorg-modern
and have no idea whether its a feature or a bug.When
org-adapt-indentation
(a built-in variable) is set,indent-region
is expected to indent heading, text and blocks to their correct level like this:But when
org-modern
is turned on in this buffer,indent-region
makes the region:Every time I run
indent-region
, the block tag got indented once more, but the text after a block just remain unchanged.Currently what I can do is define a function to turn off
org-modern
, indent and then turn onorg-modern
, and bindC-M-\
to it. Blocks get indented accordingly at this time, though they shouldn't.The text was updated successfully, but these errors were encountered: