diff --git a/src/Microdown-BookTester-Tests/MicMethodBodySyncTest.class.st b/src/Microdown-BookTester-Tests/MicMethodBodySyncTest.class.st new file mode 100644 index 00000000..34a8318d --- /dev/null +++ b/src/Microdown-BookTester-Tests/MicMethodBodySyncTest.class.st @@ -0,0 +1,237 @@ +Class { + #name : 'MicMethodBodySyncTest', + #superclass : 'TestCase', + #category : 'Microdown-BookTester-Tests', + #package : 'Microdown-BookTester-Tests' +} + +{ #category : 'data' } +MicMethodBodySyncTest class >> simpleCode [ + "The comment of simpleCode" + + ^ 100 slowFactorial +] + +{ #category : 'data' } +MicMethodBodySyncTest >> parseAndTest: docText [ + + | doc bTester | + doc := Microdown parse: docText. + bTester := MicBookTesterVisitor new. + bTester start: doc. + ^ bTester +] + +{ #category : 'data' } +MicMethodBodySyncTest >> simpleCode [ + "The comment of simpleCode" + + ^ 100 slowFactorial +] + +{ #category : 'tests - not reported' } +MicMethodBodySyncTest >> testCodeblockIsNotInSyncButShouldNotBeReported [ + + | bTester headerString methodDef | + headerString := '```sync=false&origin=MicMethodBodySyncTest>>#simpleCode'. + methodDef := +'simpleCode + "This is not the definition of simpleCode" + + ^ 100 slowFactorial + 100'. + + bTester := self parseAndTest: headerString, Character cr asString , methodDef, Character cr asString, '```'. + self assert: bTester results isEmpty. + +] + +{ #category : 'tests' } +MicMethodBodySyncTest >> testCodeblockIsNotInSyncCheckingResultAttributes [ + + | bTester brokenCodeResult headerString methodDef | + headerString := '```sync=true&origin=MicMethodBodySyncTest>>#simpleCode'. + methodDef := +'simpleCode + "This is not the definition of simpleCode" + + ^ 100 slowFactorial + 100'. + + bTester := self parseAndTest: headerString, Character cr asString , methodDef, Character cr asString, '```'. + brokenCodeResult := bTester results first. + + self + assert: brokenCodeResult originString + equals: 'MicMethodBodySyncTest>>#simpleCode'. + self + assert: brokenCodeResult bookContents + equals: methodDef. + self + assert: brokenCodeResult imageContents + equals: (self class >> #simpleCode) sourceCode +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testMetaclassInOriginSpecification [ + + | docText bTester | + docText := '```sync=true&origin=MicMethodBodySyncTest class>>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + self assert: bTester results isEmpty +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testMetaclassWithSpacesInOriginSpecification [ + + | docText bTester | + docText := '```sync=true&origin=MicMethodBodySyncTest class >>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + self assert: bTester results isEmpty +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testMetaclassWithStartingSpacesInOriginSpecification [ + + | docText bTester | + docText := '```sync=true&origin= MicMethodBodySyncTest class >>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + self assert: bTester results isEmpty +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testReportCodeBlockInSyncWithMetaclassMethod [ + + | docText bTester | + docText := '```sync=true&origin=MicMethodBodySyncTest class>>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + self assert: bTester results isEmpty +] + +{ #category : 'tests' } +MicMethodBodySyncTest >> testReportCodeblockIsInSync [ + + | docText bTester | + docText := '```sync=true&origin=MicMethodBodySyncTest>>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + self assert: bTester results isEmpty + +] + +{ #category : 'tests - broken sync' } +MicMethodBodySyncTest >> testReportCodeblockWithBrokenSync [ + "even if the code is in sync reports that the sync declaration is bogus" + + | bTester brokenHeaderString methodDef report | + brokenHeaderString := '```sync=FooBar&origin=MicMethodBodySyncTest>>#simpleCode'. + methodDef := +'simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial'. + + bTester := self parseAndTest: brokenHeaderString, Character cr asString, methodDef, Character cr asString, '```'. + + self assert: (self class >> #simpleCode) sourceCode equals: methodDef. + report := bTester results first. + self assert: report class equals: MicBrokenSyncDefinition. + self assert: report syncValue equals: 'FooBar' + +] + +{ #category : 'tests - broken sync' } +MicMethodBodySyncTest >> testReportCodeblockWithBrokenSyncDeclaration [ + + | bTester headerString methodDef | + headerString := '```sync=fooBar&origin=MicMethodBodySyncTest>>#simpleCode'. + methodDef := +'simpleCode + "This is not the definition of simpleCode" + + ^ 100 slowFactorial + 100'. + + bTester := self parseAndTest: headerString, Character cr asString , methodDef, Character cr asString, '```'. + self deny: bTester results isEmpty. + +] + +{ #category : 'tests' } +MicMethodBodySyncTest >> testReportsCodeblockIsNotInSync [ + + | bTester brokenCodeResult headerString methodDef | + headerString := '```sync=true&origin=MicMethodBodySyncTest>>#simpleCode'. + methodDef := +'simpleCode + "This is not the definition of simpleCode" + + ^ 100 slowFactorial + 100'. + + bTester := self parseAndTest: headerString, Character cr asString , methodDef, Character cr asString, '```'. + brokenCodeResult := bTester results first. + + self + assert: brokenCodeResult originString + equals: 'MicMethodBodySyncTest>>#simpleCode'. +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testUnexistingClassInOriginSpecification [ + + | docText bTester result | + docText := '```sync=true&origin=MicMethodBodySyncTestDOESNNO>>#simpleCode +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + result := bTester results first. + self assert: result class equals: MicBrokenSyncOriginDefinition + +] + +{ #category : 'tests - broken input' } +MicMethodBodySyncTest >> testUnexistingMethodInOriginSpecification [ + + | docText bTester result | + docText := '```sync=true&origin=MicMethodBodySyncTest>>#DoesNotExist +simpleCode + "The comment of simpleCode" + + ^ 100 slowFactorial +``` '. + + bTester := self parseAndTest: docText. + result := bTester results first. + self assert: result class equals: MicBrokenSyncOriginDefinition + +] diff --git a/src/Microdown-BookTester/Association.extension.st b/src/Microdown-BookTester/Association.extension.st new file mode 100644 index 00000000..c7cf365c --- /dev/null +++ b/src/Microdown-BookTester/Association.extension.st @@ -0,0 +1,10 @@ +Extension { #name : 'Association' } + +{ #category : '*Microdown-BookTester' } +Association >> isMicPaired [ + " + 7+3 >>> 10 + " + + ^ self key = self value +] diff --git a/src/Microdown-BookTester/MicBookTesterNullStrategy.class.st b/src/Microdown-BookTester/MicBookTesterNullStrategy.class.st new file mode 100644 index 00000000..1d3fa194 --- /dev/null +++ b/src/Microdown-BookTester/MicBookTesterNullStrategy.class.st @@ -0,0 +1,23 @@ +" +When there is no tag other than none or language, does not verify the code block. +" +Class { + #name : 'MicBookTesterNullStrategy', + #superclass : 'MicBookTesterStrategy', + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'hook' } +MicBookTesterNullStrategy class >> handleKey [ + + ^ #language + +] + +{ #category : 'main API' } +MicBookTesterNullStrategy >> verify: aMicCodeBlock [ + + + +] diff --git a/src/Microdown-BookTester/MicBookTesterStrategy.class.st b/src/Microdown-BookTester/MicBookTesterStrategy.class.st new file mode 100644 index 00000000..1b5acc2b --- /dev/null +++ b/src/Microdown-BookTester/MicBookTesterStrategy.class.st @@ -0,0 +1,21 @@ +Class { + #name : 'MicBookTesterStrategy', + #superclass : 'Object', + #instVars : [ + 'results' + ], + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'hook' } +MicBookTesterStrategy class >> handleKey [ + + ^ self subclassResponsibility + +] + +{ #category : 'accessing' } +MicBookTesterStrategy >> results: aCollection [ + results := aCollection +] diff --git a/src/Microdown-BookTester/MicBookTesterVisitor.class.st b/src/Microdown-BookTester/MicBookTesterVisitor.class.st index 0c778955..5a947510 100644 --- a/src/Microdown-BookTester/MicBookTesterVisitor.class.st +++ b/src/Microdown-BookTester/MicBookTesterVisitor.class.st @@ -5,7 +5,8 @@ Class { #name : 'MicBookTesterVisitor', #superclass : 'MicrodownVisitor', #instVars : [ - 'allTestResults' + 'strategies', + 'results' ], #category : 'Microdown-BookTester', #package : 'Microdown-BookTester' @@ -17,47 +18,45 @@ MicBookTesterVisitor class >> isAbstract [ ^ false ] -{ #category : 'visiting' } -MicBookTesterVisitor >> checkAssociation: aCodeBlockObject [ - "Asserts the association created in any codeBlock " - - | association | - association := self class compiler evaluate: aCodeBlockObject text. - ^ association key = association value -] - -{ #category : 'visiting' } -MicBookTesterVisitor >> executeAndReportIn: aResult [ - - | isCorrect | - isCorrect := false. - [ isCorrect := self checkAssociation: aResult ] - on: Exception - do: [ :exception | aResult message: exception asString ]. - aResult status: isCorrect. - isCorrect ifTrue: [ aResult message: 'Test passed' ] +{ #category : 'initialization' } +MicBookTesterVisitor >> collectStrategies [ + + MicBookTesterStrategy allSubclasses do: [ :each | + | strat | + strat := each new. + strat results: results. + strategies at: each handleKey put: strat ] ] { #category : 'visiting' } MicBookTesterVisitor >> failedTests [ - ^ allTestResults select: [ :each | each isFailed ] + ^ results select: [ :each | each isFailed ] ] { #category : 'initialization' } MicBookTesterVisitor >> initialize [ + super initialize. - allTestResults := OrderedCollection new. + results := OrderedCollection new. + strategies := Dictionary new. + self collectStrategies ] { #category : 'visiting' } MicBookTesterVisitor >> isOk [ | checkingVariable | checkingVariable := true. - allTestResults do: [ :each | checkingVariable := checkingVariable and: [ each isPaired ] ]. + results do: [ :each | checkingVariable := checkingVariable and: [ each isMicPaired ] ]. ^ checkingVariable ] +{ #category : 'visiting' } +MicBookTesterVisitor >> results [ + + ^ results +] + { #category : 'accessing' } MicBookTesterVisitor >> start: anObject [ anObject accept: self @@ -67,28 +66,16 @@ MicBookTesterVisitor >> start: anObject [ { #category : 'testing' } MicBookTesterVisitor >> validTests [ - ^ allTestResults select: [ :each | each isValid ] + ^ results select: [ :each | each isValid ] ] { #category : 'visiting' } MicBookTesterVisitor >> visitCode: aCodeBlock [ - "Creates an instance of PRBookTestResult with parameters depending of the type of the codeblock. - Here it is for an example and therefore uses the specialized exmple methods - textForTestcase - executeAndReportExample: - Note that there is no fragmentedText here, no need for it because the codeblock text contains - the whole text and is the equivalent of Playground execution" + "Creates an instance of PRBookTestResult with parameters depending of the type of the codeblock." + + (strategies keys includes: aCodeBlock mainExtensionTag) + ifFalse: [ ^ self ]. - aCodeBlock isExample ifTrue: [ - | result | - result := MicBookTestResult new. - result text: aCodeBlock code. - [ result source: aCodeBlock fromFile ] - on: Error - do: [ result source: nil ]. - allTestResults add: result. - self executeAndReportIn: result. - aCodeBlock isExpectedFailure ifTrue: [ - result status: result status not . - result isExpectedFailure: true ] ] + (strategies at: aCodeBlock mainExtensionTag) + verify: aCodeBlock ] diff --git a/src/Microdown-BookTester/MicBrokenSyncDefinition.class.st b/src/Microdown-BookTester/MicBrokenSyncDefinition.class.st new file mode 100644 index 00000000..6ef526d5 --- /dev/null +++ b/src/Microdown-BookTester/MicBrokenSyncDefinition.class.st @@ -0,0 +1,40 @@ +" +I report that a sync definition is not correct. + +The sync feature is checking problems when true, ignored when false and reported as a broken sync definition else. + +``` +sync=what ever +``` +" +Class { + #name : 'MicBrokenSyncDefinition', + #superclass : 'MicResult', + #instVars : [ + 'syncValue', + 'codeBlock' + ], + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'building' } +MicBrokenSyncDefinition >> codeBlock: aMicCodeBlock [ + codeBlock := aMicCodeBlock +] + +{ #category : 'accessing' } +MicBrokenSyncDefinition >> explanation [ + + ^ 'The sync definition has the following broken value: ', syncValue, ' .' +] + +{ #category : 'accessing' } +MicBrokenSyncDefinition >> syncValue [ + ^ syncValue +] + +{ #category : 'accessing' } +MicBrokenSyncDefinition >> syncValue: aString [ + syncValue := aString +] diff --git a/src/Microdown-BookTester/MicBrokenSyncOriginDefinition.class.st b/src/Microdown-BookTester/MicBrokenSyncOriginDefinition.class.st new file mode 100644 index 00000000..6bce4789 --- /dev/null +++ b/src/Microdown-BookTester/MicBrokenSyncOriginDefinition.class.st @@ -0,0 +1,15 @@ +Class { + #name : 'MicBrokenSyncOriginDefinition', + #superclass : 'MicResult', + #instVars : [ + 'origin', + 'codeBlock' + ], + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'accessing' } +MicBrokenSyncOriginDefinition >> originString: aString [ + origin := aString +] diff --git a/src/Microdown-BookTester/MicDesynchronizedCodeResult.class.st b/src/Microdown-BookTester/MicDesynchronizedCodeResult.class.st new file mode 100644 index 00000000..dbbea4b8 --- /dev/null +++ b/src/Microdown-BookTester/MicDesynchronizedCodeResult.class.st @@ -0,0 +1,61 @@ +" +I report that a code block contains code that is not the same than the one in the current image. +" +Class { + #name : 'MicDesynchronizedCodeResult', + #superclass : 'MicResult', + #instVars : [ + 'originString', + 'bookContents', + 'imageContents', + 'pharoVersion' + ], + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> bookContent [ + ^ bookContents +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> bookContents [ + ^ bookContents +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> bookContents: aString [ + bookContents := aString +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> explanation [ + + ^ 'The code block body of ', originString , ' is not in sync with the version in Pharo ', pharoVersion, 'in file: ', source fromFile fullName +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> imageContent: aString [ + imageContents := aString +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> imageContents [ + ^ imageContents +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> originString [ + ^ originString +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> originString: aString [ + originString := aString +] + +{ #category : 'accessing' } +MicDesynchronizedCodeResult >> pharoVersion: aString [ + pharoVersion := aString +] diff --git a/src/Microdown-BookTester/MicExampleTesterStrategy.class.st b/src/Microdown-BookTester/MicExampleTesterStrategy.class.st new file mode 100644 index 00000000..0c7357b6 --- /dev/null +++ b/src/Microdown-BookTester/MicExampleTesterStrategy.class.st @@ -0,0 +1,51 @@ +Class { + #name : 'MicExampleTesterStrategy', + #superclass : 'MicBookTesterStrategy', + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'hook' } +MicExampleTesterStrategy class >> handleKey [ + + ^ #example + +] + +{ #category : 'main API' } +MicExampleTesterStrategy >> checkAssociation: aCodeBlockObject [ + "Asserts the association created in any codeBlock " + + | association | + association := self class compiler evaluate: aCodeBlockObject text. + ^ association key = association value +] + +{ #category : 'main API' } +MicExampleTesterStrategy >> executeAndReportIn: aResult [ + + | isCorrect | + isCorrect := false. + [ isCorrect := self checkAssociation: aResult ] + on: Exception + do: [ :exception | aResult message: exception asString ]. + aResult status: isCorrect. + isCorrect ifTrue: [ aResult message: 'Test passed' ] +] + +{ #category : 'main API' } +MicExampleTesterStrategy >> verify: aCodeBlock [ + + | result | + result := MicBookTestResult new. + result text: aCodeBlock code. + [ result source: aCodeBlock fromFile ] + on: Error + do: [ result source: nil ]. + results add: result. + self executeAndReportIn: result. + aCodeBlock isExpectedFailure + ifTrue: [ + result status: result status not. + result isExpectedFailure: true ] +] diff --git a/src/Microdown-BookTester/MicSyncTesterStrategy.class.st b/src/Microdown-BookTester/MicSyncTesterStrategy.class.st new file mode 100644 index 00000000..c91b6853 --- /dev/null +++ b/src/Microdown-BookTester/MicSyncTesterStrategy.class.st @@ -0,0 +1,82 @@ +Class { + #name : 'MicSyncTesterStrategy', + #superclass : 'MicBookTesterStrategy', + #category : 'Microdown-BookTester', + #package : 'Microdown-BookTester' +} + +{ #category : 'hook' } +MicSyncTesterStrategy class >> handleKey [ + + ^ #sync + +] + +{ #category : 'as yet unclassified' } +MicSyncTesterStrategy >> handleBrokenOrigin: aString of: aMicCodeBlock [ + + results add: ( MicBrokenSyncOriginDefinition new + originString: aString; + source: aMicCodeBlock; + yourself ) +] + +{ #category : 'as yet unclassified' } +MicSyncTesterStrategy >> handleBrokenSyncValue: aString of: aMicCodeBlock [ + + results add: (MicBrokenSyncDefinition new + syncValue: aString; + source: aMicCodeBlock; + yourself) +] + +{ #category : 'main API' } +MicSyncTesterStrategy >> verify: aCodeBlock [ + + | originString syncValue splits method className isMeta | + + "we check to handle sync=false and sync=whatever" + syncValue := aCodeBlock arguments at: self class handleKey. + + "we do not check if declared to not check" + syncValue = 'false' + ifTrue: [ ^ self ]. + + "for all the values other than 'true' we report a problem because + the user took the time to define it but he made a typo." + + syncValue = 'true' + ifFalse: [ self handleBrokenSyncValue: syncValue of: aCodeBlock ] + ifTrue: [ + + originString := aCodeBlock arguments at: #origin. + + splits := originString splitOn: '>>'. + ( splits size = 2 ) + ifFalse: [ self handleBrokenOrigin: originString of: aCodeBlock ] + ifTrue: [ + | class | + + className := splits first splitOn: Character space. + isMeta := false. + className size = 2 + ifTrue: [ isMeta := className second trimBoth. + isMeta := isMeta = 'class' ]. + class := Smalltalk globals at: className first asSymbol ifAbsent: [ ^ self handleBrokenOrigin: originString of: aCodeBlock ]. + isMeta + ifTrue: [ class := class class ]. + + class + compiledMethodAt: splits second allButFirst asSymbol + ifPresent: [ :m | method := m ] + ifAbsent: [ ^ self handleBrokenOrigin: originString of: aCodeBlock ]. + + aCodeBlock body = method sourceCode ifFalse: [ + results add: (MicDesynchronizedCodeResult new + originString: originString; + source: aCodeBlock; + bookContents: aCodeBlock body; + imageContent: method sourceCode; + pharoVersion: Smalltalk image lastUpdateString) ] ] + ] +] diff --git a/src/Microdown-Evaluator/MicCodeBlock.extension.st b/src/Microdown-Evaluator/MicCodeBlock.extension.st index 4e2bab9a..f17f7934 100644 --- a/src/Microdown-Evaluator/MicCodeBlock.extension.st +++ b/src/Microdown-Evaluator/MicCodeBlock.extension.st @@ -57,3 +57,9 @@ MicCodeBlock >> isExpectedFailure [ arguments at: #expectedFailure ifAbsent: [ ^ false ]. ^ (arguments at: #expectedFailure) = 'true' ] + +{ #category : '*Microdown-Evaluator' } +MicCodeBlock >> isSync [ + arguments at: #sync ifAbsent: [ ^ false ]. + ^ (arguments at: #sync) = 'true' +] diff --git a/src/Microdown-Tests/MicCodeBlockTest.class.st b/src/Microdown-Tests/MicCodeBlockTest.class.st index b3beb849..f29751b9 100644 --- a/src/Microdown-Tests/MicCodeBlockTest.class.st +++ b/src/Microdown-Tests/MicCodeBlockTest.class.st @@ -266,7 +266,7 @@ Color >> asMorph { #category : 'tests - arguments' } MicCodeBlockTest >> testHasLabelFalse [ " - ```language=pharo&caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor + ```language=pharo&caption=Getting a morph for an instance of ==Color==&anchor=scr:asMorphInColor Color >> asMorph ^ Morph new color: self ``` @@ -285,7 +285,7 @@ Color >> asMorph { #category : 'tests - arguments' } MicCodeBlockTest >> testHasLabelTrue [ " - ```language=pharo&caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor + ```language=pharo&caption=Getting a morph for an instance of ==Color==&anchor=scr:asMorphInColor Color >> asMorph ^ Morph new color: self ``` @@ -294,7 +294,7 @@ Color >> asMorph | source code | source := CodeblockMarkup , - 'language=pharo&caption=`Color` is a class.&label=scr:asMorphInColor' + 'language=pharo&caption=`Color` is a class.&anchor=scr:asMorphInColor' , String cr , 'Color >> asMorph ^ Morph new color: self' , String cr , CodeblockMarkup. @@ -331,13 +331,111 @@ Color >> asMorph ``` " | source code | - source := CodeblockMarkup, 'language=pharo&caption=`Color` is a class.&label=scr:asMorphInColor', String cr, 'Color >> asMorph + source := CodeblockMarkup, 'language=pharo&caption=`Color` is a class.&anchor=scr:asMorphInColor', String cr, 'Color >> asMorph ^ Morph new color: self', String cr, CodeblockMarkup. code := (self parser parse: source) children first. self assert: code language equals: 'pharo' ] +{ #category : 'tests - arguments' } +MicCodeBlockTest >> testMainExtensionTag [ + + " + ```language=pharo&caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor +Color >> asMorph + ^ Morph new color: self + ``` + " + | source code | + source := CodeblockMarkup, 'caption=`Color` is a class.&anchor=scr:asMorphInColor', String cr, 'Color >> asMorph + ^ Morph new color: self', String cr, CodeblockMarkup. + + code := (self parser parse: source) children first. + self assert: code mainExtensionTag equals: #caption +] + +{ #category : 'tests - arguments' } +MicCodeBlockTest >> testMainExtensionTagForSync [ + + " + ```language=pharo&caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor +Color >> asMorph + ^ Morph new color: self + ``` + " + | source code | + source := CodeblockMarkup, 'sync=true&origin=Point>>#degrees&anchor=scr:asMorphInColor', String cr, 'degrees + "Answer the angle the receiver makes with origin in degrees. right is 0; down is 90." + | tan theta | + ^ x = 0 + ifTrue: + [ y >= 0 + ifTrue: [ 90.0 ] + ifFalse: [ 270.0 ] ] + ifFalse: + [ tan := y asFloat / x asFloat. + theta := tan arcTan. + x >= 0 + ifTrue: + [ y >= 0 + ifTrue: [ theta radiansToDegrees ] + ifFalse: [ 360.0 + theta radiansToDegrees ] ] + ifFalse: [ 180.0 + theta radiansToDegrees ] ]', String cr, CodeblockMarkup. + + code := (self parser parse: source) children first. + self assert: code mainExtensionTag equals: #sync +] + +{ #category : 'tests - arguments' } +MicCodeBlockTest >> testMainExtensionTagForSyncWithoutLanguage [ + + " + ```caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor +Color >> asMorph + ^ Morph new color: self + ``` + " + | source code | + source := CodeblockMarkup, 'sync=true&origin=Point>>#degrees&anchor=scr:asMorphInColor', String cr, 'degrees + "Answer the angle the receiver makes with origin in degrees. right is 0; down is 90." + | tan theta | + ^ x = 0 + ifTrue: + [ y >= 0 + ifTrue: [ 90.0 ] + ifFalse: [ 270.0 ] ] + ifFalse: + [ tan := y asFloat / x asFloat. + theta := tan arcTan. + x >= 0 + ifTrue: + [ y >= 0 + ifTrue: [ theta radiansToDegrees ] + ifFalse: [ 360.0 + theta radiansToDegrees ] ] + ifFalse: [ 180.0 + theta radiansToDegrees ] ]', String cr, CodeblockMarkup. + + code := (self parser parse: source) children first. + self assert: code mainExtensionTag equals: #sync +] + +{ #category : 'tests - arguments' } +MicCodeBlockTest >> testMainExtensionTagWithoutLanguage [ + + " + ```caption=Getting a morph for an instance of ==Color==&label=scr:asMorphInColor +Color >> asMorph + ^ Morph new color: self + ``` + " + | source code | + source := CodeblockMarkup, 'caption=`Color` is a class.&anchor=scr:asMorphInColor', String cr, 'Color >> asMorph + ^ Morph new color: self', String cr, CodeblockMarkup. + + code := (self parser parse: source) children first. + self assert: code mainExtensionTag equals: #caption +] + { #category : 'tests' } MicCodeBlockTest >> testOpenCanConsumeLine [ | source root textBody argument line code | diff --git a/src/Microdown/MicCodeBlock.class.st b/src/Microdown/MicCodeBlock.class.st index 6a475a0b..24e0b9eb 100644 --- a/src/Microdown/MicCodeBlock.class.st +++ b/src/Microdown/MicCodeBlock.class.st @@ -156,6 +156,20 @@ MicCodeBlock >> label: aString [ self anchor: aString ] +{ #category : 'accessing' } +MicCodeBlock >> mainExtensionTag [ + "return the main tag beside the language" + + "The point by default we always have language -> pharo + so when the user did not add any other bindings, then the mainExtensionTag will be language + so that we do not have to check and use a NullStrategy" + | k | + k := arguments keys. + ^ k size = 1 + ifTrue: [ k first ] + ifFalse: [ k second ] +] + { #category : 'accessing' } MicCodeBlock >> size [ "precondition: receiver hasLabel"