import 'package:flutter/material.dart'; import 'package:petitparser/petitparser.dart'; import 'package:nextcloud_reminder/types/tasks.dart'; import 'package:tuple/tuple.dart'; abstract class TodoParser { static final _definition = TodoTxtEvaluatorDefinition(); static final _todoParser = _definition.build(); static List parse(List input) { final List ret = []; var line=1; for (var element in input) { var parsed = _todoParser.parse(element); if (parsed.isSuccess) { ret.add(TaskExtra.fromTask(parsed.value, lineNumber: line)); } else { debugPrint(parsed.message); debugPrint(element); } line++; } return ret; } } class TodoTxtEvaluatorDefinition extends TodoTxtDefinition { @override Parser todoLine() => super.todoLine().map((value) => //parser gives the following structure: //0: completed? (null | true) //1: priority? (null | uppercase letter) //2: range? (null | [begin-date, (null | end-date)] //3: description: Tuple5, list, map, text without meta> Task( title: value[3].item5, done: value[0] ?? false, priority: value[1], begin: (value[2] == null ? null : value[2][0]), end: (value[2] == null ? null : value[2][1]), projects: value[3].item2, contexts: value[3].item3, meta: value[3].item4, ) ); } class TodoTxtDefinition extends GrammarDefinition { Tuple5,List, Map, String> _extract_project_and_context(String input) { final projectPattern = RegExp(r'\+([^ ]+)'); final contextPattern = RegExp(r'@([^ ]+)'); final metaPattern = RegExp(r'([^ ]+):([^ ]+)'); var projects = projectPattern.allMatches(input).map((Match m) => m[1]!); var contexts = contextPattern.allMatches(input).map((Match m) => m[1]!); var meta = metaPattern.allMatches(input).map((Match m) => MapEntry(m[1]!, m[2]!)); var cleanInput = input//.replaceAll(projectPattern, '') //.replaceAll(contextPattern, '') .replaceAll(metaPattern, ''); return Tuple5(input, List.of(projects), List.of(contexts), Map.fromEntries(meta), cleanInput.trim()); } @override Parser start() => ref0(todoLine).end(); Parser todoLine() => ref0(completed).trim().optional() & ref0(priority).trim().optional() & (ref0(date).trim() & ref0(date).trim().optional()).optional() // completion date + optional creation date & any().plus().flatten().map(_extract_project_and_context) ; Parser completed() => char('x').map((_) => true); Parser priority() => (char('(') & pattern('A-Z') & char(')')).map((values) => values[1]); Parser date() => (digit().plus().flatten() & char('-') & digit().plus().flatten() & char('-') & digit().plus().flatten()) .map((values) => DateTime(int.parse(values[0]),int.parse(values[2]),int.parse(values[4]))); }