" 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' ], #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; markdownFile: self file; 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 [ ^ body ] { #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; crlf ]. ^ 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| 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 ]. ] { #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 / (NanoID generate asLowercase, '.md') ] ] { #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. ] { #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 : #utilities } Markdown >> lines [ ^ self contents lines. ] { #category : #accessing } Markdown >> metadata [ ^ metadata ifNil: [ metadata := Dictionary new]. ] { #category : #accessing } Markdown >> metadata: rawMeta [ metadata := rawMeta ] { #category : #persistence } Markdown >> notifyExportAsFileOn: aFileReference [ self exportAsFileOn: aFileReference. self inform: 'Exported as: ', String cr, aFileReference fullName. ^ aFileReference ] { #category : #accessing } Markdown >> populateMetadata [ | rawMeta | rawMeta := MiniDocs yamlToJson: self yamlMetadataString. rawMeta associationsDo: [ :assoc | assoc value = 'false' ifTrue: [ assoc value: false ]. assoc value = 'true' ifTrue: [ assoc value: true ] ]. self metadata: rawMeta ] { #category : #accessing } Markdown >> printOn: aStream [ super printOn: aStream. aStream nextPutAll: '( ', (self metadata at: 'title' ifAbsent: ['untitled']), ' )' ] { #category : #utilities } Markdown >> startsWithYAMLMetadataDelimiter [ ^ self lines first beginsWith: self class yamlMetadataDelimiter ] { #category : #accessing } Markdown >> yamlMetadata [ ^ MiniDocs yamlToJson: self yamlMetadataString ] { #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 ]. 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. ]