Skip to content
This repository has been archived by the owner on May 27, 2019. It is now read-only.

Implement as-you-type filtering #220

Merged
merged 19 commits into from
Mar 19, 2018
Merged

Conversation

erayd
Copy link
Contributor

@erayd erayd commented Mar 19, 2018

If there are search results present:

  • Typing in the search field will filter the results.
  • Pressing enter will autofill the first entry.
  • Pressing enter when there are no matching entries will run a normal search.

If there are search results present, no filter has been entered, and Backspace is pressed:

  • All search results are cleared, as is the search input box.
  • Pressing enter will run a normal search.

The reason for the Backspace feature is to allow the user to explicitly run a manual search instead of filtering, even when the automatic domain search returns matches.

If the search results are the outcome of a manual search, and no filter text has been entered, then pressing enter will not auto-fill the first entry, but will instead re-run the search. This is to prevent the user accidentally filling a login form by pressing enter twice when running a manual search.

This PR closes #40 and #211.

If there are search results present:
 * Typing in the search field will filter the results
 * Pressing <enter> will autofill the first entry
 * Pressing <enter> when there are no matching entries will run a normal search

If there are search results present, no filter has been entered, and '\' is pressed:
 * All search results are cleared, as is the search input box
 * Pressing <enter> will run a normal search
@erayd erayd mentioned this pull request Mar 19, 2018
@maximbaz
Copy link
Member

Awesome stuff! My initial thoughts:

  • I'd like to have a visual indication of which mode we are in, filtering or searching. An interesting idea was suggested in Filter the list of already discovered passwords #211, show an indicator on the left side inside of the search box (and clear it with Backspace instead of \, because that's how Chromium behaves). Doesn't need to be fancy looking, but should be "not ugly" 🙂
  • Filtering should be fuzzy - again, no need for fancy external libraries, don't change the order of search results, but something very basic, like wk should find work.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

@maximbaz

I'd like to have a visual indication of which mode we are in, filtering or searching.

No problem - I'll add this.

clear it with Backspace instead of \

Chrome doesn't appear to fire the onkeypress event for backspace in the search field, although it does for any other key. Do you know how to catch this?

Filtering should be fuzzy - again, no need for fancy external libraries, don't change the order of search results, but something very basic, like wk should find work.

I strongly dislike this idea. The outcome of fuzzy searches tend to be non-obvious, and when I'm wanting to quickly filter a list, I need it to be predictable. IMO, fuzzy searches belong in the main search feature, not in the filter.

The way it works now is substring matching for each word in the filter, separated by either whitespace or /. Is there a reason this is undesirable? I'm struggling to understand how fuzzy searching is useful in a quick-filter.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Chrome doesn't appear to fire the onkeypress event for backspace in the search field, although it does for any other key. Do you know how to catch this?

Never mind - onkeydown does catch it, so I'll just use that.

@maximbaz
Copy link
Member

maximbaz commented Mar 19, 2018

My experience is a lot different with fuzzy search, I find it very useful - also, when done properly, typing "work" should find the "work" entry as the top one, so you would not experience any difference comparing to the substring implementation. Try searching for full substrings in the domain search in v2.0.12, it is fuzzy by default, but nevertheless searching for substrings should put the most expected results on top. However when you get used to typing "wk", you will be pleased that it works too 🙂

However I realize that perhaps a good fuzzy search is not easy to implement and probably requires some dependency to be working acceptably well. So, a substring search is definitely preferred over a bad-working fuzzy search, but a good-working fuzzy search is preferred over the substring search 🙂

And if you are still not convinced, I'm fine with implementing a configuration option, whether filtering should be fuzzy or not. In that case, fuzzy filter and configuration option don't even have to be part of this PR.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

However I realize that perhaps a good fuzzy search is not easy to implement and probably requires some dependency to be working acceptably well. So, a substring search is definitely preferred over a bad-working fuzzy search, but a good-working fuzzy search is preferred over the substring search.

Do you have a library to recommend for this, that you would be happy to have included in the extension?

And if you are still not convinced, I'm fine with implementing a configuration option, whether filtering should be fuzzy or not. Doesn't even have to be part of this PR.

If you're happy with a config option, then I have no problem with the filtering being fuzzy, even by default. Just so long as I can turn the fuzzy stuff off :-).

@maximbaz
Copy link
Member

I'll try to test some libraries later today 👍

@maximbaz
Copy link
Member

I'm using this PR and will be publishing new findings as I find something:

  • Open browserpass, it is filtering mode, type something so that no results are left - the indicator of "filter mode" is gone, but should stay.
  • Open browserpass, it is in filtering mode, press Backspace to switch to search mode, type "githud", press Enter, press Backspace once (in attempt to fix the typo and change "githud" to "github") - the entire search field is cleared, but only the last char should be removed.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Open browserpass, it is filtering mode, type something so that no results are left - the indicator of "filter mode" is gone, but should stay.

This is deliberate - if you filter out all results (i.e. to make the result list empty), it switches to regular search mode. That's why the hint vanishes. Can you explain what the desired behavior should be instead?

Typo fixing - good catch. Need to think about how to resolve that one.

I've swiched to fuzzy search using fuse.js - does how I've implemented it fit what you were expecting?

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

OK, I've sorted the typo thing. Is that now working as you expected it to work?

@maximbaz
Copy link
Member

This is deliberate - if you filter out all results (i.e. to make the result list empty), it switches to regular search mode. That's why the hint vanishes. Can you explain what the desired behavior should be instead?

Aaaah, I see - no, keep it like this, very useful indeed. I just didn't realize that it switches to the search mode, I thought it is still in the filter mode 🙂

I'll test the fuzzy search later today 👍

@maximbaz
Copy link
Member

Regarding making fuzzy filter configurable, there is already an option for fuzzy search, I'm wondering if it makes sense to reuse that same option for fuzzy filtering, or introduce a new option. Is it possible that a user would like to disable fuzzy search, but enable fuzzy filtering, or the other way around? Consider that it is also not obvious for people unfamiliar with the code to understand what's the difference between option "Fuzzy search" and "Fuzzy filter". What do you recommend?

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

I found that making the filter a little more strict was enough that I was happy with fuzzy filtering as the sole option. So I would be happy to leave it as-is, and not worry about substring matching.

If you think that it would be better to include both filter methods, then yes - using the existing option for fuzzy search seems like a good idea.

@maximbaz
Copy link
Member

OK, let's keep the fuzzy filtering as the sole option for now.

I'm only unhappy with the library, I saw people complain about it, and now I see why:

fuzzy

ig should already find no result, and no additions to ig should add results.

I'm reviewing a few options now, what do you think about this library?

https://github.com/farzher/fuzzysort

If we choose it, let's disallow typos to make it strict (i.e. allowTypo: false if it's true by default).

I haven't tested it myself yet in much details, but the demo seems quite good. Also this library is maintained now (contrary to some other existing libraries), which I like.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

I see what you mean - the behavior in your example is horrible! I'll take a look at the one you're suggesting now, hang on.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

That library seems to be overly strict with filtering on patterns that aren't at the start of a word. For example, hub does not match github. This is problematic.

@maximbaz
Copy link
Member

Hmm this is not happening on their demo page at least...

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

It happens as soon as you set a strictness threshold. Looks like it might be OK with no threshold set though - have just commited it so you can have a look. What do you think?

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

I might need to do an iterated version; seems it can't handle multi-word searches.

@maximbaz
Copy link
Member

I don't mind at all, if you like it, let's keep it 👍

From the usage point of view, this looks pretty solid. I'll review the code a bit later, and will test more in meantime.

Found this on chrome://extensions, could you please have a look?

image

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Found this on chrome://extensions, could you please have a look?

Could you please clarify how / where to reproduce that, if it's still an issue? I've just pushed a commit that fixes the type, but I would like to test to ensure that solved the issue.

@maximbaz
Copy link
Member

I didn't see when exactly that happened, but I think you most probably did fix it with the last commit. I cleared all the errors and will be testing the code in its latest stage, if any errors appear I'll compose the repro steps.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Sounds good - thanks :-).


function searchKeyHandler(e) {
// switch to search mode if '\' is pressed and no filter text has been entered
if (e.code == "Backspace" && logins && logins.length > 0 && (!e.target.value.length || e.target.value == domain)) {
Copy link
Member

Choose a reason for hiding this comment

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

  • I think && logins is not needed anymore since it can never be null
  • switch to search mode if '\' is pressed - needs to be Backspace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (!this.s.value.length) {
return;
}
if (fillOnSubmit && logins && logins.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

I think && logins is not needed anymore since it can never be null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

logins = response;
logins = resultLogins = response ? response : [];
document.getElementById("filter-search").textContent = domain;
fillOnSubmit = useFillOnSubmit && logins && logins.length > 0;
Copy link
Member

Choose a reason for hiding this comment

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

&& logins

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -222,14 +298,16 @@ function keyHandler(e) {
switchFocus("div.entry:first-child > .login", "nextElementSibling");
break;
case "c":
if (e.ctrlKey) {
if (e.target.id != "search-field" && e.ctrlKey) {
Copy link
Member

Choose a reason for hiding this comment

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

both copy shortcuts seem to not work for me anymore, do they work for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Investigating...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These seem to be working for me. Perhaps fixed by one of the other commits?

Are you still having issues, or are they working for you now?

Copy link
Member

Choose a reason for hiding this comment

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

Emm they actually don't even work for me in release version... okay, nevermind, I'll deal with them separately.

Copy link
Contributor Author

@erayd erayd Mar 19, 2018

Choose a reason for hiding this comment

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

One thought - is the item actually selected when you use the copy shortcuts? I did change the behavior so that they did not run when the search box was selected, because they conflicted with entering filter text (i.e. typing a capital C resulted in a copy-username-and-close behavior, which is obviously undesirable). That's what the target.id check is for.

function filterLogins(e) {
// remove executed search from input field
if (!fillOnSubmit && e.target.value.indexOf(domain) === 0) {
e.target.value = e.target.value.substr(domain.length);
Copy link
Member

Choose a reason for hiding this comment

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

could you please explain to me the purpose of this piece?

It works weirdly...:

  1. Open browserpass, it is in filter mode
  2. Press Backspace to switch to search mode
  3. Type "gith", press Enter
  4. Press Backspace, everything is cleared (difficult to fix typo - not because of this code block, but still a valid issue)
  5. Slowly type "gith" again, when you enter "h" of "gith" the field gets cleared again (because of this code block)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a bug. Was half of a previous solution, which I have now removed.

// switch to search mode if '\' is pressed and no filter text has been entered
if (e.code == "Backspace" && logins && logins.length > 0 && (!e.target.value.length || e.target.value == domain)) {
e.preventDefault();
logins = resultLogins = [];
Copy link
Member

Choose a reason for hiding this comment

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

This is very minor thing and most probably we shouldn't do anything here, but posting in case you have some idea how to avoid the confusion. If not, don't worry about this.

I press Backspace and I am told that there are no passwords for github.com.

backspace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will fix this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

m.redraw();

// show / hide the filter hint
showFilterHint(logins && logins.length);
Copy link
Member

Choose a reason for hiding this comment

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

one more place with logins &&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

logins = resultLogins = response ? response : [];
document.getElementById("filter-search").textContent = domain;
fillOnSubmit = useFillOnSubmit && logins.length > 0;
if (logins && logins.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

one more place with logins &&... sorry 😃

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@maximbaz
Copy link
Member

Is it intentional that now after every search the form is switched back in the filter mode? I didn't notice in which commit this appeared, but I actually think it is interesting 🙂

auto-filter

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Is it intentional that now after every search the form is switched back in the filter mode?

Yes. It will switch to filter mode if the search returns one or more results, but will stay in search mode if no results are found.

If you hit backspace immediately after it switches to filter mode from a manual search, it will go back to editing the search query.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

By the way, would you like me to squash this PR into a single commit? Or leave it as-is?

@maximbaz
Copy link
Member

Very cool. Seems pretty solid to me, is there anything else you want to do or that is not done yet?

I just tested in Firefox, there are no noticeable issues as well.

Leave as it is, Github will squash when merging, but in PR we will have history of commits.

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Very cool. Seems pretty solid to me, is there anything else you want to do or that is not done yet?

Nope, I'm pretty happy with it now. It covers all my use-cases, and I can't currently think of anything else it ought to do that others might want from it.

Leave as it is

Will do :-).

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Is there anything else you'd like me to do to this PR before it's ready to merge?

Copy link
Member

@maximbaz maximbaz left a comment

Choose a reason for hiding this comment

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

Awesome improvement, thanks a lot!

@erayd
Copy link
Contributor Author

erayd commented Mar 19, 2018

Thank you too - your input resulted in it being a lot better than it was when I initially opened the PR!

@maximbaz maximbaz merged commit 49bbfd5 into browserpass:master Mar 19, 2018
@erayd erayd deleted the quick-filter branch March 19, 2018 16:37
@jangari
Copy link

jangari commented Mar 20, 2018

Great work guys!

Question on usage: filter-as-you-type only seems to work on the search results, not where you have no matching domain, and so no automatically populated search results. Is that right?

When I saw this discussion yesterday I assumed it would add the ability to type to filter all password-store files when no search was active. So, the following should (under my assumptions) show me all passwords matching 'wor', i.e. everything in my Work folder, plus whatever else (fuzzily) matches that pattern:
image

Am I off the mark here?

@sophacles
Copy link
Contributor

@maximbaz @erayd I just got around to pulling these changes and playing with them -- this is AWESOME! Well done :)

@erayd
Copy link
Contributor Author

erayd commented Mar 20, 2018

@jangari
That's how it's supposed to work. Once you search, it will then start filtering results.

If you want to filter your entire database using filter-while-typing, just search for / - unless you have a very weird database structure, that should match every entry you have.

@maximbaz
Copy link
Member

@erayd found a tiny nitpick, not worth wasting time if the fix is not obvious, but maybe it is obvious to you?

Open browserpass, it is in filter mode, start typing something until no results appear, extension switches to the search mode, but the bottom bar with "No matching passwords for github.com" appears. Ideally it should not appear, just like if you press Backspace immediately after opening browserpass.

@erayd
Copy link
Contributor Author

erayd commented Mar 20, 2018

@maximbaz That was deliberate - i.e. "you have filtered everything, and there are no longer any results that match."

I can change it though, if you think that some other behaviour would be better?

@maximbaz
Copy link
Member

Aaah, no, it makes total sense, it's just not instantly obvious that when you are in this state, you are at the same time in the search mode (you see, I've actually asked about this already...).

Let's keep it as it is now.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Enter key to autocomplete
4 participants