1 ;;; howm-misc.el --- Wiki-like note-taking tool
2 ;;; Copyright (C) 2002, 2003, 2004, 2005-2020
3 ;;; HIRAOKA Kazuyuki <khi@users.osdn.me>
5 ;;; This program is free software; you can redistribute it and/or modify
6 ;;; it under the terms of the GNU General Public License as published by
7 ;;; the Free Software Foundation; either version 1, or (at your option)
10 ;;; This program is distributed in the hope that it will be useful,
11 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;;; GNU General Public License for more details.
15 ;;; The GNU General Public License is available by anonymouse ftp from
16 ;;; prep.ai.mit.edu in pub/gnu/COPYING. Alternately, you can write to
17 ;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
19 ;;;--------------------------------------------------------------------
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 (defun howm-version ()
29 (message "howm-%s" howm-version))
31 (defun howm-keyword-file ()
33 (when (not (file-exists-p howm-keyword-file))
35 (find-file howm-keyword-file)
37 (goto-char (point-min))
38 (insert howm-menu-top "\n"))
39 (set-buffer-modified-p t)
42 (message "Generating %s ..." howm-keyword-file)
43 (howm-keyword-add-items (howm-all-items))
47 (add-hook 'howm-view-open-hook 'howm-set-mode)
48 (defun howm-set-mode ()
49 (when (howm-set-mode-p)
50 (howm-set-configuration-for-major-mode major-mode)
53 (defun howm-set-mode-p (&optional buf)
54 (with-current-buffer (or buf (current-buffer))
55 (let ((hdir (car (howm-search-path))))
56 (and (buffer-file-name)
57 (howm-folder-territory-p hdir (buffer-file-name))))))
59 (defvar howm-configuration-for-major-mode nil)
61 ;; (setq howm-configuration-for-major-mode
66 ;; (howm-keyword-format . "(def[a-z*]+ +%s[ \t\r\n]")
67 ;; (howm-keyword-regexp-format . "%s")
68 ;; (howm-keyword-regexp . "(\\(def[a-z]+\\) +'?\\([-+=*/_~!@$%^&:<>{}?a-zA-Z0-9]+\\)") ;; ' for (defalias 'xxx ...)
69 ;; (howm-keyword-regexp-hilit-pos . 1)
70 ;; (howm-keyword-regexp-pos . 2)
71 ;; (howm-view-title-regexp . "^(.*$")
72 ;; ;; (howm-view-title-regexp . "^[^; \t\r\n].*$")
73 ;; (howm-view-title-regexp-pos . 0)
74 ;; (howm-view-title-regexp-grep . "^[^; \t\r\n].*$")
75 ;; (howm-mode-title-face . nil)
76 ;; (howm-keyword-list-alias-sep . nil)
77 ;; (howm-view-preview-narrow . nil)
81 ;; (howm-keyword-format . "(def[a-z]+ +[(]?%s[) \t\r\n]")
82 ;; (howm-keyword-regexp-format . "%s")
83 ;; (howm-keyword-regexp . "(\\(def[a-z]+\\) +[(]?\\([-+=*/_~!@$%^&:<>{}?a-zA-Z0-9]+\\)")
84 ;; (howm-keyword-regexp-hilit-pos . 1)
85 ;; (howm-keyword-regexp-pos . 2)
86 ;; (howm-view-title-regexp . "^[^; \t\r\n].*$")
87 ;; (howm-view-title-regexp-pos . 0)
88 ;; (howm-view-title-regexp-grep . "^[^; \t\r\n].*$")
89 ;; (howm-mode-title-face . nil)
90 ;; (howm-keyword-list-alias-sep . nil)
91 ;; (howm-view-preview-narrow . nil)
95 ;; (howm-keyword-format . "\\(def\\|class\\) +%s\\b")
96 ;; (howm-keyword-regexp-format . "%s")
97 ;; (howm-keyword-regexp . "\\(def\\|class\\) +\\([-+=*/_~!@$%^&:<>{}?a-zA-Z0-9]+\\)")
98 ;; (howm-keyword-regexp-hilit-pos . 1)
99 ;; (howm-keyword-regexp-pos . 2)
100 ;; (howm-view-title-regexp . "^[^# \t\r\n].*$")
101 ;; (howm-view-title-regexp-pos . 0)
102 ;; (howm-view-title-regexp-grep . "^[^# \t\r\n].*$")
103 ;; (howm-mode-title-face . nil)
104 ;; (howm-keyword-list-alias-sep . nil)
105 ;; (howm-view-preview-narrow . nil)
109 ;; (howm-keyword-format . "\\\\label%s")
110 ;; (howm-keyword-regexp-format . "%s")
111 ;; (howm-keyword-regexp . "\\(\\\\label\\)\\({[^}\r\n]+}\\)")
112 ;; (howm-keyword-regexp-hilit-pos . 1)
113 ;; (howm-keyword-regexp-pos . 2)
114 ;; (howm-view-title-regexp . "\\\\\\(\\(sub\\)*section\\|chapter\\|part\\|begin\\)")
115 ;; (howm-view-title-regexp-pos . 0)
116 ;; (howm-view-title-regexp-grep . "\\\\((sub)*section|chapter|part|begin)")
117 ;; (howm-mode-title-face . nil)
118 ;; (howm-keyword-list-alias-sep . nil)
119 ;; (howm-view-preview-narrow . nil)
123 (defun howm-set-configuration-for-file-name (f)
124 (let ((mode (howm-auto-mode f)))
125 (howm-set-configuration-for-major-mode mode)))
127 (defun howm-set-configuration-for-major-mode (mode)
128 (let ((a (cdr (assoc mode howm-configuration-for-major-mode))))
129 (when a ;; I know this is redundant.
131 (let ((symbol (car sv))
133 (set (make-local-variable symbol) value)))
136 (defmacro howm-if-unbound (var &rest alt-body)
137 `(if (boundp ',var) ,var ,@alt-body))
139 ;; copied and modified from set-auto-mode in /usr/share/emacs/21.2/lisp/files.el
140 ;; (I don't want to set the mode actually. Sigh...)
141 (howm-dont-warn-free-variable auto-mode-interpreter-regexp)
142 (defvar howm-auto-mode-interpreter-regexp
143 (howm-if-unbound auto-mode-interpreter-regexp
144 ;; xemacs doesn't have it.
145 "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)"))
146 (defun howm-auto-mode (&optional file-name)
147 "Major mode appropriate for current buffer.
148 This checks for a -*- mode tag in the buffer's text,
149 compares the filename against the entries in `auto-mode-alist',
150 or checks the interpreter that runs this file against
151 `interpreter-mode-alist'.
153 It does not check for the `mode:' local variable in the
154 Local Variables section of the file; for that, use `hack-local-variables'.
156 If `enable-local-variables' is nil, this function does not check for a
159 This function merely returns the mode; it does not set the mode.
161 ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*-
162 (let (beg end done modes ans)
164 (goto-char (point-min))
165 (skip-chars-forward " \t\n")
166 (and enable-local-variables
167 ;; Don't look for -*- if this file name matches any
168 ;; of the regexps in inhibit-first-line-modes-regexps.
169 (let ((temp (howm-if-unbound inhibit-first-line-modes-regexps
170 inhibit-local-variables-regexps))
171 (name (file-name-sans-versions (or file-name ""))))
172 (while (let ((sufs (howm-if-unbound inhibit-first-line-modes-suffixes
173 inhibit-local-variables-suffixes)))
174 (while (and sufs (not (string-match (car sufs) name)))
175 (setq sufs (cdr sufs)))
177 (setq name (substring name 0 (match-beginning 0))))
179 (not (string-match (car temp) name)))
180 (setq temp (cdr temp)))
182 (search-forward "-*-" (save-excursion
183 ;; If the file begins with "#!"
184 ;; (exec interpreter magic), look
185 ;; for mode frobs in the first two
186 ;; lines. You cannot necessarily
187 ;; put them in the first line of
188 ;; such a file without screwing up
189 ;; the interpreter invocation.
190 (end-of-line (and (looking-at "^#!") 2))
193 (skip-chars-forward " \t")
195 (search-forward "-*-"
196 (save-excursion (end-of-line) (point))
200 (skip-chars-backward " \t")
203 (if (save-excursion (search-forward ":" end t))
204 ;; Find all specifications for the `mode:' variable
205 ;; and execute them left to right.
206 (while (let ((case-fold-search t))
207 (or (and (looking-at "mode:")
208 (goto-char (match-end 0)))
209 (re-search-forward "[ \t;]mode:" end t)))
210 (skip-chars-forward " \t")
212 (if (search-forward ";" end t)
215 (skip-chars-backward " \t")
216 (push (intern (concat (downcase (buffer-substring beg (point))) "-mode"))
218 ;; Simple -*-MODE-*- case.
219 (push (intern (concat (downcase (buffer-substring beg end))
222 ;; If we found modes to use, set done.
223 (dolist (mode (nreverse modes))
224 (when (functionp mode)
227 ;; If we didn't find a mode from a -*- line, try using the file name.
228 (if (and (not done) file-name)
229 (let ((name file-name)
231 ;; Remove backup-suffixes from file name.
232 (setq name (file-name-sans-versions name))
234 (setq keep-going nil)
235 (let ((alist auto-mode-alist)
237 ;; Find first matching alist entry.
238 (let ((case-fold-search
239 (memq system-type '(vax-vms windows-nt))))
240 (while (and (not mode) alist)
241 (if (string-match (car (car alist)) name)
242 (if (and (consp (cdr (car alist)))
244 (setq mode (car (cdr (car alist)))
245 name (substring name 0 (match-beginning 0))
247 (setq mode (cdr (car alist))
249 (setq alist (cdr alist))))
252 ;; If we can't deduce a mode from the file name,
253 ;; look for an interpreter specified in the first line.
254 ;; As a special case, allow for things like "#!/bin/env perl",
255 ;; which finds the interpreter anywhere in $PATH.
258 (goto-char (point-min))
259 (if (looking-at howm-auto-mode-interpreter-regexp)
263 ;; Map interpreter name to a mode.
264 (setq elt (assoc (file-name-nondirectory interpreter)
265 interpreter-mode-alist))
267 (setq ans (cdr elt)))))))))
271 ;; copied from /usr/share/emacs/21.2/lisp/subr.el
272 ;; for emacs20 and xemacs
273 (when (not (fboundp 'replace-regexp-in-string))
274 (defun replace-regexp-in-string (regexp rep string &optional
275 fixedcase literal subexp start)
276 "Replace all matches for REGEXP with REP in STRING.
278 Return a new string containing the replacements.
280 Optional arguments FIXEDCASE, LITERAL and SUBEXP are like the
281 arguments with the same names of function `replace-match'. If START
282 is non-nil, start replacements at that index in STRING.
284 REP is either a string used as the NEWTEXT arg of `replace-match' or a
285 function. If it is a function it is applied to each match to generate
286 the replacement passed to `replace-match'; the match-data at this
287 point are such that match 0 is the function's argument.
289 To replace only the first match (if any), make REGEXP match up to \\'
290 and replace a sub-expression, e.g.
291 (replace-regexp-in-string \"\\(foo\\).*\\'\" \"bar\" \" foo foo\" nil nil 1)
295 ;; To avoid excessive consing from multiple matches in long strings,
296 ;; don't just call `replace-match' continually. Walk down the
297 ;; string looking for matches of REGEXP and building up a (reversed)
298 ;; list MATCHES. This comprises segments of STRING which weren't
299 ;; matched interspersed with replacements for segments that were.
300 ;; [For a `large' number of replacments it's more efficient to
301 ;; operate in a temporary buffer; we can't tell from the function's
302 ;; args whether to choose the buffer-based implementation, though it
303 ;; might be reasonable to do so for long enough STRING.]
304 (let ((l (length string))
308 (while (and (< start l) (string-match regexp string start))
309 (setq mb (match-beginning 0)
311 ;; If we matched the empty string, make sure we advance by one char
312 (when (= me mb) (setq me (min l (1+ mb))))
313 ;; Generate a replacement for the matched substring.
314 ;; Operate only on the substring to minimize string consing.
315 ;; Set up match data for the substring for replacement;
316 ;; presumably this is likely to be faster than munging the
317 ;; match data directly in Lisp.
318 (string-match regexp (setq str (substring string mb me)))
320 (cons (replace-match (if (stringp rep)
322 (funcall rep (match-string 0 str)))
323 fixedcase literal str subexp)
324 (cons (substring string start mb) ; unmatched prefix
327 ;; Reconstruct a string from the pieces.
328 (setq matches (cons (substring string start l) matches)) ; leftover
329 (apply #'concat (nreverse matches)))))
332 (howm-defvar-risky howm-kill-all-enable-force nil)
333 (defun howm-kill-all (&optional force-p)
334 "Kill all buffers which is howm-mode and unmodified."
336 (let ((anyway (and force-p howm-kill-all-enable-force)))
338 (yes-or-no-p "Discard all unsaved changes on howm-mode buffers? ")
339 (y-or-n-p "Kill all howm-mode buffers? "))
340 (when (eq major-mode 'howm-view-summary-mode)
341 (howm-view-restore-window-configuration))
343 (when (howm-buffer-p b)
346 (set-buffer-modified-p nil)) ;; dangerous!
347 (when (not (buffer-modified-p b))
352 (defun howm-toggle-buffer ()
355 (howm-switch-to-nonhowm-buffer)
356 (howm-switch-to-howm-buffer)))
357 (defun howm-switch-to-howm-buffer ()
359 (let ((b (howm-find-buffer #'howm-buffer-p)))
363 (defun howm-switch-to-nonhowm-buffer ()
365 (switch-to-buffer (or (howm-find-buffer #'(lambda (b)
366 (not (howm-buffer-p b))))
367 (error "No nonhowm buffer"))))
369 (defun howm-find-buffer (pred)
372 (cond ((howm-internal-buffer-p b) nil) ;; skip
373 ((funcall pred b) (throw :found b))
378 (defun howm-internal-buffer-p (buf)
379 (string= (substring (buffer-name buf) 0 1) " "))
381 (defun howm-buffer-p (&optional buf)
382 (let* ((indep-dirs (cons nil *howm-independent-directories*))
383 (keyword-bufs (mapcar
385 (let ((default-directory (or d default-directory)))
386 (howm-keyword-buffer)))
388 (with-current-buffer (or buf (current-buffer))
391 '(howm-view-summary-mode
392 howm-view-contents-mode))
393 (member buf keyword-bufs)))))
395 (defun howm-mode-add-font-lock ()
396 (cheat-font-lock-append-keywords (howm-mode-add-font-lock-internal)))
398 (defun howm-mode-add-font-lock-internal ()
400 `(,@howm-user-font-lock-keywords
401 (,howm-view-title-regexp
402 (0 howm-mode-title-face prepend))
403 (,howm-keyword-regexp
404 (,howm-keyword-regexp-hilit-pos howm-mode-keyword-face prepend))
406 (,howm-ref-regexp-hilit-pos howm-mode-ref-face prepend))
408 (,howm-wiki-regexp-pos howm-mode-wiki-face prepend))
411 ;;; unofficial. may be removed if no one needs them.
413 (defun howm-show-buffer-as-howm ()
415 (let* ((name (buffer-name))
417 (s (buffer-substring-no-properties (point-min) (point-max)))
418 (b (get-buffer-create (format "*howm[%s]*" name))))
420 (howm-rewrite-read-only-buffer
423 (howm-initialize-buffer))
425 (switch-to-buffer b)))
429 (defun howm-narrow-to-memo ()
431 (apply #'narrow-to-region (howm-view-paragraph-region t)))
433 (defun howm-toggle-narrow ()
437 (howm-narrow-to-memo)))
439 (put 'howm-narrow-to-memo 'disabled t)
440 (put 'howm-toggle-narrow 'disabled t)
442 (defun howm-narrow-p ()
443 (let ((b (point-min))
447 (not (and (equal b (point-min))
448 (equal e (point-max)))))))
450 (defun howm-auto-narrow ()
451 (when (cond (*howm-view-item-privilege* nil)
452 ((eq howm-auto-narrow t) t)
453 (t (member (howm-command) howm-auto-narrow)))
454 (howm-narrow-to-memo)))
455 ;; (when (and (member (howm-command) howm-auto-narrow)
456 ;; (not *howm-view-item-privilege*))
458 ;;; select file for new memo by hand
460 (defun howm-create-interactively (&optional use-current-directory)
462 (find-file (read-file-name "Memo file: "
463 (if use-current-directory
466 (goto-char (point-max))
469 ;;; next/previous memo
471 (defmacro howm-save-narrowing (&rest body)
473 `(let ((narrowp (howm-narrow-p)))
480 (howm-narrow-to-memo)))))
482 (defun howm-next-memo (n)
485 (when (looking-at howm-view-title-regexp)
487 (re-search-forward howm-view-title-regexp nil nil n)))
489 (defun howm-previous-memo (n)
492 (when (not (looking-at howm-view-title-regexp))
494 (re-search-backward howm-view-title-regexp nil nil n)))
496 (defun howm-first-memo ()
499 (goto-char (point-min))))
501 (defun howm-last-memo ()
504 (goto-char (point-max)))
505 (re-search-backward howm-view-title-regexp))
509 (defvar howm-random-walk-buf nil "for internal use")
510 (defvar howm-random-walk-ro t "for internal use")
512 (defun howm-random-walk ()
514 (let ((orig-bufs (buffer-list))
515 (howm-history-file nil))
516 (while (let ((v (frame-visible-p (selected-frame))))
517 (and v (not (eq v 'icon))
518 (not (input-pending-p))))
520 (cond ((eq major-mode 'howm-view-summary-mode)
521 (howm-random-walk-summary))
523 (howm-random-walk-text))
526 (howm-random-walk-summary)))
528 (when (and (not (member b orig-bufs))
529 (null (get-buffer-window b)))
532 (sit-for howm-random-walk-wait))))
534 (defun howm-random-walk-summary ()
535 (let ((n (length (riffle-item-list))))
536 (goto-char (point-min))
537 (forward-line (random n))
538 (howm-view-summary-check)
539 (sit-for howm-random-walk-wait)
540 (howm-view-summary-open)))
542 (defun howm-random-walk-text ()
543 (let* ((ks (howm-keyword-for-goto))
544 (k (nth (random (length ks)) ks))
545 (d (- (point-max) (point-min))))
546 (goto-char (+ (point-min) (random d)))
547 (if (search-forward k nil t)
548 (goto-char (match-beginning 0))
549 (search-backward k nil t))
550 (sit-for howm-random-walk-wait)
551 (howm-keyword-search k)))
555 (defun howm-open-named-file ()
556 "Ask a file name and open it as howm note if it is under howm directory."
558 (let* ((item-dir (lambda (item) (file-name-directory (howm-item-name item))))
559 (dir (cond ((eq major-mode 'howm-view-summary-mode)
560 (funcall item-dir (howm-view-summary-current-item)))
561 ((eq major-mode 'howm-view-contents-mode)
562 (funcall item-dir (howm-view-contents-current-item)))
565 (fname (read-file-name "Howm file name: " dir)))
567 (if (file-exists-p fname)
570 (howm-insert-template "")
571 (howm-create-finish)))))
573 ;; imitation of remember.el
574 ;; http://www.emacswiki.org/cgi-bin/emacs-en/RememberMode
576 ;; shamelessly copied from http://newartisans.com/johnw/Emacs/remember.el
577 ;; (I cannot browse http://sacha.free.net.ph/notebook/emacs/dev today.)
579 (defvar howm-remember-wconf nil
581 (defvar howm-remember-buffer-name "*howm-remember*")
582 (defvar howm-remember-mode-hook nil)
583 (let ((m (make-sparse-keymap)))
584 (define-key m "\C-c\C-c" 'howm-remember-submit)
585 (define-key m "\C-c\C-k" 'howm-remember-discard)
586 (howm-defvar-risky howm-remember-mode-map m))
588 (defun howm-remember ()
589 "Add text to new note in howm."
591 (setq howm-remember-wconf (current-window-configuration))
592 (switch-to-buffer-other-window (get-buffer-create howm-remember-buffer-name))
595 `("Remember (%s) or discard (%s)."
596 ,@(mapcar (lambda (f)
598 (where-is-internal f howm-remember-mode-map t)))
599 '(howm-remember-submit howm-remember-discard)))))
601 (defun howm-remember-mode ()
602 "Major mode for `howm-remember'.
604 \\{howm-remember-mode-map}"
606 (kill-all-local-variables)
608 (use-local-map howm-remember-mode-map)
609 (setq major-mode 'howm-remember-mode
610 mode-name "HowmRemember")
611 (run-hooks 'howm-remember-mode-hook))
613 (defun howm-remember-submit ()
616 (let* ((title (howm-remember-get-title)) ;; has side effect
617 (s (buffer-substring-no-properties (point-min) (point-max))))
618 (set-window-configuration howm-remember-wconf)
619 (howm-create-file-with-title title)
622 (kill-buffer (current-buffer))))
623 (howm-remember-discard))
625 (defun howm-remember-get-title ()
626 (if (not howm-remember-first-line-to-title)
629 (goto-char (point-min))
631 (buffer-substring-no-properties (point-min)
634 (delete-region (point-min) (point))))))
636 (defun howm-remember-discard ()
638 (kill-buffer (current-buffer))
639 (set-window-configuration howm-remember-wconf))
643 ;; You can rename howm buffers based on their titles.
644 ;; Only buffer names in emacs are changed; file names are kept unchanged.
646 ;; Add the next lines to your .emacs if you like this feature.
647 ;; (add-hook 'howm-mode-hook 'howm-mode-set-buffer-name)
648 ;; (add-hook 'after-save-hook 'howm-mode-set-buffer-name)
650 ;; The original idea and code are given by Mielke-san (peter at exegenix.com).
651 ;; http://lists.sourceforge.jp/mailman/archives/howm-eng/2006/000020.html
654 (defvar howm-buffer-name-limit 20)
655 (defvar howm-buffer-name-total-limit howm-buffer-name-limit)
656 (defvar howm-buffer-name-format "%s"
657 "Buffer name format for `howm-mode-set-buffer-name'.
658 For example, buffer name is _ABC_ if title is ABC and the value of
659 this variable is \"_%s_\".")
661 (defun howm-truncate-string (string limit &optional dots-str)
662 "Truncate STRING if it is longer than LIMIT.
663 For example, \"1234567...\" is returned if string is \"123456789012\"
665 When DOTS-STR is non-nil, it is used instead of \"...\"."
666 (let ((dots (or dots-str "...")))
667 (when (> (length dots) limit)
668 (setq dots (substring dots 0 limit)))
669 (if (> (length string) limit)
670 (concat (substring string 0 (- limit (length dots)))
674 (defun howm-set-buffer-name-from-title (checker title-regexp title-regexp-pos)
675 "Set the buffer name to the title(s) of the file."
676 (when (funcall checker)
680 (while (re-search-forward title-regexp nil t)
682 (cons (match-string-no-properties title-regexp-pos)
684 (let ((name0 (mapconcat
686 (howm-truncate-string s howm-buffer-name-limit))
687 (reverse (cl-remove-if (lambda (s) (string= s ""))
690 (when (not (string= name0 "")) ;; exclude "no title" case
691 (let ((name (format howm-buffer-name-format
692 (howm-truncate-string
694 howm-buffer-name-total-limit))))
695 (rename-buffer name t))))))))
697 (defun howm-mode-set-buffer-name ()
698 (howm-set-buffer-name-from-title (lambda ()
699 (and howm-mode (buffer-file-name)))
700 howm-view-title-regexp
701 howm-view-title-regexp-pos))
703 ;; memoize: used in howm-bayesian-set
705 (defun howm-memoize-put (fname value)
706 (put fname 'howm-memoize value))
707 (defun howm-memoize-get (fname)
708 (get fname 'howm-memoize))
710 (defun howm-memoize-call (fname func args)
711 (let* ((p (assoc args (howm-memoize-get fname))))
714 ;; (message "hit %s" p)
716 (let ((r (apply func args)))
717 ;; We need to get it again because func can change memory.
718 (howm-memoize-put fname `((,args . ,r) ,@(howm-memoize-get fname)))
721 (defun howm-memoize-reset (fname)
722 (howm-memoize-put fname nil))
724 (defmacro howm-defun-memoize (fname args &rest body)
727 (howm-memoize-reset ',fname)
729 "Function generated by `howm-defun-memoize'"
730 (howm-memoize-call ',fname (lambda ,args ,@body) (list ,@args)))))
733 ;; (howm-memoize-reset 'fib)
734 ;; (howm-defun-memoize fib (n) (if (<= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))
736 ;; (howm-memoize-get 'fib)
740 ;; "M-x howm-bayesian-set RET lisp scheme haskell RET" to estimate
741 ;; related keywords with lisp, scheme, and haskell.
742 ;; If you are lucky, you may find ruby, elisp, gauche, etc.
743 ;; in estimated candidates.
746 ;; Zoubin Ghahramani and Katherine Heller: "Bayesian Sets",
747 ;; Advances in Neural Information Processing Systems,
748 ;; Vol. 18, pp. 435-442, MIT Press, 2006.
749 ;; http://books.nips.cc/nips18.html
750 ;; http://books.nips.cc/papers/files/nips18/NIPS2005_0712.pdf
752 (defun howm-bset-nodup (f &rest args)
753 (cl-remove-duplicates (apply f args) :test #'equal))
754 (defun howm-bset-mapcar (func lis)
755 (howm-bset-nodup #'mapcar func lis))
756 (defun howm-bset-mapcan (func lis)
757 (howm-bset-nodup (lambda (f z) (apply #'append (mapcar f z)))
760 (defun howm-bset-message (&rest args)
761 (let (message-log-max) ;; prevent it from being logged
762 (apply #'message args)))
764 (defun howm-bset-matched-files (query)
765 ;; (howm-bset-message "Finding files for query (%s)..." query)
766 (howm-bset-mapcar #'howm-item-name
767 (howm-view-search-folder-items query (howm-folder)
770 (howm-defun-memoize howm-bset-keywords-in-file* (file keyword-list)
771 ;; (howm-bset-message "Finding keywords in file (%s)..." file)
773 (insert-file-contents file)
774 (howm-keyword-for-goto keyword-list)))
776 (defun howm-bset-keywords-in-file (file)
777 (howm-bset-keywords-in-file* file nil))
779 (defun howm-bset-candidate-keywords (query-list)
780 ;; (howm-bset-message "Collecting keywords...")
781 (let ((files (howm-bset-mapcan #'howm-bset-matched-files
783 (howm-bset-mapcan (lambda (f)
784 (howm-bset-message "Collecting keywords in file (%s)..."
786 (howm-bset-keywords-in-file f))
789 (howm-defun-memoize howm-bset-file-score (file query-list
790 coef number-of-all-keywords)
791 ;; (howm-bset-message "Scoring file (%s)..." file)
792 (let* ((m (/ (length (howm-bset-keywords-in-file file))
793 (float number-of-all-keywords)))
796 (s (length (howm-bset-keywords-in-file* file query-list)))
798 (b2 (+ b (- (length query-list) s))))
799 ;; log{(a2/a) * (b/b2)}
800 (- (- (log a2) (log a)) (- (log b2) (log b)))))
802 (howm-defun-memoize howm-bset-keyword-score (keyword query-list
804 number-of-all-keywords)
805 (howm-bset-message "Scoring keyword (%s)..." keyword)
807 (mapcar (lambda (file)
808 (howm-bset-file-score file query-list coef
809 number-of-all-keywords))
810 (howm-bset-matched-files keyword))))
812 (defun howm-bset-reset ()
813 (mapc #'howm-memoize-reset '(howm-bset-file-score
814 howm-bset-keyword-score
815 howm-bset-keywords-in-file*)))
817 (defun howm-bset (query-list)
820 (let ((n (length (howm-keyword-list)))
821 (c 2.0)) ;; heuristic value
822 (sort (copy-sequence (howm-bset-candidate-keywords query-list))
826 (howm-bset-keyword-score k query-list c n))
830 (defun howm-bayesian-set (query-str)
831 (interactive "sQueries: ")
832 (switch-to-buffer (get-buffer-create "*howm-bayesian-set*"))
833 (howm-rewrite-read-only-buffer
834 (insert (mapconcat #'identity
835 (howm-bset (split-string query-str))
838 (goto-char (point-min))
839 (howm-bset-message "Done."))
841 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
844 ;; xemacs: add-to-list doesn't have APPEND
845 ;; (add-to-list 'auto-mode-alist '("\\.howm$" . text-mode) t)
846 (setq auto-mode-alist (append auto-mode-alist
847 (list '("\\.howm$" . text-mode))))
849 ;; xyzzy doesn't have eval-after-load.
850 ;; It will be useless anyway.
851 (when (not (fboundp 'eval-after-load))
852 (defun eval-after-load (file form)
855 ;; xemacs canna doesn't use minor-mode. [2004-01-30]
856 (defvar action-lock-mode-before-canna nil)
857 (make-variable-buffer-local 'action-lock-mode-before-canna)
858 (defadvice canna:enter-canna-mode (around action-lock-fix activate)
859 (setq action-lock-mode-before-canna action-lock-mode)
860 (setq action-lock-mode nil)
862 (defadvice canna:quit-canna-mode (around action-lock-fix activate)
863 (setq action-lock-mode action-lock-mode-before-canna)
866 ;; for mcomplete.el [2003-12-17]
867 ;; http://homepage1.nifty.com/bmonkey/emacs/elisp/mcomplete.el
868 ;; error when this-command is (lambda () (interactive) ...)
869 (defadvice mcomplete-p (around symbol-check activate)
870 (and (symbolp this-command)
873 ;; for auto-save-buffers.el [2004-01-10]
874 ;; http://www.namazu.org/~satoru/auto-save/
875 ;; http://homepage3.nifty.com/oatu/emacs/misc.html
876 ;; http://www.bookshelf.jp/cgi-bin/goto.cgi?file=meadow&node=auto%20save
877 (defvar howm-auto-save-buffers-disposed nil)
878 (howm-dont-warn-free-variable auto-save-buffers-regexp)
879 (howm-dont-warn-free-variable auto-save-reject-buffers-regexp)
880 (defun howm-auto-save-buffers-p ()
881 (let ((f (howm-file-name)))
882 (and (if (boundp 'auto-save-buffers-regexp)
883 (string-match auto-save-buffers-regexp f)
885 (if (boundp 'auto-save-reject-buffers-regexp)
886 (not (string-match auto-save-reject-buffers-regexp f))
888 (defun howm-auto-save-buffers-dispose ()
889 (setq howm-menu-refresh-after-save nil)
890 (setq howm-refresh-after-save nil)
891 (setq howm-auto-save-buffers-disposed t)
892 (message "howm: Automatic refresh is disabled when auto-save-buffers is called."))
893 (defadvice auto-save-buffers (around howm-dispose activate)
894 (if (or howm-auto-save-buffers-disposed
895 (not (howm-auto-save-buffers-p)))
897 (howm-auto-save-buffers-dispose)))
899 ;; howm on ChangeLog Memo
900 (defun howm-setup-change-log ()
901 (setq howm-keyword-format "\t* %s")
902 (setq howm-keyword-regexp "^\t\\(\\*\\)[ \t]+\\([^:\r\n]+\\)")
903 (setq howm-keyword-regexp-hilit-pos 1) ;; ¡Ö´ØÏ¢¥¡¼¥ï¡¼¥É¡×ÍÑ
904 (setq howm-keyword-regexp-pos 2)
905 (setq howm-view-title-regexp "^$")
906 (setq howm-view-title-regexp-pos 0)
907 (setq howm-view-title-regexp-grep 'sorry-not-yet)
908 (setq howm-use-color nil)
909 (setq howm-menu-top nil)
910 (defadvice howm-exclude-p (around change-log (filename) activate)
911 (setq ad-return-value
912 (not (find-if (lambda (dir)
913 (string= (howm-file-name)
914 (file-relative-name filename dir)))
915 (howm-search-path)))))
916 (defadvice howm-create-file-with-title (around change-log (title) activate)
918 (when (string-match howm-keyword-regexp title)
919 (setq title (match-string-no-properties howm-keyword-regexp-pos
922 (defadvice howm-create-file (around change-log
923 (&optional keep-cursor-p) activate)
924 (let* ((default (howm-file-name))
925 (file (expand-file-name default howm-directory))
926 (dir (file-name-directory file))
927 (buffer-file-name file)) ;; don't insert file name
928 (make-directory dir t)
929 (add-change-log-entry nil file)))
930 (add-hook 'change-log-mode-hook 'howm-mode)
933 ;; howm with ChangeLog Memo
934 (defvar howm-change-log-file-name "ChangeLog")
935 (defun howm-to-change-log ()
937 (let* ((title (howm-title-at-current-point))
938 (file (expand-file-name howm-change-log-file-name howm-directory))
939 ;; cheat add-change-log-entry
940 (buffer-file-name title)
941 (default-directory howm-directory))
942 (add-change-log-entry nil file)))
943 (defun howm-from-change-log ()
945 (let* ((title-regexp "^\t[*][ \t]*\\(.*\\)$")
947 (title (howm-title-at-current-point nil
948 title-regexp title-regexp-pos)))
949 (howm-create-file-with-title title)))
951 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
954 ;; Japanese is assumed at now.
957 "Show bug report template for howm."
961 (defun howm-set-lang ()
962 (set-language-environment "Japanese")
963 (set-default-coding-systems 'euc-jp)
964 (set-buffer-file-coding-system 'euc-jp-unix)
965 (set-terminal-coding-system 'euc-jp)
966 (set-keyboard-coding-system 'euc-jp)
969 (defun howm-compiled-p ()
970 (byte-code-function-p (symbol-function 'howm-compiled-p)))
971 (defun howm-make-file-p ()
973 (getenv "HOWM_MAKE")))
974 (defun howm-test-p ()
975 (getenv "HOWM_TEST"))
977 (defun howm-bug-report (&optional show-sym)
979 (let ((report-buf (format-time-string "howm-bug-report-%Y%m%d-%H%M%S"))
980 (template "sample/bug-report.txt"))
981 (switch-to-buffer report-buf)
982 (when (not (howm-buffer-empty-p))
983 (error "Buffer %s exists (and not empty)." report-buf))
984 (if (file-exists-p template)
985 (insert-file-contents template)
986 (insert "Please copy the following text to your bug report.\n\n"))
987 (goto-char (point-max))
989 (insert (format "%s: %s\n" (car sv) (cdr sv))))
991 ("howm" . ,(howm-version-long))
992 ,@(honest-report-version-assoc)
994 (when (eq howm-view-use-grep t)
996 (format "grep: %s - %s\n"
997 (cl-mapcan (lambda (d)
998 (let ((f (expand-file-name
999 howm-view-grep-command d)))
1000 (and (file-executable-p f)
1003 (car (howm-call-process "grep" '("--version"))))))
1005 (goto-char (point-max))
1006 (insert "\n(List of variables)\n")
1007 (insert (howm-symbols-desc)))
1008 (goto-char (point-min))))
1010 (defun howm-version-long ()
1011 (format "%s (compile: %s, make: %s, test: %s)"
1017 (defun howm-symbols-desc (&optional max-desc-len)
1018 (when (null max-desc-len)
1019 (setq max-desc-len 50))
1021 (mapcar (lambda (sym)
1023 (let ((v (format "%S" (symbol-value sym))))
1024 (when (and (numberp max-desc-len)
1025 (< max-desc-len (length v)))
1027 (let* ((tl (/ max-desc-len 4))
1028 (hd (- max-desc-len tl)))
1029 (concat (substring v 0 hd)
1031 (substring v (- tl))))))
1032 (format "%s: %s\n" (symbol-name sym) v))))
1033 (sort (howm-symbols)
1035 (string< (symbol-name x) (symbol-name y)))))))
1037 (defvar howm-required-features '(
1045 "List of features which are required for, and distributed with, howm itself.")
1047 (defun howm-prefix-names ()
1048 (mapcar #'symbol-name (cons 'howm howm-required-features)))
1050 (defun howm-symbols ()
1051 (let* ((reg (format "^%s" (regexp-opt (howm-prefix-names) t)))
1053 (mapatoms (lambda (s)
1054 (when (string-match reg (symbol-name s))
1055 (setq a (cons s a)))))
1060 (mapcar #'elp-instrument-package
1061 (howm-prefix-names)))
1063 (defvar howm-sample-directory (expand-file-name "sample/")
1065 (defun howm-bug-shot ()
1067 (let* ((version (concat "[howm] " (howm-version-long)))
1068 (init (and (howm-test-p)
1069 (let ((f (expand-file-name "dot.emacs"
1070 howm-sample-directory)))
1071 (and (file-readable-p f)
1073 (insert-file-contents f)
1074 (buffer-substring-no-properties (point-min)
1077 (concat version "\n\n[init]\n" init)
1079 (footer "--- your comment ---"))
1080 (honest-report header footer)
1081 (message "Please copy this buffer to your report.")))
1083 ;;; howm-misc.el ends here