GNU bug report logs -
#79850
Wrong column with overlays
Previous Next
To reply to this bug, email your comments to 79850 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
Report forwarded
to
bug-gnu-emacs <at> gnu.org:
bug#79850; Package
emacs.
(Mon, 17 Nov 2025 07:24:02 GMT)
Full text and
rfc822 format available.
Acknowledgement sent
to
Juri Linkov <juri <at> linkov.net>:
New bug report received and forwarded. Copy sent to
bug-gnu-emacs <at> gnu.org.
(Mon, 17 Nov 2025 07:24:02 GMT)
Full text and
rfc822 format available.
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
This destructive behavior was revealed in hs-minor-mode.
Indenting a hidden block deletes text inside it.
Example:
0. emacs -Q
1. Eval: (setq hs-display-lines-hidden t)
2. 'M-x hs-minor-mode RET'
3. Copy this text to the scratch buffer:
(defun test (&optional arg)
(unless arg
(setq arg 1)))
4. Move point to the beginning of the line with 'unless'
5. 'M-x hs-toggle-hiding RET'
6. 'C-M-a' (beginning-of-defun)
7. 'C-M-q' (indent-pp-sexp)
8. 'M-x hs-minor-mode RET'
After disabling hs-minor-mode it's revealed
that the text inside is deleted.
This is because 'indent-pp-sexp' calls 'indent-sexp'
that calls 'indent-line-to' that uses 'current-column'.
'current-column' returns a wrong column number 8
instead of the correct 2. This causes (> cur-col column)
to delete the region.
Here is a short test that demonstrates that it return the wrong 8
on the buffer with 'defun test' example above:
(progn
(setq hs-display-lines-hidden t)
(hs-minor-mode 1)
(goto-char (point-min))
(search-forward "defun")
(hs-toggle-hiding)
(search-forward "unless")
(goto-char (pos-bol))
(skip-chars-forward " \t")
(current-column))
Information forwarded
to
bug-gnu-emacs <at> gnu.org:
bug#79850; Package
emacs.
(Mon, 17 Nov 2025 13:22:02 GMT)
Full text and
rfc822 format available.
Message #8 received at 79850 <at> debbugs.gnu.org (full text, mbox):
> From: Juri Linkov <juri <at> linkov.net>
> Date: Mon, 17 Nov 2025 09:20:58 +0200
>
> This destructive behavior was revealed in hs-minor-mode.
> Indenting a hidden block deletes text inside it.
> Example:
>
> 0. emacs -Q
> 1. Eval: (setq hs-display-lines-hidden t)
> 2. 'M-x hs-minor-mode RET'
> 3. Copy this text to the scratch buffer:
>
> (defun test (&optional arg)
> (unless arg
> (setq arg 1)))
>
> 4. Move point to the beginning of the line with 'unless'
> 5. 'M-x hs-toggle-hiding RET'
> 6. 'C-M-a' (beginning-of-defun)
> 7. 'C-M-q' (indent-pp-sexp)
> 8. 'M-x hs-minor-mode RET'
>
> After disabling hs-minor-mode it's revealed
> that the text inside is deleted.
>
> This is because 'indent-pp-sexp' calls 'indent-sexp'
> that calls 'indent-line-to' that uses 'current-column'.
>
> 'current-column' returns a wrong column number 8
> instead of the correct 2. This causes (> cur-col column)
> to delete the region.
Sorry, I don't understand: what glyph on display was expected to have
column = 2? After step 5 above, I see this on display:
(defun test (&optional arg)
(unless arg[1 line…]))
where the text inside [..] is a display string, and the brackets are
just for legibility, they are not really displayed. With the above on
display, if I go to the opening paren of "(unless" and type "C-x =", I
see column = 2, which is correct. If I go to the first closing parent
after the display property and type "C-x =", I see column = 7, which
is again correct (or as correct as Emacs can do -- I can explain if
needed). The next closing parent has column = 8, which is again
correct. What did I miss?
> Here is a short test that demonstrates that it return the wrong 8
> on the buffer with 'defun test' example above:
>
> (progn
> (setq hs-display-lines-hidden t)
> (hs-minor-mode 1)
> (goto-char (point-min))
> (search-forward "defun")
> (hs-toggle-hiding)
> (search-forward "unless")
> (goto-char (pos-bol))
> (skip-chars-forward " \t")
> (current-column))
This test produces a different display:
(defun test (&optional arg)[2 lines…])
The closing paren after the display property reports column = 8, which
is again "as correct as it gets" in Emacs. The text for which this
snippet searches:
(search-forward "unless")
(goto-char (pos-bol))
(skip-chars-forward " \t")
is entirely invisible, and covered by a display property, so it is
unclear to me why you expected current-column to ignore both the
invisible property and the display string, and return results as if
neither of these properties existed. That's not what current-column
does, and that is documented:
This is calculated by adding together the widths of all the displayed
representations of the character between the start of the previous line
and point (e.g., control characters will have a width of 2 or 4, tabs
will have a variable width).
[...]
Text that has an invisible property is considered as having width 0, unless
‘buffer-invisibility-spec’ specifies that it is replaced by an ellipsis.
Or what am I missing?
P.S. Indentation commands are generally unreliable when display
features are used that affect the return value of current-column,
move-to-column, and other functions that compute the column, because
those count _visual_ columns (or at least attempt to do so), whereas
indentation of code must use physical character columns in the buffer,
not what's on display.
Information forwarded
to
bug-gnu-emacs <at> gnu.org:
bug#79850; Package
emacs.
(Mon, 17 Nov 2025 17:13:03 GMT)
Full text and
rfc822 format available.
Message #11 received at 79850 <at> debbugs.gnu.org (full text, mbox):
> Sorry, I don't understand: what glyph on display was expected to have
> column = 2? After step 5 above, I see this on display:
>
> (defun test (&optional arg)
> (unless arg[1 line…]))
>
> where the text inside [..] is a display string, and the brackets are
> just for legibility, they are not really displayed. With the above on
> display, if I go to the opening paren of "(unless" and type "C-x =", I
> see column = 2, which is correct. If I go to the first closing parent
> after the display property and type "C-x =", I see column = 7, which
> is again correct (or as correct as Emacs can do -- I can explain if
> needed). The next closing parent has column = 8, which is again
> correct. What did I miss?
The problem is that when the block is not hidden, then 'current-column'
returns 2 instead of 8 at the same position.
> The closing paren after the display property reports column = 8, which
> is again "as correct as it gets" in Emacs. The text for which this
> snippet searches:
>
> (search-forward "unless")
> (goto-char (pos-bol))
> (skip-chars-forward " \t")
>
> is entirely invisible, and covered by a display property, so it is
> unclear to me why you expected current-column to ignore both the
> invisible property and the display string, and return results as if
> neither of these properties existed. That's not what current-column
> does, and that is documented:
>
> This is calculated by adding together the widths of all the displayed
> representations of the character between the start of the previous line
> and point (e.g., control characters will have a width of 2 or 4, tabs
> will have a variable width).
> [...]
> Text that has an invisible property is considered as having width 0, unless
> ‘buffer-invisibility-spec’ specifies that it is replaced by an ellipsis.
>
> Or what am I missing?
It seems the current implementation of 'indent-line-to'
wrongly assumes that 'current-column' operates on the text
in the buffer whereas it operates on its visual representation.
> P.S. Indentation commands are generally unreliable when display
> features are used that affect the return value of current-column,
> move-to-column, and other functions that compute the column, because
> those count _visual_ columns (or at least attempt to do so), whereas
> indentation of code must use physical character columns in the buffer,
> not what's on display.
Agreed. Is there a function like 'current-column' that would use
only non-visual character columns in the buffer?
This bug report was last modified today.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.