]> code.delx.au - gnu-emacs-elpa/blob - packages/dict-tree/dict-tree.el
fe3fbe2fcbc7df33dc22333bb640c167aa0eeef6
[gnu-emacs-elpa] / packages / dict-tree / dict-tree.el
1 ;;; dict-tree.el --- Dictionary data structure package
2
3 ;; Copyright (C) 2004-2012 Free Software Foundation, Inc
4
5 ;; Author: Toby Cubitt <toby-predictive@dr-qubit.org>
6 ;; Version: 0.12.7
7 ;; Keywords: extensions, matching, data structures
8 ;; trie, tree, dictionary, completion, regexp
9 ;; Package-Requires: ((trie "0.2.5") (tNFA "0.1.1") (heap "0.3"))
10 ;; URL: http://www.dr-qubit.org/emacs.php
11
12 ;; This file is part of Emacs.
13 ;;
14 ;; GNU Emacs is free software: you can redistribute it and/or modify it under
15 ;; the terms of the GNU General Public License as published by the Free
16 ;; Software Foundation, either version 3 of the License, or (at your option)
17 ;; any later version.
18 ;;
19 ;; GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
20 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 ;; more details.
23 ;;
24 ;; You should have received a copy of the GNU General Public License along
25 ;; with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26
27
28 ;;; Commentary:
29 ;;
30 ;; A dictionary is used to store strings, along with arbitrary data associated
31 ;; with each string. As well as basic data insertion, manipulation and
32 ;; retrieval, a dictionary can perform prefix searches on those strings,
33 ;; retrieving all strings with a given prefix in either alphabetical or any
34 ;; other order (see the `dictree-complete' and `dictree-complete-ordered'
35 ;; functions), and is able to cache results in order to speed up those
36 ;; searches. The package also provides persistent storage of the data
37 ;; structures to files.
38 ;;
39 ;; You create a dictionary using `dictree-create', add entries to it using
40 ;; `dictree-insert', lookup entries using `dictree-lookup', find completions
41 ;; of sequences using `dictree-complete', find completions and sort them in
42 ;; any order you speficy using `dictree-complete-ordered', map over it using
43 ;; `dictree-map' and `dictree-mapcar', save it to a file using `dictree-save'
44 ;; or `dictree-write', and load from file it using `dictree-load'. Various
45 ;; other useful functions are also provided.
46 ;;
47 ;; This package uses the trie package trie.el. the tagged NFA package tNFA.el,
48 ;; and the heap package heap.el.
49
50
51 ;;; Change Log:
52 ;;
53 ;; Version 0.12.7
54 ;; * create defstruct copier functions for dict-trees and meta-dict-trees
55 ;; * don't transform hash tables to alists when writing dictionaries if
56 ;; running in an Emacs version that supports print-readable hash tables
57 ;; * simplified `dictree-write', `dictree--write-dict-code' and
58 ;; `dictree--write-meta-dict-code'
59 ;;
60 ;; Version 0.12.6
61 ;; * replaced obsolete `interactive-p' with `called-interactively-p'
62 ;;
63 ;; Version 0.12.5
64 ;; * fixed default value handling in `read-dict'
65 ;;
66 ;; Version 0.12.4
67 ;; * minor bug-fix to `dictree--edebug-pretty-print' to print "nil" instead
68 ;; of "()"
69 ;; * modified `dictree-save-modified' to catch errors when saving
70 ;; dictionaries, and indicate failures via its return value
71 ;; * removed `dictree-save-modified' from `kill-emacs-hook' and added it
72 ;; instead to `kill-emacs-query-functions', so that dictionary save failures
73 ;; don't make it impossible to quit Emacs
74 ;; * fixed bug in `dictree--merge' that caused it to retain one too many list
75 ;; elements for non-null MAXNUM
76 ;; * fixed `dictree--update-cache', which previously never updated cached
77 ;; results for lists of prefixes in `dictree-complete' queries
78 ;; * fixed implementation of 'both cache policy
79 ;; * fixed bug in `read-dict' preventing completion on dictionary files
80 ;; in `load-path'
81 ;; * fixed bugs in synchronisation of regexp query caches, renaming
82 ;; `dictree--synchronise-query-cache' and
83 ;; `dictree--synchronise-ranked-query-cache' to
84 ;; `dictree--synchronise-completion-cache' and
85 ;; `dictree--synchronise-ranked-completion-cache', and creating separate
86 ;; `dictree--synchronise-regexp-cache' and
87 ;; `dictree--synchronise-ranked-regep-cache' functions to handle regexp
88 ;; query caches
89 ;;
90 ;; Version 0.12.3
91 ;; * bug-fix in `dictree--edebug-pretty-print'
92 ;;
93 ;; Version 0.12.2
94 ;; * bug-fix to DEFAULT argument handling in `read-dict'
95 ;;
96 ;; Version 0.12.1
97 ;; * added `edebug-prin1' and `edebug-prin1-to-string' advice to prevent
98 ;; edebug hanging whilst printing large dictionaries
99 ;;
100 ;; Version 0.12
101 ;; * complete rewrite using new trie.el library
102 ;;
103 ;; Note: version 0.11.1 dictionaries not compatible with version 0.12 and
104 ;; above
105 ;;
106 ;; Version 0.11.1
107 ;; * set and restore value of `byte-compile-disable-print-circle' instead of
108 ;; let-binding it, to avoid warnings when compiling
109 ;; * added `dictree-goto-line' macro to work around `goto-line' bug
110 ;;
111 ;; Version 0.11
112 ;; * modified `dictree-write' so that, by default, both compiled and
113 ;; uncompiled versions of dictionaries are created when writing dictionaries
114 ;; to file
115 ;; * fixed slow byte-compilation under Emacs 22
116 ;;
117 ;; Version 0.10.2
118 ;; * very minor changes to text of some messages
119 ;;
120 ;; Version 0.10.1
121 ;; * added optional DICTLIST argument to `read-dict', to allow completion from
122 ;; a restricted set of dictionaries
123 ;;
124 ;; Version 0.10
125 ;; * finally wrote a `dictree-delete' function!
126 ;;
127 ;; Version 0.9.1
128 ;; * fixed bug in `dictree-dump-words-to-buffer' (thanks to Dan Pomohaci for
129 ;; reporting it)
130 ;; * replaced "word" with "key" in function arguments and docstrings, since
131 ;; keys don't have to be words
132 ;; * removed "words" from dump functions' names, added TYPE argument in line
133 ;; with other functions, and made them non-interactive
134 ;; * added COMPARE-FUNCTION argument to `dictree-create', which defaults to
135 ;; subtraction as before
136 ;; * `dictree-read-line' reads the keys with `read', and no longer evals the
137 ;; data as this fails for simple, useful cases (e.g. constant lists)
138 ;;
139 ;; Version 0.9
140 ;; * added meta-dictionary functionality
141 ;; * dictionary data can now be referenced by any sequence type, not just
142 ;; strings * removed cl dependency
143 ;;
144 ;; Note: version 0.8 dictionaries not compatible with version 0.9 and above
145 ;;
146 ;; Version 0.8.4
147 ;; * fixed small bug in `read-dict'
148 ;;
149 ;; Version 0.8.3
150 ;; * fixed internal function and macro names
151 ;; * changed naming prefix from dict- to dictree- to avoid conflicts
152 ;; * `dict-write' now unloads old name and reloads new
153 ;;
154 ;; Version 0.8.2
155 ;; * added more commentary
156 ;;
157 ;; Version 0.8.1
158 ;; * fixed nasty bug in `dict-map' and `dict-mapcar' caused by dynamic scoping
159 ;;
160 ;; Version 0.8
161 ;; * changed `dict-map(car)' into functions and made them work with
162 ;; lookup-only dicts
163 ;; * `dict-insert' now returns the new data value
164 ;; * rewrote cache data structures: data is now wrapped inside a cons cell, so
165 ;; that cache entries can point to it instead of duplicating it. This fixes
166 ;; some caching bugs and makes updating cached data when inserting words
167 ;; much faster
168 ;; * dictionaries (but not lookup-only) can now associate two pieces of data
169 ;; with each word: normal data, used to rank words returned by
170 ;; `dict-complete-ordered', and meta-data, not used for ranking
171 ;; * modified functions to work with new caching and meta-data, and added
172 ;; `dict-set-meta-data' and `dict-lookup-meta-data'
173 ;; * renamed to `dict-tree' to help avoid conflicts with other packages
174 ;;
175 ;; Version 0.7
176 ;; * added `dict-mapcar' macro
177 ;;
178 ;; Version 0.6.2
179 ;; * minor bug fixes
180 ;;
181 ;; Version 0.6.1
182 ;; * minor bug fixes
183 ;;
184 ;; Version 0.6
185 ;; * added dict-size function
186 ;; * added dict-dump-words-to-buffer function
187 ;; * dictionaries now set their names and filenames by doing a library search
188 ;; for themselves when loaded using require
189 ;; * added `read-dict' minibuffer completion function
190 ;; * interactive commands that read a dictionary name now provide completion
191 ;;
192 ;; Version 0.5
193 ;; * added dict-dump-words-to-file function
194 ;;
195 ;; Version 0.4
196 ;; * fixed bug in dict-read-line
197 ;;
198 ;; Version 0.3
199 ;; * added dict-map function
200 ;;
201 ;; Version 0.2
202 ;; * added dictionary autosave flag and related functions;
203 ;; * fixed bug preventing dict caches being loaded properly;
204 ;; * explicitly require cl.el;
205 ;;
206 ;; Note: version 0.1 dictionaries not compatible with version 0.2 and above
207 ;;
208 ;; Version 0.1
209 ;; * initial release
210
211
212
213 ;;; Code:
214
215 (eval-when-compile (require 'cl))
216 (require 'trie)
217 (require 'tNFA)
218 (require 'bytecomp)
219
220
221
222 ;;; ================================================================
223 ;;; Replacements for CL and Elisp functions
224
225 ;; copied from cl-extra.el
226 (defun dictree--subseq (seq start &optional end)
227 "Return the subsequence of SEQ from START to END.
228 If END is omitted, it defaults to the length of the sequence.
229 If START or END is negative, it counts from the end."
230 (if (stringp seq) (substring seq start end)
231 (let (len)
232 (and end (< end 0) (setq end (+ end (setq len (length seq)))))
233 (when (< start 0)
234 (setq start (+ start (or len (setq len (length seq))))))
235 (cond ((listp seq)
236 (if (> start 0) (setq seq (nthcdr start seq)))
237 (if end
238 (let ((res nil))
239 (while (>= (setq end (1- end)) start)
240 (push (pop seq) res))
241 (nreverse res))
242 (copy-sequence seq)))
243 (t
244 (or end (setq end (or len (length seq))))
245 (let ((res (make-vector (max (- end start) 0) nil))
246 (i 0))
247 (while (< start end)
248 (aset res i (aref seq start))
249 (setq i (1+ i) start (1+ start)))
250 res))))))
251
252
253
254 ;; `goto-line' without messing around with mark and messages
255 ;; Note: This is a bug in simple.el. There's clearly a place for
256 ;; non-interactive calls to goto-line from Lisp code, and there's
257 ;; no warning against doing this in the documentation. Yet
258 ;; goto-line *always* calls push-mark, which usually *shouldn't*
259 ;; be invoked by Lisp programs, as its docstring warns.
260 (defmacro dictree--goto-line (line)
261 "Goto line LINE, counting from line 1 at beginning of buffer."
262 `(progn
263 (goto-char 1)
264 (if (eq selective-display t)
265 (re-search-forward "[\n\C-m]" nil 'end (1- ,line))
266 (forward-line (1- ,line)))))
267
268
269
270 ;;; ====================================================================
271 ;;; Internal functions and variables for use in the dictionary package
272
273 (defvar dictree-loaded-list nil
274 "Stores list of loaded dictionaries.")
275
276
277 ;; ----------------------------------------------------------------
278 ;; Dictionary data cell structures
279
280 ;; Note: It would be more elegant to use a defstruct for the data cells,
281 ;; but the problem is that the resulting setf in
282 ;; `dictree--wrap-insfun' won't get expanded into the cell-data
283 ;; accessor function at compile-time because it's burried inside a
284 ;; backquote construct. Not only is it inelegant to have to expand
285 ;; macros at run-time whenever `dictree--wrap-insfun' is called,
286 ;; but it also requires the 'cl-macs package to be loaded at
287 ;; run-time rather than just at compile-time. We could use
288 ;; `lexical-let' instead, but it doesn't seem worth it here.
289
290 ;; wrap data in a cons cell
291 (defalias 'dictree--cell-create 'cons) ; INTERNAL USE ONLY
292
293 ;; get data component from data cons cell
294 (defalias 'dictree--cell-data 'car) ; INTERNAL USE ONLY
295
296 ;; get property list component from data cons cell
297 (defalias 'dictree--cell-plist 'cdr) ; INTERNAL USE ONLY
298
299 ;; set data component of data cons cell
300 (defalias 'dictree--cell-set-data 'setcar) ; INTERNAL USE ONLY
301
302 ;; set property list component of data cons cell
303 (defalias 'dictree--cell-set-plist 'setcdr) ; INTERNAL USE ONLY
304
305 ;; define setf methods so we can use setf abstraction wherever possible
306 (defsetf dictree--cell-data dictree--cell-set-data)
307 (defsetf dictree--cell-plist dictree--cell-set-plist)
308
309
310 ;; ----------------------------------------------------------------
311 ;; Dictionary cache entry structures
312
313 ;; Note: We *could* us a defstruct for the cache entries, but for
314 ;; something this simple it doesn't seem worth it, especially
315 ;; given that we're using the defalias approach anyway for the
316 ;; data cells (above).
317
318 ;; Construct and return a completion cache entry
319 (defalias 'dictree--cache-create 'cons) ; INTERNAL USE ONLY
320
321 ;; Return the completions list for cache entry CACHE
322 (defalias 'dictree--cache-results 'car) ; INTERNAL USE ONLY
323
324 ;; Return the max number of completions returned for cache entry CACHE
325 (defalias 'dictree--cache-maxnum 'cdr) ; INTERNAL USE ONLY
326
327 ;; Set the completions list for cache entry CACHE
328 (defalias 'dictree--cache-set-completions 'setcar) ; INTERNAL USE ONLY
329
330 ;; Set the completions list for cache entry CACHE
331 (defalias 'dictree--cache-set-maxnum 'setcdr) ; INTERNAL USE ONLY
332
333
334 ;; ----------------------------------------------------------------
335 ;; Wrapping functions
336
337 (defun dictree--wrap-insfun (insfun) ; INTERNAL USE ONLY
338 ;; return wrapped insfun to deal with data wrapping
339 `(lambda (new old)
340 (dictree--cell-set-data old (,insfun (dictree--cell-data new)
341 (dictree--cell-data old)))
342 old))
343
344 (defun dictree--wrap-rankfun (rankfun) ; INTERNAL USE ONLY
345 ;; return wrapped rankfun to deal with data wrapping
346 `(lambda (a b)
347 (,rankfun (cons (car a) (dictree--cell-data (cdr a)))
348 (cons (car b) (dictree--cell-data (cdr b))))))
349
350 (defun dictree--wrap-combfun (combfun) ; INTERNAL USE ONLY
351 ;; return wrapped combfun to deal with data wrapping
352 `(lambda (cell1 cell2)
353 (cons (,combfun (dictree--cell-data cell1)
354 (dictree--cell-data cell2))
355 (append (dictree--cell-plist cell1)
356 (dictree--cell-plist cell2)))))
357
358 (defun dictree--wrap-filter (filter) ; INTERNAL USE ONLY
359 ;; return wrapped filter function to deal with data wrapping
360 `(lambda (key data) (,filter key (dictree--cell-data data))))
361
362 (defun dictree--wrap-resultfun (resultfun) ; INTERNAL USE ONLY
363 ;; return wrapped result function to deal with data wrapping
364 `(lambda (res) (,resultfun (car res) (dictree--cell-data (cdr res)))))
365
366
367
368 ;; ----------------------------------------------------------------
369 ;; The dictionary data structures
370
371 (defstruct
372 (dictree-
373 :named
374 (:constructor nil)
375 (:constructor dictree--create
376 (&optional
377 filename
378 (name (and filename
379 (file-name-sans-extension
380 (file-name-nondirectory filename))))
381 autosave
382 unlisted
383 (comparison-function '<)
384 (insert-function (lambda (a b) a))
385 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
386 (cache-policy 'time)
387 (cache-update-policy 'synchronize)
388 lookup-cache-threshold
389 complete-cache-threshold
390 complete-ranked-cache-threshold
391 regexp-cache-threshold
392 regexp-ranked-cache-threshold
393 key-savefun key-loadfun
394 data-savefun data-loadfun
395 plist-savefun plist-loadfun
396 trie-type
397 &aux
398 (modified nil)
399 (trie (trie-create comparison-function))
400 (insfun (dictree--wrap-insfun insert-function))
401 (rankfun (dictree--wrap-rankfun rank-function))
402 (lookup-cache
403 (if lookup-cache-threshold
404 (make-hash-table :test 'equal)
405 nil))
406 (complete-cache
407 (if complete-cache-threshold
408 (make-hash-table :test 'equal)
409 nil))
410 (complete-ranked-cache
411 (if complete-ranked-cache-threshold
412 (make-hash-table :test 'equal)
413 nil))
414 (regexp-cache
415 (if regexp-cache-threshold
416 (make-hash-table :test 'equal)
417 nil))
418 (regexp-ranked-cache
419 (if regexp-ranked-cache-threshold
420 (make-hash-table :test 'equal)
421 nil))
422 (metadict-list nil)
423 ))
424 (:constructor dictree--create-custom
425 (&optional
426 filename
427 (name (and filename
428 (file-name-sans-extension
429 (file-name-nondirectory filename))))
430 autosave
431 unlisted
432 (comparison-function '<)
433 (insert-function (lambda (a b) a))
434 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
435 (cache-policy 'time)
436 (cache-update-policy 'synchronize)
437 lookup-cache-threshold
438 complete-cache-threshold
439 complete-ranked-cache-threshold
440 regexp-cache-threshold
441 regexp-ranked-cache-threshold
442 key-savefun key-loadfun
443 data-savefun data-loadfun
444 plist-savefun plist-loadfun
445 &key
446 createfun insertfun deletefun
447 lookupfun mapfun emptyfun
448 stack-createfun stack-popfun stack-emptyfun
449 transform-for-print transform-from-read
450 &aux
451 (modified nil)
452 (trie (trie-create-custom
453 comparison-function
454 :createfun createfun
455 :insertfun insertfun
456 :deletefun deletefun
457 :lookupfun lookupfun
458 :mapfun mapfun
459 :emptyfun emptyfun
460 :stack-createfun stack-createfun
461 :stack-popfun stack-popfun
462 :stack-emptyfun stack-emptyfun
463 :transform-for-print transform-for-print
464 :transform-from-read transform-from-read))
465 (insfun (dictree--wrap-insfun insert-function))
466 (rankfun (dictree--wrap-rankfun rank-function))
467 (lookup-cache
468 (if lookup-cache-threshold
469 (make-hash-table :test 'equal)
470 nil))
471 (complete-cache
472 (if complete-cache-threshold
473 (make-hash-table :test 'equal)
474 nil))
475 (complete-ranked-cache
476 (if complete-ranked-cache-threshold
477 (make-hash-table :test 'equal)
478 nil))
479 (regexp-cache
480 (if regexp-cache-threshold
481 (make-hash-table :test 'equal)
482 nil))
483 (regexp-ranked-cache
484 (if regexp-ranked-cache-threshold
485 (make-hash-table :test 'equal)
486 nil))
487 (metadict-list nil)
488 ))
489 (:copier dictree--copy))
490 name filename autosave modified
491 comparison-function insert-function insfun rank-function rankfun
492 cache-policy cache-update-policy
493 lookup-cache lookup-cache-threshold
494 complete-cache complete-cache-threshold
495 complete-ranked-cache complete-ranked-cache-threshold
496 regexp-cache regexp-cache-threshold
497 regexp-ranked-cache regexp-ranked-cache-threshold
498 key-savefun key-loadfun
499 data-savefun data-loadfun
500 plist-savefun plist-loadfun
501 trie meta-dict-list)
502
503
504 (defstruct
505 (dictree--meta-dict
506 :named
507 (:constructor nil)
508 (:constructor dictree--meta-dict-create
509 (dictionary-list
510 &optional
511 filename
512 (name (file-name-sans-extension
513 (file-name-nondirectory filename)))
514 autosave
515 unlisted
516 (combine-function '+)
517 (cache-policy 'time)
518 (cache-update-policy 'synchronize)
519 lookup-cache-threshold
520 complete-cache-threshold
521 complete-ranked-cache-threshold
522 regexp-cache-threshold
523 regexp-ranked-cache-threshold
524 &aux
525 (dictlist
526 (mapcar
527 (lambda (dic)
528 (cond
529 ((dictree-p dic) dic)
530 ((symbolp dic) (eval dic))
531 (t (error "Invalid object in DICTIONARY-LIST"))))
532 dictionary-list))
533 (combfun (dictree--wrap-combfun combine-function))
534 (lookup-cache
535 (if lookup-cache-threshold
536 (make-hash-table :test 'equal)
537 nil))
538 (complete-cache
539 (if complete-cache-threshold
540 (make-hash-table :test 'equal)
541 nil))
542 (complete-ranked-cache
543 (if complete-ranked-cache-threshold
544 (make-hash-table :test 'equal)
545 nil))
546 (regexp-cache
547 (if regexp-cache-threshold
548 (make-hash-table :test 'equal)
549 nil))
550 (regexp-ranked-cache
551 (if regexp-ranked-cache-threshold
552 (make-hash-table :test 'equal)
553 nil))
554 ))
555 (:copier dictree--meta-dict-copy))
556 name filename autosave modified
557 combine-function combfun
558 cache-policy cache-update-policy
559 lookup-cache lookup-cache-threshold
560 complete-cache complete-cache-threshold
561 complete-ranked-cache complete-ranked-cache-threshold
562 regexp-cache regexp-cache-threshold
563 regexp-ranked-cache regexp-ranked-cache-threshold
564 dictlist meta-dict-list)
565
566
567
568 ;; ----------------------------------------------------------------
569 ;; Miscelaneous internal functions and macros
570
571 (defun dictree--trielist (dict)
572 ;; Return a list of all the tries on which DICT is based. If DICT is a
573 ;; meta-dict, this recursively descends the hierarchy, gathering all
574 ;; the tries from the base dictionaries.
575 (let (accumulate)
576 (dictree--do-trielist dict)
577 accumulate))
578
579 (defun dictree--do-trielist (dict)
580 (declare (special accumulate))
581 (if (dictree-meta-dict-p dict)
582 (mapc 'dictree--do-trielist (dictree--meta-dict-dictlist dict))
583 (setq accumulate (cons (dictree--trie dict) accumulate))))
584
585
586 (defun dictree--merge (list1 list2 cmpfun &optional combfun maxnum)
587 ;; Destructively merge together sorted lists LIST1 and LIST2, sorting
588 ;; elements according to CMPFUN. For non-null MAXNUM, only the first
589 ;; MAXNUM are kept. For non-null COMBFUN, duplicate elements will be
590 ;; merged by passing the two elements as arguments to COMBFUN, and
591 ;; using the return value as the merged element.
592 (or (listp list1) (setq list1 (append list1 nil)))
593 (or (listp list2) (setq list2 (append list2 nil)))
594 (let (res (i 0))
595
596 ;; build up result list backwards
597 (while (and list1 list2 (or (null maxnum) (< (incf i) maxnum)))
598 ;; move smaller element to result list
599 (if (funcall cmpfun (car list1) (car list2))
600 (push (pop list1) res)
601 (if (funcall cmpfun (car list2) (car list1))
602 (push (pop list2) res)
603 ;; if elements are equal, merge them for non-null COMBFUN
604 (if combfun
605 (push (funcall combfun (pop list1) (pop list2))
606 res)
607 ;; otherwise, add both to result list, in order
608 (push (pop list1) res)
609 (push (pop list2) res)))))
610
611 ;; return result if we already have MAXNUM entries
612 (if (and maxnum (= i maxnum))
613 (nreverse res)
614 ;; otherwise, return result plus enough leftover entries to make
615 ;; up MAXNUM (only one of list1 or list2 will be non-nil)
616 (let (tmp)
617 (or (null maxnum)
618 (and (setq tmp (nthcdr (- maxnum i 1) list1))
619 (setcdr tmp nil))
620 (and (setq tmp (nthcdr (- maxnum i 1) list2))
621 (setcdr tmp nil)))
622 (nconc (nreverse res) list1 list2)))
623 ))
624
625
626 ;; (defun dictree--merge-sort (list sortfun &optional combfun)
627 ;; ;; Destructively sort LIST according to SORTFUN, combining
628 ;; ;; identical elements using COMBFUN if supplied.
629 ;; (dictree--do-merge-sort list (/ (length list) 2) sortfun combfun))
630
631
632 ;; (defun dictree--do-merge-sort (list1 len sortfun combfun)
633 ;; ;; Merge sort LIST according to SORTFUN, combining identical
634 ;; ;; elements using COMBFUN.
635 ;; (let* ((p (nthcdr (1- len) list1))
636 ;; (list2 (cdr p)))
637 ;; (setcdr p nil)
638 ;; (dictree--merge
639 ;; (dictree--do-merge-sort list1 (/ len 2) sortfun combfun)
640 ;; (dictree--do-merge-sort list2 (/ len 2) sortfun combfun)
641 ;; sortfun combfun)))
642
643
644
645
646 ;;; ================================================================
647 ;;; The (mostly) public functions which operate on dictionaries
648
649 ;;;###autoload
650 (defun make-dictree
651 (&optional
652 name filename autosave unlisted
653 comparison-function insert-function rank-function
654 cache-policy cache-update-policy
655 lookup-cache-threshold
656 complete-cache-threshold
657 complete-ranked-cache-threshold
658 regexp-cache-threshold
659 regexp-ranked-cache-threshold
660 key-savefun key-loadfun
661 data-savefun data-loadfun
662 plist-savefun plist-loadfun
663 trie-type)
664 "Create an empty dictionary and return it.
665
666 If NAME is supplied, the dictionary is stored in the variable
667 NAME. Defaults to FILENAME stripped of directory and
668 extension. (Regardless of the value of NAME, the dictionary will
669 be stored in the default variable name when it is reloaded from
670 file.)
671
672 FILENAME supplies a directory and file name to use when saving
673 the dictionary. If the AUTOSAVE flag is non-nil, then the
674 dictionary will automatically be saved to this file when it is
675 unloaded or when exiting Emacs.
676
677 If UNLISTED is non-nil, the dictionary will not be added to the
678 list of loaded dictionaries. Note that this disables autosaving.
679
680 COMPARE-FUNCTION sets the function used to compare elements of
681 the keys. It should take two arguments, A and B, both of the type
682 contained by the sequences used as keys \(e.g. if the keys will
683 be strings, the function will be passed two characters\). It
684 should return t if the first is \"less than\" the
685 second. Defaults to `<'.
686
687 INSERT-FUNCTION sets the function used to insert data into the
688 dictionary. It should take two arguments: the new data, and the
689 data already in the dictionary, and should return the data to
690 insert. Defaults to replacing any existing data with the new
691 data.
692
693 RANK-FUNCTION sets the function used to rank the results of
694 `dictree-complete'. It should take two arguments, each a cons
695 whose car is a dictree key (a sequence) and whose cdr is the data
696 associated with that key. It should return non-nil if the first
697 argument is \"better\" than the second, nil otherwise. It
698 defaults to \"lexical\" comparison of the keys, ignoring the data
699 \(which is not very useful, since an unranked `dictree-complete'
700 query already does this much more efficiently\).
701
702 CACHE-POLICY should be a symbol ('time, 'length, or 'both), which
703 determines which query operations are cached. The 'time setting
704 caches queries that take longer (in seconds) than the
705 corresponding CACHE-THRESHOLD value. The 'length setting caches
706 lookups of key sequences that are longer than
707 LOOKUP-CACHE-THRESHOLD value (since those are likely to be the
708 slower ones), and caches completions of prefixes that are shorter
709 than the corresponding CACHE-THRESHOLD (since those are likely to
710 be the slower ones in that case). The setting 'both requires both
711 conditions to be satisfied simultaneously. In this case,
712 CACHE-THRESHOLD must be a plist with properties :time and :length
713 specifying the corresponding cache thresholds.
714
715 CACHE-UPDATE-POLICY should be a symbol ('synchronize or 'delete),
716 which determines how the caches are updated when data is inserted
717 or deleted. The former updates tainted cache entries, which makes
718 queries faster but insertion and deletion slower, whereas the
719 latter deletes any tainted cache entries, which makes queries
720 slower but insertion and deletion faster.
721
722 The CACHE-THRESHOLD settings set the threshold for caching the
723 corresponding dictionary query (lookup, completion, ranked
724 completion). The meaning of these values depends on the setting
725 of CACHE-POLICY (see above).
726
727 All CACHE-THRESHOLD's default to nil. The values nil and t are
728 special. If a CACHE-THRESHOLD is set to nil, no caching is done
729 for that type of query. If it is t, everything is cached for that
730 type of query \(similar behaviour can be obtained by setting the
731 CACHE-THRESHOLD to 0, but it is better to use t\).
732
733 KEY-SAVEFUN, DATA-SAVEFUN and PLIST-SAVEFUN are functions used to
734 convert keys, data and property lists into lisp objects that have
735 a valid read syntax, for writing to file. DATA-SAVEFUN and
736 PLIST-SAVEFUN are used when saving the dictionary (see
737 `dictree-save' and `dictree-write'), and all three functions are
738 used when dumping the contents of the dictionary \(see
739 `dictree-dump-to-buffer' and `dictree-dump-to-file'\).
740 KEY-SAVEFUN, DATA-SAVEFUN and PLIST-SAVEFUN should each accept
741 one argument: a key, data or property list from DICT,
742 respectively. They should return a lisp object which has a valid
743 read syntax. When defining these functions, be careful not to
744 accidentally modify the lisp object in the dictionary; usually,
745 you will need to make a copy before converting it.
746
747 KEY-LOADFUN, DATA-LOADFUN and PLIST-LOADFUN are used to convert
748 keys, data and property lists back again when loading a
749 dictionary (only DATA-LOADFUN and PLIST-LOADFUN, see
750 `dictree-save' and `dictree-write') or populating it from a
751 file (all three, see `dictree-populate-from-file'). They should
752 accept one argument: a lisp object of the type produced by the
753 corresponding SAVEFUN, and return a lisp object to use in the
754 loaded dictionary.
755
756 TRIE-TYPE sets the type of trie to use as the underlying data
757 structure. See `trie-create' for details."
758
759 ;; sadly, passing null values over-rides the defaults in the defstruct
760 ;; dictree--create, so we have to explicitly set the defaults again
761 ;; here
762 (or name (setq name (and filename (file-name-sans-extension
763 (file-name-nondirectory filename)))))
764 (or comparison-function (setq comparison-function '<))
765 (or insert-function (setq insert-function (lambda (a b) a)))
766 (or rank-function (setq rank-function (lambda (a b) (> (cdr a) (cdr b)))))
767 (or cache-policy (setq cache-policy 'time))
768 (or cache-update-policy (setq cache-update-policy 'synchronize))
769
770 (let ((dict
771 (dictree--create
772 filename (when name (symbol-name name)) autosave unlisted
773 comparison-function insert-function rank-function
774 cache-policy cache-update-policy
775 lookup-cache-threshold
776 complete-cache-threshold
777 complete-ranked-cache-threshold
778 regexp-cache-threshold
779 regexp-ranked-cache-threshold
780 key-savefun key-loadfun
781 data-savefun data-loadfun
782 plist-savefun plist-loadfun
783 trie-type)))
784 ;; store dictionary in variable NAME
785 (when name (set name dict))
786 ;; add it to loaded dictionary list, unless it's unlisted
787 (unless (or (null name) unlisted)
788 (push dict dictree-loaded-list))
789 dict))
790
791
792 ;;;###autoload
793 (defalias 'dictree-create 'make-dictree)
794
795
796 ;;;###autoload
797 (defun* make-dictree-custom
798 (&optional
799 name filename autosave unlisted
800 &key
801 comparison-function insert-function rank-function
802 cache-policy cache-update-policy
803 lookup-cache-threshold
804 complete-cache-threshold
805 complete-ranked-cache-threshold
806 regexp-cache-threshold
807 regexp-ranked-cache-threshold
808 key-savefun key-loadfun
809 data-savefun data-loadfun
810 plist-savefun plist-loadfun
811 createfun insertfun deletefun lookupfun mapfun emptyfun
812 stack-createfun stack-popfun stack-emptyfun
813 transform-for-print transform-from-read)
814 "Create an empty dictionary and return it.
815
816 The NAME through PLIST-LOADFUN arguments are as for
817 `dictree-create' (which see).
818
819 The remaining arguments control the type of trie to use as the
820 underlying data structure. See `trie-create' for details."
821
822 ;; sadly, passing null values over-rides the defaults in the defstruct
823 ;; dictree--create, so we have to explicitly set the defaults again
824 ;; here
825 (or name (setq name (and filename (file-name-sans-extension
826 (file-name-nondirectory filename)))))
827 (or comparison-function (setq comparison-function '<))
828 (or insert-function (setq insert-function (lambda (a b) a)))
829 (or rank-function (setq rank-function (lambda (a b) (< (cdr a) (cdr b)))))
830 (or cache-policy (setq cache-policy 'time))
831 (or cache-update-policy (setq cache-update-policy 'synchronize))
832
833 (let ((dict
834 (dictree--create-custom
835 filename (when name (symbol-name name)) autosave unlisted
836 comparison-function insert-function rank-function
837 cache-policy cache-update-policy
838 lookup-cache-threshold
839 complete-cache-threshold
840 complete-ranked-cache-threshold
841 regexp-cache-threshold
842 regexp-ranked-cache-threshold
843 key-savefun key-loadfun
844 data-savefun data-loadfun
845 plist-savefun plist-loadfun
846 :createfun createfun
847 :insertfun insertfun
848 :deletefun deletefun
849 :lookupfun lookupfun
850 :mapfun mapfun
851 :emptyfun emptyfun
852 :stack-createfun stack-createfun
853 :stack-popfun stack-popfun
854 :stack-emptyfun stack-emptyfun
855 :transform-for-print transform-for-print
856 :transform-from-read transform-from-read)))
857 ;; store dictionary in variable NAME
858 (when name (set name dict))
859 ;; add it to loaded dictionary list, unless it's unlisted
860 (unless (or (null name) unlisted)
861 (push dict dictree-loaded-list))
862 dict))
863
864
865 ;;;###autoload
866 (defalias 'dictree-create-custom 'make-dictree-custom)
867
868
869 ;;;###autoload
870 (defun make-dictree-meta-dict
871 (dictionary-list
872 &optional
873 name filename autosave unlisted
874 combine-function
875 cache-policy cache-update-policy
876 lookup-cache-threshold
877 complete-cache-threshold
878 complete-ranked-cache-threshold
879 regexp-cache-threshold
880 regexp-ranked-cache-threshold)
881 "Create a meta-dictionary based on the list of dictionaries
882 in DICTIONARY-LIST.
883
884 COMBINE-FUNCTION is used to combine data from different
885 dictionaries. It is passed two pieces of data, each an
886 association of the same key, but in different dictionaries. It
887 should return a combined datum.
888
889 The other arguments are as for `dictree-create'. Note that
890 caching is only possible if NAME is supplied, otherwise the
891 cache-threshold arguments are ignored."
892
893 ;; sadly, passing null values over-rides the defaults in the defstruct
894 ;; `dictree--create', so we have to explicitly set the defaults again
895 ;; here
896 (or name (setq name (and filename
897 (file-name-sans-extension
898 (file-name-nondirectory filename)))))
899 (or combine-function (setq combine-function '+))
900 (or cache-policy (setq cache-policy 'time))
901 (or cache-update-policy (setq cache-update-policy 'synchronize))
902
903 (let ((dict
904 (dictree--meta-dict-create
905 dictionary-list filename (when name (symbol-name name))
906 autosave unlisted
907 combine-function
908 cache-policy cache-update-policy
909 (when name lookup-cache-threshold)
910 (when name complete-cache-threshold)
911 (when name complete-ranked-cache-threshold)
912 (when name regexp-cache-threshold)
913 (when name regexp-ranked-cache-threshold))
914 ))
915 ;; store dictionary in variable NAME
916 (when name (set name dict))
917 ;; add it to loaded dictionary list, unless it's unlisted
918 (unless (or (null name) unlisted)
919 (push dict dictree-loaded-list))
920 ;; update meta-dict-list cells of constituent dictionaries
921 (unless (or (null name)
922 (not (or lookup-cache-threshold
923 complete-cache-threshold
924 complete-ranked-cache-threshold
925 regexp-cache-threshold
926 regexp-ranked-cache-threshold)))
927 (mapc
928 (lambda (dic)
929 (if (symbolp dic) (setq dic (eval dic)))
930 (setf (dictree--meta-dict-list dic)
931 (cons dict (dictree--meta-dict-list dic))))
932 dictionary-list))
933 dict))
934
935 (defalias 'dictree-create-meta-dict 'make-dictree-meta-dict)
936
937
938 ;;;###autoload
939 (defun dictree-p (obj)
940 "Return t if OBJ is a dictionary tree, nil otherwise."
941 (or (dictree--p obj) (dictree--meta-dict-p obj)))
942
943
944 (defalias 'dictree-meta-dict-p 'dictree--meta-dict-p
945 "Return t if argument is a meta-dictionary, nil otherwise.")
946
947 (defun dictree-empty-p (dict)
948 "Return t if the dictionary DICT is empty, nil otherwise."
949 (if (dictree--meta-dict-p dict)
950 (catch 'nonempty
951 (mapc (lambda (dic)
952 (if (not (dictree-empty-p dic)) (throw 'nonempty t)))
953 (dictree--meta-dict-dictlist dict)))
954 (trie-empty (dictree--trie dict))))
955
956 (defsubst dictree-autosave (dict)
957 "Return dictionary's autosave flag."
958 (if (dictree--meta-dict-p dict)
959 (dictree--meta-dict-autosave dict)
960 (dictree--autosave dict)))
961
962 (defsetf dictree-autosave (dict) (val)
963 ;; setf method for dictionary autosave flag
964 `(if (dictree--meta-dict-p ,dict)
965 (setf (dictree--meta-dict-autosave ,dict) ,val)
966 (setf (dictree--autosave ,dict) ,val)))
967
968 (defsubst dictree-modified (dict)
969 "Return dictionary's modified flag."
970 (if (dictree--meta-dict-p dict)
971 (dictree--meta-dict-modified dict)
972 (dictree--modified dict)))
973
974 (defsetf dictree-modified (dict) (val)
975 ;; setf method for dictionary modified flag
976 `(if (dictree--meta-dict-p ,dict)
977 (setf (dictree--meta-dict-modified ,dict) ,val)
978 (setf (dictree--modified ,dict) ,val)))
979
980 (defsubst dictree-name (dict)
981 "Return dictionary DICT's name."
982 (if (dictree--meta-dict-p dict)
983 (dictree--meta-dict-name dict)
984 (dictree--name dict)))
985
986 (defsetf dictree-name (dict) (name)
987 ;; setf method for dictionary name
988 `(if (dictree--meta-dict-p ,dict)
989 (setf (dictree--meta-dict-name ,dict) ,name)
990 (setf (dictree--name ,dict) ,name)))
991
992 (defsubst dictree-filename (dict)
993 "Return dictionary DICT's associated file name."
994 (if (dictree--meta-dict-p dict)
995 (dictree--meta-dict-filename dict)
996 (dictree--filename dict)))
997
998 (defsetf dictree-filename (dict) (filename)
999 ;; setf method for dictionary filename
1000 `(if (dictree--meta-dict-p ,dict)
1001 (setf (dictree--meta-dict-filename ,dict) ,filename)
1002 (setf (dictree--filename ,dict) ,filename)))
1003
1004 (defun dictree-comparison-function (dict)
1005 "Return dictionary DICT's comparison function."
1006 (if (dictree--meta-dict-p dict)
1007 (dictree-comparison-function
1008 (car (dictree--meta-dict-dictlist dict)))
1009 (dictree--comparison-function dict)))
1010
1011 (defalias 'dictree-insert-function 'dictree--insert-function
1012 "Return the insertion function for dictionary DICT.")
1013
1014 (defun dictree-rank-function (dict)
1015 "Return the rank function for dictionary DICT"
1016 (if (dictree--meta-dict-p dict)
1017 (dictree-rank-function (car (dictree--meta-dict-dictlist dict)))
1018 (dictree--rank-function dict)))
1019
1020 (defun dictree-rankfun (dict)
1021 ;; Return the rank function for dictionary DICT
1022 (if (dictree--meta-dict-p dict)
1023 (dictree-rankfun (car (dictree--meta-dict-dictlist dict)))
1024 (dictree--rankfun dict)))
1025
1026 (defalias 'dictree-meta-dict-combine-function
1027 'dictree--meta-dict-combine-function
1028 "Return the combine function for meta-dictionary DICT.")
1029
1030 (defalias 'dictree-meta-dict-dictlist
1031 'dictree--meta-dict-dictlist
1032 "Return the list of constituent dictionaries
1033 for meta-dictionary DICT.")
1034
1035 (defsubst dictree-cache-policy (dict)
1036 "Return the cache policy for dictionary DICT."
1037 (if (dictree--meta-dict-p dict)
1038 (dictree--meta-dict-cache-policy dict)
1039 (dictree--cache-policy dict)))
1040
1041 (defsubst dictree-cache-update-policy (dict)
1042 "Return the cache update policy for dictionary DICT."
1043 (if (dictree--meta-dict-p dict)
1044 (dictree--meta-dict-cache-update-policy dict)
1045 (dictree--cache-update-policy dict)))
1046
1047 (defsubst dictree-lookup-cache-threshold (dict)
1048 "Return the lookup cache threshold for dictionary DICT."
1049 (if (dictree--meta-dict-p dict)
1050 (dictree--meta-dict-lookup-cache-threshold dict)
1051 (dictree--lookup-cache-threshold dict)))
1052
1053 (defsetf dictree-lookup-cache-threshold (dict) (param)
1054 ;; setf method for lookup cache threshold
1055 `(if (dictree--meta-dict-p ,dict)
1056 (setf (dictree--meta-dict-lookup-cache-threshold ,dict)
1057 ,param)
1058 (setf (dictree--lookup-cache-threshold ,dict)
1059 ,param)))
1060
1061 (defsubst dictree-lookup-cache (dict)
1062 ;; Return the lookup cache for dictionary DICT.
1063 (if (dictree--meta-dict-p dict)
1064 (dictree--meta-dict-lookup-cache dict)
1065 (dictree--lookup-cache dict)))
1066
1067 (defsubst dictree-complete-cache-threshold (dict)
1068 "Return the completion cache threshold for dictionary DICT."
1069 (if (dictree--meta-dict-p dict)
1070 (dictree--meta-dict-complete-cache-threshold dict)
1071 (dictree--complete-cache-threshold dict)))
1072
1073 (defsetf dictree-complete-cache-threshold (dict) (param)
1074 ;; setf method for completion cache threshold
1075 `(if (dictree--meta-dict-p ,dict)
1076 (setf (dictree--meta-dict-complete-cache-threshold ,dict)
1077 ,param)
1078 (setf (dictree--complete-cache-threshold ,dict)
1079 ,param)))
1080
1081 (defun dictree-complete-cache (dict)
1082 ;; Return the completion cache for dictionary DICT.
1083 (if (dictree--meta-dict-p dict)
1084 (dictree--meta-dict-complete-cache dict)
1085 (dictree--complete-cache dict)))
1086
1087 (defsubst dictree-complete-ranked-cache-threshold (dict)
1088 "Return the ranked completion cache threshold for dictionary DICT."
1089 (if (dictree--meta-dict-p dict)
1090 (dictree--meta-dict-complete-ranked-cache-threshold dict)
1091 (dictree--complete-ranked-cache-threshold dict)))
1092
1093 (defsetf dictree-complete-ranked-cache-threshold (dict) (param)
1094 ;; setf method for ranked completion cache threshold
1095 `(if (dictree--meta-dict-p ,dict)
1096 (setf (dictree--meta-dict-complete-ranked-cache-threshold ,dict)
1097 ,param)
1098 (setf (dictree--complete-ranked-cache-threshold ,dict)
1099 ,param)))
1100
1101 (defun dictree-complete-ranked-cache (dict)
1102 ;; Return the ranked completion cache for dictionary DICT.
1103 (if (dictree--meta-dict-p dict)
1104 (dictree--meta-dict-complete-ranked-cache dict)
1105 (dictree--complete-ranked-cache dict)))
1106
1107 (defsubst dictree-regexp-cache-threshold (dict)
1108 "Return the regexp cache threshold for dictionary DICT."
1109 (if (dictree--meta-dict-p dict)
1110 (dictree--meta-dict-regexp-cache-threshold dict)
1111 (dictree--regexp-cache-threshold dict)))
1112
1113 (defsetf dictree-regexp-cache-threshold (dict) (param)
1114 ;; setf method for regexp cache threshold
1115 `(if (dictree--meta-dict-p ,dict)
1116 (setf (dictree--meta-dict-regexp-cache-threshold ,dict)
1117 ,param)
1118 (setf (dictree--regexp-cache-threshold ,dict)
1119 ,param)))
1120
1121 (defun dictree-regexp-cache (dict)
1122 ;; Return the regexp cache for dictionary DICT.
1123 (if (dictree--meta-dict-p dict)
1124 (dictree--meta-dict-regexp-cache dict)
1125 (dictree--regexp-cache dict)))
1126
1127 (defsubst dictree-regexp-ranked-cache-threshold (dict)
1128 "Return the ranked regexp cache threshold for dictionary DICT."
1129 (if (dictree--meta-dict-p dict)
1130 (dictree--meta-dict-regexp-ranked-cache-threshold dict)
1131 (dictree--regexp-ranked-cache-threshold dict)))
1132
1133 (defsetf dictree-regexp-ranked-cache-threshold (dict) (param)
1134 ;; setf method for ranked regexp cache threshold
1135 `(if (dictree--meta-dict-p ,dict)
1136 (setf (dictree--meta-dict-regexp-ranked-cache-threshold ,dict)
1137 ,param)
1138 (setf (dictree--regexp-ranked-cache-threshold ,dict)
1139 ,param)))
1140
1141 (defun dictree-regexp-ranked-cache (dict)
1142 ;; Return the ranked regexp cache for dictionary DICT.
1143 (if (dictree--meta-dict-p dict)
1144 (dictree--meta-dict-regexp-ranked-cache dict)
1145 (dictree--regexp-ranked-cache dict)))
1146
1147
1148
1149 ;; ----------------------------------------------------------------
1150 ;; Inserting and deleting data
1151
1152 (defun dictree-insert (dict key &optional data insert-function)
1153 "Insert KEY and DATA into dictionary DICT.
1154 If KEY does not already exist, this creates it. How the data is
1155 inserted depends on the dictionary's insertion function \(see
1156 `dictree-create'\).
1157
1158 The optional INSERT-FUNCTION over-rides the dictionary's own
1159 insertion function. If KEY already exists in DICT,
1160 INSERT-FUNCTION is called with two arguments: the data DATA, and
1161 the data associated with KEY in the dictionary. Its return value
1162 becomes the new association for KEY."
1163
1164 ;; if dictionary is a meta-dictionary, insert key into all the
1165 ;; dictionaries it's based on
1166 (if (dictree--meta-dict-p dict)
1167 (mapc (lambda (dic)
1168 (dictree-insert dic key data insert-function))
1169 (dictree--meta-dict-dictlist dict))
1170
1171 ;; otherwise...
1172 (let (newdata)
1173 ;; set the dictionary's modified flag
1174 (setf (dictree-modified dict) t)
1175 ;; insert key in dictionary's ternary search tree
1176 (setq newdata
1177 (trie-insert
1178 (dictree--trie dict) key (dictree--cell-create data nil)
1179 (or (and insert-function
1180 (dictree--wrap-insfun insert-function))
1181 (dictree--insfun dict))))
1182 ;; update dictionary's caches
1183 (dictree--update-cache dict key newdata)
1184 ;; update cache's of any meta-dictionaries based on dict
1185 (mapc (lambda (dic) (dictree--update-cache dic key newdata))
1186 (dictree--meta-dict-list dict))
1187
1188 ;; return the new data
1189 (dictree--cell-data newdata))))
1190
1191
1192
1193 (defun dictree-delete (dict key &optional test)
1194 "Delete KEY from DICT.
1195 Returns non-nil if KEY was deleted, nil if KEY was not in DICT.
1196
1197 If TEST is supplied, it should be a function that accepts three
1198 arguments: the key being deleted, its associated data, and its
1199 associated property list. The key will then only be deleted if
1200 TEST returns non-nil."
1201
1202 (let ((dictree--delete-test test)
1203 deleted del)
1204 (cond
1205 ;; if DICT is a meta-dictionary, delete KEY from all dictionaries
1206 ;; it's based on
1207 ((dictree--meta-dict-p dict)
1208 (dolist (dic (dictree--meta-dict-dictlist dict))
1209 (when (setq del (dictree-delete dic key))
1210 (setq deleted (cons del deleted))))
1211 (setf (dictree-modified dict) (and deleted t))
1212 (setq deleted (nreverse deleted)))
1213
1214 ;; otherwise...
1215 (t
1216 (setq deleted
1217 (trie-delete (dictree--trie dict) key
1218 (when dictree--delete-test
1219 (lambda (k cell)
1220 (funcall dictree--delete-test
1221 k (dictree--cell-data cell)
1222 (dictree--cell-plist cell))))))
1223 ;; if key was deleted, have to update the caches
1224 (when deleted
1225 (dictree--update-cache dict key nil t)
1226 (setf (dictree-modified dict) t)
1227 ;; update cache's of any meta-dictionaries based on DICT
1228 (mapc (lambda (dic)
1229 (dictree--update-cache dic key nil t))
1230 (dictree--meta-dict-list dict)))))
1231
1232 ;; return deleted key/data pair
1233 (when deleted
1234 (cons (car deleted) (dictree--cell-data (cdr deleted))))))
1235
1236
1237
1238 ;; ----------------------------------------------------------------
1239 ;; Cache updating
1240
1241 (defun dictree--prefix-p (prefix str)
1242 "Return t if PREFIX is a prefix of STR, nil otherwise.
1243
1244 PREFIX and STR can be any sequence type (string, vector, or
1245 list), but they must both be the same type. PREFIX can also be a
1246 list of sequences, in which case it returns t if any element of
1247 PREFIX is a prefix of STR."
1248 ;; wrap prefix in a list if necessary
1249 ;; FIXME: the test for a list of prefixes, below, will fail if the
1250 ;; PREFIX sequence is a list, and the elements of PREFIX are
1251 ;; themselves lists (there might be no easy way to fully fix
1252 ;; this...)
1253 (when (or (atom prefix)
1254 (and (listp prefix) (not (sequencep (car prefix)))))
1255 (setq prefix (list prefix)))
1256 (let (len)
1257 (catch 'is-prefix
1258 (dolist (pfx prefix)
1259 (setq len (length pfx))
1260 (when (and (<= len (length str))
1261 (equal pfx (dictree--subseq str 0 len)))
1262 (throw 'is-prefix t))))))
1263
1264
1265 (defun dictree--above-cache-threshold-p
1266 (time length policy threshold &optional cache-long-keys)
1267 ;; Return t if query taking TIME seconds for a key of length LENGTH
1268 ;; should be cached according to the cache POLICY and
1269 ;; THRESHOLD. Otherwise, return nil. Optional argument CACHE-LONG-KEYS
1270 ;; means that keys of length longer than THRESHOLD are to be
1271 ;; cached. Default is keys of length shorter than THRESHOLD.
1272 (and threshold
1273 (or (eq threshold t)
1274 (and (eq policy 'time) (>= time threshold))
1275 ;; note: we cache lookups of *longer* keys, because those are
1276 ;; likely to be slower ones
1277 (and (eq policy 'length)
1278 (if cache-long-keys
1279 (>= length threshold) (<= length threshold)))
1280 (and (eq policy 'both)
1281 (or (>= time (plist-get threshold :time))
1282 (if cache-long-keys
1283 (>= length (plist-get threshold :length))
1284 (<= length (plist-get threshold :length))))))))
1285
1286
1287 (defun dictree--update-cache (dict key newdata &optional deleted)
1288 ;; Synchronise dictionary DICT's caches, given that the data
1289 ;; associated with KEY has been changed to NEWDATA, or KEY has been
1290 ;; deleted if DELETED is non-nil (NEWDATA is ignored in that case)."
1291 (let (arg reverse cache cache-entry completions cmpl maxnum)
1292
1293 ;; synchronise the lookup cache if dict is a meta-dictionary, since
1294 ;; it's not done automatically
1295 (when (and (dictree--meta-dict-p dict)
1296 (dictree--meta-dict-lookup-cache-threshold dict))
1297 (setq cache (dictree--lookup-cache dict))
1298 (cond
1299 ;; if updating dirty cache entries...
1300 ((eq (dictree-cache-update-policy dict) 'synchronize)
1301 (when (gethash key cache)
1302 (if deleted (remhash key cache) (puthash key newdata cache))))
1303 ;; if deleting dirty cache entries...
1304 (t (remhash key cache))))
1305
1306 ;; synchronize the completion cache, if it exists
1307 (when (dictree-complete-cache-threshold dict)
1308 (setq cache (dictree-complete-cache dict))
1309 ;; check every cache entry to see if it matches
1310 (maphash
1311 (lambda (cache-key cache-entry)
1312 (setq arg (car cache-key))
1313 (when (dictree--prefix-p arg key)
1314 (setq reverse (cdr cache-key))
1315 (cond
1316 ;; if updating dirty cache entries...
1317 ((eq (dictree-cache-update-policy dict) 'synchronize)
1318 (dictree--synchronize-completion-cache
1319 dict cache-entry arg reverse key newdata deleted))
1320 ;; if deleting dirty cache entries...
1321 (t (remhash (cons arg reverse) cache)))))
1322 cache))
1323
1324 ;; synchronize the ranked completion cache, if it exists
1325 (when (dictree-complete-ranked-cache-threshold dict)
1326 (setq cache (dictree-complete-ranked-cache dict))
1327 ;; check every cache entry to see if it matches
1328 (maphash
1329 (lambda (cache-key cache-entry)
1330 (setq arg (car cache-key))
1331 (when (dictree--prefix-p arg key)
1332 (setq reverse (cdr cache-key))
1333 (cond
1334 ;; if updating dirty cache entries...
1335 ((eq (dictree-cache-update-policy dict) 'synchronize)
1336 (dictree--synchronize-ranked-completion-cache
1337 dict cache-entry arg reverse key newdata deleted))
1338 ;; if deleting dirty cache entries...
1339 (t (remhash (cons arg reverse) cache)))))
1340 cache))
1341
1342 ;; synchronize the regexp cache, if it exists
1343 (when (dictree-regexp-cache-threshold dict)
1344 (setq cache (dictree--regexp-cache dict))
1345 ;; check every cache entry to see if it matches
1346 (maphash
1347 (lambda (cache-key cache-entry)
1348 (setq arg (car cache-key))
1349 (when (tNFA-regexp-match
1350 arg key :test (dictree--comparison-function dict))
1351 (setq reverse (cdr cache-key))
1352 (cond
1353 ;; if updating dirty cache entries...
1354 ((eq (dictree-cache-update-policy dict) 'synchronize)
1355 (dictree--synchronize-regexp-cache
1356 dict cache-entry arg reverse key newdata deleted))
1357 ;; if deleting dirty cache entries...
1358 (t (remhash (cons arg reverse) cache)))))
1359 cache))
1360
1361 ;; synchronize the ranked regexp cache, if it exists
1362 (when (dictree-regexp-ranked-cache-threshold dict)
1363 (setq cache (dictree-regexp-ranked-cache dict))
1364 ;; have to check every cache entry to see if it matches
1365 (maphash
1366 (lambda (cache-key cache-entry)
1367 (setq arg (car cache-key))
1368 (when (tNFA-regexp-match
1369 arg key :test (dictree--comparison-function dict))
1370 (setq reverse (cdr cache-key))
1371 (cond
1372 ;; if updating dirty cache entries...
1373 ((eq (dictree-cache-update-policy dict) 'synchronize)
1374 (dictree--synchronize-ranked-regexp-cache
1375 dict cache-entry arg reverse key newdata deleted))
1376 ;; if deleting dirty cache entries...
1377 (t (remhash (cons arg reverse) cache)))))
1378 cache))
1379 ))
1380
1381
1382
1383 (defun dictree--synchronize-completion-cache
1384 (dict cache-entry arg reverse key newdata deleted)
1385 ;; Synchronize DICT's completion CACHE-ENTRY for ARG and REVERSE, for
1386 ;; a KEY whose data was either updated to NEWDATA or DELETED.
1387 (let* ((completions (dictree--cache-results cache-entry))
1388 (maxnum (dictree--cache-maxnum cache-entry))
1389 (cmpl (assoc key completions)))
1390 ;; if key was...
1391 (cond
1392 ;; deleted and in cached result: remove cache entry and re-run the
1393 ;; same completion to update the cache
1394 ((and deleted cmpl)
1395 (remhash (cons arg reverse) (dictree-complete-cache dict))
1396 (dictree-complete dict arg nil maxnum reverse))
1397 ;; modified and not in cached result: merge it into the completion
1398 ;; list, retaining only the first maxnum
1399 ((and (not deleted) (not cmpl))
1400 (dictree--cache-set-completions
1401 cache-entry
1402 (dictree--merge
1403 (list (cons key newdata)) completions
1404 `(lambda (a b)
1405 (,(trie-construct-sortfun
1406 (dictree-comparison-function dict))
1407 (car a) (car b)))
1408 (when (dictree--meta-dict-p dict)
1409 (dictree--meta-dict-combfun dict))
1410 maxnum)))
1411 ;; modified and in the cached result: update the associated data if
1412 ;; dict is a meta-dictionary (this is done automatically for a
1413 ;; normal dict)
1414 ((and (not deleted) cmpl (dictree--meta-dict-p dict))
1415 (setcdr cmpl newdata))
1416 ;; deleted and not in cached result: requires no action
1417 )))
1418
1419
1420
1421 (defun dictree--synchronize-ranked-completion-cache
1422 (dict cache-entry arg reverse key newdata deleted)
1423 ;; Synchronize DICT's ranked completion CACHE-ENTRY for ARG and
1424 ;; REVERSE, for a KEY whose data was either updated to NEWDATA or
1425 ;; DELETED.
1426 (let* ((completions (dictree--cache-results cache-entry))
1427 (maxnum (dictree--cache-maxnum cache-entry))
1428 (cmpl (assoc key completions))
1429 (cache (dictree--complete-ranked-cache dict)))
1430 ;; if key was...
1431 (cond
1432 ;; deleted and in cached result: remove cache entry and re-run the
1433 ;; same query to update the cache
1434 ((and deleted cmpl)
1435 (remhash (cons arg reverse) cache)
1436 (dictree-complete dict arg 'ranked maxnum reverse))
1437 ;; modified and not in cached result: merge it into the completion
1438 ;; list, retaining only the first maxnum
1439 ((and (not deleted) (not cmpl))
1440 (dictree--cache-set-completions
1441 cache-entry
1442 (dictree--merge
1443 (list (cons key newdata)) completions
1444 (dictree-rankfun dict)
1445 (when (dictree--meta-dict-p dict)
1446 (dictree--meta-dict-combfun dict))
1447 maxnum)))
1448 ;; modified and in the cached result: update the associated data if
1449 ;; dict is a meta-dictionary (this is done automatically for a
1450 ;; normal dict), re-sort, and if key is now at end of list re-run
1451 ;; the same query to update the cache
1452 ((and (not deleted) cmpl)
1453 (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
1454 (dictree--cache-set-completions
1455 cache-entry (sort completions (dictree-rankfun dict)))
1456 (when (equal key (car (last completions)))
1457 (remhash (cons arg reverse) cache)
1458 (dictree-complete dict arg 'ranked maxnum reverse)))
1459 ;; deleted and not in cached result: requires no action
1460 )))
1461
1462
1463 (defun dictree--synchronize-regexp-cache
1464 (dict cache-entry arg reverse key newdata deleted)
1465 ;; Synchronize DICT's completion CACHE-ENTRY for ARG and REVERSE, for
1466 ;; a KEY whose data was either updated to NEWDATA or DELETED.
1467 (let* ((completions (dictree--cache-results cache-entry))
1468 (maxnum (dictree--cache-maxnum cache-entry))
1469 group-data
1470 (cmpl (catch 'found
1471 (dolist (c completions)
1472 (if (and (listp (car c))
1473 (or (stringp (caar c))
1474 (vectorp (caar c))
1475 (listp (caar c))))
1476 (when (equal key (caar c)) (throw 'found c))
1477 (when (equal key (car c)) (throw 'found c)))))))
1478 ;; if key was...
1479 (cond
1480 ;; deleted and in cached result: remove cache entry and re-run the
1481 ;; same completion to update the cache
1482 ((and deleted cmpl)
1483 (remhash (cons arg reverse) (dictree-complete-cache dict))
1484 (dictree-regexp-search dict arg nil maxnum reverse))
1485 ;; modified and not in cached result: merge it into the completion
1486 ;; list, retaining only the first maxnum
1487 ((and (not deleted) (not cmpl))
1488 (save-match-data
1489 (set-match-data nil)
1490 (tNFA-regexp-match arg key
1491 :test (dictree--comparison-function dict))
1492 (when (setq group-data (nthcdr 2 (match-data)))
1493 (setq key (cons key group-data))))
1494 (dictree--cache-set-completions
1495 cache-entry
1496 (dictree--merge
1497 (list (cons key newdata)) completions
1498 `(lambda (a b)
1499 (,(trie-construct-sortfun (dictree-comparison-function dict))
1500 ,(if group-data '(caar a) '(car a))
1501 ,(if group-data '(caar b) '(car b))))
1502 (when (dictree--meta-dict-p dict)
1503 (dictree--meta-dict-combfun dict))
1504 maxnum)))
1505 ;; modified and in the cached result: update the associated data if
1506 ;; dict is a meta-dictionary (this is done automatically for a
1507 ;; normal dict)
1508 ((and (not deleted) cmpl (dictree--meta-dict-p dict))
1509 (setcdr cmpl newdata))
1510 ;; deleted and not in cached result: requires no action
1511 )))
1512
1513
1514
1515 (defun dictree--synchronize-ranked-regexp-cache
1516 (dict cache-entry arg reverse key newdata deleted)
1517 ;; Synchronize DICT's ranked regexp CACHE-ENTRY for ARG and REVERSE,
1518 ;; for a KEY whose data was either updated to NEWDATA or DELETED.
1519 (let ((completions (dictree--cache-results cache-entry))
1520 (maxnum (dictree--cache-maxnum cache-entry))
1521 (cache (dictree--regexp-ranked-cache dict))
1522 cmpl group-data)
1523 (setq group-data (and (listp (caar completions))
1524 (or (stringp (caar (car completions)))
1525 (vectorp (caar (car completions)))
1526 (listp (caar (car completions))))))
1527 (setq cmpl
1528 (catch 'found
1529 (dolist (c completions)
1530 (if group-data
1531 (when (equal key (caar c)) (throw 'found c))
1532 (when (equal key (car c)) (throw 'found c))))))
1533 ;; if key was...
1534 (cond
1535 ;; deleted and in cached result: remove cache entry and re-run the
1536 ;; same query to update the cache
1537 ((and deleted cmpl)
1538 (remhash (cons arg reverse) cache)
1539 (dictree-regexp-search dict arg 'ranked maxnum reverse))
1540 ;; modified and not in cached result: merge it into the completion
1541 ;; list, retaining only the first maxnum
1542 ((and (not deleted) (not cmpl))
1543 (save-match-data
1544 (set-match-data nil)
1545 (tNFA-regexp-match arg key
1546 :test (dictree--comparison-function dict))
1547 (when (setq group-data (nthcdr 2 (match-data)))
1548 (setq key (cons key group-data))))
1549 (dictree--cache-set-completions
1550 cache-entry
1551 (dictree--merge
1552 (list (cons key newdata)) completions
1553 (dictree-rankfun dict)
1554 (when (dictree--meta-dict-p dict)
1555 (dictree--meta-dict-combfun dict))
1556 maxnum)))
1557 ;; modified and in the cached result: update the associated data if
1558 ;; dict is a meta-dictionary (this is done automatically for a
1559 ;; normal dict), re-sort, and if key is now at end of list re-run
1560 ;; the same query to update the cache
1561 ((and (not deleted) cmpl)
1562 (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
1563 (dictree--cache-set-completions
1564 cache-entry
1565 (sort completions
1566 (if group-data
1567 `(lambda (a b)
1568 (,(dictree-rankfun dict)
1569 (cons (caar a) (cdr a))
1570 (cons (caar b) (cdr b))))
1571 (dictree-rankfun dict))))
1572 (when (equal key (car (last completions)))
1573 (remhash (cons arg reverse) cache)
1574 (dictree-complete dict arg 'ranked maxnum reverse)))
1575 ;; deleted and not in cached result: requires no action
1576 )))
1577
1578
1579 (defun dictree-clear-caches (dict)
1580 "Clear all DICT's query caches."
1581 (interactive (list (read-dict "Dictionary: ")))
1582 (dolist (cachefun '(dictree-lookup-cache
1583 dictree-complete-cache
1584 dictree-complete-ranked-cache
1585 dictree-regexp-cache
1586 dictree-regexp-ranked-cache))
1587 (when (funcall cachefun dict)
1588 (clrhash (funcall cachefun dict))))
1589 (when (called-interactively-p 'interactive)
1590 (message "Cleared caches for dictionary %s" (dictree-name dict))))
1591
1592
1593
1594
1595 ;; ----------------------------------------------------------------
1596 ;; Retrieving data
1597
1598 (defun dictree-member (dict key &optional nilflag)
1599 "Return the data associated with KEY in dictionary DICT,
1600 or nil if KEY is not in the dictionary.
1601
1602 Optional argument NILFLAG specifies a value to return instead of
1603 nil if KEY does not exist in TREE. This allows a non-existent KEY
1604 to be distinguished from an element with a null association. (See
1605 also `dictree-member-p' for testing existence alone.)"
1606 (let* ((data (dictree--lookup dict key nilflag)))
1607 (if (eq data nilflag)
1608 nilflag
1609 (dictree--cell-data data))))
1610
1611 (defalias 'dictree-lookup 'dictree-member)
1612
1613 (defun dictree-member-p (dict key)
1614 "Return t if KEY exists in DICT, nil otherwise."
1615 (let ((flag '(nil)))
1616 (not (eq flag (dictree-member dict key flag)))))
1617
1618
1619 (defun dictree--lookup (dict key nilflag)
1620 ;; Return association of KEY in DICT, or NILFLAG if KEY does not
1621 ;; exist. Does not do any data/meta-data unwrapping
1622
1623 (let* ((flag '(nil))
1624 (data flag)
1625 time)
1626 ;; if KEY is in the cache, then we're done
1627 (unless (and (dictree-lookup-cache dict)
1628 (setq data (gethash key (dictree--lookup-cache dict))))
1629
1630 ;; otherwise, we have to look in the dictionary itself...
1631 (cond
1632 ;; if DICT is a meta-dict, look in its constituent dictionaries
1633 ((dictree--meta-dict-p dict)
1634 (let (newdata (newflag '(nil)))
1635 ;; time the lookup for caching
1636 (setq time (float-time))
1637 ;; look in each constituent dictionary in turn
1638 (dolist (dic (dictree--meta-dict-dictlist dict))
1639 (setq newdata (dictree--lookup dic key newflag))
1640 ;; skip dictionary if it doesn't contain KEY
1641 (unless (eq newdata newflag)
1642 ;; if we haven't found KEY before, we have now!
1643 (if (eq data flag) (setq data newdata)
1644 ;; otherwise, combine the previous data with the new
1645 ;; data
1646 (setq data (funcall (dictree--meta-dict-combfun dict)
1647 data newdata)))))
1648 (setq time (- (float-time) time))))
1649
1650 ;; otherwise, DICT is a normal dictionary, so look in it's trie
1651 (t
1652 ;; time the lookup for caching
1653 (setq time (float-time))
1654 (setq data (trie-member (dictree--trie dict) key flag))
1655 (setq time (- (float-time) time))))
1656
1657 ;; if lookup found something, and we're above the lookup
1658 ;; cache-threshold, cache the result
1659 (when (and (not (eq data flag))
1660 (dictree--above-cache-threshold-p
1661 time (length key) (dictree-cache-policy dict)
1662 (dictree-lookup-cache-threshold dict) 'long-keys))
1663 (setf (dictree-modified dict) t)
1664 (puthash key data (dictree-lookup-cache dict))))
1665
1666 ;; return the desired data
1667 (if (eq data flag) nilflag data)))
1668
1669
1670
1671 ;; ----------------------------------------------------------------
1672 ;; Getting and setting meta-data
1673
1674 (defun dictree-put-property (dict key property value)
1675 "Set PROPERTY for KEY in dictionary DICT.
1676 PROPERTY should be a symbol. Returns VALUE if successful, nil if
1677 KEY was not found in DICT.
1678
1679 Note that if DICT is a meta-dictionary, then this will set KEY's
1680 PROPERTY to VALUE in *all* its constituent dictionaries.
1681
1682 Unlike the data associated with a key (cf. `dictree-insert'),
1683 properties are not included in the results of queries on the
1684 dictionary \(`dictree-lookup', `dictree-complete',
1685 `dictree-complete-ordered'\), nor do they affect the outcome of
1686 any of the queries. They merely serves to tag a key with some
1687 additional information, and can only be retrieved using
1688 `dictree-get-property'."
1689
1690 ;; sort out arguments
1691 (when (symbolp dict) (setq dict (eval dict)))
1692 (cond
1693 ;; set PROPERTY for KEY in all constituent dicts of a meta-dict
1694 ((dictree--meta-dict-p dict)
1695 (warn "Setting %s property for key %s in all constituent\
1696 dictionaries of meta-dicttionary %s" property key (dictree-name dict))
1697 (setf (dictree-modified dict) t)
1698 (let (dictree--put-property-ret)
1699 (mapc (lambda (dic k p v)
1700 (setq dictree--put-property-ret
1701 (or dictree--put-property-ret
1702 (dictree-put-property dic k p v))))
1703 (dictree--meta-dict-dictlist dict))
1704 ;; return VALUE if KEY was found in at least one constituent dict
1705 dictree--put-property-ret))
1706 (t ;; set PROPERTY for KEY in normal dict
1707 (let ((cell (trie-member (dictree--trie dict) key)))
1708 (when cell
1709 (setf (dictree-modified dict) t)
1710 (setf (dictree--cell-plist cell)
1711 (plist-put (dictree--cell-plist cell) property value))
1712 value))) ; return VALUE
1713 ))
1714
1715
1716
1717 (defun dictree-delete-property (dict key property)
1718 "Delete PROPERTY from KEY in dictionary DICT.
1719 Returns the new property list for KEY, with PROPERTY deleted.
1720
1721 Setting PROPERTY to nil using `dictree-put-property' is not quite
1722 the same thing as deleting it, since null property values can
1723 still be detected by supplying the optional argument to
1724 `dictree-get-propery' (which see).
1725
1726 Note that if DICT is a meta-dictionary, then this will delete
1727 KEY's PROPERTY in *all* its constituent dictionaries."
1728 ;; sort out arguments
1729 (when (symbolp dict) (setq dict (eval dict)))
1730 (cond
1731 ;; delete PROPERTY from KEY in all constituent dicts of a meta-dict
1732 ((dictree--meta-dict-p dict)
1733 (warn "Deleting %s property from key %s in all constituent\
1734 dictionaries of meta-dicttionary %s" property key (dictree-name dict))
1735 (setf (dictree-modified dict) t)
1736 (mapcar (lambda (dic k p) (dictree-delete-property dic k p))
1737 (dictree--meta-dict-dictlist dict)))
1738 (t ;; delete PROPERTY from KEY in normal dict
1739 (let* ((cell (trie-member (dictree--trie dict) key))
1740 plist tail tail)
1741 (when (and cell
1742 (setq tail
1743 (plist-member
1744 (setq plist (dictree--cell-plist cell))
1745 property)))
1746 (setf (dictree-modified dict) t)
1747 ;; delete property and value from plist
1748 (setcdr tail (cddr tail))
1749 (setq plist (delq property plist))
1750 (setf (dictree--cell-plist cell) plist))))
1751 ))
1752
1753
1754
1755 (defun dictree-get-property (dict key property &optional nilflag)
1756 "Get the value of PROPERTY for KEY in dictionary DICT,
1757 or return nil if KEY is not in the dictionary.
1758
1759 Optional argument NILFLAG specifies a value to return instead of
1760 nil if KEY does not exist in TREE. This allows a non-existent KEY
1761 to be distinguished from a key for which PROPERTY is not
1762 set. (See also `dictree-member-p' for testing existence alone.)"
1763 (let ((cell (dictree--lookup dict key nilflag)))
1764 (unless (eq cell nilflag)
1765 (plist-get (dictree--cell-plist cell) property))))
1766
1767
1768
1769
1770 ;; ----------------------------------------------------------------
1771 ;; Mapping functions
1772
1773 (defun dictree-mapc (function dict &optional type reverse)
1774 "Apply FUNCTION to all entries in dictionary DICT,
1775 for side-effects only.
1776
1777 FUNCTION will be passed two arguments: a key of type
1778 TYPE ('string, 'vector, or 'list, defaulting to 'vector) from the
1779 dictionary, and the data associated with that key. The dictionary
1780 entries will be traversed in \"lexical\" order, i.e. the order
1781 defined by the dictionary's comparison function (cf.
1782 `dictree-create').
1783
1784 If TYPE is 'string, it must be possible to apply the function
1785 `string' to the elements of sequences stored in DICT.
1786
1787 FUNCTION is applied in ascending order, or descending order if
1788 REVERSE is non-nil.
1789
1790 Note: to avoid nasty dynamic scoping bugs, FUNCTION must *not*
1791 bind any variables with names commencing \"--\"."
1792
1793 ;; "rename" FUNCTION to something hopefully unique to lessen the
1794 ;; likelihood of dynamic scoping bugs caused by a supplied function
1795 ;; binding a variable with the same name as one of the arguments
1796 (let ((--dictree-mapc--function function))
1797 (dictree--mapc
1798 (lambda (key data plist)
1799 (funcall --dictree-mapc--function key data))
1800 dict type reverse)))
1801
1802
1803
1804 (defun dictree--mapc (function dict &optional type reverse)
1805 ;; Like `dictree-mapc', but FUNCTION is passed three arguments: the
1806 ;; key, the data, and the property list, instead of just key and data.
1807
1808 ;; try to avoid dynamic binding bugs
1809 (let ((--dictree--mapc--function function))
1810 (if (dictree--meta-dict-p dict)
1811 ;; for a meta-dict, use a dictree-stack
1812 (let ((stack (dictree-stack dict))
1813 entry)
1814 (while (setq entry (dictree--stack-pop stack))
1815 (funcall --dictree--mapc--function
1816 (car entry)
1817 (dictree--cell-data (cdr entry))
1818 (dictree--cell-plist (cdr entry)))))
1819 ;; for a normal dictionary, map the function over its trie
1820 (trie-mapc
1821 (lambda (key cell)
1822 (funcall --dictree--mapc--function
1823 key
1824 (dictree--cell-data cell)
1825 (dictree--cell-plist cell)))
1826 (dictree--trie dict)
1827 type reverse)
1828 )))
1829
1830
1831
1832 (defun dictree-mapf (function combinator dict &optional type reverse)
1833 "Apply FUNCTION to all entries in dictionary DICT,
1834 and combine the results using COMBINATOR.
1835
1836 FUNCTION should take two arguments: a key sequence from the
1837 dictionary and its associated data.
1838
1839 Optional argument TYPE (one of the symbols vector, lisp or
1840 string; defaults to vector) sets the type of sequence passed to
1841 FUNCTION. If TYPE is 'string, it must be possible to apply the
1842 function `string' to the individual elements of key sequences
1843 stored in DICT.
1844
1845 The FUNCTION will be applied and the results combined in
1846 asscending \"lexical\" order (i.e. the order defined by the
1847 dictionary's comparison function; cf. `dictree-create'), or
1848 descending order if REVERSE is non-nil.
1849
1850 Note: to avoid nasty dynamic scoping bugs, FUNCTION and
1851 COMBINATOR must *not* bind any variables with names
1852 commencing \"--\"."
1853
1854 ;; try to avoid dynamic scoping bugs
1855 (let ((--dictree-mapf--function function)
1856 (--dictree-mapf--combinator combinator))
1857
1858 ;; for a normal dictionary, map the function over its trie
1859 (if (not (dictree--meta-dict-p dict))
1860 (trie-mapf
1861 `(lambda (key data)
1862 (,--dictree-mapf--function key (dictree--cell-data data)))
1863 --dictree-mapf--combinator (dictree--trie dict) type reverse)
1864
1865 ;; for a meta-dict, use a dictree-stack
1866 (let ((--dictree-mapf--stack (dictree-stack dict))
1867 --dictree-mapf--entry
1868 --dictree-mapf--accumulate)
1869 (while (setq --dictree-mapf--entry
1870 (dictree-stack-pop --dictree-mapf--stack))
1871 (setq --dictree-mapf--accumulate
1872 (funcall --dictree-mapf--combinator
1873 (funcall --dictree-mapf--function
1874 (car --dictree-mapf--entry)
1875 (cdr --dictree-mapf--entry))
1876 --dictree-mapf--accumulate)))
1877 --dictree-mapf--accumulate))))
1878
1879
1880
1881 (defun dictree-mapcar (function dict &optional type reverse)
1882 "Apply FUNCTION to all entries in dictionary DICT,
1883 and make a list of the results.
1884
1885 FUNCTION should take two arguments: a key sequence from the
1886 dictionary and its associated data.
1887
1888 Optional argument TYPE (one of the symbols vector, lisp or
1889 string; defaults to vector) sets the type of sequence passed to
1890 FUNCTION. If TYPE is 'string, it must be possible to apply the
1891 function `string' to the individual elements of key sequences
1892 stored in DICT.
1893
1894 The FUNCTION will be applied and the results combined in
1895 asscending \"lexical\" order \(i.e. the order defined by the
1896 dictionary's comparison function; cf. `dictree-create'\), or
1897 descending order if REVERSE is non-nil.
1898
1899 Note that if you don't care about the order in which FUNCTION is
1900 applied, just that the resulting list is in the correct order,
1901 then
1902
1903 (trie-mapf function 'cons trie type (not reverse))
1904
1905 is more efficient.
1906
1907 Note: to avoid nasty dynamic scoping bugs, FUNCTION must *not*
1908 bind any variables with names commencing \"--\"."
1909 (nreverse (dictree-mapf function 'cons dict type)))
1910
1911
1912
1913 (defun dictree-size (dict)
1914 "Return the number of entries in dictionary DICT.
1915 Interactively, DICT is read from the mini-buffer."
1916 (interactive (list (read-dict "Dictionary: ")))
1917 (let ((count 0))
1918 (dictree-mapc (lambda (&rest dummy) (incf count)) dict)
1919 (when (called-interactively-p 'interactive)
1920 (message "Dictionary %s contains %d entries"
1921 (dictree--name dict) count))
1922 count))
1923
1924
1925
1926 ;; ----------------------------------------------------------------
1927 ;; Using dictrees as stacks
1928
1929 ;; A dictree--meta-stack is the meta-dict version of a dictree-stack
1930 ;; (the ordinary version is just a single trie-stack). It consists of a
1931 ;; heap of trie-stacks for its constituent tries, where the heap order
1932 ;; is the usual lexical order over the keys at the top of the
1933 ;; trie-stacks.
1934
1935 (defstruct
1936 (dictree--meta-stack
1937 (:constructor nil)
1938 (:constructor dictree--meta-stack-create
1939 (dict &optional (type 'vector) reverse
1940 &aux
1941 (combfun (dictree--meta-dict-combfun dict))
1942 (sortfun (trie-construct-sortfun
1943 (dictree-comparison-function dict)))
1944 (heap (heap-create
1945 (dictree--construct-meta-stack-heapfun sortfun)
1946 (length (dictree--trielist dict))))
1947 (pushed '())
1948 (dummy (mapc
1949 (lambda (dic)
1950 (heap-add
1951 heap (trie-stack dic type reverse)))
1952 (dictree--trielist dict)))))
1953 (:constructor dictree--complete-meta-stack-create
1954 (dict prefix &optional reverse
1955 &aux
1956 (combfun (dictree--meta-dict-combfun dict))
1957 (sortfun (trie-construct-sortfun
1958 (dictree-comparison-function dict)))
1959 (heap (heap-create
1960 (dictree--construct-meta-stack-heapfun
1961 sortfun reverse)
1962 (length (dictree--trielist dict))))
1963 (pushed '())
1964 (dummy (mapc
1965 (lambda (trie)
1966 (let ((stack (trie-complete-stack
1967 trie prefix reverse)))
1968 (unless (trie-stack-empty-p stack)
1969 (heap-add heap stack))))
1970 (dictree--trielist dict)))))
1971 (:constructor dictree--regexp-meta-stack-create
1972 (dict regexp &optional reverse
1973 &aux
1974 (combfun (dictree--meta-dict-combfun dict))
1975 (sortfun (trie-construct-sortfun
1976 (dictree-comparison-function dict)))
1977 (heap (heap-create
1978 (dictree--construct-meta-stack-heapfun
1979 sortfun reverse)
1980 (length (dictree--trielist dict))))
1981 (pushed '())
1982 (dummy (mapc
1983 (lambda (trie)
1984 (let ((stack (trie-regexp-stack
1985 trie regexp reverse)))
1986 (unless (trie-stack-empty-p stack)
1987 (heap-add heap stack))))
1988 (dictree--trielist dict)))))
1989 (:copier nil))
1990 combfun sortfun heap pushed)
1991
1992
1993
1994 (defun dictree--construct-meta-stack-heapfun (sortfun &optional reverse)
1995 ;; Wrap SORTFUN, which sorts keys, so it can act on
1996 ;; dictree--meta-stack elements.
1997 (if reverse
1998 `(lambda (b a) (,sortfun (car (dictree-stack-first a))
1999 (car (dictree-stack-first b))))
2000 `(lambda (a b) (,sortfun (car (dictree-stack-first a))
2001 (car (dictree-stack-first b))))))
2002
2003
2004 (defun dictree-stack (dict &optional type reverse)
2005 "Create an object that allows DICT to be accessed as a stack.
2006
2007 The stack is sorted in \"lexical\" order, i.e. the order defined
2008 by the DICT's comparison function, or in reverse order if REVERSE
2009 is non-nil. Calling `dictree-stack-pop' pops the top element (a
2010 key and its associated data) from the stack.
2011
2012 Optional argument TYPE (one of the symbols vector, lisp or
2013 string) sets the type of sequence used for the keys.
2014
2015 Note that any modification to DICT *immediately* invalidates all
2016 dictree-stacks created before the modification (in particular,
2017 calling `dictree-stack-pop' will give unpredictable results).
2018
2019 Operations on dictree-stacks are significantly more efficient
2020 than constructing a real stack from the dictionary and using
2021 standard stack functions. As such, they can be useful in
2022 implementing efficient algorithms on dictionaries. However, in
2023 cases where mapping functions `dictree-mapc', `dictree-mapcar' or
2024 `dictree-mapf' would be sufficient, it is better to use one of
2025 those instead."
2026 (if (dictree--meta-dict-p dict)
2027 (dictree--meta-stack-create dict type reverse)
2028 (trie-stack (dictree--trie dict) type reverse)))
2029
2030
2031 (defun dictree-complete-stack (dict prefix &optional reverse)
2032 "Return an object that allows completions of PREFIX to be accessed
2033 as if they were a stack.
2034
2035 The stack is sorted in \"lexical\" order, i.e. the order defined
2036 by DICT's comparison function, or in reverse order if REVERSE is
2037 non-nil. Calling `dictree-stack-pop' pops the top element (a key
2038 and its associated data) from the stack.
2039
2040 PREFIX must be a sequence (vector, list or string) that forms the
2041 initial part of a TRIE key. (If PREFIX is a string, it must be
2042 possible to apply `string' to individual elements of TRIE keys.)
2043 The completions returned in the alist will be sequences of the
2044 same type as KEY. If PREFIX is a list of sequences, completions
2045 of all sequences in the list are included in the stack. All
2046 sequences in the list must be of the same type.
2047
2048 Note that any modification to DICT *immediately* invalidates all
2049 trie-stacks created before the modification (in particular,
2050 calling `dictree-stack-pop' will give unpredictable results).
2051
2052 Operations on dictree-stacks are significantly more efficient
2053 than constructing a real stack from completions of PREFIX in DICT
2054 and using standard stack functions. As such, they can be useful
2055 in implementing efficient algorithms on tries. However, in cases
2056 where `dictree-complete' or `dictree-complete-ordered' is
2057 sufficient, it is better to use one of those instead."
2058 (if (dictree--meta-dict-p dict)
2059 (dictree--complete-meta-stack-create dict prefix reverse)
2060 (trie-complete-stack (dictree--trie dict) prefix reverse)))
2061
2062
2063 (defun dictree-regexp-stack (dict regexp &optional reverse)
2064 "Return an object that allows REGEXP matches to be accessed
2065 as if they were a stack.
2066
2067 The stack is sorted in \"lexical\" order, i.e. the order defined
2068 by DICT's comparison function, or in reverse order if REVERSE is
2069 non-nil. Calling `dictree-stack-pop' pops the top element (a key
2070 and its associated data) from the stack.
2071
2072 REGEXP is a regular expression, but it need not necessarily be a
2073 string. It must be a sequence (vector, list of string) whose
2074 elements are either elements of the same type as elements of the
2075 trie keys (which behave as literals in the regexp), or any of the
2076 usual regexp special characters and backslash constructs. If
2077 REGEXP is a string, it must be possible to apply `string' to
2078 individual elements of the keys stored in the trie. The matches
2079 returned in the alist will be sequences of the same type as KEY.
2080
2081 Back-references and non-greedy postfix operators are *not*
2082 supported, and the matches are always anchored, so `$' and `^'
2083 lose their special meanings.
2084
2085 If the regexp contains any non-shy grouping constructs, subgroup
2086 match data is included in the results. In this case, the car of
2087 each match is no longer just a key. Instead, it is a list whose
2088 first element is the matching key, and whose remaining elements
2089 are cons cells whose cars and cdrs give the start and end indices
2090 of the elements that matched the corresponding groups, in order.
2091
2092 Note that any modification to DICT *immediately* invalidates all
2093 trie-stacks created before the modification (in particular,
2094 calling `dictree-stack-pop' will give unpredictable results).
2095
2096 Operations on dictree-stacks are significantly more efficient
2097 than constructing a real stack from completions of PREFIX in DICT
2098 and using standard stack functions. As such, they can be useful
2099 in implementing efficient algorithms on tries. However, in cases
2100 where `dictree-complete' or `dictree-complete-ordered' is
2101 sufficient, it is better to use one of those instead."
2102 (if (dictree--meta-dict-p dict)
2103 (dictree--regexp-meta-stack-create dict regexp reverse)
2104 (trie-regexp-stack (dictree--trie dict) regexp reverse)))
2105
2106
2107 (defun dictree-stack-pop (dictree-stack)
2108 "Pop the first element from the DICTREE-STACK.
2109 Returns nil if the stack is empty."
2110 (cond
2111 ;; if elements have been pushed onto a dict stack, pop those first
2112 ;; FIXME: shouldn't be using internal trie functions!
2113 ((and (trie-stack-p dictree-stack)
2114 (trie--stack-pushed dictree-stack))
2115 (trie-stack-pop dictree-stack))
2116 ;; if elements have been pushed onto a meta-dict stack, pop those
2117 ;; first
2118 ((and (dictree--meta-stack-p dictree-stack)
2119 (dictree--meta-stack-pushed dictree-stack))
2120 (pop (dictree--meta-stack-pushed dictree-stack)))
2121 ;; otherwise, pop first element from dictree-stack
2122 (t (let ((popped (dictree--stack-pop dictree-stack)))
2123 (when popped
2124 (cons (car popped) (dictree--cell-data (cdr popped))))))
2125 ))
2126
2127
2128 (defun dictree-stack-push (element dictree-stack)
2129 "Push ELEMENT onto DICTREE-STACK."
2130 (if (trie-stack-p dictree-stack)
2131 ;; normal dict
2132 (trie-stack-push element dictree-stack)
2133 ;; meta-dict
2134 (push element (dictree--meta-stack-pushed dictree-stack))))
2135
2136
2137 (defun dictree-stack-first (dictree-stack)
2138 "Return the first element from DICTREE-STACK, without removing it.
2139 Returns nil if the stack is empty."
2140 ;; if elements have been pushed onto the stack, return first of those
2141 (if (and (dictree--meta-stack-p dictree-stack)
2142 (dictree--meta-stack-pushed dictree-stack))
2143 (car (dictree--meta-stack-pushed dictree-stack))
2144 ;; otherwise, return first element from dictree-stack
2145 (let ((first (dictree--stack-first dictree-stack)))
2146 (cons (car first) (dictree--cell-data (cdr first))))))
2147
2148
2149 (defun dictree-stack-empty-p (dictree-stack)
2150 "Return t if DICTREE-STACK is empty, nil otherwise."
2151 (if (trie-stack-p dictree-stack)
2152 ;; normal dict
2153 (trie-stack-empty-p dictree-stack)
2154 ;; meta-dict
2155 (and (heap-empty (dictree--meta-stack-heap dictree-stack))
2156 (null (dictree--meta-stack-pushed dictree-stack)))))
2157
2158
2159 (defun dictree--stack-first (dictree-stack)
2160 "Return the first element from DICTREE-STACK, without removing it.
2161 Returns nil if the stack is empty."
2162 (if (trie-stack-p dictree-stack)
2163 ;; normal dict
2164 (trie-stack-first dictree-stack)
2165 ;; meta-dict
2166 (if (dictree--meta-stack-pushed dictree-stack)
2167 ;; pushed element
2168 (car (dictree--meta-stack-pushed dictree-stack))
2169 ;; dictree-stack element
2170 (dictree--stack-first
2171 (heap-root (dictree--meta-stack-heap dictree-stack))))))
2172
2173
2174 (defun dictree--stack-pop (dictree-stack)
2175 ;; Pop the raw first element from DICTREE-STACK. Returns nil if the
2176 ;; stack is empty.
2177
2178 ;; dictree-stack for normal dictionaries is a trie-stack
2179 (if (trie-stack-p dictree-stack)
2180 (trie-stack-pop dictree-stack)
2181
2182 ;; meta-dictionary dictree-stack...more work!
2183 ;; if elements have been pushed onto meta-dict stack, pop those
2184 ;; first
2185 (if (dictree--meta-stack-pushed dictree-stack)
2186 (pop (dictree--meta-stack-pushed dictree-stack))
2187 ;; otherwise...
2188 (let ((heap (dictree--meta-stack-heap dictree-stack))
2189 (sortfun (dictree--meta-stack-sortfun dictree-stack))
2190 stack curr next cell)
2191 (unless (heap-empty heap)
2192 ;; remove the first dictree-stack from the heap, pop it's
2193 ;; first element, and add it back to the heap (note that it
2194 ;; will almost certainly not end up at the root again)
2195 (setq stack (heap-delete-root heap))
2196 (setq curr (dictree--stack-pop stack))
2197 (unless (dictree-stack-empty-p stack) (heap-add heap stack))
2198 ;; peek at the first element of the stack now at the root of
2199 ;; the heap
2200 (unless (heap-empty heap)
2201 (setq next (dictree--stack-first (heap-root heap)))
2202 ;; repeat this as long as we keep finding elements with the
2203 ;; same key, combining them together as we go
2204 (when (dictree--meta-stack-combfun dictree-stack)
2205 (while (and (null (funcall sortfun
2206 (car curr) (car next)))
2207 (null (funcall sortfun
2208 (car next) (car curr))))
2209 (setq stack (heap-delete-root heap))
2210 (setq next (dictree--stack-pop stack))
2211 (setq curr
2212 (cons
2213 (car curr)
2214 (dictree--cell-create
2215 (funcall
2216 (dictree--meta-stack-combfun dictree-stack)
2217 (dictree--cell-data (cdr curr))
2218 (dictree--cell-data (cdr next)))
2219 (append (dictree--cell-plist (cdr curr))
2220 (dictree--cell-plist (cdr next))))))
2221 (heap-add heap stack)
2222 (setq next (dictree--stack-first (heap-root heap))))))
2223 ;; return the combined dictionary element
2224 curr)))))
2225
2226
2227
2228
2229 ;; ----------------------------------------------------------------
2230 ;; Functions for building advanced queries
2231
2232 (defun dictree--query
2233 (dict arg cachefun cacheparamfun triefun stackfun
2234 &optional rank-function maxnum reverse no-cache filter resultfun)
2235 ;; Return results of querying DICT with argument ARG using TRIEFUN or
2236 ;; STACKFUN. If result of calling CACHEPARAMFUN on DICT is non-nil,
2237 ;; look first for cached result in cache returned by calling CACHEFUN
2238 ;; on DICT, and cache result if query fulfils caching conditions. If
2239 ;; RANK-FUNCTION is non-nil, return results ordered accordingly. If
2240 ;; MAXNUM is an integer, only the first MAXNUM results will be
2241 ;; returned. If REVERSE is non-nil, results are in reverse order. A
2242 ;; non-nil NO-CACHE prevents caching of results, irrespective of
2243 ;; DICT's cache settings. If supplied, only results that pass FILTER
2244 ;; are included. A non-nil RESULTFUN is applied to results before
2245 ;; adding them to final results list. Otherwise, an alist of key-data
2246 ;; associations is returned.
2247
2248 ;; wrap DICT in a list if necessary
2249 (when (dictree-p dict) (setq dict (list dict)))
2250
2251 (let (cache cacheparam completions cmpl cache-entry)
2252 ;; map over all dictionaries in list
2253 (dolist (dic dict)
2254 (setq cache (funcall cachefun dic)
2255 cacheparam (funcall cacheparamfun dic))
2256 (cond
2257 ;; If FILTER or custom RANK-FUNCTION was specified, look in trie
2258 ;; since we don't cache custom searches. We pass a slightly
2259 ;; redefined filter to `trie-complete' to deal with data
2260 ;; wrapping.
2261 ((or filter
2262 (and rank-function
2263 (not (eq rank-function (dictree-rank-function dic)))))
2264 (setq cmpl
2265 (dictree--do-query dic arg triefun stackfun
2266 (dictree--wrap-rankfun rank-function)
2267 maxnum reverse
2268 (when filter
2269 (dictree--wrap-filter filter)))))
2270
2271
2272 ;; if there's a cached result with enough completions, use it
2273 ((and (setq cache-entry
2274 (if cacheparam
2275 (gethash (cons arg reverse) cache)
2276 nil))
2277 (or (null (dictree--cache-maxnum cache-entry))
2278 (and maxnum
2279 (<= maxnum (dictree--cache-maxnum cache-entry)))))
2280 (setq cmpl (dictree--cache-results cache-entry))
2281 ;; drop any excess completions
2282 (when (and maxnum
2283 (or (null (dictree--cache-maxnum cache-entry))
2284 (> (dictree--cache-maxnum cache-entry) maxnum)))
2285 (setcdr (nthcdr (1- maxnum) completions) nil)))
2286
2287
2288 ;; if there was nothing useful in the cache, do query and time it
2289 (t
2290 (let (time)
2291 (setq time (float-time))
2292 (setq cmpl
2293 (dictree--do-query
2294 dic arg triefun stackfun
2295 (when rank-function
2296 (dictree--wrap-rankfun rank-function))
2297 maxnum reverse nil))
2298 (setq time (- (float-time) time))
2299 ;; if we're above the dictionary's completion cache threshold,
2300 ;; cache the result
2301 (when (and (not no-cache)
2302 (dictree--above-cache-threshold-p
2303 time (length arg) (dictree-cache-policy dic)
2304 cacheparam))
2305 (setf (dictree-modified dic) t)
2306 (puthash (cons arg reverse)
2307 (dictree--cache-create cmpl maxnum)
2308 cache)))))
2309
2310 ;; merge new completion into completions list
2311 (setq completions
2312 (dictree--merge
2313 completions cmpl
2314 (if rank-function
2315 (dictree--wrap-rankfun rank-function)
2316 `(lambda (a b)
2317 (,(trie-construct-sortfun
2318 (dictree-comparison-function (car dict)))
2319 (car a) (car b))))
2320 nil maxnum)))
2321
2322 ;; return completions list, applying RESULTFUN is specified,
2323 ;; otherwise just stripping meta-data
2324 (mapcar
2325 (if resultfun
2326 (dictree--wrap-resultfun resultfun)
2327 (lambda (el) (cons (car el) (dictree--cell-data (cdr el)))))
2328 completions)))
2329
2330
2331
2332 (defun dictree--do-query
2333 (dict arg triefun stackfun &optional rank-function maxnum reverse filter)
2334 ;; Return first MAXNUM results of querying DICT with ARG using TRIEFUN
2335 ;; or STACKFUN that satisfy FILTER, ordered according to RANK-FUNCTION
2336 ;; (defaulting to "lexical" order).
2337
2338 ;; for a meta-dict, use a dictree-stack
2339 (if (dictree--meta-dict-p dict)
2340 (let ((stack (funcall stackfun dict arg reverse))
2341 (heap (when rank-function
2342 (heap-create ; heap order is inverse of rank order
2343 (if reverse
2344 rank-function
2345 (lambda (a b)
2346 (not (funcall rank-function a b))))
2347 (1+ maxnum))))
2348 (i 0) cmpl completions)
2349 ;; pop MAXNUM completions from the stack
2350 (while (and (or (null maxnum) (< i maxnum))
2351 (setq cmpl (dictree--stack-pop stack)))
2352 ;; check completion passes FILTER
2353 (when (or (null filter) (funcall filter cmpl))
2354 (if rank-function
2355 (heap-add heap cmpl) ; for ranked query, add to heap
2356 (push cmpl completions)) ; for lexical query, add to list
2357 (incf i)))
2358 (if (null rank-function)
2359 ;; for lexical query, reverse and return completion list (we
2360 ;; built it backwards)
2361 (nreverse completions)
2362 ;; for ranked query, pass rest of completions through heap
2363 (while (setq cmpl (dictree--stack-pop stack))
2364 (heap-add heap cmpl)
2365 (heap-delete-root heap))
2366 ;; extract completions from heap
2367 (while (setq cmpl (heap-delete-root heap))
2368 (push cmpl completions))
2369 completions)) ; return completion list
2370
2371 ;; for a normal dict, call corresponding trie function on dict's
2372 ;; trie. Note: could use a dictree-stack here too - would it be more
2373 ;; efficient?
2374 (funcall triefun
2375 (dictree--trie dict) arg rank-function
2376 maxnum reverse filter)))
2377
2378
2379
2380 ;; ----------------------------------------------------------------
2381 ;; Completing
2382
2383 (defun dictree-complete
2384 (dict prefix
2385 &optional rank-function maxnum reverse no-cache filter resultfun)
2386 "Return an alist containing all completions of PREFIX in DICT
2387 along with their associated data, sorted according to
2388 RANK-FUNCTION (defaulting to \"lexical\" order, i.e. the order
2389 defined by the dictionary's comparison function,
2390 cf. `dictree-create'). Return nil if no completions are found.
2391
2392 PREFIX can also be a list of sequences, in which case completions of
2393 all elements in the list are returned, merged together in a
2394 single sorted alist.
2395
2396 DICT can also be a list of dictionaries, in which case
2397 completions are sought in all dictionaries in the list. (Note
2398 that if the same key appears in multiple dictionaries, the alist
2399 may contain the same key multiple times, each copy associated
2400 with the data from a different dictionary. If you want to combine
2401 identical keys, use a meta-dictionary; see
2402 `dictree-meta-dict-create'.)
2403
2404 If optional argument RANK-FUNCTION is any non-nil value that is
2405 not a function, the completions are sorted according to the
2406 dictionary's rank-function (see `dictree-create'). Any non-nil
2407 value that *is* a function over-rides this. In that case,
2408 RANK-FUNCTION should accept two arguments, both cons cells. The
2409 car of each contains a sequence from the trie (of the same type
2410 as PREFIX), the cdr contains its associated data. The
2411 RANK-FUNCTION should return non-nil if first argument is ranked
2412 strictly higher than the second, nil otherwise.
2413
2414 The optional integer argument MAXNUM limits the results to the
2415 first MAXNUM completions. The default is to return all matches.
2416
2417 If the optional argument NO-CACHE is non-nil, it prevents caching
2418 of the result. Ignored for dictionaries that do not have
2419 completion caching enabled.
2420
2421 The FILTER argument sets a filter function for the
2422 completions. For each potential completion, it is passed two
2423 arguments: the completion, and its associated data. If the filter
2424 function returns nil, the completion is not included in the
2425 results, and doesn't count towards MAXNUM.
2426
2427 RESULTFUN defines a function used to process results before
2428 adding them to the final result list. If specified, it should
2429 accept two arguments: a key and its associated data. It's return
2430 value is what gets added to the final result list, instead of the
2431 default key-data cons cell."
2432 ;; run completion query
2433 (dictree--query
2434 dict prefix
2435 (if rank-function
2436 'dictree-complete-ranked-cache
2437 'dictree-complete-cache)
2438 (if rank-function
2439 'dictree-complete-ranked-cache-threshold
2440 'dictree-complete-cache-threshold)
2441 'trie-complete 'dictree-complete-stack
2442 (when rank-function
2443 (if (functionp rank-function)
2444 rank-function
2445 (dictree-rank-function (if (listp dict) (car dict) dict))))
2446 maxnum reverse no-cache filter resultfun))
2447
2448
2449
2450 (defun dictree-collection-function (dict string predicate all)
2451 "Function for use in `try-completion', `all-completions',
2452 and `completing-read'. To complete from dictionary DICT, use the
2453 following as the COLLECTION argument of any of those functions:
2454
2455 (lambda (string predicate all)
2456 (dictree-collection-function dict string predicate all))
2457
2458 Note that PREDICATE will be called with two arguments: the
2459 completion, and its associated data."
2460 (let ((completions
2461 (dictree-complete dict string nil nil nil nil
2462 predicate (lambda (key data) key))))
2463 (if all completions (try-completion "" completions))))
2464
2465
2466
2467 ;; ----------------------------------------------------------------
2468 ;; Regexp search
2469
2470 (defun dictree-regexp-search
2471 (dict regexp
2472 &optional rank-function maxnum reverse no-cache filter resultfun)
2473 "Return an alist containing all matches for REGEXP in TRIE
2474 along with their associated data, in the order defined by
2475 RANKFUN, defauling to \"lexical\" order (i.e. the order defined
2476 by the trie's comparison function). If REVERSE is non-nil, the
2477 completions are sorted in the reverse order. Returns nil if no
2478 completions are found.
2479
2480 DICT can also be a list of dictionaries, in which case matches
2481 are sought in all dictionaries in the list. (Note that if the
2482 same key appears in multiple dictionaries, the alist may contain
2483 the same key multiple times, each copy associated with the data
2484 from a different dictionary. If you want to combine identical
2485 keys, use a meta-dictionary; see `dictree-meta-dict-create'.)
2486
2487 REGEXP is a regular expression, but it need not necessarily be a
2488 string. It must be a sequence (vector, list of string) whose
2489 elements are either elements of the same type as elements of the
2490 trie keys (which behave as literals in the regexp), or any of the
2491 usual regexp special characters and backslash constructs. If
2492 REGEXP is a string, it must be possible to apply `string' to
2493 individual elements of the keys stored in the trie. The matches
2494 returned in the alist will be sequences of the same type as KEY.
2495
2496 Only a subset of the full Emacs regular expression syntax is
2497 supported. There is no support for regexp constructs that are
2498 only meaningful for strings (character ranges and character
2499 classes inside character alternatives, and syntax-related
2500 backslash constructs). Back-references and non-greedy postfix
2501 operators are not supported, so `?' after a postfix operator
2502 loses its special meaning. Also, matches are always anchored, so
2503 `$' and `^' lose their special meanings (use `.*' at the
2504 beginning and end of the regexp to get an unanchored match).
2505
2506 If the regexp contains any non-shy grouping constructs, subgroup
2507 match data is included in the results. In this case, the car of
2508 each match is no longer just a key. Instead, it is a list whose
2509 first element is the matching key, and whose remaining elements
2510 are cons cells whose cars and cdrs give the start and end indices
2511 of the elements that matched the corresponding groups, in order.
2512
2513 If optional argument RANK-FUNCTION is any non-nil value that is
2514 not a function, the matches are sorted according to the
2515 dictionary's rank-function (see `dictree-create'). Any non-nil
2516 value that *is* a function over-rides this. In that case,
2517 RANK-FUNCTION should accept two arguments, both cons cells. The
2518 car of each contains a sequence from the dictionary (of the same
2519 type as PREFIX), the cdr contains its associated data. The
2520 RANK-FUNCTION should return non-nil if first argument is ranked
2521 strictly higher than the second, nil otherwise.
2522
2523 The optional integer argument MAXNUM limits the results to the
2524 first MAXNUM matches. The default is to return all matches.
2525
2526 If the optional argument NO-CACHE is non-nil, it prevents caching
2527 of the result. Ignored for dictionaries that do not have wildcard
2528 caching enabled.
2529
2530 The FILTER argument sets a filter function for the matches. If
2531 supplied, it is called for each possible match with two
2532 arguments: the matching key, and its associated data. If the
2533 filter function returns nil, the match is not included in the
2534 results, and does not count towards MAXNUM.
2535
2536 RESULTFUN defines a function used to process results before
2537 adding them to the final result list. If specified, it should
2538 accept two arguments: a key and its associated data. It's return
2539 value is what gets added to the final result list, instead of the
2540 default key-data cons cell."
2541 ;; run regexp query
2542 (dictree--query
2543 dict regexp
2544 (if rank-function
2545 'dictree-regexp-ranked-cache
2546 'dictree-regexp-cache)
2547 (if rank-function
2548 'dictree-regexp-ranked-cache-threshold
2549 'dictree-regexp-cache-threshold)
2550 'trie-regexp-search 'dictree-regexp-stack
2551 (when rank-function
2552 (if (functionp rank-function)
2553 rank-function
2554 (dictree-rank-function (if (listp dict) (car dict) dict))))
2555 maxnum reverse no-cache filter resultfun))
2556
2557
2558
2559
2560 ;; ----------------------------------------------------------------
2561 ;; Persistent storage
2562
2563 (defun dictree-save (dict &optional compilation)
2564 "Save dictionary DICT to it's associated file.
2565 Use `dictree-write' to save to a different file.
2566
2567 Optional argument COMPILATION determines whether to save the
2568 dictionary in compiled or uncompiled form. The default is to save
2569 both forms. See `dictree-write'.
2570
2571 Interactively, DICT is read from the mini-buffer."
2572 (interactive (list (read-dict "Dictionary: ")))
2573
2574 (let ((filename (dictree-filename dict)))
2575
2576 ;; if dictionary has no associated file, prompt for one
2577 (unless (and filename (> (length filename) 0))
2578 (setq filename
2579 (read-file-name
2580 (format "Save dictionary %s to file\
2581 (leave blank to NOT save): "
2582 (dictree-name dict))
2583 nil "")))
2584
2585 ;; if filename is blank, don't save
2586 (if (string= filename "")
2587 (message "Dictionary %s NOT saved" (dictree-name dict))
2588 ;; otherwise write dictionary to file
2589 (setf (dictree-filename dict) filename)
2590 (dictree-write dict filename t compilation))))
2591
2592
2593
2594 (defun dictree-write (dict &optional filename overwrite compilation)
2595 "Write dictionary DICT to file FILENAME.
2596 Defaults to dictionary's current filename if FILENAME is not
2597 specified (like `dictree-save').
2598
2599 If optional argument OVERWRITE is non-nil, no confirmation will
2600 be asked for before overwriting an existing file.
2601
2602 The default is to create both compiled and uncompiled versions of
2603 the dictionary, with extensions .elc and .el respectively (if
2604 FILENAME has either of these extensions, they are stripped off
2605 before proceeding). The compiled version is always used in
2606 preference to the uncomplied version, as it loads
2607 faster. However, only the uncompiled version is portable between
2608 different Emacs versions.
2609
2610 If optional argument COMPILATION is the symbol 'compiled, only
2611 the compiled version will be created, whereas if it is the symbol
2612 'uncompiled, only the uncompiled version will be created.
2613
2614 Interactively, DICT and FILENAME are read from the mini-buffer,
2615 and OVERWRITE is the prefix argument."
2616 (interactive (list (read-dict "Dictionary: ")
2617 (read-file-name "Write dictionary to file: "
2618 nil "")
2619 current-prefix-arg))
2620 ;; default to DICT's current file, if any
2621 (when (or (null filename)
2622 (and (called-interactively-p 'any) (string= filename "")))
2623 (setq filename (dictree-filename dict)))
2624 (if (null filename)
2625 (progn
2626 (message "Dictionary %s NOT written" (dictree-name dict))
2627 nil) ; indicate dictionary wasn't written
2628
2629 (let (dictname buff tmpfile)
2630 ;; remove any .el(c) extension from filename
2631 (cond
2632 ((and (> (length filename) 3)
2633 (string= (substring filename -3) ".el"))
2634 (setq filename (substring filename 0 -3)))
2635 ((and (> (length filename) 4)
2636 (string= (substring filename -4) ".elc"))
2637 (setq filename (substring filename 0 -4))))
2638 ;; create saved dictionary name from filename
2639 (setq dictname (file-name-nondirectory filename))
2640
2641 (save-excursion
2642 ;; create a temporary file
2643 (setq buff
2644 (find-file-noselect
2645 (setq tmpfile (make-temp-file dictname))))
2646 (set-buffer buff)
2647 ;; call the appropriate write function to write the dictionary
2648 ;; code
2649 (if (dictree--meta-dict-p dict)
2650 (dictree--write-meta-dict-code dict dictname filename)
2651 (dictree--write-dict-code dict dictname filename))
2652 (save-buffer)
2653 (kill-buffer buff))
2654
2655 ;; prompt to overwrite if necessary
2656 (when (or overwrite
2657 (and
2658 (or (eq compilation 'compiled)
2659 (not (file-exists-p (concat filename ".el"))))
2660 (or (eq compilation 'uncompiled)
2661 (not (file-exists-p (concat filename ".elc")))))
2662 (y-or-n-p
2663 (format "File %s already exists. Overwrite? "
2664 (concat filename ".el(c)"))))
2665 (condition-case nil
2666 (progn
2667 ;; move the uncompiled version to its final destination
2668 (unless (eq compilation 'compiled)
2669 (copy-file tmpfile (concat filename ".el") t))
2670 ;; byte-compile and move the compiled version to its final
2671 ;; destination
2672 (unless (eq compilation 'uncompiled)
2673 (if (save-window-excursion
2674 (let ((restore byte-compile-disable-print-circle)
2675 err)
2676 (setq byte-compile-disable-print-circle t)
2677 (setq err (byte-compile-file tmpfile))
2678 (setq byte-compile-disable-print-circle restore)
2679 err))
2680 (rename-file (concat tmpfile ".elc")
2681 (concat filename ".elc") t)
2682 (error ""))))
2683 (error "Error writing dictionary. Dictionary %s NOT saved"
2684 dictname))
2685
2686 ;; if writing to a different name, unload dictionary under old
2687 ;; name and reload it under new one
2688 (setf (dictree-modified dict) nil)
2689 (setf (dictree-filename dict) filename)
2690 (unless (string= dictname (dictree-name dict))
2691 (dictree-unload dict)
2692 (dictree-load filename)))
2693
2694 (delete-file tmpfile)
2695 (message "Dictionary %s saved to %s" dictname filename)
2696 t) ; return t to indicate dictionary was successfully saved
2697 ))
2698
2699
2700
2701 (defun dictree-save-modified (&optional dict ask compilation force
2702 no-fail-query)
2703 "Save all modified dictionaries that have their autosave flag set.
2704 Returns t if all dictionaries were successfully saved. Otherwise,
2705 inform the user about the dictionaries which failed to save
2706 properly, ask them whether they wish to continue anyway, and
2707 return t or nil accordingly.
2708
2709 If optional argument DICT is a list of dictionaries or a single
2710 dictionary, only save those.
2711
2712 If optional argument ASK is non-nil, ask for confirmation before
2713 saving.
2714
2715 Optional argument COMPILATION determines whether to save the
2716 dictionaries in compiled or uncompiled form. The default is to
2717 save both forms. See `dictree-write'.
2718
2719 If optional argument FORCE is non-nil, save modified dictionaries
2720 irrespective of their autosave flag.
2721
2722 If optional argument NO-FAIL-QUERY is non-nil, the user will not
2723 be queried if a dictionary fails to save properly, and the return
2724 value is always nil.
2725
2726 Interactively, FORCE is the prefix argument, and the user will not be
2727 asked whether they wish to continue after a failed save."
2728 (interactive "P")
2729
2730 ;; sort out arguments
2731 (when (and (called-interactively-p 'any) dict) (setq dict nil force t))
2732 (when (dictree-p dict) (setq dict (list dict)))
2733
2734 ;; For each dictionary in list / each loaded dictionary, check if
2735 ;; dictionary has been modified. If so, save it if autosave is set or
2736 ;; FORCE is non-nil.
2737 (let (save-failures)
2738 (dolist (dic (if (null dict)
2739 dictree-loaded-list
2740 dict))
2741 (when (and (dictree-modified dic)
2742 (or force (dictree-autosave dic))
2743 (or (not ask)
2744 (y-or-n-p (format "Save modified dictionary %s? "
2745 (dictree-filename dic)))))
2746 (condition-case nil
2747 (progn
2748 (dictree-save dic compilation)
2749 (setf (dictree-modified dic) nil))
2750 (error (push dic save-failures)))))
2751
2752 ;; prompt if dictionary saving failed
2753 (if save-failures
2754 (if (or (called-interactively-p 'any) no-fail-query)
2755 (progn
2756 (message
2757 (concat
2758 "Error: failed to save the following modified "
2759 "dictionaries: "
2760 (mapconcat 'dictree--name save-failures ", ")))
2761 nil)
2762 (yes-or-no-p
2763 (concat "Error: failed to save the following modified "
2764 "dictionaries: "
2765 (mapconcat 'dictree--name save-failures ", ")
2766 "; continue anyway? ")))
2767 t)))
2768
2769
2770 ;; Add the dictree-save-modified function to the kill-emacs-hook to save
2771 ;; modified dictionaries when exiting emacs
2772 (add-hook 'kill-emacs-query-functions 'dictree-save-modified)
2773
2774
2775
2776 ;;;###autoload
2777 (defun dictree-load (file)
2778 "Load a dictionary object from file FILE.
2779 Returns the dictionary if successful, nil otherwise.
2780
2781 Interactively, FILE is read from the mini-buffer."
2782 (interactive (list (read-dict "Load dictionary: " nil nil t t)))
2783
2784 ;; sort out dictionary name and file name
2785 (if (dictree-p file)
2786 (message "Dictionary %s already loaded" (dictree-name file))
2787
2788 ;; load the dictionary
2789 (if (not (load file t))
2790 ;; if loading failed, throw error interactively, return nil
2791 ;; non-interactively
2792 (if (called-interactively-p 'any)
2793 (error "Cannot open dictionary file: %s" file)
2794 nil)
2795
2796 (let (dictname dict)
2797 (setq dictname
2798 (file-name-nondirectory (file-name-sans-extension file))
2799 dict (eval (intern-soft dictname)))
2800 (if (not (dictree-p dict))
2801 ;; if loading failed, throw error interactively, return nil
2802 ;; non-interactively
2803 (if (called-interactively-p 'any)
2804 (error "Error loading dictionary file: %s" file)
2805 nil)
2806
2807 ;; ensure the dictionary name and file name associated with
2808 ;; the dictionary match the file it was loaded from
2809 (when (and (string= (file-name-nondirectory file) file)
2810 (setq file
2811 (locate-file file load-path load-suffixes)))
2812 (setf (dictree-filename dict) file))
2813 (setf (dictree-name dict) dictname)
2814
2815 ;; make sure the dictionary is in dictree-loaded-list
2816 ;; (normally the lisp code in the dictionary itself should do
2817 ;; this, but just to make sure...)
2818 (unless (memq dict dictree-loaded-list)
2819 (push dict dictree-loaded-list))
2820 (message (format "Loaded dictionary %s" dictname))
2821
2822 ;; return dictionary
2823 dict)))))
2824
2825
2826
2827 (defun dictree-unload (dict &optional dont-save)
2828 "Unload dictionary DICT.
2829 If optional argument DONT-SAVE is non-nil, the dictionary will
2830 NOT be saved even if its autosave flag is set.
2831
2832 Interactively, DICT is read from the mini-buffer, and DONT-SAVE
2833 is the prefix argument."
2834 (interactive (list (read-dict "Dictionary: ")
2835 current-prefix-arg))
2836
2837 ;; if dictionary has been modified, autosave is set and not overidden,
2838 ;; save it first
2839 (when (and (dictree-modified dict)
2840 (null dont-save)
2841 (or (eq (dictree-autosave dict) t)
2842 (and (eq (dictree-autosave dict) 'ask)
2843 (y-or-n-p
2844 (format
2845 "Dictionary %s modified.\
2846 Save before unloading? "
2847 (dictree-name dict))))))
2848 (dictree-save dict))
2849
2850 ;; if unloading a meta-dict, remove reference to it from constituent
2851 ;; dictionaries' meta-dict-list cell
2852 (when (dictree--meta-dict-p dict)
2853 (mapc
2854 (lambda (dic)
2855 (setf (dictree--meta-dict-list dic)
2856 (delq dict (dictree--meta-dict-list dic))))
2857 (dictree--meta-dict-dictlist dict)))
2858
2859 ;; remove dictionary from list of loaded dictionaries and unload it
2860 (setq dictree-loaded-list (delq dict dictree-loaded-list))
2861 (unintern (dictree-name dict))
2862 (message "Dictionary %s unloaded" (dictree-name dict)))
2863
2864
2865
2866 (defun dictree--write-dict-code (dict dictname filename)
2867 ;; Write code for normal dictionary DICT to current buffer, giving it
2868 ;; the name DICTNAME and file FILENAME.
2869 (let (hashcode tmpdict tmptrie lookup-alist
2870 complete-alist complete-ranked-alist
2871 regexp-alist regexp-ranked-alist)
2872
2873 ;; --- convert trie data ---
2874 ;; if dictionary doesn't use any custom save functions, write
2875 ;; dictionary's trie directly as is
2876 (setq tmptrie (dictree--trie dict))
2877 ;; otherwise, create a temporary trie and populate it with the
2878 ;; converted contents of the dictionary's trie
2879 (when (or (dictree--data-savefun dict)
2880 (dictree--plist-savefun dict))
2881 (setq tmptrie
2882 (trie-create-custom
2883 (trie-comparison-function tmptrie)
2884 :createfun (trie--createfun tmptrie)
2885 :insertfun (trie--insertfun tmptrie)
2886 :deletefun (trie--deletefun tmptrie)
2887 :lookupfun (trie--lookupfun tmptrie)
2888 :mapfun (trie--mapfun tmptrie)
2889 :emptyfun (trie--emptyfun tmptrie)
2890 :stack-createfun (trie--stack-createfun tmptrie)
2891 :stack-popfun (trie--stack-popfun tmptrie)
2892 :stack-emptyfun (trie--stack-emptyfun tmptrie)))
2893 (trie-mapc
2894 (lambda (key cell)
2895 (trie-insert tmptrie key
2896 (dictree--cell-create
2897 (funcall (or (dictree--data-savefun dict)
2898 'identity)
2899 (dictree--cell-data cell))
2900 (funcall (or (dictree--plist-savefun dict)
2901 'identity)
2902 (dictree--cell-plist cell)))))
2903 (dictree--trie dict))
2904
2905 ;; generate code to convert contents of trie back to original form
2906 (setq hashcode
2907 (concat
2908 hashcode
2909 " (trie-map\n"
2910 " (lambda (key cell)\n"
2911 " (dictree--cell-create\n"
2912 (if (dictree--data-loadfun dict)
2913 (concat
2914 "(funcall (dictree--data-loadfun " dictname ")\n"
2915 " (dictree--cell-data cell))\n")
2916 " (dictree--cell-data cell)\n")
2917 (if (dictree--plist-loadfun dict)
2918 (concat
2919 "(funcall (dictree--plist-loadfun " dictname ")\n"
2920 " (dictree--cell-plist cell))))\n")
2921 " (dictree--cell-plist cell)))\n")
2922 " (dictree--trie " dictname "))\n")))
2923
2924
2925 ;; --- convert caches for writing to file ---
2926 ;; hash tables have no read syntax in older Emacsen, so we convert
2927 ;; them to alists for writing
2928 (unless (featurep 'hashtable-print-readable)
2929 ;; convert lookup cache hash table to alist, if it exists
2930 (when (dictree--lookup-cache-threshold dict)
2931 (maphash
2932 (lambda (key val)
2933 (push
2934 (cons key
2935 (cons (mapcar 'car (dictree--cache-results val))
2936 (dictree--cache-maxnum val)))
2937 lookup-alist))
2938 (dictree--lookup-cache dict))
2939 ;; generate code to reconstruct the lookup hash table
2940 (setq hashcode
2941 (concat
2942 hashcode
2943 "(let ((lookup-cache (make-hash-table :test 'equal))\n"
2944 " (trie (dictree--trie " dictname ")))\n"
2945 " (mapc\n"
2946 " (lambda (entry)\n"
2947 " (puthash\n"
2948 " (car entry)\n"
2949 " (dictree--cache-create\n"
2950 " (mapcar\n"
2951 " (lambda (key)\n"
2952 " (cons key (trie-member trie key)))\n"
2953 " (dictree--cache-results (cdr entry)))\n"
2954 " (dictree--cache-maxnum (cdr entry)))\n"
2955 " lookup-cache))\n"
2956 " (dictree--lookup-cache " dictname "))\n"
2957 " (setf (dictree--lookup-cache " dictname ")\n"
2958 " lookup-cache))\n")))
2959
2960 ;; convert query caches, if they exist
2961 (dolist (cache-details
2962 '((dictree--complete-cache-threshold
2963 complete-alist dictree--complete-cache)
2964 (dictree--complete-ranked-cache-threshold
2965 complete-ranked-alist dictree--complete-ranked-cache)
2966 (dictree--regexp-cache-threshold
2967 regexp-alist dictree--regexp-cache)
2968 (dictree--regexp-ranked-cache-threshold
2969 regexp-ranked-alist dictree--regexp-ranked-cache)))
2970 (when (funcall (nth 0 cache-details) dict)
2971 ;; convert hash table to alist
2972 (set (nth 1 cache-details)
2973 (let (alist)
2974 (maphash
2975 (lambda (key val)
2976 (push
2977 (cons key
2978 (cons
2979 (mapcar 'car (dictree--cache-results val))
2980 (dictree--cache-maxnum val)))
2981 alist))
2982 (funcall (nth 2 cache-details) dict))
2983 alist))
2984 ;; generate code to reconstruct hash table from alist
2985 (setq
2986 hashcode
2987 (concat
2988 hashcode
2989 "(let ((cache (make-hash-table :test 'equal))\n"
2990 " (trie (dictree--trie " dictname ")))\n"
2991 " (mapc\n"
2992 " (lambda (entry)\n"
2993 " (puthash\n"
2994 " (car entry)\n"
2995 " (dictree--cache-create\n"
2996 " (mapcar\n"
2997 " (lambda (key)\n"
2998 " (cons key\n"
2999 " (trie-member\n"
3000 " trie (if (stringp key) key (car key)))))\n"
3001 " (dictree--cache-results (cdr entry)))\n"
3002 " (dictree--cache-maxnum (cdr entry)))\n"
3003 " cache))\n"
3004 " (" (symbol-name (nth 2 cache-details)) " " dictname "))\n"
3005 " (setf (" (symbol-name (nth 2 cache-details)) " "
3006 dictname ")\n"
3007 " cache))\n")))))
3008
3009
3010 ;; --- write to file ---
3011 ;; generate the structure to save
3012 (setq tmpdict (dictree--copy dict))
3013 (setf (dictree--trie tmpdict) tmptrie
3014 (dictree--name tmpdict) dictname
3015 (dictree--filename tmpdict) filename
3016 (dictree--modified tmpdict) nil
3017 (dictree--meta-dict-list tmpdict) nil)
3018 (unless (featurep 'hashtable-print-readable)
3019 (setf (dictree--lookup-cache tmpdict) lookup-alist
3020 (dictree--complete-cache tmpdict) complete-alist
3021 (dictree--complete-ranked-cache tmpdict) complete-ranked-alist
3022 (dictree--regexp-cache tmpdict) regexp-alist
3023 (dictree--regexp-ranked-cache tmpdict) regexp-ranked-alist))
3024
3025 ;; write lisp code that generates the dictionary object
3026 (let ((print-circle t) (print-level nil) (print-length nil))
3027 (insert "(eval-when-compile (require 'cl))\n")
3028 (insert "(require 'dict-tree)\n")
3029 (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n")
3030 (insert "(setq " dictname " " (prin1-to-string tmpdict) ")\n")
3031 (when hashcode (insert hashcode))
3032 (insert "(unless (memq " dictname " dictree-loaded-list)\n"
3033 " (push " dictname " dictree-loaded-list))\n"))))
3034
3035
3036
3037 (defun dictree--write-meta-dict-code (dict dictname filename)
3038 ;; Write code for meta-dictionary DICT to current buffer, giving it
3039 ;; the name DICTNAME and file FILENAME.
3040 (let (hashcode tmpdict lookup-alist
3041 complete-alist complete-ranked-alist
3042 regexp-alist regexp-ranked-alist)
3043
3044 ;; --- convert caches for writing to file ---
3045 ;; hash tables have no read syntax in older Emacsen, so we convert
3046 ;; them to alists for writing
3047 (unless (featurep 'hashtable-print-readable)
3048 ;; convert lookup cache hash table to an alist, if it exists
3049 (when (dictree--meta-dict-lookup-cache-threshold dict)
3050 (maphash (lambda (key val)
3051 (push (cons key (mapcar 'car val)) lookup-alist))
3052 (dictree--meta-dict-lookup-cache dict))
3053 ;; generate code to reconstruct the lookup hash table
3054 (setq hashcode
3055 (concat
3056 hashcode
3057 "(let ((cache (make-hash-table :test 'equal)))\n"
3058 " (mapc (lambda (entry)\n"
3059 " (puthash (car entry) (cdr entry) cache))\n"
3060 " (dictree--meta-dict-lookup-cache " dictname "))\n"
3061 " (setf (dictree--meta-dict-lookup-cache " dictname ")\n"
3062 " cache))\n")))
3063
3064 ;; convert query caches, if they exist
3065 (dolist (cache-details
3066 '((dictree--meta-dict-complete-cache-threshold
3067 complete-alist
3068 dictree--meta-dict-complete-cache)
3069 (dictree--meta-dict-complete-ranked-cache-threshold
3070 complete-ranked-alist
3071 dictree--meta-dict-complete-ranked-cache)
3072 (dictree--meta-dict-regexp-cache-threshold
3073 regexp-alist
3074 dictree--meta-dict-regexp-cache)
3075 (dictree--meta-dict-regexp-ranked-cache-threshold
3076 regexp-ranked-alist
3077 dictree--meta-dict-regexp-ranked-cache)))
3078 (when (funcall (nth 0 cache-details) dict)
3079 ;; convert hash table to alist
3080 (set (nth 1 cache-details)
3081 (let (alist)
3082 (maphash
3083 (lambda (key val) (push (cons key val) alist))
3084 (funcall (nth 2 cache-details) dict))
3085 alist))
3086 ;; generate code to reconstruct hash table from alist
3087 (setq
3088 hashcode
3089 (concat
3090 hashcode
3091 "(let ((cache (make-hash-table :test 'equal)))\n"
3092 " (mapc (lambda (entry)\n"
3093 " (puthash (car entry) (cdr entry) cache))\n"
3094 " (" (symbol-name (nth 2 cache-details)) " "
3095 dictname "))\n"
3096 " (setf (" (symbol-name (nth 2 cache-details)) " "
3097 dictname ")\n"
3098 " cache))\n")))))
3099
3100
3101 ;; --- write to file ---
3102 ;; generate the structure to save
3103 (setq tmpdict (dictree--meta-dict-copy dict))
3104 (setf (dictree--meta-dict-name tmpdict) dictname
3105 (dictree--meta-dict-filename tmpdict) filename
3106 (dictree--meta-dict-modified tmpdict) nil
3107 (dictree--meta-dict-meta-dict-list tmpdict) nil
3108 (dictree--meta-dict-dictlist tmpdict)
3109 (mapcar (lambda (dic) (intern (dictree-name dic)))
3110 (dictree--meta-dict-dictlist dict)))
3111 (unless (featurep 'hashtable-print-readable)
3112 (setf (dictree--meta-dict-lookup-cache tmpdict) lookup-alist
3113 (dictree--meta-dict-complete-cache tmpdict) complete-alist
3114 (dictree--meta-dict-complete-ranked-cache tmpdict)
3115 complete-ranked-alist
3116 (dictree--meta-dict-regexp-cache tmpdict) regexp-alist
3117 (dictree--meta-dict-regexp-ranked-cache tmpdict)
3118 regexp-ranked-alist))
3119
3120 ;; write lisp code that generates the dictionary object
3121 (let ((print-circle t) (print-level nil) (print-length nil))
3122 (insert "(eval-when-compile (require 'cl))\n"
3123 "(require 'dict-tree)\n")
3124 (mapc
3125 (lambda (dic)
3126 (insert "(unless (dictree-load \"" (dictree-filename dic) "\")\n"
3127 " (error \"Failed to load dictionary \\\""
3128 (dictree-name dic) "\\\" required by meta-dict \\\""
3129 dictname "\\\"\"))\n"))
3130 (dictree--meta-dict-dictlist dict))
3131 (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n"
3132 "(setq " dictname " " (prin1-to-string tmpdict) ")\n"
3133 "(setf (dictree--meta-dict-dictlist " dictname ")\n"
3134 " (mapcar 'eval (dictree--meta-dict-dictlist "
3135 dictname ")))\n")
3136 (when hashcode (insert hashcode))
3137 (insert "(unless (memq " dictname " dictree-loaded-list)"
3138 " (push " dictname " dictree-loaded-list))\n"))))
3139
3140
3141
3142 ;; ----------------------------------------------------------------
3143 ;; Dumping and restoring contents
3144
3145 (defun dictree-populate-from-file
3146 (dict file
3147 &optional insert-function key-loadfun data-loadfun plist-loadfun
3148 balance)
3149 "Populate dictionary DICT from the key list in file FILE.
3150
3151 Each line of FILE should contain a key, either a string
3152 \(delimited by \"\), a vector, or a list. (Use the escape
3153 sequence \\\" to include a \" in a string.) If a line does not
3154 contain a key, it is silently ignored.
3155
3156 Each line can optionally include data and a property list (in
3157 that order) to be associated with the key. If present, these
3158 should separated from each other and the key by whitespace.
3159
3160 INSERT-FUNCTION, KEY-LOAD-FUNCTION, DATA-LOAD-FUNCTION and
3161 PLIST-LOAD-FUNCTION override the corresponding default functions
3162 for DICT (see `dictree-create').
3163
3164 Interactively, DICT and FILE are read from the mini-buffer.
3165
3166
3167 Technicalities:
3168
3169 The key, data and property list are read as lisp expressions
3170 using `read'. The keys will be read from FILE in order, unless
3171 BALANCE is non-nil, in which case they are read from the median
3172 element outwards (which can help ensure efficient data structures
3173 are created when using a trie that is not self-balancing, see
3174 `dictree-create')."
3175 (interactive (list (read-dict "Dictionary: ")
3176 (read-file-name "File to populate from: "
3177 nil "" t)))
3178
3179 (if (and (called-interactively-p 'any) (string= file ""))
3180 (message "No file specified; dictionary %s NOT populated"
3181 (dictree-name dict))
3182
3183 (unless (dictree--meta-dict-p dict)
3184 (unless key-loadfun
3185 (setq key-loadfun (dictree--key-loadfun dict)))
3186 (unless data-loadfun
3187 (setq data-loadfun (dictree--data-loadfun dict)))
3188 (unless plist-loadfun
3189 (setq plist-loadfun (dictree--plist-loadfun dict))))
3190
3191 (save-excursion
3192 (let ((buff (find-file-noselect file)))
3193 (set-buffer buff)
3194
3195 ;; insert the keys starting from the median to ensure a
3196 ;; reasonably well-balanced tree
3197 (let* ((lines (count-lines (point-min) (point-max)))
3198 (midpt (+ (/ lines 2) (mod lines 2)))
3199 entry)
3200 (message "Inserting keys in %s...(1 of %d)"
3201 (dictree-name dict) lines)
3202 ;; insert the median key and set the dictionary's modified
3203 ;; flag
3204 (if balance
3205 (dictree--goto-line midpt)
3206 (goto-char (point-min)))
3207 (when (setq entry
3208 (condition-case nil
3209 (dictree--read-line
3210 dict key-loadfun data-loadfun
3211 plist-loadfun)
3212 (error (error "Error reading line %d of %s"
3213 midpt file))))
3214 (dictree-insert dict (car entry) (nth 1 entry)
3215 insert-function)
3216 (setf (dictree--cell-plist
3217 (dictree--lookup dict (car entry) nil))
3218 (nth 2 entry)))
3219 ;; insert keys successively further away from the median in
3220 ;; both directions
3221 (dotimes (i (1- (if balance midpt lines)))
3222 (if balance
3223 (dictree--goto-line (+ midpt i 1))
3224 (forward-line 1))
3225 (when (setq entry
3226 (condition-case nil
3227 (dictree--read-line
3228 dict key-loadfun data-loadfun
3229 plist-loadfun)
3230 (error (error "Error reading line %d of %s"
3231 (+ midpt i 1) file))))
3232 (dictree-insert dict (car entry) (nth 1 entry)
3233 insert-function)
3234 (setf (dictree--cell-plist
3235 (dictree--lookup dict (car entry) nil))
3236 (nth 2 entry)))
3237 (when (= 49 (mod i 50))
3238 (message "Inserting keys in %s...(%d of %d)"
3239 (dictree-name dict)
3240 (if balance (+ (* 2 i) 2) i)
3241 lines))
3242 (when balance
3243 (dictree--goto-line (- midpt i 1))
3244 (when (setq entry
3245 (condition-case nil
3246 (dictree--read-line
3247 dict key-loadfun data-loadfun
3248 plist-loadfun)
3249 (error (error "Error reading line %d of %s"
3250 (- midpt i 1) file))))
3251 (dictree-insert dict (car entry)
3252 (nth 1 entry) insert-function)
3253 (setf
3254 (dictree--cell-plist
3255 (dictree--lookup dict (car entry) nil))
3256 (nth 2 entry)))))
3257
3258 ;; if inserting from mid-point out, and file contains an even
3259 ;; number of keys, we still have to add the last one
3260 (when (and balance (= 0 (mod lines 2)))
3261 (dictree--goto-line lines)
3262 (when (setq entry
3263 (condition-case nil
3264 (dictree--read-line
3265 dict key-loadfun data-loadfun
3266 plist-loadfun)
3267 (error (error "Error reading line %d of %s"
3268 lines file))))
3269 (dictree-insert dict (car entry) (nth 1 entry)
3270 insert-function)
3271 (setf (dictree--cell-plist
3272 (dictree--lookup dict (car entry) nil))
3273 (nth 2 entry))))
3274
3275 (message "Inserting keys in %s...done" (dictree-name dict)))
3276 (kill-buffer buff)))))
3277
3278
3279
3280 (defun dictree--read-line
3281 (dict &optional key-loadfun data-loadfun plist-loadfun)
3282 ;; Return a list containing the key, data (if any, otherwise nil) and
3283 ;; property list (ditto) at the current line of the current buffer,
3284 ;; for dictionary DICT.
3285 (save-excursion
3286 (let (key data plist)
3287 ;; read key
3288 (beginning-of-line)
3289 (when (setq key (read (current-buffer)))
3290 (when key-loadfun (setq key (funcall key-loadfun key)))
3291 ;; if there's anything after the key, use it as data
3292 (unless (eq (line-end-position) (point))
3293 (setq data (read (current-buffer))))
3294 (when data-loadfun (setq data (funcall data-loadfun data)))
3295 ;; if there's anything after the data, use is as the property
3296 ;; list
3297 (unless (eq (line-end-position) (point))
3298 (setq plist (read (current-buffer))))
3299 (when plist-loadfun (funcall plist-loadfun plist))
3300 ;; return the key and data
3301 (list key data plist)))))
3302
3303
3304
3305 (defun dictree-dump-to-buffer (dict &optional buffer type)
3306 "Dump keys and their associated data
3307 from dictionary DICT to BUFFER, in the same format as that used
3308 by `dictree-populate-from-file'. If BUFFER exists, data will be
3309 appended to the end of it. Otherwise, a new buffer will be
3310 created. If BUFFER is omitted, the current buffer is used.
3311
3312 TYPE determines the type of sequence to use to represent the
3313 keys, and should be one of 'string, 'vector or 'list. The default
3314 is 'vector.
3315
3316 Note that if the data does not have a read syntax, the dumped
3317 data can not be used to recreate the dictionary using
3318 `dictree-populate-from-file'.
3319
3320 Interactively, DICT and BUFFER are read from the mini-buffer,
3321 TYPE is always 'string."
3322 (interactive (list (read-dict "Dictionary: ")
3323 (read-buffer
3324 "Buffer to dump to (defaults to current): "
3325 (buffer-name (current-buffer)))
3326 'string))
3327
3328 ;; select the buffer, creating it if necessary
3329 (if buffer
3330 (setq buffer (get-buffer-create buffer))
3331 (setq buffer (current-buffer)))
3332 (set-buffer buffer)
3333
3334 ;; move point to end of buffer and make sure it's at start of new line
3335 (goto-char (point-max))
3336 (unless (= (point) (line-beginning-position))
3337 (insert "\n"))
3338
3339 ;; dump keys
3340 (message "Dumping keys from %s to %s..."
3341 (dictree-name dict) (buffer-name buffer))
3342 (let ((count 0) (dictsize (dictree-size dict)))
3343 (message "Dumping keys from %s to %s...(key 1 of %d)"
3344 (dictree-name dict) (buffer-name buffer) dictsize)
3345
3346 ;; map dump function over dictionary
3347 (dictree--mapc
3348 (lambda (key data plist)
3349 (when (= 99 (mod count 100))
3350 (message "Dumping keys from %s to %s...(key %d of %d)"
3351 (dictree-name dict) (buffer-name buffer)
3352 (1+ count) dictsize))
3353 (insert (prin1-to-string
3354 (funcall (or (dictree--key-savefun dict) 'identity)
3355 key)))
3356 (when (setq data
3357 (funcall (or (dictree--data-savefun dict) 'identity)
3358 data))
3359 (insert " " (prin1-to-string data)))
3360 (when (setq plist
3361 (funcall (or (dictree--plist-savefun dict) 'identity)
3362 plist))
3363 (unless data (insert " nil"))
3364 (insert " " (prin1-to-string plist)))
3365 (insert "\n")
3366 (setq count (1+ count)))
3367 dict type) ; dictree-mapc target
3368
3369 (message "Dumping keys from %s to %s...done"
3370 (dictree-name dict) (buffer-name buffer)))
3371 (switch-to-buffer buffer))
3372
3373
3374
3375 (defun dictree-dump-to-file (dict filename &optional type overwrite)
3376 "Dump keys and their associated data
3377 from dictionary DICT to a text file FILENAME, in the same format
3378 as that used by `dictree-populate-from-file'. Prompts to overwrite
3379 FILENAME if it already exists, unless OVERWRITE is non-nil.
3380
3381 TYPE determines the type of sequence to use to represent the
3382 keys, and should be one of 'string, 'vector or 'list. The default
3383 is 'vector.
3384
3385 Note that if the data does not have a read syntax and no , the dumped
3386 data can not be used to recreate the dictionary using
3387 `dictree-populate-from-file'.
3388
3389 Interactively, DICT and FILE are read from the mini-buffer,
3390 OVERWRITE is the prefix argument, and TYPE is always 'string."
3391 (interactive (list (read-dict "Dictionary: ")
3392 (read-file-name "File to dump to: " nil "")))
3393
3394 (if (and (called-interactively-p 'any) (string= filename ""))
3395 (message "Dictionary %s NOT dumped" (dictree-name dict))
3396
3397 ;; check if file exists, and prompt to overwrite it if necessary
3398 (if (and (file-exists-p filename)
3399 (not overwrite)
3400 (not (y-or-n-p
3401 (format "File %s already exists. Overwrite? "
3402 filename))))
3403 (message "Key dump cancelled")
3404
3405 (let (buff)
3406 ;; create temporary buffer, dump keys to it, and save to
3407 ;; FILENAME
3408 (setq buff (generate-new-buffer filename))
3409 (save-window-excursion
3410 (dictree-dump-to-buffer dict buff type)
3411 (write-file filename))
3412 (kill-buffer buff)))))
3413
3414
3415
3416
3417 ;; ----------------------------------------------------------------
3418 ;; Minibuffer completion
3419
3420 (defvar dictree-history nil
3421 "History list for commands that read a dictionary name.")
3422
3423 (defvar dictree-loaded-history nil
3424 "History list for commands that read a loaded dictionary name.")
3425
3426
3427 ;;;###autoload
3428 (defun read-dict
3429 (prompt &optional default dictlist allow-unloaded allow-unmatched)
3430 "Read the name of a dictionary with completion, and return it.
3431
3432 Prompt with PROMPT. By default, return DEFAULT. If DICTLIST is
3433 supplied, only complete on dictionaries in that list.
3434
3435 If ALLOW-UNLOADED is non-nil, also complete on the names of
3436 unloaded dictionaries (actually, on any Elisp file in the current
3437 `load-path' restricted to subdirectories of your home directory
3438 whose file name starts with \"dict-\"). If an unloaded dictionary
3439 is read, the name of the Elisp file will be returned, without
3440 extension, suitable for passing to `load-library'."
3441
3442 (let (dictname paths)
3443 ;; when allowing unloaded dictionaries...
3444 (when allow-unloaded
3445 ;; get paths in load-path that are subdirectories of home
3446 ;; directory
3447 (dolist (d load-path)
3448 (when (eq (aref d 0) ?~) (push d paths)))
3449 ;; gather names of all Elisp libraries in this restricted
3450 ;; load-path
3451 (dolist (f (all-completions
3452 "" (apply-partially 'locate-file-completion-table
3453 paths (get-load-suffixes))))
3454 (when (and (null (file-name-directory f))
3455 (and (> (length f) 5)
3456 (string= (substring f 0 5) "dict-"))
3457 (null (file-name-extension f))
3458 (not (member (file-name-sans-extension f) dictname)))
3459 (push (file-name-sans-extension f) dictname))))
3460 ;; gather names of loaded dictionaries
3461 (mapc (lambda (dict)
3462 (unless (or (null (dictree-name dict))
3463 (member (dictree-name dict) dictname))
3464 (push (list (dictree-name dict)) dictname)))
3465 (or dictlist dictree-loaded-list))
3466 ;; do completing-read
3467 (setq dictname (completing-read
3468 prompt
3469 (if allow-unmatched
3470 (completion-table-in-turn
3471 dictname 'read-file-name-internal)
3472 dictname)
3473 nil (not allow-unmatched) nil
3474 (if allow-unloaded
3475 'dictree-history
3476 'dictree-loaded-history)
3477 (and (dictree-p default) (dictree-name default))))
3478 ;; return dictionary
3479 (cond
3480 ;; if user typed a file name, return that
3481 ((and allow-unmatched (file-regular-p dictname)) dictname)
3482 ;; if user selected a loaded dictionary, return dict itself
3483 ((condition-case nil
3484 (dictree-p (eval (intern-soft dictname)))
3485 (void-variable nil))
3486 (eval (intern-soft dictname)))
3487 ;; if user selected an unloaded dictionary, return dict name
3488 ((and allow-unloaded (stringp dictname)) dictname)
3489 ;; if DEFAULT was specified, return that
3490 (default default)
3491 ;; should never get here!
3492 (t (error "Unknown error reading dictionary")))
3493 ))
3494
3495
3496
3497 ;; ----------------------------------------------------------------
3498 ;; Pretty-print dictionaries during edebug
3499
3500 ;; We advise the `edebug-prin1' and `edebug-prin1-to-string' functions
3501 ;; (actually, aliases) so that they print "#<dict-tree NAME>" instead of
3502 ;; the full print form for dictionaries.
3503 ;;
3504 ;; This is because, if left to its own devices, edebug hangs for ages
3505 ;; whilst printing large dictionaries, and you either have to wait for a
3506 ;; *very* long time for it to finish, or kill Emacs entirely. (Even C-g
3507 ;; C-g fails!)
3508 ;;
3509 ;; We do this also for lists of dictionaries, since those occur quite
3510 ;; often, but not for other sequence types or deeper nested structures,
3511 ;; to keep the implementation as simple as possible.
3512 ;;
3513 ;; Since the print form of a dictionary is practically incomprehensible
3514 ;; anyway, we don't lose much by doing this. If you *really* want to
3515 ;; print dictionaries in full whilst edebugging, despite this warning,
3516 ;; disable the advice.
3517 ;;
3518 ;; FIXME: Should use `cedet-edebug-prin1-extensions' instead of advice
3519 ;; when `cedet-edebug' is loaded, though I believe this still
3520 ;; works in that case.
3521
3522
3523 (eval-when-compile
3524 (require 'edebug)
3525 (require 'advice))
3526
3527
3528 (defun dictree--edebug-pretty-print (object)
3529 (cond
3530 ((dictree-p object)
3531 (concat "#<dict-tree \"" (dictree-name object) "\">"))
3532 ((null object) "nil")
3533 ((let ((dlist object) (test t))
3534 (while (or (dictree-p (car-safe dlist))
3535 (and dlist (setq test nil)))
3536 (setq dlist (cdr dlist)))
3537 test)
3538 (concat "(" (mapconcat (lambda (d)
3539 (concat "#<dict-tree \""
3540 (dictree-name d) "\">"))
3541 object " ") ")"))
3542 ;; ((vectorp object)
3543 ;; (let ((pretty "[") (len (length object)))
3544 ;; (dotimes (i (1- len))
3545 ;; (setq pretty
3546 ;; (concat pretty
3547 ;; (if (trie-p (aref object i))
3548 ;; "#<trie>" (prin1-to-string (aref object i))) " ")))
3549 ;; (concat pretty
3550 ;; (if (trie-p (aref object (1- len)))
3551 ;; "#<trie>" (prin1-to-string (aref object (1- len))))
3552 ;; "]")))
3553 ))
3554
3555
3556 (when (fboundp 'ad-define-subr-args)
3557 (ad-define-subr-args 'edebug-prin1 '(object &optional printcharfun)))
3558
3559 (defadvice edebug-prin1
3560 (around dictree activate compile preactivate)
3561 (let ((pretty (dictree--edebug-pretty-print object)))
3562 (if pretty
3563 (progn
3564 (prin1 pretty printcharfun)
3565 (setq ad-return-value pretty))
3566 ad-do-it)))
3567
3568
3569 (when (fboundp 'ad-define-subr-args)
3570 (ad-define-subr-args 'edebug-prin1-to-string '(object &optional noescape)))
3571
3572 (defadvice edebug-prin1-to-string
3573 (around dictree activate compile preactivate)
3574 (let ((pretty (dictree--edebug-pretty-print object)))
3575 (if pretty
3576 (setq ad-return-value pretty)
3577 ad-do-it)))
3578
3579
3580
3581 (provide 'dict-tree)
3582
3583 ;;; dict-tree.el ends here