Merge pull request dart-lang/markdown#131 from srawlins/add-gh-pages-script
Add update-gh-pages script
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index 416778a..1a17056 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -4,6 +4,8 @@
* `dart bin/markdown.dart --version` now shows the package version number.
* The playground app now shows the version number.
* Improve autolink parsing.
+* Added new table syntax: `TableSyntax`. This can be used by passing
+ `const TableSyntax()` to `markdownToHtml()`'s `blockSyntaxes:` argument.
* For development: added tool/travis.sh.
## 0.11.0+1
diff --git a/pkgs/markdown/example/app.dart b/pkgs/markdown/example/app.dart
index 9e0fd37..1455dc6 100644
--- a/pkgs/markdown/example/app.dart
+++ b/pkgs/markdown/example/app.dart
@@ -23,6 +23,7 @@
savedMarkdown.isNotEmpty &&
savedMarkdown != introText) {
markdownInput.value = savedMarkdown;
+ markdownInput.focus();
_renderMarkdown();
} else {
_typeItOut(introText, 82);
@@ -40,6 +41,10 @@
}
void _typeItOut(String msg, int pos) {
+ Timer timer;
+ markdownInput.onKeyUp.listen((_) {
+ timer?.cancel();
+ });
addCharacter() {
if (pos > msg.length) {
return;
@@ -48,10 +53,10 @@
markdownInput.focus();
_renderMarkdown();
pos++;
- new Timer(typing, addCharacter);
+ timer = new Timer(typing, addCharacter);
}
- new Timer(typing, addCharacter);
+ timer = new Timer(typing, addCharacter);
}
class NullTreeSanitizer implements NodeTreeSanitizer {
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index efab2a4..c26056a 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -48,6 +48,9 @@
final _olPattern =
new RegExp(r'^([ ]{0,3})(\d{1,9})([\.)])(([ \t])([ \t]*)(.*))?$');
+/// A line of hyphens separated by at least one pipe.
+final _tablePattern = new RegExp(r'^[ ]{0,3}\|?(:?\-+:?\|)+(:?\-+:?)?$');
+
/// Maintains the internal state needed to parse a series of lines into blocks
/// of Markdown suitable for further inline parsing.
class BlockParser {
@@ -113,8 +116,9 @@
///
/// `peek(1)` is equivalent to [next].
String peek(int linesAhead) {
- if (linesAhead < 0)
+ if (linesAhead < 0) {
throw new ArgumentError('Invalid linesAhead: $linesAhead; must be >= 0.');
+ }
// Don't read past the end.
if (_pos >= lines.length - linesAhead) return null;
return lines[_pos + linesAhead];
@@ -706,6 +710,75 @@
const OrderedListSyntax();
}
+/// Parses tables.
+class TableSyntax extends BlockSyntax {
+ static final _pipePattern = new RegExp(r'\s*\|\s*');
+
+ bool get canEndBlock => false;
+
+ const TableSyntax();
+
+ bool canParse(BlockParser parser) {
+ // Note: matches *next* line, not the current one. We're looking for the
+ // bar separating the head row from the body rows.
+ return parser.matchesNext(_tablePattern);
+ }
+
+ /// Parses a table into its three parts:
+ ///
+ /// * a head row of head cells (`<th>` cells)
+ /// * a divider of hyphens and pipes (not rendered)
+ /// * many body rows of body cells (`<td>` cells)
+ Node parse(BlockParser parser) {
+ var alignments = parseAlignments(parser.next);
+ var head = new Element('thead', [parseRow(parser, alignments, 'th')]);
+
+ // Advance past the divider of hyphens.
+ parser.advance();
+
+ var rows = <Element>[];
+ while (!parser.isDone && !parser.matches(_emptyPattern)) {
+ rows.add(parseRow(parser, alignments, 'td'));
+ }
+ var body = new Element('tbody', rows);
+
+ return new Element('table', [head, body]);
+ }
+
+ List<String> parseAlignments(String line) {
+ line = line
+ .replaceFirst(new RegExp(r'^\|'), '')
+ .replaceFirst(new RegExp(r'\|$'), '');
+ return line.split('|').map((column) {
+ if (column.startsWith(':') && column.endsWith(':')) return 'center';
+ if (column.startsWith(':')) return 'left';
+ if (column.endsWith(':')) return 'right';
+ return null;
+ }).toList();
+ }
+
+ Node parseRow(BlockParser parser, List<String> alignments, String cellType) {
+ var line = parser.current
+ .replaceFirst(new RegExp(r'^\|\s*'), '')
+ .replaceFirst(new RegExp(r'\s*\|$'), '');
+ var cells = line.split(_pipePattern);
+ parser.advance();
+ var row = <Element>[];
+
+ for (String cell in cells) {
+ var contents = new UnparsedContent(cell);
+ row.add(new Element(cellType, [contents]));
+ }
+
+ for (var i = 0; i < row.length && i < alignments.length; i++) {
+ if (alignments[i] == null) continue;
+ row[i].attributes['style'] = 'text-align: ${alignments[i]};';
+ }
+
+ return new Element('tr', row);
+ }
+}
+
/// Parses paragraphs of regular text.
class ParagraphSyntax extends BlockSyntax {
static final _reflinkDefinitionStart = new RegExp(r'[ ]{0,3}\[');
diff --git a/pkgs/markdown/test/extensions/tables.unit b/pkgs/markdown/test/extensions/tables.unit
new file mode 100644
index 0000000..c1d6934
--- /dev/null
+++ b/pkgs/markdown/test/extensions/tables.unit
@@ -0,0 +1,63 @@
+>>> basic table
+head | cells
+-----|------
+body | cells
+
+<<<
+<table><thead><tr><th>head</th><th>cells</th></tr></thead><tbody><tr><td>body</td><td>cells</td></tr></tbody></table>
+>>> multiple rows
+head | cells
+-----|------
+body | cells
+more | cells
+
+<<<
+<table><thead><tr><th>head</th><th>cells</th></tr></thead><tbody><tr><td>body</td><td>cells</td></tr><tr><td>more</td><td>cells</td></tr></tbody></table>
+>>> rows wrapped in pipes
+| head | cells |
+|------|-------|
+| body | cells |
+
+<<<
+<table><thead><tr><th>head</th><th>cells</th></tr></thead><tbody><tr><td>body</td><td>cells</td></tr></tbody></table>
+>>> cells with inline syntax
+head `code` | _cells_
+------------|--------
+*text* | <span>text</span>
+<<<
+<table><thead><tr><th>head <code>code</code></th><th><em>cells</em></th></tr></thead><tbody><tr><td><em>text</em></td><td><span>text</span></td></tr></tbody></table>
+>>> cells are parsed before inline syntax
+header | _foo | bar_
+-------|------------
+text | text
+<<<
+<table><thead><tr><th>header</th><th>_foo</th><th>bar_</th></tr></thead><tbody><tr><td>text</td><td>text</td></tr></tbody></table>
+>>> cells contain reference links
+header | header
+-------|--------
+text | [link][here]
+
+[here]: http://url
+<<<
+<table><thead><tr><th>header</th><th>header</th></tr></thead><tbody><tr><td>text</td><td><a href="http://url">link</a></td></tr></tbody></table>
+>>> one column tables
+head
+-----|-----
+body
+<<<
+<table><thead><tr><th>head</th></tr></thead><tbody><tr><td>body</td></tr></tbody></table>
+>>> varying cells per row
+head | foo | bar
+-----|-----
+body
+row with | two cells
+<<<
+<table><thead><tr><th>head</th><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>body</td></tr><tr><td>row with</td><td>two cells</td></tr></tbody></table>
+>>> left, center, and right alignment
+head | cells | here
+:----|:-----:|----:
+body | cells | here
+too | many | cells | here
+
+<<<
+<table><thead><tr><th style="text-align: left;">head</th><th style="text-align: center;">cells</th><th style="text-align: right;">here</th></tr></thead><tbody><tr><td style="text-align: left;">body</td><td style="text-align: center;">cells</td><td style="text-align: right;">here</td></tr><tr><td style="text-align: left;">too</td><td style="text-align: center;">many</td><td style="text-align: right;">cells</td><td>here</td></tr></tbody></table>
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 053e481..701ae63 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -148,4 +148,6 @@
testFile('extensions/setext_headers_with_ids.unit',
blockSyntaxes: [const SetextHeaderWithIdSyntax()]);
+
+ testFile('extensions/tables.unit', blockSyntaxes: [const TableSyntax()]);
}