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
prev 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).