Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed #1218 aggressive debounce for dynamic-collection #2678

Closed
wants to merge 3 commits into from
Closed

fixed #1218 aggressive debounce for dynamic-collection #2678

wants to merge 3 commits into from

Conversation

Inc0n
Copy link

@Inc0n Inc0n commented Sep 18, 2020

I proposed a fix in my comment in #1218, here's the pull request, please advice!

ivy.el Outdated Show resolved Hide resolved
ivy.el Outdated Show resolved Hide resolved
ivy.el Outdated Show resolved Hide resolved
ivy.el Outdated Show resolved Hide resolved
@Inc0n
Copy link
Author

Inc0n commented Sep 19, 2020

All done! Please review

Copy link
Collaborator

@basil-conto basil-conto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM apart from some minor comments.

ivy.el Show resolved Hide resolved
ivy.el Outdated Show resolved Hide resolved
@Inc0n
Copy link
Author

Inc0n commented Sep 19, 2020

Now that I have just implemented refinement on more-chars case in ivy-input-change-p, I consider it to be a closure predicate that serves more of the purpose to improve the user experience in ivy--queue-exhibit.
It would be more fitting to have it as an internal function.

But of course if there's any reasons for making this predicate public, that could also happen

Please advice! thanks

ivy.el Show resolved Hide resolved
Comment on lines +3241 to +3243
"Return t when lenght of `ivy-text' is changed,
But force return nil when current `ivy-text' len is smaller than `(ivy-alist-setting ivy-more-chars-alist)'.
This closure function stores a previous `ivy-text' length.")
Copy link
Collaborator

@basil-conto basil-conto Sep 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Emacs convention (as per (info "(elisp) Documentation Tips")) is to have a complete summary sentence fit on the first line, keep the body wrapped at under 70 columns (see emacs-lisp-docstring-fill-column and M-q (fill-paragraph)), and generally refer to non-nil rather than t, unless t has a specific meaning. For example:

  "Return non-nil if length of `ivy-text' changed since last input.
Return nil if current `ivy-text' is shorter than what is required
by `ivy-more-chars-alist'."

Which leads to a follow-up question: what if the contents of ivy-text have changed, but its length is unchanged?

Copy link
Author

@Inc0n Inc0n Sep 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm the case would be when the ivy-set-text has been called from functions that are not commands, the predicate would "lag" behind. It could leads to bad ui updates.

I'd sync the last-length by call ivy--input-changed-p in ivy-set-text', and allow ivy--input-changed-pto take an optionalupdate` argument to update change state when non-nil, otherwise it would return the cached change state, and set change state to nil.

Shall I commit this idea for you to review, or should we go with something different?

The doc string would look like this for the new predicate:

"Return non-nil if length of `ivy-text' changed since last input.
Return nil if current `ivy-text' is shorter than what is required
by `ivy-more-chars-alist'.  Return nil for all consequents calls
before `ivy-text' is changed. function takes an optional update
argument to update closure internal `changed' state."

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm not familiar with the relevant semantics of ivy-text, so I can't comment on the high-level design.

I don't understand what "all consequents calls before ivy-text is changed` means.

The fact that you're exposing an internal lexical variable via an optional argument, and have to document this in the docstring, makes it sound like we're trying too hard to use a closure here. Wouldn't a simple defvar+defun be simpler?

Copy link
Author

@Inc0n Inc0n Sep 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue the two implementation is fairly similar, with the difference being the latter would expose the two internal variables.

(defalias 'ivy--input-changed-p
  (let (last-len
        changed)
    (lambda (&optioanl update)
      (if (not update)
          (prog1 changed
            ;; reset state
            (when changed
              (setq changed nil)))
        (let ((len (length ivy-text))
              (more-chars-len (or (ivy-alist-setting ivy-more-chars-alist)
                                  0)))
          (setq changed
                (and
                 ;; nil when less than more-chars
                 (>= len more-chars-len)
                 ;; changed if len is different
                 (not (eql len ivy--last-len))))
          (setq last-len len)))))
  "Return non-nil if length of `ivy-text' changed since last input.
Return nil if current `ivy-text' is shorter than what is required
by `ivy-more-chars-alist'.  Return nil for all consequents calls
before `ivy-text' is changed. function takes an optional update
argument to update closure internal `changed' state.")
(defvar ivy--last-len 0
  "stores the previous length of ivy-text")
(defvar ivy--input-changed nil
  "non-nil when input is last changed")

(defun ivy--input-changed-p ()
  (prog1 ivy--input-changed
    (when ivy-input-changed
      (setq ivy-input-changed nil))))

(defun ivy--compute-changed ()
  (let ((len (length ivy-text))
        (more-chars-len (or (ivy-alist-setting ivy-more-chars-alist)
                            0)))
    (setq ivy--input-changed
          (and
           ;; nil when less than more-chars
           (>= len more-chars-len)
           ;; changed if len is different
           (not (eql len ivy--last-len))))
    (setq ivy--last-len len)))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what "all consequents calls before ivy-text is changed` means.

Ah if we want to introduce a new change state to counter the following issue:

what if the contents of ivy-text have changed, but its length is unchanged?

The change state after re-computation, i.e the synchronization function call placed within ivy-set-text. After accessing this state, should be set back to nil, otherwise it would be t all the time, when accessed, which would defeat the purpose of its implementation.

Therefore, I thought expose a flag to use the closure predicate as an update function when non-nil otherwise, it acts like the normal predicate function before we adding implementation to consider this issue:

Which leads to a follow-up question: what if the contents of ivy-text have changed, but its length is unchanged?

ivy.el Show resolved Hide resolved
also added documentation to ivy-input-change-p
added doc string and consistency fix
@kiennq
Copy link
Contributor

kiennq commented Sep 23, 2020

Just asking but instead of debounce, wouldn't it better to allow to cancel the operation on user input instead? Something like #2567

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants