]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinybookmark.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinybookmark.el
1 ;;; tinybookmark.el --- Keep file in organized sections
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    1995-2007 Jari Aalto
8 ;; Keywords:        tools
9 ;; Author:          Jari Aalto
10 ;; Maintainer:      Jari Aalto
11 ;;
12 ;; To get information on this program, call M-x tinybookmark-version
13 ;; Look at the code with folding.el
14
15 ;; COPYRIGHT NOTICE
16 ;;
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
20 ;; any later version.
21 ;;
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 ;; for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
31 ;;
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
33
34 ;;}}}
35 ;;{{{ Installation
36
37 ;;; Install:
38
39 ;; ........................................................ &t-install ...
40 ;; Put this file on your Emacs-Lisp load path, add following into your
41 ;; ~/.emacs startup file
42 ;;
43 ;;      (require 'tinybookmark)
44 ;;
45 ;; or use autoload, your emacs starts up faster, prefered:
46 ;;
47 ;;      (autoload 'tinybookmark-insert   "tinybookmark" "" t)
48 ;;      (autoload 'tinybookmark-repeat   "tinybookmark" "" t)
49 ;;      (autoload 'tinybookmark-parse    "tinybookmark" "" t)
50 ;;      (autoload 'tinybookmark-forward  "tinybookmark" "" t)
51 ;;      (autoload 'tinybookmark-backward "tinybookmark" "" t)
52 ;;      (autoload 'tinybookmark-keyboard         "tinybookmark" "" t)
53 ;;      (autoload 'tinybookmark-keyboard-parse   "tinybookmark" "" t)
54 ;;
55 ;;      (when (ti::compat-window-system)
56 ;;        (autoload 'tinybookmark-mouse          "tinybookmark" "" t)
57 ;;        (autoload 'tinybookmark-mouse-parse    "tinybookmark" "" t))
58 ;;
59 ;; To use  'M-x bm' for quick book mark command:
60 ;;
61 ;;      (defalias 'tinybookmark-insert 'bm)
62 ;;
63 ;; Suggested keybindings
64 ;;
65 ;;      ;;  This is for windowed Emacs. It brings up nice pop up menu
66 ;;      ;;  In XEmacs tou must use different mouse events: `mouse1down'
67 ;;
68 ;;      (global-set-key [(?\e) (control mouse-1)]        'tinybookmark-mouse)
69 ;;      (global-set-key [(?\e) (control shift mouse-1)]  'tinybookmark-mouse-parse)
70 ;;
71 ;;      ;;  Keyboard users can move between book marks with these
72 ;;
73 ;;      (global-set-key [(shift left)]  'tinybookmark-backward)
74 ;;      (global-set-key [(shift right)] 'tinybookmark-forward)
75 ;;
76 ;;      ;;  Or to bavigate with complete menu
77 ;;
78 ;;      (global-set-key [(shift right)] 'tinybookmark-keyboard)
79 ;;
80 ;; BE SURE THAT
81 ;;
82 ;;      you have defined comment syntax, otherwise the inserted field
83 ;;      won't have proper prefix + endings
84 ;;
85 ;; If you have any questions, use function:
86 ;;
87 ;;      M-x tinybookmark-submit-bug-report
88
89 ;;}}}
90 ;;{{{ Documentation
91
92 ;;; .................................................... &t-commentary ...
93
94 ;;; Commentary:
95
96 ;;  Preface, feb 1995
97 ;;
98 ;;      Long ago I used little function I wrote that inserted section
99 ;;      breaks, those that I call `book' `marks'. There was also
100 ;;      `folding.el' to keep the code in separate sections. Findings things
101 ;;      was easy when you just searched either book marks or jumped between
102 ;;      folds. Next *imenu.el* was announced which provided X-pop up for
103 ;;      book marks and adding support to it was the start of this package.
104 ;;
105 ;;  Overview of features
106 ;;
107 ;;      o   Provide 'setting book marks' functions: Add
108 ;;          repeated characters and sequences up till end of line with
109 ;;          named identifier.
110 ;;      o   Automatically parse book marks from file, if it contains
111 ;;          RCS identifier `bookMarkRegexp' which defines book mark syntax for
112 ;;          the file. Uses X-popup [imenu] to show those book marks and
113 ;;          moving between them.
114 ;;
115 ;;  How to keep files organized
116 ;;
117 ;;      There are several tools to keep your code organized and they are at
118 ;;      their best if you think how they can co-operate. There is
119 ;;      *folding.el* and *tinybookmark.el*, which might seem to do double
120 ;;      job, since they both divide code into more easily manageable
121 ;;      sections. The key point is that when folding is used, one works
122 ;;      _within_ some special section and possibly want to hide all the
123 ;;      rest of the code. But when jumping easily back and forth on the
124 ;;      buffer, it us *unfolded* and TinyBookmark is used. Now, to confuse
125 ;;      you more, there is also *imenu.el* which can be used to jump inside
126 ;;      code. It can be configured so that it will pick all function names
127 ;;      inside list, and when you want to go to specific function, just
128 ;;      pick one from imenu.
129 ;;
130 ;;      To summarize:
131 ;;
132 ;;      o   folding.el      -- for hide unneeded code,
133 ;;                             clear view on the structure
134 ;;      o   tinybookmark.el -- Jump between/finding  _large_ code sections
135 ;;      o   imenu.el        -- finding specific function, more detailed control.
136 ;;      o   tinyhotlist.el  -- Add/remove files from permanent X-popup list
137 ;;
138 ;;  How to use this package
139 ;;
140 ;;      There is following function that inserts book mark on the current line
141 ;;
142 ;;          tinybookmark-insert
143 ;;
144 ;;      There is also normal repeat function, that fills line with your
145 ;;      pattern:
146 ;;
147 ;;          tinybookmark-repeat
148 ;;
149 ;;      Normally the usual book mark separator is the "." <dot> , which
150 ;;      isn't so "noisy" as continuous '-' line. Normally you add some
151 ;;      unused ID character, like '&' at front of real book mark, like
152 ;;      this:
153 ;;
154 ;;          ;;; .................................. &How-to-use ...
155 ;;          (defun test ()
156 ;;           (progn
157 ;;            ..
158 ;;            (goto-char ..
159 ;;            ;; ^^^^^^^^^^^^^^^^^^^^^^^ sepratorInsideCode ^^^
160 ;;
161 ;;      The `How-to-use' is book mark, because it has `&' on it, whilst the
162 ;;      latter isn't -- it is used inside code to make it more readable and
163 ;;      The latter on is not included in *imenu*.
164 ;;
165 ;;  About the book mark identifier naming
166 ;;
167 ;;      When you name the breaks, keep in mind that when identifiers are
168 ;;      sorted, the ones that start with big letters A-Z show up first, a-z
169 ;;      come next. Allthougt it would be convenient to have all subwords in
170 ;;      capital, it is usually better to start with lowercase letter,
171 ;;      because it's easily unintentionally mix up/down case letters.
172 ;;      Besides you have to reah out for shift to have uppercase.
173 ;;
174 ;;          ............. breakName ...         ;prefered, starting low
175 ;;          ............. BreakName ...         ;watch out for mixed case!
176 ;;
177 ;;      it is also adviced that you choose some common beginning for the
178 ;;      identifier, so that they get sorted nicely. If you define variables
179 ;;      at the beginning of file it might be good idea to attach beginning
180 ;;      letter like `v-' for variables before the real identifier name
181 ;;      begins, like:
182 ;;
183 ;;          ............. v-globals ...
184 ;;          ............... v-hooks ...
185 ;;
186 ;;      Of course, we can now use the uppercase letter trick to have them
187 ;;      sorted first in the list, just change `v-' to `V-'. Generally
188 ;;      you should think which ones do you use most, do you leave the
189 ;;      variables alone when you have defined them and mostly work with new
190 ;;      functions? Then the variables can stay at the end of list and
191 ;;      there is no need for `V-' trick. but if you need to access
192 ;;      variables often, then you might want to see variables first in the
193 ;;      list. It's up to your decision how you name the variables and how
194 ;;      you want to see them listed.
195 ;;
196 ;;  Breaks and sub-break naming
197 ;;
198 ;;      If you have very large file, you'll probably need major breaks,
199 ;;      level one breaks and possibly level 2 breaks too. To keep the list
200 ;;      well sorted, put the functions into bigger groups and name the
201 ;;      sub-level breaks so that they have some common beginning in respect
202 ;;      to the major break they belong to. Let's see an example where
203 ;;      you're dealing with mail handling. Notice the CAPITAL letter.
204 ;;
205 ;;          ;; ################################# &h-Header ###
206 ;;          ;;  this is beginning block of header handling
207 ;;
208 ;;          ;; ..................................... &h-cc ...
209 ;;          ;;  Some special function here to handle CC
210 ;;          ;;  field, killing all recipients, or only
211 ;;          ;;  some of them
212 ;;
213 ;;          ;; .. .. . .. . .. . .. . .. . .. . .. . .. . .. .
214 ;;          ;;  More detailed functions under h-cc, Not
215 ;;          ;;  named, because there is only 2 small funcs
216 ;;          ;;  easily found.
217 ;;
218 ;;      Again there are couple of points to follow here. All the tricks are
219 ;;      discussed already: the `Big' letter trick put's major break to the
220 ;;      top of imenu list, common beginning keeps the subsections together.
221 ;;
222 ;;  Example breaks
223 ;;
224 ;;      Some book mark breaks are proposed here, but you can use whatever you
225 ;;      like. Thumb of rule: be consistent, always use same convention in
226 ;;      your files and consider the "level of noisiness" of your breaks, so that
227 ;;      they build up nicely and the code is easy to read. Too many
228 ;;      _different_ breaks is not good idea, because they clutter the view
229 ;;      fast, instead use variations on a theme: same break character but
230 ;;      varying spaces and continuous character lengths.
231 ;;
232 ;;      Thumb rule: select 1-3 break chars, and never change them in you
233 ;;      files; your files look alike. Vary the spacing, not the break
234 ;;      characters.
235 ;;
236 ;;      These are 'noisy breaks' , Major section separators, pick only one
237 ;;      and use it in your files, do not use all three!
238 ;;
239 ;;          ##############################################################
240 ;;          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241 ;;          ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
242 ;;
243 ;;      less noisy breaks
244 ;;
245 ;;          .`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`
246 ;;
247 ;;          .^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
248 ;;
249 ;;          .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:
250 ;;          .~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~
251 ;;
252 ;;
253 ;;      This is sub section break
254 ;;
255 ;;          ................................................................
256 ;;
257 ;;
258 ;;      This is even lighter subsection break (varying spacing)
259 ;;
260 ;;          ...  ...  ...  ...  ...  ...  ...  ...  ...  ...  ...  ...  ...
261 ;;
262 ;;      'Draw one's attention' break: something special in this section
263 ;;
264 ;;
265 ;;          --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++--
266 ;;
267 ;;      Internal break 1, inside function, long case statement etc.
268 ;;
269 ;;          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
270 ;;
271 ;;      Internal break 2, to separate long case elements etc.
272 ;;
273 ;;
274 ;;          ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^  ^^^
275 ;;
276 ;;     Book Mark cache
277 ;;
278 ;;      So that imenu works fast, it is not desirable that the breaks are
279 ;;      always parsed from scratch, because it takes time to scan the file
280 ;;      for possible book marks. That's why the information is cached. If
281 ;;      the break cache is empty, the breaks are gathered from buffer and
282 ;;      stored to the cache and when you call the imenu, the cache is
283 ;;      offered to it --> fast response time. When you add new breaks to
284 ;;      the buffer [especially at the beginning of code development], you
285 ;;      may want to call function `tinybookmark-parse' which will empty the
286 ;;      cache and re-read all book marks. If you write lot of code the
287 ;;      points that were cached do no longer represent exact points of book
288 ;;      marks, because they have been sliding off their places. If you want
289 ;;      *always* have updated book mark points, there is variable
290 ;;      `tinybookmark-cache-update' which you can set to 'always, if you
291 ;;      want the cache to be updated always prior showing X-menu. In large
292 ;;      buffer this remarkably slows down the menu appering. See variable
293 ;;      for more choices.
294 ;;
295 ;;  Automatic book mark detection
296 ;;
297 ;;      In order book marks to be detected in file, you may define following
298 ;;      RCS identifier [see ident(1)] preferably at the beginning of your
299 ;;      file:
300 ;;
301 ;;          $BookMarkRegexp:<space>'REGEXP'<space>$
302 ;;
303 ;;      Be careful so that the identifier is _exactly_ in this form: pay
304 ;;      attention to spaces and (') around the REGEXP. The regular
305 ;;      expression tells what line can be considered as book mark and the
306 ;;      book mark name is indicated in subexpression 1 [\\(.*\\)] , look at
307 ;;      this file, how it is constructed. In order to find all book marks
308 ;;      and build up the cache, it needs to widen the buffer in case the
309 ;;      file is narrowed with some folding or outline editor. When the
310 ;;      cache has been built the buffer's narrowing is restored, so you
311 ;;      shouldn't even notice this. Of course you don't want to find book
312 ;;      marks from your RMAIL file.
313 ;;
314 ;;      One word about the regexp construction, let's see regexp that
315 ;;      matches the identifier:
316 ;;
317 ;;          &+\\([^ ]+\\)
318 ;;
319 ;;      Pay attention to using exclusive regexp, not just '.*'
320 ;;      construction. When you use folding or outline editor the '.*' form
321 ;;      is very ill behaving, because if the line being scanned is
322 ;;      currently folded, IT WILL MATCH WHOLE folded section --> your
323 ;;      identifier surely isn't that one. We can't unfold the sections
324 ;;      during scanning, because if there are subfolds, what editor is on
325 ;;      use .. it's too complex/slow to handle such situations. But using
326 ;;      the exclusive list [^ ] will surely match the identifier, because
327 ;;      it stops when it can find first space. This means that you can't
328 ;;      use _spaces_ inside the identifiers. Cat the words together.
329 ;;
330 ;;  If the BookMarkRegexp isn't defined in file
331 ;;
332 ;;      Then the programs tries to search for the default book marks.
333 ;;      See function `tinybookmark-regexp-default' for more.
334 ;;
335 ;; Message: Empty cache. Building...
336 ;;
337 ;;      Do you wonder why you get this message displayed, while you were
338 ;;      sure that you the buffer had cache already? Don't be surprised. This
339 ;;      is totally normal behavior: whenever you switch mode for the
340 ;;      buffer the new mode _kills_ all local variables, including cache
341 ;;      information. Obviously the information must be restored when you
342 ;;      call the hot list again. The cache could have been programmed to be
343 ;;      buffer local, but in the present format only one cache s active at
344 ;;      the time. This was simpler to implement and manage in the code.
345 ;;
346 ;;  About imenu
347 ;;
348 ;;      You definitely want to look at the documentation of imenu to find
349 ;;      many more usages for it. It makes your day shine in X-display. You
350 ;;      should also configure few variables for it, like:
351 ;;
352 ;;          (setq imenu-max-items 20)
353 ;;
354 ;;  Test run
355 ;;
356 ;;      Load this file and set those key bindings mentioned. Hit the mouse
357 ;;      bindings and you're running book mark package. Since the break
358 ;;      marks are used in commentary also, the list of book marks are not
359 ;;      in their most informative form, I use following convention to name
360 ;;      book marks;
361 ;;
362 ;;          'v-'     variable topic
363 ;;          't-'     text topic
364 ;;
365 ;;  Design thoughts
366 ;;
367 ;;      Sooner or later someone wonders: "Can't we have sub-breaks listed
368 ;;      nicely with indentation in front lines in X-popup?" Present answer
369 ;;      "No", since it would require keeping track of the 'Main break' and
370 ;;      then seeing if there exist sub-breaks. Immediately this leads to
371 ;;      question "What is the main break?", and if we say main breaks start
372 ;;      with "#|/%" character set we limit the use of breaks. Besides deciding
373 ;;      what are sub-breaks, main-breaks with regexp may be too slow. Besides,
374 ;;      the breaks are intended to to give an *overview* of the buffer.
375 ;;      Please use imenu to find single functions if you don't feel like
376 ;;      tapping couple of pgUp/pgDown after the point is positioned in the break
377 ;;      section.
378
379 ;;}}}
380
381 ;;; Change Log:
382
383 ;;; Code:
384
385 ;;{{{ setup: require
386
387 ;;; ......................................................... &require ...
388
389 (require 'tinylibm)
390
391 (eval-and-compile
392   (autoload 'imenu--mouse-menu          "imenu"    "" t)
393   (autoload 'folding-show-current-entry "folding"  "" t))
394
395 (eval-when-compile (ti::package-use-dynamic-compilation))
396
397 (ti::package-defgroup-tiny TinyBookmark tinybookmark-: tools
398   "Minor mode for writing text in 'Technical text format'.
399   Overview of features
400
401         o   Provides some 'setting book marks' functions: adding
402             repeated characters and sequences up till end of line with
403             named identifier. (like breaks in this file)
404         o   Automatically parses book marks from file, if it contains
405             RCS identifier 'book markRegexp' which defines book mark syntax for
406             the file. Uses X-popup [imenu] for showing those book marks and
407             moving between them.")
408
409 ;;}}}
410 ;;{{{ setup: -- hooks
411
412 ;;; ......................................................... &v-hooks ...
413
414 (defcustom tinybookmark-:parse-before-hook nil
415   "*Hook that is run just before the buffer is scanned for book marks."
416   :type  'hook
417   :group 'TinyBookmark)
418
419 (defcustom tinybookmark-:load-hook nil
420   "*Hook run when file is loaded."
421   :type  'hook
422   :group 'TinyBookmark)
423
424 ;;}}}
425 ;;{{{ setup: user configuration
426
427 ;;; ........................................................ &v-public ...
428
429 (defcustom tinybookmark-:cache-update 'threshold
430   "*Method when to update cache.
431
432 nil         manual update -- you have to call `tinybookmark-parse'
433 'always     always update cache when menu displayed.
434 'threshold  update happens when buffer's total character change
435             exceeds previous value of `tinybookmark-:cache-threshold-val'."
436   :type '(choice
437           (const nil)
438           (const 'always)
439           (const 'threshold))
440   :group 'TinyBookmark)
441
442 (defcustom tinybookmark-:cache-threshold-val 100
443   "*When cache is constructed, the total character count is saved.
444 When user adds more code, the total count changes accordingly. If the
445 difference between current count and last saved count gets bigger than
446 this value the cache is re-read."
447   :type  'integer
448   :group 'TinyBookmark)
449
450 (defcustom tinybookmark-:re-default-chars "[-~+=*%/|#.,'`^]"
451   "*Default book mark repeat chars."
452   :type  'string
453   :group 'TinyBookmark)
454
455 (defcustom tinybookmark-:max-col '(progn  (tinybookmark-calc-max-col))
456   "*Last column to extend the break.
457 This can be FORM which evaluates to column number"
458   :type  'sexp
459   :group 'TinyBookmark)
460
461 (defcustom tinybookmark-:trailer-space-len 3
462   "*How much space is left to the right before the book mark ID ends."
463   :type  'integer
464   :group 'TinyBookmark)
465
466 (defcustom tinybookmark-:comment-start-func 'tinybookmark-comment-start
467   "*Function that return appropriate start comment.
468 Must return empty string if comment not defined."
469   :type  'function
470   :group 'TinyBookmark)
471
472 (defcustom tinybookmark-:comment-end-func 'tinybookmark-comment-end
473   "*Function that return appropriate end comment.
474 Must return empty string if comment not defined."
475   :type  'function
476   :group 'TinyBookmark)
477
478 (defcustom tinybookmark-:scan-filter-func 'tinybookmark-scan-filter
479   "*Filter out match.
480 When building up the book marks from file, there may be false hits,
481 or you may look at special lines only. This function accepts three arguments:
482 - current line string
483 - line beginning point
484 - identifier found from line
485
486 If the function return nil the line is not added to the cache."
487   :type  'function
488   :group 'TinyBookmark)
489
490 (defcustom tinybookmark-:goto-func 'tinybookmark-goto
491   "*Function that handles moving to the point.
492 If you have folding in effect around that point you may wish
493 to open it in your custom function.
494
495 This function receives one argument: POINT"
496   :type  'function
497   :group 'TinyBookmark)
498
499 (defcustom tinybookmark-:insert-strict t
500   "*Controls if the book Mark insertion is strict when no argument is given.
501 See `tinybookmark-insert'"
502   :type  'boolean
503   :group 'TinyBookmark)
504
505 ;;}}}
506 ;;{{{ setup: -- private vars
507
508 ;;; ....................................................... &v-private ...
509
510 (defvar tinybookmark-:cache nil
511   "Private.
512 Cache where book marks are stored in alist \(bookMarkName . point\)")
513 (make-variable-buffer-local 'tinybookmark-:cache)
514
515 ;;  We don't want cache to be wiped away when major mode changes
516 (put 'tinybookmark-:cache           'permanent-local t)
517
518 (defvar tinybookmark-:cache-char-count nil
519   "Private. Totals characters in buffer.")
520 (make-variable-buffer-local 'tinybookmark-:cache-char-count)
521
522 (defvar tinybookmark-:bookmark-regexp nil
523   "Private. Hold buffers book mark regexp.")
524 (make-variable-buffer-local 'tinybookmark-:bookmark-regexp)
525
526 ;;}}}
527 ;;{{{ setup: -- version
528
529 ;;; ....................................................... &v-version ...
530
531 (eval-and-compile
532   (ti::macrof-version-bug-report
533    "tinybookmark.el"
534    "tinybookmark"
535    tinybookmark-:version-id
536    "$Id: tinybookmark.el,v 2.42 2007/05/01 17:20:42 jaalto Exp $"
537    '(tinybookmark-:version-id
538      tinybookmark-:parse-before-hook
539      tinybookmark-:load-hook
540      tinybookmark-:cache
541      tinybookmark-:cache-char-count
542      tinybookmark-:bookmark-regexp
543      tinybookmark-:cache-update
544      tinybookmark-:cache-threshold-val
545      tinybookmark-:max-col
546      tinybookmark-:trailer-space-len
547      tinybookmark-:comment-start-func
548      tinybookmark-:comment-end-func
549      tinybookmark-:scan-filter-func
550      tinybookmark-:goto-func
551      tinybookmark-:insert-strict
552      tinybookmark-:re-default-chars)))
553
554 ;;}}}
555
556 ;;; ########################################################## &macros ###
557
558 ;;{{{ Macros
559
560 ;;; ----------------------------------------------------------------------
561 ;;;
562 (defsubst tinybookmark-regexp-read-from-buffer ()
563   "Return buffer's book mark regexp.
564 If the local value where the regexp is stored is nil, the rescan buffer.
565
566 References:
567   `tinybookmark-:bookmark-regexp'"
568   (or tinybookmark-:bookmark-regexp     ;changing mode kill local vars
569       (setq tinybookmark-:bookmark-regexp
570             (tinybookmark-search-bm-re))))
571
572 ;;; ----------------------------------------------------------------------
573 ;;; Default book mark syntax that is used if file does not contain
574 ;;; it's own definition of book mark syntax.
575 ;;;
576 (defsubst tinybookmark-regexp-default  ()
577   "Return default book mark regexp.
578 References:
579   `tinybookmark-:re-default-chars'"
580   (concat
581    tinybookmark-:re-default-chars
582    tinybookmark-:re-default-chars "+"
583    " &+\\([^ \t]+\\) "
584    tinybookmark-:re-default-chars "+"))
585
586 ;;}}}
587
588 ;;; ########################################################### &Funcs ###
589
590 ;;{{{ movement functions
591
592 ;;; ........................................................ &movement ...
593
594 ;;; ----------------------------------------------------------------------
595 ;;;
596 (defun tinybookmark-search-regexp ()
597   "Return book mark search regexp."
598   (concat "^[ \t]*" (or comment-start "") "+ *"
599           (tinybookmark-regexp-read-from-buffer)))
600
601 ;;; ----------------------------------------------------------------------
602 ;;;
603 ;;;###autoload
604 (defun tinybookmark-backward ()
605   "Search book mark line backward."
606   (interactive)
607   (re-search-backward (tinybookmark-search-regexp) nil t))
608
609 ;;; ----------------------------------------------------------------------
610 ;;;
611 ;;;###autoload
612 (defun tinybookmark-forward (&optional back)
613   "Search book mark line forward or optionally BACK."
614   (interactive)
615   (re-search-forward (tinybookmark-search-regexp) nil t))
616
617 ;;}}}
618
619 ;;{{{ miscellaneous functions
620
621 ;;; ............................................................ &misc ...
622
623 ;;; ----------------------------------------------------------------------
624 ;;; LISP column
625 ;;;  - I can hear you saying: "Why 74? why not 70 or 75 ?..."
626 ;;;  - Well, I usually add book mark section to my elisp code and while
627 ;;;    I did them by hand I added ';;; ' comment at the beginning of
628 ;;;    line and fed 70  continuous characters with ESC 70 '-'after
629 ;;;    comment  --> totals 4 + 70 chars :-/
630 ;;;
631 ;;;  - The idea of this calculation is that when you hit separator,
632 ;;;    like this: COMMENT-SPACE-70_CHAR_SEPARATOR, this will calculate
633 ;;;    the column so, that when tinybookmark-insert is called, the last
634 ;;;    char lines up with yours.
635 ;;;
636 ;;;    E.g. in shell mode:
637 ;;;
638 ;;;             # ---------------, 70 chars long sep, last col is 2 + 70
639 ;;;             # ..............., tinybookmark-insert lines up to col 72
640 ;;;
641 ;;;    But in lisp
642 ;;;
643 ;;;             ;;; -------------, again 70 chars long sep, 4 + 70
644 ;;;             ;;; ............., tinybookmark-insert lines up to col 74
645 ;;;
646 ;;;    Now you can hit 70 line separator in any mode and to be sure the
647 ;;;    tinybookmark-insert lines up with you.
648 ;;;
649 (defun tinybookmark-calc-max-col ()
650   "Calculates column for mode."
651   (let* ((mode          (symbol-name major-mode))
652          (cs            (or comment-start ""))
653          (def-len       70))            ; like "# " + 70 chars
654     (cond
655      ((string-match "lisp" mode)
656       74)
657      (t
658       (if (string-match "[ \t]+" cs)  ;does it already have space "# "
659           (+ def-len (length cs)) ;no it does not "#", add room for it.
660         (1+ (+ def-len (length cs))))))))
661
662 ;;; ----------------------------------------------------------------------
663 ;;;
664 (defun tinybookmark-goto (point)
665   "Go to the selected POINT."
666   (let* ((re  ".*{{{"))
667     (cond
668      ((and (featurep 'folding)
669            (symbol-value 'folding-mode))
670       (goto-char point)
671       (save-excursion
672         (beginning-of-line)
673         (if (looking-at re)
674             (folding-show-current-entry))))
675      (t
676       (goto-char point)))))
677
678 ;;; ----------------------------------------------------------------------
679 ;;; - include all lines
680 ;;;
681 (defun tinybookmark-scan-filter (full-line pos id)
682   "Return always t, so all matched lines are cached.
683 Ignore FULL-LINE POS ID."
684   t)
685
686 ;;; ----------------------------------------------------------------------
687 ;;;
688 (defun tinybookmark-comment-end ()
689   "Return appropriate comment end, according to mode."
690   (let* ((str (or comment-end "")))
691     (unless (equal "" str)
692       (setq str (ti::string-add-space str)))
693     str))
694
695 ;;; ----------------------------------------------------------------------
696 ;;;
697 (defun tinybookmark-comment-start ()
698   "Return appropriate comment, according to mode."
699   (let* ((str (or comment-start "")))   ;comment
700     ;;   Lisp is special, due to it's comment usage
701
702     (when (memq major-mode  '(lisp-mode emacs-lisp-mode lisp-interaction-mode))
703       (if (bolp)
704           (setq str ";;;")
705         (setq str ";;")))
706     (unless (equal "" str)
707       (setq str (ti::string-add-space str t)))
708     str))
709
710 ;;; ----------------------------------------------------------------------
711 ;;;
712 (defun tinybookmark-cache-update ()
713   "Determines when and how to update cache.
714 References: `tinybookmark-cache-update'"
715   (let* ((mode       tinybookmark-:cache-update)
716          (end        (marker-position (point-max-marker)))
717          (cache-end  (or tinybookmark-:cache-char-count  end))
718          (limit      tinybookmark-:cache-threshold-val)
719          diff)
720     (cond
721      ((eq mode nil)
722       nil)                              ;manual
723
724      ((eq mode 'always)
725       (tinybookmark-parse))
726
727      ((eq mode 'threshold)
728       (setq diff (abs (- end cache-end)))
729 ;;;         (ti::d! diff limit end cache-end )
730       (if (< diff limit) nil
731         ;; Hmm, should we print a message "threshold exceeded, reparsing..?"
732         ;; Let's be transparent this time: no messages.
733 ;;;     (ti::d! "reparsing..")
734         (tinybookmark-parse))))))
735
736 ;;}}}
737 ;;{{{ book mark line insert
738
739 ;;; ##################################################### &bookmarkAdd ###
740
741 ;;; ----------------------------------------------------------------------
742 ;;;
743 ;;;###autoload
744 (defun tinybookmark-repeat (str count &optional col strict)
745   "Repeats character or string sequence STR COUNT times.
746
747 COUNT can be:
748
749   0       repeat until position 79 or COL , or if the STR is not single
750           character, until fits below COL
751   \"\"    interactive insert, as long as user presses RET or SPACE.
752
753 STRICT has effect only if COL is given:
754
755   nil     insert as long as STR fits below COL
756   t       insert strictly up till COL and cut away portion
757           of STR if necessary"
758
759   (interactive "sString:\nsCount[0=eol]: ")
760
761   (let* ((swide (or col 79))            ;screen width
762          (i     0)
763          (ok    t)
764          ch
765          c
766          len
767          p)
768
769     (if (or (not (stringp str))         ;it's not string
770             (eq 0 (length str)))        ;string is empty
771         (throw 'done nil))
772
773     (cond
774      ((stringp count)
775       (if (equal "" count)
776           (setq c -1)                   ;interactive
777         (setq c (string-to-int count))))
778      ((numberp count)
779       (setq c count))
780      (t
781       (error "Invalid count arg" count)))
782
783 ;;;    (ti::d! "c-val" c)
784
785     ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
786     (cond
787      ((eq c -1)                        ;ask confirmation every time...
788       (while ok
789         (message "insert? <spc,enter> ") (setq ch (read-char))
790         (cond
791          ((or (char= ch ?\C-m ) (char= ch ?\ ))
792           (insert str))
793          (t (setq ok nil))))
794       (message ""))
795
796      ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
797
798      ((eq c 0)
799
800       (setq len         (length str)
801             p           (current-column))
802
803       ;; we have to remove tabs from this line to get count right
804
805       (untabify (line-beginning-position) (line-end-position))
806       (move-to-column p)                ;restore position
807
808       ;; the added str must not move point further than COL
809
810       (while (<= (+ len (current-column)) swide)
811         (insert str))
812
813 ;;;      (ti::d!  "c-val 0" (current-column) swide)
814
815       ;;   Check if it was multicharacter and we didn't get to last position
816       ;;   Insert the last string and cut after COL
817
818       (if (null strict) nil
819         (if (= (current-column) swide) nil
820           (insert str)
821           (ti::buffer-move-to-col swide)
822           (delete-region (point) (progn (end-of-line) (point))))))
823
824      ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
825      (t                                 ;straight number !
826       (while (< i c)
827         (insert str) (setq i (1+ i)))))))
828
829 ;;; ----------------------------------------------------------------------
830 ;;;
831 ;;;###autoload
832 (defun tinybookmark-insert (txt sep &optional strict)
833   "Add book mark until the end of line.
834 Normally line is filled as long as the pattern fits below max column,
835 but if the optional argument is given, it will be filled in _full_ ,
836 truncating if necessary. To see an example, try with some _long_
837 pattern.
838
839 Input:
840
841   TXT       book mark name
842   SEP       separator string that is repeated.
843   STRICT
844             0       strict is nil in spite of `tinybookmark-:insert-strict'
845             1       strict is t   in spite of `tinybookmark-:insert-strict'
846             nil     use default value in `tinybookmark-:insert-strict'
847
848 References:
849
850         `tinybookmark-:insert-strict'"
851   (interactive "sBookmark: \nsSep: \nP")
852   (let* (
853          (strict-def  tinybookmark-:insert-strict)
854          (cs-func     tinybookmark-:comment-start-func)
855          (ce-func     tinybookmark-:comment-end-func)
856          (line-col    (eval tinybookmark-:max-col))
857          (trail-len   tinybookmark-:trailer-space-len) ;how much to leave
858          (bolp        (line-beginning-position))
859          (cur-col     (current-column))
860          (orig-point  (+ bolp cur-col))
861          col
862          cs
863          ce)
864
865     (cond
866      ((eq nil strict)                   ;use default
867       (setq strict strict-def))
868
869      ((eq 0 strict)
870       (setq strict nil))
871
872      ((eq 1 strict)
873       (setq strict t))) ;; cond end
874
875     (if (= 0 (length sep))
876         (error "No separator"))
877
878     ;;  Add surrounding spaces if they are not included
879
880     (unless (equal "" txt)
881       (setq txt (ti::string-add-space txt)
882             txt (ti::string-add-space txt t)))
883
884     (setq cs (funcall cs-func)          ;Set comments
885           ce (funcall ce-func))
886
887     ;;  I tried to turn overwrite-mode on, but SUPRISE!
888     ;;  - While it was on, and I tried to do 'insert',
889     ;;    it didn't _overwrite_; emacs why can't you behave as expected ?
890     ;;  - So I have to hack with delete-region instead.
891
892     ;;  - skip lenght of comment start
893
894     (ti::buffer-move-to-col (+ cur-col (length cs)))
895
896     ;; We must clear the rest of line
897
898     (unless (looking-at "$")
899       (delete-region (point) (line-end-position)))
900
901     ;;  - draw the line until max col
902
903     (setq col line-col)                 ;maximum repeat column
904
905     (tinybookmark-repeat sep 0 col strict) ;insert full separator
906
907     ;;  - Now cut room for identifier
908
909     (backward-char (+ (length txt) trail-len)) ;leave trailer space
910     (delete-region (point) (+ (point) (length txt)))
911
912     ;;  - write the identifier
913
914     (insert txt)
915     (end-of-line)
916     (insert ce)                         ;comment end
917
918     ;;  - delete spaces at front and replace it with comment start
919
920     (goto-char orig-point)
921     (delete-region (point) (+ (point) (length cs)))
922     (insert cs)
923
924     (goto-char orig-point)))
925
926 ;;}}}
927 ;;{{{ Book Mark find, cacheing
928
929 ;;; #################################################### &bookmarkScan ###
930
931 ;;; ----------------------------------------------------------------------
932 ;;;
933 (defun tinybookmark-scan (re)
934   "Gather all book marks from current point forward using RE.
935 Return list: (id . beginning-of-line-point).
936
937 References:
938   `tinybookmark-:scan-filter-func'"
939   (let* ((func tinybookmark-:scan-filter-func) ;should we filter something ?
940          id
941          p
942          list)
943
944     (while (re-search-forward re nil t)
945 ;;;      (ti::d! (match-string 1) (ti::read-current-line))
946       (when (setq id (match-string 1))
947         (setq p (line-beginning-position))
948         ;;  Is this line allowed to add ?
949         (if (funcall func (ti::read-current-line) id p)
950             ;;  Nothing magic in this expression, just build list
951             ;;  '((id point) (id .point) ..)
952             (ti::nconc list (cons id p)))))
953     list))
954
955 ;;; ----------------------------------------------------------------------
956 ;;;
957 (defun tinybookmark-search-bm-re ()
958   "Search buffer for automatic book mark identifier 'BookMarkRegexp'.
959 Returns regexp defined in it. if is doesn't exist returns default
960 book mark regexp."
961   (let* ((id          "BookMarkRegexp")
962          (re-default  (tinybookmark-regexp-default))
963          id-val
964          fixed-val
965          ret)
966     (setq id-val (ti::vc-rcs-str-find-buffer id t))
967     ;;  while reading from buffer the \ doubles, convert it back to \
968     (setq fixed-val (ti::string-plain-string-to-regexp id-val))
969
970     (if (or  (null fixed-val)
971              ;;  We must find the '' marks
972              (not (string-match "'\\(.+\\)'" fixed-val)))
973         (setq ret re-default)
974       (setq ret (match-string 1 fixed-val)))
975     ret))
976
977 ;;; ----------------------------------------------------------------------
978 ;;;
979 ;;;###autoload
980 (defun tinybookmark-parse ()
981   "Build book mark list and save it to cache.
982
983 Return:
984
985   t     cache was built.
986   nil   book marks not found or error happened. Cache untouched."
987   (interactive)
988   (let* ((op             (point))       ;user's original point
989          (beg            (point-min-marker))
990          (end            (point-max-marker))
991          (end-pos        (marker-position (point-max-marker)))
992          (end-max        (point-max))
993          end-wmax
994          re
995          ret
996          list)
997
998     (run-hooks 'tinybookmark-:parse-before-hook)
999     (setq tinybookmark-:cache-char-count end-pos) ;<< GLOBAL
1000
1001     (if (null (setq re (tinybookmark-regexp-read-from-buffer)))
1002         (message "TinyBookmark: No book mark syntax Identifier found.")
1003       (unwind-protect                   ;handle narrowed buffers too
1004           (progn
1005             (widen)
1006             (setq end-wmax (point-max))
1007             (ti::pmin)
1008             (setq list (tinybookmark-scan re))
1009             (if (null list)
1010                 (message "TinyBookmark: No book marks found.")
1011               (setq ret t)
1012               (setq tinybookmark-:cache list)))
1013         (save-excursion
1014           (set-buffer (marker-buffer beg))
1015           ;; what about after widen ? Were we in narrow mode ?
1016           (unless (= end-wmax end-max)
1017             (narrow-to-region beg end)))))
1018     ;; only reasonable way to return to current point
1019     (goto-char op)
1020     ret))
1021
1022 ;;}}}
1023 ;;{{{ mouse
1024
1025 ;;; ################################################### &mouseHandling ###
1026
1027 ;;; ----------------------------------------------------------------------
1028 ;;;
1029 ;;;###autoload
1030 (defun tinybookmark-keyboard-parse ()
1031   "Reparse book marks."
1032   (tinybookmark-mouse-parse nil (interactive-p)))
1033
1034 ;;; ----------------------------------------------------------------------
1035 ;;;
1036 ;;;###autoload
1037 (defun tinybookmark-mouse-parse (&optional event verb)
1038   "Reparse book mark list. This function is called from mouse binding.
1039 Called with mouse EVENT. VERB displays message."
1040   (interactive "e")
1041   (ti::verb)
1042   (if (and verb (tinybookmark-parse))
1043       (message "TinyBookmark: Book Marks cached.")))
1044
1045 ;;; ----------------------------------------------------------------------
1046 ;;;
1047 (defun tinybookmark-selection (event)
1048   "Display cache menu. Called with mouse EVENT."
1049   (interactive "e")
1050   (let* ((go-func   tinybookmark-:goto-func)
1051          cache
1052          data)
1053
1054     (tinybookmark-cache-update)
1055     (setq cache     tinybookmark-:cache)
1056
1057     (if (null cache)
1058         (message "TinyBookmark: No book marks found.")
1059       (cond
1060        ((ti::compat-window-system)
1061         (setq data (imenu--mouse-menu cache event)))
1062        (t
1063         (setq data (completing-read "Select: " cache))
1064         (setq data (assoc data cache))))
1065
1066       (if data
1067           (funcall go-func (cdr data))))))
1068
1069 ;;; ----------------------------------------------------------------------
1070 ;;;
1071 (defun tinybookmark-cache-regenerate (&optional force)
1072   "Regenerate cache if needed. Optional FORCE."
1073   (let* ((cache-ok   tinybookmark-:cache))
1074     (when (or (null cache-ok)
1075               force)
1076       (message "TinyBookmark: building cache...")
1077       (sleep-for 1)
1078       (message "")
1079       (tinybookmark-parse))))
1080
1081 ;;; ----------------------------------------------------------------------
1082 ;;;
1083 ;;;###autoload
1084 (defun tinybookmark-keyboard (bookmark &optional arg)
1085   "Complete and jump to bookmarks.
1086 Optional ARG rebuilds cache."
1087   (interactive
1088    (progn
1089      (if current-prefix-arg
1090          (tinybookmark-cache-regenerate t))
1091      (let* ((ans (completing-read "TinyBookmark: "
1092                                   tinybookmark-:cache nil 'match)))
1093        (list ans
1094              current-prefix-arg))))
1095   (unless (interactive-p)
1096     (tinybookmark-cache-regenerate arg))
1097   (let* ((elt (assoc bookmark tinybookmark-:cache)))
1098     (if (not elt)
1099         (message
1100          (substitute-command-keys
1101           (concat
1102            "TinyBookmark: ERROR, rebuild with "
1103            "\\[universal-argument] \\[tinybookmark-keyboard]")))
1104       (goto-char (cdr elt)))))
1105
1106 ;;; ----------------------------------------------------------------------
1107 ;;;
1108 ;;;###autoload
1109 (defun tinybookmark-mouse (event &optional arg)
1110   "Display book mark pop up menu. Use mouse EVENT.
1111 Optional ARG rebuilds cache."
1112   (interactive "e\nP")
1113   (tinybookmark-cache-regenerate arg)
1114   (tinybookmark-selection event))
1115
1116 ;;}}}
1117
1118 (provide   'tinybookmark)
1119 (run-hooks 'tinybookmark-:load-hook)
1120
1121 ;;; tinybookmark.el ends here