2023-12-15 00:06:54 +01:00
|
|
|
# (c) 2023-present Eroax
|
|
|
|
# (c) 2023-present Yagich
|
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
2023-11-23 07:38:10 +01:00
|
|
|
class_name SearchProvider
|
|
|
|
|
2023-11-25 11:40:53 +01:00
|
|
|
## A class facilitating the searching of nodes.
|
|
|
|
##
|
|
|
|
## Allows a renderer or subscribing client to search for nodes registered in NodeDB, optionally with filters.
|
|
|
|
|
|
|
|
## A list of all filters that can be applied to a search string.
|
2023-11-23 07:38:10 +01:00
|
|
|
static var filters: Array[Filter] = [
|
|
|
|
# favorites filter. will only show nodes marked as favorite. syntax: "#f"
|
|
|
|
Filter.new(
|
|
|
|
func(search_string: String) -> bool:
|
|
|
|
return "#f" in search_string,
|
|
|
|
|
|
|
|
func(element: NodeDB.NodeDescriptor, _search_string: String, _pre_strip_string: String) -> bool:
|
|
|
|
return NodeDB.is_node_favorite(element.type),
|
|
|
|
|
|
|
|
func(search_string: String) -> String:
|
|
|
|
return search_string.replace("#f", "")
|
|
|
|
),
|
|
|
|
|
|
|
|
# category filter. will only match nodes that are in a certain category. syntax: "#c category_name"
|
|
|
|
Filter.new(
|
|
|
|
func(search_string: String) -> bool:
|
|
|
|
const p := r"#c\s[\w]+"
|
|
|
|
var r := RegEx.create_from_string(p)
|
|
|
|
var c := r.search(search_string)
|
|
|
|
|
|
|
|
return c != null,
|
|
|
|
|
|
|
|
func(element: NodeDB.NodeDescriptor, _search_string: String, pre_strip_string: String) -> bool:
|
|
|
|
const p := r"#c\s[\w]+"
|
|
|
|
var r := RegEx.create_from_string(p)
|
2023-12-14 21:52:22 +01:00
|
|
|
#print("pre: ", pre_strip_string)
|
2023-11-23 07:38:10 +01:00
|
|
|
var c := r.search(pre_strip_string).get_string().split("#c ", false)[0]
|
|
|
|
|
|
|
|
return c.is_subsequence_ofn(element.category),
|
|
|
|
|
|
|
|
func(search_string: String) -> String:
|
|
|
|
const p := r"#c\s[\w]+"
|
|
|
|
var r := RegEx.create_from_string(p)
|
|
|
|
var c := r.search(search_string).get_string()
|
2023-12-14 21:52:22 +01:00
|
|
|
#prints("c:", c, "r:", search_string.replace(c, ""))
|
2023-11-23 07:38:10 +01:00
|
|
|
return search_string.replace(c, "")
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2023-11-25 11:40:53 +01:00
|
|
|
## Performs a search for nodes. Filters can be provided directly in the search [param term].
|
2023-11-23 07:38:10 +01:00
|
|
|
static func search(term: String) -> Array[NodeDB.NodeDescriptor]:
|
|
|
|
var res: Array[NodeDB.NodeDescriptor] = []
|
|
|
|
|
|
|
|
var filters_to_apply := filters.filter(
|
|
|
|
func(f: Filter):
|
|
|
|
return f.should_apply.call(term)
|
|
|
|
)
|
|
|
|
var cleaned_search_string := term
|
|
|
|
# strip string of filter-specific substrings
|
|
|
|
for f: Filter in filters_to_apply:
|
|
|
|
f.pre_strip_string = cleaned_search_string
|
|
|
|
cleaned_search_string = f.strip_string.call(cleaned_search_string)
|
|
|
|
cleaned_search_string = cleaned_search_string.strip_edges()
|
|
|
|
|
|
|
|
for node_type: String in NodeDB.nodes:
|
|
|
|
var nd: NodeDB.NodeDescriptor = NodeDB.nodes[node_type]
|
|
|
|
if !nd.appears_in_search:
|
|
|
|
continue
|
|
|
|
|
|
|
|
var full_search_string := nd.name + nd.aliases
|
|
|
|
if cleaned_search_string.is_subsequence_ofn(full_search_string):
|
|
|
|
res.append(nd)
|
|
|
|
|
|
|
|
# no filters apply, just return the results straight
|
|
|
|
if filters_to_apply.is_empty():
|
|
|
|
return res
|
|
|
|
|
|
|
|
# apply filters
|
|
|
|
var filtered_res: Array[NodeDB.NodeDescriptor] = res.duplicate()
|
|
|
|
for f: Filter in filters_to_apply:
|
|
|
|
filtered_res = filtered_res.filter(f.match_term.bind(cleaned_search_string, f.pre_strip_string))
|
|
|
|
|
|
|
|
return filtered_res
|
|
|
|
|
|
|
|
|
2023-11-25 11:40:53 +01:00
|
|
|
## A filter that can be applied to a search term in [SearchProvider].
|
|
|
|
##
|
|
|
|
## Filters have a set of functions that are run by the [SearchProvider]
|
|
|
|
## to determine if any items should be removed from the final match list.
|
2023-11-23 07:38:10 +01:00
|
|
|
class Filter:
|
|
|
|
## Return [code]true[/code] if this filter should be applied to the search.[br]
|
|
|
|
## [code]Callable(search_string: String) -> bool[/code]
|
|
|
|
var should_apply: Callable
|
|
|
|
|
|
|
|
## Return a [code]bool[/code] if the provided [code]NodeDescriptor[/code]
|
|
|
|
## should be included in the search results array.[br]
|
|
|
|
## [code]Callable(element: NodeDB.NodeDescriptor, search_string: String, pre_strip_string: String) -> bool[/code]
|
|
|
|
var match_term: Callable
|
|
|
|
|
|
|
|
## Return a string that's stripped of this filter's shorthand.
|
|
|
|
## [code]Callable(search_string: String) -> String[/code]
|
|
|
|
var strip_string: Callable
|
|
|
|
|
|
|
|
## The search string as it was before [member strip_string] was called. Useful for filters that use arguments, like the category filter.
|
|
|
|
var pre_strip_string: String
|
|
|
|
|
|
|
|
|
|
|
|
func _init(p_should_apply: Callable, p_match_term: Callable, p_strip_string: Callable) -> void:
|
|
|
|
should_apply = p_should_apply
|
|
|
|
match_term = p_match_term
|
|
|
|
strip_string = p_strip_string
|