mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-07-15 07:56:11 +08:00
Add a Checklist class
This commit is contained in:
101
lib/core/checklist.dart
Normal file
101
lib/core/checklist.dart
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:markdown/markdown.dart' as md;
|
||||||
|
|
||||||
|
import 'package:gitjournal/core/note.dart';
|
||||||
|
|
||||||
|
class ChecklistItem {
|
||||||
|
bool checked;
|
||||||
|
String text;
|
||||||
|
md.Element element;
|
||||||
|
|
||||||
|
ChecklistItem({
|
||||||
|
@required this.checked,
|
||||||
|
@required this.text,
|
||||||
|
@required this.element,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ChecklistItem: $checked $text';
|
||||||
|
}
|
||||||
|
|
||||||
|
class Checklist {
|
||||||
|
Note note;
|
||||||
|
List<ChecklistItem> items;
|
||||||
|
|
||||||
|
Checklist(this.note) {
|
||||||
|
items = ChecklistBuilder().parse(note.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
@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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChecklistBuilder implements md.NodeVisitor {
|
||||||
|
List<ChecklistItem> list;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool visitElementBefore(md.Element element) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void visitText(md.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';
|
||||||
|
var item = ChecklistItem(
|
||||||
|
checked: val,
|
||||||
|
text: el.attributes['text'],
|
||||||
|
element: el,
|
||||||
|
);
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ChecklistItem> build(List<md.Node> nodes) {
|
||||||
|
list = <ChecklistItem>[];
|
||||||
|
for (md.Node node in nodes) {
|
||||||
|
node.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ChecklistItem> parse(String text) {
|
||||||
|
var doc = md.Document(
|
||||||
|
encodeHtml: false,
|
||||||
|
inlineSyntaxes: [TaskListSyntax()],
|
||||||
|
extensionSet: md.ExtensionSet.gitHubFlavored,
|
||||||
|
);
|
||||||
|
|
||||||
|
var nodes = doc.parseInline(text);
|
||||||
|
return build(nodes);
|
||||||
|
}
|
||||||
|
}
|
60
test/checklist_test.dart
Normal file
60
test/checklist_test.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:gitjournal/core/checklist.dart';
|
||||||
|
import 'package:gitjournal/core/note.dart';
|
||||||
|
import 'package:gitjournal/core/notes_folder.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('Note', () {
|
||||||
|
Directory tempDir;
|
||||||
|
|
||||||
|
setUpAll(() async {
|
||||||
|
tempDir = await Directory.systemTemp.createTemp('__notes_test__');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDownAll(() async {
|
||||||
|
tempDir.deleteSync(recursive: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should parse simple checklists', () async {
|
||||||
|
var content = """---
|
||||||
|
title: Foo
|
||||||
|
modified: 2017-02-15T22:41:19+01:00
|
||||||
|
---
|
||||||
|
|
||||||
|
# Title 1
|
||||||
|
|
||||||
|
How are you doing?
|
||||||
|
|
||||||
|
[ ] item 1
|
||||||
|
[x] item 2
|
||||||
|
[X] item 3
|
||||||
|
[ ] item 4
|
||||||
|
|
||||||
|
Booga Wooga
|
||||||
|
""";
|
||||||
|
|
||||||
|
var notePath = p.join(tempDir.path, "note.md");
|
||||||
|
File(notePath).writeAsString(content);
|
||||||
|
|
||||||
|
var parentFolder = NotesFolder(null, tempDir.path);
|
||||||
|
var note = Note(parentFolder, notePath);
|
||||||
|
await note.load();
|
||||||
|
|
||||||
|
var checklist = Checklist(note);
|
||||||
|
expect(checklist.items.length, equals(4));
|
||||||
|
|
||||||
|
expect(checklist.items[0].checked, false);
|
||||||
|
expect(checklist.items[1].checked, true);
|
||||||
|
expect(checklist.items[2].checked, true);
|
||||||
|
expect(checklist.items[3].checked, false);
|
||||||
|
|
||||||
|
expect(checklist.items[0].text, "item 1");
|
||||||
|
expect(checklist.items[1].text, "item 2");
|
||||||
|
expect(checklist.items[2].text, "item 3");
|
||||||
|
expect(checklist.items[3].text, "item 4");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user