Skip to content

Commit 6cd4903

Browse files
authored
feat(jsii-pacmak): add Python docstrings (#470)
Add support for docstrings to the Python generator.
1 parent 3ce0534 commit 6cd4903

File tree

11 files changed

+1202
-22
lines changed

11 files changed

+1202
-22
lines changed

packages/jsii-pacmak/lib/markdown.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import commonmark = require('commonmark');
2+
3+
/**
4+
* Convert MarkDown to RST
5+
*
6+
* This is hard, and I'm doing it very hackily to get something out quickly.
7+
*
8+
* Preferably, the next person to look at this should a little more OO
9+
* instead of procedural.
10+
*/
11+
export function md2rst(text: string) {
12+
const parser = new commonmark.Parser({ smart: false });
13+
const ast = parser.parse(text);
14+
15+
const ret = new Array<string>();
16+
17+
let indent = 0;
18+
function line(...xs: string[]) {
19+
for (const x of xs) {
20+
ret.push((' '.repeat(indent) + x).trimRight());
21+
}
22+
}
23+
24+
function directive(name: string, opening: boolean) {
25+
if (opening) {
26+
line(`.. ${name}::`);
27+
brk();
28+
indent += 3;
29+
} else {
30+
indent -= 3;
31+
}
32+
}
33+
34+
function brk() {
35+
if (ret.length > 0 && ret[ret.length - 1].trim() !== '') { ret.push(''); }
36+
}
37+
38+
function textOf(node: commonmark.Node) {
39+
return node.literal || '';
40+
}
41+
42+
let para = new Paragraph(); // Where to accumulate text fragments
43+
let lastParaLine: number; // Where the last paragraph ended, in order to add ::
44+
let nextParaPrefix: string | undefined;
45+
46+
pump(ast, {
47+
block_quote(_node, entering) {
48+
directive('epigraph', entering);
49+
},
50+
51+
heading(node, _entering) {
52+
line(node.literal || '');
53+
line(headings[node.level - 1].repeat(textOf(node).length));
54+
},
55+
56+
paragraph(node, entering) {
57+
if (entering) {
58+
para = new Paragraph(nextParaPrefix);
59+
nextParaPrefix = undefined;
60+
} else {
61+
// Don't break inside list item
62+
if (node.parent == null || node.parent.type !== 'item') {
63+
brk();
64+
}
65+
line(...para.lines());
66+
lastParaLine = ret.length - 1;
67+
}
68+
},
69+
70+
text(node) { para.add(textOf(node)); },
71+
softbreak() { para.newline(); },
72+
linebreak() { para.newline(); },
73+
thematic_break() { line('------'); },
74+
code(node) { para.add('``' + textOf(node) + '``'); },
75+
strong() { para.add('**'); },
76+
emph() { para.add('*'); },
77+
78+
list() {
79+
brk();
80+
},
81+
82+
link(node, entering) {
83+
if (entering) {
84+
para.add('`');
85+
} else {
86+
para.add(' <' + (node.destination || '') + '>`_');
87+
}
88+
},
89+
90+
item(node, _entering) {
91+
// AST hierarchy looks like list -> item -> paragraph -> text
92+
if (node.listType === 'bullet') {
93+
nextParaPrefix = '- ';
94+
} else {
95+
nextParaPrefix = `${node.listStart}. `;
96+
}
97+
98+
},
99+
100+
code_block(node) {
101+
// Poke a double :: at the end of the previous line as per ReST "literal block" syntax.
102+
if (lastParaLine !== undefined) {
103+
const lastLine = ret[lastParaLine];
104+
ret[lastParaLine] = lastLine.replace(/[\W]$/, '::');
105+
if (ret[lastParaLine] === lastLine) { ret[lastParaLine] = lastLine + '::'; }
106+
} else {
107+
line('Example::');
108+
}
109+
110+
brk();
111+
112+
indent += 3;
113+
114+
for (const l of textOf(node).split('\n')) {
115+
line(l);
116+
}
117+
118+
indent -= 3;
119+
}
120+
121+
});
122+
123+
return ret.join('\n').trimRight();
124+
}
125+
126+
class Paragraph {
127+
private readonly parts = new Array<string>();
128+
129+
constructor(text?: string) {
130+
if (text !== undefined) { this.parts.push(text); }
131+
}
132+
133+
public add(text: string) {
134+
this.parts.push(text);
135+
}
136+
137+
public newline() {
138+
this.parts.push('\n');
139+
}
140+
141+
public lines(): string[] {
142+
return this.parts.length > 0 ? this.toString().split('\n') : [];
143+
}
144+
145+
public toString() {
146+
return this.parts.join('').trimRight();
147+
}
148+
}
149+
150+
const headings = ['=', '-', '^', '"'];
151+
152+
type Handler = (node: commonmark.Node, entering: boolean) => void;
153+
type Handlers = {[key in commonmark.NodeType]?: Handler };
154+
155+
function pump(ast: commonmark.Node, handlers: Handlers) {
156+
const walker = ast.walker();
157+
let event = walker.next();
158+
while (event) {
159+
const h = handlers[event.node.type];
160+
if (h) {
161+
h(event.node, event.entering);
162+
}
163+
164+
event = walker.next();
165+
}
166+
}

0 commit comments

Comments
 (0)