Вопрос

I've noticed that Clojure multiline docstrings seem to be manually formatted in most cases, including the ones in clojure.core. Example from https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj :

(defn flatten
  "Takes any nested combination of sequential things (lists, vectors,
  etc.) and returns their contents as a single, flat sequence.
  (flatten nil) returns an empty sequence."
  {:added "1.2"
   :static true}
  [x]
  (filter (complement sequential?)
          (rest (tree-seq sequential? seq x))))

This seems odd, as it means that different docstrings will have different line wrap lengths etc. which need to be manually maintained.

Is there a better way to format multiline docstrings?

Это было полезно?

Решение

If you're using Emacs, grab clojure-mode.el from technomancy's Github, which differs from the one in ELPA (I don't know why, both claim to be version 1.11.5, maybe someone can comment on that?) but includes clojure-fill-docstring which will format docstrings with nice indentation and linewrapping, bound by default to C-c M-q.

It will take this:

(defn flatten
  "Takes any nested combination of sequential things (lists, vectors, etc.) and returns their contents as a single, flat sequence. (flatten nil) returns an empty sequence."
  {:added "1.2"
   :static true}
  [x]
  (filter (complement sequential?)
          (rest (tree-seq sequential? seq x))))

and turn it into this:

(defn flatten
  "Takes any nested combination of sequential things (lists, vectors,
  etc.) and returns their contents as a single, flat sequence.
  (flatten nil) returns an empty sequence."
  {:added "1.2"
   :static true}
  [x]
  (filter (complement sequential?)
          (rest (tree-seq sequential? seq x))))

after you do C-c M-q with your point inside the docstring.

Другие советы

Is there a better way to format multiline docstrings?

My suggestion is to use Markdown formatting in your docstrings. Here are some reasons why:

  • it's what's used at github in README's and project wikis (and many Clojure users use and are familiar with github).

  • judging by the number of .md files you find present in various Clojure projects, it appears to be a preferred markup format among Clojure users.

  • the popular Marginalia doc tool renders markdown-formatted docstrings and comments (and my understanding is that Autodoc (the tool used to generate the docs at clojure.org) will eventually render markdown in docstrings as well).

  • It looks good as plain text, is easy to type, doesn't require any special editor support, and the markup is minimal and easy to remember.

Also, you're probably already familiar with it, since Stackoverflow uses it for questions/answers/comments (and sites like reddit and various blog commenting systems use Markdown as well).

I agree with @uvtc that markdown is a nice choice. As an addendum, I'd like to note that it is trivial to generate your own markdown doc viewing function for use at the REPL. The following code assumes you have the markdown-clj package on your classpath (e.g. via dev dependencies), and are using a REPL in OSX:

(ns docs
  (:require [clojure.java.shell :as s]
            [markdown.core :as md]))

(defmacro opendoc [name]
   `(do
        (md/md-to-html (java.io.StringReader. (:doc (meta (var ~name)))) "/tmp/doc.html")
        (s/sh "open" "/tmp/doc.html")
    )
  )

You might want to look at the source for clojure.repl/doc to handle special cases (e.g. this one assumes you'll be passing in a proper symbol for a var). It might also be nice to have the filename reflect the namespace/function name for "caching", instead of just reusing the same filename for every request...but I'm keeping it simple for illustration purposes.

The OSX open command simply asks the OS to open a file by detecting its type. Thus:

REPL=> (docs/opendoc my.ns/f)

will cause your default browser to open the HTMLified version of your function's docstring.

One other caveat: If you indent your multiline string (which editors commonly do), then your MD may end up with weirdness (e.g. bullet lists might nest in a way you do not intend). One way to solve this is to trim that back out. For example:

(defn boo
  "
  # Title
  My thing

  * Item one
  * Item two
  "
  [args] ...)

and then modify the opendoc function to first apply a left trim:

(defn ltrim [str] (clojure.string/replace str #"(?m)^ {0,3}" ""))

(defmacro opendoc [name]
  `(do
    (md/md-to-html (java.io.StringReader. (ltrim (:doc (meta (var ~name))))) "/tmp/doc.html")
    (s/sh "open" "/tmp/doc.html")
   )
  )
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top