" I model a Markdown document. At some point the idea is to have a full native parser implemented to deal with my syntax, but meanwhile I will be collaborating with external parsers, particularly the ones provided by Pandoc and/or Lunamark. " Class { #name : #Markdown, #superclass : #MarkupFile, #instVars : [ 'metadata', 'body', 'title' ], #category : #'MiniDocs-Core' } { #category : #'instance creation' } Markdown class >> fromFile: aFileReference [ ^ self new fromFile: aFileReference ] { #category : #utilities } Markdown class >> yamlMetadataDelimiter [ ^ '---' ] { #category : #accessing } Markdown >> asMarkdeep [ ^ Markdeep new body: self body; commentYAMLMetadata ] { #category : #accessing } Markdown >> body [ ^ body ] { #category : #accessing } Markdown >> body: aString [ body := aString ] { #category : #operation } Markdown >> commentYAMLMetadata [ | newContents | self detectYAMLMetadata ifFalse: [ ^ self ]. newContents := '' writeStream. newContents nextPutAll: ''; lf; lf. (self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line | newContents nextPutAll: line; lf ]. ^ newContents contents. ] { #category : #utilities } Markdown >> containsYAMLMetadataClosing [ ^ self yamlMetadataClosingLineNumber > 0 ] { #category : #accessing } Markdown >> contents [ | response | response := WriteStream on: ''. response nextPutAll: '---'; cr; nextPutAll: self metadataAsYAML; cr; nextPutAll: '---'; cr; nextPutAll: self body. ^ response contents ] { #category : #accessing } Markdown >> contents: anObject [ body := anObject ] { #category : #accessing } Markdown >> contentsWithoutYAMLMetadata [ | newContents | self detectYAMLMetadata ifFalse: [ ^ self ]. newContents := '' writeStream. (self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line | newContents nextPutAll: line; cr ]. ^ newContents contents. ] { #category : #operation } Markdown >> deleteYAMLMetadata [ | newContents | self detectYAMLMetadata ifFalse: [ ^ self ]. newContents := '' writeStream. (self lines copyFrom: self yamlMetadataClosingLineNumber + 1 to: self lines size) do: [ :line | newContents nextPutAll: line; lf;lf ]. ^ newContents contents. ] { #category : #utilities } Markdown >> detectYAMLMetadata [ | lines | lines := self lines. ^ self startsWithYAMLMetadataDelimiter and: [ lines allButFirst detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ] ifFound: [ ^ true ] ifNone: [ ^ false ] ] ] { #category : #accessing } Markdown >> documentTree [ | parser| self contents ifNil: [^ nil]. parser := PPCommonMarkBlockParser new parse: self contents. ^ parser accept: CMBlockVisitor new ] { #category : #persistence } Markdown >> exportAsFile [ | newFile | newFile := (self file fullName ) asFileReference. ^ self notifyExportAsFileOn: newFile. ] { #category : #persistence } Markdown >> exportAsFileOn: aFileReference [ aFileReference ensureDelete. aFileReference exists ifFalse: [ aFileReference ensureCreateFile ]. aFileReference writeStreamDo: [ :stream | stream nextPutAll: self contents withInternetLineEndings ]. ] { #category : #accessing } Markdown >> exportAsHTML [ ^ Pandoc markdownToHtml: self file ] { #category : #operation } Markdown >> exportMetadataAsJson [ "TBD: Lua scripts should be checked and installed when missing. Maybe a shared location in '.local/share/Grafoscopio/Scripts' should be developed in the near future." | output luaScript | luaScript := FileLocator home / '.local/share/Brea/scripts/meta-to-json.lua'. Smalltalk platformName = 'unix' ifTrue: [ OSSUnixSubprocess new workingDirectory: self file parent fullName; command: 'pandoc'; arguments: { '--lua-filter=', luaScript fullName . self file basename }; redirectStdout; redirectStdin; runAndWaitOnExitDo: [ :process :outString :errString | output := process isSuccess ifTrue: [ outString ] ifFalse: [ errString ] ]]. ^ output correctAccentedCharacters ] { #category : #operation } Markdown >> exportMetadataAsYaml [ | exportedFile | exportedFile := FileLocator temp / 'metadata.yaml'. MarkupFile exportAsFileOn: exportedFile containing: self yamlMetadataStringWithDelimiters. ^ exportedFile ] { #category : #accessing } Markdown >> file [ ^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md.html' ] ] { #category : #accessing } Markdown >> file: aFileReference [ "I store the origen/destination of the Markdown contents." file := aFileReference ] { #category : #'instance creation' } Markdown >> fromFile: aFileReference [ self contents: aFileReference contents. self file: aFileReference. self populateMetadata. self body: self contentsWithoutYAMLMetadata ] { #category : #'instance creation' } Markdown >> fromString: markdownString [ self contents: markdownString. self populateMetadata. self contents: self contentsWithoutYAMLMetadata ] { #category : #accessing } Markdown >> gtTextFor: aView [ ^ aView textEditor title: 'Text'; text: [ self contents ] ] { #category : #accessing } Markdown >> headerAsTitle [ | headerNode | headerNode := self documentTree children detect: [ :node | node className = 'PPCMHeader' and: [ node level = 1 ] ] ifNone: [ ^ nil ]. ^ headerNode text ] { #category : #utilities } Markdown >> lines [ self file ifNotNil: [^ self file contents lines ]. ^ self contents lines. ] { #category : #accessing } Markdown >> metadata [ ^ metadata ifNil: [ metadata := Dictionary new]. ] { #category : #accessing } Markdown >> metadata: rawMeta [ metadata := rawMeta ] { #category : #accessing } Markdown >> metadataAsYAML [ self metadata isEmptyOrNil ifTrue: [ ^ '' ]. ^ YQ jsonToYaml: self metadata ] { #category : #persistence } Markdown >> notifyExportAsFileOn: aFileReference [ self exportAsFileOn: aFileReference. self inform: 'Exported as: ', String cr, aFileReference fullName. ^ aFileReference ] { #category : #accessing } Markdown >> options [ ^ self metadata at: 'options' ifAbsentPut: [ self defaultOptions] ] { #category : #accessing } Markdown >> populateMetadata [ self metadata: (YAML2JSON fromString: self yamlMetadataString) ] { #category : #accessing } Markdown >> printOn: aStream [ super printOn: aStream. aStream nextPutAll: '( ', self title , ' )' ] { #category : #utilities } Markdown >> startsWithYAMLMetadataDelimiter [ self lines ifEmpty: [^false]. ^ self lines first beginsWith: self class yamlMetadataDelimiter ] { #category : #accessing } Markdown >> title [ ^ self metadata at: 'title' ifAbsentPut: [ self headerAsTitle] ] { #category : #utilities } Markdown >> yamlMetadataClosingLineNumber [ "I return the line where the closing of the YAML metadata occurs or 0 if no closing is found." self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ]. self lines allButFirst doWithIndex: [ :currentLine :i | (currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ ^ i + 1 ]] ] { #category : #operation } Markdown >> yamlMetadataString [ | output yamlLines | self detectYAMLMetadata ifFalse: [ ^nil ]. self lines ifEmpty: [ ^nil ]. yamlLines := self lines copyFrom: 2 to: self yamlMetadataClosingLineNumber - 1. output := '' writeStream. yamlLines do: [ :line | output nextPutAll: line; nextPut: Character lf. ]. ^ output contents ] { #category : #utilities } Markdown >> yamlMetadataStringWithDelimiters [ | output | self yamlMetadataString ifNil: [ ^ nil ]. output := String new writeStream. output nextPutAll: self class yamlMetadataDelimiter; cr. output nextPutAll: self yamlMetadataString. output nextPutAll: self class yamlMetadataDelimiter; cr. ^ output contents. ]