| OLD | NEW |
| (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 during code generation of |
| 7 * analyzer and analysis server. |
| 8 */ |
| 9 library analyzer.src.codegen.text_formatter; |
| 10 |
| 11 import 'package:analyzer/src/codegen/tools.dart'; |
| 12 import 'package:html/dom.dart' as dom; |
| 13 |
| 14 final RegExp whitespace = new RegExp(r'\s'); |
| 15 |
| 16 /** |
| 17 * Convert the HTML in [desc] into text, word wrapping at width [width]. |
| 18 * |
| 19 * If [javadocStyle] is true, then the output is compatable with Javadoc, |
| 20 * which understands certain HTML constructs. |
| 21 */ |
| 22 String nodesToText(List<dom.Node> desc, int width, bool javadocStyle, |
| 23 {bool removeTrailingNewLine: false}) { |
| 24 _TextFormatter formatter = new _TextFormatter(width, javadocStyle); |
| 25 return formatter.collectCode(() { |
| 26 formatter.addAll(desc); |
| 27 formatter.lineBreak(false); |
| 28 }, removeTrailingNewLine: removeTrailingNewLine); |
| 29 } |
| 30 |
| 31 /** |
| 32 * Engine that transforms HTML to text. The input HTML is processed one |
| 33 * character at a time, gathering characters into words and words into lines. |
| 34 */ |
| 35 class _TextFormatter extends CodeGenerator { |
| 36 /** |
| 37 * Word-wrapping width. |
| 38 */ |
| 39 final int width; |
| 40 |
| 41 /** |
| 42 * The word currently being gathered. |
| 43 */ |
| 44 String word = ''; |
| 45 |
| 46 /** |
| 47 * The line currently being gathered. |
| 48 */ |
| 49 String line = ''; |
| 50 |
| 51 /** |
| 52 * True if a blank line should be inserted before the next word. |
| 53 */ |
| 54 bool verticalSpaceNeeded = false; |
| 55 |
| 56 /** |
| 57 * True if no text has been output yet. This suppresses blank lines. |
| 58 */ |
| 59 bool atStart = true; |
| 60 |
| 61 /** |
| 62 * True if we are processing a <pre> element, thus whitespace should be |
| 63 * preserved. |
| 64 */ |
| 65 bool preserveSpaces = false; |
| 66 |
| 67 /** |
| 68 * True if the output should be Javadoc compatible. |
| 69 */ |
| 70 final bool javadocStyle; |
| 71 |
| 72 _TextFormatter(this.width, this.javadocStyle); |
| 73 |
| 74 /** |
| 75 * Process an HTML node. |
| 76 */ |
| 77 void add(dom.Node node) { |
| 78 if (node is dom.Text) { |
| 79 for (String char in node.text.split('')) { |
| 80 if (preserveSpaces) { |
| 81 wordBreak(); |
| 82 write(escape(char)); |
| 83 } else if (whitespace.hasMatch(char)) { |
| 84 wordBreak(); |
| 85 } else { |
| 86 resolveVerticalSpace(); |
| 87 word += escape(char); |
| 88 } |
| 89 } |
| 90 } else if (node is dom.Element) { |
| 91 switch (node.localName) { |
| 92 case 'br': |
| 93 lineBreak(false); |
| 94 break; |
| 95 case 'dl': |
| 96 case 'dt': |
| 97 case 'h1': |
| 98 case 'h2': |
| 99 case 'h3': |
| 100 case 'h4': |
| 101 case 'p': |
| 102 lineBreak(true); |
| 103 addAll(node.nodes); |
| 104 lineBreak(true); |
| 105 break; |
| 106 case 'div': |
| 107 lineBreak(false); |
| 108 if (node.classes.contains('hangingIndent')) { |
| 109 resolveVerticalSpace(); |
| 110 indentSpecial('', ' ', () { |
| 111 addAll(node.nodes); |
| 112 lineBreak(false); |
| 113 }); |
| 114 } else { |
| 115 addAll(node.nodes); |
| 116 lineBreak(false); |
| 117 } |
| 118 break; |
| 119 case 'ul': |
| 120 lineBreak(false); |
| 121 addAll(node.nodes); |
| 122 lineBreak(false); |
| 123 break; |
| 124 case 'li': |
| 125 lineBreak(false); |
| 126 resolveVerticalSpace(); |
| 127 indentSpecial('- ', ' ', () { |
| 128 addAll(node.nodes); |
| 129 lineBreak(false); |
| 130 }); |
| 131 break; |
| 132 case 'dd': |
| 133 lineBreak(true); |
| 134 indent(() { |
| 135 addAll(node.nodes); |
| 136 lineBreak(true); |
| 137 }); |
| 138 break; |
| 139 case 'pre': |
| 140 lineBreak(false); |
| 141 resolveVerticalSpace(); |
| 142 if (javadocStyle) { |
| 143 writeln('<pre>'); |
| 144 } |
| 145 bool oldPreserveSpaces = preserveSpaces; |
| 146 try { |
| 147 preserveSpaces = true; |
| 148 addAll(node.nodes); |
| 149 } finally { |
| 150 preserveSpaces = oldPreserveSpaces; |
| 151 } |
| 152 writeln(); |
| 153 if (javadocStyle) { |
| 154 writeln('</pre>'); |
| 155 } |
| 156 lineBreak(false); |
| 157 break; |
| 158 case 'a': |
| 159 case 'b': |
| 160 case 'body': |
| 161 case 'html': |
| 162 case 'i': |
| 163 case 'span': |
| 164 case 'tt': |
| 165 addAll(node.nodes); |
| 166 break; |
| 167 case 'head': |
| 168 break; |
| 169 default: |
| 170 throw new Exception('Unexpected HTML element: ${node.localName}'); |
| 171 } |
| 172 } else { |
| 173 throw new Exception('Unexpected HTML: $node'); |
| 174 } |
| 175 } |
| 176 |
| 177 /** |
| 178 * Process a list of HTML nodes. |
| 179 */ |
| 180 void addAll(List<dom.Node> nodes) { |
| 181 for (dom.Node node in nodes) { |
| 182 add(node); |
| 183 } |
| 184 } |
| 185 |
| 186 /** |
| 187 * Escape the given character for HTML. |
| 188 */ |
| 189 String escape(String char) { |
| 190 if (javadocStyle) { |
| 191 switch (char) { |
| 192 case '<': |
| 193 return '<'; |
| 194 case '>': |
| 195 return '>'; |
| 196 case '&': |
| 197 return '&'; |
| 198 } |
| 199 } |
| 200 return char; |
| 201 } |
| 202 |
| 203 /** |
| 204 * Terminate the current word and/or line, if either is in progress. |
| 205 */ |
| 206 void lineBreak(bool gap) { |
| 207 wordBreak(); |
| 208 if (line.isNotEmpty) { |
| 209 writeln(line); |
| 210 line = ''; |
| 211 } |
| 212 if (gap && !atStart) { |
| 213 verticalSpaceNeeded = true; |
| 214 } |
| 215 } |
| 216 |
| 217 /** |
| 218 * Insert vertical space if necessary. |
| 219 */ |
| 220 void resolveVerticalSpace() { |
| 221 if (verticalSpaceNeeded) { |
| 222 writeln(); |
| 223 verticalSpaceNeeded = false; |
| 224 } |
| 225 } |
| 226 |
| 227 /** |
| 228 * Terminate the current word, if a word is in progress. |
| 229 */ |
| 230 void wordBreak() { |
| 231 if (word.isNotEmpty) { |
| 232 atStart = false; |
| 233 if (line.isNotEmpty) { |
| 234 if (indentWidth + line.length + 1 + word.length <= width) { |
| 235 line += ' $word'; |
| 236 } else { |
| 237 writeln(line); |
| 238 line = word; |
| 239 } |
| 240 } else { |
| 241 line = word; |
| 242 } |
| 243 word = ''; |
| 244 } |
| 245 } |
| 246 } |
| OLD | NEW |