From c638d78f6083c339718e280b20527b255e9d3c75 Mon Sep 17 00:00:00 2001 From: Erwan Leroy Date: Wed, 8 Nov 2017 12:04:39 -0500 Subject: [PATCH 1/3] Update tabtabtab.py Improving results of the search by doing: - First show direct matches (weighted) - Second Show nonconsecutive anchored matches (weighted) - Third Show nonconsecutive non-anchored matches (weighted) --- tabtabtab.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tabtabtab.py b/tabtabtab.py index 204254a..1287cbe 100644 --- a/tabtabtab.py +++ b/tabtabtab.py @@ -243,31 +243,56 @@ def __init__(self, mlist, weights, num_items = 15, filtertext = ""): def set_filter(self, filtertext): self._filtertext = filtertext self.update() - + def update(self): filtertext = self._filtertext.lower() # Two spaces as a shortcut for [ filtertext = filtertext.replace(" ", "[") - scored = [] + scored_a = [] + scored_b = [] + scored_c = [] for n in self._all: # Turn "3D/Shader/Phong" into "Phong [3D/Shader]" menupath = n['menupath'].replace("&", "") uiname = "%s [%s]" % (menupath.rpartition("/")[2], menupath.rpartition("/")[0]) - if nonconsec_find(filtertext, uiname.lower(), anchored=True): + if uiname.lower().startswith(filtertext): + # Matches, get weighting and add to list of stuff + score = self.weights.get(n['menupath']) + + scored_a.append({ + 'text': uiname, + 'menupath': n['menupath'], + 'menuobj': n['menuobj'], + 'score': score}) + + elif nonconsec_find(filtertext, uiname.lower(), anchored=True): + # Matches, get weighting and add to list of stuff + score = self.weights.get(n['menupath']) + + scored_b.append({ + 'text': uiname, + 'menupath': n['menupath'], + 'menuobj': n['menuobj'], + 'score': score}) + + elif nonconsec_find(filtertext, uiname.lower(), anchored=False): # Matches, get weighting and add to list of stuff score = self.weights.get(n['menupath']) - scored.append({ + scored_c.append({ 'text': uiname, 'menupath': n['menupath'], 'menuobj': n['menuobj'], 'score': score}) # Store based on scores (descending), then alphabetically - s = sorted(scored, key = lambda k: (-k['score'], k['text'])) + sort_a = sorted(scored_a, key = lambda k: (-k['score'], k['text'])) + sort_b = sorted(scored_b, key = lambda k: (-k['score'], k['text'])) + sort_c = sorted(scored_c, key = lambda k: (-k['score'], k['text'])) + s = sort_a + sort_b + sort_c self._items = s self.modelReset.emit() From d755a60d807312ed92d13471e8c8f5d54eb9520e Mon Sep 17 00:00:00 2001 From: Erwan Leroy Date: Fri, 10 Nov 2017 13:53:03 -0500 Subject: [PATCH 2/3] Match names excluding only special characters The consecutive name search now ignores special characters (space, -, _) --- tabtabtab.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tabtabtab.py b/tabtabtab.py index 1287cbe..7d97f18 100644 --- a/tabtabtab.py +++ b/tabtabtab.py @@ -257,8 +257,9 @@ def update(self): # Turn "3D/Shader/Phong" into "Phong [3D/Shader]" menupath = n['menupath'].replace("&", "") uiname = "%s [%s]" % (menupath.rpartition("/")[2], menupath.rpartition("/")[0]) + stripped_name = uiname.replace(' ','').replace('-','').replace('_','') - if uiname.lower().startswith(filtertext): + if uiname.lower().startswith(filtertext) or stripped_name.startswith(filtertext): # Matches, get weighting and add to list of stuff score = self.weights.get(n['menupath']) From 1e2c3dfb36731f0a3d37a5a7353c29fae6a3dd4a Mon Sep 17 00:00:00 2001 From: Erwan Leroy Date: Mon, 13 Nov 2017 14:54:01 -0500 Subject: [PATCH 3/3] Search Results improvement Refined filtering of search results: - Consecutive finds are weighted higher than non-consecutive - '*' disables anchoring - '**' forces results to be un-anchored ('**blur' will return 'motionblur' but not 'blur') - using '[' as the first character now also disables anchoring - using ' ' (double space) still becomes '[' - using ' ' (space) still disables non-consecutive search. --- tabtabtab.py | 59 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/tabtabtab.py b/tabtabtab.py index 7d97f18..6a4ee2f 100644 --- a/tabtabtab.py +++ b/tabtabtab.py @@ -69,6 +69,27 @@ def find_menu_items(menu, _path = None): return found +def consec_find(needle, haystack, anchored = False): + ''' searches for the "needle" string in the "haystack" string. + added to tabtabtab as a way to prioritize more relevant results. + ''' + + if "[" not in needle: + haystack = haystack.rpartition(" [")[0] + + stripped_haystack = haystack.replace(' ','').replace('-','').replace('_','') + + if anchored: + if haystack.startswith(needle) or stripped_haystack.startswith(needle): + return True + + else: + if needle in haystack or needle in stripped_haystack: + return True + return False + + + def nonconsec_find(needle, haystack, anchored = False): """checks if each character of "needle" can be found in order (but not necessarily consecutivly) in haystack. @@ -227,7 +248,7 @@ def increment(self, key): class NodeModel(QtCore.QAbstractListModel): - def __init__(self, mlist, weights, num_items = 15, filtertext = ""): + def __init__(self, mlist, weights, num_items = 18, filtertext = ""): super(NodeModel, self).__init__() self.weights = weights @@ -249,17 +270,30 @@ def update(self): # Two spaces as a shortcut for [ filtertext = filtertext.replace(" ", "[") + + anchored = True + force_non_anchored = False + # Starting the string with * or [ disables anchoring. + # Starting with ** forces non anchored results + if filtertext.startswith('*') or filtertext.startswith('['): + anchored = False + filtertext = filtertext.replace("*", "", 1) + if filtertext.startswith('*'): + force_non_anchored = True + filtertext = filtertext.replace("*", "") scored_a = [] scored_b = [] - scored_c = [] for n in self._all: # Turn "3D/Shader/Phong" into "Phong [3D/Shader]" menupath = n['menupath'].replace("&", "") uiname = "%s [%s]" % (menupath.rpartition("/")[2], menupath.rpartition("/")[0]) - stripped_name = uiname.replace(' ','').replace('-','').replace('_','') + search_string = uiname.lower() - if uiname.lower().startswith(filtertext) or stripped_name.startswith(filtertext): + if force_non_anchored: + search_string = search_string[1:] + + if consec_find(filtertext, search_string, anchored): # Matches, get weighting and add to list of stuff score = self.weights.get(n['menupath']) @@ -269,7 +303,7 @@ def update(self): 'menuobj': n['menuobj'], 'score': score}) - elif nonconsec_find(filtertext, uiname.lower(), anchored=True): + elif nonconsec_find(filtertext, search_string, anchored): # Matches, get weighting and add to list of stuff score = self.weights.get(n['menupath']) @@ -279,21 +313,10 @@ def update(self): 'menuobj': n['menuobj'], 'score': score}) - elif nonconsec_find(filtertext, uiname.lower(), anchored=False): - # Matches, get weighting and add to list of stuff - score = self.weights.get(n['menupath']) - - scored_c.append({ - 'text': uiname, - 'menupath': n['menupath'], - 'menuobj': n['menuobj'], - 'score': score}) - - # Store based on scores (descending), then alphabetically + # Sort based on scores (descending), then alphabetically sort_a = sorted(scored_a, key = lambda k: (-k['score'], k['text'])) sort_b = sorted(scored_b, key = lambda k: (-k['score'], k['text'])) - sort_c = sorted(scored_c, key = lambda k: (-k['score'], k['text'])) - s = sort_a + sort_b + sort_c + s = sort_a + sort_b self._items = s self.modelReset.emit()