Skip to content

Commit 4b113d5

Browse files
committed
Release 0.6
1 parent 82a263f commit 4b113d5

File tree

11 files changed

+99
-91
lines changed

11 files changed

+99
-91
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.6.0] - 2023-08-06
8+
### Changed
9+
- Bumped SDK to 3.0.0
10+
- Context interface
11+
712
## [0.5.1] - 2023-02-14
813
### Added
914
- Dart 3 support
@@ -79,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7984
### Added
8085
- Initial library version
8186

87+
[0.6.0]: https://github.com/f3ath/marker/compare/0.5.1...0.6.0
8288
[0.5.1]: https://github.com/f3ath/marker/compare/0.5.0...0.5.1
8389
[0.5.0]: https://github.com/f3ath/marker/compare/0.4.0...0.5.0
8490
[0.4.0]: https://github.com/f3ath/marker/compare/0.3.0...0.4.0
@@ -91,4 +97,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9197
[0.1.0]: https://github.com/f3ath/marker/compare/0.0.4...0.1.0
9298
[0.0.4]: https://github.com/f3ath/marker/compare/0.0.3...0.0.4
9399
[0.0.3]: https://github.com/f3ath/marker/compare/0.0.2...0.0.3
94-
[0.0.2]: https://github.com/f3ath/marker/compare/0.0.1...0.0.2
100+
[0.0.2]: https://github.com/f3ath/marker/compare/0.0.1...0.0.2

lib/flavors.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:marker/src/flavors/original.dart';
44

55
/// The original flavor.
66
/// See https://daringfireball.net/projects/markdown/syntax
7-
final Map<String, Node Function()> original = {
7+
final original = <String, Node Function()>{
88
'h1': () => Header(1),
99
'h2': () => Header(2),
1010
'h3': () => Header(3),
@@ -27,7 +27,7 @@ final Map<String, Node Function()> original = {
2727
};
2828

2929
/// The changelog flavor.
30-
final Map<String, Node Function()> changelog = {
30+
final changelog = <String, Node Function()>{
3131
...original,
3232
'h2': () => Release(), // Release header is special
3333
};

lib/marker.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ String render(Iterable<md.Node> nodes,
2020
node.accept(builder);
2121
}
2222
return (builder.root.render(context) +
23-
context.references.join(context.lineBreak))
23+
context.ref.entries
24+
.map((e) => '[${e.key}]: ${e.value}')
25+
.join(context.lineBreak))
2426
.trim();
2527
}

lib/src/ast/builder.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import 'package:marker/src/ast/node.dart';
66
/// element in the parsed tree the [Node] producer returns a node of
77
/// the rendering tree.
88
class Builder implements md.NodeVisitor {
9-
Builder(this.flavor) {
10-
_stack.add(Node());
11-
}
9+
Builder(this.flavor);
1210

1311
final Map<String, Node Function()> flavor;
14-
final List<Node> _stack = [];
12+
final _stack = [Node()];
1513

1614
/// Contains the root of the rendering tree
1715
Node get root => _stack.last;
@@ -30,8 +28,7 @@ class Builder implements md.NodeVisitor {
3028

3129
@override
3230
bool visitElementBefore(md.Element element) {
33-
final tag = element.tag;
34-
final node = flavor[tag]?.call() ?? Node();
31+
final node = flavor[element.tag]?.call() ?? Node();
3532
node.attributes.addAll(element.attributes);
3633
_stack.add(node);
3734
return true;

lib/src/ast/context.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
class Context {
33
Context(this.inlineImages, this.inlineLinks, {this.lineBreak = '\n'});
44

5+
/// Returns the next generated id for images and links.
6+
String nextId() => 'id${_id++}';
7+
58
/// Whether the images should be rendered inline (or as references)
69
final bool inlineImages;
710

@@ -14,5 +17,8 @@ class Context {
1417
/// Accumulates references to images and links generated during rendering.
1518
/// When the rendering is done, these lines will be added to the bottom
1619
/// of the generated document.
17-
final List<String> references = [];
20+
final ref = <String, String>{};
21+
22+
/// Internal id generator.
23+
int _id = 1;
1824
}

lib/src/ast/node.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import 'package:marker/src/ast/text.dart';
55
/// The base rendering tree node.
66
class Node implements Renderable {
77
/// Node's children
8-
final List<Renderable> children = [];
8+
final children = <Renderable>[];
99

1010
/// Attributes are copied from the corresponding parsed tree
1111
/// during tree building.
12-
final Map<String, String> attributes = {};
12+
final attributes = <String, String>{};
1313

1414
/// Renders all children and returns concatenated output.
1515
@override
1616
String render(Context context) =>
17-
children.map((node) => node.render(context)).join();
17+
children.map((it) => it.render(context)).join();
1818

1919
/// Adds a child text node
2020
void addText(String text) {

lib/src/ast/text.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ import 'package:marker/src/ast/renderable.dart';
44
class Text implements Renderable {
55
Text(this.text);
66

7+
static final _header = RegExp(r'^#');
8+
static final _orderedListItem = RegExp(r'^(\d+)\. ');
9+
static final _specialChars = RegExp(r'[*_~|\[\]]');
10+
static final _unorderedListItem = RegExp(r'^[+-] ');
11+
712
final String text;
813

914
/// Escapes common markdown special characters if those could be
1015
/// misinterpreted.
1116
@override
12-
String render(Context context) => text
17+
String render(Context _) => text
1318
// dot after a number in the beginning of the string is a list item
14-
.replaceAllMapped(RegExp(r'^(\d+)\. '), (m) => '${m[1]}\\. ')
19+
.replaceAllMapped(_orderedListItem, (m) => '${m[1]}\\. ')
1520
// Special markdown chars: emphasis, strikethrough, table cell, links
16-
.replaceAllMapped(RegExp(r'[*_~|\[\]]'), (m) => '\\${m[0]}')
21+
.replaceAllMapped(_specialChars, (m) => '\\${m[0]}')
1722
// + and - in the beginning of the string is a list item
18-
.replaceAllMapped(RegExp(r'^[+-] '), (m) => '\\${m[0]}')
23+
.replaceAllMapped(_unorderedListItem, (m) => '\\${m[0]}')
1924
// # in the beginning of the string is a header
20-
.replaceAllMapped(RegExp(r'^#'), (m) => '\\${m[0]}');
25+
.replaceAllMapped(_header, (m) => '\\${m[0]}');
2126
}

lib/src/flavors/changelog.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@ class _Link implements Renderable {
2020

2121
@override
2222
String render(Context context) {
23-
final innerText =
24-
'[${_link.children.map((e) => e.render(context)).join()}]';
23+
final innerText = _link.children.map((e) => e.render(context)).join();
2524
var href = _link.attributes['href']!;
2625
final title = _link.attributes['title'];
2726
if (title != null) href += ' "$title"';
28-
context.references.add('$innerText: $href');
29-
return innerText;
27+
context.ref[innerText] = href;
28+
return '[$innerText]';
3029
}
3130
}

lib/src/flavors/original.dart

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -56,59 +56,56 @@ class OrderedList extends Node {
5656
class ListItem extends Node {
5757
@override
5858
String render(Context context) {
59-
if (children.isNotEmpty) {
60-
if (_isBlock(children.first)) {
61-
return _renderBlockChildren(context);
62-
}
59+
if (children.isNotEmpty && _isBlock(children.first)) {
60+
return _renderBlockChildren(context);
6361
}
6462
return children
65-
.map((node) {
66-
if (node is OrderedList || node is UnorderedList) {
67-
// We have a sublist. It must be separated from the previous
68-
// text node and from other block elements.
69-
final indent = (node is UnorderedList) ? 2 : 4;
70-
return context.lineBreak +
71-
node
72-
.render(context)
73-
.addLinePrefix(' ' * indent, context.lineBreak) +
74-
context.lineBreak;
75-
}
76-
return node.render(context);
77-
})
63+
.map((node) => switch (node) {
64+
OrderedList() ||
65+
UnorderedList() =>
66+
// We have a sublist. It must be separated from the previous
67+
// text node and from other block elements.
68+
context.lineBreak +
69+
node.render(context).addLinePrefix(
70+
' ' * ((node is UnorderedList) ? 2 : 4),
71+
context.lineBreak) +
72+
context.lineBreak,
73+
_ => node.render(context)
74+
})
7875
.join()
7976
.trim() +
8077
context.lineBreak;
8178
}
8279

8380
String _renderBlockChildren(Context context) =>
8481
children
85-
.map((node) {
86-
if (node is BlockQuote) {
87-
return node
88-
.render(context)
89-
.addLinePrefix(' ' * 4, context.lineBreak);
90-
}
91-
if (node is Pre) {
92-
// A code block in a list gets 3 spaces
93-
// despite the standard requiring 4.
94-
// @see https://daringfireball.net/projects/markdown/syntax#list
95-
// So we have to compensate here.
96-
return node
97-
.render(context)
98-
.addLinePrefix(' ' * 3, context.lineBreak);
99-
}
100-
return ' ' * 4 + node.render(context).trim();
101-
})
82+
.map((node) => switch (node) {
83+
BlockQuote() => node
84+
.render(context)
85+
.addLinePrefix(' ' * 4, context.lineBreak),
86+
Pre() =>
87+
// A code block in a list gets 3 spaces
88+
// despite the standard requiring 4.
89+
// @see https://daringfireball.net/projects/markdown/syntax#list
90+
// So we have to compensate here.
91+
node
92+
.render(context)
93+
.addLinePrefix(' ' * 3, context.lineBreak),
94+
_ => ' ' * 4 + node.render(context).trim()
95+
})
10296
.join(context.lineBreak * 2)
10397
.trim() +
10498
context.lineBreak * 2;
10599

106-
bool _isBlock(node) =>
107-
node is Header ||
108-
node is Paragraph ||
109-
node is BlockQuote ||
110-
node is OrderedList ||
111-
node is UnorderedList;
100+
bool _isBlock(node) => switch (node) {
101+
Header() ||
102+
Paragraph() ||
103+
BlockQuote() ||
104+
OrderedList() ||
105+
UnorderedList() =>
106+
true,
107+
_ => false
108+
};
112109
}
113110

114111
class Pre extends Node {}
@@ -122,20 +119,11 @@ class Code extends Node {
122119
if (text.contains(context.lineBreak)) {
123120
return text.addLinePrefix(' ' * 4, context.lineBreak);
124121
}
125-
final fencing = _detectFencing(text);
126-
return fencing + text + fencing;
122+
return text.fenced('`');
127123
}
128124

129125
@override
130126
void addText(String text) => _buf.write(text);
131-
132-
String _detectFencing(String text) {
133-
var fencing = '';
134-
do {
135-
fencing += '`';
136-
} while (text.contains(fencing));
137-
return fencing;
138-
}
139127
}
140128

141129
class HorizontalRule extends Node {
@@ -157,46 +145,51 @@ class Link extends Node {
157145
String render(Context context) {
158146
final innerText = '[${super.render(context)}]';
159147
var href = attributes['href']!;
160-
if (attributes.containsKey('title')) {
161-
href += ' "${attributes['title']}"';
148+
if (attributes case {'title': String title}) {
149+
href += ' "$title"';
162150
}
163-
164151
if (context.inlineLinks) {
165152
return '$innerText($href)';
166153
}
167-
final id = '[id${_id++}]';
168-
context.references.add('$id: $href');
169-
return '$innerText$id';
154+
final id = context.nextId();
155+
context.ref[id] = href;
156+
return '$innerText[$id]';
170157
}
171158
}
172159

173160
class Image extends Node {
174161
@override
175162
String render(Context context) {
176163
var src = attributes['src']!;
177-
if (attributes.containsKey('title')) {
178-
src += ' "${attributes['title']}"';
164+
if (attributes case {'title': String title}) {
165+
src += ' "$title"';
179166
}
180167
final alt = attributes['alt'] ?? '';
181168
if (context.inlineImages) {
182169
return '![$alt]($src)';
183170
}
184-
final id = '[id${_id++}]';
185-
context.references.add('$id: $src');
186-
return '![$alt]$id';
171+
final id = context.nextId();
172+
context.ref[id] = src;
173+
return '![$alt][$id]';
187174
}
188175
}
189176

190-
int _id = 1; // id generator for images and links
191-
192177
extension _StringExt on String {
193178
static const _splitter = LineSplitter();
194179

195180
/// Adds [prefix] to all lines in the string.
196181
addLinePrefix(String prefix, String lineBreak, {bool trim = false}) =>
197182
_splitter
198183
.convert(this)
199-
.map((_) => prefix + _)
200-
.map((_) => trim ? _.trim() : _)
184+
.map((it) => prefix + it)
185+
.map((it) => trim ? it.trim() : it)
201186
.join(lineBreak);
187+
188+
String fenced(String block) {
189+
var fence = '';
190+
do {
191+
fence += block;
192+
} while (contains(fence));
193+
return fence + this + fence;
194+
}
202195
}

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: marker
22
description: A renderer (printer) for the markdown library. Renders the parsed AST back into markdown.
3-
version: 0.5.1
3+
version: 0.6.0
44
homepage: https://github.com/f3ath/marker
55

66
cider:
@@ -9,7 +9,7 @@ cider:
99
diff: https://github.com/f3ath/marker/compare/%from%...%to%
1010

1111
environment:
12-
sdk: '>=2.19.0 <4.0.0'
12+
sdk: '>=3.0.0 <4.0.0'
1313

1414
dependencies:
1515
markdown: ^7.0.0

test/original/list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ sit amet velit.
4242
b = 2;
4343

4444

45-
123\. This is not a list item.
45+
123\. This is not a list item.

0 commit comments

Comments
 (0)