diff --git a/plugin/__init__.py b/plugin/__init__.py index 812038f3..e9511d24 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -65,6 +65,7 @@ def plugin_loaded() -> None: + """Executed when this plugin is loaded.""" # somehow "AutoSetSyntaxAppendLogCommand" won't be ready if we don't wait a bit sublime.set_timeout(_plugin_loaded) @@ -86,6 +87,7 @@ def _plugin_loaded() -> None: def plugin_unloaded() -> None: + """Executed when this plugin is unloaded.""" AioSettings.clear_on_change(PLUGIN_NAME) AioSettings.tear_down() diff --git a/plugin/rules/constraint.py b/plugin/rules/constraint.py index 1595a0a9..867c45c7 100644 --- a/plugin/rules/constraint.py +++ b/plugin/rules/constraint.py @@ -14,7 +14,15 @@ from ..constants import PLUGIN_NAME, ST_PLATFORM from ..snapshot import ViewSnapshot from ..types import Optimizable, ST_ConstraintRule -from ..utils import camel_to_snake, compile_regex, list_all_subclasses, merge_regexes, parse_regex_flags, remove_suffix +from ..utils import ( + camel_to_snake, + compile_regex, + drop_falsy, + list_all_subclasses, + merge_regexes, + parse_regex_flags, + remove_suffix, +) T = TypeVar("T") @@ -116,7 +124,7 @@ def test(self, view_snapshot: ViewSnapshot) -> bool: @final def _handled_args(self, normalizer: Callable[[T], T] | None = None) -> tuple[T, ...]: """Filter falsy args and normalize them. Note that `0`, `""` and `None` are falsy.""" - args: Iterable[T] = filter(None, self.args) + args: Iterable[T] = drop_falsy(self.args) if normalizer: args = map(normalizer, args) return tuple(args) diff --git a/plugin/rules/syntax.py b/plugin/rules/syntax.py index 4d7b831c..4e4fb8dc 100644 --- a/plugin/rules/syntax.py +++ b/plugin/rules/syntax.py @@ -10,7 +10,7 @@ from ..constants import VERSION from ..snapshot import ViewSnapshot from ..types import ListenerEvent, Optimizable, ST_SyntaxRule -from ..utils import find_syntax_by_syntax_likes +from ..utils import drop_falsy, find_syntax_by_syntax_likes from .match import MatchRule @@ -74,7 +74,7 @@ def make(cls, syntax_rule: ST_SyntaxRule) -> Self: if (on_events := syntax_rule.get("on_events")) is not None: if isinstance(on_events, str): on_events = [on_events] - obj.on_events = set(filter(None, map(ListenerEvent.from_value, on_events))) + obj.on_events = set(drop_falsy(map(ListenerEvent.from_value, on_events))) if match_rule_compiled := MatchRule.make(syntax_rule): obj.root_rule = match_rule_compiled diff --git a/plugin/settings.py b/plugin/settings.py index e9fd24ef..dac12f1e 100644 --- a/plugin/settings.py +++ b/plugin/settings.py @@ -9,6 +9,7 @@ from more_itertools import unique_everseen from .types import ST_SyntaxRule +from .utils import drop_falsy def get_merged_plugin_setting( @@ -52,8 +53,7 @@ def extra_settings_producer(settings: MergedSettingsDict) -> dict[str, Any]: # use tuple to freeze setting for better performance (cache-able) ret["trim_suffixes"] = tuple( - filter( - None, # remove falsy values + drop_falsy( unique_everseen( chain( settings.get("project_trim_suffixes", []), diff --git a/plugin/types.py b/plugin/types.py index be1a5feb..e3e0a93d 100644 --- a/plugin/types.py +++ b/plugin/types.py @@ -3,7 +3,7 @@ import sys from abc import ABC, abstractmethod from collections import UserDict as BuiltinUserDict -from collections.abc import Generator, Iterator, KeysView +from collections.abc import Generator, Hashable, Iterator, KeysView from enum import Enum from typing import Any, Generic, TypedDict, TypeVar, Union, overload @@ -14,37 +14,37 @@ WindowId = int WindowIdAble = Union[WindowId, sublime.Window] -_K = TypeVar("_K") +_KT = TypeVar("_KT", bound=Hashable) +_KV = TypeVar("_KV") _T = TypeVar("_T") -_V = TypeVar("_V") if sys.version_info < (3, 9): - class UserDict(BuiltinUserDict, Generic[_K, _V]): + class UserDict(BuiltinUserDict, Generic[_KT, _KV]): """Workaround class for the fact that `UserDict` is not subscriptable until Python 3.9...""" def __init__(self, dict=None, /, **kwargs) -> None: - self.data: dict[_K, _V] = {} + self.data: dict[_KT, _KV] = {} super().__init__(dict, **kwargs) - def __getitem__(self, key: _K) -> _V: + def __getitem__(self, key: _KT) -> _KV: return super().__getitem__(key) - def __setitem__(self, key: _K, item: _V) -> None: + def __setitem__(self, key: _KT, item: _KV) -> None: super().__setitem__(key, item) - def __delitem__(self, key: _K) -> None: + def __delitem__(self, key: _KT) -> None: super().__delitem__(key) - def __iter__(self) -> Iterator[_K]: + def __iter__(self) -> Iterator[_KT]: return super().__iter__() @overload - def get(self, key: _K) -> _V | None: ... + def get(self, key: _KT) -> _KV | None: ... @overload - def get(self, key: _K, default: _T) -> _V | _T: ... + def get(self, key: _KT, default: _T) -> _KV | _T: ... - def get(self, key: _K, default: _T | None = None) -> _V | _T | None: + def get(self, key: _KT, default: _T | None = None) -> _KV | _T | None: return super().get(key, default) else: UserDict = BuiltinUserDict # noqa: F401 diff --git a/plugin/utils.py b/plugin/utils.py index 4aa90b84..90f5bc18 100644 --- a/plugin/utils.py +++ b/plugin/utils.py @@ -63,6 +63,11 @@ def compile_regex(regex: str | Pattern[str], flags: int = 0) -> Pattern[str]: return re.compile(regex, flags) +def drop_falsy(iterable: Iterable[_T | None]) -> Generator[_T, None, None]: + """Drops falsy values from the iterable.""" + yield from filter(None, iterable) + + def get_fqcn(obj: Any) -> str: if obj is None: return "None" @@ -86,7 +91,7 @@ def merge_regexes(regexes: Iterable[str]) -> str: """Merge regex strings into a single regex string.""" regexes = tuple(regexes) if len(regexes) == 0: - return "" + return r"~^(?#match nothing)" if len(regexes) == 1: merged = regexes[0] else: