forked from mirrors/org-mode
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.
This commit is contained in:
parent
f8d1d373fc
commit
9e52d2ed01
|
@ -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
|
||||
|
|
185
lisp/org-src.el
185
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.
|
||||
|
|
Loading…
Reference in New Issue