1 ;;; tinymy.el --- Collection of simple solutions.
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1995-2007 Jari Aalto
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program, call M-x tinymy-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
39 ;; ....................................................... &t-install ...
40 ;; Put this file on your Emacs-Lisp load path, add following into your
41 ;; ~/.emacs startup file.
44 ;; (tinymy-compile-run-command-advice) ;; Activate smart M-x compile
46 ;; If you get key binding conflict when you load this package, either
47 ;; relocate keys, modify `tinymy-:define-key-table' or use forced bindings
48 ;; by adding this statement prior `require' command.
50 ;; (setq tinymy-:define-key-force t)
52 ;; AUTOLOAD SETUP INSTRUCTIONS
54 ;; This package can't be autoloaded easily, because it installs timers
55 ;; and many global bindings. One possible way to autoload this package is
56 ;; to rely on the fact that you will most likely use function to match
57 ;; parens: like "(this)". The autoload below is quite tricky, see if
58 ;; you can learn from it. What if effective does, is a) put temporary
59 ;; function under key "%", when you press it b) function gets called
60 ;; and tinymy.el is loaded c) it wipes itself away and assigns
61 ;; function `tinymy-vi-type-paren-match' to the "%" key.
64 ;; (ti::definteractive
65 ;; (let ((function (lookup-key global-map "%")))
66 ;; (global-unset-key "%") ;; tinymy.el doesn't complain
68 ;; ;; Now run whatever user had there.
71 ;; (self-insert-command 1))
72 ;; ;; Second time, direc calls here
75 ;; 'tinymy-vi-type-paren-match))))
77 ;; There are some scripts included in this module and you can unpack them
78 ;; with following commands. You need `pgp' and `tar' executable in path for
79 ;; this to work. The extra scripts are for compile command C-z c c,
80 ;; but you don't need them necessarily.
82 ;; M-x load-library RET tinymy RET
84 ;; Modify the following variable and put your own installation there if the
85 ;; default setting is interfering your setup. Please remember to look the
86 ;; _source_ code of `tinymy-define-keys' which is run when package loads.
87 ;; Function overrides some default Emacs key bindings.
89 ;; tinymy-:define-key-table
91 ;; ;; Redefine hook so that it doesn't
92 ;; ;; override Emacs keys. Define them somewhere else.
94 ;; (add-hook 'tinymy-:load-hook 'tinymy-install)
95 ;; (add-hook 'tinymy-:load-hook 'tinymy-alias)
97 ;; If you have any questions, use 'submit' function. In case of error
98 ;; or misbehavior, turn on the debug too and send the debug result and
99 ;; describe what you did and where went wrong.
101 ;; M-x tinymy-debug-toggle
102 ;; M-x tinymy-submit-bug-report
107 ;; ..................................................... &t-commentary ...
112 ;; Emacs startup files started to look quite interesting:
114 ;; emacs-rc-tips emacrs-rc-el emacrs-rc-el
115 ;; emacs-rc-18 emacs-rc-19 emacs-rc-abb emacs-rc-compile
116 ;; emacs-rc-debug emacs-rc-default emacs-rc-font emacs-rc-ding
117 ;; emacs-rc-font.b emacs-rc-gnus emacs-rc-hooks
118 ;; emacs-rc-init emacs-rc-init2 emacs-rc-mail
119 ;; emacs-rc-o emacs-rc-o-19 emacs-rc-out
120 ;; emacs-rc-path emacs-rc-pc emacs-rc-prog emacs-rc-set
121 ;; emacs-rc-test emacs-rc-time emacs-rc-tips emacs-rc-vc
122 ;; emacs-rc-w3 emacs-rc-x-menu emacs-rc-xe
126 ;; Private functions seemed to grow all the time, most of which were
127 ;; turned into packages, but sometimes it was just function or two
128 ;; that made a life with Emacs easier. What you see here is a
129 ;; selection of so called general *my* functions. The term *my* does
130 ;; not refer to *mine*, but has a background in function
131 ;; naming. Remember? All user functions are recommended to be named
132 ;; so, that the first word is `my-', like `my-FUNC-NAME-HERE'
134 ;; Overview of features
138 ;; o RMAIL/other buffers saved in regular intervals.
139 ;; o Revert buffer in background and ask confirmation, if file
140 ;; has changed on disk. By <duthen@cegelec-red.fr>
141 ;; (Jacques Prestataire) This feature is automatically disabled
142 ;; if autorevert.el is present and running.
143 ;; o Mail lock watchdog. If you have this lock in your file system,
144 ;; you cannot receive mail.
148 ;; o Cursor changes shape according to `overwrite-mode'
149 ;; o Rename any buffer with one key `C-z' `n' to be able to launch
150 ;; e.g. new *shell* or *mail* buffer.
151 ;; o Scroll command goes to window end/beginning and does not scroll
152 ;; immediately. See variable `tinymy-:scroll-mode' for more.
153 ;; o Trim trailing whites paces from the buffer when file
154 ;; is saved. This featue is automatically disabled if
155 ;; whitespace.el is noticed.
156 ;; o Gzip or unzip current file buffer.
157 ;; o Add up numbers in rectangle area
161 ;; o Guess compile command by looking at the buffer content
162 ;; Configure variable `tinymy-:compile-table' and
163 ;; `tinymy-:compile-command-c-code'. The compile command you
164 ;; chose is buffer local and lasts until you change it.
165 ;; This is different than hitting M-x compile, because compile
166 ;; Does not "remember" each buffer's correct compile command.
170 ;; o Toggle write/read-only file permissions on disk with
171 ;; C-x q or `M-x' `tinymy-buffer-file-chmod'
172 ;; o If file saved had #!, it is automatically made chmod u+x.
173 ;; This feature is not installed if function
174 ;; `executable-make-buffer-file-executable-if-script-p'
179 ;; o Save lisp package in buffer like *mail* to file: find
181 ;; o Copy current buffer's contents to new mail buffer and
182 ;; set subject line. You can send diff buffers and file buffers
183 ;; conveniently this way: `C-z' `m' (Zend buffer as Mail)
187 ;; o Jump to matching paren "{([". _Bound_ to key "%".
188 ;; o Better word movement: LikeThisInC++Mode.
189 ;; Moving forward/backward always keeps cursor at the
190 ;; beginning of word. See also `c-forward-into-nomenclature'
191 ;; _Bound_ to keys `C-left', `C-right' in X and `Esc-b', `Esc-f'
192 ;; in non-windowed Emacs.
193 ;; o PgUp and PgDown behave differently; they jump to
194 ;; window's beg/end first and only next key hit scrolls.
195 ;; _Bound_ to keys `prior' and `next'. Check if your keyboard
196 ;; produces another pgUp and PgDown events.
200 ;; o Fix all backslash(\) lines in current paragraph to the
201 ;; same column as the starting line. Very useful in makefile mode,
202 ;; shell mode or when writing C/C++ macros. It even inserts missing
207 ;; o Point window and it gets cursor focus: The frame is
208 ;; raised and window selected. No need to click window any more.
209 ;; o Show File information in echo-area: Point mouse near
210 ;; the end of window and Displayed info contains
211 ;; BUFFER MODES SIZE PATH. You do not consume your mode line
212 ;; or frame title any more for buffer specific information.
215 ;; TinyMy: -rw-r--r-- 108k /users/jaalto/elisp/tinymy.el
219 ;; o Easy shar/tar/UU commands. configure variables
220 ;; `tinymy-:shar-command' and `tinymy-:tar-command'
224 ;; o Key C-x C-q now won't call vc blindly. To prevent mistakes,
225 ;; a confirmation will be asked. You can also just toggle the
226 ;; buffer's read-only flag, without engaging vc.
230 ;; o Flip the order of two windows
232 ;; Minor modes in this package
236 ;; If you have data in columns, use `C-cmS' or `M-x' `tinymy-sort-mode'
237 ;; to toggle sort mode on and off. With it you can sort columns 1-9
238 ;; easily. Mode line indicator is "S"
240 ;; Features immediately activated when package loads
242 ;; Configure variable `tinymy-:save-buffer-modes' and
243 ;; `tinymy-:save-buffer-regexp'
244 ;; o You mailbox lock is kept on eye on, if the lock remains,
245 ;; you won't be able to receive mail. (safety measure).
246 ;; o If you use procmail you want to configure
247 ;; `tinymy-:mail-check-inbox-file-permissions'
248 ;; otherwise, your mailbox's mode permissions are kept eye on:
249 ;; "Permission error: -rw-------" warning will be show if the
250 ;; mailbox doesn't have right modes.
251 ;; o Automatic window selection when you point it with mouse cursor.
252 ;; See `tinymy-:install-select-window-auto'.
253 ;; o When buffer that has `#!' to indicate shell
254 ;; script, is save, the +x flag is set on for the file.
256 ;; What commands are defined when you load this file?
258 ;; It's better to look at the code of this file, than to explain all the
259 ;; key definitions here, because I may not remember update this
260 ;; text section every time I add new interactive commands to the file.
262 ;; All the new interactive commands can be found from these two
265 ;; tinymy-define-keys
266 ;; tinymy-mail-common-keys
268 ;; See their description, or alternatively hit
270 ;; C-h m ;; to view all bindings
271 ;; M-x delete-non-matching-lines tinymy ;; show bound keys
275 ;; When you load this package, you can also install global
276 ;; key-bindings that if you set the load hook:
278 ;; (add-hook 'tinymy-:load-hook 'tinymy-install)
279 ;; (add-hook 'tinymy-:load-hook 'tinymy-define-keys)
280 ;; (add-hook 'tinymy-:load-hook 'tinymy-define-key-extra)
281 ;; (add-hook 'tinymy-:load-hook 'tinymy-alias)
283 ;; If you want to use your own bindings, use it like this:
285 ;; (add-hook 'tinymy-:load-hook 'tinymy-install
286 ;; (add-hook 'tinymy-:load-hook 'tinymy-alias)
287 ;; (add-hook 'tinymy-:load-hook 'my-tinymy-keys)
289 ;; (defun my-tinymy-keys ()
290 ;; <define my own global key mappings>)
292 ;; There is table of global bindings which you can modify if the
293 ;; bindings clash: the auto install will warn you about this
294 ;; automatically and your own bindings are not replaced by default.
295 ;; See variable: `tinymy-:define-key-table'
305 ;;; ......................................................... &require ...
310 (defvar track-mouse) ;ByteComp silencer for XEmacs
311 (ti::package-package-require-timer)
312 (autoload 'compile-internal "compile")
313 (autoload 'operate-on-rectangle "rect")
314 (defvar gnus-article-buffer)
315 (defvar gnus-original-article-buffer)
316 (defvar gnus-summary-buffer))
319 (ti::package-use-dynamic-compilation)
322 (ti::package-defgroup-tiny TinyMy tinymy-: tools
323 "Collection of small so called 'my' utility functions.
324 The full feature list is in the source code documentation, read it well.")
327 ;;{{{ setup: variables
329 ;;; .......................................................... &v-bind ...
330 ;;; Change this table if you have conflicting bindings.
333 (defcustom tinymy-:define-key-force nil
334 "*If non-nil; assign keys without any check."
338 (defcustom tinymy-:define-key-table
340 ;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. C-x . .
341 ;; The 'rectangle' map. This sould be free
343 ("\C-xrA" . tinymy-add-rectangle)
345 ;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. C-c . .
346 ;; minor modes in 'm' map
348 ("\C-cmS" . tinymy-sort-mode)
350 ;; .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. C-z . .
351 ;; Pick "c" for all (c)ompile commads, now define additional
352 ;; "c" for this particular command
354 ("\C-zcc" . tinymy-compile-run-command)
356 ("\C-zm" . tinymy-mail-buffer)
357 ("\C-zS" . ti::buffer-surround-with-char)
359 ;; Oher miscellaneout to "x" extra map
361 ("\C-zxc" . tinymy-copy-file) ;; Make backup (RCS version included)
363 ("\C-zxf" . tinymy-package-save-to-file)
364 ("\C-zxt" . tinymy-trim-blanks)
366 ("\C-zxw" . tinymy-flip-windows)
367 ("\C-zxz" . tinymy-buffer-file-gzip)
369 ;; 's' for shell commands
371 ("\C-zxss" . tinymy-shar)
372 ("\C-zxst" . tinymy-tar))
373 "*Define command to `global-map' keys.
374 See also source code for `tinymy-define-keys' which will overwrite
375 default Emacs keybindings if installed in `tinymy-:load-hook'.
384 (string :tag "Key Bind sequence")
388 ;;; ......................................................... &v-hooks ...
390 (defcustom tinymy-:load-hook '(tinymy-install)
391 "*Hook that is run when package is loaded.
392 The default value is '(tinymy-install)"
396 (defcustom tinymy-:mail-buffer-hook nil
397 "*This hook run last in `tinymy-mail-buffer' function."
401 ;;; ....................................................... &vu-config ...
402 ;;; all "vu" -- "variable user" sections are meant for user configurable
404 (defcustom tinymy-:install-select-window-auto 'no
405 "*Variable is used only in window system.
406 The automatic window selection function selects window by pointing
407 at it with mouse . No clicking is needed. However if you use menu bar, it is
408 a bit difficult to use this automatic selection feature, because the
409 menu bar reflects the current window: When you are at lower window and reach
410 for the menu bar, the upper window gets selected and the menu bar reflects
411 that window. You never get menu bar for the other windows but for the
412 topmost one. (Well, you can go round of Emacs, and then reach for
413 menu-bar, but that's a bit awkward)
415 Values in this variable:
426 (defcustom tinymy-:register ?r
427 "*An Emacs register where to put results of commands.
428 User can then afterwards yank the result into desired buffer."
432 (defcustom tinymy-:scroll-mode 'window
433 "*If non-nil, then `tinymy-scroll-down' does not immediately scroll.
434 The following happen if variable is non-nil.
435 o up: if the cursor is not at the window's start line, go there
436 o up: if cursor is at window's top, line, now scroll
437 o down: --''-- behaves same as up"
441 (defcustom tinymy-:copy-file-suffix ".original"
442 "Suffix to add when making copy of file with `tinymy-copy-file'.
443 This variable is only used in interactive call. Default extension
444 is \".original\", same as used by Unix 'patch' program to save original
447 If the version number can be found from file, that is suggested instead
452 ;;; ...................................................... &v-matching ...
454 (defcustom tinymy-:vi-type-paren-match-special-list '( ?\" ?\' ?\$ )
455 "*List of special character to matched in \\[tinymy-vi-type-paren-match].
456 If the sentence delimited by these chars spread multiple lines,
457 the missing part is searched backward.
459 If you call \\[tinymy-vi-type-paren-match] with optional arg, then
460 the search is forced FORWARD."
461 :type '(repeat character)
464 ;; This could have been (CH . CH) list but because XEmacs20
465 ;; has different character handling that Emacs; we prefer to check
468 ;; This is not configurable variable right now, because the match
469 ;; function uses hard coded regexps.
471 (defconst tinymy-:vi-type-paren-match-list
473 ;; NOPE, DO NOT add these. It won't work - the reason is currently unknown.
478 "List of character string pairs to match.
482 ((BEGIN-CHARACTER-PAIR-STR . END-CHARACTER-PAIR-STR)
490 ( \"[\" . \"]\" )))")
492 ;;; ......................................................... &vu-word ...
494 (defcustom tinymy-:move-word-set "-[]_$%@#&*\":;,.{}()<>/\\ \t\n"
495 "*How to move forward/backward word. This is character set."
496 :type '(string :tag "Charset")
499 (defcustom tinymy-:move-word-case-set "-[]_$%@#&*\":{}()<>/\\ \t\na-z"
500 "*How to move forward/backward word. This is character set.
501 used only over mixed case words."
502 :type '(string :tag "Charset")
505 (defcustom tinymy-:move-word-case-modes
521 "*Modes where `tinymy-:move-word-case-set' is used."
522 :type '(repeat function)
525 ;;; ........................................................ &vu-shell ...
527 (defcustom tinymy-:tar-command "tar -cf"
528 "*Tar create command, e.g. used in `tinymy-tar'."
529 :type '(string :tag "Shell command")
532 (defcustom tinymy-:shar-command "shar -a -c -C -e -t -u"
533 "*Shar command used by `tinymy-shar'.
536 -a do not protect them specially (uu)
537 -c data-integrity check using wc
538 -C Insert a line of the form --- cut here ---
539 -e code that prevents shar .. overwrite existing files.
540 -t Write diagnostics to stdout
541 -u Assume that the remote site has uudecode"
542 :type '(string :tag "Shell command")
545 ;;; ...................................................... &vu-compile ...
547 (defcustom tinymy-:compile-table
549 '("perl" . "perl -w %s")
550 '("code-shell-sh" . "sh -x %s")
551 '("code-shell-bash" . "bash -x %s")
552 '("code-shell-t?csh-" . "csh -x %s")
553 '("code-shell-ksh" . "ksh -x %s")
554 '("awk" . "awk -f %s")
555 '("xml" . tinymy-compile-xml-command)
556 '("c[+]+\\|^cc?-\\|code-c" . tinymy-compile-cc-command)
558 '("text-white-paper" . tinymy-compile-tinytf-command)
564 " -batch -f batch-byte-compile %s"))
565 '("java" . "javac %s")
567 (let ((php (executable-find "php"))
568 (php4 (executable-find "php4")))
570 (concat (or php php4) " %s"))))
572 '("sql" . tinymy-compile-sql))
573 "*Compilation table, how to run the code through interpreters.
574 The command is put into %s in the COMPILE-COMMAND part.
578 '((REGEXP-for-buffer-type . COMPILE-COMMAND)
579 (REGEXP-for-buffer-type . COMPILE-COMMAND)
584 The regexp is like 'code-c' 'code-pascal' or alternatively a
585 `mode-name' if buffer content can't be identified. See
586 tinylibid.el and function `ti::id-info' for more.
590 STRING with %s where `buffer-file-name' is inserted.
592 -- If string, then this command is suggested for file.
593 -- if something else, the content is evaled and it should return
594 compile command STRING with %s for file name.
595 -- If function, function must return complete compile command,
600 Suppose you have several perl interpreters and you want to use the
601 shebang interpreter (first line in the script) for your project's perl
602 scripts. The following code:
604 -- Looks up the existing perl compile command and stores it to ELT
605 -- Changes the right hand COMPILE-COMMAND to Lisp form that
606 determines the perl command according to file name. Function
607 `ti::buffer-shebang' reads the command interpreter from the first line.
609 (add-hook 'tinymy-load-hook 'my-tinymy-compile-customisations)
610 (autoload 'aput \"assoc\")
612 (defun my-tinymy-compile-customisations ()
613 (aput 'tinymy-:compile-table
615 '(if (string-match \"project\" buffer-file-name)
616 (concat (or (ti::buffer-shebang) \"perl\") \" -w %s\")
619 If you always want to use the shebang command interpreter, then you
622 (add-hook 'tinymy-load-hook 'my-tinymy-compile-customisations)
623 (autoload 'aput \"assoc\")
625 (defun my-tinymy-compile-customisations ()
626 (aput 'tinymy-:compile-table
628 '(concat (or (ti::buffer-shebang) \"perl\") \" -w %s\")))
630 After this package has been loaded. (Place customizations like this
631 to `tinymy-:load-hook'."
633 (string :tag "Regexp")
634 (string :tag "Shell command"))
638 ;;{{{ setup: other, version
640 (defvar tinymy-:buffer-info-cache nil
641 "Cached buffer data values in function `tinymy-buffer-info'.
643 '((buffer-pointer size message-string)
646 ;;;###autoload (autoload 'tinymy-version "tinymy" "Display commentary." t)
649 (ti::macrof-version-bug-report
653 "$Id: tinymy.el,v 2.86 2007/05/07 10:50:08 jaalto Exp $"
656 tinymy-:vi-type-paren-match-list
657 tinymy-:define-key-force
658 tinymy-:define-key-table
660 tinymy-:mail-buffer-hook
661 tinymy-:install-select-window-auto
664 tinymy-:copy-file-suffix
665 tinymy-:vi-type-paren-match-special-list
666 tinymy-:move-word-set
667 tinymy-:move-word-case-set
668 tinymy-:move-word-case-modes
671 tinymy-:compile-table
672 tinymy-:save-buffer-modes
673 tinymy-:save-buffer-regexp
675 tinymy-:revert-in-progress
676 tinymy-:revert-buffer-info-list
677 tinymy-:window-previous)
678 '(tinymy-:debug-buffer)))
680 ;;;### (autoload 'tinymy-debug-toggle "tinymy" t t)
682 (eval-and-compile (ti::macrof-debug-standard "tinymy" "-:"))
687 ;;; ----------------------------------------------------------------------
690 (defun tinymy-define-keys ()
694 (when (boundp 'shared-lisp-mode-map)
695 (defvar shared-lisp-mode-map nil) ;; Byte compiler silencer
696 (define-key shared-lisp-mode-map "%" 'tinymy-vi-type-paren-match))
698 (define-key emacs-lisp-mode-map "%" 'tinymy-vi-type-paren-match)
699 (define-key lisp-mode-map "%" 'tinymy-vi-type-paren-match)
701 ;; was C-xq was kbd-macro-query
703 (global-set-key "\C-xq" 'tinymy-buffer-file-chmod)
705 ;; Redefine scroll keys, we don't confirm these...
707 (global-set-key [(prior)] 'tinymy-scroll-up)
708 (global-set-key [(next)] 'tinymy-scroll-down)
710 ;; In XEmacs these already have default bindings, but we override them.
712 (global-set-key [(control right)] 'tinymy-word-forward)
713 (global-set-key [(control left)] 'tinymy-word-backward)
714 (global-set-key [(control up)] 'tinymy-beginning-of-defun)
715 (global-set-key [(control down)] 'tinymy-end-of-defun)
717 (unless (ti::compat-window-system)
718 (global-set-key [(meta f)] 'tinymy-word-forward)
719 (global-set-key [(meta b)] 'tinymy-word-backward))
721 ;; Use C-z prefix because it is most user friendly to pinky
722 ;; Pretty useless in X-windowed Emacs, and in windowed
723 ;; Emacs you seldom use suspend-emacs because emacs has M-x shell
725 (ti::use-prefix-key global-map "\C-z")
727 ;; Set global keys, confirm these
732 (if tinymy-:define-key-force
733 (define-key global-map (car x) (cdr x))
734 (ti::define-key-if-free global-map
737 'tinymy-define-key-error))))
738 tinymy-:define-key-table)
740 ;; .................................................... &emacs-modes ...
742 (add-hook 'makefile-mode-hook 'tinymy-makefile-mode-hook)
744 (defun tinymy-makefile-mode-hook ()
745 "Define key C-c/ to adjust \\ continuing lines."
747 (symbol-value 'makefile-mode-map) "\C-c\\"
748 'ti::buffer-backslash-fix-paragraph)))
750 ;;; ----------------------------------------------------------------------
753 (defun tinymy-define-keys-extra ()
754 "Define extra global keys."
756 (global-set-key "%" 'tinymy-vi-type-paren-match)
757 (global-set-key "\C-x\C-q" 'tinymy-buffer-read-only))
759 ;;; ----------------------------------------------------------------------
761 (defun tinymy-install-mouse-movement-handler (&optional uninstall)
762 "Install or UNINSTALL `tinymy-mouse-movement-handler'
764 `tinymy-:install-select-window-auto'."
766 (when (and (not uninstall)
767 (ti::compat-window-system))
769 (or (eq tinymy-:install-select-window-auto 'yes)
771 (eq tinymy-:install-select-window-auto 'ask)
775 "TinyMy: Are you sure? "
776 "This feature conflicts with menubar usage")))))))
780 (ti::win32-p) ;; Bug in Win32; works in Unix Emacs
781 (ti::emacs-type-win32-p) ;; Cygwin Emacs is ok
782 (string-match "^21" emacs-version))
783 ;; Bug in Win32 21.[123] makes Emacs to behave starangely
784 ;; when mouse-movement tracking is enabled.
786 ;; Use this code to check your Emacs: Start fresh emacs, and run it.
787 ;; If the Frame's menu-bar
788 ;; line constantly flickers, then Emacs is broken. This code
789 ;; does not work in XEmacs (there is no track-mouse)
792 ;; (defun test (event)
794 ;; (message "mouse movement ok")
796 ;; (setq track-mouse t)
797 ;; (global-set-key [(mouse-movement)] 'test))
799 (message "Tinymy: [NOTICE] `mouse-movement' \
800 has changed in Emacs 21.x. Unable to install handler."))
803 (setq track-mouse t) ;This is essential
804 ;; Make sure that this handler is not occupied yet
805 (if (memq (lookup-key global-map [(mouse-movement)])
806 '(tinymy-mouse-movement-handler
810 (global-set-key [(mouse-movement)]
811 'tinymy-mouse-movement-handler)
813 ** tinymy.el: can't install mouse-movement handler, already occupied.")))
816 ;; See also package mode-motion+.el
818 ;; `mode-motion-hook' is buffer local. Hm. And it is called from
819 ;; `default-mouse-motion-handler' inside `save-window-excursion'.
820 ;; Not good. Window can't be changed form that hook, so we must
821 ;; replace function in `mouse-motion-handler'.
823 (defvar mouse-motion-handler nil) ;ByteComp silencer in Emacs
824 (if (eq (symbol-value 'mouse-motion-handler)
825 'default-mouse-motion-handler)
826 (defconst mouse-motion-handler
827 'tinymy-default-mouse-motion-handler-xemacs)
829 ** tinymy.el: Can't install: `mouse-motion-handler' is not default."))
830 (defun tinymy-default-mouse-motion-handler-xemacs (event)
831 "Call `default-mouse-motion-handler' and
832 `tinymy-mouse-movement-handler'."
833 (prog1 (ti::funcall 'default-mouse-motion-handler event)
834 (tinymy-mouse-movement-handler event))))))))
836 ;;; ----------------------------------------------------------------------
839 (defun tinymy-install-after-save-hook (&optional uninstall)
840 "Intall or UNINSTALL functions to `after-save-hook'."
841 (let ((func 'executable-make-buffer-file-executable-if-script-p))
842 (when (and (fboundp func)
843 (memq func after-save-hook))
844 ;; #todo: Watch Emacs version when this is fixed.
845 ;; Latest Emacs versons have this in executable.el
846 ;; Un fortortunately Emacs 21.3 has bug for Ange-FTP remote
847 ;; files, where this signals error, so don't use it.
850 "TinyMy: `%s' does not work for remote files. Removed from"
851 " `after-save-hook'.")
853 (remove-hook 'after-save-hook func))
854 (ti::add-hooks 'after-save-hook
855 'tinymy-maybe-make-file-executable
858 ;;; ----------------------------------------------------------------------
861 (defun tinymy-install (&optional uninstall)
862 "Intall or UNINSTALL package. Configure Emacs variables and bindings."
864 (when (ti::compat-window-system)
865 (tinymy-install-mouse-movement-handler uninstall))
866 (tinymy-install-after-save-hook uninstall))
868 ;;; ----------------------------------------------------------------------
870 (defun tinymy-define-key-error (key def)
871 "Call back function. Warn about conflicting key binding for KEY and DEF."
872 (message "TinyMy: Cannot auto-install, key already occupied: %s %s"
878 ;;; ----------------------------------------------------------------------
881 (defun tinymy-buffer-file-chmod (&optional verb)
882 "Toggle current buffer's Read-Write permission permanently on disk. VERB.
883 Does nothing if buffer is not visiting a file or file is not owned by us."
885 (let* ((file (buffer-file-name))
888 (when (and file (file-modes file)) ;File modes is nil in Ange-ftp
889 (setq stat (ti::file-chmod-w-toggle file))
893 (message "TinyMy: chmod w+")
894 (setq buffer-read-only nil))
896 (message "TinyMy: chmod w-")
897 (setq buffer-read-only t))
899 (message "TinyMy: couldn't chmod")))
900 (ti::compat-modeline-update)))))
905 ;;; ----------------------------------------------------------------------
907 (defun tinymy-buffer-file-gzip ()
908 "Compress or uncompress current file buffer with gzip."
911 (let* ((gzip "gzip"))
913 ((or (not (stringp buffer-file-name))
914 (null (file-modes buffer-file-name))) ;Ange ftp
915 (message "timy. Can't gzip this buffer."))
916 ((or (ti::vc-rcs-file-exists-p buffer-file-name)
917 (and (fboundp 'vc-registered)
918 (ti::funcall 'vc-registered buffer-file-name)))
919 (message "TinyMy: This file is VC controlled. No gzip allowed."))
920 ((string-match "\\.gz$" buffer-file-name)
921 (call-process gzip nil nil nil "-d" buffer-file-name)
922 (setq buffer-file-name (replace-regexp-in-string
923 "\\.gz$" "" buffer-file-name))
924 (rename-buffer (file-name-nondirectory buffer-file-name))
925 (set-visited-file-modtime))
927 (call-process gzip nil nil nil "-9" buffer-file-name)
928 (unless (string-match "\\.gz$" buffer-file-name)
929 (setq buffer-file-name (concat buffer-file-name ".gz")))
930 (rename-buffer (file-name-nondirectory buffer-file-name))
931 (set-visited-file-modtime)))))
933 ;;; ----------------------------------------------------------------------
935 (defun tinymy-buffer-read-only ()
936 "Put buffer in `view-mode' if read-only is turned on.
938 Important, If file is vc controlled:
940 This function is ment for changing the
941 buffer characteristics without changing the version control state.
943 Normally \\[toggle-read-only] would do CheckOut if the file was
944 read-only, but sometimes it is convenient to put buffer to read-only
945 state to prevent changing anything in there for a while."
947 (let* ((fid "tinymy-buffer-read-only")
948 (key-func (if (or (featurep 'vc)
949 (featurep 'vc-hooks))
955 (unless fid ;; No-op. XEmacs byte compiler silencer
961 "FILE" buffer-file-name)
962 (ti::save-line-column-macro nil nil
964 ((memq major-mode '(dired-mode)) ;plain C-x C-q for these modes...
968 ((and (eq key-func 'vc-toggle-read-only)
969 buffer-file-name ;maybe *temp* buffer ?
970 (vc-name buffer-file-name)) ;is file registered ?
972 (if (y-or-n-p "Call vc? ")
973 (call-interactively 'vc-toggle-read-only)
976 (call-interactively key-func)))
977 (tinymy-debug fid "STATE after" buffer-read-only)
978 (setq state buffer-read-only) ;what happened ?
979 (setq turn-mode ;can't use nil, because it toggles
981 (view-mode turn-mode))))))
986 ;;; ----------------------------------------------------------------------
988 (defun tinymy-flip-windows ()
989 "Switch window order. There must be only 2 windows."
991 (when (> (count-windows) 1)
992 (let ((first-buffer (window-buffer (selected-window)))
993 (second-buffer (window-buffer (next-window (selected-window)))))
994 (set-window-buffer (selected-window) second-buffer)
995 (set-window-buffer (next-window (selected-window)) first-buffer))))
1000 ;;; ----------------------------------------------------------------------
1002 (defun tinymy-cursor-set-type (cursor &optional frame)
1003 "Set the CURSOR type for the named FRAME."
1005 (setq frame (selected-frame)))
1006 ;; Do the modification.
1007 (modify-frame-parameters
1009 (list (cons 'cursor-type cursor))))
1011 ;;; ----------------------------------------------------------------------
1013 (defun tinymy-cursor-overwrite-mode ()
1014 "Set the cursor-type according to the insertion mode"
1017 (let ((cursor (or (frame-parameter (selected-frame) 'cursor-type)
1019 (put 'tinymy-cursor-overwrite-mode 'saved-cursor-type cursor)
1020 ;; The type is going to change to 'bar, but if user has it
1021 ;; on by default, pick the opposite.
1022 (tinymy-cursor-set-type (if (equal cursor 'bar)
1026 (tinymy-cursor-set-type
1027 (get 'tinymy-cursor-overwrite-mode 'saved-cursor-type)))))
1029 ;;; ----------------------------------------------------------------------
1031 (if (fboundp 'overwrite-mode-hook)
1032 (add-hook 'overwrite-mode-hook 'tinymy-cursor-overwrite-mode-hook)
1033 (defadvice overwrite-mode (around tinymy act)
1034 "Change cursor to 'block or 'bar according to `overwrite-mode'."
1036 (tinymy-cursor-overwrite-mode)))
1038 ;;; ----------------------------------------------------------------------
1040 (defsubst tinymy-buffer-info-cache-string (buffer)
1041 "If same size, return cached string from `tinymy-:buffer-info-cache'."
1042 (when (and (setq buffer (assq buffer tinymy-:buffer-info-cache))
1043 (or (eq (nth 1 buffer) (buffer-size))
1044 ;; It it's modified, it hasn't been written to disk yet,
1045 (buffer-modified-p)))
1048 ;;; ----------------------------------------------------------------------
1050 (defun tinymy-buffer-info-1 ()
1051 "Display buffer information:
1052 If buffer is associated to file: -rwx-rw-r-- 20k /absolute/path/file.txt
1053 If no file: SIZEk SIZE-IN-BYTES"
1055 (let* ((file buffer-file-name)
1056 (ssize (buffer-size))
1057 (size (/ ssize 1000)) ;; well, it's 1024 to exact but this suffices
1060 ;; E.g. Gnus defines `buffer-file-name' for Draft messages,
1061 ;; but the file is not actually written, so we test for existense
1062 ;; to prevent suprises from happening.
1064 ((and (memq major-mode '(dired-mode vc-dired-mode))
1065 (boundp 'dired-directory))
1066 (setq lines (- (count-lines (point-min) (point-max)) 2))
1067 (format "Tinymy: count %d %s"
1069 (symbol-value 'dired-directory)))
1071 (or (string-match "@" file) ;; Ange-ftp file is ok.
1072 (and (file-exists-p file)
1074 (ti::file-access-mode-to-string (file-modes file)))))
1075 (format "%s %dk %s" (or modes "") size file))
1077 (format "buffer size %dk (%d bytes)" size ssize)))))
1079 ;;; ----------------------------------------------------------------------
1081 (defun tinymy-buffer-info ()
1082 "Display buffer information."
1083 (let ((old-message (tinymy-buffer-info-cache-string (current-buffer))))
1085 (message old-message)
1086 (setq old-message (tinymy-buffer-info-1))
1087 (setq tinymy-:buffer-info-cache
1088 (delq (current-buffer) tinymy-:buffer-info-cache))
1093 tinymy-:buffer-info-cache))))
1095 ;;; ----------------------------------------------------------------------
1096 ;;; >How can I get the selected window to change as I move the mouse cursor
1097 ;;; >into that window? In other words, I don't want to have to click the
1098 ;;; >mouse in the new window every time I move between windows (windows, not
1099 ;;; >frames, this is not a click-to-focus window manager question).
1101 ;;; This function was elp'ed to see how heavy it is for `mouse-handler'.
1102 ;;; In byte compiled format the results in HP 10.20/9000/715
1104 ;;; Function Name Call Count Elapsed Time Average Time
1105 ;;; =========================== ========== ============ ============
1106 ;;; tinymy-mouse-movement-handler 29 0.0571780000 0.0019716551
1108 (defvar tinymy-:window-previous nil
1109 "Used in `tinymy-mouse-movement-handler'.")
1111 (defun tinymy-mouse-movement-handler (event)
1112 "Nice mouse movement EVENT handler.
1114 Change window automatically:
1116 If you point a nother window where cursor was, the new window is
1117 automatically made active.
1119 Show information on echo-area:
1121 If you point mouse near the end of botton line (right hand corner),
1122 a brief file information is shown in echo area. If window is bigger
1123 than the text that is at the beginning of it, pointing to the end
1124 of text is sufficient. Example output:
1126 TinyMy: -rw-r--r-- 108k /users/jaalto/elisp/tinymy.el"
1129 (let* ((case-fold-search t)
1137 ((and (fboundp 'event-window)
1139 ;; XEmacs calls us from motion hook
1140 ;; #<motion-event 644, 221>
1141 (setq win (ti::funcall 'event-window event)))
1142 ((and (fboundp 'posn-window)
1143 (fboundp 'event-start)
1145 (setq win (posn-window (event-start event))))
1147 ;; Unknown Emacs or interface changed radically
1148 (message "Tinymy: tinymy-mouse-movement-handler error.\
1149 Contact maintaner with M-x tinymy-submit-bug-report.")))
1151 (setq bottom (and win (window-end))
1156 ;; ............................................ auto window select ...
1157 ;; The WIN could be frame pointer too, that's why we check it.
1159 ((null win)) ;; WE HAVE NO WINDOW INFORMATION, stop.
1160 ;; ............................................. different window ...
1163 ;; Motion in same window as prereviously?
1164 (not (eq tinymy-:window-previous win)))
1165 (setq tinymy-:window-previous win
1166 mini (window-minibuffer-p win))
1167 ;; 1. Select window if it's not minibuffer
1168 ;; 2. if it's minibuffer, select it _only_ if it's active
1169 (when (or (not mini)
1170 (minibuffer-window-active-p win))
1171 (setq frame (window-frame (select-window win)))
1173 ;; FIXME: Is this really needed?
1174 (select-frame frame)))
1175 ;; ....................................... Special 'info' handler ...
1176 ((and (not (window-minibuffer-p (selected-window)))
1177 (not (eq (point-min) (point-max)))) ;Not empty buffer?
1178 ;; ........................................... pointing with mouse ...
1179 (when (integerp point) ;POINT could be 'mode-line
1180 (setq p point) ;Crossing window border
1181 ;; (message "%d %d %d " p bottom (- bottom p) )
1182 ;; Threshold of NN characters, near the right hand lower corner.
1183 ;; Make the call `inline' because `tinymy-mouse-movement-handler'
1184 ;; is called very often
1185 (when (and p (< (- bottom p) 50))
1186 (inline (tinymy-buffer-info))
1187 ;; mic paren: If your cursor is at end of defun
1188 ;; parenthesis, and this function is called, the
1189 ;; mic-paren will still display the beginning of function
1190 ;; info. Out info is not show...
1191 (defvar mic-paren-backw-overlay nil) ;No-op, ByteComp
1192 (if (and (featurep 'mic-paren)
1193 ;; This overlay exists if cursor was on paren
1194 mic-paren-backw-overlay)
1196 ;; Integrate with Emacs 21.3
1197 (when (fboundp 'tooltip-mouse-motion)
1198 (ti::funcall 'tooltip-mouse-motion event))))
1201 ;;{{{ elisp: package saving from mail, gnus
1203 ;;; ----------------------------------------------------------------------
1205 (defun tinymy-package-save-get-file-name ()
1206 "See `tinymy-package-save-to-file'. Find out package file name.
1207 Return '(file-name point)."
1208 (let* ((fid "tinymy-package-save-get-file-name:")
1209 ;; - the file start and it's name
1210 ;; - The regexp will jump until there is a-zA-Z0-9
1211 (com "^\\(#\\|;;+\\)")
1212 (re1 (concat com "[ \t]+\\([^ \t]+\\.el\\)[ \t]+[-][-]+"))
1213 (re2 (concat com "[ \t]+\\([^ \t]+\\)[ \t]+[-][-]+[ \t]"))
1214 (re3 (concat com "[ \t]+\\(.*\\)[ \t]+[-][-]+"))
1215 (re4 (concat com "[ \t]+\\(.*\\)[ \t]+[-]+"))
1216 (re5 "^\\(;;;*\\)[ \t]+\\([^ \t\n]+\\.el\\)[ \t]+")
1219 (unless fid ;; No-op. XEmacs byte compiler silencer
1223 ;; See if we can detect the package name in this buffer
1224 (when (dolist (re (list re5 re1 re2 re3 re4))
1225 (when (re-search-forward re nil t)
1226 (tinymy-debug fid 'MATCH re 'LINE (ti::read-current-line) "\n")
1228 (setq file (match-string 2)
1229 com (match-string 1)
1230 point (line-beginning-position))
1231 ;; Verify that we found correct point
1233 (when (or (looking-at "^.*end.*here")
1234 ;; If the point is near the end of file, reject it
1235 (> (- (point-max) (* 3 80))
1237 ;; Nope, wrong position found. Try again.
1238 (goto-char (point-min))
1239 (when (re-search-forward "^;;;")
1240 (setq point (line-beginning-position))))
1241 ;; Suppose this is a lisp file, because comment mark is colon(;)
1242 ;; make sure the filename has .el at the end
1243 (tinymy-debug fid 'BUFFER (buffer-name) 'FILE file "\n")
1245 (when (looking-at "^[ \t]*;")
1246 (setq file (ti::string-verify-ends file "\\.el" ".el")
1247 file (or (locate-library file) file)))))
1248 (tinymy-debug fid 'BUFFER (current-buffer) 'RET file 'POINT point)
1253 (message "TinyMy: (package save) No proper File header found.")
1256 ;;; ----------------------------------------------------------------------
1258 (defun tinymy-package-save-to-file-buffer-beginning (file)
1259 "Find proper file beginning point.
1264 (let* ((fid "tinymy-package-save-to-file-buffer-beginning:")
1265 (fname (file-name-sans-extension (file-name-nondirectory file)))
1266 (ext (file-name-extension file))
1268 ;; file\\(.ext\\)? -- description
1269 ;; ;;; @(#) file.ext --- description
1271 ;; see unix SunOS what(1) command
1272 (format "^\\([^ \t\n:,.-]+\\) +\\(%s[ \t]*\\)?%s[ \t]+-+[ \t]+"
1273 (regexp-quote "@(#)")
1275 (regexp-quote fname)
1276 "\\(\\." (regexp-quote ext) "\\)?")))
1278 (unless fid ;; No-op. XEmacs byte compiler silencer
1282 (when (re-search-forward regexp nil t)
1283 (setq point (line-beginning-position))))
1286 'BUFFER (current-buffer)
1291 ;;; ----------------------------------------------------------------------
1293 (defun tinymy-package-save-to-file-buffer-ending (&optional start-point)
1294 "Find proper file ending starting from START-POINT.
1295 Return point or nil."
1296 (let* ((fid "tinymy-package-save-to-file-buffer-ending:")
1297 ;; - the file start and it's name
1298 ;; - The regexp will jump until there is a-zA-Z0-9
1299 (com "^\\(#\\|;;+\\)")
1302 "[ \t]+\\(end[ \t]+of[ \t]\\(file\\)?\\|^;.*&eof\\)"
1303 "\\|^;;+[ \t]+.*ends here"))
1304 ;; Yes, it really does have trailing space
1305 ;; "- -- \n" is for PGP signed message which breaks the
1307 (signature-end "^\\(- \\)?-- \n")
1309 (unless fid ;; No-op. XEmacs byte compiler silencer
1313 (goto-char start-point)
1316 ((re-search-forward regexp nil t)
1318 (tinymy-debug fid 'REGEXP regexp (point) (ti::read-current-line))
1319 (setq end-point (line-beginning-position)))
1322 (re-search-backward signature-end start-point t))
1323 (tinymy-debug fid 'SIGNATURE (point))
1324 (setq end-point (line-beginning-position)))))
1327 ;;; ----------------------------------------------------------------------
1329 (defun tinymy-package-save-to-file-buffer ()
1330 "Return correct code buffer, usually `current-buffer'.
1331 For Gnus this is `gnus-original-article-buffer'."
1333 ((and (featurep 'gnus)
1334 (or (string= (buffer-name)
1335 gnus-article-buffer)
1336 (and (equal (current-buffer) gnus-summary-buffer)
1339 (buffer-name gnus-summary-buffer))))))
1340 (let ((buffer (get-buffer gnus-original-article-buffer)))
1342 (y-or-n-p "TinyMy: Use unformatted *Original Article Buffer*? "))
1343 ;; For Gnus, use the unformatted buffer
1349 ;;; ----------------------------------------------------------------------
1350 ;;; - Imagine that you're reading gnu.emacs.sources and want to get
1351 ;;; that package in the post.
1352 ;;; - Or you receive a package in private mail message...
1353 ;;; - This does the job of saving that package to file very easily.
1355 (defun tinymy-package-save-to-file (file &optional code-buffer save-start)
1356 "Save FILE in current buffer starting at optional SAVE-START.
1358 The file is supposed to have special heading and when the heading
1359 is found the file ends at `point-max' or when the footer is found
1360 The following are valid heading. See unix what(1) for the second line.
1362 ;; file.el -- description
1363 # @(#) file.txt -- description
1365 If function can't find footer
1372 it'll add one and include everything to the end of buffer,
1375 (let ((buffer (tinymy-package-save-to-file-buffer)))
1376 (with-current-buffer buffer
1377 (multiple-value-bind (file point buf)
1378 (tinymy-package-save-get-file-name)
1381 "TinyMy: Can't find filename. Select a region, M-x write-region."))
1383 (read-file-name "Save to file: "
1384 (file-name-directory file)
1385 nil ;; users null string
1387 (file-name-nondirectory file))
1390 (let* ((fid "tinymy-package-save-to-file:")
1391 (orig-point (point))
1397 (unless fid ;; No-op. XEmacs byte compiler silencer
1401 (tinymy-package-save-to-file-buffer)))
1402 ;; See if we can detect the package name in this buffer
1404 (with-current-buffer code-buffer
1406 (setq p1 (or save-start
1407 (tinymy-package-save-to-file-buffer-beginning file)
1410 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ end ^^^
1411 (setq point (tinymy-package-save-to-file-buffer-ending p1))
1416 (setq str "TinyMy: Hm, No proper save ending. Using point-max ")
1417 (tinymy-debug fid str)
1420 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ existing file ^^^
1421 (when (file-exists-p file)
1422 (setq ans (read-from-minibuffer "overwrite?: " file))
1427 (error "TinyMy: Aborted."))
1430 (tinymy-debug fid 'SAVE-FROM code-buffer p1 p2 'TO file)
1431 (when (or (eq p1 p2)
1434 TinyMy: [ERROR] Can't find region. Save manually (See M-x tinymy-version)."))
1435 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ saving ^^^
1437 (insert-buffer-substring code-buffer p1 p2)
1439 (when (string-match "\\.\\(zip\\|gz\\)$" file)
1440 (ti::use-file-compression))
1441 (write-file file) ;jka handles compressing
1443 (message (concat "TinyMy: Package saved to " file)))
1445 (goto-char orig-point)))))
1450 ;;; ----------------------------------------------------------------------
1452 (defun tinymy-maybe-make-file-executable ()
1453 "If file's first line starts with #!, make file executable.
1454 Ignores file whose `file-modes' can't be read, e.g. for ange-ftp files."
1455 (let* ((file (buffer-file-name))
1457 (not (ti::file-name-remote-p file))
1458 (file-modes file))))
1463 (let ((stat (looking-at "^#!")))
1468 ;; Do not use ".+", because it overflows
1469 ;; Emacs egexp matcher in files which are
1470 ;; one big line, like in Gnus
1471 ".?.?.?.?.?.?.?.?.?.?.?.?.?.?.?"
1473 (message "Tinymy: Suspicious #! first line."))
1475 (unless (eq 64 (logand 64 mode))
1476 (set-file-modes file (ti::file-mode-make-executable mode))))))
1478 ;;; ----------------------------------------------------------------------
1480 (defun tinymy-trim-blanks ()
1481 "Delete trailing blanks from all lines; including lines from end of buffer."
1484 (unless buffer-read-only
1485 (ti::buffer-trim-blanks (point-min) (point-max))
1486 ;; Now delete extra lines from the end of buffer
1487 (goto-char (point-max))
1488 (when (not (zerop (skip-chars-backward " \t\n")) )
1489 (forward-char 1) ;Leave newline
1490 (unless (eq (point) (point-max))
1491 (delete-region (point-max) (point))))))
1493 (message "TinyMy: Blanks trimmed"))
1494 nil) ;Clean return code
1496 ;;; ----------------------------------------------------------------------
1497 ;;; - Especially when I'm making diff to the Author I find this
1501 (defun tinymy-copy-file (file1 file2 &optional arg)
1502 "Make copy of current buffer FILE1 to FILE2 (FILE1.orig or FILE1.VER).
1503 Function tries to find possible RCS version.
1504 You usually make backup if you make a change and send diff to author.
1506 If you supply PREFIX ARG, then
1508 C - u remove the copy files; namely, (buffer-file-name).*
1509 nbr Copy back: this like doing
1513 If you had made a safe copy previously, this restores
1514 the safe copy to original file."
1516 (let* ((suf tinymy-:copy-file-suffix)
1517 (ver (or (ti::vc-rcs-buffer-version)
1518 ;; No rcs string found, then try Regular lisp package
1522 (ti::re-search-check
1523 "^;+[ \t]+Version:[ \t]*\\([0-9.]+\\)" 1 nil 'read)))
1524 (file1 (or (buffer-file-name)
1525 (error "Buffer does not visit a file.")))
1530 (if current-prefix-arg
1531 (list file1 nil current-prefix-arg)
1532 (setq file2 (read-from-minibuffer "Make copy to: " (concat file1 ext)))
1533 (list file1 file2))))
1535 (let* ((re (format "^%s\\." (file-name-nondirectory file1)))
1536 (file-list (ti::directory-files (file-name-directory file1)
1541 ((or (not (file-exists-p file2))
1542 (and (file-exists-p file2)
1543 (y-or-n-p (format "%s exists. Remove? " file2))
1547 (ti::file-delete-safe file2)
1548 (copy-file file1 file2)
1549 (message "TinyMy: safe copy done."))
1551 (message "TinyMy: sorry; cannot decide how to do the copying."))))
1553 (if (null file-list)
1554 (message "TinyMy: There are no safe copy files matching %s" re)
1555 (dolist (file1 file-list)
1556 (if (y-or-n-p (format "Delete %s ? " file1))
1557 (delete-file file1)))))
1561 (message "TinyMy: There is no safe copy for %s" file1))
1562 ((eq 1 (length file-list))
1564 (message "TinyMy: Found safe copy %s; copy it over original? "))
1565 (delete-file file1) ;copy-file barfs otherwise
1566 (copy-file (car file-list) file1)
1567 (message "TinyMy: Safe copy restored.")))
1568 ((> (length file-list) 1)
1571 "Don't know which one to use as source, complete: "
1572 (ti::list-to-assoc-menu
1573 (mapcar 'file-name-nondirectory file-list))
1575 (setq file2 (concat (file-name-directory file1) file2))
1577 (copy-file file2 file1)
1578 (message "TinyMy: Safe copy restored: %s --> %s"
1579 (file-name-nondirectory file2 )
1580 (file-name-nondirectory file1))))))))
1583 ;;{{{ key: % matching
1585 ;;; ----------------------------------------------------------------------
1586 ;;; All the posts so far in the internet to make the "%" match parens
1587 ;;; right in every possible _mode_ failed. That's why I started writing
1588 ;;; my own function, which you see here.
1590 (defun tinymy-vi-type-paren-match (&optional arg)
1591 "Match engine: find {[( or )]} pairs. ARG is character repeat count.
1592 See also 'tinymy-:vi-type-paren-match-special-list
1595 `tinymy-:vi-type-paren-match-list'
1596 `tinymy-:vi-type-paren-match-special-list'"
1599 (ptable (syntax-table)) ;previous, the original
1600 (ch (following-char))
1601 (ch-next (ti::buffer-read-char nil 1))
1602 (ch-prev (preceding-char))
1603 (pairs tinymy-:vi-type-paren-match-list)
1604 (left (car-safe (assoc (char-to-string ch) pairs)))
1605 (right (car-safe (rassoc (char-to-string ch) pairs)))
1606 (m-list tinymy-:vi-type-paren-match-special-list) ;match list
1607 (spread-limit (* 10 60)) ;approx 10 lines of code.
1609 s-func add-func max-func bigger-func
1614 ;; check if the parens are "closed", ie. there is nothing beween them
1616 ((and (ti::char-in-list-case ch m-list) ch-next)
1618 (not (string= (char-to-string ch-next) (char-to-string ch)))))
1622 (string= (char-to-string ch-prev) left)))
1625 (string= (char-to-string ch-next) right))))
1626 (when (and ch self-insert)
1627 (self-insert-command (or arg 1))
1628 (throw 'terminate t))
1629 ;; already calculated ? No ?
1630 (unless (setq table (get 'tinymy-:vi-type-paren-match-list 'syntax-table))
1631 (setq table (make-syntax-table))
1632 ;; We want everything to look like word
1633 (ti::dotimes counter 0 255 (modify-syntax-entry counter "w" table))
1637 (modify-syntax-entry (string-to-char (car x)) "(" table)
1638 (modify-syntax-entry (string-to-char (cdr x)) ")" table)))
1640 (put 'tinymy-:vi-type-paren-match-list 'syntax-table table))
1641 ;; In lisp; only () are matched.
1642 (when (not (string-match "lisp" (symbol-name major-mode)))
1643 (set-syntax-table table))
1647 ;; ........................................ handle quotes ...
1648 ((ti::char-in-list-case ch m-list)
1649 (setq ch (regexp-quote (char-to-string ch)))
1650 (setq s-func 're-search-forward
1655 ((looking-at (concat ch "[ \t]*$"))
1656 ;; Only search backward if no ARG given.
1657 ;; if the " char is at the end of line,
1658 ;; then it propably is the 'closing' one.
1660 (setq s-func 're-search-backward
1664 ;; Do not go too far away....
1665 (setq go (funcall add-func p spread-limit))
1666 (setq max (funcall max-func))
1668 (if (funcall bigger-func go max)
1670 ;; ... ... ... ... ... ... ... ... ... ... ... do search . .
1671 (if (eq s-func 're-search-forward)
1672 (forward-char 1)) ;move out of way
1673 (funcall s-func ch go t)
1674 ;; This is funny, it both a) restores the position
1675 ;; if search failed, b) adjusts the "after" search
1676 ;; point back to char.
1677 (if (eq s-func 're-search-forward)
1679 ;; ..................................... handle BEG pairs ...
1680 ((looking-at "[[({<]")
1683 (cond ((not (looking-at "[])}>]"))
1684 (error "..booomerang"))))
1685 ;; ..................................... handle END pairs ...
1686 ((looking-at "[])}>]")
1689 (when (not (eq p (point))) ;moved ?
1690 ;; In lisp, jumping from closing ) to starting
1691 ;; "'(lambda" puts cursor at "'"?? Correct it.
1692 (if (and (not (eq (following-char) ?\( ))
1693 (looking-at ".[]({<]"))
1695 ;; ...................................... no special char ...
1697 (self-insert-command (or arg 1))))
1699 (goto-char p) ;restore position
1700 (message "TinyMy: No match.")))
1701 ;; make sure we restore this
1702 (set-syntax-table ptable)))))
1707 ;;; ----------------------------------------------------------------------
1709 (defun tinymy-mail-subject-get ()
1710 "Look buffer content and return subject for mail message.
1714 'context diff file.txt'
1718 'diff 1.23 --> 1.25 file.txt'
1722 '1.25 file.txt' ;; possibly without version information
1724 buffer with no filename:
1727 (let* ((rcs-re "retrieving revision +\\(.*\\)")
1737 ;; See if this is rcsdiff
1739 ;; RCS file: RCS/tinylib.el,v
1740 ;; retrieving revision 1.95
1741 ;; retrieving revision 1.97
1742 ;; diff -c -r1.95 -r1.97
1743 ;; *** 1.95 1997/03/22 12:26:59
1744 ;; --- 1.97 1997/03/22 15:17:22
1745 ((re-search-forward "^RCS file:[^/]*/?\\(.*\\),v" nil t)
1746 (setq file (match-string 1))
1747 (and (re-search-forward rcs-re nil t)
1748 (setq v1 (match-string 1))
1749 (re-search-forward rcs-re nil t)
1750 (setq v2 (match-string 1)))
1751 (setq msg (format "patch: %s --> %s %s" v1 v2 file)))
1753 ;; Regular file, see if this one has RCS version information
1754 (if (setq ver (ti::vc-rcs-buffer-version))
1755 (setq ver (concat " " ver " ")))
1758 (file-name-nondirectory buffer-file-name))))
1759 ((setq type (ti::buffer-diff-type-p))
1760 ;; *** /users/jaalto/T.orig Sun Mar 23 16:37:43 1997
1761 ;; --- /users/jaalto/T Sat Mar 22 14:44:34 1997
1764 (if (or (re-search-forward "^--- \\([^ \t\n]+\\)" nil t)
1765 (re-search-forward "^\\*\\*\\* \\([^ \t\n]+\\)" nil t)
1766 (re-search-forward "^\\+\\+\\+ \\([^ \t\n]+\\)" nil t))
1767 (setq file (match-string 1))))
1768 (setq msg (format "%s diff %s"
1769 (prin1-to-string (car type))
1771 (file-name-nondirectory file )
1775 ;;; ----------------------------------------------------------------------
1777 (defun tinymy-mail-buffer ()
1778 "Mail current buffer.
1779 The subject line is constructed by looking at the buffer content:
1780 eg if buffer contains rcsdiff of diff,
1781 The subject line will tell the versions."
1783 (let* ((data-buffer (current-buffer))
1785 (setq subj (tinymy-mail-subject-get))
1787 ;; This package gives nice alias expansion
1788 (ti::package-require-mail-abbrevs)
1789 (ti::mail-text-start 'move)
1791 (save-excursion (insert-buffer data-buffer))
1792 ;; Make sure the outlline/folding is opened first
1793 (ti::buffer-outline-widen)
1795 (ti::mail-kill-field "Subject:" subj))
1797 (end-of-line) ;"TO:" field
1798 (run-hooks 'tinymy-:mail-buffer-hook)))
1801 ;;{{{ Programming: function bounds, debug
1803 ;;; ----------------------------------------------------------------------
1805 (defun tinymy-function-bounds (&optional forward)
1806 "Find function area. Return (beg . end).
1807 The search is first done backward, unless FORWARD is given,
1808 to find function beginning.
1811 All function start lines must be left flushed, ie. no empty spaces before
1812 function name declaration. Functions must start/end with left flushed
1820 (let* ((mode (or (ti::id-info) (symbol-name major-mode)))
1821 (max-lines 1500) ;rows, function cannot be bigger
1822 (skip-lines 1300) ;maximum skip lines backward
1828 ((string-match "lisp" mode)
1829 ;; Only lisp has decent find functions
1837 (beginning-of-defun)
1839 (if (or (and beg (null forward))
1840 (and beg forward fwd-flag))
1843 (setq end (point))))))
1844 ((string-match "perl\\|awk" mode)
1848 (if (re-search-forward "^sub\\|^function" nil t)
1849 (setq fwd-flag t))))
1851 ((and (or (null forward)
1852 (and forward fwd-flag))
1853 (re-search-backward "^sub\\|^function" nil t))
1856 (if (re-search-forward "^}" nil t)
1857 (setq end (point)))))))
1858 ((string-match "code-c\\|c-\\|cc-\\|c[+]" mode)
1859 ;; The opening block says where is function start, this is only
1860 ;; possible for NEW styled programming, not K&R styled 'hanging'
1862 ;; C++: int funtion() { ;; nope, too diffucult to detect.
1863 ;; perl sub funtion { ;; allowed
1867 (if (re-search-forward "^{" nil t)
1868 (setq fwd-flag t))))
1870 ((and (or (null forward)
1871 (and forward fwd-flag))
1872 (re-search-backward "^{" nil t)
1873 (re-search-backward "(" nil t)) ;find parameter list beginning
1874 ;; There is a problem in writing the C++ funcs:
1884 (setq point (point))
1885 ;; We just search line by line backward until no comment,
1887 (while (not (looking-at " *//+\\| *[*]+/\\| *[*]+ \\|^[ \t]*$"))
1889 (if (not (eq point (point))) ;if the while loop moved.
1890 (forward-line 1)) ;go to func beginning.
1892 (if (re-search-forward "^}" nil t) ;; This is easy.
1893 (setq end (point))))))))
1895 ;; must not be too far away from current point
1896 (< (count-lines beg start) skip-lines))
1897 (setq range (count-lines beg end)))
1899 (if (and range (< range max-lines))
1903 ;;; ----------------------------------------------------------------------
1905 (defun tinymy-beginning-of-defun (&optional end-of-fun)
1906 "See `tinymy-function-bounds'. END-OF-FUN must be nil or t."
1908 (let* ((bounds (tinymy-function-bounds end-of-fun))
1909 (beg (car-safe bounds))
1910 (end (cdr-safe bounds))
1911 (point (if end-of-fun end beg)))
1913 (message "TinyMy: Sorry, can't find function.")
1914 (goto-char point))))
1916 ;;; ----------------------------------------------------------------------
1918 (defun tinymy-end-of-defun ()
1919 "See `tinymy-function-bounds'."
1921 (tinymy-beginning-of-defun 'end))
1926 ;;; ----------------------------------------------------------------------
1928 (defun tinymy-add-rectangle (START END &optional insert)
1929 "Add or Multiply columns in rectangle in START END.
1930 With optional arg INSERT, insert the sum and product to
1933 (interactive "r\nP")
1939 (operate-on-rectangle
1940 (lambda (POS BEFORE AFTER)
1941 (setq rownum (1+ rownum))
1942 (setq rowval (string-to-number (buffer-substring POS (point))))
1943 (setq sum (+ sum rowval))
1944 (setq prod (* prod rowval)))
1947 (message "TinyMy: For %d rows, sum=%f, product=%f" rownum sum prod))
1949 (insert (format "%0.2f %0.2f" sum prod)))))
1954 ;;; ----------------------------------------------------------------------
1956 (defun tinymy-scroll-up ()
1957 "Call `tinymy-scroll-down'."
1959 (tinymy-scroll-down 'up))
1961 ;;; ----------------------------------------------------------------------
1963 (defun tinymy-scroll-down (&optional up)
1964 "Scrolls down, optionally UP. No errors generated.
1965 Cursor is positioned at first call to the top or bottom of window and
1966 and only next call scrolls the window. If possible, cursor in kept at the
1967 yop or bottom line of window. (Caveat: for long lines, this cannot be done)."
1969 ;; Don't know which Emacs version introduced this function.
1970 ;; Use old trusted implementation if it doesn't exist
1971 (let ((point (point)))
1972 (if (not (fboundp 'move-to-window-line))
1973 (tinymy-scroll-old up)
1975 ((and up (ti::window-pmin-visible-p))
1977 ((and up (eq (point) (window-start)))
1979 (move-to-window-line 0))
1981 (move-to-window-line 0)
1982 (when (eq point (point))
1983 ;; Point didn't move? Use Emacs function.
1986 (move-to-window-line 0)))
1990 ((ti::window-pmax-visible-p)
1992 ((eq (point) (ti::window-pmax-line-bol))
1994 ;; Keep cursor at bottom
1995 (move-to-window-line -1))
1996 ((move-to-window-line -1)
1998 (when (eq point (point))
1999 ;; Point didn't move? Use Emacs function.
2002 (move-to-window-line -1))))
2003 ;; Make sure point is at the beginning
2004 (move-to-column 0))))
2006 ;;; ----------------------------------------------------------------------
2008 (defun tinymy-scroll-old (&optional up)
2009 "Scrolls down, optionally UP. No errors generated.
2010 This function behaves like DOS/windows scroll commands, where cursor jumps
2011 to the end or beginning of window first and only next scrolls. It also
2012 keeps the cursor in the bottom or top of window according to the direction
2017 This function does not work properly if the lines in the window
2018 exceed the length of the window. If the current line is longer than
2019 window length, then normal Emacs scroll command will be called."
2021 ;; - the 'error' call is most disturbing if you have
2022 ;; - debug-on-error t
2023 ;; - This is for *interactive* only! Lisp manual forbids using scroll
2024 ;; command in normal lisp code.
2027 (let* ((mode tinymy-:scroll-mode)
2031 ;; ............................................. wrapping line ...
2034 (if (ti::window-pmin-visible-p)
2038 (if (ti::window-pmax-visible-p)
2042 ;; ........................................... non wrapping line ...
2046 (message "TinyMy: beg of buffer.")
2047 (if (ti::window-pmin-visible-p) ;if top is visible
2048 (goto-char (point-min))
2051 (ti::window-pmin-line-p)))
2053 (goto-char (window-start))
2054 (beginning-of-line))))
2057 (message "TinyMy: end of buffer.")
2058 (if (ti::window-pmax-visible-p)
2059 (goto-char (point-max))
2063 (ti::window-pmax-line-p)))
2065 ;; - if outline/folding mode is on, we can't determine
2066 ;; line count with count-lines function
2067 ;; - The count gives 1 extra line, check with M-x =
2068 ;; around the window region
2071 (count-char-in-region
2072 (window-start) (window-end) ?\n)))
2073 ;; the scroll command does not update window points
2074 ;; in 19.28! That means that the function window-end
2075 ;; can't be trusted. only when this function ends,
2076 ;; the window is updated.
2078 ;; We must manually go to the end line
2079 ;; - The cursor is always left in the line 2, after
2080 ;; scrolling in window. We have to go N lines downward
2081 ;; to put cursor at window end line
2082 ;; - next-line is used, because it hanbdles folding/outline.
2083 ;; forward-line can't be used.
2086 ;; - If the lines are longer than window-width; then this
2087 ;; whole next-line call may end anywhere...can't help
2089 ;; - The safe do is here in case this calls error,
2090 ;; which it does if the buffer size has changes, like
2091 ;; in live *Messages* buffer
2092 (ignore-errors (next-line (- lines 1))))
2093 (goto-char (ti::window-pmax-line-bol)))))))))))
2096 ;;{{{ shell -- shar, tar, uu
2098 ;;; ----------------------------------------------------------------------
2100 (defun tinymy-shar (single-or-list)
2101 "Generate SHAR file using SINGLE-OR-LIST.
2102 List of files can include shell regexps. The result is put into
2103 `tinymy-:register'."
2107 (ti::file-complete-filename-minibuffer-macro
2108 (read-from-minibuffer
2109 (format "[%s] Shar files: " default-directory)
2113 (let* ((cmd (concat tinymy-:shar-command " "))
2114 (register tinymy-:register)
2115 (verb (interactive-p))
2117 (if (ti::nil-p single-or-list)
2118 (error "Missing args")
2120 (shell-command-to-string
2121 (format "cd %s; %s %s"
2124 (ti::list-to-string (ti::list-make single-or-list)))))
2125 (set-register register out)
2127 (message (format "TinyMy: Register %s has shar"
2128 (char-to-string register)))))))
2130 ;;; ----------------------------------------------------------------------
2132 (defun tinymy-tar (tar-file file-list)
2133 "Generate TAR-FILE using FILE-LIST.
2135 t or nil if tar created."
2137 (let* ((default-directory default-directory)
2138 (default-tar-name "pkg.tar")
2139 (default-tar (concat default-directory default-tar-name))
2143 (ti::file-complete-filename-minibuffer-macro
2144 (read-from-minibuffer
2145 (format "[%s] Tar name: " default-tar)
2149 (setq arg1 default-tar))
2150 ((file-directory-p arg1)
2151 (setq arg1 (concat arg1 default-tar))))
2153 (setq tar-dir (or (file-name-directory arg1)
2155 (setq default-directory tar-dir)
2158 (ti::file-complete-filename-minibuffer-macro
2159 (read-from-minibuffer
2160 (format "[%s] Files: " arg1)
2164 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... main . .
2165 (if (or (ti::nil-p tar-file)
2166 (ti::nil-p file-list))
2167 (error "Missing args"))
2168 (let* ((tar-cmd (concat tinymy-:tar-command " "))
2169 (edir (file-name-directory
2170 (expand-file-name tar-file)))
2171 (cmd (concat "cd " edir "; "
2173 (file-name-nondirectory tar-file)
2176 ;; ................................................... then case ...
2177 (if (and (file-exists-p edir) ;; must exist
2178 (or (not (file-exists-p tar-file)) ;; good if not exist
2179 (and (file-exists-p tar-file) ;; we have to remove it
2180 (y-or-n-p "Tar exists, remove ? ")
2182 (delete-file tar-file) t))))
2184 (setq cmd (read-from-minibuffer "cmd: " cmd))
2186 (setq ret (file-exists-p tar-file)))
2187 (message "TinyMy: Aborted"))
2193 ;;; ----------------------------------------------------------------------
2194 ;;; Some special compile commands for C/C++, which usually
2197 (defun tinymy-compile-command-search (type)
2198 "Search match car of `tinymy-:compile-table' against TYPE and return cdr."
2199 (dolist (elt tinymy-:compile-table)
2200 (when (string-match (car elt) type)
2201 (return (cdr elt)))))
2203 ;;; ----------------------------------------------------------------------
2205 (defun tinymy-compile-tinytf-command ()
2206 "Compile .txt file into HTML."
2207 (concat "perl -S t2html.pl --Out --print-url "
2208 (file-name-nondirectory
2209 (buffer-file-name))))
2211 ;;; ----------------------------------------------------------------------
2213 (defun tinymy-compile-xml-command ()
2214 "Compile .xml file by running validator."
2215 ;; #todo: incomplete
2216 (let ((list '(("xmlwf") ;; Expat, included in Cygwin
2217 ( ;; http://xml.coverpages.org/rxpWindows19991018.html
2218 ;; Richard Tobin "rxp XML parser"
2219 ;; -> Compiles under Cygwin
2221 ;; verbose, Validate
2224 (multiple-value-bind (cmd args)
2226 (when (executable-find cmd)
2227 (return (format "%s %s %s"
2230 (file-name-nondirectory
2231 (buffer-file-name)))))))))
2233 ;;; ----------------------------------------------------------------------
2235 (defun tinymy-compile-sql ()
2237 The correct SQL compile command is determined by
2239 1) searching first word from the file that matches string:
2240 PostgreSQL, MySQL, Oracle.
2242 2) Or if the last part of the directory contains portion
2243 /pg /postgre /postgres /postgresql
2247 E.g. file in location ~/sql/pg/my-file.sql is supposed to belong
2249 (when buffer-file-name
2250 (let* ((file buffer-file-name)
2252 (ti::directory-part-last
2253 (file-name-directory file))))
2255 (flet ((type-p (regexp1 regexp2)
2256 (or (ti::re-search-check regexp1)
2257 (string-match regexp2 (or last "")))))
2258 (or (and (type-p "postgreSQL"
2259 "\\(^pg$\\|postgres?\\|postgresql\\)")
2260 (executable-find "psql")
2262 "psql -h HOST -U user -d database < %s"))
2263 ;;; ;; PostgreSQL is native Cygwin application
2264 ;;; ;; and must see Cygwin path.
2265 ;;; (if (and (ti::win32-p)
2266 ;;; (ti::emacs-type-win32-p))
2267 ;;; (w32-cygwin-dos-path-to-cygwin file)
2269 (and (type-p "MySQL" "mysql")
2270 (executable-find "mysql")
2271 (setq cmd "mysql -h HOST -u USER database < %s"))
2272 (and (type-p "Oracle" "oracle")
2273 (executable-find "sqlplus")
2274 (setq cmd "sqlplus USER/LOGIN@DATABASE < %s")))
2276 (if (string-match "%" cmd)
2277 (format cmd (file-name-nondirectory buffer-file-name))
2281 ;;; ----------------------------------------------------------------------
2282 ;;; Some special compile commands for C/C++, which usually
2285 (defun tinymy-compile-cc-command ()
2286 "Construct C/C++ compile command"
2287 (let* ( ;; Check if there are any .mak files in directory ?
2288 (file (file-name-nondirectory (buffer-file-name)))
2289 (make-files (and file
2290 (ti::directory-files
2291 (file-name-directory (buffer-file-name))
2292 "\\.make?$\\|makefile$\\|Makefile$")))
2293 (cc-cmd (or (getenv "CC") "gcc"))
2294 (flags (or (getenv "CFLAGS") "-g")))
2297 (format "%s %s %s -o %s "
2302 (ti::string-match "^[^.]+" 0 file)))))
2304 ;;; ----------------------------------------------------------------------
2306 (defun tinymy-compile-command-for-buffer (mode &optional buffer value)
2307 "Use MODE to control BUFFER' compile command VALUE.
2308 If mode is 'get, recall the buffer's value.
2309 If mode is 'clear, clear previous compile command.
2310 Any other value is equal to 'put with BUFFER and VALUE.
2314 `tinymy-:compile-table' Values are stored to property list
2315 '(<buffer> compile-command ..)"
2317 (setq buffer (current-buffer)))
2320 (get 'tinymy-:compile-table buffer))
2322 (put 'tinymy-:compile-table buffer nil))
2324 (put 'tinymy-:compile-table buffer value))))
2326 ;;; ----------------------------------------------------------------------
2328 (defun tinymy-compile-command-for-buffer-clear ()
2329 "Clear buffer's compile command."
2330 (tinymy-compile-command-for-buffer 'clear))
2332 ;;; ----------------------------------------------------------------------
2334 (defun tinymy-compile-run-command-ask (&optional clear)
2335 "Run current buffer through compile buffer.
2336 This function remembers what command you have used for each buffer
2337 and offers it next time you call it.
2339 Parameter CLEAR instructs to \"forget\" any previously
2340 acched command try the search again from fresh. You can
2341 supply the \\[universal-argument\\] if you have made changes
2342 to `tinymy-:compile-table'.
2344 If mode 'text' or 'fundamental'
2346 Do not try to identify buffer, but ask compile command directly
2348 If mode is not 'text' or 'fundamental'
2350 Try to find suitable compile command by identifying the buffer
2351 and looking at the command table.
2353 For C/C++ code the default command suggested if 'mak', but if there
2354 is no makefile in the directory, then a normal compile command
2359 `tinymy-:compile-table'
2360 `tinymy-:compile-command-c-code'"
2363 (tinymy-compile-command-for-buffer-clear))
2364 (let* ( ;; We change this so that compile goes to right dir
2365 (fid "tinymy-compile-run-command")
2366 (file (buffer-file-name))
2367 (mname (symbol-name major-mode))
2369 (type (or (ti::id-info)
2372 (buffer (current-buffer))
2375 filename ;without directory part
2377 (unless fid ;; No-op. XEmacs byte compiler silencer
2379 (tinymy-debug fid file "type" type)
2381 (message "TinyMy: Can't compile, no file in this buffer.")
2382 ;; Try to find last typed commad first, only if there is
2383 ;; no previous command, make one.
2384 (unless (setq cmd (tinymy-compile-command-for-buffer 'get))
2385 (setq filename (file-name-nondirectory file)
2386 elt (tinymy-compile-command-search type))
2387 (unless filename ;; No-op, XEmacs byte compiler silencer
2388 (setq filename nil))
2389 (tinymy-debug "No prev cmd" filename elt)
2390 ;; .............................................. make command ...
2391 ;; Only if the compile command is constant string: save it
2392 ;; Dynamically evaled compile commands cannot be saved.
2399 ((setq elt (eval elt))
2400 (format elt file))))) ;; unless
2401 ;; ............................................... ask from user ...
2403 (ti::file-complete-filename-minibuffer-macro
2404 (read-from-minibuffer
2405 "Compile: " (or cmd "make")
2409 ;; ......................................... per buffer cmd save ...
2410 ;; Save command per buffer basis
2411 (tinymy-debug "CMD" cmd)
2412 (when (or (not (setq cmd (tinymy-compile-command-for-buffer 'get)))
2413 ;; User gave different command. Update
2414 (not (string= cmd run-it)))
2415 (tinymy-compile-command-for-buffer 'put buffer run-it))
2418 ;;; ----------------------------------------------------------------------
2420 (defun tinymy-compile-run-command (&optional clear)
2421 "See `tinymy-compile-run-command-ask'."
2423 (let* ((cmd (tinymy-compile-run-command-ask clear)))
2424 (when (not (ti::nil-p cmd))
2425 (compile-internal cmd "No more errors.")
2426 (pop-to-buffer "*compilation*"))))
2428 ;;; ----------------------------------------------------------------------
2430 (defadvice compile (around tinymy dis)
2431 "Change interactive SPEC to determine default compile command.
2432 See `tinymy-compile-run-command-ask'."
2435 (or (tinymy-compile-run-command)
2436 (if (or compilation-read-command
2438 (read-from-minibuffer "Compile command: "
2439 (eval compile-command) nil nil
2440 '(compile-history . 1))
2441 (eval compile-command)))))
2444 ;;; ----------------------------------------------------------------------
2447 (defun tinymy-compile-run-command-advice (&optional disable)
2448 "Activate or DISABLE smart compile command vie \\[compile\\].
2449 See `tinymy-compile-run-command-ask' for more."
2451 (ti::advice-control 'compile "^tinymy"
2453 "TinyMy: smart M-x compile advice support "))
2458 ;;; --------------------------------------------------- &word-movement ---
2459 ;;; #todo: Uhm; rewrite sometime.
2461 (defun tinymy-word-move-1 (&optional back)
2462 "Low level word movement control. Optionally move BACK."
2463 (let* ((up-case (memq major-mode tinymy-:move-word-case-modes))
2464 (regexp "[a-z0-9]*[A-Z]+[a-z0-9]+[A-Z]+")
2465 (case-fold-search (not up-case))
2472 ((not (eq 0 (skip-chars-backward "a-z")))
2473 (let (case-fold-search)
2476 (char-to-string (preceding-char)))))
2477 ((not (eq 0 (skip-chars-backward " \t")))
2479 (looking-at regexp))
2480 ;; Cursor is at the end of word
2482 (member (char-to-string (char-syntax (preceding-char)))
2485 (member (char-to-string (char-syntax (following-char)))
2487 ;; This Upcase charset is only used if the cursor is
2488 ;; within AnUpCaseWord.
2489 tinymy-:move-word-case-set)
2491 tinymy-:move-word-set))))
2493 ;; Skip to the end of word if at EOL
2494 ;; (this-he-is-word sse-it-now?)
2497 ;; otherwise it would skip to next line's word begin.
2499 (or (looking-at "\\([A-Z]+\\)[^a-zA-Z \t]*$")
2500 (looking-at "\\([a-z]+\\)[^a-zA-Z \t]*$")
2501 (looking-at "\\([A-Z][a-z]+\\)[^a-zA-Z \t]*$")))
2502 (goto-char (match-end 1)))
2504 (ti::buffer-word-move charset back)))))
2506 ;;; ----------------------------------------------------------------------
2508 (defun tinymy-word-move-2 (&optional back)
2509 "If at whitespace, skip to next non-whitespace. Optionally BACK.
2510 Otherwise call `tinymy-word-move-1'."
2511 (when (and (looking-at "[ \t\f\r\n]")
2512 (not (ti::buffer-looking-at-one-space)))
2515 (skip-chars-backward " \t\f\r\n")
2519 (skip-chars-forward " \t\f\r\n"))))
2520 (tinymy-word-move-1 back))
2522 ;;; ----------------------------------------------------------------------
2524 (defun tinymy-word-backward ()
2525 "Word backward See `tinymy-:move-word-case-set'."
2527 (tinymy-word-move-2 'back))
2529 ;;; ----------------------------------------------------------------------
2531 (defun tinymy-word-forward ()
2532 "Word forward. See `tinymy-:move-word-case-set'."
2534 (tinymy-word-move-2))
2537 ;;{{{ minor mode: sort
2539 ;;;### (autoload 'turn-off-tinymy-sort-mode "tinymy" "" t)
2540 ;;;### (autoload 'turn-on-tinymy-sort-mode "tinymy" "" t)
2541 ;;;### (autoload 'tinymy-sort-mode "tinymy" "" t)
2543 (add-hook 'tinymy-sort-:mode-define-keys-hook ;To be sure
2544 'tinymy-sort-mode-define-keys)
2548 (ti::macrof-minor-mode-wizard
2549 "tinymy-sort-" " S" "\C-cS" "Tsort" 'TinySort "tinymy-sort-:" ;1-6
2551 "Minor mode for sorting lines (by columns) in the buffer easily.
2552 Remember to select region to sort.
2554 When you sort by columns, the line must have enough columns, e.g.
2555 if you select following area and try to sort by clumn 3, that is
2556 not possible. Also, There must be no empty lines inside sorted area.
2563 \\{tinymy-sort-:mode-map}"
2569 "Column sort minor mode"
2572 tinymy-sort-:mode-easymenu-name
2573 ["By column 1" tinymy-sort-column-1 t]
2574 ["By column 2" tinymy-sort-column-2 t]
2575 ["By column 3" tinymy-sort-column-3 t]
2576 ["By column 4" tinymy-sort-column-4 t]
2577 ["By column 5" tinymy-sort-column-5 t]
2578 ["By column 6" tinymy-sort-column-6 t]
2579 ["By column 7" tinymy-sort-column-7 t]
2580 ["By column 8" tinymy-sort-column-8 t]
2581 ["By column 9" tinymy-sort-column-9 t])
2583 (define-key map "1" 'tinymy-sort-mode-column-1)
2584 (define-key map "2" 'tinymy-sort-mode-column-2)
2585 (define-key map "3" 'tinymy-sort-mode-column-3)
2586 (define-key map "4" 'tinymy-sort-mode-column-4)
2587 (define-key map "5" 'tinymy-sort-mode-column-5)
2588 (define-key map "6" 'tinymy-sort-mode-column-6)
2589 (define-key map "7" 'tinymy-sort-mode-column-7)
2590 (define-key map "8" 'tinymy-sort-mode-column-8)
2591 (define-key map "9" 'tinymy-sort-mode-column-9)
2592 (define-key map "?" 'tinymy-sort-mode-help))))
2594 ;; Create functions like this:
2596 ;; (defun tinymy-sort-column-0 (beg end)
2597 ;; (interactive "*r") (tinymy-sort-column beg end 0))
2601 (let ((sym (intern (format "tinymy-sort-mode-column-%d" x)))
2604 (` (defun (, sym) (beg end)
2606 (tinymy-sort-column beg end (, x) ))))
2608 '(1 2 3 4 5 6 7 8 9))
2610 ;;; ----------------------------------------------------------------------
2612 (defun tinymy-sort-column (beg end nbr)
2613 "Sort region BEG END according to column NBR."
2614 (interactive "r\np")
2615 (let* ((opoint (point)))
2617 (goto-char (min beg end)) ;Sort breaks otherwise
2618 (sort-fields nbr beg end)
2619 (goto-char opoint)))
2622 ;;{{{ alias definitions and others
2624 (defun tinymy-alias ()
2625 "Install some aliases."
2626 ;; Say always y-or-n-p; so that there is no need to type "yes" or "no"
2627 (defalias 'yes-or-no-p 'y-or-n-p))
2629 ;;; ----------------------------------------------------------------------
2630 ;;; Idea by 1997-11-05 Kevin Rodgers gnu-emacs.help
2632 (defun tinymy-maybe-disable-auto-save ()
2633 "If the directory is read only, do not keep auto save files."
2634 (when (and (stringp buffer-file-name)
2635 (not (file-writable-p
2636 (file-name-directory buffer-file-name))))
2637 (auto-save-mode nil)
2638 (set (make-variable-buffer-local 'auto-save-interval) 0)))
2640 ;;; ----------------------------------------------------------------------
2642 (defun tinymy-find-file-hook ()
2643 "Activate DOS display table for dos files (in UNIX) ."
2644 (tinymy-maybe-disable-auto-save)
2645 ;; hook return value
2651 (run-hooks 'tinymy-:load-hook)
2653 ;;; tinymy.el ends here