Compare commits
4 Commits
504c2b1bfa
...
9d25a4c02d
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d25a4c02d | |||
| 7577659e42 | |||
| 6b9f2a0cf1 | |||
| 59aaf6fc14 |
12
lisp/edit-indirect/edit-indirect-pkg.el
Normal file
12
lisp/edit-indirect/edit-indirect-pkg.el
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
(define-package "edit-indirect" "20240128.119" "Edit regions in separate buffers"
|
||||||
|
'((emacs "24.3"))
|
||||||
|
:commit "82a28d8a85277cfe453af464603ea330eae41c05" :authors
|
||||||
|
'(("Fanael Linithien" . "fanael4@gmail.com"))
|
||||||
|
:maintainers
|
||||||
|
'(("Fanael Linithien" . "fanael4@gmail.com"))
|
||||||
|
:maintainer
|
||||||
|
'("Fanael Linithien" . "fanael4@gmail.com")
|
||||||
|
:url "https://github.com/Fanael/edit-indirect")
|
||||||
|
;; Local Variables:
|
||||||
|
;; no-byte-compile: t
|
||||||
|
;; End:
|
||||||
434
lisp/edit-indirect/edit-indirect.el
Normal file
434
lisp/edit-indirect/edit-indirect.el
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
;;; edit-indirect.el --- Edit regions in separate buffers -*- lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Author: Fanael Linithien <fanael4@gmail.com>
|
||||||
|
;; URL: https://github.com/Fanael/edit-indirect
|
||||||
|
;; Version: 0.1.13
|
||||||
|
;; Package-Requires: ((emacs "24.3"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; SPDX-License-Identifier: BSD-2-clause
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2014-2024, Fanael Linithien
|
||||||
|
;; All rights reserved.
|
||||||
|
;;
|
||||||
|
;; Redistribution and use in source and binary forms, with or without
|
||||||
|
;; modification, are permitted provided that the following conditions are
|
||||||
|
;; met:
|
||||||
|
;;
|
||||||
|
;; * Redistributions of source code must retain the above copyright
|
||||||
|
;; notice, this list of conditions and the following disclaimer.
|
||||||
|
;; * Redistributions in binary form must reproduce the above copyright
|
||||||
|
;; notice, this list of conditions and the following disclaimer in the
|
||||||
|
;; documentation and/or other materials provided with the distribution.
|
||||||
|
;;
|
||||||
|
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
;; IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
;; TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
;; PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||||
|
;; OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
;; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
;; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
;; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
|
||||||
|
;; Edit regions in separate buffers, like `org-edit-src-code' but for arbitrary
|
||||||
|
;; regions.
|
||||||
|
;;
|
||||||
|
;; See the docstring of `edit-indirect-region' for details.
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
(defgroup edit-indirect nil
|
||||||
|
"Editing regions in separate buffers."
|
||||||
|
:group 'editing)
|
||||||
|
|
||||||
|
(defcustom edit-indirect-guess-mode-function #'edit-indirect-default-guess-mode
|
||||||
|
"The function used to guess the major mode of an edit-indirect buffer.
|
||||||
|
It's called with the edit-indirect buffer as the current buffer.
|
||||||
|
It's called with three arguments, the parent buffer, the beginning
|
||||||
|
and the end of the parent buffer region being editing.
|
||||||
|
|
||||||
|
Note that the buffer-local value from the parent buffer is used."
|
||||||
|
:type 'function
|
||||||
|
:group 'edit-indirect)
|
||||||
|
|
||||||
|
(defcustom edit-indirect-after-creation-hook nil
|
||||||
|
"Functions called after the edit-indirect buffer is created.
|
||||||
|
The functions are called with the edit-indirect buffer as the
|
||||||
|
current buffer.
|
||||||
|
|
||||||
|
Note that the buffer-local value from the parent buffer is used."
|
||||||
|
:type 'hook
|
||||||
|
:group 'edit-indirect)
|
||||||
|
|
||||||
|
(defcustom edit-indirect-before-commit-hook nil
|
||||||
|
"Functions called before the edit-indirect buffer is committed.
|
||||||
|
The functions are called with the edit-indirect buffer as the
|
||||||
|
current buffer.
|
||||||
|
|
||||||
|
Note that the buffer-local value from the edit-indirect buffer is
|
||||||
|
used."
|
||||||
|
:type 'hook
|
||||||
|
:group 'edit-indirect)
|
||||||
|
|
||||||
|
(defcustom edit-indirect-before-commit-functions nil
|
||||||
|
"Functions called before an edit-indirect buffer is committed.
|
||||||
|
The functions are called with the parent buffer as the current
|
||||||
|
buffer.
|
||||||
|
Each function is called with two arguments, the beginning and the
|
||||||
|
end of the region to be changed."
|
||||||
|
:type 'hook
|
||||||
|
:group 'edit-indirect)
|
||||||
|
|
||||||
|
(defcustom edit-indirect-after-commit-functions nil
|
||||||
|
"Functions called after an edit-indirect buffer has been committed.
|
||||||
|
The functions are called with the parent buffer as the current
|
||||||
|
buffer.
|
||||||
|
Each function is called with two arguments, the beginning and the
|
||||||
|
end of the changed region."
|
||||||
|
:type 'hook
|
||||||
|
:group 'edit-indirect)
|
||||||
|
|
||||||
|
(defgroup edit-indirect-faces nil
|
||||||
|
"Faces used in `edit-indirect'."
|
||||||
|
:group 'edit-indirect
|
||||||
|
:group 'faces
|
||||||
|
:prefix "edit-indirect")
|
||||||
|
|
||||||
|
(defface edit-indirect-edited-region
|
||||||
|
'((t :inherit secondary-selection))
|
||||||
|
"Face used to highlight an indirectly edited region."
|
||||||
|
:group 'edit-indirect-faces)
|
||||||
|
|
||||||
|
;; Emacs <= 24.3 has no `define-error'.
|
||||||
|
(let* ((user-error-conditions (get 'user-error 'error-conditions))
|
||||||
|
(define-user-error (lambda (name message)
|
||||||
|
(put name 'error-conditions
|
||||||
|
(cons name user-error-conditions))
|
||||||
|
(put name 'error-message message))))
|
||||||
|
(funcall define-user-error 'edit-indirect-overlapping
|
||||||
|
"Indirectly edited regions cannot overlap")
|
||||||
|
(funcall define-user-error 'edit-indirect-read-only
|
||||||
|
"Text is read-only, modify the edit-indirect buffer instead")
|
||||||
|
(funcall define-user-error 'edit-indirect-not-indirect
|
||||||
|
"This is not an edit-indirect buffer"))
|
||||||
|
|
||||||
|
(defvar edit-indirect--overlay)
|
||||||
|
(defvar edit-indirect--should-quit-window nil)
|
||||||
|
(put 'edit-indirect--should-quit-window 'permanent-local t)
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(defun edit-indirect-region (beg end &optional display-buffer)
|
||||||
|
"Edit the region BEG..END in a separate buffer.
|
||||||
|
The region is copied, without text properties, to a separate
|
||||||
|
buffer, called edit-indirect buffer, and
|
||||||
|
`edit-indirect-guess-mode-function' is called to set the major
|
||||||
|
mode.
|
||||||
|
When done, exit with `edit-indirect-commit', which will remove the
|
||||||
|
original region and replace it with the edited version; or with
|
||||||
|
`edit-indirect-abort', which will drop the modifications.
|
||||||
|
|
||||||
|
This differs from `clone-indirect-buffer' with narrowing in that
|
||||||
|
the text properties are not shared, so the parent buffer major mode
|
||||||
|
and the edit-indirect buffer major mode will not be able to tread
|
||||||
|
on each other's toes by setting up potentially conflicting text
|
||||||
|
properties, which happens surprisingly often when the font-lock
|
||||||
|
mode is used.
|
||||||
|
|
||||||
|
Edit-indirect buffers use the `edit-indirect-mode-map' keymap.
|
||||||
|
Regions with active edit-indirect buffers use the
|
||||||
|
`edit-indirect-overlay-map' keymap.
|
||||||
|
|
||||||
|
If there's already an edit-indirect buffer for BEG..END, use that.
|
||||||
|
If there's already an edit-indirect buffer active overlapping any
|
||||||
|
portion of BEG..END, an `edit-indirect-overlapping' error is
|
||||||
|
signaled.
|
||||||
|
|
||||||
|
When DISPLAY-BUFFER is non-nil or when called interactively,
|
||||||
|
display the edit-indirect buffer in some window and select it.
|
||||||
|
|
||||||
|
In any case, return the edit-indirect buffer."
|
||||||
|
(interactive
|
||||||
|
(if (or (use-region-p) (not transient-mark-mode))
|
||||||
|
(prog1 (list (region-beginning) (region-end) t)
|
||||||
|
(deactivate-mark))
|
||||||
|
(user-error "No region")))
|
||||||
|
(let ((buffer (edit-indirect--get-edit-indirect-buffer beg end)))
|
||||||
|
(when display-buffer
|
||||||
|
(edit-indirect--display-buffer buffer))
|
||||||
|
buffer))
|
||||||
|
|
||||||
|
(defvar edit-indirect-mode-map
|
||||||
|
(let ((map (make-sparse-keymap)))
|
||||||
|
(define-key map [remap save-buffer] #'edit-indirect-save)
|
||||||
|
(define-key map (kbd "C-c '") #'edit-indirect-commit)
|
||||||
|
(define-key map (kbd "C-c C-c") #'edit-indirect-commit)
|
||||||
|
(define-key map (kbd "C-c C-k") #'edit-indirect-abort)
|
||||||
|
map)
|
||||||
|
"Keymap for edit-indirect buffers.
|
||||||
|
|
||||||
|
\\{edit-indirect-mode-map}")
|
||||||
|
|
||||||
|
(defvar edit-indirect-overlay-map
|
||||||
|
(let ((map (make-sparse-keymap)))
|
||||||
|
(define-key map (kbd "RET") #'edit-indirect-display-active-buffer)
|
||||||
|
map)
|
||||||
|
"Keymap for regions with active edit-indirect buffers.
|
||||||
|
|
||||||
|
\\{edit-indirect-overlay-map}")
|
||||||
|
|
||||||
|
(defun edit-indirect-commit ()
|
||||||
|
"Commit the modifications done in an edit-indirect buffer.
|
||||||
|
That is, replace the original region in the parent buffer with the
|
||||||
|
contents of the edit-indirect buffer.
|
||||||
|
The edit-indirect buffer is then killed.
|
||||||
|
|
||||||
|
Can be called only when the current buffer is an edit-indirect
|
||||||
|
buffer."
|
||||||
|
(interactive)
|
||||||
|
(edit-indirect--barf-if-not-indirect)
|
||||||
|
(edit-indirect--commit)
|
||||||
|
(edit-indirect--abort t))
|
||||||
|
|
||||||
|
(defun edit-indirect-save ()
|
||||||
|
"Save the modifications done in an edit-indirect buffer.
|
||||||
|
That is, replace the original region in the parent buffer with the
|
||||||
|
contents of the edit-indirect buffer.
|
||||||
|
|
||||||
|
Can be called only when the current buffer is an edit-indirect
|
||||||
|
buffer."
|
||||||
|
(interactive)
|
||||||
|
(edit-indirect--barf-if-not-indirect)
|
||||||
|
(edit-indirect--commit))
|
||||||
|
|
||||||
|
(defun edit-indirect-abort ()
|
||||||
|
"Abort indirect editing in the current buffer and kill the buffer.
|
||||||
|
|
||||||
|
Can be called only when the current buffer is an edit-indirect
|
||||||
|
buffer."
|
||||||
|
(interactive)
|
||||||
|
(edit-indirect--barf-if-not-indirect)
|
||||||
|
(edit-indirect--abort t))
|
||||||
|
|
||||||
|
(defun edit-indirect-buffer-indirect-p (&optional buffer)
|
||||||
|
"Non-nil iff the BUFFER is an edit-indirect buffer.
|
||||||
|
BUFFER defaults to the current buffer."
|
||||||
|
(save-current-buffer
|
||||||
|
(when buffer
|
||||||
|
(set-buffer buffer))
|
||||||
|
;; (not (null)) so we don't leak the overlay to the outside world.
|
||||||
|
(not (null edit-indirect--overlay))))
|
||||||
|
|
||||||
|
(defun edit-indirect-default-guess-mode (_parent-buffer _beg _end)
|
||||||
|
"Guess the major mode for an edit-indirect buffer.
|
||||||
|
It's done by calling `normal-mode'."
|
||||||
|
(normal-mode))
|
||||||
|
|
||||||
|
(defun edit-indirect-display-active-buffer ()
|
||||||
|
"Display the active edit-indirect buffer of the region the point is in."
|
||||||
|
(interactive)
|
||||||
|
(let ((overlay
|
||||||
|
(let ((p (point)))
|
||||||
|
(edit-indirect--search-for-edit-indirect p (1+ p)))))
|
||||||
|
(unless overlay
|
||||||
|
(signal 'edit-indirect-not-indirect '()))
|
||||||
|
(edit-indirect--display-buffer (overlay-get overlay 'edit-indirect-buffer))))
|
||||||
|
|
||||||
|
(defvar edit-indirect--overlay nil
|
||||||
|
"The overlay spanning the region of the parent buffer being edited.
|
||||||
|
|
||||||
|
It's also used as the variable determining if we're in an
|
||||||
|
edit-indirect buffer at all.")
|
||||||
|
(make-variable-buffer-local 'edit-indirect--overlay)
|
||||||
|
(put 'edit-indirect--overlay 'permanent-local t)
|
||||||
|
|
||||||
|
;; Normally this would use `define-minor-mode', but that makes the mode function
|
||||||
|
;; interactive, which we don't want, because it's just an implementation detail.
|
||||||
|
(defun edit-indirect--mode (overlay)
|
||||||
|
"Turn the `edit-indirect--mode' \"minor mode\" on.
|
||||||
|
OVERLAY is the value to set `edit-indirect--overlay' to."
|
||||||
|
(setq edit-indirect--overlay overlay)
|
||||||
|
(add-hook 'kill-buffer-hook #'edit-indirect--abort-on-kill-buffer nil t))
|
||||||
|
(with-no-warnings
|
||||||
|
(add-minor-mode
|
||||||
|
'edit-indirect--overlay " indirect" edit-indirect-mode-map nil #'ignore))
|
||||||
|
|
||||||
|
(defun edit-indirect--display-buffer (buffer)
|
||||||
|
"Display the given BUFFER in some window and select it."
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(setq-local edit-indirect--should-quit-window t))
|
||||||
|
(select-window (display-buffer buffer))
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(defun edit-indirect--get-edit-indirect-buffer (beg end)
|
||||||
|
"Return an edit-indirect buffer for the region BEG..END.
|
||||||
|
If there's already an edit-indirect buffer active overlapping any
|
||||||
|
portion of BEG..END, an `edit-indirect-overlapping' error is
|
||||||
|
signaled."
|
||||||
|
(let ((old-overlay (edit-indirect--search-for-edit-indirect beg end)))
|
||||||
|
(cond
|
||||||
|
((null old-overlay)
|
||||||
|
(let ((overlay (edit-indirect--create-overlay beg end)))
|
||||||
|
(edit-indirect--create-indirect-buffer beg end overlay)))
|
||||||
|
((and (= beg (overlay-start old-overlay))
|
||||||
|
(= end (overlay-end old-overlay)))
|
||||||
|
(overlay-get old-overlay 'edit-indirect-buffer))
|
||||||
|
(t
|
||||||
|
(signal 'edit-indirect-overlapping '())))))
|
||||||
|
|
||||||
|
(defun edit-indirect--search-for-edit-indirect (beg end)
|
||||||
|
"Return an existing edit-indirect overlay for some region inside BEG..END.
|
||||||
|
If there's no indirectly edited region inside BEG..END, return
|
||||||
|
nil."
|
||||||
|
(catch 'done
|
||||||
|
(dolist (overlay (overlays-in beg end))
|
||||||
|
(when (overlay-get overlay 'edit-indirect-buffer)
|
||||||
|
(throw 'done overlay)))
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(defmacro edit-indirect--buffer-local-value (buffer variable)
|
||||||
|
"Get the BUFFER local value of VARIABLE.
|
||||||
|
VARIABLE shall be a symbol."
|
||||||
|
(unless (symbolp variable)
|
||||||
|
(signal 'wrong-type-argument (list #'symbolp variable)))
|
||||||
|
;; `with-current-buffer' is used instead of `buffer-local-value' because
|
||||||
|
;; the latter doesn't give warnings about free variables when
|
||||||
|
;; byte-compiled.
|
||||||
|
`(with-current-buffer ,buffer ,variable))
|
||||||
|
|
||||||
|
(defun edit-indirect--create-indirect-buffer (beg end overlay)
|
||||||
|
"Create an edit-indirect buffer and return it.
|
||||||
|
|
||||||
|
BEG..END is the parent buffer region to insert.
|
||||||
|
OVERLAY is the overlay, see `edit-indirect--overlay'."
|
||||||
|
(add-hook 'after-change-major-mode-hook #'edit-indirect--rebind-save-hooks)
|
||||||
|
(let ((buffer (generate-new-buffer (format "*edit-indirect %s*" (buffer-name))))
|
||||||
|
(parent-buffer (current-buffer)))
|
||||||
|
(overlay-put overlay 'edit-indirect-buffer buffer)
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(insert-buffer-substring-no-properties parent-buffer beg end)
|
||||||
|
(set-buffer-modified-p nil)
|
||||||
|
(edit-indirect--mode overlay)
|
||||||
|
;; Use the buffer-local values from the parent buffer. Don't retrieve the
|
||||||
|
;; values before actual uses in case these variables are changed by some
|
||||||
|
;; of the many possible hooks.
|
||||||
|
(funcall (edit-indirect--buffer-local-value
|
||||||
|
parent-buffer edit-indirect-guess-mode-function)
|
||||||
|
parent-buffer beg end)
|
||||||
|
(if (local-variable-p 'edit-indirect-after-creation-hook parent-buffer)
|
||||||
|
;; Copy the parent buffer hook to the indirect buffer instead of
|
||||||
|
;; let-binding it to avoid running it twice.
|
||||||
|
(setq-local edit-indirect-after-creation-hook
|
||||||
|
(edit-indirect--buffer-local-value
|
||||||
|
parent-buffer edit-indirect-after-creation-hook))
|
||||||
|
;; No need to do copy anything if the parent buffer has no local value,
|
||||||
|
;; the global value will be used instead. Just kill the local value in
|
||||||
|
;; the indirect buffer in case a prior hook set it, because we're not
|
||||||
|
;; supposed to use it.
|
||||||
|
(kill-local-variable 'edit-indirect-after-creation-hook))
|
||||||
|
(run-hooks 'edit-indirect-after-creation-hook))
|
||||||
|
buffer))
|
||||||
|
|
||||||
|
(defun edit-indirect--create-overlay (beg end)
|
||||||
|
"Create the edit-indirect overlay and return it.
|
||||||
|
|
||||||
|
BEG and END specify the region the overlay should encompass."
|
||||||
|
(let ((overlay (make-overlay beg end)))
|
||||||
|
(overlay-put overlay 'face 'edit-indirect-edited-region)
|
||||||
|
(overlay-put overlay 'modification-hooks '(edit-indirect--barf-read-only))
|
||||||
|
(overlay-put overlay 'insert-in-front-hooks '(edit-indirect--barf-read-only))
|
||||||
|
(overlay-put overlay 'keymap edit-indirect-overlay-map)
|
||||||
|
overlay))
|
||||||
|
|
||||||
|
(defvar edit-indirect--inhibit-read-only nil
|
||||||
|
"Non-nil means disregard read-only status of indirectly-edited region.")
|
||||||
|
|
||||||
|
(defun edit-indirect--barf-read-only (_ov _after _beg _end &optional _len)
|
||||||
|
"Signal an error because the text is read-only.
|
||||||
|
No error is signaled if `inhibit-read-only' or
|
||||||
|
`edit-indirect--inhibit-read-only' is non-nil."
|
||||||
|
(unless (or inhibit-read-only edit-indirect--inhibit-read-only)
|
||||||
|
(signal 'edit-indirect-read-only '())))
|
||||||
|
|
||||||
|
(defun edit-indirect--commit ()
|
||||||
|
"Commit the modifications done in an edit-indirect buffer."
|
||||||
|
(run-hooks 'edit-indirect-before-commit-hook)
|
||||||
|
(let ((beg (overlay-start edit-indirect--overlay))
|
||||||
|
(end (overlay-end edit-indirect--overlay))
|
||||||
|
(buffer (current-buffer))
|
||||||
|
(edit-indirect--inhibit-read-only t))
|
||||||
|
(with-current-buffer (overlay-buffer edit-indirect--overlay)
|
||||||
|
(save-excursion
|
||||||
|
(let ((beg-marker (copy-marker beg))
|
||||||
|
(end-marker (copy-marker end)))
|
||||||
|
(edit-indirect--run-hook-with-positions
|
||||||
|
'edit-indirect-before-commit-functions beg-marker end-marker)
|
||||||
|
(save-match-data
|
||||||
|
(set-match-data (list beg-marker end-marker))
|
||||||
|
(let ((new-data
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(buffer-substring-no-properties 1 (1+ (buffer-size))))))
|
||||||
|
(unless (string= new-data (match-string 0))
|
||||||
|
(replace-match new-data t t))))
|
||||||
|
(edit-indirect--run-hook-with-positions
|
||||||
|
'edit-indirect-after-commit-functions beg-marker end-marker)
|
||||||
|
(set-marker beg-marker nil)
|
||||||
|
(set-marker end-marker nil))))
|
||||||
|
(set-buffer-modified-p nil)))
|
||||||
|
|
||||||
|
(defun edit-indirect--run-hook-with-positions (hook beg end)
|
||||||
|
"Run HOOK with the specified markers BEG and END.
|
||||||
|
HOOK should be a symbol, a hook variable.
|
||||||
|
The functions are passed integer positions instead of markers.
|
||||||
|
If a function changes the buffer contents, the next function will be
|
||||||
|
called with updated positions."
|
||||||
|
(run-hook-wrapped hook
|
||||||
|
(lambda (f beg end)
|
||||||
|
(funcall f (marker-position beg) (marker-position end))
|
||||||
|
nil)
|
||||||
|
beg end))
|
||||||
|
|
||||||
|
(defun edit-indirect--abort (kill)
|
||||||
|
"Abort an indirect edit and clean up the edit-indirect buffer."
|
||||||
|
(delete-overlay edit-indirect--overlay)
|
||||||
|
;; Kill the overlay reference so that `edit-indirect--abort-on-kill-buffer'
|
||||||
|
;; won't try to call us again.
|
||||||
|
(setq edit-indirect--overlay nil)
|
||||||
|
;; If we created a window, get rid of it. Kill the buffer we created.
|
||||||
|
(if (and edit-indirect--should-quit-window
|
||||||
|
(eq (window-buffer (selected-window))
|
||||||
|
(current-buffer)))
|
||||||
|
(quit-window kill)
|
||||||
|
(and kill (kill-buffer))))
|
||||||
|
|
||||||
|
(defun edit-indirect--rebind-save-hooks ()
|
||||||
|
"Bind our `save-buffer' hooks in the current buffer.
|
||||||
|
Does nothing if the current buffer is not an edit-indirect buffer."
|
||||||
|
(when (edit-indirect-buffer-indirect-p)
|
||||||
|
(setq buffer-offer-save t)
|
||||||
|
(add-hook 'write-contents-functions #'edit-indirect--commit-on-save nil t)))
|
||||||
|
|
||||||
|
(defun edit-indirect--commit-on-save ()
|
||||||
|
"Commit the indirect edit.
|
||||||
|
Should only be called from `write-contents-functions'."
|
||||||
|
(edit-indirect--commit)
|
||||||
|
t)
|
||||||
|
|
||||||
|
(defun edit-indirect--abort-on-kill-buffer ()
|
||||||
|
"Abort indirect edit.
|
||||||
|
Should be called only from `kill-buffer-hook'."
|
||||||
|
(when edit-indirect--overlay
|
||||||
|
(edit-indirect--abort nil)))
|
||||||
|
|
||||||
|
(defun edit-indirect--barf-if-not-indirect ()
|
||||||
|
"Signal an error if the current buffer is not an edit-indirect buffer.
|
||||||
|
The error signaled is `edit-indirect-not-indirect'."
|
||||||
|
(unless edit-indirect--overlay
|
||||||
|
(signal 'edit-indirect-not-indirect '())))
|
||||||
|
|
||||||
|
(provide 'edit-indirect)
|
||||||
|
;;; edit-indirect.el ends here
|
||||||
@@ -516,7 +516,7 @@ Currently only one i.e. A.x is possible."
|
|||||||
\\DeclareNameAlias{default}{family-given}
|
\\DeclareNameAlias{default}{family-given}
|
||||||
|
|
||||||
% Autorennamen in Kapitälchen
|
% Autorennamen in Kapitälchen
|
||||||
\\renewcommand*{\\mkbibnamelast}[1]{\\textsc{#1}}
|
\\renewcommand*{\\mkbibnamefamily}[1]{\\textsc{#1}}
|
||||||
|
|
||||||
% muss ich noch nachvollziehen, nur dick?
|
% muss ich noch nachvollziehen, nur dick?
|
||||||
\\renewbibmacro*{author/editor+others/translator+others}{%
|
\\renewbibmacro*{author/editor+others/translator+others}{%
|
||||||
@@ -709,7 +709,6 @@ replacements.")
|
|||||||
headsepline,
|
headsepline,
|
||||||
%<<documentclass>>,
|
%<<documentclass>>,
|
||||||
captions=tableheading,
|
captions=tableheading,
|
||||||
version=first,
|
|
||||||
bibliography=totoc,
|
bibliography=totoc,
|
||||||
listof=totoc,
|
listof=totoc,
|
||||||
dvipsnames,
|
dvipsnames,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Four windows:
|
|||||||
(interactive)
|
(interactive)
|
||||||
(require 'python)
|
(require 'python)
|
||||||
(setq eldoc-echo-area-prefer-doc-buffer t)
|
(setq eldoc-echo-area-prefer-doc-buffer t)
|
||||||
|
(let ((eldoc-echo-area-prefer-doc-buffer t)
|
||||||
|
(eglot-sync-connect t))
|
||||||
|
|
||||||
;; first make visiting file full and run eglot
|
;; first make visiting file full and run eglot
|
||||||
(delete-other-windows)
|
(delete-other-windows)
|
||||||
@@ -29,14 +31,19 @@ Four windows:
|
|||||||
|
|
||||||
;; split horizontal 0.6/0.4
|
;; split horizontal 0.6/0.4
|
||||||
;; (split-window-horizontally (truncate (* 0.6 (window-body-width))))
|
;; (split-window-horizontally (truncate (* 0.6 (window-body-width))))
|
||||||
;; (other-window 1)
|
(split-window-horizontally)
|
||||||
|
|
||||||
|
(other-window 1)
|
||||||
;; (switch-to-buffer (concat "*eldoc*"))
|
;; (switch-to-buffer (concat "*eldoc*"))
|
||||||
;; (other-window 1)
|
(when (get-buffer (eldoc-doc-buffer))
|
||||||
(eldoc-doc-buffer 1)
|
(switch-to-buffer (eldoc-doc-buffer)))
|
||||||
(window-resize nil (truncate (* 0.2 (window-body-width))) t nil nil)
|
|
||||||
|
;; ;; (other-window 1)
|
||||||
|
;; ;; (eldoc-doc-buffer 1)
|
||||||
|
;; ;; (window-resize nil (truncate (* 0.2 (window-body-width))) t nil nil)
|
||||||
|
|
||||||
;; split vertical xref and python shell
|
;; split vertical xref and python shell
|
||||||
(other-window 1)
|
;; (other-window 1)
|
||||||
(split-window-vertically)
|
(split-window-vertically)
|
||||||
(other-window 1)
|
(other-window 1)
|
||||||
(split-window-vertically)
|
(split-window-vertically)
|
||||||
@@ -48,7 +55,9 @@ Four windows:
|
|||||||
;; size
|
;; size
|
||||||
(other-window 1)
|
(other-window 1)
|
||||||
(balance-windows)
|
(balance-windows)
|
||||||
(window-resize nil (truncate (* 0.2 (window-body-width))) t nil nil))
|
(window-resize nil (truncate (* 0.2 (window-body-width))) t nil nil)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
;; python old using anaconda
|
;; python old using anaconda
|
||||||
;; (defun my-view-python ()
|
;; (defun my-view-python ()
|
||||||
|
|||||||
15
lisp/org-preview-html/org-preview-html-pkg.el
Normal file
15
lisp/org-preview-html/org-preview-html-pkg.el
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
(define-package "org-preview-html" "20220809.1033" "Automatically preview org-exported HTML files within Emacs"
|
||||||
|
'((emacs "25.1")
|
||||||
|
(org "8.0"))
|
||||||
|
:commit "785e1f5c99c0f2d76a9a6611a06b4552a343e221" :authors
|
||||||
|
'(("Jake B" . "jakebox0@protonmail.com"))
|
||||||
|
:maintainers
|
||||||
|
'(("Jake B" . "jakebox0@protonmail.com"))
|
||||||
|
:maintainer
|
||||||
|
'("Jake B" . "jakebox0@protonmail.com")
|
||||||
|
:keywords
|
||||||
|
'("org" "convenience" "outlines")
|
||||||
|
:url "https://github.com/jakebox/org-preview-html")
|
||||||
|
;; Local Variables:
|
||||||
|
;; no-byte-compile: t
|
||||||
|
;; End:
|
||||||
238
lisp/org-preview-html/org-preview-html.el
Normal file
238
lisp/org-preview-html/org-preview-html.el
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
;;; org-preview-html.el --- Automatically preview org-exported HTML files within Emacs -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Copyright (C) 2021 Jake B <jakebox0@protonmail.com>
|
||||||
|
|
||||||
|
;; Author: Jake B <jakebox0@protonmail.com>
|
||||||
|
;; Original author of org-preview-html (until 2021-09): DarkSun <lujun9972@gmail.com>
|
||||||
|
;; Url: https://github.com/jakebox/org-preview-html
|
||||||
|
;; Keywords: Org, convenience, outlines
|
||||||
|
;; Version: 0.3.1
|
||||||
|
;; Package-Requires: ((emacs "25.1") (org "8.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
;;
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
;;
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;; This minor mode provides a side-by-side preview of your org-exported HTML
|
||||||
|
;; files using the either the eww or xwidget browsers. The update frequency of
|
||||||
|
;; the preview can be configured to suit your preference.
|
||||||
|
;;
|
||||||
|
;; Quick start:
|
||||||
|
;; Put this file under your load path.
|
||||||
|
;; Enable the minor mode in an Org buffer:
|
||||||
|
;; M-x org-preview-html-mode
|
||||||
|
;; Configure options with M-x customize-group org-preview-html
|
||||||
|
;;
|
||||||
|
;; Source code
|
||||||
|
;; org-preview-html's code can be found here:
|
||||||
|
;; http://github.com/jakebox/org-preview-html
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
;;;; Requirements
|
||||||
|
(require 'org)
|
||||||
|
(require 'xwidget)
|
||||||
|
(require 'eww)
|
||||||
|
|
||||||
|
|
||||||
|
(defgroup org-preview-html nil
|
||||||
|
"Automatically preview org-exported HTML files within Emacs."
|
||||||
|
:group 'org-mode
|
||||||
|
:link '(url-link :tag "Homepage" "https://github.com/jakebox/org-preview-html/"))
|
||||||
|
|
||||||
|
(defcustom org-preview-html-refresh-configuration 'save
|
||||||
|
"Specifies how often the HTML preview will be refreshed.
|
||||||
|
|
||||||
|
If `manual', update manually by running `org-preview-html-refresh'.
|
||||||
|
If `save', update on save (default).
|
||||||
|
If `export', update on manual export \(using `org-html-export-to-html').
|
||||||
|
If `timer', update preview on timer (`org-preview-html-timer-interval')."
|
||||||
|
:type '(choice
|
||||||
|
(symbol :tag "Update preview manually" manual)
|
||||||
|
(symbol :tag "Update preview on save" save)
|
||||||
|
(symbol :tag "Update preview on export" export)
|
||||||
|
(symbol :tag "Update preview on a timer" timer))
|
||||||
|
:group 'org-preview-html)
|
||||||
|
|
||||||
|
(defcustom org-preview-html-timer-interval 2
|
||||||
|
"Integer seconds to wait between exports when in 'timer mode."
|
||||||
|
:type 'integer
|
||||||
|
:group 'org-preview-html)
|
||||||
|
|
||||||
|
(defcustom org-preview-html-viewer 'eww
|
||||||
|
"Which Emacs browser `org-preview-html-mode' will use.
|
||||||
|
|
||||||
|
If `eww', use eww browser (default).
|
||||||
|
If `xwidget', use xwidget browser."
|
||||||
|
:type '(choice
|
||||||
|
(symbol :tag "Use eww" eww)
|
||||||
|
(symbol :tag "Use xwidget" xwidget))
|
||||||
|
:group 'org-preview-html)
|
||||||
|
|
||||||
|
(define-obsolete-variable-alias 'org-preview-html/body-only 'org-preview-html-subtree-only "Version 0.3")
|
||||||
|
|
||||||
|
(defcustom org-preview-html-subtree-only nil
|
||||||
|
"If non-nil, scope the preview to the current subtree."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'org-preview-html)
|
||||||
|
|
||||||
|
(defcustom org-preview-html/body-only nil
|
||||||
|
"Scope the preview to the body or include the entire document.
|
||||||
|
Obselete as of version 0.3, instead use `org-preview-html-subtree-only'."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'org-preview-html)
|
||||||
|
|
||||||
|
|
||||||
|
;; Internal variables
|
||||||
|
(defvar org-preview-html--browser-buffer nil)
|
||||||
|
(defvar org-preview-html--previewed-buffer-name nil)
|
||||||
|
(defvar org-preview-html--refresh-timer nil)
|
||||||
|
(defvar-local org-preview-html--html-file nil)
|
||||||
|
|
||||||
|
|
||||||
|
;; https://emacs.stackexchange.com/questions/7116/pop-a-window-into-a-frame
|
||||||
|
(defun org-preview-html-pop-window-to-frame ()
|
||||||
|
"Pop a window to a frame."
|
||||||
|
(interactive)
|
||||||
|
(let ((buffer (current-buffer)))
|
||||||
|
(unless (one-window-p)
|
||||||
|
(delete-window))
|
||||||
|
(display-buffer-pop-up-frame buffer nil)))
|
||||||
|
|
||||||
|
;; Taken from frame.el Emacs 27.1, copied here for better version compatibility.
|
||||||
|
;; Without this here 27.1 required. With, 25.1.
|
||||||
|
(defun org-preview-html--previous-window-any-frame ()
|
||||||
|
(select-window (previous-window (selected-window)
|
||||||
|
(> (minibuffer-depth) 0)
|
||||||
|
0))
|
||||||
|
(select-frame-set-input-focus (selected-frame)))
|
||||||
|
|
||||||
|
(defun org-preview-html-refresh ()
|
||||||
|
"Exports the org file to HTML and refreshes the preview."
|
||||||
|
(interactive)
|
||||||
|
(when (eq org-preview-html-refresh-configuration 'manual)
|
||||||
|
(pop-to-buffer org-preview-html--previewed-buffer-name nil t))
|
||||||
|
(org-preview-html--org-export-html)
|
||||||
|
(org-preview-html--reload-preview))
|
||||||
|
|
||||||
|
(defun org-preview-html--org-export-html ()
|
||||||
|
"Silently export org to HTML."
|
||||||
|
(let ((standard-output 'ignore))
|
||||||
|
(org-export-to-file 'html org-preview-html--html-file
|
||||||
|
nil org-preview-html-subtree-only nil nil nil nil)))
|
||||||
|
|
||||||
|
(defun org-preview-html--reload-preview ()
|
||||||
|
"Reload preview."
|
||||||
|
(save-selected-window
|
||||||
|
(pop-to-buffer org-preview-html--browser-buffer)
|
||||||
|
(cond ((eq org-preview-html-viewer 'xwidget) (xwidget-webkit-reload))
|
||||||
|
((eq org-preview-html-viewer 'eww)
|
||||||
|
(with-selected-window (selected-window)
|
||||||
|
;; This stuff is to keep eww window scrolled at same point
|
||||||
|
(let ((eww-point (point))
|
||||||
|
(eww-window-start (window-start)))
|
||||||
|
(eww-reload)
|
||||||
|
(goto-char eww-point)
|
||||||
|
(set-window-start nil eww-window-start)))))))
|
||||||
|
|
||||||
|
(defun org-preview-html--kill-preview-buffer ()
|
||||||
|
"Kill the preview buffer and pop back to the previewed org buffer."
|
||||||
|
;; Only do these things if the preview is around
|
||||||
|
(when (bound-and-true-p org-preview-html--browser-buffer)
|
||||||
|
;; If preview is visible we first delete the window, otherwise
|
||||||
|
;; just kill the preview buffer
|
||||||
|
(if (get-buffer-window org-preview-html--browser-buffer 'visible)
|
||||||
|
(delete-window (get-buffer-window org-preview-html--browser-buffer)))
|
||||||
|
(let ((kill-buffer-query-functions nil))
|
||||||
|
(kill-buffer org-preview-html--browser-buffer))
|
||||||
|
(pop-to-buffer org-preview-html--previewed-buffer-name)))
|
||||||
|
|
||||||
|
(defun org-preview-html--run-with-timer ()
|
||||||
|
"Configure timer to refresh preview for `timer' mode."
|
||||||
|
(setq org-preview-html--refresh-timer
|
||||||
|
(run-at-time 1 org-preview-html-timer-interval #'org-preview-html-refresh)))
|
||||||
|
|
||||||
|
(defun org-preview-html--config ()
|
||||||
|
"Configure buffer for preview: add exit hooks; configure refresh hooks."
|
||||||
|
(setq org-preview-html--previewed-buffer-name (buffer-name))
|
||||||
|
(dolist (hook '(kill-buffer-hook kill-emacs-hook)) ;; Configure exit hooks
|
||||||
|
(add-hook hook #'org-preview-html--stop-preview nil t))
|
||||||
|
(let ((conf org-preview-html-refresh-configuration))
|
||||||
|
(cond
|
||||||
|
((eq conf 'manual))
|
||||||
|
((eq conf 'save) ;; On save
|
||||||
|
(add-hook 'after-save-hook #'org-preview-html-refresh nil t))
|
||||||
|
((eq conf 'timer) ;; every X seconds
|
||||||
|
(org-preview-html--run-with-timer))
|
||||||
|
((eq conf 'export) ;; On export using org-html-export-html command manually
|
||||||
|
(advice-add 'org-html-export-to-html :after #'org-preview-html--reload-preview)))))
|
||||||
|
|
||||||
|
(defun org-preview-html--unconfig ()
|
||||||
|
"Unconfigure 'org-preview-html-mode' (remove hooks and advice)."
|
||||||
|
(let ((conf org-preview-html-refresh-configuration))
|
||||||
|
(cond ((eq conf 'save)
|
||||||
|
(remove-hook 'after-save-hook #'org-preview-html-refresh t))
|
||||||
|
((eq conf 'timer)
|
||||||
|
(cancel-timer org-preview-html--refresh-timer))
|
||||||
|
((eq conf 'export)
|
||||||
|
(advice-remove 'org-html-export-to-html #'org-preview-html--reload-preview))))
|
||||||
|
(dolist (hook '(kill-buffer-hook kill-emacs-hook)) ;; Remove hooks
|
||||||
|
(remove-hook hook #'org-preview-html--stop-preview t))
|
||||||
|
;; Reset variables
|
||||||
|
(dolist (var '(org-preview-html--browser-buffer org-preview-html--previewed-buffer-name))
|
||||||
|
(set var nil)))
|
||||||
|
|
||||||
|
(defun org-preview-html--open-browser ()
|
||||||
|
"Open a browser to preview the exported HTML file."
|
||||||
|
;; Store the exported HTML filename
|
||||||
|
(setq-local org-preview-html--html-file (concat (file-name-sans-extension buffer-file-name) ".html"))
|
||||||
|
(org-preview-html--org-export-html) ;; Export the org file to HTML
|
||||||
|
;; Procedure to open the side-by-side preview
|
||||||
|
(split-window-right)
|
||||||
|
(other-window 1)
|
||||||
|
(let ((file org-preview-html--html-file))
|
||||||
|
(cond ((eq org-preview-html-viewer 'xwidget) (xwidget-webkit-browse-url (concat "file://" file)))
|
||||||
|
((eq org-preview-html-viewer 'eww) (eww-open-file file))))
|
||||||
|
(setq org-preview-html--browser-buffer (get-buffer (buffer-name)))
|
||||||
|
(org-preview-html--previous-window-any-frame))
|
||||||
|
|
||||||
|
(defun org-preview-html--start-preview ()
|
||||||
|
"Begin the org-preview-html preview."
|
||||||
|
(when buffer-file-name
|
||||||
|
(cond ((derived-mode-p 'org-mode)
|
||||||
|
(message "org-preview-html has recieved a major update - xwidgets support, refresh configurations and more! \n M-x customize-group org-preview-html-mode")
|
||||||
|
(org-preview-html--open-browser)
|
||||||
|
(org-preview-html--config))
|
||||||
|
(t
|
||||||
|
(org-preview-html-mode -1)
|
||||||
|
(user-error "`%s' not supported by org-preview-html preview, only `org mode'!" major-mode)))))
|
||||||
|
|
||||||
|
(defun org-preview-html--stop-preview ()
|
||||||
|
"Stop the org-preview-html preview."
|
||||||
|
(org-preview-html--kill-preview-buffer)
|
||||||
|
(org-preview-html--unconfig))
|
||||||
|
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-minor-mode org-preview-html-mode
|
||||||
|
"(Optionally) live preview for Org exports to HTML."
|
||||||
|
:lighter " org-preview-html"
|
||||||
|
(if org-preview-html-mode
|
||||||
|
(org-preview-html--start-preview)
|
||||||
|
(org-preview-html--stop-preview)))
|
||||||
|
|
||||||
|
(provide 'org-preview-html)
|
||||||
|
|
||||||
|
;;; org-preview-html.el ends here
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
;; connects to LSP
|
;; connects to LSP
|
||||||
;; activate with `eglot'
|
;; activate with `eglot'
|
||||||
;; after changing configurations run `eglot-reconnect'
|
;; after changing configurations run `eglot-reconnect'
|
||||||
;; follow documentation at point `eldoc-doc-buffer'
|
;; follow documentation at point `eldoc-doc-buffer' / `display-local-help'
|
||||||
|
|
||||||
;; Language Server Protocol (LSP), python package `python-lsp-server'
|
;; Language Server Protocol (LSP), python package `python-lsp-server'
|
||||||
;; https://github.com/python-lsp/python-lsp-server
|
;; https://github.com/python-lsp/python-lsp-server
|
||||||
@@ -19,8 +19,10 @@
|
|||||||
;; arch linux: extra/python-lsp-server
|
;; arch linux: extra/python-lsp-server
|
||||||
;; - pycodestyle (formerly called pep8) - Python style guide checker
|
;; - pycodestyle (formerly called pep8) - Python style guide checker
|
||||||
;; https://pycodestyle.pycqa.org/en/latest/
|
;; https://pycodestyle.pycqa.org/en/latest/
|
||||||
|
;; to ignore violations see:
|
||||||
|
;; - https://flake8.pycqa.org/en/latest/user/violations.html
|
||||||
|
;; - https://flake8.pycqa.org/en/latest/user/configuration.html#configuration
|
||||||
;; arch linux: extra/python-pycodestyle
|
;; arch linux: extra/python-pycodestyle
|
||||||
;; to ignore a check in a code line use # noqa
|
|
||||||
;; config via ~/.config/pycodestyle
|
;; config via ~/.config/pycodestyle
|
||||||
;; - pylsp-mypy: pylsp plugin, type checking for Python >=3.8
|
;; - pylsp-mypy: pylsp plugin, type checking for Python >=3.8
|
||||||
;; https://mypy-lang.org/
|
;; https://mypy-lang.org/
|
||||||
@@ -28,6 +30,7 @@
|
|||||||
;; arch linux: aur/python-pylsp-mypy
|
;; arch linux: aur/python-pylsp-mypy
|
||||||
|
|
||||||
;; Syntax Checking, Emacs built-in package `flymake'
|
;; Syntax Checking, Emacs built-in package `flymake'
|
||||||
|
;; (flymake-running-backends) -> (eglot-flymake-backend)
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
(use-package python
|
(use-package python
|
||||||
|
|||||||
Reference in New Issue
Block a user