Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 97 additions & 89 deletions B0.ml
Original file line number Diff line number Diff line change
Expand Up @@ -13,158 +13,166 @@ let cmdliner = B0_ocaml.libname "cmdliner"
let uucp = B0_ocaml.libname "uucp"

let b0_std = B0_ocaml.libname "b0.std"
let b0_b00_kit = B0_ocaml.libname "b0.b00.kit"
let b0_file = B0_ocaml.libname "b0.file"

(* Libraries *)

let cmarkit_lib =
let srcs = Fpath.[ `Dir (v "src") ] in
let srcs = [ `Dir ~/"src" ] in
let requires = [] and name = "cmarkit-lib" in
B0_ocaml.lib cmarkit ~name ~doc:"The cmarkit library" ~srcs ~requires

(* Tools *)

let cmarkit_tool =
let srcs = Fpath.[`Dir (v "tool")] in
let srcs = [ `Dir ~/"tool" ] in
let requires = [cmarkit; cmdliner] in
B0_ocaml.exe "cmarkit" ~doc:"The cmarkit tool" ~srcs ~requires
B0_ocaml.exe "cmarkit" ~public:true ~doc:"The cmarkit tool" ~srcs ~requires

(* Unicode support *)
(* Unicode support

XXX we could do without both an exe and an action, cf. the Unicode libs. *)

let unicode_data =
let srcs = Fpath.[`File (v "support/unicode_data.ml")] in
let srcs = [ `File ~/"support/unicode_data.ml" ] in
let requires = [uucp] in
let doc = "Generate cmarkit Unicode data" in
B0_ocaml.exe "unicode_data" ~doc ~srcs ~requires

let update_unicode =
B0_cmdlet.v "update_unicode_data" ~doc:"Update Unicode character data" @@
fun env _args -> B0_cmdlet.exit_of_result @@
(* FIXME b0 *)
let b0 = Os.Cmd.get_tool (Fpath.v "b0") |> Result.get_ok in
let unicode_data = Cmd.(path b0 % "--" % "unicode_data") in
let outf = B0_cmdlet.in_scope_dir env (Fpath.v "src/cmarkit_data_uchar.ml") in
let doc = "Update Unicode character data " in
B0_action.make' "update_unicode_data" ~units:[unicode_data] ~doc @@
fun _ env ~args:_ ->
let* unicode_data = B0_env.unit_cmd env unicode_data in
let outf = B0_env.in_scope_dir env ~/"src/cmarkit_data_uchar.ml" in
let outf = Os.Cmd.out_file ~force:true ~make_path:false outf in
Os.Cmd.run ~stdout:outf unicode_data

(* Tests *)

let update_spec_tests =
B0_cmdlet.v "update_spec_tests" ~doc:"Update the CommonMark spec tests" @@
fun env _args -> B0_cmdlet.exit_of_result @@
B0_action.make' "update_spec_tests" ~doc:"Update the CommonMark spec tests" @@
fun _ env ~args:_ ->
let tests =
Fmt.str "https://spec.commonmark.org/%s/spec.json" commonmark_version
in
let dest = B0_cmdlet.in_scope_dir env (Fpath.v ("test/spec.json")) in
let dest = B0_env.in_scope_dir env ~/"test/spec.json" in
let dest = Os.Cmd.out_file ~force:true ~make_path:false dest in
let* curl = Os.Cmd.get Cmd.(atom "curl" % "-L" % tests) in
let* curl = B0_env.get_cmd env Cmd.(arg "curl" % "-L" % tests) in
Os.Cmd.run ~stdout:dest curl

let spec_srcs = Fpath.[`File (v "test/spec.ml"); `File (v "test/spec.mli")]
let spec_srcs = [`File ~/"test/spec.mli"; `File ~/"test/spec.ml"]

let bench =
let srcs = Fpath.[`File (v "test/bench.ml")] in
let doc = "Simple standard CommonMark to HTML renderer for benchmarking" in
let srcs = [ `File ~/"test/bench.ml" ] in
let requires = [cmarkit] in
let meta = B0_meta.(empty |> tag bench) in
let doc = "Simple standard CommonMark to HTML renderer for benchmarking" in
B0_ocaml.exe "bench" ~doc ~meta ~srcs ~requires

let test_spec =
let srcs = Fpath.(`File (v "test/test_spec.ml") :: spec_srcs) in
let requires = [ b0_std; b0_b00_kit; cmarkit ] in
let doc = "Test CommonMark specification conformance tests" in
let srcs = `File ~/"test/test_spec.ml" :: spec_srcs in
let requires = [ b0_std; b0_file; cmarkit ] in
let meta =
B0_meta.(empty |> add B0_unit.Action.exec_cwd B0_unit.Action.scope_cwd)
B0_meta.empty
|> B0_meta.tag B0_meta.test
|> B0_meta.add B0_unit.exec_cwd `Scope_dir
in
let doc = "Test CommonMark specification conformance tests" in
B0_ocaml.exe "test_spec" ~doc ~meta ~srcs ~requires

let trip_spec =
let srcs = Fpath.(`File (v "test/trip_spec.ml") :: spec_srcs) in
let requires = [ b0_std; b0_b00_kit; cmarkit ] in
let doc = "Test CommonMark renderer on conformance tests" in
let srcs = `File ~/"test/trip_spec.ml" :: spec_srcs in
let requires = [ b0_std; b0_file; cmarkit ] in
let meta =
B0_meta.(empty |> add B0_unit.Action.exec_cwd B0_unit.Action.scope_cwd)
B0_meta.empty
|> B0_meta.tag B0_meta.test
|> B0_meta.add B0_unit.exec_cwd `Scope_dir
in
let doc = "Test CommonMark renderer on conformance tests" in
B0_ocaml.exe "trip_spec" ~doc ~meta ~srcs ~requires

let pathological =
let srcs = Fpath.[`File (v "test/pathological.ml")] in
let requires = [ b0_std ] in
let doc = "Test a CommonMark parser on pathological tests." in
let srcs = [ `File ~/"test/pathological.ml" ] in
let requires = [ b0_std ] in
B0_ocaml.exe "pathological" ~doc ~srcs ~requires

let examples =
let srcs = Fpath.[`File (v "test/examples.ml")] in
let requires = [cmarkit] in
let meta = B0_meta.(empty |> tag test) in
let doc = "Doc sample code" in
let srcs = [ `File ~/"test/examples.ml" ] in
let requires = [ cmarkit ] in
let meta = B0_meta.empty |> B0_meta.(tag test) in
B0_ocaml.exe "examples" ~doc ~meta ~srcs ~requires

let expect_trip_spec exp env =
(* FIXME b0 *)
let trip_spec = Cmd.(atom "b0" % "--path" % "--" % "trip_spec") in
let* trip_spec = Result.map Cmd.atom (Os.Cmd.run_out ~trim:true trip_spec) in
let stdout = Fpath.(B0_expect.base exp / "spec.trip") in
let cwd = B0_cmdlet.Env.scope_dir env in
B0_expect.stdout exp ~cwd ~stdout trip_spec

let expect_test =
(* FIXME B0_expect, There's still a bit to streamline here *)
let doc = "Test the expectations" in
B0_cmdlet.v "test_expect" ~doc @@ fun env args ->
B0_cmdlet.exit_of_result' @@
(* FIXME b0 *)
let trip = Cmd.(atom "b0" % "--path" % "--" % "cmarkit") in
let* trip = Result.map Cmd.atom (Os.Cmd.run_out ~trim:true trip) in
let* exp = B0_expect.make env ~base:Fpath.(v "test/expect") in
let* base_files = B0_expect.base_files exp ~recurse:false in
let is_input f = Fpath.has_ext ".md" f && not (Fpath.has_ext ".trip.md" f) in
let test_files = List.filter is_input base_files in
let render_tests file acc =
let run_test (cmd, ext) acc =
let cwd = B0_expect.base exp and stdout = Fpath.(file -+ ext) in
let base = Fpath.basename file in
let with_exts = String.ends_with ~suffix:"exts.md" base in
let inf = Fpath.strip_prefix cwd file |> Option.get in
let cmd = Cmd.(cmd %% if' with_exts (atom "--exts") %% path inf) in
let* o = B0_expect.stdout exp ~cwd ~stdout Cmd.(trip %% cmd) in
Ok (o :: acc)
in
let renderers =
[ Cmd.(atom "html" % "-c" % "--unsafe"), ".html";
Cmd.(atom "latex"), ".latex";
Cmd.(atom "commonmark"), ".trip.md";
Cmd.(atom "locs"), ".locs";
Cmd.(atom "locs" % "--no-layout"), ".nolayout.locs"; ]
in
List.fold_stop_on_error run_test renderers acc
(* Expectation tests *)

let expect_trip_spec ctx =
let trip_spec = (* TODO b0 something more convenient. *)
B0_env.unit_cmd (B0_expect.env ctx) trip_spec
|> B0_expect.result_to_abort
in
let cwd = B0_env.scope_dir (B0_expect.env ctx) in
B0_expect.stdout ctx ~cwd ~stdout:(Fpath.v "spec.trip") trip_spec

let expect_cmarkit_renders ctx =
let cmarkit = (* TODO b0 something more convenient. *)
B0_env.unit_cmd (B0_expect.env ctx) cmarkit_tool
|> B0_expect.result_to_abort
in
let renderers = (* command, output suffix *)
[ Cmd.(arg "html" % "-c" % "--unsafe"), ".html";
Cmd.(arg "latex"), ".latex";
Cmd.(arg "commonmark"), ".trip.md";
Cmd.(arg "locs"), ".locs";
Cmd.(arg "locs" % "--no-layout"), ".nolayout.locs"; ]
in
let* os = List.fold_stop_on_error render_tests test_files [] in
let* o = expect_trip_spec exp env in
Ok (B0_expect.log_results exp (o :: os))
let test_renderer ctx cmarkit file (cmd, ext) =
let with_exts = Fpath.has_ext ".exts.md" file in
let cmd = Cmd.(cmd %% if' with_exts (arg "--exts") %% path file) in
let cwd = B0_expect.base ctx and stdout = Fpath.(file -+ ext) in
B0_expect.stdout ctx ~cwd ~stdout Cmd.(cmarkit %% cmd)
in
let test_file ctx cmarkit file =
List.iter (test_renderer ctx cmarkit file) renderers
in
let test_files =
let base_files = B0_expect.base_files ctx ~rel:true ~recurse:false in
let input f = Fpath.has_ext ".md" f && not (Fpath.has_ext ".trip.md" f) in
List.filter input base_files
in
List.iter (test_file ctx cmarkit) test_files

let expect =
let doc = "Test expectations" in
B0_action.make "expect" ~units:[trip_spec; cmarkit_tool] ~doc @@
B0_expect.action_func ~base:(Fpath.v "test/expect") @@ fun ctx ->
expect_cmarkit_renders ctx;
expect_trip_spec ctx;
()

(* Packs *)

let default =
let meta =
let open B0_meta in
empty
|> add authors ["The cmarkit programmers"]
|> add maintainers ["Daniel Bünzli <daniel.buenzl [email protected]>"]
|> add homepage "https://erratique.ch/software/cmarkit"
|> add online_doc "https://erratique.ch/software/cmarkit/doc"
|> add licenses ["ISC"]
|> add repo "git+https://erratique.ch/repos/cmarkit.git"
|> add issues "https://github.com/dbuenzli/cmarkit/issues"
|> add description_tags
B0_meta.empty
|> B0_meta.(add authors) ["The cmarkit programmers"]
|> B0_meta.(add maintainers)
["Daniel Bünzli <daniel.buenzl [email protected]>"]
|> B0_meta.(add homepage) "https://erratique.ch/software/cmarkit"
|> B0_meta.(add online_doc) "https://erratique.ch/software/cmarkit/doc"
|> B0_meta.(add licenses) ["ISC"]
|> B0_meta.(add repo) "git+https://erratique.ch/repos/cmarkit.git"
|> B0_meta.(add issues) "https://github.com/dbuenzli/cmarkit/issues"
|> B0_meta.(add description_tags)
["codec"; "commonmark"; "markdown"; "org:erratique"; ]
|> add B0_opam.Meta.build
|> B0_meta.tag B0_opam.tag
|> B0_meta.add B0_opam.build
{|[["ocaml" "pkg/pkg.ml" "build" "--dev-pkg" "%{dev}%"
"--with-cmdliner" "%{cmdliner:installed}%"]]|}
|> tag B0_opam.tag
|> add B0_opam.Meta.depopts ["cmdliner", ""]
|> add B0_opam.Meta.conflicts [ "cmdliner", {|< "1.1.0"|}]
|> add B0_opam.Meta.depends
|> B0_meta.add B0_opam.depopts ["cmdliner", ""]
|> B0_meta.add B0_opam.conflicts [ "cmdliner", {|< "1.1.0"|}]
|> B0_meta.add B0_opam.depends
[ "ocaml", {|>= "4.14.0"|};
"ocamlfind", {|build|};
"ocamlbuild", {|build|};
Expand All @@ -173,5 +181,5 @@ let default =
"b0", {|dev & with-test|};
]
in
B0_pack.v "default" ~doc:"cmarkit package" ~meta ~locked:true @@
B0_pack.make "default" ~doc:"cmarkit package" ~meta ~locked:true @@
B0_unit.list ()
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
v0.3.0 2023-12-12 La Forclaz (VS)
---------------------------------

- Fix ordered item marker escaping. Thanks to Rafał Gwoździński for
the report (#11).

- Data updated for Unicode 15.1.0 (no changes except
for the value of `Cmarkit.Doc.unicode_version`).

- Fix table extension column parsing, toplevel text inlines were being
dropped. Thanks to Javier Chávarri for the report (#10).
Expand All @@ -6,6 +14,10 @@
We don't want to generate invalid CommonMark by default. Thanks to
Rafał Gwoździński for the report (#9).

- Add option `-f/--full-featured`, to `cmarkit html`. A synonym for a
bunch of existing options to generate a publishable document with extensions
and math rendering without hassle. See `cmarkit html --help` for details.

v0.2.0 2023-05-10 La Forclaz (VS)
---------------------------------

Expand Down
3 changes: 2 additions & 1 deletion DEVEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ To add a new test, add an `.md` test in `test/expect`, run the tests
and add the new generated files to the repo.

```sh
b0 -- test_expect
b0 -- expect
b0 -- expect --help
```

# Specification tests
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ See also [`bench.ml`] and the [doc examples].

## Acknowledgements

A grant from the [OCaml Software Foundation] helped to bring the first
public release of `cmarkit`.
A grant from the [OCaml Software Foundation] helped to bring the first
public release of `cmarkit`.

The `cmarkit` implementation benefited from the work of John
MacFarlane ([spec][CommonMark specification], [`cmark`]) and Martin
Expand Down
4 changes: 2 additions & 2 deletions _tags
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
true : bin_annot, safe_string
<_b0> : -traverse
<src> : include
<test/test*> : package(b0.std b0.b00.kit)
<test/spec*> : package(b0.std b0.b00.kit)
<test/test*> : package(b0.std b0.kit)
<test/spec*> : package(b0.std b0.kit)
<tool/*> : package(cmdliner)
16 changes: 2 additions & 14 deletions opam → cmarkit.opam
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,11 @@ doc: "https://erratique.ch/software/cmarkit/doc"
bug-reports: "https://github.com/dbuenzli/cmarkit/issues"
depends: [
"ocaml" {>= "4.14.0"}
"ocamlfind" {build}
"ocamlbuild" {build}
"topkg" {build & >= "1.0.3"}
"uucp" {dev}
"b0" {dev & with-test}
"dune"
]
depopts: ["cmdliner"]
conflicts: [
"cmdliner" {< "1.1.0"}
]
build: [
"ocaml"
"pkg/pkg.ml"
"build"
"--dev-pkg"
"%{dev}%"
"--with-cmdliner"
"%{cmdliner:installed}%"
]
build: [ "dune" "build" "-p" name "-j" jobs ]
dev-repo: "git+https://erratique.ch/repos/cmarkit.git"
2 changes: 2 additions & 0 deletions dune-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(lang dune 1.1)
(name cmarkit)
1 change: 1 addition & 0 deletions pkg/META
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ archive(byte) = "cmarkit.cma"
archive(native) = "cmarkit.cmxa"
plugin(byte) = "cmarkit.cma"
plugin(native) = "cmarkit.cmxs"
exists_if = "cmarkit.cma cmarkit.cmxa"
4 changes: 1 addition & 3 deletions pkg/pkg.ml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ let () =
in
Ok [ Pkg.mllib ~api "src/cmarkit.mllib";
Pkg.doc "doc/index.mld" ~dst:"odoc-pages/index.mld";
Pkg.bin ~cond:cmdliner "tool/cmd_main" ~dst:"cmarkit";
Pkg.test "test/bench";
Pkg.test "test/test_spec"; ]
Pkg.bin ~cond:cmdliner "tool/cmd_main" ~dst:"cmarkit" ]
10 changes: 5 additions & 5 deletions src/cmarkit_commonmark.ml
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ let buffer_add_escaped_text b s =
let esc_tilde s max prev next =
not (Char.equal prev '~') && next <= max && s.[next] = '~'
in
let esc_item_marker s i prev =
if prev <> '1' then false else
let k = ref (i - 2) in
while !k >= 0 && s.[!k] = '0' do decr k done;
let esc_item_marker s i =
if i = 0 || i > 9 (* marker has from 1-9 digits *) then false else
let k = ref (i - 1) in
while !k >= 0 && Cmarkit_base.Ascii.is_digit s.[!k] do decr k done;
!k < 0
in
let flush b max start i =
Expand All @@ -95,7 +95,7 @@ let buffer_add_escaped_text b s =
flush b max start i; buffer_add_bslash_esc b c; loop b s max next c next
| '!' when i = max ->
flush b max start i; buffer_add_bslash_esc b c; loop b s max next c next
| '.' | ')' when esc_item_marker s i prev ->
| '.' | ')' when esc_item_marker s i ->
flush b max start i; buffer_add_bslash_esc b c; loop b s max next c next
| '\\' | '<' | '>' | '[' | ']' | '*' | '_' | '$' | '|' ->
flush b max start i; buffer_add_bslash_esc b c; loop b s max next c next
Expand Down
3 changes: 1 addition & 2 deletions src/cmarkit_commonmark.mli
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ val escaped_text : Cmarkit_renderer.context -> string -> unit
({{!Cmarkit.ext_strikethrough}strikethrough extension}).}
{- [&] if followed by an US-ASCII letter or [#].}
{- [!] if it is the last character of [s].}
{- [.] or [)] only if preceeded by a single [1] and zero or more [0] to
the start of text.}
{- [.] or [)] only if preceeded by at most 9 digits to the start of text.}
{- Everywhere, [*] [_] [\ ] [<] [>] [\[] [\]],
{{:https://spec.commonmark.org/0.30/#ascii-control-character}
ASCII control characters}, [$] ({{!Cmarkit.ext_math_inline}inline math
Expand Down
Loading