1 ;;; tinylibid.el --- Library for (Id)entifying buffer, regardless of mode
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1995-2007 Jari Aalto
8 ;; Keywords: extensions
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program, call M-x ti::id-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
43 ;; (require 'tinylibid)
45 ;; Or use autoload. This is preferred method
47 ;; (autolad 'ti::id-info "tinylibid" "Buffer info" t)
49 ;; Function to call to get buffer identity. You normally call this
50 ;; from lisp code and not interactively.
54 ;; If you have any questions, always use function
56 ;; M-x ti::id-submit-bug-report
60 ;; Please send any example file or mode that I'm not aware of,
61 ;; I'll try to support any programming mode
66 ;; .................................................... &t-commentary ...
72 ;; Seems quite uninteresting package to you? I bet it does, unless
73 ;; you're a lisp programmer who has infite time to twiddle his
74 ;; c++-mode-hook + 20 others hooks and have all the time develop nice
75 ;; little funcs to make living in emacs easier.
77 ;; While someone may think that all users stick to one mode e.g. when
78 ;; they are programming C++, that's not obvious. For example programmer
79 ;; may move between all kind of modes during programming and the
80 ;; trouble is, that while the buffer's logical content remains the same,
81 ;; the hooks know nothing about it. Hooks are just dummies that get
82 ;; called whenever you turn some mode on, try C++ mode over nroff code
83 ;; and you'll be surprised.
85 ;; Now let's see one session example:
87 ;; . write Lisp ;lisp-mode + folding-mode
88 ;; . hmm, need center-command ;move to text-mode
89 ;; . code again ;lisp-mode
90 ;; . adjust defconst var pos. ;turn on tiny tab minor mode
91 ;; . code again ;lisp-mode
93 ;; Programmer may have bound all common modes into keys so that he can
94 ;; can access various modes very fast; changing modes is no
95 ;; problem. What is the problem, is that when you turn off the
96 ;; CODE-mode, all information about comment-start etc. vanish.
98 ;; Overview of features
100 ;; o This is LIBRARY package
101 ;; o Try to identify buffer content
102 ;; o Useful for checking what kind of file is in buffer and making
103 ;; decisions based on that. Suitable for hooks.
107 ;; If you're using *imenu.el* to generate buffer jump points, it is
108 ;; very likely that the imenu command won't generate right jump points
109 ;; if you're in wrong mode. Let's use imenu example. Here is first
110 ;; try: The code sniffs around to see if we're on some mode and then
111 ;; configures imenu according to it.
113 ;; (defun my-imenu-mouse (event)
117 ;; (defun my-imenu (&optional arg)
118 ;; "Sets parameters to imenu."
120 ;; (setq imenu-max-items 20
121 ;; imenu-sort-function nil)
124 ;; '(lisp-mode emacs-lisp-mode lisp-interaction-mode))
125 ;; (setq imenu-create-index-function
126 ;; 'imenu-example--create-lisp-index
127 ;; imenu-sort-function
128 ;; 'imenu--sort-by-name
130 ;; ((memq major-mode '(c++-mode))
131 ;; (setq imenu-create-index-function
132 ;; 'imenu-example--create-c++-index
133 ;; imenu-sort-function
134 ;; 'imenu--sort-by-name
136 ;; ((memq major-mode '(c-mode))
137 ;; (setq imenu-create-index-function
138 ;; 'imenu-example--create-c-index
139 ;; imenu-sort-function
140 ;; 'imenu--sort-by-name
145 ;; Here is better and more robust way. It'll let you be in any
146 ;; mode while retaining right imenu.
150 ;; ;; Separate functions for keyboard and mouse.
151 ;; (defun my-imenu-mouse (event &optional arg)
152 ;; (interactive "e\nP")
155 ;; (defun my-imenu (&optional arg)
156 ;; "Sets parameters to imenu. If called with arg, the output is
159 ;; (let* ((sort-func (if arg nil 'imenu--sort-by-name))
162 ;; imenu-example--create-lisp-index)
164 ;; imenu-example--create-lisp-index)
166 ;; imenu-example--create-c++-index)
168 ;; imenu-example--create-c-index)))
169 ;; ;; So, in what mode were really?
170 ;; (mode (or (ti::id-info t) major-mode))
171 ;; (el (assoc mode table)))
173 ;; (message "Sorry, no imenu for this buffer.")
174 ;; (setq imenu-max-items 20
175 ;; imenu-sort-function sort-func
176 ;; imenu-create-index-function (nth 1 el))
190 (ti::package-use-dynamic-compilation))
195 (defvar ti::id-:load-hook nil
196 "*Hook run when file has been loaded.")
201 (defvar ti::id-:info nil
202 "Buffer local variable.This value is updated every time
203 function ti::id-info called. For faster responses, you may wan to write your
206 (setq info ti::id-info nil 'var)
208 Because peeking the variable is 40x times faster.")
210 (make-variable-buffer-local 'ti::id-:info)
212 ;; Global variables set by functions.
213 ;; - These are heavily used. User may check these too.
214 ;; - They are Set after the buffer is studied.
216 (defconst ti::id-:global-buffer-name nil
217 "Global: set by study func, buffer name")
219 (defconst ti::id-:global-buffer-file-name nil
220 "Global: set by study func, buffer file name")
222 (defconst ti::id-:global-buffer-extension nil
223 "Global: set by study func, buffer fn ext.")
225 (defconst ti::id-:global-buffer-first-line nil
226 "Global: set by study func, 1st line of buffer")
229 ;;{{{ setup: public, user configurable
231 ;;; it is INTENTIONAL that the variables are defconst, change these
232 ;;; with ti::id-:load-hook
234 (defvar ti::id-:file-ext-re "[a-zA-Z0-9]\\(\\.\\)[a-zA-Z0-9]+$"
235 "A regexp that says what files can have extension. Everything after the
236 DOT is considered to form extension. Files like ~/.cshrc are not
237 considered to have an extension.
239 The sub match at level 1 indicates the start of an extension.
243 See function `ti::id-file-extension'.")
245 (defconst ti::id-:buffer-first-line-regexp-list
246 '(("^#.*perl" "code-perl")
247 ("^#.*python" "code-python")
248 ("^#.*scm" "code-scheme")
249 ("^#.*tcl" "code-tcl")
250 ;; Of course this is not bullet proof, but many lisp code package
251 ;; has first line describing the package.
252 ("^;;[ \t]+.*\.el " "code-lisp")
253 ("^#.*awk" "code-awk")
254 ;; It's custom to start the file with ':' no-op, if it's
255 ;; sh-coded, since it prevents accidental # for the first line
256 ;; --> intepreted as csh code by shell if it sees # as first char
257 ("^#.*\/sh\\|^[ \t]*:[ \t]*$" "code-shell-sh")
258 ("^#.*\/csh" "code-shell-csh")
259 ("^#.*\/tcsh" "code-shell-tcsh")
260 ("^#.*\/ksh" "code-shell-ksh")
261 ("^#.*\/zsh" "code-shell-zsh")
262 ("^#.*\/bash" "code-shell-bash")
263 ;; Fortran code uses comments beginning with "c", we assume that
264 ;; there must be at least TWO spaces after initial comment
265 ("^c +" "code-fortran")
266 ("^[ \t]*!" "resource-x") ;.Xinitrc ot the like comment
267 ;; It's custom to start an nroff man page with a comment that
268 ;; holds version control Id string. Comment is = .\"
269 ("^\.\\\"" "text-nroff")
270 ;; A "white paper" document that starts with TOC. See e.g tinytf.el
271 ("^Table of contents" "text-white-paper-toc"))
272 "*list of \(REGEXP str) where RE is tried upon 1 line match, normally
273 a bang-slash or emacs --** notation")
275 ;; - Remember, first one that macthes id useti::d! Put most restrictive at
277 ;; - The regexp scanning should be the last resort, because its potential
279 ;; - There is no need to add regexp here if buffer can be identified by other
280 ;; means easily ie. all WWW files have universal .html extension.
282 (defconst ti::id-:buffer-match-regexp-list
286 ;; interface CServicePreferences; /* Forward references */
287 "interface [0-9A-Za-z]+[ \t]+[0-9A-Za-z]+[ \t]*;"
288 ;; exception InvalidRequest{TString aReason;};
289 "\\|exception[ \t]+[0-9A-Za-z]+{.*;"
290 ;; typedef sequence<CosPropertyService::PropertyNames> PropertyNamesList;
291 "\\|typedef[ \t]+[0-9A-Za-z]+<")
293 ;; *FvwmIdentBack MidnightBlue
294 ;; *FvwmIdentFore Yellow
295 ;; Style "FvwmButtons" CirculateSkip
296 '("Style[ \t]+\"FvwmButtons\"[ \t]+[A-Z]\\|^[*]Fvwm"
297 "resource-code-fvwm")
305 '("^:0[ \t]*[\r\n]+[ \t]*[*{]"
309 ;; #declare Purple_Clouds = pigment {
310 ;; #include "woodmaps.inc"
313 ;; scale <30.0, 10.0, 40.0>
315 ;; translate <40.0, 14.0, 50.0>
318 "^#include[ \t]+.*.inc\""
319 "\\|^#declare[ \t]+[^ \t]+[ \t]*="
320 "\\|\\(scale\\|translate\\)[ \t]<[ \t]*[0-9.][ \t]*,.*>")
324 "[(][ \t]*\\(defmacro\\|defvar\\|defconst\||defun\\|setq"
325 "\\|add-hook\\|remove-hook\\|let[*]"
328 '("entity[ \t]+[a-z_A-Z]+[ \t]+is" "code-vhdl")
332 ;; 02 PI PIC S9V9(14).
334 "01[ \t]+WORK-AREA\\.\\|01[ \t]+CONSTANTS\\."
336 "\\|\\(working-storage\\|portability\\)[ \t]+section\\."
337 "\\|perform[ \t]+initialize-environment\\."
338 ;; display "F4 = Exit Demonstration", line 11, column 8.
339 "\\|display [\"].*,[ \t]*column[ \t]+[0-9]+\\.")
341 ;; it's "write(*,*)" , and definitely fortran
342 '("write[(][*],[*][)]" "code-fortran")
345 "class.+\\(extends\\implements\\)"
347 "\\(protected\\|public\\)[ \t]+"
348 "\\(synchronized[ \t]+\\)?Object")
350 ;; Function definition
351 ;; def add_doc(self, document, keyword_list):
352 ;; def __init__(self):
353 '("^[ \t]+def[ \t]+[a-zA-Z_]+[(].*:" "code-python")
355 ;; select * from p_msc_rej where measurement_time = '1995061914124675';
356 '("select.*from.*where.*=.*;" "code-sql")
357 '("order[ \t]+by[ \t]+[^ \t\n]+.*\\(asc\\|desc\\)" "code-sql")
358 '("^[.]TH" "text-nroff")
359 '("^Newsgroup:" "text-news")
360 '("^To:.*@" "text-mail")
362 ;; conjug :: Words -> Words -> String
363 ;; netails :: [a] -> [[a]]
364 ;; replies :: Words -> Words -> [String]
366 "conjug[ \t]+::.*->\\|netails[ \t]+::.*->\\|replies::[ \t]+.*->"
370 "^REM[ \t]\\|^CALL[ \t].*%[^ \t\r\n]+%\\[^ \t\r\n]+"
372 "*List of \(REGEXP str\) where RE is tried upon whole file.
373 First one matched is used to determine file type, so put most restrictive
376 (defconst ti::id-:file-extension-alist
377 '((".a" . "code-ada") ;Ada 83/87
378 (".ada" . "code-ada") ;Ada 83/87
379 (".ads" . "code-ada") ;ada 95
380 (".adb" . "code-ada") ;Ada 95 body/implementation
381 (".asp" . "code-asp")
382 (".awk" . "code-awk")
383 (".bat" . "code-bat")
384 (".bash" . "code-shell-bash")
386 (".cbl" . "code-cobol") ;this is the standard hdr & strc
388 (".cmd" . "code-bat")
389 (".cob" . "code-cobol") ;non-standard unix
390 (".cpp" . "code-c++")
391 (".csh" . "code-shell-csh")
392 (".cxx" . "code-c++")
394 (".el" . "code-lisp")
395 (".f" . "code-fortran")
396 (".F" . "code-fortran")
397 (".for" . "code-fortran")
398 (".fvwmrc". "resource-code-fvwm")
399 (".h" . "code-c-header")
400 (".hh" . "code-c++-header")
401 (".hs" . "code-hugs")
402 (".i" . "code-cobol-header") ;non-standard unix
403 (".idl" . "code-idl") ;CORBA idl, hassan@cs.stanford.edu
404 (".html" . "text-html")
405 (".java" . "code-java")
406 (".class" . "code-java-compiled")
407 (".jsp" . "code-jsp")
408 (".ksh" . "code-shell-ksh")
409 (".m" . "code-objective-c")
410 (".mod" . "code-objective-c")
411 (".md" . "code-modula-header") ;at least modula-2
412 (".mi" . "code-modula") ;implementation modula-2
413 (".pas" . "code-pascal")
414 (".php[34]?" . "code-php")
415 (".pl" . "code-perl-library")
416 (".pls" . "code-perl-shell")
417 (".pm" . "code-perl")
418 ;; also uses .inc and .map but I hesitate to add those extension,
419 ;; because some other may use .inc or .map too for other purposes.
420 (".pov" . "code-povray")
421 (".py" . "code-python")
422 (".sh" . "code-shell") ;might be csh/ksh/csh
423 (".sql" . "code-sql")
424 (".tex" . "text-tex")
425 (".texi" . "text-tex-info")
426 (".txt" . "text-normal")
427 (".vhd" . "code-vhdl")
428 (".vhdl" . "code-vhdl")
429 (".wml" . "code-wml")
430 (".xml" . "code-xml")
431 (".xsl" . "code-xsl")
432 (".xsp" . "code-xsp"))
433 "*List of (ASSOC-KEY STR) where KEY is tried upon
434 buffer-file-name's extension.")
436 ;; If the file cannot be identified by extension...
438 (defconst ti::id-:file-regexp-match-list
439 '(("\\.ema" "code-lisp") ;.emacs , .emacs.dired
440 ("\/\\.[Xx]" "resource-x") ;.Xdefauls, .xinirc
441 ("\/\\.kshrc" "resource-code-shell-ksh")
442 ("\/\\.t?cshrc" "resource-code-shell-csh") ;alike csh = tcsh
443 ("\/\\.bashrc" "resource-code-shell-bash")
444 ("\/\\.bashrc" "resource-code-shell-sh") ;alike bash = sh
445 ("\\.csh" "shell-csh")) ;like .cshrc or myScript.csh
446 "*List of (REGEXP STR) where RE is tried upon _whole_ buffer-file-name")
448 ;; - Buffers that do not have buffer-file-name property at all.
449 ;; - Only put 'trusted' buffer names that are known to all here.
451 (defconst ti::id-:buffer-name-regexp-list
452 '(("[*]info" "text-manual-info")
453 ("[*]man" "text-manual-shell")
454 ("[*]shell" "process-shell")
455 ("[*]ftp" "process-ftp")
456 ("[*]Article" "text-news")
457 ("[*]Summary" "text-news"))
458 "*List of (REGEXP STR) where RE is tried upon buffer-name")
460 (defconst ti::id-:function-list
461 '( ;; This first line -*- test should represent exact mode, we trust
462 ;; to it blindly. If the content is not what this mode says, it's
463 ;; user's own mistake.
464 ti::id-test-first-line-emacs-special
465 ti::id-test-first-line
466 ti::id-test-buffer-file-name
467 ti::id-test-buffer-content-special
468 ti::id-test-extension
469 ti::id-test-buffer-name
470 ti::id-test-buffer-search-regexp)
471 "*List of unctions to call to determine buffer type.
472 The calling of functions stops immediately when some function
473 returns non-nil. Notice, that this is also the order of evaluation.")
475 (defconst ti::id-:type2mode
476 '(("ada" ada-mode "--")
478 ("code-c$" c-mode "/*" "*/")
479 ("code-c++" c++-mode "//")
480 ("code-cobol" cobol-mode )
481 ("code-fortran" fortran-mode "C")
482 ("code-fvwm" fvwm-mode )
483 ("code-hugs" hugs-mode "--" )
484 ("code-idl" idl-mode )
485 ("code-java" java-mode "/*" "*/")
486 ("code-objective-c" c-mode )
487 ("code-pascal" pascal-mode )
488 ("html" html-mode "<!---" "-->")
489 ("code-php" php-mode "//")
490 ("code-python" python-mode )
491 ("code-scheme" scheme-mode )
492 ("code-sql" sql-mode "-- ")
493 ("code-tcl" tcl-mode)
494 ("pascal" pascal-mode "{" "}")
495 ("perl" perl-mode "#")
496 ("code-povray" povray-mode "/*" "*/")
497 ("lisp\\|emacs-lisp" lisp-mode ";")
498 ("text-tex" tex-mode "%")
499 ("text-texinfo" texinfo-mode "@c")
500 ("text-tex-info" texinfo-mode "%")
501 ("text-mail" mail-mode)
502 ("text-news" mail-mode)
503 ("shell-sh" sh-mode "#")
504 ("shell-csh" csh-mode "#")
505 ("shell-ksh" ksh-mode "#")
506 ("shell-zsh" zsh-mode "#")
507 ("resource-code-shell-csh" csh-mode "#")
508 ("resource-code-shell-sh" sh-mode "#")
509 ("tex$" tex-mode "%"))
511 '((REGEXP MODE-NAME-SYMBOL [COMMENT-START COMMENT-END]) (
514 where RE represent match against string that describes the buffer
515 contents. The comment-start and end fields are optional.")
520 ;;; ....................................................... &v-version ...
523 (ti::macrof-version-bug-report
527 "$Id: tinylibid.el,v 2.50 2007/05/01 17:20:45 jaalto Exp $"
529 ti::id-:function-list
531 ti::id-:global-buffer-name
532 ti::id-:global-buffer-file-name
533 ti::id-:global-buffer-extension
534 ti::id-:global-buffer-first-line
537 ti::id-:buffer-first-line-regexp-list
538 ti::id-:buffer-match-regexp-list
539 ti::id-:file-extension-alist
540 ti::id-:file-regexp-match-list
541 ti::id-:buffer-name-regexp-list
547 ;;; ----------------------------------------------------------------------
549 (defun ti::id-cnv-txt2mode (txt)
550 "This is kinda fake function, it returns the original MODE based
551 on the text that represents the buffer contents. This functions purpose
552 is solely to return you a _symbol_ that is more commonly known to all, than
553 the _string_ representing a mode.
556 Symbol returned does not necessary representy any mode you can turn on.
557 Use 'fboundp' test to be sure the symbol is callable function."
559 (dolist (elt ti::id-:type2mode)
560 (when (string-match (nth 0 elt) txt)
561 (setq ret (nth 1 elt)) ;Mode name
565 ;;; ----------------------------------------------------------------------
567 (defun ti::id-cnv-txt2comment (txt)
568 "Returns (COMMENT-START . COMMENT-END) variables for text representing
569 the buffer contents. Notice that comment-end may be nil meaning it
570 is not needed for mode."
574 (dolist (elt ti::id-:type2mode)
575 (setq re (nth 0 elt))
576 (if (> (length elt) 2)
577 (setq com-s (nth 2 elt)))
578 (if (> (length elt) 3)
579 (setq com-s (nth 3 elt)))
580 (if (null (string-match re txt))
581 (setq com-s nil com-e nil)
584 (cons com-s com-e))))
589 ;;; ----------------------------------------------------------------------
591 (defun ti::id-file-extension (file)
592 "Return file extension.
595 See variable `ti::id-:file-ext-re' how file extension is determined."
596 (let* ((re ti::id-:file-ext-re)
598 (when (and file ;doesn't have filename at all *temp*
599 (string-match re file))
600 (setq point (match-beginning 1)) ;dot position
601 (substring file point))))
603 ;;; ----------------------------------------------------------------------
605 (defun ti::id-read-first-line ()
606 "Return first ID line of the file. Empty lines are skipped."
607 (let* ((comment-beg (regexp-quote (or comment-start " ")))
608 (empty-re (concat "[ \t]*" comment-beg "[ \t]*$")))
612 (while (and (not (eobp)) ;search first sensible line
613 (looking-at empty-re))
616 (ti::read-current-line))))))
618 ;;; ----------------------------------------------------------------------
620 (defun ti::id-read-first-line-emacs-mode (str)
621 "Emacs supports special first line syntax e.g. -*-Emacs-Lisp-*-,
622 to turn on mode when file loads. Try to find function <text>-mode
623 from the internal symbol list of emacs if line contains -*- marks.
627 symbol real mode function name found from emacs.
628 Btw, emacs barks you automatically if functions given
629 in line doesn't exist when file is loaded.
635 ((setq mode (ti::string-match "-[*]-\\(.*\\)-[*]-" 1 str))
636 ;; let's make symbol out of it
637 (setq mode (concat (downcase mode) "-mode"))
638 (if (null (setq sym (intern-soft mode)))
640 ;; too bad, such mode not loaded into emacs, well if person
641 ;; has loaded file, emacs had barfed already about this unknown
642 ;; mode: "file mode specification error, void function, <mode>"
644 (if (fboundp sym) ;let's make sure sym is func ...
645 (setq ret sym))))) ;it's valid mode
648 ;;; ----------------------------------------------------------------------
650 (defun ti::id-match (string list)
651 "Match STRING against LIST el 1, return LIST elt 2"
655 (setq regexp (nth 0 elt))
656 (when (string-match regexp string)
657 (setq ret (nth 1 elt))
661 ;;; ----------------------------------------------------------------------
663 (defun ti::id-buffer-re-search (&optional point)
664 "Search `ti::id-:buffer-match-regexp-list' from buffer.
665 Start searching from `point-min' or from optional POINT."
666 (let ((list ti::id-:buffer-match-regexp-list)
669 (setq point (point-min)))
672 (goto-char point) ;start here
674 (when (re-search-forward (nth 0 elt) nil t)
675 (setq ret (nth 1 elt))
682 ;;; ----------------------------------------------------------------------
684 (defun ti::id-global-variable-reset ()
685 "Reset some globals."
686 (setq ti::id-:global-buffer-file-name nil
687 ti::id-:global-buffer-extension nil
688 ti::id-:global-buffer-first-line nil))
690 ;;; ----------------------------------------------------------------------
692 (defun ti::id-global-variable-set ()
693 "Set some globals, so that they can be used by all functions.
694 This reduces overhead of getting these variables multiple times."
695 (let* ((bp (current-buffer))
697 (bfn (buffer-file-name bp))
698 (ext (ti::id-file-extension bn))
699 (id (ti::id-read-first-line)))
700 (ti::id-global-variable-reset)
701 (setq ti::id-:global-buffer-file-name bfn
702 ti::id-:global-buffer-extension ext
703 ti::id-:global-buffer-first-line id
704 ti::id-:global-buffer-name bn)
705 ;; so that can be hook
708 ;;; ----------------------------------------------------------------------
710 (defun ti::id-study-buffer (type)
711 "Chew buffer contents.
712 Be sure to run `ti::id-global-variable-set' first so that global variables get set.
715 TYPE symbol; See source code of function.
719 symbol if real mode found in first line -*- ..-*-
721 (let* ( ;; these are already set
722 (id ti::id-:global-buffer-first-line)
723 (ext ti::id-:global-buffer-extension)
724 (bname ti::id-:global-buffer-name)
728 ((eq type 'extension)
729 (if (setq el (assoc ext ti::id-:file-extension-alist))
730 (setq ret (cdr el))))
731 ((eq type 'buffer-file-name) ;buffer name test
733 (setq ret (ti::id-match bname ti::id-:file-regexp-match-list)))
734 ((eq type 'buffer-name) ;buffer name test
735 (setq ret (ti::id-match bname ti::id-:buffer-name-regexp-list)))
736 ((and (eq type '1st-emacs) ;special -*-Emacs-Lisp-*-
738 (setq ret (ti::id-read-first-line-emacs-mode id)))
739 ((and (eq type '1st-regexp)
741 (setq ret (ti::id-match id ti::id-:buffer-first-line-regexp-list)))
742 ((eq type 'buffer-regexp) ;whole buffer is searched
743 (setq ret (ti::id-buffer-re-search))))
746 ;;; ----------------------------------------------------------------------
748 (defun ti::id-test-buffer-content-special ()
749 "Check special buffer content."
750 (let* ((text (memq major-mode '(fundamental-mode text-mode))))
753 (fboundp 'tinytf-text-format-p)
754 (ti::funcall 'tinytf-text-format-p))
755 "text-white-paper"))))
757 ;;; ----------------------------------------------------------------------
758 ;;; - testing/evaluation functions
760 (defun ti::id-test-extension ()
762 (ti::id-study-buffer 'extension))
764 (defun ti::id-test-buffer-file-name ()
766 (ti::id-study-buffer 'buffer-file-name))
768 (defun ti::id-test-buffer-name ()
770 (ti::id-study-buffer 'buffer-name))
772 (defun ti::id-test-first-line ()
774 (ti::id-study-buffer '1st-regexp))
776 (defun ti::id-test-first-line-emacs-special
779 (ti::id-study-buffer '1st-emacs))
781 (defun ti::id-test-buffer-search-regexp
784 (ti::id-study-buffer 'buffer-regexp))
789 ;;; ############################################################ &main ###
791 ;;; ----------------------------------------------------------------------
794 (defun ti::id-info (&optional mode variable-lookup verb)
795 "Try to identify buffer type.
797 Function doesn't rely on mode, because that doesn't necessarily tell what
798 the buffer holds. Many users still program their shell scripts in
799 `fundamental-mode' or so. This means that `major-mode' isn't checked,
800 because calling function can do it easily.
802 If this function returns nil, _then_ it's probably the time to check
805 The normal order of evaluation is as follows:
806 - First line in the file
807 - Whole filename including path = `buffer-file-name'
808 - File name extension
809 - `buffer-name' is checked. [temporary buffer has no file name]
810 - Whole buffer is searched for RE texts
814 MODE flag, controls return value format
815 VARIABLE-LOOKUP flag, read buffer type from cache. (From previous call)
816 VERB if non-nil, verbose messages allowed.
820 when optional MODE = nil
821 Some appropriate _string_ that represents the content. notice that this
822 string is usually generalised description, _but_ it the file has special
823 1st line in form of -*-..-*- the string is direct mode name string.
825 when optional MODE = non-nil
826 Return possible mode name as _symbol_
828 when VARIABLE is non-nil, the variable `ti::id-:info' is read instead.
829 If it has non-nil value, the value is returned, otherwise full buffer
830 is parsed again and variable's value is updated.
834 `ti::id-func-alist' order of evaluation.
835 `ti::id-:info' buffer local variable updated during every call."
838 (let* ((funcs ti::id-:function-list)
843 ;; .................................................... do lookup? ...
844 (setq ret ti::id-:info)
846 ((null variable-lookup)
848 ((and variable-lookup (null ti::id-:info)) ;no value stored
850 ((and variable-lookup ;must same type
851 (null mode) ;string request
852 (not (stringp ti::id-:info)))
854 ((and variable-lookup ;must same type
856 (not (symbolp ti::id-:info)))
858 ;; .................................................... do the job ...
860 ;; prepare globals to avoid overhead
861 (ti::id-global-variable-set)
862 (while (and (setq func (pop funcs))
863 (null (setq ret (funcall func)))))
864 ;; how the results should be returned ?
865 (when ret ;found anything?
867 (if (symbolp ret) ;return symbol
868 ret ;it's real mode name
869 (setq ret (ti::id-cnv-txt2mode ret))) ;return possible mode name
871 (setq ret (symbol-name ret))))
873 (message (prin1-to-string ret))))
874 ;; Update the buffer local variable
875 (setq ti::id-:info ret))
881 (run-hooks 'ti::id-:load-hook)
883 ;;; tinylibid.el ends here