1 ;;; tinybookmark.el --- Keep file in organized sections
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 tinybookmark-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 'tinybookmark)
45 ;; or use autoload, your emacs starts up faster, prefered:
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)
55 ;; (when (ti::compat-window-system)
56 ;; (autoload 'tinybookmark-mouse "tinybookmark" "" t)
57 ;; (autoload 'tinybookmark-mouse-parse "tinybookmark" "" t))
59 ;; To use 'M-x bm' for quick book mark command:
61 ;; (defalias 'tinybookmark-insert 'bm)
63 ;; Suggested keybindings
65 ;; ;; This is for windowed Emacs. It brings up nice pop up menu
66 ;; ;; In XEmacs tou must use different mouse events: `mouse1down'
68 ;; (global-set-key [(?\e) (control mouse-1)] 'tinybookmark-mouse)
69 ;; (global-set-key [(?\e) (control shift mouse-1)] 'tinybookmark-mouse-parse)
71 ;; ;; Keyboard users can move between book marks with these
73 ;; (global-set-key [(shift left)] 'tinybookmark-backward)
74 ;; (global-set-key [(shift right)] 'tinybookmark-forward)
76 ;; ;; Or to bavigate with complete menu
78 ;; (global-set-key [(shift right)] 'tinybookmark-keyboard)
82 ;; you have defined comment syntax, otherwise the inserted field
83 ;; won't have proper prefix + endings
85 ;; If you have any questions, use function:
87 ;; M-x tinybookmark-submit-bug-report
92 ;;; .................................................... &t-commentary ...
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.
105 ;; Overview of features
107 ;; o Provide 'setting book marks' functions: Add
108 ;; repeated characters and sequences up till end of line with
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.
115 ;; How to keep files organized
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.
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
138 ;; How to use this package
140 ;; There is following function that inserts book mark on the current line
142 ;; tinybookmark-insert
144 ;; There is also normal repeat function, that fills line with your
147 ;; tinybookmark-repeat
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
154 ;; ;;; .................................. &How-to-use ...
159 ;; ;; ^^^^^^^^^^^^^^^^^^^^^^^ sepratorInsideCode ^^^
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*.
165 ;; About the book mark identifier naming
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.
174 ;; ............. breakName ... ;prefered, starting low
175 ;; ............. BreakName ... ;watch out for mixed case!
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
183 ;; ............. v-globals ...
184 ;; ............... v-hooks ...
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.
196 ;; Breaks and sub-break naming
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.
205 ;; ;; ################################# &h-Header ###
206 ;; ;; this is beginning block of header handling
208 ;; ;; ..................................... &h-cc ...
209 ;; ;; Some special function here to handle CC
210 ;; ;; field, killing all recipients, or only
213 ;; ;; .. .. . .. . .. . .. . .. . .. . .. . .. . .. .
214 ;; ;; More detailed functions under h-cc, Not
215 ;; ;; named, because there is only 2 small funcs
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.
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.
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
236 ;; These are 'noisy breaks' , Major section separators, pick only one
237 ;; and use it in your files, do not use all three!
239 ;; ##############################################################
240 ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241 ;; ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
245 ;; .`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`
247 ;; .^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
249 ;; .:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:
250 ;; .~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~
253 ;; This is sub section break
255 ;; ................................................................
258 ;; This is even lighter subsection break (varying spacing)
260 ;; ... ... ... ... ... ... ... ... ... ... ... ... ...
262 ;; 'Draw one's attention' break: something special in this section
265 ;; --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++--
267 ;; Internal break 1, inside function, long case statement etc.
269 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
271 ;; Internal break 2, to separate long case elements etc.
274 ;; ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^
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
295 ;; Automatic book mark detection
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
301 ;; $BookMarkRegexp:<space>'REGEXP'<space>$
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.
314 ;; One word about the regexp construction, let's see regexp that
315 ;; matches the identifier:
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.
330 ;; If the BookMarkRegexp isn't defined in file
332 ;; Then the programs tries to search for the default book marks.
333 ;; See function `tinybookmark-regexp-default' for more.
335 ;; Message: Empty cache. Building...
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.
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:
352 ;; (setq imenu-max-items 20)
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
362 ;; 'v-' variable topic
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
387 ;;; ......................................................... &require ...
392 (autoload 'imenu--mouse-menu "imenu" "" t)
393 (autoload 'folding-show-current-entry "folding" "" t))
395 (eval-when-compile (ti::package-use-dynamic-compilation))
397 (ti::package-defgroup-tiny TinyBookmark tinybookmark-: tools
398 "Minor mode for writing text in 'Technical text format'.
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.")
410 ;;{{{ setup: -- hooks
412 ;;; ......................................................... &v-hooks ...
414 (defcustom tinybookmark-:parse-before-hook nil
415 "*Hook that is run just before the buffer is scanned for book marks."
417 :group 'TinyBookmark)
419 (defcustom tinybookmark-:load-hook nil
420 "*Hook run when file is loaded."
422 :group 'TinyBookmark)
425 ;;{{{ setup: user configuration
427 ;;; ........................................................ &v-public ...
429 (defcustom tinybookmark-:cache-update 'threshold
430 "*Method when to update cache.
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'."
440 :group 'TinyBookmark)
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."
448 :group 'TinyBookmark)
450 (defcustom tinybookmark-:re-default-chars "[-~+=*%/|#.,'`^]"
451 "*Default book mark repeat chars."
453 :group 'TinyBookmark)
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"
459 :group 'TinyBookmark)
461 (defcustom tinybookmark-:trailer-space-len 3
462 "*How much space is left to the right before the book mark ID ends."
464 :group 'TinyBookmark)
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."
470 :group 'TinyBookmark)
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."
476 :group 'TinyBookmark)
478 (defcustom tinybookmark-:scan-filter-func 'tinybookmark-scan-filter
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
486 If the function return nil the line is not added to the cache."
488 :group 'TinyBookmark)
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.
495 This function receives one argument: POINT"
497 :group 'TinyBookmark)
499 (defcustom tinybookmark-:insert-strict t
500 "*Controls if the book Mark insertion is strict when no argument is given.
501 See `tinybookmark-insert'"
503 :group 'TinyBookmark)
506 ;;{{{ setup: -- private vars
508 ;;; ....................................................... &v-private ...
510 (defvar tinybookmark-:cache nil
512 Cache where book marks are stored in alist \(bookMarkName . point\)")
513 (make-variable-buffer-local 'tinybookmark-:cache)
515 ;; We don't want cache to be wiped away when major mode changes
516 (put 'tinybookmark-:cache 'permanent-local t)
518 (defvar tinybookmark-:cache-char-count nil
519 "Private. Totals characters in buffer.")
520 (make-variable-buffer-local 'tinybookmark-:cache-char-count)
522 (defvar tinybookmark-:bookmark-regexp nil
523 "Private. Hold buffers book mark regexp.")
524 (make-variable-buffer-local 'tinybookmark-:bookmark-regexp)
527 ;;{{{ setup: -- version
529 ;;; ....................................................... &v-version ...
532 (ti::macrof-version-bug-report
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
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)))
556 ;;; ########################################################## ¯os ###
560 ;;; ----------------------------------------------------------------------
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.
567 `tinybookmark-:bookmark-regexp'"
568 (or tinybookmark-:bookmark-regexp ;changing mode kill local vars
569 (setq tinybookmark-:bookmark-regexp
570 (tinybookmark-search-bm-re))))
572 ;;; ----------------------------------------------------------------------
573 ;;; Default book mark syntax that is used if file does not contain
574 ;;; it's own definition of book mark syntax.
576 (defsubst tinybookmark-regexp-default ()
577 "Return default book mark regexp.
579 `tinybookmark-:re-default-chars'"
581 tinybookmark-:re-default-chars
582 tinybookmark-:re-default-chars "+"
584 tinybookmark-:re-default-chars "+"))
588 ;;; ########################################################### &Funcs ###
590 ;;{{{ movement functions
592 ;;; ........................................................ &movement ...
594 ;;; ----------------------------------------------------------------------
596 (defun tinybookmark-search-regexp ()
597 "Return book mark search regexp."
598 (concat "^[ \t]*" (or comment-start "") "+ *"
599 (tinybookmark-regexp-read-from-buffer)))
601 ;;; ----------------------------------------------------------------------
604 (defun tinybookmark-backward ()
605 "Search book mark line backward."
607 (re-search-backward (tinybookmark-search-regexp) nil t))
609 ;;; ----------------------------------------------------------------------
612 (defun tinybookmark-forward (&optional back)
613 "Search book mark line forward or optionally BACK."
615 (re-search-forward (tinybookmark-search-regexp) nil t))
619 ;;{{{ miscellaneous functions
621 ;;; ............................................................ &misc ...
623 ;;; ----------------------------------------------------------------------
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 :-/
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.
636 ;;; E.g. in shell mode:
638 ;;; # ---------------, 70 chars long sep, last col is 2 + 70
639 ;;; # ..............., tinybookmark-insert lines up to col 72
643 ;;; ;;; -------------, again 70 chars long sep, 4 + 70
644 ;;; ;;; ............., tinybookmark-insert lines up to col 74
646 ;;; Now you can hit 70 line separator in any mode and to be sure the
647 ;;; tinybookmark-insert lines up with you.
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
655 ((string-match "lisp" mode)
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))))))))
662 ;;; ----------------------------------------------------------------------
664 (defun tinybookmark-goto (point)
665 "Go to the selected POINT."
668 ((and (featurep 'folding)
669 (symbol-value 'folding-mode))
674 (folding-show-current-entry))))
676 (goto-char point)))))
678 ;;; ----------------------------------------------------------------------
679 ;;; - include all lines
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."
686 ;;; ----------------------------------------------------------------------
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)))
695 ;;; ----------------------------------------------------------------------
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
702 (when (memq major-mode '(lisp-mode emacs-lisp-mode lisp-interaction-mode))
706 (unless (equal "" str)
707 (setq str (ti::string-add-space str t)))
710 ;;; ----------------------------------------------------------------------
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)
725 (tinybookmark-parse))
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))))))
737 ;;{{{ book mark line insert
739 ;;; ##################################################### &bookmarkAdd ###
741 ;;; ----------------------------------------------------------------------
744 (defun tinybookmark-repeat (str count &optional col strict)
745 "Repeats character or string sequence STR COUNT times.
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.
753 STRICT has effect only if COL is given:
755 nil insert as long as STR fits below COL
756 t insert strictly up till COL and cut away portion
759 (interactive "sString:\nsCount[0=eol]: ")
761 (let* ((swide (or col 79)) ;screen width
769 (if (or (not (stringp str)) ;it's not string
770 (eq 0 (length str))) ;string is empty
776 (setq c -1) ;interactive
777 (setq c (string-to-int count))))
781 (error "Invalid count arg" count)))
783 ;;; (ti::d! "c-val" c)
785 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
787 ((eq c -1) ;ask confirmation every time...
789 (message "insert? <spc,enter> ") (setq ch (read-char))
791 ((or (char= ch ?\C-m ) (char= ch ?\ ))
796 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
800 (setq len (length str)
803 ;; we have to remove tabs from this line to get count right
805 (untabify (line-beginning-position) (line-end-position))
806 (move-to-column p) ;restore position
808 ;; the added str must not move point further than COL
810 (while (<= (+ len (current-column)) swide)
813 ;;; (ti::d! "c-val 0" (current-column) swide)
815 ;; Check if it was multicharacter and we didn't get to last position
816 ;; Insert the last string and cut after COL
818 (if (null strict) nil
819 (if (= (current-column) swide) nil
821 (ti::buffer-move-to-col swide)
822 (delete-region (point) (progn (end-of-line) (point))))))
824 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
825 (t ;straight number !
827 (insert str) (setq i (1+ i)))))))
829 ;;; ----------------------------------------------------------------------
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_
842 SEP separator string that is repeated.
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'
850 `tinybookmark-:insert-strict'"
851 (interactive "sBookmark: \nsSep: \nP")
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))
866 ((eq nil strict) ;use default
867 (setq strict strict-def))
873 (setq strict t))) ;; cond end
875 (if (= 0 (length sep))
876 (error "No separator"))
878 ;; Add surrounding spaces if they are not included
880 (unless (equal "" txt)
881 (setq txt (ti::string-add-space txt)
882 txt (ti::string-add-space txt t)))
884 (setq cs (funcall cs-func) ;Set comments
885 ce (funcall ce-func))
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.
892 ;; - skip lenght of comment start
894 (ti::buffer-move-to-col (+ cur-col (length cs)))
896 ;; We must clear the rest of line
898 (unless (looking-at "$")
899 (delete-region (point) (line-end-position)))
901 ;; - draw the line until max col
903 (setq col line-col) ;maximum repeat column
905 (tinybookmark-repeat sep 0 col strict) ;insert full separator
907 ;; - Now cut room for identifier
909 (backward-char (+ (length txt) trail-len)) ;leave trailer space
910 (delete-region (point) (+ (point) (length txt)))
912 ;; - write the identifier
916 (insert ce) ;comment end
918 ;; - delete spaces at front and replace it with comment start
920 (goto-char orig-point)
921 (delete-region (point) (+ (point) (length cs)))
924 (goto-char orig-point)))
927 ;;{{{ Book Mark find, cacheing
929 ;;; #################################################### &bookmarkScan ###
931 ;;; ----------------------------------------------------------------------
933 (defun tinybookmark-scan (re)
934 "Gather all book marks from current point forward using RE.
935 Return list: (id . beginning-of-line-point).
938 `tinybookmark-:scan-filter-func'"
939 (let* ((func tinybookmark-:scan-filter-func) ;should we filter something ?
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)))))
955 ;;; ----------------------------------------------------------------------
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
961 (let* ((id "BookMarkRegexp")
962 (re-default (tinybookmark-regexp-default))
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))
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)))
977 ;;; ----------------------------------------------------------------------
980 (defun tinybookmark-parse ()
981 "Build book mark list and save it to cache.
986 nil book marks not found or error happened. Cache untouched."
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))
998 (run-hooks 'tinybookmark-:parse-before-hook)
999 (setq tinybookmark-:cache-char-count end-pos) ;<< GLOBAL
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
1006 (setq end-wmax (point-max))
1008 (setq list (tinybookmark-scan re))
1010 (message "TinyBookmark: No book marks found.")
1012 (setq tinybookmark-:cache list)))
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
1025 ;;; ################################################### &mouseHandling ###
1027 ;;; ----------------------------------------------------------------------
1030 (defun tinybookmark-keyboard-parse ()
1031 "Reparse book marks."
1032 (tinybookmark-mouse-parse nil (interactive-p)))
1034 ;;; ----------------------------------------------------------------------
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."
1042 (if (and verb (tinybookmark-parse))
1043 (message "TinyBookmark: Book Marks cached.")))
1045 ;;; ----------------------------------------------------------------------
1047 (defun tinybookmark-selection (event)
1048 "Display cache menu. Called with mouse EVENT."
1050 (let* ((go-func tinybookmark-:goto-func)
1054 (tinybookmark-cache-update)
1055 (setq cache tinybookmark-:cache)
1058 (message "TinyBookmark: No book marks found.")
1060 ((ti::compat-window-system)
1061 (setq data (imenu--mouse-menu cache event)))
1063 (setq data (completing-read "Select: " cache))
1064 (setq data (assoc data cache))))
1067 (funcall go-func (cdr data))))))
1069 ;;; ----------------------------------------------------------------------
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)
1076 (message "TinyBookmark: building cache...")
1079 (tinybookmark-parse))))
1081 ;;; ----------------------------------------------------------------------
1084 (defun tinybookmark-keyboard (bookmark &optional arg)
1085 "Complete and jump to bookmarks.
1086 Optional ARG rebuilds cache."
1089 (if current-prefix-arg
1090 (tinybookmark-cache-regenerate t))
1091 (let* ((ans (completing-read "TinyBookmark: "
1092 tinybookmark-:cache nil 'match)))
1094 current-prefix-arg))))
1095 (unless (interactive-p)
1096 (tinybookmark-cache-regenerate arg))
1097 (let* ((elt (assoc bookmark tinybookmark-:cache)))
1100 (substitute-command-keys
1102 "TinyBookmark: ERROR, rebuild with "
1103 "\\[universal-argument] \\[tinybookmark-keyboard]")))
1104 (goto-char (cdr elt)))))
1106 ;;; ----------------------------------------------------------------------
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))
1118 (provide 'tinybookmark)
1119 (run-hooks 'tinybookmark-:load-hook)
1121 ;;; tinybookmark.el ends here