diff --git a/README.md b/README.md index 6657c311..ad78d60f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Microdown Parser and Elements + + Pharo + + I'm a parser and object model for Microdown originally implemented by S. Ducasse, L. Dargaud and G. Polito. The implementation is based on the work on markdown of K. Osterbye. Further developments by S. Ducasse and K. Osterbye. diff --git a/src/BaselineOfMicrodown/BaselineOfMicrodown.class.st b/src/BaselineOfMicrodown/BaselineOfMicrodown.class.st index 6889f573..b088eedc 100644 --- a/src/BaselineOfMicrodown/BaselineOfMicrodown.class.st +++ b/src/BaselineOfMicrodown/BaselineOfMicrodown.class.st @@ -64,6 +64,9 @@ BaselineOfMicrodown >> baseline: spec [ package: #'Microdown-ReferenceChecker' with: [ spec requires: #( #'Microdown' ) ]; + package: #'Microdown-ParentChildrenChecker' with: [ + spec requires: #( #'Microdown' ) ]; + package: #'Microdown-Blog' with: [ spec requires: #( #'Microdown' ) ]; package: #'Microdown-Blog-Tests' with: [ @@ -80,13 +83,13 @@ BaselineOfMicrodown >> baseline: spec [ group: 'Tests' with: #( 'Core' 'Microdown-Tests' ); group: 'RichText' with: #( 'Core' 'Microdown-RichTextComposer' ); group: 'Extensions' - with: #( #'Microdown-Evaluator' #'Microdown-Evaluator-Tests' #'Microdown-ReferenceChecker' + with: #( #'Microdown-Evaluator' #'Microdown-Evaluator-Tests' #'Microdown-ReferenceChecker' #'Microdown-PrettyPrinter' #'Microdown-PrettyPrinter-Tests' #'Microdown-HTMLExporter' #'Microdown-HTMLExporter-Tests' #'Microdown-LaTeXExporter' #'Microdown-LaTeXExporter-Tests' #'Microdown-Transformer' #'Microdown-Transformer-Tests' ); group: 'All' - with: #( 'Core' 'Tests' 'Extensions' 'Microdown-Pharo-Tools' + with: #( 'Core' 'Tests' 'Extensions' 'Microdown-Pharo-Tools' 'RichText' ) " #'Microdown-RichTextPresenter' diff --git a/src/Microdown-BeamerExporter-Tests/MicMicrodownToSlideTest.class.st b/src/Microdown-BeamerExporter-Tests/MicMicrodownToSlideTest.class.st index 627bd059..a1987466 100644 --- a/src/Microdown-BeamerExporter-Tests/MicMicrodownToSlideTest.class.st +++ b/src/Microdown-BeamerExporter-Tests/MicMicrodownToSlideTest.class.st @@ -39,3 +39,26 @@ MicMicrodownToSlideTest >> testSlideGotCorrectParent [ self assert: doc children size equals: 2 ] + +{ #category : 'tests' } +MicMicrodownToSlideTest >> testSlideMetaData [ + + | doc | + doc := MicrodownParser new parse: ' +{ + "title":"Essence of Dispatch", + "subtitle":"Taking Pharo Booleans as example", + "slidesid" : "M1S1" +} + +# Slide title + +- item 1 +- item 2 +'. + self assert: doc children size equals: 3. + MicMicrodownToSlideVisitor new visit: doc. + self assert: doc children first class equals: MicMetaDataBlock. + self assert: doc children second class equals: MicSlideBlock + +] diff --git a/src/Microdown-ParentChildrenChecker/MicParentChildrenChecker.class.st b/src/Microdown-ParentChildrenChecker/MicParentChildrenChecker.class.st index fd79c21c..14f28565 100644 --- a/src/Microdown-ParentChildrenChecker/MicParentChildrenChecker.class.st +++ b/src/Microdown-ParentChildrenChecker/MicParentChildrenChecker.class.st @@ -9,46 +9,62 @@ Class { #package : 'Microdown-ParentChildrenChecker' } -{ #category : 'visiting main API' } -MicParentChildrenChecker >> check: anElement [ - "Check if the parent of the element correctly includes this element as a child" - - anElement parent - ifNil: [ - anElement class = MicRootBlock - ifFalse: [ orphanList add: anElement ] - ifTrue: [ anElement children do: [ :each | self check: each ] ] ] - ifNotNil: [ :p | "We cannot identify bad parent that are refered by child not in the children - list, because by construction the algo only considers the children of an element). - (p children includes: anElement) ifFalse: [ self addParent: p ]." - p children do: [ :child | - child parent = p ifFalse: [ confusedKids add: child ] ]. - - anElement children do: [ :each | self check: each ] ] +{ #category : 'accessing' } +MicParentChildrenChecker >> check: anElement [ + "Check if the parent of the element correctly includes this element as a child and if the element's parent pointer is correct." + + anElement parent + ifNil: [ + "Check if the root element is indeed supposed to have no parent" + (anElement class = MicRootBlock) + ifFalse: [ + "If it's not a root, it should have a parent" + orphanList add: anElement + ]. + "Recursively check all children of the current element" + anElement children do: [ :each | self check: each ] + ] + ifNotNil: [ :p | + "Check if the parent’s list of children actually includes this element" + (p children includes: anElement) + ifFalse: [ + confusedKids add: anElement + ]. + + "Recursively check all children of the current element" + anElement children do: [ :child | + self check: child + ] + ]. ] { #category : 'accessing' } -MicParentChildrenChecker >> confusedKids [ +MicParentChildrenChecker >> confusedKids [ ^ confusedKids + + ] -{ #category : 'visiting main API' } -MicParentChildrenChecker >> initialize [ +{ #category : 'accessing' } +MicParentChildrenChecker >> initialize [ super initialize. orphanList := OrderedCollection new. confusedKids := OrderedCollection new + + ] -{ #category : 'testing' } -MicParentChildrenChecker >> isOk [ +{ #category : 'accessing' } +MicParentChildrenChecker >> isOk [ + + ^ (orphanList isEmpty and: [ confusedKids isEmpty ]) - ^ confusedKids isEmpty and: - (orphanList isEmpty and: [ confusedKids isEmpty ]) ] { #category : 'accessing' } -MicParentChildrenChecker >> orphanList [ +MicParentChildrenChecker >> orphanList [ ^orphanList + ] diff --git a/src/Microdown-ParentChildrenChecker/MicParentChildrenCheckerTest.class.st b/src/Microdown-ParentChildrenChecker/MicParentChildrenCheckerTest.class.st index 09758be0..a63963c0 100644 --- a/src/Microdown-ParentChildrenChecker/MicParentChildrenCheckerTest.class.st +++ b/src/Microdown-ParentChildrenChecker/MicParentChildrenCheckerTest.class.st @@ -5,51 +5,203 @@ Class { #package : 'Microdown-ParentChildrenChecker' } -{ #category : 'tests' } +{ #category : 'accessing' } MicParentChildrenCheckerTest >> document [ ^ Microdown parse: '#Microdown is quite cool - Here is some code - ```language=Pharo&caption=Beautiful&anchor=Fig1 1000 factorial / 999 factorial ``` - Here is a figure and a link: [http://pharo.org](http://pharo.org). - ![Pharologo](https://files.pharo.org/media/logo/logo.png size=80&anchor=figLogo.) - - - Here is a list: - item 1 1. sub item 1 3. sub item 2 - item 2 - - **Bold**, _italic_, `monospace` - In Pharo, Microdown supports hyperlinks to: - classes e.g., `Point`, - methodes e.g., `Point class`, `Point>>#setX:setY:`, and - packages e.g., `#''Microdown-Tests''` (for packages). - You can edit this file clicking on `ClySyntaxHelpMorph>>#rawMicrodownSyntax`.'. + ] -{ #category : 'tests' } -MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormed [ +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testComplexDocumentWithConfusedKidsAndOrphans [ + | checker document child1 child2 wrongChild orphan | + + checker := MicParentChildrenChecker new. + + "Parse a complex Microdown document" + document := Microdown parse: '# Microdown Documentation Example + +Welcome to the **Microdown Documentation**. This document serves as a tutorial and reference for creating rich markdown documents with various formatting options in Microdown. + +## Introduction + +Microdown allows you to write documentation directly in your Pharo environment, supporting standard markdown syntax and Pharo-specific enhancements. + +### What is Markdown? + +Markdown is a lightweight markup language with plain-text formatting syntax. It is designed so that it can be converted to HTML and many other formats. + +## Text Formatting + +Text in Microdown can be formatted in several ways: + +- **Bold**: `**Bold text**` renders as **Bold text** +- _Italic_: `*Italic text*` renders as _Italic text_ +- **_Combined Emphasis_**: `**_Bold and italic_**` renders as **_Bold and italic_** + +## Lists + +Microdown supports both ordered and unordered lists. + +### Unordered List + +- Item 1 +- Item 2 + - Subitem 2.1 + - Subitem 2.2 + +### Ordered List + +1. First item +2. Second item + 1. Subitem 2.1 + 2. Subitem 2.2 + +## Links and Images + +### Linking + +This is how you create a [link](http://example.com). + +### Images + +Here''s how you embed an image: + +![Alt text for the image](http://example.com/image.png) + +## Code Blocks + +You can add code blocks for code snippets, which are especially useful for programming documentation: + +```smalltalk +Transcript show: ''Hello, world!'. + + "Create confused kids: Misassign a sub-subheading to a different subheading" + child1 := document children second children second. "Assuming this is Sub-Subheading 1.1" + child2 := document children third. "Assuming this is Subheading 2" + wrongChild := child1 children first. "Content of Sub-Subheading 1.1" + wrongChild basicParent: child2. "Incorrectly setting it to Subheading 2" + + "Create an orphan: Detach a sub-subheading without assigning a new parent" + orphan := document children third children last. "Assuming this is Sub-Subheading 2.2" + orphan basicParent: nil. + + "Verify incorrect setups before checking" + self assert: (wrongChild parent = child2). + self assert: orphan parent isNil. + + "Run the checker on the modified document" + checker check: document. + + "Assert that the checker identifies the document as not OK" + self deny: checker isOk + + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testDocumentWithConfusedKids [ + | checker document child1 child2 | + checker := MicParentChildrenChecker new. + document := Microdown parse: '# Microdown is quite cool + ## Subheading 1 + Microdown enables rich text formatting. + ## Subheading 2 + It’s also quite easy to use.'. + + "Manually access children and reassign to create a 'confused kid'" + child1 := document children first. + child2 := document children second children first . + child2 basicParent: child1 . + + checker check: document. + + "Assert that the checker identifies the document as not OK due to confused relationships" + self deny: checker isOk + + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormed [ | checker | checker := MicParentChildrenChecker new. checker check: self document. self assert: checker isOk + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormedAnnotation [ + + | checker | + checker := MicParentChildrenChecker new. + checker check: (Microdown parse: '{!citation|name=Duca99a!}'). + self assert: checker isOk + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormedFormat [ + + | checker | + checker := MicParentChildrenChecker new. + checker check: (Microdown parse: '**Bold**'). + self assert: checker isOk + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormedInlineCode [ + + | checker | + checker := MicParentChildrenChecker new. + checker check: (Microdown parse: '`hello`'). + self assert: checker isOk + ] -{ #category : 'tests' } -MicParentChildrenCheckerTest >> testSimpleDocumentWithOrphans [ +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormedInput [ + + | checker | + checker := MicParentChildrenChecker new. + checker check: (Microdown parse: ''). + self assert: checker isOk + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentIsWellFormedLink [ + + | checker | + checker := MicParentChildrenChecker new. + checker check: (Microdown parse: 'a link: [http://pharo.org](http://pharo.org)'). + self assert: checker isOk. + self assert: checker orphanList isEmpty + +] + +{ #category : 'accessing' } +MicParentChildrenCheckerTest >> testSimpleDocumentWithOrphans [ | brokenDocument visitor orphan | visitor := MicParentChildrenChecker new. @@ -61,4 +213,5 @@ MicParentChildrenCheckerTest >> testSimpleDocumentWithOrphans [ visitor check: brokenDocument. self deny: visitor isOk + ] 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-Tests/MicrodownTest.class.st b/src/Microdown-Tests/MicrodownTest.class.st index 874db2d9..9effffbe 100644 --- a/src/Microdown-Tests/MicrodownTest.class.st +++ b/src/Microdown-Tests/MicrodownTest.class.st @@ -4,11 +4,29 @@ A MicrodownTest is a test class for testing the behavior of Microdown Class { #name : 'MicrodownTest', #superclass : 'TestCase', + #instVars : [ + 'file', + 'filesystem' + ], #category : 'Microdown-Tests-Core', #package : 'Microdown-Tests', #tag : 'Core' } +{ #category : 'running' } +MicrodownTest >> setUp [ + super setUp. + filesystem := FileSystem memory. + file := filesystem workingDirectory / 'anExample1.md'. + file writeStreamDo: [ :stream | stream nextPutAll: '# Foo +Pharo is cool + +``` + this is a code snippet +``` +' ]. +] + { #category : 'tests - parent/children' } MicrodownTest >> testAddedAsChildWhenUsingParent [ @@ -45,6 +63,34 @@ MicrodownTest >> testFigureLabelAreNotReified [ ] +{ #category : 'tests' } +MicrodownTest >> testFromFileIfAbsent [ + + | root | + root := Microdown parse: (filesystem workingDirectory / 'anExample1.md') contents. + self deny: root hasFile +] + +{ #category : 'tests' } +MicrodownTest >> testFromFileInAnyElement [ + + | root | + root := Microdown parseFile: filesystem workingDirectory / 'anExample1.md'. + self assert: root children first children first fromFile equals: (filesystem workingDirectory / 'anExample1.md') fullName + + +] + +{ #category : 'tests' } +MicrodownTest >> testFromFileInRootDocument [ + + | root | + root := Microdown parseFile: filesystem workingDirectory / 'anExample1.md'. + self assert: root fromFile equals: (filesystem workingDirectory / 'anExample1.md') fullName + + +] + { #category : 'tests - parent/children' } MicrodownTest >> testMathCaptionIsReified [ "And as such their parent is the math block" 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/MicDocumentListBlock.class.st b/src/Microdown/MicDocumentListBlock.class.st deleted file mode 100644 index 6874482b..00000000 --- a/src/Microdown/MicDocumentListBlock.class.st +++ /dev/null @@ -1,50 +0,0 @@ -Class { - #name : 'MicDocumentListBlock', - #superclass : 'MicAnnotationBlock', - #category : 'Microdown-Extensions', - #package : 'Microdown', - #tag : 'Extensions' -} - -{ #category : 'accessing' } -MicDocumentListBlock class >> tag [ - ^ #documentlist -] - -{ #category : 'visiting' } -MicDocumentListBlock >> accept: aVisitor [ - - "^ aVisitor visitDocListAnnotation: self" -] - -{ #category : 'visiting' } -MicDocumentListBlock >> limit [ - - ^ self arguments at: #limit ifAbsent: [ '3' ] -] - -{ #category : 'visiting' } -MicDocumentListBlock >> path [ - - ^ self arguments at: #path -] - -{ #category : 'visiting' } -MicDocumentListBlock >> sort [ - - ^ self arguments at: #sort ifAbsent: [ '' ] -] - -{ #category : 'visiting' } -MicDocumentListBlock >> summaryMaxSize [ - - ^ self arguments at: #summaryMaxSize - ifPresent: [ :size | size asInteger ] - ifAbsent: [ 500 ] -] - -{ #category : 'visiting' } -MicDocumentListBlock >> templates [ - - ^ self arguments at: #templates ifAbsent: [ #() ] -] diff --git a/src/Microdown/MicElement.class.st b/src/Microdown/MicElement.class.st index 79ff0df2..5bd523c1 100644 --- a/src/Microdown/MicElement.class.st +++ b/src/Microdown/MicElement.class.st @@ -47,6 +47,12 @@ MicElement >> basicParent: aMicBlock [ parent := aMicBlock ] +{ #category : 'accessing' } +MicElement >> fromFile [ + "Let us bubble up to the root to get the file" + ^ self parent fromFile +] + { #category : 'properties' } MicElement >> hasProperty: aKey [ "Answer whether there is a property named aKey." diff --git a/src/Microdown/MicEvaluatedBlock.class.st b/src/Microdown/MicEvaluatedBlock.class.st index c0383fea..c077c64f 100644 --- a/src/Microdown/MicEvaluatedBlock.class.st +++ b/src/Microdown/MicEvaluatedBlock.class.st @@ -34,6 +34,13 @@ MicEvaluatedBlock >> children: anObject [ children := anObject ] +{ #category : 'visiting' } +MicEvaluatedBlock >> closeMe [ + + super closeMe. + self children do: [ :each | each basicParent: self ]. +] + { #category : 'printing' } MicEvaluatedBlock >> plainText [ ^ ( self children collect: [:each | each plainText ] ) joinUsing: ' ' diff --git a/src/Microdown/MicInlineBlockWithUrl.class.st b/src/Microdown/MicInlineBlockWithUrl.class.st index 133eea8b..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." @@ -110,7 +116,7 @@ MicInlineBlockWithUrl >> closeMe [ "the link url - ![alt text](url) - url is allowed to have title in quotes (url ""title"") " - | split title| + | split title | split := url splitOnFirst: Character space. self reference: (MicResourceReference fromUri: split first). title := (split second ifNil: [ '' ]) diff --git a/src/Microdown/MicLinkBlock.class.st b/src/Microdown/MicLinkBlock.class.st index f24fd8ef..3de7d3a9 100644 --- a/src/Microdown/MicLinkBlock.class.st +++ b/src/Microdown/MicLinkBlock.class.st @@ -28,6 +28,13 @@ MicLinkBlock >> accept: aVisitor [ ^ aVisitor visitLink: self ] +{ #category : 'visiting' } +MicLinkBlock >> closeMe [ + + super closeMe. + self children do: [ :each | each basicParent: self ]. +] + { #category : 'printing' } MicLinkBlock >> plainText [ ^ '[', ((self children collect: [:each | each plainText]) joinUsing: ' '), '](', url ,')' 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/MicMonospaceFormatBlock.class.st b/src/Microdown/MicMonospaceFormatBlock.class.st index 91eeeffd..1ebd39c4 100644 --- a/src/Microdown/MicMonospaceFormatBlock.class.st +++ b/src/Microdown/MicMonospaceFormatBlock.class.st @@ -4,6 +4,9 @@ I represent a monospaced text section. I'm delimited using `\`` as in `\`monospa Class { #name : 'MicMonospaceFormatBlock', #superclass : 'MicUnEvaluatedBlock', + #instVars : [ + 'children' + ], #category : 'Microdown-Model', #package : 'Microdown', #tag : 'Model' @@ -33,14 +36,29 @@ MicMonospaceFormatBlock >> accept: aVisitor [ { #category : 'accessing' } MicMonospaceFormatBlock >> children [ - "should be deprecated" - ^ Array with: (MicTextBlock new bodyString: self bodyString) + ^ children +] + +{ #category : 'accessing' } +MicMonospaceFormatBlock >> children: aCollection [ + + children := aCollection +] + +{ #category : 'accessing' } +MicMonospaceFormatBlock >> closeMe [ + "Please note that in the children we will only store text. So this could be all removed + but at the cost of updating all the visitor that expect that the treatment on MicTextBlock will be done. + so for now to make sure that the tree is well formed we just store a text bloc and set correctly its parent. Just returning the text via children was not super nice and recipe to disaster since different nodes where returned at each invocation. " + + super closeMe. + self children: (Array with: (MicTextBlock new bodyString: self bodyString)). + self children do: [ :each | each basicParent: self ]. ] { #category : 'accessing' } MicMonospaceFormatBlock >> text [ - "should be deprecated " ^ self bodyString ] diff --git a/src/Microdown/MicRootBlock.class.st b/src/Microdown/MicRootBlock.class.st index d8a8db7b..bdf4fcd9 100644 --- a/src/Microdown/MicRootBlock.class.st +++ b/src/Microdown/MicRootBlock.class.st @@ -32,6 +32,23 @@ MicRootBlock >> canConsumeLine: line [ ^ true ] +{ #category : 'accessing' } +MicRootBlock >> fromFile [ + ^ self propertyAt: #file +] + +{ #category : 'accessing' } +MicRootBlock >> fromFile: aFile [ + "Store the file from which the document was built" + + self propertyAt: #file put: aFile +] + +{ #category : 'testing' } +MicRootBlock >> hasFile [ + ^ self hasProperty: #file +] + { #category : 'accessing' } MicRootBlock >> indent [ ^0 diff --git a/src/Microdown/Microdown.class.st b/src/Microdown/Microdown.class.st index 1125bd3a..36a91619 100644 --- a/src/Microdown/Microdown.class.st +++ b/src/Microdown/Microdown.class.st @@ -117,6 +117,12 @@ Microdown class >> parse: aStreamOrString [ ^ self new parse: aStreamOrString ] +{ #category : 'facade' } +Microdown class >> parseFile: aFile [ + + ^ self new parseFile: aFile +] + { #category : 'facade' } Microdown class >> resolveDocument: document withBase: base [ "resolve all relative urls in document with respect to the absolute uri @@ -146,6 +152,16 @@ Microdown >> parse: aStreamOrString [ ^ MicrodownParser parse: aStreamOrString ] +{ #category : 'facade' } +Microdown >> parseFile: aFile [ + + |root| + root := MicrodownParser parse: aFile contents. + root fromFile: aFile fullName. + ^ root + +] + { #category : 'facade' } Microdown >> render: aStringOrDoc [ "Facade method to render a microdown string to Text" diff --git a/src/microdown-fileParser/FileParser.class.st b/src/microdown-fileParser/FileParser.class.st new file mode 100644 index 00000000..db3efccb --- /dev/null +++ b/src/microdown-fileParser/FileParser.class.st @@ -0,0 +1,30 @@ +Class { + #name : 'FileParser', + #superclass : 'TestCase', + #category : 'microdown-fileParser', + #package : 'microdown-fileParser' +} + +{ #category : 'as yet unclassified' } +FileParser >> testCreatingFileAndParsingtheContent [ + + | file root | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + + + + file writeStreamDo: [ :stream | stream nextPutAll: ' + ### title + @first + + just talking about somthing @*first*@ ' ] . + + root := Microdown parseFile: file contents . + + self assert: (root isKindOf: MicRootBlock ) . + self assert: root fromFile equals: file contents . + file ensureDelete . + + +] diff --git a/src/microdown-fileParser/package.st b/src/microdown-fileParser/package.st new file mode 100644 index 00000000..0a177453 --- /dev/null +++ b/src/microdown-fileParser/package.st @@ -0,0 +1 @@ +Package { #name : 'microdown-fileParser' }