summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2015-05-02 08:37:37 +0200
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2015-05-02 16:45:51 +0200
commit9e52d2ed012cf25a66a9a3d1100510974219f967 (patch)
treebae03c81cc653ef938631e603de085acdaa0ea7d
parentf8d1d373fcbcbd6642555de6b2c52c55d1f318c1 (diff)
downloadorg-mode-9e52d2ed012cf25a66a9a3d1100510974219f967.tar.gz
org-src: Allow to edit inline footnote references
* lisp/org-src.el (org-src--edit-element): Do not rely on :value to extract contents. Small refactoring. (org-src--contents-area): Renamed from `org-src--element-contents-area'. Throw an error on unknown elements. (org-src--on-datum-p): Rename from `org-src--on-element-p'. Handle objects. (org-edit-export-block, org-edit-src-code, org-edit-fixed-width-region, org-edit-table.el): Apply renaming. (org-edit-src-save, org-edit-src-exit): Handle inline text. (org-edit-src-exit): Allow empty or blank code. Handle inline text. (org-src--edit-element): Rename an argument (org-edit-footnote-reference): Allow to edit inline definitions. * etc/ORG-NEWS: Document new feature.
-rw-r--r--etc/ORG-NEWS2
-rw-r--r--lisp/org-src.el185
2 files changed, 106 insertions, 81 deletions
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index b31ec86..7e75a69 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -367,7 +367,7 @@ Org can typeset a subtitle in some export backends. See the manual
for details.
*** Edit remotely a footnote definition
Calling ~org-edit-footnote-reference~ (C-c ') on a footnote reference
-allows to edit its definition, as long as it is not inline, in
+allows to edit its definition, as long as it is not anonymous, in
a dedicated buffer. It works even if buffer is currently narrowed.
** Miscellaneous
*** Strip all meta data from ITEM special property
diff --git a/lisp/org-src.el b/lisp/org-src.el
index 34c5907..d82068d 100644
--- a/lisp/org-src.el
+++ b/lisp/org-src.el
@@ -266,34 +266,35 @@ which see. BEG and END are buffer positions."
(org-move-to-column (max (+ (current-column) (cdr coord)) 0))
(point)))))
-(defun org-src--element-contents-area (element)
- "Return contents boundaries of ELEMENT.
-Return value is a pair (BEG . END) where BEG and END are buffer
-positions."
- (let ((type (org-element-type element)))
+(defun org-src--contents-area (datum)
+ "Return contents boundaries of DATUM.
+DATUM is an element or object. Return a pair (BEG . END) where
+BEG and END are buffer positions."
+ (let ((type (org-element-type datum)))
(cond
((eq type 'footnote-definition)
(let ((beg (org-with-wide-buffer
- (goto-char (org-element-property :post-affiliated element))
+ (goto-char (org-element-property :post-affiliated datum))
(search-forward "]"))))
- (cons beg (or (org-element-property :contents-end element) beg))))
- ((org-element-property :contents-begin element)
- (cons (org-element-property :contents-begin element)
- (org-element-property :contents-end element)))
+ (cons beg (or (org-element-property :contents-end datum) beg))))
+ ((org-element-property :contents-begin datum)
+ (cons (org-element-property :contents-begin datum)
+ (org-element-property :contents-end datum)))
((memq type '(example-block export-block src-block))
(cons (org-with-wide-buffer
- (goto-char (org-element-property :post-affiliated element))
+ (goto-char (org-element-property :post-affiliated datum))
(line-beginning-position 2))
(org-with-wide-buffer
- (goto-char (org-element-property :end element))
+ (goto-char (org-element-property :end datum))
(skip-chars-backward " \r\t\n")
(line-beginning-position 1))))
- (t
- (cons (org-element-property :post-affiliated element)
+ ((memq type '(fixed-width table))
+ (cons (org-element-property :post-affiliated datum)
(org-with-wide-buffer
- (goto-char (org-element-property :end element))
+ (goto-char (org-element-property :end datum))
(skip-chars-backward " \r\t\n")
- (line-beginning-position 2)))))))
+ (line-beginning-position 2))))
+ (t (error "Unsupported element or object: %s" type)))))
(defun org-src--make-source-overlay (beg end edit-buffer)
"Create overlay between BEG and END positions and return it.
@@ -323,14 +324,18 @@ END."
"Remove overlay from current source buffer."
(when (overlayp org-src--overlay) (delete-overlay org-src--overlay)))
-(defun org-src--on-element-p (element)
- "Non-nil when point is on ELEMENT."
- (and (>= (point) (org-element-property :begin element))
+(defun org-src--on-datum-p (datum)
+ "Non-nil when point is on DATUM.
+DATUM is an element or an object. Consider blank lines or white
+spaces after it as being outside."
+ (and (>= (point) (org-element-property :begin datum))
(<= (point)
(org-with-wide-buffer
- (goto-char (org-element-property :end element))
+ (goto-char (org-element-property :end datum))
(skip-chars-backward " \r\t\n")
- (line-end-position)))))
+ (if (memq (org-element-type datum) org-element-all-elements)
+ (line-end-position)
+ (point))))))
(defun org-src--contents-for-write-back ()
"Return buffer contents in a format appropriate for write back.
@@ -352,28 +357,30 @@ Assume point is in the corresponding edit buffer."
(buffer-string))))
(defun org-src--edit-element
- (element name &optional major write-back contents remote)
- "Edit ELEMENT contents in a dedicated buffer NAME.
+ (datum name &optional major write-back contents remote)
+ "Edit DATUM contents in a dedicated buffer NAME.
MAJOR is the major mode used in the edit buffer. A nil value is
equivalent to `fundamental-mode'.
When WRITE-BACK is non-nil, assume contents will replace original
-region. If it is a function, applied in the edit buffer, from
-point min, before returning the contents.
+region. Moreover, if it is a function, apply it in the edit
+buffer, from point min, before returning the contents.
When CONTENTS is non-nil, display them in the edit buffer.
-Otherwise, assume they are located in property `:value'.
+Otherwise, show DATUM contents as specified by
+`org-src--contents-area'.
When REMOTE is non-nil, do not try to preserve point or mark when
moving from the edit area to the source.
Leave point in edit buffer."
(setq org-src--saved-temp-window-config (current-window-configuration))
- (let* ((area (org-src--element-contents-area element))
+ (let* ((area (org-src--contents-area datum))
(beg (copy-marker (car area)))
(end (copy-marker (cdr area) t))
- (old-edit-buffer (org-src--edit-buffer beg end)))
+ (old-edit-buffer (org-src--edit-buffer beg end))
+ (contents (or contents (buffer-substring-no-properties beg end))))
(if (and old-edit-buffer
(or (not org-src-ask-before-returning-to-edit-buffer)
(y-or-n-p "Return to existing edit buffer ([n] will revert changes)? ")))
@@ -384,13 +391,13 @@ Leave point in edit buffer."
(with-current-buffer old-edit-buffer (org-src--remove-overlay))
(kill-buffer old-edit-buffer))
(let* ((org-mode-p (derived-mode-p 'org-mode))
- (type (org-element-type element))
+ (type (org-element-type datum))
(ind (org-with-wide-buffer
- (goto-char (org-element-property :begin element))
+ (goto-char (org-element-property :begin datum))
(org-get-indentation)))
(preserve-ind
(and (memq type '(example-block src-block))
- (or (org-element-property :preserve-indent element)
+ (or (org-element-property :preserve-indent datum)
org-src-preserve-indentation)))
;; Store relative positions of mark (if any) and point
;; within the edited area.
@@ -408,7 +415,7 @@ Leave point in edit buffer."
;; Switch to edit buffer.
(org-src-switch-to-buffer buffer 'edit)
;; Insert contents.
- (insert (or contents (org-element-property :value element)))
+ (insert contents)
(remove-text-properties (point-min) (point-max)
'(display nil invisible nil intangible nil))
(unless preserve-ind (org-do-remove-indentation))
@@ -435,17 +442,21 @@ Leave point in edit buffer."
(org-src-mode)
;; Move mark and point in edit buffer to the corresponding
;; location.
- (when mark-coordinates
- (org-src--goto-coordinates mark-coordinates (point-min) (point-max))
- (push-mark (point) 'no-message t)
- (setq deactivate-mark nil))
- (if (not remote)
- (org-src--goto-coordinates
- point-coordinates (point-min) (point-max))
- (goto-char (or (text-property-any
- (point-min) (point-max) 'read-only nil)
- (point-max)))
- (skip-chars-forward " \r\t\n"))))))
+ (if remote
+ (progn
+ ;; Put point at first non read-only character after
+ ;; leading blank.
+ (goto-char
+ (or (text-property-any (point-min) (point-max) 'read-only nil)
+ (point-max)))
+ (skip-chars-forward " \r\t\n"))
+ ;; Set mark and point.
+ (when mark-coordinates
+ (org-src--goto-coordinates mark-coordinates (point-min) (point-max))
+ (push-mark (point) 'no-message t)
+ (setq deactivate-mark nil))
+ (org-src--goto-coordinates
+ point-coordinates (point-min) (point-max)))))))
@@ -679,39 +690,52 @@ If BUFFER is non-nil, test it instead."
(defun org-edit-footnote-reference ()
"Edit definition of footnote reference at point."
(interactive)
- (let ((context (org-element-context)))
+ (let* ((context (org-element-context))
+ (label (org-element-property :label context)))
(unless (and (eq (org-element-type context) 'footnote-reference)
- (< (point)
- (org-with-wide-buffer
- (goto-char (org-element-property :end context))
- (skip-chars-backward " \t")
- (point))))
+ (org-src--on-datum-p context))
(user-error "Not on a footnote reference"))
- (let* ((label (org-element-property :label context))
- (definition
- (org-with-wide-buffer
- (org-footnote-goto-definition label)
- (beginning-of-line)
- (org-element-at-point))))
- (unless (eq (org-element-type definition) 'footnote-definition)
- (user-error "Cannot edit remotely inline footnotes"))
+ (unless label (user-error "Cannot edit remotely anonymous footnotes"))
+ (let* ((definition (org-with-wide-buffer
+ (org-footnote-goto-definition label)
+ (org-element-context)))
+ (inline (eq (org-element-type definition) 'footnote-reference))
+ (contents
+ (let ((c (org-with-wide-buffer
+ (org-trim (buffer-substring-no-properties
+ (org-element-property :begin definition)
+ (org-element-property :end definition))))))
+ (add-text-properties
+ 0
+ (progn (string-match (if inline "\\`\\[fn:.*?:" "\\`.*?\\]") c)
+ (match-end 0))
+ '(read-only "Cannot edit footnote label" front-sticky t
+ rear-nonsticky t)
+ c)
+ (when inline
+ (let ((l (length c)))
+ (add-text-properties
+ (1- l) l
+ '(read-only "Cannot edit past footnote reference"
+ front-sticky nil rear-nonsticky nil)
+ c)))
+ c)))
(org-src--edit-element
- definition (format "*Edit footnote [%s]*" label)
+ definition
+ (format "*Edit footnote [%s]*" label)
#'org-mode
- (lambda () (delete-region (point) (search-forward "]")))
- (concat
- (org-propertize (format "[%s]" label)
- 'read-only "Cannot edit footnote label"
- 'front-sticky t
- 'rear-nonsticky t)
- (and (org-element-property :contents-begin definition)
- (org-with-wide-buffer
- (buffer-substring-no-properties
- (progn
- (goto-char (org-element-property :contents-begin definition))
- (skip-chars-backward " \r\t\n")
- (point))
- (org-element-property :contents-end definition)))))
+ `(lambda ()
+ (if ,(not inline) (delete-region (point) (search-forward "]"))
+ (delete-region (point) (search-forward ":" nil t 2))
+ (delete-region (1- (point-max)) (point-max))
+ (when (re-search-forward "\n[ \t]*\n" nil t)
+ (user-error "Inline definitions cannot contain blank lines"))
+ ;; If footnote reference belongs to a table, make sure to
+ ;; remove any newline characters in order to preserve
+ ;; table's structure.
+ (when ,(org-element-lineage definition '(table-cell))
+ (while (search-forward "\n" nil t) (delete-char -1)))))
+ contents
'remote))
;; Report success.
t))
@@ -729,7 +753,7 @@ Throw an error when not at such a table."
(let ((element (org-element-at-point)))
(unless (and (eq (org-element-type element) 'table)
(eq (org-element-property :type element) 'table.el)
- (org-src--on-element-p element))
+ (org-src--on-datum-p element))
(user-error "Not in a table.el table"))
(org-src--edit-element
element
@@ -752,7 +776,7 @@ Throw an error when not at an export block."
(interactive)
(let ((element (org-element-at-point)))
(unless (and (eq (org-element-type element) 'export-block)
- (org-src--on-element-p element))
+ (org-src--on-datum-p element))
(user-error "Not in an export block"))
(let* ((type (downcase (org-element-property :type element)))
(mode (org-src--get-lang-mode type)))
@@ -783,7 +807,7 @@ name of the sub-editing buffer."
(let* ((element (org-element-at-point))
(type (org-element-type element)))
(unless (and (memq type '(example-block src-block))
- (org-src--on-element-p element))
+ (org-src--on-datum-p element))
(user-error "Not in a source or example block"))
(let* ((lang
(if (eq type 'src-block) (org-element-property :language element)
@@ -835,7 +859,7 @@ the area in the Org mode buffer."
(interactive)
(let ((element (org-element-at-point)))
(unless (and (eq (org-element-type element) 'fixed-width)
- (org-src--on-element-p element))
+ (org-src--on-datum-p element))
(user-error "Not in a fixed-width area"))
(org-src--edit-element
element
@@ -875,8 +899,9 @@ Throw an error if there is no such buffer."
;; insert new contents.
(delete-overlay overlay)
(delete-region beg end)
- (when (org-string-nw-p edited-code) (insert edited-code))
- (unless (bolp) (insert "\n"))
+ (let ((expecting-bol (bolp)))
+ (insert edited-code)
+ (when (and expecting-bol (not (bolp))) (insert "\n")))
(save-buffer)
(move-overlay overlay beg (point)))))
@@ -902,9 +927,9 @@ Throw an error if there is no such buffer."
(undo-boundary)
(goto-char beg)
(delete-region beg end)
- (when (org-string-nw-p code)
+ (let ((expecting-bol (bolp)))
(insert code)
- (unless (bolp) (insert "\n")))))
+ (when (and expecting-bol (not (bolp))) (insert "\n")))))
;; If we are to return to source buffer, put point at an
;; appropriate location. In particular, if block is hidden, move
;; to the beginning of the block opening line.