Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

zig-codeblocks

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zig-codeblocks - pypi Package Compare versions

Comparing version
0.1.2
to
0.2.0
+102
src/zig_codeblocks/consts.py
from zig_codeblocks.styling import Color, Style, Theme
KEYWORDS = frozenset(
{
"addrspace",
"align",
"allowzero",
"and",
"anyframe",
"anytype",
"asm",
"async",
"await",
"break",
"callconv",
"catch",
"comptime",
"const",
"continue",
"defer",
"else",
"enum",
"errdefer",
"error",
"export",
"extern",
"fn",
"for",
"if",
"inline",
"linksection",
"noalias",
"noinline",
"nosuspend",
"opaque",
"or",
"orelse",
"packed",
"pub",
"resume",
"return",
"struct",
"suspend",
"switch",
"test",
"threadlocal",
"try",
"union",
"unreachable",
"usingnamespace",
"var",
"volatile",
"while",
}
)
IDENTIFIERS = frozenset({"identifier", "c"})
NUMERIC_LITERALS = frozenset({"integer", "float"})
PRIMITIVE_VALUES = frozenset({"true", "false", "null", "undefined"})
TYPES = frozenset(
{
"anyerror",
"anyopaque",
"bool",
"builtin_type",
"c_char",
"c_int",
"c_long",
"c_longdouble",
"c_longlong",
"c_short",
"c_uint",
"c_ulong",
"c_ulonglong",
"c_ushort",
"comptime_float",
"comptime_int",
"f128",
"f16",
"f32",
"f64",
"f80",
"isize",
"noreturn",
"type",
"usize",
"void",
}
)
STRINGLIKE = frozenset(
{"string_content", "multiline_string", '"', "'", "character_content"}
)
DEFAULT_THEME = Theme(
builtin_identifiers=Style(Color.BLUE, bold=True),
calls=Style(Color.BLUE),
comments=Style(Color.GRAY),
keywords=Style(Color.MAGENTA),
numeric=Style(Color.CYAN),
primitive_values=Style(Color.CYAN),
strings=Style(Color.GREEN),
types=Style(Color.ORANGE),
)
+12
-0

@@ -8,2 +8,13 @@ # Changelog

## [v0.2.0] - 2025-02-09
### Added
- Theming support
- Improved reset code insertion
- Improved type highlighting
- Primitive value highlighting (i.e. `true`, `false`, `null`, `undefined`)
### Changed
- Swapped keyword and type colors in the default theme
## [v0.1.2] - 2025-02-09

@@ -34,1 +45,2 @@

[v0.1.2]: https://github.com/trag1c/ixia/compare/v0.1.1...v0.1.2
[v0.2.0]: https://github.com/trag1c/ixia/compare/v0.1.2...v0.2.0
+105
-9
Metadata-Version: 2.4
Name: zig-codeblocks
Version: 0.1.2
Version: 0.2.0
Summary: Zig ANSI syntax highlighting library

@@ -20,4 +20,5 @@ Project-URL: repository, https://github.com/trag1c/zig-codeblocks

`zig-codeblocks` is a CPython 3.10+ library for adding syntax highlighting to
Zig code blocks in Markdown files through ANSI escape codes. Originally intended
for patching the lack of syntax highlighting for Zig on Discord.
Zig code blocks in Markdown files through ANSI escape codes.
Originally intended for patching the lack of syntax highlighting for Zig on
Discord.

@@ -40,5 +41,6 @@

```py
def extract_codeblocks(source: str) -> Iterator[Codeblock]
def extract_codeblocks(source: str | bytes) -> Iterator[CodeBlock]
```
Yields [`CodeBlock`](#codeblock)s from a Markdown source.
Assumes UTF-8 if source is `bytes`.

@@ -72,5 +74,8 @@ **Example usage:**

```py
def highlight_zig_code(source: str) -> str
def highlight_zig_code(source: str | bytes, theme: Theme = DEFAULT_THEME) -> str
```
Returns an ANSI syntax-highlighted version of the given Zig source code.
Assumes UTF-8 if source is `bytes`.
An optional [`Theme`](#theme) can be supplied (defaults to
[`DEFAULT_THEME`](#default_theme)).

@@ -81,8 +86,18 @@ **Example usage:**

from zig_codeblocks import highlight_zig_code
from zig_codeblocks import DEFAULT_THEME, Color, Style, highlight_zig_code
source = Path("examples/hello_world.zig").read_text()
print(highlight_zig_code(source))
theme = DEFAULT_THEME.copy()
theme.builtin_identifiers = Style(Color.ORANGE, underline=True)
theme.strings = Style(Color.CYAN)
theme.types = None
print(
highlight_zig_code(source),
highlight_zig_code(source, theme),
sep="\n\n",
)
```
<img src="examples/highlighted-zig-code.png" width="55%">
<img src="examples/highlighted-zig-code.png" width="65%">

@@ -92,5 +107,13 @@

```py
def process_markdown(source: str, *, only_code: bool = False) -> str
def process_markdown(
source: str | bytes,
theme: Theme = DEFAULT_THEME,
*,
only_code: bool = False,
) -> str
```
Returns a Markdown source with Zig code blocks syntax-highlighted.
Assumes UTF-8 if source is `bytes`.
An optional [`Theme`](#theme) can be supplied (defaults to
[`DEFAULT_THEME`](#default_theme)).
If `only_code` is True, only processed Zig code blocks will be returned.

@@ -119,2 +142,75 @@

### `Color`
```py
class Color(Enum):
GRAY = "30"
RED = "31"
GREEN = "32"
ORANGE = "33"
BLUE = "34"
MAGENTA = "35"
CYAN = "36"
WHITE = "37" # Black for light mode
```
An enumeration of 3-bit ANSI colors.
Some names were adjusted to match Discord's style.
### `Style`
```py
@dataclass(slots=True, frozen=True)
class Style:
color: Color
_: KW_ONLY
bold: bool = False
underline: bool = False
```
A style for syntax highlighting.
Takes a [`Color`](#color) and can optionally be bold and/or underlined.
Immutable.
Produces an SGR sequence when converted to a string.
### `Theme`
```py
@dataclass(slots=True, kw_only=True)
class Theme:
builtin_identifiers: Style | None = None
calls: Style | None = None
comments: Style | None = None
identifiers: Style | None = None
keywords: Style | None = None
numeric: Style | None = None
strings: Style | None = None
primitive_values: Style | None = None
types: Style | None = None
```
A theme for syntax highlighting Zig code.
Each field is optional and can be provided a [`Style`](#style) to apply to the
corresponding token type.
#### `Theme.copy`
```py
def copy(self) -> Theme
```
Returns a copy of the theme.
### `DEFAULT_THEME`
The default theme used for highlighting, defined as follows:
```py
DEFAULT_THEME = Theme(
builtin_identifiers=Style(Color.BLUE, bold=True),
calls=Style(Color.BLUE),
comments=Style(Color.GRAY),
identifiers=None,
keywords=Style(Color.MAGENTA),
numeric=Style(Color.CYAN),
primitive_values=Style(Color.CYAN),
strings=Style(Color.GREEN),
types=Style(Color.ORANGE),
)
```
## License

@@ -121,0 +217,0 @@ `zig-codeblocks` is licensed under the [MIT License].

+1
-1
[project]
name = "zig-codeblocks"
version = "0.1.2"
version = "0.2.0"
description = "Zig ANSI syntax highlighting library"

@@ -5,0 +5,0 @@ readme = "README.md"

+104
-8

@@ -7,4 +7,5 @@ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)

`zig-codeblocks` is a CPython 3.10+ library for adding syntax highlighting to
Zig code blocks in Markdown files through ANSI escape codes. Originally intended
for patching the lack of syntax highlighting for Zig on Discord.
Zig code blocks in Markdown files through ANSI escape codes.
Originally intended for patching the lack of syntax highlighting for Zig on
Discord.

@@ -27,5 +28,6 @@

```py
def extract_codeblocks(source: str) -> Iterator[Codeblock]
def extract_codeblocks(source: str | bytes) -> Iterator[CodeBlock]
```
Yields [`CodeBlock`](#codeblock)s from a Markdown source.
Assumes UTF-8 if source is `bytes`.

@@ -59,5 +61,8 @@ **Example usage:**

```py
def highlight_zig_code(source: str) -> str
def highlight_zig_code(source: str | bytes, theme: Theme = DEFAULT_THEME) -> str
```
Returns an ANSI syntax-highlighted version of the given Zig source code.
Assumes UTF-8 if source is `bytes`.
An optional [`Theme`](#theme) can be supplied (defaults to
[`DEFAULT_THEME`](#default_theme)).

@@ -68,8 +73,18 @@ **Example usage:**

from zig_codeblocks import highlight_zig_code
from zig_codeblocks import DEFAULT_THEME, Color, Style, highlight_zig_code
source = Path("examples/hello_world.zig").read_text()
print(highlight_zig_code(source))
theme = DEFAULT_THEME.copy()
theme.builtin_identifiers = Style(Color.ORANGE, underline=True)
theme.strings = Style(Color.CYAN)
theme.types = None
print(
highlight_zig_code(source),
highlight_zig_code(source, theme),
sep="\n\n",
)
```
<img src="examples/highlighted-zig-code.png" width="55%">
<img src="examples/highlighted-zig-code.png" width="65%">

@@ -79,5 +94,13 @@

```py
def process_markdown(source: str, *, only_code: bool = False) -> str
def process_markdown(
source: str | bytes,
theme: Theme = DEFAULT_THEME,
*,
only_code: bool = False,
) -> str
```
Returns a Markdown source with Zig code blocks syntax-highlighted.
Assumes UTF-8 if source is `bytes`.
An optional [`Theme`](#theme) can be supplied (defaults to
[`DEFAULT_THEME`](#default_theme)).
If `only_code` is True, only processed Zig code blocks will be returned.

@@ -106,2 +129,75 @@

### `Color`
```py
class Color(Enum):
GRAY = "30"
RED = "31"
GREEN = "32"
ORANGE = "33"
BLUE = "34"
MAGENTA = "35"
CYAN = "36"
WHITE = "37" # Black for light mode
```
An enumeration of 3-bit ANSI colors.
Some names were adjusted to match Discord's style.
### `Style`
```py
@dataclass(slots=True, frozen=True)
class Style:
color: Color
_: KW_ONLY
bold: bool = False
underline: bool = False
```
A style for syntax highlighting.
Takes a [`Color`](#color) and can optionally be bold and/or underlined.
Immutable.
Produces an SGR sequence when converted to a string.
### `Theme`
```py
@dataclass(slots=True, kw_only=True)
class Theme:
builtin_identifiers: Style | None = None
calls: Style | None = None
comments: Style | None = None
identifiers: Style | None = None
keywords: Style | None = None
numeric: Style | None = None
strings: Style | None = None
primitive_values: Style | None = None
types: Style | None = None
```
A theme for syntax highlighting Zig code.
Each field is optional and can be provided a [`Style`](#style) to apply to the
corresponding token type.
#### `Theme.copy`
```py
def copy(self) -> Theme
```
Returns a copy of the theme.
### `DEFAULT_THEME`
The default theme used for highlighting, defined as follows:
```py
DEFAULT_THEME = Theme(
builtin_identifiers=Style(Color.BLUE, bold=True),
calls=Style(Color.BLUE),
comments=Style(Color.GRAY),
identifiers=None,
keywords=Style(Color.MAGENTA),
numeric=Style(Color.CYAN),
primitive_values=Style(Color.CYAN),
strings=Style(Color.GREEN),
types=Style(Color.ORANGE),
)
```
## License

@@ -108,0 +204,0 @@ `zig-codeblocks` is licensed under the [MIT License].

@@ -0,4 +1,15 @@

from zig_codeblocks.consts import DEFAULT_THEME
from zig_codeblocks.formatting import highlight_zig_code, process_markdown
from zig_codeblocks.parsing import CodeBlock, extract_codeblocks
from zig_codeblocks.styling import Color, Style, Theme
__all__ = ("CodeBlock", "extract_codeblocks", "highlight_zig_code", "process_markdown")
__all__ = (
"DEFAULT_THEME",
"CodeBlock",
"Color",
"Style",
"Theme",
"extract_codeblocks",
"highlight_zig_code",
"process_markdown",
)

@@ -6,4 +6,5 @@ from __future__ import annotations

from zig_codeblocks import consts
from zig_codeblocks.parsing import extract_codeblocks, tokenize_zig
from zig_codeblocks.styling import Color, Reset, Style
from zig_codeblocks.styling import Reset, Style, Theme

@@ -18,80 +19,21 @@ if TYPE_CHECKING:

ZIG_KEYWORDS = frozenset(
{
"addrspace",
"align",
"allowzero",
"and",
"anyframe",
"anytype",
"asm",
"async",
"await",
"break",
"callconv",
"catch",
"comptime",
"const",
"continue",
"defer",
"else",
"enum",
"errdefer",
"error",
"export",
"extern",
"fn",
"for",
"if",
"inline",
"linksection",
"noalias",
"noinline",
"nosuspend",
"opaque",
"or",
"orelse",
"packed",
"pub",
"resume",
"return",
"struct",
"suspend",
"switch",
"test",
"threadlocal",
"try",
"union",
"unreachable",
"usingnamespace",
"var",
"volatile",
"while",
}
)
ZIG_TYPE_TOKENS = frozenset({"builtin_type", "f64"})
ZIG_NUMERIC_TOKENS = frozenset({"integer", "float"})
ZIG_STRING_TOKENS = frozenset(
{"string_content", "multiline_string", '"', "'", "character_content"}
)
_KIND_MAPPINGS = {
"comment": Style(Color.GRAY),
"builtin_identifier": Style(Color.BLUE, bold=True),
}
_GROUP_KIND_MAPPINGS: tuple[tuple[frozenset[str], Style], ...] = (
(ZIG_STRING_TOKENS, Style(Color.GREEN)),
(ZIG_KEYWORDS, Style(Color.ORANGE)),
(ZIG_NUMERIC_TOKENS, Style(Color.CYAN)),
(ZIG_TYPE_TOKENS, Style(Color.MAGENTA)),
)
def _get_style(kind: str, theme: Theme) -> Style | None:
for options, style in (
(consts.IDENTIFIERS, theme.identifiers),
(consts.KEYWORDS, theme.keywords),
(consts.NUMERIC_LITERALS, theme.numeric),
(consts.PRIMITIVE_VALUES, theme.primitive_values),
(consts.STRINGLIKE, theme.strings),
(consts.TYPES, theme.types),
):
if kind in options:
return style
if kind == "comment":
return theme.comments
if kind == "builtin_identifier":
return theme.builtin_identifiers
return None
def _get_style(kind: str) -> Style | None:
return next(
(style for options, style in _GROUP_KIND_MAPPINGS if kind in options),
None,
) or _KIND_MAPPINGS.get(kind)
def _peek(iterator: Iterator[T]) -> T:

@@ -105,11 +47,3 @@ return next(tee(iterator, 1)[0])

def _adjust_reset(body: Body) -> None:
last_style = _last_applied_style(body)
if last_style is Reset.BOLD:
body[~body[::-1].index(Reset.BOLD)] = Reset.FULL
elif last_style is not Reset.FULL:
body.append(Reset.FULL)
def _adjust_string_idents(body: Body, token: Token) -> None:
def _adjust_string_idents(body: Body, token: Token, theme: Theme) -> None:
# Special case for `whatever.@"something here"`,

@@ -119,10 +53,15 @@ # which is an identifier, so should have no string highlighting

token.kind == '"'
and _last_applied_style(body) == Style(Color.GREEN)
and _last_applied_style(body) == theme.strings
and len(body) > 3
and body[-4] == "@"
):
del body[-3]
if theme.identifiers:
body[-3] = theme.identifiers
else:
del body[-3]
def _process_zig_tokens(source: bytes, tokens: Iterator[Token]) -> str:
def _process_zig_tokens(
source: bytes, tokens: Iterator[Token], theme: Theme = consts.DEFAULT_THEME
) -> str:
body: Body = []

@@ -135,4 +74,5 @@ pointer = 0

filler = source[pointer : token.byte_range.start]
if not filler.isspace():
_adjust_reset(body)
match filler.isspace(), _last_applied_style(body):
case (False, _) | (True, Style(underline=True) | Style(bold=True)):
body.append(Reset.FULL)
body.append(filler.decode())

@@ -147,21 +87,16 @@ pointer = token.byte_range.start

style = (
Style(Color.BLUE) # Special case for function calls
theme.calls
if token.kind == "identifier" and _peek(tokens).kind == "("
else _get_style(token.kind)
else _get_style(token.kind, theme)
)
if style is None:
_adjust_reset(body)
if _last_applied_style(body) is not Reset.FULL:
body.append(Reset.FULL)
elif _last_applied_style(body) is not style:
body.append(style)
_adjust_string_idents(body, token)
_adjust_string_idents(body, token, theme)
body.append(token.value.decode())
match style:
case Style(bold=True):
body.append(Reset.BOLD)
case Style(underline=True):
body.append(Reset.UNDERLINE)
pointer = token.byte_range.stop

@@ -175,3 +110,3 @@ try:

def highlight_zig_code(source: str | bytes) -> str:
def highlight_zig_code(source: str | bytes, theme: Theme = consts.DEFAULT_THEME) -> str:
"""

@@ -183,6 +118,8 @@ Return an ANSI syntax-highlighted version of the given Zig source code.

source = source.encode()
return _process_zig_tokens(source, tokenize_zig(source))
return _process_zig_tokens(source, tokenize_zig(source), theme)
def process_markdown(source: str | bytes, *, only_code: bool = False) -> str:
def process_markdown(
source: str | bytes, theme: Theme = consts.DEFAULT_THEME, *, only_code: bool = False
) -> str:
"""

@@ -200,8 +137,9 @@ Return a Markdown source with Zig code blocks syntax-highlighted.

return "\n".join(
f"```ansi\n{highlight_zig_code(code)}\n```" for code in zig_codeblocks
f"```ansi\n{highlight_zig_code(code, theme)}\n```"
for code in zig_codeblocks
)
for codeblock in zig_codeblocks:
original_source = f"```zig\n{codeblock}```"
highlighted_source = f"```ansi\n{highlight_zig_code(codeblock)}\n```"
highlighted_source = f"```ansi\n{highlight_zig_code(codeblock, theme)}\n```"
source = source.replace(original_source, highlighted_source)
return source

@@ -1,2 +0,4 @@

from dataclasses import KW_ONLY, dataclass
from __future__ import annotations
from dataclasses import KW_ONLY, dataclass, replace
from enum import Enum

@@ -20,3 +22,7 @@ from itertools import compress

class Color(Enum):
# Names adjusted to match Discord's style
"""
An enumeration of 3-bit ANSI colors.
Some names were adjusted to match Discord's style.
"""
GRAY = "30"

@@ -34,2 +40,8 @@ RED = "31"

class Style:
"""
A style for syntax highlighting.
Takes a `Color` and can optionally be bold and/or underlined.
Produces an SGR sequence when converted to a string.
"""
color: Color

@@ -43,1 +55,20 @@ _: KW_ONLY

return _to_sgr(self.color.value, *modifiers)
@dataclass(slots=True, kw_only=True)
class Theme:
"""A theme for syntax highlighting Zig code."""
builtin_identifiers: Style | None = None
calls: Style | None = None
comments: Style | None = None
identifiers: Style | None = None
keywords: Style | None = None
numeric: Style | None = None
strings: Style | None = None
primitive_values: Style | None = None
types: Style | None = None
def copy(self) -> Theme:
"""Create a copy of the `Theme`."""
return replace(self)
import pytest
from zig_codeblocks.styling import Color, Reset, Style
from zig_codeblocks.styling import Color, Reset, Style, Theme

@@ -32,1 +32,12 @@

assert str(Reset[reset_type]) == expected_sgr
def test_theme_copy() -> None:
original = Theme(calls=Style(Color.BLUE))
copy = original.copy()
assert original == copy
assert original is not copy
copy.calls = None
assert original != copy
assert original.calls is not None

@@ -316,3 +316,3 @@ version = 1

name = "zig-codeblocks"
version = "0.1.2"
version = "0.2.0"
source = { editable = "." }

@@ -319,0 +319,0 @@ dependencies = [

Sorry, the diff of this file is not supported yet