Automatically migrate from old checklist format

This way people existing checklists do not disappear
This commit is contained in:
Vishesh Handa
2020-05-01 18:41:30 +02:00
parent b919f1f2ba
commit 01db30c613
3 changed files with 27 additions and 137 deletions

View File

@ -47,6 +47,12 @@ class Checklist {
multiLine: false,
);
// FIXME: Added on 2020-05-01: Remove after 3-4 months
static final _oldPattern = RegExp(
r'^ *\[([ xX])\] +(.*)$',
multiLine: false,
);
Note _note;
List<ChecklistItem> items = [];
@ -59,6 +65,11 @@ class Checklist {
for (var i = 0; i < _lines.length; i++) {
var line = _lines[i];
var oldPatMatch = _oldPattern.firstMatch(line);
if (oldPatMatch != null) {
line = '- ' + line;
}
var match = _pattern.firstMatch(line);
if (match == null) {
continue;

View File

@ -1,137 +0,0 @@
import 'package:markdown/markdown.dart' as md;
import 'package:gitjournal/core/note.dart';
class ChecklistLegacyMigrator {
Note _note;
List<md.Node> nodes;
ChecklistLegacyMigrator(this._note) {
var doc = md.Document(
encodeHtml: false,
inlineSyntaxes: [TaskListSyntax()],
extensionSet: md.ExtensionSet.gitHubFlavored,
);
nodes = doc.parseInline(_note.body);
_cleanupNodes(nodes);
}
void _cleanupNodes(List<md.Node> nodes) {
if (nodes.length <= 1) {
return;
}
var last = nodes.last;
var secLast = nodes[nodes.length - 2];
if (last is! md.Text) {
return;
}
if (secLast is! md.Element) {
return;
}
var elem = secLast as md.Element;
if (elem.tag != 'input' || elem.attributes['type'] != 'checkbox') {
return;
}
// Some times we get an extra \n in the end, not sure why.
if (last.textContent == '\n') {
nodes.length = nodes.length - 1;
if (!elem.attributes["text"].endsWith('\n')) {
elem.attributes["text"] += '\n';
}
}
}
Note get note {
if (nodes.isEmpty) return _note;
var renderer = CustomRenderer();
_note.body = renderer.render(nodes);
return _note;
}
}
/// Copied from flutter-markdown - cannot be merged as we added xUpperCase and changed the regexp
/// Parse [task list items](https://github.github.com/gfm/#task-list-items-extension-).
class TaskListSyntax extends md.InlineSyntax {
// FIXME: Waiting for dart-lang/markdown#269 to land
static final String _pattern = r'^ *\[([ xX])\] +(.*)';
TaskListSyntax() : super(_pattern, startCharacter: '['.codeUnitAt(0));
@override
bool onMatch(md.InlineParser parser, Match match) {
md.Element el = md.Element.withTag('input');
el.attributes['type'] = 'checkbox';
el.attributes['checked'] = '${match[1].trim().isNotEmpty}';
var m = match[1].trim();
if (m.isNotEmpty) {
el.attributes['xUpperCase'] = (m[0] == 'X').toString();
}
el.attributes['text'] = '${match[2]}';
parser.addNode(el);
var lenToConsume = match[0].length;
if (match.end + 1 < match.input.length) {
lenToConsume += 1; // Consume \n
}
parser.consume(lenToConsume);
return false; // We are advancing manually
}
}
class CustomRenderer implements md.NodeVisitor {
StringBuffer buffer;
@override
bool visitElementBefore(md.Element element) {
return true;
}
@override
void visitText(md.Text text) {
//print("visitText ${text.text}#");
buffer.write(text.text);
}
@override
void visitElementAfter(md.Element element) {
final String tag = element.tag;
if (tag == 'input') {
var el = element;
if (el is md.Element && el.attributes['type'] == 'checkbox') {
bool val = el.attributes['checked'] != 'false';
if (val) {
if (el.attributes['xUpperCase'] != 'false') {
buffer.write('- [x] ');
} else {
buffer.write('- [X] ');
}
} else {
buffer.write('- [ ] ');
}
var text = el.attributes['text'];
buffer.write(text);
//print("writeElem $text#");
if (!text.endsWith('\n')) {
//print("writeElem newLine#");
buffer.write('\n');
}
}
}
}
String render(List<md.Node> nodes) {
buffer = StringBuffer();
for (final node in nodes) {
node.accept(this);
}
return buffer.toString();
}
}

View File

@ -222,5 +222,21 @@ Booga Wooga
note = checklist.note;
expect(note.body, content);
});
test('Migrate from old checklist format', () async {
var content = "[X] One\n[ ] Two";
var notePath = p.join(tempDir.path, "note448.md");
await File(notePath).writeAsString(content);
var parentFolder = NotesFolderFS(null, tempDir.path);
var note = Note(parentFolder, notePath);
await note.load();
var checklist = Checklist(note);
note = checklist.note;
expect(note.body, "- [X] One\n- [ ] Two");
});
});
}