]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinyindent.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinyindent.el
1 ;;; tinyindent.el --- Like indented-text-mode, but minor-mode.
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    1994-2007 Jari Aalto
8 ;; Keywords:        tools
9 ;; Author:          Jari Aalto
10 ;; Maintainer:      Jari Aalto
11 ;;
12 ;; To get information on this program, call M-x tinyindent-version.
13 ;; Look at the code with folding.el
14
15 ;; COPYRIGHT NOTICE
16 ;;
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)
20 ;; any later version.
21 ;;
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
25 ;; for more details.
26 ;;
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.
31 ;;
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
33
34 ;;}}}
35 ;;{{{ Installation
36
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into
39 ;;  ~/.emacs startup file:
40 ;;
41 ;;      (require 'tinyindent)
42 ;;
43 ;; OR use this; your .emacs loads quicker
44 ;;
45 ;;      (autoload 'tinyindent-mode    "tinyindent" "" t)
46 ;;      (autoload 'tinyindent-tt-mode "tinyindent" "" t)
47 ;;
48 ;; Suggested keybindings, you're going to use them a lot..
49 ;;
50 ;;      (global-set-key [C-tab]     'tinyindent-tt-mode) ;; this is toggle
51 ;;
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
55 ;;
56 ;;      For some PC:s in nonWindowed, this is same as S-tab
57 ;;      --> check out with C-h l
58 ;;
59 ;;
60 ;;      (define-key esc-map  "OI" 'tinyindent-mode)
61 ;;
62 ;; If you have any questions, use this function
63 ;;
64 ;;      M-x tinyindent-submit-bug-report
65
66 ;;}}}
67 ;;{{{ Documentation
68
69 ;; ..................................................... &t-commentary ...
70
71 ;;; Commentary:
72
73 ;;  Preface, sep 1994
74 ;;
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.
80 ;;
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.
86 ;;
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...
90 ;;
91 ;;  Overview of features
92 ;;
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
97 ;;          when not at BOL.
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
104 ;;          tab.
105
106 ;;}}}
107
108 ;;; Change Log:
109
110 ;;; Code:
111
112 ;;{{{ setup: require
113
114 ;;; ......................................................... &require ...
115
116 (require 'tinylibm)
117 (eval-when-compile (ti::package-use-dynamic-compilation))
118
119 (ti::package-defgroup-tiny TinyIndent tinyindent-: tools
120   "like `indented-text-mode', but minor-mode.
121   Overview of features
122
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
127             when not at BOL.
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
134             tab.")
135
136 ;;}}}
137 ;;{{{ setup: all
138
139 ;;; .......................................................... &v-bind ...
140
141 (defvar tinyindent-:mode-map nil
142   "Minor keymap, only for TAB key. Copy of `current-local-map'.")
143
144 (defvar tinyindent-:mode-prefix-map nil
145   "Prefix minor keymap, only for TAB key.")
146
147 ;;; ......................................................... &v-hooks ...
148
149 (defcustom tinyindent-:mode-load-hook nil
150   "*Hook run when file is loaded."
151   :type 'hook
152   :group 'TinyIndent)
153
154 (defcustom tinyindent-:mode-hook nil
155   "*Hook run when function `tinyindent-mode' turned on."
156   :type 'hook
157   :group 'TinyIndent)
158
159 (defcustom tinyindent-:mode-define-keys-hook 'tinyindent-mode-map-define-keys
160   "*Hook to define keys for mode."
161   :type 'hook
162   :group 'TinyIndent)
163
164 ;;; .......................................................... &v-mode ...
165
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)
170
171 (defvar tinyindent-tt-mode nil
172   "Hard tab submode.")
173 (make-variable-buffer-local 'tinyindent-tt-mode)
174
175 ;;; ....................................................... &v-private ...
176
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)
181
182 (defvar tinyindent-:cp 0  "Internal. Current point.")
183 (make-variable-buffer-local 'tinyindent-:cp)
184
185 (defvar tinyindent-:cl 0  "Internal. Current line.")
186 (make-variable-buffer-local 'tinyindent-:cl)
187
188 ;;; ........................................................ &v-public ...
189 ;;; User configurable
190
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.
194
195 (defcustom tinyindent-:bol t
196   "*Flag that determines if beg. of line should be treated differently."
197   :type  'boolean
198   :group 'TinyIndent)
199
200 (defcustom tinyindent-:special-regexp
201   (concat
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
210 #          Perl, awk, shell
211 //         C++
212 ;;;        Lisp
213 REM        Oracle Sqlplus, SQL files in general"
214   :type  'string
215   :group 'TinyIndent)
216
217 (defcustom tinyindent-:mode-str-orig " Tii"
218   "*String to be displayed in mode line."
219   :type 'string
220   :group 'TinyIndent)
221
222 (defcustom tinyindent-:tt-mode-str-orig " TiiT"
223   "*String to be displayed in mode line."
224   :type  'string
225   :group 'TinyIndent)
226
227 ;;  This is not a user variable
228
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)
232
233 ;;; ......................................................... &version ...
234
235 ;;;###autoload (autoload 'tinyindent-version "tinyindent" "Display commentary." t)
236
237 (eval-and-compile
238   (ti::macrof-version-bug-report
239    "tinyindent.el"
240    "tinyindent"
241    tinyindent-:version-id
242    "$Id: tinyindent.el,v 2.42 2007/05/01 17:20:44 jaalto Exp $"
243    '(tinyindent-:version-id
244      tinyindent-:mode-map
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)))
252
253 ;;}}}
254
255 ;;; ########################################################### &Funcs ###
256
257 ;;{{{ code: misc
258
259 ;;; ----------------------------------------------------------------------
260 ;;;
261 (defun tinyindent-mode-map-define-keys ()
262   "Defines keybindings to `tinyindent-:mode-map'."
263
264   (define-key  tinyindent-:mode-map "\t" 'tinyindent-tab-key)
265
266   ;;  e.g. lisp-mode uses backward-delete-char-untabify which is
267   ;;  uncomfortable in editing.
268   ;;
269   ;;  The 2nd bind works in X env only
270
271   (define-key  tinyindent-:mode-map "\177" 'delete-backward-char))
272
273 ;;; ----------------------------------------------------------------------
274 ;;;
275 ;;;### (autoload 'tinyindent-install-mode "tinyindent" t t)
276 (ti::macrof-minor-mode-install
277  tinyindent-install-mode
278  tinyindent-mode
279  tinyindent-:mode-map
280  tinyindent-:mode-prefix-map
281  tinyindent-:mode-name
282  tinyindent-:mode-define-keys-hook)
283
284 ;;; ----------------------------------------------------------------------
285 ;;;
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
294       t
295     nil))
296
297 ;;}}}
298 ;;{{{ engine
299
300 ;;; .......................................................... &engine ...
301
302 ;;; ----------------------------------------------------------------------
303 ;;;
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.
308
309 Returns:
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)
313          fill
314          line)
315     (when (looking-at s-re)
316       ;;  back to original line
317       ;;
318       (forward-line 1)          ;otherwise visible to user when asking
319       (when (tinyindent-confirm "indent special? ")
320         (setq line
321               (save-excursion
322                 (goto-char  (- (point) 1))
323                 (ti::read-current-line)))
324         (string-match s-re line)
325         (setq fill
326               (substring line (match-beginning 0) (match-end 0)))))
327     fill))
328
329 ;;; ----------------------------------------------------------------------
330 ;;;
331 (defun tinyindent-tab-key  ()
332   "Handle tab key.
333 Check if TinyMail is present and call Header completions in header area,
334 in BODY use relative indent."
335   (interactive)
336   (or (and (featurep 'tinymail)
337            (ti::mail-mail-p)
338            (< (point) (ti::mail-hmax))
339            (ti::funcall 'timi-complete-key))
340       (tinyindent-relative)))
341
342 ;;; ----------------------------------------------------------------------
343 ;;;
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.
348
349 References:
350   `tinyindent-:special-regexp'"
351   (interactive)
352   (let* ((bolp-flag     tinyindent-:bol)
353          (p             (point))
354          (cur-col       (current-column))
355          (imode         t)
356          (SPC           (char-to-int ?\ ))
357
358          prev-empty
359          prev-col
360          bp ep                          ;BEG END point
361          fill
362          line
363          ch
364          skip)
365
366     (catch 'cancel
367       (save-excursion
368         (save-excursion
369           (setq bp (line-beginning-position))
370           (setq ep (line-end-position))
371           (forward-line -1)
372           (setq prev-empty (looking-at "[ \t]*$"))
373           (end-of-line)
374           (setq prev-col (current-column)))
375
376         ;;  make sure these are NOT nil
377
378         (if (null tinyindent-:cp) (setq tinyindent-:cp 0))
379         (if (null tinyindent-:cl) (setq tinyindent-:cl 0))
380
381         ;;  Count lines has A BUG! , If I'm at the beg of line
382         ;;  or 1 char forward it gives different values!
383
384         (setq line (count-lines 1 p))
385
386         (if (or (eq p bp) (eobp))
387             (setq line (1+ line)))      ;BEG of line error
388
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)
391
392         (if prev-empty
393             (if (and                    ;already asked ?
394                  (>= tinyindent-:cp bp)
395                  (<= tinyindent-:cp ep))
396                 (setq skip 1))
397           (if (null (bolp))
398               (setq skip 2))            ;BOL ?
399           (if (< prev-col cur-col)
400               ;;  previous line is shorter
401               (setq skip 3)))
402
403 ;;;  (ti::d! skip "POINT" p  " " tinyindent-:cl line " bp ep  " bp ep tinyindent-:cp)
404
405         (if skip
406             (throw 'cancel t))          ;we were on this line already
407
408         (setq tinyindent-:cl line)      ;update line number
409         (setq tinyindent-:cp p)         ;current point position
410
411         ;;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
412         ;;  The real engine
413
414         (setq tinyindent-:RET nil)
415         (cond
416          ((bobp)
417           (tab-to-tab-stop))
418          ((bolp)
419           (forward-line -1)             ;Check previous line
420           (if (setq fill (tinyindent-special-handle))
421               (progn
422                 (throw 'cancel t))
423             (forward-line 1)
424             (when tinyindent-tt-mode
425               (if (null bolp-flag)
426                   (setq imode t)
427                 (setq imode (tinyindent-confirm "indent relative?")))))
428           ;; this was pressed
429           (setq ch tinyindent-:RET)))))
430
431     ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch end ^^^
432     (if fill
433         (insert fill)                 ;see save-excursion, fill is set
434       (cond
435        (tinyindent-tt-mode
436         (setq ch (or ch (ti::read-char-safe)))
437         (cond
438          ((not (characterp ch)))
439          ((eq ch SPC)
440           (indent-relative))
441          ((char= ch ?\t)
442           (tab-to-tab-stop)
443           ;;  tab stop already does this
444           (setq ch nil))
445          (t
446           (indent-relative))))
447        (t
448         (cond
449          (imode
450           (indent-relative)             ;other char follows
451           (if (eq ch SPC)            ;kill space, because it means YES
452               (setq ch nil)))
453          (t
454           (tab-to-tab-stop)             ;use hard tab
455           ;;  kill the TAB char
456           (setq ch nil)))))
457
458       ;; (ti::d! imode ch (ti::print-p ch)))
459       ;;  the TAB char automatically moves to tab-to-tab-stop
460       ;;  if it's inserted
461
462       (if (and (characterp ch)
463                (ti::print-p ch)
464                (not (eq ch SPC)))
465           ;; this is already handled
466           ;; add the character, don't loose it
467           (insert ch)))))
468
469 ;;}}}
470 ;;{{{ modes
471
472 ;;; ........................................................... &modes ...
473
474 ;;; ----------------------------------------------------------------------
475 ;;;
476 ;;;###autoload
477 (defun tinyindent-tt-mode (&optional arg)
478   "Toggle variable `tinyindent-tt-mode' with ARG. See description in `tinyindent-mode'."
479   (interactive "P")
480   (ti::bool-toggle tinyindent-tt-mode arg) ;toggle mode variable
481   (cond
482    (tinyindent-tt-mode
483
484     (unless tinyindent-mode    ;turn on the major mode tinyindent-mode
485       (tinyindent-mode)                 ;turn it on
486       (setq tinyindent-tt-mode t))
487
488     (setq tinyindent-:mode-name tinyindent-:tt-mode-str-orig))
489    (t
490     (setq tinyindent-:mode-name tinyindent-:mode-str-orig)))
491   (ti::compat-modeline-update))
492
493 ;;; ----------------------------------------------------------------------
494 ;;;
495 ;;;###autoload
496 (defun tinyindent-mode (&optional arg)
497   "Toggle relative indentation mode with ARG.
498
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.
502
503 Abount function `tinyindent-tt-mode'
504
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'.
508
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:
512
513 TAB TAB     inserts hard tab
514 TAB SPC     indent relative without inserting space char.
515 TAB x       indents relative and inserting character x
516
517 \\{tinyindent-:mode-map}"
518   (interactive "P")
519
520   (if (null (assq 'tinyindent-mode minor-mode-alist))
521       (tinyindent-install-mode))
522
523   (ti::bool-toggle tinyindent-mode arg) ;toggle mode variable
524
525   (cond
526    (tinyindent-mode
527     (unless tinyindent-tt-mode
528       (setq tinyindent-:mode-name
529             tinyindent-:mode-str-orig))
530     (run-hooks 'tinyindent-:mode-hook))
531    (t
532     (setq tinyindent-tt-mode nil)))
533   (ti::compat-modeline-update))
534
535 ;;}}}
536
537 (add-hook 'tinyindent-:mode-define-keys-hook 'tinyindent-mode-map-define-keys)
538 (provide   'tinyindent)
539 (run-hooks 'tinyindent-:mode-load-hook)
540
541 ;;; tinyindent.el ends here