Skip to content

Commit

Permalink
Add checkbox support to markdown codec (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amir-P authored May 9, 2024
1 parent 8ab66b1 commit eb7b3ca
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
45 changes: 43 additions & 2 deletions packages/parchment/lib/src/codecs/markdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class _ParchmentMarkdownDecoder extends Converter<String, ParchmentDocument> {
RegExp(r'\[(?<text>.+)\]\((?<url>[^ ]+)(?: "(?<title>.+)")?\)');
static final _ulRegExp = RegExp(r'^( *)\* +(.*)');
static final _olRegExp = RegExp(r'^( *)\d+[.)] +(.*)');
static final _clRegExp = RegExp(r'^( *)- +\[( |x|X)\] +(.*)');
static final _bqRegExp = RegExp(r'^> *(.*)');
static final _codeRegExpTag = RegExp(r'^( *)```');

Expand Down Expand Up @@ -101,7 +102,8 @@ class _ParchmentMarkdownDecoder extends Converter<String, ParchmentDocument> {
}

if (_handleOrderedList(line, delta, style) ||
_handleUnorderedList(line, delta, style)) {
_handleUnorderedList(line, delta, style) ||
_handleCheckList(line, delta, style)) {
return true;
}

Expand Down Expand Up @@ -163,6 +165,31 @@ class _ParchmentMarkdownDecoder extends Converter<String, ParchmentDocument> {
return false;
}

bool _handleCheckList(String line, Delta delta, [ParchmentStyle? style]) {
// we do not support nested blocks
if (style?.contains(ParchmentAttribute.block) ?? false) {
return false;
}

ParchmentStyle newStyle =
(style ?? ParchmentStyle()).put(ParchmentAttribute.cl);

final match = _clRegExp.matchAsPrefix(line);
final span = match?.group(3);
final isChecked = match?.group(2) != ' ';
if (isChecked) {
newStyle = newStyle.put(ParchmentAttribute.checked);
}
if (span != null) {
_handleSpan(span, delta, false,
ParchmentStyle().putAll(newStyle.inlineAttributes));
_handleSpan(
'\n', delta, false, ParchmentStyle().putAll(newStyle.lineAttributes));
return true;
}
return false;
}

bool _handleHeading(String line, Delta delta, [ParchmentStyle? style]) {
final match = _headingRegExp.matchAsPrefix(line);
final levelTag = match?.group(1);
Expand Down Expand Up @@ -411,6 +438,16 @@ class _ParchmentMarkdownEncoder extends Converter<ParchmentDocument, String> {
for (final lineNode in node.children) {
if (node.style.containsSame(ParchmentAttribute.ol)) {
lineBuffer.write(currentItemOrder);
} else if (node.style.containsSame(ParchmentAttribute.cl)) {
lineBuffer.write('- [');
if ((lineNode as LineNode)
.style
.contains(ParchmentAttribute.checked)) {
lineBuffer.write('X');
} else {
lineBuffer.write(' ');
}
lineBuffer.write('] ');
}
handleLine(lineNode as LineNode);
if (!lineNode.isLast) {
Expand Down Expand Up @@ -454,6 +491,8 @@ class _ParchmentMarkdownEncoder extends Converter<ParchmentDocument, String> {
} else if (attribute?.key == ParchmentAttribute.block.key) {
_writeBlockTag(buffer, attribute as ParchmentAttribute<String>,
close: close);
} else if (attribute?.key == ParchmentAttribute.checked.key) {
// no-op
} else {
throw ArgumentError('Cannot handle $attribute');
}
Expand Down Expand Up @@ -501,7 +540,9 @@ class _ParchmentMarkdownEncoder extends Converter<ParchmentDocument, String> {
if (close) return; // no close tag needed for simple blocks.

final tag = simpleBlocks[block];
buffer.write(tag);
if (tag != null) {
buffer.write(tag);
}
}
}
}
37 changes: 36 additions & 1 deletion packages/parchment/test/codecs/markdown_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,19 @@ void main() {
expect(act, exp);
});

test('cl', () {
var markdown = '- [ ] Hello\n- [X] This is a\n- [ ] Checklist\n\n';
final act = parchmentMarkdown.decode(markdown).toDelta();
final exp = Delta()
..insert('Hello')
..insert('\n', {'block': 'cl'})
..insert('This is a')
..insert('\n', {'block': 'cl', 'checked': true})
..insert('Checklist')
..insert('\n', {'block': 'cl'});
expect(act, exp);
});

test('simple bq', () {
// var markdown = '> quote\n> > nested\n>#Heading\n>**bold**\n>_italics_\n>* bullet\n>1. 1st point\n>1. 2nd point\n\n';
var markdown =
Expand Down Expand Up @@ -543,6 +556,25 @@ void main() {
expect(result, expected);
});

test('cl', () {
final delta = Delta()
..insert('Hello')
..insert('\n', ParchmentAttribute.cl.toJson())
..insert(
'This is a',
)
..insert('\n', {
...ParchmentAttribute.cl.toJson(),
...ParchmentAttribute.checked.toJson(),
})
..insert('Checklist')
..insert('\n', ParchmentAttribute.cl.toJson());
final result =
parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta));
final expected = '- [ ] Hello\n- [X] This is a\n- [ ] Checklist\n\n';
expect(result, expected);
});

test('multiline blocks', () {
void runFor(ParchmentAttribute<String> attribute, String source,
String expected) {
Expand Down Expand Up @@ -571,7 +603,7 @@ void main() {
}

final doc =
r'[{"insert":"Fleather"},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\nFleather is an "},{"insert":"early preview","attributes":{"b":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Fleather’s rich text editor is built with "},{"insert": "simplicity and flexibility", "attributes":{"i":true}},{"insert":" in mind. It provides clean interface for distraction-free editing. Think "},{"insert": "Medium.com", "attributes":{"c":true}},{"insert": "-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:parchment/parchment.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}}]';
r'[{"insert":"Fleather"},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\nFleather is an "},{"insert":"early preview","attributes":{"b":true}},{"insert":" open source library.\n"},{"insert":"That even supports"},{"insert":"\n","attributes":{"block":"cl"}},{"insert":"Checklists"},{"insert":"\n","attributes":{"checked":true,"block":"cl"}},{"insert":"Documentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Fleather’s rich text editor is built with "},{"insert": "simplicity and flexibility", "attributes":{"i":true}},{"insert":" in mind. It provides clean interface for distraction-free editing. Think "},{"insert": "Medium.com", "attributes":{"c":true}},{"insert": "-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:parchment/parchment.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}}]';
final delta = Delta.fromJson(json.decode(doc) as List);

final markdown = '''
Expand All @@ -581,6 +613,9 @@ _Soft and gentle rich text editing for Flutter applications._
Fleather is an **early preview** open source library.
- [ ] That even supports
- [X] Checklists
### Documentation
* Quick Start
Expand Down

0 comments on commit eb7b3ca

Please sign in to comment.