diff --git a/src/Microdown-ReferenceChecker/MicReferenceChecker.class.st b/src/Microdown-ReferenceChecker/MicReferenceChecker.class.st index c6e500e9..89c4356b 100644 --- a/src/Microdown-ReferenceChecker/MicReferenceChecker.class.st +++ b/src/Microdown-ReferenceChecker/MicReferenceChecker.class.st @@ -16,6 +16,31 @@ Class { #package : 'Microdown-ReferenceChecker' } +{ #category : 'visiting' } +MicReferenceChecker >> checkDirectory: aDir [ + "Take the directory, parse all its children with microdown file parser and let the visitor visit each time then return visitor is ok which should be true if every thing is okay, the visitor turned out to treat the many documents that it visits as one, so if anchor is duplicated in another file it will detect that . " + + | parsedFile | + aDir allFiles do: [ :each | + (parsedFile := Microdown parseFile: each) accept: self. + ]. + ^ self isOk + +] + +{ #category : 'visiting' } +MicReferenceChecker >> checkFile: aFile [ + "Will parse the given file and invite the visitor and return visitor isOk value" + + | parsedFile | + parsedFile := Microdown parseFile: aFile. + parsedFile accept: self. + ^ self isOk + + + +] + { #category : 'reporting' } MicReferenceChecker >> duplicatedAnchors [ @@ -25,15 +50,22 @@ MicReferenceChecker >> duplicatedAnchors [ { #category : 'visiting' } MicReferenceChecker >> handleAnchorOf: anElement [ - anElement hasAnchor ifTrue: [ - (anchors includes: anElement anchor) - ifTrue: [ duplicatedAnchors add: anElement anchor ] - ifFalse: [ anchors add: anElement anchor ] ] - - "Pay attention if we want to report all the occurrences of the - anchor definition we would have to count the one already included - in anchors - Imagine that you have File1/anc1 File2/anc1 and File3/anc1 - the reporting should be anc1 is File1, File2 and File3" + anElement hasAnchor ifFalse: [ ^ self ]. + (self hasAlreadyDefinedAs: anElement) + ifTrue: [ duplicatedAnchors add: anElement ]. + anchors add: anElement + +] + +{ #category : 'visiting' } +MicReferenceChecker >> hasAlreadyDefinedAs: anAnchor [ + + | alreadyDefined | + alreadyDefined := false. + anchors do: + [ :each | each anchorLabel = anAnchor anchorLabel + ifTrue: [ alreadyDefined := true ] ]. + ^ alreadyDefined ] { #category : 'initialization' } @@ -49,36 +81,35 @@ MicReferenceChecker >> initialize [ MicReferenceChecker >> isOk [ ^ duplicatedAnchors isEmpty and: [ - references allSatisfy: [ :each | anchors includes: each ] ] + references allSatisfy: [ :each | self hasAlreadyDefinedAs: each ] ] ] { #category : 'reporting' } MicReferenceChecker >> unknownAnchors [ - | unk | - unk := references copy. - anchors do: [ :each | - unk remove: each ifAbsent: [ ] ]. - ^ unk + | unknown ref | + unknown := OrderedCollection new. + ref := references copy. + ref do: [ :ref | + (anchors noneSatisfy: [ :each | + ref anchorLabel = each anchorLabel ]) + ifTrue: [ unknown add: ref ] ]. + ^ unknown ] { #category : 'visiting' } MicReferenceChecker >> visitAnchor: anAnchor [ - (anchors includes: anAnchor label) - ifTrue: [ - duplicatedAnchors add: anAnchor label - "Pay attention if we want to report all the occurrences of the - anchor definition we would have to count the one already included - in anchors - Imagine that you have File1/anc1 File2/anc1 and File3/anc1 - the reporting should be anc1 is File1, File2 and File3" ] - ifFalse: [ anchors add: anAnchor label ] + | isAlready | + isAlready := self hasAlreadyDefinedAs: anAnchor. + isAlready ifTrue: [ duplicatedAnchors add: anAnchor ]. + anchors add: anAnchor ] { #category : 'visiting' } MicReferenceChecker >> visitAnchorReference: anAnchorReference [ - references add: anAnchorReference bodyString + references add: anAnchorReference ] { #category : 'visiting' } diff --git a/src/Microdown-ReferenceChecker/MicReferenceCheckerTest.class.st b/src/Microdown-ReferenceChecker/MicReferenceCheckerTest.class.st index 4fe4d320..3daef1f4 100644 --- a/src/Microdown-ReferenceChecker/MicReferenceCheckerTest.class.st +++ b/src/Microdown-ReferenceChecker/MicReferenceCheckerTest.class.st @@ -17,7 +17,102 @@ See *@anchorSection1@* '. visitor := MicReferenceChecker new. doc accept: visitor. - self assert: visitor isOk. + self assert: visitor isOk +] + +{ #category : 'tests' } +MicReferenceCheckerTest >> testAllReferencesAreCorrectInFile [ + + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 +@anchorSection1 + +See *@anchorSection1@* + +' ] . + + + + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file ). + file ensureDelete +] + +{ #category : 'tests' } +MicReferenceCheckerTest >> testAllReferencesAreCorrectinDir [ + + | dir file1 file2 visitor | + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory. + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +# Section 1 +@anchorSection1 + +' ] . + + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: ' See *@anchorSection1@* and *@anchorSection1@*'] . + file2 ensureCreateFile. + + visitor := MicReferenceChecker new. + self assert: (visitor checkDirectory: dir). + +] + +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testDuplicatedAnchorDir [ + + | dir file1 file2 visitor | + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory. + + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +# Section 1 +@anchorSection1 + + +' ] . + file1 ensureCreateFile . + + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection3 + +# Section 4 +@anchorSection1 + + +'] . + file2 ensureCreateFile . + + visitor := MicReferenceChecker new. + + self deny: ( visitor checkDirectory: dir ) + +] + +{ #category : 'tests' } +MicReferenceCheckerTest >> testFile [ + + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 +![alittle caption.](figures/f.png anchor=anchorSection1) + +See *@anchorSection0@* + +' ]. + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file) ] { #category : 'tests' } @@ -35,6 +130,22 @@ See *@anchorSection1@* self assert: visitor isOk ] +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAFigureInFile [ + + | file visitor | + file := (FileSystem memory / 'myFile2.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 +![alittle caption.](figures/f.png anchor=anchorSection1) + +See *@anchorSection1@* + +' ]. + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file). +] + { #category : 'tests' } MicReferenceCheckerTest >> testReferToAMathEquation [ @@ -52,6 +163,31 @@ See *@anchorSection1@* self assert: visitor isOk ] +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAMathEquationInFile [ + + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. + + + + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 + +$$ %anchor=anchorSection1 +balbalbalb! +$$ +See *@anchorSection1@* + +' ] . + + + + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file). + file ensureDelete +] + { #category : 'tests' } MicReferenceCheckerTest >> testReferToAnUknownAnchor [ @@ -66,6 +202,27 @@ See *@anchorSection1@* self deny: visitor isOk ] +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAnUknownAnchorInFile [ + + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. + + + + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 + +See *@anchorSection1@* + +' ] . + + + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file) . + file ensureDelete +] + { #category : 'tests - duplicated anchors' } MicReferenceCheckerTest >> testReportingDuplicatedAnchors [ @@ -88,7 +245,41 @@ See *@anchorSection1@* and *@anchorSection0@* visitor := MicReferenceChecker new. doc accept: visitor. self deny: visitor isOk. - self assert: visitor duplicatedAnchors equals: OrderedCollection <- #('anchorSection1' 'anchorSection1') + self assert: (visitor duplicatedAnchors collect: [:each | each anchorLabel ]) equals: OrderedCollection <- #('anchorSection1' 'anchorSection1') +] + +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedAnchorsInFile [ + + | file visitor | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + + + + file writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +# Section 1 +@anchorSection1 + +# Section 2 +@anchorSection1 + +# Section 3 +@anchorSection1 + +See *@anchorSection1@* and *@anchorSection0@* + +'] . + + + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file). + self + assert: (visitor duplicatedAnchors collect: [:each | each anchorLabel]) + equals: OrderedCollection <- #('anchorSection1' 'anchorSection1'). + file ensureDelete ] { #category : 'tests - duplicated anchors' } @@ -111,10 +302,36 @@ See *@anchorSection1@* and *@anchorSection3@* doc accept: visitor. self deny: visitor isOk. self - assert: visitor duplicatedAnchors + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) equals: OrderedCollection <- #( 'anchorSection1' ) ] +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedFiguresInFile [ + + | file visitor | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +![a caption 1](figures/f.png anchor=anchorSection1) + +![a caption 2](figures/f.png anchor=anchorSection1) + +![a caption 3](figures/f.png anchor=anchorSection2) + +See *@anchorSection1@* and *@anchorSection3@* + +']. + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file). + self + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'anchorSection1' ). + file ensureDelete +] + { #category : 'tests - duplicated anchors' } MicReferenceCheckerTest >> testReportingDuplicatedMaths [ @@ -145,10 +362,47 @@ See *@anchorSection1@* and *@anchorSection3@* doc accept: visitor. self deny: visitor isOk. self - assert: visitor duplicatedAnchors + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) equals: OrderedCollection <- #( 'anchorSection1' 'anchorSection1' ) ] +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedMathsInFile [ + + | file visitor | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +$$ %anchor=anchorSection1 +balbalbalb! +$$ + +$$ %anchor=anchorSection1 +balbalbalb! +$$ + +$$ %anchor=anchorSection1 +balbalbalb! +$$ + +$$ %anchor=anchorSection3 +balbalbalb! +$$ + +See *@anchorSection1@* and *@anchorSection3@* + +'] . + + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file). + self + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'anchorSection1' 'anchorSection1' ). + file ensureDelete +] + { #category : 'tests' } MicReferenceCheckerTest >> testReportingUnknownAnchor [ @@ -165,5 +419,39 @@ See *@anchorSection1@* and *@anchorSection2@* visitor := MicReferenceChecker new. doc accept: visitor. self deny: visitor isOk. - self assert: visitor unknownAnchors equals: (OrderedCollection <- #('anchorSection2')) + self + assert: (visitor unknownAnchors collect: [ :each | each anchorLabel ]) + equals: (OrderedCollection <- #('anchorSection2')) +] + +{ #category : 'tests' } +MicReferenceCheckerTest >> testReportingUnknownAnchorDir [ + + | dir file1 file2 visitor | + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory . + + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 + +# Section 1 +@anchorSection1 + + +' ] . + file1 ensureCreateFile . + + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: ' See *@anchorSection1@* and *@anchorSection2@*'] . + file2 ensureCreateFile . + + visitor := MicReferenceChecker new. + + self deny: ( visitor checkDirectory: dir ) . + + file1 ensureDelete. + file2 ensureDelete. + dir ensureDelete + ] diff --git a/src/Microdown/MicAnchorBlock.class.st b/src/Microdown/MicAnchorBlock.class.st index 4fe06945..5d460f32 100644 --- a/src/Microdown/MicAnchorBlock.class.st +++ b/src/Microdown/MicAnchorBlock.class.st @@ -32,6 +32,12 @@ MicAnchorBlock >> addLineAndReturnNextNode: line [ ^ self ] +{ #category : 'accessing' } +MicAnchorBlock >> anchorLabel [ + "This should only be invoked when checking references" + ^ self label +] + { #category : 'testing' } MicAnchorBlock >> hasLabel [ label ifNil: [ ^ false ]. @@ -49,6 +55,14 @@ MicAnchorBlock >> label: aLabel [ ] +{ #category : 'printing' } +MicAnchorBlock >> printOn: aStream [ + + super printOn: aStream. + self hasLabel + ifTrue: [ aStream nextPutAll: ' (', self label, ')' ] +] + { #category : 'accessing' } MicAnchorBlock >> target [ ^ target diff --git a/src/Microdown/MicAnchorReferenceBlock.class.st b/src/Microdown/MicAnchorReferenceBlock.class.st index 2b100a0e..f1f7505f 100644 --- a/src/Microdown/MicAnchorReferenceBlock.class.st +++ b/src/Microdown/MicAnchorReferenceBlock.class.st @@ -34,6 +34,12 @@ MicAnchorReferenceBlock >> accept: aVisitor [ ^ aVisitor visitAnchorReference: self ] +{ #category : 'accessing' } +MicAnchorReferenceBlock >> anchorLabel [ + + ^ bodyString +] + { #category : 'accessing' } MicAnchorReferenceBlock >> reference [ diff --git a/src/Microdown/MicInlineBlockWithUrl.class.st b/src/Microdown/MicInlineBlockWithUrl.class.st index 1c105c1c..92787615 100644 --- a/src/Microdown/MicInlineBlockWithUrl.class.st +++ b/src/Microdown/MicInlineBlockWithUrl.class.st @@ -39,6 +39,12 @@ MicInlineBlockWithUrl >> anchor: aString [ arguments removeKey: #label ifAbsent: []. ] +{ #category : 'handle' } +MicInlineBlockWithUrl >> anchorLabel [ + "This should only be invoked when checking references" + ^ self anchor +] + { #category : 'accessing' } MicInlineBlockWithUrl >> argumentAt: aKey ifPresent: aBlock [ "Lookup the given key in the receiver. If it is present, answer the value of evaluating the given block with the value associated with the key. Otherwise, answer self." diff --git a/src/Microdown/MicMathBlock.class.st b/src/Microdown/MicMathBlock.class.st index bd0d2d28..50fc57c3 100644 --- a/src/Microdown/MicMathBlock.class.st +++ b/src/Microdown/MicMathBlock.class.st @@ -62,6 +62,12 @@ MicMathBlock >> anchor: aString [ arguments removeKey: #label ifAbsent: []. ] +{ #category : 'handle' } +MicMathBlock >> anchorLabel [ + "This should only be invoked when checking references" + ^ self anchor +] + { #category : 'handle' } MicMathBlock >> extractFirstLineFrom: aLine [ diff --git a/src/Microdown/Microdown.class.st b/src/Microdown/Microdown.class.st index 3bcc9cc6..36a91619 100644 --- a/src/Microdown/Microdown.class.st +++ b/src/Microdown/Microdown.class.st @@ -156,7 +156,7 @@ Microdown >> parse: aStreamOrString [ Microdown >> parseFile: aFile [ |root| - root := MicrodownParser parse: (aFile contents). + root := MicrodownParser parse: aFile contents. root fromFile: aFile fullName. ^ root