]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinytab.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinytab.el
1 ;;; tinytab.el --- Programmers TAB minor mode. Very flexible.
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    1995-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 tinytab-version.
13 ;; Look at the code with folding.el.
14
15 ;; COPYIGHT 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 ;;{{{ Install
36
37 ;; ....................................................... &t-install ...
38 ;;   Put this file on your Emacs-Lisp load path, add following into your
39 ;;   ~/.emacs startup file.
40 ;;
41 ;;     (require 'tinytab)
42 ;;
43 ;;   or use this; your .emacs loads up a bit quicker
44 ;;
45 ;;      (autoload 'tinytab-mode            "tinytab" "" t)
46 ;;      (autoload 'tinytab-return-key-mode "tinytab" "" t)
47 ;;
48 ;;   Suggested keybindings
49 ;;
50 ;;      (global-set-key "\C-cT"        'tinytab-mode)
51 ;;      (global-set-key [(shift tab)]  'tinytab-tab-del-key)
52 ;;      (global-set-key "\C-c\C-m"     'tinytab-return-key-mode)
53 ;;
54 ;;  For more customisation, do this:
55 ;;
56 ;;       (add-hook 'tinytab-mode-define-keys-hook 'my-tinytab-keys)
57 ;;
58 ;;      (defun my-tinytab-keys ()
59 ;;        "My tinytab key additions, override settings."
60 ;;        ... code here ...)
61
62 ;;}}}
63
64 ;;{{{ Documentation
65
66 ;; ..................................................... &t-commentary ...
67
68 ;;; Commentary:
69
70 ;; Preface, oct 1995
71 ;;
72 ;;      There was a post in gnu.emacs.sources (what an source of
73 ;;      inspiration), where someone asked:
74 ;;
75 ;;          "Is there anyway to reset the number of spaces that TAB does?
76 ;;           Like, I want to make it jump four spaces instead of the
77 ;;           usual whatever.How can I set the tabs to 4?"
78 ;;
79 ;;      and the typical answer was:
80 ;;
81 ;;          "In .emacs, set the variable tab-stop-list, like so:
82 ;;           (setq tab-stop-list (list 4 8 12 ...))"
83 ;;
84 ;;      Well, A regular user does not want to touch the original
85 ;;      `tab-stop-list', because the 8 spaces per tab is the norm. But for
86 ;;      programming the 4 tabs is norm, like for shell programming or for
87 ;;      simple memos and text documents. The goal was to write a minor
88 ;;      mode, which you can turn on and off, which handles _only_ tab key.
89 ;;      This mode was supposed to be plain rigid. The tab goes where you
90 ;;      want it, and you can control the amount of movement to either
91 ;;      direction, back or forward.
92 ;;
93 ;;  Overview of features
94 ;;
95 ;;      o   Programmable TAB. If you set the count to to 4,
96 ;;          you can virtually program "blindly" without any other modes.
97 ;;      o   Selectable: 2, 4, 8 .. space indent.
98 ;;      o   moving commands: tab-forward, tab-backward
99 ;;      o   indent commands: tab indent forward, tab indent backward
100 ;;      o   Simple positioning of braces { } with TAB key.
101 ;;
102 ;;      Extras
103 ;;
104 ;;      o   Special auto-indent function offered for return key.
105 ;;          Switch it on, and you can continue your shell, awk, SQL, C++,
106 ;;          Perl comments and more.
107 ;;      o   C-c TAB enters interactive indentation mode where
108 ;;          keys "qw" "as" abd "zx" control the amount of indentation.
109 ;;
110 ;;  What this package does?
111 ;;
112 ;;      Mimic `tab-stop-list' with minor mode if some analogy can be
113 ;;      drawn. You only set one variable, that controls the amount of
114 ;;      movement, whereas you would have to put many values inside
115 ;;      `tab-stop-list'. The variable to control tab widths is:
116 ;;
117 ;;          tinytab-:width-table
118 ;;
119 ;;      When the mode is off, the tab key behaves as the mode thinks
120 ;;      it should behave. The tab step forward and backward keys
121 ;;      respect `tinytab-:width'. Normally the current position
122 ;;      in the line is advanced, but if you select a region, all the
123 ;;      lines are indented:
124 ;;
125 ;;          This text here
126 ;;          More text Here
127 ;;          And so on.
128 ;;
129 ;;          -- Supposing all of the above text was selected and TAB was pressed
130 ;;
131 ;;              This text here
132 ;;              More text Here
133 ;;              And so on.
134 ;;
135 ;;          -- Select all lines and press ESC-tab (or Shift-Tab) and lines
136 ;;          -- are unintended
137 ;;
138 ;;          This text here
139 ;;          More text Here
140 ;;          And so on.
141 ;;
142 ;;          -- If you have to "shoot" carefully how much indentation is needed,
143 ;;          -- select region and call C-c TAB
144 ;;
145 ;;           This text here             Use keys q - w   (un/indent by 1)
146 ;;           More text Here                      a - s   (un/indent by 2)
147 ;;           And so on.                          z - x   (un/indent by 4)
148 ;;
149 ;;      To change permanently current tab division, use function
150 ;;      `tinytab-change-tab-width' which steps through list
151 ;;      `tinytab-:width-table'; tab factors 2, 4, and 8.
152 ;;
153 ;;  Major modes and this minor mode
154 ;;
155 ;;      When you use some programming mode, say C++ mode, it usually
156 ;;      provides function to indent the line right with tab key.
157 ;;      If you then turn on this mode, you loose the mode specific
158 ;;      indenting, because turning on minor mode overrides the underlying
159 ;;      major mode bindings. However this package co-operates with
160 ;;      major modes so that it preserves the original indenting style in some
161 ;;      extent. In variable `tinytab-:tab-insert-hook' there is function
162 ;;      `tinytab-tab-mode-control' which looks at variable
163 ;;
164 ;;          tinytab-:mode-table
165 ;;
166 ;;      If the mode is listed in the table _and_ current point is at the
167 ;;      *beginning* of line, then the line is handled by original major mode
168 ;;      and not by this minor mode.
169 ;;
170 ;;      However, this minor mode is normally meant to be used as turn
171 ;;      on/off basis in such programming modes that indent lines when you
172 ;;      pressing tab key. Current compatibility function
173 ;;      `tinytab-tab-mode-control' only allows you to get some flexibility
174 ;;      when this mode is temporarily on. Bind this mode to some fast key
175 ;;      which you can use to toggle this mode on/off when you need tab for
176 ;;      a moment in programming modes.
177 ;;
178 ;;          (global-set-key "\C-cT" 'tinytab-mode)
179 ;;
180 ;;      If you don't want any support to major modes, put following
181 ;;      into your $HOME/.emacs
182 ;;
183 ;;          (setq tinytab-:mode-table nil)
184 ;;
185 ;;  Return key addition
186 ;;
187 ;;      This package also includes a little function
188 ;;      `tinytab-return-key-mode' which will keep the line's indentation.
189 ;;      You can bind it to key C-c RET:
190 ;;
191 ;;          (global-set-key "\C-c\C-m" 'tinytab-return-key-mode)
192 ;;
193 ;;      When the function is active, you can continue indenting from
194 ;;      the current position, like this:
195 ;;
196 ;;          // Comment here. Call C-c C-m...and press RET
197 ;;          // And it automatically indents here.
198 ;;
199 ;;      See variable
200 ;;
201 ;;          tinytab-:auto-indent-regexp
202 ;;
203 ;;      what line prefixes are "copied" along with the indented spaces.
204
205 ;;}}}
206
207 ;;; Change Log:
208
209 ;;; Code:
210
211 ;;{{{ setup: require
212
213 ;; (require 'tinylibm)
214
215 ;;}}}
216 ;;{{{ version
217
218 (defvar tinytab-:version-id
219   "$Id: tinytab.el,v 2.61 2007/05/06 23:15:20 jaalto Exp $")
220
221 ;;}}}
222 ;;{{{ setup: mode
223
224 ;;;###autoload (autoload 'tinytab-mode                  "tinytab" "" t)
225 ;;;###autoload (autoload 'turn-on-tinytab-mode          "tinytab" "" t)
226 ;;;###autoload (autoload 'turn-off-tinytab-mode         "tinytab" "" t)
227 ;;;###autoload (autoload 'tinytab-commentary            "tinytab" "" t)
228 ;;;###autoload (autoload 'tinytab-version               "tinytab" "" t)
229
230 (eval-and-compile
231
232   (ti::macrof-minor-mode-wizard
233    "tinytab-"
234    " " ;; This used to be " +" to indicate "Plussed tab"
235    nil
236    "Tab"
237    'TinyTab
238    "tinytab-:"                          ;parameters 1-6
239
240    "Tab movement minor mode. Adjustable movement step.
241 If you're running non/windowed version, Try to figure out which key
242 combinations work there best, In X, you have more flexible bindings.
243
244 If region is active, the indentation  (backward or forward) is
245 applied to whole region.
246
247 References:
248
249   tinytab-:width
250
251 Mode description:
252
253 \\{tinytab-:mode-map}
254
255 "
256    "Tab indent mode"
257    (progn
258      (if tinytab-mode
259          (tinytab-set-mode-name)))
260    "Tab indent mode"
261    (list                                ;arg 10
262     tinytab-:mode-easymenu-name
263     ["Insert"                        tinytab-tab-key                     t]
264     ["Delete"                        tinytab-tab-del-key                 t]
265     ["Indent region forward"         tinytab-indent-by-tab-width         t]
266     ["Indent region backward"        tinytab-indent-by-tab-width-back    t]
267     ["Indent region dynamically"     tinytab-indent-region-dynamically   t]
268     ["Forward"                       tinytab-tab-forward                 t]
269     ["Backward"                      tinytab-tab-backward                t]
270     ["Change step factor"            tinytab-change-tab-width            t]
271     ["Return-key indent mode"        tinytab-return-key-mode             t]
272     "----"
273     ["Package version"               tinytab-version                     t]
274     ["Package commentary"            tinytab-commentary                  t]
275     ["Mode help"                     tinytab-mode-help                   t]
276     ["Mode off"                      turn-off-tinytab-mode               t])
277    (progn
278      ;; ... ... ... ... ... ... ... ... ... ... ... ... ...  non-X keys . .
279      (define-key   root-map "\t"         'tinytab-tab-key)
280      (define-key   root-map "\e\t"       'tinytab-tab-del-key)
281      (define-key   root-map "\C-c\t"     'tinytab-indent-region-dynamically)
282      (define-key   root-map "\C-c\C-m"   'tinytab-return-key-mode)
283      ;; ........................................................ X-keys ...
284      ;;  Standard key
285      (define-key root-map (kbd "<S-tab>")        'tinytab-tab-del-key)
286      ;;  Other keyboards
287      (define-key root-map [(shift backtab)]      'tinytab-tab-del-key)
288      (define-key root-map [(shift hpBackTab)]    'tinytab-tab-del-key) ;; XEmacs
289      (define-key root-map [(shift kp-tab)]       'tinytab-tab-del-key)
290      (define-key root-map [(shift iso-lefttab)]  'tinytab-tab-del-key))))
291
292 ;;}}}
293 ;;{{{ setup: hooks
294
295 (defcustom tinytab-:load-hook nil
296   "*Hook that is run when package is loaded."
297   :type  'hook
298   :group 'TinyTab)
299
300 (add-hook 'tinytab-:load-hook 'tinytab-install-mode)
301
302 (defcustom tinytab-:tab-insert-hook
303   '(tinytab-tab-mode-control
304     tinytab-tab-brace-control
305     tinytab-tab-forward-insert
306     tab-to-tab-stop)
307   "*List of functions to call for inserting logical TAB.
308 If any of these functions return non-nil, it is assumed,
309 that the tab key was handled."
310   :type  'hook
311   :group 'TinyTab)
312
313 (defcustom tinytab-:tab-delete-hook
314   '(tinytab-tab-backward-del
315     tinytab-bol-forward-del)
316   "*List of functions to delete a logical TAB backward.
317 If any of these functions return non-nil, it is assumed,
318 that the tab handling was performed."
319   :type  'hook
320   :group 'TinyTab)
321
322 ;;}}}
323 ;;{{{ setup: public, user configurable
324
325 ;;   Simple name is enough. Think this as "Tab +" or "extended tab" -mode
326 ;;
327 (defcustom tinytab-:mode-name-base " +"
328   "*Minor mode's base name. Default value is ` +'."
329   :type  'string
330   :group 'TinyTab)
331
332 ;;  If I accidentally press key I didn't meant to, I want to know
333 ;;  about it. Like in empty line, where is no visual aids
334 ;;
335 (defcustom tinytab-:verbose nil
336   "*Enable verbose messages."
337   :type  'boolean
338   :group 'TinyTab)
339
340 (defcustom tinytab-:width-table '(2 4 8)
341   "*After call to \\[tinytab-change-tab-width], cycle through list of tab positions.
342 Default values are '(2 4 8)"
343   :type  '(repeat integer)
344   :group 'TinyTab)
345
346 (defcustom tinytab-:mode-table
347   '(c++-mode cc-mode c-mode perl-mode cperl-mode java-mode)
348   "*List of mode name symbols where the TAB key calls mode's TAB function.
349 But, only if the point is at the beginning of line."
350   :type '(repeat (symbol
351                   :tag "Mode name symbols"))
352   :group 'TinyTab)
353
354 (defcustom tinytab-:indent-region-key-message
355   "Dynamic Indent <>: [qw]=1 [as]=2 [zx]=4 [Esc]=exit"
356   "*Message displayed while in dynamic indent mode.
357 If you change this, see also `tinytab-:indent-region-key-list'."
358   :type  'string
359   :group 'TinyTab)
360
361 (defcustom tinytab-:indent-region-key-list
362   '(?q ?w
363        ?a ?s
364        ?z ?x
365        ?\e)
366   "*List of keys to control dynamic indenting. Not case sensitive.
367 The first 6 keys go in pairs.
368
369 elt 0 1       left and right by 1
370 elt 2 3       left and right by 2
371 elt 4 5       left and right by 4
372 elt 6         exit key
373
374 If you chnage this variable, change also
375 `tinytab-:indent-region-key-message'."
376   :type '(list
377           character character
378           character character
379           character character
380           character)
381   :group 'TinyTab)
382
383 (defcustom tinytab-:auto-indent-regexp "[#!;*/]\\|REM\\|//"
384   "*If previous line match this regexp, it is copied when you hit RET.
385 This allows e.g. continuing C++'s // comments.
386 See function `tinytab-return-key-mode' to turn on this auto-indent feature."
387   :type  'string
388   :group 'TinyTab)
389
390 ;;}}}
391 ;;{{{ setup: private
392
393 (defvar tinytab-:width 4
394   "Current tab division.")
395
396 ;;}}}
397 ;;{{{ extra functions
398
399 ;;; ----------------------------------------------------------------------
400 ;;;
401 (defsubst tinytab-activate-region (beg end)
402   "Activate region which was previously selected.")
403   ;;  #todo:
404
405 ;;; ----------------------------------------------------------------------
406 ;;;
407 (defmacro tinytab-message (&rest body)
408   "Run BODY if `tinytab-:verbose' is non nil."
409   (`
410    (when tinytab-:verbose
411      (message (,@ body)))))
412
413 ;;; ----------------------------------------------------------------------
414 ;;;
415 (defsubst tinytab-region-bounds (&optional beg end)
416   "Set variables BEG and END if region is active.
417 Otherwise use current line's end points."
418   (if (and beg
419            end)
420       (list beg end)
421     (if (and (region-active-p)
422              ;;  Must be active too, otherwise may cause suprises to indent
423              ;;  many lines in the buffer
424              transient-mark-mode)
425         (list (region-beginning)
426               (region-end))
427       ;; Interactive call. Single line
428       nil)))
429
430 ;;; ----------------------------------------------------------------------
431 ;;;
432 (defsubst tinytab-width ()
433   "Return TAB advance."
434   (if (not (integerp tinytab-:width))
435       (setq tinytab-:width 4))
436   tinytab-:width)
437
438 ;;; ----------------------------------------------------------------------
439 ;;;
440 (defun tinytab-indent-by-tab-width (&optional beg end back)
441   "Indent region BEG END by current tab division.
442 Optional BACK indents backward. If BEG is nil and region is active,
443 determine BEG and END."
444   ;; (interactive "*r")
445   (let* ((width (tinytab-width))
446          (div   (if back
447                     (- 0 width)
448                   width)))
449     (multiple-value-bind (b e)
450         (tinytab-region-bounds beg end)
451       (if (eq b e)
452           (tinytab-tab-forward-insert)
453         ;;  This deactivates region, keep it on
454         (indent-rigidly b e div)
455         (tinytab-activate-region b e)))))
456
457 ;;; ----------------------------------------------------------------------
458 ;;; - So that you can bind this to fast key
459 ;;;
460 (defun tinytab-indent-by-tab-width-back (&optional beg end)
461   "Just shortcut to `tinytab-indent-by-tab-width'. Indent BEG END backward.
462 If BEG and END are nil, indent current line. (interactive call on current line)
463 If region is active, use that. (interactive + region selected)."
464   (interactive)
465   (multiple-value-bind (b e)
466       (tinytab-region-bounds beg end)
467     (tinytab-indent-by-tab-width b e 'back)))
468
469 ;;; ----------------------------------------------------------------------
470 ;;;
471 (defun tinytab-bol-forward-del ()
472   "If at beginning of line, delete `tinytab-:width' spaces to the right."
473   (interactive)
474   (when (bolp)
475     ;;  They may be \t, so convert all to spaces first and
476     ;;  then we know if we can delete enough spcaes.
477     (let* ((line   (buffer-substring (line-beginning-position)
478                                      (line-end-position)))
479            (width  (tinytab-width))
480            (str    (and width
481                         (with-temp-buffer
482                           (insert line)
483                           ;;  Only convert from the start, not whole line
484                           (untabify (point-min) (min
485                                                  (+ (point-min) width)
486                                                  (point-max)))
487                           (goto-char (point-min))
488                           (when (looking-at
489                                  (concat
490                                   "^"
491                                   (make-string width ?\  )))
492                             (forward-char 4)
493                             (buffer-substring (point) (point-max)))))))
494       (cond
495        (str
496         (delete-region (point) (line-end-position))
497         (save-excursion
498           (insert str)))
499        (tinytab-:verbose
500         (message "TinyTab: Cannot delete %d spaces" width))))))
501
502 ;;; ----------------------------------------------------------------------
503 ;;;
504 (defun tinytab-tab-mode-control ()
505   "If `mode-name' in in the list `tinytab-:mode-table', call mode's tab key.
506 But only if
507 o  point is at the beginning of line.
508 o  the line is empty
509
510 This way you can partly mix e.g. C++ mode and this minor mode."
511   (interactive)
512   (let* ((sym  (intern (format "%s-map" (symbol-name major-mode))))
513          (point (point))
514          map
515          func)
516     ;;  If we're at the beginnning of line, see if there is keymap for
517     ;;  this current mode. Then try to find function for "\t" key
518     ;;  and call it
519     ;;
520     (when (and (or (bolp)
521                    (save-excursion
522                      (beginning-of-line)
523                      (looking-at "^[ \t]+$")))
524                (memq major-mode tinytab-:mode-table)
525                (boundp sym)
526                (keymapp (setq map (eval sym)))
527                (setq func (lookup-key map "\t")))
528       (call-interactively func)
529       (if (eq (point) point)
530           ;; Not moved? Then continue calling other functions.
531           nil
532         t))))
533
534 ;;; ----------------------------------------------------------------------
535 ;;; - For a little more smarter TAB key to line up { } braces
536 ;;;   in variaous programming modes I made this. It's simple,
537 ;;;   but suffices for most common needs.
538 ;;; - I don't know how the C-mode or cc-mode does this, but, hey,
539 ;;;   this is one way :-)
540 ;;;
541 ;;;
542 (defun tinytab-tab-brace-control ()
543   "When hitting TAB, line up {} braces, otherwise do nothing special.
544 Remember that opening brace, the {, follows previous line's indentation
545 and } follows the \"{\".
546
547 Return:
548
549   t             ,if TAB handled in this function.
550   nil           ,nothing done."
551   (interactive)
552   (let* (line
553          rest
554          indent
555          pindent                        ;previous
556          col
557          ret                            ;flag
558          handle
559          equal)
560     ;; ... ... ... ... ... ... ... ... ... ... ... ... ... check brace ...
561     (save-excursion
562       (beginning-of-line)
563       ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... { . .
564       (cond
565        ((looking-at "^\\([ \t]*\\)\\({.*\\)")
566         (setq indent (or (match-string 1) "")
567               rest   (match-string 2)
568               handle '{ )
569         (save-excursion
570           (forward-line -1)             ; peek previous line indent
571           (let ((line (buffer-substring (line-beginning-position)
572                                         (line-end-position))))
573             (setq pindent (if (string-match "^[ \t]+" line)
574                               (match-string 1 line)
575                             "")))))
576        ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. } ..
577        ((looking-at "^\\([ \t]*\\)\\(}.*\\)")
578         (setq indent (or (match-string 1) "")
579               rest   (match-string 2)
580               handle '{)
581         (save-excursion
582           (cond
583            ((re-search-backward "^\\([ \t]*\\){" nil t)
584             (setq pindent (or (match-string 1) ""))))))))
585     ;; ... ... ... ... ... ... ... ... ... ... ... ... .. adjust brace ...
586     (setq equal (and indent pindent (string= indent pindent))
587           col   (and indent (length (subst-char-with-string indent))))
588     (cond
589      ((and indent
590            pindent
591            equal
592            (memq handle '({ }))
593            (< (current-column) col))
594       ;;  Pressing TAB, before the {, puts cursor to brace.
595       (move-to-column col)
596       (setq ret t))
597      ((and indent
598            pindent
599            (not (string= indent pindent)))
600       ;;  This is reindent case: { and } didn't line up.
601       (setq line (concat pindent rest)
602             col  (current-column)
603             ret  t)
604       (setq col (current-column))
605       (delete-region (line-beginning-position) (line-end-position))
606       (insert line)
607       ;;  If user is in the LEFT side of brace, put cursor to brace
608       ;;  If user if in the RIGHT side, then move-to-column will
609       ;;  preserve position.
610       ;;
611       (move-to-column col)
612       (when (< col (length (subst-char-with-string pindent)))
613         (re-search-forward "{\\|}")
614         (forward-char -1))))
615     ret))
616
617 ;;}}}
618 ;;{{{ code: return key
619
620 ;;; ----------------------------------------------------------------------
621 ;;; Replaces the RET key
622 ;;; The arg is just due to: (newline &optional ARG1)
623 ;;;
624 (defun tinytab-auto-indent (&optional arg)
625   "Automatically indent according to previous line.
626 If optional ARG is given, behave exactly like 'newline' function."
627   (interactive "P")
628   ;;  The RE matches few common comments and empty whitespaces
629   ;;  #     = shell
630   ;;  ;     = lisp comments
631   ;;  *     = C comments
632   ;;  !     = .Xdefaults comments
633   ;;  //    =  C++ comments
634   ;;  REM   = oracle SQL comments
635   ;;
636   (let ((re  (concat "^[ \t]*\\(" tinytab-:auto-indent-regexp "\\)*[ \t]*"))
637         str)
638     (cond
639      (arg ;;  We do not do anything special if user has given arg.
640       (newline arg))
641      ((not (eolp)) ;; Now let's see if user wanted fresh line
642       ;;  User wanted to divide a line.
643       ;;  Read the line up till cursor point
644       (if (> (current-column) 0)
645           (setq str (buffer-substring (line-beginning-position) (point))))
646       ;;  Ignore portion match --> nothing important matched
647       (if (or (null str)
648               (not (and (string-match re str)
649                         (equal (length str)
650                                ;;  The position (column in string)
651                                (match-end 0)))))
652           (newline)                ;something else than re, break line
653         (let ((left-margin 0))       ;Can't add string right otherwise
654           (newline)
655           (insert str))))
656      (t
657       ;; ... ... ... ... ... ... ... ... ... ... ... ... ... .. else ...
658       ;;  Let's peek current line
659       (if (> (current-column) 0)
660           (save-excursion
661             (beginning-of-line)
662             (setq str (if (looking-at re)
663                           (match-string 0)))))
664       (if (null str)                    ;Nothing important here
665           (newline)
666         (let ((left-margin 0))
667           (newline)
668           (insert str)))))))
669
670 ;;}}}
671 ;;{{{ code: tab
672
673 ;;; ----------------------------------------------------------------------
674 ;;;
675 (defun tinytab-set-mode-name ()
676   "Set mode name according to tab count in effect."
677   (interactive)
678   (let* ((base  tinytab-:mode-name-base)
679          (val   (tinytab-width)))
680     (setq tinytab-:mode-name (format "%s%d" (or base "") val))
681     (if (fboundp 'force-mode-line-update)
682         (force-mode-line-update))))
683
684 ;;; ----------------------------------------------------------------------
685 ;;;
686 (defun tinytab-change-tab-width ()
687   "Toggle tab width according to `tinytab-:width-table'."
688   (interactive)
689   (let* ((verb  (interactive-p))
690          (val   (tinytab-width))
691          (table tinytab-:width-table)
692          elt)
693     (cond
694      ((not (integerp val))
695       (setq val (car table)))           ;default value
696      ((setq elt (memq val table))
697       (if (eq 1 (length elt))           ;it's last item
698           (setq val (car table))        ;pick first value
699         (setq val (nth 1 elt))))        ;get next in the list
700
701      (t                                 ;can't find value from table ?
702       (setq val (car table))))          ;get first then.
703     (setq tinytab-:width val)           ;update
704     (tinytab-set-mode-name)
705     (if verb                            ;this does no harm....
706         (message "TinyTab: Tab factor is now %d" val))))
707
708 ;;; ----------------------------------------------------------------------
709 ;;;
710 (defun tinytab-tab-backward-del ()
711   "Move Tab backward.
712 Delete whitespace if all characters preceding the point are white spaces
713 _and_ the final position is in divide-able by current div-factor.
714
715 Eg. If you factor is 4, and there is 2 spaces before your cursor \"*\",
716     This function will not delete the extra spaces, because it can't reach
717     position 8.
718
719          bar Geezy *
720          12345678901234
721             ^   ^     ^
722
723 In this case, calling the function is no-op.
724
725 References:
726
727   `tinytab-:tab-delete-hook'
728   `tinytab-:width'"
729   (interactive "*")
730   (let* ((div   (tinytab-width))
731          (col   (current-column))
732          (dest  (- col (% col div)))
733          MARK
734          str
735          eob                            ;flag
736          p                              ;points
737          p2)
738     (setq MARK  (save-excursion
739                   (if (eobp)
740                       (setq eob t)
741                     (forward-char 1))   ;push marker one forward
742                   (point-marker)))
743     (if (= col dest )                   ; would be exact
744         (setq dest (- col div )))
745     (if (< dest 0)
746         (setq dest 0))
747     (if (= col 0)                       ;beg of line
748         nil
749       (move-to-column dest t)           ;converts tabs to spaces.
750       ;; consider following:
751       ;;    actual         seen
752       ;;    12345678       123456789
753       ;;    ----------------------------------
754       ;;    #\thello      "#       hello"     ,suppose cursor is in "h"
755       ;;    |  |
756       ;;    |  point 3
757       ;;    point 1
758       ;;
759       ;;    Now you indent back by 4, this is what happens
760       ;;    12345678       12345678
761       ;;    #   hello    "#   hello"
762       ;;    |   |
763       ;;    |  point 5                        , Geez!
764       ;;    point 1
765       ;;
766       ;;    The tab is converted and it caused all point to be altered.
767       ;;    That's why we have to use the marker, because it stays
768       ;;    releative to text, in this case just _behind_ the letter "h"
769       ;;
770       (setq p  (if eob
771                    (marker-position MARK)
772                  (1- (marker-position MARK))))
773       (setq p2 (point))
774       (setq str (buffer-substring p2 p))
775       (if (not (string-match "^[ \t]+$" str))
776           (progn
777             (tinytab-message "TinyTab: Can't reach previous tab position")
778             (goto-char p))              ;do not move. Stay put.
779         (delete-region p2 p)
780         (tinytab-message "Tinytab: Deleted")))
781     (setq MARK nil)))                   ;kill the marker
782
783 ;;; ----------------------------------------------------------------------
784 ;;;
785 (defun tinytab-tab-backward ()
786   "Logical tab movement backward, until reach beginning of line."
787   (interactive)
788   (let* ((div   (tinytab-width))
789          (dest  (- (current-column) div)))
790     (if (< dest 0)
791         (setq dest 0))
792     (move-to-column dest t)
793     (if (looking-at "[ \t]+$")
794         (tinytab-message "TinyTab: Moved."))))
795
796 ;;; ----------------------------------------------------------------------
797 ;;;
798 (defun tinytab-tab-forward-insert ()
799   "Move tab forward, insert spaces or tabs, see variable `indent-tabs-mode'.
800
801 References:
802
803   `tinytab-:width'"
804   (interactive "*")
805   (let* ((col   (current-column))
806          (div   (tinytab-width))
807          (nbr   (- div (% col div)))
808          div
809          eob                            ;flag
810          MARK                           ;marker
811          str)
812     (if (= 0 nbr)
813         (setq str (make-string div ?\ ))
814       (setq str (make-string nbr ?\ )))
815     (insert str)
816     (when indent-tabs-mode
817       ;; - When we insert non-tabs, like in mode "tab 4", what happens is
818       ;;   that we insert "    " + "    " ie. 4 + 4 spaces.
819       ;; - but, we really like them to be like one "\t" code in text,
820       ;;   So, let's fix the line every time something is inserted.
821       ;; - We have to use markers again due to tabify.
822       ;; - The EOB is special case
823       ;;
824       (setq MARK (save-excursion
825                    (if (eobp)
826                        (setq eob t)
827                      (forward-char 1))
828                    (point-marker)))
829       (tabify (line-beginning-position) (point))
830       (goto-char (if eob
831                      (line-end-position)
832                    (1- (marker-position MARK))))
833
834       (setq MARK nil))                  ;kill it
835     t))                                 ;we handled this
836
837 ;;; ----------------------------------------------------------------------
838 ;;;
839 (defun tinytab-tab-forward ()
840   "Step logical tab forward. Does not insert anything. Stops at EOL.
841 Tabs are converted to spaces when needed; because you can't step inside
842 '\t' character in the line otherwise.."
843   (interactive)
844   (let* ((div   (tinytab-width))
845          (col   (current-column))
846          (nbr   (- div (% col div)))
847          (ecol  (save-excursion (end-of-line) (current-column)))
848          (dest  (+ col nbr)))
849     (cond
850      ((> dest ecol)
851       (end-of-line)
852       (tinytab-message "End of line."))
853      (t
854       (move-to-column dest t)
855       (if (looking-at "[ \t]+$")
856           (tinytab-message "Tinytab: Moved."))))))
857
858 ;;; ----------------------------------------------------------------------
859 ;;;
860 (defun tinytab-tab-key-insert ()
861   "Run all functions in `tinytab-:tab-insert-hook' until success."
862   ;;  We could use this instead:
863   ;;
864   ;;  (run-hook-with-args-until-success 'tinytab-:tab-insert-hook)
865   ;;
866   ;;  But then it would not be possible to debug which function gets
867   ;;  called.
868   (dolist (function tinytab-:tab-insert-hook)
869     (when (funcall function)
870       (tinytab-message "TinyTab: %s" (symbol-name function))
871       (return))))
872
873 ;;; ----------------------------------------------------------------------
874 ;;;
875 ;;;###autoload
876 (defun tinytab-tab-key (&optional beg end)
877   "Run list of function to handle TAB key. See variable `tinytab-:tab-insert-hook'.
878 If region is active and BEG and END are nil, then call function
879 `tinytab-indent-by-tab-width'."
880   (interactive)
881   (cond
882    ((and (null beg)
883          (region-active-p))
884     (tinytab-indent-by-tab-width))
885    (t
886     ;;  Integrate this function with tinymail.el tab-key.
887     (let* ((sym   'tinymail-:complete-key-hook)
888            (tinymail-:complete-key-hook (if (boundp sym)
889                                             (symbol-value sym))))
890       ;; No-op: byte compiler silencer
891       (if (null tinymail-:complete-key-hook)
892           (setq tinymail-:complete-key-hook nil))
893       (remove-hook sym 'tinymail-complete-guest-packages)
894       ;; keep this at the end
895       (when (memq 'tab-to-tab-stop tinytab-:tab-insert-hook)
896         (remove-hook 'tinytab-:tab-insert-hook 'tab-to-tab-stop)
897         (add-hook    'tinytab-:tab-insert-hook 'tab-to-tab-stop 'append))
898       (tinytab-tab-key-insert)))))
899
900 ;;; ----------------------------------------------------------------------
901 ;;;
902 ;;;###autoload
903 (defun tinytab-tab-del-key (&optional beg end)
904   "Remove indentation. See variable `tinytab-:tab-delete-hook'.
905 If region is active, indent all lines backward."
906   (interactive)
907   (cond
908    ((and (region-active-p)
909          transient-mark-mode)
910     (tinytab-indent-by-tab-width-back)
911     (tinytab-activate-region beg end))
912    (t
913     (run-hook-with-args-until-success 'tinytab-:tab-delete-hook))))
914
915 ;;; ----------------------------------------------------------------------
916 ;;;
917 ;;;###autoload
918 (defun turn-on-tinytab-return-key-mode ()
919   "Turn on auto indent after RET key."
920   (tinytab-return-key-mode 1 (interactive-p)))
921
922 ;;; ----------------------------------------------------------------------
923 ;;;
924 ;;;###autoload
925 (defun turn-off-tinytab-return-key-mode ()
926   "Turn on auto indent after RET key."
927   (tinytab-return-key-mode 1 (interactive-p)))
928
929 ;;; ----------------------------------------------------------------------
930 ;;;
931 ;;;###autoload
932 (defun tinytab-return-key-mode (&optional mode verb)
933   "Toggle auto indent MODE / regular newline mode. VERB."
934   (interactive)
935   (let* ((func  'tinytab-auto-indent)
936          (now
937           (or (and
938                ;;  e.g. in fundamental-map this value is nil and
939                ;;  nil cannot be used as an keymap for lookup-key
940                ;;
941                (current-local-map)
942                (lookup-key  (current-local-map) "\C-m"))
943               (lookup-key  (current-global-map) "\C-m")))
944          to)
945     ;;  If we redefine return key here, user will nver get out.
946     ;;  C-m is exit-minibuffer.
947
948     (if (string-match "minibuf" (buffer-name))
949         (error "TinyTab: Return key-mode not allowed in minibuffer."))
950     (setq verb (interactive-p))
951     (cond
952      ((or (null mode) (not (integerp mode)))
953       (setq to (if (eq now 'tinytab-auto-indent)
954                    'newline
955                  func)))
956      ((< mode 1)
957       (setq to 'newline))
958      (t
959       (setq to func)))
960     (local-set-key "\C-m" to)
961     (if verb
962         (message "TinyTab Return key auto indent %s"
963                  (if (eq to func)
964                      "on"
965                    "off")))
966     to))
967
968 ;;; ----------------------------------------------------------------------
969 ;;;
970 (defun tinytab-indent-region-dynamically (beg end)
971   "Move region BEG END until exit key is pressed.
972 For ey setup, see `tinytab-:indent-region-key-list'. The default keys  are:
973
974 LEFT   RIGHT
975    q   w      by 1
976    a   s      by 2
977    z   x      by 4"
978   (interactive "*r")
979   (let* ((i     1)
980          (k     tinytab-:indent-region-key-list)
981          (msg   tinytab-:indent-region-key-message)
982          ch
983          EXIT)
984     (if (not (eq (length k) 7))
985         (error "Not enough members in tinytab-:indent-region-key-list."))
986     (setq EXIT (nth 6 k))
987     (while (not (eq EXIT (setq ch
988                                (downcase
989                                 (read-char-exclusive msg)))))
990       (setq i nil)
991       (cond
992        ((eq ch (nth 0 k))
993         (setq i -1))
994        ((eq ch (nth 1 k))
995         (setq i 1))
996        ((eq ch (nth 2 k))
997         (setq i -2))
998        ((eq ch (nth 3 k))
999         (setq i 2))
1000        ((eq ch (nth 4 k))
1001         (setq i -4))
1002        ((eq ch (nth 5 k))
1003         (setq i 4)))
1004       (if i
1005           (indent-rigidly (region-beginning) (region-end) i)))))
1006
1007 ;;}}}
1008
1009 (add-hook 'tinytab-:mode-define-keys-hook 'tinytab-mode-define-keys)
1010
1011 (provide   'tinytab)
1012 (run-hooks 'tinytab-:load-hook)
1013
1014 ;;; tinytab.el ends here