Compare commits

..

No commits in common. "482d4433142b1149497a4f02b32c939be1fb5e5b" and "96f21845ddcf8585b332465257074efbaf39dd24" have entirely different histories.

6 changed files with 39 additions and 172 deletions

View File

@ -1,15 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pdf_data import get_page_pdf from pdf_data import get_metadata_pdf
from datatypes import * from datatypes import *
from enum import Enum, auto from enum import Enum, auto
from os import environ
from urllib.parse import urlencode
import subprocess import subprocess
import pydbus import pydbus
import argparse
import formatter.typst as typst_fmt
parser = argparse.ArgumentParser()
parser.add_argument("--section", help="Copy reference to the section title instead of the page number.", action="store_true")
class LinkFormat(Enum): class LinkFormat(Enum):
@ -23,11 +20,33 @@ def clip_copy(txt: str):
raise Exception("Please install `xsel`.") from e raise Exception("Please install `xsel`.") from e
def format_typst(ref: Reference) -> str:
path_str = environ.get("TYPST_ROOT", None)
if path_str is None:
raise KeyError("Please set TYPST_ROOT to format links with Typst.")
typst_root = Path(path_str)
relative: bool = ref.filepath.is_relative_to(typst_root)
format_path: str
if relative:
format_path = "/" + str(ref.filepath.relative_to(typst_root))
else:
format_path = str(ref.filepath.absolute())
params = dict(page=ref.page)
if relative:
return f'#lref("{format_path}?{urlencode(params)}", pdfref: true)[]'
else:
return f'#link("pdfref://{format_path}?{urlencode(params)}")[]'
def copy_ref(ref: Reference, format: LinkFormat) -> None: def copy_ref(ref: Reference, format: LinkFormat) -> None:
"""Formats Reference and copies it to clipboard.""" """Formats Reference and copies it to clipboard."""
match format: match format:
case LinkFormat.TYPST: case LinkFormat.TYPST:
link_txt = typst_fmt.ref(ref) link_txt = format_typst(ref)
clip_copy(link_txt) clip_copy(link_txt)
@ -40,12 +59,7 @@ def notify(title:str, txt: str) -> None:
if __name__ == "__main__": if __name__ == "__main__":
args = parser.parse_args() ref = get_metadata_pdf()
if args.section:
raise NotImplementedError("--section isn't implemented")
ref = get_page_pdf()
format = LinkFormat.TYPST format = LinkFormat.TYPST
copy_ref(ref, format) copy_ref(ref, format)

View File

@ -1,4 +1,4 @@
from typing import NewType, Union from typing import NewType
from pathlib import Path from pathlib import Path
from dataclasses import dataclass from dataclasses import dataclass
@ -6,60 +6,12 @@ from dataclasses import dataclass
WindowId = NewType("WindowId", int) WindowId = NewType("WindowId", int)
# PID int # PID int
ProcessId = NewType("ProcessId", int) ProcessId = NewType("ProcessId", int)
# page number
@dataclass
class _Reference:
"""Reference to a location within a file."""
pass
@dataclass
class _PDFReference(_Reference):
"""Reference to a location within a PDF file.
Attributes
----------
filepath
Path of the relevant PDF file.
"""
filepath: Path
PageNumber = NewType("PageNumber", int) PageNumber = NewType("PageNumber", int)
# reference to a specific page in a specific pdf
@dataclass @dataclass
class PDFPage(_PDFReference): class Reference:
"""Reference to a specific page in a PDF. filepath: Path
Attributes
----------
page
Page number.
"""
page: PageNumber page: PageNumber
SectionTitle = NewType("SectionTitle", str)
@dataclass
class PDFSection(_PDFReference):
"""Reference to a specific section title in a PDF.
Attributes
----------
title
Section title.
"""
title: SectionTitle
PDFReference = Union[PDFPage, PDFSection]
# for now no other format is implemented
# replace this with an union if that happens
Reference = PDFReference

View File

View File

@ -1,41 +0,0 @@
from os import environ
from urllib.parse import urlencode
from datatypes import PDFPage, PDFSection, PDFReference, Reference
from typing import assert_never
from pathlib import Path
def format_pdf_link(ref: PDFReference) -> str:
path_str = environ.get("TYPST_ROOT", None)
if path_str is None:
raise KeyError("Please set TYPST_ROOT to format links with Typst.")
typst_root = Path(path_str)
relative: bool = ref.filepath.is_relative_to(typst_root)
format_path: str
if relative:
format_path = "/" + str(ref.filepath.relative_to(typst_root))
else:
format_path = str(ref.filepath.absolute())
params = {}
match ref:
case PDFPage():
params["page"] = ref.page
case PDFSection():
params["section"] = ref.title
case _ as obj:
assert_never(obj)
if relative:
return f'#lref("{format_path}?{urlencode(params)}", pdfref: true)[]'
else:
return f'#link("pdfref://{format_path}?{urlencode(params)}")[]'
def ref(ref: Reference) -> str:
"""Formats a Reference."""
# for now no other types are implemented
# replace this with a match/case when that happens
return format_pdf_link(ref)

View File

@ -4,12 +4,12 @@ import pydbus
import subprocess import subprocess
def get_page_pdf() -> PDFPage: def get_metadata_pdf() -> Reference:
"""Find current page of focused PDF reader window. """Find current page of focused PDF reader window.
Returns Returns
------- -------
`PDFPage` reference to the current page. `Reference` to the current page, or None if not found.
""" """
try: try:
res = subprocess.run( res = subprocess.run(
@ -44,16 +44,16 @@ def get_page_pdf() -> PDFPage:
match wm_class[0]: match wm_class[0]:
case "Zathura": case "Zathura":
return get_page_zathura(pid) return get_metadata_zathura(pid)
case "org.pwmt.zathura": case "org.pwmt.zathura":
return get_page_zathura(pid) return get_metadata_zathura(pid)
case _: case _:
raise Exception( raise Exception(
f"Can not retrieve pdf data from this type of window {wm_class}." f"Can not retrieve pdf data from this type of window {wm_class}."
) )
def get_page_zathura(pid: ProcessId) -> PDFPage: def get_metadata_zathura(pid: ProcessId) -> Reference:
"""Given the PID of a Zathura instance, find which page of which file it's on. """Given the PID of a Zathura instance, find which page of which file it's on.
Parameters Parameters
@ -63,7 +63,7 @@ def get_page_zathura(pid: ProcessId) -> PDFPage:
Returns Returns
------- -------
`PDFPage` that the Zathura instance is currently on. `Reference` that the Zathura instance is currently on
""" """
bus = pydbus.SessionBus() bus = pydbus.SessionBus()
@ -73,4 +73,4 @@ def get_page_zathura(pid: ProcessId) -> PDFPage:
# zathura returns 0-indexed pages # zathura returns 0-indexed pages
pagenumber: PageNumber = obj.pagenumber + 1 pagenumber: PageNumber = obj.pagenumber + 1
return PDFPage(filepath=Path(filename), page=pagenumber) return Reference(filepath=Path(filename), page=pagenumber)

58
util.py
View File

@ -1,58 +0,0 @@
import subprocess
from dataclasses import dataclass
from typing import Optional
@dataclass
class RofiResult:
"""Data returned from Rofi.
Attributes
----------
index
Index within entries of the selected entry.
`None` if nothing was selected.
value
Selected entry's string value.
`None` if the value is not in the list or nothing was selected.
custom_bind
ID of custom bind used to select entry. None if no custom bind was used.
"""
index: Optional[int]
value: str
custom_bind: Optional[int]
def rofi(entries: list[str], prompt: str="> ", fuzzy=True, extra_args=[]) -> Optional[RofiResult]:
"""Start a Rofi prompt.
Returns
-------
None if the prompt was cancelled, or a `RofiResult`.
"""
args = ["rofi", "-dmenu", "-sep", "\\0"]
args += ["-p", prompt]
if fuzzy:
args += ["-matching", "fuzzy"]
args += extra_args
ret = RofiResult(None, "", None)
res = subprocess.run(args, input="\0".join(entries), stdout=subprocess.PIPE, text=True)
match res.returncode:
case 0:
pass
case 1:
return None
case x if x >= 10 and x <= 28:
ret.custom_bind = x - 9
case _ as retc:
raise RuntimeError(f"Rofi returned an unexpected return code `{retc}`.")
ret.value = res.stdout.strip()
try:
ret.index = entries.index(ret.value)
except ValueError:
pass
return ret