diff --git a/plugin/commands/auto_set_syntax.py b/plugin/commands/auto_set_syntax.py index 065a8017..7138f3f8 100644 --- a/plugin/commands/auto_set_syntax.py +++ b/plugin/commands/auto_set_syntax.py @@ -67,7 +67,7 @@ def run_auto_set_syntax_on_view( # prerequsites if not ( (window := view.window()) - and is_syntaxable_view(view, must_plaintext) + and is_syntaxable_view(view, must_plaintext=must_plaintext) and (syntax_rule_collection := G.syntax_rule_collections.get(window)) ): return False diff --git a/plugin/helpers.py b/plugin/helpers.py index 09e8a281..34068487 100644 --- a/plugin/helpers.py +++ b/plugin/helpers.py @@ -1,12 +1,14 @@ from __future__ import annotations +from collections import deque + import sublime from .settings import get_st_setting -from .utils import is_plaintext_syntax, is_transient_view, stable_unique +from .utils import is_plaintext_syntax, is_transient_view -def is_syntaxable_view(view: sublime.View, must_plaintext: bool = False) -> bool: +def is_syntaxable_view(view: sublime.View, *, must_plaintext: bool = False) -> bool: """Determinates whether the view is what we want to set a syntax on.""" return bool( view.is_valid() @@ -18,15 +20,18 @@ def is_syntaxable_view(view: sublime.View, must_plaintext: bool = False) -> bool def resolve_magika_label_with_syntax_map(label: str, syntax_map: dict[str, list[str]]) -> list[str]: - res: list[str] = [] - queue: list[str] = syntax_map.get(label, []).copy() - - # @todo what if there are circular references? - while queue: - syntax_like = queue.pop() - if syntax_like.startswith("="): - queue.extend(syntax_map.get(syntax_like[1:], [])) + # note that dict is insertion-ordered (since Python 3.7) + res: dict[str, bool] = {} + + deq = deque(syntax_map.get(label, [])) + while deq: + if (symbol := deq.popleft()) in res: continue - res.append(syntax_like) + res[symbol] = False # visited + + if symbol.startswith("="): + deq.extendleft(reversed(syntax_map.get(symbol[1:], []))) + else: + res[symbol] = True # parsed - return list(stable_unique(reversed(res))) + return [scope for scope, is_parsed in res.items() if is_parsed]