Class { #name : 'CMHTMLVisitor', #superclass : 'CMVisitor', #instVars : [ 'links', 'shouldEscape', 'tight', 'shouldHtmlSpecChars' ], #category : 'PetitMarkdown-Visitors' } { #category : 'support' } CMHTMLVisitor >> encodeEntities: text [ ^ PPCommonMarkUtils instance encodeEntities: text ] { #category : 'escape' } CMHTMLVisitor >> escape: string [ | retval regex | self shouldEscape ifFalse: [ ^ string ]. retval := string. retval := retval copyReplaceAll: '\\' with: '\'. "Remove backlashes, \! -> !" regex := '\\[!#$%''()*+,-./:;=?@^_`{|}~]' asRegex. retval := regex copy: retval translatingMatchesUsing: [ :match | match second asString ]. retval := retval copyReplaceAll: '\[' with: '['. retval := retval copyReplaceAll: '\]' with: ']'. retval := retval copyReplaceAll: '\\' with: '\'. ^ retval ] { #category : 'escape' } CMHTMLVisitor >> forbidEscape [ shouldEscape push: false ] { #category : 'support' } CMHTMLVisitor >> forbidHtmlSpecChars [ shouldHtmlSpecChars push: false ] { #category : 'initialization' } CMHTMLVisitor >> initialize [ super initialize. links := IdentityDictionary new. shouldEscape := Stack with: true. shouldHtmlSpecChars := Stack with: true. ] { #category : 'string operations' } CMHTMLVisitor >> removeLeadingEmptyLines: collection [ | retval | collection isEmpty ifTrue: [ ^ collection ]. retval := collection copy. [retval first text = ''] whileTrue: [ retval removeFirst ]. ^ retval ] { #category : 'string operations' } CMHTMLVisitor >> removeTrailingEmptyLines: collection [ | retval | collection isEmpty ifTrue: [ ^ collection ]. retval := collection copy. [retval last text = ''] whileTrue: [ retval removeLast ]. ^ retval ] { #category : 'escape' } CMHTMLVisitor >> restoreEscape [ shouldEscape pop ] { #category : 'string operations' } CMHTMLVisitor >> restoreHtmlSpecChars [ shouldHtmlSpecChars pop ] { #category : 'escape' } CMHTMLVisitor >> shouldEscape [ ^ shouldEscape top ] { #category : 'support' } CMHTMLVisitor >> shouldHtmlSpecChars [ ^ shouldHtmlSpecChars top ] { #category : 'string operations' } CMHTMLVisitor >> trimLeadingEmptyLines: string [ | retval | retval := string. [retval beginsWith: String cr] whileTrue: [ retval := retval copyFrom: 2 to: retval size. ]. ^ retval ] { #category : 'visiting' } CMHTMLVisitor >> visitBlockQuote: node [ | stream content | stream := WriteStream on: ''. stream nextPut: Character cr. stream nextPutAll: '
'. content := node child accept: self. content := content trimRight. stream nextPutAll: content. stream nextPut: Character cr. stream nextPutAll: ''. ^ stream contents. ] { #category : 'visiting' } CMHTMLVisitor >> visitContainer: node [ | parts concat | parts := node children collect: [ :child | child accept: self ]. concat := (parts reject: [ :e | e = '' ]) inject: '' into: [ :string :e | string, e ]. ^ concat ] { #category : 'visiting' } CMHTMLVisitor >> visitDelegate: node [ | parts | parts := node children collect: [ :child | child accept: self ]. ^ String cr join: parts ] { #category : 'visiting' } CMHTMLVisitor >> visitDocument: node [ ^ self trimLeadingEmptyLines: (self visitContainer: node) ] { #category : 'visiting' } CMHTMLVisitor >> visitEmphasize: node [ | retval | retval:= WriteStream on: ''. retval nextPutAll: ''. node children do: [ :child | retval nextPutAll: (child accept: self) ]. retval nextPutAll: ''. ^ retval contents ] { #category : 'visiting' } CMHTMLVisitor >> visitFencedCode: node [ | stream | stream := WriteStream on: ''. stream nextPut: Character cr. stream nextPutAll: '
.
self forbidEscape.
(node children) do: [ :child |
stream nextPutAll: (child accept: self).
stream nextPut: Character cr.
].
self restoreEscape.
stream nextPutAll: '
'.
^ stream contents.
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitHRule: node [
^ String cr, ''.
self forbidEscape.
(self removeTrailingEmptyLines: (self removeLeadingEmptyLines: node children)) do: [ :child |
stream nextPutAll: (child accept: self).
stream nextPut: Character cr.
].
self restoreEscape.
stream nextPutAll: '
'.
^ stream contents.
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitInlinedCode: node [
| code code2 |
code := node code.
code := code copyReplaceAll: (String cr) with: (String space).
code := code.
code2 := code.
[
code := code2.
code2 := code copyReplaceAll: ' ' with: ' '
] doWhileFalse: [ code2 = code ].
^ '', code trim , '
'
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitLine: node [
| stream |
stream := WriteStream on: ''.
node children do: [ :child |
stream nextPutAll: (child accept: self).
].
^ stream contents
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitLink: node [
| stream |
stream := WriteStream on: ''.
stream nextPutAll: ''.
stream nextPutAll: (node label accept: self).
stream nextPutAll: ''.
^ stream contents
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitLinkRef: node [
| stream ref |
stream := WriteStream on: ''.
ref := links at: node label text asLowercase asSymbol.
stream nextPutAll: ''.
stream nextPutAll: (self escape: node label text).
stream nextPutAll: ''.
^ stream contents
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitLinkRefDef: node [
links at: node label text asLowercase asSymbol ifAbsentPut: node.
^ ''
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitLinkRefDefPlaceholder: node [
^ ''
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitList: node [
| stream tag tmp start |
stream := WriteStream on: ''.
tmp := tight.
tight := node isTight.
start := ''.
(node type = #ordered) ifTrue: [
tag := 'ol'.
(node start = 1) ifFalse: [ start := ' start="', node start asString, '"' ]
] ifFalse: [
tag := 'ul'
].
stream nextPut: Character cr.
stream nextPut: $<.
stream nextPutAll: tag.
stream nextPutAll: start.
stream nextPut: $>.
node children do: [ :child |
child isBlankLine ifFalse: [
stream nextPutAll: (child accept: self).
]
].
stream nextPut: Character cr.
stream nextPutAll: ''.
stream nextPutAll: tag.
stream nextPut: $>.
tight := tmp.
^ stream contents
]
{ #category : 'visiting' }
CMHTMLVisitor >> visitListItem: node [
| stream nodeChildren |
stream := WriteStream on: ''.
nodeChildren := node child children reject: [:e | e isBlankLine ].
stream nextPut: Character cr.
stream nextPutAll: ''. node children do: [ :child | stream nextPutAll: (child accept: self) ]. stream nextPutAll: '
'. ^ stream contents ] { #category : 'visiting' } CMHTMLVisitor >> visitPlainLine: node [ ^ self error: 'should not happen' ] { #category : 'visiting' } CMHTMLVisitor >> visitPlainText: node [ ^ self error: 'should not happen' ] { #category : 'visiting' } CMHTMLVisitor >> visitSoftBreak: node [ ^ String cr ] { #category : 'visiting' } CMHTMLVisitor >> visitStrong: node [ | retval | retval:= WriteStream on: ''. retval nextPutAll: ''. node children do: [ :child | retval nextPutAll: (child accept: self) ]. retval nextPutAll: ''. ^ retval contents ] { #category : 'visiting' } CMHTMLVisitor >> visitText: node [ ^ node text ]