From 751a041c922bfb18fa9d70a176731155c76aac1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20Morales=20Durand?= Date: Sat, 26 Feb 2022 04:15:07 +0100 Subject: [PATCH 001/146] Add two main entry points for HTML generation from Microdown input: - asHTMLDocument: - asHTMLString: Add MicHTMLDocBuilder for heling in the build of a valid HTML document. Add tests --- .../MicHTMLWriterTest.class.st | 53 +++++-- .../MicHTMLDocBuilder.class.st | 132 ++++++++++++++++++ .../MicHTMLWriter.class.st | 65 +++++++-- src/Microdown/Microdown.class.st | 18 +++ 4 files changed, 247 insertions(+), 21 deletions(-) create mode 100644 src/Microdown-HTMLExporter/MicHTMLDocBuilder.class.st diff --git a/src/Microdown-HTMLExporter-Tests/MicHTMLWriterTest.class.st b/src/Microdown-HTMLExporter-Tests/MicHTMLWriterTest.class.st index e685dda3..1cc0f0bb 100644 --- a/src/Microdown-HTMLExporter-Tests/MicHTMLWriterTest.class.st +++ b/src/Microdown-HTMLExporter-Tests/MicHTMLWriterTest.class.st @@ -5,8 +5,8 @@ Class { #name : #MicHTMLWriterTest, #superclass : #TestCase, #instVars : [ - 'converter', - 'filesystem' + 'filesystem', + 'micHTMLWriter' ], #category : #'Microdown-HTMLExporter-Tests' } @@ -39,7 +39,7 @@ Pharo is **cool** { #category : #running } MicHTMLWriterTest >> setUp [ super setUp. - converter := MicHTMLWriter new. + micHTMLWriter := MicHTMLWriter new. filesystem := FileSystem memory. self generateFilesystemExample. ] @@ -47,21 +47,21 @@ MicHTMLWriterTest >> setUp [ { #category : #test } MicHTMLWriterTest >> testCanvasClass [ - self assert: converter canvasClass equals: MicHTMLCanvas + self assert: micHTMLWriter canvasClass equals: MicHTMLCanvas ] { #category : #test } MicHTMLWriterTest >> testContents [ - self assert: converter contents equals: String empty. - converter visitItalic: (MicItalicFormatBlock new textElement: (MicTextBlock new substring: 'test')). - self assert: converter contents equals: 'test'. + self assert: micHTMLWriter contents equals: String empty. + micHTMLWriter visitItalic: (MicItalicFormatBlock new textElement: (MicTextBlock new substring: 'test')). + self assert: micHTMLWriter contents equals: 'test'. ] { #category : #test } -MicHTMLWriterTest >> testConvertFile [ +MicHTMLWriterTest >> testConvertMicFile [ - converter convertFile: (filesystem / 'anExample1.md') asFileReference. + micHTMLWriter convertMicFile: (filesystem / 'anExample1.md') asFileReference. self assert: (filesystem / 'anExample1.html') asFileReference exists. self @@ -73,10 +73,39 @@ MicHTMLWriterTest >> testConvertFile [ '. ] +{ #category : #test } +MicHTMLWriterTest >> testConvertMicString [ + + | result | + + result := micHTMLWriter convertMicString: MicMicrodownSnippetFactory buildDocument. + + self assert: (result isKindOf: String). + self assert: result notEmpty. + +] + +{ #category : #test } +MicHTMLWriterTest >> testConvertMicStringToDoc [ + + | expectedHtmlDoc | + + expectedHtmlDoc := Microdown asHTMLDocument: MicMicrodownSnippetFactory buildDocument. + self + assert: (expectedHtmlDoc isKindOf: String) + description: 'It tests that result is a String object'. + self + deny: expectedHtmlDoc isEmpty + description: 'It test that result is not empty'. + self + assert: (expectedHtmlDoc beginsWith: MicHTMLDocBuilder documentType) + description: 'It test that result contain the expected document type tag' +] + { #category : #test } MicHTMLWriterTest >> testCreateAnchorWith [ - self assert: converter contents equals: String empty. - converter createAnchorWith: 'id1'. - self assert: converter contents equals: ''. + self assert: micHTMLWriter contents equals: String empty. + micHTMLWriter createAnchorWith: 'id1'. + self assert: micHTMLWriter contents equals: ''. ] diff --git a/src/Microdown-HTMLExporter/MicHTMLDocBuilder.class.st b/src/Microdown-HTMLExporter/MicHTMLDocBuilder.class.st new file mode 100644 index 00000000..1ed916f3 --- /dev/null +++ b/src/Microdown-HTMLExporter/MicHTMLDocBuilder.class.st @@ -0,0 +1,132 @@ +" +Represents a Text/HTML file with default character set as UTF-8. +Assume it receives as input raw HTML String, without any valid mandatory header tag, and builds a header and body tags around it. + +" +Class { + #name : #MicHTMLDocBuilder, + #superclass : #Object, + #instVars : [ + 'fileName', + 'contents', + 'charSet', + 'stream' + ], + #category : #'Microdown-HTMLExporter' +} + +{ #category : #initialization } +MicHTMLDocBuilder class >> documentType [ + "Answer a specifying the mandatory document specification tag" + + ^ '' +] + +{ #category : #accessing } +MicHTMLDocBuilder >> charSet [ + + ^ charSet +] + +{ #category : #accessing } +MicHTMLDocBuilder >> charSet: anObject [ + + charSet := anObject +] + +{ #category : #accessing } +MicHTMLDocBuilder >> contents [ + + ^ contents +] + +{ #category : #accessing } +MicHTMLDocBuilder >> contents: anObject [ + + contents := anObject +] + +{ #category : #initialization } +MicHTMLDocBuilder >> document [ + "Answer a represented as valid HTML document" + + self writeDocumentHeader. + self writeDocumentBody. + ^ stream contents +] + +{ #category : #initialization } +MicHTMLDocBuilder >> documentType [ + "Answer a specifying the mandatory document specification tag" + + ^ self class documentType +] + +{ #category : #accessing } +MicHTMLDocBuilder >> fileName [ + + ^ fileName +] + +{ #category : #accessing } +MicHTMLDocBuilder >> fileName: anObject [ + + fileName := anObject +] + +{ #category : #initialization } +MicHTMLDocBuilder >> initialize [ + + super initialize. + self setCharSetUTF8. + stream := MicOutputStream new setStream: (WriteStream on: (String new: 1000)). + +] + +{ #category : #initialization } +MicHTMLDocBuilder >> setCharSetUTF8 [ + + self charSet: 'utf-8' +] + +{ #category : #accessing } +MicHTMLDocBuilder >> stream [ + + ^ stream +] + +{ #category : #accessing } +MicHTMLDocBuilder >> stream: anObject [ + + stream := anObject +] + +{ #category : #initialization } +MicHTMLDocBuilder >> writeDocumentBody [ + "Answer a representing a valid HTML body for the receiver" + + stream + << ''; + newLine; + << self contents; + << ''; + newLine; + << '' +] + +{ #category : #initialization } +MicHTMLDocBuilder >> writeDocumentHeader [ + "Answer a representing a valid HTML header for the receiver" + + stream + << self documentType; + newLine; + << ''; + newLine; + << ''; + << ''; + << ''; + newLine +] diff --git a/src/Microdown-HTMLExporter/MicHTMLWriter.class.st b/src/Microdown-HTMLExporter/MicHTMLWriter.class.st index 32a0e2cd..b556d43f 100644 --- a/src/Microdown-HTMLExporter/MicHTMLWriter.class.st +++ b/src/Microdown-HTMLExporter/MicHTMLWriter.class.st @@ -11,6 +11,34 @@ Class { #category : #'Microdown-HTMLExporter' } +{ #category : #converting } +MicHTMLWriter class >> asHTMLDocument: aMicrodownString [ + "Answer a formatted as HTML with UTF-8 encoding from aMicrodownString as input" + + ^ self new convertMicStringToDoc: aMicrodownString +] + +{ #category : #converting } +MicHTMLWriter class >> asHTMLString: aMicrodownString [ + "Answer a formatted as HTML from aMicrodownString as input" + + ^ self new convertMicString: aMicrodownString +] + +{ #category : #examples } +MicHTMLWriter class >> exampleHTMLDocument [ + + + ^ (Microdown asHTMLDocument: MicMicrodownSnippetFactory buildDocument) inspect. +] + +{ #category : #examples } +MicHTMLWriter class >> exampleHTMLString [ + + + ^ (Microdown asHTMLString: MicMicrodownSnippetFactory buildDocument) inspect. +] + { #category : #initialization } MicHTMLWriter >> canvasClass [ @@ -19,21 +47,38 @@ MicHTMLWriter >> canvasClass [ { #category : #accessing } MicHTMLWriter >> contents [ + "Answer a of HTML formatted receiver's canvas" + ^ canvas contents ] { #category : #converting } -MicHTMLWriter >> convertFile: aFile [ +MicHTMLWriter >> convertMicFile: aFilename [ - | doc write fileref | - fileref := aFile asFileReference. - doc := Microdown parse: fileref contents. + | fileRef | - write := MicHTMLWriter new. - write visit: doc. + fileRef := aFilename asFileReference. + self visit: (Microdown parse: fileRef contents). + (fileRef parent / (fileRef basenameWithoutExtension: 'md') , 'html') asFileReference + writeStreamDo: [ : writeStream | writeStream nextPutAll: self contents ] +] + +{ #category : #converting } +MicHTMLWriter >> convertMicString: aMicString [ + "Answer a representing the HTML equivalent of Microdown formatted aMicString " - (fileref parent / (fileref basenameWithoutExtension: 'md'), 'html') asFileReference - writeStreamDo: [ :st | st nextPutAll: write contents ] + self visit: (Microdown parse: aMicString). + ^ self contents +] + +{ #category : #converting } +MicHTMLWriter >> convertMicStringToDoc: aMicString [ + "Answer a representing the HTML equivalent of Microdown formatted aMicString " + + self visit: (Microdown parse: aMicString). + ^ MicHTMLDocBuilder new + contents: self contents; + document ] { #category : #visiting } @@ -260,7 +305,9 @@ MicHTMLWriter >> visitUnorderedList: anUnorderedList [ canvas newLine. canvas tag name: 'ul'; - with: [ canvas newLine. super visitUnorderedList: anUnorderedList ] + with: [ + canvas newLine. + super visitUnorderedList: anUnorderedList ] ] { #category : #visiting } diff --git a/src/Microdown/Microdown.class.st b/src/Microdown/Microdown.class.st index 7633e5c8..c3f6c19d 100644 --- a/src/Microdown/Microdown.class.st +++ b/src/Microdown/Microdown.class.st @@ -13,6 +13,24 @@ Class { #category : #'Microdown-Core' } +{ #category : #facade } +Microdown class >> asHTMLDocument: aStringOrDoc [ + "Facade method to render a microdown document or string to HTML" + + ^ MicHTMLWriter asHTMLDocument: aStringOrDoc. + + +] + +{ #category : #facade } +Microdown class >> asHTMLString: aStringOrDoc [ + "Facade method to render a microdown document or string to HTML" + + ^ MicHTMLWriter asHTMLString: aStringOrDoc. + + +] + { #category : #facade } Microdown class >> asRichText: aStringOrDoc [ "Facade method to render a microdown document or string to Text" From bad07a06536b839f1e61ddf24ae33e5812d47f08 Mon Sep 17 00:00:00 2001 From: kasperosterbye Date: Sat, 12 Mar 2022 11:44:22 +0100 Subject: [PATCH 002/146] interim commit --- .../MicFileResourceTest.class.st | 10 +-- .../MicZincPathResolverTest.class.st | 77 ++++++++++++++++++ .../MicAbsoluteResourceReference.class.st | 5 ++ .../MicFileResourceReference.class.st | 78 +++++-------------- src/Microdown/MicInputfileBlock.class.st | 11 +++ .../MicRelativeResourceReference.class.st | 5 ++ src/Microdown/MicResourceReference.class.st | 5 ++ src/Microdown/MicZincPathResolver.class.st | 33 ++++++-- 8 files changed, 151 insertions(+), 73 deletions(-) create mode 100644 src/Microdown-Tests/MicZincPathResolverTest.class.st diff --git a/src/Microdown-Tests/MicFileResourceTest.class.st b/src/Microdown-Tests/MicFileResourceTest.class.st index 87153c30..4d267ec7 100644 --- a/src/Microdown-Tests/MicFileResourceTest.class.st +++ b/src/Microdown-Tests/MicFileResourceTest.class.st @@ -19,7 +19,7 @@ MicFileResourceTest >> exampleMicFileResource [ | file | file := self exampleReadmeFile. - ^ MicFileResource on: file reference: (filesystem resolvePath: file path) + ^ MicFileResourceReference fromFileRef: file ] { #category : #private } @@ -101,13 +101,9 @@ MicFileResourceTest >> setUp [ super setUp. emptyResolver := MicUrlResolver new. filesystem := FileSystem memory. - readme := self exampleReadmeFile contents. "(ZnEasy get: (MicHTTPResourceTest githubTestData uri + 'readme.md')) contents." - image := self examplePNGFile. "ZnEasy getPng: (MicHTTPResourceTest githubTestData uri + 'toplevel.png')." + readme := self exampleReadmeFile contents. + image := self examplePNGFile. - fullResolver := MicUrlResolver new - currentWorkingDirectory: filesystem workingDirectory; - yourself. - file := filesystem workingDirectory / 'readme.md'. file writeStreamDo: [ :stream | stream nextPutAll: readme]. diff --git a/src/Microdown-Tests/MicZincPathResolverTest.class.st b/src/Microdown-Tests/MicZincPathResolverTest.class.st new file mode 100644 index 00000000..db1eff39 --- /dev/null +++ b/src/Microdown-Tests/MicZincPathResolverTest.class.st @@ -0,0 +1,77 @@ +" +A MicZincPathResolverTest is a test class for testing the behavior of MicZincPathResolver +" +Class { + #name : #MicZincPathResolverTest, + #superclass : #TestCase, + #category : #'Microdown-Tests-Resources' +} + +{ #category : #tests } +MicZincPathResolverTest >> testVisitFileImage [ + | doc imageNode absoluteFile | + doc := Microdown parse: '![alttext](images/pharo.png)'. + imageNode := doc children first children first. + self assert: imageNode reference originalString equals: 'images/pharo.png'. + absoluteFile := (MicResourceReference fromUri: 'file:/my/deep/directory/sourceFile.md') + fileSystem: #Bogus; + yourself. + + MicZincPathResolver new + absoluteReference: absoluteFile; + resolveDocument: doc. + self assert: imageNode reference originalString equals: 'file:///my/deep/directory/images/pharo.png'. + self assert: imageNode reference fileSystem equals: #Bogus +] + +{ #category : #tests } +MicZincPathResolverTest >> testVisitHttpImage [ + | doc imageNode | + doc := Microdown parse: '![alttext](images/pharo.png)'. + imageNode := doc children first children first. + self assert: imageNode reference originalString equals: 'images/pharo.png'. + + MicZincPathResolver new + absoluteReference: 'http://nowhere/my/deep/directory/sourceFile.md'; + resolveDocument: doc. + self assert: imageNode reference originalString equals: 'http://nowhere/my/deep/directory/images/pharo.png'. +] + +{ #category : #tests } +MicZincPathResolverTest >> testVisitHttpInput [ + | doc inputNode | + doc := Microdown parse: '{!inputFile|path=otherFile.md!}'. + inputNode := doc children first children first. + self assert: inputNode reference originalString equals: 'otherFile.md'. + + MicZincPathResolver new + absoluteReference: 'http://nowhere/my/deep/directory/sourceFile.md'; + resolveDocument: doc. + self assert: inputNode reference originalString equals: 'http://nowhere/my/deep/directory/otherFile.md'. +] + +{ #category : #tests } +MicZincPathResolverTest >> testVisitHttpInput_WithReference [ + | doc inputNode | + doc := Microdown parse: '{!inputFile|path=otherFile.md!}'. + inputNode := doc children first children first. + self assert: inputNode reference originalString equals: 'otherFile.md'. + + MicZincPathResolver new + absoluteReference: (MicResourceReference fromUri: 'http://nowhere/my/deep/directory/sourceFile.md'); + resolveDocument: doc. + self assert: inputNode reference originalString equals: 'http://nowhere/my/deep/directory/otherFile.md'. +] + +{ #category : #tests } +MicZincPathResolverTest >> testVisitHttpLink [ + | doc linkNode | + doc := Microdown parse: '[follow me](otherFile.md)'. + linkNode := doc children first children first. + self assert: linkNode reference originalString equals: 'otherFile.md'. + + MicZincPathResolver new + absoluteReference: 'http://nowhere/my/deep/directory/sourceFile.md'; + resolveDocument: doc. + self assert: linkNode reference originalString equals: 'http://nowhere/my/deep/directory/otherFile.md'. +] diff --git a/src/Microdown/MicAbsoluteResourceReference.class.st b/src/Microdown/MicAbsoluteResourceReference.class.st index 110483ce..96baa6f7 100644 --- a/src/Microdown/MicAbsoluteResourceReference.class.st +++ b/src/Microdown/MicAbsoluteResourceReference.class.st @@ -13,6 +13,11 @@ Class { #category : #'Microdown-Core' } +{ #category : #testing } +MicAbsoluteResourceReference >> isFileReference [ + ^ false +] + { #category : #accessing } MicAbsoluteResourceReference >> path [ "Answer a with the receiver's relative URI without the scheme" diff --git a/src/Microdown/MicFileResourceReference.class.st b/src/Microdown/MicFileResourceReference.class.st index 7a033f72..35ffb5af 100644 --- a/src/Microdown/MicFileResourceReference.class.st +++ b/src/Microdown/MicFileResourceReference.class.st @@ -12,8 +12,8 @@ It is possible to add new hosts, a primary use case is to give memory file store Class { #name : #MicFileResourceReference, #superclass : #MicAbsoluteResourceReference, - #classVars : [ - 'Hosts' + #instVars : [ + 'fileSystem' ], #category : #'Microdown-Core' } @@ -21,73 +21,33 @@ Class { { #category : #'instance creation' } MicFileResourceReference class >> fromFileRef: aFileReference [ "return an instance of me which references aFileReference" - | host micRef | - aFileReference fileSystem isDiskFileSystem - ifTrue:[ ^ self new uri: ('file://',aFileReference pathString ) asZnUrl]. - "fileReference is a memory filesystem" - host := self hostOf: aFileReference. - host ifNil: [ - host := #'memory.default'. - self host: host is: aFileReference fileSystem]. - micRef := self new uri: ('file://', host, aFileReference pathString ) asZnUrl . - ^ micRef + ^ self new + uri: ('file://',aFileReference pathString) asZnUrl; + fileSystem: aFileReference fileSystem ] -{ #category : #'host resolution' } -MicFileResourceReference class >> host: hostName is: aFileReference [ - self hosts at: hostName put: aFileReference - -] - -{ #category : #'host resolution' } -MicFileResourceReference class >> hostFileReference: hostName [ - "returns a file reference for the host, or nil if no such host exists" - ^ self hosts at: hostName ifAbsent: [ ^ nil ] - -] - -{ #category : #'host resolution' } -MicFileResourceReference class >> hostOf: aFileReference [ - | host | - host := self hosts associations - detect: [ :assoc | assoc value = aFileReference fileSystem ] - ifNone: [ ^ nil ]. - ^ host key -] - -{ #category : #'host resolution' } -MicFileResourceReference class >> hosts [ - Hosts ifNil: [ - Hosts := Dictionary new. - self populateHosts ]. - ^ Hosts -] - -{ #category : #'class initialization' } -MicFileResourceReference class >> initialize [ -