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 +-----------+
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)
-
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 |
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
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"
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.
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.
#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)
Describe a variable, move cursor to the local/global value form, press C-c ' to edit it.
Don't get stuck in minibuffer, press C-c ' to open a 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 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 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
.
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 | -> | |
| ...)" | | |
| | | |
+====================+ +====================+
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)
(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)))))
(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")))))
(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)
(edit-indirect-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))))