Skip to content

Commit

Permalink
Fix handling multiple definition descriptions
Browse files Browse the repository at this point in the history
The following valid markdown code:

	Some term (DT)
	: Description (DD1).
	: Also description (DD2).

is currently not handled correctly, resulting in this roff output

	.TP
	Some term (DT)
	Description (DD1).

	.TP
	Also description (DD2).

In other words, the second definition description (DD2) is
misrepresented as a separate definition title.

This happens because the code is not looking into ListTypeTerm flag
(perhaps it was not supported at the time of writing?), instead
alternating between DT and DD (assuming DD goes after DT and vice
versa), which is not the case if we have two DTs.

The fix is to check the ListTypeTerm flag: if set, this is a DT,
otherwise a DD.

Unfortunately we still have to use a state variable. This is because the
first DD should follow the DT immediately (i.e. no extra newlines or
anything), while the subsequent DDs needs to be separated with an extra
newline.

With these fixes, the above input results in:

	.TP
	Some term (DT)
	Description (DD1).

	Also description (DD2).

which is rendered by GNU troff + man macros to something like:

	Some term (DT)
		Description (DD1).

		Also description (DD2).

Add a test case to verify the fix and avoid future regressions.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
  • Loading branch information
kolyshkin committed Jul 13, 2021
1 parent af8da76 commit da4e952
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 9 deletions.
24 changes: 15 additions & 9 deletions md2man/roff.go
Expand Up @@ -15,7 +15,7 @@ type roffRenderer struct {
extensions blackfriday.Extensions
listCounters []int
firstHeader bool
defineTerm bool
firstDD bool
listDepth int
}

Expand All @@ -42,7 +42,8 @@ const (
quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n"
listCloseTag = "\n.RE\n"
arglistTag = "\n.TP\n"
dtTag = "\n.TP\n"
dd2Tag = "\n"
tableStart = "\n.TS\nallbox;\n"
tableEnd = "\n.TE\n"
tableCellStart = "T{\n"
Expand Down Expand Up @@ -230,15 +231,20 @@ func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
r.listCounters[len(r.listCounters)-1]++
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
// DT (definition term): line just before DD (see below).
out(w, dtTag)
r.firstDD = true
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// state machine for handling terms and following definitions
// since blackfriday does not distinguish them properly, nor
// does it seperate them into separate lists as it should
if !r.defineTerm {
out(w, arglistTag)
r.defineTerm = true
// DD (definition description): line that starts with ": ".
//
// We have to distinguish between the first DD and the
// subsequent ones, as there should be no vertical
// whitespace between the DT and the first DD.
if r.firstDD {
r.firstDD = false
} else {
r.defineTerm = false
out(w, dd2Tag)
}
} else {
out(w, ".IP \\(bu 2\n")
Expand Down
2 changes: 2 additions & 0 deletions md2man/roff_test.go
Expand Up @@ -196,6 +196,8 @@ func TestListLists(t *testing.T) {
var tests = []string{
"\n\n**[grpc]**\n: Section for gRPC socket listener settings. Contains three properties:\n - **address** (Default: \"/run/containerd/containerd.sock\")\n - **uid** (Default: 0)\n - **gid** (Default: 0)",
".nh\n\n.TP\n\\fB[grpc]\\fP\nSection for gRPC socket listener settings. Contains three properties:\n.RS\n.IP \\(bu 2\n\\fBaddress\\fP (Default: \"/run/containerd/containerd.sock\")\n.IP \\(bu 2\n\\fBuid\\fP (Default: 0)\n.IP \\(bu 2\n\\fBgid\\fP (Default: 0)\n\n.RE\n\n",
"Definition title\n: Definition description one\n: And two\n: And three\n",
".nh\n\n.TP\nDefinition title\nDefinition description one\n\nAnd two\n\nAnd three\n",
}
doTestsParam(t, tests, TestParams{blackfriday.DefinitionLists})
}
Expand Down

0 comments on commit da4e952

Please sign in to comment.