Thanks for the comments.

This email is long (sorry); fortunately there are many blank lines. . .

Thierry Banel <tbanelweb...@free.fr> writes:
> If not, maybe it would be better to keep the oriGiNaL cASe.

Yeah, fixed.

Nicolas Goaziou <m...@nicolasgoaziou.fr> writes:

> Rasmus <ras...@gmx.us> writes:
>>> Moreover, it can get in the way of expected M-RET behaviour, as in the
>>> following example
>>>   - item
>>>   | #+caption: test
>>>     untenrsiu
>> I don't know if I should do something about this case.  I guess it would
>> be possible, but I find it awkward when behavior $BUTTON depends not
>> only on not only the adjacent element, but also the previous element.
>> If it's important, I guess it should be toggled explicitly on by a
>> custom variable.
> M-RET, is, first and foremost, an important keybinding for editing the
> /structure/ of the document. The behaviour you want to add has nothing
> to do with structure.

I see it differently.  I see M-RET as a function that "magically" adds
more of what is adjacent to point.

I—obviously—think what I propose is better than what we have now.  Let's
go through the current functionality.

* Example 1:


Click M-RET anywhere above (line-beginning-position) and you get
something like

* _HEADER: foo

This is nonsense.

Click M-RET at (line-beginning-position) and you get


This is nonsense.

* Example 2

- s

#+LATEX_HEADER: foo % no space between ^ and #.

Same as example 2.

* Next case

- s

 #+LATEX_HEADER: foo % space between ^ and # 

M-RET at a position greater than (line-beginning-position) yields
something like

- s


- _HEADER: tes

This is nonsense.

M-RET at (line-beginning-position) will yield

- s

- | ← that's point

 #+LATEX_HEADER: foo % space between ^ and #

Thus far this is the only sensible result that we'd loose with the
patch.  If this is a great loss (rather than a bug in the current
implementation) this can be dealt with, though it adds complexity to the
meaning of M-RET (since it's a function of the previous element rather
than the adjacent one).

* Example 4

- s

#+LATEX_HEADER: foo % three spaces

This works like example 1.

> As a consequence, I'm not sure it should go with M-RET, and if it does,
> I'm pretty sure it should not override the main purpose of the binding.
> Inserting headlines, and possibly items, is much more important than
> duplicating keywords.

IMO the examples above show that M-RET fails at doing this in a sensible
manner is all but one case above.  The case where it does not fail
requires (i) that the keyword is not at the beginning of the line and
(ii) that the use has point before the keyword.  I don't care strongly
about this "feature"—It's just check that point is not at bol in

In fact I would be happy to go further.  One also get nonsense result
doing M-RET on #+begin_src lines and probably elsewhere.

>> Also, `org-insert-keyword' is not really using org-element anymore.
> It should since you make it interactive and can, therefore, be called on
> its own.

I moved the check to a separate function,
`org-looking-at-repeatable-keyword-p'.  There are now the following

  1. it's called twice: once in org-meta-return and once in
     org-insert-keyword.  I'm not sure if it's possible to emit a signal
     to avoid the second check somehow...  Perhaps the performance-loss
     is such that we should not worry about it.

  2. The name is terrible.  I'm also not sure if this function should
     reside in org.el.  Same for the regexp.  Thoughts?

> Some comments follow:

Thanks for these!

>> +     (indention
>> +      (buffer-substring
>> +       (line-beginning-position)
>> +       (save-excursion
>> +         (beginning-of-line)
>> +         (skip-chars-forward " \t")
>> +         (point))))
> Another option:
>   (ind (org-get-indentation))

Cool.  I really need to internalize to look for org-prefixed functions!

>> +     (keyword-re "#\\+\\(.+?\\):")
>> +     (key (save-excursion (beginning-of-line)
>> +                          (skip-chars-forward " \t")
>> +                          (looking-at keyword-re)
>> +                          (upcase (org-match-string-no-properties 1))))
> As discussed above, this is too fragile.  You need to duplicate the
> checks done in `org-meta-return' or refactor the code.

Yeah, I see your point.  Casual testing suggest it's more solid now
albeit there's now the double check as outlined above.
>> +     (end-of-keyword (save-excursion
>> +                       (beginning-of-line)
>> +                       (re-search-forward keyword-re (line-end-position) t)
>> +                       (point))))
>   (end-of-keyword (save-excursion (beginning-of-line) (search-forward ":"))
> [...]
> I think end-of-keyword should be bound after KEY is checked to avoid
> errors.

Since there's now a check before entering the function I guess this
should be safe.  Still I added extra checks, that I'm pretty sure are
unnecessary.  I'd be happy to remove these.


Don't panic!!!
>From 7443986c8d10efc7ee3216c3dbac6a5ace8a4468 Mon Sep 17 00:00:00 2001
From: rasmus <ras...@gmx.us>
Date: Wed, 19 Nov 2014 15:39:19 +0100
Subject: [PATCH] org.el: Add keyword-support to M-RET

* org.el (org-keyword-regexp): Regexp to detect keyword.
(org-looking-at-repeatable-keyword-p): New function.
(org-insert-keyword): New function.
(org-meta-return): May call `org-insert-keyword'.
(org-M-RET-may-split-line): Add keyword.
 lisp/org.el | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 101 insertions(+), 11 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 6ab13f4..6e75db4 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -440,6 +440,12 @@ Matched keyword is in group 1.")
   (concat "\\<" org-closed-string " *\\[\\([^]]+\\)\\]")
   "Matches the CLOSED keyword together with a time stamp.")
+(defconst org-keyword-regexp
+  "^[ \t]*#\\+\\(.+?\\):"
+  "Matches keywords such as #+LATEX_HEADER: and #+CAPTION:.
+See also `org-looking-at-repeatable-keyword-p'.")
 (defconst org-keyword-time-regexp
   (concat "\\<"
@@ -1601,6 +1607,7 @@ contexts.  Valid contexts are:
 headline  when creating a new headline
 item      when creating a new item
 table     in a table field
+keyword   when creating a new keyword
 default   the value to be used for all contexts not explicitly
   :group 'org-structure
@@ -1614,6 +1621,7 @@ default   the value to be used for all contexts not explicitly
 			   (const headline)
 			   (const item)
 			   (const table)
+			   (const keyword)
 			   (const default))
@@ -7725,8 +7733,8 @@ split the line and create a new headline with the text in the
 current line after point \(see `org-M-RET-may-split-line' on how
 to modify this behavior).
-If point is at the beginning of a normal line, turn this line
-into a heading.
+If point is at the beginning of a normal line, excluding some
+keywords, turn this line into a heading.
 When INVISIBLE-OK is set, stop at invisible headlines when going
 back.  This is important for non-interactive uses of the
@@ -21291,10 +21299,90 @@ number of stars to add."
     (unless toggled (message "Cannot toggle heading from here"))))
+(defun org-looking-at-repeatable-keyword-p ()
+  "Return non-nil if point is at repeatable keyword.
+Repeatable keyword are either keywords that are not members of
+`org-element-affiliated-keywords' or affiliated keywords that are
+members of `org-element-multiple-keywords'."
+  (let ((element (org-element-at-point))
+	(type (org-element-type element)))
+    (or (eq type 'keyword)
+	(<  (point) (org-element-property :post-affiliated element)))
+    (let ((key (save-excursion
+		 (beginning-of-line)
+		 (and (looking-at org-keyword-regexp)
+		      (org-match-string-no-properties 1)))))
+      (and key
+	   (or (not (member-ignore-case
+		     key org-element-affiliated-keywords))
+	       (member-ignore-case
+		key org-element-multiple-keywords))))))
+(defun org-insert-keyword (&optional arg)
+  "Insert a new keyword-line, such as #+CAPTION or #+LATEX_HEADER.
+If point is between the beginning of the line and the beginning
+of the keyword, as denoted by \"#\", a keyword-line is inserted
+above the current one.  If the point is within the keyword, a new
+keyword-line is inserted below.
+If point is in the middle of a keyword-line, after the keyword
+itself, split the line depending on the value of
+`org-M-RET-may-split-line'.  See the docstring of this variable
+for further details.
+Currently arg is ignored and the keyword is determined from the
+The function is used by `org-meta-return'.  Note, affiliated
+keywords that are not allowed to appear multiple times are
+ignored by `org-meta-return' (see
+`org-element-affiliated-keywords' and
+  (interactive "P")
+  (when (org-looking-at-repeatable-keyword-p)
+    (let* ((may-split (org-get-alist-option
+		       org-M-RET-may-split-line 'keyword))
+	   (point-at-bol-p
+	    (save-excursion (skip-chars-backward " \t") (bolp)))
+	   (indention (org-get-indentation))
+	   (key (save-excursion (beginning-of-line)
+				(and (looking-at org-keyword-regexp)
+				     (org-string-nw-p (org-match-string-no-properties 1)))))
+	   (end-of-keyword (and key
+				(save-excursion
+				  (beginning-of-line)
+				  (search-forward ":" (line-end-position) t)
+				  (point)))))
+      (when key
+	(cond (point-at-bol-p
+	       ;; Point is before keyword.
+	       ;; TODO: Perhaps it should call `org-insert-heading'
+	       ;; when point-at-bol-p.  If so this check should be in
+	       ;; `org-meta-return'.
+	       ;; TODO: it would be nice to have the insert \n after
+	       ;; the cond, but the save-excursion would somehow need
+	       ;; to be applied...
+	       (save-excursion
+		 (beginning-of-line)
+		 (insert "\n")))
+	      ((or (not may-split)
+		   (< (point) end-of-keyword))
+	       (end-of-line)
+	       (insert "\n"))
+	      (t (insert "\n")))
+	(org-indent-line-to indention)
+	(insert (format "#+%s: " key))
+	(delete-region (point) (save-excursion (skip-chars-forward " \t") (point)))))))
 (defun org-meta-return (&optional arg)
-  "Insert a new heading or wrap a region in a table.
-Calls `org-insert-heading' or `org-table-wrap-region', depending
-on context.  See the individual commands for more information."
+  "Insert a new heading, a new keyword or wrap a region in a table.
+Calls `org-insert-heading', `org-insert-keyword' or
+`org-table-wrap-region', depending on context.  See the
+individual commands for more information."
   (interactive "P")
   (org-check-before-invisible-edit 'insert)
   (or (run-hook-with-args-until-success 'org-metareturn-hook)
@@ -21303,12 +21391,14 @@ on context.  See the individual commands for more information."
         (when (eq type 'table-row)
           (setq element (org-element-property :parent element))
           (setq type 'table))
-        (if (and (eq type 'table)
-                 (eq (org-element-property :type element) 'org)
-                 (>= (point) (org-element-property :contents-begin element))
-                 (< (point) (org-element-property :contents-end element)))
-            (call-interactively 'org-table-wrap-region)
-          (call-interactively 'org-insert-heading)))))
+        (cond  ((and (eq type 'table)
+		      (eq (org-element-property :type element) 'org)
+		      (>= (point) (org-element-property :contents-begin element))
+		      (< (point) (org-element-property :contents-end element)))
+		(call-interactively 'org-table-wrap-region))
+	       ((org-looking-at-repeatable-keyword-p)
+		(call-interactively 'org-insert-keyword))
+	       (t (call-interactively 'org-insert-heading))))))
 ;;; Menu entries

Reply via email to