Hello,
Here's patch for slime-like fuzzy autocompletion for input. Completion type is specified in *input-completion-type* variable which can be either :prefix or :fuzzy. Old prefix completion type is left as default. I hope someone will find this useful.
diff --git a/input.lisp b/input.lisp index 9b3dcd9..97d7aa6 100644 --- a/input.lisp +++ b/input.lisp @@ -377,19 +377,94 @@ functions are passed this structure as their first argument." ;;; "interactive" input functions -(defun input-find-completions (str completions) - (if (or (functionp completions) - (and (symbolp completions) - (fboundp completions))) - (funcall completions str) - (remove-if-not (lambda (elt) +(defun fuzzy-match (short full) + (declare (optimize (speed 3) (safety 0) (space 0))) + (declare (type simple-string short full)) + (labels ((word-separator-p (c) + (or (eq c #\-) (eq c #\Space))) + (chunk-split (short full) + (let ((chunks '()) + (chunk (list 0 0 0 0))) + (loop for i from 0 to (1- (length full)) for c = (aref full i) do + (if (word-separator-p c) + (let ((new-short (cadr chunk)) + (new-full (1+ i))) + (push chunk chunks) + (when (and (< new-short (length short)) + (word-separator-p (aref short new-short))) + (setf new-short (1+ new-short))) + (setf chunk (list new-short new-short new-full new-full))) + (progn + (when (and (< (cadr chunk) (length short)) + (eq c (aref short (cadr chunk)))) + (setf (cadr chunk) (1+ (cadr chunk)))) + (setf (cadddr chunk) (1+ (cadddr chunk)))))) + (and (eq (length short) (cadr chunk)) + (reverse (push chunk chunks))))) + (distance (short s0 s full f0 f) + (let* ((short-len (- s s0)) + (full-len (- f f0)) + (prev-row (make-array (1+ full-len))) + (curr-row (make-array (1+ full-len))) + (flag) + (delta 1) + (next-j 1)) + (declare + (type fixnum short-len full-len delta next-j s0 s f0 f) + (type simple-vector prev-row curr-row)) + (loop for i from 0 to full-len do + (setf (aref curr-row i) (* i 2))) + (loop for i from 1 to short-len do + (let ((tmp prev-row)) + (setf prev-row curr-row + curr-row tmp + flag NIL)) + (loop for j from next-j to full-len do + (if (and (eq (aref short (+ s0 (1- i))) (aref full (+ f0 (1- j)))) + (not flag)) + (setf (aref curr-row j) (aref prev-row (1- j)) + flag T + delta (1+ (- j i)) + next-j (1+ j)) + (setf (aref curr-row j) (+ delta (aref curr-row (1- j))) + delta 1)))) + (aref curr-row full-len)))) + (let ((chunks (chunk-split short full))) + (when chunks + (let ((distance (loop for (s0 s f0 f) in chunks + for factor from 1 to (length chunks) + sum (* factor (distance short s0 s full f0 f))))) + (cons full distance)))))) + +(defun input-find-completions/fuzzy (str completions) + (let ((matches (remove-if-not + 'identity + (loop for candidate in completions + collect (fuzzy-match str candidate))))) + (mapcar 'car (sort matches '< :key 'cdr)))) + +(defun input-find-completions/prefix (str completions) + (remove-if-not (lambda (elt) (when (listp elt) (setf elt (car elt))) (and (<= (length str) (length elt)) (string= str elt :end1 (length str) :end2 (length str)))) - completions))) + completions)) + +(defvar *input-completion-type* :prefix) + +(defun input-find-completions (str completions) + (if (or (functionp completions) + (and (symbolp completions) + (fboundp completions))) + (funcall completions str) + (let* ((method-name (format nil "input-find-completions/~A" *input-completion-type*)) + (method (find-symbol (string-upcase method-name)))) + (if method + (funcall method str completions) + (message "Invalid *input-completion-type* (:~A)" *input-completion-type*))))) (defun input-complete (input direction) ;; reset the completion list if this is the first time they're
_______________________________________________ Stumpwm-devel mailing list Stumpwm-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/stumpwm-devel