lewang / flx

Fuzzy matching for Emacs ... a la Sublime Text.
GNU General Public License v3.0
518 stars 37 forks source link

On integrate flx with Helm #59

Closed tuhdo closed 2 years ago

tuhdo commented 10 years ago

Hi Le Wang,

This is my high level idea of how we could use flx for Helm. If it is not applicable, then I guess we can leave this an open issue that hopefully be solved in the future.

My idea is that, if we enter a bunch of patterns, some of which can be normal Helm pattern, some can be fuzzy pattern, then we divide the narrowing process into two phases:

The key point is that flx is only applied when a pattern can result in no candidate.

Thanks.

lewang commented 10 years ago

It doesn't have to be that complicated. "Fuzzy" searches through flx should not have spaces at all. So for a query "term1 term2 term3" we could say only term1 is treated as "fuzzy" the rest are regular helm searches.

The searching isn't difficult, the problem is I would also want to highlight the letters that are actually matched. This allows people to learn the matching algorithm by intuition and become more efficient. I couldn't figure out how to do that when I first tried.

If you're curious, have a look at helm-match-plugin.el for how current matching is implemented.

tuhdo commented 10 years ago

Hi Le Wang,

Today I notice that helm-buffers-list can fuzzy match and it provides no highlighting. Perhaps if you can make flx work with Helm but provides no highlighting, it can still be acceptable to Helm users. Is it possible for you to add it to Helm now? You should just do it and see how the users response. I am curious to see how I can use flx.

tuhdo commented 9 years ago

@lewang

Hi Le Wang,

I'm not sure if you looked into this but I will give you the result of my investigation and hope it helps. Basically, Helm highlighting relies on these variables and functions:

Variables: helm-mp-highlight-delay
Variables: helm-mp-highlight-threshold

Functions: helm-mp-highlight-match ()
Functions: helm-mp-highlight-region (start,end,regexp,face)
Functions: helm-mp-highlight-match-internal (end)

First, let's start with helm-mp-highlight-match:

(defun helm-mp-highlight-match ()
  "Highlight matches after `helm-mp-highlight-delay' seconds."
  (unless (or (assoc 'nohighlight (helm-get-current-source))
              (not helm-mp-highlight-delay)
              (helm-empty-buffer-p)
              (string= helm-pattern ""))
    (helm-mp-highlight-match-internal (window-end (helm-window)))
    (run-with-idle-timer helm-mp-highlight-delay nil
                         'helm-mp-highlight-match-internal
                         (with-current-buffer helm-buffer (point-max)))))

The function checks that if the current Helm buffer is eligible for highlighting:

Then it calls helm-mp-highlight-match-internal:

(defun helm-mp-highlight-match-internal (end)
  (when helm-alive-p
    (set-buffer helm-buffer)
    (let ((requote (cl-loop for (pred . re) in
                         (helm-mp-3-get-patterns helm-pattern)
                         when (and (eq pred 'identity)
                                   (>= (length re)
                                       helm-mp-highlight-threshold))
                         collect re into re-list
                         finally return
                         (if (and re-list (>= (length re-list) 1))
                             (mapconcat 'identity re-list "\\|")
                           (regexp-quote helm-pattern)))))
      (when (>= (length requote) helm-mp-highlight-threshold)
        (helm-mp-highlight-region
         (point-min) end requote 'helm-match)))))

If the helm buffer is alive, it collects a list of regexps from Helm pattern and concat into a big regexp using mapconcat. Finally, it passes into the helm-mp-highlight-region which is the function that does actual highlighting. The function accepts starting point for highlighting with start parameter, end point for stopping highlighting with end parameter, a regexp and highlight face:

(defun helm-mp-highlight-region (start end regexp face)
  (save-excursion
    (goto-char start)
    (let ((case-fold-search (helm-set-case-fold-search regexp)) me)
      (condition-case _err
          (while (and (setq me (re-search-forward regexp nil t))
                      (< (point) end)
                      (< 0 (- (match-end 0) (match-beginning 0))))
            (unless (helm-pos-header-line-p)
              (put-text-property (match-beginning 0) me 'face face)))
        (invalid-regexp nil)))))

end argument in helm-mp-highlight-match-internal is passed by helm-mp-highlight-match and is (point-max). The other arguments is inside helm-mp-highlight-match-internal. In helm-mp-highlight-region, it keeps re-search-forward for the regexp passed into and highlight matched text with put-text-property.

That's all to it. So, to solve your highlighting problem, I suggest:

(defalias `helm-mp-highlight-region `helm-flx-mp-highlight-region)
(defalias `helm-mp-original-highlight-region `helm-mp-highlight-region)

You should name this package helm-flx and do not include Ido.

colonelpanic8 commented 9 years ago

Is anyone actively working on this? It seems like there is not too much left to do. Would it be worth my time to try to add highlighting and make a package out of this?

lewang commented 9 years ago

I will take any PRs with or without highlighting. @tuhdo has some good suggestions here that I haven't even looked through yet. I also won't have time to in the foreseeable future, so any help is appreciated.

PythonNut commented 2 years ago

I think helm integration is addressed by helm-flx. If there are shortcomings in my implementation, feel free to open an issue there and we can talk discuss.