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 , '
  1. Header1' , String crlf + , '
      ' , String crlf , '
    1. Header11
    2. ' , String crlf + , '
    ' , String crlf , '
  2. ' , 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' +]