diff --git a/.gitignore b/.gitignore index b768683..50a99c3 100644 --- a/.gitignore +++ b/.gitignore @@ -71,4 +71,4 @@ docs/_build/ # Pyenv .python-version -*.pdf \ No newline at end of file +*.pdf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f12ff96 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,55 @@ +# run all hooks with: +# pre-commit run --hook-stage manual --all +ci: + skip: + # pre-commit.ci doesn't have Rust installed + - cargo-fmt + +repos: + - repo: local + hooks: + - id: cargo-fmt # rustup component add rustfmt + name: cargo fmt + entry: cargo fmt --all -- + language: system + types: [rust] + pass_filenames: false + + - id: cargo-check + name: cargo check + entry: cargo check --all-features --all-targets -- + language: system + pass_filenames: false + types: [rust] + stages: [manual] # because it's slow + + - id: cargo-clippy # rustup component add clippy + name: cargo clippy + entry: cargo clippy --tests --all-features -- -D warnings + language: system + pass_filenames: false + types: [rust] + stages: [manual] # because it's slow + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-yaml + - id: check-toml + - id: end-of-file-fixer + exclude: | + (?x)( + (^sysconfig/)| + (.*\.stdout) + ) + - id: trailing-whitespace + exclude: | + (?x)( + (^sysconfig/)| + (.*\.stdout) + ) + - id: mixed-line-ending + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.8 + hooks: + - id: ruff-format + - id: ruff diff --git a/python/typst/py.typed b/python/typst/py.typed index 8b13789..e69de29 100644 --- a/python/typst/py.typed +++ b/python/typst/py.typed @@ -1 +0,0 @@ - diff --git a/python/typst/syntax.pyi b/python/typst/syntax.pyi new file mode 100644 index 0000000..72cf200 --- /dev/null +++ b/python/typst/syntax.pyi @@ -0,0 +1,614 @@ +from typing import List, Optional + +class Span: + """A span in a source file. + + A span is an opaque identifier for a location in the source code. + It can be used to associate nodes with their source positions. + """ + + def is_detached(self) -> bool: + """Check if this is a detached span (not associated with any source).""" + ... + + def __eq__(self, other: object) -> bool: + """Check if two spans are equal.""" + ... + + def __hash__(self) -> int: + """Return hash of the span.""" + ... + +class SyntaxKind: + """A syntactical building block of a Typst file.""" + + def name(self) -> str: + """Get the name of this syntax kind.""" + ... + + def is_keyword(self) -> bool: + """Check if this is a keyword.""" + ... + + def is_trivia(self) -> bool: + """Check if this is trivia (whitespace, comments).""" + ... + + def is_error(self) -> bool: + """Check if this is an error.""" + ... + +class SyntaxNode: + """A node in the untyped syntax tree.""" + + def kind(self) -> SyntaxKind: + """The type of the node.""" + ... + + def text(self) -> str: + """The text of the node if it's a leaf or error node. + Returns the empty string if this is an inner node.""" + ... + + def is_empty(self) -> bool: + """Return `true` if the length is 0.""" + ... + + def erroneous(self) -> bool: + """Whether the node or its children contain an error.""" + ... + + def children(self) -> List[SyntaxNode]: + """The node's children.""" + ... + + def span(self) -> Span: + """The span of this node in the source file.""" + ... + + def __len__(self) -> int: + """The byte length of the node in the source text.""" + ... + +class Markup: + """Markup content - the root of a Typst document.""" + + def exprs(self) -> List["Expr"]: + """Get all expressions in this markup.""" + ... + + def to_untyped(self) -> SyntaxNode: + """Get the underlying syntax node.""" + ... + + def text(self) -> str: + """The text content of this markup.""" + ... + +class Expr: + """An expression in Typst markup or code. + + This is the base class for all expression types. Use the `variant()` method + to check the specific type, or use `isinstance()` to check for subclasses + like `Heading`. + """ + + def variant(self) -> str: + """Get the variant name of this expression (e.g. 'Heading', 'Strong', 'Text').""" + ... + + def to_untyped(self) -> SyntaxNode: + """Get the underlying syntax node.""" + ... + + def text(self) -> str: + """The text content of this expression.""" + ... + + def span(self) -> Span: + """The span of this expression.""" + ... + +# ============================================================================= +# Markup elements +# ============================================================================= + +class Heading(Expr): + """A heading expression: `= Heading`.""" + + @property + def depth(self) -> int: + """The nesting depth of this heading (number of `=` signs).""" + ... + + @property + def body(self) -> Optional[Markup]: + """Get the body of this heading as a Markup node.""" + ... + +class Text(Expr): + """A text expression: just plain text.""" + + @property + def content(self) -> str: + """The text content.""" + ... + +class Strong(Expr): + """Strong (bold) content: `*strong*`.""" + + @property + def body(self) -> Optional[Markup]: + """Get the body of this strong element as a Markup node.""" + ... + +class Emph(Expr): + """Emphasis (italic) content: `_emph_`.""" + + @property + def body(self) -> Optional[Markup]: + """Get the body of this emphasis element as a Markup node.""" + ... + +class Space(Expr): + """A space expression (whitespace between content).""" + + ... + +class Parbreak(Expr): + """A paragraph break (blank line).""" + + ... + +class Raw(Expr): + """Raw text (code block): `` `code` `` or ``` ```code``` ```.""" + + @property + def block(self) -> bool: + """Whether this is a block-level raw element.""" + ... + + @property + def lang(self) -> Optional[str]: + """The language tag, if any.""" + ... + + @property + def lines(self) -> List[str]: + """Get the text content of all lines in this raw element.""" + ... + +class Equation(Expr): + """A math equation: `$x$` or `$ x $`.""" + + @property + def block(self) -> bool: + """Whether this is a block-level equation (display math).""" + ... + +class Linebreak(Expr): + """A line break: `\\`.""" + + ... + +class Escape(Expr): + """An escape sequence: `\\#`, `\\*`, etc.""" + + @property + def character(self) -> str: + """The escaped character.""" + ... + +class Shorthand(Expr): + """A shorthand for a unicode codepoint: `~`, `---`, `--`, `...`.""" + + @property + def character(self) -> str: + """The resolved unicode character.""" + ... + +class SmartQuote(Expr): + """A smart quote: `'` or `\"`.""" + + @property + def double(self) -> bool: + """Whether this is a double quote.""" + ... + +class Ref(Expr): + """A reference to a label: `@label`.""" + + @property + def target(self) -> str: + """The target label name.""" + ... + +class Label(Expr): + """A label: `