Skip to content

Commit

Permalink
feat: also works on init views
Browse files Browse the repository at this point in the history
Useful when opening a file from the context menu with ST hasn't opened
yet.

Signed-off-by: Jack Cherng <jfcherng@gmail.com>
  • Loading branch information
jfcherng committed Sep 17, 2024
1 parent 4f74798 commit 2afa90f
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 69 deletions.
3 changes: 2 additions & 1 deletion plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# import all listeners and commands
from .listener import FollowLnkViewEventListener
from .listener import FollowLnkEventListener, FollowLnkViewEventListener

__all__ = (
# ST: core
"plugin_loaded",
"plugin_unloaded",
# ST: listeners
"FollowLnkEventListener",
"FollowLnkViewEventListener",
)

Expand Down
146 changes: 78 additions & 68 deletions plugin/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,83 +13,93 @@
from .libs.LnkParse3.lnk_file import LnkFile


class FollowLnkEventListener(sublime_plugin.EventListener):
@classmethod
def is_applicable(cls, settings: sublime.Settings) -> bool:
return sublime.platform() == "windows"

def on_init(self, views: list[sublime.View]) -> None:
for view in views:
_handle_view(view)


class FollowLnkViewEventListener(sublime_plugin.ViewEventListener):
@classmethod
def is_applicable(cls, settings: sublime.Settings) -> bool:
return sublime.platform() == "windows"

def on_load(self) -> None:
if not (
(window := self.view.window())
and (path := self.view.file_name())
and (target_path := self._resolve_lnk(path))
):
return

if os.path.isfile(target_path):
view_group = window.get_view_index(self.view)[0]
self.view.close()
window.open_file(target_path, 0, view_group)
return

if os.path.isdir(target_path):
project = window.project_data() or {}
project.setdefault("folders", [])
if target_path not in project["folders"]:
project["folders"].append({"path": target_path})
window.set_project_data(project)
self.view.close()
return

raise RuntimeError(f"[{PLUGIN_NAME}] Uh, what's this: {path = }; {target_path = }")
_handle_view(self.view)

@classmethod
def _resolve_lnk(cls, path: str) -> str | None:
if not (lnk_file := cls._make_lnk_file(path)):
return None

# print(f"[DEBUG] {lnk_file.get_json() = }")
def _handle_view(view: sublime.View) -> None:
if not ((window := view.window()) and (path := view.file_name()) and (target_path := _resolve_lnk(path))):
return

if os.path.isfile(target_path):
view_group = window.get_view_index(view)[0]
view.close()
window.open_file(target_path, 0, view_group)
return

if os.path.isdir(target_path):
project = window.project_data() or {}
project.setdefault("folders", [])
if target_path not in project["folders"]:
project["folders"].append({"path": target_path})
window.set_project_data(project)
view.close()
return

raise RuntimeError(f"[{PLUGIN_NAME}] Uh, what's this: {path = }; {target_path = }")


def _resolve_lnk(path: str) -> str | None:
if not (lnk_file := _make_lnk_file(path)):
return None

# print(f"[DEBUG] {lnk_file.get_json() = }")

try:
return first_true(_list_lnk_targets(lnk_file, lnk_path=path))
except Exception:
return None


def _list_lnk_targets(lnk_file: LnkFile, *, lnk_path: str | None = None) -> Generator[str, None, None]:
string_data_dict = lnk_file.string_data.as_dict()

# from property: relative path
if lnk_path and lnk_file.has_relative_path():
path = Path(lnk_path).parent / string_data_dict["relative_path"]
yield str(path.resolve())

# from metadata
for extra in lnk_file.extras:
if isinstance(extra, Metadata):
for store in extra.property_store():
assert isinstance(store, SerializedPropertyStorage)
if store.format_id() == "28636AA6-953D-11D2-B5D6-00C04FD918D0":
for prop in store.serialized_property_values():
if isinstance(prop, SerializedPropertyValueIntegerName) and prop.id() == 30:
yield prop.value().value() # type: ignore

# from info
if lnk_file.info:
try:
if target := lnk_file.info.local_base_path_unicode():
yield target
except Exception:
pass
try:
return first_true(cls._list_lnk_targets(lnk_file, lnk_path=path))
if target := lnk_file.info.local_base_path():
yield target
except Exception:
return None
pass

@classmethod
def _list_lnk_targets(cls, lnk_file: LnkFile, *, lnk_path: str | None = None) -> Generator[str, None, None]:
string_data_dict = lnk_file.string_data.as_dict()

# from property: relative path
if lnk_path and lnk_file.has_relative_path():
path = Path(lnk_path).parent / string_data_dict["relative_path"]
yield str(path.resolve())

# from metadata
for extra in lnk_file.extras:
if isinstance(extra, Metadata):
for store in extra.property_store():
assert isinstance(store, SerializedPropertyStorage)
if store.format_id() == "28636AA6-953D-11D2-B5D6-00C04FD918D0":
for prop in store.serialized_property_values():
if isinstance(prop, SerializedPropertyValueIntegerName) and prop.id() == 30:
yield prop.value().value() # type: ignore

# from info
if lnk_file.info:
try:
if target := lnk_file.info.local_base_path_unicode():
yield target
except Exception:
pass
try:
if target := lnk_file.info.local_base_path():
yield target
except Exception:
pass

@staticmethod
def _make_lnk_file(path: str) -> LnkFile | None:
if path.lower().endswith(".lnk"):
with open(path, "rb") as indata:
return LnkFile(indata)
return None

def _make_lnk_file(path: str) -> LnkFile | None:
if path.lower().endswith(".lnk"):
with open(path, "rb") as indata:
return LnkFile(indata)
return None

0 comments on commit 2afa90f

Please sign in to comment.