On integrate flx with Helm
tuhdo opened this issue · 6 comments
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:
- One is for normal Helm filtering. That is, we tried each pattern one by one; if one pattern results in empty set of candidates, then store that pattern and try the next one (without including the "failed" pattern). We filter until no more pattern and result in a set of candidates.
- One is for
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.
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
.
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:
- not explicity specified
'nohighlight
. helm-mp-highlight-delay
is not nil- Current Helm buffer is not empty.
- Helm pattern is not an empty string.
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:
- Create another function called
helm-flx-mp-highlight-region
that accepts the same parameters ashelm-mp-highlight-region
. Then usedefalias
to rebindinghelm-flx-mp-highlight-region
tohelm-mp-highlight-region
andhelm-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 withput-text-property
, similar to the originalhelm-mp-highlight-region
function, but usingflx
returned indexes.- After done highlighting for
flx
, since the first pattern belongs toflx
, calls the originalhelm-mp-original-highlight-region
with the rest of the pattern for highlighting. Remember to remove the first pattern, as it is used byflx
.
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?