separedit.el
Edit comment/string/docstring/code block in separate buffer with your favorite mode.
+----------+ Edit +-----------+ Edit +-----------+
| | ---------------------> | edit | ---------------------> | edit | ...
| | point-at-comment? | buffer | point-at-comment? | buffer |
| source | point-at-string? | | point-at-string? | | ...
| buffer | point-at-codeblock? | (markdown | point-at-codeblock? | (markdown | ...
| | point-at-...? | orgmode | point-at-...? | orgmode |
| | <--------------------- | ...) | <--------------------- | ...) | ...
+----------+ Commit changes +-----------+ Commit changes +-----------+
Installation
Clone this repository, or install from MELPA. Add the following to your
.emacs
:
(require 'separedit)
;; Key binding for modes you want edit
;; or simply bind ‘global-map’ for all.
(define-key prog-mode-map (kbd "C-c '") #'separedit)
(define-key minibuffer-local-map (kbd "C-c '") #'separedit)
(define-key help-mode-map (kbd "C-c '") #'separedit)
(define-key helpful-mode-map (kbd "C-c '") #'separedit)
;; Default major-mode for edit buffer
;; can also be other mode e.g. ‘org-mode’.
(setq separedit-default-mode 'markdown-mode)
;; Feature options
;; (setq separedit-preserve-string-indentation t)
;; (setq separedit-continue-fill-column t)
;; (setq separedit-write-file-when-execute-save t)
;; (setq separedit-remove-trailing-spaces-in-comment t)
Usage
-
Move the cursor to a comment/string/code block or any supported place.
-
Press C-c '.
or press C-u C-c ' to starting edit with manually selected major mode.
Can also press C-c ' on an active region.
Following are default keys in edit buffer:
Key | Function | Summary |
---|---|---|
C-c C-c | separedit-commit |
Commit changes and close edit buffer |
C-x C-s | separedit-save |
Commit changes (even write source file) without closing edit buffer |
C-c C-k | separedit-abort |
Discard changes and close edit buffer |
C-c ' | separedit or follow the settings of markdown/org |
Open a new edit buffer |
Edit comment
separedit
use continuity as basis for determining whether it is a comment
block or line. Continuous means that there is no barrier (e.g. code or
blank line) between the end of previous line and the beginning of next line, for
example:
/*
* this is a
* comment block
*/
//
// this is also a
// comment block
//
//
// this is another
// comment block
//
code 1 /* all this are comment lines */
code 2 /* all this are comment lines */
code 3 // all this are comment lines
code 4 // all this are comment lines
By setting separedit-default-mode
to choose the mode (e.g. markdown-mode
or
org-mode
) for edit buffer. In edit buffer, the comment delimiter will be
removed, for example (â–ˆ represents the cursor):
source buffer -> edit buffer -> edit buffer
/*
* # Exampleâ–ˆ # Example
*
* ``` C ``` C
* foo("bar"); foo("bar");â–ˆ foo("bar");
* ``` ```
*/
// * Exampleâ–ˆ * Example
//
// #+BEGIN_SRC C #+BEGIN_SRC C
// foo("bar"); foo("bar");â–ˆ foo("bar");
// #+END_SRC #+END_SRC
Edit string
separedit
provides convenience for editing escaped strings, if there are
nested string or code block, just continue press C-c ' to enter a new
edit buffer:
source buffer -> edit buffer -> edit buffer
"aâ–ˆ\"b\\\"c\\\"\"" a"bâ–ˆ\"c\"" b"c"
Edit code block
separedit
also support for editing code block directly in comment or string:
source buffer -> edit buffer
",--- elisp
| (foo \"bar\")â–ˆ (foo "bar")
`---"
/*
* ``` C
* foo("bar");â–ˆ foo("bar");
* ```
*/
If the language identifier of code block is omitted, the edit buffer uses the same mode as the source buffer.
Edit heredoc
The heredoc marker can be used to specify the language:
source buffer -> edit buffer (css-mode)
...<<CSS
h1 { h1 {
color: red;â–ˆ color: red;â–ˆ
} }
CSS
Both LANG
and __LANG__
are supported, see
separedit-heredoc-language-regexp-alist
for more detail.
Edit C/C++ macro
#defineâ–ˆFOO(a, b) \ -> #defineâ–ˆFOO(a, b)
do { \ do {
auto _a = (a); \ auto _a = (a);
auto _b = (b); \ auto _b = (b);
} while (false) } while (false)
Edit value form of variable in help/helpful buffer
Describe a variable, move cursor to the local/global value form, press C-c ' to edit it.
Edit minibuffer
Don't get stuck in minibuffer, press C-c ' to open a edit buffer.
Edit in vterm
Make sure the the vterm Directory tracking and Prompt tracking is set correctly.
Then put the cursor after prompt, press C-c ' to start a new edit, or C-p C-c ' to edit previous command.
Customization
Change key bindings in edit buffer
If you don't like the default key bindings in edit buffer, you can change it:
separedit-save-key
separedit-entry-key
separedit-abort-key
separedit-commit-key
Add support for a new major mode
- Add the start/end delimiter of block style comment to
separedit-comment-encloser-alist
. - Add the delimiter of each comment line to
separedit-comment-delimiter-alist
. - Add the string (including docstring) quotes to
separedit-string-quotes-alist
. - Add definition to
separedit-string-indent-offset-alist
if there is base indent offset in docstring. - Add a mode name to
separedit-not-support-docstring-modes
if not support docstring.
Add support for a new code block
- Add a set of regexps matching the new code block to
separedit-block-regexp-plists
. - Add a language name to
separedit-code-lang-modes
if can't get mode by simply adding suffix-mode
.
Preserving indentation of block in string
If separedit-preserve-string-indentation
is non-nil, the indentation of string
block will be preseved in edit buffer, e.g:
source buffer edit buffer
+--------------------+ +--------------------+
| def func(): | | Usage: |
| ''' | | func() |
| Usage: | -> | |
| func() | | |
| ''' | | |
| pass | | |
+====================+ +====================+
No only for the docsting, normal string are also supported:
source buffer edit buffer
+--------------------+ +--------------------+
| emacs \ | | (progn |
| --batch \ | | ...) |
| --eval "(progn | -> | |
| ...)" | | |
| | | |
+====================+ +====================+
Continue fill-column width in edit buffer
If separedit-continue-fill-column
is non-nil, use the remaining fill-width in
edit buffer:
source buffer edit buffer
//
// this is a this is a
// comment block comment block
//
|<---->|<------------>| |<------------->|
| | |
| '-- available width for --'
| edit buffer
used width
You may also like to enable auto-fill-mode
in edit buffer:
(add-hook 'separedit-buffer-creation-hook #'auto-fill-mode)
Some extended usage
Combine multipe adjacent blocks as a single edit block
(defun separedit//region-of-el-commentary ()
(save-excursion
(goto-char (point-min))
(when (re-search-forward "^;;; Commentary:\n+")
(let ((begin (point)))
(when (re-search-forward "\n;;; .*$" nil t)
(goto-char (match-beginning 0))
(list begin (point)))))))
(defun separedit/edit-el-commentary ()
"Edit whole commentary section as a single block."
(interactive)
(let ((separedit-leave-blank-line-in-comment t))
(separedit-dwim
(apply #'separedit-mark-region
`(,@(separedit//region-of-el-commentary)
markdown-mode)))))
Break long lines in comment
(defun separedit/re-fill ()
(interactive)
(let ((separedit-continue-fill-column t))
(with-current-buffer (separedit-dwim)
(fill-region (point-min) (point-max))
(execute-kbd-macro (kbd "C-c C-c")))))
Eval multiple-line sexp in comment
(defun separedit/eval-last-sexp-in-comment ()
(interactive)
(let ((separedit-default-mode 'emacs-lisp-mode)
(separedit-inhibit-edit-window-p t))
(with-current-buffer (separedit)
(unwind-protect (call-interactively #'eval-last-sexp)
(separedit-abort)))))
(define-key emacs-lisp-mode-map (kbd "C-x C-e")
(lambda ()
(interactive)
(call-interactively
(if (separedit--point-at-comment)
#'separedit/eval-last-sexp-in-comment
#'eval-last-sexp))))