diff --git a/src/Microdown-Toc/MicHTMLVisitor.extension.st b/src/Microdown-Toc/MicHTMLVisitor.extension.st
index 64750f5a..c1d3b695 100644
--- a/src/Microdown-Toc/MicHTMLVisitor.extension.st
+++ b/src/Microdown-Toc/MicHTMLVisitor.extension.st
@@ -1,12 +1,28 @@
Extension { #name : 'MicHTMLVisitor' }
{ #category : '*Microdown-Toc' }
-MicHTMLVisitor >> visitTocEntry: aMicTocEntry [
-
- canvas tag
- name: 'li';
- with: [
- aMicTocEntry header headerElements do:
- [:each | each accept: self] ].
- canvas newLine
+MicHTMLVisitor >> visitTocEntry: aMicTocEntry [
+
+ aMicTocEntry isLeaf
+ ifTrue: [
+ canvas tag
+ name: 'li';
+ with: [
+ aMicTocEntry header headerElements do: [ :each |
+ each accept: self ] ].
+ canvas newLine ]
+ ifFalse: [
+ canvas tag
+ name: 'li';
+ with: [
+ aMicTocEntry header headerElements do: [ :each |
+ each accept: self ].
+ canvas newLine.
+ canvas tag
+ name: 'ol';
+ with: [
+ canvas newLine.
+ aMicTocEntry children do: [ :each | each accept: self ] ].
+ canvas newLine ].
+ canvas newLine ]
]
diff --git a/src/Microdown-Toc/MicTocBuilder.class.st b/src/Microdown-Toc/MicTocBuilder.class.st
index 6809ea7f..41a54840 100644
--- a/src/Microdown-Toc/MicTocBuilder.class.st
+++ b/src/Microdown-Toc/MicTocBuilder.class.st
@@ -3,18 +3,28 @@ Class {
#superclass : 'MicrodownVisitor',
#instVars : [
'tocItems',
- 'headerStack'
+ 'headerStack',
+ 'limit'
],
#category : 'Microdown-Toc',
#package : 'Microdown-Toc'
}
{ #category : 'initialization' }
-MicTocBuilder >> initialize [
-
+MicTocBuilder >> initialize [
+
super initialize.
tocItems := OrderedCollection new.
headerStack := Stack new.
+ limit := 6
+]
+
+{ #category : 'configure' }
+MicTocBuilder >> limitUpTo: anInteger [
+
+ (anInteger between: 1 and: 5) ifFalse: [
+ self error: 'Invalid input. Limit should be between 1 and 5' ].
+ limit := anInteger
]
{ #category : 'accessing' }
@@ -34,23 +44,30 @@ MicTocBuilder >> tocItems [
{ #category : 'visiting' }
MicTocBuilder >> visitHeader: aMicHeader [
- headerStack isEmpty
- ifTrue: [
- | newTopLevelTocEnty |
- newTopLevelTocEnty := MicTocEntry new for: aMicHeader.
- headerStack push: newTopLevelTocEnty.
- tocItems add: newTopLevelTocEnty.
+ | newTocEntry |
+ newTocEntry := MicTocEntry new for: aMicHeader.
+
+ aMicHeader level > limit ifTrue: [ ^ self ].
+
+ headerStack isEmpty ifTrue: [
+ headerStack push: newTocEntry.
+ tocItems add: newTocEntry.
^ self ].
+
aMicHeader level = headerStack top level
- ifTrue: [
- | newTopLevelTocEnty |
- headerStack pop.
- newTopLevelTocEnty := MicTocEntry new for: aMicHeader.
- headerStack push: newTopLevelTocEnty.
- tocItems add: newTopLevelTocEnty.
- ]
- ifFalse: [
- aMicHeader level > headerStack top level
- ifTrue: [
- headerStack top addChild: (MicTocEntry new for: aMicHeader) ] ]
+ ifTrue: [
+ headerStack pop.
+ headerStack isEmpty
+ ifTrue: [ tocItems add: newTocEntry ]
+ ifFalse: [ headerStack top addChild: newTocEntry ] ]
+ ifFalse: [
+ aMicHeader level > headerStack top level
+ ifTrue: [ headerStack top addChild: newTocEntry ]
+ ifFalse: [
+ [
+ headerStack isNotEmpty and: [
+ headerStack top level >= aMicHeader level ] ] whileTrue: [
+ headerStack pop ].
+ headerStack isEmpty ifTrue: [ tocItems add: newTocEntry ] ] ].
+ headerStack push: newTocEntry
]
diff --git a/src/Microdown-Toc/MicTocEntry.class.st b/src/Microdown-Toc/MicTocEntry.class.st
index 7b7b147b..b00aed15 100644
--- a/src/Microdown-Toc/MicTocEntry.class.st
+++ b/src/Microdown-Toc/MicTocEntry.class.st
@@ -43,6 +43,12 @@ MicTocEntry >> initialize [
children := OrderedCollection new
]
+{ #category : 'testing' }
+MicTocEntry >> isLeaf [
+
+ ^ children isEmpty
+]
+
{ #category : 'accessing' }
MicTocEntry >> level [
^ header level
diff --git a/src/Microdown-Toc/MicTocEntryTest.class.st b/src/Microdown-Toc/MicTocEntryTest.class.st
new file mode 100644
index 00000000..356c89db
--- /dev/null
+++ b/src/Microdown-Toc/MicTocEntryTest.class.st
@@ -0,0 +1,28 @@
+"
+A MicTocEntryTest is a test class for testing the behavior of MicTocEntry
+"
+Class {
+ #name : 'MicTocEntryTest',
+ #superclass : 'TestCase',
+ #category : 'Microdown-Toc',
+ #package : 'Microdown-Toc'
+}
+
+{ #category : 'tests' }
+MicTocEntryTest >> testWithChildrenIsNotLeaf [
+
+ | entry |
+ entry := MicTocEntry new.
+ entry addChild: MicTocEntry new.
+
+ self deny: entry isLeaf
+]
+
+{ #category : 'tests' }
+MicTocEntryTest >> testWithoutChildrenIsLeaf [
+
+ | entry |
+ entry := MicTocEntry new.
+
+ self assert: entry isLeaf
+]
diff --git a/src/Microdown-Toc/MicTocHTMLTest.class.st b/src/Microdown-Toc/MicTocHTMLTest.class.st
index aec5e3c5..89a7243d 100644
--- a/src/Microdown-Toc/MicTocHTMLTest.class.st
+++ b/src/Microdown-Toc/MicTocHTMLTest.class.st
@@ -40,3 +40,26 @@ MicTocHTMLTest >> testHTMLRender [
''
]
+
+{ #category : 'tests' }
+MicTocHTMLTest >> testHTMLRenderSimpleNesting [
+
+ | doc builder htmlVisitor |
+ doc := Microdown parse: '
+# Header1
+## Header11
+'.
+ builder := MicTocBuilder new.
+ builder visit: doc.
+ self assert: builder toc children first children isNotEmpty.
+
+ htmlVisitor := MicHTMLVisitor new.
+ htmlVisitor visit: builder toc.
+
+ self
+ assert: htmlVisitor contents
+ equals:
+ String crlf , '
' , String crlf , '- Header1' , String crlf
+ , '
' , String crlf , '- Header11
' , String crlf
+ , '
' , String crlf , ' ' , String crlf , '
'
+]
diff --git a/src/Microdown-Toc/MicTocTest.class.st b/src/Microdown-Toc/MicTocTest.class.st
index a0c48e09..f06bc989 100644
--- a/src/Microdown-Toc/MicTocTest.class.st
+++ b/src/Microdown-Toc/MicTocTest.class.st
@@ -5,6 +5,192 @@ Class {
#package : 'Microdown-Toc'
}
+{ #category : 'tests' }
+MicTocTest >> testAlternatingheaderLevels [
+
+ | doc builder topLevelTocItems |
+ doc := Microdown parse: '
+# Header1
+## Header11
+# Header2
+## Header21
+'.
+ builder := MicTocBuilder new.
+
+ builder visit: doc.
+
+ topLevelTocItems := builder tocItems.
+ self assert: builder tocItems size equals: 2.
+ self assert: topLevelTocItems first header text equals: 'Header1'.
+ self
+ assert: topLevelTocItems first children first header text
+ equals: 'Header11'.
+ self assert: topLevelTocItems second header text equals: 'Header2'.
+ self
+ assert: topLevelTocItems second children first header text
+ equals: 'Header21'
+]
+
+{ #category : 'tests' }
+MicTocTest >> testComplexDeeplyNestedEntries [
+
+ | doc builder |
+ doc := Microdown parse: '
+# Header1
+## Header11
+### Header111
+### Header112
+## Header12
+# Header2
+## Header21
+'.
+ builder := MicTocBuilder new.
+
+ builder visit: doc.
+
+ self assert: builder tocItems size equals: 2.
+ self assert: builder tocItems first header text equals: 'Header1'.
+ self
+ assert: builder tocItems first children first header text
+ equals: 'Header11'.
+ self
+ assert:
+ builder tocItems first children first children first header text
+ equals: 'Header111'.
+ self
+ assert:
+ builder tocItems first children first children second header text
+ equals: 'Header112'.
+ self assert: builder tocItems second header text equals: 'Header2'
+]
+
+{ #category : 'tests' }
+MicTocTest >> testLimitDepthToLevelOne [
+
+ | doc builder topLevelTocItems |
+ doc := Microdown parse: '
+# Header1
+## Header11
+# Header2
+## Header21
+'.
+ builder := MicTocBuilder new.
+
+ builder limitUpTo: 1.
+ builder visit: doc.
+
+ topLevelTocItems := builder tocItems.
+ self assert: builder tocItems size equals: 2.
+ self assert: topLevelTocItems first header text equals: 'Header1'.
+ self assert: topLevelTocItems first isLeaf.
+ self assert: topLevelTocItems second header text equals: 'Header2'.
+ self assert: topLevelTocItems second isLeaf
+]
+
+{ #category : 'tests' }
+MicTocTest >> testLimitDepthWithInvalidInput [
+
+ | builder should |
+ builder := MicTocBuilder new.
+ should := false.
+
+ [ builder limitUpTo: 0 ]
+ on: Error
+ do: [ :ex |
+ self
+ assert: ex description
+ equals: 'Error: Invalid input. Limit should be between 1 and 5'.
+ should := true ].
+ self assert: should
+]
+
+{ #category : 'tests' }
+MicTocTest >> testLimitDepthWithInvalidInput0 [
+
+ | builder should |
+ builder := MicTocBuilder new.
+ should := false.
+
+ [ builder limitUpTo: 0 ]
+ on: Error
+ do: [ :ex |
+ self
+ assert: ex description
+ equals: 'Error: Invalid input. Limit should be between 1 and 5'.
+ should := true ].
+ self assert: should
+]
+
+{ #category : 'tests' }
+MicTocTest >> testLimitDepthWithInvalidInput6 [
+
+ | builder should |
+ builder := MicTocBuilder new.
+ should := false.
+
+ [ builder limitUpTo: 6 ]
+ on: Error
+ do: [ :ex |
+ self
+ assert: ex description
+ equals: 'Error: Invalid input. Limit should be between 1 and 5'.
+ should := true ].
+ self assert: should
+]
+
+{ #category : 'tests' }
+MicTocTest >> testMalformedHeaderOrder [
+
+ | doc builder firstTocItem secondTocItem thirdTocItem |
+ doc := Microdown parse: '
+### Header001
+## Header01
+# Header1
+'.
+ builder := MicTocBuilder new.
+
+ builder visit: doc.
+
+ self assert: builder tocItems size equals: 3.
+ firstTocItem := builder tocItems first.
+ self assert: firstTocItem header text equals: 'Header001'.
+ self assertEmpty: firstTocItem children.
+ secondTocItem := builder tocItems second.
+ self assert: secondTocItem header text equals: 'Header01'.
+ self assertEmpty: secondTocItem children.
+ thirdTocItem := builder tocItems third.
+ self assert: thirdTocItem header text equals: 'Header1'.
+ self assertEmpty: thirdTocItem children
+]
+
+{ #category : 'tests' }
+MicTocTest >> testSimpleDeeplyNestedEntries [
+
+ | doc builder |
+ doc := Microdown parse: '
+# Header1
+## Header11
+### Header111
+#### Header1111
+'.
+ builder := MicTocBuilder new.
+ builder visit: doc.
+ self assert: builder tocItems size equals: 1.
+ self assert: builder tocItems first header text equals: 'Header1'.
+ self
+ assert: builder tocItems first children first header text
+ equals: 'Header11'.
+ self
+ assert:
+ builder tocItems first children first children first header text
+ equals: 'Header111'.
+ self
+ assert:
+ builder tocItems first children first children first children first
+ header text
+ equals: 'Header1111'
+]
+
{ #category : 'tests' }
MicTocTest >> testSimpleFlatEntries [
@@ -49,3 +235,27 @@ lkhkhk
self assert: builder tocItems first children first header text equals: 'Header11'.
]
+
+{ #category : 'tests' }
+MicTocTest >> testTopLevelHeaderWithTwoSecondLevelHeaders [
+
+ | doc builder topLevelTocItem |
+ doc := Microdown parse: '
+# Header1
+## Header11
+## Header12
+'.
+ builder := MicTocBuilder new.
+
+ builder visit: doc.
+
+ topLevelTocItem := builder tocItems first.
+ self assert: builder tocItems size equals: 1.
+ self assert: topLevelTocItem header text equals: 'Header1'.
+ self
+ assert: topLevelTocItem children first header text
+ equals: 'Header11'.
+ self
+ assert: topLevelTocItem children second header text
+ equals: 'Header12'
+]