6

When I'm in the middle of implementing a new function, I like to narrow my source buffer to just that function (C-x n d). However, this leaves the function indented as it was.

I would like to have the function aligned to the left, but returned to its original position when the buffer is widened again. Example:

Default

        function foo(bar) {
            return 'baz';
        }

Desired

function foo(bar) {
    return 'baz';
}

Is there an existing method to accomplish this?

Drew
  • 75,699
  • 9
  • 109
  • 225
J David Smith
  • 2,635
  • 1
  • 16
  • 27
  • Do you indent with tabs or spaces or a combination of both? – lawlist May 22 '15 at 17:59
  • Spaces is the convention in JS-land (where I mostly work), so I use those. I'm open to either. If there isn't an existing solution, then I have an idea for how to implement it with advice, but I wanted to check first. – J David Smith May 22 '15 at 18:02

3 Answers3

3

Both @andreas-röhler and @drew had reasonable answers. However, I wasn't happy with either of them on their own.

The problems I had were:

  • Both leave stuff on the undo stack, which interferes with editing and can neadlessly mark a buffer as dirty.
  • @drew's requires rebinding, which I'd like to avoid.

So, what I did is make my own based mostly on @drew's code. It defines a minor mode narrow-reindent-mode which advises narrow-to-* and widen to use indent-rigidly to perform alignment.

The full code is ~70 lines so I won't post it here, but it is in this gist.

The heart of it is narrow-reindent--after-narrow:

(defun narrow-reindent--after-narrow (&rest _r)
  "Indent narrowed buffer. This function is used as advice for
`narrow-to-defun' and friends."
  (when narrow-reindent-mode
    (let ((beg (point-min))
          (end (point-max)))
      (setq narrow-reindent--point-min beg)
      (setq narrow-reindent--point-max end)
      (setq narrow-reindent--indent-amount (indent-rigidly--current-indentation beg end))
      (without-undo
       (indent-rigidly beg end (- narrow-reindent--indent-amount))))))

It works with both narrow-to-defun and narrow-to-region (have not tested narrow-to-page).

EDIT: This has been turned into a full-fledged package and is available on MELPA and MELPA Stable as narrow-reindent. The current repository is here.

J David Smith
  • 2,635
  • 1
  • 16
  • 27
2

For the first part advise narrow-to-defun with that:

(defvar my-indent-narrowed-region-counter 0) 

(defun my-indent-narrowed-region ()
  (interactive "*")
  (setq my-indent-narrowed-region-counter 0)
  (save-excursion
    (goto-char (point-min))
    (while (< 0 (current-indentation))
      (setq my-indent-narrowed-region-counter (1+ my-indent-narrowed-region-counter))
      (indent-rigidly (point-min) (point-max) -1))))

Now widen needs a reciproke advise called before, taking the my-indent-narrowed-region-counter for re-indent.

Scott Weldon
  • 2,695
  • 1
  • 17
  • 31
Andreas Röhler
  • 1,894
  • 10
  • 10
  • I just tested and selecting the narrowed region and re-indenting correctly left-aligns the content. I suppose I just need to locate the offset and do this myself then. – J David Smith May 22 '15 at 20:01
0

Bind a key to toggle-removing-indentation.

(defvar saved-indent nil
  "Start, end, and indentation from last `narrow-and-outdent' here.")
(make-variable-buffer-local 'saved-indent)

(defun toggle-removing-indentation ()
  "Toggle removal of indentation for the region.
When removed, the least indented line of region is moved to bol,
and relative indentation is preserved within the region."
  (interactive)
  (if saved-indent (widen-and-indent) (narrow-and-outdent)))

(defun narrow-and-outdent ()
  "Narrow region and outdent to left margin, keeping relative indentation."
  (interactive)
  (unless (use-region-p) (error "No non-empty active region"))
  (let ((beg  (save-excursion (goto-char (region-beginning)) (line-beginning-position)))
        (end  (region-end)))
    (narrow-to-region beg end)
    (setq saved-indent  `(,beg ,end ,(indent-rigidly--current-indentation beg end)))
    (indent-rigidly beg end (- (nth 2 saved-indent)))))

(defun widen-and-indent ()
  "Widen, restoring indentation before last `narrow-and-outdent' here."
  (interactive)
  (let ((beg   (nth 0 saved-indent))
        (end   (nth 1 saved-indent))
        (dent  (nth 2 saved-indent)))
    (indent-rigidly beg end dent))
  (setq saved-indent  nil)
  (widen))

(It's not clear to me why you want to do this, however.)

Note that if you want to remove indentation from more than one region in the same buffer, and you don't want to widen in between, you can do that using command narrow-and-outdent for each region.

Drew
  • 75,699
  • 9
  • 109
  • 225