Commit b7d5bd82 authored by Teemu Likonen's avatar Teemu Likonen Committed by Stefan Monnier
Browse files

* indent.el: Provide interactive indent-rigidly mode. Use lexical-binding.

(indent-rigidly--current-indentation): New function.
(indent-rigidly-map): New var.
(indent-rigidly): Use it to provide interactive mode.
(indent-region): Add progress reporter.
(tab-stop-list): Make it implicitly extend to infinity by repeating the
last step.
(indent--next-tab-stop): New function to implement this behavior.
(tab-to-tab-stop, move-to-tab-stop): Use it.

Fixes: debbugs:8196
parent d180bde8
...@@ -163,6 +163,13 @@ and this variable has been marked obsolete. ...@@ -163,6 +163,13 @@ and this variable has been marked obsolete.
* Editing Changes in Emacs 24.4 * Editing Changes in Emacs 24.4
** C-x TAB enters a transient interactive mode.
You can then use the left/right cursor keys to move the block of text.
** `tab-stop-list' is now implicitly extended to infinity.
Its default value is changed to nil which means a tab stop every
`tab-width' columns.
** `split-window' is no longer a command, just a non-interactive function. ** `split-window' is no longer a command, just a non-interactive function.
As a command it was a special case of `split-window-below', and as such As a command it was a special case of `split-window-below', and as such
superfluous. After being reimplemented in Lisp, its interactive form superfluous. After being reimplemented in Lisp, its interactive form
......
2013-10-08 Stefan Monnier <monnier@iro.umontreal.ca>
* indent.el: Use lexical-binding.
(indent-region): Add progress reporter.
(tab-stop-list): Make it implicitly extend to infinity by repeating the
last step.
(indent--next-tab-stop): New function to implement this behavior.
(tab-to-tab-stop, move-to-tab-stop): Use it.
2013-10-08 Teemu Likonen <tlikonen@iki.fi>
* indent.el (indent-rigidly--current-indentation): New function.
(indent-rigidly-map): New var.
(indent-rigidly): Use it to provide interactive mode (bug#8196).
2013-10-08 Bastien Guerry <bzg@gnu.org> 2013-10-08 Bastien Guerry <bzg@gnu.org>
* register.el (insert-register): Fix 2013-10-07T01:28:34Z!sdl.web@gmail.com. * register.el (insert-register): Fix 2013-10-07T01:28:34Z!sdl.web@gmail.com.
......
;;; indent.el --- indentation commands for Emacs ;;; indent.el --- indentation commands for Emacs -*- lexical-binding:t -*-
;; Copyright (C) 1985, 1995, 2001-2013 Free Software Foundation, Inc. ;; Copyright (C) 1985, 1995, 2001-2013 Free Software Foundation, Inc.
...@@ -154,27 +154,68 @@ prefix argument is ignored." ...@@ -154,27 +154,68 @@ prefix argument is ignored."
(insert-char ?\t count) (insert-char ?\t count)
(indent-to (* tab-width (+ count (/ (current-column) tab-width))))))) (indent-to (* tab-width (+ count (/ (current-column) tab-width)))))))
(defun indent-rigidly (start end arg) (defun indent-rigidly--current-indentation (beg end)
"Return the smallest indentation in range from BEG to END.
Blank lines are ignored."
(save-excursion
(save-match-data
(let ((beg (progn (goto-char beg) (line-beginning-position)))
indent)
(goto-char beg)
(while (re-search-forward "^\\s-*[[:print:]]" end t)
(setq indent (min (or indent (current-indentation))
(current-indentation))))
indent))))
(defvar indent-rigidly-map
(let ((map (make-sparse-keymap)))
(define-key map [left]
(lambda (beg end) (interactive "r") (indent-rigidly beg end -1)))
(define-key map [right]
(lambda (beg end) (interactive "r") (indent-rigidly beg end 1)))
(define-key map [S-right]
(lambda (beg end) (interactive "r")
(let* ((current (indent-rigidly--current-indentation beg end))
(next (indent--next-tab-stop current)))
(indent-rigidly beg end (- next current)))))
(define-key map [S-left]
(lambda (beg end) (interactive "r")
(let* ((current (indent-rigidly--current-indentation beg end))
(next (indent--next-tab-stop current 'prev)))
(indent-rigidly beg end (- next current)))))
map))
(defun indent-rigidly (start end arg &optional interactive)
"Indent all lines starting in the region sideways by ARG columns. "Indent all lines starting in the region sideways by ARG columns.
Called from a program, takes three arguments, START, END and ARG. Called from a program, takes three arguments, START, END and ARG.
You can remove all indentation from a region by giving a large negative ARG." You can remove all indentation from a region by giving a large negative ARG.
(interactive "r\np") If used interactively and no prefix argument is given, use a transient
(save-excursion mode that lets you move the text with cursor keys."
(goto-char end) (interactive "r\nP\np")
(setq end (point-marker)) (if (and (not arg) interactive)
(goto-char start) (progn
(or (bolp) (forward-line 1)) (message "Edit region indentation with <left>, <right>, <S-left> \
(while (< (point) end) and <S-right>.")
(let ((indent (current-indentation)) (set-temporary-overlay-map indent-rigidly-map t))
eol-flag) (save-excursion
(save-excursion (goto-char end)
(skip-chars-forward " \t") (setq end (point-marker))
(setq eol-flag (eolp))) (goto-char start)
(or eol-flag (or (bolp) (forward-line 1))
(indent-to (max 0 (+ indent arg)) 0)) (while (< (point) end)
(delete-region (point) (progn (skip-chars-forward " \t") (point)))) (let ((indent (current-indentation))
(forward-line 1)) eol-flag)
(move-marker end nil))) (save-excursion
(skip-chars-forward " \t")
(setq eol-flag (eolp)))
(or eol-flag
(indent-to (max 0 (+ indent arg)) 0))
(delete-region (point) (progn (skip-chars-forward " \t") (point))))
(forward-line 1))
(move-marker end nil))))
(defun indent-line-to (column) (defun indent-line-to (column)
"Indent current line to COLUMN. "Indent current line to COLUMN.
...@@ -405,6 +446,7 @@ If the third argument COLUMN is an integer, it specifies the ...@@ -405,6 +446,7 @@ If the third argument COLUMN is an integer, it specifies the
column to indent to; if it is nil, use one of the three methods above." column to indent to; if it is nil, use one of the three methods above."
(interactive "r\nP") (interactive "r\nP")
(cond (cond
;; If a numeric prefix is given, indent to that column.
(column (column
(setq column (prefix-numeric-value column)) (setq column (prefix-numeric-value column))
(save-excursion (save-excursion
...@@ -416,8 +458,9 @@ column to indent to; if it is nil, use one of the three methods above." ...@@ -416,8 +458,9 @@ column to indent to; if it is nil, use one of the three methods above."
(delete-region (point) (progn (skip-chars-forward " \t") (point))) (delete-region (point) (progn (skip-chars-forward " \t") (point)))
(or (eolp) (or (eolp)
(indent-to column 0)) (indent-to column 0))
(forward-line 1)) (forward-line 1))
(move-marker end nil))) (move-marker end nil)))
;; If a fill-prefix is specified, use it.
(fill-prefix (fill-prefix
(save-excursion (save-excursion
(goto-char end) (goto-char end)
...@@ -429,17 +472,23 @@ column to indent to; if it is nil, use one of the three methods above." ...@@ -429,17 +472,23 @@ column to indent to; if it is nil, use one of the three methods above."
(and (bolp) (eolp)) (and (bolp) (eolp))
(insert fill-prefix)) (insert fill-prefix))
(forward-line 1))))) (forward-line 1)))))
;; Use indent-region-function is available.
(indent-region-function (indent-region-function
(funcall indent-region-function start end)) (funcall indent-region-function start end))
;; Else, use a default implementation that calls indent-line-function on
;; each line.
(t (t
(save-excursion (save-excursion
(setq end (copy-marker end)) (setq end (copy-marker end))
(goto-char start) (goto-char start)
(let ((pr (make-progress-reporter "Indenting region..." (point) end)))
(while (< (point) end) (while (< (point) end)
(or (and (bolp) (eolp)) (or (and (bolp) (eolp))
(indent-according-to-mode)) (indent-according-to-mode))
(forward-line 1)) (forward-line 1)
(move-marker end nil)))) (progress-reporter-update pr (point)))
(progress-reporter-done pr)
(move-marker end nil)))))
;; In most cases, reindenting modifies the buffer, but it may also ;; In most cases, reindenting modifies the buffer, but it may also
;; leave it unmodified, in which case we have to deactivate the mark ;; leave it unmodified, in which case we have to deactivate the mark
;; by hand. ;; by hand.
...@@ -493,9 +542,12 @@ See also `indent-relative-maybe'." ...@@ -493,9 +542,12 @@ See also `indent-relative-maybe'."
(tab-to-tab-stop)))) (tab-to-tab-stop))))
(defcustom tab-stop-list (defcustom tab-stop-list
'(8 16 24 32 40 48 56 64 72 80 88 96 104 112 120) nil
"List of tab stop positions used by `tab-to-tab-stop'. "List of tab stop positions used by `tab-to-tab-stop'.
This should be a list of integers, ordered from smallest to largest." This should be a list of integers, ordered from smallest to largest.
It implicitly extends to infinity by repeating the last step (e.g. '(1 2 5)
is equivalent to '(1 2 5 8 11)).
If the list has less than 2 elements, `tab-width' is used as the \"last step\"."
:group 'indent :group 'indent
:type '(repeat integer)) :type '(repeat integer))
(put 'tab-stop-list 'safe-local-variable 'listp) (put 'tab-stop-list 'safe-local-variable 'listp)
...@@ -520,8 +572,7 @@ You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops ...@@ -520,8 +572,7 @@ You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops
(setq edit-tab-stops-buffer (current-buffer)) (setq edit-tab-stops-buffer (current-buffer))
(switch-to-buffer (get-buffer-create "*Tab Stops*")) (switch-to-buffer (get-buffer-create "*Tab Stops*"))
(use-local-map edit-tab-stops-map) (use-local-map edit-tab-stops-map)
(make-local-variable 'indent-tabs-mode) (setq-local indent-tabs-mode nil)
(setq indent-tabs-mode nil)
(overwrite-mode 1) (overwrite-mode 1)
(setq truncate-lines t) (setq truncate-lines t)
(erase-buffer) (erase-buffer)
...@@ -557,6 +608,29 @@ You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops ...@@ -557,6 +608,29 @@ You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops
(setq tab-stop-list tabs)) (setq tab-stop-list tabs))
(message "Tab stops installed")) (message "Tab stops installed"))
(defun indent--next-tab-stop (column &optional prev)
"Return the next tab stop after COLUMN.
If PREV is non-nil, return the previous one instead."
(let ((tabs tab-stop-list))
(while (and tabs (>= column (car tabs)))
(setq tabs (cdr tabs)))
(if tabs
(if (not prev)
(car tabs)
(let ((prevtabs (cdr (memq (car tabs) (reverse tab-stop-list)))))
(if (null prevtabs) 0
(if (= column (car prevtabs))
(or (nth 1 prevtabs) 0)
(car prevtabs)))))
;; We passed the end of tab-stop-list: guess a continuation.
(let* ((last2 (last tab-stop-list 2))
(step (if (cdr last2) (- (cadr last2) (car last2)) tab-width))
(last (or (cadr last2) (car last2) 0)))
;; Repeat the last tab's length.
(+ last (* step (if prev
(if (<= column last) -1 (/ (- column last 1) step))
(1+ (/ (- column last) step)))))))))
(defun tab-to-tab-stop () (defun tab-to-tab-stop ()
"Insert spaces or tabs to next defined tab-stop column. "Insert spaces or tabs to next defined tab-stop column.
The variable `tab-stop-list' is a list of columns at which there are tab stops. The variable `tab-stop-list' is a list of columns at which there are tab stops.
...@@ -564,37 +638,29 @@ Use \\[edit-tab-stops] to edit them interactively." ...@@ -564,37 +638,29 @@ Use \\[edit-tab-stops] to edit them interactively."
(interactive) (interactive)
(and abbrev-mode (= (char-syntax (preceding-char)) ?w) (and abbrev-mode (= (char-syntax (preceding-char)) ?w)
(expand-abbrev)) (expand-abbrev))
(let ((tabs tab-stop-list)) (let ((nexttab (indent--next-tab-stop (current-column))))
(while (and tabs (>= (current-column) (car tabs))) (delete-horizontal-space t)
(setq tabs (cdr tabs))) (indent-to nexttab)))
(if tabs
(progn
(delete-horizontal-space t)
(indent-to (car tabs)))
(insert ?\s))))
(defun move-to-tab-stop () (defun move-to-tab-stop ()
"Move point to next defined tab-stop column. "Move point to next defined tab-stop column.
The variable `tab-stop-list' is a list of columns at which there are tab stops. The variable `tab-stop-list' is a list of columns at which there are tab stops.
Use \\[edit-tab-stops] to edit them interactively." Use \\[edit-tab-stops] to edit them interactively."
(interactive) (interactive)
(let ((tabs tab-stop-list)) (let ((nexttab (indent--next-tab-stop (current-column))))
(while (and tabs (>= (current-column) (car tabs))) (let ((before (point)))
(setq tabs (cdr tabs))) (move-to-column nexttab t)
(if tabs (save-excursion
(let ((before (point))) (goto-char before)
(move-to-column (car tabs) t) ;; If we just added a tab, or moved over one,
(save-excursion ;; delete any superfluous spaces before the old point.
(goto-char before) (if (and (eq (preceding-char) ?\s)
;; If we just added a tab, or moved over one, (eq (following-char) ?\t))
;; delete any superfluous spaces before the old point. (let ((tabend (* (/ (current-column) tab-width) tab-width)))
(if (and (eq (preceding-char) ?\s) (while (and (> (current-column) tabend)
(eq (following-char) ?\t)) (eq (preceding-char) ?\s))
(let ((tabend (* (/ (current-column) tab-width) tab-width))) (forward-char -1))
(while (and (> (current-column) tabend) (delete-region (point) before)))))))
(eq (preceding-char) ?\s))
(forward-char -1))
(delete-region (point) before))))))))
(define-key global-map "\t" 'indent-for-tab-command) (define-key global-map "\t" 'indent-for-tab-command)
(define-key esc-map "\C-\\" 'indent-region) (define-key esc-map "\C-\\" 'indent-region)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment