emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
To: emacs-org list <emacs-orgmode@gnu.org>
Subject: Re: Lexical binding bug in org-list.el?
Date: Wed, 11 Nov 2015 10:33:50 +0100	[thread overview]
Message-ID: <87oaf098xt.fsf@nicolasgoaziou.fr> (raw)
In-Reply-To: <87pozks1uq.fsf@gmail.com> (Aaron Ecay's message of "Sun, 08 Nov 2015 19:55:09 +0000")

Hello,

Aaron Ecay <aaronecay@gmail.com> writes:

> LGTM.  I’ve probably met my quota of org-related fun for the day (see
> below...), but implementing this in terms of elements will be my next
> org-list related task.

What is "this"?

Unfortunately org-list and Elements do not play nicely with each other
at the moment, because of org-struct, i.e., because of lists outside Org
buffers. Hence the `org-list-struct' and `org-element--list-struct'
duplication.

> It’s very lightly tested so far.  I basically just used the following
> snippet as a test case: put it in an org-mode buffer, put your cursor
> somewhere inside the list, and M-: (org-list-to-subtree2)

OK. Some comments follow.

> OK.  Don’t hesitate to ask if there’s some way we can help, of course.

I'll send a patch once I have the time to do this (depending on the bug
reports). I'd appreciate if you could update Babel accordingly.

> +(defun org-list--partial-parse-contents (parse)
> +  "Get the actual contents of a partial org-mode parse.
> +
> +Specifically, when parsing a piece of text smaller than a
> +headline, `org-element-parse-buffer' wraps its result with a
> +dummy `section' element, as well as the standard `org-data'
> +wrapper.  This function removes these, returning a list of
> +org-elements.
> +
> +TODO: maybe this needs a more general name."
> +  (org-element-contents
> +   ;; strip the org-data element
> +   (nth 0 (org-element-contents
> +	   ;; and the section element
> +	   parse))))

Actually, I often wonder if `section' should appear at all in the parse
tree. I mean, it is an important feature for export, but it could be
added automatically at export time (i.e., in `org-export-as', like many
other tree transforms) without cluttering the original parse tree.

Also, here, you probably really want

  (org-element-map parse org-element-all-elements #'identity nil t)

since you're going to handle a single element anyway.

> +(defun org-list--split-first-line (contents)
> +  "Remove the first line of text from an org-element item.
> +
> +CONTENTS are the contents of the item org-element: at least a
> +paragraph followed by zero or more other elements.
> +
> +Returns a cons of the first textual line and a list of
> +org-elements representing the structure of the item minus this
> +line.
> +
> +TODO: is the first daughter of an item always a paragraph?"

Absolutely not. E.g.,

   - 

     | a |

   - item 2


> +  (let ((graf (nth 0 contents)))
> +    (unless (eq (org-element-type graf) 'paragraph)
> +      (error "`org-list--split-first-line' got confused"))

See above.

> +    (goto-char (org-element-property :begin graf))
> +    (let* ((eol (point-at-eol))

Use the original! -> `line-end-position'. This one is a compatibility
alias for XEmacs.

> +	   (end (org-element-property :end graf))
> +	   (first-line (buffer-substring-no-properties (point) eol)))
> +      (if (> (1+ eol) end)
> +	  ;; One line paragraph: it becomes the entirety of the
> +	  ;; headline, and we remove it from contents
> +	  (setq contents (cdr contents))
> +	;; Multi-line paragraph: narrow the buffer to lines 2-N, parse
> +	;; them, and set them as the contents of the paragraph.
> +	(save-restriction
> +	  (widen)
> +	  (narrow-to-region (1+ eol) end)
> +	  (org-element-set-contents graf
> +				    (org-list--partial-parse-contents
> +				     ;; TODO: We're playing a trick on
> +				     ;; the parser here.  AFAICT, the
> +				     ;; parse does not rely on the
> +				     ;; cache.  But maybe we should
> +				     ;; let org-element-use-cache to
> +				     ;; nil around this call, in case
> +				     ;; that changes in the future.
> +				     (org-element-parse-buffer)))))

It shouldn't change. Also, there is `org-element-copy' if you need to
alter an element obtained through `org-element-at-point'.

> +      (cons first-line contents))))
> +
> +(defun org-list--item-to-headline (item level)
> +  "Convert an org-element list item to a headline.
> +
> +The first line of the list item becomes the "
> +  (unless (eq (car item) 'item)
> +    (error "`org-list--item-to-headline' expects an item argument"))
> +  (let* ((r (org-list--split-first-line (org-element-contents item)))
> +	 (title (car r))
> +	 (other-contents (cdr r)))
> +    (list 'headline
> +	  `(:level ,level
> +		   ,@(when (eq (org-element-property :checkbox item) 'on)
> +		       (list :todo-keyword
> +			     ;; TODO: how to fish the approporiate
> +			     ;; value out of org-todo-keywords?
> +			     "TODO"))

I'd try

  (car org-not-done-keywords)

However, it returns nil if we're parsing a non-Org buffer.

> +		   :title ,title)
> +	  (mapcar (lambda (x) (if (eq (org-element-type x) 'plain-list)
> +				  (org-list--to-headlines x (1+ level))
> +				x))
> +		  other-contents))))
> +
> +(defun org-list--to-headlines (list level)
> +  (unless (eq (car list) 'plain-list)
> +    (error "`org-list-to-subtree' expects a plain-list argument"))
> +  (mapcar (lambda (x) (org-list--item-to-headline x level))
> +	  (org-element-contents list)))
> +
> +(defun org-list-to-subtree2 ()
> +  (let* ((e (org-element-at-point))
> +	 (l (org-element-lineage e))
> +	 (list (cl-find-if (lambda (x) (eq (org-element-type x) 'plain-list))
> +			   (nreverse l)))

Also,

  ;; Find the top-most plain-list containing element at point.
  (org-element-map (nreverse l) 'plain-list #'identity nil t)

> +	 (level (org-reduced-level (or (org-current-level) 0)))
> +	 (begin (org-element-property :begin list))
> +	 (end (org-element-property :end list))
> +	 (parse (save-restriction
> +		  (widen)
> +		  (narrow-to-region begin end)
> +		  (org-element-parse-buffer)))
> +	 (new-subtree (org-list--to-headlines
> +		       (nth 0 (org-list--partial-parse-contents parse))
> +		       level)))
> +    (goto-char end)
> +    ;; Don't eat the blank lines after the list.
> +    (skip-chars-backward " \n\t\f")

Why "\f"?

> +    (delete-region begin (point))
> +    (insert (org-element-interpret-data new-subtree))))

Note that current implementation is slightly smarter since it handles
`org-blank-before-new-entry'.

One issue here is that we are going to duplicate some code since
`org-list-parse-list' is going to do slightly the same but with
a different representation.


Regards,

-- 
Nicolas Goaziou

      parent reply	other threads:[~2015-11-11  9:32 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-06 19:43 Lexical binding bug in org-list.el? Kaushal Modi
2015-11-06 19:47 ` Kaushal Modi
2015-11-06 20:45   ` Aaron Ecay
2015-11-06 21:13     ` Kaushal Modi
2015-11-07  0:20     ` Nicolas Goaziou
2015-11-07 11:54       ` Aaron Ecay
2015-11-07 16:48         ` Nicolas Goaziou
2015-11-07 21:30           ` Aaron Ecay
2015-11-08 14:57             ` Nicolas Goaziou
2015-11-08 19:55               ` Aaron Ecay
2015-11-09 15:23                 ` Kaushal Modi
2015-11-11  9:33                 ` Nicolas Goaziou [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87oaf098xt.fsf@nicolasgoaziou.fr \
    --to=mail@nicolasgoaziou.fr \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).