yegappan / fileselect

File Selector Vim Plugin
BSD 2-Clause "Simplified" License
18 stars 2 forks source link

Highlight the matches #1

Closed noscript closed 3 years ago

noscript commented 4 years ago

Would it be possible to highlight the matches like with CtrlP or FZF?

lacygoill commented 3 years ago

Would it be possible to highlight the matches like with CtrlP or FZF?

Yes, we would need to use matchfuzzypos() instead of matchfuzzy(). That's what I do here.

gif

I will try to send a PR. I briefly tried to implement the feature, but stumbled on a Vim bug which causes a crash. Once it's fixed in Vim, I will try again.

lacygoill commented 3 years ago

This patch seems to work, but sometimes, the highlighting is lost after 1 second:

diff --git a/autoload/fileselect.vim b/autoload/fileselect.vim
index cfae6c5..4b34de2 100644
--- a/autoload/fileselect.vim
+++ b/autoload/fileselect.vim
@@ -21,6 +21,8 @@ if v:version < 802 || !has('patch-8.2.1744')
   finish
 endif

+prop_type_add('fileselect', #{highlight: 'Title'})
+
 var s:filelist: list<string> = []
 var s:popup_text: list<string> = []
 var s:filter_text: string = ''
@@ -142,14 +144,23 @@ def FilterNames(id: number, key: string): number
       prevSelName = popup_text[curLine - 1]
     endif

+    var pos: list<list<number>>
     if filter_text != ''
-      popup_text = filelist->matchfuzzy(filter_text)
+      var matchfuzzypos: list<any> = filelist->matchfuzzypos(filter_text)
+      popup_text = matchfuzzypos[0]
+      pos = matchfuzzypos[1]
+      map(pos, {i, v -> map(v, {_, w -> w - 1 - fnamemodify(popup_text[i], ':h')->strchars(true)})})
     else
       popup_text = filelist
     endif
     var items: list<string> = popup_text->copy()
     MakeMenuName(items)
-    id->popup_settext(items)
+    var text: list<dict<any>> = items
+      ->map({i, v -> #{
+        text: v,
+        props: map(pos[i], {_, w -> #{col: w + 1, length: 1, type: 'fileselect'}})
+      }})
+    id->popup_settext(text)
     echo 'File: ' .. filter_text

     # Select the previously selected entry. If not present, select first entry

I think that's because of a timer which can reset the text of the popup without re-applying the highlighting. Unfortunately, I don't have more time to delve into the code.

Just a thought: I'm not sure what is the purpose of the timer, but if it gets the list of files asynchronously, maybe it would be better to use a job.

yegappan commented 3 years ago

Hi,

On Friday, October 9, 2020, 07:03:56 PM PDT, lacygoill notifications@github.com wrote:

This patch seems to work, but sometimes, the highlighting is lost after 1 second:

Thanks for the patch. I have made some modifications and merged it.

I think that's because of a timer which can reset the text of the popup without re-applying the highlighting.  Unfortunately, I don't have more time to delve into the code.

Just a thought: I'm not sure what is the purpose of the timer, but if it gets the list of files asynchronously, maybe it would be better to use a job.  

A timer is used to asynchronously process the files/directories in the background. Note that a job cannot be used here, as the plugin doesn't depend on any external tool/utility to recursively get the list of files.

When trying to list and search the files from a deep directory tree, it will take a long time to process the files. Meanwhile, the user cannot interact with Vim and it will look like Vim is hung. The timer is used to process few directories at a time in the background. So that the user can continue to interact with Vim, while the large number of files/directories are processed in the background.

Regards, Yegappan

lacygoill commented 3 years ago

Note that a job cannot be used here, as the plugin doesn't dependon any external tool/utility to recursively get the list of files.

Ok, I understand. I just thought the list of files could have been obtained via a unix utility like maybe find(1). This way, we could have used a job. The reason I say this, is because I'm studying how the fzf.vim implements fuzzy commands, without blocking the user. Even when there are a lot of lines to filter, the plugin opens its window almost instantaneously, and I don't think it relies on a timer; I think it relies on term_start(), which is a kind of a job. Hence why I'm tempted to use a job.

But I need to study the plugin more to be sure.

Anyway, I can totally understand the desire to not depend on any external tool.

yegappan commented 3 years ago

Note that a job cannot be used here, as the plugin doesn't dependon any external tool/utility to recursively get the list of files.

Ok, I understand. I just thought the list of files could have been obtained via a unix utility like maybe find(1). This way, we could have used a job.

Yes. But the find utility is available only on Linux/Unix/MacOS systems and is not available on MS-Windows (out of the box).

The reason I say this, is because I'm studying how the fzf.vim implements fuzzy commands, without blocking the user. Even when there are a lot of lines to filter, the plugin opens its window almost instantaneously, and I don't think it relies on a timer; I think it relies on term_start(), which is a kind of a job. Hence why I'm tempted to use a job.

The fzf.vim plugin relies on the external fzf utility. So it makes sense to use a job or a terminal to run the fzf utility and parse the output and also it will be very optimized.

The file selector plugin uses only the Vim built-in functions to recursively walk the directory tree and get the list of files. This will take some time depending on the number of directories and files.

Regards, Yegappan

But I need to study the plugin more to be sure.

Anyway, I can totally understand the desire to not depend on any external tool.

lacygoill commented 3 years ago

The fzf.vim plugin relies on the external fzf utility. So it makes sense to use a job or a terminal to run the fzf utility and parse the output and also it will be very optimized.

Just to be clear, I didn't say that fzf got its results instantaneously. It really does not. The more entries it has to filter, the more time it needs. It's just that the window is opened fast. After that, we still have to wait for it to be fully populated; but we can type while it's getting populated. The fileselect plugin achieves the same result in a different way, so all is good.

Also, I think that by default fzf uses the files in the current working directory as the source to filter. I don't know yet how it gets this list. But in the general case, fzf is used to filter any source of data; not necessarily file names. The same is true for matchfuzzy*(). It doesn't care what it has to filter. And in my limited tests, matchfuzzy*() is at least as fast as fzf. In fact, I suspect it's faster. For example, I've re-implemented the fzf.vim :HelpTags command, using matchfuzzypos(). :HelpTags need 35 ms to open the window which in the end displays 12204 help tags. The command which relies on matchfuzzy*() only needs 7ms.

Not only that, but matchfuzzy*() gives more relevant results.

You really have done a great job by introducing these functions, because we can now remove fzf.vim completely (and get rid of the fzf dependency) which came with its own abstraction layer, and was too time-consuming to debug when something went wrong.

yegappan commented 3 years ago

The support for highlighting the matching characters is added.