Merge pull request #4 from arne314/dev

V1.1.1: Anki fixes and optimization
This commit is contained in:
arne314
2025-01-03 17:23:47 +01:00
committed by GitHub
8 changed files with 178 additions and 166 deletions

View File

@@ -4,7 +4,7 @@ build-backend = "pdm.backend"
[project] [project]
name = "typstar" name = "typstar"
version = "1.1.0" version = "1.1.1"
description = "Neovim plugin for efficient note taking in Typst" description = "Neovim plugin for efficient note taking in Typst"
authors = [ authors = [
{ name = "arne314" } { name = "arne314" }
@@ -14,10 +14,20 @@ requires-python = ">=3.11.10"
dependencies = [ dependencies = [
"aiohttp>=3.11.11", "aiohttp>=3.11.11",
"appdirs>=1.4.4", "appdirs>=1.4.4",
"tree-sitter-language-pack>=0.2.0", "tree-sitter>=0.23.2",
"typer>=0.15.1", "typer>=0.15.1",
"tree-sitter-typst @ git+https://github.com/uben0/tree-sitter-typst"
] ]
[project.scripts] [project.scripts]
typstar-anki = "anki.main:main" typstar-anki = "anki.main:main"
[tool.ruff]
lint.extend-select = ["I"]
line-length = 100
[dependency-groups]
dev = [
"ruff>=0.8.5",
]

View File

@@ -42,11 +42,17 @@ class AnkiConnectApi:
update[card.deck].append(card) update[card.deck].append(card)
n_update += 1 n_update += 1
print(f"Pushing {n_add} new flashcards and {n_update} updated flashcards to Anki...", flush=True) print(
f"Pushing {n_add} new flashcards and {n_update} updated flashcards to Anki...",
flush=True,
)
await self._create_required_decks({*add.keys(), *update.keys()}) await self._create_required_decks({*add.keys(), *update.keys()})
await self._add_new_cards(add) await self._add_new_cards(add)
await _gather_exceptions( await _gather_exceptions(
[*self._update_cards_requests(add), *self._update_cards_requests(update, True)] [
*self._update_cards_requests(add),
*self._update_cards_requests(update, True),
]
) )
async def _request_api(self, action, **params): async def _request_api(self, action, **params):
@@ -71,12 +77,16 @@ class AnkiConnectApi:
await self._request_api("updateNoteModel", note=card.as_anki_model()) await self._request_api("updateNoteModel", note=card.as_anki_model())
async def _store_media(self, card): async def _store_media(self, card):
await self._request_api("storeMediaFile", await self._request_api(
filename=card.svg_filename(True), "storeMediaFile",
data=base64.b64encode(card.svg_front).decode()) filename=card.svg_filename(True),
await self._request_api("storeMediaFile", data=base64.b64encode(card.svg_front).decode(),
filename=card.svg_filename(False), )
data=base64.b64encode(card.svg_back).decode()) await self._request_api(
"storeMediaFile",
filename=card.svg_filename(False),
data=base64.b64encode(card.svg_back).decode(),
)
async def _change_deck(self, deck: str, cards: List[int]): async def _change_deck(self, deck: str, cards: List[int]):
await self._request_api("changeDeck", deck=deck, cards=cards) await self._request_api("changeDeck", deck=deck, cards=cards)
@@ -107,7 +117,9 @@ class AnkiConnectApi:
requests.append(self._request_api("createDeck", deck=deck)) requests.append(self._request_api("createDeck", deck=deck))
await _gather_exceptions(requests) await _gather_exceptions(requests)
def _update_cards_requests(self, cards_map: dict[str, List[Flashcard]], update_deck: bool = True): def _update_cards_requests(
self, cards_map: dict[str, List[Flashcard]], update_deck: bool = True
):
requests = [] requests = []
for deck, cards in cards_map.items(): for deck, cards in cards_map.items():
card_ids = [] card_ids = []

View File

@@ -1,5 +1,4 @@
import hashlib import hashlib
from pathlib import Path from pathlib import Path
from typing import List from typing import List
@@ -8,7 +7,7 @@ import tree_sitter
class FileHandler: class FileHandler:
file_path: Path file_path: Path
file_content: List[str] file_content: List[bytes]
def __init__(self, path: Path): def __init__(self, path: Path):
self.file_path = path self.file_path = path
@@ -19,32 +18,39 @@ class FileHandler:
return self.file_path.parent return self.file_path.parent
def get_bytes(self) -> bytes: def get_bytes(self) -> bytes:
return bytes("".join(self.file_content), encoding="utf-8") return b"".join(self.file_content)
def get_file_hash(self) -> str: def get_file_hash(self) -> str:
return hashlib.md5("".join(self.file_content).encode(), usedforsecurity=False).hexdigest() return hashlib.md5(self.get_bytes(), usedforsecurity=False).hexdigest()
def get_node_content(self, node: tree_sitter.Node, remove_outer=False): def get_node_content(self, node: tree_sitter.Node, remove_outer=False) -> str:
content = "".join( content = (
self.file_content[node.start_point.row:node.end_point.row + 1] b"".join(self.file_content[node.start_point.row : node.end_point.row + 1])[
)[node.start_point.column:-(len(self.file_content[node.end_point.row]) - node.end_point.column)] node.start_point.column : -(
len(self.file_content[node.end_point.row]) - node.end_point.column
)
]
).decode()
return content[1:-1] if remove_outer else content return content[1:-1] if remove_outer else content
def update_node_content(self, node: tree_sitter.Node, value): def update_node_content(self, node: tree_sitter.Node, value):
new_lines = self.file_content[:node.start_point.row] new_lines = self.file_content[: node.start_point.row]
first_line = self.file_content[node.start_point.row][:node.start_point.column] first_line = self.file_content[node.start_point.row][: node.start_point.column]
last_line = self.file_content[node.end_point.row][node.end_point.column:] last_line = self.file_content[node.end_point.row][node.end_point.column :]
new_lines.extend(( new_lines.extend(
line + "\n" for line in (first_line + str(value) + last_line).split("\n") (
if line != "" line + b"\n"
)) for line in (first_line + str(value).encode() + last_line).split(b"\n")
new_lines.extend(self.file_content[node.end_point.row + 1:]) if line != b""
)
)
new_lines.extend(self.file_content[node.end_point.row + 1 :])
self.file_content = new_lines self.file_content = new_lines
def read(self): def read(self):
with self.file_path.open(encoding="utf-8") as f: with self.file_path.open("rb") as f:
self.file_content = f.readlines() self.file_content = f.readlines()
def write(self): def write(self):
with self.file_path.open("w", encoding="utf-8") as f: with self.file_path.open("wb") as f:
f.writelines(self.file_content) f.writelines(self.file_content)

View File

@@ -10,7 +10,7 @@ class Flashcard:
deck: str deck: str
id_updated: bool id_updated: bool
preamble: str preamble: str | None
file_handler: FileHandler file_handler: FileHandler
note_id_node: tree_sitter.Node note_id_node: tree_sitter.Node
@@ -20,7 +20,15 @@ class Flashcard:
svg_front: bytes svg_front: bytes
svg_back: bytes svg_back: bytes
def __init__(self, front: str, back: str, deck: str | None, note_id: int, preamble: str, file_handler: FileHandler): def __init__(
self,
front: str,
back: str,
deck: str | None,
note_id: int,
preamble: str | None,
file_handler: FileHandler,
):
if deck is None: if deck is None:
deck = "Default" deck = "Default"
if not note_id: if not note_id:
@@ -51,7 +59,7 @@ class Flashcard:
"Front": f"tmp typst: {self.front}" if tmp else self.as_html(True), "Front": f"tmp typst: {self.front}" if tmp else self.as_html(True),
"Back": f"tmp typst: {self.back}" if tmp else self.as_html(False), "Back": f"tmp typst: {self.back}" if tmp else self.as_html(False),
}, },
"tags": ["typst"] "tags": ["typst"],
} }
if not self.is_new(): if not self.is_new():
model["id"] = self.note_id model["id"] = self.note_id
@@ -63,7 +71,9 @@ class Flashcard:
def is_new(self) -> bool: def is_new(self) -> bool:
return self.note_id == 0 or self.note_id is None return self.note_id == 0 or self.note_id is None
def set_ts_nodes(self, front: tree_sitter.Node, back: tree_sitter.Node, note_id: tree_sitter.Node): def set_ts_nodes(
self, front: tree_sitter.Node, back: tree_sitter.Node, note_id: tree_sitter.Node
):
self.front_node = front self.front_node = front
self.back_node = back self.back_node = back
self.note_id_node = note_id self.note_id_node = note_id

View File

@@ -36,16 +36,33 @@ async def export_flashcards(root_dir, force_scan, clear_cache, typst_cmd, anki_u
@cli.command() @cli.command()
def cmd(root_dir: Annotated[ def cmd(
Path, typer.Option(help="Directory scanned for flashcards and passed over to typst compile command")] = os.getcwd(), root_dir: Annotated[
force_scan: Annotated[Path, typer.Option(help="File/directory to scan for flashcards while ignoring stored " Path,
"file hashes (e.g. on preamble change)")] = None, typer.Option(
clear_cache: Annotated[bool, typer.Option(help="Clear all stored file hashes (more aggressive than force-scan " help="Directory scanned for flashcards and passed over to typst compile command"
"as it clears hashes regardless of their path)")] = False, ),
typst_cmd: Annotated[str, typer.Option(help="Typst command used for flashcard compilation")] = "typst", ] = Path(os.getcwd()),
anki_url: Annotated[str, typer.Option(help="Url for Anki-Connect")] = "http://127.0.0.1:8765", force_scan: Annotated[
anki_key: Annotated[str, typer.Option(help="Api key for Anki-Connect")] = None, Path | None,
): typer.Option(
help="File/directory to scan for flashcards while ignoring stored "
"file hashes (e.g. on preamble change)"
),
] = None,
clear_cache: Annotated[
bool,
typer.Option(
help="Clear all stored file hashes (more aggressive than force-scan "
"as it clears hashes regardless of their path)"
),
] = False,
typst_cmd: Annotated[
str, typer.Option(help="Typst command used for flashcard compilation")
] = "typst",
anki_url: Annotated[str, typer.Option(help="Url for Anki-Connect")] = "http://127.0.0.1:8765",
anki_key: Annotated[str | None, typer.Option(help="Api key for Anki-Connect")] = None,
):
asyncio.run(export_flashcards(root_dir, force_scan, clear_cache, typst_cmd, anki_url, anki_key)) asyncio.run(export_flashcards(root_dir, force_scan, clear_cache, typst_cmd, anki_url, anki_key))

View File

@@ -1,14 +1,13 @@
import glob import glob
import json import json
import re import re
from functools import cache from functools import cache
from pathlib import Path from pathlib import Path
from typing import List, Tuple from typing import List, Tuple
import appdirs import appdirs
import tree_sitter import tree_sitter
from tree_sitter_language_pack import get_language, get_parser from tree_sitter_typst import language as get_typst_language
from .file_handler import FileHandler from .file_handler import FileHandler
from .flashcard import Flashcard from .flashcard import Flashcard
@@ -36,7 +35,7 @@ ts_flashcard_query = """
ts_deck_query = """ ts_deck_query = """
((comment) @deck) ((comment) @deck)
""" """
deck_regex = re.compile(r"\W+ANKI:\s*(\S*)") deck_regex = re.compile(r"\W+ANKI:\s*([\S ]*)")
class FlashcardParser: class FlashcardParser:
@@ -50,14 +49,14 @@ class FlashcardParser:
file_hashes_store_path: Path = Path(appdirs.user_state_dir("typstar") + "/file_hashes.json") file_hashes_store_path: Path = Path(appdirs.user_state_dir("typstar") + "/file_hashes.json")
def __init__(self): def __init__(self):
self.typst_language = get_language("typst") self.typst_language = tree_sitter.Language(get_typst_language())
self.typst_parser = get_parser("typst") self.typst_parser = tree_sitter.Parser(self.typst_language)
self.flashcard_query = self.typst_language.query(ts_flashcard_query) self.flashcard_query = self.typst_language.query(ts_flashcard_query)
self.deck_query = self.typst_language.query(ts_deck_query) self.deck_query = self.typst_language.query(ts_deck_query)
self.file_handlers = [] self.file_handlers = []
self._load_file_hashes() self._load_file_hashes()
def _parse_file(self, file: FileHandler, preamble: str) -> List[Flashcard]: def _parse_file(self, file: FileHandler, preamble: str | None) -> List[Flashcard]:
cards = [] cards = []
tree = self.typst_parser.parse(file.get_bytes(), encoding="utf8") tree = self.typst_parser.parse(file.get_bytes(), encoding="utf8")
card_captures = self.flashcard_query.captures(tree.root_node) card_captures = self.flashcard_query.captures(tree.root_node)
@@ -72,17 +71,27 @@ class FlashcardParser:
card_captures["front"].sort(key=row_compare) card_captures["front"].sort(key=row_compare)
card_captures["back"].sort(key=row_compare) card_captures["back"].sort(key=row_compare)
deck_refs: List[Tuple[int, str]] = [] deck_refs: List[Tuple[int, str | None]] = []
deck_refs_idx = -1 deck_refs_idx = -1
current_deck = None current_deck = None
if deck_captures: if deck_captures:
deck_captures["deck"].sort(key=row_compare) deck_captures["deck"].sort(key=row_compare)
for comment in deck_captures["deck"]: for comment in deck_captures["deck"]:
if match := deck_regex.match(file.get_node_content(comment)): if match := deck_regex.match(file.get_node_content(comment)):
deck_refs.append((comment.start_point.row, None if match[1].isspace() else match[1])) deck_refs.append(
(
comment.start_point.row,
None if match[1].isspace() else match[1],
)
)
for note_id, front, back in zip(card_captures["id"], card_captures["front"], card_captures["back"]): for note_id, front, back in zip(
while deck_refs_idx < len(deck_refs) - 1 and back.end_point.row >= deck_refs[deck_refs_idx + 1][0]: card_captures["id"], card_captures["front"], card_captures["back"]
):
while (
deck_refs_idx < len(deck_refs) - 1
and back.end_point.row >= deck_refs[deck_refs_idx + 1][0]
):
deck_refs_idx += 1 deck_refs_idx += 1
current_deck = deck_refs[deck_refs_idx][1] current_deck = deck_refs[deck_refs_idx][1]
@@ -98,7 +107,7 @@ class FlashcardParser:
cards.append(card) cards.append(card)
return cards return cards
def parse_directory(self, root_dir: Path, force_scan: Path = None): def parse_directory(self, root_dir: Path, force_scan: Path | None = None):
single_file = None single_file = None
is_force_scan = force_scan is not None is_force_scan = force_scan is not None
if is_force_scan: if is_force_scan:
@@ -110,7 +119,10 @@ class FlashcardParser:
else: else:
scan_dir = root_dir scan_dir = root_dir
print(f"Parsing flashcards in {scan_dir if single_file is None else single_file} ...", flush=True) print(
f"Parsing flashcards in {scan_dir if single_file is None else single_file} ...",
flush=True,
)
preambles = {} preambles = {}
flashcards = [] flashcards = []
@@ -121,12 +133,12 @@ class FlashcardParser:
return preamble return preamble
path = path.parent path = path.parent
for file in sorted(glob.glob(f"{root_dir}/**/.anki.typ", include_hidden=True, recursive=True)): for file in glob.glob(f"{root_dir}/**/.anki.typ", include_hidden=True, recursive=True):
file = Path(file) file = Path(file)
if file.name == ".anki.typ": if file.name == ".anki.typ":
preambles[file.parent] = file.read_text(encoding="utf-8") preambles[file.parent] = file.read_text(encoding="utf-8")
for file in sorted(glob.glob(f"{scan_dir}/**/**.typ", recursive=True)): for file in glob.glob(f"{scan_dir}/**/**.typ", recursive=True):
file = Path(file) file = Path(file)
if single_file is not None and file != single_file: if single_file is not None and file != single_file:
continue continue

View File

@@ -31,7 +31,11 @@ class TypstCompiler:
def __init__(self, typst_root_dir: Path, typst_cmd: str): def __init__(self, typst_root_dir: Path, typst_cmd: str):
self.typst_cmd = typst_cmd self.typst_cmd = typst_cmd
self.typst_root_dir = typst_root_dir self.typst_root_dir = typst_root_dir
self.max_processes = round(os.cpu_count() * 1.5) n_cpus = os.cpu_count()
if n_cpus is None:
self.max_processes = 10
else:
self.max_processes = round(1.5 * n_cpus)
async def _compile(self, src: str, directory: Path) -> bytes: async def _compile(self, src: str, directory: Path) -> bytes:
tmp_path = f"{directory}/tmp_{random.randint(1, 1000000000)}.typ" tmp_path = f"{directory}/tmp_{random.randint(1, 1000000000)}.typ"
@@ -50,8 +54,12 @@ class TypstCompiler:
async def _compile_flashcard(self, card: Flashcard): async def _compile_flashcard(self, card: Flashcard):
preamble = default_preamble if card.preamble is None else card.preamble preamble = default_preamble if card.preamble is None else card.preamble
front = await self._compile(preamble + "\n" + card.as_typst(True), card.file_handler.directory_path) front = await self._compile(
back = await self._compile(preamble + "\n" + card.as_typst(False), card.file_handler.directory_path) preamble + "\n" + card.as_typst(True), card.file_handler.directory_path
)
back = await self._compile(
preamble + "\n" + card.as_typst(False), card.file_handler.directory_path
)
card.set_svgs(front, back) card.set_svgs(front, back)
async def compile_flashcards(self, cards: List[Flashcard]): async def compile_flashcards(self, cards: List[Flashcard]):
@@ -62,7 +70,9 @@ class TypstCompiler:
async with semaphore: async with semaphore:
return await self._compile_flashcard(card) return await self._compile_flashcard(card)
results = await asyncio.gather(*(compile_coro(card) for card in cards), return_exceptions=True) results = await asyncio.gather(
*(compile_coro(card) for card in cards), return_exceptions=True
)
for result in results: for result in results:
if isinstance(result, Exception): if isinstance(result, Exception):
raise result raise result

145
uv.lock generated
View File

@@ -340,6 +340,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
] ]
[[package]]
name = "ruff"
version = "0.8.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/25/5d/4b5403f3e89837decfd54c51bea7f94b7d3fae77e08858603d0e04d7ad17/ruff-0.8.5.tar.gz", hash = "sha256:1098d36f69831f7ff2a1da3e6407d5fbd6dfa2559e4f74ff2d260c5588900317", size = 3454835 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/73/f8/03391745a703ce11678eb37c48ae89ec60396ea821e9d0bcea7c8e88fd91/ruff-0.8.5-py3-none-linux_armv6l.whl", hash = "sha256:5ad11a5e3868a73ca1fa4727fe7e33735ea78b416313f4368c504dbeb69c0f88", size = 10626889 },
{ url = "https://files.pythonhosted.org/packages/55/74/83bb74a44183b904216f3edfb9995b89830c83aaa6ce84627f74da0e0cf8/ruff-0.8.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f69ab37771ea7e0715fead8624ec42996d101269a96e31f4d31be6fc33aa19b7", size = 10398233 },
{ url = "https://files.pythonhosted.org/packages/e8/7a/a162a4feb3ef85d594527165e366dde09d7a1e534186ff4ba5d127eda850/ruff-0.8.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b5462d7804558ccff9c08fe8cbf6c14b7efe67404316696a2dde48297b1925bb", size = 10001843 },
{ url = "https://files.pythonhosted.org/packages/e7/9f/5ee5dcd135411402e35b6ec6a8dfdadbd31c5cd1c36a624d356a38d76090/ruff-0.8.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d56de7220a35607f9fe59f8a6d018e14504f7b71d784d980835e20fc0611cd50", size = 10872507 },
{ url = "https://files.pythonhosted.org/packages/b6/67/db2df2dd4a34b602d7f6ebb1b3744c8157f0d3579973ffc58309c9c272e8/ruff-0.8.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9d99cf80b0429cbebf31cbbf6f24f05a29706f0437c40413d950e67e2d4faca4", size = 10377200 },
{ url = "https://files.pythonhosted.org/packages/fe/ff/fe3a6a73006bced73e60d171d154a82430f61d97e787f511a24bd6302611/ruff-0.8.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b75ac29715ac60d554a049dbb0ef3b55259076181c3369d79466cb130eb5afd", size = 11433155 },
{ url = "https://files.pythonhosted.org/packages/e3/95/c1d1a1fe36658c1f3e1b47e1cd5f688b72d5786695b9e621c2c38399a95e/ruff-0.8.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c9d526a62c9eda211b38463528768fd0ada25dad524cb33c0e99fcff1c67b5dc", size = 12139227 },
{ url = "https://files.pythonhosted.org/packages/1b/fe/644b70d473a27b5112ac7a3428edcc1ce0db775c301ff11aa146f71886e0/ruff-0.8.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:587c5e95007612c26509f30acc506c874dab4c4abbacd0357400bd1aa799931b", size = 11697941 },
{ url = "https://files.pythonhosted.org/packages/00/39/4f83e517ec173e16a47c6d102cd22a1aaebe80e1208a1f2e83ab9a0e4134/ruff-0.8.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:622b82bf3429ff0e346835ec213aec0a04d9730480cbffbb6ad9372014e31bbd", size = 12967686 },
{ url = "https://files.pythonhosted.org/packages/1a/f6/52a2973ff108d74b5da706a573379eea160bece098f7cfa3f35dc4622710/ruff-0.8.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99be814d77a5dac8a8957104bdd8c359e85c86b0ee0e38dca447cb1095f70fb", size = 11253788 },
{ url = "https://files.pythonhosted.org/packages/ce/1f/3b30f3c65b1303cb8e268ec3b046b77ab21ed8e26921cfc7e8232aa57f2c/ruff-0.8.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01c048f9c3385e0fd7822ad0fd519afb282af9cf1778f3580e540629df89725", size = 10860360 },
{ url = "https://files.pythonhosted.org/packages/a5/a8/2a3ea6bacead963f7aeeba0c61815d9b27b0d638e6a74984aa5cc5d27733/ruff-0.8.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7512e8cb038db7f5db6aae0e24735ff9ea03bb0ed6ae2ce534e9baa23c1dc9ea", size = 10457922 },
{ url = "https://files.pythonhosted.org/packages/17/47/8f9514b670969aab57c5fc826fb500a16aee8feac1bcf8a91358f153a5ba/ruff-0.8.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:762f113232acd5b768d6b875d16aad6b00082add40ec91c927f0673a8ec4ede8", size = 10958347 },
{ url = "https://files.pythonhosted.org/packages/0d/d6/78a9af8209ad99541816d74f01ce678fc01ebb3f37dd7ab8966646dcd92b/ruff-0.8.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:03a90200c5dfff49e4c967b405f27fdfa81594cbb7c5ff5609e42d7fe9680da5", size = 11328882 },
{ url = "https://files.pythonhosted.org/packages/54/77/5c8072ec7afdfdf42c7a4019044486a2b6c85ee73617f8875ec94b977fed/ruff-0.8.5-py3-none-win32.whl", hash = "sha256:8710ffd57bdaa6690cbf6ecff19884b8629ec2a2a2a2f783aa94b1cc795139ed", size = 8802515 },
{ url = "https://files.pythonhosted.org/packages/bc/b6/47d2b06784de8ae992c45cceb2a30f3f205b3236a629d7ca4c0c134839a2/ruff-0.8.5-py3-none-win_amd64.whl", hash = "sha256:4020d8bf8d3a32325c77af452a9976a9ad6455773bcb94991cf15bd66b347e47", size = 9684231 },
{ url = "https://files.pythonhosted.org/packages/bf/5e/ffee22bf9f9e4b2669d1f0179ae8804584939fb6502b51f2401e26b1e028/ruff-0.8.5-py3-none-win_arm64.whl", hash = "sha256:134ae019ef13e1b060ab7136e7828a6d83ea727ba123381307eb37c6bd5e01cb", size = 9124741 },
]
[[package]] [[package]]
name = "shellingham" name = "shellingham"
version = "1.5.4" version = "1.5.4"
@@ -382,109 +407,9 @@ wheels = [
] ]
[[package]] [[package]]
name = "tree-sitter-c-sharp" name = "tree-sitter-typst"
version = "0.23.1" version = "0.0.1"
source = { registry = "https://pypi.org/simple" } source = { git = "https://github.com/uben0/tree-sitter-typst#26dfb4b94a99cc9d6044342b7ad0ba761da77d60" }
sdist = { url = "https://files.pythonhosted.org/packages/22/85/a61c782afbb706a47d990eaee6977e7c2bd013771c5bf5c81c617684f286/tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb", size = 1317728 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/04/f6c2df4c53a588ccd88d50851155945cff8cd887bd70c175e00aaade7edf/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd", size = 372235 },
{ url = "https://files.pythonhosted.org/packages/99/10/1aa9486f1e28fc22810fa92cbdc54e1051e7f5536a5e5b5e9695f609b31e/tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e", size = 419046 },
{ url = "https://files.pythonhosted.org/packages/0f/21/13df29f8fcb9ba9f209b7b413a4764b673dfd58989a0dd67e9c7e19e9c2e/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d", size = 415999 },
{ url = "https://files.pythonhosted.org/packages/ca/72/fc6846795bcdae2f8aa94cc8b1d1af33d634e08be63e294ff0d6794b1efc/tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2", size = 402830 },
{ url = "https://files.pythonhosted.org/packages/fe/3a/b6028c5890ce6653807d5fa88c72232c027c6ceb480dbeb3b186d60e5971/tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7", size = 397880 },
{ url = "https://files.pythonhosted.org/packages/47/d2/4facaa34b40f8104d8751746d0e1cd2ddf0beb9f1404b736b97f372bd1f3/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3", size = 377562 },
{ url = "https://files.pythonhosted.org/packages/d8/88/3cf6bd9959d94d1fec1e6a9c530c5f08ff4115a474f62aedb5fedb0f7241/tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5", size = 375157 },
]
[[package]]
name = "tree-sitter-embedded-template"
version = "0.23.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/28/d6/5a58ea2f0480f5ed188b733114a8c275532a2fd1568b3898793b13d28af5/tree_sitter_embedded_template-0.23.2.tar.gz", hash = "sha256:7b24dcf2e92497f54323e617564d36866230a8bfb719dbb7b45b461510dcddaa", size = 8471 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/c1/be0c48ed9609b720e74ade86f24ea086e353fe9c7405ee9630c3d52d09a2/tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a505c2d2494464029d79db541cab52f6da5fb326bf3d355e69bf98b84eb89ae0", size = 9554 },
{ url = "https://files.pythonhosted.org/packages/6d/a5/7c12f5d302525ee36d1eafc28a68e4454da5bad208436d547326bee4ed76/tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:28028b93b42cc3753261ae7ce066675d407f59de512417524f9c3ab7792b1d37", size = 10051 },
{ url = "https://files.pythonhosted.org/packages/cd/87/95aaba8b64b849200bd7d4ae510cc394ecaef46a031499cbff301766970d/tree_sitter_embedded_template-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec399d59ce93ffb60759a2d96053eed529f3c3f6a27128f261710d0d0de60e10", size = 17532 },
{ url = "https://files.pythonhosted.org/packages/13/f8/8c837b898f00b35f9f3f76a4abc525e80866a69343083c9ff329e17ecb03/tree_sitter_embedded_template-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcfa01f62b88d50dbcb736cc23baec8ddbfe08daacfdc613eee8c04ab65efd09", size = 17394 },
{ url = "https://files.pythonhosted.org/packages/89/9b/893adf9e465d2d7f14870871bf2f3b30045e5ac417cb596f667a72eda493/tree_sitter_embedded_template-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6debd24791466f887109a433c31aa4a5deeba2b217817521c745a4e748a944ed", size = 16439 },
{ url = "https://files.pythonhosted.org/packages/40/96/e79934572723673db9f867000500c6eea61a37705e02c7aee9ee031bbb6f/tree_sitter_embedded_template-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:158fecb38be5b15db0190ef7238e5248f24bf32ae3cab93bc1197e293a5641eb", size = 12572 },
{ url = "https://files.pythonhosted.org/packages/63/06/27f678b9874e4e2e39ddc6f5cce3374c8c60e6046ea8588a491ab6fc9fcb/tree_sitter_embedded_template-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:9f1f3b79fe273f3d15a5b64c85fc6ebfb48decfbe8542accd05f5b7694860df0", size = 11232 },
]
[[package]]
name = "tree-sitter-language-pack"
version = "0.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tree-sitter" },
{ name = "tree-sitter-c-sharp" },
{ name = "tree-sitter-embedded-template" },
{ name = "tree-sitter-php" },
{ name = "tree-sitter-typescript" },
{ name = "tree-sitter-xml" },
{ name = "tree-sitter-yaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4a/5f/1f237a4e19770b015961ce90c75b161c39ea1495f6cb4de7d5cd46c1e138/tree_sitter_language_pack-0.2.0.tar.gz", hash = "sha256:14c83c43a2f7236a2c39c1c196fd0a2415305dee97263aa80c653623fa23ef13", size = 35706652 }
[[package]]
name = "tree-sitter-php"
version = "0.23.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/db/65/9d5ec24a9494770c65324923f7a2ae205b7537575e7f0533b88955b63373/tree_sitter_php-0.23.11.tar.gz", hash = "sha256:8aabb262ad97299c1004f619458091d64187414e54215e152585c097c7a415e5", size = 422853 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/f2/8c940f82ea7f1f6864fa987a1dd7d18a7dd6a0967939c46fa194b4754931/tree_sitter_php-0.23.11-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:f46b741fd4843a7287e46505c35b4712cf2bea8481ce5236d2a32add45921c42", size = 179667 },
{ url = "https://files.pythonhosted.org/packages/66/dd/2522c074504d414135b522be5586f03a087601c7d23714d6db76c4bb2f02/tree_sitter_php-0.23.11-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:315f5da5ad8f9ed014008da481ed49a88db21e03e9f723f1c631eec36b6b3691", size = 177478 },
{ url = "https://files.pythonhosted.org/packages/9d/72/889dfb1154ecfc96e009b1f6c14ec8b44f4a2b81d09a7087f3c8c2f8ba47/tree_sitter_php-0.23.11-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1f515e5875efe492b8970962cd70cab7de4dac90f20229c52360815b4e60f2", size = 258418 },
{ url = "https://files.pythonhosted.org/packages/a8/49/b40635d3281729cb81b1874e62ccae2826831b56d79fd353222cb9dbdf25/tree_sitter_php-0.23.11-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6076af83f549361390be5875c695fe50c319ce2e0388cb8127e6e6529d98dd48", size = 251469 },
{ url = "https://files.pythonhosted.org/packages/a9/db/e47b00ca732eddf22c1795dea1be3f3130da29dfd4985212c6335ccef5fd/tree_sitter_php-0.23.11-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:29bb2450714d2a6911a939e5c20913f17e497cfc8b1dc5a751b15dc6fe6af2b7", size = 245437 },
{ url = "https://files.pythonhosted.org/packages/df/c2/6861b8d013d25697d995b270a0a0d842311439dc1bff22680c125a36abad/tree_sitter_php-0.23.11-cp39-abi3-win_amd64.whl", hash = "sha256:7799d56dfb557414b5172044dbe75bf33f877976dd0193e0775dc7f1f2f7cf8e", size = 168021 },
{ url = "https://files.pythonhosted.org/packages/0f/38/dfa82ffd66810323d0d20d19a8e5d318fe1a65001ee654fa3e80b0871d05/tree_sitter_php-0.23.11-cp39-abi3-win_arm64.whl", hash = "sha256:c4869a6dd13faec316cea19ed8551416da35ccabd0811b9880ad54dbbc24867b", size = 166472 },
]
[[package]]
name = "tree-sitter-typescript"
version = "0.23.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/fc/bb52958f7e399250aee093751e9373a6311cadbe76b6e0d109b853757f35/tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d", size = 773053 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/95/4c00680866280e008e81dd621fd4d3f54aa3dad1b76b857a19da1b2cc426/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478", size = 286677 },
{ url = "https://files.pythonhosted.org/packages/8f/2f/1f36fda564518d84593f2740d5905ac127d590baf5c5753cef2a88a89c15/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8", size = 302008 },
{ url = "https://files.pythonhosted.org/packages/96/2d/975c2dad292aa9994f982eb0b69cc6fda0223e4b6c4ea714550477d8ec3a/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31", size = 351987 },
{ url = "https://files.pythonhosted.org/packages/49/d1/a71c36da6e2b8a4ed5e2970819b86ef13ba77ac40d9e333cb17df6a2c5db/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c", size = 344960 },
{ url = "https://files.pythonhosted.org/packages/7f/cb/f57b149d7beed1a85b8266d0c60ebe4c46e79c9ba56bc17b898e17daf88e/tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0", size = 340245 },
{ url = "https://files.pythonhosted.org/packages/8b/ab/dd84f0e2337296a5f09749f7b5483215d75c8fa9e33738522e5ed81f7254/tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9", size = 278015 },
{ url = "https://files.pythonhosted.org/packages/9f/e4/81f9a935789233cf412a0ed5fe04c883841d2c8fb0b7e075958a35c65032/tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7", size = 274052 },
]
[[package]]
name = "tree-sitter-xml"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/41/ba/77a92dbb4dfb374fb99863a07f938de7509ceeaa74139933ac2bd306eeb1/tree_sitter_xml-0.7.0.tar.gz", hash = "sha256:ab0ff396f20230ad8483d968151ce0c35abe193eb023b20fbd8b8ce4cf9e9f61", size = 54635 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/1d/6b8974c493973c0c9df2bbf220a1f0a96fa785da81a5a13461faafd1441c/tree_sitter_xml-0.7.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cc3e516d4c1e0860fb22172c172148debb825ba638971bc48bad15b22e5b0bae", size = 35404 },
{ url = "https://files.pythonhosted.org/packages/75/f5/31013d04c4e3b9a55e90168cc222a601c84235ba4953a5a06b5cdf8353c4/tree_sitter_xml-0.7.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0674fdf4cc386e4d323cb287d3b072663de0f20a9e9af5d5e09821aae56a9e5c", size = 35488 },
{ url = "https://files.pythonhosted.org/packages/c8/e6/e7493217f950a7c5969e3f3f057664142fa948abefd2dba5acea25719d55/tree_sitter_xml-0.7.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c0fe5f2d6cc09974c8375c8ea9b24909f493b5bf04aacdc4c694b5d2ae6b040", size = 74199 },
{ url = "https://files.pythonhosted.org/packages/94/27/1dd6815592489de51fa7b5fffc1160cd385ade7fa06f07b998742ac18020/tree_sitter_xml-0.7.0-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd3209516a4d84dff90bc91d2ad2ce246de8504cede4358849687fa8e71536e7", size = 76244 },
{ url = "https://files.pythonhosted.org/packages/20/10/2e4e84c50b2175cb53d255ef154aa893cb82cc9d035d7a1a73be9d2d2db4/tree_sitter_xml-0.7.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:87578e15fa55f44ecd9f331233b6f8a2cbde3546b354c830ecb862a632379455", size = 75112 },
{ url = "https://files.pythonhosted.org/packages/ae/91/77c348568bccb179eca21062c923f6f54026900b09fe0cf1aae89d78a0c8/tree_sitter_xml-0.7.0-cp39-abi3-win_amd64.whl", hash = "sha256:9ba2dafc6ce9feaf4ccc617d3aeea57f8e0ca05edad34953e788001ebff79133", size = 36558 },
{ url = "https://files.pythonhosted.org/packages/be/cc/6b4de230770d7be87b2a415583121ac565ce1ff7d9a1ad7fec11f8e613fc/tree_sitter_xml-0.7.0-cp39-abi3-win_arm64.whl", hash = "sha256:fc759f710a8fd7a01c23e2d7cb013679199045bea3dc0e5151650a11322aaf40", size = 34610 },
]
[[package]]
name = "tree-sitter-yaml"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/93/04/6de8be8112c50450cab753fcd6b74d8368c60f6099bf551cee0bec69563a/tree_sitter_yaml-0.7.0.tar.gz", hash = "sha256:9c8bb17d9755c3b0e757260917240c0d19883cd3b59a5d74f205baa8bf8435a4", size = 85085 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/1d/243dbdf59fae8a4109e19f0994e2627ddedb2e16b7cf99bd42be64367742/tree_sitter_yaml-0.7.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e21553ac190ae05bf82796df8beb4d9158ba195b5846018cb36fbc3a35bd0679", size = 43335 },
{ url = "https://files.pythonhosted.org/packages/e2/63/e5d5868a1498e20fd07e7db62933766fd64950279862e3e7f150b88ec69d/tree_sitter_yaml-0.7.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c022054f1f9b54201082ea83073a6c24c42d0436ad8ee99ff2574cba8f928c28", size = 44574 },
{ url = "https://files.pythonhosted.org/packages/f5/ba/9cff9a3fddb1b6b38bc71ce1dfdb8892ab15a4042c104f4582e30318b412/tree_sitter_yaml-0.7.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cd1725142f19e41c51d27c99cfc60780f596e069eb181cfa6433d993a19aa3d", size = 93088 },
{ url = "https://files.pythonhosted.org/packages/19/09/39d29d9a22cee0b3c3e4f3fdbd23e4534b9c2a84b5f962f369eafcfbf88c/tree_sitter_yaml-0.7.0-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d1b268378254f75bb27396d83c96d886ccbfcda6bd8c2778e94e3e1d2459085", size = 91367 },
{ url = "https://files.pythonhosted.org/packages/b0/b7/285653b894b351436917b5fe5e738eecaeb2128b4e4bf72bfe0c6043f62e/tree_sitter_yaml-0.7.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:27c2e7f4f49ddf410003abbb82a7b00ec77ea263d8ef08dbce1a15d293eed2fd", size = 87405 },
{ url = "https://files.pythonhosted.org/packages/bb/73/0cdc82ea653c190475a4f63dd4a1f4efd5d1c7d09d2668b8d84008a4c4f8/tree_sitter_yaml-0.7.0-cp39-abi3-win_amd64.whl", hash = "sha256:98dce0d6bc376f842cfb1d3c32512eea95b37e61cd2c87074bb4b05c999917c8", size = 45360 },
{ url = "https://files.pythonhosted.org/packages/2e/32/af2d676b0176a958f22a75b04be836e09476a10844baab78c018a5030297/tree_sitter_yaml-0.7.0-cp39-abi3-win_arm64.whl", hash = "sha256:f0f8d8e05fa8e70f08d0f18a209d6026e171844f4ea7090e7c779b9c375b3a31", size = 43650 },
]
[[package]] [[package]]
name = "typer" name = "typer"
@@ -517,18 +442,28 @@ source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiohttp" }, { name = "aiohttp" },
{ name = "appdirs" }, { name = "appdirs" },
{ name = "tree-sitter-language-pack" }, { name = "tree-sitter" },
{ name = "tree-sitter-typst" },
{ name = "typer" }, { name = "typer" },
] ]
[package.dev-dependencies]
dev = [
{ name = "ruff" },
]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "aiohttp", specifier = ">=3.11.11" }, { name = "aiohttp", specifier = ">=3.11.11" },
{ name = "appdirs", specifier = ">=1.4.4" }, { name = "appdirs", specifier = ">=1.4.4" },
{ name = "tree-sitter-language-pack", specifier = ">=0.2.0" }, { name = "tree-sitter", specifier = ">=0.23.2" },
{ name = "tree-sitter-typst", git = "https://github.com/uben0/tree-sitter-typst" },
{ name = "typer", specifier = ">=0.15.1" }, { name = "typer", specifier = ">=0.15.1" },
] ]
[package.metadata.requires-dev]
dev = [{ name = "ruff", specifier = ">=0.8.5" }]
[[package]] [[package]]
name = "yarl" name = "yarl"
version = "1.18.3" version = "1.18.3"