From mboxrd@z Thu Jan 1 00:00:00 1970 From: Leo Vivier Subject: Re: [PATCH] Fix narrowed 1-line subtrees Date: Thu, 21 Feb 2019 16:41:43 +0100 Message-ID: <8736ohywqw.fsf@hidden> References: <20190218002547.30325-1-leo.vivier@gmail.com> <87mumsqepg.fsf@nicolasgoaziou.fr> <871s44cbzb.fsf@hidden> <87sgwkoue5.fsf@nicolasgoaziou.fr> <87d0nnnbkf.fsf@hidden> <877edvn6g3.fsf@hidden> <874l8zn5vz.fsf@hidden> <87y36asiap.fsf@nicolasgoaziou.fr> <87y36atwdl.fsf@hidden> <878sy9ywwm.fsf@hidden> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([209.51.188.92]:37571) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gwqTz-0002pb-Cy for emacs-orgmode@gnu.org; Thu, 21 Feb 2019 10:41:56 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gwqTy-0005zJ-2C for emacs-orgmode@gnu.org; Thu, 21 Feb 2019 10:41:55 -0500 Received: from mail-wr1-x443.google.com ([2a00:1450:4864:20::443]:40347) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gwqTw-0005qn-6p for emacs-orgmode@gnu.org; Thu, 21 Feb 2019 10:41:52 -0500 Received: by mail-wr1-x443.google.com with SMTP id q1so31099511wrp.7 for ; Thu, 21 Feb 2019 07:41:46 -0800 (PST) In-Reply-To: <878sy9ywwm.fsf@hidden> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: "Emacs-orgmode" To: Nicolas Goaziou Cc: emacs-orgmode@gnu.org Hello, Here=E2=80=99s a detailed account of the problem and solution of the patch I=E2=80=99ve just sent. I don=E2=80=99t have the time to write the tests today, but I=E2=80=99ll ge= t on it as soon as I can. ----------------------------------------------------------------------- This patch addresses multiple issues occuring when running commands on a 1-line subtree when the buffer is narrowed to it. A 1-line subtree is a subtree only containing a heading and a newline at the end. The problem is due to the way narrowing works in Emacs. It requires a region defined by two bounds on which to anchor the narrowing. The bounds respectively become the `point-min' and `point-max of the narrowed buffer. As the content within the narrowed region evolves, `point-max' is pushed or shrinked to accommodate the modifications to the content. Since a position within a buffer in Emacs is defined as the number of characters between the top of the file (whose value is 1) and `point', that means that everything after `point-max' evolves in unisson with the narrowed buffer. For example, in an org-mode buffer narrowed to a subtree, adding a newline at the end of a heading adds 1 character to the buffer which then pushes `point-max' *and* everything after it in the widened buffer by 1. The problem occurs when the bounds of the region to narrow are ambiguous, as is the case with 1-line subtrees. When you narrow an org-mode buffer to a 1-line subtree, the end of the line becomes `point-max'. Remembering our definition of a 1-line subtree above, you might wonder why we're not including the newline, but the reason is simple: that newline might also belong to another subtree. Going back to our example, if narrowing to a 1-line subtree included that last newline, we could delete it inside our narrowed buffer. If that newline was also the beginning of a new subtree, the subtree would not be functional anymore, since we'd end up with something like this: `*Subtree 1* Subtree 2'. ----------------------------------------------------------------------- Example: --------------------------------[START]-------------------------------- * Tree 1 :PROPERTIES: :TEST: t :END: * Tree 2 ---------------------------------[END]--------------------------------- With point on `Tree 1', run the following: (progn (org-narrow-to-subtree) (org-delete-property "TEST") (org-back-to-heading) (end-of-line) (delete-char 1) (widen)) Result: --------------------------------[START]-------------------------------- * Tree 1* Tree 2 ---------------------------------[END]--------------------------------- Observation: The newline between the two headings has been removed despite the fact that it wasn't in the buffer restriction. ----------------------------------------------------------------------- This ambiguous newline causes a lot of unexpected behaviours with commands inserting or removing content, e.g. clocking, scheduling as well as manipulating deadlines, properties, etc. Some of those commands act on a widened buffer which prevents them from inadvertently deleting that newline. That's the case for clocking in a task, since it adds `CLOCK:' lines below the heading at point. However, because those commands act on a widened buffer, they do not have access to the narrowed buffer's `point-max'. The consequence is that, when the restriction of the buffer is restored after `save-restriction', the narrowing function sees that the content between `point-min' and `point-max' hasn't changed (there's still a newline at the end) and restores the region as if nothing had happened. The command worked, but there's no way to see it in the narrowed buffer. Another example of an unexpected behaviour with commands acting on a widened buffer is when the command creates a 1-line subtree. That's the case when removing a :PROPERTIES: drawer. When the command removes the content *and* the last newline, upon restoring the restriction, `point-max' is seen to have shrunk, and becomes the first character which hasn't changed, which is the newline after the heading. The problem is that this is the ambiguous newline we discussed above, and that deleting it could break the following subtree if there was any. The solution to this problem is to ensure that those commands never act beyond the `point-max' of the narrowed buffer even when working in the widened buffer. As an example, when clocking in a task, rather than inserting a newline *after* the last char which isn't part of the heading, i.e. the ambiguous newline, we insert it after the last unambiguous character. This works because when narrowing, as we saw, `point-max' *is* that unambiguous character, and adding characters before it simply pushes `point-max' by as many characters as you've inserted, and this is tracked by `save-restriction'. This happens because `save-restriction' adds a special property to *all* the characters within the current restriction, not only `point-min' and `point-max'. Upon restoring the previous restriction, `save-restriction' looks for those special characters and try to include them all inside the new restriction. Practically, this is done by looking for the first and last characters with that special property and using them as the new `point-min' and `point-max'. This last bit is important to understand why the second example with the :PROPERTIES: drawer didn't behave properly. The original command deletes the drawer from the ambiguous newline to the bottom of the heading, which means that the newline at the end of the heading isn't touched. When `save-restriction' attempts to resume the previous narrowing, since the former `point-max' has been deleted (it was the `:' at the end of the :PROPERTIES: drawer), it looks for the first special character. But the problem is that this first character is the bottom of the heading, and that it has now become ambiguous. The solution is the same: we do not touch the ambiguous newline. Instead, we delete the newline at the end of the heading so that upon restoring the restriction, it becomes the last special character. Visually, instead of deleting the following bracketed region... --------------------------------[START]-------------------------------- * Tree 1 {:PROPERTIES: :TEST: t :END: }* Tree 2 ---------------------------------[END]--------------------------------- We delete the following one: --------------------------------[START]-------------------------------- * Tree 1{ :PROPERTIES: :TEST: t :END:} * Tree 2 ---------------------------------[END]--------------------------------- It's likely that I haven't addressed all the commands that do not play well with the ambiguous newlines. However, the solutions I've presented here should be enough to address them. ----------------------------------------------------------------------- HTH Best, -- Leo Vivier English Studies & General Linguistics Master Student, English Department Universit=C3=A9 Rennes 2