From c2c817a95b7bf02a789b6daf5f59371fc8852883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phaneDucasse?= Date: Thu, 31 Oct 2024 18:54:10 +0100 Subject: [PATCH] Introduce a new hook to support the contextual redefinition of line massage. This fixes in particular the handling of prefixTabs that are needed in environments such as column but were breaking in codeblock nesting. --- .../MicCodeBlockValidator.class.st | 1 + src/Microdown-Tests/MicCodeBlockTest.class.st | 60 ++++++++++++++++--- src/Microdown/MicAbstractBlock.class.st | 7 +++ src/Microdown/MicEnvironmentBlock.class.st | 9 +++ src/Microdown/MicRootBlock.class.st | 7 +++ .../MicStartStopMarkupBlock.class.st | 2 +- src/Microdown/MicrodownParser.class.st | 2 +- 7 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/Microdown-BookTester/MicCodeBlockValidator.class.st b/src/Microdown-BookTester/MicCodeBlockValidator.class.st index f430cfba..95d98abc 100644 --- a/src/Microdown-BookTester/MicCodeBlockValidator.class.st +++ b/src/Microdown-BookTester/MicCodeBlockValidator.class.st @@ -93,6 +93,7 @@ MicCodeBlockValidator >> validTests [ MicCodeBlockValidator >> visitCode: aCodeBlock [ "Creates an instance of PRBookTestResult with parameters depending of the type of the codeblock." + (strategies keys includes: aCodeBlock mainExtensionTag) ifFalse: [ ^ self ]. diff --git a/src/Microdown-Tests/MicCodeBlockTest.class.st b/src/Microdown-Tests/MicCodeBlockTest.class.st index f29751b9..d873915a 100644 --- a/src/Microdown-Tests/MicCodeBlockTest.class.st +++ b/src/Microdown-Tests/MicCodeBlockTest.class.st @@ -72,11 +72,17 @@ MicCodeBlockTest >> testCodeBlockWithNestedCodeBlock [ "This test shows that we can write a code block inside a code block but that the later is not interpreter." | source root code textBody | - textBody := ' ``` - my fancy code - is really cool - ```'. - source := CodeblockMarkup, String cr, textBody, String cr, CodeblockMarkup. + source := '``` +This is a first level code + ``` + this is one with tabs + ``` + + ``` + this is one with space + ``` +``` '. + root := parser parse: source. self @@ -84,9 +90,7 @@ MicCodeBlockTest >> testCodeBlockWithNestedCodeBlock [ equals: 1. code := root children first. self assert: code children isEmpty. - self - assert: code code - equals: textBody. + self assert: code class equals: MicCodeBlock ] @@ -355,6 +359,30 @@ Color >> asMorph self assert: code mainExtensionTag equals: #caption ] +{ #category : 'tests' } +MicCodeBlockTest >> testMainExtensionTag2 [ + " + ```example=true + 3 + 7 + >> 10 + ``` + " + + | source root code textBody argument | + textBody := '3 + 7 +>>> 10'. + argument := 'example=true'. + source := CodeblockMarkup , argument , String cr , textBody + , String cr , CodeblockMarkup. + root := self parser parse: source. + self assert: root children size equals: 1. + code := root children first. + self assert: code code equals: textBody. + self assert: (code arguments at: #language) equals: 'Pharo'. + self assert: code mainExtensionTag equals: #example. + +] + { #category : 'tests - arguments' } MicCodeBlockTest >> testMainExtensionTagForSync [ @@ -436,6 +464,22 @@ Color >> asMorph self assert: code mainExtensionTag equals: #caption ] +{ #category : 'tests - code' } +MicCodeBlockTest >> testNestedCodeBlockOnlyProducesOneCodeBlock [ + + | doc | + doc := Microdown parse: '``` + ```example=true&expectedFailure=true + 3 + 4 + >>> 12 + ``` +```'. + + self assert: doc children size equals: 1. + self assert: doc children first class equals: MicCodeBlock. + +] + { #category : 'tests' } MicCodeBlockTest >> testOpenCanConsumeLine [ | source root textBody argument line code | diff --git a/src/Microdown/MicAbstractBlock.class.st b/src/Microdown/MicAbstractBlock.class.st index 8305b038..6a16f865 100644 --- a/src/Microdown/MicAbstractBlock.class.st +++ b/src/Microdown/MicAbstractBlock.class.st @@ -117,6 +117,13 @@ MicAbstractBlock >> listItemBlockClass [ ^ MicListItemBlock ] +{ #category : 'hooks' } +MicAbstractBlock >> massageLine: aLine [ + "In some cases an element may want to massage the line such as removing leading tab. By default we delegate to the parent because environment may contain nodes such as code block and this is this nesting that will determine what should be done with the tab. In such a case the environment should remov the tabs." + + ^ self parent massageLine: aLine +] + { #category : 'public' } MicAbstractBlock >> nestedLevel [ "Return the nesting level of main blocks. Basically only list increases this." diff --git a/src/Microdown/MicEnvironmentBlock.class.st b/src/Microdown/MicEnvironmentBlock.class.st index 1e733c7b..c0af9ac9 100644 --- a/src/Microdown/MicEnvironmentBlock.class.st +++ b/src/Microdown/MicEnvironmentBlock.class.st @@ -100,10 +100,13 @@ MicEnvironmentBlock >> addLineAndReturnNextNode: line [ "add line to this node. Notice, the action is allowed to create new nodes in the block tree. Returns the node to handle next line - typically self." + + | withoutPreTabs | isClosed ifTrue: [ ^ self ]. withoutPreTabs := line withoutPreTabs. + "Here the withoutPreTabs is probably not necessary because handlingPrefixTabOfLine: should do its job." (self doesLineStartWithStopMarkup: withoutPreTabs) ifTrue: [ ^ self ]. firstLine @@ -195,3 +198,9 @@ MicEnvironmentBlock >> lineStopMarkup [ ^ EnvironmentClosingBlockMarkup ] + +{ #category : 'handle' } +MicEnvironmentBlock >> massageLine: aLine [ + + ^ aLine withoutPreTabs +] diff --git a/src/Microdown/MicRootBlock.class.st b/src/Microdown/MicRootBlock.class.st index 829299d4..146a8167 100644 --- a/src/Microdown/MicRootBlock.class.st +++ b/src/Microdown/MicRootBlock.class.st @@ -67,6 +67,13 @@ MicRootBlock >> indent [ ^0 ] +{ #category : 'hooks' } +MicRootBlock >> massageLine: aLine [ + "In some cases an element may want to massage the line such as removing leading tab. By default we delegate to the parent because environments may contain nodes such as code block and this is this nesting that will determine what should be done with the tab. In such a case the environment should remov the tabs. Here on the root we do nothing special." + + ^ aLine +] + { #category : 'testing' } MicRootBlock >> metaDataElement [ "if self hasMetaDataElement then metaDataElement returns it. diff --git a/src/Microdown/MicStartStopMarkupBlock.class.st b/src/Microdown/MicStartStopMarkupBlock.class.st index c1ccefb4..d6dcdc6d 100644 --- a/src/Microdown/MicStartStopMarkupBlock.class.st +++ b/src/Microdown/MicStartStopMarkupBlock.class.st @@ -75,7 +75,7 @@ MicStartStopMarkupBlock >> canConsumeLine: line [ { #category : 'testing' } MicStartStopMarkupBlock >> doesLineStartWithStopMarkup: line [ - + "return if the line starts with a stop markup" ^ line beginsWith: self lineStopMarkup ] diff --git a/src/Microdown/MicrodownParser.class.st b/src/Microdown/MicrodownParser.class.st index 0d19f4fa..7c90d25a 100644 --- a/src/Microdown/MicrodownParser.class.st +++ b/src/Microdown/MicrodownParser.class.st @@ -259,7 +259,7 @@ MicrodownParser >> handleLine: line [ -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: (line withoutPreTabs)) + (current canConsumeLine: (current massageLine: line)) ifTrue: [ current := current addLineAndReturnNextNode: line. ^ current ]