1 ;;; tinyindent.el --- Like indented-text-mode, but minor-mode.
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1994-2007 Jari Aalto
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program, call M-x tinyindent-version.
13 ;; Look at the code with folding.el
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into
39 ;; ~/.emacs startup file:
41 ;; (require 'tinyindent)
43 ;; OR use this; your .emacs loads quicker
45 ;; (autoload 'tinyindent-mode "tinyindent" "" t)
46 ;; (autoload 'tinyindent-tt-mode "tinyindent" "" t)
48 ;; Suggested keybindings, you're going to use them a lot..
50 ;; (global-set-key [C-tab] 'tinyindent-tt-mode) ;; this is toggle
52 ;; ;;; the first one is for some PC machines (XCeed emulated X)
53 ;; (global-set-key [S-kp-tab] 'tinyindent-mode)
54 ;; (global-set-key [S-backtab] 'tinyindent-mode) ;; this is on/off mode
56 ;; For some PC:s in nonWindowed, this is same as S-tab
57 ;; --> check out with C-h l
60 ;; (define-key esc-map "OI" 'tinyindent-mode)
62 ;; If you have any questions, use this function
64 ;; M-x tinyindent-submit-bug-report
69 ;; ..................................................... &t-commentary ...
75 ;; The original auto-indent-mode from autoindent.el was very short
76 ;; and limited, so I thought I extend it a little...here is the
77 ;; result. Thank you Alan for giving me a push to extend your code
78 ;; into new directions. When I spoke with Alan and he gave me free
79 ;; hands, because he hadn't used the .el for quite a long time.
81 ;; I wasn't satisfied with the indent-relative function, so
82 ;; I coded a preprocessor for it. Now the cursor won't jump
83 ;; all over the line if the previous one was empty. Just
84 ;; try original M-x indent-relative when there is empty line above
85 ;; and you'll see what I mean.
87 ;; And where this module really shines: Has it ever been easier to line
88 ;; up variables according to '=' or in within lisp 'let*', or writing
89 ;; mail messages while this mode is turned on...
91 ;; Overview of features
93 ;; o General block editing or indentation MINOR mode. Replacement for
94 ;; `indented-text-mode'.
95 ;; o Takes over the TAB and BACKSPACE key.
96 ;; o Looks back to guess right indentation. and uses relative indent
98 ;; o Special indentation is suggested if cursor is at BOL and
99 ;; user defined regexp is matched in line above. (like adding
100 ;; multiple c++ comments)
101 ;; o Extra tinyindent-tt-mode for writing descriptions within comments. This
102 ;; allows user to choose when to use HARD tab or SOFT tab = relative
103 ;; to the text above. TAB TAB inserts hard tab, TAB SPC inserts soft
114 ;;; ......................................................... &require ...
117 (eval-when-compile (ti::package-use-dynamic-compilation))
119 (ti::package-defgroup-tiny TinyIndent tinyindent-: tools
120 "like `indented-text-mode', but minor-mode.
123 o General block editing or indentation MINOR mode. Replacement for
124 `indented-text-mode'.
125 o Takes over the TAB and BACKSPACE key.
126 o Looks back to guess right indentation. and uses relative indent
128 o Special indentation is suggested if cursor is at BOL and
129 user defined regexp is matched in line above. (like adding
130 multiple c++ comments)
131 o Extra tinyindent-tt-mode for writing descriptions within comments. This
132 allows user to choose when to use HARD tab or SOFT tab = relative
133 to the text above. TAB TAB inserts hard tab, TAB SPC inserts soft
139 ;;; .......................................................... &v-bind ...
141 (defvar tinyindent-:mode-map nil
142 "Minor keymap, only for TAB key. Copy of `current-local-map'.")
144 (defvar tinyindent-:mode-prefix-map nil
145 "Prefix minor keymap, only for TAB key.")
147 ;;; ......................................................... &v-hooks ...
149 (defcustom tinyindent-:mode-load-hook nil
150 "*Hook run when file is loaded."
154 (defcustom tinyindent-:mode-hook nil
155 "*Hook run when function `tinyindent-mode' turned on."
159 (defcustom tinyindent-:mode-define-keys-hook 'tinyindent-mode-map-define-keys
160 "*Hook to define keys for mode."
164 ;;; .......................................................... &v-mode ...
166 (defvar tinyindent-mode nil
167 "If set, indicates that auto-indent mode is active.
168 This variable isautomatically set by invoking \\[tinyindent-mode].")
169 (make-variable-buffer-local 'tinyindent-mode)
171 (defvar tinyindent-tt-mode nil
173 (make-variable-buffer-local 'tinyindent-tt-mode)
175 ;;; ....................................................... &v-private ...
177 (defvar tinyindent-:RET nil
178 "Global return value used, when multiple values are neede.
179 Shouldn't interest regular user.")
180 (make-variable-buffer-local 'tinyindent-:RET)
182 (defvar tinyindent-:cp 0 "Internal. Current point.")
183 (make-variable-buffer-local 'tinyindent-:cp)
185 (defvar tinyindent-:cl 0 "Internal. Current line.")
186 (make-variable-buffer-local 'tinyindent-:cl)
188 ;;; ........................................................ &v-public ...
189 ;;; User configurable
191 ;; - The BOL is special, because when you write code, the crucial
192 ;; point is line start: you decide indentation or cursor positioning with
193 ;; that first keystroke.
195 (defcustom tinyindent-:bol t
196 "*Flag that determines if beg. of line should be treated differently."
200 (defcustom tinyindent-:special-regexp
202 "^[ \t]*\\(//\\|\#\\|!\\|REM\\)[ \t]*"
203 ;; don't put ;;+, since someone may draw ;;;;;;;;;;...
204 "\\|^[ \t]*;;:?;?[ \t]*")
205 "*Special indent at the beginning of line.
206 Sometimes single indent isn't enough. For example it would be convenient
207 to write long C++ comments by hitting the TAB on the next line. Original
208 RE handles considers these as special cases.
209 ! .Xdefauls or X-related files
213 REM Oracle Sqlplus, SQL files in general"
217 (defcustom tinyindent-:mode-str-orig " Tii"
218 "*String to be displayed in mode line."
222 (defcustom tinyindent-:tt-mode-str-orig " TiiT"
223 "*String to be displayed in mode line."
227 ;; This is not a user variable
229 (defvar tinyindent-:mode-name tinyindent-:mode-str-orig
230 "Current minor mode status displayed. Changed dynamically.")
231 (make-variable-buffer-local ' tinyindent-:mode-name)
233 ;;; ......................................................... &version ...
235 ;;;###autoload (autoload 'tinyindent-version "tinyindent" "Display commentary." t)
238 (ti::macrof-version-bug-report
241 tinyindent-:version-id
242 "$Id: tinyindent.el,v 2.42 2007/05/01 17:20:44 jaalto Exp $"
243 '(tinyindent-:version-id
245 tinyindent-:mode-prefix-map
246 tinyindent-:mode-load-hook
247 tinyindent-:mode-hook
248 tinyindent-:mode-define-keys-hook
249 tinyindent-:special-regexp
250 tinyindent-:mode-str-orig
251 tinyindent-:mode-str)))
255 ;;; ########################################################### &Funcs ###
259 ;;; ----------------------------------------------------------------------
261 (defun tinyindent-mode-map-define-keys ()
262 "Defines keybindings to `tinyindent-:mode-map'."
264 (define-key tinyindent-:mode-map "\t" 'tinyindent-tab-key)
266 ;; e.g. lisp-mode uses backward-delete-char-untabify which is
267 ;; uncomfortable in editing.
269 ;; The 2nd bind works in X env only
271 (define-key tinyindent-:mode-map "\177" 'delete-backward-char))
273 ;;; ----------------------------------------------------------------------
275 ;;;### (autoload 'tinyindent-install-mode "tinyindent" t t)
276 (ti::macrof-minor-mode-install
277 tinyindent-install-mode
280 tinyindent-:mode-prefix-map
281 tinyindent-:mode-name
282 tinyindent-:mode-define-keys-hook)
284 ;;; ----------------------------------------------------------------------
286 (defun tinyindent-confirm (msg)
287 "Confirms action with MSG.
288 RET/SPC = ok. The real character pressed is available
289 thru global variable `tinyindent-:RET'."
290 (setq tinyindent-:RET (ti::read-char-safe msg))
291 (if (and (characterp tinyindent-:RET)
292 (or (char= tinyindent-:RET ?\C-m)
293 (char= tinyindent-:RET ?\ ))) ; RET/SPC
300 ;;; .......................................................... &engine ...
302 ;;; ----------------------------------------------------------------------
304 (defun tinyindent-special-handle ()
305 "Handle some special lines -- like `gin-mode', but simpler.
306 Supposes that point is at the beginning of investigated line.
307 Moves point 1 line forward af ter done.
310 filling pattern to use at front of line or nil"
311 ;; Look for some special lines, like C++
312 (let* ((s-re tinyindent-:special-regexp)
315 (when (looking-at s-re)
316 ;; back to original line
318 (forward-line 1) ;otherwise visible to user when asking
319 (when (tinyindent-confirm "indent special? ")
322 (goto-char (- (point) 1))
323 (ti::read-current-line)))
324 (string-match s-re line)
326 (substring line (match-beginning 0) (match-end 0)))))
329 ;;; ----------------------------------------------------------------------
331 (defun tinyindent-tab-key ()
333 Check if TinyMail is present and call Header completions in header area,
334 in BODY use relative indent."
336 (or (and (featurep 'tinymail)
338 (< (point) (ti::mail-hmax))
339 (ti::funcall 'timi-complete-key))
340 (tinyindent-relative)))
342 ;;; ----------------------------------------------------------------------
344 (defun tinyindent-relative ()
345 "Almost like `indent-relative', but handles some special cases.
346 - if the above line if NOT empty, then we indent relatively automatically
347 - if above line IS empty, then ask if normal TAB/relative indent.
350 `tinyindent-:special-regexp'"
352 (let* ((bolp-flag tinyindent-:bol)
354 (cur-col (current-column))
356 (SPC (char-to-int ?\ ))
369 (setq bp (line-beginning-position))
370 (setq ep (line-end-position))
372 (setq prev-empty (looking-at "[ \t]*$"))
374 (setq prev-col (current-column)))
376 ;; make sure these are NOT nil
378 (if (null tinyindent-:cp) (setq tinyindent-:cp 0))
379 (if (null tinyindent-:cl) (setq tinyindent-:cl 0))
381 ;; Count lines has A BUG! , If I'm at the beg of line
382 ;; or 1 char forward it gives different values!
384 (setq line (count-lines 1 p))
386 (if (or (eq p bp) (eobp))
387 (setq line (1+ line))) ;BEG of line error
389 ;; - the user has answered to question, we are on the same line
390 ;; - if he is at the beginning, then ALWAYS ask (forced ask)
393 (if (and ;already asked ?
394 (>= tinyindent-:cp bp)
395 (<= tinyindent-:cp ep))
398 (setq skip 2)) ;BOL ?
399 (if (< prev-col cur-col)
400 ;; previous line is shorter
403 ;;; (ti::d! skip "POINT" p " " tinyindent-:cl line " bp ep " bp ep tinyindent-:cp)
406 (throw 'cancel t)) ;we were on this line already
408 (setq tinyindent-:cl line) ;update line number
409 (setq tinyindent-:cp p) ;current point position
411 ;;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
414 (setq tinyindent-:RET nil)
419 (forward-line -1) ;Check previous line
420 (if (setq fill (tinyindent-special-handle))
424 (when tinyindent-tt-mode
427 (setq imode (tinyindent-confirm "indent relative?")))))
429 (setq ch tinyindent-:RET)))))
431 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch end ^^^
433 (insert fill) ;see save-excursion, fill is set
436 (setq ch (or ch (ti::read-char-safe)))
438 ((not (characterp ch)))
443 ;; tab stop already does this
450 (indent-relative) ;other char follows
451 (if (eq ch SPC) ;kill space, because it means YES
454 (tab-to-tab-stop) ;use hard tab
458 ;; (ti::d! imode ch (ti::print-p ch)))
459 ;; the TAB char automatically moves to tab-to-tab-stop
462 (if (and (characterp ch)
465 ;; this is already handled
466 ;; add the character, don't loose it
472 ;;; ........................................................... &modes ...
474 ;;; ----------------------------------------------------------------------
477 (defun tinyindent-tt-mode (&optional arg)
478 "Toggle variable `tinyindent-tt-mode' with ARG. See description in `tinyindent-mode'."
480 (ti::bool-toggle tinyindent-tt-mode arg) ;toggle mode variable
484 (unless tinyindent-mode ;turn on the major mode tinyindent-mode
485 (tinyindent-mode) ;turn it on
486 (setq tinyindent-tt-mode t))
488 (setq tinyindent-:mode-name tinyindent-:tt-mode-str-orig))
490 (setq tinyindent-:mode-name tinyindent-:mode-str-orig)))
491 (ti::compat-modeline-update))
493 ;;; ----------------------------------------------------------------------
496 (defun tinyindent-mode (&optional arg)
497 "Toggle relative indentation mode with ARG.
499 Indentation is determined according to previous lines. Special
500 indent happens only at the beginning of line, where user is asked if
501 he wants to have relative or \"hard\" indentation.
503 Abount function `tinyindent-tt-mode'
505 This isn't really mode. It just turns one flag on in `tinyindent-mode', so that
506 it behaves a little differently. If the `tinyindent-mode' is not running, it
507 wiil be turned on. turning off `tinyindent-tt-mode' _does_not_ end `tinyindent-mode'.
509 Sometimes you want to control between 'hard' tab and 'soft' tab, ie.
510 relative indent. This mode causes second character to be read after
511 tab key is hit. The following happens:
513 TAB TAB inserts hard tab
514 TAB SPC indent relative without inserting space char.
515 TAB x indents relative and inserting character x
517 \\{tinyindent-:mode-map}"
520 (if (null (assq 'tinyindent-mode minor-mode-alist))
521 (tinyindent-install-mode))
523 (ti::bool-toggle tinyindent-mode arg) ;toggle mode variable
527 (unless tinyindent-tt-mode
528 (setq tinyindent-:mode-name
529 tinyindent-:mode-str-orig))
530 (run-hooks 'tinyindent-:mode-hook))
532 (setq tinyindent-tt-mode nil)))
533 (ti::compat-modeline-update))
537 (add-hook 'tinyindent-:mode-define-keys-hook 'tinyindent-mode-map-define-keys)
538 (provide 'tinyindent)
539 (run-hooks 'tinyindent-:mode-load-hook)
541 ;;; tinyindent.el ends here