As mentioned at the end of Part 2, after the creation date, the rest of the string is called the Description. It can contain projects that start with a plus sign(+) or contexts that start with an at symbol(@) or key/value pairs with a colon(:). We’ll test the projects piece now.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Sub TEST_Projects() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom+Dad due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 1 Debug.Assert clsTodo.Projects(1).Tag = "Family" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom +Phone +Family due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 2 Debug.Print "TEST_Projects" End Sub |
I’m testing zero, one, and two projects. Now let’s update Raw to make this pass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject End If Next i End Property |
This loops through the rest of the elements of the split array and looks for a plus sign at the start. If it finds one, it creates a Project instance and adds it to the Projects collection class. The contexts will be handled similarly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Sub TEST_Contexts() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 1 Debug.Assert clsTodo.Contexts(1).Tag = "Phone" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone @Family due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 2 Debug.Print "TEST_Contexts" End Sub |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject Dim clsContext As CContext vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext End If Next i End Property |
The final special case inside the description is key/value pairs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Sub TEST_KeyValue() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home" Debug.Assert clsTodo.KeyValues.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.KeyValues.Count = 1 Debug.Assert clsTodo.KeyValues(1).Key = "due" Debug.Assert clsTodo.KeyValues(1).Value = "2016-05-30" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone @Family due:2016-05-30 key:val:ue" Debug.Assert clsTodo.KeyValues.Count = 2 Debug.Print "TEST_KeyValue" End Sub |
Again I’m testing zero, one, and two instances.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject Dim clsContext As CContext Dim clsKeyValue As CKeyValue Dim vaKeys As Variant vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext ElseIf InStr(1, vaSplit(i), ":") > 1 Then vaKeys = Split(vaSplit(i), ":", 2) Set clsKeyValue = New CKeyValue clsKeyValue.Key = vaKeys(0) clsKeyValue.Value = vaKeys(1) Me.KeyValues.Add clsKeyValue End If Next i End Property |
Everything else is the description
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Sub TEST_Description() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home" Debug.Assert clsTodo.Desc = "Call Mom@home" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Desc = "Call Mom" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone and Dad @Family due:2016-05-30 key:val:ue" Debug.Assert clsTodo.Desc = "Call Mom and Dad" Debug.Print "TEST_Description" End Sub |
Here are the changes to the bottom of Raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext ElseIf InStr(1, vaSplit(i), ":") > 1 Then vaKeys = Split(vaSplit(i), ":", 2) Set clsKeyValue = New CKeyValue clsKeyValue.Key = vaKeys(0) clsKeyValue.Value = vaKeys(1) Me.KeyValues.Add clsKeyValue Else Me.Desc = Me.Desc & vaSplit(i) & Space(1) End If Next i 'remove the trailing space If Len(Me.Desc) > 1 Then Me.Desc = Left$(Me.Desc, Len(Me.Desc) - 1) End If |
And that’s it. A properly parsed Todo.txt string ready to be used in your application. And if I make an changes to my app, I can run these tests to make sure I didn’t break anything.
You can download TodoTxt.zip
Series:
Series: