Closed tuhdo closed 2 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.
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
.
@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:
'nohighlight
.helm-mp-highlight-delay
is not nilThen 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:
helm-flx-mp-highlight-region
that accepts the same parameters as helm-mp-highlight-region
. Then use defalias
to rebinding helm-flx-mp-highlight-region
to helm-mp-highlight-region
and helm-mp-highlight-region
to something else.(defalias `helm-mp-highlight-region `helm-flx-mp-highlight-region)
(defalias `helm-mp-original-highlight-region `helm-mp-highlight-region)
helm-flx-mp-highlight-region
should highlight with put-text-property
, similar to the original helm-mp-highlight-region
function, but using flx
returned indexes.flx
, since the first pattern belongs to flx
, calls the original helm-mp-original-highlight-region
with the rest of the pattern for highlighting. Remember to remove the first pattern, as it is used by flx
.You should name this package helm-flx
and do not include Ido
.
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?
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.
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:
flx
filtering. That is, we applyflx
on the output from the above phases and narrow down further. The result of this phase is the final set to be returned to user.The key point is that
flx
is only applied when a pattern can result in no candidate.Thanks.