carve-lang 0.1.8 → 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 +27 -1
- data/ext/carve/Cargo.lock +3 -3
- data/ext/carve/Cargo.toml +2 -2
- data/ext/carve/src/lib.rs +60 -20
- 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,31 @@ 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
|
+
|
|
10
35
|
## [0.1.8] - 2026-10-14
|
|
11
36
|
|
|
12
37
|
### Breaking
|
|
@@ -463,7 +488,8 @@ are not listed, because no release ever shipped them.
|
|
|
463
488
|
Arrays (every AST node type is covered), enabling custom renderers such as
|
|
464
489
|
[carve-hexapdf](https://github.com/markup-carve/carve-hexapdf).
|
|
465
490
|
|
|
466
|
-
[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
|
|
467
493
|
[0.1.8]: https://github.com/markup-carve/carve-rb/compare/v0.1.7...v0.1.8
|
|
468
494
|
[0.1.7]: https://github.com/markup-carve/carve-rb/compare/v0.1.6...v0.1.7
|
|
469
495
|
[0.1.6]: https://github.com/markup-carve/carve-rb/compare/v0.1.5...v0.1.6
|
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",
|
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 {
|
|
@@ -522,7 +532,9 @@ fn migration_diagnostic_json(diagnostic: &carve_rs::MigrationDiagnostic) -> serd
|
|
|
522
532
|
fn to_ast_json(source: String) -> String {
|
|
523
533
|
let mut options = Options::new();
|
|
524
534
|
options.positions = true;
|
|
525
|
-
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
|
+
)))
|
|
526
538
|
}
|
|
527
539
|
|
|
528
540
|
/// Render Carve source to HTML with the named extensions enabled.
|
|
@@ -842,42 +854,70 @@ macro_rules! guarded {
|
|
|
842
854
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty => $inner:ident) => {
|
|
843
855
|
#[allow(clippy::too_many_arguments)]
|
|
844
856
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
845
|
-
guard(|| Ok($inner($($arg),*)))
|
|
857
|
+
guard(|| Ok($inner($($arg.into()),*)))
|
|
846
858
|
}
|
|
847
859
|
};
|
|
848
860
|
($wrapper:ident ( $($arg:ident : $ty:ty),* ) -> $ret:ty =>? $inner:ident) => {
|
|
849
861
|
#[allow(clippy::too_many_arguments)]
|
|
850
862
|
fn $wrapper($($arg: $ty),*) -> Result<$ret, Error> {
|
|
851
|
-
guard(|| $inner($($arg),*))
|
|
863
|
+
guard(|| $inner($($arg.into()),*))
|
|
852
864
|
}
|
|
853
865
|
};
|
|
854
866
|
}
|
|
855
867
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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);
|
|
863
903
|
guarded!(g_extension_names() -> Vec<String> => extension_names);
|
|
864
904
|
guarded!(g_panic_probe() -> String => panic_probe);
|
|
865
905
|
guarded!(
|
|
866
|
-
g_stamp_needs_review(source:
|
|
906
|
+
g_stamp_needs_review(source: Source, current_version: Option<String>) -> bool
|
|
867
907
|
=> stamp_needs_review
|
|
868
908
|
);
|
|
869
909
|
guarded!(
|
|
870
|
-
g_from_html_json(ruby: &Ruby, source:
|
|
910
|
+
g_from_html_json(ruby: &Ruby, source: Source, mode: String) -> String =>? from_html_json
|
|
871
911
|
);
|
|
872
|
-
guarded!(g_read_stamp(ruby: &Ruby, source:
|
|
912
|
+
guarded!(g_read_stamp(ruby: &Ruby, source: Source) -> Value =>? read_stamp);
|
|
873
913
|
guarded!(
|
|
874
|
-
g_to_html_with_extensions(ruby: &Ruby, source:
|
|
914
|
+
g_to_html_with_extensions(ruby: &Ruby, source: Source, names: RArray) -> String
|
|
875
915
|
=>? to_html_with_extensions
|
|
876
916
|
);
|
|
877
917
|
guarded!(
|
|
878
918
|
g_to_html_full(
|
|
879
919
|
ruby: &Ruby,
|
|
880
|
-
source:
|
|
920
|
+
source: Source,
|
|
881
921
|
names: RArray,
|
|
882
922
|
mode: String,
|
|
883
923
|
renderers: RHash
|
|
@@ -886,7 +926,7 @@ guarded!(
|
|
|
886
926
|
guarded!(
|
|
887
927
|
g_to_html_full_with_symbols(
|
|
888
928
|
ruby: &Ruby,
|
|
889
|
-
source:
|
|
929
|
+
source: Source,
|
|
890
930
|
names: RArray,
|
|
891
931
|
mode: String,
|
|
892
932
|
renderers: RHash,
|
|
@@ -896,7 +936,7 @@ guarded!(
|
|
|
896
936
|
guarded!(
|
|
897
937
|
g_to_html_safe(
|
|
898
938
|
ruby: &Ruby,
|
|
899
|
-
source:
|
|
939
|
+
source: Source,
|
|
900
940
|
names: RArray,
|
|
901
941
|
mode: String,
|
|
902
942
|
renderers: RHash,
|
|
@@ -909,7 +949,7 @@ guarded!(
|
|
|
909
949
|
guarded!(
|
|
910
950
|
g_render_with_includes_json(
|
|
911
951
|
ruby: &Ruby,
|
|
912
|
-
source:
|
|
952
|
+
source: Source,
|
|
913
953
|
root: String,
|
|
914
954
|
source_path: String,
|
|
915
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
|