carve-lang 0.1.8 → 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 +67 -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 +124 -23
- data/lib/carve/version.rb +1 -1
- data/lib/carve.rb +49 -6
- 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,70 @@ 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
|
+
|
|
49
|
+
## [0.1.9] - 2026-10-18
|
|
50
|
+
|
|
51
|
+
### Breaking
|
|
52
|
+
|
|
53
|
+
- `Carve.parse` and AST include results carry `astVersion: "1.2"` on the root.
|
|
54
|
+
Exact-key readers must accept the new field (#210).
|
|
55
|
+
- The carve-lang 0.1.10 pin adds `resolved` to resolved link and image references
|
|
56
|
+
and `form` to comments. `[text]()` and `![alt]()` render as links and images,
|
|
57
|
+
and a bare `[r]:` defines an empty destination. Escape `\[` to keep that
|
|
58
|
+
spelling literal. Checked render reports can also include
|
|
59
|
+
`table-span-flattened`, `table-header-row-demoted`,
|
|
60
|
+
`table-header-row-hoisted`, and `table-header-cell-demoted` (#207).
|
|
61
|
+
|
|
62
|
+
### Fixes
|
|
63
|
+
|
|
64
|
+
- Source strings in other encodings such as Shift_JIS are transcoded to UTF-8.
|
|
65
|
+
Malformed UTF-8 and binary bytes become replacement characters instead of
|
|
66
|
+
failing at the native boundary (#207).
|
|
67
|
+
|
|
68
|
+
### Improvements
|
|
69
|
+
|
|
70
|
+
- The carve-lang 0.1.10 pin adds CSV and TSV table fences and keeps plain-text
|
|
71
|
+
list continuation blocks at their item's indentation. It also corrects
|
|
72
|
+
parsing of nested lists with quotes and containers (#207).
|
|
73
|
+
|
|
10
74
|
## [0.1.8] - 2026-10-14
|
|
11
75
|
|
|
12
76
|
### Breaking
|
|
@@ -463,7 +527,9 @@ are not listed, because no release ever shipped them.
|
|
|
463
527
|
Arrays (every AST node type is covered), enabling custom renderers such as
|
|
464
528
|
[carve-hexapdf](https://github.com/markup-carve/carve-hexapdf).
|
|
465
529
|
|
|
466
|
-
[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
|
|
532
|
+
[0.1.9]: https://github.com/markup-carve/carve-rb/compare/v0.1.8...v0.1.9
|
|
467
533
|
[0.1.8]: https://github.com/markup-carve/carve-rb/compare/v0.1.7...v0.1.8
|
|
468
534
|
[0.1.7]: https://github.com/markup-carve/carve-rb/compare/v0.1.6...v0.1.7
|
|
469
535
|
[0.1.6]: https://github.com/markup-carve/carve-rb/compare/v0.1.5...v0.1.6
|
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
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
use carve_rs::extensions::registry;
|
|
28
28
|
use carve_rs::{CarveExtension, Mode, Options, Profile, StaticRenderers};
|
|
29
29
|
use magnus::value::{InnerValue, Opaque};
|
|
30
|
-
use magnus::{
|
|
30
|
+
use magnus::{
|
|
31
|
+
function, prelude::*, Error, ExceptionClass, RArray, RHash, RString, Ruby, TryConvert, Value,
|
|
32
|
+
};
|
|
31
33
|
use std::any::Any;
|
|
32
34
|
use std::cell::RefCell;
|
|
33
35
|
use std::panic::{self, AssertUnwindSafe};
|
|
@@ -97,6 +99,14 @@ fn extension_names() -> Vec<String> {
|
|
|
97
99
|
registry::keys().map(str::to_string).collect()
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
fn ast_json_with_version(json: String) -> String {
|
|
103
|
+
let (body, closing) = json.split_at(json.len() - 1);
|
|
104
|
+
format!(
|
|
105
|
+
"{body},\"astVersion\":\"{}\"{closing}",
|
|
106
|
+
carve_rs::AST_CONTRACT_VERSION
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
/// Map a Ruby-facing mode string to a carve-rs [`Mode`].
|
|
101
111
|
///
|
|
102
112
|
/// Rejects any unknown string with `ArgumentError`, mirroring the spec's
|
|
@@ -216,8 +226,44 @@ fn to_ansi(source: String) -> String {
|
|
|
216
226
|
carve_rs::to_ansi(&source)
|
|
217
227
|
}
|
|
218
228
|
|
|
219
|
-
|
|
220
|
-
|
|
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
|
+
)
|
|
221
267
|
}
|
|
222
268
|
|
|
223
269
|
/// Which renderer runs over the expanded document.
|
|
@@ -334,6 +380,12 @@ fn render_with_includes_json(
|
|
|
334
380
|
if !sections {
|
|
335
381
|
render_options = render_options.with_sections(false);
|
|
336
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
|
+
}
|
|
337
389
|
let mut include_options = carve_rs::IncludeOptions::new()
|
|
338
390
|
.with_resolver(&resolver)
|
|
339
391
|
.with_source_path(source_real.to_string_lossy());
|
|
@@ -378,7 +430,7 @@ fn render_with_includes_json(
|
|
|
378
430
|
// parse JSON twice. Positions stay OFF, unlike `to_ast_json`: spec I4
|
|
379
431
|
// leaves position remapping out of scope in every engine, so a child's
|
|
380
432
|
// spans would point into a document the caller never passed.
|
|
381
|
-
IncludeTarget::Ast => Ok(carve_rs::to_json(&prepared.doc)),
|
|
433
|
+
IncludeTarget::Ast => Ok(ast_json_with_version(carve_rs::to_json(&prepared.doc))),
|
|
382
434
|
}
|
|
383
435
|
.map_err(|error| Error::new(ruby.exception_runtime_error(), error.to_string()))?;
|
|
384
436
|
let value: serde_json::Value = if which == IncludeTarget::Ast {
|
|
@@ -409,6 +461,17 @@ fn render_with_includes_json(
|
|
|
409
461
|
"rule": warning.rule,
|
|
410
462
|
"message": warning.message,
|
|
411
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<_>>(),
|
|
412
475
|
})
|
|
413
476
|
})
|
|
414
477
|
.collect::<Vec<_>>();
|
|
@@ -522,7 +585,9 @@ fn migration_diagnostic_json(diagnostic: &carve_rs::MigrationDiagnostic) -> serd
|
|
|
522
585
|
fn to_ast_json(source: String) -> String {
|
|
523
586
|
let mut options = Options::new();
|
|
524
587
|
options.positions = true;
|
|
525
|
-
carve_rs::to_json(&carve_rs::parse_with_options(
|
|
588
|
+
ast_json_with_version(carve_rs::to_json(&carve_rs::parse_with_options(
|
|
589
|
+
&source, &options,
|
|
590
|
+
)))
|
|
526
591
|
}
|
|
527
592
|
|
|
528
593
|
/// Render Carve source to HTML with the named extensions enabled.
|
|
@@ -842,42 +907,74 @@ macro_rules! guarded {
|
|
|
842
907
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty => $inner:ident) => {
|
|
843
908
|
#[allow(clippy::too_many_arguments)]
|
|
844
909
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
845
|
-
guard(|| Ok($inner($($arg),*)))
|
|
910
|
+
guard(|| Ok($inner($($arg.into()),*)))
|
|
846
911
|
}
|
|
847
912
|
};
|
|
848
913
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty =>? $inner:ident) => {
|
|
849
914
|
#[allow(clippy::too_many_arguments)]
|
|
850
915
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
851
|
-
guard(|| $inner($($arg),*))
|
|
916
|
+
guard(|| $inner($($arg.into()),*))
|
|
852
917
|
}
|
|
853
918
|
};
|
|
854
919
|
}
|
|
855
920
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
921
|
+
struct Source(String);
|
|
922
|
+
|
|
923
|
+
impl TryConvert for Source {
|
|
924
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
925
|
+
let source = RString::try_convert(value)?;
|
|
926
|
+
let encoding: Value = source.funcall("encoding", ())?;
|
|
927
|
+
let name: String = encoding.funcall("name", ())?;
|
|
928
|
+
let text = if name == "ASCII-8BIT" {
|
|
929
|
+
String::from_utf8_lossy(unsafe { source.as_slice() }).into_owned()
|
|
930
|
+
} else {
|
|
931
|
+
let ruby = Ruby::get_with(source);
|
|
932
|
+
let options = ruby.hash_new();
|
|
933
|
+
options.aset(ruby.to_symbol("invalid"), ruby.to_symbol("replace"))?;
|
|
934
|
+
options.aset(ruby.to_symbol("undef"), ruby.to_symbol("replace"))?;
|
|
935
|
+
options.aset(ruby.to_symbol("replace"), "\u{FFFD}")?;
|
|
936
|
+
let encoded: RString = source.funcall("encode", ("UTF-8", magnus::KwArgs(options)))?;
|
|
937
|
+
encoded.to_string()?
|
|
938
|
+
};
|
|
939
|
+
Ok(Self(text))
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
impl From<Source> for String {
|
|
944
|
+
fn from(source: Source) -> Self {
|
|
945
|
+
source.0
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
guarded!(g_to_html(source: Source) -> String => to_html);
|
|
950
|
+
guarded!(g_to_markdown(source: Source) -> String => to_markdown);
|
|
951
|
+
guarded!(g_to_plain_text(source: Source) -> String => to_plain_text);
|
|
952
|
+
guarded!(g_to_ansi(source: Source) -> String => to_ansi);
|
|
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
|
+
);
|
|
958
|
+
guarded!(g_from_markdown_json(ruby: &Ruby, source: Source) -> String =>? from_markdown_json);
|
|
959
|
+
guarded!(g_to_ast_json(source: Source) -> String => to_ast_json);
|
|
863
960
|
guarded!(g_extension_names() -> Vec<String> => extension_names);
|
|
864
961
|
guarded!(g_panic_probe() -> String => panic_probe);
|
|
865
962
|
guarded!(
|
|
866
|
-
g_stamp_needs_review(source:
|
|
963
|
+
g_stamp_needs_review(source: Source, current_version: Option<String>) -> bool
|
|
867
964
|
=> stamp_needs_review
|
|
868
965
|
);
|
|
869
966
|
guarded!(
|
|
870
|
-
g_from_html_json(ruby: &Ruby, source:
|
|
967
|
+
g_from_html_json(ruby: &Ruby, source: Source, mode: String) -> String =>? from_html_json
|
|
871
968
|
);
|
|
872
|
-
guarded!(g_read_stamp(ruby: &Ruby, source:
|
|
969
|
+
guarded!(g_read_stamp(ruby: &Ruby, source: Source) -> Value =>? read_stamp);
|
|
873
970
|
guarded!(
|
|
874
|
-
g_to_html_with_extensions(ruby: &Ruby, source:
|
|
971
|
+
g_to_html_with_extensions(ruby: &Ruby, source: Source, names: RArray) -> String
|
|
875
972
|
=>? to_html_with_extensions
|
|
876
973
|
);
|
|
877
974
|
guarded!(
|
|
878
975
|
g_to_html_full(
|
|
879
976
|
ruby: &Ruby,
|
|
880
|
-
source:
|
|
977
|
+
source: Source,
|
|
881
978
|
names: RArray,
|
|
882
979
|
mode: String,
|
|
883
980
|
renderers: RHash
|
|
@@ -886,7 +983,7 @@ guarded!(
|
|
|
886
983
|
guarded!(
|
|
887
984
|
g_to_html_full_with_symbols(
|
|
888
985
|
ruby: &Ruby,
|
|
889
|
-
source:
|
|
986
|
+
source: Source,
|
|
890
987
|
names: RArray,
|
|
891
988
|
mode: String,
|
|
892
989
|
renderers: RHash,
|
|
@@ -896,7 +993,7 @@ guarded!(
|
|
|
896
993
|
guarded!(
|
|
897
994
|
g_to_html_safe(
|
|
898
995
|
ruby: &Ruby,
|
|
899
|
-
source:
|
|
996
|
+
source: Source,
|
|
900
997
|
names: RArray,
|
|
901
998
|
mode: String,
|
|
902
999
|
renderers: RHash,
|
|
@@ -909,7 +1006,7 @@ guarded!(
|
|
|
909
1006
|
guarded!(
|
|
910
1007
|
g_render_with_includes_json(
|
|
911
1008
|
ruby: &Ruby,
|
|
912
|
-
source:
|
|
1009
|
+
source: Source,
|
|
913
1010
|
root: String,
|
|
914
1011
|
source_path: String,
|
|
915
1012
|
target: String,
|
|
@@ -950,7 +1047,11 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
950
1047
|
module.define_singleton_method("to_markdown", function!(g_to_markdown, 1))?;
|
|
951
1048
|
module.define_singleton_method("to_plain_text", function!(g_to_plain_text, 1))?;
|
|
952
1049
|
module.define_singleton_method("to_ansi", function!(g_to_ansi, 1))?;
|
|
953
|
-
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
|
+
)?;
|
|
954
1055
|
module.define_singleton_method(
|
|
955
1056
|
"_render_with_includes_json",
|
|
956
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)
|
|
@@ -319,9 +361,10 @@ module Carve
|
|
|
319
361
|
# # => {type: "document",
|
|
320
362
|
# # children: [{type: "heading", level: 1,
|
|
321
363
|
# # children: [{type: "text", value: "Hi"}], attrs: nil}],
|
|
322
|
-
# # srcByteLength: 4}
|
|
364
|
+
# # srcByteLength: 4, astVersion: "1.2"}
|
|
323
365
|
#
|
|
324
|
-
# The root carries exactly +:type+, +:children
|
|
366
|
+
# The root carries exactly +:type+, +:children+, +:srcByteLength+ and
|
|
367
|
+
# +:astVersion+ (spec
|
|
325
368
|
# PART 12 §7). Frontmatter and footnote definitions are block nodes in
|
|
326
369
|
# +:children+, not root fields: a root field cannot carry the position §4
|
|
327
370
|
# requires of every node, and both are source an editor navigates to.
|
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
|