1 /**
2  * Converts embedded code sections to plain text inside `(D_CODE)` without any
3  * syntax highlighting applied. This can be used as lightweight alternative to
4  * ddoc.highlight when syntax highlighting the code is not actually needed.
5  *
6  * Copyright: © 2014 Economic Modeling Specialists, Intl.
7  * Authors: Brian Schott, Mathias 'Geod24' Lang, Jan Jurzitza
8  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  */
10 module ddoc.unhighlight;
11 
12 import std.array;
13 
14 /**
15  * Parses a string and replace embedded code (code between at least 3 '-') with
16  * plaintext.
17  *
18  * Params:
19  * str = A string that might contain embedded code. Only code will be modified.
20  * 	If the string doesn't contain any embedded code, it will be returned as is.
21  *
22  * Returns:
23  * A (possibly new) string containing the embedded code inside `D_CODE` macros.
24  */
25 string unhighlight(string str)
26 {
27 	return highlightBase(str, (code, ref o) { o.put(code); });
28 }
29 
30 /**
31  * Base code for highlight and unhighlight, calling the $(LREF highlightCode)
32  * callback parameter on all embedded sections to handle how it is emitted.
33  *
34  * Params:
35  * str = A string that might contain embedded code. Only code will be modified.
36  * 	If the string doesn't contain any embedded code, it will be returned as is.
37  * highlightCode = The callback to call for embedded and inlined code sections.
38  * 	`D_CODE` macross will be automatically prefixed and suffixed before/after
39  * 	the call to this function.
40  */
41 string highlightBase(string str, void delegate(string code, ref Appender!string output) highlightCode)
42 {
43 	import ddoc.lexer;
44 	import ddoc.macros : tokOffset;
45 
46 	auto lex = Lexer(str, true);
47 	auto output = appender!string;
48 	size_t start;
49 	// We need this because there's no way to tell how many dashes precede
50 	// an embedded.
51 	size_t end;
52 	while (!lex.empty)
53 	{
54 		if (lex.front.type == Type.embedded)
55 		{
56 			if (start != end)
57 				output.put(lex.text[start .. end]);
58 			output.put("$(D_CODE ");
59 			highlightCode(lex.front.text, output);
60 			output.put(")");
61 			start = lex.offset;
62 		}
63 		else if (lex.front.type == Type.inlined)
64 		{
65 			if (start != end)
66 				output.put(lex.text[start .. end]);
67 			output.put("$(DDOC_BACKQUOTED ");
68 			highlightCode(lex.front.text, output);
69 			output.put(")");
70 			start = lex.offset;
71 		}
72 		end = lex.offset;
73 		lex.popFront();
74 	}
75 
76 	if (start)
77 	{
78 		output.put(lex.text[start .. end]);
79 		return output.data;
80 	}
81 	else
82 	{
83 		return str;
84 	}
85 }
86 
87 unittest
88 {
89 	import ddoc.lexer;
90 	import ddoc.macros;
91 
92 	string[string] macros = null;
93 
94 	string text = `description
95 
96 Something else
97 
98 ---
99 // an example
100 ---
101 Throws: a fit
102 ---
103 /// another example
104 ---`;
105 	text = unhighlight(text);
106 	auto lexer = Lexer(text, true);
107 	assert(expand(lexer, macros, false) == `description
108 
109 Something else
110 
111 <pre class="d_code">// an example</pre>
112 Throws: a fit
113 <pre class="d_code">/// another example</pre>`);
114 }