Skip to content

Commit

Permalink
refactor: tidy codes
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <[email protected]>
  • Loading branch information
jfcherng committed Nov 1, 2023
1 parent c37e4fe commit 8d6a434
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 87 deletions.
18 changes: 9 additions & 9 deletions plugin/commands/auto_set_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from ..rules import SyntaxRuleCollection
from ..settings import get_merged_plugin_setting, get_merged_plugin_settings, pref_trim_suffixes
from ..shared import G
from ..snapshot import ViewSnapshot, ViewSnapshotCollection
from ..snapshot import ViewSnapshot
from ..types import ListenerEvent
from ..utils import (
find_syntax_by_syntax_like,
Expand Down Expand Up @@ -147,11 +147,11 @@ def _view_snapshot_context(view: sublime.View) -> Generator[ViewSnapshot, None,

try:
settings.set(VIEW_RUN_ID_SETTINGS_KEY, run_id)
ViewSnapshotCollection.add(run_id, view)
yield ViewSnapshotCollection.get(run_id) # type: ignore
G.view_snapshot_collection.add(run_id, view)
yield G.view_snapshot_collection.get(run_id) # type: ignore
finally:
settings.erase(VIEW_RUN_ID_SETTINGS_KEY)
ViewSnapshotCollection.pop(run_id)
G.view_snapshot_collection.pop(run_id)


def _snapshot_view(failed_ret: Any = None) -> Callable[[_T_Callable], _T_Callable]:
Expand Down Expand Up @@ -184,7 +184,7 @@ def run_auto_set_syntax_on_view(
if not (
(window := view.window())
and is_syntaxable_view(view, must_plaintext)
and (syntax_rule_collection := G.get_syntax_rule_collection(window))
and (syntax_rule_collection := G.syntax_rule_collections.get(window))
):
return False

Expand Down Expand Up @@ -258,7 +258,7 @@ def _assign_syntax_for_new_view(view: sublime.View, event: ListenerEvent | None

def _assign_syntax_for_st_syntax_test(view: sublime.View, event: ListenerEvent | None = None) -> bool:
if (
(view_snapshot := ViewSnapshotCollection.get_by_view(view))
(view_snapshot := G.view_snapshot_collection.get_by_view(view))
and (not view_snapshot.syntax or is_plaintext_syntax(view_snapshot.syntax))
and (m := RE_ST_SYNTAX_TEST_LINE.search(view_snapshot.first_line))
and (new_syntax := m.group("syntax")).endswith(".sublime-syntax")
Expand Down Expand Up @@ -288,7 +288,7 @@ def _assign_syntax_with_plugin_rules(


def _assign_syntax_with_first_line(view: sublime.View, event: ListenerEvent | None = None) -> bool:
if not (view_snapshot := ViewSnapshotCollection.get_by_view(view)):
if not (view_snapshot := G.view_snapshot_collection.get_by_view(view)):
return False

# Note that this only works for files under some circumstances.
Expand Down Expand Up @@ -359,7 +359,7 @@ def _assign_syntax_with_trimmed_filename(view: sublime.View, event: ListenerEven

def _assign_syntax_with_special_cases(view: sublime.View, event: ListenerEvent | None = None) -> bool:
if not (
(view_snapshot := ViewSnapshotCollection.get_by_view(view))
(view_snapshot := G.view_snapshot_collection.get_by_view(view))
and view_snapshot.syntax
and is_plaintext_syntax(view_snapshot.syntax)
):
Expand Down Expand Up @@ -392,7 +392,7 @@ def is_json(view: sublime.View) -> bool:
def _assign_syntax_with_guesslang_async(view: sublime.View, event: ListenerEvent | None = None) -> None:
if not (
G.guesslang_client
and (view_snapshot := ViewSnapshotCollection.get_by_view(view))
and (view_snapshot := G.view_snapshot_collection.get_by_view(view))
# don't apply on those have an extension
and (event == ListenerEvent.COMMAND or "." not in view_snapshot.file_name_unhidden)
# only apply on plain text syntax
Expand Down
4 changes: 2 additions & 2 deletions plugin/commands/auto_set_syntax_debug_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def run(self) -> None:
},
}
info["plugin_settings"] = get_merged_plugin_settings(window=self.window)
info["syntax_rule_collection"] = G.get_syntax_rule_collection(self.window)
info["dropped_rules"] = G.get_dropped_rules(self.window)
info["syntax_rule_collection"] = G.syntax_rule_collections.get(self.window)
info["dropped_rules"] = G.dropped_rules_collection.get(self.window, [])

msg = TEMPLATE.format_map(_pythonize(info))

Expand Down
10 changes: 5 additions & 5 deletions plugin/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def set_up_window(window: sublime.Window) -> None:


def tear_down_window(window: sublime.Window) -> None:
G.clear_syntax_rule_collection(window)
G.clear_dropped_rules(window)
G.syntax_rule_collections.pop(window, None)
G.dropped_rules_collection.pop(window, None)
Logger.log("👋 Bye!", window=window)
Logger.destroy(window=window)

Expand All @@ -59,11 +59,11 @@ def names_as_str(items: Iterable[Any], *, sep: str = ", ") -> str:
Logger.log(f'🔍 Found "Constraint" implementations: {names_as_str(get_constraints())}', window=window)

syntax_rule_collection = SyntaxRuleCollection.make(pref_syntax_rules(window=window))
G.set_syntax_rule_collection(window, syntax_rule_collection)
G.syntax_rule_collections[window] = syntax_rule_collection
Logger.log(f"📜 Compiled syntax rule collection: {stringify(syntax_rule_collection)}", window=window)

dropped_rules = tuple(syntax_rule_collection.optimize())
G.set_dropped_rules(window, dropped_rules)
dropped_rules = list(syntax_rule_collection.optimize())
G.dropped_rules_collection[window] = dropped_rules
Logger.log(f"✨ Optimized syntax rule collection: {stringify(syntax_rule_collection)}", window=window)
Logger.log(f"💀 Dropped rules during optimizing: {stringify(dropped_rules)}", window=window)

Expand Down
9 changes: 6 additions & 3 deletions plugin/rules/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

from ..cache import clearable_lru_cache
from ..constants import PLUGIN_NAME, ST_PLATFORM
from ..snapshot import ViewSnapshot, ViewSnapshotCollection
from ..shared import G
from ..snapshot import ViewSnapshot
from ..types import Optimizable, ST_ConstraintRule
from ..utils import (
camel_to_snake,
Expand Down Expand Up @@ -164,7 +165,7 @@ def _handled_case_insensitive(kwargs: dict[str, Any]) -> bool:
@staticmethod
def get_view_snapshot(view: sublime.View) -> ViewSnapshot:
"""Gets the cached information for the `view`."""
snapshot = ViewSnapshotCollection.get_by_view(view)
snapshot = G.view_snapshot_collection.get_by_view(view)
assert snapshot # our workflow guarantees this won't be None
return snapshot

Expand All @@ -176,8 +177,10 @@ def find_parent_with_sibling(base: str | Path, sibling: str, *, use_exists: bool

if use_exists:
checker = Path.exists
elif sibling.endswith(("\\", "/")):
checker = Path.is_dir # type: ignore
else:
checker = Path.is_dir if sibling.endswith(("\\", "/")) else Path.is_file
checker = Path.is_file # type: ignore

return first_true(path.parents, pred=lambda p: checker(p / sibling))

Expand Down
61 changes: 28 additions & 33 deletions plugin/shared.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Iterable, Tuple
from typing import TYPE_CHECKING, Iterable, List

import sublime

from .guesslang.server import GuesslangServer
from .settings import get_merged_plugin_settings
from .types import Optimizable
from .snapshot import ViewSnapshotCollection
from .types import Optimizable, WindowKeyedDict

if TYPE_CHECKING:
from .guesslang.client import GuesslangClient
from .rules import SyntaxRuleCollection

WindowId = int
DroppedRules = Tuple[Optimizable, ...]
DroppedRules = List[Optimizable]
DroppedRulesArg = Iterable[Optimizable]

# `UserDict` is not subscriptable until Python 3.9...
if TYPE_CHECKING:
_WindowKeyedDict_DroppedRules = WindowKeyedDict[DroppedRules]
_WindowKeyedDict_SyntaxRuleCollection = WindowKeyedDict[SyntaxRuleCollection]
else:
_WindowKeyedDict_DroppedRules = WindowKeyedDict
_WindowKeyedDict_SyntaxRuleCollection = WindowKeyedDict


class DroppedRulesCollection(_WindowKeyedDict_DroppedRules):
pass


class SyntaxRuleCollections(_WindowKeyedDict_SyntaxRuleCollection):
pass


class G:
"""This class holds "G"lobal variables as its class variables."""
Expand All @@ -29,36 +45,15 @@ class G:
startup_views: set[sublime.View] = set()
"""Views exist before this plugin is loaded when Sublime Text just starts."""

windows_syntax_rule_collection: dict[WindowId, SyntaxRuleCollection] = {}
"""(Per window) The compiled top-level plugin rules."""
syntax_rule_collections = SyntaxRuleCollections()
"""The compiled per-window top-level plugin rules."""

windows_dropped_rules: dict[WindowId, DroppedRules] = {}
"""(Per window) Those rules which are dropped after doing optimizations."""
dropped_rules_collection = DroppedRulesCollection()
"""Those per-window rules which are dropped after doing optimizations."""

@classmethod
def is_plugin_ready(cls, window: sublime.Window) -> bool:
return bool(get_merged_plugin_settings(window=window) and cls.get_syntax_rule_collection(window))

@classmethod
def set_syntax_rule_collection(cls, window: sublime.Window, value: SyntaxRuleCollection) -> None:
cls.windows_syntax_rule_collection[window.id()] = value
view_snapshot_collection = ViewSnapshotCollection()
"""Caches of view attributes."""

@classmethod
def get_syntax_rule_collection(cls, window: sublime.Window) -> SyntaxRuleCollection | None:
return cls.windows_syntax_rule_collection.get(window.id())

@classmethod
def clear_syntax_rule_collection(cls, window: sublime.Window) -> SyntaxRuleCollection | None:
return cls.windows_syntax_rule_collection.pop(window.id(), None)

@classmethod
def set_dropped_rules(cls, window: sublime.Window, value: DroppedRulesArg) -> None:
cls.windows_dropped_rules[window.id()] = tuple(value)

@classmethod
def get_dropped_rules(cls, window: sublime.Window) -> DroppedRules:
return cls.windows_dropped_rules.get(window.id()) or tuple()

@classmethod
def clear_dropped_rules(cls, window: sublime.Window) -> DroppedRules | None:
return cls.windows_dropped_rules.pop(window.id(), None)
def is_plugin_ready(cls, window: sublime.Window) -> bool:
return bool(get_merged_plugin_settings(window=window) and cls.syntax_rule_collections.get(window))
60 changes: 26 additions & 34 deletions plugin/snapshot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from __future__ import annotations

from collections import UserDict
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

import sublime

Expand Down Expand Up @@ -34,26 +36,30 @@ class ViewSnapshot:
"""The syntax object. Note that the value is as-is when it's cached."""


class ViewSnapshotCollection:
_snapshots: dict[str, ViewSnapshot] = {}
# `UserDict` is not subscriptable until Python 3.9...
if TYPE_CHECKING:

@classmethod
def add(cls, cache_id: str, view: sublime.View) -> None:
window = view.window() or sublime.active_window()
class ViewSnapshotCollection(UserDict[str, ViewSnapshot]):
def add(self, cache_id: str, view: sublime.View) -> None: ...
def get_by_view(self, view: sublime.View) -> ViewSnapshot | None: ...

# is real file on a disk?
if (_path := view.file_name()) and (path := Path(_path).resolve()).is_file():
file_name = path.name
file_path = path.as_posix()
file_size = path.stat().st_size
else:
file_name = ""
file_path = ""
file_size = -1
else:

cls.set(
cache_id,
ViewSnapshot(
class ViewSnapshotCollection(UserDict):
def add(self, cache_id: str, view: sublime.View) -> None:
window = view.window() or sublime.active_window()

# is real file on a disk?
if (_path := view.file_name()) and (path := Path(_path).resolve()).is_file():
file_name = path.name
file_path = path.as_posix()
file_size = path.stat().st_size
else:
file_name = ""
file_path = ""
file_size = -1

self[cache_id] = ViewSnapshot(
id=view.id(),
char_count=view.size(),
content=get_view_pseudo_content(view, window),
Expand All @@ -64,24 +70,10 @@ def add(cls, cache_id: str, view: sublime.View) -> None:
first_line=get_view_pseudo_first_line(view, window),
line_count=view.rowcol(view.size())[0] + 1,
syntax=view.syntax(),
),
)

@classmethod
def get(cls, cache_id: str) -> ViewSnapshot | None:
return cls._snapshots.get(cache_id, None)

@classmethod
def get_by_view(cls, view: sublime.View) -> ViewSnapshot | None:
return cls.get(view.settings().get(VIEW_RUN_ID_SETTINGS_KEY))

@classmethod
def set(cls, cache_id: str, snapshot: ViewSnapshot) -> None:
cls._snapshots[cache_id] = snapshot
)

@classmethod
def pop(cls, cache_id: str) -> ViewSnapshot | None:
return cls._snapshots.pop(cache_id, None)
def get_by_view(self, view: sublime.View) -> ViewSnapshot | None:
return self.get(view.settings().get(VIEW_RUN_ID_SETTINGS_KEY))


def get_view_pseudo_content(view: sublime.View, window: sublime.Window) -> str:
Expand Down
41 changes: 40 additions & 1 deletion plugin/types.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from collections import UserDict
from dataclasses import dataclass
from enum import Enum
from typing import Any, Generator, TypedDict, Union
from typing import TYPE_CHECKING, Any, Generator, KeysView, TypedDict, TypeVar, Union

import sublime

SyntaxLike = Union[str, sublime.Syntax]
WindowId = int
WindowIdAble = Union[WindowId, sublime.Window]

_T = TypeVar("_T")


class ListenerEvent(Enum):
Expand Down Expand Up @@ -76,6 +81,40 @@ class ST_SyntaxRule(ST_MatchRule):
on_events: str | list[str] | None


# `UserDict` is not subscriptable until Python 3.9...
if TYPE_CHECKING:

class WindowKeyedDict(UserDict[WindowIdAble, _T]):
def __setitem__(self, key: WindowIdAble, value: _T) -> None: ...
def __getitem__(self, key: WindowIdAble) -> _T: ...
def __delitem__(self, key: WindowIdAble) -> None: ...
def keys(self) -> KeysView[WindowId]: ...
@staticmethod
def _to_window_id(value: WindowIdAble) -> WindowId: ...

else:

class WindowKeyedDict(UserDict):
def __setitem__(self, key: WindowIdAble, value: _T) -> None:
key = self._to_window_id(key)
super().__setitem__(key, value)

def __getitem__(self, key: WindowIdAble) -> _T:
key = self._to_window_id(key)
return super().__getitem__(key)

def __delitem__(self, key: WindowIdAble) -> None:
key = self._to_window_id(key)
super().__delitem__(key)

def keys(self) -> KeysView[WindowId]:
return super().keys()

@staticmethod
def _to_window_id(value: WindowIdAble) -> WindowId:
return value.id() if isinstance(value, sublime.Window) else value


@dataclass
class SemanticVersion:
major: int
Expand Down

0 comments on commit 8d6a434

Please sign in to comment.