[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
- [elpa] externals/dict-tree e1a9f19 051/154: Documentation updates related to wildcard searches and predictive features that make use of them, (continued)
- [elpa] externals/dict-tree e1a9f19 051/154: Documentation updates related to wildcard searches and predictive features that make use of them, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree bf4002d 063/154: Bug-fixes to dictree--write-dict-code; allow loading non-matching filenames in read-dict, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 6d9921e 067/154: Updated docstrings for regexp-related functions and others., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree d88b867 002/154: Version 0.10.3 of the predictive completion package., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 7b52ebd 005/154: Version 0.13.1 of the predictive completion package., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree ac40f3c 004/154: Version 0.12.2 of the predictive completion package., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 2bca928 003/154: Version 0.12 of the predictive completion package., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 3ecd763 006/154: Added minor miscellaneous new features, mostly to predictive.el., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 26f5dd3 011/154: Work around byte-compilation and goto-line bugs., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 7562023 012/154: Make use of :family attribute of completion-tooltip-face., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree f9bf379 013/154: Complete re-write of dict-tree.el, based on new trie.el.,
Stefan Monnier <=
- [elpa] externals/dict-tree 329110b 018/154: Fixed bug in unranked dictree-complete., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree baa4931 028/154: Doesn't quite work - revert to breaking setf abstraction, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 5834dac 036/154: Replaced bare avl-trees, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 673fd2a 029/154: Abstract away the setcar used to set the data component of a cell, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 2700e21 035/154: Don't compile wrapped functions explicitly, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 70f99ee 037/154: Make certain dictionary commands (mostly saving and loading) interactive again., Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 35346a4 041/154: Bug-fix in dictree--write-dict-code, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 608fcd4 049/154: Bug-fix to dictree--write-dict-code, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 179c0b5 045/154: Bug-fix to messages displayed by dictree-populate-from-file, Stefan Monnier, 2020/12/14
- [elpa] externals/dict-tree 6b24547 052/154: Allow "]" to be included in a negated character alternatives, by placing immediately after the "[^"., Stefan Monnier, 2020/12/14