Also there were bugs in original patch, here is the fixed version, I
tested it with  SBCL 1.0.40 and CLISP 2.48 and everything seems to be
working fine.

2010/10/1 Anonymous <swel...@gmail.com>:
> Evgeny <maet...@gmail.com> writes:
>
>> 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.
>
> Not sure why but it doesn't work without (in-package :stumpwm), e.g.
>
>  Invalid *input-completion-type* (:PREFIX)
>
> %%
> diff --git a/input.lisp b/input.lisp
> index 97d7aa6..3802c97 100644
> --- a/input.lisp
> +++ b/input.lisp
> @@ -461,7 +461,7 @@ functions are passed this structure as their first 
> argument."
>                (fboundp completions)))
>       (funcall completions str)
>       (let* ((method-name (format nil "input-find-completions/~A" 
> *input-completion-type*))
> -            (method (find-symbol (string-upcase method-name))))
> +            (method (find-symbol (string-upcase method-name) :stumpwm)))
>        (if method
>            (funcall method str completions)
>            (message "Invalid *input-completion-type* (:~A)" 
> *input-completion-type*)))))
> %%
>
diff --git a/input.lisp b/input.lisp
index 9b3dcd9..1c0662c 100644
--- a/input.lisp
+++ b/input.lisp
@@ -26,6 +26,7 @@
 
 (export '(*input-history-ignore-duplicates*
           *input-map*
+	  *input-completion-type*
 	  completing-read
 	  input-delete-region
 	  input-goto-char
@@ -377,19 +378,98 @@ 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))
+	   (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) :initial-element 0))
+	   	    (curr-row (make-array (1+ full-len) :initial-element 0))
+	   	    (flag)
+		    (delta 1)
+		    (next-j 1))
+	       (declare (dynamic-extent prev-row curr-row)
+			(type simple-vector prev-row curr-row)
+		        (type fixnum short-len full-len delta next-j s0 s f0 f))
+	       (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* ((str (string-downcase str))
+	 (completions (mapcar 'string-downcase completions))
+	 (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) :stumpwm)))
+	(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

Reply via email to