pyflowlauncher 0.1.0.dev0__tar.gz → 0.3.0.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/PKG-INFO +1 -1
  2. pyflowlauncher-0.3.0.dev0/pyflowlauncher/manifest.py +25 -0
  3. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/plugin.py +14 -0
  4. pyflowlauncher-0.3.0.dev0/pyflowlauncher/string_matcher.py +222 -0
  5. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher.egg-info/PKG-INFO +1 -1
  6. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher.egg-info/SOURCES.txt +2 -0
  7. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyproject.toml +2 -2
  8. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/README.md +0 -0
  9. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/__init__.py +0 -0
  10. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/api.py +0 -0
  11. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/event.py +0 -0
  12. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/icons.py +0 -0
  13. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/jsonrpc.py +0 -0
  14. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/method.py +0 -0
  15. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/result.py +0 -0
  16. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher/shared.py +0 -0
  17. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher.egg-info/dependency_links.txt +0 -0
  18. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher.egg-info/requires.txt +0 -0
  19. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/pyflowlauncher.egg-info/top_level.txt +0 -0
  20. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/setup.cfg +0 -0
  21. {pyflowlauncher-0.1.0.dev0 → pyflowlauncher-0.3.0.dev0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflowlauncher
3
- Version: 0.1.0.dev0
3
+ Version: 0.3.0.dev0
4
4
  Summary: Python library to help build Flow Launcher plugins.
5
5
  Author-email: William McAllister <dev.garulf@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,25 @@
1
+ from typing import TypedDict, Literal
2
+
3
+ MANIFEST_FILE = 'plugin.json'
4
+
5
+ Languages = Literal[
6
+ 'Python',
7
+ 'CSharp',
8
+ 'FSharp',
9
+ 'Executable',
10
+ 'TypeScript',
11
+ 'JavaScript',
12
+ ]
13
+
14
+
15
+ class PluginManifestSchema(TypedDict):
16
+ ID: str
17
+ ActionKeyword: str
18
+ Name: str
19
+ Description: str
20
+ Author: str
21
+ Version: str
22
+ Language: Languages
23
+ Website: str
24
+ IcoPath: str
25
+ ExecuteFileName: str
@@ -1,13 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
3
4
  from functools import wraps
4
5
  from typing import Any, Callable, Iterable, Union
6
+ from pathlib import Path
7
+ import json
5
8
 
6
9
  from pyflowlauncher.shared import logger
7
10
 
8
11
  from .event import EventHandler
9
12
  from .jsonrpc import JsonRPCClient
10
13
  from .result import JsonRPCAction, ResultResponse
14
+ from .manifest import PluginManifestSchema, MANIFEST_FILE
11
15
 
12
16
  Method = Callable[..., Union[ResultResponse, JsonRPCAction]]
13
17
 
@@ -54,3 +58,13 @@ class Plugin:
54
58
  if 'result' in feedback and self._settings is not None:
55
59
  feedback['SettingsChange'] = self.settings
56
60
  self._client.send(feedback)
61
+
62
+ def plugin_root_dir(self) -> Path:
63
+ """Return the root directory of the plugin."""
64
+ return Path(sys.argv[0]).parent
65
+
66
+ def manifest(self) -> PluginManifestSchema:
67
+ """Return the plugin manifest."""
68
+ with open(self.plugin_root_dir() / MANIFEST_FILE, 'r', encoding='utf-8') as f:
69
+ manifest = json.load(f)
70
+ return manifest
@@ -0,0 +1,222 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List
3
+
4
+ SPACE_CHAR: str = ' '
5
+ QUERY_SEARCH_PRECISION = {
6
+ 'Regular': 50,
7
+ 'Low': 20,
8
+ 'None': 0
9
+ }
10
+ DEFAULT_QUERY_SEARCH_PRECISION = QUERY_SEARCH_PRECISION['Regular']
11
+
12
+ """
13
+ This is a python copy of Flow Launcher's string matcher.
14
+ I take no credit for the algorithm, I just translated it to python.
15
+ """
16
+
17
+
18
+ @dataclass
19
+ class MatchData:
20
+ """Match data"""
21
+ matched: bool
22
+ score_cutoff: int
23
+ index_list: List[int] = field(default_factory=list)
24
+ score: int = 0
25
+
26
+
27
+ def string_matcher(query: str, text: str, ignore_case: bool = True,
28
+ query_search_precision: int = DEFAULT_QUERY_SEARCH_PRECISION) -> MatchData:
29
+ """Compare query to text"""
30
+ if not text or not query:
31
+ return MatchData(False, query_search_precision)
32
+
33
+ query = query.strip()
34
+
35
+ current_acronym_query_index = 0
36
+ acronym_match_data: List[int] = []
37
+ acronyms_total_count: int = 0
38
+ acronyms_matched: int = 0
39
+
40
+ full_text_lower: str = text.lower() if ignore_case else text
41
+ query_lower: str = query.lower() if ignore_case else query
42
+
43
+ query_substrings: List[str] = query_lower.split(' ')
44
+ current_query_substring_index: int = 0
45
+ current_query_substring = query_substrings[current_query_substring_index]
46
+ current_query_substring_char_index = 0
47
+
48
+ first_match_index = -1
49
+ first_match_index_in_word = -1
50
+ last_match_index = 0
51
+ all_query_substrings_matched: bool = False
52
+ match_found_in_previous_loop: bool = False
53
+ all_substrings_contained_in_text: bool = True
54
+
55
+ index_list: List[int] = []
56
+ space_indices: List[int] = []
57
+ for text_index in range(len(full_text_lower)):
58
+ if (current_acronym_query_index >= len(query_lower) or
59
+ (current_acronym_query_index >= len(query_lower) and all_query_substrings_matched)):
60
+ break
61
+
62
+ if full_text_lower[text_index] == SPACE_CHAR and current_query_substring_char_index == 0:
63
+ space_indices.append(text_index)
64
+
65
+ if is_acronym(text, text_index):
66
+ if full_text_lower[text_index] == query_lower[current_acronym_query_index]:
67
+ acronym_match_data.append(text_index)
68
+ acronyms_matched += 1
69
+ current_acronym_query_index += 1
70
+
71
+ if is_acronym_count(text, text_index):
72
+ acronyms_total_count += 1
73
+
74
+ if all_query_substrings_matched or (full_text_lower[text_index]
75
+ != current_query_substring[current_query_substring_char_index]):
76
+ match_found_in_previous_loop = False
77
+ continue
78
+
79
+ if first_match_index < 0:
80
+ first_match_index = text_index
81
+
82
+ if current_query_substring_char_index == 0:
83
+ match_found_in_previous_loop = True
84
+ first_match_index_in_word = text_index
85
+ elif not match_found_in_previous_loop:
86
+ start_index_to_verify = text_index - current_query_substring_char_index
87
+
88
+ if all_previous_chars_matched(start_index_to_verify, current_query_substring_char_index, full_text_lower,
89
+ current_query_substring):
90
+ match_found_in_previous_loop = True
91
+ first_match_index_in_word = start_index_to_verify if current_query_substring_index == 0 else first_match_index
92
+
93
+ index_list = get_updated_index_list(
94
+ start_index_to_verify, current_query_substring_char_index, first_match_index_in_word, index_list)
95
+
96
+ last_match_index = text_index + 1
97
+ index_list.append(text_index)
98
+
99
+ current_query_substring_char_index += 1
100
+
101
+ if current_query_substring_char_index == len(current_query_substring):
102
+ all_substrings_contained_in_text = match_found_in_previous_loop and all_substrings_contained_in_text
103
+
104
+ current_query_substring_index += 1
105
+
106
+ all_query_substrings_matched = all_query_substrings_matched_func(
107
+ current_query_substring_index, len(query_substrings))
108
+
109
+ if all_query_substrings_matched:
110
+ continue
111
+
112
+ current_query_substring = query_substrings[current_query_substring_index]
113
+ current_query_substring_char_index = 0
114
+
115
+ if acronyms_matched > 0 and acronyms_matched == len(query):
116
+ acronyms_score: int = int(acronyms_matched * 100 / acronyms_total_count)
117
+
118
+ if acronyms_score >= query_search_precision:
119
+ return MatchData(True, query_search_precision, acronym_match_data, acronyms_score)
120
+
121
+ if all_query_substrings_matched:
122
+
123
+ nearest_space_index = calculate_closest_space_index(
124
+ space_indices, first_match_index)
125
+
126
+ score = calculate_search_score(query, text, first_match_index - nearest_space_index - 1,
127
+ space_indices, last_match_index - first_match_index, all_substrings_contained_in_text)
128
+
129
+ return MatchData(True, query_search_precision, index_list, score)
130
+
131
+ return MatchData(False, query_search_precision)
132
+
133
+
134
+ def calculate_search_score(query: str, text: str, first_index: int, space_indices: List[int],
135
+ match_length: int, all_substrings_contained_in_text: bool):
136
+ score = 100 * (len(query) + 1) / ((1 + first_index) + (match_length + 1))
137
+
138
+ if first_index == 0 and all_substrings_contained_in_text:
139
+ score -= len(space_indices)
140
+
141
+ if (len(text) - len(query)) < 5:
142
+ score += 20
143
+ elif (len(text) - len(query)) < 10:
144
+ score += 10
145
+
146
+ if all_substrings_contained_in_text:
147
+ count: int = len(query.replace(' ', ''))
148
+ threshold: int = 4
149
+ if count <= threshold:
150
+ score += count * 10
151
+ else:
152
+ score += threshold * 10 + (count - threshold) * 5
153
+
154
+ return score
155
+
156
+
157
+ def get_updated_index_list(start_index_to_verify: int,
158
+ current_query_substring_char_index: int,
159
+ first_matched_index_in_word: int, index_list: List[int]):
160
+ updated_list: List[int] = []
161
+
162
+ for idx, item in enumerate(index_list):
163
+ if item >= first_matched_index_in_word:
164
+ index_list.pop(idx)
165
+
166
+ updated_list.extend(index_list)
167
+
168
+ for i in range(current_query_substring_char_index):
169
+ updated_list.append(start_index_to_verify + i)
170
+
171
+ return updated_list
172
+
173
+
174
+ def all_query_substrings_matched_func(current_query_substring_index: int, query_substrings_length: int) -> bool:
175
+ return current_query_substring_index >= query_substrings_length
176
+
177
+
178
+ def all_previous_chars_matched(start_index_to_verify: int,
179
+ current_query_substring_char_index: int,
180
+ full_text_lower: str, current_query_substring: str) -> bool:
181
+ all_match = True
182
+ for i in range(current_query_substring_char_index):
183
+ if full_text_lower[start_index_to_verify + i] != current_query_substring[i]:
184
+ all_match = False
185
+
186
+ return all_match
187
+
188
+
189
+ def is_acronym(text: str, text_index: int) -> bool:
190
+ if is_acronym_char(text, text_index) or is_acronym_number(text, text_index):
191
+ return True
192
+ return False
193
+
194
+
195
+ def is_acronym_count(text: str, text_index: int) -> bool:
196
+ if is_acronym_char(text, text_index):
197
+ return True
198
+ if is_acronym_number(text, text_index):
199
+ return text_index == 0 or text[text_index - 1] == SPACE_CHAR
200
+
201
+ return False
202
+
203
+
204
+ def is_acronym_char(text: str, text_index: int) -> bool:
205
+ return text[text_index].isupper() or text_index == 0 or text[text_index - 1] == SPACE_CHAR
206
+
207
+
208
+ def is_acronym_number(text: str, text_index: int) -> bool:
209
+ return text[text_index].isdigit()
210
+
211
+
212
+ def calculate_closest_space_index(space_indices: List[int], first_match_index: int) -> int:
213
+
214
+ closest_space_index = -1
215
+
216
+ for i in space_indices:
217
+ if i < first_match_index:
218
+ closest_space_index = i
219
+ else:
220
+ break
221
+
222
+ return closest_space_index
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyflowlauncher
3
- Version: 0.1.0.dev0
3
+ Version: 0.3.0.dev0
4
4
  Summary: Python library to help build Flow Launcher plugins.
5
5
  Author-email: William McAllister <dev.garulf@gmail.com>
6
6
  License: MIT
@@ -6,10 +6,12 @@ pyflowlauncher/api.py
6
6
  pyflowlauncher/event.py
7
7
  pyflowlauncher/icons.py
8
8
  pyflowlauncher/jsonrpc.py
9
+ pyflowlauncher/manifest.py
9
10
  pyflowlauncher/method.py
10
11
  pyflowlauncher/plugin.py
11
12
  pyflowlauncher/result.py
12
13
  pyflowlauncher/shared.py
14
+ pyflowlauncher/string_matcher.py
13
15
  pyflowlauncher.egg-info/PKG-INFO
14
16
  pyflowlauncher.egg-info/SOURCES.txt
15
17
  pyflowlauncher.egg-info/dependency_links.txt
@@ -6,7 +6,7 @@ name = "pyflowlauncher"
6
6
  authors = [
7
7
  {name = "William McAllister", email = "dev.garulf@gmail.com"}
8
8
  ]
9
- version = '0.1.0-dev.0'
9
+ version = '0.3.0-dev.0'
10
10
  description = "Python library to help build Flow Launcher plugins."
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.8"
@@ -24,7 +24,7 @@ dependencies = [
24
24
  packages = ["pyflowlauncher"]
25
25
 
26
26
  [tool.bumpversion]
27
- current_version = "0.1.0-dev.0"
27
+ current_version = "0.3.0-dev.0"
28
28
  parse = """(?x)
29
29
  (?P<major>[0-9]+)
30
30
  \\.(?P<minor>[0-9]+)