carve-lang 0.1.7 → 0.1.9
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 +47 -1
- data/ext/carve/Cargo.lock +10 -3
- data/ext/carve/Cargo.toml +2 -2
- data/ext/carve/src/lib.rs +68 -23
- data/lib/carve/version.rb +1 -1
- data/lib/carve.rb +3 -2
- 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: 796f44a79683bfd2b7add107652d71a7d38aaf6d4aedf7169acde4fa2401af26
|
|
4
|
+
data.tar.gz: d7f0ba287fc9c13eab535e7e376de3096f1baedfd1df415b5f870ec7bfca395f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aec21e3a2aafe3fd84c6debf48af398ea241610d606b954c770a84bd6b5e38b90b0b1d056ecb4c47f8ba9ec976d85498a5d3013ab6885e1b9fe8fdae572d516b
|
|
7
|
+
data.tar.gz: 6adc9afe363cd5c135a649b6f336cf41a55b6197a4cb8927273fd57fe4e0c484d47bbe159a8469eea11dc49a41771637ca048712bd596a9bd9515e099e0f611a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.9] - 2026-10-18
|
|
11
|
+
|
|
12
|
+
### Breaking
|
|
13
|
+
|
|
14
|
+
- `Carve.parse` and AST include results carry `astVersion: "1.2"` on the root.
|
|
15
|
+
Exact-key readers must accept the new field (#210).
|
|
16
|
+
- The carve-lang 0.1.10 pin adds `resolved` to resolved link and image references
|
|
17
|
+
and `form` to comments. `[text]()` and `![alt]()` render as links and images,
|
|
18
|
+
and a bare `[r]:` defines an empty destination. Escape `\[` to keep that
|
|
19
|
+
spelling literal. Checked render reports can also include
|
|
20
|
+
`table-span-flattened`, `table-header-row-demoted`,
|
|
21
|
+
`table-header-row-hoisted`, and `table-header-cell-demoted` (#207).
|
|
22
|
+
|
|
23
|
+
### Fixes
|
|
24
|
+
|
|
25
|
+
- Source strings in other encodings such as Shift_JIS are transcoded to UTF-8.
|
|
26
|
+
Malformed UTF-8 and binary bytes become replacement characters instead of
|
|
27
|
+
failing at the native boundary (#207).
|
|
28
|
+
|
|
29
|
+
### Improvements
|
|
30
|
+
|
|
31
|
+
- The carve-lang 0.1.10 pin adds CSV and TSV table fences and keeps plain-text
|
|
32
|
+
list continuation blocks at their item's indentation. It also corrects
|
|
33
|
+
parsing of nested lists with quotes and containers (#207).
|
|
34
|
+
|
|
35
|
+
## [0.1.8] - 2026-10-14
|
|
36
|
+
|
|
37
|
+
### Breaking
|
|
38
|
+
|
|
39
|
+
- The engine is the published `carve-lang` 0.1.9 crate, so the AST follows contract 1.1: a cell joined across lines holds a `cell_wrap` node between its text halves, tables carry `separatorWidths`, and `delim` and `bulletChar` appear only for `)` and `*` (#200, markup-carve/carve-rs#2482).
|
|
40
|
+
- New loss codes such as `carrier-attribute-dropped`, `list-style-dropped` and `frontmatter-synthesized`. A check that fails on unknown codes has to list them (#200, markup-carve/carve-rs#2523).
|
|
41
|
+
- Image alt text and quoted include paths decode punctuation escapes, so double a literal backslash. The include rename warning's rule id is now `include-id-rename` instead of `include-heading-id-rename` (#184, markup-carve/carve#2772).
|
|
42
|
+
|
|
43
|
+
### Fixes
|
|
44
|
+
|
|
45
|
+
- A Markdown writer refusal raises `ArgumentError` instead of panicking in the engine (#184).
|
|
46
|
+
- Adjacent alphabetic and Roman ordered lists stay separate through HTML import and formatting (#185).
|
|
47
|
+
- Under the article profile, denied raw blocks become code blocks that keep their payload (#185).
|
|
48
|
+
|
|
49
|
+
### Improvements
|
|
50
|
+
|
|
51
|
+
- Markdown import reports assess construct fidelity, including preserved constructs as well as losses (#185).
|
|
52
|
+
- Ordered lists keep a `)` delimiter as `data-delim` in HTML and through Markdown import (#185).
|
|
53
|
+
|
|
10
54
|
## [0.1.7] - 2026-10-07
|
|
11
55
|
|
|
12
56
|
### Changed
|
|
@@ -444,7 +488,9 @@ are not listed, because no release ever shipped them.
|
|
|
444
488
|
Arrays (every AST node type is covered), enabling custom renderers such as
|
|
445
489
|
[carve-hexapdf](https://github.com/markup-carve/carve-hexapdf).
|
|
446
490
|
|
|
447
|
-
[Unreleased]: https://github.com/markup-carve/carve-rb/compare/v0.1.
|
|
491
|
+
[Unreleased]: https://github.com/markup-carve/carve-rb/compare/v0.1.9...HEAD
|
|
492
|
+
[0.1.9]: https://github.com/markup-carve/carve-rb/compare/v0.1.8...v0.1.9
|
|
493
|
+
[0.1.8]: https://github.com/markup-carve/carve-rb/compare/v0.1.7...v0.1.8
|
|
448
494
|
[0.1.7]: https://github.com/markup-carve/carve-rb/compare/v0.1.6...v0.1.7
|
|
449
495
|
[0.1.6]: https://github.com/markup-carve/carve-rb/compare/v0.1.5...v0.1.6
|
|
450
496
|
[0.1.5]: https://github.com/markup-carve/carve-rb/compare/v0.1.4...v0.1.5
|
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.10"
|
|
41
41
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
checksum = "
|
|
42
|
+
checksum = "4a9b7a2a3b758f76ce1888ad1371bd933c5f2ccded01305c03f1550641a45c1d"
|
|
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.9"
|
|
58
58
|
dependencies = [
|
|
59
59
|
"carve-lang",
|
|
60
60
|
"magnus",
|
|
@@ -327,9 +327,16 @@ checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e"
|
|
|
327
327
|
dependencies = [
|
|
328
328
|
"bitflags",
|
|
329
329
|
"memchr",
|
|
330
|
+
"pulldown-cmark-escape",
|
|
330
331
|
"unicase",
|
|
331
332
|
]
|
|
332
333
|
|
|
334
|
+
[[package]]
|
|
335
|
+
name = "pulldown-cmark-escape"
|
|
336
|
+
version = "0.11.0"
|
|
337
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
338
|
+
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
|
339
|
+
|
|
333
340
|
[[package]]
|
|
334
341
|
name = "quote"
|
|
335
342
|
version = "1.0.46"
|
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.9"
|
|
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.10" }
|
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
|
|
@@ -378,7 +388,7 @@ fn render_with_includes_json(
|
|
|
378
388
|
// parse JSON twice. Positions stay OFF, unlike `to_ast_json`: spec I4
|
|
379
389
|
// leaves position remapping out of scope in every engine, so a child's
|
|
380
390
|
// spans would point into a document the caller never passed.
|
|
381
|
-
IncludeTarget::Ast => Ok(carve_rs::to_json(&prepared.doc)),
|
|
391
|
+
IncludeTarget::Ast => Ok(ast_json_with_version(carve_rs::to_json(&prepared.doc))),
|
|
382
392
|
}
|
|
383
393
|
.map_err(|error| Error::new(ruby.exception_runtime_error(), error.to_string()))?;
|
|
384
394
|
let value: serde_json::Value = if which == IncludeTarget::Ast {
|
|
@@ -471,15 +481,20 @@ fn from_html_json(ruby: &Ruby, source: String, mode: String) -> Result<String, E
|
|
|
471
481
|
.to_string())
|
|
472
482
|
}
|
|
473
483
|
|
|
474
|
-
fn from_markdown_json(source: String) -> String {
|
|
475
|
-
let result = carve_rs::
|
|
484
|
+
fn from_markdown_json(ruby: &Ruby, source: String) -> Result<String, Error> {
|
|
485
|
+
let result = carve_rs::try_migrate_markdown(&source).map_err(|error| {
|
|
486
|
+
Error::new(
|
|
487
|
+
ruby.exception_arg_error(),
|
|
488
|
+
format!("Markdown import failed: {error}"),
|
|
489
|
+
)
|
|
490
|
+
})?;
|
|
476
491
|
let diagnostics = result
|
|
477
492
|
.report
|
|
478
493
|
.diagnostics
|
|
479
494
|
.iter()
|
|
480
495
|
.map(migration_diagnostic_json)
|
|
481
496
|
.collect::<Vec<_>>();
|
|
482
|
-
serde_json::json!({"value": result.value, "report": {"schemaVersion": result.report.schema_version, "sourceFormat": result.report.source_format.as_str(), "diagnostics": diagnostics}}).to_string()
|
|
497
|
+
Ok(serde_json::json!({"value": result.value, "report": {"schemaVersion": result.report.schema_version, "sourceFormat": result.report.source_format.as_str(), "diagnostics": diagnostics}}).to_string())
|
|
483
498
|
}
|
|
484
499
|
|
|
485
500
|
fn migration_diagnostic_json(diagnostic: &carve_rs::MigrationDiagnostic) -> serde_json::Value {
|
|
@@ -517,7 +532,9 @@ fn migration_diagnostic_json(diagnostic: &carve_rs::MigrationDiagnostic) -> serd
|
|
|
517
532
|
fn to_ast_json(source: String) -> String {
|
|
518
533
|
let mut options = Options::new();
|
|
519
534
|
options.positions = true;
|
|
520
|
-
carve_rs::to_json(&carve_rs::parse_with_options(
|
|
535
|
+
ast_json_with_version(carve_rs::to_json(&carve_rs::parse_with_options(
|
|
536
|
+
&source, &options,
|
|
537
|
+
)))
|
|
521
538
|
}
|
|
522
539
|
|
|
523
540
|
/// Render Carve source to HTML with the named extensions enabled.
|
|
@@ -837,42 +854,70 @@ macro_rules! guarded {
|
|
|
837
854
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty => $inner:ident) => {
|
|
838
855
|
#[allow(clippy::too_many_arguments)]
|
|
839
856
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
840
|
-
guard(|| Ok($inner($($arg),*)))
|
|
857
|
+
guard(|| Ok($inner($($arg.into()),*)))
|
|
841
858
|
}
|
|
842
859
|
};
|
|
843
860
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty =>? $inner:ident) => {
|
|
844
861
|
#[allow(clippy::too_many_arguments)]
|
|
845
862
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
846
|
-
guard(|| $inner($($arg),*))
|
|
863
|
+
guard(|| $inner($($arg.into()),*))
|
|
847
864
|
}
|
|
848
865
|
};
|
|
849
866
|
}
|
|
850
867
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
868
|
+
struct Source(String);
|
|
869
|
+
|
|
870
|
+
impl TryConvert for Source {
|
|
871
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
872
|
+
let source = RString::try_convert(value)?;
|
|
873
|
+
let encoding: Value = source.funcall("encoding", ())?;
|
|
874
|
+
let name: String = encoding.funcall("name", ())?;
|
|
875
|
+
let text = if name == "ASCII-8BIT" {
|
|
876
|
+
String::from_utf8_lossy(unsafe { source.as_slice() }).into_owned()
|
|
877
|
+
} else {
|
|
878
|
+
let ruby = Ruby::get_with(source);
|
|
879
|
+
let options = ruby.hash_new();
|
|
880
|
+
options.aset(ruby.to_symbol("invalid"), ruby.to_symbol("replace"))?;
|
|
881
|
+
options.aset(ruby.to_symbol("undef"), ruby.to_symbol("replace"))?;
|
|
882
|
+
options.aset(ruby.to_symbol("replace"), "\u{FFFD}")?;
|
|
883
|
+
let encoded: RString = source.funcall("encode", ("UTF-8", magnus::KwArgs(options)))?;
|
|
884
|
+
encoded.to_string()?
|
|
885
|
+
};
|
|
886
|
+
Ok(Self(text))
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
impl From<Source> for String {
|
|
891
|
+
fn from(source: Source) -> Self {
|
|
892
|
+
source.0
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
guarded!(g_to_html(source: Source) -> String => to_html);
|
|
897
|
+
guarded!(g_to_markdown(source: Source) -> String => to_markdown);
|
|
898
|
+
guarded!(g_to_plain_text(source: Source) -> String => to_plain_text);
|
|
899
|
+
guarded!(g_to_ansi(source: Source) -> String => to_ansi);
|
|
900
|
+
guarded!(g_to_carve(source: Source) -> String => to_carve);
|
|
901
|
+
guarded!(g_from_markdown_json(ruby: &Ruby, source: Source) -> String =>? from_markdown_json);
|
|
902
|
+
guarded!(g_to_ast_json(source: Source) -> String => to_ast_json);
|
|
858
903
|
guarded!(g_extension_names() -> Vec<String> => extension_names);
|
|
859
904
|
guarded!(g_panic_probe() -> String => panic_probe);
|
|
860
905
|
guarded!(
|
|
861
|
-
g_stamp_needs_review(source:
|
|
906
|
+
g_stamp_needs_review(source: Source, current_version: Option<String>) -> bool
|
|
862
907
|
=> stamp_needs_review
|
|
863
908
|
);
|
|
864
909
|
guarded!(
|
|
865
|
-
g_from_html_json(ruby: &Ruby, source:
|
|
910
|
+
g_from_html_json(ruby: &Ruby, source: Source, mode: String) -> String =>? from_html_json
|
|
866
911
|
);
|
|
867
|
-
guarded!(g_read_stamp(ruby: &Ruby, source:
|
|
912
|
+
guarded!(g_read_stamp(ruby: &Ruby, source: Source) -> Value =>? read_stamp);
|
|
868
913
|
guarded!(
|
|
869
|
-
g_to_html_with_extensions(ruby: &Ruby, source:
|
|
914
|
+
g_to_html_with_extensions(ruby: &Ruby, source: Source, names: RArray) -> String
|
|
870
915
|
=>? to_html_with_extensions
|
|
871
916
|
);
|
|
872
917
|
guarded!(
|
|
873
918
|
g_to_html_full(
|
|
874
919
|
ruby: &Ruby,
|
|
875
|
-
source:
|
|
920
|
+
source: Source,
|
|
876
921
|
names: RArray,
|
|
877
922
|
mode: String,
|
|
878
923
|
renderers: RHash
|
|
@@ -881,7 +926,7 @@ guarded!(
|
|
|
881
926
|
guarded!(
|
|
882
927
|
g_to_html_full_with_symbols(
|
|
883
928
|
ruby: &Ruby,
|
|
884
|
-
source:
|
|
929
|
+
source: Source,
|
|
885
930
|
names: RArray,
|
|
886
931
|
mode: String,
|
|
887
932
|
renderers: RHash,
|
|
@@ -891,7 +936,7 @@ guarded!(
|
|
|
891
936
|
guarded!(
|
|
892
937
|
g_to_html_safe(
|
|
893
938
|
ruby: &Ruby,
|
|
894
|
-
source:
|
|
939
|
+
source: Source,
|
|
895
940
|
names: RArray,
|
|
896
941
|
mode: String,
|
|
897
942
|
renderers: RHash,
|
|
@@ -904,7 +949,7 @@ guarded!(
|
|
|
904
949
|
guarded!(
|
|
905
950
|
g_render_with_includes_json(
|
|
906
951
|
ruby: &Ruby,
|
|
907
|
-
source:
|
|
952
|
+
source: Source,
|
|
908
953
|
root: String,
|
|
909
954
|
source_path: String,
|
|
910
955
|
target: String,
|
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.9"
|
|
12
12
|
end
|
data/lib/carve.rb
CHANGED
|
@@ -319,9 +319,10 @@ module Carve
|
|
|
319
319
|
# # => {type: "document",
|
|
320
320
|
# # children: [{type: "heading", level: 1,
|
|
321
321
|
# # children: [{type: "text", value: "Hi"}], attrs: nil}],
|
|
322
|
-
# # srcByteLength: 4}
|
|
322
|
+
# # srcByteLength: 4, astVersion: "1.2"}
|
|
323
323
|
#
|
|
324
|
-
# The root carries exactly +:type+, +:children
|
|
324
|
+
# The root carries exactly +:type+, +:children+, +:srcByteLength+ and
|
|
325
|
+
# +:astVersion+ (spec
|
|
325
326
|
# PART 12 §7). Frontmatter and footnote definitions are block nodes in
|
|
326
327
|
# +:children+, not root fields: a root field cannot carry the position §4
|
|
327
328
|
# 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.9
|
|
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-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rb_sys
|