From 0e15492849555754cfec7e822983563026bb0364 Mon Sep 17 00:00:00 2001 From: Fanirindrainy Date: Tue, 17 Mar 2026 01:08:08 +0300 Subject: [PATCH 1/4] first commit after cloning --- .../MicHTMLExporterTest.class.st | 56 +- .../MicAgendaBlockTest.class.st | 49 - .../MicAgendaGeneratorTest.class.st | 456 -------- .../MicDayBlockTest.class.st | 49 - .../MicSegmentBlockTest.class.st | 49 - .../MicReferenceCheckerTest.class.st | 1007 +++++------------ .../MicReferenceChecker.class.st | 240 +--- .../MicHTMLExporterTest.class.st | 362 +----- .../MicHTMLBrush.class.st | 26 - .../MicHTMLCanvas.class.st | 46 +- .../MicHTMLTag.class.st | 102 +- .../MicHTMLVisitor.class.st | 19 +- .../MicQuoteBlockTest.extension.st | 17 - ...ension.st => MicrodownParser.extension.st} | 4 +- .../ManifestMicrodownRules.class.st | 24 + .../MicEnglishTypoChecker.class.st | 10 +- .../MicEnglishTypoCheckerTest.class.st | 24 +- .../MicRuleHeaderChecker.class.st | 5 +- .../MicRuleUppercaseHeaderStrategy.class.st | 14 +- .../MicSpaceBeforeDiacriticsResult.class.st | 4 +- .../MicUseofWrongVocabularyResult.class.st | 2 +- .../MicVocabularyChecker.class.st | 28 +- .../MicVocabularyCheckerTest.class.st | 33 +- .../MicSlideConverter.class.st | 24 - .../MicAnchorLinkerTest.class.st | 6 +- src/Microdown-Tests/MicElementTest.class.st | 2 +- .../MicHTMLTagParagraphBlockTest.class.st | 176 --- .../MicParagraphBlockTest.class.st | 2 +- src/Microdown-Tests/MicParserTest.class.st | 2 +- .../MicRawParagraphBlockTest.class.st | 198 +++- .../MicrodownParserTest.class.st | 2 +- src/Microdown-Tests/MicrodownTest.class.st | 4 +- ...downObjectToPillarObjectConverter.class.st | 33 +- src/Microdown/MicAbstractBlock.class.st | 2 +- .../MicHTMLTagParagraphBlock.class.st | 118 -- src/Microdown/MicParser.class.st | 378 ------- src/Microdown/MicRawBlock.class.st | 1 - src/Microdown/MicRawParagraphBlock.class.st | 62 +- src/Microdown/MicSharedPool.class.st | 5 - .../MicStartStopMarkupBlock.class.st | 3 +- src/Microdown/Microdown.class.st | 6 +- src/Microdown/MicrodownParser.class.st | 372 +++++- src/Microdown/MicrodownVisitor.class.st | 19 - 43 files changed, 1096 insertions(+), 2945 deletions(-) delete mode 100644 src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st delete mode 100644 src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st delete mode 100644 src/Microdown-Agenda-Tests/MicDayBlockTest.class.st delete mode 100644 src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st delete mode 100644 src/Microdown-HTMLExporter/MicHTMLBrush.class.st delete mode 100644 src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st rename src/Microdown-RichTextComposer/{MicParser.extension.st => MicrodownParser.extension.st} (53%) create mode 100644 src/Microdown-Rules/ManifestMicrodownRules.class.st delete mode 100644 src/Microdown-Slide-Utils/MicSlideConverter.class.st delete mode 100644 src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st delete mode 100644 src/Microdown/MicHTMLTagParagraphBlock.class.st delete mode 100644 src/Microdown/MicParser.class.st diff --git a/src/Microdown - HTML/MicHTMLExporterTest.class.st b/src/Microdown - HTML/MicHTMLExporterTest.class.st index 9002c508c..852578258 100644 --- a/src/Microdown - HTML/MicHTMLExporterTest.class.st +++ b/src/Microdown - HTML/MicHTMLExporterTest.class.st @@ -1,15 +1,28 @@ Class { #name : 'MicHTMLExporterTest', - #superclass : 'TestCase', + #superclass : 'ParametrizedTestCase', #instVars : [ 'parser', 'writer', - 'factory' + 'factory', + 'newLine' ], - #category : 'Microdown - HTML', - #package : 'Microdown - HTML' + #category : 'Microdown-HTMLExporter-Tests-HTML', + #package : 'Microdown-HTMLExporter-Tests', + #tag : 'HTML' } +{ #category : 'tests - list' } +MicHTMLExporterTest >> orderedListString [ + +^ ' +Here is a list: +1. item 1 +2. item 2 +3. item 3 +' +] + { #category : 'utils' } MicHTMLExporterTest >> parse: aString andCheckWeGet: aResultingString [ @@ -33,13 +46,32 @@ MicHTMLExporterTest >> testHeaderLevel1 [ ] -{ #category : 'tests' } +{ #category : 'tests - list' } +MicHTMLExporterTest >> testOrderedList [ + + self + parse: self orderedListString + andCheckWeGet: + +newLine , '

Here is a list:

' , newLine , newLine , '
    ' , newLine , '
  1. item 1
  2. ' , newLine , '
  3. item 2
  4. ' , newLine , '
  5. item 3
  6. ' , newLine , '
' +] + +{ #category : 'tests - list' } +MicHTMLExporterTest >> testOrderedListIsRecognized [ + + | doc | + doc := Microdown parse: self orderedListString. + self assert: doc children second class equals: MicOrderedListBlock +] + +{ #category : 'tests - list' } MicHTMLExporterTest >> testUnorderedList [ - | mic | - mic := parser parse: factory unorderedListWithTwoItemsSample. - self assert: (writer visit: mic) contents equals: ' -' + + self + parse: factory unorderedListWithTwoItemsSample + andCheckWeGet: newLine , +'' ] diff --git a/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st b/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st deleted file mode 100644 index e1ce31537..000000000 --- a/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st +++ /dev/null @@ -1,49 +0,0 @@ -Class { - #name : 'MicAgendaBlockTest', - #superclass : 'TestCase', - #instVars : [ - 'builder', - 'parser' - ], - #pools : [ - 'MicMicrodownSharedPool' - ], - #category : 'Microdown-Agenda-Tests', - #package : 'Microdown-Agenda-Tests' -} - -{ #category : 'running' } -MicAgendaBlockTest >> setUp [ - - super setUp. - builder := MicMicrodownTextualBuilder new. - parser := Microdown new. -] - -{ #category : 'tests' } -MicAgendaBlockTest >> testAgenda [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'agenda', String cr, - EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicAgendaBlock). - self assert: env environmentName equals: 'agenda' -] - -{ #category : 'tests' } -MicAgendaBlockTest >> testAgendaWithArgument [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'agenda|title=A cool agenda', String cr, EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicAgendaBlock). - self assert: env environmentName equals: 'agenda'. - self assert: env title equals: 'A cool agenda' -] diff --git a/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st b/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st deleted file mode 100644 index cdac03f63..000000000 --- a/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st +++ /dev/null @@ -1,456 +0,0 @@ -" -This class contains tests but all the test are not correct beacaus we need to have a file in a repertory with the templates because if we don't have them we can't generate the calendar. -" -Class { - #name : 'MicAgendaGeneratorTest', - #superclass : 'ParametrizedTestCase', - #instVars : [ - 'builder', - 'parser', - 'writer', - 'fileSystem', - 'newLine' - ], - #pools : [ - 'MicMicrodownSharedPool' - ], - #category : 'Microdown-Agenda-Tests', - #package : 'Microdown-Agenda-Tests' -} - -{ #category : 'building suites' } -MicAgendaGeneratorTest class >> testParameters [ - - ^ ParametrizedTestMatrix new - forSelector: #writer addOptions: { MicAgendaGenerator }; - forSelector: #fileSystem addOptions: { FileSystem }; - forSelector: #newLine addOptions: { String crlf . String cr . String lf }; - yourself -] - -{ #category : 'exemple' } -MicAgendaGeneratorTest >> agendaExempleMultiplesTalks [ - - ^ ' - - - -!> - -!> - - - - - -!> - -!> - -!>' - -] - -{ #category : 'exemple' } -MicAgendaGeneratorTest >> agendaExempleOneTalk [ - - ^ ' -!> - -!> - -!>' - -] - -{ #category : 'exemple' } -MicAgendaGeneratorTest >> agendaExempleWithRealInfo [ - - ^ ' - - - - - - - - - - - - - - - - - - - - - -!> - -!> - -!>' - -] - -{ #category : 'accessing' } -MicAgendaGeneratorTest >> fileSystem: aFileSystem [ - fileSystem := aFileSystem memory. -] - -{ #category : 'accessing' } -MicAgendaGeneratorTest >> newLine: aNewLine [ - (aNewLine = String cr) ifTrue:[ writer crAsNewLine ]. - (aNewLine = String lf) ifTrue:[ writer lfAsNewLine ]. - (aNewLine = String crlf) ifTrue:[ writer crlfAsNewLine ]. - newLine := aNewLine -] - -{ #category : 'running' } -MicAgendaGeneratorTest >> setUp [ - - super setUp. - builder := MicMicrodownTextualBuilder new. - parser := Microdown new. -] - -{ #category : 'tests' } -MicAgendaGeneratorTest >> testAgendaAstIsCorrect [ - - | fs file ast agenda day segment| - - fs := FileSystem memory. - file := fs root / 'agenda.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: self agendaExempleMultiplesTalks ]. - - ast := parser parse: file contents. - agenda := ast children first. - day := agenda children first. - segment := day children first. - self assert: agenda class equals: MicAgendaBlock. - self assert: day class equals: MicDayBlock. - self assert: segment class equals: MicSegmentBlock. - self assert: segment children size equals: 3. - -] - -{ #category : 'tests' } -MicAgendaGeneratorTest >> testConvertMicFileCorrectly [ - - "Look at the class comment for more information about this test" - - "| fs file ast| - fs := FileSystem memory. - file := fs root / 'agenda.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: self agendaExempleMultiplesTalks ]. - - ast := parser parse: file contents. - - writer visit: ast. - - self assert: writer contents equals: -'3 mars 2023', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 10h30 - 11h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Delporte Gaylord', newLine, -'
    ', newLine, -' ', newLine, -'
    First talk
    ', newLine, -' ', newLine, -'
    Building B
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 11h00 - 13h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -'
    Break
    ', newLine, -' ', newLine, -'
    Building A
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 13h00 - 14h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Not me', newLine, -'
    ', newLine, -' ', newLine, -'
    Second talk
    ', newLine, -' ', newLine, -'
    Building C
    ', newLine, -'
    ', newLine, -'
  • ', newLine, newLine" - -] - -{ #category : 'tests' } -MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithMultiplesTalks [ - - "Look at the class comment for more information about this test" - - "| fs file ast| - - fs := FileSystem memory. - file := fs root / 'agenda.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: self agendaExempleMultiplesTalks ]. - - ast := parser parse: file contents. - - writer visit: ast. - - self assert: writer contents equals: -'3 mars 2023', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 10h30 - 11h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Delporte Gaylord', newLine, -'
    ', newLine, -' ', newLine, -'
    First talk
    ', newLine, -' ', newLine, -'
    Building B
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 11h00 - 13h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -'
    Break
    ', newLine, -' ', newLine, -'
    Building A
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 13h00 - 14h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Not me', newLine, -'
    ', newLine, -' ', newLine, -'
    Second talk
    ', newLine, -' ', newLine, -'
    Building C
    ', newLine, -'
    ', newLine, -'
  • ', newLine, newLine" - -] - -{ #category : 'tests' } -MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithOneTalk [ - - "Look at the class comment for more information about this test" - - "| fs file ast| - - fs := FileSystem memory. - file := fs root / 'agenda.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: self agendaExempleOneTalk ]. - - ast := parser parse: file contents. - - writer visit: ast. - - self assert: writer contents equals: -'3 mars 2023', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 10h30 - 11h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Delporte Gaylord', newLine, -'
    ', newLine, -' ', newLine, -'
    First talk
    ', newLine, -' ', newLine, -'
    Building B
    ', newLine, -'
    ', newLine, -'
  • ', newLine, newLine" -] - -{ #category : 'tests' } -MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithRealInfo [ - - "Look at the class comment for more information about this test" - - "| fs file ast| - - fs := FileSystem memory. - file := fs root / 'agenda.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: self agendaExempleWithRealInfo ]. - - ast := parser parse: file contents. - - writer visit: ast. - - self assert: writer contents equals: -'3 mars 2023', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 10h30 - 11h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Delporte Gaylord', newLine, -'
    ', newLine, -' ', newLine, -'
    First talk
    ', newLine, -' ', newLine, -'
    Building B
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 11h00 - 13h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -'
    Break
    ', newLine, -' ', newLine, -'
    Building A
    ', newLine, -'
    ', newLine, -'
  • ', newLine, -' ', newLine, -'
  • ', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' 13h00 - 14h00', newLine, -'
    ', newLine, -' ', newLine, -'
    ', newLine, -' ', newLine, -' Not me', newLine, -'
    ', newLine, -' ', newLine, -'
    Second talk
    ', newLine, -' ', newLine, -'
    Building C
    ', newLine, -'
    ', newLine, -'
  • ', newLine, newLine" - -] - -{ #category : 'accessing' } -MicAgendaGeneratorTest >> writer: aWriter [ - writer := aWriter new -] diff --git a/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st b/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st deleted file mode 100644 index ffe3f6a4e..000000000 --- a/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st +++ /dev/null @@ -1,49 +0,0 @@ -Class { - #name : 'MicDayBlockTest', - #superclass : 'TestCase', - #instVars : [ - 'builder', - 'parser' - ], - #pools : [ - 'MicMicrodownSharedPool' - ], - #category : 'Microdown-Agenda-Tests', - #package : 'Microdown-Agenda-Tests' -} - -{ #category : 'running' } -MicDayBlockTest >> setUp [ - - super setUp. - builder := MicMicrodownTextualBuilder new. - parser := Microdown new. -] - -{ #category : 'tests' } -MicDayBlockTest >> testDay [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'day', String cr, - EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicDayBlock). - self assert: env environmentName equals: 'day' -] - -{ #category : 'tests' } -MicDayBlockTest >> testDayWithArgument [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'day|start=05/12/2022', String cr, EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicDayBlock). - self assert: env environmentName equals: 'day'. - self assert: env start equals: '05/12/2022' -] diff --git a/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st b/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st deleted file mode 100644 index 1c1ce6481..000000000 --- a/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st +++ /dev/null @@ -1,49 +0,0 @@ -Class { - #name : 'MicSegmentBlockTest', - #superclass : 'TestCase', - #instVars : [ - 'builder', - 'parser' - ], - #pools : [ - 'MicMicrodownSharedPool' - ], - #category : 'Microdown-Agenda-Tests', - #package : 'Microdown-Agenda-Tests' -} - -{ #category : 'running' } -MicSegmentBlockTest >> setUp [ - - super setUp. - builder := MicMicrodownTextualBuilder new. - parser := Microdown new. -] - -{ #category : 'tests' } -MicSegmentBlockTest >> testSegment [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'segment', - String cr, EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicSegmentBlock). - self assert: env environmentName equals: 'segment'. -] - -{ #category : 'tests' } -MicSegmentBlockTest >> testSegmentWithArgument [ - "' -'" - | source root env| - source := EnvironmentOpeningBlockMarkup , 'segment|start=10h', String cr, EnvironmentClosingBlockMarkup, String cr. - root := parser parse: source. - env := root children first. - self assert: (env isKindOf: MicSegmentBlock). - self assert: env environmentName equals: 'segment'. - self assert: env start equals: '10h' -] diff --git a/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st b/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st index 915d0a8f2..e41132cd4 100644 --- a/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st +++ b/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st @@ -1,855 +1,435 @@ Class { #name : 'MicReferenceCheckerTest', - #superclass : 'MicFileTest', - #instVars : [ - 'fileDefAncS1UndAncS0', - 'refAncS1', - 'defEq1AndReferToEq1', - 'defAndRefFig1', - 'defFig1AndRefToAnsC0', - 'defFig1AndRefToAncUnkS0', - 'refToUnkS1', - 'defCode1AndReferToCode1', - 'defFig1' - ], - #category : 'Microdown-BookTester-Tests', - #package : 'Microdown-BookTester-Tests' + #superclass : 'TestCase', + #category : 'Microdown-ReferenceChecker', + #package : 'Microdown-ReferenceChecker' } -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> anchorNames: checker [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testAllReferencesAreCorrect [ - ^ checker results - select: [ :each | each isKindOf: MicAnchorResult ] - thenCollect: [ :each | each anchorLabel ] -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defAnCS0DoubleEq2DoubleEq1RefEq1 [ - - | defAnCS0DoubleEq2DoubleEq1RefEq1 | - defAnCS0DoubleEq2DoubleEq1RefEq1 := dir - / - 'defAnCS0DoubleEq2DoubleEq1RefEq1.md'. - defAnCS0DoubleEq2DoubleEq1RefEq1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 - -$$ %anchor=Eq1 -balbalbalb! -$$ - -$$ %anchor=Eq2 -balbalbalb! -$$ - -$$ %anchor=Eq2 -balbalbalb! -$$ - -$$ %anchor=Eq1 -balbalbalb! -$$ + | doc visitor | + doc := Microdown parse: '# Section 1 +@anchorSection1 -See *@Eq1@* +See *@anchorSection1@* -' ]. - defAnCS0DoubleEq2DoubleEq1RefEq1 ensureCreateFile. - ^ defAnCS0DoubleEq2DoubleEq1RefEq1 +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + self assert: visitor isOk ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defAncS0DoubleFig1Fig2RefAncS1 [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testAllReferencesAreCorrectInFile [ - | doubleFig1 | - doubleFig1 := dir / 'defAncS0DoubleFig1Fig2RefAncS1.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - doubleFig1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 - -![a caption 1](figures/f.png anchor=fig1) - -![a caption 2](figures/f.png anchor=fig1) - -![a caption 2](figures/f.png anchor=fig1) + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 +@anchorSection1 -![a caption 2](figures/f.png anchor=fig1) +See *@anchorSection1@* -![a caption 3](figures/f.png anchor=fig2) +' ] . + -See *@ancS1@* -' ]. - doubleFig1 ensureCreateFile. - ^ doubleFig1 + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file ). + file ensureDelete ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defAncS0TripleAncS1RefAncS1AncS0 [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testAllReferencesAreCorrectinDir [ - | defAncS0TripleAncS1RefAncS1AncS0 | - defAncS0TripleAncS1RefAncS1AncS0 := dir / 'defAncS0TripleAncS1RefAncS1AncS0.md'. - defAncS0TripleAncS1RefAncS1AncS0 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 + | dir file1 file2 visitor | + self skip. + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory. + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 # Section 1 -@ancS1 - -# Section 2 -@ancS1 - -# Section 3 -@ancS1 +@anchorSection1 -See *@ancS1@* and *@ancS0@* +' ] . + + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: ' See *@anchorSection1@* and *@anchorSection1@*'] . + file2 ensureCreateFile. -' ]. - defAncS0TripleAncS1RefAncS1AncS0 ensureCreateFile. - ^ defAncS0TripleAncS1RefAncS1AncS0 + visitor := MicReferenceChecker new. + self assert: (visitor checkDirectory: dir). + ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defCode1AndReferToCode1 [ +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testDuplicatedAnchorDir [ - defCode1AndReferToCode1 := fs / 'defCode1AndReferToCode1.md'. - defCode1AndReferToCode1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 + | dir file1 file2 visitor | + self skip. + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory. -```language=pharo&anchor=Code1 -balbalbalb! -``` - -See *@Code1@* + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 -' ]. - defCode1AndReferToCode1 ensureCreateFile -] +# Section 1 +@anchorSection1 -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defEq1AndReferToEq1 [ - defEq1AndReferToEq1 := fs / 'defEq1AndReferToEq1.md'. - defEq1AndReferToEq1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 +' ] . + file1 ensureCreateFile . -$$ %anchor=Eq1 -balbalbalb! -$$ -See *@Eq1@* + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection3 -' ]. - defEq1AndReferToEq1 ensureCreateFile -] +# Section 4 +@anchorSection1 -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defFig1 [ - defFig1 := fs / 'defFig1.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - defFig1 ensureCreateFile. - defFig1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 -![alittle caption. %anchor=Fig1](figures/f.png) +'] . + file2 ensureCreateFile . + visitor := MicReferenceChecker new. -' ] + self deny: ( visitor checkDirectory: dir ) + ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defFig1AndRefFig1 [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testFile [ - defAndRefFig1 := dir / 'defAndRefFig1.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - defAndRefFig1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 - -![alittle caption.](figures/f.png anchor=Fig1) + | 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 *@Fig1@* +See *@anchorSection0@* ' ]. - defAndRefFig1 ensureCreateFile. - ^ defAndRefFig1 -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defFig1AndRefToAncS0 [ - - defFig1AndRefToAnsC0 := fs / 'defFig1AndRefToAncS0.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - defFig1AndRefToAnsC0 ensureCreateFile. - defFig1AndRefToAnsC0 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 -![alittle caption. %anchor=Fig1](figures/f.png) - -See *@ancS0@* - -' ] + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file) ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defFig1AndRefToAncUnkS0 [ - - defFig1AndRefToAncUnkS0 := fs / 'defFig1AndRefToAncUnkS0.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - defFig1AndRefToAncUnkS0 ensureCreateFile. - defFig1AndRefToAncUnkS0 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 -![alittle caption.](figures/f.png anchor=Fig1) - -See *@ancUnkS0@* +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAFigure [ -' ]. - defFig1AndRefToAncUnkS0 ensureCreateFile -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> defFig1FilePathToUnk [ + | doc visitor | + doc := Microdown parse: '# Section 1 +![alittle caption.](figures/f.png anchor=anchorSection1) - defAndRefFig1 := dir / 'defAndRefFig1.md'. - defAndRefFig1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 - -![alittle caption.](figures/f.png anchor=Fig1) +See *@anchorSection1@* -' ]. - defAndRefFig1 ensureCreateFile. - ^ defAndRefFig1 +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + self assert: visitor isOk ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> duplicatedFigSecEq [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAFigureInFile [ - | conflictBetweenFigSecEq | - conflictBetweenFigSecEq := dir / 'duplicatedFigSecEq.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - conflictBetweenFigSecEq writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 - -![a caption 1](figures/f.png anchor=ancS1) - -# Section 1 -@ancS1 - -We have a duplication between the section and the figure. - -# Section 3 -@ancS3 - -We have a duplication between section and equation -$$ %anchor=ancS0 -balbalbalb! -$$ - -Here we have a duplication between the figure and the equation - -![a caption 1](figures/f.png anchor=fig2) - -$$ %anchor=fig2 -balbalbalb! -$$ + | 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 *@ancS1@* and *@ancS0@* +See *@anchorSection1@* ' ]. - conflictBetweenFigSecEq ensureCreateFile. - ^ conflictBetweenFigSecEq + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file). ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> duplicatedFigSecEqPart1 [ - - | duplicatedFigSecEqPart1 | - duplicatedFigSecEqPart1 := dir / 'duplicatedFigSecEqPart1.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - duplicatedFigSecEqPart1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAMathEquation [ -![a caption 1](figures/f.png anchor=ancS1) - - -$$ %anchor=fig2 + | doc visitor | + doc := Microdown parse: '# Section 1 + +$$ %anchor=anchorSection1 balbalbalb! $$ +See *@anchorSection1@* - -# Section 3 -@ancS3 - -' ]. - duplicatedFigSecEqPart1 ensureCreateFile. - ^ duplicatedFigSecEqPart1 +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + self assert: visitor isOk ] -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> duplicatedFigSecEqPart2 [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAMathEquationInFile [ - | duplicatedFigSecEqPart2 | - duplicatedFigSecEqPart2 := dir / 'duplicatedFigSecEqPart2.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - duplicatedFigSecEqPart2 writeStreamDo: [ :stream | - stream nextPutAll: ' -# Duplication with Figure + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. -@ancS1 -We have a duplication between section and equation -$$ %anchor=ancS0 + + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 + +$$ %anchor=anchorSection1 balbalbalb! $$ +See *@anchorSection1@* -Here we have a duplication between the figure and the equation - -![a caption 1](figures/f.png anchor=fig2) - -See *@ancS1@* and *@ancS0@* - -' ]. - duplicatedFigSecEqPart2 ensureCreateFile. - ^ duplicatedFigSecEqPart2 -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> fileDefAncS1UndAncS0 [ - - fileDefAncS1UndAncS0 := fs / 'fileDefAncS1UndAncS0.md'. - (dir / 'figures' / 'f.png') ensureCreateFile. - fileDefAncS1UndAncS0 ensureCreateFile. - fileDefAncS1UndAncS0 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 - -![alittle caption.](figures/f.png anchor=ancS1) - -See *@ancS0@* - -' ]. - fileDefAncS1UndAncS0 ensureCreateFile -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> fileRefAncS1 [ - - refAncS1 := fs/ 'fileRefAncS1.md'. - refAncS1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 - -See *@ancS1@* -' ]. - refAncS1 ensureCreateFile -] - -{ #category : 'helpers - anchors & references' } -MicReferenceCheckerTest >> refToUnkS1 [ - - refToUnkS1 := fs / 'refToUnkS1.md'. - refToUnkS1 ensureCreateFile. - - refToUnkS1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 - -See *@ancS1@* - -' ] -] - -{ #category : 'tests - single file ok references' } -MicReferenceCheckerTest >> testDefAndReferToACodeAnchorInFile [ - - | visitor | - self defCode1AndReferToCode1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: defCode1AndReferToCode1. - self assert: visitor isOkay. - self assert: visitor results isEmpty -] +' ] . + -{ #category : 'tests - single file ok references' } -MicReferenceCheckerTest >> testDefAndReferToAMathEquationInFile [ - | visitor | - self defEq1AndReferToEq1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: defEq1AndReferToEq1. - self assert: visitor isOkay. - self assert: visitor results isEmpty + visitor := MicReferenceChecker new. + self assert: (visitor checkFile: file). + file ensureDelete ] -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testDefFig1AndRefToAncS0UnknowAnchor [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAnUknownAnchor [ - | visitor badRefs | - self defFig1AndRefToAncS0. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: defFig1AndRefToAnsC0. - self deny: visitor isOkay. - badRefs := visitor results select: [ :each | each isKindOf: MicAnchorResult ]. - self assert: badRefs first anchorLabel equals: 'ancS0'. - self assert: badRefs first sourceFileReference fullName equals: '/defFig1AndRefToAncS0.md' -] + | doc visitor | + doc := Microdown parse: '# Section 1 -{ #category : 'tests - single file ok references' } -MicReferenceCheckerTest >> testDefFig1AndRefToAncUnkS0 [ +See *@anchorSection1@* - | visitor | - self defFig1AndRefToAncUnkS0. +'. visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkList: { defFig1AndRefToAncUnkS0 }. - self deny: visitor isOkay. - self assert: visitor results size equals: 3. - self assert: visitor results second anchorLabel equals: 'ancUnkS0'. - self - assert: visitor results second sourceFileReference fullName - equals: '/defFig1AndRefToAncUnkS0.md' + doc accept: visitor. + self deny: visitor isOk ] -{ #category : 'tests - single file ok references' } -MicReferenceCheckerTest >> testDefFig1AndReferToFig1 [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testReferToAnUknownAnchorInFile [ - | visitor | - self defFig1AndRefFig1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: defAndRefFig1. - self assert: visitor isOkay. - self assert: visitor results isEmpty -] + | file visitor | + file := (FileSystem memory / 'myFile.txt') asFileReference. + file ensureCreateFile. -{ #category : 'tests - single file ok references' } -MicReferenceCheckerTest >> testDefFig1ReferToAFigureInFile [ - | visitor | - self defFig1AndRefFig1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkList: { defAndRefFig1 }. - self assert: visitor isOkay -] + + file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testDefS1ButRefersToS0UnknownAnchor [ +See *@anchorSection1@* - | visitor | - self fileDefAncS1UndAncS0. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: fileDefAncS1UndAncS0. - self deny: visitor isOkay. - self assert: visitor results size equals: 3. - self assert: visitor results second anchorLabel equals: 'ancS0'. - self assert: visitor results second sourceFileReference fullName equals: '/fileDefAncS1UndAncS0.md' +' ] . + + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file) . + file ensureDelete ] -{ #category : 'tests - directory api' } -MicReferenceCheckerTest >> testDirWithReferenceInAnotherFile [ +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedAnchors [ - | aSectionWithAnchorAndRef file2 visitor | - aSectionWithAnchorAndRef := dir / 'aSectionWithAnchorAndRef.md'. - aSectionWithAnchorAndRef writeStreamDo: [ :stream | - stream nextPutAll: '# Section 1 -@ancS1 - -See *@ancS1@* and *@ancS2@* + | doc visitor | + doc := Microdown parse: '# Section +@anchorSection0 -' ]. - aSectionWithAnchorAndRef ensureCreateFile. +# Section 1 +@anchorSection1 - file2 := dir / 'justReferenceToSection.md'. - file2 writeStreamDo: [ :stream2 | - stream2 nextPutAll: ' # Section 2 -@ancS2 +@anchorSection1 -Just a reference See *@ancS1@* ' ]. - file2 ensureCreateFile. +# Section 3 +@anchorSection1 +See *@anchorSection1@* and *@anchorSection0@* + +'. visitor := MicReferenceChecker new. - visitor checkDirectory: dir. - self assert: visitor isOkay - + doc accept: visitor. + self deny: visitor isOk. + 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. -{ #category : 'skipped for now' } -MicReferenceCheckerTest >> testDuplicatedAnchorDir [ - | file1 file2 visitor | - self skip. - dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. - dir ensureCreateDirectory. - file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. - file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section + file writeStreamDo: [ :stream | stream nextPutAll: '# Section @anchorSection0 # Section 1 @anchorSection1 +# Section 2 +@anchorSection1 -' ] . - file1 ensureCreateFile . - - file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. - file2 writeStreamDo: [ :stream | stream nextPutAll: '# Section -@anchorSection3 - -# Section 4 +# Section 3 @anchorSection1 +See *@anchorSection1@* and *@anchorSection0@* '] . - file2 ensureCreateFile . - - visitor := MicReferenceChecker new. - - self deny: ( visitor checkDirectory: dir ) -] - -{ #category : 'tests - directory api' } -MicReferenceCheckerTest >> testDuplicatedAnchorInDifferentFilesOfTheSameDir [ - - | file1 file2 visitor dict duplicated | - file1 := dir / 'file1.md'. - file1 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS0 -# Section 1 -@ancS1 -' ]. - file1 ensureCreateFile. - file2 := dir / 'file2.md'. - file2 writeStreamDo: [ :stream | - stream nextPutAll: '# Section -@ancS3 - -# Section 4 -@ancS1 -' ]. - file2 ensureCreateFile. - - visitor := MicReferenceChecker new. - visitor checkDirectory: dir. - self deny: visitor isOkay. - - self assert: visitor duplicatedAnchors size equals: 2. - self - assert: visitor duplicatedAnchors first anchorLabel - equals: 'ancS1'. - dict := visitor results groupedBy: [ :each | each class ]. - duplicated := (dict at: MicDuplicatedAnchorResult) first. - self - assert: visitor results first sourceFileReference fullName - equals: '/myDirectory/file2.md'. - self assert: duplicated anchorLabel equals: 'ancS1' + 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' } -MicReferenceCheckerTest >> testDuplicatedAnchors [ - - | defAncS0TripleAncS1RefAncS1AncS0 checker dict dup1 | - defAncS0TripleAncS1RefAncS1AncS0 := self defAncS0TripleAncS1RefAncS1AncS0. - checker := MicReferenceChecker new. - checker checkList: { defAncS0TripleAncS1RefAncS1AncS0 }. - self deny: checker isOkay. - self - assert: (checker results collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'ancS1' 'ancS1' 'ancS1' ). +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedFigures [ - dict := checker results groupedBy: [ :each | each class ]. + | doc visitor | + doc := Microdown parse: '# Section +@anchorSection0 - dup1 := (dict at: MicDuplicatedAnchorResult) first. - self - assert: dup1 sourceFileReference fullName - equals: '/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'. - self assert: dup1 anchorLabel equals: 'ancS1' -] +![a caption 1](figures/f.png anchor=anchorSection1) -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testDuplicatedBetweenSectionFigureEq [ +![a caption 2](figures/f.png anchor=anchorSection1) - | conflictBetweenFigSecEq visitor | - conflictBetweenFigSecEq := self duplicatedFigSecEq. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkList: { conflictBetweenFigSecEq }. - self deny: visitor isOkay. - self - assert: (self anchorNames: visitor) - equals: OrderedCollection - <- #( 'ancS1' 'ancS1' 'ancS0' 'ancS0' 'fig2' 'fig2' ) -] +![a caption 3](figures/f.png anchor=anchorSection2) -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testDuplicatedBetweenSectionFigureEqInDifferentFile [ +See *@anchorSection1@* and *@anchorSection3@* - | duplicatedFigSecEqPart1 duplicatedFigSecEqPart2 visitor | - duplicatedFigSecEqPart1 := self duplicatedFigSecEqPart1. - duplicatedFigSecEqPart2 := self duplicatedFigSecEqPart2. +'. visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkList: { - duplicatedFigSecEqPart1. - duplicatedFigSecEqPart2 }. - self deny: visitor isOkay. + doc accept: visitor. + self deny: visitor isOk. self - assert: (self anchorNames: visitor) - equals: OrderedCollection - <- #( 'ancS1' 'ancS1' 'ancS0' 'ancS0' 'fig2' 'fig2' ) + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'anchorSection1' ) ] -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testDuplicatedFigures [ - - | doubleFig1 checker dict dup1 | - doubleFig1 := self defAncS0DoubleFig1Fig2RefAncS1. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkList: { doubleFig1 }. - self deny: checker isOkay. - self - assert: (self anchorNames: checker) - equals: - OrderedCollection <- #( 'fig1' 'fig1' 'fig1' 'fig1' 'ancS1' ). +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedFiguresInFile [ - dict := checker results groupedBy: [ :each | each class ]. + | file visitor | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 - dup1 := (dict at: MicDuplicatedAnchorResult) first. - self - assert: dup1 sourceFileReference fullName - equals: '/myDirectory/defAncS0DoubleFig1Fig2RefAncS1.md'. - self assert: dup1 anchorLabel equals: 'fig1' -] +![a caption 1](figures/f.png anchor=anchorSection1) -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testDuplicatedMaths [ +![a caption 2](figures/f.png anchor=anchorSection1) - | defAnCS0DoubleEq2DoubleEq1RefEq1 checker dict dup1 dup2 | - defAnCS0DoubleEq2DoubleEq1RefEq1 := self defAnCS0DoubleEq2DoubleEq1RefEq1. - checker := MicReferenceChecker new. - checker checkList: { defAnCS0DoubleEq2DoubleEq1RefEq1 }. - self deny: checker isOkay. - self - assert: checker results first sourceFileReference fullName - equals: defAnCS0DoubleEq2DoubleEq1RefEq1 fullName. - self - assert: (checker results collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'Eq2' 'Eq2' 'Eq1' 'Eq1' ). - - dict := checker results groupedBy: [ :each | each class ]. - - dup1 := (dict at: MicDuplicatedAnchorResult) first. - self assert: dup1 sourceFileReference fullName equals: '/myDirectory/defAnCS0DoubleEq2DoubleEq1RefEq1.md'. - self assert: dup1 anchorLabel equals: 'Eq2'. - - dup2 := (dict at: MicDuplicatedAnchorResult) third. - self assert: dup2 sourceFileReference fullName equals: '/myDirectory/defAnCS0DoubleEq2DoubleEq1RefEq1.md'. - self assert: dup2 anchorLabel equals: 'Eq1'. -] +![a caption 3](figures/f.png anchor=anchorSection2) -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testFigureFilePathToUnkFile [ +See *@anchorSection1@* and *@anchorSection3@* - | visitor refToUnexistingFiles | - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: self defFig1FilePathToUnk. - self deny: visitor isOkay. - self assert: visitor results size equals: 2. - refToUnexistingFiles := (visitor results groupedBy: [ :each | each class name ]) at: #MicUndefinedFigureFileResult. - self assert: refToUnexistingFiles size equals: 1. - self assert: refToUnexistingFiles first what equals: 'figures/f.png'. - self assert: refToUnexistingFiles first figureFileString equals: 'figures/f.png'. - +']. + visitor := MicReferenceChecker new. + self deny: (visitor checkFile: file). self - assert: refToUnexistingFiles first sourceFileReference fullName - equals: '/myDirectory/defAndRefFig1.md' -] - -{ #category : 'tests - full project' } -MicReferenceCheckerTest >> testFullProjectWithFowardAndBackWardRefBetweenTwoFiles [ - - | checker | - self createProjectCorrectReferencesOnTwoFiles. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkProject: section1. - self assert: checker isOkay -] - -{ #category : 'tests - full project' } -MicReferenceCheckerTest >> testFullProjectWithReferencesToUnknowAnchor [ - - | checker dict unk undefined | - self createProjectBadAndCorrectReferences. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkProject: section1. - self deny: checker isOkay. - self assert: checker results size equals: 2. - - dict := checker results groupedBy: [ :each | each class ]. - - unk := (dict at: MicReferenceToUnexistingAnchorResult) first. - self assert: unk sourceFileReference fullName equals: '/myDirectory/sections/section2.md'. - self assert: unk anchorLabel equals: 'ancUnkS2'. - - undefined := (dict at: MicUndefinedInputFileResult) first. - self assert: undefined sourceFileReference fullName equals: '/myDirectory/sections/section2.md'. - - + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'anchorSection1' ). + file ensureDelete ] -{ #category : 'tests - internal - parse only' } -MicReferenceCheckerTest >> testLowLevelAllReferencesAreCorrect [ +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedMaths [ | doc visitor | - doc := Microdown parse: '# Section 1 -@anchorSection1 + doc := Microdown parse: '# Section +@anchorSection0 -See *@anchorSection1@* +$$ %anchor=anchorSection1 +balbalbalb! +$$ -'. - visitor := MicReferenceChecker new. - doc accept: visitor. - visitor collectReferencesToUnexistingAnchors. - self assert: visitor isOkay -] +$$ %anchor=anchorSection1 +balbalbalb! +$$ -{ #category : 'tests - internal - parse only' } -MicReferenceCheckerTest >> testLowLevelReferToAFigure [ +$$ %anchor=anchorSection1 +balbalbalb! +$$ - | doc visitor | - doc := Microdown parse: '# Section 1 -![alittle caption.](figures/f.png anchor=anchorSection1) +$$ %anchor=anchorSection3 +balbalbalb! +$$ -See *@anchorSection1@* +See *@anchorSection1@* and *@anchorSection3@* '. - doc fromFile: 'file.md' asFileReference. visitor := MicReferenceChecker new. doc accept: visitor. - visitor collectReferencesToUnexistingAnchors. - self assert: visitor results first class equals: MicUndefinedFigureFileResult - + self deny: visitor isOk. + self + assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'anchorSection1' 'anchorSection1' ) ] -{ #category : 'tests - internal - parse only' } -MicReferenceCheckerTest >> testLowLevelReferToAMathEquation [ +{ #category : 'tests - duplicated anchors' } +MicReferenceCheckerTest >> testReportingDuplicatedMathsInFile [ + + | file visitor | + file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. + file ensureCreateFile. + file writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection0 - | doc visitor | - doc := Microdown parse: '# Section 1 - $$ %anchor=anchorSection1 balbalbalb! $$ -See *@anchorSection1@* - -'. - visitor := MicReferenceChecker new. - doc accept: visitor. - visitor collectReferencesToUnexistingAnchors. - self assert: visitor isOkay -] -{ #category : 'tests - internal - parse only' } -MicReferenceCheckerTest >> testLowLevelReferToAnUknownAnchor [ - - | doc visitor | - doc := Microdown parse: '# Section 1 - -See *@anchorSection1@* - -'. - doc fromFile: 'fakedFile'. - visitor := MicReferenceChecker new. - doc accept: visitor. - visitor collectReferencesToUnexistingAnchors. - self deny: visitor isOkay -] +$$ %anchor=anchorSection1 +balbalbalb! +$$ -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testRefToUnkS1 [ +$$ %anchor=anchorSection1 +balbalbalb! +$$ - | visitor | - self refToUnkS1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: refToUnkS1. - self deny: visitor isOkay - +$$ %anchor=anchorSection3 +balbalbalb! +$$ -] +See *@anchorSection1@* and *@anchorSection3@* -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testRefersToUnkAncS1 [ +'] . - | visitor | - self fileRefAncS1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: refAncS1. - self deny: visitor isOkay. - self assert: visitor results size equals: 1. - self assert: visitor results first anchorLabel equals: 'ancS1'. - self assert: visitor results first sourceFileReference fullName equals: '/fileRefAncS1.md'. + 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' } -MicReferenceCheckerTest >> testReportingSTONFormatDuplicatedAnchors [ +{ #category : 'tests' } +MicReferenceCheckerTest >> testReportingUnknownAnchor [ - | defAncS0TripleAncS1RefAncS1AncS0 checker | - self skip. - defAncS0TripleAncS1RefAncS1AncS0 := self defAncS0TripleAncS1RefAncS1AncS0. - checker := MicReferenceChecker new. - checker checkList: { defAncS0TripleAncS1RefAncS1AncS0 }. - - self assert: checker reportSTONFormat equals: '[''type'':''DuplicatedAnchor'',[''ancS1'':''/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'',''ancS1'':''/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'']]' - - -] + | doc visitor | + doc := Microdown parse: '# Section +@anchorSection0 -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testReportingSTONFormatDuplicatedFigure [ +# Section 1 +@anchorSection1 - | checker | - self skip. - self fileRefAncS1. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkProject: refAncS1. - - self assert: checker reportSTONFormat equals: '[''type'':''UndefinedAnchor'',[''ancS1'':''/fileRefAncS1.md'']]' -] +See *@anchorSection1@* and *@anchorSection2@* -{ #category : 'tests - duplicated' } -MicReferenceCheckerTest >> testReportingSTONFormatUnknownAnchorDir [ - - | checker | - self skip. - self fileRefAncS1. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkProject: refAncS1. - - self assert: checker reportSTONFormat equals: '[''type'':''UndefinedAnchor'',[''ancS1'':''/fileRefAncS1.md'']]' - +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + self deny: visitor isOk. + self + assert: (visitor unknownAnchors collect: [ :each | each anchorLabel ]) + equals: (OrderedCollection <- #('anchorSection2')) ] -{ #category : 'skipped for now' } +{ #category : 'tests' } MicReferenceCheckerTest >> testReportingUnknownAnchorDir [ - | file1 file2 visitor | + | dir file1 file2 visitor | self skip. dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. dir ensureCreateDirectory. @@ -878,48 +458,3 @@ MicReferenceCheckerTest >> testReportingUnknownAnchorDir [ dir ensureDelete ] - -{ #category : 'tests - undefined files' } -MicReferenceCheckerTest >> testUndefinedInputFilesAreMentionedInResult [ - - | checker | - self createProjectWithUnexistingSection3And5. - checker := MicReferenceChecker new. - checker rootDirectory: dir. - checker checkProject: section1. - - self assert: checker results size equals: 3. - checker results do: [ :each | - self - assert: each class - equals: MicUndefinedInputFileResult]. - - (checker results collect: [ :each | each inputFileBlock path path "Ugly API to improve later" ]) - do: [ :each | - self - assert: - ({ - '/myDirectory/sections/section3.md' . - '/myDirectory/sections/subsections/section5.md' . - '/myDirectory/sections/subsections/section3.md' - } includes: each) - ] - -] - -{ #category : 'tests - single file' } -MicReferenceCheckerTest >> testUnreferencedFigure [ - - | visitor badRefs unref | - self defFig1. - visitor := MicReferenceChecker new. - visitor rootDirectory: dir. - visitor checkProject: defFig1. - self deny: visitor isOkay. - badRefs := (visitor results groupedBy: [:each | each class name]) at: #MicUnreferencedFigureResult. - unref := badRefs first. - self assert: badRefs size equals: 1. - self assert: unref figurePath equals: 'figures/f.png'. - self assert: unref anchorLabel equals: 'Fig1'. - self assert: unref sourceFileReference fullName equals: '/defFig1.md' -] diff --git a/src/Microdown-BookTester/MicReferenceChecker.class.st b/src/Microdown-BookTester/MicReferenceChecker.class.st index 21496d9a0..b8e5e306a 100644 --- a/src/Microdown-BookTester/MicReferenceChecker.class.st +++ b/src/Microdown-BookTester/MicReferenceChecker.class.st @@ -1,183 +1,80 @@ " -I'm a nice little tool that checks whether a document has -- references to undeclared anchors -- duplicated anchors +I'm a little tool that checks whether a document has (1) references to undeclared anchors or (2) duplicated anchors. -I check -- figures -- math equations -- or plain references. - -I use the `MicFileCollector` to support all the file input relations. -This lets the user have for example unused, broken or underway files on the side. -As soon as they are not used I do not analyse them. - -# API -My main API to integrate with the ReportWriter is the method `checkProject:` -I support the following API to be able to chained over a report writer: -- result/results: -- reportWriter: - - -# Potential enhancements - -When we use the checkDir or the checkFile: API, it should still do the full closure of used files (i.e. use the file collector). This is a lot simpler to understand for the user. Having a systematic approach is the way to go. +For now Microdown is file agnostic so this is impossible to report in which file such duplication or reference occurs. +Once file support is introduced I should be revised to report better information to the user. " Class { #name : 'MicReferenceChecker', - #superclass : 'MicChecker', + #superclass : 'MicrodownVisitor', #instVars : [ 'references', - 'duplicatedAnchors', - 'listOfFiles', - 'figures', - 'anchoringEntities' + 'anchors', + 'duplicatedAnchors' ], - #category : 'Microdown-BookTester-References', - #package : 'Microdown-BookTester', - #tag : 'References' + #category : 'Microdown-ReferenceChecker', + #package : 'Microdown-ReferenceChecker' } -{ #category : 'accessing' } -MicReferenceChecker >> addDuplicatedAnchor: anAnchor [ - - | micResultInstance | - micResultInstance := MicDuplicatedAnchorResult new. - micResultInstance - anchorLabel: anAnchor anchorLabel; - sourceFileReference: anAnchor fromFile. - results add: micResultInstance -] - { #category : 'visiting' } -MicReferenceChecker >> addDuplicatedFirstAnchor: anAnchor [ - - anchoringEntities do: [ :each | - each anchorLabel = anAnchor anchorLabel ifTrue: [ - (duplicatedAnchors includes: each) ifFalse: [ - duplicatedAnchors add: each. - self addDuplicatedAnchor: each ] ] ] -] - -{ #category : 'accessing' } -MicReferenceChecker >> addReferenceToUnexistingAnchor: anAnchorReference [ - - | micResultInstance | - micResultInstance := MicReferenceToUnexistingAnchorResult new. - micResultInstance - anchorLabel: anAnchorReference anchorLabel; - sourceFileReference: anAnchorReference fromFile. - results add: micResultInstance -] - -{ #category : 'accessing' } -MicReferenceChecker >> addUnexistingFigureFile: aFigure [ - - | micResultInstance | - micResultInstance := MicUndefinedFigureFileResult new. - micResultInstance - figureFileString: aFigure reference path; - sourceFileReference: aFigure fromFile. - results add: micResultInstance -] +MicReferenceChecker >> check: aDocument [ + "Pay attention checking a file in isolation is DIFFERENT from a list, because document + can have references between them and the checker should be shared amongst the documents + since it collects the references." -{ #category : 'accessing' } -MicReferenceChecker >> addUnreferencedFigure: aFigure [ + aDocument accept: self. + ^ self isOk + + - | micResultInstance | - micResultInstance := MicUnreferencedFigureResult new. - micResultInstance - figurePath: aFigure reference relativePath; - anchorLabel: aFigure anchorLabel; - sourceFileReference: aFigure fromFile. - results add: micResultInstance ] -{ #category : 'main API' } +{ #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 . " - self checkList: aDir allFiles + ^ self checkList: aDir allFiles ] -{ #category : 'internal' } -MicReferenceChecker >> checkList: aCollection [ - "Pay attention checking a file in isolation is DIFFERENT from a list, because a document can have references between them and the checker should be shared amongst the documents since it collects the references." - - aCollection do: [ :each | - | document | - document := Microdown parseFile: each. - document accept: self ]. - self - collectReferencesToUnexistingAnchors; - collectUnreferencedFigures - -] +{ #category : 'visiting' } +MicReferenceChecker >> checkFile: aFile [ + "Will parse the given file and invite the visitor and return visitor isOk value" -{ #category : 'main API' } -MicReferenceChecker >> checkProject: aFileReference [ - "Given the root of the document such as an index.md file, check the complete project." + | document | + document := Microdown parseFile: aFile. + ^ self check: document + + - | collector | - collector := self fileCollectorForMainFileReference: aFileReference. - self handleUndefinedFilesFrom: collector. - listOfFiles := collector visitedFileStrings collect: [ :file | rootDirectory resolve: file ]. - self checkList: listOfFiles ] -{ #category : 'internal' } -MicReferenceChecker >> collectReferencesToUnexistingAnchors [ - "Should be called after all the docs are visited otherwise the result can be wrong." - - | badReference existingAnchorNames | - existingAnchorNames := anchoringEntities collect: [ :each | each anchorLabel ]. - badReference := references reject: [ :anchorReference | - existingAnchorNames includes: anchorReference anchorLabel ]. - badReference do: [ :each | self addReferenceToUnexistingAnchor: each ] -] +{ #category : 'visiting' } +MicReferenceChecker >> checkList: aCollection [ + "Pay attention checking a file in isolation is DIFFERENT from a list, because document + can have references between them and the checker should be shared amongst the documents + since it collects the references." -{ #category : 'internal' } -MicReferenceChecker >> collectUnreferencedFigures [ + aCollection do: [ :each | self checkFile: each ]. + ^ self isOk + + - | unreferencedFigures anchorReferenceNames | - anchorReferenceNames := references collect: [ :each | each anchorLabel ]. - unreferencedFigures := figures reject: [ :figure | anchorReferenceNames includes: figure anchorLabel ]. - unreferencedFigures do: [ :each | self addUnreferencedFigure: each ] ] -{ #category : 'internal' } +{ #category : 'reporting' } MicReferenceChecker >> duplicatedAnchors [ ^ duplicatedAnchors ] -{ #category : 'accessing' } -MicReferenceChecker >> fileSystem: aFileSystem [ - - rootDirectory := aFileSystem -] - { #category : 'visiting' } MicReferenceChecker >> handleAnchorOf: anElement [ anElement hasAnchor ifFalse: [ ^ self ]. - (self hasAlreadyDefinedAs: anElement) ifTrue: [ - duplicatedAnchors add: anElement. - self addDuplicatedAnchor: anElement. - self addDuplicatedFirstAnchor: anElement. ]. - anchoringEntities add: anElement -] - -{ #category : 'internal' } -MicReferenceChecker >> handleUndefinedFilesFrom: collector [ - - collector unexistingFiles do: [ :each | - results add: (MicUndefinedInputFileResult new - inputFileBlock: each ; - sourceFileReference: each fromFile; - yourself ) - - ] + (self hasAlreadyDefinedAs: anElement) + ifTrue: [ duplicatedAnchors add: anElement ]. + anchors add: anElement + ] { #category : 'visiting' } @@ -185,7 +82,7 @@ MicReferenceChecker >> hasAlreadyDefinedAs: anAnchor [ | alreadyDefined | alreadyDefined := false. - anchoringEntities do: + anchors do: [ :each | each anchorLabel = anAnchor anchorLabel ifTrue: [ alreadyDefined := true ] ]. ^ alreadyDefined @@ -195,34 +92,16 @@ MicReferenceChecker >> hasAlreadyDefinedAs: anAnchor [ MicReferenceChecker >> initialize [ super initialize. - - rootDirectory := FileSystem workingDirectory. references := OrderedCollection new. - figures := OrderedCollection new. - anchoringEntities := OrderedCollection new. - duplicatedAnchors := OrderedCollection new. "by default if the reporter is not set from outside - then it shares the results of this current checker." - reportWriter := MicAnalysisReportWriter new results: results + anchors := OrderedCollection new. + duplicatedAnchors := OrderedCollection new ] { #category : 'testing' } MicReferenceChecker >> isOk [ - self flag: #fixThisLogic. - "isOkay vs. isOk ???" - - ^ duplicatedAnchors isEmpty and: [ - references allSatisfy: [ :each | self hasAlreadyDefinedAs: each ] ] -] -{ #category : 'reporting' } -MicReferenceChecker >> report [ - - ^ reportWriter report -] - -{ #category : 'reporting' } -MicReferenceChecker >> reportSTONFormat [ - ^ reportWriter reportSTONFormat + ^ duplicatedAnchors isEmpty and: [ + references allSatisfy: [ :each | self hasAlreadyDefinedAs: each ] ] ] { #category : 'internal' } @@ -240,40 +119,23 @@ MicReferenceChecker >> unknownAnchors [ { #category : 'visiting' } MicReferenceChecker >> visitAnchor: anAnchor [ - "the problem with visit anchor is that it only concerns - @anchor and not the one of figure. - So we should then keep a list of anchorNames for the figures, math equations - or reify these anchor - would be nicer" - + | isAlready | isAlready := self hasAlreadyDefinedAs: anAnchor. - isAlready ifTrue: [ - duplicatedAnchors add: anAnchor. - self addDuplicatedAnchor: anAnchor. - self addDuplicatedFirstAnchor: anAnchor. ]. - anchoringEntities add: anAnchor + isAlready ifTrue: [ duplicatedAnchors add: anAnchor ]. + anchors add: anAnchor ] { #category : 'visiting' } MicReferenceChecker >> visitAnchorReference: anAnchorReference [ - - references add: anAnchorReference -] - -{ #category : 'visiting' } -MicReferenceChecker >> visitCode: aCodeBlock [ - self handleAnchorOf: aCodeBlock + references add: anAnchorReference ] { #category : 'visiting' } MicReferenceChecker >> visitFigure: aFigure [ - - figures add: aFigure. - self handleAnchorOf: aFigure. - "check for unexisting file." - (aFigure fromFile parent / aFigure reference path) exists - ifFalse: [ self addUnexistingFigureFile: aFigure ] + + self handleAnchorOf: aFigure ] { #category : 'visiting' } diff --git a/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st b/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st index a08d40caa..852578258 100644 --- a/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st +++ b/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st @@ -12,44 +12,6 @@ Class { #tag : 'HTML' } -{ #category : 'tests' } -MicHTMLExporterTest class >> testParameters [ - - ^ ParametrizedTestMatrix new - forSelector: #writer addOptions: { MicHTMLVisitor }; - forSelector: #factory addOptions: { MicMicrodownSnippetFactory }; - forSelector: #parser addOptions: { Microdown }; - forSelector: #newLine addOptions: { String cr . String lf . String crlf }; - yourself -] - -{ #category : 'accessing' } -MicHTMLExporterTest >> factory: aFactory [ - self flag: #todo. - "This is horrible either factory: should be renamed factoryClass or we should remove the new." - factory := aFactory new -] - -{ #category : 'accessing' } -MicHTMLExporterTest >> newLine [ - - ^ newLine -] - -{ #category : 'accessing' } -MicHTMLExporterTest >> newLine: aNewLine [ - (aNewLine = String cr) ifTrue:[ writer crAsNewLine ]. - (aNewLine = String lf) ifTrue:[ writer lfAsNewLine ]. - (aNewLine = String crlf) ifTrue:[ writer crlfAsNewLine ]. - newLine := aNewLine -] - -{ #category : 'tests - paragraph' } -MicHTMLExporterTest >> newLineParagraphAround: aString [ - - ^ newLine, '

    ', aString, '

    ', newLine -] - { #category : 'tests - list' } MicHTMLExporterTest >> orderedListString [ @@ -61,210 +23,29 @@ Here is a list: ' ] -{ #category : 'utilities' } +{ #category : 'utils' } MicHTMLExporterTest >> parse: aString andCheckWeGet: aResultingString [ - | mic c | + | mic | mic := parser parse: aString. - writer visit: mic. - c := writer contents. - self assert: c equals: aResultingString. - ^ c -] - -{ #category : 'utilities' } -MicHTMLExporterTest >> parser [ - ^ parser -] - -{ #category : 'accessing' } -MicHTMLExporterTest >> parser: aParser [ - parser := aParser new -] - -{ #category : 'tests - paragraph' } -MicHTMLExporterTest >> pharoPage [ - - ^ - '{ -"title" : "About Pharo", -"layout" : "index", -"publishDate" : "2025-06-01" -} - -# Pharo - -[Pharo](http://www.pharo.org) is a beautiful dynamically-typed reflective pure object-oriented language that we have been developing since 2008. -It is inspired by Smalltalk (I''m extremely grateful to Alan Kay and Dan Ingalls - they were so visionary and right). Now our vision for Pharo is to reinvent Smalltalk and produce a better system. -From that perspective, I''m used to say that Pharo is what we have and not what we want. -In essence, Pharo is the beginning of the journey and not the final goal. And you can change its future. - -You can check what companies are saying about it: [Video](https://youtu.be/6tdkKNX2g4s) - -There are many aspects I would like to see being explored either by us or by others. -If you want to explore some of the following aspects, please go and let us know. -I''m really interested in any topics that evolve Pharo into a better Pharo. - -' -] - -{ #category : 'tests - paragraph' } -MicHTMLExporterTest >> testAccents [ - - self parse: 'éà' andCheckWeGet: (self newLineParagraphAround: -'éà') -] - -{ #category : 'utilities' } -MicHTMLExporterTest >> testAnchor [ - - self parse: factory anchorSample andCheckWeGet: '' - -] - -{ #category : 'utilities' } -MicHTMLExporterTest >> testCodeCreatesInnerText [ - - | code | - code := (self parser parse: '```a b```') children first. - self assert: code class equals: MicCodeBlock. - self assert: code language equals: 'a b```' -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testCodeWithoutParam [ - - | doc | - doc := (parser parse: '``` -ab -foo bar -```') children first. - writer visit: doc. - self assert: writer contents equals: newLine , -'
    ab', newLine ,
    -'foo bar
    ', newLine -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigure [ - - self - parse: factory figureSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ') -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testFigureBold [ - - self - parse: factory figureBoldSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ' ) -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureItalic [ - - self - parse: factory figureItalicSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureNested [ - - self - parse: factory figureNestedSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo_
    Foo_
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureReal [ - - self - parse: factory figureRealSample - andCheckWeGet: (self newLineParagraphAround: -'
    A logo png under figures folder
    A logo png under figures folder
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureStrike [ - - self - parse: factory figureStrikeSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureWithLabelWithoutSize [ - - self - parse: factory figureWithLabelWithoutSizeSample - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureWithoutCaption [ - - self - parse: factory figureWithoutCaptionSample - andCheckWeGet: (self newLineParagraphAround: -'
    ') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testFigureWithoutSizeAndLabel [ - - self - parse: factory figureSampleWithoutSizeAndLabel - andCheckWeGet: (self newLineParagraphAround: -'
    Foo
    Foo
    ') + self assert: (writer visit: mic) contents equals: aResultingString ] -{ #category : 'tests' } -MicHTMLExporterTest >> testGoutDeFraise [ - - self - parse: factory figureGoutDeFraise - andCheckWeGet: (self newLineParagraphAround: -'
    Proposition pour le thème :  Un goût de fraise
    Proposition pour le thème : Un goût de fraise
    ') +{ #category : 'running' } +MicHTMLExporterTest >> setUp [ + super setUp. + parser := MicroDownParser new. + writer := MicHTMLWriter new. + factory := MicMicrodownSnippetFactory new ] { #category : 'tests' } MicHTMLExporterTest >> testHeaderLevel1 [ - self - parse: factory headerLevel1Sample - andCheckWeGet: newLine , '

    Foo

    ' + self parse: factory headerLevel1Sample andCheckWeGet: writer usedNewLine , '

    Foo

    ' ] -{ #category : 'tests' } -MicHTMLExporterTest >> testHeaderLevel2 [ - - self - parse: factory headerLevel2Sample - andCheckWeGet: newLine , '

    Foo

    ' -] - -{ #category : 'tests - metadata' } -MicHTMLExporterTest >> testMetaDataIsIgnored [ - - self parse: factory metaDataSample andCheckWeGet: '' -] - -{ #category : 'tests - metadata' } -MicHTMLExporterTest >> testMetaDataIsNotIgnored [ - - writer doNotIgnoreMetaData. - self parse: factory metaDataSample andCheckWeGet: '' -] - { #category : 'tests - list' } MicHTMLExporterTest >> testOrderedList [ @@ -283,124 +64,6 @@ MicHTMLExporterTest >> testOrderedListIsRecognized [ self assert: doc children second class equals: MicOrderedListBlock ] -{ #category : 'tests - paragraph' } -MicHTMLExporterTest >> testParagraph [ - - self parse: factory paragraphSample andCheckWeGet: (self newLineParagraphAround: 'Foo') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testParagraphLongWithAccents [ - - self parse: factory paragraphOnMultipleLinesSample andCheckWeGet: (self newLineParagraphAround: -'Je ne connais pas la peur, car la peur tue l''esprit. La peur est la petite mort qui conduit à l''oblitération totale. J''affonterai ma peur. Je lui permettrais de passer sur moi, au travers de moi. Et lorsqu''elle sera passée, je tournerai mon oeil interieur sur son chemin. Et là où elle sera passée, il n''y aura plus rien, rien que moi.') -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testParagraphNestedSample [ - - self parse: factory paragraphNestedSample andCheckWeGet: (self newLineParagraphAround: -'this is a paragraph') -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testParagraphWithBold [ - - self - parse: factory paragraphBoldSample - andCheckWeGet: (self newLineParagraphAround: -'this is a paragraph') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testParagraphWithItalic [ - - self parse: factory paragraphItalicSample andCheckWeGet: (self newLineParagraphAround: -'this is a paragraph') -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testParagraphWithMonospace [ - - self parse: factory paragraphMonospaceSample andCheckWeGet: (self newLineParagraphAround: -'this is a paragraph') -] - -{ #category : 'tests - paragraph' } -MicHTMLExporterTest >> testPharoPage [ - - self parse: self pharoPage andCheckWeGet: newLine, '

    Pharo

    ', newLine, -'

    Pharo is a beautiful dynamically-typed reflective pure object-oriented language that we have been developing since 2008.
    It is inspired by Smalltalk (I''m extremely grateful to Alan Kay and Dan Ingalls - they were so visionary and right). Now our vision for Pharo is to reinvent Smalltalk and produce a better system.
    From that perspective, I''m used to say that Pharo is what we have and not what we want.
    In essence, Pharo is the beginning of the journey and not the final goal. And you can change its future.

    ', newLine, newLine, - -'

    You can check what companies are saying about it: Video

    ', newLine, newLine, - -'

    There are many aspects I would like to see being explored either by us or by others.
    If you want to explore some of the following aspects, please go and let us know.
    I''m really interested in any topics that evolve Pharo into a better Pharo.

    ', newLine -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testQuote [ - - self parse: factory quoteSample andCheckWeGet: - 'Foo' -] - -{ #category : 'tests - raw' } -MicHTMLExporterTest >> testRawOnMultipleLinesIsNotRawButTwoPargraphs [ - - | mic | - mic := parser - parse: 'some text here!{{ ', self newLine,' - macOs arm64 }}...and there'. - "notice that I did on purpose that I do not have space around" - - self assert: mic children size equals: 2. - self assert: mic children first class equals: MicParagraphBlock . - self assert: mic children second class equals: MicParagraphBlock . - - - -] - -{ #category : 'tests - raw' } -MicHTMLExporterTest >> testRawOnSingleLineShouldNotGoToTheNextLine [ - - | mic | - mic := parser - parse: 'some text here!{{ macOs arm64 }}...and there'. - "notice that I did on purpose that I do not have space around" - - writer visit: mic children first. - self - assert: writer contents - equals: - ( -self newLine, '

    some text here! macOs arm64 ...and there

    ', self newLine - - ) - - -] - -{ #category : 'tests - formats' } -MicHTMLExporterTest >> testStrike [ - - self parse: factory strikethroughFormatSample andCheckWeGet: (self newLineParagraphAround: -'Foo') -] - -{ #category : 'tests' } -MicHTMLExporterTest >> testTable [ - - | micTable | - micTable := parser parse: factory tableSample. - writer visit: micTable. - self assert: writer contents equals: newLine , -'', newLine , -'', newLine , -'', newLine , -'
    aaabjkhjh
    barrab
    ' -] - { #category : 'tests - list' } MicHTMLExporterTest >> testUnorderedList [ @@ -412,8 +75,3 @@ MicHTMLExporterTest >> testUnorderedList [ '
  • Bar
  • ', newLine , '' ] - -{ #category : 'accessing' } -MicHTMLExporterTest >> writer: aWriter [ - writer := aWriter new -] diff --git a/src/Microdown-HTMLExporter/MicHTMLBrush.class.st b/src/Microdown-HTMLExporter/MicHTMLBrush.class.st deleted file mode 100644 index ee19642ed..000000000 --- a/src/Microdown-HTMLExporter/MicHTMLBrush.class.st +++ /dev/null @@ -1,26 +0,0 @@ -" -I'm an abstract brush dedicated to HTML documents. As of today, the only subclass is the tag brush but we can imagine others (e.g., to write HTML comments). -" -Class { - #name : 'MicHTMLBrush', - #superclass : 'MicExportBrush', - #instVars : [ - 'name' - ], - #category : 'Microdown-HTMLExporter-Core', - #package : 'Microdown-HTMLExporter', - #tag : 'Core' -} - -{ #category : 'accessing' } -MicHTMLBrush >> name [ - "Answer a with the receiver's name" - - ^ name -] - -{ #category : 'accessing' } -MicHTMLBrush >> name: aString [ - name := aString. - stream nextPut: $<; << aString -] diff --git a/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st b/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st index 632fcace1..e3ec237d7 100644 --- a/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st @@ -1,37 +1,31 @@ -" -A specialized canvas to emit HTML tags. -" Class { #name : 'MicHTMLCanvas', #superclass : 'MicExportCanvas', - #category : 'Microdown-HTMLExporter-Core', - #package : 'Microdown-HTMLExporter', - #tag : 'Core' + #instVars : [ + 'name' + ], + #classVars : [ + 'HTMLCharacters' + ], + #category : 'Microdown - HTML', + #package : 'Microdown - HTML' } -{ #category : 'writing text' } -MicHTMLCanvas >> initialize [ - "Private - Initialize the receiver's stream" - - super initialize. - self initializeWith: String empty. -] - { #category : 'initialization' } -MicHTMLCanvas >> initializeWith: aString [ - "Private - Set the receiver's stream to store the HTML contents" - - self setStream: (self newMicStreamOn: aString) +MicHTMLCanvas class >> initialize [ + HTMLCharacters := Dictionary new. + HTMLCharacters + at: $" put: '"'; + at: $& put: '&'; + at: $< put: '<'; + at: $> put: '>' ] -{ #category : 'initialization' } -MicHTMLCanvas >> newMicStreamOn: aString [ - "Answer a new wrapper over aString to help not hardcoding line ending everywhere." - - ^ MicOutputStream new - setStream: (WriteStream on: aString); - "nextPutAll: aString;" - yourself +{ #category : 'accessing' } +MicHTMLCanvas >> nextPut: aCharacter [ + (HTMLCharacters at: aCharacter ifAbsent: nil) + ifNil: [ super nextPut: aCharacter ] + ifNotNil: [ :string | self raw: string ] ] { #category : 'accessing' } diff --git a/src/Microdown-HTMLExporter/MicHTMLTag.class.st b/src/Microdown-HTMLExporter/MicHTMLTag.class.st index 03ee30951..093fa2dba 100644 --- a/src/Microdown-HTMLExporter/MicHTMLTag.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLTag.class.st @@ -1,107 +1,19 @@ -" -HTML tags are used to delimit the start and end of elements in the markup. An HTML tag is composed of the name of the element, surrounded by angle brackets: - -``` -

    Content

    -``` - -A tag could be: - -- A start tag: Can have HTML attributes. -- An end tag: Has a slash after the opening angle bracket, to distinguish it from the start tag. - -Note that not all elements require an **end** tag, for example the `
    ` tag. -This class contains utility methods to append HTML tags to a stream, and to manage parameters: - -- #addArguments: -- #parameterAt:put: - -" Class { #name : 'MicHTMLTag', #superclass : 'MicHTMLBrush', - #instVars : [ - 'htmlArgumentsMap' - ], - #category : 'Microdown-HTMLExporter-Core', - #package : 'Microdown-HTMLExporter', - #tag : 'Core' + #category : 'Microdown - HTML', + #package : 'Microdown - HTML' } { #category : 'accessing' } -MicHTMLTag >> addArguments: aMicInlineBlockWithUrl [ - - aMicInlineBlockWithUrl hasArguments ifFalse: [ ^ self ]. - self htmlArgumentsMapDo: [ :argAssoc | - aMicInlineBlockWithUrl arguments - at: argAssoc key - ifPresent: [ :labelString | self parameterAt: argAssoc value put: labelString ] ] +MicHTMLTag >> name: aString [ + name := aString. + stream nextPut: $<; << aString ] { #category : 'accessing' } -MicHTMLTag >> close [ - "Close the receiver's tag. Note that this method does not check if the tag is opened" - - stream << $> -] - -{ #category : 'accessing' } -MicHTMLTag >> contents [ - "Answer a with receiver's output without modifying the stream position" - - ^ stream contents -] - -{ #category : 'accessing' } -MicHTMLTag >> htmlArgumentsMap [ - "Answer a of receiver's associations between Microdown attributes and HTML attributes" - - ^ htmlArgumentsMap - ifNil: [ htmlArgumentsMap := self initializeArgumentsMap ] -] - -{ #category : 'accessing' } -MicHTMLTag >> htmlArgumentsMapDo: aFullBlockClosure [ - "Iterate over the receiver's mapping of Microdown to HTML attributes" - - self htmlArgumentsMap associationsDo: aFullBlockClosure -] - -{ #category : 'initialization' } -MicHTMLTag >> initializeArgumentsMap [ - - ^ Dictionary new - at: 'label' put: 'class'; - at: 'width' put: 'width'; - at: 'rel' put: 'rel'; - at: 'target' put: 'target'; - yourself -] - -{ #category : 'accessing' } -MicHTMLTag >> parameterAt: keyString put: valueString [ - "Write a parameter named keyString with valueString as value. Note that this method does not close the tag" - - stream - space; - << keyString; - << '="'; - << valueString; - << '"' -] - -{ #category : 'printing' } -MicHTMLTag >> printOn: aStream [ - - super printOn: aStream. - aStream << ' name: ['. - name - ifNotNil: [ aStream << name ]. - aStream << '] contents: ('. - stream - ifNotNil: [ aStream << stream contents ]. - aStream - << ')' +MicHTMLTag >> parameterAt: aString put: anotherString [ + stream space. stream << aString << '="' << anotherString << '"' ] { #category : 'accessing' } diff --git a/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st b/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st index be9cd3645..759a1c30b 100644 --- a/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st @@ -534,17 +534,26 @@ MicHTMLVisitor >> visitQuote: aQuote [ ] { #category : 'visiting - html extensions' } -MicHTMLVisitor >> visitRaw: aMicRaw [ - "Raw should be raw and it should NOT go the next line or whatever" - "Pay attention aMicRaw is not a paragraph but an inline element." +MicHTMLVisitor >> visitRaw: aParagraph [ - canvas raw: aMicRaw bodyString + canvas raw: aParagraph bodyString. + canvas newLine ] { #category : 'visiting - html extensions' } MicHTMLVisitor >> visitRawParagraph: aParagraph [ - canvas raw: aParagraph body + canvas + newLine; + raw: '<' , aParagraph label. + aParagraph hasArguments + ifTrue: [ canvas space; raw: aParagraph argumentString ]. + canvas raw: '>' . + + aParagraph children do: [ :each | each accept: self ]. + canvas + newLine; + raw: aParagraph lineStopMarkup. ] { #category : 'templating ping pong' } diff --git a/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st b/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st deleted file mode 100644 index c276187f4..000000000 --- a/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st +++ /dev/null @@ -1,17 +0,0 @@ -Extension { #name : 'MicQuoteBlockTest' } - -{ #category : '*Microdown-Pillar-Tests' } -MicQuoteBlockTest >> testQuoteBlockAsPilar [ - | source root pillarNode | - source := '> text1 -> text2'. - root := parser parse: source. - self assert: root children size equals: 1. - pillarNode := root children first asPillar. - self assert: pillarNode class equals: PRPreformatted. - self - assert: pillarNode text - equals: - 'text1 -text2' -] diff --git a/src/Microdown-RichTextComposer/MicParser.extension.st b/src/Microdown-RichTextComposer/MicrodownParser.extension.st similarity index 53% rename from src/Microdown-RichTextComposer/MicParser.extension.st rename to src/Microdown-RichTextComposer/MicrodownParser.extension.st index 26bb65e39..ca23a5a9b 100644 --- a/src/Microdown-RichTextComposer/MicParser.extension.st +++ b/src/Microdown-RichTextComposer/MicrodownParser.extension.st @@ -1,6 +1,6 @@ -Extension { #name : 'MicParser' } +Extension { #name : 'MicrodownParser' } { #category : '*Microdown-RichTextComposer' } -MicParser class >> convertToRichText: aString [ +MicrodownParser class >> convertToRichText: aString [ ^ MicRichTextComposer new visit: (self new parse: aString) ] diff --git a/src/Microdown-Rules/ManifestMicrodownRules.class.st b/src/Microdown-Rules/ManifestMicrodownRules.class.st new file mode 100644 index 000000000..d76f96730 --- /dev/null +++ b/src/Microdown-Rules/ManifestMicrodownRules.class.st @@ -0,0 +1,24 @@ +" +Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser +" +Class { + #name : 'ManifestMicrodownRules', + #superclass : 'PackageManifest', + #category : 'Microdown-Rules-Manifest', + #package : 'Microdown-Rules', + #tag : 'Manifest' +} + +{ #category : 'code-critics' } +ManifestMicrodownRules class >> ruleNotEliminationRuleV1FalsePositive [ + + + ^ #(#(#(#RGMethodDefinition #(#MicRuleUppercaseHeaderStrategy #checkRest:of: #false)) #'2026-03-05T07:42:00.35682+03:00') ) +] + +{ #category : 'code-critics' } +ManifestMicrodownRules class >> ruleUnclassifiedMethodsRuleV1FalsePositive [ + + + ^ #(#(#(#RGMethodDefinition #(#MicRuleUppercaseHeaderStrategy #checkRest:of: #false)) #'2026-03-05T07:41:58.207851+03:00') ) +] diff --git a/src/Microdown-Rules/MicEnglishTypoChecker.class.st b/src/Microdown-Rules/MicEnglishTypoChecker.class.st index e4c387661..f81c5fbfd 100644 --- a/src/Microdown-Rules/MicEnglishTypoChecker.class.st +++ b/src/Microdown-Rules/MicEnglishTypoChecker.class.st @@ -16,7 +16,7 @@ MicEnglishTypoChecker >> addResultFor: anElement message: aMessage [ ). ] -{ #category : 'adding' } +{ #category : 'visiting - inline elements' } MicEnglishTypoChecker >> checkString: aString for: anElement [ | checks | aString ifNil: [ ^self ]. @@ -45,21 +45,19 @@ checks := { ]. ] -{ #category : 'adding' } +{ #category : 'visiting - inline elements' } MicEnglishTypoChecker >> visitCode: aCodeBlock [ - aCodeBlock caption ifNil: [ ^self ]. self checkString: aCodeBlock caption for: aCodeBlock. ] -{ #category : 'adding' } +{ #category : 'visiting - inline elements' } MicEnglishTypoChecker >> visitMonospace: aCodeBlock [ ^ self ] -{ #category : 'adding' } +{ #category : 'visiting - inline elements' } MicEnglishTypoChecker >> visitText: aText [ - self checkString: aText bodyString for: aText. ] diff --git a/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st b/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st index e1b02ab79..5529b91a2 100644 --- a/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st +++ b/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st @@ -66,41 +66,41 @@ MicEnglishTypoCheckerTest >> setUp [ ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testCorrectFile [ checker checkProject: fileSystem / 'correct.md'. self assert: checker isOkay. ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testMonospaceIgnored [ checker checkProject: fileSystem / 'mono.md'. self assert: checker isOkay. ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testNoSpaceAfterColon [ checker checkProject: fileSystem / 'noSpaceAfterColon.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong:example." in file/noSpaceAfterColon.md contains a mistake: There is no space after :' + assert: checker results first explanation + equals: 'Text: "Wrong:example." in file/noSpaceAfterColon.md contains a mistake: There is no space after :' ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testNoSpaceAfterComma [ checker checkProject: fileSystem / 'noSpaceAfterComma.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong,example." in file/noSpaceAfterComma.md contains a mistake: There is no space after ,' + assert: checker results first explanation + equals: 'Text: "Wrong,example." in file/noSpaceAfterComma.md contains a mistake: There is no space after ,' ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testSpaceBeforeColon [ checker checkProject: fileSystem / 'spaceBeforeColon.md'. @@ -110,12 +110,12 @@ MicEnglishTypoCheckerTest >> testSpaceBeforeColon [ equals: 'Text: "This is wrong : example." in file/spaceBeforeColon.md contains a mistake: There is a space before :' ] -{ #category : 'tests' } +{ #category : 'tests - english typos' } MicEnglishTypoCheckerTest >> testSpaceBeforeComma [ checker checkProject: fileSystem / 'spaceBeforeComma.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong ,example." in file/spaceBeforeComma.md contains a mistake: There is a space before ,' + assert: checker results first explanation + equals: 'Text: "Wrong ,example." in file/spaceBeforeComma.md contains a mistake: There is a space before ,' ] diff --git a/src/Microdown-Rules/MicRuleHeaderChecker.class.st b/src/Microdown-Rules/MicRuleHeaderChecker.class.st index b83f4342f..2b1a8dbfe 100644 --- a/src/Microdown-Rules/MicRuleHeaderChecker.class.st +++ b/src/Microdown-Rules/MicRuleHeaderChecker.class.st @@ -35,7 +35,6 @@ MicRuleHeaderChecker >> checkRest: words of: aMicHeader [ e.g., 'Today and tomorrow: The days' and not 'Today and tomorrow: the days' " - strategy checkRest: words of: aMicHeader ] @@ -52,7 +51,7 @@ MicRuleHeaderChecker >> initialize [ 'upon' 'when' 'with' 'yet' ) do: [ :each | self addUncapitalized: each ]. ] -{ #category : 'asserting' } +{ #category : 'as yet unclassified' } MicRuleHeaderChecker >> shouldCapitalizedWord: aString [ | normalized | normalized := (aString select: [ :c | c isLetter ]) asLowercase. @@ -74,7 +73,7 @@ MicRuleHeaderChecker >> verifyUppercase [ strategy checker: self ] -{ #category : 'asserting' } +{ #category : 'visiting' } MicRuleHeaderChecker >> visitHeader: aMicHeader [ | words | diff --git a/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st b/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st index b09573351..53db8448d 100644 --- a/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st +++ b/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st @@ -6,24 +6,16 @@ Class { #tag : 'Capitalization' } -{ #category : 'visiting' } +{ #category : 'as yet unclassified' } MicRuleUppercaseHeaderStrategy >> checkRest: words of: aMicHeader [ - - "In the future we should manage that if a word finishes by : the following one must be captitalized - e.g., 'Today and tomorrow: The days' and not - 'Today and tomorrow: the days' - " + | realWords | self checkColonCapitalizationIn: words of: aMicHeader. realWords := words allButFirst select: [ :each | | normalized | normalized := (each select: [:c | c isLetter]) asLowercase. checker shouldCapitalizedWord: normalized ]. - "note that we should verify two points not yet done - Point 1 'To' in input should be reported as an error in both uppercase and lowercase - - Point 2 'to' should not be generate an error in uppercase setup. - " + realWords do: [ :word | | cleaned | diff --git a/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st b/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st index 6b9346154..41fd6657f 100644 --- a/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st +++ b/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st @@ -16,9 +16,9 @@ MicSpaceBeforeDiacriticsResult >> detail: aString [ { #category : 'accessing' } MicSpaceBeforeDiacriticsResult >> explanation [ +| text | - | text | - text := micElement bodyString ifNil: ['']. +text := micElement bodyString ifNil: ['']. ^ 'Text: "' , text , '" in file' , fileReference fullName , ' contains a mistake: ', detail ] diff --git a/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st b/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st index 615da0525..341bb8d49 100644 --- a/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st +++ b/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st @@ -17,7 +17,7 @@ Class { MicUseofWrongVocabularyResult >> explanation [ | wrongWord goodWord | - wrongWord := patternPair key allButFirst allButLast. + wrongWord := patternPair key. goodWord := patternPair value. ^ 'Text: "' , micElement bodyString , '" in file' , fileReference fullName , ' contains a mistake: [' , wrongWord , '] should not be used. We should use ' , goodWord diff --git a/src/Microdown-Rules/MicVocabularyChecker.class.st b/src/Microdown-Rules/MicVocabularyChecker.class.st index fa0343caf..203bb6193 100644 --- a/src/Microdown-Rules/MicVocabularyChecker.class.st +++ b/src/Microdown-Rules/MicVocabularyChecker.class.st @@ -11,20 +11,30 @@ Class { { #category : 'visiting - inline elements' } MicVocabularyChecker >> checkText: aMicText [ - - patternPairs do: [ :patternPair | - "note that match: ignore upper/lower case differences." - (patternPair key match: aMicText bodyString ) ifTrue: [ - results add: (MicUseofWrongVocabularyResult new - micElement: aMicText; - inFile: aMicText fromFile; - patternPair: patternPair) ] ] + | text sortedPairs | + + text := aMicText bodyString asLowercase. + sortedPairs := patternPairs asSortedCollection: [ :a :b | a key size > b key size ]. + sortedPairs do: [ :patternPair | + | pattern | + pattern := patternPair key asLowercase. + (text includesSubstring: pattern ) ifTrue: [ + results add: ( + MicUseofWrongVocabularyResult new + micElement: aMicText; + inFile: aMicText fromFile; + patternPair: patternPair; + yourself + ). + text := text copyReplaceAll: pattern with: '' + ]. + ]. ] { #category : 'visiting - inline elements' } MicVocabularyChecker >> pairs: aCollectionOfPairs [ - patternPairs := aCollectionOfPairs collect: [ :each | ('*' , each key , '*') -> each value ] + patternPairs := aCollectionOfPairs ] diff --git a/src/Microdown-Rules/MicVocabularyCheckerTest.class.st b/src/Microdown-Rules/MicVocabularyCheckerTest.class.st index bf9b8d4b9..2c2ccb6a9 100644 --- a/src/Microdown-Rules/MicVocabularyCheckerTest.class.st +++ b/src/Microdown-Rules/MicVocabularyCheckerTest.class.st @@ -117,7 +117,7 @@ MicVocabularyCheckerTest >> setUp [ ('allows to' -> 'allows one to')}. ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsAllowToMistake [ checker checkProject: fileSystem / 'mistakeAllowTo.md'. @@ -129,7 +129,7 @@ MicVocabularyCheckerTest >> testCheckerFindsAllowToMistake [ 'Text: "We allow to edit the file." in file/mistakeAllowTo.md contains a mistake: [allow to] should not be used. We should use allow one to' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsAllowedToMistake [ checker checkProject: fileSystem / 'mistakeAllowedTo.md'. self deny: checker isOkay. @@ -140,10 +140,9 @@ checker checkProject: fileSystem / 'mistakeAllowedTo.md'. 'Text: "You are allowed to enter." in file/mistakeAllowedTo.md contains a mistake: [allowed to] should not be used. We should use allowed one to' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsAllowsToMistake [ - - checker checkProject: fileSystem / 'mistakeAllowsTo.md'. +checker checkProject: fileSystem / 'mistakeAllowsTo.md'. self deny: checker isOkay. self @@ -152,7 +151,7 @@ MicVocabularyCheckerTest >> testCheckerFindsAllowsToMistake [ 'Text: "The system allows to change settings." in file/mistakeAllowsTo.md contains a mistake: [allows to] should not be used. We should use allows one to' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsBehaviourMistake [ checker checkProject: fileSystem / 'mistakeBehaviour.md'. @@ -164,7 +163,7 @@ MicVocabularyCheckerTest >> testCheckerFindsBehaviourMistake [ 'Text: "Pharo has interesting behaviour." in file/mistakeBehaviour.md contains a mistake: [behaviour] should not be used. We should use behavior' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsCentreMistake [ checker checkProject: fileSystem / 'mistakeCentre.md'. @@ -176,7 +175,7 @@ MicVocabularyCheckerTest >> testCheckerFindsCentreMistake [ 'Text: "The centre of the universe." in file/mistakeCentre.md contains a mistake: [centre] should not be used. We should use center' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsColourMistake [ checker checkProject: fileSystem / 'mistakeColour.md'. @@ -188,19 +187,19 @@ MicVocabularyCheckerTest >> testCheckerFindsColourMistake [ 'Text: "The colour of the sky is blue." in file/mistakeColour.md contains a mistake: [colour] should not be used. We should use color' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsColoursMistake [ checker checkProject: fileSystem / 'mistakeColours.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals:'Text: "Colours are beautiful." in file/mistakeColours.md contains a mistake: [colour] should not be used. We should use color' - + assert: checker results first explanation + equals: + 'Text: "Colours are beautiful." in file/mistakeColours.md contains a mistake: [colours] should not be used. We should use colors' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInFigure [ checker checkProject: fileSystem / 'mistakeInFigure.md'. @@ -213,7 +212,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInFigure [ 'Text: "sub-presenter is not correct even in caption" in file/mistakeInFigure.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInHeader [ checker checkProject: fileSystem / 'mistakeInHeader.md'. @@ -225,7 +224,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInHeader [ 'Text: "sub-presenter is not correct." in file/mistakeInHeader.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInParagraph [ checker checkProject: fileSystem / 'mistakeInParagraph.md'. @@ -237,7 +236,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInParagraph [ 'Text: "Spec uses a sub presenter. And there is a mistake in the previous sentence." in file/mistakeInParagraph.md contains a mistake: [sub presenter] should not be used. We should use subpresenter' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInUppercasedText [ checker checkProject: fileSystem / 'mistakeInUppercase.md'. @@ -249,7 +248,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInUppercasedText [ 'Text: "sub-Presenter is not correct. The checker should pay attention that the text may use a different case than the pattern." in file/mistakeInUppercase.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'tests' } +{ #category : 'running' } MicVocabularyCheckerTest >> testCheckerFindsVisualiseMistake [ checker checkProject: fileSystem / 'mistakeVisualise.md'. diff --git a/src/Microdown-Slide-Utils/MicSlideConverter.class.st b/src/Microdown-Slide-Utils/MicSlideConverter.class.st deleted file mode 100644 index 3cc76331e..000000000 --- a/src/Microdown-Slide-Utils/MicSlideConverter.class.st +++ /dev/null @@ -1,24 +0,0 @@ -Class { - #name : 'MicSlideConverter', - #superclass : 'PRVisitor', - #instVars : [ - 'mic' - ], - #category : 'Microdown-Slide-Utils', - #package : 'Microdown-Slide-Utils' -} - -{ #category : 'visiting' } -MicSlideConverter >> start: aPRDocument [ - mic := MicRootBlock new. - super start: aPRDocument. - ^ mic - - -] - -{ #category : 'visiting' } -MicSlideConverter >> visitSlide: aSlide [ - - -] diff --git a/src/Microdown-Tests/MicAnchorLinkerTest.class.st b/src/Microdown-Tests/MicAnchorLinkerTest.class.st index b9de317ed..2ec4da6e7 100644 --- a/src/Microdown-Tests/MicAnchorLinkerTest.class.st +++ b/src/Microdown-Tests/MicAnchorLinkerTest.class.st @@ -10,7 +10,7 @@ Class { MicAnchorLinkerTest >> testAnchorIsAttachedToItsTarget [ | linker doc | linker := MicAnchorLinker new. - doc := MicParser parse: ' + doc := MicrodownParser parse: ' this is a sentence #Header1 @@ -26,7 +26,7 @@ this is a sentence MicAnchorLinkerTest >> testAnchorIsAttachedToItsTarget2 [ | linker doc | linker := MicAnchorLinker new. - doc := MicParser parse: ' + doc := MicrodownParser parse: ' this is a sentence #Header1 @@ -44,7 +44,7 @@ this is a sentence MicAnchorLinkerTest >> testHeaderLast [ | linker doc | linker := MicAnchorLinker new. - doc := MicParser parse: ' + doc := MicrodownParser parse: ' this is a sentence #Header1'. diff --git a/src/Microdown-Tests/MicElementTest.class.st b/src/Microdown-Tests/MicElementTest.class.st index 7d1db4c34..735367040 100644 --- a/src/Microdown-Tests/MicElementTest.class.st +++ b/src/Microdown-Tests/MicElementTest.class.st @@ -28,7 +28,7 @@ MicElementTest >> parserClass [ "This references to MicrodownParser is needed for the test. Replacing it by Microdown does not work." - ^ MicParser + ^ MicrodownParser ] { #category : 'running' } diff --git a/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st b/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st deleted file mode 100644 index c11cd6373..000000000 --- a/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st +++ /dev/null @@ -1,176 +0,0 @@ -Class { - #name : 'MicHTMLTagParagraphBlockTest', - #superclass : 'MicBlockTest', - #category : 'Microdown-Tests-Extensions', - #package : 'Microdown-Tests', - #tag : 'Extensions' -} - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> subjectClass [ - ^ MicHTMLTagParagraphBlock -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testASimpleDivWithClass [ - | mic | - mic := (parser parse: '
    - -# Contributing to Pharo - -In a giving mood? There are many ways to get involved! - -
    -') children first. - self assert: mic class equals: MicHTMLTagParagraphBlock. - self assert: mic label equals: 'div'. - self assert: mic children first class equals: MicHeaderBlock. - self assert: mic children second class equals: MicParagraphBlock - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testASimpleRawParagraph [ - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicHTMLTagParagraphBlock. - self assert: mic label equals: 'title'. - self assert: mic children first class equals: MicParagraphBlock - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testNotASimpleRawParagraphBecauseNotDeclaredTag [ - - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicParagraphBlock. - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testNotASimpleRawParagraphNoSpaceInFrontToClose [ - - | children | - children := (parser parse: ' -No idea what it is :) - - -And after we get a paragraph') children. - self assert: children size equals: 1. - "so far we eat as much as we can and put everything in the raw" - - - self assert: children first class equals: MicHTMLTagParagraphBlock. - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testParagrapWithP [ - | mic | - mic := (parser parse: ' -

    -This is a paragraph started normally with

    -

    -') children first. - self assert: mic class equals: MicHTMLTagParagraphBlock. - self assert: mic label equals: 'p'. - self assert: mic children first class equals: MicParagraphBlock - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testRawParagraphCanBeNested [ - | children | - children := (parser parse: ' -No idea what it is :) - -<button> -hkhkjhkj -</button> -') children. - self assert: children size equals: 1. - self assert: children first class equals: MicHTMLTagParagraphBlock. - self assert: children first label equals: 'title'. - self assert: children first children size equals: 2. - self assert: children first children first class equals: MicParagraphBlock. - self assert: children first children second label equals: 'button'. - self assert: children first children second class equals: MicHTMLTagParagraphBlock. -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testRawParagraphForP [ - | children | - children := (parser parse: '

    -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicHTMLTagParagraphBlock. - self assert: children first label equals: 'p'. - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpace [ - | children | - children := (parser parse: '

    WeDoNotLikeSpace -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicHTMLTagParagraphBlock. - self assert: children first label equals: 'p'. - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpace2 [ - | children newLine | - newLine := Character cr asString. - children := (MicParser new parse: '
    ', newLine, -'

    ', newLine, -'We use the <div> tag to group two paragraphs for applying a background to the text, and to add color to this', newLine, -'', newLine, -'word', newLine, -'', newLine, -'we place it within <span> tag.', -'

    ', newLine, -'

    ', newLine, -'Pay attention, that the <div> tag is a block-level element, so a line break is placed before and after it.', newLine, -'

    ', newLine, -'
    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicHTMLTagParagraphBlock. - self assert: children first label equals: 'div'. - self assert: children first children first class equals: MicHTMLTagParagraphBlock. - self assert: children first children first label equals: 'p'. - - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpaceAndAttributes [ - | children | - children := (parser parse: '

    WeDoNotLikeSpace -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicHTMLTagParagraphBlock. - self assert: children first label equals: 'p'. - -] - -{ #category : 'tests' } -MicHTMLTagParagraphBlockTest >> testThereIsNoValidationIfThetagStartWithAKnowHtml [ - "for the moment we only check the first couple of characters to start with. - So it may happens that we match the beginning of a tag." - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicParagraphBlock. - -] diff --git a/src/Microdown-Tests/MicParagraphBlockTest.class.st b/src/Microdown-Tests/MicParagraphBlockTest.class.st index 39a1a34c9..143018bb3 100644 --- a/src/Microdown-Tests/MicParagraphBlockTest.class.st +++ b/src/Microdown-Tests/MicParagraphBlockTest.class.st @@ -37,7 +37,7 @@ MicParagraphBlockTest >> testASimpleRawParagaaph [ mic := (parser parse: ' No idea what it is :) ') children first. - self assert: mic class equals: MicHTMLTagParagraphBlock. + self assert: mic class equals: MicRawParagraphBlock. self assert: mic label equals: 'title'. ] diff --git a/src/Microdown-Tests/MicParserTest.class.st b/src/Microdown-Tests/MicParserTest.class.st index 1890b12c8..4f566b2b8 100644 --- a/src/Microdown-Tests/MicParserTest.class.st +++ b/src/Microdown-Tests/MicParserTest.class.st @@ -16,7 +16,7 @@ Class { MicParserTest >> setUp [ super setUp. - parser := MicParser new. + parser := MicrodownParser new. ] { #category : 'tests' } diff --git a/src/Microdown-Tests/MicRawParagraphBlockTest.class.st b/src/Microdown-Tests/MicRawParagraphBlockTest.class.st index 54f455fd9..815c40540 100644 --- a/src/Microdown-Tests/MicRawParagraphBlockTest.class.st +++ b/src/Microdown-Tests/MicRawParagraphBlockTest.class.st @@ -1,9 +1,9 @@ Class { #name : 'MicRawParagraphBlockTest', #superclass : 'MicBlockTest', - #category : 'Microdown-Tests-Parser', + #category : 'Microdown-Tests-Extensions', #package : 'Microdown-Tests', - #tag : 'Parser' + #tag : 'Extensions' } { #category : 'tests' } @@ -12,77 +12,165 @@ MicRawParagraphBlockTest >> subjectClass [ ] { #category : 'tests' } -MicRawParagraphBlockTest >> testFirstLineAfterMarkupShouldBeIgnored [ - "contents starts on the following line of the {{{" - | root | - root := parser parse: '{{{ blabla -**Hello is ... cut for real +MicRawParagraphBlockTest >> testASimpleDivWithClass [ + | mic | + mic := (parser parse: '
    -a paragraph on two lines** -}}}'. +# Contributing to Pharo - self assert: root children first bodyString equals: '**Hello is ... cut for real +In a giving mood? There are many ways to get involved! -a paragraph on two lines**'. +
    +') children first. + self assert: mic class equals: MicRawParagraphBlock. + self assert: mic label equals: 'div'. + self assert: mic children first class equals: MicHeaderBlock. + self assert: mic children second class equals: MicParagraphBlock + +] + +{ #category : 'tests' } +MicRawParagraphBlockTest >> testASimpleRawParagraph [ + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicRawParagraphBlock. + self assert: mic label equals: 'title'. + self assert: mic children first class equals: MicParagraphBlock + +] + +{ #category : 'tests' } +MicRawParagraphBlockTest >> testNotASimpleRawParagraphBecauseNotDeclaredTag [ + + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicParagraphBlock. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testSimpleParagraphWithMarkupShouldBeTakenAsIs [ - " - {{{ - Hello - }}} - " - - | root | - root := parser parse: '{{{ -**Hello is ... cut for real -a paragraph on two lines** -}}}'. - - self assert: root children size equals: 1. - self assert: root children first class equals: MicRawParagraphBlock. - self assert: root children first bodyString equals: '**Hello is ... cut for real -a paragraph on two lines**'. +MicRawParagraphBlockTest >> testNotASimpleRawParagraphNoSpaceInFrontToClose [ + + | children | + children := (parser parse: ' +No idea what it is :) + + +And after we get a paragraph') children. + self assert: children size equals: 1. + "so far we eat as much as we can and put everything in the raw" + + + self assert: children first class equals: MicRawParagraphBlock. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testWithEmptyLine [ - " - {{{ - Hello - }}} - " +MicRawParagraphBlockTest >> testParagrapWithP [ + | mic | + mic := (parser parse: ' +

    +This is a paragraph started normally with

    +

    +') children first. + self assert: mic class equals: MicRawParagraphBlock. + self assert: mic label equals: 'p'. + self assert: mic children first class equals: MicParagraphBlock - | root | - root := parser parse: '{{{ -**Hello is ... cut for real +] -a paragraph on two lines** -}}}'. +{ #category : 'tests' } +MicRawParagraphBlockTest >> testRawParagraphCanBeNested [ + | children | + children := (parser parse: ' +No idea what it is :) + +<button> +hkhkjhkj +</button> +') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'title'. + self assert: children first children size equals: 2. + self assert: children first children first class equals: MicParagraphBlock. + self assert: children first children second label equals: 'button'. + self assert: children first children second class equals: MicRawParagraphBlock. +] + +{ #category : 'tests' } +MicRawParagraphBlockTest >> testRawParagraphForP [ + | children | + children := (parser parse: '

    +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'p'. + +] + +{ #category : 'tests' } +MicRawParagraphBlockTest >> testRawParagraphForPNoSpace [ + | children | + children := (parser parse: '

    WeDoNotLikeSpace +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'p'. + +] - self assert: root children size equals: 1. - self assert: root children first bodyString equals: '**Hello is ... cut for real +{ #category : 'tests' } +MicRawParagraphBlockTest >> testRawParagraphForPNoSpace2 [ + | children newLine | + newLine := Character cr asString. + children := (MicrodownParser new parse: '
    ', newLine, +'

    ', newLine, +'We use the <div> tag to group two paragraphs for applying a background to the text, and to add color to this', newLine, +'', newLine, +'word', newLine, +'', newLine, +'we place it within <span> tag.', +'

    ', newLine, +'

    ', newLine, +'Pay attention, that the <div> tag is a block-level element, so a line break is placed before and after it.', newLine, +'

    ', newLine, +'
    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'div'. + self assert: children first children first class equals: MicRawParagraphBlock. + self assert: children first children first label equals: 'p'. + + +] -a paragraph on two lines**'. +{ #category : 'tests' } +MicRawParagraphBlockTest >> testRawParagraphForPNoSpaceAndAttributes [ + | children | + children := (parser parse: '

    WeDoNotLikeSpace +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'p'. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testWithHTMLTag [ - - | root | - root := parser parse: '{{{ -**Hello is ... cut for real -https://pharo.org -a paragraph on two lines** -}}}'. - - self assert: root children size equals: 1. - self assert: root children first bodyString equals: '**Hello is ... cut for real -https://pharo.org -a paragraph on two lines**' +MicRawParagraphBlockTest >> testThereIsNoValidationIfThetagStartWithAKnowHtml [ + "for the moment we only check the first couple of characters to start with. + So it may happens that we match the beginning of a tag." + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicParagraphBlock. ] diff --git a/src/Microdown-Tests/MicrodownParserTest.class.st b/src/Microdown-Tests/MicrodownParserTest.class.st index 54a0a9afd..d8491ffae 100644 --- a/src/Microdown-Tests/MicrodownParserTest.class.st +++ b/src/Microdown-Tests/MicrodownParserTest.class.st @@ -15,7 +15,7 @@ Class { { #category : 'running' } MicrodownParserTest >> parser [ "for now this reference is needed and cannot be changed into Microdown" - ^ MicParser new + ^ MicrodownParser new ] { #category : 'running' } diff --git a/src/Microdown-Tests/MicrodownTest.class.st b/src/Microdown-Tests/MicrodownTest.class.st index 6fa70625e..f4a4e3e28 100644 --- a/src/Microdown-Tests/MicrodownTest.class.st +++ b/src/Microdown-Tests/MicrodownTest.class.st @@ -93,7 +93,7 @@ MicrodownTest >> testAddedAsChildWhenUsingParent [ MicrodownTest >> testExtensionArgumentsAreNotReified [ | doc slide | - doc := MicParser parse: ''. + doc := MicrodownParser parse: ''. slide := doc children first. self assert: slide class equals: MicSlideBlock. self assert: slide title equals: 'A title'. @@ -105,7 +105,7 @@ MicrodownTest >> testFigureLabelAreNotReified [ "but they should" | doc fig | - doc := MicParser new parse: '![A caption.](figures/f.png width=120&label=fig)'. + doc := MicrodownParser new parse: '![A caption.](figures/f.png width=120&label=fig)'. fig := doc children first children first. self assert: fig class equals: MicFigureBlock. self assert: fig anchor equals: 'fig'. diff --git a/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st b/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st index 7f31dd293..0b669cced 100644 --- a/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st +++ b/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st @@ -1,8 +1,13 @@ +" +This visitor is about to disappeared. It was created just to make sure that we can have asPillar expressed as a visitor. +Now this visitor is working on non expanded code blocks. +" Class { #name : 'MicMicrodownObjectToPillarObjectConverter', - #superclass : 'Object', - #category : 'Microdown-ToHelpMacrodownLoading', - #package : 'Microdown-ToHelpMacrodownLoading' + #superclass : 'MicrodownVisitor', + #category : 'Microdown-Pillar-Visitor', + #package : 'Microdown-Pillar', + #tag : 'Visitor' } { #category : 'formatting' } @@ -28,18 +33,12 @@ MicMicrodownObjectToPillarObjectConverter >> urlBlock: aMicUrlBlock [ aMicUrlBlock reference uri query: queries. ^ aMicUrlBlock associatedPillarClass new setChildren: - (aMicUrlBlock children collect: [ :n | + ((aMicUrlBlock inlineParse: aMicUrlBlock substring) collect: [ :n | n accept: self ]); reference: urlString; yourself ] -{ #category : 'visiting' } -MicMicrodownObjectToPillarObjectConverter >> visit: aNode [ - - aNode accept: self -] - { #category : 'visiting' } MicMicrodownObjectToPillarObjectConverter >> visitAnchor: aMicAnchorBlock [ ^ PRAnchor new name: aMicAnchorBlock label; yourself @@ -129,9 +128,9 @@ MicMicrodownObjectToPillarObjectConverter >> visitEnvironment: aMicEnvironmentBl MicMicrodownObjectToPillarObjectConverter >> visitFigure: aMicFigureBlock [ | aPRFigure | - aPRFigure := self urlBlock: aMicFigureBlock. + aPRFigure := aMicFigureBlock associatedPillarClass new. ^ aPRFigure - label: (aMicFigureBlock children collect: [:each | self visit: each ]); + label: aMicFigureBlock altText; parameters: aMicFigureBlock arguments; yourself ] @@ -171,8 +170,7 @@ MicMicrodownObjectToPillarObjectConverter >> visitLink: aMicLink [ ^ aMicLink associatedPillarClass new setChildren: - (aMicLink children collect: [ :n | - n accept: self ]); + (aMicLink children collect: [ :n | n accept: self ]); reference: aMicLink url asString; yourself ] @@ -245,13 +243,6 @@ MicMicrodownObjectToPillarObjectConverter >> visitRaw: aMicRaw [ yourself ] -{ #category : 'visiting' } -MicMicrodownObjectToPillarObjectConverter >> visitRawParagraph: aMicParagraphBlock [ - ^ PRParagraph new - setChildren: (aMicParagraphBlock pillarFromString: aMicParagraphBlock text); - yourself -] - { #category : 'visiting' } MicMicrodownObjectToPillarObjectConverter >> visitRoot: aMicRootBlock [ ^ PRDocument new diff --git a/src/Microdown/MicAbstractBlock.class.st b/src/Microdown/MicAbstractBlock.class.st index 752217379..8adec23f2 100644 --- a/src/Microdown/MicAbstractBlock.class.st +++ b/src/Microdown/MicAbstractBlock.class.st @@ -154,7 +154,7 @@ MicAbstractBlock >> parser [ { #category : 'private' } MicAbstractBlock >> parserClass [ - ^ MicParser + ^ MicrodownParser ] { #category : 'adding' } diff --git a/src/Microdown/MicHTMLTagParagraphBlock.class.st b/src/Microdown/MicHTMLTagParagraphBlock.class.st deleted file mode 100644 index 9c15583bf..000000000 --- a/src/Microdown/MicHTMLTagParagraphBlock.class.st +++ /dev/null @@ -1,118 +0,0 @@ -" -I'm a block level that handle html tag. -For this to work tag should start at the beginning of the line. -I can contain nested tags and my body can contain microdown elements that are then interpreted. - - -``` -| mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicRawParagraphBlock. - self assert: mic label equals: 'title'. - self assert: mic children first class equals: MicParagraphBlock -``` - -``` -testRawParagraphCanBeNested - | children | - children := (parser parse: ' -No idea what it is :) - -<button> -hkhkjhkj -</button> -') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'title'. - self assert: children first children size equals: 2. - self assert: children first children first class equals: MicParagraphBlock. - self assert: children first children second label equals: 'button'. - self assert: children first children second class equals: MicRawParagraphBlock. -``` -" -Class { - #name : 'MicHTMLTagParagraphBlock', - #superclass : 'MicNestedMarkupBlock', - #instVars : [ - 'label', - 'argumentsString' - ], - #category : 'Microdown-Model', - #package : 'Microdown', - #tag : 'Model' -} - -{ #category : 'public' } -MicHTMLTagParagraphBlock class >> alternateBlockClassFor: aLine [ - "We do not want to have extention for MicRawParaphTag" - - ^ self -] - -{ #category : 'visiting' } -MicHTMLTagParagraphBlock >> accept: aVisitor [ - - aVisitor visitHTMLTagParagraph: self -] - -{ #category : 'activation' } -MicHTMLTagParagraphBlock >> argumentString [ - ^ argumentsString -] - -{ #category : 'handle' } -MicHTMLTagParagraphBlock >> extractFirstLineFrom: aLine [ - "we got < tag ...x > foo bar or < tag ...x and we return tag ...x " - - | splits | - firstLine := aLine copyFrom: self lineStartMarkup size to: aLine size. - firstLine := firstLine trimBoth. - firstLine := firstLine copyFrom: 1 to: firstLine size -1. - - splits := firstLine splitOnFirst: $>. - splits second isNotEmpty - ifTrue: [ "here we p>Blo and we discard Blo" - label := splits first ]. - - "now we may have

    > hasArguments [ - ^ argumentsString isNotEmpty -] - -{ #category : 'initialization' } -MicHTMLTagParagraphBlock >> initialize [ - - super initialize. - argumentsString := '' -] - -{ #category : 'accessing' } -MicHTMLTagParagraphBlock >> label [ - ^ label -] - -{ #category : 'markups' } -MicHTMLTagParagraphBlock >> lineStopMarkup [ - - ^ '' -] - -{ #category : 'printing' } -MicHTMLTagParagraphBlock >> printOn: aStream [ - - super printOn: aStream. - aStream << '(' ; - << label ; - << ')' -] diff --git a/src/Microdown/MicParser.class.st b/src/Microdown/MicParser.class.st deleted file mode 100644 index 048f5e878..000000000 --- a/src/Microdown/MicParser.class.st +++ /dev/null @@ -1,378 +0,0 @@ -" -# Microdown Parser and Elements - -I'm a parser for Microdown, implemented by S. Ducasse, L. Dargaud and G. Polito (2020). -It is based on K. Osterbye's work on GitHubMarkdown. - -Microdown covers the basic syntax of Markdown, with great Pillar features. It's also more extensible. - -## MicroDown Syntax in a Nutshell! - -**Headers** (up to 6 levels) >>>> `# Header level 1` - -**Unordered list** -```text -- item a - on another line -- item b - - item b1 haaaaaaaaaa - - item b2 -- item c -``` -Produces: -- item a - on another line -- item b - - item b1 - - item b2 -- item c - -**Ordered list** -```text -1. item one - 1. subitem -2. item two -``` -Produces: -1. item one - 1. subitem -2. item two - - -**Codeblock with arguments** -```text - ```language=Pharo&caption=Beautiful&label=Fig1 - some piece of code - ``` -``` -Produces: -```language=Pharo&caption=Beautiful&label=Fig1 -some piece of code -``` - -**Anchor and its reference** >>>> `@anchor` and its reference in text with `*@anchor@*` - -**Math environment** (rendered with LaTex in Pharo) -- inline: $\cos(\theta_i+C_3)$ >>>> `$\cos(\theta_i+C_3)$` -- block: -```text -$$ -\cos(\theta_i+C_3) -$$ -``` -$$ -\cos(\theta_i+C_3) -$$ - -**Annotated paragraph** -```text -!!note Then you can write your note -on several lines, like a normal paragraph -``` - -**Quote** to indent some lines -```text -> This is -> A nice quote -``` - -**Environment** (called Annotation for inline) -- inline: `\` -- block: -```text - -``` - -**Metadata** as a block -```text -{ -type your metadata -} -``` - -**Table** -Support for tables with a simpler and more relaxed setup than GitHub. -A table is not forced to have a header definition. - -```text -| aaa | bbb -| ccc | ddd -``` -Produces: -| aaa | bbb -| ccc | ddd - -**Horizontal line** >>>> `***` alone on a line will define a separator in your text. -*** - -**Comments** don't appear after parsing >>>> `% A commented line` -% This can only be read in the original source, as you are doing. - -**Inline formats** -- **bold** >>>> `**bold**` -- _italic_ >>>> `_italic_` -- `monospace` aka inline code >>>> `\`monospace\`` - -_Note: inline escape character is the backslash: `\\`._ -_Note: text in monospace is analyzed for you to generate hypertext Pharo code objects! Isn't it so cool to write a great doc? It is! Click on them!_ -- `Point` for classes -- `Point class` for class side -- `Point>>#setX:setY:` for methods -- `#'Microdown-Tests'` for packages - -**Raw** for your other code (inline) >>>> `{{ some code }}` - -**Link** >>>> `[link's name](url|key1=value1&key2=value2)` - -**Figure** >>>> `![figure's name](url|key1=value1&key2=value2)` - -`![Pharo logo](https://files.pharo.org/media/logo/logo.png)` -produces - -![Pharo logo](https://files.pharo.org/media/logo/logo.png) - - -## Implementation Notes - -I follow the design mentioned in [https://github.github.com/gfm](https://github.github.com/gfm), in particular the parsing strategy in the appendix. - -In short, the strategy is that at any point in time, we can have a number of children of the root that are ""open"". The deepest in open in the tree is called ""current"". All the parents of current are open. - -When a new line is read, we do the following: - -1. Check if the new line can be consumed by current. - - as part of this, a child of current can be made which can consume the new line. For example, when consuming `\`\`\``, the root block node will create a new code block that will become current and consume the body of the `\`\`\`` element, then close. -2. If current cannot consume the new line, we close current, move current to its parent, and repeat 1. -3. The root node can consume anything, for instance by making new nodes to store the new line. -4. The root node is not closed until input is exhausted. - -I do not accept lazy definition. I do not accept three different ways to do the same thing. Except for bulleted lists, where `*` and `-` are accepted. - -The spec says: -```text --> document - -> block_quote - paragraph - ""Lorem ipsum dolor\nsit amet."" - -> list (type=bullet tight=true bullet_char=-) - list_item - paragraph - ""Qui *quodsi iracundia*"" - -> list_item - -> paragraph - ""aliquando id"" -``` -The current implementation does not create a paragraph in the list_item element. -" -Class { - #name : 'MicParser', - #superclass : 'Object', - #instVars : [ - 'current', - 'root', - 'dispatchTable' - ], - #pools : [ - 'MicSharedPool' - ], - #category : 'Microdown-Parser', - #package : 'Microdown', - #tag : 'Parser' -} - -{ #category : 'builder' } -MicParser class >> builder [ - ^ MicMicrodownTextualBuilder new -] - -{ #category : 'examples' } -MicParser class >> example [ - - - ^ self parse: self comment -] - -{ #category : 'instance creation' } -MicParser class >> parse: aString [ - - ^ self new parse: aString -] - -{ #category : 'node creation' } -MicParser >> blockStarterClassFrom: line [ - "return the class of a block which can start with line, or nil if none" - - line ifEmpty: [ ^ nil ]. - (self matchOrdered: line) - ifTrue: [ ^ self orderedListBlockClass ] - ifFalse: [ - "right now this is super ugly and slow - but this is to try. Once we understand and probably change the environment syntax for now it is , i and i> - This is working p src=800 - " - - (line includes: Character space) - ifTrue: [ HTMLTags includes: ((line allButFirst splitOnFirst: Character space) first) ] - ifFalse: [ HTMLTags includes: ((line allButFirst splitOnFirst: $>) first) ] - ]) - ifTrue: [ - "we have a rawparagraph block" - ^ MicHTMLTagParagraphBlock - ] - ifFalse: [ - - "the max significant length of a markup is 3" - (3 to: 1 by: -1) do: [:i | | prefix | - prefix := (line truncateTo: i). - "If we have a inline starter, it takes precedence" - (Delimiters includes: prefix) - ifTrue: [ ^ self delimiterBlockClassFor: prefix ]. - dispatchTable - at: prefix - ifPresent: [ :blockClass | ^ blockClass ] ] ] ]. - - ^ self nonMatchedBlockClassFor: line -] - -{ #category : 'accessing' } -MicParser >> builder [ - "return a little helper to build microdown correct expression" - - ^ self class builder -] - -{ #category : 'accessing' } -MicParser >> current [ - - ^ current -] - -{ #category : 'node creation' } -MicParser >> delimiterBlockClassFor: prefix [ - - ^ self paragraphBlockClass -] - -{ #category : 'parsing' } -MicParser >> handleLine: line [ - - "The logic is the following: - -if the current block can consume the line, it manages it and this potentially creates a new block that becomes the current one. - When the line is not consumed, the current block is closed and its parent becomes the current one and the process is called back to treat the line." - - (current canConsumeLine: (current massageLine: line)) - ifTrue: [ - current := current addLineAndReturnNextNode: line. - ^ current ] - ifFalse: [ current closeMe ]. - current := current parent. - self handleLine: line -] - -{ #category : 'initialization' } -MicParser >> initialize [ - - super initialize. - dispatchTable := Dictionary new. - dispatchTable at: AnchorMarkup put: MicAnchorBlock. - dispatchTable at: HeaderMarkup put: MicHeaderBlock. - dispatchTable at: CodeblockMarkup put: MicAbstractCodeBlock. - dispatchTable at: AnnotatedParagraphOpeningMarkup put: MicAnnotatedParagraphBlock. - dispatchTable at: AnnotatedParagraphBackwardCompatibleMarkup put: MicAnnotatedParagraphBlock. - dispatchTable at: CommentedLineMarkup put: MicCommentBlock. - dispatchTable at: HorizontalLineMarkup put: MicHorizontalLineBlock. - dispatchTable at: MathOpeningBlockMarkup put: MicMathBlock. - dispatchTable at: EnvironmentOpeningBlockMarkup put: MicEnvironmentBlock. - dispatchTable at: BlockLevelRawOpenerMarkup put: MicRawParagraphBlock. - dispatchTable at: MetaDataOpeningBlockMarkup put: MicMetaDataBlock. - dispatchTable at: UnorderedListPlusMarkup put: MicUnorderedListBlock. - dispatchTable at: UnorderedListMarkup put: MicUnorderedListBlock. - dispatchTable at: UnorderedListStarMarkup put: MicUnorderedListBlock. - dispatchTable at: PreformattedMarkup put: MicBlockQuoteBlock. - dispatchTable at: TableCellMarkup put: MicTableBlock. -] - -{ #category : 'testing' } -MicParser >> isAList: normalized [ - - ^ (self matchUnordered: normalized) or: [ self matchOrdered: normalized ] -] - -{ #category : 'node creation' } -MicParser >> matchOrdered: line [ - ^ line prefixMatchesRegex: '\d+(\.|\))' -] - -{ #category : 'node creation' } -MicParser >> matchUnordered: line [ - ^ ( line beginsWith: UnorderedListPlusMarkup ) - | ( line beginsWith: UnorderedListStarMarkup ) - | ( line beginsWith: UnorderedListMarkup ) -] - -{ #category : 'parsing' } -MicParser >> newRootBlock [ - ^ MicRootBlock new -] - -{ #category : 'node creation' } -MicParser >> nonMatchedBlockClassFor: line [ - - ^ self paragraphBlockClass -] - -{ #category : 'node creation' } -MicParser >> orderedListBlockClass [ - - ^ MicOrderedListBlock -] - -{ #category : 'node creation' } -MicParser >> paragraphBlockClass [ - - ^ MicParagraphBlock -] - -{ #category : 'parsing' } -MicParser >> parse: aStreamOrString [ - "returns the root node of aStreamOrText" - - | inStream line | - current := root := self newRootBlock - setParser: self; - yourself. - inStream := aStreamOrString isStream - ifTrue: [ aStreamOrString readStream ] - ifFalse: [ aStreamOrString asString readStream ]. - [ - line := inStream nextLine. - line isNil ] - whileFalse: [ - self handleLine: line ]. - [ current = root ] - whileFalse: [ - current closeMe. - current := current parent ]. - root hasMetaDataElement - ifTrue: [ root properties: root metaDataElement body ]. - - - ^ root -] - -{ #category : 'accessing' } -MicParser >> setCurrent: aBlock [ - "aBlock parent = current - ifFalse: [ self error: 'When changing current, the current block should be the parent of the new one' ]. - note for me: we could just inforce this. aBlock parent: current. - No this is not correct since - the parent of an item list is the list and this is the list that is parent of environment." - current := aBlock -] diff --git a/src/Microdown/MicRawBlock.class.st b/src/Microdown/MicRawBlock.class.st index 4ca266fd4..58566d5a5 100644 --- a/src/Microdown/MicRawBlock.class.st +++ b/src/Microdown/MicRawBlock.class.st @@ -28,6 +28,5 @@ MicRawBlock class >> openingDelimiter [ { #category : 'visiting' } MicRawBlock >> accept: aVisitor [ - "A raw element should not do any interpretation of its contents." ^ aVisitor visitRaw: self ] diff --git a/src/Microdown/MicRawParagraphBlock.class.st b/src/Microdown/MicRawParagraphBlock.class.st index fb8fab4d0..fd78773f4 100644 --- a/src/Microdown/MicRawParagraphBlock.class.st +++ b/src/Microdown/MicRawParagraphBlock.class.st @@ -28,27 +28,73 @@ Class { #tag : 'Model' } +{ #category : 'public' } +MicRawParagraphBlock class >> alternateBlockClassFor: aLine [ + "We do not want to have extention for MicRawParaphTag" + + ^ self +] + { #category : 'visiting' } MicRawParagraphBlock >> accept: aVisitor [ ^ aVisitor visitRawParagraph: self ] -{ #category : 'markups' } -MicRawParagraphBlock >> lineStartMarkup [ +{ #category : 'activation' } +MicRawParagraphBlock >> argumentString [ + ^ argumentsString +] + +{ #category : 'handle' } +MicRawParagraphBlock >> extractFirstLineFrom: aLine [ + "we got < tag ...x > foo bar or < tag ...x and we return tag ...x " + + | splits | + firstLine := aLine copyFrom: self lineStartMarkup size to: aLine size. + firstLine := firstLine trimBoth. + firstLine := firstLine copyFrom: 1 to: firstLine size -1. + + splits := firstLine splitOnFirst: $>. + splits second isNotEmpty + ifTrue: [ "here we p>Blo and we discard Blo" + label := splits first ]. + + "now we may have

    > hasArguments [ + ^ argumentsString isNotEmpty +] + +{ #category : 'initialization' } +MicRawParagraphBlock >> initialize [ + + super initialize. + argumentsString := '' +] - ^ BlockLevelRawOpenerMarkup +{ #category : 'accessing' } +MicRawParagraphBlock >> label [ + ^ label ] { #category : 'markups' } MicRawParagraphBlock >> lineStopMarkup [ - ^ MetaDataClosingBlockMarkup + ^ '' ] { #category : 'printing' } -MicRawParagraphBlock >> printOn: aStream [ +MicRawParagraphBlock >> printOn: aStream [ - aStream nextPutAll: '{{{'. - aStream nextPutAll: self body. - aStream nextPutAll: '}}}' + super printOn: aStream. + aStream << '(' ; + << label ; + << ')' ] diff --git a/src/Microdown/MicSharedPool.class.st b/src/Microdown/MicSharedPool.class.st index 317ca0b3b..f6e2bf12a 100644 --- a/src/Microdown/MicSharedPool.class.st +++ b/src/Microdown/MicSharedPool.class.st @@ -17,8 +17,6 @@ Class { 'ArgumentListEqualsDelimiter', 'ArgumentListOfFigureStartDelimiter', 'ArgumentListStartDelimiter', - 'BlockLevelRawCloserMarkup', - 'BlockLevelRawOpenerMarkup', 'BoldMarkup', 'CodeblockMarkup', 'CommentedLineMarkup', @@ -109,9 +107,6 @@ MicSharedPool class >> initialize [ UnorderedListMarkup := '- '. UnorderedListPlusMarkup := '+ '. UnorderedListStarMarkup := '* '. - - BlockLevelRawCloserMarkup := '}}}'. - BlockLevelRawOpenerMarkup := '{{{'. self initializeRawParagraph. diff --git a/src/Microdown/MicStartStopMarkupBlock.class.st b/src/Microdown/MicStartStopMarkupBlock.class.st index 87b684335..f5c29c9c9 100644 --- a/src/Microdown/MicStartStopMarkupBlock.class.st +++ b/src/Microdown/MicStartStopMarkupBlock.class.st @@ -81,14 +81,13 @@ MicStartStopMarkupBlock >> canConsumeLine: line [ { #category : 'testing' } MicStartStopMarkupBlock >> doesLineStartWithStopMarkup: line [ + "return if the line starts with a stop markup" - ^ line beginsWith: self lineStopMarkup ] { #category : 'handle' } MicStartStopMarkupBlock >> extractFirstLineFrom: line [ - ^ line copyFrom: self lineStartMarkup size + 1 to: line size ] diff --git a/src/Microdown/Microdown.class.st b/src/Microdown/Microdown.class.st index deb4c792e..6dac537c0 100644 --- a/src/Microdown/Microdown.class.st +++ b/src/Microdown/Microdown.class.st @@ -19,7 +19,7 @@ Class { { #category : 'facade' } Microdown class >> builder [ - ^ MicParser builder + ^ MicrodownParser builder ] { #category : 'facade' } @@ -140,7 +140,7 @@ Microdown >> builder [ { #category : 'facade' } Microdown >> parse: aStreamOrString [ - ^ MicParser parse: aStreamOrString + ^ MicrodownParser parse: aStreamOrString ] { #category : 'facade' } @@ -148,7 +148,7 @@ Microdown >> parseFile: aFile [ "Parse and return a document from the argument marking it with the file it is contained in. This is important for path resolution." | root | - root := MicParser parse: aFile contents. + root := MicrodownParser parse: aFile contents. root fromFile: aFile. ^ root diff --git a/src/Microdown/MicrodownParser.class.st b/src/Microdown/MicrodownParser.class.st index 9d772613f..60e11c104 100644 --- a/src/Microdown/MicrodownParser.class.st +++ b/src/Microdown/MicrodownParser.class.st @@ -1,7 +1,377 @@ +" +# Microdown Parser and Elements + +I'm a parser for Microdown, implemented by S. Ducasse, L. Dargaud and G. Polito (2020). +It is based on K. Osterbye's work on GitHubMarkdown. + +Microdown covers the basic syntax of Markdown, with great Pillar features. It's also more extensible. + +## MicroDown Syntax in a Nutshell! + +**Headers** (up to 6 levels) >>>> `# Header level 1` + +**Unordered list** +```text +- item a + on another line +- item b + - item b1 haaaaaaaaaa + - item b2 +- item c +``` +Produces: +- item a + on another line +- item b + - item b1 + - item b2 +- item c + +**Ordered list** +```text +1. item one + 1. subitem +2. item two +``` +Produces: +1. item one + 1. subitem +2. item two + + +**Codeblock with arguments** +```text + ```language=Pharo&caption=Beautiful&label=Fig1 + some piece of code + ``` +``` +Produces: +```language=Pharo&caption=Beautiful&label=Fig1 +some piece of code +``` + +**Anchor and its reference** >>>> `@anchor` and its reference in text with `*@anchor@*` + +**Math environment** (rendered with LaTex in Pharo) +- inline: $\cos(\theta_i+C_3)$ >>>> `$\cos(\theta_i+C_3)$` +- block: +```text +$$ +\cos(\theta_i+C_3) +$$ +``` +$$ +\cos(\theta_i+C_3) +$$ + +**Annotated paragraph** +```text +!!note Then you can write your note +on several lines, like a normal paragraph +``` + +**Quote** to indent some lines +```text +> This is +> A nice quote +``` + +**Environment** (called Annotation for inline) +- inline: `\` +- block: +```text + +``` + +**Metadata** as a block +```text +{ +type your metadata +} +``` + +**Table** +Support for tables with a simpler and more relaxed setup than GitHub. +A table is not forced to have a header definition. + +```text +| aaa | bbb +| ccc | ddd +``` +Produces: +| aaa | bbb +| ccc | ddd + +**Horizontal line** >>>> `***` alone on a line will define a separator in your text. +*** + +**Comments** don't appear after parsing >>>> `% A commented line` +% This can only be read in the original source, as you are doing. + +**Inline formats** +- **bold** >>>> `**bold**` +- _italic_ >>>> `_italic_` +- `monospace` aka inline code >>>> `\`monospace\`` + +_Note: inline escape character is the backslash: `\\`._ +_Note: text in monospace is analyzed for you to generate hypertext Pharo code objects! Isn't it so cool to write a great doc? It is! Click on them!_ +- `Point` for classes +- `Point class` for class side +- `Point>>#setX:setY:` for methods +- `#'Microdown-Tests'` for packages + +**Raw** for your other code (inline) >>>> `{{ some code }}` + +**Link** >>>> `[link's name](url|key1=value1&key2=value2)` + +**Figure** >>>> `![figure's name](url|key1=value1&key2=value2)` + +`![Pharo logo](https://files.pharo.org/media/logo/logo.png)` +produces + +![Pharo logo](https://files.pharo.org/media/logo/logo.png) + + +## Implementation Notes + +I follow the design mentioned in [https://github.github.com/gfm](https://github.github.com/gfm), in particular the parsing strategy in the appendix. + +In short, the strategy is that at any point in time, we can have a number of children of the root that are ""open"". The deepest in open in the tree is called ""current"". All the parents of current are open. + +When a new line is read, we do the following: + +1. Check if the new line can be consumed by current. + - as part of this, a child of current can be made which can consume the new line. For example, when consuming `\`\`\``, the root block node will create a new code block that will become current and consume the body of the `\`\`\`` element, then close. +2. If current cannot consume the new line, we close current, move current to its parent, and repeat 1. +3. The root node can consume anything, for instance by making new nodes to store the new line. +4. The root node is not closed until input is exhausted. + +I do not accept lazy definition. I do not accept three different ways to do the same thing. Except for bulleted lists, where `*` and `-` are accepted. + +The spec says: +```text +-> document + -> block_quote + paragraph + ""Lorem ipsum dolor\nsit amet."" + -> list (type=bullet tight=true bullet_char=-) + list_item + paragraph + ""Qui *quodsi iracundia*"" + -> list_item + -> paragraph + ""aliquando id"" +``` +The current implementation does not create a paragraph in the list_item element. +" Class { #name : 'MicrodownParser', - #superclass : 'MicParser', + #superclass : 'Object', + #instVars : [ + 'current', + 'root', + 'dispatchTable' + ], + #pools : [ + 'MicSharedPool' + ], #category : 'Microdown-Parser', #package : 'Microdown', #tag : 'Parser' } + +{ #category : 'builder' } +MicrodownParser class >> builder [ + ^ MicMicrodownTextualBuilder new +] + +{ #category : 'examples' } +MicrodownParser class >> example [ + + + ^ self parse: self comment +] + +{ #category : 'instance creation' } +MicrodownParser class >> parse: aString [ + + ^ self new parse: aString +] + +{ #category : 'node creation' } +MicrodownParser >> blockStarterClassFrom: line [ + "return the class of a block which can start with line, or nil if none" + + line ifEmpty: [ ^ nil ]. + (self matchOrdered: line) + ifTrue: [ ^ self orderedListBlockClass ] + ifFalse: [ + "right now this is super ugly and slow + but this is to try. Once we understand and probably change the environment syntax for now it is , i and i> + This is working p src=800 + " + + (line includes: Character space) + ifTrue: [ HTMLTags includes: ((line allButFirst splitOnFirst: Character space) first) ] + ifFalse: [ HTMLTags includes: ((line allButFirst splitOnFirst: $>) first) ] + ]) + ifTrue: [ + "we have a rawparagraph block" + ^ MicRawParagraphBlock + ] + ifFalse: [ + + "the max significant length of a markup is 3" + (3 to: 1 by: -1) do: [:i | | prefix | + prefix := (line truncateTo: i). + "If we have a inline starter, it takes precedence" + (Delimiters includes: prefix) + ifTrue: [ ^ self delimiterBlockClassFor: prefix ]. + dispatchTable + at: prefix + ifPresent: [ :blockClass | ^ blockClass ] ] ] ]. + + ^ self nonMatchedBlockClassFor: line +] + +{ #category : 'accessing' } +MicrodownParser >> builder [ + "return a little helper to build microdown correct expression" + + ^ self class builder +] + +{ #category : 'accessing' } +MicrodownParser >> current [ + + ^ current +] + +{ #category : 'node creation' } +MicrodownParser >> delimiterBlockClassFor: prefix [ + + ^ self paragraphBlockClass +] + +{ #category : 'parsing' } +MicrodownParser >> handleLine: line [ + + "The logic is the following: + -if the current block can consume the line, it manages it and this potentially creates a new block that becomes the current one. + When the line is not consumed, the current block is closed and its parent becomes the current one and the process is called back to treat the line." + + (current canConsumeLine: (current massageLine: line)) + ifTrue: [ + current := current addLineAndReturnNextNode: line. + ^ current ] + ifFalse: [ current closeMe ]. + current := current parent. + self handleLine: line +] + +{ #category : 'initialization' } +MicrodownParser >> initialize [ + + super initialize. + dispatchTable := Dictionary new. + dispatchTable at: AnchorMarkup put: MicAnchorBlock. + dispatchTable at: HeaderMarkup put: MicHeaderBlock. + dispatchTable at: CodeblockMarkup put: MicAbstractCodeBlock. + dispatchTable at: AnnotatedParagraphOpeningMarkup put: MicAnnotatedParagraphBlock. + dispatchTable at: AnnotatedParagraphBackwardCompatibleMarkup put: MicAnnotatedParagraphBlock. + dispatchTable at: CommentedLineMarkup put: MicCommentBlock. + dispatchTable at: HorizontalLineMarkup put: MicHorizontalLineBlock. + dispatchTable at: MathOpeningBlockMarkup put: MicMathBlock. + dispatchTable at: EnvironmentOpeningBlockMarkup put: MicEnvironmentBlock. + dispatchTable at: MetaDataOpeningBlockMarkup put: MicMetaDataBlock. + dispatchTable at: UnorderedListPlusMarkup put: MicUnorderedListBlock. + dispatchTable at: UnorderedListMarkup put: MicUnorderedListBlock. + dispatchTable at: UnorderedListStarMarkup put: MicUnorderedListBlock. + dispatchTable at: PreformattedMarkup put: MicBlockQuoteBlock. + dispatchTable at: TableCellMarkup put: MicTableBlock. +] + +{ #category : 'testing' } +MicrodownParser >> isAList: normalized [ + + ^ (self matchUnordered: normalized) or: [ self matchOrdered: normalized ] +] + +{ #category : 'node creation' } +MicrodownParser >> matchOrdered: line [ + ^ line prefixMatchesRegex: '\d+(\.|\))' +] + +{ #category : 'node creation' } +MicrodownParser >> matchUnordered: line [ + ^ ( line beginsWith: UnorderedListPlusMarkup ) + | ( line beginsWith: UnorderedListStarMarkup ) + | ( line beginsWith: UnorderedListMarkup ) +] + +{ #category : 'parsing' } +MicrodownParser >> newRootBlock [ + ^ MicRootBlock new +] + +{ #category : 'node creation' } +MicrodownParser >> nonMatchedBlockClassFor: line [ + + ^ self paragraphBlockClass +] + +{ #category : 'node creation' } +MicrodownParser >> orderedListBlockClass [ + + ^ MicOrderedListBlock +] + +{ #category : 'node creation' } +MicrodownParser >> paragraphBlockClass [ + + ^ MicParagraphBlock +] + +{ #category : 'parsing' } +MicrodownParser >> parse: aStreamOrString [ + "returns the root node of aStreamOrText" + + | inStream line | + current := root := self newRootBlock + setParser: self; + yourself. + inStream := aStreamOrString isStream + ifTrue: [ aStreamOrString readStream ] + ifFalse: [ aStreamOrString asString readStream ]. + [ + line := inStream nextLine. + line isNil ] + whileFalse: [ + self handleLine: line ]. + [ current = root ] + whileFalse: [ + current closeMe. + current := current parent ]. + root hasMetaDataElement + ifTrue: [ root properties: root metaDataElement body ]. + + + ^ root +] + +{ #category : 'accessing' } +MicrodownParser >> setCurrent: aBlock [ + "aBlock parent = current + ifFalse: [ self error: 'When changing current, the current block should be the parent of the new one' ]. + note for me: we could just inforce this. aBlock parent: current. + No this is not correct since + the parent of an item list is the list and this is the list that is parent of environment." + current := aBlock +] diff --git a/src/Microdown/MicrodownVisitor.class.st b/src/Microdown/MicrodownVisitor.class.st index 03822d82e..80f7fa08c 100644 --- a/src/Microdown/MicrodownVisitor.class.st +++ b/src/Microdown/MicrodownVisitor.class.st @@ -46,12 +46,6 @@ MicrodownVisitor >> visitAnnotated: anAnnotated [ MicrodownVisitor >> visitAnnotation: anAnnotation [ ] -{ #category : 'visiting - html extensions' } -MicrodownVisitor >> visitBlockLevelRawParagraph: aRawParagraph [ - - -] - { #category : 'visiting - inline elements' } MicrodownVisitor >> visitBold: aFormat [ @@ -133,12 +127,6 @@ MicrodownVisitor >> visitFootnote: aFootnote [ "subclassResponsibility" ] -{ #category : 'visiting - html extensions' } -MicrodownVisitor >> visitHTMLTagParagraph: anHTMLTagParagraph [ - - -] - { #category : 'visiting' } MicrodownVisitor >> visitHeader: aHeader [ @@ -243,13 +231,6 @@ MicrodownVisitor >> visitQuote: aQuote [ MicrodownVisitor >> visitRaw: aRawFormat [ -] - -{ #category : 'visiting - html extensions' } -MicrodownVisitor >> visitRawParagraph: aRawParagraph [ - "a raw paragraph is a paragraph whose contents is never changed." - - ] { #category : 'visiting' } From 2134a3bf20c78c75625b08204f1856db9223539c Mon Sep 17 00:00:00 2001 From: Fanirindrainy Date: Tue, 17 Mar 2026 01:36:01 +0300 Subject: [PATCH 2/4] Revert "first commit after cloning " This reverts commit 0e15492849555754cfec7e822983563026bb0364. --- .../MicHTMLExporterTest.class.st | 56 +- .../MicAgendaBlockTest.class.st | 49 + .../MicAgendaGeneratorTest.class.st | 456 ++++++++ .../MicDayBlockTest.class.st | 49 + .../MicSegmentBlockTest.class.st | 49 + .../MicReferenceCheckerTest.class.st | 1007 ++++++++++++----- .../MicReferenceChecker.class.st | 240 +++- .../MicHTMLExporterTest.class.st | 362 +++++- .../MicHTMLBrush.class.st | 26 + .../MicHTMLCanvas.class.st | 46 +- .../MicHTMLTag.class.st | 102 +- .../MicHTMLVisitor.class.st | 19 +- .../MicQuoteBlockTest.extension.st | 17 + ...er.extension.st => MicParser.extension.st} | 4 +- .../ManifestMicrodownRules.class.st | 24 - .../MicEnglishTypoChecker.class.st | 10 +- .../MicEnglishTypoCheckerTest.class.st | 24 +- .../MicRuleHeaderChecker.class.st | 5 +- .../MicRuleUppercaseHeaderStrategy.class.st | 14 +- .../MicSpaceBeforeDiacriticsResult.class.st | 4 +- .../MicUseofWrongVocabularyResult.class.st | 2 +- .../MicVocabularyChecker.class.st | 28 +- .../MicVocabularyCheckerTest.class.st | 33 +- .../MicSlideConverter.class.st | 24 + .../MicAnchorLinkerTest.class.st | 6 +- src/Microdown-Tests/MicElementTest.class.st | 2 +- .../MicHTMLTagParagraphBlockTest.class.st | 176 +++ .../MicParagraphBlockTest.class.st | 2 +- src/Microdown-Tests/MicParserTest.class.st | 2 +- .../MicRawParagraphBlockTest.class.st | 198 +--- .../MicrodownParserTest.class.st | 2 +- src/Microdown-Tests/MicrodownTest.class.st | 4 +- ...downObjectToPillarObjectConverter.class.st | 33 +- src/Microdown/MicAbstractBlock.class.st | 2 +- .../MicHTMLTagParagraphBlock.class.st | 118 ++ src/Microdown/MicParser.class.st | 378 +++++++ src/Microdown/MicRawBlock.class.st | 1 + src/Microdown/MicRawParagraphBlock.class.st | 62 +- src/Microdown/MicSharedPool.class.st | 5 + .../MicStartStopMarkupBlock.class.st | 3 +- src/Microdown/Microdown.class.st | 6 +- src/Microdown/MicrodownParser.class.st | 372 +----- src/Microdown/MicrodownVisitor.class.st | 19 + 43 files changed, 2945 insertions(+), 1096 deletions(-) create mode 100644 src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st create mode 100644 src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st create mode 100644 src/Microdown-Agenda-Tests/MicDayBlockTest.class.st create mode 100644 src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st create mode 100644 src/Microdown-HTMLExporter/MicHTMLBrush.class.st create mode 100644 src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st rename src/Microdown-RichTextComposer/{MicrodownParser.extension.st => MicParser.extension.st} (53%) delete mode 100644 src/Microdown-Rules/ManifestMicrodownRules.class.st create mode 100644 src/Microdown-Slide-Utils/MicSlideConverter.class.st create mode 100644 src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st create mode 100644 src/Microdown/MicHTMLTagParagraphBlock.class.st create mode 100644 src/Microdown/MicParser.class.st diff --git a/src/Microdown - HTML/MicHTMLExporterTest.class.st b/src/Microdown - HTML/MicHTMLExporterTest.class.st index 852578258..9002c508c 100644 --- a/src/Microdown - HTML/MicHTMLExporterTest.class.st +++ b/src/Microdown - HTML/MicHTMLExporterTest.class.st @@ -1,28 +1,15 @@ Class { #name : 'MicHTMLExporterTest', - #superclass : 'ParametrizedTestCase', + #superclass : 'TestCase', #instVars : [ 'parser', 'writer', - 'factory', - 'newLine' + 'factory' ], - #category : 'Microdown-HTMLExporter-Tests-HTML', - #package : 'Microdown-HTMLExporter-Tests', - #tag : 'HTML' + #category : 'Microdown - HTML', + #package : 'Microdown - HTML' } -{ #category : 'tests - list' } -MicHTMLExporterTest >> orderedListString [ - -^ ' -Here is a list: -1. item 1 -2. item 2 -3. item 3 -' -] - { #category : 'utils' } MicHTMLExporterTest >> parse: aString andCheckWeGet: aResultingString [ @@ -46,32 +33,13 @@ MicHTMLExporterTest >> testHeaderLevel1 [ ] -{ #category : 'tests - list' } -MicHTMLExporterTest >> testOrderedList [ - - self - parse: self orderedListString - andCheckWeGet: - -newLine , '

    Here is a list:

    ' , newLine , newLine , '
      ' , newLine , '
    1. item 1
    2. ' , newLine , '
    3. item 2
    4. ' , newLine , '
    5. item 3
    6. ' , newLine , '
    ' -] - -{ #category : 'tests - list' } -MicHTMLExporterTest >> testOrderedListIsRecognized [ - - | doc | - doc := Microdown parse: self orderedListString. - self assert: doc children second class equals: MicOrderedListBlock -] - -{ #category : 'tests - list' } +{ #category : 'tests' } MicHTMLExporterTest >> testUnorderedList [ - - self - parse: factory unorderedListWithTwoItemsSample - andCheckWeGet: newLine , -'
      ', newLine , -'
    • Foo
    • ', newLine , -'
    • Bar
    • ', newLine , -'
    ' + | mic | + mic := parser parse: factory unorderedListWithTwoItemsSample. + self assert: (writer visit: mic) contents equals: ' +
      +
    • Foo
    • +
    • Bar
    • +
    ' ] diff --git a/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st b/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st new file mode 100644 index 000000000..e1ce31537 --- /dev/null +++ b/src/Microdown-Agenda-Tests/MicAgendaBlockTest.class.st @@ -0,0 +1,49 @@ +Class { + #name : 'MicAgendaBlockTest', + #superclass : 'TestCase', + #instVars : [ + 'builder', + 'parser' + ], + #pools : [ + 'MicMicrodownSharedPool' + ], + #category : 'Microdown-Agenda-Tests', + #package : 'Microdown-Agenda-Tests' +} + +{ #category : 'running' } +MicAgendaBlockTest >> setUp [ + + super setUp. + builder := MicMicrodownTextualBuilder new. + parser := Microdown new. +] + +{ #category : 'tests' } +MicAgendaBlockTest >> testAgenda [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'agenda', String cr, + EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicAgendaBlock). + self assert: env environmentName equals: 'agenda' +] + +{ #category : 'tests' } +MicAgendaBlockTest >> testAgendaWithArgument [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'agenda|title=A cool agenda', String cr, EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicAgendaBlock). + self assert: env environmentName equals: 'agenda'. + self assert: env title equals: 'A cool agenda' +] diff --git a/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st b/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st new file mode 100644 index 000000000..cdac03f63 --- /dev/null +++ b/src/Microdown-Agenda-Tests/MicAgendaGeneratorTest.class.st @@ -0,0 +1,456 @@ +" +This class contains tests but all the test are not correct beacaus we need to have a file in a repertory with the templates because if we don't have them we can't generate the calendar. +" +Class { + #name : 'MicAgendaGeneratorTest', + #superclass : 'ParametrizedTestCase', + #instVars : [ + 'builder', + 'parser', + 'writer', + 'fileSystem', + 'newLine' + ], + #pools : [ + 'MicMicrodownSharedPool' + ], + #category : 'Microdown-Agenda-Tests', + #package : 'Microdown-Agenda-Tests' +} + +{ #category : 'building suites' } +MicAgendaGeneratorTest class >> testParameters [ + + ^ ParametrizedTestMatrix new + forSelector: #writer addOptions: { MicAgendaGenerator }; + forSelector: #fileSystem addOptions: { FileSystem }; + forSelector: #newLine addOptions: { String crlf . String cr . String lf }; + yourself +] + +{ #category : 'exemple' } +MicAgendaGeneratorTest >> agendaExempleMultiplesTalks [ + + ^ ' + + + +!> + +!> + + + + + +!> + +!> + +!>' + +] + +{ #category : 'exemple' } +MicAgendaGeneratorTest >> agendaExempleOneTalk [ + + ^ ' +!> + +!> + +!>' + +] + +{ #category : 'exemple' } +MicAgendaGeneratorTest >> agendaExempleWithRealInfo [ + + ^ ' + + + + + + + + + + + + + + + + + + + + + +!> + +!> + +!>' + +] + +{ #category : 'accessing' } +MicAgendaGeneratorTest >> fileSystem: aFileSystem [ + fileSystem := aFileSystem memory. +] + +{ #category : 'accessing' } +MicAgendaGeneratorTest >> newLine: aNewLine [ + (aNewLine = String cr) ifTrue:[ writer crAsNewLine ]. + (aNewLine = String lf) ifTrue:[ writer lfAsNewLine ]. + (aNewLine = String crlf) ifTrue:[ writer crlfAsNewLine ]. + newLine := aNewLine +] + +{ #category : 'running' } +MicAgendaGeneratorTest >> setUp [ + + super setUp. + builder := MicMicrodownTextualBuilder new. + parser := Microdown new. +] + +{ #category : 'tests' } +MicAgendaGeneratorTest >> testAgendaAstIsCorrect [ + + | fs file ast agenda day segment| + + fs := FileSystem memory. + file := fs root / 'agenda.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: self agendaExempleMultiplesTalks ]. + + ast := parser parse: file contents. + agenda := ast children first. + day := agenda children first. + segment := day children first. + self assert: agenda class equals: MicAgendaBlock. + self assert: day class equals: MicDayBlock. + self assert: segment class equals: MicSegmentBlock. + self assert: segment children size equals: 3. + +] + +{ #category : 'tests' } +MicAgendaGeneratorTest >> testConvertMicFileCorrectly [ + + "Look at the class comment for more information about this test" + + "| fs file ast| + fs := FileSystem memory. + file := fs root / 'agenda.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: self agendaExempleMultiplesTalks ]. + + ast := parser parse: file contents. + + writer visit: ast. + + self assert: writer contents equals: +'3 mars 2023', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 10h30 - 11h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Delporte Gaylord', newLine, +'
    ', newLine, +' ', newLine, +'
    First talk
    ', newLine, +' ', newLine, +'
    Building B
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 11h00 - 13h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +'
    Break
    ', newLine, +' ', newLine, +'
    Building A
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 13h00 - 14h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Not me', newLine, +'
    ', newLine, +' ', newLine, +'
    Second talk
    ', newLine, +' ', newLine, +'
    Building C
    ', newLine, +'
    ', newLine, +'
  • ', newLine, newLine" + +] + +{ #category : 'tests' } +MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithMultiplesTalks [ + + "Look at the class comment for more information about this test" + + "| fs file ast| + + fs := FileSystem memory. + file := fs root / 'agenda.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: self agendaExempleMultiplesTalks ]. + + ast := parser parse: file contents. + + writer visit: ast. + + self assert: writer contents equals: +'3 mars 2023', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 10h30 - 11h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Delporte Gaylord', newLine, +'
    ', newLine, +' ', newLine, +'
    First talk
    ', newLine, +' ', newLine, +'
    Building B
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 11h00 - 13h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +'
    Break
    ', newLine, +' ', newLine, +'
    Building A
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 13h00 - 14h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Not me', newLine, +'
    ', newLine, +' ', newLine, +'
    Second talk
    ', newLine, +' ', newLine, +'
    Building C
    ', newLine, +'
    ', newLine, +'
  • ', newLine, newLine" + +] + +{ #category : 'tests' } +MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithOneTalk [ + + "Look at the class comment for more information about this test" + + "| fs file ast| + + fs := FileSystem memory. + file := fs root / 'agenda.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: self agendaExempleOneTalk ]. + + ast := parser parse: file contents. + + writer visit: ast. + + self assert: writer contents equals: +'3 mars 2023', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 10h30 - 11h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Delporte Gaylord', newLine, +'
    ', newLine, +' ', newLine, +'
    First talk
    ', newLine, +' ', newLine, +'
    Building B
    ', newLine, +'
    ', newLine, +'
  • ', newLine, newLine" +] + +{ #category : 'tests' } +MicAgendaGeneratorTest >> testConvertMicFileCorrectlyWithRealInfo [ + + "Look at the class comment for more information about this test" + + "| fs file ast| + + fs := FileSystem memory. + file := fs root / 'agenda.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: self agendaExempleWithRealInfo ]. + + ast := parser parse: file contents. + + writer visit: ast. + + self assert: writer contents equals: +'3 mars 2023', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 10h30 - 11h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Delporte Gaylord', newLine, +'
    ', newLine, +' ', newLine, +'
    First talk
    ', newLine, +' ', newLine, +'
    Building B
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 11h00 - 13h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +'
    Break
    ', newLine, +' ', newLine, +'
    Building A
    ', newLine, +'
    ', newLine, +'
  • ', newLine, +' ', newLine, +'
  • ', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' 13h00 - 14h00', newLine, +'
    ', newLine, +' ', newLine, +'
    ', newLine, +' ', newLine, +' Not me', newLine, +'
    ', newLine, +' ', newLine, +'
    Second talk
    ', newLine, +' ', newLine, +'
    Building C
    ', newLine, +'
    ', newLine, +'
  • ', newLine, newLine" + +] + +{ #category : 'accessing' } +MicAgendaGeneratorTest >> writer: aWriter [ + writer := aWriter new +] diff --git a/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st b/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st new file mode 100644 index 000000000..ffe3f6a4e --- /dev/null +++ b/src/Microdown-Agenda-Tests/MicDayBlockTest.class.st @@ -0,0 +1,49 @@ +Class { + #name : 'MicDayBlockTest', + #superclass : 'TestCase', + #instVars : [ + 'builder', + 'parser' + ], + #pools : [ + 'MicMicrodownSharedPool' + ], + #category : 'Microdown-Agenda-Tests', + #package : 'Microdown-Agenda-Tests' +} + +{ #category : 'running' } +MicDayBlockTest >> setUp [ + + super setUp. + builder := MicMicrodownTextualBuilder new. + parser := Microdown new. +] + +{ #category : 'tests' } +MicDayBlockTest >> testDay [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'day', String cr, + EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicDayBlock). + self assert: env environmentName equals: 'day' +] + +{ #category : 'tests' } +MicDayBlockTest >> testDayWithArgument [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'day|start=05/12/2022', String cr, EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicDayBlock). + self assert: env environmentName equals: 'day'. + self assert: env start equals: '05/12/2022' +] diff --git a/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st b/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st new file mode 100644 index 000000000..1c1ce6481 --- /dev/null +++ b/src/Microdown-Agenda-Tests/MicSegmentBlockTest.class.st @@ -0,0 +1,49 @@ +Class { + #name : 'MicSegmentBlockTest', + #superclass : 'TestCase', + #instVars : [ + 'builder', + 'parser' + ], + #pools : [ + 'MicMicrodownSharedPool' + ], + #category : 'Microdown-Agenda-Tests', + #package : 'Microdown-Agenda-Tests' +} + +{ #category : 'running' } +MicSegmentBlockTest >> setUp [ + + super setUp. + builder := MicMicrodownTextualBuilder new. + parser := Microdown new. +] + +{ #category : 'tests' } +MicSegmentBlockTest >> testSegment [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'segment', + String cr, EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicSegmentBlock). + self assert: env environmentName equals: 'segment'. +] + +{ #category : 'tests' } +MicSegmentBlockTest >> testSegmentWithArgument [ + "' +'" + | source root env| + source := EnvironmentOpeningBlockMarkup , 'segment|start=10h', String cr, EnvironmentClosingBlockMarkup, String cr. + root := parser parse: source. + env := root children first. + self assert: (env isKindOf: MicSegmentBlock). + self assert: env environmentName equals: 'segment'. + self assert: env start equals: '10h' +] diff --git a/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st b/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st index e41132cd4..915d0a8f2 100644 --- a/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st +++ b/src/Microdown-BookTester-Tests/MicReferenceCheckerTest.class.st @@ -1,435 +1,855 @@ Class { #name : 'MicReferenceCheckerTest', - #superclass : 'TestCase', - #category : 'Microdown-ReferenceChecker', - #package : 'Microdown-ReferenceChecker' + #superclass : 'MicFileTest', + #instVars : [ + 'fileDefAncS1UndAncS0', + 'refAncS1', + 'defEq1AndReferToEq1', + 'defAndRefFig1', + 'defFig1AndRefToAnsC0', + 'defFig1AndRefToAncUnkS0', + 'refToUnkS1', + 'defCode1AndReferToCode1', + 'defFig1' + ], + #category : 'Microdown-BookTester-Tests', + #package : 'Microdown-BookTester-Tests' } -{ #category : 'tests' } -MicReferenceCheckerTest >> testAllReferencesAreCorrect [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> anchorNames: checker [ - | doc visitor | - doc := Microdown parse: '# Section 1 -@anchorSection1 + ^ checker results + select: [ :each | each isKindOf: MicAnchorResult ] + thenCollect: [ :each | each anchorLabel ] +] -See *@anchorSection1@* +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defAnCS0DoubleEq2DoubleEq1RefEq1 [ -'. - visitor := MicReferenceChecker new. - doc accept: visitor. - self assert: visitor isOk + | defAnCS0DoubleEq2DoubleEq1RefEq1 | + defAnCS0DoubleEq2DoubleEq1RefEq1 := dir + / + 'defAnCS0DoubleEq2DoubleEq1RefEq1.md'. + defAnCS0DoubleEq2DoubleEq1RefEq1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 + +$$ %anchor=Eq1 +balbalbalb! +$$ + +$$ %anchor=Eq2 +balbalbalb! +$$ + +$$ %anchor=Eq2 +balbalbalb! +$$ + +$$ %anchor=Eq1 +balbalbalb! +$$ + +See *@Eq1@* + +' ]. + defAnCS0DoubleEq2DoubleEq1RefEq1 ensureCreateFile. + ^ defAnCS0DoubleEq2DoubleEq1RefEq1 ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testAllReferencesAreCorrectInFile [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defAncS0DoubleFig1Fig2RefAncS1 [ - | file visitor | - file := (FileSystem memory / 'myFile.txt') asFileReference. - file ensureCreateFile. - file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 -@anchorSection1 + | doubleFig1 | + doubleFig1 := dir / 'defAncS0DoubleFig1Fig2RefAncS1.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + doubleFig1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 -See *@anchorSection1@* +![a caption 1](figures/f.png anchor=fig1) -' ] . - +![a caption 2](figures/f.png anchor=fig1) +![a caption 2](figures/f.png anchor=fig1) - visitor := MicReferenceChecker new. - self assert: (visitor checkFile: file ). - file ensureDelete +![a caption 2](figures/f.png anchor=fig1) + +![a caption 3](figures/f.png anchor=fig2) + +See *@ancS1@* + +' ]. + doubleFig1 ensureCreateFile. + ^ doubleFig1 ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testAllReferencesAreCorrectinDir [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defAncS0TripleAncS1RefAncS1AncS0 [ - | dir file1 file2 visitor | - self skip. - dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. - dir ensureCreateDirectory. - file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. - file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section -@anchorSection0 + | defAncS0TripleAncS1RefAncS1AncS0 | + defAncS0TripleAncS1RefAncS1AncS0 := dir / 'defAncS0TripleAncS1RefAncS1AncS0.md'. + defAncS0TripleAncS1RefAncS1AncS0 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 # Section 1 -@anchorSection1 +@ancS1 -' ] . - - file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. - file2 writeStreamDo: [ :stream | stream nextPutAll: ' See *@anchorSection1@* and *@anchorSection1@*'] . - file2 ensureCreateFile. +# Section 2 +@ancS1 - visitor := MicReferenceChecker new. - self assert: (visitor checkDirectory: dir). - +# Section 3 +@ancS1 + +See *@ancS1@* and *@ancS0@* + +' ]. + defAncS0TripleAncS1RefAncS1AncS0 ensureCreateFile. + ^ defAncS0TripleAncS1RefAncS1AncS0 ] -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testDuplicatedAnchorDir [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defCode1AndReferToCode1 [ - | dir file1 file2 visitor | - self skip. - dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. - dir ensureCreateDirectory. + defCode1AndReferToCode1 := fs / 'defCode1AndReferToCode1.md'. + defCode1AndReferToCode1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 - file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. - file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section -@anchorSection0 +```language=pharo&anchor=Code1 +balbalbalb! +``` -# Section 1 -@anchorSection1 +See *@Code1@* +' ]. + defCode1AndReferToCode1 ensureCreateFile +] -' ] . - file1 ensureCreateFile . +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defEq1AndReferToEq1 [ + + defEq1AndReferToEq1 := fs / 'defEq1AndReferToEq1.md'. + defEq1AndReferToEq1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 - file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. - file2 writeStreamDo: [ :stream | stream nextPutAll: '# Section -@anchorSection3 +$$ %anchor=Eq1 +balbalbalb! +$$ +See *@Eq1@* -# Section 4 -@anchorSection1 +' ]. + defEq1AndReferToEq1 ensureCreateFile +] +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defFig1 [ -'] . - file2 ensureCreateFile . + defFig1 := fs / 'defFig1.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + defFig1 ensureCreateFile. + defFig1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 +![alittle caption. %anchor=Fig1](figures/f.png) - visitor := MicReferenceChecker new. - self deny: ( visitor checkDirectory: dir ) - +' ] ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testFile [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defFig1AndRefFig1 [ - | file visitor | - file := (FileSystem memory / 'myFile.txt') asFileReference. - file ensureCreateFile. - file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 -![alittle caption.](figures/f.png anchor=anchorSection1) + defAndRefFig1 := dir / 'defAndRefFig1.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + defAndRefFig1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 + +![alittle caption.](figures/f.png anchor=Fig1) -See *@anchorSection0@* +See *@Fig1@* ' ]. - visitor := MicReferenceChecker new. - self deny: (visitor checkFile: file) + defAndRefFig1 ensureCreateFile. + ^ defAndRefFig1 ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAFigure [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defFig1AndRefToAncS0 [ - | doc visitor | - doc := Microdown parse: '# Section 1 -![alittle caption.](figures/f.png anchor=anchorSection1) + defFig1AndRefToAnsC0 := fs / 'defFig1AndRefToAncS0.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + defFig1AndRefToAnsC0 ensureCreateFile. + defFig1AndRefToAnsC0 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 +![alittle caption. %anchor=Fig1](figures/f.png) -See *@anchorSection1@* +See *@ancS0@* -'. - visitor := MicReferenceChecker new. - doc accept: visitor. - self assert: visitor isOk +' ] ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAFigureInFile [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defFig1AndRefToAncUnkS0 [ - | file visitor | - file := (FileSystem memory / 'myFile2.txt') asFileReference. - file ensureCreateFile. - file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 -![alittle caption.](figures/f.png anchor=anchorSection1) + defFig1AndRefToAncUnkS0 := fs / 'defFig1AndRefToAncUnkS0.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + defFig1AndRefToAncUnkS0 ensureCreateFile. + defFig1AndRefToAncUnkS0 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 +![alittle caption.](figures/f.png anchor=Fig1) -See *@anchorSection1@* +See *@ancUnkS0@* ' ]. - visitor := MicReferenceChecker new. - self assert: (visitor checkFile: file). + defFig1AndRefToAncUnkS0 ensureCreateFile ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAMathEquation [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> defFig1FilePathToUnk [ - | doc visitor | - doc := Microdown parse: '# Section 1 - -$$ %anchor=anchorSection1 + defAndRefFig1 := dir / 'defAndRefFig1.md'. + defAndRefFig1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 + +![alittle caption.](figures/f.png anchor=Fig1) + +' ]. + defAndRefFig1 ensureCreateFile. + ^ defAndRefFig1 +] + +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> duplicatedFigSecEq [ + + | conflictBetweenFigSecEq | + conflictBetweenFigSecEq := dir / 'duplicatedFigSecEq.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + conflictBetweenFigSecEq writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 + +![a caption 1](figures/f.png anchor=ancS1) + +# Section 1 +@ancS1 + +We have a duplication between the section and the figure. + +# Section 3 +@ancS3 + +We have a duplication between section and equation +$$ %anchor=ancS0 balbalbalb! $$ -See *@anchorSection1@* -'. - visitor := MicReferenceChecker new. - doc accept: visitor. - self assert: visitor isOk +Here we have a duplication between the figure and the equation + +![a caption 1](figures/f.png anchor=fig2) + +$$ %anchor=fig2 +balbalbalb! +$$ + +See *@ancS1@* and *@ancS0@* + +' ]. + conflictBetweenFigSecEq ensureCreateFile. + ^ conflictBetweenFigSecEq ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAMathEquationInFile [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> duplicatedFigSecEqPart1 [ - | file visitor | - file := (FileSystem memory / 'myFile.txt') asFileReference. - file ensureCreateFile. + | duplicatedFigSecEqPart1 | + duplicatedFigSecEqPart1 := dir / 'duplicatedFigSecEqPart1.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + duplicatedFigSecEqPart1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 +![a caption 1](figures/f.png anchor=ancS1) - - file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 - -$$ %anchor=anchorSection1 + +$$ %anchor=fig2 balbalbalb! $$ -See *@anchorSection1@* -' ] . - +# Section 3 +@ancS3 - visitor := MicReferenceChecker new. - self assert: (visitor checkFile: file). - file ensureDelete +' ]. + duplicatedFigSecEqPart1 ensureCreateFile. + ^ duplicatedFigSecEqPart1 ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAnUknownAnchor [ +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> duplicatedFigSecEqPart2 [ - | doc visitor | - doc := Microdown parse: '# Section 1 + | duplicatedFigSecEqPart2 | + duplicatedFigSecEqPart2 := dir / 'duplicatedFigSecEqPart2.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + duplicatedFigSecEqPart2 writeStreamDo: [ :stream | + stream nextPutAll: ' +# Duplication with Figure -See *@anchorSection1@* +@ancS1 -'. +We have a duplication between section and equation +$$ %anchor=ancS0 +balbalbalb! +$$ + +Here we have a duplication between the figure and the equation + +![a caption 1](figures/f.png anchor=fig2) + +See *@ancS1@* and *@ancS0@* + +' ]. + duplicatedFigSecEqPart2 ensureCreateFile. + ^ duplicatedFigSecEqPart2 +] + +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> fileDefAncS1UndAncS0 [ + + fileDefAncS1UndAncS0 := fs / 'fileDefAncS1UndAncS0.md'. + (dir / 'figures' / 'f.png') ensureCreateFile. + fileDefAncS1UndAncS0 ensureCreateFile. + fileDefAncS1UndAncS0 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 + +![alittle caption.](figures/f.png anchor=ancS1) + +See *@ancS0@* + +' ]. + fileDefAncS1UndAncS0 ensureCreateFile +] + +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> fileRefAncS1 [ + + refAncS1 := fs/ 'fileRefAncS1.md'. + refAncS1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 + +See *@ancS1@* +' ]. + refAncS1 ensureCreateFile +] + +{ #category : 'helpers - anchors & references' } +MicReferenceCheckerTest >> refToUnkS1 [ + + refToUnkS1 := fs / 'refToUnkS1.md'. + refToUnkS1 ensureCreateFile. + + refToUnkS1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 + +See *@ancS1@* + +' ] +] + +{ #category : 'tests - single file ok references' } +MicReferenceCheckerTest >> testDefAndReferToACodeAnchorInFile [ + + | visitor | + self defCode1AndReferToCode1. visitor := MicReferenceChecker new. - doc accept: visitor. - self deny: visitor isOk + visitor rootDirectory: dir. + visitor checkProject: defCode1AndReferToCode1. + self assert: visitor isOkay. + self assert: visitor results isEmpty ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReferToAnUknownAnchorInFile [ +{ #category : 'tests - single file ok references' } +MicReferenceCheckerTest >> testDefAndReferToAMathEquationInFile [ - | file visitor | - file := (FileSystem memory / 'myFile.txt') asFileReference. - file ensureCreateFile. + | visitor | + self defEq1AndReferToEq1. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: defEq1AndReferToEq1. + self assert: visitor isOkay. + self assert: visitor results isEmpty +] +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testDefFig1AndRefToAncS0UnknowAnchor [ - - file writeStreamDo: [ :stream | stream nextPutAll: '# Section 1 + | visitor badRefs | + self defFig1AndRefToAncS0. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: defFig1AndRefToAnsC0. + self deny: visitor isOkay. + badRefs := visitor results select: [ :each | each isKindOf: MicAnchorResult ]. + self assert: badRefs first anchorLabel equals: 'ancS0'. + self assert: badRefs first sourceFileReference fullName equals: '/defFig1AndRefToAncS0.md' +] -See *@anchorSection1@* +{ #category : 'tests - single file ok references' } +MicReferenceCheckerTest >> testDefFig1AndRefToAncUnkS0 [ -' ] . - + | visitor | + self defFig1AndRefToAncUnkS0. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkList: { defFig1AndRefToAncUnkS0 }. + self deny: visitor isOkay. + self assert: visitor results size equals: 3. + self assert: visitor results second anchorLabel equals: 'ancUnkS0'. + self + assert: visitor results second sourceFileReference fullName + equals: '/defFig1AndRefToAncUnkS0.md' +] + +{ #category : 'tests - single file ok references' } +MicReferenceCheckerTest >> testDefFig1AndReferToFig1 [ + + | visitor | + self defFig1AndRefFig1. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: defAndRefFig1. + self assert: visitor isOkay. + self assert: visitor results isEmpty +] + +{ #category : 'tests - single file ok references' } +MicReferenceCheckerTest >> testDefFig1ReferToAFigureInFile [ + + | visitor | + self defFig1AndRefFig1. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkList: { defAndRefFig1 }. + self assert: visitor isOkay +] + +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testDefS1ButRefersToS0UnknownAnchor [ + + | visitor | + self fileDefAncS1UndAncS0. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: fileDefAncS1UndAncS0. + self deny: visitor isOkay. + self assert: visitor results size equals: 3. + self assert: visitor results second anchorLabel equals: 'ancS0'. + self assert: visitor results second sourceFileReference fullName equals: '/fileDefAncS1UndAncS0.md' - visitor := MicReferenceChecker new. - self deny: (visitor checkFile: file) . - file ensureDelete ] -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testReportingDuplicatedAnchors [ +{ #category : 'tests - directory api' } +MicReferenceCheckerTest >> testDirWithReferenceInAnotherFile [ - | doc visitor | - doc := Microdown parse: '# Section -@anchorSection0 + | aSectionWithAnchorAndRef file2 visitor | + aSectionWithAnchorAndRef := dir / 'aSectionWithAnchorAndRef.md'. + aSectionWithAnchorAndRef writeStreamDo: [ :stream | + stream nextPutAll: '# Section 1 +@ancS1 -# Section 1 -@anchorSection1 +See *@ancS1@* and *@ancS2@* -# Section 2 -@anchorSection1 +' ]. + aSectionWithAnchorAndRef ensureCreateFile. -# Section 3 -@anchorSection1 + file2 := dir / 'justReferenceToSection.md'. + file2 writeStreamDo: [ :stream2 | + stream2 nextPutAll: ' +# Section 2 +@ancS2 -See *@anchorSection1@* and *@anchorSection0@* +Just a reference See *@ancS1@* ' ]. + file2 ensureCreateFile. -'. visitor := MicReferenceChecker new. - doc accept: visitor. - self deny: visitor isOk. - self assert: (visitor duplicatedAnchors collect: [:each | each anchorLabel ]) equals: OrderedCollection <- #('anchorSection1' 'anchorSection1') -] + visitor checkDirectory: dir. + self assert: visitor isOkay + -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testReportingDuplicatedAnchorsInFile [ - | file visitor | - file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. - file ensureCreateFile. +] +{ #category : 'skipped for now' } +MicReferenceCheckerTest >> testDuplicatedAnchorDir [ + | file1 file2 visitor | + self skip. + dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. + dir ensureCreateDirectory. - file writeStreamDo: [ :stream | stream nextPutAll: '# Section + file1 := (FileSystem workingDirectory / 'myDirectory' / 'file1.txt') asFileReference. + file1 writeStreamDo: [ :stream | stream nextPutAll: '# Section @anchorSection0 # Section 1 @anchorSection1 -# Section 2 -@anchorSection1 -# Section 3 +' ] . + file1 ensureCreateFile . + + file2 := (FileSystem workingDirectory / 'myDirectory' / 'file2.txt') asFileReference. + file2 writeStreamDo: [ :stream | stream nextPutAll: '# Section +@anchorSection3 + +# Section 4 @anchorSection1 -See *@anchorSection1@* and *@anchorSection0@* '] . + file2 ensureCreateFile . + + visitor := MicReferenceChecker new. + + self deny: ( visitor checkDirectory: dir ) +] - visitor := MicReferenceChecker new. - self deny: (visitor checkFile: file). - self - assert: (visitor duplicatedAnchors collect: [:each | each anchorLabel]) - equals: OrderedCollection <- #('anchorSection1' 'anchorSection1'). - file ensureDelete +{ #category : 'tests - directory api' } +MicReferenceCheckerTest >> testDuplicatedAnchorInDifferentFilesOfTheSameDir [ + + | file1 file2 visitor dict duplicated | + file1 := dir / 'file1.md'. + file1 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS0 + +# Section 1 +@ancS1 +' ]. + file1 ensureCreateFile. + file2 := dir / 'file2.md'. + file2 writeStreamDo: [ :stream | + stream nextPutAll: '# Section +@ancS3 + +# Section 4 +@ancS1 +' ]. + file2 ensureCreateFile. + + visitor := MicReferenceChecker new. + visitor checkDirectory: dir. + self deny: visitor isOkay. + + self assert: visitor duplicatedAnchors size equals: 2. + self + assert: visitor duplicatedAnchors first anchorLabel + equals: 'ancS1'. + dict := visitor results groupedBy: [ :each | each class ]. + duplicated := (dict at: MicDuplicatedAnchorResult) first. + self + assert: visitor results first sourceFileReference fullName + equals: '/myDirectory/file2.md'. + self assert: duplicated anchorLabel equals: 'ancS1' ] -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testReportingDuplicatedFigures [ +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testDuplicatedAnchors [ - | doc visitor | - doc := Microdown parse: '# Section -@anchorSection0 + | defAncS0TripleAncS1RefAncS1AncS0 checker dict dup1 | + defAncS0TripleAncS1RefAncS1AncS0 := self defAncS0TripleAncS1RefAncS1AncS0. + checker := MicReferenceChecker new. + checker checkList: { defAncS0TripleAncS1RefAncS1AncS0 }. + self deny: checker isOkay. + self + assert: (checker results collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'ancS1' 'ancS1' 'ancS1' ). -![a caption 1](figures/f.png anchor=anchorSection1) + dict := checker results groupedBy: [ :each | each class ]. -![a caption 2](figures/f.png anchor=anchorSection1) + dup1 := (dict at: MicDuplicatedAnchorResult) first. + self + assert: dup1 sourceFileReference fullName + equals: '/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'. + self assert: dup1 anchorLabel equals: 'ancS1' +] -![a caption 3](figures/f.png anchor=anchorSection2) +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testDuplicatedBetweenSectionFigureEq [ -See *@anchorSection1@* and *@anchorSection3@* + | conflictBetweenFigSecEq visitor | + conflictBetweenFigSecEq := self duplicatedFigSecEq. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkList: { conflictBetweenFigSecEq }. + self deny: visitor isOkay. + self + assert: (self anchorNames: visitor) + equals: OrderedCollection + <- #( 'ancS1' 'ancS1' 'ancS0' 'ancS0' 'fig2' 'fig2' ) +] -'. +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testDuplicatedBetweenSectionFigureEqInDifferentFile [ + + | duplicatedFigSecEqPart1 duplicatedFigSecEqPart2 visitor | + duplicatedFigSecEqPart1 := self duplicatedFigSecEqPart1. + duplicatedFigSecEqPart2 := self duplicatedFigSecEqPart2. visitor := MicReferenceChecker new. - doc accept: visitor. - self deny: visitor isOk. + visitor rootDirectory: dir. + visitor checkList: { + duplicatedFigSecEqPart1. + duplicatedFigSecEqPart2 }. + self deny: visitor isOkay. self - assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'anchorSection1' ) + assert: (self anchorNames: visitor) + equals: OrderedCollection + <- #( 'ancS1' 'ancS1' 'ancS0' 'ancS0' 'fig2' 'fig2' ) ] -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testReportingDuplicatedFiguresInFile [ +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testDuplicatedFigures [ - | file visitor | - file := (FileSystem workingDirectory / 'myFile.txt') asFileReference. - file ensureCreateFile. - file writeStreamDo: [ :stream | stream nextPutAll: '# Section -@anchorSection0 + | doubleFig1 checker dict dup1 | + doubleFig1 := self defAncS0DoubleFig1Fig2RefAncS1. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkList: { doubleFig1 }. + self deny: checker isOkay. + self + assert: (self anchorNames: checker) + equals: + OrderedCollection <- #( 'fig1' 'fig1' 'fig1' 'fig1' 'ancS1' ). -![a caption 1](figures/f.png anchor=anchorSection1) + dict := checker results groupedBy: [ :each | each class ]. -![a caption 2](figures/f.png anchor=anchorSection1) + dup1 := (dict at: MicDuplicatedAnchorResult) first. + self + assert: dup1 sourceFileReference fullName + equals: '/myDirectory/defAncS0DoubleFig1Fig2RefAncS1.md'. + self assert: dup1 anchorLabel equals: 'fig1' +] -![a caption 3](figures/f.png anchor=anchorSection2) +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testDuplicatedMaths [ -See *@anchorSection1@* and *@anchorSection3@* + | defAnCS0DoubleEq2DoubleEq1RefEq1 checker dict dup1 dup2 | + defAnCS0DoubleEq2DoubleEq1RefEq1 := self defAnCS0DoubleEq2DoubleEq1RefEq1. + checker := MicReferenceChecker new. + checker checkList: { defAnCS0DoubleEq2DoubleEq1RefEq1 }. + self deny: checker isOkay. + self + assert: checker results first sourceFileReference fullName + equals: defAnCS0DoubleEq2DoubleEq1RefEq1 fullName. + self + assert: (checker results collect: [ :each | each anchorLabel ]) + equals: OrderedCollection <- #( 'Eq2' 'Eq2' 'Eq1' 'Eq1' ). + + dict := checker results groupedBy: [ :each | each class ]. + + dup1 := (dict at: MicDuplicatedAnchorResult) first. + self assert: dup1 sourceFileReference fullName equals: '/myDirectory/defAnCS0DoubleEq2DoubleEq1RefEq1.md'. + self assert: dup1 anchorLabel equals: 'Eq2'. + + dup2 := (dict at: MicDuplicatedAnchorResult) third. + self assert: dup2 sourceFileReference fullName equals: '/myDirectory/defAnCS0DoubleEq2DoubleEq1RefEq1.md'. + self assert: dup2 anchorLabel equals: 'Eq1'. +] -']. - visitor := MicReferenceChecker new. - self deny: (visitor checkFile: file). +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testFigureFilePathToUnkFile [ + + | visitor refToUnexistingFiles | + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: self defFig1FilePathToUnk. + self deny: visitor isOkay. + self assert: visitor results size equals: 2. + refToUnexistingFiles := (visitor results groupedBy: [ :each | each class name ]) at: #MicUndefinedFigureFileResult. + self assert: refToUnexistingFiles size equals: 1. + self assert: refToUnexistingFiles first what equals: 'figures/f.png'. + self assert: refToUnexistingFiles first figureFileString equals: 'figures/f.png'. + self - assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'anchorSection1' ). - file ensureDelete + assert: refToUnexistingFiles first sourceFileReference fullName + equals: '/myDirectory/defAndRefFig1.md' +] + +{ #category : 'tests - full project' } +MicReferenceCheckerTest >> testFullProjectWithFowardAndBackWardRefBetweenTwoFiles [ + + | checker | + self createProjectCorrectReferencesOnTwoFiles. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkProject: section1. + self assert: checker isOkay +] + +{ #category : 'tests - full project' } +MicReferenceCheckerTest >> testFullProjectWithReferencesToUnknowAnchor [ + + | checker dict unk undefined | + self createProjectBadAndCorrectReferences. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkProject: section1. + self deny: checker isOkay. + self assert: checker results size equals: 2. + + dict := checker results groupedBy: [ :each | each class ]. + + unk := (dict at: MicReferenceToUnexistingAnchorResult) first. + self assert: unk sourceFileReference fullName equals: '/myDirectory/sections/section2.md'. + self assert: unk anchorLabel equals: 'ancUnkS2'. + + undefined := (dict at: MicUndefinedInputFileResult) first. + self assert: undefined sourceFileReference fullName equals: '/myDirectory/sections/section2.md'. + + ] -{ #category : 'tests - duplicated anchors' } -MicReferenceCheckerTest >> testReportingDuplicatedMaths [ +{ #category : 'tests - internal - parse only' } +MicReferenceCheckerTest >> testLowLevelAllReferencesAreCorrect [ | doc visitor | - doc := Microdown parse: '# Section -@anchorSection0 + doc := Microdown parse: '# Section 1 +@anchorSection1 -$$ %anchor=anchorSection1 -balbalbalb! -$$ +See *@anchorSection1@* -$$ %anchor=anchorSection1 -balbalbalb! -$$ +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + visitor collectReferencesToUnexistingAnchors. + self assert: visitor isOkay +] -$$ %anchor=anchorSection1 -balbalbalb! -$$ +{ #category : 'tests - internal - parse only' } +MicReferenceCheckerTest >> testLowLevelReferToAFigure [ -$$ %anchor=anchorSection3 -balbalbalb! -$$ + | doc visitor | + doc := Microdown parse: '# Section 1 +![alittle caption.](figures/f.png anchor=anchorSection1) -See *@anchorSection1@* and *@anchorSection3@* +See *@anchorSection1@* '. + doc fromFile: 'file.md' asFileReference. visitor := MicReferenceChecker new. doc accept: visitor. - self deny: visitor isOk. - self - assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'anchorSection1' 'anchorSection1' ) + visitor collectReferencesToUnexistingAnchors. + self assert: visitor results first class equals: MicUndefinedFigureFileResult + ] -{ #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! -$$ +{ #category : 'tests - internal - parse only' } +MicReferenceCheckerTest >> testLowLevelReferToAMathEquation [ + | doc visitor | + doc := Microdown parse: '# Section 1 + $$ %anchor=anchorSection1 balbalbalb! $$ +See *@anchorSection1@* -$$ %anchor=anchorSection1 -balbalbalb! -$$ +'. + visitor := MicReferenceChecker new. + doc accept: visitor. + visitor collectReferencesToUnexistingAnchors. + self assert: visitor isOkay +] -$$ %anchor=anchorSection3 -balbalbalb! -$$ +{ #category : 'tests - internal - parse only' } +MicReferenceCheckerTest >> testLowLevelReferToAnUknownAnchor [ -See *@anchorSection1@* and *@anchorSection3@* + | doc visitor | + doc := Microdown parse: '# Section 1 -'] . +See *@anchorSection1@* - visitor := MicReferenceChecker new. - self deny: (visitor checkFile: file). - self - assert: (visitor duplicatedAnchors collect: [ :each | each anchorLabel ]) - equals: OrderedCollection <- #( 'anchorSection1' 'anchorSection1' ). - file ensureDelete +'. + doc fromFile: 'fakedFile'. + visitor := MicReferenceChecker new. + doc accept: visitor. + visitor collectReferencesToUnexistingAnchors. + self deny: visitor isOkay ] -{ #category : 'tests' } -MicReferenceCheckerTest >> testReportingUnknownAnchor [ +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testRefToUnkS1 [ - | doc visitor | - doc := Microdown parse: '# Section -@anchorSection0 + | visitor | + self refToUnkS1. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: refToUnkS1. + self deny: visitor isOkay + -# Section 1 -@anchorSection1 +] -See *@anchorSection1@* and *@anchorSection2@* +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testRefersToUnkAncS1 [ -'. + | visitor | + self fileRefAncS1. visitor := MicReferenceChecker new. - doc accept: visitor. - self deny: visitor isOk. - self - assert: (visitor unknownAnchors collect: [ :each | each anchorLabel ]) - equals: (OrderedCollection <- #('anchorSection2')) + visitor rootDirectory: dir. + visitor checkProject: refAncS1. + self deny: visitor isOkay. + self assert: visitor results size equals: 1. + self assert: visitor results first anchorLabel equals: 'ancS1'. + self assert: visitor results first sourceFileReference fullName equals: '/fileRefAncS1.md'. +] + +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testReportingSTONFormatDuplicatedAnchors [ + + | defAncS0TripleAncS1RefAncS1AncS0 checker | + self skip. + defAncS0TripleAncS1RefAncS1AncS0 := self defAncS0TripleAncS1RefAncS1AncS0. + checker := MicReferenceChecker new. + checker checkList: { defAncS0TripleAncS1RefAncS1AncS0 }. + + self assert: checker reportSTONFormat equals: '[''type'':''DuplicatedAnchor'',[''ancS1'':''/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'',''ancS1'':''/myDirectory/defAncS0TripleAncS1RefAncS1AncS0.md'']]' + + +] + +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testReportingSTONFormatDuplicatedFigure [ + + | checker | + self skip. + self fileRefAncS1. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkProject: refAncS1. + + self assert: checker reportSTONFormat equals: '[''type'':''UndefinedAnchor'',[''ancS1'':''/fileRefAncS1.md'']]' +] + +{ #category : 'tests - duplicated' } +MicReferenceCheckerTest >> testReportingSTONFormatUnknownAnchorDir [ + + | checker | + self skip. + self fileRefAncS1. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkProject: refAncS1. + + self assert: checker reportSTONFormat equals: '[''type'':''UndefinedAnchor'',[''ancS1'':''/fileRefAncS1.md'']]' + ] -{ #category : 'tests' } +{ #category : 'skipped for now' } MicReferenceCheckerTest >> testReportingUnknownAnchorDir [ - | dir file1 file2 visitor | + | file1 file2 visitor | self skip. dir := (FileSystem workingDirectory / 'myDirectory') asFileReference. dir ensureCreateDirectory. @@ -458,3 +878,48 @@ MicReferenceCheckerTest >> testReportingUnknownAnchorDir [ dir ensureDelete ] + +{ #category : 'tests - undefined files' } +MicReferenceCheckerTest >> testUndefinedInputFilesAreMentionedInResult [ + + | checker | + self createProjectWithUnexistingSection3And5. + checker := MicReferenceChecker new. + checker rootDirectory: dir. + checker checkProject: section1. + + self assert: checker results size equals: 3. + checker results do: [ :each | + self + assert: each class + equals: MicUndefinedInputFileResult]. + + (checker results collect: [ :each | each inputFileBlock path path "Ugly API to improve later" ]) + do: [ :each | + self + assert: + ({ + '/myDirectory/sections/section3.md' . + '/myDirectory/sections/subsections/section5.md' . + '/myDirectory/sections/subsections/section3.md' + } includes: each) + ] + +] + +{ #category : 'tests - single file' } +MicReferenceCheckerTest >> testUnreferencedFigure [ + + | visitor badRefs unref | + self defFig1. + visitor := MicReferenceChecker new. + visitor rootDirectory: dir. + visitor checkProject: defFig1. + self deny: visitor isOkay. + badRefs := (visitor results groupedBy: [:each | each class name]) at: #MicUnreferencedFigureResult. + unref := badRefs first. + self assert: badRefs size equals: 1. + self assert: unref figurePath equals: 'figures/f.png'. + self assert: unref anchorLabel equals: 'Fig1'. + self assert: unref sourceFileReference fullName equals: '/defFig1.md' +] diff --git a/src/Microdown-BookTester/MicReferenceChecker.class.st b/src/Microdown-BookTester/MicReferenceChecker.class.st index b8e5e306a..21496d9a0 100644 --- a/src/Microdown-BookTester/MicReferenceChecker.class.st +++ b/src/Microdown-BookTester/MicReferenceChecker.class.st @@ -1,80 +1,183 @@ " -I'm a little tool that checks whether a document has (1) references to undeclared anchors or (2) duplicated anchors. +I'm a nice little tool that checks whether a document has +- references to undeclared anchors +- duplicated anchors -For now Microdown is file agnostic so this is impossible to report in which file such duplication or reference occurs. -Once file support is introduced I should be revised to report better information to the user. +I check +- figures +- math equations +- or plain references. + +I use the `MicFileCollector` to support all the file input relations. +This lets the user have for example unused, broken or underway files on the side. +As soon as they are not used I do not analyse them. + +# API +My main API to integrate with the ReportWriter is the method `checkProject:` +I support the following API to be able to chained over a report writer: +- result/results: +- reportWriter: + + +# Potential enhancements + +When we use the checkDir or the checkFile: API, it should still do the full closure of used files (i.e. use the file collector). This is a lot simpler to understand for the user. Having a systematic approach is the way to go. " Class { #name : 'MicReferenceChecker', - #superclass : 'MicrodownVisitor', + #superclass : 'MicChecker', #instVars : [ 'references', - 'anchors', - 'duplicatedAnchors' + 'duplicatedAnchors', + 'listOfFiles', + 'figures', + 'anchoringEntities' ], - #category : 'Microdown-ReferenceChecker', - #package : 'Microdown-ReferenceChecker' + #category : 'Microdown-BookTester-References', + #package : 'Microdown-BookTester', + #tag : 'References' } +{ #category : 'accessing' } +MicReferenceChecker >> addDuplicatedAnchor: anAnchor [ + + | micResultInstance | + micResultInstance := MicDuplicatedAnchorResult new. + micResultInstance + anchorLabel: anAnchor anchorLabel; + sourceFileReference: anAnchor fromFile. + results add: micResultInstance +] + { #category : 'visiting' } -MicReferenceChecker >> check: aDocument [ - "Pay attention checking a file in isolation is DIFFERENT from a list, because document - can have references between them and the checker should be shared amongst the documents - since it collects the references." +MicReferenceChecker >> addDuplicatedFirstAnchor: anAnchor [ - aDocument accept: self. - ^ self isOk - - + anchoringEntities do: [ :each | + each anchorLabel = anAnchor anchorLabel ifTrue: [ + (duplicatedAnchors includes: each) ifFalse: [ + duplicatedAnchors add: each. + self addDuplicatedAnchor: each ] ] ] +] + +{ #category : 'accessing' } +MicReferenceChecker >> addReferenceToUnexistingAnchor: anAnchorReference [ + | micResultInstance | + micResultInstance := MicReferenceToUnexistingAnchorResult new. + micResultInstance + anchorLabel: anAnchorReference anchorLabel; + sourceFileReference: anAnchorReference fromFile. + results add: micResultInstance ] -{ #category : 'visiting' } +{ #category : 'accessing' } +MicReferenceChecker >> addUnexistingFigureFile: aFigure [ + + | micResultInstance | + micResultInstance := MicUndefinedFigureFileResult new. + micResultInstance + figureFileString: aFigure reference path; + sourceFileReference: aFigure fromFile. + results add: micResultInstance +] + +{ #category : 'accessing' } +MicReferenceChecker >> addUnreferencedFigure: aFigure [ + + | micResultInstance | + micResultInstance := MicUnreferencedFigureResult new. + micResultInstance + figurePath: aFigure reference relativePath; + anchorLabel: aFigure anchorLabel; + sourceFileReference: aFigure fromFile. + results add: micResultInstance +] + +{ #category : 'main API' } 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 . " - ^ self checkList: aDir allFiles + self checkList: aDir allFiles ] -{ #category : 'visiting' } -MicReferenceChecker >> checkFile: aFile [ - "Will parse the given file and invite the visitor and return visitor isOk value" +{ #category : 'internal' } +MicReferenceChecker >> checkList: aCollection [ + "Pay attention checking a file in isolation is DIFFERENT from a list, because a document can have references between them and the checker should be shared amongst the documents since it collects the references." - | document | - document := Microdown parseFile: aFile. - ^ self check: document - - + aCollection do: [ :each | + | document | + document := Microdown parseFile: each. + document accept: self ]. + self + collectReferencesToUnexistingAnchors; + collectUnreferencedFigures + +] +{ #category : 'main API' } +MicReferenceChecker >> checkProject: aFileReference [ + "Given the root of the document such as an index.md file, check the complete project." + + | collector | + collector := self fileCollectorForMainFileReference: aFileReference. + self handleUndefinedFilesFrom: collector. + listOfFiles := collector visitedFileStrings collect: [ :file | rootDirectory resolve: file ]. + self checkList: listOfFiles ] -{ #category : 'visiting' } -MicReferenceChecker >> checkList: aCollection [ - "Pay attention checking a file in isolation is DIFFERENT from a list, because document - can have references between them and the checker should be shared amongst the documents - since it collects the references." +{ #category : 'internal' } +MicReferenceChecker >> collectReferencesToUnexistingAnchors [ + "Should be called after all the docs are visited otherwise the result can be wrong." - aCollection do: [ :each | self checkFile: each ]. - ^ self isOk - - + | badReference existingAnchorNames | + existingAnchorNames := anchoringEntities collect: [ :each | each anchorLabel ]. + badReference := references reject: [ :anchorReference | + existingAnchorNames includes: anchorReference anchorLabel ]. + badReference do: [ :each | self addReferenceToUnexistingAnchor: each ] +] + +{ #category : 'internal' } +MicReferenceChecker >> collectUnreferencedFigures [ + | unreferencedFigures anchorReferenceNames | + anchorReferenceNames := references collect: [ :each | each anchorLabel ]. + unreferencedFigures := figures reject: [ :figure | anchorReferenceNames includes: figure anchorLabel ]. + unreferencedFigures do: [ :each | self addUnreferencedFigure: each ] ] -{ #category : 'reporting' } +{ #category : 'internal' } MicReferenceChecker >> duplicatedAnchors [ ^ duplicatedAnchors ] +{ #category : 'accessing' } +MicReferenceChecker >> fileSystem: aFileSystem [ + + rootDirectory := aFileSystem +] + { #category : 'visiting' } MicReferenceChecker >> handleAnchorOf: anElement [ anElement hasAnchor ifFalse: [ ^ self ]. - (self hasAlreadyDefinedAs: anElement) - ifTrue: [ duplicatedAnchors add: anElement ]. - anchors add: anElement - + (self hasAlreadyDefinedAs: anElement) ifTrue: [ + duplicatedAnchors add: anElement. + self addDuplicatedAnchor: anElement. + self addDuplicatedFirstAnchor: anElement. ]. + anchoringEntities add: anElement +] + +{ #category : 'internal' } +MicReferenceChecker >> handleUndefinedFilesFrom: collector [ + + collector unexistingFiles do: [ :each | + results add: (MicUndefinedInputFileResult new + inputFileBlock: each ; + sourceFileReference: each fromFile; + yourself ) + + ] ] { #category : 'visiting' } @@ -82,7 +185,7 @@ MicReferenceChecker >> hasAlreadyDefinedAs: anAnchor [ | alreadyDefined | alreadyDefined := false. - anchors do: + anchoringEntities do: [ :each | each anchorLabel = anAnchor anchorLabel ifTrue: [ alreadyDefined := true ] ]. ^ alreadyDefined @@ -92,16 +195,34 @@ MicReferenceChecker >> hasAlreadyDefinedAs: anAnchor [ MicReferenceChecker >> initialize [ super initialize. + + rootDirectory := FileSystem workingDirectory. references := OrderedCollection new. - anchors := OrderedCollection new. - duplicatedAnchors := OrderedCollection new + figures := OrderedCollection new. + anchoringEntities := OrderedCollection new. + duplicatedAnchors := OrderedCollection new. "by default if the reporter is not set from outside + then it shares the results of this current checker." + reportWriter := MicAnalysisReportWriter new results: results ] { #category : 'testing' } MicReferenceChecker >> isOk [ - + self flag: #fixThisLogic. + "isOkay vs. isOk ???" + ^ duplicatedAnchors isEmpty and: [ - references allSatisfy: [ :each | self hasAlreadyDefinedAs: each ] ] + references allSatisfy: [ :each | self hasAlreadyDefinedAs: each ] ] +] + +{ #category : 'reporting' } +MicReferenceChecker >> report [ + + ^ reportWriter report +] + +{ #category : 'reporting' } +MicReferenceChecker >> reportSTONFormat [ + ^ reportWriter reportSTONFormat ] { #category : 'internal' } @@ -119,23 +240,40 @@ MicReferenceChecker >> unknownAnchors [ { #category : 'visiting' } MicReferenceChecker >> visitAnchor: anAnchor [ - + "the problem with visit anchor is that it only concerns + @anchor and not the one of figure. + So we should then keep a list of anchorNames for the figures, math equations + or reify these anchor - would be nicer" + | isAlready | isAlready := self hasAlreadyDefinedAs: anAnchor. - isAlready ifTrue: [ duplicatedAnchors add: anAnchor ]. - anchors add: anAnchor + isAlready ifTrue: [ + duplicatedAnchors add: anAnchor. + self addDuplicatedAnchor: anAnchor. + self addDuplicatedFirstAnchor: anAnchor. ]. + anchoringEntities add: anAnchor ] { #category : 'visiting' } MicReferenceChecker >> visitAnchorReference: anAnchorReference [ - + references add: anAnchorReference ] { #category : 'visiting' } -MicReferenceChecker >> visitFigure: aFigure [ +MicReferenceChecker >> visitCode: aCodeBlock [ + + self handleAnchorOf: aCodeBlock +] - self handleAnchorOf: aFigure +{ #category : 'visiting' } +MicReferenceChecker >> visitFigure: aFigure [ + + figures add: aFigure. + self handleAnchorOf: aFigure. + "check for unexisting file." + (aFigure fromFile parent / aFigure reference path) exists + ifFalse: [ self addUnexistingFigureFile: aFigure ] ] { #category : 'visiting' } diff --git a/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st b/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st index 852578258..a08d40caa 100644 --- a/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st +++ b/src/Microdown-HTMLExporter-Tests/MicHTMLExporterTest.class.st @@ -12,6 +12,44 @@ Class { #tag : 'HTML' } +{ #category : 'tests' } +MicHTMLExporterTest class >> testParameters [ + + ^ ParametrizedTestMatrix new + forSelector: #writer addOptions: { MicHTMLVisitor }; + forSelector: #factory addOptions: { MicMicrodownSnippetFactory }; + forSelector: #parser addOptions: { Microdown }; + forSelector: #newLine addOptions: { String cr . String lf . String crlf }; + yourself +] + +{ #category : 'accessing' } +MicHTMLExporterTest >> factory: aFactory [ + self flag: #todo. + "This is horrible either factory: should be renamed factoryClass or we should remove the new." + factory := aFactory new +] + +{ #category : 'accessing' } +MicHTMLExporterTest >> newLine [ + + ^ newLine +] + +{ #category : 'accessing' } +MicHTMLExporterTest >> newLine: aNewLine [ + (aNewLine = String cr) ifTrue:[ writer crAsNewLine ]. + (aNewLine = String lf) ifTrue:[ writer lfAsNewLine ]. + (aNewLine = String crlf) ifTrue:[ writer crlfAsNewLine ]. + newLine := aNewLine +] + +{ #category : 'tests - paragraph' } +MicHTMLExporterTest >> newLineParagraphAround: aString [ + + ^ newLine, '

    ', aString, '

    ', newLine +] + { #category : 'tests - list' } MicHTMLExporterTest >> orderedListString [ @@ -23,29 +61,210 @@ Here is a list: ' ] -{ #category : 'utils' } +{ #category : 'utilities' } MicHTMLExporterTest >> parse: aString andCheckWeGet: aResultingString [ - | mic | + | mic c | mic := parser parse: aString. - self assert: (writer visit: mic) contents equals: aResultingString + writer visit: mic. + c := writer contents. + self assert: c equals: aResultingString. + ^ c +] + +{ #category : 'utilities' } +MicHTMLExporterTest >> parser [ + ^ parser +] + +{ #category : 'accessing' } +MicHTMLExporterTest >> parser: aParser [ + parser := aParser new +] + +{ #category : 'tests - paragraph' } +MicHTMLExporterTest >> pharoPage [ + + ^ + '{ +"title" : "About Pharo", +"layout" : "index", +"publishDate" : "2025-06-01" +} + +# Pharo + +[Pharo](http://www.pharo.org) is a beautiful dynamically-typed reflective pure object-oriented language that we have been developing since 2008. +It is inspired by Smalltalk (I''m extremely grateful to Alan Kay and Dan Ingalls - they were so visionary and right). Now our vision for Pharo is to reinvent Smalltalk and produce a better system. +From that perspective, I''m used to say that Pharo is what we have and not what we want. +In essence, Pharo is the beginning of the journey and not the final goal. And you can change its future. + +You can check what companies are saying about it: [Video](https://youtu.be/6tdkKNX2g4s) + +There are many aspects I would like to see being explored either by us or by others. +If you want to explore some of the following aspects, please go and let us know. +I''m really interested in any topics that evolve Pharo into a better Pharo. + +' +] + +{ #category : 'tests - paragraph' } +MicHTMLExporterTest >> testAccents [ + + self parse: 'éà' andCheckWeGet: (self newLineParagraphAround: +'éà') +] + +{ #category : 'utilities' } +MicHTMLExporterTest >> testAnchor [ + + self parse: factory anchorSample andCheckWeGet: '' + +] + +{ #category : 'utilities' } +MicHTMLExporterTest >> testCodeCreatesInnerText [ + + | code | + code := (self parser parse: '```a b```') children first. + self assert: code class equals: MicCodeBlock. + self assert: code language equals: 'a b```' +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testCodeWithoutParam [ + + | doc | + doc := (parser parse: '``` +ab +foo bar +```') children first. + writer visit: doc. + self assert: writer contents equals: newLine , +'
    ab', newLine ,
    +'foo bar
    ', newLine +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigure [ + + self + parse: factory figureSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ') +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testFigureBold [ + + self + parse: factory figureBoldSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ' ) +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureItalic [ + + self + parse: factory figureItalicSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureNested [ + + self + parse: factory figureNestedSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo_
    Foo_
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureReal [ + + self + parse: factory figureRealSample + andCheckWeGet: (self newLineParagraphAround: +'
    A logo png under figures folder
    A logo png under figures folder
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureStrike [ + + self + parse: factory figureStrikeSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureWithLabelWithoutSize [ + + self + parse: factory figureWithLabelWithoutSizeSample + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureWithoutCaption [ + + self + parse: factory figureWithoutCaptionSample + andCheckWeGet: (self newLineParagraphAround: +'
    ') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testFigureWithoutSizeAndLabel [ + + self + parse: factory figureSampleWithoutSizeAndLabel + andCheckWeGet: (self newLineParagraphAround: +'
    Foo
    Foo
    ') ] -{ #category : 'running' } -MicHTMLExporterTest >> setUp [ - super setUp. - parser := MicroDownParser new. - writer := MicHTMLWriter new. - factory := MicMicrodownSnippetFactory new +{ #category : 'tests' } +MicHTMLExporterTest >> testGoutDeFraise [ + + self + parse: factory figureGoutDeFraise + andCheckWeGet: (self newLineParagraphAround: +'
    Proposition pour le thème :  Un goût de fraise
    Proposition pour le thème : Un goût de fraise
    ') ] { #category : 'tests' } MicHTMLExporterTest >> testHeaderLevel1 [ - self parse: factory headerLevel1Sample andCheckWeGet: writer usedNewLine , '

    Foo

    ' + self + parse: factory headerLevel1Sample + andCheckWeGet: newLine , '

    Foo

    ' ] +{ #category : 'tests' } +MicHTMLExporterTest >> testHeaderLevel2 [ + + self + parse: factory headerLevel2Sample + andCheckWeGet: newLine , '

    Foo

    ' +] + +{ #category : 'tests - metadata' } +MicHTMLExporterTest >> testMetaDataIsIgnored [ + + self parse: factory metaDataSample andCheckWeGet: '' +] + +{ #category : 'tests - metadata' } +MicHTMLExporterTest >> testMetaDataIsNotIgnored [ + + writer doNotIgnoreMetaData. + self parse: factory metaDataSample andCheckWeGet: '' +] + { #category : 'tests - list' } MicHTMLExporterTest >> testOrderedList [ @@ -64,6 +283,124 @@ MicHTMLExporterTest >> testOrderedListIsRecognized [ self assert: doc children second class equals: MicOrderedListBlock ] +{ #category : 'tests - paragraph' } +MicHTMLExporterTest >> testParagraph [ + + self parse: factory paragraphSample andCheckWeGet: (self newLineParagraphAround: 'Foo') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testParagraphLongWithAccents [ + + self parse: factory paragraphOnMultipleLinesSample andCheckWeGet: (self newLineParagraphAround: +'Je ne connais pas la peur, car la peur tue l''esprit. La peur est la petite mort qui conduit à l''oblitération totale. J''affonterai ma peur. Je lui permettrais de passer sur moi, au travers de moi. Et lorsqu''elle sera passée, je tournerai mon oeil interieur sur son chemin. Et là où elle sera passée, il n''y aura plus rien, rien que moi.') +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testParagraphNestedSample [ + + self parse: factory paragraphNestedSample andCheckWeGet: (self newLineParagraphAround: +'this is a paragraph') +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testParagraphWithBold [ + + self + parse: factory paragraphBoldSample + andCheckWeGet: (self newLineParagraphAround: +'this is a paragraph') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testParagraphWithItalic [ + + self parse: factory paragraphItalicSample andCheckWeGet: (self newLineParagraphAround: +'this is a paragraph') +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testParagraphWithMonospace [ + + self parse: factory paragraphMonospaceSample andCheckWeGet: (self newLineParagraphAround: +'this is a paragraph') +] + +{ #category : 'tests - paragraph' } +MicHTMLExporterTest >> testPharoPage [ + + self parse: self pharoPage andCheckWeGet: newLine, '

    Pharo

    ', newLine, +'

    Pharo is a beautiful dynamically-typed reflective pure object-oriented language that we have been developing since 2008.
    It is inspired by Smalltalk (I''m extremely grateful to Alan Kay and Dan Ingalls - they were so visionary and right). Now our vision for Pharo is to reinvent Smalltalk and produce a better system.
    From that perspective, I''m used to say that Pharo is what we have and not what we want.
    In essence, Pharo is the beginning of the journey and not the final goal. And you can change its future.

    ', newLine, newLine, + +'

    You can check what companies are saying about it: Video

    ', newLine, newLine, + +'

    There are many aspects I would like to see being explored either by us or by others.
    If you want to explore some of the following aspects, please go and let us know.
    I''m really interested in any topics that evolve Pharo into a better Pharo.

    ', newLine +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testQuote [ + + self parse: factory quoteSample andCheckWeGet: + 'Foo' +] + +{ #category : 'tests - raw' } +MicHTMLExporterTest >> testRawOnMultipleLinesIsNotRawButTwoPargraphs [ + + | mic | + mic := parser + parse: 'some text here!{{ ', self newLine,' + macOs arm64 }}...and there'. + "notice that I did on purpose that I do not have space around" + + self assert: mic children size equals: 2. + self assert: mic children first class equals: MicParagraphBlock . + self assert: mic children second class equals: MicParagraphBlock . + + + +] + +{ #category : 'tests - raw' } +MicHTMLExporterTest >> testRawOnSingleLineShouldNotGoToTheNextLine [ + + | mic | + mic := parser + parse: 'some text here!{{ macOs arm64 }}...and there'. + "notice that I did on purpose that I do not have space around" + + writer visit: mic children first. + self + assert: writer contents + equals: + ( +self newLine, '

    some text here! macOs arm64 ...and there

    ', self newLine + + ) + + +] + +{ #category : 'tests - formats' } +MicHTMLExporterTest >> testStrike [ + + self parse: factory strikethroughFormatSample andCheckWeGet: (self newLineParagraphAround: +'Foo') +] + +{ #category : 'tests' } +MicHTMLExporterTest >> testTable [ + + | micTable | + micTable := parser parse: factory tableSample. + writer visit: micTable. + self assert: writer contents equals: newLine , +'', newLine , +'', newLine , +'', newLine , +'
    aaabjkhjh
    barrab
    ' +] + { #category : 'tests - list' } MicHTMLExporterTest >> testUnorderedList [ @@ -75,3 +412,8 @@ MicHTMLExporterTest >> testUnorderedList [ '
  • Bar
  • ', newLine , '' ] + +{ #category : 'accessing' } +MicHTMLExporterTest >> writer: aWriter [ + writer := aWriter new +] diff --git a/src/Microdown-HTMLExporter/MicHTMLBrush.class.st b/src/Microdown-HTMLExporter/MicHTMLBrush.class.st new file mode 100644 index 000000000..ee19642ed --- /dev/null +++ b/src/Microdown-HTMLExporter/MicHTMLBrush.class.st @@ -0,0 +1,26 @@ +" +I'm an abstract brush dedicated to HTML documents. As of today, the only subclass is the tag brush but we can imagine others (e.g., to write HTML comments). +" +Class { + #name : 'MicHTMLBrush', + #superclass : 'MicExportBrush', + #instVars : [ + 'name' + ], + #category : 'Microdown-HTMLExporter-Core', + #package : 'Microdown-HTMLExporter', + #tag : 'Core' +} + +{ #category : 'accessing' } +MicHTMLBrush >> name [ + "Answer a with the receiver's name" + + ^ name +] + +{ #category : 'accessing' } +MicHTMLBrush >> name: aString [ + name := aString. + stream nextPut: $<; << aString +] diff --git a/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st b/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st index e3ec237d7..632fcace1 100644 --- a/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLCanvas.class.st @@ -1,31 +1,37 @@ +" +A specialized canvas to emit HTML tags. +" Class { #name : 'MicHTMLCanvas', #superclass : 'MicExportCanvas', - #instVars : [ - 'name' - ], - #classVars : [ - 'HTMLCharacters' - ], - #category : 'Microdown - HTML', - #package : 'Microdown - HTML' + #category : 'Microdown-HTMLExporter-Core', + #package : 'Microdown-HTMLExporter', + #tag : 'Core' } +{ #category : 'writing text' } +MicHTMLCanvas >> initialize [ + "Private - Initialize the receiver's stream" + + super initialize. + self initializeWith: String empty. +] + { #category : 'initialization' } -MicHTMLCanvas class >> initialize [ - HTMLCharacters := Dictionary new. - HTMLCharacters - at: $" put: '"'; - at: $& put: '&'; - at: $< put: '<'; - at: $> put: '>' +MicHTMLCanvas >> initializeWith: aString [ + "Private - Set the receiver's stream to store the HTML contents" + + self setStream: (self newMicStreamOn: aString) ] -{ #category : 'accessing' } -MicHTMLCanvas >> nextPut: aCharacter [ - (HTMLCharacters at: aCharacter ifAbsent: nil) - ifNil: [ super nextPut: aCharacter ] - ifNotNil: [ :string | self raw: string ] +{ #category : 'initialization' } +MicHTMLCanvas >> newMicStreamOn: aString [ + "Answer a new wrapper over aString to help not hardcoding line ending everywhere." + + ^ MicOutputStream new + setStream: (WriteStream on: aString); + "nextPutAll: aString;" + yourself ] { #category : 'accessing' } diff --git a/src/Microdown-HTMLExporter/MicHTMLTag.class.st b/src/Microdown-HTMLExporter/MicHTMLTag.class.st index 093fa2dba..03ee30951 100644 --- a/src/Microdown-HTMLExporter/MicHTMLTag.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLTag.class.st @@ -1,19 +1,107 @@ +" +HTML tags are used to delimit the start and end of elements in the markup. An HTML tag is composed of the name of the element, surrounded by angle brackets: + +``` +

    Content

    +``` + +A tag could be: + +- A start tag: Can have HTML attributes. +- An end tag: Has a slash after the opening angle bracket, to distinguish it from the start tag. + +Note that not all elements require an **end** tag, for example the `
    ` tag. +This class contains utility methods to append HTML tags to a stream, and to manage parameters: + +- #addArguments: +- #parameterAt:put: + +" Class { #name : 'MicHTMLTag', #superclass : 'MicHTMLBrush', - #category : 'Microdown - HTML', - #package : 'Microdown - HTML' + #instVars : [ + 'htmlArgumentsMap' + ], + #category : 'Microdown-HTMLExporter-Core', + #package : 'Microdown-HTMLExporter', + #tag : 'Core' } { #category : 'accessing' } -MicHTMLTag >> name: aString [ - name := aString. - stream nextPut: $<; << aString +MicHTMLTag >> addArguments: aMicInlineBlockWithUrl [ + + aMicInlineBlockWithUrl hasArguments ifFalse: [ ^ self ]. + self htmlArgumentsMapDo: [ :argAssoc | + aMicInlineBlockWithUrl arguments + at: argAssoc key + ifPresent: [ :labelString | self parameterAt: argAssoc value put: labelString ] ] ] { #category : 'accessing' } -MicHTMLTag >> parameterAt: aString put: anotherString [ - stream space. stream << aString << '="' << anotherString << '"' +MicHTMLTag >> close [ + "Close the receiver's tag. Note that this method does not check if the tag is opened" + + stream << $> +] + +{ #category : 'accessing' } +MicHTMLTag >> contents [ + "Answer a with receiver's output without modifying the stream position" + + ^ stream contents +] + +{ #category : 'accessing' } +MicHTMLTag >> htmlArgumentsMap [ + "Answer a of receiver's associations between Microdown attributes and HTML attributes" + + ^ htmlArgumentsMap + ifNil: [ htmlArgumentsMap := self initializeArgumentsMap ] +] + +{ #category : 'accessing' } +MicHTMLTag >> htmlArgumentsMapDo: aFullBlockClosure [ + "Iterate over the receiver's mapping of Microdown to HTML attributes" + + self htmlArgumentsMap associationsDo: aFullBlockClosure +] + +{ #category : 'initialization' } +MicHTMLTag >> initializeArgumentsMap [ + + ^ Dictionary new + at: 'label' put: 'class'; + at: 'width' put: 'width'; + at: 'rel' put: 'rel'; + at: 'target' put: 'target'; + yourself +] + +{ #category : 'accessing' } +MicHTMLTag >> parameterAt: keyString put: valueString [ + "Write a parameter named keyString with valueString as value. Note that this method does not close the tag" + + stream + space; + << keyString; + << '="'; + << valueString; + << '"' +] + +{ #category : 'printing' } +MicHTMLTag >> printOn: aStream [ + + super printOn: aStream. + aStream << ' name: ['. + name + ifNotNil: [ aStream << name ]. + aStream << '] contents: ('. + stream + ifNotNil: [ aStream << stream contents ]. + aStream + << ')' ] { #category : 'accessing' } diff --git a/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st b/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st index 759a1c30b..be9cd3645 100644 --- a/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLVisitor.class.st @@ -534,26 +534,17 @@ MicHTMLVisitor >> visitQuote: aQuote [ ] { #category : 'visiting - html extensions' } -MicHTMLVisitor >> visitRaw: aParagraph [ +MicHTMLVisitor >> visitRaw: aMicRaw [ + "Raw should be raw and it should NOT go the next line or whatever" + "Pay attention aMicRaw is not a paragraph but an inline element." - canvas raw: aParagraph bodyString. - canvas newLine + canvas raw: aMicRaw bodyString ] { #category : 'visiting - html extensions' } MicHTMLVisitor >> visitRawParagraph: aParagraph [ - canvas - newLine; - raw: '<' , aParagraph label. - aParagraph hasArguments - ifTrue: [ canvas space; raw: aParagraph argumentString ]. - canvas raw: '>' . - - aParagraph children do: [ :each | each accept: self ]. - canvas - newLine; - raw: aParagraph lineStopMarkup. + canvas raw: aParagraph body ] { #category : 'templating ping pong' } diff --git a/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st b/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st new file mode 100644 index 000000000..c276187f4 --- /dev/null +++ b/src/Microdown-Pillar-Tests/MicQuoteBlockTest.extension.st @@ -0,0 +1,17 @@ +Extension { #name : 'MicQuoteBlockTest' } + +{ #category : '*Microdown-Pillar-Tests' } +MicQuoteBlockTest >> testQuoteBlockAsPilar [ + | source root pillarNode | + source := '> text1 +> text2'. + root := parser parse: source. + self assert: root children size equals: 1. + pillarNode := root children first asPillar. + self assert: pillarNode class equals: PRPreformatted. + self + assert: pillarNode text + equals: + 'text1 +text2' +] diff --git a/src/Microdown-RichTextComposer/MicrodownParser.extension.st b/src/Microdown-RichTextComposer/MicParser.extension.st similarity index 53% rename from src/Microdown-RichTextComposer/MicrodownParser.extension.st rename to src/Microdown-RichTextComposer/MicParser.extension.st index ca23a5a9b..26bb65e39 100644 --- a/src/Microdown-RichTextComposer/MicrodownParser.extension.st +++ b/src/Microdown-RichTextComposer/MicParser.extension.st @@ -1,6 +1,6 @@ -Extension { #name : 'MicrodownParser' } +Extension { #name : 'MicParser' } { #category : '*Microdown-RichTextComposer' } -MicrodownParser class >> convertToRichText: aString [ +MicParser class >> convertToRichText: aString [ ^ MicRichTextComposer new visit: (self new parse: aString) ] diff --git a/src/Microdown-Rules/ManifestMicrodownRules.class.st b/src/Microdown-Rules/ManifestMicrodownRules.class.st deleted file mode 100644 index d76f96730..000000000 --- a/src/Microdown-Rules/ManifestMicrodownRules.class.st +++ /dev/null @@ -1,24 +0,0 @@ -" -Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser -" -Class { - #name : 'ManifestMicrodownRules', - #superclass : 'PackageManifest', - #category : 'Microdown-Rules-Manifest', - #package : 'Microdown-Rules', - #tag : 'Manifest' -} - -{ #category : 'code-critics' } -ManifestMicrodownRules class >> ruleNotEliminationRuleV1FalsePositive [ - - - ^ #(#(#(#RGMethodDefinition #(#MicRuleUppercaseHeaderStrategy #checkRest:of: #false)) #'2026-03-05T07:42:00.35682+03:00') ) -] - -{ #category : 'code-critics' } -ManifestMicrodownRules class >> ruleUnclassifiedMethodsRuleV1FalsePositive [ - - - ^ #(#(#(#RGMethodDefinition #(#MicRuleUppercaseHeaderStrategy #checkRest:of: #false)) #'2026-03-05T07:41:58.207851+03:00') ) -] diff --git a/src/Microdown-Rules/MicEnglishTypoChecker.class.st b/src/Microdown-Rules/MicEnglishTypoChecker.class.st index f81c5fbfd..e4c387661 100644 --- a/src/Microdown-Rules/MicEnglishTypoChecker.class.st +++ b/src/Microdown-Rules/MicEnglishTypoChecker.class.st @@ -16,7 +16,7 @@ MicEnglishTypoChecker >> addResultFor: anElement message: aMessage [ ). ] -{ #category : 'visiting - inline elements' } +{ #category : 'adding' } MicEnglishTypoChecker >> checkString: aString for: anElement [ | checks | aString ifNil: [ ^self ]. @@ -45,19 +45,21 @@ checks := { ]. ] -{ #category : 'visiting - inline elements' } +{ #category : 'adding' } MicEnglishTypoChecker >> visitCode: aCodeBlock [ + aCodeBlock caption ifNil: [ ^self ]. self checkString: aCodeBlock caption for: aCodeBlock. ] -{ #category : 'visiting - inline elements' } +{ #category : 'adding' } MicEnglishTypoChecker >> visitMonospace: aCodeBlock [ ^ self ] -{ #category : 'visiting - inline elements' } +{ #category : 'adding' } MicEnglishTypoChecker >> visitText: aText [ + self checkString: aText bodyString for: aText. ] diff --git a/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st b/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st index 5529b91a2..e1b02ab79 100644 --- a/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st +++ b/src/Microdown-Rules/MicEnglishTypoCheckerTest.class.st @@ -66,41 +66,41 @@ MicEnglishTypoCheckerTest >> setUp [ ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testCorrectFile [ checker checkProject: fileSystem / 'correct.md'. self assert: checker isOkay. ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testMonospaceIgnored [ checker checkProject: fileSystem / 'mono.md'. self assert: checker isOkay. ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testNoSpaceAfterColon [ checker checkProject: fileSystem / 'noSpaceAfterColon.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong:example." in file/noSpaceAfterColon.md contains a mistake: There is no space after :' + assert: checker results first explanation + equals: 'Text: "Wrong:example." in file/noSpaceAfterColon.md contains a mistake: There is no space after :' ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testNoSpaceAfterComma [ checker checkProject: fileSystem / 'noSpaceAfterComma.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong,example." in file/noSpaceAfterComma.md contains a mistake: There is no space after ,' + assert: checker results first explanation + equals: 'Text: "Wrong,example." in file/noSpaceAfterComma.md contains a mistake: There is no space after ,' ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testSpaceBeforeColon [ checker checkProject: fileSystem / 'spaceBeforeColon.md'. @@ -110,12 +110,12 @@ MicEnglishTypoCheckerTest >> testSpaceBeforeColon [ equals: 'Text: "This is wrong : example." in file/spaceBeforeColon.md contains a mistake: There is a space before :' ] -{ #category : 'tests - english typos' } +{ #category : 'tests' } MicEnglishTypoCheckerTest >> testSpaceBeforeComma [ checker checkProject: fileSystem / 'spaceBeforeComma.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: 'Text: "Wrong ,example." in file/spaceBeforeComma.md contains a mistake: There is a space before ,' + assert: checker results first explanation + equals: 'Text: "Wrong ,example." in file/spaceBeforeComma.md contains a mistake: There is a space before ,' ] diff --git a/src/Microdown-Rules/MicRuleHeaderChecker.class.st b/src/Microdown-Rules/MicRuleHeaderChecker.class.st index 2b1a8dbfe..b83f4342f 100644 --- a/src/Microdown-Rules/MicRuleHeaderChecker.class.st +++ b/src/Microdown-Rules/MicRuleHeaderChecker.class.st @@ -35,6 +35,7 @@ MicRuleHeaderChecker >> checkRest: words of: aMicHeader [ e.g., 'Today and tomorrow: The days' and not 'Today and tomorrow: the days' " + strategy checkRest: words of: aMicHeader ] @@ -51,7 +52,7 @@ MicRuleHeaderChecker >> initialize [ 'upon' 'when' 'with' 'yet' ) do: [ :each | self addUncapitalized: each ]. ] -{ #category : 'as yet unclassified' } +{ #category : 'asserting' } MicRuleHeaderChecker >> shouldCapitalizedWord: aString [ | normalized | normalized := (aString select: [ :c | c isLetter ]) asLowercase. @@ -73,7 +74,7 @@ MicRuleHeaderChecker >> verifyUppercase [ strategy checker: self ] -{ #category : 'visiting' } +{ #category : 'asserting' } MicRuleHeaderChecker >> visitHeader: aMicHeader [ | words | diff --git a/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st b/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st index 53db8448d..b09573351 100644 --- a/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st +++ b/src/Microdown-Rules/MicRuleUppercaseHeaderStrategy.class.st @@ -6,16 +6,24 @@ Class { #tag : 'Capitalization' } -{ #category : 'as yet unclassified' } +{ #category : 'visiting' } MicRuleUppercaseHeaderStrategy >> checkRest: words of: aMicHeader [ - + + "In the future we should manage that if a word finishes by : the following one must be captitalized + e.g., 'Today and tomorrow: The days' and not + 'Today and tomorrow: the days' + " | realWords | self checkColonCapitalizationIn: words of: aMicHeader. realWords := words allButFirst select: [ :each | | normalized | normalized := (each select: [:c | c isLetter]) asLowercase. checker shouldCapitalizedWord: normalized ]. - + "note that we should verify two points not yet done + Point 1 'To' in input should be reported as an error in both uppercase and lowercase + + Point 2 'to' should not be generate an error in uppercase setup. + " realWords do: [ :word | | cleaned | diff --git a/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st b/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st index 41fd6657f..6b9346154 100644 --- a/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st +++ b/src/Microdown-Rules/MicSpaceBeforeDiacriticsResult.class.st @@ -16,9 +16,9 @@ MicSpaceBeforeDiacriticsResult >> detail: aString [ { #category : 'accessing' } MicSpaceBeforeDiacriticsResult >> explanation [ -| text | -text := micElement bodyString ifNil: ['']. + | text | + text := micElement bodyString ifNil: ['']. ^ 'Text: "' , text , '" in file' , fileReference fullName , ' contains a mistake: ', detail ] diff --git a/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st b/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st index 341bb8d49..615da0525 100644 --- a/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st +++ b/src/Microdown-Rules/MicUseofWrongVocabularyResult.class.st @@ -17,7 +17,7 @@ Class { MicUseofWrongVocabularyResult >> explanation [ | wrongWord goodWord | - wrongWord := patternPair key. + wrongWord := patternPair key allButFirst allButLast. goodWord := patternPair value. ^ 'Text: "' , micElement bodyString , '" in file' , fileReference fullName , ' contains a mistake: [' , wrongWord , '] should not be used. We should use ' , goodWord diff --git a/src/Microdown-Rules/MicVocabularyChecker.class.st b/src/Microdown-Rules/MicVocabularyChecker.class.st index 203bb6193..fa0343caf 100644 --- a/src/Microdown-Rules/MicVocabularyChecker.class.st +++ b/src/Microdown-Rules/MicVocabularyChecker.class.st @@ -11,30 +11,20 @@ Class { { #category : 'visiting - inline elements' } MicVocabularyChecker >> checkText: aMicText [ - | text sortedPairs | - - text := aMicText bodyString asLowercase. - sortedPairs := patternPairs asSortedCollection: [ :a :b | a key size > b key size ]. - sortedPairs do: [ :patternPair | - | pattern | - pattern := patternPair key asLowercase. - (text includesSubstring: pattern ) ifTrue: [ - results add: ( - MicUseofWrongVocabularyResult new - micElement: aMicText; - inFile: aMicText fromFile; - patternPair: patternPair; - yourself - ). - text := text copyReplaceAll: pattern with: '' - ]. - ]. + + patternPairs do: [ :patternPair | + "note that match: ignore upper/lower case differences." + (patternPair key match: aMicText bodyString ) ifTrue: [ + results add: (MicUseofWrongVocabularyResult new + micElement: aMicText; + inFile: aMicText fromFile; + patternPair: patternPair) ] ] ] { #category : 'visiting - inline elements' } MicVocabularyChecker >> pairs: aCollectionOfPairs [ - patternPairs := aCollectionOfPairs + patternPairs := aCollectionOfPairs collect: [ :each | ('*' , each key , '*') -> each value ] ] diff --git a/src/Microdown-Rules/MicVocabularyCheckerTest.class.st b/src/Microdown-Rules/MicVocabularyCheckerTest.class.st index 2c2ccb6a9..bf9b8d4b9 100644 --- a/src/Microdown-Rules/MicVocabularyCheckerTest.class.st +++ b/src/Microdown-Rules/MicVocabularyCheckerTest.class.st @@ -117,7 +117,7 @@ MicVocabularyCheckerTest >> setUp [ ('allows to' -> 'allows one to')}. ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsAllowToMistake [ checker checkProject: fileSystem / 'mistakeAllowTo.md'. @@ -129,7 +129,7 @@ MicVocabularyCheckerTest >> testCheckerFindsAllowToMistake [ 'Text: "We allow to edit the file." in file/mistakeAllowTo.md contains a mistake: [allow to] should not be used. We should use allow one to' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsAllowedToMistake [ checker checkProject: fileSystem / 'mistakeAllowedTo.md'. self deny: checker isOkay. @@ -140,9 +140,10 @@ checker checkProject: fileSystem / 'mistakeAllowedTo.md'. 'Text: "You are allowed to enter." in file/mistakeAllowedTo.md contains a mistake: [allowed to] should not be used. We should use allowed one to' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsAllowsToMistake [ -checker checkProject: fileSystem / 'mistakeAllowsTo.md'. + + checker checkProject: fileSystem / 'mistakeAllowsTo.md'. self deny: checker isOkay. self @@ -151,7 +152,7 @@ checker checkProject: fileSystem / 'mistakeAllowsTo.md'. 'Text: "The system allows to change settings." in file/mistakeAllowsTo.md contains a mistake: [allows to] should not be used. We should use allows one to' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsBehaviourMistake [ checker checkProject: fileSystem / 'mistakeBehaviour.md'. @@ -163,7 +164,7 @@ MicVocabularyCheckerTest >> testCheckerFindsBehaviourMistake [ 'Text: "Pharo has interesting behaviour." in file/mistakeBehaviour.md contains a mistake: [behaviour] should not be used. We should use behavior' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsCentreMistake [ checker checkProject: fileSystem / 'mistakeCentre.md'. @@ -175,7 +176,7 @@ MicVocabularyCheckerTest >> testCheckerFindsCentreMistake [ 'Text: "The centre of the universe." in file/mistakeCentre.md contains a mistake: [centre] should not be used. We should use center' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsColourMistake [ checker checkProject: fileSystem / 'mistakeColour.md'. @@ -187,19 +188,19 @@ MicVocabularyCheckerTest >> testCheckerFindsColourMistake [ 'Text: "The colour of the sky is blue." in file/mistakeColour.md contains a mistake: [colour] should not be used. We should use color' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsColoursMistake [ checker checkProject: fileSystem / 'mistakeColours.md'. self deny: checker isOkay. self - assert: checker results first explanation - equals: - 'Text: "Colours are beautiful." in file/mistakeColours.md contains a mistake: [colours] should not be used. We should use colors' + assert: checker results first explanation + equals:'Text: "Colours are beautiful." in file/mistakeColours.md contains a mistake: [colour] should not be used. We should use color' + ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInFigure [ checker checkProject: fileSystem / 'mistakeInFigure.md'. @@ -212,7 +213,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInFigure [ 'Text: "sub-presenter is not correct even in caption" in file/mistakeInFigure.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInHeader [ checker checkProject: fileSystem / 'mistakeInHeader.md'. @@ -224,7 +225,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInHeader [ 'Text: "sub-presenter is not correct." in file/mistakeInHeader.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInParagraph [ checker checkProject: fileSystem / 'mistakeInParagraph.md'. @@ -236,7 +237,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInParagraph [ 'Text: "Spec uses a sub presenter. And there is a mistake in the previous sentence." in file/mistakeInParagraph.md contains a mistake: [sub presenter] should not be used. We should use subpresenter' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsMistakeInUppercasedText [ checker checkProject: fileSystem / 'mistakeInUppercase.md'. @@ -248,7 +249,7 @@ MicVocabularyCheckerTest >> testCheckerFindsMistakeInUppercasedText [ 'Text: "sub-Presenter is not correct. The checker should pay attention that the text may use a different case than the pattern." in file/mistakeInUppercase.md contains a mistake: [sub-presenter] should not be used. We should use subpresenter' ] -{ #category : 'running' } +{ #category : 'tests' } MicVocabularyCheckerTest >> testCheckerFindsVisualiseMistake [ checker checkProject: fileSystem / 'mistakeVisualise.md'. diff --git a/src/Microdown-Slide-Utils/MicSlideConverter.class.st b/src/Microdown-Slide-Utils/MicSlideConverter.class.st new file mode 100644 index 000000000..3cc76331e --- /dev/null +++ b/src/Microdown-Slide-Utils/MicSlideConverter.class.st @@ -0,0 +1,24 @@ +Class { + #name : 'MicSlideConverter', + #superclass : 'PRVisitor', + #instVars : [ + 'mic' + ], + #category : 'Microdown-Slide-Utils', + #package : 'Microdown-Slide-Utils' +} + +{ #category : 'visiting' } +MicSlideConverter >> start: aPRDocument [ + mic := MicRootBlock new. + super start: aPRDocument. + ^ mic + + +] + +{ #category : 'visiting' } +MicSlideConverter >> visitSlide: aSlide [ + + +] diff --git a/src/Microdown-Tests/MicAnchorLinkerTest.class.st b/src/Microdown-Tests/MicAnchorLinkerTest.class.st index 2ec4da6e7..b9de317ed 100644 --- a/src/Microdown-Tests/MicAnchorLinkerTest.class.st +++ b/src/Microdown-Tests/MicAnchorLinkerTest.class.st @@ -10,7 +10,7 @@ Class { MicAnchorLinkerTest >> testAnchorIsAttachedToItsTarget [ | linker doc | linker := MicAnchorLinker new. - doc := MicrodownParser parse: ' + doc := MicParser parse: ' this is a sentence #Header1 @@ -26,7 +26,7 @@ this is a sentence MicAnchorLinkerTest >> testAnchorIsAttachedToItsTarget2 [ | linker doc | linker := MicAnchorLinker new. - doc := MicrodownParser parse: ' + doc := MicParser parse: ' this is a sentence #Header1 @@ -44,7 +44,7 @@ this is a sentence MicAnchorLinkerTest >> testHeaderLast [ | linker doc | linker := MicAnchorLinker new. - doc := MicrodownParser parse: ' + doc := MicParser parse: ' this is a sentence #Header1'. diff --git a/src/Microdown-Tests/MicElementTest.class.st b/src/Microdown-Tests/MicElementTest.class.st index 735367040..7d1db4c34 100644 --- a/src/Microdown-Tests/MicElementTest.class.st +++ b/src/Microdown-Tests/MicElementTest.class.st @@ -28,7 +28,7 @@ MicElementTest >> parserClass [ "This references to MicrodownParser is needed for the test. Replacing it by Microdown does not work." - ^ MicrodownParser + ^ MicParser ] { #category : 'running' } diff --git a/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st b/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st new file mode 100644 index 000000000..c11cd6373 --- /dev/null +++ b/src/Microdown-Tests/MicHTMLTagParagraphBlockTest.class.st @@ -0,0 +1,176 @@ +Class { + #name : 'MicHTMLTagParagraphBlockTest', + #superclass : 'MicBlockTest', + #category : 'Microdown-Tests-Extensions', + #package : 'Microdown-Tests', + #tag : 'Extensions' +} + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> subjectClass [ + ^ MicHTMLTagParagraphBlock +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testASimpleDivWithClass [ + | mic | + mic := (parser parse: '
    + +# Contributing to Pharo + +In a giving mood? There are many ways to get involved! + +
    +') children first. + self assert: mic class equals: MicHTMLTagParagraphBlock. + self assert: mic label equals: 'div'. + self assert: mic children first class equals: MicHeaderBlock. + self assert: mic children second class equals: MicParagraphBlock + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testASimpleRawParagraph [ + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicHTMLTagParagraphBlock. + self assert: mic label equals: 'title'. + self assert: mic children first class equals: MicParagraphBlock + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testNotASimpleRawParagraphBecauseNotDeclaredTag [ + + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicParagraphBlock. + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testNotASimpleRawParagraphNoSpaceInFrontToClose [ + + | children | + children := (parser parse: ' +No idea what it is :) + + +And after we get a paragraph') children. + self assert: children size equals: 1. + "so far we eat as much as we can and put everything in the raw" + + + self assert: children first class equals: MicHTMLTagParagraphBlock. + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testParagrapWithP [ + | mic | + mic := (parser parse: ' +

    +This is a paragraph started normally with

    +

    +') children first. + self assert: mic class equals: MicHTMLTagParagraphBlock. + self assert: mic label equals: 'p'. + self assert: mic children first class equals: MicParagraphBlock + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testRawParagraphCanBeNested [ + | children | + children := (parser parse: ' +No idea what it is :) + +<button> +hkhkjhkj +</button> +') children. + self assert: children size equals: 1. + self assert: children first class equals: MicHTMLTagParagraphBlock. + self assert: children first label equals: 'title'. + self assert: children first children size equals: 2. + self assert: children first children first class equals: MicParagraphBlock. + self assert: children first children second label equals: 'button'. + self assert: children first children second class equals: MicHTMLTagParagraphBlock. +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testRawParagraphForP [ + | children | + children := (parser parse: '

    +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicHTMLTagParagraphBlock. + self assert: children first label equals: 'p'. + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpace [ + | children | + children := (parser parse: '

    WeDoNotLikeSpace +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicHTMLTagParagraphBlock. + self assert: children first label equals: 'p'. + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpace2 [ + | children newLine | + newLine := Character cr asString. + children := (MicParser new parse: '
    ', newLine, +'

    ', newLine, +'We use the <div> tag to group two paragraphs for applying a background to the text, and to add color to this', newLine, +'', newLine, +'word', newLine, +'', newLine, +'we place it within <span> tag.', +'

    ', newLine, +'

    ', newLine, +'Pay attention, that the <div> tag is a block-level element, so a line break is placed before and after it.', newLine, +'

    ', newLine, +'
    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicHTMLTagParagraphBlock. + self assert: children first label equals: 'div'. + self assert: children first children first class equals: MicHTMLTagParagraphBlock. + self assert: children first children first label equals: 'p'. + + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testRawParagraphForPNoSpaceAndAttributes [ + | children | + children := (parser parse: '

    WeDoNotLikeSpace +No idea what it is :) +

    ') children. + self assert: children size equals: 1. + self assert: children first class equals: MicHTMLTagParagraphBlock. + self assert: children first label equals: 'p'. + +] + +{ #category : 'tests' } +MicHTMLTagParagraphBlockTest >> testThereIsNoValidationIfThetagStartWithAKnowHtml [ + "for the moment we only check the first couple of characters to start with. + So it may happens that we match the beginning of a tag." + | mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicParagraphBlock. + +] diff --git a/src/Microdown-Tests/MicParagraphBlockTest.class.st b/src/Microdown-Tests/MicParagraphBlockTest.class.st index 143018bb3..39a1a34c9 100644 --- a/src/Microdown-Tests/MicParagraphBlockTest.class.st +++ b/src/Microdown-Tests/MicParagraphBlockTest.class.st @@ -37,7 +37,7 @@ MicParagraphBlockTest >> testASimpleRawParagaaph [ mic := (parser parse: ' No idea what it is :) ') children first. - self assert: mic class equals: MicRawParagraphBlock. + self assert: mic class equals: MicHTMLTagParagraphBlock. self assert: mic label equals: 'title'. ] diff --git a/src/Microdown-Tests/MicParserTest.class.st b/src/Microdown-Tests/MicParserTest.class.st index 4f566b2b8..1890b12c8 100644 --- a/src/Microdown-Tests/MicParserTest.class.st +++ b/src/Microdown-Tests/MicParserTest.class.st @@ -16,7 +16,7 @@ Class { MicParserTest >> setUp [ super setUp. - parser := MicrodownParser new. + parser := MicParser new. ] { #category : 'tests' } diff --git a/src/Microdown-Tests/MicRawParagraphBlockTest.class.st b/src/Microdown-Tests/MicRawParagraphBlockTest.class.st index 815c40540..54f455fd9 100644 --- a/src/Microdown-Tests/MicRawParagraphBlockTest.class.st +++ b/src/Microdown-Tests/MicRawParagraphBlockTest.class.st @@ -1,9 +1,9 @@ Class { #name : 'MicRawParagraphBlockTest', #superclass : 'MicBlockTest', - #category : 'Microdown-Tests-Extensions', + #category : 'Microdown-Tests-Parser', #package : 'Microdown-Tests', - #tag : 'Extensions' + #tag : 'Parser' } { #category : 'tests' } @@ -12,165 +12,77 @@ MicRawParagraphBlockTest >> subjectClass [ ] { #category : 'tests' } -MicRawParagraphBlockTest >> testASimpleDivWithClass [ - | mic | - mic := (parser parse: '
    +MicRawParagraphBlockTest >> testFirstLineAfterMarkupShouldBeIgnored [ + "contents starts on the following line of the {{{" + | root | + root := parser parse: '{{{ blabla +**Hello is ... cut for real -# Contributing to Pharo +a paragraph on two lines** +}}}'. -In a giving mood? There are many ways to get involved! + self assert: root children first bodyString equals: '**Hello is ... cut for real -
    -') children first. - self assert: mic class equals: MicRawParagraphBlock. - self assert: mic label equals: 'div'. - self assert: mic children first class equals: MicHeaderBlock. - self assert: mic children second class equals: MicParagraphBlock - -] - -{ #category : 'tests' } -MicRawParagraphBlockTest >> testASimpleRawParagraph [ - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicRawParagraphBlock. - self assert: mic label equals: 'title'. - self assert: mic children first class equals: MicParagraphBlock - -] - -{ #category : 'tests' } -MicRawParagraphBlockTest >> testNotASimpleRawParagraphBecauseNotDeclaredTag [ - - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicParagraphBlock. +a paragraph on two lines**'. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testNotASimpleRawParagraphNoSpaceInFrontToClose [ - - | children | - children := (parser parse: ' -No idea what it is :) - - -And after we get a paragraph') children. - self assert: children size equals: 1. - "so far we eat as much as we can and put everything in the raw" - - - self assert: children first class equals: MicRawParagraphBlock. +MicRawParagraphBlockTest >> testSimpleParagraphWithMarkupShouldBeTakenAsIs [ + " + {{{ + Hello + }}} + " + + | root | + root := parser parse: '{{{ +**Hello is ... cut for real +a paragraph on two lines** +}}}'. + + self assert: root children size equals: 1. + self assert: root children first class equals: MicRawParagraphBlock. + self assert: root children first bodyString equals: '**Hello is ... cut for real +a paragraph on two lines**'. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testParagrapWithP [ - | mic | - mic := (parser parse: ' -

    -This is a paragraph started normally with

    -

    -') children first. - self assert: mic class equals: MicRawParagraphBlock. - self assert: mic label equals: 'p'. - self assert: mic children first class equals: MicParagraphBlock +MicRawParagraphBlockTest >> testWithEmptyLine [ + " + {{{ + Hello + }}} + " -] + | root | + root := parser parse: '{{{ +**Hello is ... cut for real -{ #category : 'tests' } -MicRawParagraphBlockTest >> testRawParagraphCanBeNested [ - | children | - children := (parser parse: ' -No idea what it is :) - -<button> -hkhkjhkj -</button> -') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'title'. - self assert: children first children size equals: 2. - self assert: children first children first class equals: MicParagraphBlock. - self assert: children first children second label equals: 'button'. - self assert: children first children second class equals: MicRawParagraphBlock. -] - -{ #category : 'tests' } -MicRawParagraphBlockTest >> testRawParagraphForP [ - | children | - children := (parser parse: '

    -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'p'. - -] - -{ #category : 'tests' } -MicRawParagraphBlockTest >> testRawParagraphForPNoSpace [ - | children | - children := (parser parse: '

    WeDoNotLikeSpace -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'p'. - -] +a paragraph on two lines** +}}}'. -{ #category : 'tests' } -MicRawParagraphBlockTest >> testRawParagraphForPNoSpace2 [ - | children newLine | - newLine := Character cr asString. - children := (MicrodownParser new parse: '
    ', newLine, -'

    ', newLine, -'We use the <div> tag to group two paragraphs for applying a background to the text, and to add color to this', newLine, -'', newLine, -'word', newLine, -'', newLine, -'we place it within <span> tag.', -'

    ', newLine, -'

    ', newLine, -'Pay attention, that the <div> tag is a block-level element, so a line break is placed before and after it.', newLine, -'

    ', newLine, -'
    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'div'. - self assert: children first children first class equals: MicRawParagraphBlock. - self assert: children first children first label equals: 'p'. - - -] + self assert: root children size equals: 1. + self assert: root children first bodyString equals: '**Hello is ... cut for real -{ #category : 'tests' } -MicRawParagraphBlockTest >> testRawParagraphForPNoSpaceAndAttributes [ - | children | - children := (parser parse: '

    WeDoNotLikeSpace -No idea what it is :) -

    ') children. - self assert: children size equals: 1. - self assert: children first class equals: MicRawParagraphBlock. - self assert: children first label equals: 'p'. +a paragraph on two lines**'. ] { #category : 'tests' } -MicRawParagraphBlockTest >> testThereIsNoValidationIfThetagStartWithAKnowHtml [ - "for the moment we only check the first couple of characters to start with. - So it may happens that we match the beginning of a tag." - | mic | - mic := (parser parse: ' -No idea what it is :) -') children first. - self assert: mic class equals: MicParagraphBlock. +MicRawParagraphBlockTest >> testWithHTMLTag [ + + | root | + root := parser parse: '{{{ +**Hello is ... cut for real +https://pharo.org +a paragraph on two lines** +}}}'. + + self assert: root children size equals: 1. + self assert: root children first bodyString equals: '**Hello is ... cut for real +https://pharo.org +a paragraph on two lines**' ] diff --git a/src/Microdown-Tests/MicrodownParserTest.class.st b/src/Microdown-Tests/MicrodownParserTest.class.st index d8491ffae..54a0a9afd 100644 --- a/src/Microdown-Tests/MicrodownParserTest.class.st +++ b/src/Microdown-Tests/MicrodownParserTest.class.st @@ -15,7 +15,7 @@ Class { { #category : 'running' } MicrodownParserTest >> parser [ "for now this reference is needed and cannot be changed into Microdown" - ^ MicrodownParser new + ^ MicParser new ] { #category : 'running' } diff --git a/src/Microdown-Tests/MicrodownTest.class.st b/src/Microdown-Tests/MicrodownTest.class.st index f4a4e3e28..6fa70625e 100644 --- a/src/Microdown-Tests/MicrodownTest.class.st +++ b/src/Microdown-Tests/MicrodownTest.class.st @@ -93,7 +93,7 @@ MicrodownTest >> testAddedAsChildWhenUsingParent [ MicrodownTest >> testExtensionArgumentsAreNotReified [ | doc slide | - doc := MicrodownParser parse: ''. + doc := MicParser parse: ''. slide := doc children first. self assert: slide class equals: MicSlideBlock. self assert: slide title equals: 'A title'. @@ -105,7 +105,7 @@ MicrodownTest >> testFigureLabelAreNotReified [ "but they should" | doc fig | - doc := MicrodownParser new parse: '![A caption.](figures/f.png width=120&label=fig)'. + doc := MicParser new parse: '![A caption.](figures/f.png width=120&label=fig)'. fig := doc children first children first. self assert: fig class equals: MicFigureBlock. self assert: fig anchor equals: 'fig'. diff --git a/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st b/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st index 0b669cced..7f31dd293 100644 --- a/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st +++ b/src/Microdown-ToHelpMacrodownLoading/MicMicrodownObjectToPillarObjectConverter.class.st @@ -1,13 +1,8 @@ -" -This visitor is about to disappeared. It was created just to make sure that we can have asPillar expressed as a visitor. -Now this visitor is working on non expanded code blocks. -" Class { #name : 'MicMicrodownObjectToPillarObjectConverter', - #superclass : 'MicrodownVisitor', - #category : 'Microdown-Pillar-Visitor', - #package : 'Microdown-Pillar', - #tag : 'Visitor' + #superclass : 'Object', + #category : 'Microdown-ToHelpMacrodownLoading', + #package : 'Microdown-ToHelpMacrodownLoading' } { #category : 'formatting' } @@ -33,12 +28,18 @@ MicMicrodownObjectToPillarObjectConverter >> urlBlock: aMicUrlBlock [ aMicUrlBlock reference uri query: queries. ^ aMicUrlBlock associatedPillarClass new setChildren: - ((aMicUrlBlock inlineParse: aMicUrlBlock substring) collect: [ :n | + (aMicUrlBlock children collect: [ :n | n accept: self ]); reference: urlString; yourself ] +{ #category : 'visiting' } +MicMicrodownObjectToPillarObjectConverter >> visit: aNode [ + + aNode accept: self +] + { #category : 'visiting' } MicMicrodownObjectToPillarObjectConverter >> visitAnchor: aMicAnchorBlock [ ^ PRAnchor new name: aMicAnchorBlock label; yourself @@ -128,9 +129,9 @@ MicMicrodownObjectToPillarObjectConverter >> visitEnvironment: aMicEnvironmentBl MicMicrodownObjectToPillarObjectConverter >> visitFigure: aMicFigureBlock [ | aPRFigure | - aPRFigure := aMicFigureBlock associatedPillarClass new. + aPRFigure := self urlBlock: aMicFigureBlock. ^ aPRFigure - label: aMicFigureBlock altText; + label: (aMicFigureBlock children collect: [:each | self visit: each ]); parameters: aMicFigureBlock arguments; yourself ] @@ -170,7 +171,8 @@ MicMicrodownObjectToPillarObjectConverter >> visitLink: aMicLink [ ^ aMicLink associatedPillarClass new setChildren: - (aMicLink children collect: [ :n | n accept: self ]); + (aMicLink children collect: [ :n | + n accept: self ]); reference: aMicLink url asString; yourself ] @@ -243,6 +245,13 @@ MicMicrodownObjectToPillarObjectConverter >> visitRaw: aMicRaw [ yourself ] +{ #category : 'visiting' } +MicMicrodownObjectToPillarObjectConverter >> visitRawParagraph: aMicParagraphBlock [ + ^ PRParagraph new + setChildren: (aMicParagraphBlock pillarFromString: aMicParagraphBlock text); + yourself +] + { #category : 'visiting' } MicMicrodownObjectToPillarObjectConverter >> visitRoot: aMicRootBlock [ ^ PRDocument new diff --git a/src/Microdown/MicAbstractBlock.class.st b/src/Microdown/MicAbstractBlock.class.st index 8adec23f2..752217379 100644 --- a/src/Microdown/MicAbstractBlock.class.st +++ b/src/Microdown/MicAbstractBlock.class.st @@ -154,7 +154,7 @@ MicAbstractBlock >> parser [ { #category : 'private' } MicAbstractBlock >> parserClass [ - ^ MicrodownParser + ^ MicParser ] { #category : 'adding' } diff --git a/src/Microdown/MicHTMLTagParagraphBlock.class.st b/src/Microdown/MicHTMLTagParagraphBlock.class.st new file mode 100644 index 000000000..9c15583bf --- /dev/null +++ b/src/Microdown/MicHTMLTagParagraphBlock.class.st @@ -0,0 +1,118 @@ +" +I'm a block level that handle html tag. +For this to work tag should start at the beginning of the line. +I can contain nested tags and my body can contain microdown elements that are then interpreted. + + +``` +| mic | + mic := (parser parse: ' +No idea what it is :) +') children first. + self assert: mic class equals: MicRawParagraphBlock. + self assert: mic label equals: 'title'. + self assert: mic children first class equals: MicParagraphBlock +``` + +``` +testRawParagraphCanBeNested + | children | + children := (parser parse: ' +No idea what it is :) + +<button> +hkhkjhkj +</button> +') children. + self assert: children size equals: 1. + self assert: children first class equals: MicRawParagraphBlock. + self assert: children first label equals: 'title'. + self assert: children first children size equals: 2. + self assert: children first children first class equals: MicParagraphBlock. + self assert: children first children second label equals: 'button'. + self assert: children first children second class equals: MicRawParagraphBlock. +``` +" +Class { + #name : 'MicHTMLTagParagraphBlock', + #superclass : 'MicNestedMarkupBlock', + #instVars : [ + 'label', + 'argumentsString' + ], + #category : 'Microdown-Model', + #package : 'Microdown', + #tag : 'Model' +} + +{ #category : 'public' } +MicHTMLTagParagraphBlock class >> alternateBlockClassFor: aLine [ + "We do not want to have extention for MicRawParaphTag" + + ^ self +] + +{ #category : 'visiting' } +MicHTMLTagParagraphBlock >> accept: aVisitor [ + + aVisitor visitHTMLTagParagraph: self +] + +{ #category : 'activation' } +MicHTMLTagParagraphBlock >> argumentString [ + ^ argumentsString +] + +{ #category : 'handle' } +MicHTMLTagParagraphBlock >> extractFirstLineFrom: aLine [ + "we got < tag ...x > foo bar or < tag ...x and we return tag ...x " + + | splits | + firstLine := aLine copyFrom: self lineStartMarkup size to: aLine size. + firstLine := firstLine trimBoth. + firstLine := firstLine copyFrom: 1 to: firstLine size -1. + + splits := firstLine splitOnFirst: $>. + splits second isNotEmpty + ifTrue: [ "here we p>Blo and we discard Blo" + label := splits first ]. + + "now we may have

    > hasArguments [ + ^ argumentsString isNotEmpty +] + +{ #category : 'initialization' } +MicHTMLTagParagraphBlock >> initialize [ + + super initialize. + argumentsString := '' +] + +{ #category : 'accessing' } +MicHTMLTagParagraphBlock >> label [ + ^ label +] + +{ #category : 'markups' } +MicHTMLTagParagraphBlock >> lineStopMarkup [ + + ^ '' +] + +{ #category : 'printing' } +MicHTMLTagParagraphBlock >> printOn: aStream [ + + super printOn: aStream. + aStream << '(' ; + << label ; + << ')' +] diff --git a/src/Microdown/MicParser.class.st b/src/Microdown/MicParser.class.st new file mode 100644 index 000000000..048f5e878 --- /dev/null +++ b/src/Microdown/MicParser.class.st @@ -0,0 +1,378 @@ +" +# Microdown Parser and Elements + +I'm a parser for Microdown, implemented by S. Ducasse, L. Dargaud and G. Polito (2020). +It is based on K. Osterbye's work on GitHubMarkdown. + +Microdown covers the basic syntax of Markdown, with great Pillar features. It's also more extensible. + +## MicroDown Syntax in a Nutshell! + +**Headers** (up to 6 levels) >>>> `# Header level 1` + +**Unordered list** +```text +- item a + on another line +- item b + - item b1 haaaaaaaaaa + - item b2 +- item c +``` +Produces: +- item a + on another line +- item b + - item b1 + - item b2 +- item c + +**Ordered list** +```text +1. item one + 1. subitem +2. item two +``` +Produces: +1. item one + 1. subitem +2. item two + + +**Codeblock with arguments** +```text + ```language=Pharo&caption=Beautiful&label=Fig1 + some piece of code + ``` +``` +Produces: +```language=Pharo&caption=Beautiful&label=Fig1 +some piece of code +``` + +**Anchor and its reference** >>>> `@anchor` and its reference in text with `*@anchor@*` + +**Math environment** (rendered with LaTex in Pharo) +- inline: $\cos(\theta_i+C_3)$ >>>> `$\cos(\theta_i+C_3)$` +- block: +```text +$$ +\cos(\theta_i+C_3) +$$ +``` +$$ +\cos(\theta_i+C_3) +$$ + +**Annotated paragraph** +```text +!!note Then you can write your note +on several lines, like a normal paragraph +``` + +**Quote** to indent some lines +```text +> This is +> A nice quote +``` + +**Environment** (called Annotation for inline) +- inline: `\` +- block: +```text + +``` + +**Metadata** as a block +```text +{ +type your metadata +} +``` + +**Table** +Support for tables with a simpler and more relaxed setup than GitHub. +A table is not forced to have a header definition. + +```text +| aaa | bbb +| ccc | ddd +``` +Produces: +| aaa | bbb +| ccc | ddd + +**Horizontal line** >>>> `***` alone on a line will define a separator in your text. +*** + +**Comments** don't appear after parsing >>>> `% A commented line` +% This can only be read in the original source, as you are doing. + +**Inline formats** +- **bold** >>>> `**bold**` +- _italic_ >>>> `_italic_` +- `monospace` aka inline code >>>> `\`monospace\`` + +_Note: inline escape character is the backslash: `\\`._ +_Note: text in monospace is analyzed for you to generate hypertext Pharo code objects! Isn't it so cool to write a great doc? It is! Click on them!_ +- `Point` for classes +- `Point class` for class side +- `Point>>#setX:setY:` for methods +- `#'Microdown-Tests'` for packages + +**Raw** for your other code (inline) >>>> `{{ some code }}` + +**Link** >>>> `[link's name](url|key1=value1&key2=value2)` + +**Figure** >>>> `![figure's name](url|key1=value1&key2=value2)` + +`![Pharo logo](https://files.pharo.org/media/logo/logo.png)` +produces + +![Pharo logo](https://files.pharo.org/media/logo/logo.png) + + +## Implementation Notes + +I follow the design mentioned in [https://github.github.com/gfm](https://github.github.com/gfm), in particular the parsing strategy in the appendix. + +In short, the strategy is that at any point in time, we can have a number of children of the root that are ""open"". The deepest in open in the tree is called ""current"". All the parents of current are open. + +When a new line is read, we do the following: + +1. Check if the new line can be consumed by current. + - as part of this, a child of current can be made which can consume the new line. For example, when consuming `\`\`\``, the root block node will create a new code block that will become current and consume the body of the `\`\`\`` element, then close. +2. If current cannot consume the new line, we close current, move current to its parent, and repeat 1. +3. The root node can consume anything, for instance by making new nodes to store the new line. +4. The root node is not closed until input is exhausted. + +I do not accept lazy definition. I do not accept three different ways to do the same thing. Except for bulleted lists, where `*` and `-` are accepted. + +The spec says: +```text +-> document + -> block_quote + paragraph + ""Lorem ipsum dolor\nsit amet."" + -> list (type=bullet tight=true bullet_char=-) + list_item + paragraph + ""Qui *quodsi iracundia*"" + -> list_item + -> paragraph + ""aliquando id"" +``` +The current implementation does not create a paragraph in the list_item element. +" +Class { + #name : 'MicParser', + #superclass : 'Object', + #instVars : [ + 'current', + 'root', + 'dispatchTable' + ], + #pools : [ + 'MicSharedPool' + ], + #category : 'Microdown-Parser', + #package : 'Microdown', + #tag : 'Parser' +} + +{ #category : 'builder' } +MicParser class >> builder [ + ^ MicMicrodownTextualBuilder new +] + +{ #category : 'examples' } +MicParser class >> example [ + + + ^ self parse: self comment +] + +{ #category : 'instance creation' } +MicParser class >> parse: aString [ + + ^ self new parse: aString +] + +{ #category : 'node creation' } +MicParser >> blockStarterClassFrom: line [ + "return the class of a block which can start with line, or nil if none" + + line ifEmpty: [ ^ nil ]. + (self matchOrdered: line) + ifTrue: [ ^ self orderedListBlockClass ] + ifFalse: [ + "right now this is super ugly and slow + but this is to try. Once we understand and probably change the environment syntax for now it is , i and i> + This is working p src=800 + " + + (line includes: Character space) + ifTrue: [ HTMLTags includes: ((line allButFirst splitOnFirst: Character space) first) ] + ifFalse: [ HTMLTags includes: ((line allButFirst splitOnFirst: $>) first) ] + ]) + ifTrue: [ + "we have a rawparagraph block" + ^ MicHTMLTagParagraphBlock + ] + ifFalse: [ + + "the max significant length of a markup is 3" + (3 to: 1 by: -1) do: [:i | | prefix | + prefix := (line truncateTo: i). + "If we have a inline starter, it takes precedence" + (Delimiters includes: prefix) + ifTrue: [ ^ self delimiterBlockClassFor: prefix ]. + dispatchTable + at: prefix + ifPresent: [ :blockClass | ^ blockClass ] ] ] ]. + + ^ self nonMatchedBlockClassFor: line +] + +{ #category : 'accessing' } +MicParser >> builder [ + "return a little helper to build microdown correct expression" + + ^ self class builder +] + +{ #category : 'accessing' } +MicParser >> current [ + + ^ current +] + +{ #category : 'node creation' } +MicParser >> delimiterBlockClassFor: prefix [ + + ^ self paragraphBlockClass +] + +{ #category : 'parsing' } +MicParser >> handleLine: line [ + + "The logic is the following: + -if the current block can consume the line, it manages it and this potentially creates a new block that becomes the current one. + When the line is not consumed, the current block is closed and its parent becomes the current one and the process is called back to treat the line." + + (current canConsumeLine: (current massageLine: line)) + ifTrue: [ + current := current addLineAndReturnNextNode: line. + ^ current ] + ifFalse: [ current closeMe ]. + current := current parent. + self handleLine: line +] + +{ #category : 'initialization' } +MicParser >> initialize [ + + super initialize. + dispatchTable := Dictionary new. + dispatchTable at: AnchorMarkup put: MicAnchorBlock. + dispatchTable at: HeaderMarkup put: MicHeaderBlock. + dispatchTable at: CodeblockMarkup put: MicAbstractCodeBlock. + dispatchTable at: AnnotatedParagraphOpeningMarkup put: MicAnnotatedParagraphBlock. + dispatchTable at: AnnotatedParagraphBackwardCompatibleMarkup put: MicAnnotatedParagraphBlock. + dispatchTable at: CommentedLineMarkup put: MicCommentBlock. + dispatchTable at: HorizontalLineMarkup put: MicHorizontalLineBlock. + dispatchTable at: MathOpeningBlockMarkup put: MicMathBlock. + dispatchTable at: EnvironmentOpeningBlockMarkup put: MicEnvironmentBlock. + dispatchTable at: BlockLevelRawOpenerMarkup put: MicRawParagraphBlock. + dispatchTable at: MetaDataOpeningBlockMarkup put: MicMetaDataBlock. + dispatchTable at: UnorderedListPlusMarkup put: MicUnorderedListBlock. + dispatchTable at: UnorderedListMarkup put: MicUnorderedListBlock. + dispatchTable at: UnorderedListStarMarkup put: MicUnorderedListBlock. + dispatchTable at: PreformattedMarkup put: MicBlockQuoteBlock. + dispatchTable at: TableCellMarkup put: MicTableBlock. +] + +{ #category : 'testing' } +MicParser >> isAList: normalized [ + + ^ (self matchUnordered: normalized) or: [ self matchOrdered: normalized ] +] + +{ #category : 'node creation' } +MicParser >> matchOrdered: line [ + ^ line prefixMatchesRegex: '\d+(\.|\))' +] + +{ #category : 'node creation' } +MicParser >> matchUnordered: line [ + ^ ( line beginsWith: UnorderedListPlusMarkup ) + | ( line beginsWith: UnorderedListStarMarkup ) + | ( line beginsWith: UnorderedListMarkup ) +] + +{ #category : 'parsing' } +MicParser >> newRootBlock [ + ^ MicRootBlock new +] + +{ #category : 'node creation' } +MicParser >> nonMatchedBlockClassFor: line [ + + ^ self paragraphBlockClass +] + +{ #category : 'node creation' } +MicParser >> orderedListBlockClass [ + + ^ MicOrderedListBlock +] + +{ #category : 'node creation' } +MicParser >> paragraphBlockClass [ + + ^ MicParagraphBlock +] + +{ #category : 'parsing' } +MicParser >> parse: aStreamOrString [ + "returns the root node of aStreamOrText" + + | inStream line | + current := root := self newRootBlock + setParser: self; + yourself. + inStream := aStreamOrString isStream + ifTrue: [ aStreamOrString readStream ] + ifFalse: [ aStreamOrString asString readStream ]. + [ + line := inStream nextLine. + line isNil ] + whileFalse: [ + self handleLine: line ]. + [ current = root ] + whileFalse: [ + current closeMe. + current := current parent ]. + root hasMetaDataElement + ifTrue: [ root properties: root metaDataElement body ]. + + + ^ root +] + +{ #category : 'accessing' } +MicParser >> setCurrent: aBlock [ + "aBlock parent = current + ifFalse: [ self error: 'When changing current, the current block should be the parent of the new one' ]. + note for me: we could just inforce this. aBlock parent: current. + No this is not correct since + the parent of an item list is the list and this is the list that is parent of environment." + current := aBlock +] diff --git a/src/Microdown/MicRawBlock.class.st b/src/Microdown/MicRawBlock.class.st index 58566d5a5..4ca266fd4 100644 --- a/src/Microdown/MicRawBlock.class.st +++ b/src/Microdown/MicRawBlock.class.st @@ -28,5 +28,6 @@ MicRawBlock class >> openingDelimiter [ { #category : 'visiting' } MicRawBlock >> accept: aVisitor [ + "A raw element should not do any interpretation of its contents." ^ aVisitor visitRaw: self ] diff --git a/src/Microdown/MicRawParagraphBlock.class.st b/src/Microdown/MicRawParagraphBlock.class.st index fd78773f4..fb8fab4d0 100644 --- a/src/Microdown/MicRawParagraphBlock.class.st +++ b/src/Microdown/MicRawParagraphBlock.class.st @@ -28,73 +28,27 @@ Class { #tag : 'Model' } -{ #category : 'public' } -MicRawParagraphBlock class >> alternateBlockClassFor: aLine [ - "We do not want to have extention for MicRawParaphTag" - - ^ self -] - { #category : 'visiting' } MicRawParagraphBlock >> accept: aVisitor [ ^ aVisitor visitRawParagraph: self ] -{ #category : 'activation' } -MicRawParagraphBlock >> argumentString [ - ^ argumentsString -] - -{ #category : 'handle' } -MicRawParagraphBlock >> extractFirstLineFrom: aLine [ - "we got < tag ...x > foo bar or < tag ...x and we return tag ...x " - - | splits | - firstLine := aLine copyFrom: self lineStartMarkup size to: aLine size. - firstLine := firstLine trimBoth. - firstLine := firstLine copyFrom: 1 to: firstLine size -1. - - splits := firstLine splitOnFirst: $>. - splits second isNotEmpty - ifTrue: [ "here we p>Blo and we discard Blo" - label := splits first ]. - - "now we may have

    > hasArguments [ - ^ argumentsString isNotEmpty -] - -{ #category : 'initialization' } -MicRawParagraphBlock >> initialize [ - - super initialize. - argumentsString := '' -] +{ #category : 'markups' } +MicRawParagraphBlock >> lineStartMarkup [ -{ #category : 'accessing' } -MicRawParagraphBlock >> label [ - ^ label + ^ BlockLevelRawOpenerMarkup ] { #category : 'markups' } MicRawParagraphBlock >> lineStopMarkup [ - ^ '' + ^ MetaDataClosingBlockMarkup ] { #category : 'printing' } -MicRawParagraphBlock >> printOn: aStream [ +MicRawParagraphBlock >> printOn: aStream [ - super printOn: aStream. - aStream << '(' ; - << label ; - << ')' + aStream nextPutAll: '{{{'. + aStream nextPutAll: self body. + aStream nextPutAll: '}}}' ] diff --git a/src/Microdown/MicSharedPool.class.st b/src/Microdown/MicSharedPool.class.st index f6e2bf12a..317ca0b3b 100644 --- a/src/Microdown/MicSharedPool.class.st +++ b/src/Microdown/MicSharedPool.class.st @@ -17,6 +17,8 @@ Class { 'ArgumentListEqualsDelimiter', 'ArgumentListOfFigureStartDelimiter', 'ArgumentListStartDelimiter', + 'BlockLevelRawCloserMarkup', + 'BlockLevelRawOpenerMarkup', 'BoldMarkup', 'CodeblockMarkup', 'CommentedLineMarkup', @@ -107,6 +109,9 @@ MicSharedPool class >> initialize [ UnorderedListMarkup := '- '. UnorderedListPlusMarkup := '+ '. UnorderedListStarMarkup := '* '. + + BlockLevelRawCloserMarkup := '}}}'. + BlockLevelRawOpenerMarkup := '{{{'. self initializeRawParagraph. diff --git a/src/Microdown/MicStartStopMarkupBlock.class.st b/src/Microdown/MicStartStopMarkupBlock.class.st index f5c29c9c9..87b684335 100644 --- a/src/Microdown/MicStartStopMarkupBlock.class.st +++ b/src/Microdown/MicStartStopMarkupBlock.class.st @@ -81,13 +81,14 @@ MicStartStopMarkupBlock >> canConsumeLine: line [ { #category : 'testing' } MicStartStopMarkupBlock >> doesLineStartWithStopMarkup: line [ - "return if the line starts with a stop markup" + ^ line beginsWith: self lineStopMarkup ] { #category : 'handle' } MicStartStopMarkupBlock >> extractFirstLineFrom: line [ + ^ line copyFrom: self lineStartMarkup size + 1 to: line size ] diff --git a/src/Microdown/Microdown.class.st b/src/Microdown/Microdown.class.st index 6dac537c0..deb4c792e 100644 --- a/src/Microdown/Microdown.class.st +++ b/src/Microdown/Microdown.class.st @@ -19,7 +19,7 @@ Class { { #category : 'facade' } Microdown class >> builder [ - ^ MicrodownParser builder + ^ MicParser builder ] { #category : 'facade' } @@ -140,7 +140,7 @@ Microdown >> builder [ { #category : 'facade' } Microdown >> parse: aStreamOrString [ - ^ MicrodownParser parse: aStreamOrString + ^ MicParser parse: aStreamOrString ] { #category : 'facade' } @@ -148,7 +148,7 @@ Microdown >> parseFile: aFile [ "Parse and return a document from the argument marking it with the file it is contained in. This is important for path resolution." | root | - root := MicrodownParser parse: aFile contents. + root := MicParser parse: aFile contents. root fromFile: aFile. ^ root diff --git a/src/Microdown/MicrodownParser.class.st b/src/Microdown/MicrodownParser.class.st index 60e11c104..9d772613f 100644 --- a/src/Microdown/MicrodownParser.class.st +++ b/src/Microdown/MicrodownParser.class.st @@ -1,377 +1,7 @@ -" -# Microdown Parser and Elements - -I'm a parser for Microdown, implemented by S. Ducasse, L. Dargaud and G. Polito (2020). -It is based on K. Osterbye's work on GitHubMarkdown. - -Microdown covers the basic syntax of Markdown, with great Pillar features. It's also more extensible. - -## MicroDown Syntax in a Nutshell! - -**Headers** (up to 6 levels) >>>> `# Header level 1` - -**Unordered list** -```text -- item a - on another line -- item b - - item b1 haaaaaaaaaa - - item b2 -- item c -``` -Produces: -- item a - on another line -- item b - - item b1 - - item b2 -- item c - -**Ordered list** -```text -1. item one - 1. subitem -2. item two -``` -Produces: -1. item one - 1. subitem -2. item two - - -**Codeblock with arguments** -```text - ```language=Pharo&caption=Beautiful&label=Fig1 - some piece of code - ``` -``` -Produces: -```language=Pharo&caption=Beautiful&label=Fig1 -some piece of code -``` - -**Anchor and its reference** >>>> `@anchor` and its reference in text with `*@anchor@*` - -**Math environment** (rendered with LaTex in Pharo) -- inline: $\cos(\theta_i+C_3)$ >>>> `$\cos(\theta_i+C_3)$` -- block: -```text -$$ -\cos(\theta_i+C_3) -$$ -``` -$$ -\cos(\theta_i+C_3) -$$ - -**Annotated paragraph** -```text -!!note Then you can write your note -on several lines, like a normal paragraph -``` - -**Quote** to indent some lines -```text -> This is -> A nice quote -``` - -**Environment** (called Annotation for inline) -- inline: `\` -- block: -```text - -``` - -**Metadata** as a block -```text -{ -type your metadata -} -``` - -**Table** -Support for tables with a simpler and more relaxed setup than GitHub. -A table is not forced to have a header definition. - -```text -| aaa | bbb -| ccc | ddd -``` -Produces: -| aaa | bbb -| ccc | ddd - -**Horizontal line** >>>> `***` alone on a line will define a separator in your text. -*** - -**Comments** don't appear after parsing >>>> `% A commented line` -% This can only be read in the original source, as you are doing. - -**Inline formats** -- **bold** >>>> `**bold**` -- _italic_ >>>> `_italic_` -- `monospace` aka inline code >>>> `\`monospace\`` - -_Note: inline escape character is the backslash: `\\`._ -_Note: text in monospace is analyzed for you to generate hypertext Pharo code objects! Isn't it so cool to write a great doc? It is! Click on them!_ -- `Point` for classes -- `Point class` for class side -- `Point>>#setX:setY:` for methods -- `#'Microdown-Tests'` for packages - -**Raw** for your other code (inline) >>>> `{{ some code }}` - -**Link** >>>> `[link's name](url|key1=value1&key2=value2)` - -**Figure** >>>> `![figure's name](url|key1=value1&key2=value2)` - -`![Pharo logo](https://files.pharo.org/media/logo/logo.png)` -produces - -![Pharo logo](https://files.pharo.org/media/logo/logo.png) - - -## Implementation Notes - -I follow the design mentioned in [https://github.github.com/gfm](https://github.github.com/gfm), in particular the parsing strategy in the appendix. - -In short, the strategy is that at any point in time, we can have a number of children of the root that are ""open"". The deepest in open in the tree is called ""current"". All the parents of current are open. - -When a new line is read, we do the following: - -1. Check if the new line can be consumed by current. - - as part of this, a child of current can be made which can consume the new line. For example, when consuming `\`\`\``, the root block node will create a new code block that will become current and consume the body of the `\`\`\`` element, then close. -2. If current cannot consume the new line, we close current, move current to its parent, and repeat 1. -3. The root node can consume anything, for instance by making new nodes to store the new line. -4. The root node is not closed until input is exhausted. - -I do not accept lazy definition. I do not accept three different ways to do the same thing. Except for bulleted lists, where `*` and `-` are accepted. - -The spec says: -```text --> document - -> block_quote - paragraph - ""Lorem ipsum dolor\nsit amet."" - -> list (type=bullet tight=true bullet_char=-) - list_item - paragraph - ""Qui *quodsi iracundia*"" - -> list_item - -> paragraph - ""aliquando id"" -``` -The current implementation does not create a paragraph in the list_item element. -" Class { #name : 'MicrodownParser', - #superclass : 'Object', - #instVars : [ - 'current', - 'root', - 'dispatchTable' - ], - #pools : [ - 'MicSharedPool' - ], + #superclass : 'MicParser', #category : 'Microdown-Parser', #package : 'Microdown', #tag : 'Parser' } - -{ #category : 'builder' } -MicrodownParser class >> builder [ - ^ MicMicrodownTextualBuilder new -] - -{ #category : 'examples' } -MicrodownParser class >> example [ - - - ^ self parse: self comment -] - -{ #category : 'instance creation' } -MicrodownParser class >> parse: aString [ - - ^ self new parse: aString -] - -{ #category : 'node creation' } -MicrodownParser >> blockStarterClassFrom: line [ - "return the class of a block which can start with line, or nil if none" - - line ifEmpty: [ ^ nil ]. - (self matchOrdered: line) - ifTrue: [ ^ self orderedListBlockClass ] - ifFalse: [ - "right now this is super ugly and slow - but this is to try. Once we understand and probably change the environment syntax for now it is , i and i> - This is working p src=800 - " - - (line includes: Character space) - ifTrue: [ HTMLTags includes: ((line allButFirst splitOnFirst: Character space) first) ] - ifFalse: [ HTMLTags includes: ((line allButFirst splitOnFirst: $>) first) ] - ]) - ifTrue: [ - "we have a rawparagraph block" - ^ MicRawParagraphBlock - ] - ifFalse: [ - - "the max significant length of a markup is 3" - (3 to: 1 by: -1) do: [:i | | prefix | - prefix := (line truncateTo: i). - "If we have a inline starter, it takes precedence" - (Delimiters includes: prefix) - ifTrue: [ ^ self delimiterBlockClassFor: prefix ]. - dispatchTable - at: prefix - ifPresent: [ :blockClass | ^ blockClass ] ] ] ]. - - ^ self nonMatchedBlockClassFor: line -] - -{ #category : 'accessing' } -MicrodownParser >> builder [ - "return a little helper to build microdown correct expression" - - ^ self class builder -] - -{ #category : 'accessing' } -MicrodownParser >> current [ - - ^ current -] - -{ #category : 'node creation' } -MicrodownParser >> delimiterBlockClassFor: prefix [ - - ^ self paragraphBlockClass -] - -{ #category : 'parsing' } -MicrodownParser >> handleLine: line [ - - "The logic is the following: - -if the current block can consume the line, it manages it and this potentially creates a new block that becomes the current one. - When the line is not consumed, the current block is closed and its parent becomes the current one and the process is called back to treat the line." - - (current canConsumeLine: (current massageLine: line)) - ifTrue: [ - current := current addLineAndReturnNextNode: line. - ^ current ] - ifFalse: [ current closeMe ]. - current := current parent. - self handleLine: line -] - -{ #category : 'initialization' } -MicrodownParser >> initialize [ - - super initialize. - dispatchTable := Dictionary new. - dispatchTable at: AnchorMarkup put: MicAnchorBlock. - dispatchTable at: HeaderMarkup put: MicHeaderBlock. - dispatchTable at: CodeblockMarkup put: MicAbstractCodeBlock. - dispatchTable at: AnnotatedParagraphOpeningMarkup put: MicAnnotatedParagraphBlock. - dispatchTable at: AnnotatedParagraphBackwardCompatibleMarkup put: MicAnnotatedParagraphBlock. - dispatchTable at: CommentedLineMarkup put: MicCommentBlock. - dispatchTable at: HorizontalLineMarkup put: MicHorizontalLineBlock. - dispatchTable at: MathOpeningBlockMarkup put: MicMathBlock. - dispatchTable at: EnvironmentOpeningBlockMarkup put: MicEnvironmentBlock. - dispatchTable at: MetaDataOpeningBlockMarkup put: MicMetaDataBlock. - dispatchTable at: UnorderedListPlusMarkup put: MicUnorderedListBlock. - dispatchTable at: UnorderedListMarkup put: MicUnorderedListBlock. - dispatchTable at: UnorderedListStarMarkup put: MicUnorderedListBlock. - dispatchTable at: PreformattedMarkup put: MicBlockQuoteBlock. - dispatchTable at: TableCellMarkup put: MicTableBlock. -] - -{ #category : 'testing' } -MicrodownParser >> isAList: normalized [ - - ^ (self matchUnordered: normalized) or: [ self matchOrdered: normalized ] -] - -{ #category : 'node creation' } -MicrodownParser >> matchOrdered: line [ - ^ line prefixMatchesRegex: '\d+(\.|\))' -] - -{ #category : 'node creation' } -MicrodownParser >> matchUnordered: line [ - ^ ( line beginsWith: UnorderedListPlusMarkup ) - | ( line beginsWith: UnorderedListStarMarkup ) - | ( line beginsWith: UnorderedListMarkup ) -] - -{ #category : 'parsing' } -MicrodownParser >> newRootBlock [ - ^ MicRootBlock new -] - -{ #category : 'node creation' } -MicrodownParser >> nonMatchedBlockClassFor: line [ - - ^ self paragraphBlockClass -] - -{ #category : 'node creation' } -MicrodownParser >> orderedListBlockClass [ - - ^ MicOrderedListBlock -] - -{ #category : 'node creation' } -MicrodownParser >> paragraphBlockClass [ - - ^ MicParagraphBlock -] - -{ #category : 'parsing' } -MicrodownParser >> parse: aStreamOrString [ - "returns the root node of aStreamOrText" - - | inStream line | - current := root := self newRootBlock - setParser: self; - yourself. - inStream := aStreamOrString isStream - ifTrue: [ aStreamOrString readStream ] - ifFalse: [ aStreamOrString asString readStream ]. - [ - line := inStream nextLine. - line isNil ] - whileFalse: [ - self handleLine: line ]. - [ current = root ] - whileFalse: [ - current closeMe. - current := current parent ]. - root hasMetaDataElement - ifTrue: [ root properties: root metaDataElement body ]. - - - ^ root -] - -{ #category : 'accessing' } -MicrodownParser >> setCurrent: aBlock [ - "aBlock parent = current - ifFalse: [ self error: 'When changing current, the current block should be the parent of the new one' ]. - note for me: we could just inforce this. aBlock parent: current. - No this is not correct since - the parent of an item list is the list and this is the list that is parent of environment." - current := aBlock -] diff --git a/src/Microdown/MicrodownVisitor.class.st b/src/Microdown/MicrodownVisitor.class.st index 80f7fa08c..03822d82e 100644 --- a/src/Microdown/MicrodownVisitor.class.st +++ b/src/Microdown/MicrodownVisitor.class.st @@ -46,6 +46,12 @@ MicrodownVisitor >> visitAnnotated: anAnnotated [ MicrodownVisitor >> visitAnnotation: anAnnotation [ ] +{ #category : 'visiting - html extensions' } +MicrodownVisitor >> visitBlockLevelRawParagraph: aRawParagraph [ + + +] + { #category : 'visiting - inline elements' } MicrodownVisitor >> visitBold: aFormat [ @@ -127,6 +133,12 @@ MicrodownVisitor >> visitFootnote: aFootnote [ "subclassResponsibility" ] +{ #category : 'visiting - html extensions' } +MicrodownVisitor >> visitHTMLTagParagraph: anHTMLTagParagraph [ + + +] + { #category : 'visiting' } MicrodownVisitor >> visitHeader: aHeader [ @@ -231,6 +243,13 @@ MicrodownVisitor >> visitQuote: aQuote [ MicrodownVisitor >> visitRaw: aRawFormat [ +] + +{ #category : 'visiting - html extensions' } +MicrodownVisitor >> visitRawParagraph: aRawParagraph [ + "a raw paragraph is a paragraph whose contents is never changed." + + ] { #category : 'visiting' } From 3f9c6cbdb26b74d2a82af7a8bc209554f751bc68 Mon Sep 17 00:00:00 2001 From: Fanirindrainy Date: Fri, 20 Mar 2026 18:13:16 +0300 Subject: [PATCH 3/4] Add method structure checker for empty line after selector without comment --- .../MicMethodStructureChecker.class.st | 27 +++++++ .../MicMethodStructureCheckerTest.class.st | 77 +++++++++++++++++++ .../MicMethodStructureResult.class.st | 31 ++++++++ 3 files changed, 135 insertions(+) create mode 100644 src/Microdown-Rules/MicMethodStructureChecker.class.st create mode 100644 src/Microdown-Rules/MicMethodStructureCheckerTest.class.st create mode 100644 src/Microdown-Rules/MicMethodStructureResult.class.st diff --git a/src/Microdown-Rules/MicMethodStructureChecker.class.st b/src/Microdown-Rules/MicMethodStructureChecker.class.st new file mode 100644 index 000000000..310c28a65 --- /dev/null +++ b/src/Microdown-Rules/MicMethodStructureChecker.class.st @@ -0,0 +1,27 @@ +Class { + #name : 'MicMethodStructureChecker', + #superclass : 'MicChecker', + #category : 'Microdown-Rules-MethodStructure', + #package : 'Microdown-Rules', + #tag : 'MethodStructure' +} + +{ #category : 'visiting' } +MicMethodStructureChecker >> visitCode: aMicCode [ + | lines firstLine secondLine | + lines := aMicCode body lines. + lines size < 2 ifTrue: [ ^ self ]. + firstLine := lines first. + secondLine := lines second. + "If the second line begins with a comment, that's correct." + (secondLine trimLeft beginsWith: '"') ifTrue: [ ^ self ]. + "If the second line is empty, it's correct." + secondLine trimLeft isEmpty ifTrue: [ ^ self ]. + "ifelse, theres's a blank line missing after the selector" + results add: (MicMethodStructureResult new + micElement: aMicCode; + inFile: aMicCode; + detail: firstLine; + yourself + ) +] diff --git a/src/Microdown-Rules/MicMethodStructureCheckerTest.class.st b/src/Microdown-Rules/MicMethodStructureCheckerTest.class.st new file mode 100644 index 000000000..f95f0376f --- /dev/null +++ b/src/Microdown-Rules/MicMethodStructureCheckerTest.class.st @@ -0,0 +1,77 @@ +Class { + #name : 'MicMethodStructureCheckerTest', + #superclass : 'TestCase', + #instVars : [ + 'fileSystem', + 'checker' + ], + #category : 'Microdown-Rules-MethodStructure', + #package : 'Microdown-Rules', + #tag : 'MethodStructure' +} + +{ #category : 'running' } +MicMethodStructureCheckerTest >> generateFilesystemExample [ + | file | + file := fileSystem workingDirectory / 'methodWrong.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: '``` + goodWordFor: aString + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value + + `````' + ]. + file := fileSystem workingDirectory / 'methodCorrect.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: '``` + goodWordFor: aString + + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value + + `````' + ]. + file := fileSystem workingDirectory / 'methodWithComment.md'. + file writeStreamDo: [ :stream | + stream nextPutAll: '``` + goodWordFor: aString + "there is a comment" + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value + + `````' + ]. +] + +{ #category : 'running' } +MicMethodStructureCheckerTest >> setUp [ + super setUp. + + fileSystem := FileSystem memory. + self generateFilesystemExample. + checker := MicMethodStructureChecker new. +] + +{ #category : 'tests' } +MicMethodStructureCheckerTest >> testMethodCorrect [ + checker checkProject: fileSystem / 'methodCorrect.md'. + self assert: checker isOkay. +] + +{ #category : 'tests' } +MicMethodStructureCheckerTest >> testMethodWithCommentCorrect [ + checker checkProject: fileSystem / 'methodWithComment.md'. + self assert: checker isOkay. +] + +{ #category : 'tests' } +MicMethodStructureCheckerTest >> testMethodWrongDetected [ + checker checkProject: fileSystem / 'methodWrong.md'. + self deny: checker isOkay. + self assert: checker results size equals: 1. + self assert: checker results first detail equals: 'goodWordFor: aString'. +] diff --git a/src/Microdown-Rules/MicMethodStructureResult.class.st b/src/Microdown-Rules/MicMethodStructureResult.class.st new file mode 100644 index 000000000..9ffbb6141 --- /dev/null +++ b/src/Microdown-Rules/MicMethodStructureResult.class.st @@ -0,0 +1,31 @@ +Class { + #name : 'MicMethodStructureResult', + #superclass : 'MicAbstractResult', + #instVars : [ + 'detail' + ], + #category : 'Microdown-Rules-MethodStructure', + #package : 'Microdown-Rules', + #tag : 'MethodStructure' +} + +{ #category : 'accessing' } +MicMethodStructureResult >> detail [ + + ^ detail +] + +{ #category : 'accessing' } +MicMethodStructureResult >> detail: aString [ + + detail := aString +] + +{ #category : 'accessing' } +MicMethodStructureResult >> explanation [ + ^ 'Text: "' , micElement body + , '" in file' , fileReference fullName + , ' contains a mistake: method [' + , detail + , '] should have an empty line after the selector when is no comment.' +] From c4970b194fc55dffe1b64de50328ff303ae26b91 Mon Sep 17 00:00:00 2001 From: Fanirindrainy Date: Sun, 22 Mar 2026 13:51:43 +0300 Subject: [PATCH 4/4] Add class comment to MicMethodStructureChecker. --- .../MicMethodStructureChecker.class.st | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Microdown-Rules/MicMethodStructureChecker.class.st b/src/Microdown-Rules/MicMethodStructureChecker.class.st index 310c28a65..94db38033 100644 --- a/src/Microdown-Rules/MicMethodStructureChecker.class.st +++ b/src/Microdown-Rules/MicMethodStructureChecker.class.st @@ -1,3 +1,26 @@ +" +I check that methods in code blocks have an empty line after the selector when there is no comment. + +Example of incorrect method (no comment, no empty line): + goodWordFor: aString + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value + +Example of correct method (no comment, empty line after selector): + goodWordFor: aString + + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value + +Example of correct method (with comment, no empty line needed): + goodWordFor: aString + ""there is a comment"" + | pair | + pair := pairs detect: [ :each | each key = aString ]. + ^ pair value +" Class { #name : 'MicMethodStructureChecker', #superclass : 'MicChecker',