mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-07-01 12:23:44 +08:00
Automatically migrate from old checklist format
This way people existing checklists do not disappear
This commit is contained in:
@ -47,6 +47,12 @@ class Checklist {
|
|||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// FIXME: Added on 2020-05-01: Remove after 3-4 months
|
||||||
|
static final _oldPattern = RegExp(
|
||||||
|
r'^ *\[([ xX])\] +(.*)$',
|
||||||
|
multiLine: false,
|
||||||
|
);
|
||||||
|
|
||||||
Note _note;
|
Note _note;
|
||||||
List<ChecklistItem> items = [];
|
List<ChecklistItem> items = [];
|
||||||
|
|
||||||
@ -59,6 +65,11 @@ class Checklist {
|
|||||||
|
|
||||||
for (var i = 0; i < _lines.length; i++) {
|
for (var i = 0; i < _lines.length; i++) {
|
||||||
var line = _lines[i];
|
var line = _lines[i];
|
||||||
|
var oldPatMatch = _oldPattern.firstMatch(line);
|
||||||
|
if (oldPatMatch != null) {
|
||||||
|
line = '- ' + line;
|
||||||
|
}
|
||||||
|
|
||||||
var match = _pattern.firstMatch(line);
|
var match = _pattern.firstMatch(line);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -222,5 +222,21 @@ Booga Wooga
|
|||||||
note = checklist.note;
|
note = checklist.note;
|
||||||
expect(note.body, content);
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user