miggor-StreamGraph/classes/deck/search_provider.gd

113 lines
4 KiB
GDScript3
Raw Permalink Normal View History

# (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)
class_name SearchProvider
## 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.
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)
#print("pre: ", pre_strip_string)
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()
#prints("c:", c, "r:", search_string.replace(c, ""))
return search_string.replace(c, "")
),
]
## Performs a search for nodes. Filters can be provided directly in the search [param term].
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 not 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
## 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.
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