Compare commits
No commits in common. "482d4433142b1149497a4f02b32c939be1fb5e5b" and "96f21845ddcf8585b332465257074efbaf39dd24" have entirely different histories.
482d443314
...
96f21845dd
40
copy_ref
40
copy_ref
@ -1,15 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pdf_data import get_page_pdf
|
||||
from pdf_data import get_metadata_pdf
|
||||
from datatypes import *
|
||||
from enum import Enum, auto
|
||||
from os import environ
|
||||
from urllib.parse import urlencode
|
||||
import subprocess
|
||||
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):
|
||||
@ -23,11 +20,33 @@ def clip_copy(txt: str):
|
||||
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:
|
||||
"""Formats Reference and copies it to clipboard."""
|
||||
match format:
|
||||
case LinkFormat.TYPST:
|
||||
link_txt = typst_fmt.ref(ref)
|
||||
link_txt = format_typst(ref)
|
||||
|
||||
clip_copy(link_txt)
|
||||
|
||||
@ -40,12 +59,7 @@ def notify(title:str, txt: str) -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.section:
|
||||
raise NotImplementedError("--section isn't implemented")
|
||||
|
||||
ref = get_page_pdf()
|
||||
ref = get_metadata_pdf()
|
||||
|
||||
format = LinkFormat.TYPST
|
||||
copy_ref(ref, format)
|
||||
|
58
datatypes.py
58
datatypes.py
@ -1,4 +1,4 @@
|
||||
from typing import NewType, Union
|
||||
from typing import NewType
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
@ -6,60 +6,12 @@ from dataclasses import dataclass
|
||||
WindowId = NewType("WindowId", int)
|
||||
# PID int
|
||||
ProcessId = NewType("ProcessId", int)
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
||||
# page number
|
||||
PageNumber = NewType("PageNumber", int)
|
||||
|
||||
|
||||
# reference to a specific page in a specific pdf
|
||||
@dataclass
|
||||
class PDFPage(_PDFReference):
|
||||
"""Reference to a specific page in a PDF.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
page
|
||||
Page number.
|
||||
"""
|
||||
|
||||
class Reference:
|
||||
filepath: Path
|
||||
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
|
||||
|
@ -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)
|
14
pdf_data.py
14
pdf_data.py
@ -4,12 +4,12 @@ import pydbus
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_page_pdf() -> PDFPage:
|
||||
def get_metadata_pdf() -> Reference:
|
||||
"""Find current page of focused PDF reader window.
|
||||
|
||||
Returns
|
||||
-------
|
||||
`PDFPage` reference to the current page.
|
||||
`Reference` to the current page, or None if not found.
|
||||
"""
|
||||
try:
|
||||
res = subprocess.run(
|
||||
@ -44,16 +44,16 @@ def get_page_pdf() -> PDFPage:
|
||||
|
||||
match wm_class[0]:
|
||||
case "Zathura":
|
||||
return get_page_zathura(pid)
|
||||
return get_metadata_zathura(pid)
|
||||
case "org.pwmt.zathura":
|
||||
return get_page_zathura(pid)
|
||||
return get_metadata_zathura(pid)
|
||||
case _:
|
||||
raise Exception(
|
||||
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.
|
||||
|
||||
Parameters
|
||||
@ -63,7 +63,7 @@ def get_page_zathura(pid: ProcessId) -> PDFPage:
|
||||
|
||||
Returns
|
||||
-------
|
||||
`PDFPage` that the Zathura instance is currently on.
|
||||
`Reference` that the Zathura instance is currently on
|
||||
"""
|
||||
|
||||
bus = pydbus.SessionBus()
|
||||
@ -73,4 +73,4 @@ def get_page_zathura(pid: ProcessId) -> PDFPage:
|
||||
# zathura returns 0-indexed pages
|
||||
pagenumber: PageNumber = obj.pagenumber + 1
|
||||
|
||||
return PDFPage(filepath=Path(filename), page=pagenumber)
|
||||
return Reference(filepath=Path(filename), page=pagenumber)
|
||||
|
58
util.py
58
util.py
@ -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
|
Loading…
x
Reference in New Issue
Block a user