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:
The file doesn’t work.
The error mesage is….
—-
Public Sub Initialize()
Set gclsTodos = New CTodos
gclsTodos.FillFromFile <—– (Yellow)
—-
How do I fix it?
Thank you
The error is happening inside the class. It’s almost certainly because you don’t have a file named
todo.txt
in the same folder as the TodoTxt.xlsm workbook. The text file can be empty, but it must be there and be named properly.To see the real error, in the VBE go to Tools – Options – General and choose Break in Class Module in the Error Trapping area. The offending line inside the class module will be highlighted. If it’s not the file opening, post back and let me know.