Skip to content

Commit 7fda549

Browse files
committed
Format comments with topiary
1 parent 281d2e1 commit 7fda549

18 files changed

+265
-26
lines changed

.github/workflows/build_wheels.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ jobs:
1818
with:
1919
submodules: recursive
2020

21+
- uses: astral-sh/setup-uv@v6
22+
2123
- name: Build SDist
22-
run: python ./setup.py sdist
24+
run: uv build --sdist
2325

2426
- uses: actions/upload-artifact@v5
2527
with:
@@ -31,17 +33,16 @@ jobs:
3133
strategy:
3234
fail-fast: false
3335
matrix:
34-
python-version: ["3.10", "3.11", "3.12", "3.13"]
36+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
3537
steps:
3638
- uses: actions/checkout@v5
3739
- uses: actions/setup-python@v6
3840
with:
3941
python-version: ${{ matrix.python-version }}
42+
- uses: astral-sh/setup-uv@v6
4043
- name: Install dependencies
41-
run: pip install .[dev]
42-
- run: pytest
43-
44-
44+
run: uv sync --all-extras
45+
- run: uv run pytest
4546

4647
upload_all:
4748
needs: [build_sdist, check]

Cargo.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "zeekscript"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
[lib]
8+
name = "zeekscript"
9+
crate-type = ["cdylib"]
10+
11+
[dependencies]
12+
pyo3 = { version = "0.27.1", optional = true }
13+
thiserror = "2.0.17"
14+
topiary-core = { version = "0.7.0", default-features = false }
15+
topiary-tree-sitter-facade = { version = "0.7.0", default-features = false }
16+
tree-sitter-zeek = { git = "https://github.com/zeek/tree-sitter-zeek", version = "0.2.9" }
17+
18+
[dev-dependencies]
19+
insta = "1.43.2"
20+
21+
[features]
22+
default = ["python"]
23+
python = ["pyo3"]

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[build-system]
2-
requires = ["setuptools"]
2+
requires = ["maturin>=1.8,<2.0"]
3+
build-backend = "maturin"
34

45
[project]
56
name = "zeekscript"
@@ -9,6 +10,9 @@ readme = "README.md"
910

1011
classifiers=[
1112
"Programming Language :: Python :: 3.7",
13+
"Programming Language :: Rust",
14+
"Programming Language :: Python :: Implementation :: CPython",
15+
"Programming Language :: Python :: Implementation :: PyPy",
1216
"License :: OSI Approved :: BSD License",
1317
"Topic :: Utilities",
1418
]
@@ -50,8 +54,8 @@ Repository = "https://github.com/zeek/zeekscript"
5054
zeek-format = "zeekscript.cli:zeek_format"
5155
zeek-script = "zeekscript.cli:zeek_script"
5256

53-
[tool.setuptools]
54-
packages = ["zeekscript"]
57+
[tool.maturin]
58+
features = ["pyo3/extension-module"]
5559

5660
[tool.ruff.lint]
5761
select = ["PL", "UP", "RUF", "N", "I", "RET"]

setup.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/lib.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::string::FromUtf8Error;
2+
3+
#[cfg(feature = "python")]
4+
use pyo3::{exceptions::PyException, prelude::*};
5+
6+
use thiserror::Error;
7+
use topiary_core::{FormatterError, TopiaryQuery};
8+
9+
#[derive(Error, Debug)]
10+
pub enum FormatError {
11+
#[error("parse error")]
12+
Parse,
13+
14+
#[error("internal query error")]
15+
Query(String),
16+
17+
#[error("idempotency violated")]
18+
Idempotency,
19+
20+
#[error("UTF8 conversion error")]
21+
UTF8(FromUtf8Error),
22+
23+
#[error("unknown error")]
24+
Unknown,
25+
}
26+
27+
const QUERY: &str = include_str!("query.scm");
28+
29+
pub fn format(
30+
input: &str,
31+
skip_idempotence: bool,
32+
tolerate_parsing_errors: bool,
33+
) -> Result<String, FormatError> {
34+
let mut output = Vec::new();
35+
36+
let grammar = topiary_tree_sitter_facade::Language::from(tree_sitter_zeek::LANGUAGE);
37+
38+
let query = TopiaryQuery::new(&grammar, QUERY).map_err(|e| match e {
39+
FormatterError::Query(m, e) => FormatError::Query(match e {
40+
None => m,
41+
Some(e) => format!("{m}: {e}"),
42+
}),
43+
_ => FormatError::Unknown,
44+
})?;
45+
46+
let language = topiary_core::Language {
47+
name: "zeek".to_string(),
48+
indent: Some("\t".into()),
49+
grammar,
50+
query,
51+
};
52+
53+
if let Err(e) = topiary_core::formatter(
54+
&mut input.as_bytes(),
55+
&mut output,
56+
&language,
57+
topiary_core::Operation::Format {
58+
skip_idempotence,
59+
tolerate_parsing_errors,
60+
},
61+
) {
62+
Err(match e {
63+
FormatterError::Query(m, e) => FormatError::Query(match e {
64+
None => m,
65+
Some(e) => format!("{m}: {e}"),
66+
}),
67+
FormatterError::Idempotence => FormatError::Idempotency,
68+
FormatterError::Parsing { .. } => FormatError::Parse,
69+
_ => FormatError::Unknown,
70+
})?;
71+
};
72+
73+
let output = String::from_utf8(output).map_err(FormatError::UTF8)?;
74+
75+
Ok(output)
76+
}
77+
78+
#[cfg(feature = "python")]
79+
/// Formats the sum of two numbers as string.
80+
#[pyfunction(name = "format")]
81+
fn format_wrapped(input: &str) -> PyResult<String> {
82+
format(input, false, true).map_err(|e| PyException::new_err(e.to_string()))
83+
}
84+
85+
#[cfg(feature = "python")]
86+
/// A Python module implemented in Rust.
87+
#[pymodule]
88+
fn zeekscript(m: &Bound<'_, PyModule>) -> PyResult<()> {
89+
m.add_function(wrap_pyfunction!(format_wrapped, m)?)?;
90+
Ok(())
91+
}
92+
93+
#[cfg(test)]
94+
mod test {
95+
use insta::assert_debug_snapshot;
96+
97+
use crate::FormatError;
98+
99+
fn format(input: &str) -> Result<String, FormatError> {
100+
crate::format(input, false, false)
101+
}
102+
103+
#[test]
104+
fn comments() {
105+
assert_debug_snapshot!(format("# foo\n;1;"));
106+
assert_debug_snapshot!(format("##! foo\n;1;"));
107+
assert_debug_snapshot!(format("## foo\n1;"));
108+
assert_debug_snapshot!(format("##< foo\n1;"));
109+
110+
assert_debug_snapshot!(format("1;# foo\n;1;"));
111+
assert_debug_snapshot!(format("1;##! foo\n;1;"));
112+
assert_debug_snapshot!(format("1;## foo\n1;"));
113+
assert_debug_snapshot!(format("1;##< foo"));
114+
assert_debug_snapshot!(format("1;##< foo\n##< bar"));
115+
}
116+
}

src/query.scm

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; Rules for formatting Spicy.
2+
;
3+
; Formatting is specified here in terms of tree-sitter nodes. We select nodes
4+
; with tree-sitter queries[^1] and then attach topiary formatting rules[^2] in
5+
; the captures.
6+
;
7+
; See the Development section in README.md for a workflow on how to modify or
8+
; extend these rules.
9+
10+
; [^1]: https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
11+
; [^2]: https://github.com/tweag/topiary#design
12+
13+
; Comments are always followed by a linebreak.
14+
[
15+
(minor_comment)
16+
(zeekygen_head_comment)
17+
(zeekygen_prev_comment)
18+
(zeekygen_next_comment)
19+
] @append_hardline
20+
21+
; Comments are preceeded by a space.
22+
(
23+
[
24+
(_)
25+
(nl) @do_nothing
26+
]
27+
.
28+
[
29+
(minor_comment)
30+
(zeekygen_head_comment)
31+
(zeekygen_prev_comment)
32+
(zeekygen_next_comment)
33+
] @prepend_space
34+
)
35+
36+
; If we have multiple comments documenting an item with `##<` align them all.
37+
(zeekygen_prev_comment) @multi_line_indent_all
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: src/lib.rs
3+
expression: "format(\"##! foo\\n;1;\")"
4+
---
5+
Ok(
6+
"##! foo\n;1;\n",
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: src/lib.rs
3+
expression: "format(\"## foo\\n1;\")"
4+
---
5+
Ok(
6+
"## foo\n1;\n",
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: src/lib.rs
3+
expression: "format(\"##< foo\\n1;\")"
4+
---
5+
Ok(
6+
"##< foo\n1;\n",
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: src/lib.rs
3+
expression: "format(\"1;# foo\\n;1;\")"
4+
---
5+
Ok(
6+
"1; # foo\n;1;\n",
7+
)

0 commit comments

Comments
 (0)