Просмотр исходного кода

Minor improvements to dynamic collection updating

Now we save time by avoiding dynamic updates when the new text is a
prefix of the text that was used for the previous dynamic update,
unless the user presses TAB to explicitly request more completions. We
also properly maintain the value of the "ido-rescan" variable through
dynamic updates, so that ido does not overzealously reorder the list
of completions without user input.

Fixes #153.
Ryan C. Thompson 7 лет назад
Родитель
Сommit
8402b5e6e5
1 измененных файлов с 86 добавлено и 71 удалено
  1. 86 71
      ido-completing-read+.el

+ 86 - 71
ido-completing-read+.el

@@ -182,6 +182,9 @@ either of those functions directly won't set `this-command'.")
 This allows ido-cr+ to update the set of completion candidates
 dynamically.")
 
+(defvar ido-cr+-last-dynamic-update-text nil
+  "The value of `ido-text' last time a dynamic update occurred.")
+
 (defvar ido-cr+-dynamic-update-idle-time 0.25
   "Time to wait before updating dynamic completion list.")
 
@@ -511,6 +514,7 @@ completion for them."
           (when (and (not ido-cr+-assume-static-collection)
                      (functionp collection))
             collection))
+         (ido-cr+-last-dynamic-update-text nil)
          ;; Only memoize if the collection is dynamic.
          (ido-cr+-all-prefix-completions-memoized
           (if ido-cr+-dynamic-collection
@@ -897,78 +901,87 @@ Normal `ido-chop' hangs infinitely in this case."
   "Update the set of completions for a dynamic collection.
 
 This has no effect unless `ido-cr+-dynamic-collection' is non-nil."
-  (when (and (ido-cr+-active)
-             ido-cr+-dynamic-collection)
+  (when (and ido-cr+-dynamic-collection
+             (ido-cr+-active))
     ;; (cl-assert (not (ido-cr+-cyclicp ido-cur-list)))
-    (let ((orig-ido-cur-list ido-cur-list))
-      (condition-case-unless-debug err
-          (let* ((ido-text
-                  (buffer-substring-no-properties (minibuffer-prompt-end)
-                                                  ido-eoinput))
-                 (predicate (nth 2 ido-cr+-orig-completing-read-args))
-                 (first-match (car ido-matches))
-                 (strings-to-check
-                  (cond
-                   ;; If no match, then we only check `ido-text'
-                   ((null first-match)
-                    (list ido-text))
-                   ;; If `ido-text' is a prefix of `first-match', then we
-                   ;; only need to check `first-match'
-                   ((and first-match
-                         (s-prefix? ido-text first-match))
-                    (list first-match))
-                   ;; Otherwise we need to check both
-                   (t
-                    (list ido-text first-match))))
-                 (new-completions
-                  (cl-loop
-                   for string in strings-to-check
-                   append
-                   (funcall
-                    ido-cr+-all-prefix-completions-memoized
-                    string ido-cr+-dynamic-collection predicate)
-                   into result
-                   finally return result)))
-            ;; (cl-assert (not (ido-cr+-cyclicp new-completions)))
-            ;; Put the previous first match back at the front if possible
-            (when (and new-completions
-                       first-match
-                       (member first-match new-completions))
-              (setq new-completions
-                    (ido-chop new-completions first-match)))
-            (when (not (equal new-completions ido-cur-list))
-              (when (and (bound-and-true-p flx-ido-mode)
-                         (functionp 'flx-ido-reset))
-                ;; Reset flx-ido since the set of completions has changed
-                (funcall 'flx-ido-reset))
-              (setq ido-cur-list (delete-dups new-completions))
-              (when ido-cr+-active-restrictions
-                (setq ido-cur-list (ido-cr+-apply-restrictions
-                                    ido-cur-list
-                                    ido-cr+-active-restrictions)))
-              (ido-cr+--debug-message
-               "Updated completion candidates for dynamic collection because `ido-text' changed to %S. `ido-cur-list' now has %s elements"
-               ido-text (length ido-cur-list))
-              ;; Recompute matches with new completions
-              (setq ido-rescan t)
-              (ido-set-matches)
-              ;; Rebuild the completion display unless ido is already planning
-              ;; to do it anyway
-              (unless ido-cr+-exhibit-pending
-                (ido-tidy)
-                (ido-exhibit))))
-        (error
-         (display-warning 'ido-cr+
-                          (format
-                           "Disabling dynamic update due to error: %S"
-                           err))
-         ;; Reset any variables that might have been modified during
-         ;; the failed update
-         (setq ido-cur-list orig-ido-cur-list)
-         ;; Prevent any further attempts at dynamic updating
-         (setq ido-cr+-dynamic-collection nil)
-         (cl-assert (not (ido-cr+-cyclicp ido-cur-list)))
-         (cl-assert (null ido-cr+-dynamic-collection))))))
+    (let ((orig-ido-cur-list ido-cur-list)
+          (ido-text
+           (buffer-substring-no-properties (minibuffer-prompt-end)
+                                           ido-eoinput)))
+      ;; If current `ido-text' is equal to or a prefix of the previous
+      ;; one, a dynamic update is not needed.
+      (when (or (null ido-cr+-last-dynamic-update-text)
+                (not (s-prefix? ido-text ido-cr+-last-dynamic-update-text)))
+        (ido-cr+--debug-message "Doing a dynamic update because `ido-text' changed from %S to %S"
+                                ido-cr+-last-dynamic-update-text ido-text)
+        (setq ido-cr+-last-dynamic-update-text ido-text)
+        (condition-case-unless-debug err
+            (let* ((predicate (nth 2 ido-cr+-orig-completing-read-args))
+                   (first-match (car ido-matches))
+                   (strings-to-check
+                    (cond
+                     ;; If no match, then we only check `ido-text'
+                     ((null first-match)
+                      (list ido-text))
+                     ;; If `ido-text' is a prefix of `first-match', then we
+                     ;; only need to check `first-match'
+                     ((and first-match
+                           (s-prefix? ido-text first-match))
+                      (list first-match))
+                     ;; Otherwise we need to check both
+                     (t
+                      (list ido-text first-match))))
+                   (new-completions
+                    (cl-loop
+                     for string in strings-to-check
+                     append
+                     (funcall
+                      ido-cr+-all-prefix-completions-memoized
+                      string ido-cr+-dynamic-collection predicate)
+                     into result
+                     finally return result)))
+              ;; (cl-assert (not (ido-cr+-cyclicp new-completions)))
+              (if (equal new-completions ido-cur-list)
+                  (ido-cr+--debug-message "Skipping dynamic update because the completion list did not change.")
+                (when (and (bound-and-true-p flx-ido-mode)
+                           (functionp 'flx-ido-reset))
+                  ;; Reset flx-ido since the set of completions has changed
+                  (funcall 'flx-ido-reset))
+                (setq ido-cur-list (delete-dups (append ido-cur-list new-completions)))
+                (when ido-cr+-active-restrictions
+                  (setq ido-cur-list (ido-cr+-apply-restrictions
+                                      ido-cur-list
+                                      ido-cr+-active-restrictions)))
+                (ido-cr+--debug-message
+                 "Updated completion candidates for dynamic collection. `ido-cur-list' now has %s elements"
+                 ido-text (length ido-cur-list))
+                ;; Recompute matches with new completions
+                (let ((ido-rescan t))
+                  (ido-set-matches))
+                (setq ido-rescan nil)
+                ;; Put the pre-update first match (if any) back in
+                ;; front
+                (when (and first-match
+                           (not (equal first-match (car ido-matches)))
+                           (member first-match ido-matches))
+                  (ido-cr+--debug-message "Restoring first match %S after dynamic update" first-match)
+                  (setq ido-matches (ido-chop ido-matches first-match)))
+                ;; Rebuild the completion display unless ido is already planning
+                ;; to do it anyway
+                (unless ido-cr+-exhibit-pending
+                  (ido-tidy)
+                  (let ((ido-rescan nil))
+                    (ido-exhibit)))))
+          (error
+           (display-warning 'ido-cr+
+                            (format
+                             "Disabling dynamic update due to error: %S"
+                             err))
+           ;; Reset any variables that might have been modified during
+           ;; the failed update
+           (setq ido-cur-list orig-ido-cur-list)
+           ;; Prevent any further attempts at dynamic updating
+           (setq ido-cr+-dynamic-collection nil))))))
   ;; Always cancel an active timer when this function is called.
   (when ido-cr+-dynamic-update-timer
     (cancel-timer ido-cr+-dynamic-update-timer)
@@ -1011,6 +1024,8 @@ This has no effect unless `ido-cr+-dynamic-collection' is non-nil."
       (apply oldfun args))
     ;; Update `ido-eoinput'
     (setq ido-eoinput (point-max))
+    ;; Clear this var to force an update
+    (setq ido-cr+-last-dynamic-update-text nil)
     ;; Now do update
     (ido-cr+-update-dynamic-collection))
   ;; After maybe updating the dynamic collection, if there's still