Skip to content

Commit

Permalink
Allow resolving links that contain inline syntax.
Browse files Browse the repository at this point in the history
  • Loading branch information
munificent committed Aug 5, 2015
1 parent 85bb148 commit dcf784e
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 102 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
.packages
.pub
packages
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.7.2

* Allow resolving links that contain inline syntax (#42).

## 0.7.1+3

* Updated homepage.
Expand Down
2 changes: 0 additions & 2 deletions lib/src/html_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ class HtmlRenderer implements NodeVisitor {
buffer.write('<${element.tag}');

// Sort the keys so that we generate stable output.
// TODO(rnystrom): This assumes keys returns a fresh mutable
// collection.
final attributeNames = element.attributes.keys.toList();
attributeNames.sort((a, b) => a.compareTo(b));
for (final name in attributeNames) {
Expand Down
11 changes: 4 additions & 7 deletions lib/src/inline_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,10 @@ class LinkSyntax extends TagSyntax {
if (isNullOrEmpty(match[1])) {
if (linkResolver == null) return null;

// Only allow implicit links if the content is just text.
// TODO(rnystrom): Do we want to relax this?
if (state.children.any((child) => child is! Text)) return null;
// If there are multiple children, but they are all text, send the
// combined text to linkResolver.
var textToResolve =
state.children.fold('', (oldVal, child) => oldVal + child.text);
// Treat the contents as unparsed text even if they happen to match. This
// way, we can handle things like [LINK_WITH_UNDERSCORES] as a link and
// not get confused by the emphasis.
var textToResolve = parser.source.substring(state.endPos, parser.pos);

// See if we have a resolver that will generate a link for us.
resolved = true;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: markdown
version: 0.7.1+3
version: 0.7.2-dev
author: Dart Team <[email protected]>
description: A library for converting markdown to HTML.
homepage: https://github.com/dart-lang/markdown
Expand Down
106 changes: 14 additions & 92 deletions test/markdown_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.

/// Unit tests for markdown.
library markdownTests;
library markdown.test.markdown_test;

import 'package:unittest/unittest.dart';

import 'package:markdown/markdown.dart';

import 'utils.dart';

/// Most of these tests are based on observing how showdown behaves:
/// http://softwaremaniacs.org/playground/showdown-highlight/
void main() {
Expand Down Expand Up @@ -949,7 +952,8 @@ void main() {
});

group('Resolver', () {
var nyanResolver = (text) => new Text('~=[,,_${text}_,,]:3');
nyanResolver(text) => new Text('~=[,,_${text}_,,]:3');

validate('simple link resolver', '''
resolve [this] thing
''', '''
Expand All @@ -960,12 +964,17 @@ void main() {
''', '''
<p>resolve ~=[,,_this_,,]:3 thing</p>
''', imageLinkResolver: nyanResolver);

validate('can resolve link containing inline tags', '''
resolve [*star* _underline_] thing
''', '''
<p>resolve ~=[,,_*star* _underline__,,]:3 thing</p>
''', linkResolver: nyanResolver);
});

group('Custom inline syntax', () {
List<InlineSyntax> nyanSyntax = [
new TextSyntax('nyan', sub: '~=[,,_,,]:3')
];
var nyanSyntax = [new TextSyntax('nyan', sub: '~=[,,_,,]:3')];

validate('simple inline syntax', '''
nyan
''', '''
Expand Down Expand Up @@ -1022,90 +1031,3 @@ void main() {
''', inlineOnly: true);
});
}

/**
* Removes eight spaces of leading indentation from a multiline string.
*
* Note that this is very sensitive to how the literals are styled. They should
* be:
* '''
* Text starts on own line. Lines up with subsequent lines.
* Lines are indented exactly 8 characters from the left margin.'''
*
* This does nothing if text is only a single line.
*/
// TODO(nweiz): Make this auto-detect the indentation level from the first
// non-whitespace line.
String cleanUpLiteral(String text) {
var lines = text.split('\n');
if (lines.length <= 1) return text;

for (var j = 0; j < lines.length; j++) {
if (lines[j].length > 8) {
lines[j] = lines[j].substring(8, lines[j].length);
} else {
lines[j] = '';
}
}

return lines.join('\n');
}

void validate(String description, String markdown, String html,
{List<InlineSyntax> inlineSyntaxes,
Resolver linkResolver, Resolver imageLinkResolver,
bool inlineOnly: false}) {
test(description, () {
markdown = cleanUpLiteral(markdown);
html = cleanUpLiteral(html);

var result = markdownToHtml(markdown,
inlineSyntaxes: inlineSyntaxes,
linkResolver: linkResolver,
imageLinkResolver: imageLinkResolver,
inlineOnly: inlineOnly);
var passed = compareOutput(html, result);

if (!passed) {
// Remove trailing newline.
html = html.substring(0, html.length - 1);

var sb = new StringBuffer();
sb.writeln('Expected: ${html.replaceAll("\n", "\n ")}');
sb.writeln(' Actual: ${result.replaceAll("\n", "\n ")}');

fail(sb.toString());
}
});
}

/// Does a loose comparison of the two strings of HTML. Ignores differences in
/// newlines and indentation.
bool compareOutput(String a, String b) {
int i = 0;
int j = 0;

skipIgnored(String s, int i) {
// Ignore newlines.
while ((i < s.length) && (s[i] == '\n')) {
i++;
// Ignore indentation.
while ((i < s.length) && (s[i] == ' ')) i++;
}

return i;
}

while (true) {
i = skipIgnored(a, i);
j = skipIgnored(b, j);

// If one string runs out of non-ignored strings, the other must too.
if (i == a.length) return j == b.length;
if (j == b.length) return i == a.length;

if (a[i] != b[j]) return false;
i++;
j++;
}
}
94 changes: 94 additions & 0 deletions test/utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library markdown.test.utils;

import 'package:unittest/unittest.dart';

import 'package:markdown/markdown.dart';

/// Removes eight spaces of leading indentation from a multiline string.
///
/// Note that this is very sensitive to how the literals are styled. They should
/// be:
/// '''
/// Text starts on own line. Lines up with subsequent lines.
/// Lines are indented exactly 8 characters from the left margin.'''
///
/// This does nothing if text is only a single line.
// TODO(nweiz): Make this auto-detect the indentation level from the first
// non-whitespace line.
String cleanUpLiteral(String text) {
var lines = text.split('\n');
if (lines.length <= 1) return text;

for (var j = 0; j < lines.length; j++) {
if (lines[j].length > 8) {
lines[j] = lines[j].substring(8, lines[j].length);
} else {
lines[j] = '';
}
}

return lines.join('\n');
}

void validate(String description, String markdown, String html,
{List<InlineSyntax> inlineSyntaxes,
Resolver linkResolver, Resolver imageLinkResolver,
bool inlineOnly: false}) {
test(description, () {
markdown = cleanUpLiteral(markdown);
html = cleanUpLiteral(html);

var result = markdownToHtml(markdown,
inlineSyntaxes: inlineSyntaxes,
linkResolver: linkResolver,
imageLinkResolver: imageLinkResolver,
inlineOnly: inlineOnly);
var passed = compareOutput(html, result);

if (!passed) {
// Remove trailing newline.
html = html.substring(0, html.length - 1);

var sb = new StringBuffer();
sb.writeln('Expected: ${html.replaceAll("\n", "\n ")}');
sb.writeln(' Actual: ${result.replaceAll("\n", "\n ")}');

fail(sb.toString());
}
});
}

/// Does a loose comparison of the two strings of HTML. Ignores differences in
/// newlines and indentation.
bool compareOutput(String a, String b) {
int i = 0;
int j = 0;

skipIgnored(String s, int i) {
// Ignore newlines.
while ((i < s.length) && (s[i] == '\n')) {
i++;
// Ignore indentation.
while ((i < s.length) && (s[i] == ' ')) i++;
}

return i;
}

while (true) {
i = skipIgnored(a, i);
j = skipIgnored(b, j);

// If one string runs out of non-ignored strings, the other must too.
if (i == a.length) return j == b.length;
if (j == b.length) return i == a.length;

if (a[i] != b[j]) return false;
i++;
j++;
}
}

0 comments on commit dcf784e

Please sign in to comment.