;;; vi-indent.el --- vi-style autoindent ;; Copyright (C) 2007 Juri Pakaste ;; Author: Juri Pakaste ;; Keywords: ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; A nicer alternative for modes where the autoindent is completely broken. ;; Tries to emulate vi's autoindent, basically leaving it up to the user how ;; to indent. Doesn't always succeed, but it's better than what's already there, ;; sometimes. ;; ;; To use: ;; ;; (require 'vi-indent) ;; ;; (define-key XXX-mode-map [tab] 'vi-tab-to-next-stop) ;; (define-key XXX-mode-map [backtab] 'vi-backtab-to-previous-stop) ;; (define-key XXX-mode-map [(control tab)] 'vi-indent-line) ;; (define-key XXX-mode-map [return] 'vi-newline-and-indent) ;; ;; Where XXX-mode-map is the key map for the mode you want to use it with. ;; ;;; Code: (provide 'vi-indent) (provide 'backward-tab) (defun vi-tab-to-next-stop () "Add one level indentation to the current line." (interactive) (let ((empty-line nil)) (save-excursion (end-of-line) (let ((search-bound (point))) (beginning-of-line) (setq empty-line (save-excursion (eq (search-forward-regexp "[^[:space:]]" search-bound t) nil))) (tab-to-tab-stop))) (when empty-line (end-of-line)))) (defun vi-backtab-to-previous-stop () "Remove one level of indentation from the current line." (interactive) (save-excursion (end-of-line) (let ((search-bound (point))) (beginning-of-line) (condition-case nil (progn (search-forward-regexp "[^[:space:]]" search-bound) (backward-char)) (error (end-of-line))) (delete-backward-or-unindent-tab-stop)))) (defun vi-indent-line () "Indent the current line the same as the previous and move point to the beginning of the line." (interactive) (beginning-of-line) (indent-line-same-as-previous)) (defun vi-newline-and-indent () "Add newline and indent the new line the same as the current one." (interactive) (newline) (indent-line-same-as-previous)) (defun backward-move-to-tab-stop () "Move point to previous (greatest less than point) tab-stop. The variable `tab-stop-list' is a list of columns at which there are tab stops. Use \\[edit-tab-stops] to edit tab stops interactively. This is a move-backward version of \\[move-to-tab-stop]." (interactive) ;; loop to find greatest tab stop less than point (let ((tabs (reverse tab-stop-list))) (while (and tabs (<= (current-column) (car tabs))) (setq tabs (cdr tabs))) ;; if tabs not nil, car tabs is that column ;; Otherwise, column should be 0. ;; So go there. (cond (tabs (move-to-column (car tabs) t)) (t (move-to-column 0 t))))) (defun delete-backward-or-unindent-tab-stop () (interactive) (let ((saved-pos (point))) (if (save-excursion (block 'look-for-ws (beginning-of-line) (let ((tmp-pos (point))) (when (= saved-pos tmp-pos) (return-from 'look-for-ws t)) (while (< tmp-pos saved-pos) (when (not (memq (char-after tmp-pos) '(?\t ?\ ))) (return-from 'look-for-ws t)) (incf tmp-pos))) nil)) (delete-backward-char 1) (backward-move-to-tab-stop) (delete-region (point) saved-pos)))) (defun indent-line-same-as-previous () (interactive) (let ((level (save-excursion (previous-line) (beginning-of-line) (when (re-search-forward "[^ \t]" (save-excursion (end-of-line) (point)) 'move-to-eol-if-no-match) (backward-char)) (current-column)))) (indent-line-to level))) ;;; vi-indent.el ends here