Is there a Elisp function that deletes "redundant" leading whitespace of the region while maintaining relative indentation.
This behavior is like textwrap.dedent
in python.
https://docs.python.org/2/library/textwrap.html#textwrap.dedent
Is there a Elisp function that deletes "redundant" leading whitespace of the region while maintaining relative indentation.
This behavior is like textwrap.dedent
in python.
https://docs.python.org/2/library/textwrap.html#textwrap.dedent
This is a variant of my answer to copy region without leading indentation which acts directly in the original buffer.
(defun my-unindent-region (pad beginning end)
"Un-indent the region by the length of its minimum indent.
If numeric prefix argument PAD is supplied, indent the resulting
text by that amount."
(interactive "P\nr")
(save-excursion
;; Establish the minimum level of indentation.
(let ((indent nil))
(goto-char beginning)
(while (and (re-search-forward "^[[:space:]\n]*" nil :noerror)
(< (point) end))
(let ((length (current-column)))
(when (or (not indent) (< length indent))
(setq indent length)))
(forward-line 1))
(if (not indent)
(error "Region is entirely whitespace")
;; Un-indent the buffer contents by the length of the minimum
;; indent level.
(when pad
(setq indent (- indent (prefix-numeric-value pad))))
(indent-rigidly beginning end (- indent))))))
N.B. See the answer by phils and its linked inspiration for a similar but more general solution.
The command indent-rigidly
comes quite close to the desired functionality:
indent-rigidly is an interactive compiled Lisp function in
‘indent.el’.
It is bound to C-x TAB.
(indent-rigidly START END ARG &optional INTERACTIVE)
Indent all lines starting in the region.
If called interactively with no prefix argument, activate a
transient mode in which the indentation can be adjusted interactively
by typing <left>, <right>, <S-left>, or <S-right>.
Typing any other key deactivates the transient mode.
If called from a program, or interactively with prefix ARG,
indent all lines starting in the region forward by ARG columns.
If called from a program, START and END specify the beginning and
end of the text to act on, in place of the region.
Negative values of ARG indent backward, so you can remove all
indentation by specifying a large negative ARG.
but unfortunately a large enough ARG
will change the relative indentation of lines. A workaround is to dedent by (i.e. pass a negative ARG
of absolute value equal to) the smallest indentation level in the region:
(defun my-dedent (&optional start end)
"Delete all common indentation between START and END.
If called interactively, START and END are the start/end of the
region if the mark is active, or of the buffer's accessible
portion if the mark is inactive.
Blank lines are ignored."
(interactive (and (use-region-p)
(list (region-beginning)
(region-end))))
(setq start (or start (point-min)))
(setq end (or end (point-max)))
(undo-boundary)
(indent-rigidly start end
(- (indent-rigidly--current-indentation start end))))
indent-rigidly--current-indentation
. That seems like a function which shouldn't really be marked as internal.
– phils
Nov 14 '17 at 1:14
indent-rigidly--current-indentation
seems too useful to be marked as internal, but the fact of the matter is it is, so one should use it with caution. If there are no objections, I'll leave my answer open purely as an example of an alternative approach.
– Basil
Nov 14 '17 at 1:27
indent-rigidly--current-indentation
calls current-indentation
, whereas I was using current-column
in my answer. I haven't tested, but my gut tells me that the former is going to be more correct, given that we're both calling indent-rigidly
.
– phils
Nov 14 '17 at 4:18
current-column
would not DTRT in certain edge cases, as it handles more scenarios. You could probably use current-indentation
as a drop-in replacement for current-column
in your answer, though, no?
– Basil
Nov 14 '17 at 12:05
C-x TAB
with a numeric prefix arg (for the number of columns to outdent). – Drew Nov 13 '17 at 15:19whitespace-mode
. – xmonk Nov 13 '17 at 23:26