emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/dict-tree f9bf379 013/154: Complete re-write of dict-tr


From: Stefan Monnier
Subject: [elpa] externals/dict-tree f9bf379 013/154: Complete re-write of dict-tree.el, based on new trie.el.
Date: Mon, 14 Dec 2020 12:21:34 -0500 (EST)

branch: externals/dict-tree
commit f9bf37901ddbf3428b5ee5daf3fabfd86e74040d
Author: Toby Cubitt <toby-predictive@dr-qubit.org>
Commit: tsc25 <toby-predictive@dr-qubit.org>

    Complete re-write of dict-tree.el, based on new trie.el.
---
 dict-tree.el | 3399 ++++++++++++++++++++++++++++++----------------------------
 1 file changed, 1772 insertions(+), 1627 deletions(-)

diff --git a/dict-tree.el b/dict-tree.el
index eb36e89..79bb7dd 100644
--- a/dict-tree.el
+++ b/dict-tree.el
@@ -49,7 +49,7 @@
 ;; `dictree-write', and load from file it using
 ;; `dictree-load'. Various other useful functions are also provided.
 ;;
-;; This package uses the ternary search tree package, tstree.el.
+;; This package uses the trie package, trie.el.
 
 
 ;;; Change log:
@@ -168,13 +168,13 @@
 ;;; Code:
 
 (provide 'dict-tree)
-(require 'tstree)
+(require 'trie)
 (require 'bytecomp)
 
 
 
 ;;; ================================================================
-;;;                Replacements for CL functions
+;;;            Replacements for CL and Elisp functions
 
 ;; copied from cl-extra.el
 (defun dictree--subseq (seq start &optional end)
@@ -205,26 +205,19 @@ If START or END is negative, it counts from the end."
 
 
 
-;; adapted from cl-seq.el
-(defun dictree--merge (list1 list2 predicate)
-  "Destructively merge the two lists to produce a new list
-sorted according to PREDICATE. The lists are assumed to already
-be sorted. The function PREDICATE is passed one entry from each
-list, and should return non-nil if the first argument should be
-sorted before the second."
-  (or (listp list1) (setq list1 (append list1 nil)))
-  (or (listp list2) (setq list2 (append list2 nil)))
-  (let ((res nil))
-    ;; build up result list backwards
-    (while (and list1 list2)
-      (if (funcall predicate (car list1) (car list2))
-           (push (pop list1) res)
-         (push (pop list2) res)))
-    ;; return result, plus any leftover entries (only one of list1 or
-    ;; list2 will be non-nil)
-    (nconc (nreverse res) list1 list2))
-)
-
+;; `goto-line' without messing around with mark and messages
+;; Note: this is a bug in simple.el; there's clearly a place for
+;;       non-interactive calls to goto-line from Lisp code, and
+;;       there's no warning against doing this. Yet goto-line *always*
+;;       calls push-mark, which usually *shouldn't* be invoked by
+;;       Lisp programs, as its docstring warns.
+(defmacro dictree-goto-line (line)
+  "Goto line LINE, counting from line 1 at beginning of buffer."
+  `(progn
+     (goto-char 1)
+     (if (eq selective-display t)
+        (re-search-forward "[\n\C-m]" nil 'end (1- ,line))
+       (forward-line (1- ,line)))))
 
 
 
@@ -236,167 +229,167 @@ sorted before the second."
   "Stores list of loaded dictionaries.")
 
 
-(defmacro dictree--name (dict)  ; INTERNAL USE ONLY
-  ;; Return the name of dictonary DICT
-  `(nth 1 ,dict)
-)
-
-
-(defmacro dictree--set-name (dict name)  ; INTERBAL USE ONLY
-  ;; Set the name of dictionary DICT
-  `(setcar (cdr ,dict) ,name)
-)
-
-
-(defmacro dictree--filename (dict)  ; INTERNAL USE ONLY.
-  ;; Return the filename of dictionary DICT
-  `(nth 2 ,dict)
-)
-
-
-(defmacro dictree--set-filename (dict filename)  ; INTERNAL USE ONLY.
-  ;; Set the filename of dictionary DICT
-  `(setcar (nthcdr 2 ,dict) ,filename)
-)
-
-
-(defmacro dictree--autosave (dict)  ; INTERNAL USE ONLY
-  ;; Return the autosave flag of dictionary DICT
-  `(nth 3 ,dict))
-
-
-(defmacro dictree--set-autosave (dict flag)  ; INTERNAL USE ONLY
-  ;; Set the autosave flag of dictionary DICT
-  `(setcar (nthcdr 3 ,dict) ,flag))
-
-
-(defmacro dictree--modified (dict)  ; INTERNAL USE ONLY
-  ;; Return the modified flag of dictionary DICT
-  `(nth 4 ,dict))
-
-
-(defmacro dictree--set-modified (dict flag)  ; INTERNAL USE ONLY
-  ;; Set the modified flag of dictionary DICT
-  `(setcar (nthcdr 4 ,dict) ,flag))
-
-
-(defmacro dictree--lookup-only (dict)  ; INTERNAL USE ONLY.
-  ;; Return non-nil if dictionary DICT is lookup-only
-  `(nth 5 ,dict))
-
-
-(defmacro dictree--dict-list (dict)
-  ;; Return the list of dictionaries on which meta-dictionary DICT is
-  ;; based.
-  `(nth 6 ,dict))
-
-
-(defmacro dictree--set-dict-list (dict tstree)  ; INTERNAL USE ONLY.
-  ;; Set the ternary search tree of dictionary DICT.
-  `(setcar (nthcdr 6 ,dict) ,tstree))
-
-
-(defmacro dictree--meta-dict-p (dict)  ; INTERNAL USE ONLY
-  ;; Return non-nil if DICT is a meta-dictionary.
-  `(not (tstree-p (dictree--dict-list ,dict))))
-
-
-(defun dictree--tstree (dict)  ; INTERNAL USE ONLY.
-  ;; Return the ternary search tree of dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (mapcar (lambda (dic) (dictree--tstree dic)) (nth 6 dict))
-    (nth 6 dict)))
-
-
-(defmacro dictree--set-tstree (dict tstree)  ; INTERNAL USE ONLY.
-  ;; Set the ternary search tree of dictionary DICT.
-  `(setcar (nthcdr 6 ,dict) ,tstree))
-
-
-(defmacro dictree--insfun (dict)  ; INTERNAL USE ONLY.
-  ;; Return the insert function of dictionary DICT.
-  `(nth 7 ,dict))
-
-
-(defmacro dictree--combfun (dict)  ; INTERNAL USE ONLY.
-  ;; Return the combine function of meta-dictionary DICT.
-  `(nth 7 ,dict))
-
-
-(defmacro dictree--rankfun (dict)  ; INTERNAL USE ONLY
-  ;; Return the rank function of dictionary DICT.
-  `(nth 8 ,dict))
-
-
-(defmacro dictree--lookup-hash (dict)  ; INTERNAL USE ONLY
-  ;; Return the lookup hash table of dictionary DICT
-  `(nth 9 ,dict))
-
-
-(defmacro dictree--set-lookup-hash (dict hash)  ; INTERNAL USE ONLY
-  ;; Set the completion hash for dictionary DICT
-  `(setcar (nthcdr 9 ,dict) ,hash))
-
-
-(defmacro dictree--lookup-speed (dict)  ; INTERNAL USE ONLY
-  ;; Return the lookup speed of dictionary DICT
-  `(nth 10 ,dict))
-
-
-(defmacro dictree--set-lookup-speed (dict speed)  ; INTERNAL USE ONLY
-  ;; Set the lookup speed of dictionary DICT
-  `(setcar (nthcdr 10 ,dict) ,speed))
-
-
-(defmacro dictree--completion-hash (dict)  ; INTERNAL USE ONLY
-  ;; Return the completion hash table of dictionary DICT
-  `(nth 11 ,dict))
-
-
-(defmacro dictree--set-completion-hash (dict hash)  ; INTERNAL USE ONLY
-  ;; Set the completion hash for dictionary DICT
-  `(setcar (nthcdr 11 ,dict) ,hash))
-
-
-(defmacro dictree--completion-speed (dict)  ; INTERNAL USE ONLY
-  ;; Return the completion speed of dictionary DICT
-  `(nth 12 ,dict))
-
-
-(defmacro dictree--set-completion-speed (dict speed)  ; INTERNAL USE ONLY
-  ;; Set the lookup speed of dictionary DICT
-  `(setcar (nthcdr 12 ,dict) ,speed))
-
-
-(defmacro dictree--ordered-hash (dict)  ; INTERNAL USE ONLY
-  ;; Return the ordered completion hash table of dictionary DICT
-  `(nth 13 ,dict))
-
-
-(defmacro dictree--set-ordered-hash (dict hash)  ; INTERNAL USE ONLY
-  ;; Set the completion hash for dictionary DICT
-  `(setcar (nthcdr 13 ,dict) ,hash))
-
-
-(defmacro dictree--ordered-speed (dict)  ; INTERNAL USE ONLY
-  ;; Return the ordered completion speed of dictionary DICT
-  `(nth 14 ,dict))
-
-
-(defmacro dictree--set-ordered-speed (dict speed)  ; INTERNAL USE ONLY
-  ;; Set the lookup speed of dictionary DICT
-  `(setcar (nthcdr 14 ,dict) ,speed))
-
-
-(defmacro dictree--meta-dict-list (dict)  ; INTERNAL USE ONLY
-  ;; Return list of meta-dictionaries which depend on DICT.
-  `(nthcdr 15 ,dict))
-
-
-(defmacro dictree--set-meta-dict-list (dict list)  ; INTERNAL USE ONLY
-  ;; Set list of dictionaries on which a meta-dictionary dict is based, or
-  ;; the list of meta-dictionaries dependent on dictionary DICT.
-  `(setcdr (nthcdr 14 ,dict) ,list))
+(defsubst dictree-p (obj)
+  "Return t if OBJ is a dictionary tree, nil otherwise."
+  (or (dictree--p obj) (dictree--meta-dict-p obj)))
+
+
+(defstruct
+  (dictree-
+   :named
+   (:constructor nil)
+   (:constructor dictree--create
+                (&optional
+                 filename
+                 (name (and filename
+                            (file-name-sans-extension
+                             (file-name-nondirectory filename))))
+                 autosave
+                 unlisted
+                 (comparison-function '<)
+                 (insert-function (lambda (a b) a))
+                 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
+                 (cache-policy 'time)
+                 (cache-update-policy 'synchronize)
+                 lookup-cache-threshold
+                 complete-cache-threshold
+                 complete-ranked-cache-threshold
+                 trie-type
+                 &aux
+                 (modified nil)
+                 (trie (trie-create comparison-function))
+                 (insfun (eval (macroexpand
+                                `(dictree--wrap-insfun ,insert-function))))
+                 (rankfun (eval (macroexpand
+                                 `(dictree--wrap-rankfun ,rank-function))))
+                 (lookup-cache
+                  (if lookup-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (complete-cache
+                  (if complete-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (complete-ranked-cache
+                  (if complete-ranked-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (metadict-list nil)
+                 ))
+   (:constructor dictree--create-custom
+                (&optional
+                 filename
+                 (name (and filename
+                            (file-name-sans-extension
+                             (file-name-nondirectory filename))))
+                 autosave
+                 unlisted
+                 (comparison-function '<)
+                 (insert-function (lambda (a b) a))
+                 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
+                 (cache-policy 'time)
+                 (cache-update-policy 'synchronize)
+                 lookup-cache-threshold
+                 complete-cache-threshold
+                 complete-ranked-cache-threshold
+                 &key
+                 createfun insertfun deletefun lookupfun mapfun emptyfun
+                 stackfun popfun stackemptyfun
+                 &aux
+                 (modified nil)
+                 (trie (trie-create-custom comparison-function
+                                    :createfun createfun
+                                    :insertfun insertfun
+                                    :deletefun deletefun
+                                    :lookupfun lookupfun
+                                    :mapfun mapfun
+                                    :emptyfun emptyfun
+                                    :stackfun stackfun
+                                    :popfun popfun
+                                    :stackemptyfun stackemptyfun))
+                 (insfun (eval (macroexpand
+                                `(dictree--wrap-insfun ,insert-function))))
+                 (rankfun (eval (macroexpand
+                                 `(dictree--wrap-rankfun ,rank-function))))
+                 (lookup-cache
+                  (if lookup-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (complete-cache
+                  (if complete-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (complete-ranked-cache
+                  (if complete-ranked-cache-threshold
+                      (make-hash-table :test 'equal)
+                    nil))
+                 (metadict-list nil)
+                 ))
+   (:copier nil))
+  name filename autosave modified
+  comparison-function insert-function insfun rank-function rankfun
+  cache-policy cache-update-policy
+  lookup-cache lookup-cache-threshold
+  complete-cache complete-cache-threshold
+  complete-ranked-cache complete-ranked-cache-threshold
+  trie meta-dict-list)
+
+
+
+(defstruct
+  (dictree--meta-dict
+   :named
+   (:constructor nil)
+   (:constructor dictree--meta-dict-create
+                (dictionary-list
+                 &optional
+                 filename
+                 (name (file-name-sans-extension
+                        (file-name-nondirectory filename)))
+                 autosave
+                 unlisted
+                 (combine-function '+)
+                 (cache-policy 'time)
+                 (cache-update-policy 'synchronize)
+                 lookup-cache-threshold
+                 complete-cache-threshold
+                 complete-ranked-cache-threshold
+                 &aux
+                 (dictlist
+                  (mapcar
+                   (lambda (dic)
+                     (cond
+                      ((dictree-p dic) dic)
+                      ((symbolp dic) (eval dic))
+                      (t (error "Invalid object in DICTIONARY-LIST"))))
+                   dictionary-list))
+                 (combfun (eval (macroexpand
+                                 `(dictree--wrap-combfun
+                                   ,combine-function))))
+                 ))
+   (:copier nil))
+  name filename autosave modified
+  combine-function combfun
+  cache-policy cache-update-policy
+  lookup-cache lookup-cache-threshold
+  complete-cache complete-cache-threshold
+  complete-ranked-cache complete-ranked-cache-threshold
+  dictlist meta-dict-list)
+
+
+(defun dictree--trielist (dict)
+  ;; Return a list of all the tries on which DICT is based. If DICT is a
+  ;; meta-dict, this recursively descends the hierarchy, gathering all the
+  ;; tries from the base dictionaries.
+  (let (accumulate)
+    (dictree--do-trielist dict)
+    accumulate))
+
+(defun dictree--do-trielist (dict)
+  (declare (special accumulate))
+  (if (dictree-meta-dict-p dict)
+      (mapc 'dictree--do-trielist (dictree--meta-dict-dictlist dict))
+    (setq accumulate (cons (dictree--trie dict) accumulate))))
 
 
 
@@ -405,463 +398,564 @@ sorted before the second."
   ;; wrap the data in a cons cell
   `(cons ,data ,meta-data))
 
+;; get data component from data cons cell
+(defalias 'dictree--unwrap-data 'car)  ; INTERNAL USE ONLY
 
-(defmacro dictree--get-data (cell)  ; INTERNAL USE ONLY
-  ;; get data component from data cons cell
-  `(car ,cell))
-
-
-(defmacro dictree--set-data (cell data)  ; INTERNAL USE ONLY
-  ;; set data component of data cons cell
-  `(setcar ,cell ,data))
-
+;; set data component of data cons cell
+(defalias 'dictree--set-data 'setcar)  ; INTERNAL USE ONLY
 
-(defmacro dictree--get-metadata (cell)  ; INTERNAL USE ONLY
-  ;; get meta-data component of data cons cell
-  `(cdr ,cell))
-
-
-(defmacro dictree--set-metadata (cell meta-data)  ; INTERNAL USE ONLY
-  ;; set meta-data component of data cons cell
-  `(setcdr ,cell ,meta-data))
+;; get meta-data component of data cons cell
+(defalias 'dictree--unwrap-metadata 'cdr)  ; INTERNAL USE ONLY
 
+;; set meta-data component of data cons cell
+(defalias 'dictree--set-metadata 'setcdr)  ; INTERNAL USE ONLY
 
 
 (defmacro dictree--wrap-insfun (insfun)  ; INTERNAL USE ONLY
   ;; return wrapped insfun to deal with data wrapping
-  `(lambda (new cell)
-     ;; if data doesn't already exist, wrap and return new data
-     (if (null cell)
-        (dictree--wrap-data (funcall ,insfun new nil))
-       ;; otherhwise, update data cons cell with new data and return it
-       (dictree--set-data cell (funcall ,insfun new
-                                       (dictree--get-data cell)))
-       cell)))
-
+  `(lambda (new old)
+     (dictree--set-data old (,insfun (dictree--unwrap-data new)
+                                    (dictree--unwrap-data old)))
+     old))
 
 (defmacro dictree--wrap-rankfun (rankfun)  ; INTERNAL USE ONLY
   ;; return wrapped rankfun to deal with data wrapping
-  `(lambda (a b) (funcall ,rankfun
-                         (cons (car a) (dictree--get-data (cdr a)))
-                         (cons (car b) (dictree--get-data (cdr b))))))
-
+  `(lambda (a b)
+     (,rankfun (cons (car a) (dictree--unwrap-data (cdr a)))
+              (cons (car b) (dictree--unwrap-data (cdr b))))))
 
 (defmacro dictree--wrap-filter (filter)  ; INTERNAL USE ONLY
   ;; return wrapped filter function to deal with data wrapping
-  `(lambda (str data) (funcall ,filter str (dictree--get-data data))))
+  `(lambda (key data) (,filter key (dictree--unwrap-data data))))
 
+(defmacro dictree--wrap-combfun (combfun)  ; INTERNAL USE ONLY
+  `(lambda (cell1 cell2)
+     (cons (,combfun (dictree--unwrap-data cell1)
+                    (dictree--unwrap-data cell2))
+          (append (list (dictree--unwrap-metadata cell1))
+                  (list (dictree--unwrap-metadata cell2))))))
 
+;; Construct and return a completion cache entry
+(defalias 'dictree--cache-create 'cons)  ; INTERNAL USE ONLY
 
-(defmacro dictree--cache-create (list maxnum)  ; INTERNAL USE ONLY
-  ;; Return a completion cache entry
-  `(cons ,list ,maxnum))
+;; Return the completions list for cache entry CACHE
+(defalias 'dictree--cache-completions 'car)  ; INTERNAL USE ONLY
 
+;; Return the max number of completions returned for cache entry CACHE
+(defalias 'dictree--cache-maxnum 'cdr)  ; INTERNAL USE ONLY
 
-(defmacro dictree--cache-completions (cache)  ; INTERNAL USE ONLY
-  ;; Return the completions list for cache entry CACHE
-  `(car ,cache))
+;; Set the completions list for cache entry CACHE
+(defalias 'dictree--set-cache-completions 'setcar)  ; INTERNAL USE ONLY
 
+;; Set the completions list for cache entry CACHE
+(defalias 'dictree--set-cache-maxnum 'setcdr)  ; INTERNAL USE ONLY
 
-(defmacro dictree--cache-maxnum (cache)  ; INTERNAL USE ONLY
-  ;; Return the max number of completions returned for cache entry CACHE
-  `(cdr ,cache))
 
 
-(defmacro dictree--set-cache-completions (cache completions)
-  ;; INTERNAL USE ONLY
-  ;; Set the completions list for cache entry CACHE
-  `(setcar ,cache ,completions))
+(defun dictree--merge (list1 list2 cmpfun &optional combfun maxnum)
+  ;; Destructively merge together sorted lists LIST1 and LIST2 of completions,
+  ;; sorting elements according to CMPFUN. For non-null MAXNUM, only the first
+  ;; MAXNUM are kept. For non-null COMBFUN, duplicate elements will be merged
+  ;; by passing the two elements as arguments to COMBFUN, and using the return
+  ;; value as the merged element.
+  (or (listp list1) (setq list1 (append list1 nil)))
+  (or (listp list2) (setq list2 (append list2 nil)))
+  (let (res (i -1))
 
+    ;; build up result list backwards
+    (while (and list1 list2 (or (null maxnum) (< (incf i) maxnum)))
+      ;; move smaller element to result list
+      (if (funcall cmpfun (car list1) (car list2))
+         (push (pop list1) res)
+       (if (funcall cmpfun (car list2) (car list1))
+           (push (pop list2) res)
+         ;; if elements are equal, merge them for non-null COMBFUN
+         (if combfun
+             (push (funcall combfun (pop list1) (pop list2))
+                   res)
+           ;; otherwise, add both to result list, in order
+           (push (pop list1) res)
+           (push (pop list2) res)))))
 
-(defmacro dictree--set-cache-maxnum (cache maxnum)  ; INTERNAL USE ONLY
-  ;; Set the completions list for cache entry CACHE
-  `(setcdr ,cache ,maxnum))
+    ;; return result if we already have MAXNUM entries
+    (if (and maxnum (= i maxnum))
+       (nreverse res)
+      ;; otherwise, return result plus enough leftover entries to make up
+      ;; MAXNUM (only one of list1 or list2 will be non-nil)
+      (let (tmp)
+       (or (null maxnum)
+           (and (setq tmp (nthcdr (- maxnum i 1) list1))
+                (setcdr tmp nil))
+           (and (setq tmp (nthcdr (- maxnum i 1) list2))
+                (setcdr tmp nil)))
+       (nconc (nreverse res) list1 list2)))
+    ))
 
 
+;; (defun dictree--merge-sort (list sortfun &optional combfun)
+;;   ;; Destructively sort LIST according to SORTFUN, combining identical
+;;   ;; elements using COMBFUN if supplied.
+;;   (dictree--do-merge-sort list (/ (length list) 2) sortfun combfun))
 
-;;; ================================================================
-;;;                   Miscelaneous macros
 
-;; `goto-line' without messing around with mark and messages
-;; Note: this is a bug in simple.el; there's clearly a place fro
-;;       non-interactive calls to goto-line from Lisp code, and
-;;       there's no warning against doing this. Yet goto-line *always*
-;;       calls push-mark, which usually *shouldn't* be invoked by
-;;       Lisp programs, as its docstring warns.
-(defmacro dictree-goto-line (line)
-  "Goto line LINE, counting from line 1 at beginning of buffer."
-  `(progn
-     (goto-char 1)
-     (if (eq selective-display t)
-        (re-search-forward "[\n\C-m]" nil 'end (1- ,line))
-       (forward-line (1- ,line)))))
+;; (defun dictree--do-merge-sort (list1 len sortfun combfun)
+;;   ;; Merge sort LIST according to SORTFUN, combining identical elements 
using
+;;   ;; COMBFUN.
+;;   (let* ((p (nthcdr (1- len) list1))
+;;      (list2 (cdr p)))
+;;     (setcdr p nil)
+;;     (dictree--merge (dictree--do-merge-sort list1 (/ len 2) sortfun combfun)
+;;                 (dictree--do-merge-sort list2 (/ len 2) sortfun combfun)
+;;                 sortfun combfun)))
 
 
 
 ;;; ================================================================
 ;;;      The public functions which operate on dictionaries
 
+(defun dictree-create
+  (&optional
+   name filename autosave unlisted
+   comparison-function insert-function rank-function
+   cache-policy cache-update-policy
+   lookup-cache-threshold
+   complete-cache-threshold
+   complete-ranked-cache-threshold
+   trie-type)
+  "Create an empty dictionary and return it.
 
-(defun dictree-p (obj)
-  "Return t if OBJ is a dictionary, nil otherwise."
-  (eq (car-safe obj) 'DICT)
-)
-
-
-(defun dictree-name (dict)
-  "Return dictionary DICT's name."
-  (dictree--name dict))
-
-
-(defun dictree-insert-function (dict)
-  "Return the insertion function for dictionary DICT."
-  (dictree--insfun dict))
+If NAME is supplied, the dictionary is stored in the variable
+NAME. Defaults to FILENAME stripped of directory and
+extension. (Regardless of the value of NAME, the dictionary will
+be stored in the default variable name when it is reloaded from
+file.)
 
+Optional argument FILENAME supplies a directory and file name to
+use when saving the dictionary. If the AUTOSAVE flag is non-nil,
+then the dictionary will automatically be saved to this file when
+it is unloaded or when exiting Emacs.
 
-(defun dictree-rank-function (dict)
-  "Return the rank function for the dictionary DICT (note: returns nil if
-lookup-only is set for the dictionary)."
-  (dictree--rankfun dict))
+If optional argument UNLISTED is non-nil, the dictionary will not
+be added to the list of loaded dictionaries. Note that this
+disables autosaving.
 
+Optional argument COMPARE-FUNCTION sets the function used to
+compare elements of the keys. It should take two arguments, A and
+B, both of the type contained by the sequences used as keys
+\(e.g. if the keys will be strings, the function will be passed
+two characters\). It should return t if the first is \"less
+than\" the second. Defaults to `<'.
 
+Optional argument INSERT-FUNCTION sets the function used to
+insert data into the dictionary. It should take two arguments:
+the new data, and the data already in the dictionary, and should
+return the data to insert. Defaults to replacing any existing
+data with the new data.
 
-(defun dictree-empty (dict)
-  "Return t if the dictionary DICT is empty, nil otherwise."
-  (if (dictree--lookup-only dict)
-      (= 0 (hash-table-count (dictree--lookup-hash dict)))
-    (tstree-empty (dictree--tstree dict)))
-)
+Optional argument RANK-FUNCTION sets the function used to rank
+the results of `dictree-complete'. It should take two arguments,
+each a cons whose car is a dictree key (a sequence) and whose cdr
+is the data associated with that key. It should return non-nil if
+the first argument is \"better\" than the second, nil
+otherwise. It defaults to \"lexical\" comparison of the keys,
+ignoring the data \(which is not very useful, since the
+`dictree-complete' function already does this much more
+efficiently\).
+
+CACHE-POLICY should be a symbol (time or length), which
+determines which query operations are cached. The former caches
+queries that take longer (in seconds) than the corresponding
+CACHE-THRESHOLD value. The latter caches queries on key sequences
+that are longer than the corresponding CACHE-THRESHOLD value.
+
+CACHE-UPDATE-POLICY should be a symbol (update or delete), which
+determines how the caches are updated when data is inserted or
+deleted. The former updates tainted cache entries, which makes
+queries faster but insertion and deleteion slower, whereas the
+latter deletes any tainted cache entries, which makes queries
+slower but insertion and deletion faster.
+
+The CACHE-THRESHOLD settings set the threshold for caching the
+corresponding dictionary query (lookup, completion, ranked
+completion). The meaning of these values depends on the setting
+of CACHE-POLICY (see above).
+
+All CACHE-THRESHOLD's default to nil. The values nil and t are
+special. If a CACHE-THRESHOLD is set to nil, no caching is done
+for that type of query. If it is t, everything is cached for that
+type of query \(similar behaviour can be obtained by setting the
+CACHE-THRESHOLD to 0, but it is better to use t\).
+
+TRIE-TYPE sets the type of trie to use as the underlying data
+structure. See `trie-create' for details."
+
+  ;; sadly, passing null values over-rides the defaults in the defstruct
+  ;; dictree--create, so we have to explicitly set the defaults again here
+  (or name (setq name (and filename (file-name-sans-extension
+                                    (file-name-nondirectory filename)))))
+  (or comparison-function (setq comparison-function '<))
+  (or insert-function (setq insert-function (lambda (a b) a)))
+  (or rank-function (setq rank-function (lambda (a b) (> (cdr a) (cdr b)))))
+  (or cache-policy (setq cache-policy 'time))
+  (or cache-update-policy (setq cache-update-policy 'synchronize))
+
+  (let ((dict
+        (dictree--create
+         filename name autosave unlisted
+         comparison-function insert-function rank-function
+         cache-policy cache-update-policy
+         lookup-cache-threshold
+         complete-cache-threshold
+         complete-ranked-cache-threshold
+         trie-type)))
+    ;; store dictionary in variable NAME
+    (when name (set name dict))
+    ;; add it to loaded dictionary list, unless it's unlisted
+    (unless unlisted
+      (push dict dictree-loaded-list)
+      (provide name))
+    dict))
 
 
 
-(defun dictree-create (&optional name filename autosave
-                                lookup-speed complete-speed
-                                ordered-speed lookup-only
-                                compare-function
-                                insert-function
-                                rank-function
-                                unlisted)
+(defun dictree-create-custom
+  (&optional
+   name filename autosave unlisted
+   comparison-function insert-function rank-function
+   cache-policy cache-update-policy
+   lookup-cache-threshold
+   complete-cache-threshold
+   complete-ranked-cache-threshold
+   createfun insertfun deletefun lookupfun mapfun emptyfun
+   stackfun popfun stackemptyfun)
   "Create an empty dictionary and return it.
 
-If NAME is supplied, also store it in variable NAME,
+If NAME is supplied, the dictionary is stored in the variable
+NAME. Defaults to FILENAME stripped of directory and
+extension. (Regardless of the value of NAME, the dictionary will
+be stored in the default variable name when it is reloaded from
+file.)
 
 Optional argument FILENAME supplies a directory and file name to
 use when saving the dictionary. If the AUTOSAVE flag is non-nil,
 then the dictionary will automatically be saved to this file when
-it is unloaded or when exiting emacs.
-
-The SPEED settings set the desired speed for the corresponding
-dictionary search operations (lookup, completion, ordered
-completion), in seconds. If a particular instance of the
-operation takes longer than this, the results will be cached in a
-hash table. If exactly the same operation is requested
-subsequently, it should perform significantly faster. \(Note
-\"should\": there's no guarantee!\) The down side is that the
-memory or disk space required to store the dictionary grows, and
-inserting keys into the dictionary becomes slightly slower, since
-the cache has to be synchronized.
-
-All SPEED's default to nil. The values nil and t are special. If
-a SPEED is set to nil, no caching is done for that operation. If
-it is set to t, everything is cached for that operation \(similar
-behaviour can be obtained by setting the SPEED to 0, but it is
-better to use t\).
-
-If LOOKUP-ONLY is non-nil, it disables all advanced search
-features for the dictionary \(currently, completion\). All the
-SPEED settings are ignored, as is the RANK-FUNCTION, and
-everything is stored in the lookup cache, even when inserting
-data. This is appropriate when a dictionary is only going to be
-used for lookup, since it speeds up lookups *and* decreases the
-memory required.
+it is unloaded or when exiting Emacs.
+
+If optional argument UNLISTED is non-nil, the dictionary will not
+be added to the list of loaded dictionaries. Note that this
+disables autosaving.
 
 Optional argument COMPARE-FUNCTION sets the function used to
 compare elements of the keys. It should take two arguments, A and
 B, both of the type contained by the sequences used as keys
 \(e.g. if the keys will be strings, the function will be passed
-two integers, since characters are represented as integers\). It
-should return a negative number if A is \"smaller\" than B, a
-positive number if A is \"larger\" than B, and 0 if A and B are
-\"equal\". It defaults to subtraction, which requires the key
-sequences to contain numbers or characters.
+two characters\). It should return t if the first is \"less
+than\" the second. Defaults to `<'.
 
 Optional argument INSERT-FUNCTION sets the function used to
 insert data into the dictionary. It should take two arguments:
-the new data, and the data already in the dictionary (or nil if
-none exists yet). It should return the data to insert. It
-defaults to replacing any existing data with the new data.
+the new data, and the data already in the dictionary, and should
+return the data to insert. Defaults to replacing any existing
+data with the new data.
 
 Optional argument RANK-FUNCTION sets the function used to rank
-the results of the `dictree-complete-ordered' function. It should
-take two arguments, each a cons whose car is a key in the
-dictionary and whose cdr is the data associated with that key. It
-should return non-nil if the first argument is \"better\" than
-the second, nil otherwise. It defaults to string comparison of
-the keys, ignoring the data \(which is not very useful, since the
-`dictree-complete' function already returns completions in
-alphabetical order much more efficiently, but at least will never
-cause any errors, whatever data is stored!\)
+the results of `dictree-complete'. It should take two arguments,
+each a cons whose car is a dictree key (a sequence) and whose cdr
+is the data associated with that key. It should return non-nil if
+the first argument is \"better\" than the second, nil
+otherwise. It defaults to \"lexical\" comparison of the keys,
+ignoring the data \(which is not very useful, since the
+`dictree-complete' function already does this much more
+efficiently\).
+
+CACHE-POLICY should be a symbol (time or length), which
+determines which query operations are cached. The former caches
+queries that take longer (in seconds) than the corresponding
+CACHE-THRESHOLD value. The latter caches queries on key sequences that
+are longer than the corresponding CACHE-THRESHOLD value.
+
+CACHE-UPDATE-POLICY should be a symbol (update or delete), which
+determines how the caches are updated when data is inserted or
+deleted. The former updates tainted cache entries, which makes
+queries faster but insertion and deleteion slower, whereas the
+latter deletes any tainted cache entries, which makes queries
+slower but insertion and deletion faster.
+
+The CACHE-THRESHOLD settings set the threshold for caching the
+corresponding dictionary query (lookup, completion, ranked
+completion). The meaning of these values depends on the setting
+of CACHE-POLICY (see above).
+
+All CACHE-THRESHOLD's default to nil. The values nil and t are
+special. If a CACHE-THRESHOLD is set to nil, no caching is done for
+that type of query. If it is t, everything is cached for that
+type of query \(similar behaviour can be obtained by setting the
+CACHE-THRESHOLD to 0, but it is better to use t\).
+
+The remaining arguments determine the type of trie to use as the
+underlying data structure. See `trie-create' for details."
+
+  ;; sadly, passing null values over-rides the defaults in the defstruct
+  ;; dictree--create, so we have to explicitly set the defaults again here
+  (or name (setq name (and filename (file-name-sans-extension
+                                    (file-name-nondirectory filename)))))
+  (or comparison-function (setq comparison-function '<))
+  (or insert-function (setq insert-function (lambda (a b) a)))
+  (or rank-function (setq rank-function (lambda (a b) (< (cdr a) (cdr b)))))
+  (or cache-policy (setq cache-policy 'time))
+  (or cache-update-policy (setq cache-update-policy 'synchronize))
+
+  (let ((dict
+        (dictree--create-custom
+         filename name autosave unlisted
+         comparison-function insert-function rank-function
+         cache-policy cache-update-policy
+         lookup-cache-threshold
+         complete-cache-threshold
+         complete-ranked-cache-threshold
+         :createfun createfun
+         :insertfun insertfun
+         :deletefun deletefun
+         :lookupfun lookupfun
+         :mapfun mapfun
+         :emptyfun emptyfun
+         :stackfun stackfun
+         :popfun popfun
+         :stackemptyfun stackemptyfun)))
+    ;; store dictionary in variable NAME
+    (when name (set name dict))
+    ;; add it to loaded dictionary list, unless it's unlisted
+    (unless unlisted
+      (push dict dictree-loaded-list)
+      (provide name))
+    dict))
 
-If optional argument UNLISTED is non-nil, the dictionary will not
-be added to the list of loaded dictionaries. Note that this will
-disable autosaving."
-
-  ;; a dictionary is a list containing:
-  ;; ('DICT
-  ;;  name
-  ;;  filename
-  ;;  autosave flag
-  ;;  modified flag
-  ;;  lookup-only
-  ;;  tstree / nil (if lookup-only)
-  ;;  insert-function
-  ;;  rank-function / nil
-  ;;  lookup-hash
-  ;;  lookup-speed / nil
-  ;;  complete-hash / nil
-  ;;  complete-speed / nil
-  ;;  ordered-hash / nil
-  ;;  ordered-speed / nil
-  ;; )
-  (let (dict compfun insfun rankfun)
-
-    (if lookup-only
-       ;; if dict is lookup only, use insert-function since there's no
-       ;; need to wrap data
-       (setq insfun insert-function)
-      ;; otherwise, wrap insert-function to deal with data wrapping
-      (setq insfun (if insert-function
-                      (eval (macroexpand
-                             `(dictree--wrap-insfun ,insert-function)))
-                    ;; insert-function defaults to "replace"
-                    (lambda (a b) a))))
-
-    ;; comparison function defaults to subtraction
-    (unless lookup-only
-      (setq compfun (if compare-function compare-function '-)))
-
-    (unless lookup-only
-      (setq rankfun (if rank-function
-                       (eval (macroexpand
-                              `(dictree--wrap-rankfun ,rank-function)))
-                     ;; rank-function defaults to comparison of the
-                     ;; sequences
-                     (eval (macroexpand
-                            `(dictree--wrap-rankfun
-                              (lambda (a b)
-                                (,(tstree-construct-sortfun '-)
-                                 (car a) (car b)))))))))
-
-    ;; create the dictionary
-    (setq dict
-        (if lookup-only
-            ;; lookup-only dictionary
-            (list 'DICT (symbol-name name) filename autosave t t
-                  nil insfun nil (make-hash-table :test 'equal)
-                  nil nil nil nil nil)
-
-          ;; normal dictionary
-          (list 'DICT (if name (symbol-name name) "") filename
-                autosave t nil
-                (tstree-create compfun insfun rankfun) insfun rankfun
-                (if lookup-speed (make-hash-table :test 'equal) nil)
-                lookup-speed
-                (if complete-speed (make-hash-table :test 'equal) nil)
-                complete-speed
-                (if ordered-speed (make-hash-table :test 'equal) nil)
-                ordered-speed)))
-
-    ;; store dictionary in variable NAME, add it to loaded list, and
-    ;; return it
+
+
+(defun dictree-meta-dict-create
+  (dictionary-list
+   &optional
+   name filename autosave unlisted
+   combine-function
+   cache-policy cache-update-policy
+   lookup-cache-threshold
+   complete-cache-threshold
+   complete-ranked-cache-threshold)
+  "Create a meta-dictionary based on the list of dictionaries
+in DICTIONARY-LIST.
+
+COMBINE-FUNCTION is used to combine data from different
+dictionaries. It is passed two pieces of data, each an
+association of the same key, but in different dictionaries. It
+should return a combined data.
+
+The other arguments are as for `dictree-create'."
+
+  ;; sadly, passing null values over-rides the defaults in the defstruct
+  ;; dictree--create, so we have to explicitly set the defaults again here
+  (or name (setq name (and filename (file-name-sans-extension
+                                    (file-name-nondirectory filename)))))
+  (or combine-function (setq combine-function '+))
+  (or cache-policy (setq cache-policy 'time))
+  (or cache-update-policy (setq cache-update-policy 'synchronize))
+
+  (let ((dict
+        (dictree--meta-dict-create
+         dictionary-list filename name autosave unlisted
+         combine-function
+         cache-policy cache-update-policy
+         lookup-cache-threshold
+         complete-cache-threshold
+         complete-ranked-cache-threshold)
+        ))
+    ;; store dictionary in variable NAME
     (when name (set name dict))
+    ;; add it to loaded dictionary list, unless it's unlisted
     (unless unlisted
       (push dict dictree-loaded-list)
       (provide name))
-    dict)
-)
+    dict))
+
+
+(defalias 'dictree-meta-dict-p 'dictree--meta-dict-p
+  "Return t if argument is a meta-dictionary, nil otherwise.")
+
+(defun dictree-empty-p (dict)
+  "Return t if the dictionary DICT is empty, nil otherwise."
+  (if (dictree--meta-dict-p dict)
+      (catch 'nonempty
+       (mapc (lambda (dic)
+               (if (not (dictree-empty-p dic)) (throw 'nonempty t)))
+             (dictree--meta-dict-dictlist dict)))
+    (trie-empty (dictree--trie dict))))
 
+(defsubst dictree-autosave (dict)
+  "Return dictionary's autosave flag."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-autosave dict)
+    (dictree--autosave dict)))
 
+(defsetf dictree-autosave (dict) (val)
+  ;; setf method for dictionary autosave flag
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-autosave ,dict) ,val)
+     (setf (dictree--autosave ,dict) ,val)))
 
+(defsubst dictree-modified (dict)
+  "Return dictionary's modified flag."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-modified dict)
+    (dictree--modified dict)))
 
-(defun dictree-create-type (name type &optional filename autosave
-                             lookup-speed complete-speed ordered-speed)
-  "Create an empty dictionary of type TYPE stored in variable
-NAME, and return it. Type can be one of dictionary, spell-check,
-lookup, or frequency. `dictree-create-type' is a simplified
-interface to `dictree-create'.
+(defsetf dictree-modified (dict) (val)
+  ;; setf method for dictionary modified flag
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-modified ,dict) ,val)
+     (setf (dictree--modified ,dict) ,val)))
 
-The \"dictionary\" type is exactly like a normal, paper-based
-dictionary: it can associate arbitrary data with any word in the
-dictionary. Inserting data for a word will replace any existing
-data for that word. All SPEED arguments default to nil.
+(defsubst dictree-name (dict)
+  "Return dictionary DICT's name."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-name dict)
+    (dictree--name dict)))
 
-A \"spell-check\" dictionary stores words, but can not associate
-any data with the words. It is appropriate when the dictionary
-will only be used for checking if a word is in the
-dictionary (e.g. for spell-checking). All SPEED arguments default
-to nil.
+(defsetf dictree-name (dict) (name)
+  ;; setf method for dictionary name
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-name ,dict) ,name)
+    (setf (dictree--name ,dict) ,name)))
 
-A \"lookup\" dictionary is like a dictionary-type dictionary, but
-can only be used to look up words, not for more advanced
-searches (e.g. word completion). This has both speed and memory
-benefits. It is appropriate when the more advanced searches are
-not required. Any SPEED arguments are ignored.
+(defsubst dictree-filename (dict)
+  "Return dictionary DICT's associated file name."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-filename dict)
+    (dictree--filename dict)))
 
-A \"frequency\" dictionary associates a number with each word in
-the dictionary. Inserting new data adds it to the existing
-data. It is appropriate, for instance, when storing
-word-frequencies\; the `dictree-complete-ordered' function can
-then be used to return the most likely completions. All SPEED
-arguments default to nil.
+(defsetf dictree-filename (dict) (filename)
+  ;; setf method for dictionary filename
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-filename ,dict) ,filename)
+     (setf (dictree--filename ,dict) ,filename)))
 
-See `dictree-create' for more details.
+(defun dictree-comparison-function (dict)
+  "Return dictionary DICT's comparison function."
+  (if (dictree--meta-dict-p dict)
+      (dictree-comparison-function (car (dictree--meta-dict-dictlist dict)))
+    (dictree--comparison-function dict)))
 
+(defalias 'dictree-insert-function 'dictree--insert-function
+  "Return the insertion function for dictionary DICT.")
 
-Technicalities:
+(defun dictree-rank-function (dict)
+  "Return the rank function for dictionary DICT"
+  (if (dictree--meta-dict-p dict)
+      (dictree-rank-function (car (dictree--meta-dict-dictlist dict)))
+    (dictree--rank-function dict)))
 
-For the \"dictionary\" type, INSERT-FUNCTION is set to
-\"replace\", and RANK-FUNCTION to string comparison of the
-words (not very useful, since the `dictree-complete' function
-already returns completions sorted alphabetically, and does it
-much more efficiently than `dictree-complete-ordered', but at
-least it will not cause errors!).
+(defun dictree-rankfun (dict)
+  ;; Return the rank function for dictionary DICT
+  (if (dictree--meta-dict-p dict)
+      (dictree-rankfun (car (dictree--meta-dict-dictlist dict)))
+    (dictree--rankfun dict)))
 
-For the \"spell-check\" type, INSERT-FUNCTION is set to a
-function that always returns t. RANK-FUNCTION is set to string
-comparison of the words.
+(defalias 'dictree-meta-dict-combine-function
+  'dictree--meta-dict-combine-function
+  "Return the combine function for meta-dictionary DICT.")
 
-For the \"lookup\" type, INSERT-FUNCTION is set to \"replace\",
-and LOOKUP-ONLY is set to t.
+(defalias 'dictree-meta-dict-dictlist
+  'dictree--meta-dict-dictlist
+  "Return the list of constituent dictionaries for meta-dictionary DICT.")
 
-For the \"frequency\" type, INSERT-FUNCTION sums the new and
-existing data. Nil is treated as 0. The RANK-FUNCTION is set to
-numerical \"greater-than\" comparison of the data."
+(defsubst dictree-lookup-cache-threshold (dict)
+  "Return the lookup cache threshold for dictionary DICT."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-lookup-cache-threshold dict)
+    (dictree--lookup-cache-threshold dict)))
 
-  (let (insfun rankfun lookup-only)
-    ;; set arguments based on type
-    (cond
-     ;; dictionary type
-     ((eq type 'dictionary)
-      (setq insfun (lambda (a b) a))
-      (setq rankfun (lambda (a b) (string< (car a) (car b)))))
-
-     ;; spell-check type
-     ((eq type 'spell-check)
-      (setq insfun (lambda (a b) t))
-      (setq rankfun (lambda (a b) (string< (car a) (car b)))))
-
-     ;; lookup type
-     ((eq type 'lookup)
-      (setq insfun (lambda (a b) a))
-      (setq rankfun (lambda (a b) (string< (car a) (car b))))
-      (setq lookup-only t))
-
-     ;; frequency type
-     ((eq type 'frequency)
-      (setq insfun (lambda (new old)
-                    (cond ((and (null new) (null old)) 0)
-                          ((null new) old)
-                          ((null old) new)
-                          (t (+ old new)))))
-      (setq rankfun (lambda (a b) (> (cdr a) (cdr b)))))
-     )
-
-    (dictree-create name filename autosave
-                lookup-speed complete-speed ordered-speed
-                lookup-only nil insfun rankfun))
-)
-
-
-
-(defun dictree-create-meta-dict (name dictlist &optional filename autosave
-                                     lookup-speed complete-speed
-                                     ordered-speed lookup-only
-                                     combine-function rank-function
-                                     unlisted)
-  "Create a meta-dictionary called NAME, based on dictionaries
-in DICTLIST.
-
-COMBINE-FUNCTION is used to combine data from the dictionaries in
-DICTLIST. It is passed two cons cells, each of whose car contains
-data and whose cdr contains meta-data from the tree. Both cons
-cells contain data associated with the same key, but from
-different dictionaries. The function should return a cons cell
-containing the combined data and meta-data in the car and cdr
-respectively.
+(defsetf dictree-lookup-cache-threshold (dict) (param)
+  ;; setf method for lookup cache threshold
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-lookup-cache-threshold ,dict) ,param)
+     (setf (dictree--lookup-cache-threshold ,dict) ,param)))
 
-The other arguments are as for `dictree-create'."
+(defsubst dictree-lookup-cache (dict)
+  ;; Return the lookup cache for dictionary DICT.
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-lookup-cache dict)
+    (dictree--lookup-cache dict)))
 
-    ;; a meta-dictionary is a list containing:
-  ;; ('DICT
-  ;;  name
-  ;;  filename
-  ;;  autosave flag
-  ;;  modified flag
-  ;;  lookup-only
-  ;;  tstree / nil (if lookup-only)
-  ;;  combine-function
-  ;;  rank-function / nil
-  ;;  lookup-hash
-  ;;  lookup-speed
-  ;;  complete-hash / nil
-  ;;  complete-speed / nil
-  ;;  ordered-hash / nil
-  ;;  ordered-speed / nil
-  ;;  dictlist)
-  (let (dict combfun rankfun)
-
-    ;; wrap rank-function to deal with data wrapping
-    (setq combfun combine-function)
-    (when rank-function
-      (setq rankfun
-           (eval (macroexpand
-                  `(dictree--wrap-rankfun ,rank-function)))))
-
-    ;; if any of the dictionaries in DICTLIST are lookup-only, the
-    ;; meta-dictionary has to be lookup-only
-    (mapc (lambda (dic)
-           (setq lookup-only
-                 (or lookup-only (dictree--lookup-only dic))))
-         dictlist)
-
-;;     ;; make sure all dictionaries this meta-dict is based on are loaded
-;;     (dolist (dic dictlist) (require (dictree--name dic)))
-
-    ;; create meta-dictionary
-    (setq dict
-         (if lookup-only
-             ;; lookup-only dictionary
-             (list 'DICT (symbol-name name) filename autosave t t
-                   dictlist combfun nil
-                   (if lookup-speed (make-hash-table :test 'equal) nil)
-                   lookup-speed
-                   nil nil nil nil)
-           ;; normal dictionary
-           (list 'DICT (symbol-name name) filename autosave t nil
-                 dictlist combfun rankfun
-                 (if lookup-speed (make-hash-table :test 'equal) nil)
-                 lookup-speed
-                 (if complete-speed (make-hash-table :test 'equal) nil)
-                 complete-speed
-                 (if ordered-speed (make-hash-table :test 'equal) nil)
-                 ordered-speed)))
-
-    ;; add meta-dictionary to lists of meta-dicts for all dictionaries it
-    ;; depends on
-    (mapc (lambda (dic) (nconc dic (list dict))) dictlist)
-
-    ;; store dictionary in variable NAME, add it to loaded list, and
-    ;; return it
-    (set name dict)
-    (unless unlisted
-      (push dict dictree-loaded-list)
-      (provide name))
-    dict)
-)
+(defsubst dictree-complete-cache-threshold (dict)
+  "Return the completion cache threshold for dictionary DICT."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-complete-cache-threshold dict)
+    (dictree--complete-cache-threshold dict)))
+
+(defsetf dictree-complete-cache-threshold (dict) (param)
+  ;; setf method for completion cache threshold
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-complete-cache-threshold ,dict) ,param)
+     (setf (dictree--complete-cache-threshold ,dict) ,param)))
+
+(defsubst dictree-complete-cache (dict)
+  ;; Return the completion cache for dictionary DICT.
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-complete-cache dict)
+    (dictree--complete-cache dict)))
+
+(defsubst dictree-complete-ranked-cache-threshold (dict)
+  "Return the ranked completion cache threshold for dictionary DICT."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-complete-ranked-cache-threshold dict)
+    (dictree--complete-ranked-cache-threshold dict)))
+
+(defsetf dictree-complete-ranked-cache-threshold (dict) (param)
+  ;; setf method for ranked completion cache threshold
+  `(if (dictree--meta-dict-p ,dict)
+       (setf (dictree--meta-dict-complete-ranked-cache-threshold ,dict) ,param)
+     (setf (dictree--complete-ranked-cache-threshold ,dict) ,param)))
+
+(defsubst dictree-complete-ranked-cache (dict)
+  ;; Return the ranked completion cache for dictionary DICT.
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-dict-complete-ranked-cache dict)
+    (dictree--complete-ranked-cache dict)))
+
+
+(defmacro dictree--query-triefun (query-type)
+  ;; Return trie query function corresponding to QUERY-TYPE
+  `(intern (concat "trie-" (symbol-name ,query-type))))
+
+(defmacro dictree--query-stackfun (query-type)
+  ;; Return dictree stack creation function corresponding to QUERY-TYPE
+  `(intern (concat "dictree-" (symbol-name ,query-type) "-stack")))
+
+(defmacro dictree--query-cacheparam (query-type dict ranked)
+  ;; Return DICT's QUERY-TYPE cache threshold.
+  `(if ,ranked
+       (funcall (intern (concat "dictree-" (symbol-name ,query-type)
+                               "-ranked-cache-threshold"))
+               ,dict)
+     (funcall (intern (concat "dictree-" (symbol-name ,query-type)
+                             "-cache-threshold"))
+             ,dict)))
 
+(defmacro dictree--query-cache (query-type dict ranked)
+  ;; Return DICT's QUERY-TYPE cache.
+  `(if ,ranked
+       (funcall
+       (intern (concat "dictree-" (symbol-name ,query-type) "-ranked-cache"))
+       ,dict)
+     (funcall
+      (intern (concat "dictree-" (symbol-name ,query-type) "-cache"))
+      ,dict)))
 
 
 
+
+
+;; ----------------------------------------------------------------
+;;                  Inserting and deleting data
+
 (defun dictree-insert (dict key &optional data insert-function)
   "Insert KEY and DATA into dictionary DICT.
 If KEY does not already exist, this creates it. How the data is
@@ -884,42 +978,28 @@ already exists). It should return the data to insert."
   (if (dictree--meta-dict-p dict)
       (mapc (lambda (dic)
              (dictree-insert dic key data insert-function))
-           (dictree--dict-list dict))
-
+           (dictree--meta-dict-dictlist dict))
 
-    ;; otherwise, dictionary is a normal dictionary...
-    (let ((insfun (if insert-function
-                     (eval (macroexpand
-                            `(dictree--wrap-insfun ,insert-function)))
-                   (dictree--insfun dict)))
-         newdata)
+    ;; otherwise...
+    (let (newdata)
       ;; set the dictionary's modified flag
-      (dictree--set-modified dict t)
-
-      ;; if dictionary is lookup-only, just insert the data in the
-      ;; lookup cache
-      (if (dictree--lookup-only dict)
-         (let ((lookup-hash (dictree--lookup-hash dict)))
-           (puthash key
-                    (setq newdata
-                          (funcall insfun data
-                                   (gethash key lookup-hash)))
-                    lookup-hash))
-
-       ;; otherwise...
-       (let ((tstree (dictree--tstree dict)))
-         ;; insert key in dictionary's ternary search tree
-         (setq newdata (tstree-insert tstree key data insfun))
-         ;; update dictionary's caches
-         (dictree-update-cache dict key newdata)
-         ;; update cache's of any meta-dictionaries based on dict
-         (mapc (lambda (dic)
-                 (dictree-update-cache dic key newdata))
-               (dictree--meta-dict-list dict))))
+      (setf (dictree-modified dict) t)
+      ;; insert key in dictionary's ternary search tree
+      (setq newdata
+           (trie-insert
+            (dictree--trie dict) key (dictree--wrap-data data)
+            (or (and insert-function
+                     (eval (macroexpand
+                            `(dictree--wrap-insfun ,insert-function))))
+                (dictree--insfun dict))))
+      ;; update dictionary's caches
+      (dictree-update-cache dict key newdata)
+      ;; update cache's of any meta-dictionaries based on dict
+      (mapc (lambda (dic) (dictree-update-cache dic key newdata))
+           (dictree--meta-dict-list dict))
 
       ;; return the new data
-      (dictree--get-data newdata)))
-)
+      (dictree--unwrap-data newdata))))
 
 
 
@@ -932,707 +1012,762 @@ Returns non-nil if KEY was deleted, nil if KEY was not 
in DICT."
      ;; if DICT is a meta-dictionary, delete KEY from all dictionaries
      ;; it's based on
      ((dictree--meta-dict-p dict)
-      (dolist (dic (dictree--dict-list dict))
+      (dolist (dic (dictree--meta-dict-dictlist dict))
        (setq deleted (or deleted (dictree-delete dic key))))
-      (dictree--set-modified dict deleted)
-      deleted)
-
-     ;; if dictionary is lookup-only, just delete KEY from the lookup
-     ;; hash
-     ((dictree--lookup-only dict)
-      (setq deleted (dictree-member-p dict key))
-      (when deleted
-       (remhash key (dictree--lookup-hash dict))
-       (dictree--set-modified dict t))
-      deleted)
+      (setf (dictree-modified dict) (and deleted t)))
 
      ;; otherwise...
      (t
-      (setq deleted (tstree-delete (dictree--tstree dict) key))
+      (setq deleted (trie-delete (dictree--trie dict) key))
       ;; if key was deleted, have to update the caches
       (when deleted
        (dictree-update-cache dict key nil t)
-       (dictree--set-modified dict t))
-      deleted)
-     ))
-)
-
-
-
-(defun dictree-lookup (dict key)
-  "Return the data associated with KEY in dictionary DICT,
-or nil if KEY is not in the dictionary.
-
-Note: this will not distinguish between a non-existent KEY and a
-KEY whose data is nil. \(\"spell-check\" type dictionaries
-created using `dictree-create-type' store t as the data for every
-key to avoid this problem) Use `dictree-member-p' to distinguish
-non-existent keys from nil data."
-
-  ;; first check the lookup hash for the key
-  (let ((data (when (dictree--lookup-speed dict)
-               (gethash key (dictree--lookup-hash dict))))
-       (combfun (when (dictree--meta-dict-p dict)
-                  (dictree--combfun dict)))
-       time)
-
-    ;; if it wasn't in the lookup hash...
-    (unless data
-      (cond
-
-       ;; if the dictionary is lookup-only and is a meta-dictionary,
-       ;; search in the dictionaries it's based on
-       ((and (dictree--lookup-only dict) (dictree--meta-dict-p dict))
-       (setq time (float-time))
+       (setf (dictree-modified dict) t)
+       ;; update cache's of any meta-dictionaries based on DICT
        (mapc (lambda (dic)
-               (setq data (funcall (dictree--combfun dict) data
-                                   (dictree-lookup dic key))))
-             (dictree--dict-list dict))
-       (setq time (- (float-time) time))
-
-        ;; if the lookup was slower than the dictionary's lookup speed,
-        ;; add it to the lookup hash and set the modified flag
-       (when (and (dictree--lookup-speed dict)
-                  (or (eq (dictree--lookup-speed dict) t)
-                      (> time (dictree--lookup-speed dict))))
-         (dictree--set-modified dict t)
-         (puthash key data (dictree--lookup-hash dict))))
-
-
-       ;; if nothing was found in the cache, and the dictionary is not
-       ;; lookup-only, look in the ternary search tree
-       ((not (dictree--lookup-only dict))
-       ;; time the lookup
-       (setq time (float-time))
-       (setq data (tstree-member (dictree--tstree dict) key combfun))
-       (setq time (- (float-time) time))
-
-       ;; if the lookup was slower than the dictionary's lookup speed,
-       ;; add it to the lookup hash and set the modified flag
-       (when (and (dictree--lookup-speed dict)
-                  (or (eq (dictree--lookup-speed dict) t)
-                      (> time (dictree--lookup-speed dict))))
-         (dictree--set-modified dict t)
-         (puthash key data (dictree--lookup-hash dict))))
-       ))
-
-    ;; return the data
-    (dictree--get-data data))
-)
-
-
+               (dictree-update-cache dic key nil t))
+             (dictree--meta-dict-list dict)))))
 
-(defun dictree-set-meta-data (dict key meta-data)
-  "Set meta-data (data not used to rank keys) for KEY
-in dictionary DICT."
-
-  (when (not (dictree-p dict))
-    (error "Wrong argument type dictree-p"))
-
-  ;; set the dictionary's modified flag
-  (dictree--set-modified dict t)
+    ;; return deleted key/data pair
+    (cons (car deleted) (dictree--unwrap-data (cdr deleted)))))
 
-  ;; if dictionary is lookup-only, refuse!
-  (if (dictree--lookup-only dict)
-      (error "Lookup-only dictionaries can't contain meta-data")
-    ;; otherwise, set key's meta-data
-    (dictree--set-metadata
-     (tstree-member (dictree--tstree dict) key) meta-data))
-)
 
 
+;; ----------------------------------------------------------------
+;;                     Cache updating
 
-(defun dictree-lookup-meta-data (dict key)
-  "Return any meta-data (data not used to rank keys)
-associated with KEY in dictionary DICT, or nil if KEY is not in
-the dictionary.
-
-Note: this will not distinguish between a non-existent KEY and a
-KEY with no meta-data. Use `dictree-member-p' to distinguish
-non-existent keys."
+(defun dictree-update-cache (dict key newdata &optional deleted)
+  "Synchronise dictionary DICT's caches,
+given that the data associated with KEY has been changed to
+NEWDATA, or KEY has been deleted if DELETED is non-nil (NEWDATA
+is ignored in that case)."
 
-  (when (dictree--lookup-only dict)
-    (error "Lookup-only dictionaries can't contain meta-data"))
+  (let (prefix cache entry completions cmpl maxnum)
 
-  ;; first check the lookup hash for the key
-  (let ((data (if (dictree--lookup-speed dict)
-                 (gethash key (dictree--lookup-hash dict))
-               nil))
-       (combfun (when (dictree--meta-dict-p dict)
-                  (dictree--combfun dict)))
-       time)
+    ;; synchronise the lookup cache if dict is a meta-dictionary,
+    ;; since it's not done automatically
+    (when (and (dictree--meta-dict-p dict)
+              (dictree--lookup-cache-threshold dict)
+              (gethash key (dictree--lookup-cache dict)))
+      (if deleted
+         (remhash key (dictree--lookup-cache dict))
+       (puthash key newdata (dictree--lookup-cache dict))))
 
-    ;; if it wasn't in the lookup hash, search in the ternary search tree
-    (unless data
-      ;; time the lookup
-      (let (time)
-       (setq time (float-time))
-       (setq data (tstree-member (dictree--tstree dict) key combfun))
-       (setq time (- (float-time) time))
 
-        ;; if the lookup was slower than the dictionary's lookup speed,
-        ;; add it to the lookup hash and set the modified flag
-       (when (and (dictree--lookup-speed dict)
-                  (or (eq (dictree--lookup-speed dict) t)
-                      (> time (dictree--lookup-speed dict))))
-         (dictree--set-modified dict t)
-         (puthash key data (dictree--lookup-hash dict)))))
+    ;; synchronize the completion cache, if it exists
+    (when (dictree-complete-cache-threshold dict)
+      ;; have to check every possible prefix that could be cached!
+      (dotimes (i (1+ (length key)))
+       (setq prefix (dictree--subseq key 0 i))
+       (dolist (reverse '(nil t))
+         (when (setq cache (gethash (cons prefix reverse)
+                                    (dictree-complete-cache dict)))
+           (setq completions (dictree--cache-completions cache))
+           (setq maxnum (dictree--cache-maxnum cache))
+           (setq cmpl (assoc key completions))
+           (cond
+            ;; if key was deleted and was in cached result, remove cache
+            ;; entry and re-run the same completion to update the cache
+            ((and deleted cmpl)
+             (remhash (cons prefix reverse) (dictree-complete-cache dict))
+             (dictree-complete dict prefix maxnum reverse))
+            ;; if key was modified and was not in cached result, merge it
+            ;; into the completion list, retaining only the first maxnum
+            ((and (not deleted) (not cmpl))
+             (dictree--set-cache-completions
+              cache
+              (dictree--merge
+               (list (cons key newdata)) completions
+               `(lambda (a b)
+                  (,(eval (macroexpand
+                          `(trie-construct-sortfun
+                            ,(dictree-comparison-function dict))))
+                   (car a) (car b)))
+               (when (dictree--meta-dict-p dict)
+                 (dictree--meta-dict-combfun dict))
+               maxnum)))
+            ;; if key was modified and was in the cached result, update the
+            ;; associated data if dict is a meta-dictionary (this is done
+            ;; automatically for a normal dict)
+            ((and (not deleted) cmpl (dictree--meta-dict-p dict))
+             (setcdr cmpl newdata))
+            ;; the final combination, deleted and not in cached result,
+            ;; requires no action
+            )))))
+
+
+    ;; synchronize the ranked completion cache, if it exists
+    (when (dictree--complete-ranked-cache-threshold dict)
+      ;; have to check every possible prefix that could be cached!
+      (dotimes (i (1+ (length key)))
+       (setq prefix (dictree--subseq key 0 i))
+       (dolist (reverse '(nil t))
+         (when (setq cache (gethash (cons prefix reverse)
+                                    (dictree-complete-ranked-cache dict)))
+           (setq completions (dictree--cache-completions cache))
+           (setq maxnum (dictree--cache-maxnum cache))
+           (setq cmpl (assoc key completions))
+           (cond
+            ;; if key was deleted and was in cached result, remove cache
+            ;; entry and re-run the same query to update the cache
+            ((and deleted cmpl)
+             (remhash (cons prefix reverse)
+                      (dictree-complete-ranked-cache dict))
+             (dictree-complete dict prefix maxnum reverse nil nil 'ranked))
+            ;; if key was modified and was not in cached result, merge it
+            ;; into the completion list, retaining only the first maxnum
+            ((and (not deleted) (not cmpl))
+             (dictree--set-cache-completions
+              cache
+              (dictree--merge
+               (list (cons key newdata)) completions
+               (dictree-rankfun dict)
+               (when (dictree--meta-dict-p dict)
+                 (dictree--meta-dict-combfun dict))
+               maxnum)))
+            ;; if key was modified and was in the cached result, update the
+            ;; associated data if dict is a meta-dictionary (this is done
+            ;; automatically for a normal dict), re-sort, and if key is now
+            ;; at end of list re-run the same query to update the cache
+            ((and (not deleted) cmpl)
+             (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
+             (dictree--set-cache-completions
+              cache (sort completions (dictree-rankfun dict)))
+             (when (equal key (car (last completions)))
+               (remhash (cons prefix reverse)
+                        (dictree-complete-ranked-cache dict))
+               (dictree-complete dict prefix maxnum reverse nil nil 'ranked)))
+            ;; the final combination, deleted and not in cached result,
+            ;; requires no action
+            )))))
+    ))
+
+
+
+;; ----------------------------------------------------------------
+;;                        Retrieving data
+
+(defun dictree-lookup (dict key &optional nilflag)
+  "Return the data associated with KEY in dictionary DICT,
+or nil if KEY is not in the dictionary.
 
-    ;; return the meta-data
-    (dictree--get-metadata data))
-)
+Optional argument NILFLAG specifies a value to return instead of
+nil if KEY does not exist in TREE. This allows a non-existent KEY
+to be distinguished from an element with a null association. (See
+also `dictree-member-p' for testing existence alone.)"
+  (let ((data (dictree--lookup dict key nilflag)))
+    (unless (eq data nilflag)
+      (dictree--unwrap-data data))))
 
 
+(defalias 'dictree-member 'dictree-lookup)
 
 
 (defun dictree-member-p (dict key)
-  "Return t if KEY is in dictionary DICT, nil otherwise."
-
-  ;; if DICT is a meta-dictionary, look in dictionaries it's based on
-  (cond
-   ((dictree--meta-dict-p dict)
-    (catch 'found
-      (dolist (dic (dictree--dict-list dict))
-       (when (dictree-member-p dic key) (throw 'found t)))))
-
-   ;; lookup-only, look in lookup hash and use dummy symbol to
-   ;; distinguish non-existent keys from those with nil data
-   ((dictree--lookup-only dict)
-    (if (eq (gethash key (dictree--lookup-hash dict) 'not-in-here)
-           'not-in-here)
-       nil t))
-
-   ;; otherwise look in the ternary search tree
-   (t (tstree-member-p (dictree--tstree dict) key)))
-)
-
-
-
-(defun dictree-map (function dict &optional type)
-  "Apply FUNCTION to all entries in dictionary DICT,
-for side-effects only.
-
-FUNCTION will be passed two arguments: a key of type
-TYPE ('string, 'vector, or 'list, defaulting to 'vector) from the
-dictionary, and the data associated with that key. It is safe to
-assume the dictionary entries will be traversed in
-\"alphabetical\" order.
-
-If TYPE is 'string, it must be possible to apply the function
-`string' to the type used to reference data in the dictionary."
-
-  (if (dictree--lookup-only dict)
-      (maphash function (dictree--lookup-hash dict))
-;;     ;; need to "rename" `function' or we hit a nasty dynamic scoping
-;;     ;; problem, since `tstree-map' also binds the symbol `function'
-;;     ;; (let ((dictree-map-function function))
-      (tstree-map
-       `(lambda (key data)
-         (funcall ,function key (dictree--get-data data)))
-       (dictree--tstree dict) type));)
-)
-
-
-
-(defun dictree-mapcar (function dict)
-  "Apply FUNCTION to all entries in dictionary DICT,
-and make a list of the results.
-
-FUNCTION will be passed two arguments: a key from the
-dictionary, and the data associated with that key. It is safe to
-assume the dictionary entries will be traversed in alphabetical
-order."
-
-  (if (dictree--lookup-only dict)
-      (let (result)
-       (maphash `(lambda function (key data)
-                   (cons (,function key data) result))
-                (dictree--lookup-hash dict))
-       result)
-    ;; need to "rename" `function' or we hit a nasty dynamic scoping
-    ;; problem, since `tstree-map' also binds the symbol `function'
-    (let ((dictree-map-function function))
-      (tstree-map
-       (lambda (key data)
-        (funcall dictree-map-function key (dictree--get-data data)))
-       (dictree--tstree dict) t t)))
-)
-
-
-
-(defun dictree-size (dict)
-  "Return the number of entries in dictionary DICT."
-  (interactive (list (read-dict "Dictionary: ")))
-
-  ;; lookup-only
-  (if (dictree--lookup-only dict)
-      (if (not (dictree--meta-dict-p dict))
-         ;; normal dictionary
-         (hash-table-size (dictree--lookup-hash dict))
-       ;; meta-dictionary
-       (let ((count 0))
-         (mapc (lambda (dic) (setq count (+ count (dictree-size dic))))
-               (dictree--dict-list dict))
-         count))
-    ;; non lookup-only
-    (let ((count 0))
-      (tstree-map (lambda (&rest dummy) (setq count (1+ count)))
-                 (dictree--tstree dict))
-      (when (interactive-p)
-       (message "Dictionary %s contains %d entries"
-                (dictree--name dict) count))
-      count))
-)
-
-
-
-(defun dictree-complete
-  (dict sequence &optional maxnum all combine-function filter no-cache)
-  "Return an alist containing all completions of SEQUENCE
-found in dictionary DICT, along with their associated data, in
-the order defined by the dictionary's comparison function (see
-`dictree-create'). If no completions are found, return nil.
-
-SEQUENCE can be a single sequence or a list of sequences. If a
-list is supplied, completions of all elements in the list are
-returned, merged together in a single alist.
-
-The optional numerical argument MAXNUM limits the results to the
-first MAXNUM completions. If it is absent or nil, all completions
-are included in the returned alist.
-
-DICT can also be a list of dictionaries, in which case
-completions are sought in all dictionaries in the list and the
-results are merged together, keeping the first MAXNUM. Note that
-if a key appears in more than one dictionary, the returned alist
-may contain that key more than once. To have multiple
-dictionaries treated as a single, combined dictionary, they
-should be combined into a meta-dictionary. See
-`dict-create-metadict'.
-
-Normally, only the remaining characters needed to complete
-SEQUENCE are returned. If the optional argument ALL is non-nil,
-the entire completion is returned.
-
-The optional COMBINE-FUNCTION argument overrides a
-meta-dictionary's default combine-function. It is ignored if none
-of the dictionaries in DICT are meta-dictionaries. See
-`dict-create-metadict' for details.
-
-The FILTER argument sets a filter function for the
-completions. If supplied, it is called for each possible
-completion with two arguments: the completion, and its associated
-data. If the filter function returns nil, the completion is not
-included in the results.
-
-If the optional argument NO-CACHE is non-nil, it prevents caching
-of the result."
-
-  ;; ----- sort out arguments ------
-
-  ;; wrap dict in a list if necessary
-  (when (dictree-p dict) (setq dict (list dict)))
-
-  ;; wrap sequence in a list if necessary
-  ;; FIXME: this will fail if SEQUENCE is a list, and tree's reference
-  ;;        type is itself a sequence (actually, there might be no way
-  ;;        to fully fix this...)
-  (when (or (atom sequence)
-           (and (listp sequence) (not (sequencep (car sequence)))))
-    (setq sequence (list sequence)))
+  "Return t if KEY exists in DICT, nil otherwise."
+  (let ((flag '(nil)))
+    (not (eq flag (dictree-member dict key flag)))))
 
 
-  ;; redefine filter to deal with data wrapping
-  (when filter
-    (setq filter (eval (macroexpand `(dictree--wrap-filter ,filter)))))
 
+(defun dictree--lookup (dict key nilflag)
+  ;; Return association of KEY in DICT, or NILFLAG if KEY does not exist. Does
+  ;; not do any data/meta-data unwrapping
 
-  ;; ----- search for completions -----
+  (let* ((flag '(nil))
+        (data flag)
+        time)
+    ;; if KEY is in the cache, then we're done
+    (unless (and (dictree-lookup-cache dict)
+                (setq data (gethash key (dictree--lookup-cache dict))))
 
-  (let (completions cmpl cache time speed combfun)
-    ;; search each dictionary in the list
-    (dolist (dic dict)
-      ;; throw a wobbly if dictionary is lookup-only
-      (when (dictree--lookup-only dic)
-       (error "Dictionary is lookup-only; completion disabled"))
-      ;; get meta-dictionary's combine function
-      (when (dictree--meta-dict-p dic)
-       (if combine-function
-           (setq combfun combine-function)
-         (setq combfun (dictree--combfun dic))))
-      ;; complete each sequence in the list
-     (dolist (seq sequence)
-       (cond
-
-        ;; If FILTER or COMBINE-FUNCTION was supplied, look in ternary
-        ;; search tree since we don't cache these custom searches.
-        ((or filter combine-function)
-         (setq cmpl
-               (tstree-complete (dictree--tstree dic) seq maxnum
-                                combfun filter)))
-
-
-        ;; if there's a cached result with enough completions, use it
-        ((and (setq cache
-                    (if (dictree--completion-speed dic)
-                        (gethash seq (dictree--completion-hash dic))
-                      nil))
-              (or (null (dictree--cache-maxnum cache))
-                  (and maxnum
-                       (<= maxnum (dictree--cache-maxnum cache)))))
-         (setq cmpl (dictree--cache-completions cache))
-         ;; drop any excess cached completions
-         (when (and maxnum (> (length cmpl) maxnum))
-           (setcdr (nthcdr (1- maxnum) cmpl) nil)))
-
-
-        ;; If nothing was in the cache or the cached result didn't
-        ;; contain enough completions, look in the ternary search tree
-        ;; and time it.
-        (t
+      ;; otherwise, we have to look in the dictionary itself...
+      (cond
+       ;; if DICT is a meta-dict, look in its constituent dictionaries
+       ((dictree--meta-dict-p dict)
+       (let (newdata (newflag '(nil)))
+         ;; time the lookup for caching
          (setq time (float-time))
-         (setq cmpl
-               (tstree-complete (dictree--tstree dic)
-                                seq maxnum combfun))
-         (setq time (- (float-time) time))
-         ;; If the completion function was slower than the dictionary's
-         ;; completion speed, add the results to the completion hash and
-         ;; set the dictionary's modified flag.
-         (when (and (not no-cache)
-                    (setq speed (dictree--completion-speed dic))
-                    (or (eq speed t) (> time speed)))
-           (dictree--set-modified dic t)
-           (puthash seq (dictree--cache-create cmpl maxnum)
-                    (dictree--completion-hash dic)))))
-
-
-       ;; ----- construct completion list -----
-
-       ;; drop prefix from front of the completions if ALL is not set
-       (unless all
-         (setq cmpl (mapcar
-                     (lambda (s)
-                       (cons (dictree--subseq (car s) (length seq))
-                             (cdr s)))
-                     cmpl)))
-       ;; merge the cached completions with those already found
-       (let ((sortfun `(lambda (a b)
-                         (,(tstree-construct-sortfun
-                            (tstree--tree-cmpfun (dictree--tstree dic)))
-                          (car a) (car b)))))
-         (setq completions (dictree--merge completions cmpl sortfun))
-         ;; drop any excess completions
-         (when (and maxnum (> (length completions) maxnum))
-           (setcdr (nthcdr (1- maxnum) completions) nil)))
-       ))
-
-
-    ;; return the completions list, unwrapping the data
-    (mapcar (lambda (c) (cons (car c) (dictree--get-data (cdr c))))
-           completions))
-)
-
-
-
-(defun dictree-complete-ordered
-  (dict sequence &optional maxnum all rank-function combine-function
-                          filter no-cache)
-  "Return an alist containing all completions of SEQUENCE
-found in dictionary DICT, along with their associated data,
-sorted according to the rank function. If no completions are found,
-return nil.
-
-Note that `dictree-complete' is significantly more efficient than
-`dictree-complete-ordered', especially when a MAXNUM is
-specified. Always use `dictree-complete' when you don't care
-about the ordering of the completions, or you need the
-completions ordered according to the dictionary's comparison
-function (see `dictree-create').
-
-SEQUENCE can be a single sequence or a list of sequences. If a
-list is supplied, completions of all elements in the list are
-returned, merged together in a single alist.
-
-The optional numerical argument MAXNUM limits the results to the
-\"best\" MAXNUM completions. If it is absent or nil, all
-completions are included in the returned alist.
-
-DICT can also be a list of dictionaries, in which case
-completions are sought in all dictionaries in the list and the
-results are merged together, keeping the \"best\" MAXNUM. Note
-that if a key appears in more than one dictionary, the returned
-alist may contain that key more than once. To have multiple
-dictionaries treated as a single, combined dictionary, they
-should be combined into a meta-dictionary. See
-`dict-create-metadict'.
-
-Normally, only the remaining characters needed to complete
-SEQUENCE are returned. If the optional argument ALL is non-nil,
-the entire completion is returned.
-
-The optional argument RANK-FUNCTION over-rides the dictionary's
-default rank function (see `dictree-create' for details). The
-elements of the returned list are sorted according to this
-rank-function, in descending order.
-
-The optional COMBINE-FUNCTION argument overrides a
-meta-dictionary's default combine-function. It is ignored if none
-of the dictionaries in DICT are meta-dictionaries. See
-`dict-create-metadict' for details.
-
-The FILTER argument sets a filter function for the
-completions. If supplied, it is called for each possible
-completion with two arguments: the completion, and its associated
-data. If the filter function returns nil, the completion is not
-included in the results.
-
-If the optional argument NO-CACHE is non-nil, it prevents caching
-of the result."
-
-  (let (rankfun combfun completions seq cmpl time speed cache)
-    ;; wrap dict in a list if necessary
-    (when (dictree-p dict) (setq dict (list dict)))
-
-    ;; ----- sort out arguments -----
-
-    ;; wrap sequence in a list if necessary
-    ;; FIXME: this will fail if SEQUENCE is a list, and tree's reference
-    ;;        type is itself a sequence (actually, there might be no way
-    ;;        to fully fix this...)
-    (when (or (atom sequence)
-             (and (listp sequence) (not (sequencep (car sequence)))))
-      (setq sequence (list sequence)))
-
-    (if rank-function
-       ;; redefine supplied rank-function to deal with data wrapping
-       (setq rankfun
-             (eval (macroexpand
-                    `(dictree--wrap-rankfun ,rank-function))))
-      ;; Note: we default to the rank function of first dict in list, and
-      ;;       hope it's compatible with the data in the other
-      ;;       dictionaries
-      (setq rankfun (dictree--rankfun (car dict))))
-
-    ;; redefine filter to deal with data wrapping
-    (when filter
-      (setq filter (eval (macroexpand `(dictree--wrap-filter ,filter)))))
+         ;; look in each constituent dictionary in turn
+         (dolist (dic (dictree--meta-dict-dictlist dict))
+           (setq newdata (dictree--lookup dic key newflag))
+           ;; skip dictionary if it doesn't contain KEY
+           (unless (eq newdata newflag)
+             ;; if we haven't found KEY before, we have now!
+             (if (eq data flag)
+                 (setq data newdata)
+               ;; otherwise, combine the previous data with the new data
+               (setq data (funcall (dictree--meta-dict-combfun dict)
+                                   data newdata)))))
+         (setq time (- (float-time) time))))
+
+       ;; otherwise, DICT is a normal dictionary, so look in it's trie
+       (t
+       ;; time the lookup for caching
+       (setq time (float-time))
+       (setq data (trie-member (dictree--trie dict) key flag))
+       (setq time (- (float-time) time))))
 
+      ;; if lookup found something, but was slower than lookup cache-threshold,
+      ;; cache the result
+      (when (and (not (eq data flag))
+                (dictree-lookup-cache-threshold dict)
+                (or (eq (dictree-lookup-cache-threshold dict) t)
+                    (> time (dictree-lookup-cache-threshold dict))))
+       (setf (dictree-modified dict) t)
+       (puthash key data (dictree-lookup-cache dict))))
 
-    ;; ----- search for completions -----
+    ;; return the desired data
+    (if (eq data flag) nilflag data)))
 
-    ;; search each dictionary in the list
-    (dolist (dic dict)
-      ;; throw a wobbly if dictionary is lookup-only
-      (when (dictree--lookup-only dic)
-       (error "Dictionary is lookup-only; completion disabled"))
-      ;; get meta-dictionary's combine function
-      (when (dictree--meta-dict-p dic)
-       (if combine-function
-           (setq combfun combine-function)
-         (setq combfun (dictree--combfun dic))))
-      ;; complete each sequence in the list
-      (dolist (seq sequence)
-       (cond
-
-        ;; If the default rank-function or combine-function have been
-        ;; over-ridden or a filter supplied, look in the ternary search
-        ;; tree since we don't cache these non-default searches.
-        ((or rank-function filter combine-function)
-         (setq cmpl
-               (tstree-complete-ordered (dictree--tstree dic)
-                                        sequence maxnum
-                                        rankfun combfun filter)))
-
-
-        ;; if there's a cached result with enough completions, use it
-        ((and (setq cache (if (dictree--ordered-speed dic)
-                              (gethash seq (dictree--ordered-hash dic))
-                            nil))
-              (or (null (dictree--cache-maxnum cache))
-                  (and maxnum
-                       (<= maxnum (dictree--cache-maxnum cache)))))
-         (setq cmpl (dictree--cache-completions cache))
-         ;; drop any excess cached completions
-         (when (and maxnum (> (length cmpl) maxnum))
-           (setcdr (nthcdr (1- maxnum) cmpl) nil)))
-
-
-        ;; If nothing was in the cache or the cached result didn't
-        ;; contain enough completions, search tree and time the search.
-        (t
-         (setq time (float-time))
-         (setq cmpl (tstree-complete-ordered (dictree--tstree dic)
-                                             seq maxnum rankfun combfun))
-         (setq time (- (float-time) time))
-         ;; If the completion function was slower than the dictionary's
-         ;; completion speed, add the results to the completion cache and
-         ;; set the dictionary's modified flag.
-         (when (and (not no-cache)
-                    (setq speed (dictree--ordered-speed dic))
-                    (or (eq speed t) (> time speed)))
-           (dictree--set-modified dic t)
-           (puthash seq (dictree--cache-create cmpl maxnum)
-                    (dictree--ordered-hash dic)))))
-
-
-       ;; ----- construct completion list -----
-
-       ;; drop prefix from front of the completions if ALL is not set
-       (unless all
-         (setq cmpl (mapcar
-                     (lambda (s)
-                       (cons (dictree--subseq (car s) (length seq))
-                             (cdr s)))
-                     cmpl)))
-       ;; merge the cached completions with those already found
-       (setq completions (dictree--merge completions cmpl rankfun))
-       ;; drop any excess completions
-       (when (and maxnum (> (length completions) maxnum))
-         (setcdr (nthcdr (1- maxnum) completions) nil))
-       ))
 
 
-    ;; return the completions list, unwrapping the data
-    (mapcar (lambda (c) (cons (car c) (dictree--get-data (cdr c))))
-           completions))
-)
+;; ----------------------------------------------------------------
+;;                 Getting and setting meta-data
 
+(defun dictree-set-meta-data (dict key meta-data)
+  "Set meta-data for KEY in dictionary DICT.
+Returns META-DATA if successful, nil if KEY was not found in
+DICT.
+
+Note that if DICT is a meta-dictionary, then this will set the
+meta-data for KEY in *all* its constituent dictionaries.
+
+Unlike the data associated with a key (cf. `dictree-insert'),
+meta-data is not included in the results of queries on the
+dictionary \(`dictree-lookup', `dictree-complete',
+`dictree-complete-ordered'\), nor does it affect the outcome of
+any of the queries. It merely serves to tag a key with some
+additional information, and can only be retrieved using
+`dictree-lookup-meta-data'."
+  (cond
+   ((dictree--meta-dict-p dict)
+    (warn "Setting meta-data in all constituent dictionaries of a meta-dict")
+    (setf (dictree-modified dict) t)
+    (mapc 'dictree-set-meta-data (dictree--meta-dict-dictlist dict)))
+   (t
+    (setf (dictree-modified dict) t)
+    (let ((cell (trie-member (dictree--trie dict) key)))
+      (when cell (dictree--set-metadata cell meta-data))))))
 
 
-(defun dictree-populate-from-file (dict file)
-  "Populate dictionary DICT from the key list in file FILE.
 
-Each line of the file should contain a key, either a string
-\(delimeted by \"\), a vector or a list. (Use the escape sequence
-\\\" to include a \" in a string.) If a line does not contain a
-key, it is silently ignored. The keys should ideally be sorted
-\"alphabetically\", as defined by the dictionary's
-comparison-function \(see `dictree-create'\).
+(defun dictree-get-meta-data (dict key &optional nilflag)
+  "Return the meta-data associated with KEY in dictionary DICT,
+or nil if KEY is not in the dictionary.
 
-Each line can optionally include data and meta-data to be
-associated with the key, separated from each other and the key by
-whitespace.
+Optional argument NILFLAG specifies a value to return instead of
+nil if KEY does not exist in TREE. This allows a non-existent KEY
+to be distinguished from a key that does not have any
+meta-data. (See also `dictree-member-p' for testing existence
+alone.)"
+  (let ((data (dictree--lookup dict key nilflag)))
+    (unless (eq data nilflag)
+      (dictree--unwrap-metadata data))))
 
 
-Technicalities:
 
-The key, data and meta-data are read as lisp expressions using
-`read', and are read from the middle outwards, i.e. first the
-middle key is read, then the key directly after it, then the key
-directly before it, then the one two lines after the middle, and
-so on. Assuming the keys in the file are sorted
-\"alphabetically\", this helps produce a reasonably efficient
-dictionary structure."
 
-  (save-excursion
-    (let ((buff (generate-new-buffer " *dictree-populate*")))
-      ;; insert the key list into a temporary buffer
-      (set-buffer buff)
-      (insert-file-contents file)
+;; ----------------------------------------------------------------
+;;                        Mapping functions
 
-      ;; insert the keys starting from the median to ensure a reasonably
-      ;; well-balanced tree
-      (let* ((lines (count-lines (point-min) (point-max)))
-            (midpt (+ (/ lines 2) (mod lines 2)))
-            entry)
-        ;; insert the median key and set the dictionary's modified flag
-       (dictree-goto-line midpt)
-       (when (setq entry (dictree-read-line))
-         (dictree-insert dict (car entry) (nth 1 entry))
-         (dictree-set-meta-data dict (car entry) (nth 2 entry)))
-       (message "Inserting keys in %s...(1 of %d)"
-                (dictree--name dict) lines)
-        ;; insert keys successively further away from the median in both
-        ;; directions
-       (dotimes (i (1- midpt))
-         (dictree-goto-line (+ midpt i 1))
-         (when (setq entry (dictree-read-line))
-           (dictree-insert dict (car entry) (nth 1 entry))
-           (dictree-set-meta-data dict (car entry) (nth 2 entry)))
-         (when (= 49 (mod i 50))
-           (message "Inserting keys in %s...(%d of %d)"
-                    (dictree--name dict) (+ (* 2 i) 2) lines))
-         (dictree-goto-line (- midpt i 1))
-         (when (setq entry (dictree-read-line))
-           (dictree-insert dict (car entry) (nth 1 entry))
-           (dictree-set-meta-data dict (car entry) (nth 2 entry))))
+(defun dictree-mapc (function dict &optional type reverse)
+  "Apply FUNCTION to all entries in dictionary DICT,
+for side-effects only.
 
-        ;; if file contains an even number of keys, we still have to add
-        ;; the last one
-       (when (= 0 (mod lines 2))
-         (dictree-goto-line lines)
-         (when (setq entry (dictree-read-line))
-           (dictree-insert dict (car entry) (nth 1 entry))
-           (dictree-set-meta-data dict (car entry) (nth 2 entry))))
-       (message "Inserting keys in %s...done" (dictree--name dict)))
+FUNCTION will be passed two arguments: a key of type
+TYPE ('string, 'vector, or 'list, defaulting to 'vector) from the
+dictionary, and the data associated with that key. The dictionary
+entries will be traversed in \"lexical\" order, i.e. the order
+defined by the dictionary's comparison function (cf.
+`dictree-create').
 
-      (kill-buffer buff)))
-)
+If TYPE is 'string, it must be possible to apply the function
+`string' to the elements of sequences stored in DICT.
+
+FUNCTION is applied in ascending order, or descending order if
+REVERSE is non-nil."
+
+  ;; "rename" FUNCTION to something hopefully unique, to help avoid nasty
+  ;; dynamical scoping bugs
+  (let ((dictree-mapc--function function))
+    (dictree--mapc
+     (lambda (key data metadata)
+       (funcall dictree-mapc--function key data))
+     dict type reverse)))
+
+
+(defun dictree--mapc (function dict &optional type reverse)
+  ;; Like `dictree-mapc', but FUNCTION is passed a cons cell containing the
+  ;; data (car) and meta-data (cdr) as its second argument, instead of just
+  ;; the data.
+
+  ;; "rename" FUNCTION to something hopefully unique, to help avoid nasty
+  ;; dynamical scoping bugs
+  (let ((dictree--mapc--function function))
+    ;; for a normal dictionary, map the function over its trie
+    (if (not (dictree--meta-dict-p dict))
+       (trie-mapc
+        (lambda (key data)
+          (funcall dictree--mapc--function
+                   key
+                   (dictree--unwrap-data data)
+                   (dictree--unwrap-metadata data)))
+        (dictree--trie dict)
+        type reverse)
+      ;; for a meta-dict, use a dictree-stack
+      (let ((stack (dictree-stack dict))
+           entry)
+       (while (setq entry (dictree--stack-pop stack))
+         (funcall dictree--mapc--function
+                  (car entry)
+                  (dictree--unwrap-data (cdr entry))
+                  (dictree--unwrap-metadata (cdr entry)))))
+      )))
+
+
+(defun dictree-mapf (function combinator dict &optional type reverse)
+  "Apply FUNCTION to all entries in dictionary DICT,
+and combine the results using COMBINATOR.
+
+FUNCTION should take two arguments: a key sequence from the
+dictionary and its associated data.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string; defaults to vector) sets the type of sequence passed to
+FUNCTION. If TYPE is 'string, it must be possible to apply the
+function `string' to the individual elements of key sequences
+stored in DICT.
+
+The FUNCTION will be applied and the results combined in
+asscending \"lexical\" order (i.e. the order defined by the
+dictionary's comparison function; cf. `dictree-create'), or
+descending order if REVERSE is non-nil."
+
+  ;; "rename" functions to something hopefully unique, to help avoid nasty
+  ;; dynamical scoping bugs
+  (let ((dictree-mapf--function function)
+       (dictree-mapf--combinator combinator))
+
+    ;; for a normal dictionary, map the function over its trie
+    (if (not (dictree--meta-dict-p dict))
+       (trie-mapf
+        `(lambda (key data)
+           (,dictree-mapf--function key (dictree--unwrap-data data)))
+        dictree-mapf--combinator (dictree--trie dict) type reverse)
+
+      ;; for a meta-dict, use a dictree-stack
+      (let ((dictree-mapf--stack (dictree-stack dict))
+           dictree-mapf--entry
+           dictree-mapf--accumulate)
+       (while (setq dictree-mapf--entry
+                    (dictree-stack-pop dictree-mapf--stack))
+         (funcall dictree-mapf--combinator
+                  (funcall dictree-mapf--function
+                           (car dictree-mapf--entry)
+                           (cdr dictree-mapf--entry)))))
+      )))
 
 
 
-;;; FIXME: doesn't fail gracefully if file has invalid format
-(defun dictree-read-line ()
-  "Return a cons containing the key and data \(if any, otherwise
-nil\) at the current line of the current buffer. Returns nil if
-line is in wrong format."
+(defun dictree-size (dict)
+  "Return the number of entries in dictionary DICT."
+  (interactive (list (read-dict "Dictionary: ")))
+  (let ((count 0))
+    (dictree-mapc (lambda (&rest dummy) (incf count))
+                 (dictree--trie dict))
+    (when (interactive-p)
+      (message "Dictionary %s contains %d entries"
+              (dictree--name dict) count))
+    count))
+
+
+
+;; ----------------------------------------------------------------
+;;                        Using dictrees as stacks
+
+;; A dictree--meta-stack is the meta-dict version of a dictree-stack (the
+;; ordinary version is just a single trie-stack). It consists of a heap of
+;; trie-stacks for its constituent tries, where the heap order is the usual
+;; lexical order over the keys at the top of the trie-stacks.
+(defstruct
+  (dictree--meta-stack
+   (:constructor nil)
+   (:constructor dictree--meta-stack-create
+                (dict &optional (type 'vector) reverse
+                 &aux
+                 (combfun (dictree--meta-dict-combfun dict))
+                 (sortfun (eval (macroexpand
+                                 `(trie-construct-sortfun
+                                   ,(dictree-comparison-function dict)))))
+                 (heap (heap-create
+                        (eval (macroexpand
+                               `(dictree--construct-meta-stack-heapfun
+                                 ,sortfun)))
+                        (length (dictree--trielist dict))))
+                 (dummy (mapc
+                         (lambda (dic)
+                           (heap-add heap (trie-stack dic type reverse)))
+                         (dictree--trielist dict)))))
+   (:constructor dictree--complete-meta-stack-create
+                (dict prefix &optional reverse
+                 &aux
+                 (combfun (dictree--meta-dict-combfun dict))
+                 (sortfun (eval (macroexpand
+                                 `(trie-construct-sortfun
+                                   ,(dictree-comparison-function dict)))))
+                 (heap (heap-create
+                        (eval (macroexpand
+                               `(dictree--construct-meta-stack-heapfun
+                                 ,sortfun
+                                 ,reverse)))
+                        (length (dictree--trielist dict))))
+                 (dummy (mapc
+                         (lambda (trie)
+                           (let ((stack (trie-complete-stack
+                                         trie prefix reverse)))
+                             (unless (trie-stack-empty-p stack)
+                               (heap-add heap stack))))
+                         (dictree--trielist dict)))))
+   (:copier nil))
+  combfun sortfun heap)
+
+
+(defmacro dictree--construct-meta-stack-heapfun (sortfun &optional reverse)
+  ;; Wrap SORTFUN, which sorts keys, so it can act on dictree--meta-stack
+  ;; elements.
+  (if reverse
+      `(lambda (a b) (,sortfun (car (dictree-stack-first b))
+                              (car (dictree-stack-first a))))
+    `(lambda (a b) (,sortfun (car (dictree-stack-first a))
+                            (car (dictree-stack-first b))))))
+
+
+(defun dictree-stack (dict &optional type reverse)
+  "Create an object that allows DICT to be accessed as if it were a stack.
+
+The stack is sorted in \"lexical\" order, i.e. the order defined
+by the DICT's comparison function, or in reverse order if REVERSE
+is non-nil. Calling `dictree-stack-pop' pops the top element (a
+key and its associated data) from the stack.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence used for the keys.
+
+Note that any modification to DICT *immediately* invalidates all
+dictree-stacks created before the modification (in particular,
+calling `dictree-stack-pop' will give unpredictable results).
+
+Operations on dictree-stacks are significantly more efficient
+than constructing a real stack from the dictionary and using
+standard stack functions. As such, they can be useful in
+implementing efficient algorithms on dictionaries. However, in
+cases where mapping functions `dictree-mapc', `dictree-mapcar' or
+`dictree-mapf' would be sufficient, it is better to use one of
+those instead."
+  (if (dictree--meta-dict-p dict)
+      (dictree--meta-stack-create dict type reverse)
+    (trie-stack (dictree--trie dict) type reverse)))
+
+
+(defun dictree-complete-stack (dict prefix &optional reverse)
+  "Return an object that allows completions of PREFIX to be accessed
+as if they were a stack.
+
+The stack is sorted in \"lexical\" order, i.e. the order defined
+by DICT's comparison function, or in reverse order if REVERSE is
+non-nil. Calling `dictree-stack-pop' pops the top element (a key
+and its associated data) from the stack.
+
+PREFIX must be a sequence (vector, list or string) that forms the
+initial part of a TRIE key. (If PREFIX is a string, it must be
+possible to apply `string' to individual elements of TRIE keys.)
+The completions returned in the alist will be sequences of the
+same type as KEY. If PREFIX is a list of sequences, completions
+of all sequences in the list are included in the stack. All
+sequences in the list must be of the same type.
+
+Note that any modification to DICT *immediately* invalidates all
+trie-stacks created before the modification (in particular,
+calling `dictree-stack-pop' will give unpredictable results).
+
+Operations on dictree-stacks are significantly more efficient
+than constructing a real stack from completions of PREFIX in DICT
+and using standard stack functions. As such, they can be useful
+in implementing efficient algorithms on tries. However, in cases
+where `dictree-complete' or `dictree-complete-ordered' is
+sufficient, it is better to use one of those instead."
+  (if (dictree--meta-dict-p dict)
+      (dictree--complete-meta-stack-create dict prefix reverse)
+    (trie-complete-stack (dictree--trie dict) prefix reverse)))
+
+
+(defun dictree-stack-pop (dictree-stack)
+  "Pop the first element from the DICTREE-STACK.
+Returns nil if the stack is empty."
+  (let ((popped (dictree--stack-pop dictree-stack)))
+    (when popped (cons (car popped) (dictree--unwrap-data (cdr popped))))))
+
+
+(defun dictree--stack-pop (dictree-stack)
+  ;; Pop the raw first element from DICTREE-STACK. Returns nil if the stack is
+  ;; empty.
+
+  ;; dictree-stack for normal dictionaries is a trie-stack
+  (if (trie-stack-p dictree-stack)
+      (trie-stack-pop dictree-stack)
+
+    ;; meta-dictionary dictree-stack...more work!
+    (let ((heap (dictree--meta-stack-heap dictree-stack))
+         (sortfun (dictree--meta-stack-sortfun dictree-stack))
+         stack curr next cell)
+      (unless (heap-empty heap)
+       ;; remove the first dictree-stack from the heap, pop it's first
+       ;; element, and add it back to the heap (note that it will almost
+       ;; certainly not end up at the root again)
+       (setq stack (heap-delete-root heap))
+       (setq curr (dictree--stack-pop stack))
+       (unless (dictree-stack-empty-p stack) (heap-add heap stack))
+       ;; peek at the first element of the new stack at the root of the heap
+       (unless (heap-empty heap)
+         (setq next (dictree--stack-first (heap-root heap)))
+         ;; repeat this as long as we keep finding elements with the same key,
+         ;; combining them together as we go
+         (when (dictree--meta-stack-combfun dictree-stack)
+           (while (and (null (funcall sortfun (car curr) (car next)))
+                       (null (funcall sortfun (car next) (car curr))))
+             (setq stack (heap-delete-root heap))
+             (setq next (dictree--stack-pop stack))
+             (setq curr
+                   (cons (car curr)
+                         (dictree--wrap-data
+                          (funcall (dictree--meta-stack-combfun dictree-stack)
+                                   (dictree--unwrap-data (cdr curr))
+                                   (dictree--unwrap-data (cdr next)))
+                          (list (dictree--unwrap-metadata (cdr curr))
+                                (dictree--unwrap-metadata (cdr next))))))
+             (heap-add heap stack)
+             (setq next (dictree--stack-first (heap-root heap))))))
+       ;; return the combined dictionary element
+       curr))))
+
+
+(defun dictree--stack-first (dictree-stack)
+  "Return the first element from DICTREE-STACK, without removing it.
+Returns nil if the stack is empty."
+  (if (trie-stack-p dictree-stack)
+      ;; normal dict
+      (trie-stack-first dictree-stack)
+    ;; meta-dict
+    (dictree--stack-first
+     (heap-root (dictree--meta-stack-heap dictree-stack)))))
+
+
+(defun dictree-stack-first (dictree-stack)
+  "Return the first element from DICTREE-STACK, without removing it.
+Returns nil if the stack is empty."
+  (let ((first (dictree--stack-first dictree-stack)))
+    (cons (car first) (dictree--unwrap-data (cdr first)))))
+
+
+(defun dictree-stack-empty-p (dictree-stack)
+  "Return t if DICTREE-STACK is empty, nil otherwise."
+  (if (trie-stack-p dictree-stack)
+      (trie-stack-empty-p dictree-stack)  ; normal dict
+    (heap-empty (dictree--meta-stack-heap dictree-stack))))  ; meta--dict
+
+
+
+
+;; ----------------------------------------------------------------
+;;                        Advanced queries
+
+(defun dictree--query (query-type dict arg
+                                 &optional
+                                 rankfun maxnum reverse no-cache filter)
+  ;; Return results of QUERY-TYPE (currently, only 'complete is implemented)
+  ;; on DICT. If RANKFUN is non-nil, return results ordered accordingly.
+
+  ;; wrap DICT in a list if necessary
+  (when (dictree-p dict) (setq dict (list dict)))
 
-  (save-excursion
-    (let (key data meta-data)
-      ;; search for text between quotes "", ignoring escaped quotes \"
-      (beginning-of-line)
-      (setq key (read (current-buffer)))
-      ;; if there is anything after the quoted text, use it as data
-      (if (eq (line-end-position) (point))
-         (list key)
-       (setq data (read (current-buffer)))
-       (if (eq (line-end-position) (point))
-           (list key data)
-         (setq meta-data (read (current-buffer)))
-         ;; return the key and data
-         (list key data meta-data)))
-      ))
-)
+  (let (cache completions cmpl)
+    ;; map over all dictionaries in list
+    (dolist (dic dict)
+      (cond
+       ;; If FILTER or custom RANKFUN was specified, look in trie since we 
don't
+       ;; cache custom searches. We pass a slightly redefined filter to
+       ;; `trie-complete' to deal with data wrapping.
+       ((or filter
+           (and rankfun (not (eq rankfun (dictree-rank-function dic)))))
+       (setq cmpl
+             (dictree--do-query
+              query-type dic arg rankfun maxnum reverse
+              (when filter
+                (eval (macroexpand `(dictree--wrap-filter ,filter)))))))
+
+
+       ;; if there's a cached result with enough completions, use it
+       ((and (setq cache
+                  (if (dictree--query-cacheparam query-type dic rankfun)
+                      (gethash (cons arg reverse)
+                               (dictree--query-cache
+                                query-type dic rankfun))
+                    nil))
+            (or (null (dictree--cache-maxnum cache))
+                (and maxnum (<= maxnum (dictree--cache-maxnum cache)))))
+       (setq cmpl (dictree--cache-completions cache))
+       ;; drop any excess completions
+       (when (and maxnum
+                  (or (null (dictree--cache-maxnum cache))
+                      (> (dictree--cache-maxnum cache) maxnum)))
+         (setcdr (nthcdr (1- maxnum) completions) nil)))
+
+       ;; if there was nothing useful in the cache, do query and time it
+       (t
+       (let (time)
+         (setq time (float-time))
+         (setq cmpl (dictree--do-query query-type
+                                       dic arg rankfun maxnum reverse nil))
+         (setq time (- (float-time) time))
+         ;; if we took longer than dictionary's completion cache threshold,
+         ;; cache the result
+         (when (and (not no-cache)
+                    (dictree--query-cacheparam query-type dic rankfun)
+                  (or (eq (dictree--query-cacheparam query-type dic rankfun)
+                          t)
+                      (> time (dictree--query-cacheparam
+                               query-type dic rankfun))))
+         (setf (dictree-modified dic) t)
+         (puthash (cons arg reverse)
+                  (dictree--cache-create cmpl maxnum)
+                  (dictree--query-cache query-type dic rankfun))))))
+
+      ;; merge new completion into completions list
+      (setq completions
+           (dictree--merge
+            completions cmpl
+            (or rankfun
+                `(lambda (a b)
+                   (,(eval (macroexpand
+                            `(trie-construct-sortfun
+                              ,(dictree-comparison-function dict))))
+                    (car a) (car b))))
+            nil maxnum))
+      )
+    completions))
+
+
+
+(defun dictree--do-query (query-type dict arg
+                                    &optional rankfun maxnum reverse filter)
+  ;; Return first MAXNUM results of running QUERY-TYPE on DICT that satisfy
+  ;; FILTER, ordered according to RANKFUN (defaulting to "lexical" order).
+
+  ;; for a meta-dict, use a dictree-stack
+  (if (dictree--meta-dict-p dict)
+      (let ((stack (funcall (dictree--query-stackfun query-type)
+                           dict arg reverse))
+           (heap (when rankfun
+                   (heap-create     ; heap order is inverse of rank order
+                       (if reverse
+                           rankfun
+                         (lambda (a b) (not (funcall rankfun a b))))
+                       (1+ maxnum))))
+           (i 0) cmpl completions)
+       ;; pop MAXNUM completions from the stack
+       (while (and (or (null maxnum) (< i maxnum))
+                   (setq cmpl (dictree-stack-pop stack)))
+         ;; check completion passes FILTER
+         (when (or (null filter) (funcall filter cmpl))
+           (if rankfun
+               (heap-add heap cmpl)   ; for ranked query, add to heap
+             (push cmpl completions)) ; for lexical query, add to list
+           (incf i)))
+       (if (null rankfun)
+           ;; for lexical query, reverse and return completion list (we built
+           ;; it backwards)
+           (nreverse completions)
+         ;; for ranked query, pass rest of completions through heap
+         (while (setq cmpl (dictree-stack-pop stack))
+           (heap-add heap cmpl)
+           (heap-delete-root heap))
+         ;; extract completions from heap
+         (while (setq cmpl (heap-delete-root heap))
+           (push cmpl completions))
+         completions))  ; return completion list
+
+    ;; for a normal dict, call corresponding trie function on dict's trie
+    ;; Note: could use a dictree-stack here too - would it be more efficient?
+    (funcall (dictree--query-triefun query-type)
+            (dictree--trie dict) arg
+            (when rankfun
+              (eval (macroexpand `(dictree--wrap-rankfun ,rankfun))))
+            maxnum reverse filter)))
+
+
+
+;; ----------------------------------------------------------------
+;;                        Completing
+
+(defun dictree-complete (dict prefix
+                        &optional
+                        rank-function maxnum reverse no-cache filter)
+  "Return an alist containing all completions of sequence PREFIX
+from dictionary DICT, along with their associated data, sorted
+according to RANKFUN (defaulting to \"lexical\" order, i.e. the
+order defined by the dictionary's comparison function,
+cf. `dictree-create'). If no completions are found, return nil.
+
+PREFIX can also be a list of sequences, in which case completions of
+all elements in the list are returned, merged together in a
+single sorted alist.
 
+DICT can also be a list of dictionaries, in which case
+completions are sought in all dictionaries in the list. (Note
+that if the same key appears in multiple dictionaries, the alist
+may contain the same key multiple times, each copy associated
+with the data from a different dictionary. If you want to combine
+identical keys, use a meta-dictionary; see
+`dictree-meta-dict-create'.)
 
+The optional integer argument MAXNUM limits the results to the
+first MAXNUM completions.
 
-(defun dictree-save-modified (&optional dict ask compilation)
-  "Save all modified dictionaries that have a non-nil autosave flag.
+If the optional argument NO-CACHE is non-nil, it prevents caching
+of the result. Ignored for dictionaries that do not have
+completion caching enabled.
 
-If optional argument DICT is a list of dictionaries or a single
-dictionary, only save those (even if their autosave flags are not
-set). If DICT is non-nil but not a list of dictionaries, save all
-dictionaries, irrespective of their autosave flag. Interactively,
-this can be set by supplying a prefix argument.
+The FILTER argument sets a filter function for the
+completions. For each potential completion, it is passed two
+arguments: the completion, and its associated data. If the filter
+function returns nil, the completion is not included in the
+results, and doesn't count towards MAXNUM.
 
-If optional argument ASK is non-nil, ask for confirmation before
-saving.
+If optional argument RANK-FUNCTION is any non-nil value that is
+not a function, the completions are sorted according to the
+dictionary's rank-function (see `dictree-create'). Any non-nil
+value that *is* a function over-rides this. In that case,
+RANK-FUNCTION should accept two arguments, both cons cells. The
+car of each contains a sequence from the trie (of the same type
+as PREFIX), the cdr contains its associated data. The
+RANK-FUNCTION should return non-nil if first argument is ranked
+strictly higher than the second, nil otherwise."
+  ;; run completion query
+  (dictree--query
+   'complete dict prefix
+   (when rank-function
+     (if (functionp rank-function)
+        rank-function
+       (dictree-rank-function (if (listp dict) (car dict) dict))))
+   maxnum reverse no-cache filter))
 
-Optional argument COMPILATION determines whether to save the
-dictionaries in compiled or uncompiled form. The default is to
-save both forms. See `dictree-write'."
 
-  ;; sort out DICT argument
-  (cond
-   ((dictree-p dict) (setq dict (list dict)))
-   ((and (listp dict) (dictree-p (car dict))))
-   (dict (setq dict 'all)))
 
-  ;; For each dictionary in list / each loaded dictionary, check if dictionary
-  ;; has been modified. If so, save it if autosave is on or if saving all
-  (dolist (dic (if (or (null dict) (eq dict 'all))
-                   dictree-loaded-list
-                 dict))
-    (when (and (dictree--modified dic)
-              (or (eq dict 'all) (dictree--autosave dic))
-              (or (not ask)
-                  (y-or-n-p (format "Save modified dictionary %s? "
-                                    (dictree--filename dic)))))
-      (dictree-save dic compilation)
-      (dictree--set-modified dic nil)))
-)
 
 
+;; ----------------------------------------------------------------
+;;                    Persistent storage
 
 (defun dictree-save (dict &optional compilation)
   "Save dictionary DICT to it's associated file.
@@ -1650,14 +1785,13 @@ both forms. See `dictree-write'."
            (read-file-name
             (format "Save %s to file (leave blank to NOT save): "
                     (dictree--name dict))))
-      (dictree--set-filename dict filename))
+      (setf (dictree-filename dict) filename))
 
     ;; if filename is blank, don't save
     (if (string= filename "")
-       (message "Dictionary %s NOT saved" (dictree--name dict))
+       (message "No file supplied. Dictionary %s NOT saved" (dictree--name 
dict))
       ;; otherwise write dictionary to file without requiring confirmation
-      (dictree-write dict filename t compilation)))
-)
+      (dictree-write dict filename t compilation))))
 
 
 
@@ -1716,7 +1850,7 @@ and OVERWRITE is the prefix argument."
              (y-or-n-p
               (format "File %s already exists. Overwrite? "
                       (concat filename ".el(c)"))))
-;      (condition-case nil
+      (condition-case nil
          (progn
            ;; move the uncompiled version to its final destination
            (unless (eq compilation 'compiled)
@@ -1734,19 +1868,60 @@ and OVERWRITE is the prefix argument."
                  (rename-file (concat tmpfile ".elc")
                               (concat filename ".elc") t)
                (error))))
-;      (error (error "Error saving %s. Dictionary not saved" dictname)))
+       (error (error "Error saving. Dictionary %s NOT saved" dictname)))
 
       ;; if writing to a different name, unload dictionary under old name and
       ;; reload it under new one
-      (dictree--set-modified dict nil)
-      (unless (string= dictname (dictree--name dict))
+      (setf (dictree-modified dict) nil)
+      (unless (string= dictname (dictree-name dict))
        (dictree-unload dict)
        (dictree-load filename)))
 
     (delete-file tmpfile)
     (message "Dictionary %s saved to %s" dictname filename)
-    t)  ; return t to indicate dictionary was successfully saved
-)
+    t))  ; return t to indicate dictionary was successfully saved
+
+
+
+(defun dictree-save-modified (&optional dict ask compilation)
+  "Save all modified dictionaries that have a non-nil autosave flag.
+
+If optional argument DICT is a list of dictionaries or a single
+dictionary, only save those (even if their autosave flags are not
+set). If DICT is non-nil but not a list of dictionaries, save all
+dictionaries, irrespective of their autosave flag. Interactively,
+this can be set by supplying a prefix argument.
+
+If optional argument ASK is non-nil, ask for confirmation before
+saving.
+
+Optional argument COMPILATION determines whether to save the
+dictionaries in compiled or uncompiled form. The default is to
+save both forms. See `dictree-write'."
+
+  ;; sort out DICT argument
+  (cond
+   ((dictree-p dict) (setq dict (list dict)))
+   ((and (listp dict) (dictree-p (car dict))))
+   (dict (setq dict 'all)))
+
+  ;; For each dictionary in list / each loaded dictionary, check if dictionary
+  ;; has been modified. If so, save it if autosave is on or if saving all
+  (dolist (dic (if (or (null dict) (eq dict 'all))
+                   dictree-loaded-list
+                 dict))
+    (when (and (dictree-modified dic)
+              (or (eq dict 'all) (dictree-autosave dic))
+              (or (not ask)
+                  (y-or-n-p (format "Save modified dictionary %s? "
+                                    (dictree-filename dic)))))
+      (dictree-save dic compilation)
+      (setf (dictree-modified dic) nil))))
+
+
+;; Add the dictree-save-modified function to the kill-emacs-hook to save
+;; modified dictionaries when exiting emacs
+(add-hook 'kill-emacs-hook 'dictree-save-modified)
 
 
 
@@ -1770,15 +1945,14 @@ Returns t if successful, nil otherwise."
 
     ;; ensure the dictionary name and file name associated with the
     ;; dictionary match the file it was loaded from
-    (dictree--set-filename dict (expand-file-name file))
-    (dictree--set-name dict dictname)
+    (setf (dictree-filename dict) (expand-file-name file))
+    (setf (dictree-name dict) dictname)
 
-    ;; make sure the dictionary is in dictree-loaded-list (normally the
-    ;; lisp code in the dictionary itself should do that)
+    ;; make sure the dictionary is in dictree-loaded-list (normally the lisp
+    ;; code in the dictionary itself should do this, but just to make sure...)
     (unless (memq dict dictree-loaded-list)
       (push dict dictree-loaded-list))
-    (message (format "Loaded dictionary %s" dictname)))
-)
+    (message (format "Loaded dictionary %s" dictname))))
 
 
 
@@ -1791,22 +1965,116 @@ NOT be saved even if its autosave flag is set."
 
   ;; if dictionary has been modified, autosave is set and not overidden,
   ;; save it first
-  (when (and (dictree--modified dict)
+  (when (and (dictree-modified dict)
             (null dont-save)
-            (or (eq (dictree--autosave dict) t)
-                (and (eq (dictree--autosave dict) 'ask)
+            (or (eq (dictree-autosave dict) t)
+                (and (eq (dictree-autosave dict) 'ask)
                      (y-or-n-p
                       (format
                        "Dictionary %s modified. Save before unloading? "
-                       (dictree--name dict))))))
+                       (dictree-name dict))))))
     (dictree-save dict)
-    (dictree--set-modified dict nil))
+    (setf (dictree-modified dict) nil))
 
   ;; remove dictionary from list of loaded dictionaries and unload it
   (setq dictree-loaded-list (delq dict dictree-loaded-list))
-  (unintern (dictree--name dict))
-  (message "Dictionary %s unloaded" (dictree--name dict))
-)
+  (unintern (dictree-name dict))
+  (message "Dictionary %s unloaded" (dictree-name dict)))
+
+
+
+;; ----------------------------------------------------------------
+;;                Dumping and restoring contents
+
+(defun dictree-populate-from-file (dict file)
+  "Populate dictionary DICT from the key list in file FILE.
+
+Each line of the file should contain a key, either a string
+\(delimeted by \"\), a vector or a list. (Use the escape sequence
+\\\" to include a \" in a string.) If a line does not contain a
+key, it is silently ignored. The keys should ideally be sorted
+\"lexically\", as defined by the dictionary's comparison-function
+\(see `dictree-create'\).
+
+Each line can optionally include data and meta-data to be
+associated with the key, in that order, and separated from each
+other and the key by whitespace.
+
+
+Technicalities:
+
+The key, data and meta-data are read as lisp expressions using
+`read', and are read from the middle outwards, i.e. first the
+middle key is read, then the key directly after it, then the key
+directly before it, then the one two lines after the middle, and
+so on. Assuming the keys in the file are sorted \"lexically\",
+this helps produce a reasonably efficient dictionary structure."
+
+  (save-excursion
+    (let ((buff (generate-new-buffer " *dictree-populate*")))
+      ;; insert the key list into a temporary buffer
+      (set-buffer buff)
+      (insert-file-contents file)
+
+      ;; insert the keys starting from the median to ensure a reasonably
+      ;; well-balanced tree
+      (let* ((lines (count-lines (point-min) (point-max)))
+            (midpt (+ (/ lines 2) (mod lines 2)))
+            entry)
+        ;; insert the median key and set the dictionary's modified flag
+       (dictree-goto-line midpt)
+       (when (setq entry (dictree-read-line))
+         (dictree-insert dict (car entry) (nth 1 entry))
+         (dictree-set-meta-data dict (car entry) (nth 2 entry)))
+       (message "Inserting keys in %s...(1 of %d)"
+                (dictree-name dict) lines)
+        ;; insert keys successively further away from the median in both
+        ;; directions
+       (dotimes (i (1- midpt))
+         (dictree-goto-line (+ midpt i 1))
+         (when (setq entry (dictree-read-line))
+           (dictree-insert dict (car entry) (nth 1 entry))
+           (dictree-set-meta-data dict (car entry) (nth 2 entry)))
+         (when (= 49 (mod i 50))
+           (message "Inserting keys in %s...(%d of %d)"
+                    (dictree-name dict) (+ (* 2 i) 2) lines))
+         (dictree-goto-line (- midpt i 1))
+         (when (setq entry (dictree-read-line))
+           (dictree-insert dict (car entry) (nth 1 entry))
+           (dictree-set-meta-data dict (car entry) (nth 2 entry))))
+
+        ;; if file contains an even number of keys, we still have to add
+        ;; the last one
+       (when (= 0 (mod lines 2))
+         (dictree-goto-line lines)
+         (when (setq entry (dictree-read-line))
+           (dictree-insert dict (car entry) (nth 1 entry))
+           (dictree-set-meta-data dict (car entry) (nth 2 entry))))
+       (message "Inserting keys in %s...done" (dictree-name dict)))
+
+      (kill-buffer buff))))
+
+
+
+;;; FIXME: doesn't fail gracefully if file has invalid format
+(defun dictree-read-line ()
+  "Return a cons containing the key and data \(if any, otherwise
+nil\) at the current line of the current buffer. Returns nil if
+line is in wrong format."
+  (save-excursion
+    (let (key data meta-data)
+      ;; search for text between quotes "", ignoring escaped quotes \"
+      (beginning-of-line)
+      (setq key (read (current-buffer)))
+      ;; if there is anything after the quoted text, use it as data
+      (if (eq (line-end-position) (point))
+         (list key)
+       (setq data (read (current-buffer)))
+       (if (eq (line-end-position) (point))
+           (list key data)
+         (setq meta-data (read (current-buffer)))
+         ;; return the key and data
+         (list key data meta-data))))))
 
 
 
@@ -1838,33 +2106,29 @@ data can not be used to recreate the dictionary using
 
   ;; dump keys
   (message "Dumping keys from %s to %s..."
-          (dictree--name dict) (buffer-name buffer))
+          (dictree-name dict) (buffer-name buffer))
   (let ((count 0) (dictsize (dictree-size dict)))
     (message "Dumping keys from %s to %s...(key 1 of %d)"
-            (dictree--name dict) (buffer-name buffer) dictsize)
-    ;; construct dump function
-    (let ((dump-func
-          (lambda (key cell)
-            (when (= 99 (mod count 100))
-              (message "Dumping keys from %s to %s...(key %d of %d)"
-                       (dictree--name dict) (buffer-name buffer)
-                       (1+ count) dictsize))
-            (insert (prin1-to-string key))
-            (let (data)
-              (when (setq data (dictree--get-data cell))
-                (insert " " (prin1-to-string data)))
-              (when (setq data (dictree--get-metadata cell))
-                (insert " " (prin1-to-string data)))
-              (insert "\n"))
-            (setq count (1+ count)))))
-      ;; map dump function over dictionary
-      (if (dictree--lookup-only dict)
-         (maphash dump-func (dictree--lookup-hash dict))
-       (tstree-map dump-func (dictree--tstree dict) type)))
+            (dictree-name dict) (buffer-name buffer) dictsize)
+
+    ;; map dump function over dictionary
+    (dictree--mapc
+     (lambda (key data metadata)
+       (when (= 99 (mod count 100))
+        (message "Dumping keys from %s to %s...(key %d of %d)"
+                 (dictree-name dict) (buffer-name buffer)
+                 (1+ count) dictsize))
+       (insert (prin1-to-string key))
+       (let (data)
+        (when data (insert " " (prin1-to-string data)))
+        (when metadata (insert " " (prin1-to-string metadata)))
+        (insert "\n"))
+       (setq count (1+ count)))
+     dict type)  ; dictree-mapc target
+
     (message "Dumping keys from %s to %s...done"
-            (dictree--name dict) (buffer-name buffer)))
-  (switch-to-buffer buffer)
-)
+            (dictree-name dict) (buffer-name buffer)))
+  (switch-to-buffer buffer))
 
 
 
@@ -1896,133 +2160,7 @@ data can not be used to recreate the dictionary using
       (save-window-excursion
        (dictree-dump-to-buffer dict buff type)
        (write-file filename))
-      (kill-buffer buff)))
-)
-
-
-
-
-
-;;; ==================================================================
-;;;                   Internal dictionary functions
-
-(defun dictree-update-cache (dict key newdata &optional deleted)
-  "Synchronise dictionary DICT's caches,
-given that the data associated with KEY has been changed to
-NEWDATA, or KEY has been deleted if DELETED is non-nil (NEWDATA
-is ignored in that case)."
-
-  (let (seq cache entry cmpl maxnum)
-
-    ;; synchronise the lookup cache if dict is a meta-dictionary,
-    ;; since it's not done automatically
-    (when (and (dictree--meta-dict-p dict)
-              (dictree--lookup-speed dict)
-              (gethash key (dictree--lookup-hash dict)))
-      (if deleted
-         (remhash key (dictree--lookup-hash dict))
-       (puthash key newdata (dictree--lookup-hash dict))))
-
-
-    ;; synchronize the completion hash, if it exists
-    (when (dictree--completion-speed dict)
-      ;; have to check every possible subsequence that could be cached!
-      (dotimes (i (1+ (length key)))
-       (setq seq (substring key 0 i))
-       (when (setq cache (gethash seq (dictree--completion-hash dict)))
-         (setq cmpl (dictree--cache-completions cache))
-         (setq maxnum (dictree--cache-maxnum cache))
-         ;; If key has not been deleted, and is already in the
-         ;; completion list, only update it if dict is a meta-dictionary
-         ;; (since it's not updated automatically).
-         (if (and (not deleted) (setq entry (assoc key cmpl)))
-             (when (dictree--meta-dict-p dict)
-               (setcdr entry (dictree-lookup dict key)))
-           ;; Otherwise...
-           ;; (Note: we could avoid looking in the tree by adding the key
-           ;; to the cache list, re-sorting alphabetically, and deleting
-           ;; the last key in the list, but it's probably not worth it,
-           ;; and would deny us the opportunity of shrinking the cache.)
-           (let (time newcmpl)
-             ;; re-complete from the tree
-             (setq time (float-time))
-             (setq newcmpl
-                   (tstree-complete (dictree--tstree dict) seq maxnum))
-             (setq time (- (float-time) time))
-             ;; if the lookup still takes too long, update the cache,
-             ;; otherwise delete the cache entry
-             (if (or (eq (dictree--completion-speed dict) t)
-                     (> time (dictree--completion-speed dict)))
-                 (dictree--set-cache-completions cache newcmpl)
-               (remhash seq (dictree--completion-hash dict))))
-           ))))
-
-
-    ;; synchronize the ordered completion hash, if it exists
-    (when (dictree--ordered-speed dict)
-      ;; have to check every possible subsequence that could
-      ;; be cached!
-      (dotimes (i (1+ (length key)))
-       (setq seq (dictree--subseq key 0 i))
-       (when (setq cache (gethash seq (dictree--ordered-hash dict)))
-         (setq cmpl (dictree--cache-completions cache))
-         (setq maxnum (dictree--cache-maxnum cache))
-         (cond
-
-          ;; if key was deleted, have to update cache from the tree
-          (deleted
-           (let (time newcmpl)
-             ;; re-complete from the tree
-             (setq time (float-time))
-             (setq newcmpl (tstree-complete-ordered
-                            (dictree--tstree dict) seq maxnum))
-             (setq time (- (float-time) time))
-             ;; if the lookup still takes too long, update the cache,
-             ;; otherwise delete the cache entry
-             (if (or (eq (dictree--ordered-speed dict) t)
-                     (> time (dictree--ordered-speed dict)))
-                 (dictree--set-cache-completions cache newcmpl)
-               (remhash seq (dictree--ordered-hash dict)))))
-
-          ;; if key is in the completion list...
-          ((setq entry (assoc key cmpl))
-           ;; Update the cache entry if dict is a meta-dictionary,
-           ;; since it's not done automatically.
-           (when (dictree--meta-dict-p dict)
-             (setcdr entry
-                     (dictree--wrap-data (dictree-lookup dict key))))
-           ;; re-sort the list
-           (dictree--set-cache-completions
-            cache (sort cmpl (dictree--rankfun dict)))
-           (setq cmpl (dictree--cache-completions cache))
-           ;; If key is now at the end of the list, we've no choice but
-           ;; to update from the tree.
-           (when (equal (caar (last cmpl)) key)
-             (let (time newcmpl)
-               ;; re-complete from the tree
-               (setq time (float-time))
-               (setq newcmpl (tstree-complete-ordered
-                           (dictree--tstree dict) seq maxnum))
-               (setq time (- (float-time) time))
-               ;; if the lookup still takes too long, update the cache,
-               ;; otherwise delete the cache entry
-               (if (or (eq (dictree--ordered-speed dict) t)
-                       (> time (dictree--ordered-speed dict)))
-                   (dictree--set-cache-completions cache newcmpl)
-                 (remhash seq (dictree--ordered-hash dict))))))
-
-          ;; if key isn't in the completion list...
-          (t
-           ;; add key to the end of the list and re-sort
-           (setcdr (last cmpl) (list (cons key newdata)))
-           (dictree--set-cache-completions
-            cache (sort cmpl (dictree--rankfun dict)))
-           (setq cmpl (dictree--cache-completions cache))
-           ;; remove excess completions
-           (when (> (length cmpl) maxnum)
-             (setcdr (nthcdr (1- maxnum) cmpl) nil)))
-          )))))
-)
+      (kill-buffer buff))))
 
 
 
@@ -2030,141 +2168,142 @@ is ignored in that case)."
   "Write code for normal dictionary DICT to current buffer,
 giving it the name DICTNAME."
 
-  (let (hashcode tmpdict lookup-alist completion-alist ordered-alist)
-
-    ;; if the dictionary is lookup only, dump the lookup cache to an alist
-    (if (dictree--lookup-only dict)
-       (progn
-         (maphash (lambda (key val) (push (cons key val) lookup-alist))
-                  (dictree--lookup-hash dict))
-         ;; generate code to reconstruct the lookup hash table
-         (setq hashcode
-               (concat
-                "(let ((lookup-hash (make-hash-table :test 'equal)))\n"
-                "  (mapcar (lambda (entry)\n"
-                "    (puthash (car entry) (cdr entry) lookup-hash))\n"
-                "    (dictree--lookup-hash " dictname "))\n"
-                "  (dictree--set-lookup-hash " dictname
-                                             " lookup-hash)\n"))
-         ;; generate the structure to save
-         (setq tmpdict (list 'DICT dictname nil
-                             (dictree--autosave dict) nil t
-                             nil (dictree--insfun dict) nil
-                             lookup-alist nil nil nil nil nil)))
-
-
-      ;; otherwise, dump caches to alists as necessary and generate code
-      ;; to reonstruct the hash tables from the alists
-      (let ((lookup-speed (dictree--lookup-speed dict))
-           (completion-speed (dictree--completion-speed dict))
-           (ordered-speed (dictree--ordered-speed dict)))
-
-       ;; create the lookup alist, if necessary
-       (when lookup-speed
-         (maphash
-          (lambda (key val)
-            (push
-             (cons key (cons
-                        (mapcar 'car (dictree--cache-completions val))
-                        (dictree--cache-maxnum val)))
-             lookup-alist))
-          (dictree--lookup-hash dict))
-         ;; generate code to reconstruct the lookup hash table
-         (setq hashcode
-               (concat
-                hashcode
-                "(let ((lookup-hash (make-hash-table :test 'equal))\n"
-                "      (tstree (dictree--tstree " dictname ")))\n"
-                "  (mapc\n"
-                "   (lambda (entry)\n"
-                "     (puthash\n"
-                "      (car entry)\n"
-                "      (dictree--cache-create\n"
-                "       (mapcar\n"
-                "        (lambda (key)\n"
-                "          (cons key (tstree-member tstree key)))\n"
-                "        (dictree--cache-completions (cdr entry)))\n"
-                "       (dictree--cache-maxnum (cdr entry)))\n"
-                "      lookup-hash))\n"
-                "   (dictree--lookup-hash " dictname "))\n"
-                "  (dictree--set-lookup-hash " dictname
-                                             " lookup-hash))\n")))
-
-       ;; create the completion alist, if necessary
-       (when completion-speed
-         (maphash
-          (lambda (key val)
-            (push
-             (cons key (cons
-                        (mapcar 'car (dictree--cache-completions val))
-                        (dictree--cache-maxnum val)))
-             completion-alist))
-          (dictree--completion-hash dict))
-         ;; generate code to reconstruct the completion hash table
-         (setq
-          hashcode
-          (concat
-           hashcode
-           "(let ((completion-hash (make-hash-table :test 'equal))\n"
-           "      (tstree (dictree--tstree " dictname ")))\n"
-           "  (mapc\n"
-           "   (lambda (entry)\n"
-           "     (puthash\n"
-           "      (car entry)\n"
-           "      (dictree--cache-create\n"
-           "       (mapcar\n"
-           "        (lambda (key)\n"
-           "          (cons key (tstree-member tstree key)))\n"
-           "        (dictree--cache-completions (cdr entry)))\n"
-           "       (dictree--cache-maxnum (cdr entry)))\n"
-           "      completion-hash))\n"
-           "   (dictree--completion-hash " dictname "))\n"
-           "  (dictree--set-completion-hash " dictname
-                                            " completion-hash))\n")))
-
-       ;; create the ordered completion alist, if necessary
-       (when ordered-speed
-         (maphash
-          (lambda (key val)
-            (push
-             (cons key (cons
-                        (mapcar 'car (dictree--cache-completions val))
-                        (dictree--cache-maxnum val)))
-             ordered-alist))
-          (dictree--ordered-hash dict))
-         ;; generate code to reconstruct the ordered hash table
-         (setq hashcode
-               (concat
-                hashcode
-                "(let ((ordered-hash (make-hash-table :test 'equal))\n"
-                "      (tstree (dictree--tstree " dictname ")))\n"
-                "  (mapc\n"
-                "   (lambda (entry)\n"
-                "     (puthash\n"
-                "      (car entry)\n"
-                "      (dictree--cache-create\n"
-                "       (mapcar\n"
-                "        (lambda (key)\n"
-                "          (cons key (tstree-member tstree key)))\n"
-                "        (dictree--cache-completions (cdr entry)))\n"
-                "       (dictree--cache-maxnum (cdr entry)))\n"
-                "      ordered-hash))\n"
-                "   (dictree--ordered-hash " dictname "))\n"
-                "  (dictree--set-ordered-hash " dictname
-                                              " ordered-hash))\n")))
-
-       ;; generate the structure to save
-       (setq tmpdict (list 'DICT dictname nil
-                           (dictree--autosave dict)
-                           nil nil
-                           (dictree--tstree dict)
-                           (dictree--insfun dict)
-                           (dictree--rankfun dict)
-                           lookup-alist lookup-speed
-                           completion-alist completion-speed
-                           ordered-alist ordered-speed))
-       ))
-
+  (let (hashcode
+       tmpdict
+       lookup-alist
+       complete-alist
+       complete-ranked-alist)
+
+    ;; dump caches to alists as necessary and generate code to reonstruct the
+    ;; hash tables from the alists
+
+    ;; create the lookup alist, if necessary
+    (when (dictree--lookup-cache-threshold dict)
+      (maphash
+       (lambda (key val)
+        (push
+         (cons key
+               (cons (mapcar 'car (dictree--cache-completions val))
+                     (dictree--cache-maxnum val)))
+         lookup-alist))
+       (dictree--lookup-cache dict))
+      ;; generate code to reconstruct the lookup hash table
+      (setq hashcode
+           (concat
+            hashcode
+            "(let ((lookup-cache (make-hash-table :test 'equal))\n"
+            "      (trie (dictree--trie " dictname ")))\n"
+            "  (mapc\n"
+            "   (lambda (entry)\n"
+            "     (puthash\n"
+            "      (car entry)\n"
+            "      (dictree--cache-create\n"
+            "       (mapcar\n"
+            "        (lambda (key)\n"
+            "          (cons key (trie-member trie key)))\n"
+            "        (dictree--cache-completions (cdr entry)))\n"
+            "       (dictree--cache-maxnum (cdr entry)))\n"
+            "      lookup-cache))\n"
+            "   (dictree--lookup-cache " dictname "))\n"
+            "  (setf (dictree--lookup-cache " dictname ") lookup-cache))\n"
+            )))
+
+    ;; create the completion alist, if necessary
+    (when (dictree--complete-cache-threshold dict)
+      (maphash
+       (lambda (key val)
+        (push
+         (cons key
+               (cons (mapcar 'car (dictree--cache-completions val))
+                     (dictree--cache-maxnum val)))
+         complete-alist))
+       (dictree-complete-cache dict))
+      ;; generate code to reconstruct the completion hash table
+      (setq
+       hashcode
+       (concat
+       hashcode
+       "(let ((complete-cache (make-hash-table :test 'equal))\n"
+       "      (trie (dictree--trie " dictname ")))\n"
+       "  (mapc\n"
+       "   (lambda (entry)\n"
+       "     (puthash\n"
+       "      (car entry)\n"
+       "      (dictree--cache-create\n"
+       "       (mapcar\n"
+       "        (lambda (key)\n"
+       "          (cons key (trie-member trie key)))\n"
+       "        (dictree--cache-completions (cdr entry)))\n"
+       "       (dictree--cache-maxnum (cdr entry)))\n"
+       "      complete-cache))\n"
+       "   (dictree--complete-cache " dictname "))\n"
+       "  (setf (dictree--complete-cache " dictname ") complete-cache))\n"
+       )))
+
+    ;; create the ordered completion alist, if necessary
+    (when (dictree--complete-ranked-cache-threshold dict)
+      (maphash
+       (lambda (key val)
+        (push
+         (cons key
+               (cons (mapcar 'car (dictree--cache-completions val))
+                     (dictree--cache-maxnum val)))
+         complete-ranked-alist))
+       (dictree--complete-ranked-cache dict))
+      ;; generate code to reconstruct the ordered hash table
+      (setq hashcode
+           (concat
+            hashcode
+            "(let ((complete-ranked-cache"
+            "(make-hash-table :test 'equal))\n"
+            "      (trie (dictree--trie " dictname ")))\n"
+            "  (mapc\n"
+            "   (lambda (entry)\n"
+            "     (puthash\n"
+            "      (car entry)\n"
+            "      (dictree--cache-create\n"
+            "       (mapcar\n"
+            "        (lambda (key)\n"
+            "          (cons key (trie-member trie key)))\n"
+            "        (dictree--cache-completions (cdr entry)))\n"
+            "       (dictree--cache-maxnum (cdr entry)))\n"
+            "      complete-ranked-cache))\n"
+            "   (dictree--complete-ranked-cache " dictname "))\n"
+            "  (setf (dictree--complete-ranked-cache " dictname ")"
+                   " complete-ranked-cache))\n"
+            )))
+
+    ;; generate the structure to save
+    (setq tmpdict (dictree-create))
+    (setf (dictree--name tmpdict) dictname)
+    (setf (dictree--filename tmpdict) nil)  ; filename gets set on loading
+    (setf (dictree--autosave tmpdict) (dictree--autosave dict))
+    (setf (dictree--modified tmpdict) nil)
+    (setf (dictree--comparison-function tmpdict)
+         (dictree--comparison-function dict))
+    (setf (dictree--insert-function tmpdict)
+         (dictree--insert-function dict))
+    (setf (dictree--insfun tmpdict)
+         (dictree--insfun dict))
+    (setf (dictree--rank-function tmpdict)
+         (dictree--rank-function dict))
+    (setf (dictree--rankfun tmpdict)
+         (dictree--rankfun dict))
+    (setf (dictree--cache-policy tmpdict)
+         (dictree--cache-policy dict))
+    (setf (dictree--cache-update-policy tmpdict)
+         (dictree--cache-update-policy dict))
+    (setf (dictree--lookup-cache tmpdict) lookup-alist)
+    (setf (dictree--lookup-cache-threshold tmpdict)
+         (dictree--lookup-cache-threshold dict))
+    (setf (dictree--complete-cache tmpdict) complete-alist)
+    (setf (dictree--complete-cache-threshold tmpdict)
+         (dictree--complete-cache-threshold dict))
+    (setf (dictree--complete-ranked-cache tmpdict) complete-ranked-alist)
+    (setf (dictree--complete-ranked-cache-threshold tmpdict)
+         (dictree--complete-ranked-cache-threshold dict))
+    (setf (dictree--trie tmpdict) (dictree--trie dict))
+    (setf (dictree--meta-dict-list tmpdict) nil)
 
     ;; write lisp code that generates the dictionary object
     (insert "(provide '" dictname ")\n")
@@ -2172,11 +2311,10 @@ giving it the name DICTNAME."
     (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n")
     (insert "(setq " dictname " '" (prin1-to-string tmpdict) ")\n")
     (insert hashcode)
-    (insert "(dictree--set-filename " dictname
+    (insert "(setf (dictree-filename " dictname ")"
            " (locate-library \"" dictname "\"))\n")
     (insert "(unless (memq " dictname " dictree-loaded-list)"
-           " (push " dictname " dictree-loaded-list))\n"))
-)
+           " (push " dictname " dictree-loaded-list))\n")))
 
 
 
@@ -2185,99 +2323,114 @@ giving it the name DICTNAME."
   "Write code for meta-dictionary DICT to current buffer,
 giving it the name DICTNAME."
 
-  (let (hashcode tmpdict lookup-alist completion-alist ordered-alist)
+  (let (hashcode tmpdict lookup-alist complete-alist
+                complete-ranked-alist)
 
-    ;; dump caches to alists as necessary and generate code to reonstruct
+    ;; dump caches to alists as necessary and generate code to reconstruct
     ;; the hash tables from the alists
-    (let ((lookup-speed (dictree--lookup-speed dict))
-         (completion-speed (dictree--completion-speed dict))
-         (ordered-speed (dictree--ordered-speed dict)))
-
-      ;; create the lookup alist, if necessary
-      (when lookup-speed
-       (maphash (lambda (key val)
-                  (push (cons key (mapcar 'car val)) lookup-alist))
-                (dictree--lookup-hash dict))
-       ;; generate code to reconstruct the lookup hash table
-       (setq hashcode
-             (concat
-              hashcode
-              "(let ((lookup-hash (make-hash-table :test 'equal)))\n"
-              "  (mapc (lambda (entry)\n"
-              "    (puthash (car entry) (cdr entry) lookup-hash))\n"
-              "    (dictree--lookup-hash " dictname "))\n"
-              "  (dictree--set-lookup-hash " dictname
-                                           " lookup-hash))\n")))
-
-      ;; create the completion alist, if necessary
-      (when completion-speed
-       (maphash (lambda (key val)
-                  (push (cons key (mapcar 'car val)) completion-alist))
-                (dictree--completion-hash dict))
-       ;; generate code to reconstruct the completion hash table
-       (setq hashcode
-             (concat
-              hashcode
-              "(let ((completion-hash (make-hash-table :test 'equal)))\n"
-              "  (mapc (lambda (entry)\n"
-              "    (puthash (car entry) (cdr entry) completion-hash))\n"
-              "    (dictree--completion-hash " dictname "))\n"
-              "  (dictree--set-completion-hash " dictname
-                                               " completion-hash))\n")))
-
-      ;; create the ordered completion alist, if necessary
-      (when ordered-speed
-       (maphash (lambda (key val) (push (cons key val) ordered-alist))
-                (dictree--ordered-hash dict))
-       ;; generate code to reconstruct the ordered hash table
-       (setq hashcode
-             (concat
-              hashcode
-              "(let ((ordered-hash (make-hash-table :test 'equal)))\n"
-              "  (mapc (lambda (entry)\n"
-              "    (puthash (car entry) (cdr entry) ordered-hash))\n"
-              "    (dictree--ordered-hash " dictname "))\n"
-              "  (dictree--set-ordered-hash " dictname
-                                            " ordered-hash))\n")))
-
-
-      ;; generate the structure to save
-      (setq tmpdict
-           (if (dictree--lookup-only dict)
-               ;; lookup-only meta-dictionary
-               (list 'DICT dictname nil (dictree--autosave dict) nil t
-                     nil (dictree--combfun dict) nil
-                     lookup-alist lookup-speed nil nil nil nil)
-             ;; normal meta-dictionary
-             (list 'DICT dictname nil (dictree--autosave dict) nil nil
-                   (mapcar 'dictree-name (dictree--dict-list dict))
-                   (dictree--combfun dict) (dictree--rankfun dict)
-                   lookup-alist lookup-speed
-                   completion-alist completion-speed
-                   ordered-alist ordered-speed))))
 
+    ;; create the lookup alist, if necessary
+    (when (dictree--lookup-cache-threshold dict)
+      (maphash (lambda (key val)
+                (push (cons key (mapcar 'car val)) lookup-alist))
+              (dictree--meta-dict-lookup-cache dict))
+      ;; generate code to reconstruct the lookup hash table
+      (setq hashcode
+           (concat
+            hashcode
+            "(let ((lookup-cache (make-hash-table :test 'equal)))\n"
+            "  (mapc (lambda (entry)\n"
+            "    (puthash (car entry) (cdr entry) lookup-cache))\n"
+            "    (dictree--meta-dict-lookup-cache " dictname "))\n"
+            "  (setf (dictree--meta-dict-lookup-cache " dictname ")"
+                   " lookup-cache))\n")))
+
+    ;; create the completion alist, if necessary
+    (when (dictree--complete-cache-threshold dict)
+      (maphash (lambda (key val)
+                (push (cons key (mapcar 'car val)) complete-alist))
+              (dictree--meta-dict-complete-cache dict))
+      ;; generate code to reconstruct the completion hash table
+      (setq hashcode
+           (concat
+            hashcode
+            "(let ((complete-cache (make-hash-table :test 'equal)))\n"
+            "  (mapc (lambda (entry)\n"
+            "    (puthash (car entry) (cdr entry) complete-cache))\n"
+            "    (dictree--meta-dict-complete-cache " dictname "))\n"
+            "  (setf (dictree--meta-dict-complete-cache " dictname ")"
+                   " complete-cache))\n")))
+
+    ;; create the ordered completion alist, if necessary
+    (when (dictree--complete-ranked-cache-threshold dict)
+      (maphash (lambda (key val)
+                (push (cons key val) complete-ranked-alist))
+              (dictree--meta-dict-complete-ranked-cache dict))
+      ;; generate code to reconstruct the ordered hash table
+      (setq hashcode
+           (concat
+            hashcode
+            "(let ((complete-ranked-cache (make-hash-table :test 'equal)))\n"
+            "  (mapc (lambda (entry)\n"
+            "    (puthash (car entry) (cdr entry) complete-ranked-cache))\n"
+            "    (dictree--meta-dict-complete-ranked-cache " dictname "))\n"
+            "  (setf (dictree--meta-dict-complete-ranked-cache " dictname ")"
+                   " complete-ranked-cache))\n")))
+
+
+    ;; generate the structure to save
+    (setq tmpdict (dictree-create))
+    (setf (dictree--meta-dict-name tmpdict) dictname)
+    (setf (dictree--meta-dict-filename tmpdict) nil)  ; set on loading
+    (setf (dictree--meta-dict-autosave tmpdict) (dictree--autosave dict))
+    (setf (dictree--meta-dict-modified tmpdict) nil)
+    (setf (dictree--meta-dict-combine-function tmpdict)
+         (dictree--meta-dict-combine-function dict))
+    (setf (dictree--meta-dict-combfun tmpdict)
+         (dictree--meta-dict-combfun dict))
+    (setf (dictree--meta-dict-cache-policy tmpdict)
+         (dictree--meta-dict-cache-policy dict))
+    (setf (dictree--meta-dict-cache-update-policy tmpdict)
+         (dictree--meta-dict-cache-update-policy dict))
+    (setf (dictree--meta-dict-lookup-cache tmpdict)
+         lookup-alist)
+    (setf (dictree--meta-dict-lookup-cache-threshold tmpdict)
+         (dictree--meta-dict-lookup-cache-threshold dict))
+    (setf (dictree--meta-dict-complete-cache tmpdict)
+         complete-alist)
+    (setf (dictree--meta-dict-complete-cache-threshold tmpdict)
+         (dictree--meta-dict-complete-cache-threshold dict))
+    (setf (dictree--meta-dict-complete-ranked-cache tmpdict)
+         complete-ranked-alist)
+    (setf (dictree--meta-dict-complete-ranked-cache-threshold tmpdict)
+         (dictree--meta-dict-complete-ranked-cache-threshold dict))
+    (setf (dictree--meta-dict-dictlist tmpdict)
+         (dictree--meta-dict-dictlist dict))
+    (setf (dictree--meta-dict-meta-dict-list tmpdict) nil)
 
     ;; write lisp code that generates the dictionary object
     (insert "(provide '" dictname ")\n")
     (insert "(require 'dict-tree)\n")
     (mapc (lambda (name) (insert "(require '" name ")\n"))
-         (dictree--meta-dict-list tmpdict))
+         (dictree--meta-dict-dictlist tmpdict))
     (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n")
     (insert "(setq " dictname " '" (prin1-to-string tmpdict) ")\n")
-    (insert "(dictree--set-dict-list\n"
+    (insert "(dictree--meta-dict-dictlist\n"
            " " dictname "\n"
            " (mapcar (lambda (name) (eval (intern-soft name)))\n"
-           "         (dictree--dict-list " dictname " )))\n")
+           "         (dictree--meta-dict-dictlist " dictname " )))\n")
     (insert hashcode)
-    (insert "(dictree--set-filename " dictname
+    (insert "(setf (dictree-filename " dictname ")"
            " (locate-library \"" dictname "\"))\n")
     (insert "(unless (memq " dictname " dictree-loaded-list)"
-           " (push " dictname " dictree-loaded-list))\n"))
-)
+           " (push " dictname " dictree-loaded-list))\n")))
 
 
 
 
+;; ----------------------------------------------------------------
+;;                     Minibuffer completion
+
 (defvar dictree-history nil
   "History list for commands that read an existing ditionary name.")
 
@@ -2289,21 +2442,13 @@ Prompt with PROMPT. By default, return DEFAULT. If 
DICTLIST is
 supplied, only complete on dictionaries in that list."
   (let (dictnames)
     (mapc (lambda (dict)
-           (unless (or (null (dictree--name dict))
-                       (member (dictree--name dict) dictnames))
-             (push (list (dictree--name dict)) dictnames)))
+           (unless (or (null (dictree-name dict))
+                       (member (dictree-name dict) dictnames))
+             (push (list (dictree-name dict)) dictnames)))
          (or dictlist dictree-loaded-list))
     (eval (intern-soft
           (completing-read prompt dictnames
-                           nil t nil 'dictree-history default))))
-)
-
-
-
-;; Add the dictree-save-modified function to the kill-emacs-hook to save
-;; modified dictionaries when exiting emacs
-(add-hook 'kill-emacs-hook 'dictree-save-modified)
-
+                           nil t nil 'dictree-history default)))))
 
 
 ;;; dict-tree.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]