carve-lang 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -1
- data/README.md +46 -2
- data/ext/carve/Cargo.lock +3 -3
- data/ext/carve/Cargo.toml +2 -2
- data/ext/carve/src/lib.rs +65 -4
- data/lib/carve/version.rb +1 -1
- data/lib/carve.rb +46 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d4e85e4ab93621b11f1502ca546d64cc0d46e513e4074e6e4014cec8f5aee04
|
|
4
|
+
data.tar.gz: 3100558ccb654d5c2441358ce8a13ba363db7937e0e979be3944780322b62561
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db44ca3900ce90483edd037944efa79adf6c55a921afe8b00b00a31ba687d6f0f5fce97efba6ec3fb67e69f3c931544bd6b0e09169d62e9a8549b99e0836c693
|
|
7
|
+
data.tar.gz: 28dbc892917d7fb9074005e71cff53fe603bc25ca4d3a54add3042be7813aa4bed7e2db0eea5b950b1a5e2c416de9d9588c38ac7f8985cb90aa126f482d17c86
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.10] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- The engine is the published `carve-lang` 0.1.11 crate. A quote-prefixed `+`
|
|
15
|
+
needs one space after each `>`, so `>+` and `>>+` now render as text, and a
|
|
16
|
+
continuation marker reads only at column 0 or right after a block opener.
|
|
17
|
+
Re-check documents that place `+` elsewhere (#215, markup-carve/carve#3421).
|
|
18
|
+
- Includes of a file that is not valid UTF-8 are refused with an
|
|
19
|
+
`include-non-text` warning instead of being spliced (#215,
|
|
20
|
+
markup-carve/carve-rs#2885).
|
|
21
|
+
|
|
22
|
+
### Fixes
|
|
23
|
+
|
|
24
|
+
- The carve-lang 0.1.11 pin corrects container ownership for quotes, lazy lines,
|
|
25
|
+
colon divs and footnotes, fences inside nested lists and quotes, and list
|
|
26
|
+
tightness around captions (#215).
|
|
27
|
+
- Includes see through a byte order mark and keep link definitions and region
|
|
28
|
+
markers in place. Markdown import keeps link destinations, autolink brackets
|
|
29
|
+
and pipe escapes as written (#215).
|
|
30
|
+
- `Carve.render_with_includes` with the `markdown`, `plain` and `ansi` targets
|
|
31
|
+
keeps a link definition where it was written, as `Carve.to_markdown` does,
|
|
32
|
+
instead of moving it to the end (#221).
|
|
33
|
+
|
|
34
|
+
### Improvements
|
|
35
|
+
|
|
36
|
+
- Precompiled gems for `x86_64-linux`, `x86_64-linux-musl`, `aarch64-linux`,
|
|
37
|
+
`x86_64-darwin`, `arm64-darwin` and `x64-mingw-ucrt` install without a Rust
|
|
38
|
+
toolchain on Ruby 3.0 to 4.0 (#214).
|
|
39
|
+
- `Carve.to_carve(source, align_tables: true)` pads table columns to display
|
|
40
|
+
width (#215).
|
|
41
|
+
- `Carve.select_include_region(source, name, mode:)` resolves a named
|
|
42
|
+
`@region` to a Range of content lines and raises `Carve::IncludeRegionError`
|
|
43
|
+
for a missing or malformed region (#215).
|
|
44
|
+
- An include with `@code` or `@code:lang` splices a file or region as one code
|
|
45
|
+
block (#215).
|
|
46
|
+
- Include warnings carry `includedBy`, the chain of directives that reached the
|
|
47
|
+
file the warning arose in, with root-relative paths, lines and columns (#221).
|
|
48
|
+
|
|
10
49
|
## [0.1.9] - 2026-10-18
|
|
11
50
|
|
|
12
51
|
### Breaking
|
|
@@ -488,7 +527,8 @@ are not listed, because no release ever shipped them.
|
|
|
488
527
|
Arrays (every AST node type is covered), enabling custom renderers such as
|
|
489
528
|
[carve-hexapdf](https://github.com/markup-carve/carve-hexapdf).
|
|
490
529
|
|
|
491
|
-
[Unreleased]: https://github.com/markup-carve/carve-rb/compare/v0.1.
|
|
530
|
+
[Unreleased]: https://github.com/markup-carve/carve-rb/compare/v0.1.10...HEAD
|
|
531
|
+
[0.1.10]: https://github.com/markup-carve/carve-rb/compare/v0.1.9...v0.1.10
|
|
492
532
|
[0.1.9]: https://github.com/markup-carve/carve-rb/compare/v0.1.8...v0.1.9
|
|
493
533
|
[0.1.8]: https://github.com/markup-carve/carve-rb/compare/v0.1.7...v0.1.8
|
|
494
534
|
[0.1.7]: https://github.com/markup-carve/carve-rb/compare/v0.1.6...v0.1.7
|
data/README.md
CHANGED
|
@@ -16,7 +16,48 @@ bundle install
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
The distribution name is `carve-lang`, while the require path is `carve`.
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
Releases after 0.1.9 ship precompiled gems for `x86_64-linux`,
|
|
21
|
+
`x86_64-linux-musl`, `aarch64-linux`, `x86_64-darwin`, `arm64-darwin` and
|
|
22
|
+
`x64-mingw-ucrt`, with a binary for each Ruby from 3.0 to 4.0 (from 3.1 on
|
|
23
|
+
Windows, where Ruby 3.0 has no UCRT build). On those platforms `gem install`
|
|
24
|
+
needs no Rust toolchain or libclang. Everywhere else, including newer Rubies,
|
|
25
|
+
RubyGems installs the source gem and builds it as described below.
|
|
26
|
+
|
|
27
|
+
## Installing from source
|
|
28
|
+
|
|
29
|
+
Version 0.1.9 and earlier ship source only, and so does every release on a
|
|
30
|
+
platform or Ruby without a precompiled gem. There `gem install carve-lang`
|
|
31
|
+
compiles a Rust extension on your machine, so the install needs:
|
|
32
|
+
|
|
33
|
+
- A Rust toolchain with `cargo`, Rust 1.77 or newer. [rustup](https://rustup.rs)
|
|
34
|
+
is the usual way to get one.
|
|
35
|
+
- Ruby development headers (`ruby-dev` on Debian and Ubuntu).
|
|
36
|
+
- libclang and clang. The build runs `bindgen` through `rb-sys` to read Ruby's
|
|
37
|
+
C headers. On Debian and Ubuntu:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sudo apt install libclang-dev clang
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If only libclang is installed, without the `clang` package, the build stops
|
|
44
|
+
with this error:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
/usr/include/ruby-3.2.0/ruby/ruby.h:23:10: fatal error: 'stdarg.h' file not found
|
|
48
|
+
thread 'main' panicked at .../rb-sys-0.9.128/build/main.rs:52:6:
|
|
49
|
+
generate bindings: ClangDiagnostic("/usr/include/ruby-3.2.0/ruby/ruby.h:23:10: fatal error: 'stdarg.h' file not found\n")
|
|
50
|
+
error: failed to run custom build command for `rb-sys v0.9.128`
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
libclang cannot find its own builtin headers. Install `clang`, or point
|
|
54
|
+
bindgen at GCC's copy of those headers:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
BINDGEN_EXTRA_CLANG_ARGS="-I$(gcc -print-file-name=include)" gem install carve-lang
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
With Bundler, export the variable before running `bundle install`.
|
|
20
61
|
|
|
21
62
|
## Render Carve
|
|
22
63
|
|
|
@@ -28,6 +69,7 @@ Carve.to_markdown(source)
|
|
|
28
69
|
Carve.to_plain_text(source)
|
|
29
70
|
Carve.to_ansi(source)
|
|
30
71
|
Carve.to_carve(source)
|
|
72
|
+
Carve.to_carve(source, align_tables: true) # pad table columns
|
|
31
73
|
```
|
|
32
74
|
|
|
33
75
|
Options configure extensions, safe rendering, profiles, symbols, section
|
|
@@ -91,7 +133,9 @@ result = Carve.to_html_with_includes(
|
|
|
91
133
|
)
|
|
92
134
|
```
|
|
93
135
|
|
|
94
|
-
The result contains the value, warnings, and root-relative dependencies.
|
|
136
|
+
The result contains the value, warnings, and root-relative dependencies. A
|
|
137
|
+
warning from an included file names the chain of directives that reached it in
|
|
138
|
+
`:includedBy`.
|
|
95
139
|
Other render methods leave directives literal. See the
|
|
96
140
|
[include reference](docs/reference.md#file-includes) for budgets, AST expansion,
|
|
97
141
|
targets, and containment behavior.
|
data/ext/carve/Cargo.lock
CHANGED
|
@@ -37,9 +37,9 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
|
|
37
37
|
|
|
38
38
|
[[package]]
|
|
39
39
|
name = "carve-lang"
|
|
40
|
-
version = "0.1.
|
|
40
|
+
version = "0.1.11"
|
|
41
41
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
checksum = "
|
|
42
|
+
checksum = "753b90fbe80fab43887d5d559996c153347d9631452bbcb8d0283aad6dfa7ef8"
|
|
43
43
|
dependencies = [
|
|
44
44
|
"html5ever",
|
|
45
45
|
"markup5ever_rcdom",
|
|
@@ -54,7 +54,7 @@ dependencies = [
|
|
|
54
54
|
|
|
55
55
|
[[package]]
|
|
56
56
|
name = "carve-rb"
|
|
57
|
-
version = "0.1.
|
|
57
|
+
version = "0.1.10"
|
|
58
58
|
dependencies = [
|
|
59
59
|
"carve-lang",
|
|
60
60
|
"magnus",
|
data/ext/carve/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "carve-rb"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.10"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
rust-version = "1.75"
|
|
6
6
|
publish = false
|
|
@@ -22,4 +22,4 @@ serde_json = "1"
|
|
|
22
22
|
# The Carve engine. Imported under the Rust crate alias `carve_rs` so the
|
|
23
23
|
# binding's own crate name (`carve-rb`) does not collide with the engine's
|
|
24
24
|
# package name (`carve`) in the Cargo lockfile.
|
|
25
|
-
carve_rs = { package = "carve-lang", version = "=0.1.
|
|
25
|
+
carve_rs = { package = "carve-lang", version = "=0.1.11" }
|
data/ext/carve/src/lib.rs
CHANGED
|
@@ -226,8 +226,44 @@ fn to_ansi(source: String) -> String {
|
|
|
226
226
|
carve_rs::to_ansi(&source)
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
/// Canonical Carve source; a writer refusal returns the source as authored.
|
|
230
|
+
fn to_carve(source: String, align_tables: bool) -> String {
|
|
231
|
+
let options = Options::default().with_align_tables(align_tables);
|
|
232
|
+
carve_rs::try_to_carve_with_options(&source, &options).unwrap_or(source)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// Resolve `@region:name` with the engine's marker rules (spec I1b).
|
|
236
|
+
///
|
|
237
|
+
/// Returns `[start, end]` (1-based, inclusive content lines) or, when the
|
|
238
|
+
/// region is invalid, the warning rule id as a String; `lib/carve.rb` turns
|
|
239
|
+
/// that into `Carve::IncludeRegionError`.
|
|
240
|
+
fn select_include_region(
|
|
241
|
+
ruby: &Ruby,
|
|
242
|
+
source: String,
|
|
243
|
+
name: String,
|
|
244
|
+
mode: String,
|
|
245
|
+
) -> Result<Value, Error> {
|
|
246
|
+
let mode = match mode.as_str() {
|
|
247
|
+
"carve" => carve_rs::IncludeRegionMode::Carve,
|
|
248
|
+
"code" => carve_rs::IncludeRegionMode::Code,
|
|
249
|
+
other => {
|
|
250
|
+
return Err(Error::new(
|
|
251
|
+
ruby.exception_arg_error(),
|
|
252
|
+
format!("mode must be :carve or :code, got {other:?}"),
|
|
253
|
+
))
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
Ok(
|
|
257
|
+
match carve_rs::select_include_region(&source, &name, mode) {
|
|
258
|
+
Ok(region) => ruby
|
|
259
|
+
.ary_new_from_values(&[
|
|
260
|
+
ruby.integer_from_u64(region.start as u64).as_value(),
|
|
261
|
+
ruby.integer_from_u64(region.end as u64).as_value(),
|
|
262
|
+
])
|
|
263
|
+
.as_value(),
|
|
264
|
+
Err(e) => ruby.str_new(e.rule()).as_value(),
|
|
265
|
+
},
|
|
266
|
+
)
|
|
231
267
|
}
|
|
232
268
|
|
|
233
269
|
/// Which renderer runs over the expanded document.
|
|
@@ -344,6 +380,12 @@ fn render_with_includes_json(
|
|
|
344
380
|
if !sections {
|
|
345
381
|
render_options = render_options.with_sections(false);
|
|
346
382
|
}
|
|
383
|
+
// Positions let the engine record the directive chain behind a warning
|
|
384
|
+
// (`includedBy`). The AST target keeps them off: spec I4 leaves position
|
|
385
|
+
// remapping out of scope, so a child's spans would point into the wrong file.
|
|
386
|
+
if which != IncludeTarget::Ast {
|
|
387
|
+
render_options = render_options.with_positions(true);
|
|
388
|
+
}
|
|
347
389
|
let mut include_options = carve_rs::IncludeOptions::new()
|
|
348
390
|
.with_resolver(&resolver)
|
|
349
391
|
.with_source_path(source_real.to_string_lossy());
|
|
@@ -419,6 +461,17 @@ fn render_with_includes_json(
|
|
|
419
461
|
"rule": warning.rule,
|
|
420
462
|
"message": warning.message,
|
|
421
463
|
"file": warning.file.as_deref().map(&safe_path),
|
|
464
|
+
"includedBy": warning
|
|
465
|
+
.included_by
|
|
466
|
+
.iter()
|
|
467
|
+
.map(|site| {
|
|
468
|
+
serde_json::json!({
|
|
469
|
+
"file": safe_path(&site.file),
|
|
470
|
+
"line": site.line,
|
|
471
|
+
"column": site.column,
|
|
472
|
+
})
|
|
473
|
+
})
|
|
474
|
+
.collect::<Vec<_>>(),
|
|
422
475
|
})
|
|
423
476
|
})
|
|
424
477
|
.collect::<Vec<_>>();
|
|
@@ -897,7 +950,11 @@ guarded!(g_to_html(source: Source) -> String => to_html);
|
|
|
897
950
|
guarded!(g_to_markdown(source: Source) -> String => to_markdown);
|
|
898
951
|
guarded!(g_to_plain_text(source: Source) -> String => to_plain_text);
|
|
899
952
|
guarded!(g_to_ansi(source: Source) -> String => to_ansi);
|
|
900
|
-
guarded!(g_to_carve(source: Source) -> String => to_carve);
|
|
953
|
+
guarded!(g_to_carve(source: Source, align_tables: bool) -> String => to_carve);
|
|
954
|
+
guarded!(
|
|
955
|
+
g_select_include_region(ruby: &Ruby, source: Source, name: String, mode: String) -> Value
|
|
956
|
+
=>? select_include_region
|
|
957
|
+
);
|
|
901
958
|
guarded!(g_from_markdown_json(ruby: &Ruby, source: Source) -> String =>? from_markdown_json);
|
|
902
959
|
guarded!(g_to_ast_json(source: Source) -> String => to_ast_json);
|
|
903
960
|
guarded!(g_extension_names() -> Vec<String> => extension_names);
|
|
@@ -990,7 +1047,11 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
990
1047
|
module.define_singleton_method("to_markdown", function!(g_to_markdown, 1))?;
|
|
991
1048
|
module.define_singleton_method("to_plain_text", function!(g_to_plain_text, 1))?;
|
|
992
1049
|
module.define_singleton_method("to_ansi", function!(g_to_ansi, 1))?;
|
|
993
|
-
module.define_singleton_method("
|
|
1050
|
+
module.define_singleton_method("_to_carve", function!(g_to_carve, 2))?;
|
|
1051
|
+
module.define_singleton_method(
|
|
1052
|
+
"_select_include_region",
|
|
1053
|
+
function!(g_select_include_region, 3),
|
|
1054
|
+
)?;
|
|
994
1055
|
module.define_singleton_method(
|
|
995
1056
|
"_render_with_includes_json",
|
|
996
1057
|
function!(g_render_with_includes_json, 15),
|
data/lib/carve/version.rb
CHANGED
|
@@ -8,5 +8,5 @@ module Carve
|
|
|
8
8
|
# compares it against ext/carve/Cargo.toml and against the newest cut
|
|
9
9
|
# CHANGELOG section on every run, and .github/workflows/release.yml refuses to
|
|
10
10
|
# publish a gem whose version is not the tag being released.
|
|
11
|
-
VERSION = "0.1.
|
|
11
|
+
VERSION = "0.1.10"
|
|
12
12
|
end
|
data/lib/carve.rb
CHANGED
|
@@ -8,11 +8,30 @@
|
|
|
8
8
|
require "json"
|
|
9
9
|
require_relative "carve/version"
|
|
10
10
|
|
|
11
|
-
# Load the compiled native extension.
|
|
12
|
-
# lib/carve
|
|
13
|
-
|
|
11
|
+
# Load the compiled native extension. A precompiled platform gem ships one per
|
|
12
|
+
# Ruby ABI under lib/carve/<major.minor>/; a source install and `rake compile`
|
|
13
|
+
# build lib/carve/carve.so (or .bundle on macOS).
|
|
14
|
+
# Chosen by directory rather than by rescuing LoadError, so a binary that is
|
|
15
|
+
# present but cannot load (an older glibc, say) reports its own error.
|
|
16
|
+
abi = RUBY_VERSION[/\A\d+\.\d+/]
|
|
17
|
+
if File.directory?(File.join(__dir__, "carve", abi))
|
|
18
|
+
require_relative "carve/#{abi}/carve"
|
|
19
|
+
else
|
|
20
|
+
require_relative "carve/carve"
|
|
21
|
+
end
|
|
14
22
|
|
|
15
23
|
module Carve
|
|
24
|
+
# Raised by .select_include_region when the region is missing or a marker is
|
|
25
|
+
# malformed, unpaired or duplicated. +rule+ is the engine's warning rule id.
|
|
26
|
+
class IncludeRegionError < ArgumentError
|
|
27
|
+
attr_reader :rule
|
|
28
|
+
|
|
29
|
+
def initialize(rule, name)
|
|
30
|
+
@rule = rule
|
|
31
|
+
super("#{rule}: region #{name.inspect} is missing or its markers are malformed")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
16
35
|
# Extension names the native binding understands.
|
|
17
36
|
#
|
|
18
37
|
# Taken from the engine itself rather than typed out here. This was a literal
|
|
@@ -55,7 +74,8 @@ module Carve
|
|
|
55
74
|
#
|
|
56
75
|
# Returns +{value:, warnings:, dependencies:, suppressedWarnings:}+. Paths
|
|
57
76
|
# in +warnings+ and +dependencies+ are relative to the root, so a report can
|
|
58
|
-
# be shown to a reader as it stands.
|
|
77
|
+
# be shown to a reader as it stands. Each warning's +includedBy+ lists the
|
|
78
|
+
# directives that reached its file, top-level first, as +{file:, line:, column:}+.
|
|
59
79
|
#
|
|
60
80
|
# Every render option +to_html+ takes is accepted and reaches the included
|
|
61
81
|
# children too: an extension, a symbol map or a profile means the same thing
|
|
@@ -130,6 +150,28 @@ module Carve
|
|
|
130
150
|
)
|
|
131
151
|
end
|
|
132
152
|
|
|
153
|
+
# Format +source+ into canonical Carve. A writer refusal returns the source
|
|
154
|
+
# as authored. +align_tables:+ pads table cells into aligned columns.
|
|
155
|
+
def to_carve(source, align_tables: false)
|
|
156
|
+
_to_carve(source, align_tables ? true : false)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Resolve <tt>@region:name</tt> in a file's text with the engine's marker
|
|
160
|
+
# rules, so an editor jumping to a region agrees with the render.
|
|
161
|
+
#
|
|
162
|
+
# Carve.select_include_region(text, "setup", mode: :carve) # => 3..4
|
|
163
|
+
#
|
|
164
|
+
# Returns the 1-based, inclusive Range of content lines; an empty region is
|
|
165
|
+
# an empty Range such as +2..1+. +mode: :carve+ accepts only
|
|
166
|
+
# <tt>%% #region</tt> markers, +mode: :code+ also the source-comment forms
|
|
167
|
+
# of an +@code+ include. Raises IncludeRegionError otherwise.
|
|
168
|
+
def select_include_region(source, name, mode:)
|
|
169
|
+
result = _select_include_region(source, name.to_s, mode.to_s)
|
|
170
|
+
raise IncludeRegionError.new(result, name.to_s) if result.is_a?(String)
|
|
171
|
+
|
|
172
|
+
result[0]..result[1]
|
|
173
|
+
end
|
|
174
|
+
|
|
133
175
|
# Import HTML or Markdown into canonical Carve. Both methods return the
|
|
134
176
|
# shared migration shape `{ value:, report: }`.
|
|
135
177
|
def from_html(source, mode: :safe)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: carve-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- markup-carve
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rb_sys
|