diff --git a/src/nix/local.mk b/src/nix/local.mk index b057b7cc6..8ed8eaef7 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -15,11 +15,12 @@ nix_SOURCES := \ $(wildcard src/nix-prefetch-url/*.cc) \ $(wildcard src/nix-store/*.cc) \ -nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain +# -fpermissive is needed by lowdown. +nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -fpermissive nix_LIBS = libexpr libmain libfetchers libstore libutil -nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system +nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system -llowdown $(foreach name, \ nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-url nix-shell nix-store, \ diff --git a/src/nix/markdown.cc b/src/nix/markdown.cc new file mode 100644 index 000000000..40788a42f --- /dev/null +++ b/src/nix/markdown.cc @@ -0,0 +1,50 @@ +#include "markdown.hh" +#include "util.hh" +#include "finally.hh" + +#include +extern "C" { +#include +} + +namespace nix { + +std::string renderMarkdownToTerminal(std::string_view markdown) +{ + struct lowdown_opts opts { + .type = LOWDOWN_TERM, + .maxdepth = 20, + .cols = std::min(getWindowSize().second, (unsigned short) 80), + .hmargin = 0, + .vmargin = 0, + .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, + .oflags = 0, + }; + + auto doc = lowdown_doc_new(&opts); + if (!doc) + throw Error("cannot allocate Markdown document"); + Finally freeDoc([&]() { lowdown_doc_free(doc); }); + + size_t maxn = 0; + auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size()); + if (!node) + throw Error("cannot parse Markdown document"); + Finally freeNode([&]() { lowdown_node_free(node); }); + + auto renderer = lowdown_term_new(&opts); + if (!renderer) + throw Error("cannot allocate Markdown renderer"); + Finally freeRenderer([&]() { lowdown_term_free(renderer); }); + + auto buf = lowdown_buf_new(16384); + if (!buf) + throw Error("cannot allocate Markdown output buffer"); + Finally freeBuffer([&]() { lowdown_buf_free(buf); }); + + lowdown_term_rndr(buf, nullptr, renderer, node); + + return std::string(buf->data, buf->size); +} + +} diff --git a/src/nix/markdown.hh b/src/nix/markdown.hh new file mode 100644 index 000000000..78320fcf5 --- /dev/null +++ b/src/nix/markdown.hh @@ -0,0 +1,7 @@ +#include "types.hh" + +namespace nix { + +std::string renderMarkdownToTerminal(std::string_view markdown); + +} diff --git a/src/nix/repl.cc b/src/nix/repl.cc index d370ca767..c7f8af742 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -32,6 +32,7 @@ extern "C" { #include "globals.hh" #include "command.hh" #include "finally.hh" +#include "markdown.hh" #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -518,10 +519,16 @@ bool NixRepl::processLine(string line) while (v2->type == tPrimOpApp) v2 = v2->primOpApp.left; if (v2->primOp->doc) { - // FIXME: format markdown. - if (!v2->primOp->args.empty()) - std::cout << fmt("Arguments: %s\n\n", concatStringsSep(" ", v2->primOp->args)); - std::cout << trim(stripIndentation(v2->primOp->doc)) << "\n"; + auto args = v2->primOp->args; + for (auto & arg : args) + arg = "*" + arg + "*"; + + auto markdown = + "**Synopsis:** `builtins." + (std::string) v2->primOp->name + "` " + + concatStringsSep(" ", args) + "\n\n" + + trim(stripIndentation(v2->primOp->doc)); + + std::cout << renderMarkdownToTerminal(markdown); } else throw Error("builtin function '%s' does not have documentation", v2->primOp->name); } else