edit-indirect
edit-indirect copied to clipboard
Preserve indentation based on the beginning of the buffer
Let's say I'm editing a "json" region inside a yaml file
/SearchMp:
post:
description: Search resource meters at given address
body:
application/json:
type: !include ../lib/search/schema.json
example: |
{
"postalcode": "1011AB",
"number": 105,
"status": {
"attributes": {
"customerType": {
"value": "individual"
}
}
}
}
I would like to select the example, edit it and then have it aligned like in the example above. Instead, the body is aligned like in the indirect buffer, which is to say to the very left margin.
org-mode
can do this properly so I think it would be possible to steal some code from there, but in either case shouldn't be very difficult. I might try to hack a PR together, would this be accepted?
Well, I've fixed it with this code
(defun my-after-indirect-edit-realign (beg end)
(save-excursion
(goto-char beg)
(let ((cc (current-column))
(end-marker (set-marker (make-marker) end)))
(while (< (progn
(forward-line)
(point)) end-marker)
(line-beginning-position)
(insert (make-string cc 32)))
(when indent-tabs-mode
(tabify beg end-marker))
(set-marker end-marker nil))))
(add-hook 'edit-indirect-after-commit-functions 'my-after-indirect-edit-realign)
Might get bundled along for people to enable or not.
I might try to hack a PR together, would this be accepted?
Sure.
I am using this for similar effect:
(require 's)
(require 'dash)
(defvar edit-indirect--left-margin 0)
(defun vbe:compute-left-margin (code)
"Compute left margin of a string of code."
(-min
(-map #'(lambda (line) (length (car (s-match "^\\s-*" line))))
(-remove 's-blank? (s-lines code)))))
(defun vbe:after-indirect-edit-remove-left-margin ()
"Remove left-margin and save it into a local variable."
(let ((lm (vbe:compute-left-margin (buffer-substring (point-min) (point-max)))))
(indent-rigidly (point-min) (point-max) (* -1 lm))
(setq-local edit-indirect--left-margin lm)))
(defun vbe:after-indirect-edit-restore-left-margin ()
"Restore left-margin before commiting."
(indent-rigidly (point-min) (point-max) edit-indirect--left-margin))
(add-hook 'edit-indirect-after-creation-hook #'vbe:after-indirect-edit-remove-left-margin)
(add-hook 'edit-indirect-before-commit-hook #'vbe:after-indirect-edit-restore-left-margin)
@vincentbernat thanks, this works very well! May I suggest adding
(put 'edit-indirect--left-margin 'permanent-local t) ;; don't lose this setting when another major mode is started
to keep the value even if another major-mode is started.
I was looking for something similar to this and was working on my own when I hit a wall:
It's easy to trim the indent in the *-after-creation-hook
and restore it in *-before-commit-hook
, but that doesn't play well with edit-indirect-save
: the indent will be restored every time you save, but it is never trimmed after creation, leading to multiply stacked indents.
There doesn't seem to be a good way to re-trim after saving; it seems an edit-indirect-after-commit-hook
is called for. Would you consider accepting a PR for that?
I should note that my trimming needs are a more complex version of @Fuco1's described above: I have a JavaScript application where I am often writing SQL in a string:
function doQuery() {
...
const query = `
SELECT
foo,
bar,
FROM table
WHERE condition1
AND condition2
ORDER BY something DESC
`;
...
}
I use thing-at-point-bounds-of-string-at-point
to grab the whole string (from `
to `
) and indirectly edit that; then I trim leading and trailing blank lines, and only then do I remove the longest common indent prefix. I retain the actual strings to restore, rather than a length.
I think this algorithm should be generally useful, but maybe there are use cases it doesn't handle well.
Edit: Here is my implementation: https://github.com/amake/.emacs.d/blob/ddb84ffa5cd6d4cda378e6e59a5e4cc273b6446d/lisp/edit-string.el
And configuration: https://github.com/amake/.emacs.d/blob/ddb84ffa5cd6d4cda378e6e59a5e4cc273b6446d/init.el#L1333-L1346
@vincentbernat thanks, this works very well! May I suggest adding
(put 'edit-indirect--left-margin 'permanent-local t) ;; don't lose this setting when another major mode is started
to keep the value even if another major-mode is started.
I was looking for something similar to this and was working on my own when I hit a wall:
It's easy to trim the indent in the
*-after-creation-hook
and restore it in*-before-commit-hook
, but that doesn't play well withedit-indirect-save
: the indent will be restored every time you save, but it is never trimmed after creation, leading to multiply stacked indents.There doesn't seem to be a good way to re-trim after saving; it seems an
edit-indirect-after-commit-hook
is called for. Would you consider accepting a PR for that?
Here's my config based on @vincentbernat one with points above considered:
;; <-------------------------
;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-387945773
(require 's)
(require 'dash)
(defvar edit-indirect--left-margin 0)
(defun vbe/compute-left-margin (code)
"Compute left margin of a string of code."
(-min
(-map #'(lambda (line) (length (car (s-match "^\\s-*" line))))
(-remove 's-blank? (s-lines code)))))
(defun vbe/edit-indirect/remove-left-margin ()
"Remove left-margin and save it into a local variable."
(let ((lm (vbe/compute-left-margin (buffer-substring (point-min) (point-max)))))
(indent-rigidly (point-min) (point-max) (* -1 lm))
(setq-local edit-indirect--left-margin lm)
;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-1055542145
;; buffer-local variable whose value should not be reset when changing major modes
(put 'edit-indirect--left-margin 'permanent-local t)))
(defun vbe/edit-indirect/restore-left-margin ()
"Restore left-margin before commiting."
(indent-rigidly (point-min) (point-max) edit-indirect--left-margin))
(add-hook 'edit-indirect-after-creation-hook #'vbe/edit-indirect/remove-left-margin)
(add-hook 'edit-indirect-before-commit-hook #'vbe/edit-indirect/restore-left-margin)
(require 'edit-indirect)
;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-1284144173
(define-key edit-indirect-mode-map [remap save-buffer] #'edit-indirect-commit)
;; >-------------------------