Merge pull request #9 from jepler/fuzzy
Switch to textual's own fuzzzy matcher
This commit is contained in:
commit
a6aa81882d
2 changed files with 30 additions and 16 deletions
|
|
@ -75,10 +75,10 @@ Your Operating System may report that `ttotp` "pasted from the clipboard".
|
|||
This is because `ttotp` tries to only clear values that it set,
|
||||
by checking that the current clipboard value is equal to the value it pasted earlier.
|
||||
|
||||
Search for a key by pressing "/" and then entering a modified case insensitive regular expression.
|
||||
Search for a key by pressing "/" and then entering sub-strings to search for.
|
||||
Press Ctrl+A to show all keys again.
|
||||
|
||||
In this type of regular expression, a space ` ` stands for "zero or more characters, followed by whitespace, followed by zero or more characters"; the sequence backslash-space stands for a literal space.
|
||||
Textual's built in [fuzzy match](https://textual.textualize.io/api/fuzzy_matcher/) algorithm is used.
|
||||
|
||||
This makes it easy to search for e.g., "Jay Doe / example.com" by entering "ja d ex", while not requiring any sophisticated fuzzy search technology.
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import re
|
|||
from typing import TYPE_CHECKING, Any, Sequence, cast
|
||||
|
||||
import rich.text
|
||||
from textual.fuzzy import Matcher
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Label, Footer, ProgressBar, Button, Input
|
||||
|
|
@ -354,20 +355,33 @@ class TTOTP(App[None]):
|
|||
self.screen.focus_next()
|
||||
|
||||
def on_input_changed(self, event: Input.Changed) -> None:
|
||||
haystack = event.value.replace(" ", ".* .*")
|
||||
try:
|
||||
rx = re.compile(haystack, re.I)
|
||||
except re.error:
|
||||
self.search.add_class("error")
|
||||
return
|
||||
self.search.remove_class("error")
|
||||
for otp in self.otp_data:
|
||||
parent = otp.name_widget.parent
|
||||
assert parent is not None
|
||||
if rx.search(otp.name):
|
||||
parent.remove_class("otp-hidden")
|
||||
else:
|
||||
parent.add_class("otp-hidden")
|
||||
with self.batch_update():
|
||||
needle = event.value
|
||||
if not needle:
|
||||
for otp in self.otp_data:
|
||||
name_widget = otp.name_widget
|
||||
parent = name_widget.parent
|
||||
assert parent is not None
|
||||
parent.remove_class("otp-hidden")
|
||||
name_widget.update(
|
||||
rich.text.Text(otp.name, overflow="ellipsis", no_wrap=True)
|
||||
)
|
||||
return
|
||||
|
||||
matcher = Matcher(needle)
|
||||
for otp in self.otp_data:
|
||||
name_widget = otp.name_widget
|
||||
parent = name_widget.parent
|
||||
assert parent is not None
|
||||
score = matcher.match(otp.name)
|
||||
if score > 0:
|
||||
highlighted = matcher.highlight(otp.name)
|
||||
highlighted.overflow = "ellipsis"
|
||||
highlighted.no_wrap = True
|
||||
name_widget.update(highlighted)
|
||||
parent.remove_class("otp-hidden")
|
||||
else:
|
||||
parent.add_class("otp-hidden")
|
||||
|
||||
def on_input_submitted(self, event: Input.Changed) -> None:
|
||||
self.screen.focus_next()
|
||||
|
|
|
|||
Loading…
Reference in a new issue