Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(341)

Side by Side Diff: pkg/analysis_server/spec/text_formatter.dart

Issue 443873006: Add analysis server API specification and related tools. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Code for converting HTML into text, for use in doc comments.
7 */
8 library textFormatter;
9
10 import 'package:html5lib/dom.dart' as dom;
11
12 import 'codegen_tools.dart';
13
14 /**
15 * Convert the HTML in [desc] into text, word wrapping at width [width].
16 *
17 * If [javadocStyle] is true, then the output is compatable with Javadoc,
18 * which understands certain HTML constructs.
19 */
20 String nodesToText(List<dom.Node> desc, int width, bool javadocStyle) {
21 _TextFormatter formatter = new _TextFormatter(width, javadocStyle);
22 return formatter.collectCode(() {
23 formatter.addAll(desc);
24 formatter.lineBreak(false);
25 });
26 }
27
28 final RegExp whitespace = new RegExp(r'\s');
29
30 /**
31 * Engine that transforms HTML to text. The input HTML is processed one
32 * character at a time, gathering characters into words and words into lines.
33 */
34 class _TextFormatter extends CodeGenerator {
35 /**
36 * Word-wrapping width.
37 */
38 final int width;
39
40 /**
41 * The word currently being gathered.
42 */
43 String word = '';
44
45 /**
46 * The line currently being gathered.
47 */
48 String line = '';
49
50 /**
51 * True if a blank line should be inserted before the next word.
52 */
53 bool verticalSpaceNeeded = false;
54
55 /**
56 * True if no text has been output yet. This suppresses blank lines.
57 */
58 bool atStart = true;
59
60 /**
61 * True if we are processing a <pre> element, thus whitespace should be
62 * preserved.
63 */
64 bool preserveSpaces = false;
65
66 /**
67 * True if the output should be Javadoc compatible.
68 */
69 final bool javadocStyle;
70
71 _TextFormatter(this.width, this.javadocStyle);
72
73 /**
74 * Escape the given character for HTML.
75 */
76 String escape(String char) {
77 if (javadocStyle) {
78 switch (char) {
79 case '<':
80 return '&lt;';
81 case '>':
82 return '&gt;';
83 case '&':
84 return '&amp;';
85 }
86 }
87 return char;
88 }
89
90 /**
91 * Process an HTML node.
92 */
93 void add(dom.Node node) {
94 if (node is dom.Text) {
95 for (String char in node.text.split('')) {
96 if (preserveSpaces) {
97 wordBreak();
98 write(escape(char));
99 } else if (whitespace.hasMatch(char)) {
100 wordBreak();
101 } else {
102 resolveVerticalSpace();
103 word += escape(char);
104 }
105 }
106 } else if (node is dom.Element) {
107 switch (node.localName) {
108 case 'br':
109 lineBreak(false);
110 break;
111 case 'dl':
112 case 'dt':
113 case 'h1':
114 case 'h2':
115 case 'h3':
116 case 'h4':
117 case 'p':
118 lineBreak(true);
119 addAll(node.nodes);
120 lineBreak(true);
121 break;
122 case 'ul':
123 lineBreak(false);
124 addAll(node.nodes);
125 lineBreak(false);
126 break;
127 case 'li':
128 lineBreak(false);
129 resolveVerticalSpace();
130 indentSpecial('- ', ' ', () {
131 addAll(node.nodes);
132 lineBreak(false);
133 });
134 break;
135 case 'dd':
136 lineBreak(true);
137 indent(() {
138 addAll(node.nodes);
139 lineBreak(true);
140 });
141 break;
142 case 'pre':
143 lineBreak(false);
144 resolveVerticalSpace();
145 if (javadocStyle) {
146 writeln('<pre>');
147 }
148 bool oldPreserveSpaces = preserveSpaces;
149 try {
150 preserveSpaces = true;
151 addAll(node.nodes);
152 } finally {
153 preserveSpaces = oldPreserveSpaces;
154 }
155 writeln();
156 if (javadocStyle) {
157 writeln('</pre>');
158 }
159 lineBreak(false);
160 break;
161 case 'a':
162 case 'b':
163 case 'body':
164 case 'html':
165 case 'i':
166 case 'span':
167 case 'tt':
168 addAll(node.nodes);
169 break;
170 case 'head':
171 break;
172 default:
173 throw new Exception('Unexpected HTML element: ${node.localName}');
174 }
175 } else {
176 throw new Exception('Unexpected HTML: $node');
177 }
178 }
179
180 /**
181 * Insert vertical space if necessary.
182 */
183 void resolveVerticalSpace() {
184 if (verticalSpaceNeeded) {
185 writeln();
186 verticalSpaceNeeded = false;
187 }
188 }
189
190 /**
191 * Terminate the current word, if a word is in progress.
192 */
193 void wordBreak() {
194 if (word.isNotEmpty) {
195 atStart = false;
196 if (line.isNotEmpty) {
197 if (indentWidth + line.length + 1 + word.length <= width)
198 {
199 line += ' $word';
200 } else {
201 writeln(line);
202 line = word;
203 }
204 } else {
205 line = word;
206 }
207 word = '';
208 }
209 }
210
211 /**
212 * Terminate the current word and/or line, if either is in progress.
213 */
214 void lineBreak(bool gap) {
215 wordBreak();
216 if (line.isNotEmpty) {
217 writeln(line);
218 line = '';
219 }
220 if (gap && !atStart) {
221 verticalSpaceNeeded = true;
222 }
223 }
224
225 /**
226 * Process a list of HTML nodes.
227 */
228 void addAll(List<dom.Node> nodes) {
229 for (dom.Node node in nodes) {
230 add(node);
231 }
232 }
233 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698