diff --git a/src/MiniDocs/LeDatabase.extension.st b/src/MiniDocs/LeDatabase.extension.st index 0c8c063..fa78983 100644 --- a/src/MiniDocs/LeDatabase.extension.st +++ b/src/MiniDocs/LeDatabase.extension.st @@ -1,164 +1,164 @@ -Extension { #name : #LeDatabase } - -{ #category : #'*MiniDocs' } -LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [ - | remoteMetadata divSnippets snippets page | - divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection - collect: [ :xmlElement | xmlElement postCopy ]. - snippets := divSnippets - collect: [ :xmlElement | - (xmlElement attributes at: 'st-class') = 'LeTextSnippet' - ifTrue: [ LeTextSnippet new contentFrom: xmlElement ] - ifFalse: [ (xmlElement attributes at: 'st-class') = 'LePharoSnippet' - ifTrue: [ LePharoSnippet new contentFrom: xmlElement ] ] ]. - remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree. - page := LePage new - title: (remoteMetadata at: 'title'); - basicUid: (UUID fromString36: (remoteMetadata at: 'id')); - createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime); - editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); - latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); - createEmail: (LeEmail new email: (remoteMetadata at: 'creator')); - editEmail: (LeEmail new email: (remoteMetadata at: 'modifier')). - snippets do: [ :snippet | page addSnippet: snippet ]. - page children - do: [ :snippet | - (self hasBlockUID: snippet uid) - ifTrue: [ | existingPage | - existingPage := self pages - detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ]. - self importErrorForLocal: existingPage withRemote: externalDocLocation. - ^ self ] - ifFalse: [ snippet database: self. - self registerSnippet: snippet ] ]. - self addPage: page. - ^ page -] - -{ #category : #'*MiniDocs' } -LeDatabase >> addPageFromMarkdeepUrl: aString [ - | page | - page := self detectLocalPageForRemote: aString. - page - ifNotNil: [ :arg | - self importErrorForLocal: page withRemote: aString. - ^ self ]. - ^ self addPageFromMarkdeep: (self docTreeForLink: aString) withRemote: aString -] - -{ #category : #'*MiniDocs' } -LeDatabase >> detectLocalPageForRemote: markdeepDocUrl [ - | markdeepHelper id remoteMetadata docTree | - markdeepHelper := Markdeep new. - docTree := self docTreeForLink: markdeepDocUrl. - remoteMetadata := markdeepHelper metadataFromXML: docTree. - id := remoteMetadata at: 'id' ifAbsent: [ nil ]. - ^ self pageWithID: id ifAbsent: [ ^ nil ]. -] - -{ #category : #'*MiniDocs' } -LeDatabase >> docTreeForLink: aString [ - ^ (XMLHTMLParser on: aString asUrl retrieveContents) parseDocument -] - -{ #category : #'*MiniDocs' } -LeDatabase >> errorCardFor: error [ - - | keepButton overwriteButton backupButton errorMessageUI localPage errorKey | - errorKey := error keys first. - localPage := self pageWithID: errorKey. - keepButton := BrButton new - aptitude: BrGlamorousButtonWithIconAndLabelAptitude; - label: 'Keep existing local page'; - icon: BrGlamorousVectorIcons cancel; - margin: (BlInsets left: 10); - action: [ :aButton | - aButton phlow spawnObject: localPage. - self errors removeKey: errorKey - ]. - overwriteButton := BrButton new - aptitude: BrGlamorousButtonWithIconAndLabelAptitude; - label: 'Overwrite with remote page'; - icon: BrGlamorousVectorIcons edit; - action: [ :aButton | - self removePage: localPage. - aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (error at: errorKey at: 'remote')). - self errors removeKey: errorKey - ]; - margin: (BlInsets left: 10). - backupButton := BrButton new - aptitude: BrGlamorousButtonWithIconAndLabelAptitude; - label: 'Backup local page'; - icon: BrGlamorousVectorIcons changes; - action: [ :aButton | ]; - margin: (BlInsets left: 10). - - errorMessageUI := BrEditor new - aptitude: BrGlamorousRegularEditorAptitude new ; - text: (error at: errorKey at: 'message'); - vFitContent. - ^ { errorMessageUI. keepButton. overwriteButton. backupButton } -] - -{ #category : #'*MiniDocs' } -LeDatabase >> errors [ - - ^ self optionAt: 'errors' ifAbsentPut: [ Dictionary new ] -] - -{ #category : #'*MiniDocs' } -LeDatabase >> gtViewErrorDetailsOn: aView [ - - ^ aView explicit - title: 'Errors' translated; - priority: 5; - stencil: [ | container | - container := BlElement new - layout: BlFlowLayout new; - constraintsDo: [ :c | - c vertical fitContent. - c horizontal matchParent ]; - padding: (BlInsets all: 10). - container - addChildren: (self errorCardFor: self errors) - ]. -] - -{ #category : #'*MiniDocs' } -LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [ - - | message id error | - id := page uidString. - message := String streamContents: [ :stream | - stream - nextPutAll: 'IMPORTATION ERROR: A page with - '; - nextPut: Character lf; - nextPutAll: ' id: ' , id; - nextPut: Character lf; - nextPutAll: ' title: ' , page contentAsString; - nextPut: Character lf; - nextPut: Character lf; - nextPutAll: 'already exists in this database and includes overlapping contents'; - nextPut: Character lf; - nextPutAll: 'with the page you are trying to import from: - '; - nextPut: Character lf; - nextPutAll: externalDocLocation; - nextPut: Character lf; - nextPut: Character lf; - nextPutAll: - 'Please choose one of the following options to addres the issue: - ' ]. - error := Dictionary new - at: 'remote' put: externalDocLocation; - at: 'message' put: message ; - yourself. - self errors at: id put: error -] - -{ #category : #'*MiniDocs' } -LeDatabase >> options [ - - ^ options -] +Extension { #name : #LeDatabase } + +{ #category : #'*MiniDocs' } +LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [ + | remoteMetadata divSnippets snippets page | + divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection + collect: [ :xmlElement | xmlElement postCopy ]. + snippets := divSnippets + collect: [ :xmlElement | + (xmlElement attributes at: 'st-class') = 'LeTextSnippet' + ifTrue: [ LeTextSnippet new contentFrom: xmlElement ] + ifFalse: [ (xmlElement attributes at: 'st-class') = 'LePharoSnippet' + ifTrue: [ LePharoSnippet new contentFrom: xmlElement ] ] ]. + remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree. + page := LePage new + title: (remoteMetadata at: 'title'); + basicUid: (UUID fromString36: (remoteMetadata at: 'id')); + createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime); + editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); + latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); + createEmail: (LeEmail new email: (remoteMetadata at: 'creator')); + editEmail: (LeEmail new email: (remoteMetadata at: 'modifier')). + snippets do: [ :snippet | page addSnippet: snippet ]. + page children + do: [ :snippet | + (self hasBlockUID: snippet uid) + ifTrue: [ | existingPage | + existingPage := self pages + detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ]. + self importErrorForLocal: existingPage withRemote: externalDocLocation. + ^ self ] + ifFalse: [ snippet database: self. + self registerSnippet: snippet ] ]. + self addPage: page. + ^ page +] + +{ #category : #'*MiniDocs' } +LeDatabase >> addPageFromMarkdeepUrl: aString [ + | page | + page := self detectLocalPageForRemote: aString. + page + ifNotNil: [ :arg | + self importErrorForLocal: page withRemote: aString. + ^ self ]. + ^ self addPageFromMarkdeep: (self docTreeForLink: aString) withRemote: aString +] + +{ #category : #'*MiniDocs' } +LeDatabase >> detectLocalPageForRemote: markdeepDocUrl [ + | markdeepHelper id remoteMetadata docTree | + markdeepHelper := Markdeep new. + docTree := self docTreeForLink: markdeepDocUrl. + remoteMetadata := markdeepHelper metadataFromXML: docTree. + id := remoteMetadata at: 'id' ifAbsent: [ nil ]. + ^ self pageWithID: id ifAbsent: [ ^ nil ]. +] + +{ #category : #'*MiniDocs' } +LeDatabase >> docTreeForLink: aString [ + ^ (XMLHTMLParser on: aString asUrl retrieveContents) parseDocument +] + +{ #category : #'*MiniDocs' } +LeDatabase >> errorCardFor: error [ + + | keepButton overwriteButton backupButton errorMessageUI localPage errorKey | + errorKey := error keys first. + localPage := self pageWithID: errorKey. + keepButton := BrButton new + aptitude: BrGlamorousButtonWithIconAndLabelAptitude; + label: 'Keep existing local page'; + icon: BrGlamorousVectorIcons cancel; + margin: (BlInsets left: 10); + action: [ :aButton | + aButton phlow spawnObject: localPage. + self errors removeKey: errorKey + ]. + overwriteButton := BrButton new + aptitude: BrGlamorousButtonWithIconAndLabelAptitude; + label: 'Overwrite with remote page'; + icon: BrGlamorousVectorIcons edit; + action: [ :aButton | + self removePage: localPage. + aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (error at: errorKey at: 'remote')). + self errors removeKey: errorKey + ]; + margin: (BlInsets left: 10). + backupButton := BrButton new + aptitude: BrGlamorousButtonWithIconAndLabelAptitude; + label: 'Backup local page'; + icon: BrGlamorousVectorIcons changes; + action: [ :aButton | ]; + margin: (BlInsets left: 10). + + errorMessageUI := BrEditor new + aptitude: BrGlamorousRegularEditorAptitude new ; + text: (error at: errorKey at: 'message'); + vFitContent. + ^ { errorMessageUI. keepButton. overwriteButton. backupButton } +] + +{ #category : #'*MiniDocs' } +LeDatabase >> errors [ + + ^ self optionAt: 'errors' ifAbsentPut: [ Dictionary new ] +] + +{ #category : #'*MiniDocs' } +LeDatabase >> gtViewErrorDetailsOn: aView [ + + ^ aView explicit + title: 'Errors' translated; + priority: 5; + stencil: [ | container | + container := BlElement new + layout: BlFlowLayout new; + constraintsDo: [ :c | + c vertical fitContent. + c horizontal matchParent ]; + padding: (BlInsets all: 10). + container + addChildren: (self errorCardFor: self errors) + ]. +] + +{ #category : #'*MiniDocs' } +LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [ + + | message id error | + id := page uidString. + message := String streamContents: [ :stream | + stream + nextPutAll: 'IMPORTATION ERROR: A page with + '; + nextPut: Character lf; + nextPutAll: ' id: ' , id; + nextPut: Character lf; + nextPutAll: ' title: ' , page contentAsString; + nextPut: Character lf; + nextPut: Character lf; + nextPutAll: 'already exists in this database and includes overlapping contents'; + nextPut: Character lf; + nextPutAll: 'with the page you are trying to import from: + '; + nextPut: Character lf; + nextPutAll: externalDocLocation; + nextPut: Character lf; + nextPut: Character lf; + nextPutAll: + 'Please choose one of the following options to addres the issue: + ' ]. + error := Dictionary new + at: 'remote' put: externalDocLocation; + at: 'message' put: message ; + yourself. + self errors at: id put: error +] + +{ #category : #'*MiniDocs' } +LeDatabase >> options [ + + ^ options +] diff --git a/src/MiniDocs/LePage.extension.st b/src/MiniDocs/LePage.extension.st index b273357..b37047a 100644 --- a/src/MiniDocs/LePage.extension.st +++ b/src/MiniDocs/LePage.extension.st @@ -1,218 +1,218 @@ -Extension { #name : #LePage } - -{ #category : #'*MiniDocs' } -LePage >> asHtmlFile [ - - self asMarkdownFile. - self defaultPandocTemplate exists - ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ]. - - OSSUnixSubprocess new - command: 'pandoc' ; - arguments: { - self markdownFileName. '-o'. self htmlFileName . - '--toc' . - '--template=', self defaultPandocTemplate basenameWithoutExtension }; - workingDirectory: self storage fullName; - runAndWaitOnExitDo: [ :process :outString | ^ self storage / self htmlFileName]. -] - -{ #category : #'*MiniDocs' } -LePage >> asMarkdeep [ - | bodyStream markdeep | - bodyStream := '' writeStream. - self preorderTraversal do: [:snippet | - bodyStream nextPutAll: snippet asMarkdeep - ]. - markdeep := Markdeep new - title: self title; - body: bodyStream contents; - navTop: self navTop. - self metadata keysAndValuesDo: [:k :v | - k = 'lang' - ifTrue: [ - markdeep head - add: ''; - yourself. - ] - ifFalse: [ - markdeep head - add: ''; - yourself. - ] - ]. - self metadata at: 'authors' ifPresent: [:author | markdeep metadata at: 'authors' put: author ]. - self metadata at: 'version' ifPresent: [:version | markdeep metadata at: 'version' put: version ]. - ^ markdeep. -] - -{ #category : #'*MiniDocs' } -LePage >> asMarkdeepFile [ - | folder | - folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. - ^ self asMarkdeep exportAsFileOn: folder / self markdeepFileName -] - -{ #category : #'*MiniDocs' } -LePage >> asMarkdown [ - | bodyStream markdown | - bodyStream := '' writeStream. - bodyStream - nextPutAll: '---'; - nextPutAll: String lf. - self metadata keysAndValuesDo: [ :k :v | - bodyStream - nextPutAll: k , ': "' , v, '"'; - nextPutAll: String lf ]. - bodyStream nextPutAll: '---' , String lf , String lf. - self preorderTraversal - do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ]. - markdown := Markdown new contents: bodyStream contents. - ^ markdown -] - -{ #category : #'*MiniDocs' } -LePage >> asMarkdownFile [ - | folder | - folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. - ^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdown contents -] - -{ #category : #'*MiniDocs' } -LePage >> defaultPandocTemplate [ - - ^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html' -] - -{ #category : #'*MiniDocs' } -LePage >> detectParentSnippetWithUid: uidString [ - "Answer a boolean indicating whether the supplied uid is present" - - ^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ] ifNone: [ ^ self ] -] - -{ #category : #'*MiniDocs' } -LePage >> exportedFileName [ - | sanitized | - sanitized := self title asDashedLowercase copyWithoutAll: #($/). - ^ sanitized , '--' , (self uidString copyFrom: 1 to: 5) -] - -{ #category : #'*MiniDocs' } -LePage >> fromMarkdeepUrl: aString [ - | docTree pageMetadata | - docTree := GrafoscopioUtils xmlFromUrl: aString. - pageMetadata := Markdeep new metadataFromXML: docTree. - self - basicUid: (pageMetadata at: 'id'); - title: (pageMetadata at: 'title'); - createTime: (pageMetadata at: 'created') asDateAndTime; - editTime: (pageMetadata at: 'modified') asDateAndTime; - createEmail: (pageMetadata at: 'creator'); - editEmail: (pageMetadata at: 'modifier'); - optionAt: 'metadata' put: pageMetadata. - self populateChildrenFrom: (docTree xpath: '//div') - -] - -{ #category : #'*MiniDocs' } -LePage >> htmlFileName [ - ^ self exportedFileName, '.html' -] - -{ #category : #'*MiniDocs' } -LePage >> latestEditTime: aLeTime [ - "Used for adding a LePage to database from a shared markdeep LePage version." - - latestEditTime := aLeTime -] - -{ #category : #'*MiniDocs' } -LePage >> markdeepFileName [ - - ^ self markdownFileName , '.html' -] - -{ #category : #'*MiniDocs' } -LePage >> markdownFileName [ - ^ self exportedFileName, '.md' -] - -{ #category : #'*MiniDocs' } -LePage >> metadata [ - - ^ self options at: 'metadata' ifAbsentPut: [ self metadataInit] -] - -{ #category : #'*MiniDocs' } -LePage >> metadataInit [ - - ^ OrderedDictionary new - at: 'id' put: self uidString; - at: 'title' put: self contentAsString; - at: 'created' put: self createTime greaseString; - at: 'modified' put: self latestEditTime greaseString; - at: 'creator' put: self createEmail greaseString; - at: 'modifier' put: self editEmail greaseString; - yourself -] - -{ #category : #'*MiniDocs' } -LePage >> navTop [ - | topNavFile | - topNavFile := ((self optionAt: 'storage' ifAbsentPut: [ FileLocator temp ]) / '_navtop.html'). - topNavFile exists - ifFalse: [ ^ '' ] - ifTrue: [ ^ topNavFile contents ] -] - -{ #category : #'*MiniDocs' } -LePage >> options [ - ^ options -] - -{ #category : #'*MiniDocs' } -LePage >> preorderTraversal [ - | output | - output := OrderedCollection new. - self withDeepCollect: [:each | each allChildrenBreadthFirstDo: [:child | output add: child]]. - ^ output. -] - -{ #category : #'*MiniDocs' } -LePage >> removeSnippetsMetadata [ - self preorderTraversal do: [ :snippet | - (snippet options isNotNil and: [ snippet options includesKey: 'metadata' ]) - ifTrue: [ snippet options removeKey: 'metadata' ] ] -] - -{ #category : #'*MiniDocs' } -LePage >> sharedVariablesBindings [ - | codeSnippets shared | - codeSnippets := self preorderTraversal select: [:snippet | - snippet class = LePharoSnippet and: [ snippet code includesSubstring: ':='] - ]. - - codeSnippets first in: [:snippet | | context | - context := snippet coder evaluationContext. - snippet coder doItInContext: context. - shared := context bindingStrategy bindings detect: [:each | - each isKindOf: GtSharedVariablesBindings - ] - ]. - - codeSnippets asArray allButFirstDo: [:snippet| | context| - context := snippet coder evaluationContext. - context addBindings: shared. - snippet coder doItInContext: context - ]. - - ^ shared asDictionary -] - -{ #category : #'*MiniDocs' } -LePage >> storage [ - - ^ self optionAt: 'storage' - ifAbsent: [ ^ FileLocator temp ] -] +Extension { #name : #LePage } + +{ #category : #'*MiniDocs' } +LePage >> asHtmlFile [ + + self asMarkdownFile. + self defaultPandocTemplate exists + ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ]. + + OSSUnixSubprocess new + command: 'pandoc' ; + arguments: { + self markdownFileName. '-o'. self htmlFileName . + '--toc' . + '--template=', self defaultPandocTemplate basenameWithoutExtension }; + workingDirectory: self storage fullName; + runAndWaitOnExitDo: [ :process :outString | ^ self storage / self htmlFileName]. +] + +{ #category : #'*MiniDocs' } +LePage >> asMarkdeep [ + | bodyStream markdeep | + bodyStream := '' writeStream. + self preorderTraversal do: [:snippet | + bodyStream nextPutAll: snippet asMarkdeep + ]. + markdeep := Markdeep new + title: self title; + body: bodyStream contents; + navTop: self navTop. + self metadata keysAndValuesDo: [:k :v | + k = 'lang' + ifTrue: [ + markdeep head + add: ''; + yourself. + ] + ifFalse: [ + markdeep head + add: ''; + yourself. + ] + ]. + self metadata at: 'authors' ifPresent: [:author | markdeep metadata at: 'authors' put: author ]. + self metadata at: 'version' ifPresent: [:version | markdeep metadata at: 'version' put: version ]. + ^ markdeep. +] + +{ #category : #'*MiniDocs' } +LePage >> asMarkdeepFile [ + | folder | + folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. + ^ self asMarkdeep exportAsFileOn: folder / self markdeepFileName +] + +{ #category : #'*MiniDocs' } +LePage >> asMarkdown [ + | bodyStream markdown | + bodyStream := '' writeStream. + bodyStream + nextPutAll: '---'; + nextPutAll: String lf. + self metadata keysAndValuesDo: [ :k :v | + bodyStream + nextPutAll: k , ': "' , v, '"'; + nextPutAll: String lf ]. + bodyStream nextPutAll: '---' , String lf , String lf. + self preorderTraversal + do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ]. + markdown := Markdown new contents: bodyStream contents. + ^ markdown +] + +{ #category : #'*MiniDocs' } +LePage >> asMarkdownFile [ + | folder | + folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. + ^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdown contents +] + +{ #category : #'*MiniDocs' } +LePage >> defaultPandocTemplate [ + + ^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html' +] + +{ #category : #'*MiniDocs' } +LePage >> detectParentSnippetWithUid: uidString [ + "Answer a boolean indicating whether the supplied uid is present" + + ^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ] ifNone: [ ^ self ] +] + +{ #category : #'*MiniDocs' } +LePage >> exportedFileName [ + | sanitized | + sanitized := self title asDashedLowercase copyWithoutAll: #($/). + ^ sanitized , '--' , (self uidString copyFrom: 1 to: 5) +] + +{ #category : #'*MiniDocs' } +LePage >> fromMarkdeepUrl: aString [ + | docTree pageMetadata | + docTree := GrafoscopioUtils xmlFromUrl: aString. + pageMetadata := Markdeep new metadataFromXML: docTree. + self + basicUid: (pageMetadata at: 'id'); + title: (pageMetadata at: 'title'); + createTime: (pageMetadata at: 'created') asDateAndTime; + editTime: (pageMetadata at: 'modified') asDateAndTime; + createEmail: (pageMetadata at: 'creator'); + editEmail: (pageMetadata at: 'modifier'); + optionAt: 'metadata' put: pageMetadata. + self populateChildrenFrom: (docTree xpath: '//div') + +] + +{ #category : #'*MiniDocs' } +LePage >> htmlFileName [ + ^ self exportedFileName, '.html' +] + +{ #category : #'*MiniDocs' } +LePage >> latestEditTime: aLeTime [ + "Used for adding a LePage to database from a shared markdeep LePage version." + + latestEditTime := aLeTime +] + +{ #category : #'*MiniDocs' } +LePage >> markdeepFileName [ + + ^ self markdownFileName , '.html' +] + +{ #category : #'*MiniDocs' } +LePage >> markdownFileName [ + ^ self exportedFileName, '.md' +] + +{ #category : #'*MiniDocs' } +LePage >> metadata [ + + ^ self options at: 'metadata' ifAbsentPut: [ self metadataInit] +] + +{ #category : #'*MiniDocs' } +LePage >> metadataInit [ + + ^ OrderedDictionary new + at: 'id' put: self uidString; + at: 'title' put: self contentAsString; + at: 'created' put: self createTime greaseString; + at: 'modified' put: self latestEditTime greaseString; + at: 'creator' put: self createEmail greaseString; + at: 'modifier' put: self editEmail greaseString; + yourself +] + +{ #category : #'*MiniDocs' } +LePage >> navTop [ + | topNavFile | + topNavFile := ((self optionAt: 'storage' ifAbsentPut: [ FileLocator temp ]) / '_navtop.html'). + topNavFile exists + ifFalse: [ ^ '' ] + ifTrue: [ ^ topNavFile contents ] +] + +{ #category : #'*MiniDocs' } +LePage >> options [ + ^ options +] + +{ #category : #'*MiniDocs' } +LePage >> preorderTraversal [ + | output | + output := OrderedCollection new. + self withDeepCollect: [:each | each allChildrenBreadthFirstDo: [:child | output add: child]]. + ^ output. +] + +{ #category : #'*MiniDocs' } +LePage >> removeSnippetsMetadata [ + self preorderTraversal do: [ :snippet | + (snippet options isNotNil and: [ snippet options includesKey: 'metadata' ]) + ifTrue: [ snippet options removeKey: 'metadata' ] ] +] + +{ #category : #'*MiniDocs' } +LePage >> sharedVariablesBindings [ + | codeSnippets shared | + codeSnippets := self preorderTraversal select: [:snippet | + snippet class = LePharoSnippet and: [ snippet code includesSubstring: ':='] + ]. + + codeSnippets first in: [:snippet | | context | + context := snippet coder evaluationContext. + snippet coder doItInContext: context. + shared := context bindingStrategy bindings detect: [:each | + each isKindOf: GtSharedVariablesBindings + ] + ]. + + codeSnippets asArray allButFirstDo: [:snippet| | context| + context := snippet coder evaluationContext. + context addBindings: shared. + snippet coder doItInContext: context + ]. + + ^ shared asDictionary +] + +{ #category : #'*MiniDocs' } +LePage >> storage [ + + ^ self optionAt: 'storage' + ifAbsent: [ ^ FileLocator temp ] +] diff --git a/src/MiniDocs/LePageHeaderBuilder.extension.st b/src/MiniDocs/LePageHeaderBuilder.extension.st index 778bc43..efe3f99 100644 --- a/src/MiniDocs/LePageHeaderBuilder.extension.st +++ b/src/MiniDocs/LePageHeaderBuilder.extension.st @@ -1,15 +1,15 @@ -Extension { #name : #LePageHeaderBuilder } - -{ #category : #'*MiniDocs' } -LePageHeaderBuilder >> addExportPageButton [ - - | newButton | - - newButton := BrButton new - aptitude: BrGlamorousButtonWithIconAptitude; - label: 'Export Page'; - icon: BrGlamorousVectorIcons down; - action: [ :aButton | - aButton phlow spawnObject: self page asMarkdeepFile ]. - self toolbarElement addItem: newButton. -] +Extension { #name : #LePageHeaderBuilder } + +{ #category : #'*MiniDocs' } +LePageHeaderBuilder >> addExportPageButton [ + + | newButton | + + newButton := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + label: 'Export Page'; + icon: BrGlamorousVectorIcons down; + action: [ :aButton | + aButton phlow spawnObject: self page asMarkdeepFile ]. + self toolbarElement addItem: newButton. +] diff --git a/src/MiniDocs/LePharoSnippet.extension.st b/src/MiniDocs/LePharoSnippet.extension.st index 4127758..c418bd9 100644 --- a/src/MiniDocs/LePharoSnippet.extension.st +++ b/src/MiniDocs/LePharoSnippet.extension.st @@ -1,64 +1,64 @@ -Extension { #name : #LePharoSnippet } - -{ #category : #'*MiniDocs' } -LePharoSnippet >> contentAsStringCustomized [ - | thisObject | - (self tags includes: 'output') ifFalse: [ ^ self contentAsString ]. - thisObject := ((self page sharedVariablesBindings) at: self detectObject) value. - ^ thisObject perform: self detectMessage trimmed asSymbol. -] - -{ #category : #'*MiniDocs' } -LePharoSnippet >> contentFrom: markdeepDiv [ - - | sanitizedStringText metadata joinedText | - metadata := STON fromString: (markdeepDiv attributes at: 'st-data'). - sanitizedStringText := markdeepDiv contentString lines. - sanitizedStringText := sanitizedStringText copyFrom: 4 to: sanitizedStringText size -2. - joinedText := '' writeStream. - sanitizedStringText do: [ :line | joinedText nextPutAll: line; nextPut: Character lf ]. - self code: joinedText contents allButLast; - uid: (LeUID new uidString: (metadata at: 'id')); - parent: (metadata at: 'parent'); - createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime)); - editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime)); - editEmail: (metadata at: 'modifier'); - createEmail: (metadata at: 'creator') -] - -{ #category : #'*MiniDocs' } -LePharoSnippet >> markdeepCustomCloser [ - ^ String streamContents: [ :stream | - stream - nextPutAll: '~~~'; lf; - nextPutAll: ''; lf. - ] -] - -{ #category : #'*MiniDocs' } -LePharoSnippet >> markdeepCustomOpener [ - ^ String streamContents: [ :stream | - stream - nextPutAll: ''; lf. + ] +] + +{ #category : #'*MiniDocs' } +LePharoSnippet >> markdeepCustomOpener [ + ^ String streamContents: [ :stream | + stream + nextPutAll: ''. - ^ providers - - -] - -{ #category : #utilities } -Markdeep >> commentsSupport [ - "I enable comments of the page." - - self comments ifFalse: [ ^ self ]. - ^ self commentsProviderStrings at: self commentsProvider -] - -{ #category : #accessing } -Markdeep >> config [ - - ^ config ifNil: [ config := Dictionary new] -] - -{ #category : #accessing } -Markdeep >> config: aDictionary [ - - config := aDictionary -] - -{ #category : #'instance creation' } -Markdeep >> contents [ - | output | - self title ifNil: [ ^ self body ]. - output := '' writeStream. - output - nextPutAll: self headContents; lf; lf; - nextPutAll: ' **', self title, '**'; lf; - nextPutAll: self authorsString ; lf; - nextPutAll: ' ', self version; lf; - nextPutAll: self navTop; lf; lf; - nextPutAll: self body; lf; lf; - nextPutAll: self tail; lf; lf; lf; lf; - nextPutAll: self commentsSupport. - ^ output contents. -] - -{ #category : #persistence } -Markdeep >> exportAsFile [ - | newFile | - self markdownFile ifNil: [ self inform: 'Define an input Markdown file or use #exportAsFileOn: instead.' ]. - newFile := (self markdownFile file fullName, '.html') asFileReference. - ^ self exportAsFileOn: newFile. -] - -{ #category : #persistence } -Markdeep >> exportAsFileOn: aFileReference [ - aFileReference ensureDelete. - aFileReference exists ifFalse: [ aFileReference ensureCreateFile ]. - aFileReference writeStreamDo: [ :stream | - stream nextPutAll: self contents ]. - self inform: 'Exported as: ', String cr, aFileReference fullName. - ^ aFileReference -] - -{ #category : #utilities } -Markdeep >> fontAwesomeHeader [ - "I enable the font awesome support in the document header" - - ^ '' -] - -{ #category : #'instance creation' } -Markdeep >> fromMarkdownFile: aFileReference [ - "I create a Markdeep document from a given Markdown file." - self processMarkdownFor: aFileReference. - ^ self. -] - -{ #category : #accessing } -Markdeep >> gtTextFor: aView [ - - ^ aView textEditor - title: 'Text'; - text: [ self contents ] -] - -{ #category : #accessing } -Markdeep >> head [ - ^ head ifNil: [ head := OrderedCollection new. - head add: self fontAwesomeHeader; yourself ] -] - -{ #category : #accessing } -Markdeep >> head: anOrderedCollection [ - head := anOrderedCollection -] - -{ #category : #'instance creation' } -Markdeep >> headContents [ - - ^ String streamContents: [ :stream | - stream - nextPutAll: ''; - nextPut: Character lf. - self head do: [ :line | - stream - nextPutAll: ' '; - nextPutAll: line; - nextPut: Character lf - ]. - stream - nextPutAll: ''; - nextPut: Character lf. - ]. -] - -{ #category : #accessing } -Markdeep >> language [ - ^ language -] - -{ #category : #accessing } -Markdeep >> language: anObject [ - language := anObject -] - -{ #category : #accessing } -Markdeep >> markdeepScriptTag [ - ^ ' -' -] - -{ #category : #accessing } -Markdeep >> markdownFile [ - ^ Markdown new fromFile: (self config at: 'markdownFile') -] - -{ #category : #accessing } -Markdeep >> markdownFile: aFileReference [ - "Where the Mardown file associated with me is stored. Used for sync. and import/export purposes." - self config at: 'markdownFile' put: aFileReference -] - -{ #category : #'instance creation' } -Markdeep >> metadata [ - ^ metadata ifNil: [ metadata := OrderedDictionary new ] -] - -{ #category : #accessing } -Markdeep >> metadata: anOrderedDictionary [ - metadata := anOrderedDictionary -] - -{ #category : #utilities } -Markdeep >> metadataFromXML: aXMLDocument [ - | metaDict | - - metaDict := OrderedDictionary new. - (aXMLDocument xpath: '//meta') do: [ :each | - metaDict at: (each @ 'name') stringValue put: (each @ 'content') stringValue ]. - ^ metaDict -] - -{ #category : #'instance creation' } -Markdeep >> navTop [ - ^ navTop ifNil: [ navTop := '' ] -] - -{ #category : #'as yet unclassified' } -Markdeep >> navTop: aString [ - navTop:= aString. -] - -{ #category : #accessing } -Markdeep >> options [ - ^ options ifNil: [ - "Setting defaults accoding to: https://casual-effects.com/markdeep/#api" - options := Dictionary new - at: 'mode' put: nil; - at: 'lang' put: nil; - at: 'tocStyle' put: 'auto'; - at: 'autoLinkImages' put: true; - yourself - ] -] - -{ #category : #printing } -Markdeep >> printOn: aStream [ - - super printOn: aStream. - aStream - nextPutAll: '( ', self title, ' )' -] - -{ #category : #'instance creation' } -Markdeep >> processMarkdownFor: aFileReference [ - "comment stating purpose of message" - | markdownContent | - self markdownFile: aFileReference. - markdownContent := Markdown fromFile: aFileReference. - self body: (markdownContent commentYAMLMetadata contents). - self metadata: markdownContent yamlMetadata -] - -{ #category : #accessing } -Markdeep >> pubPubFootnoteMetadataFromString: string [ - | sanitized footnoteData altLine altString id | - (string lines size <= 1) ifTrue: [ ^ nil ]. - sanitized := '' writeStream. - altString := string copyReplaceAll: '.footnote' with: ''. - altString := altString copyReplaceAll: ' node-type=' - with: ' -node-type= '. - altString lines allButFirstDo: [:line | - (line beginsWith: '>') - ifTrue: [ altLine := line allButFirst ] - ifFalse: [ altLine := line ]. - sanitized - nextPutAll: altLine trimBoth; - nextPutAll: String lf - ]. - sanitized := sanitized contents. - sanitized := sanitized copyReplaceAll: 'type=' with: 'type: '. - sanitized := sanitized copyReplaceAll: 'value=' with: 'value: '. - id := (altString lines first) allButFirst trimmed. - footnoteData := { 'id' -> id } asDictionary. - footnoteData addAll: (MiniDocs yamlToJson: sanitized trimmed). - ^ footnoteData -] - -{ #category : #accessing } -Markdeep >> pubPubFootnoteRawLinks [ - ^ self selectPubPubLinksWithSize: 2 -] - -{ #category : #accessing } -Markdeep >> pubPubFootnotesLinesRange [ - | beginningLine endingLine | - beginningLine := self contents lines size + 1. - self contents lines doWithIndex: [:line :i | - (line beginsWith: '::: {.pub-notes}') ifTrue: [ beginningLine := i ]. - (i > beginningLine and: [ line beginsWith: ':::' ]) - ifTrue: [ - endingLine := i. - ^ {beginningLine . endingLine} - ] - ] -] - -{ #category : #accessing } -Markdeep >> pubPubFootnotesText [ - | footnotesLines output | - footnotesLines := self contents lines - copyFrom: self pubPubFootnotesLinesRange first + 3 - to: self pubPubFootnotesLinesRange second - 1. - output := '' writeStream. - footnotesLines do: [:line | - output - nextPutAll: line; - nextPutAll: String crlf. - ]. - ^ output contents allButLast -] - -{ #category : #accessing } -Markdeep >> pubPubFootnotesToMarkdeep [ - | footnotes sanitized cleanedFootnotesText parsedLinks | - footnotes := OrderedDictionary new. - parsedLinks := self pubPubFootnoteRawLinks. - parsedLinks ifEmpty: [ ^self ]. - sanitized := self body. - parsedLinks do: [:link | | footnote | - footnote := self pubPubFootnoteMetadataFromString: link second. - footnote ifNotNil: [ | toReplace | - footnotes at: (footnote at: 'id') put: footnote. - toReplace := '[', link first, ']{', link second, '}'. - sanitized := sanitized copyReplaceAll: toReplace with: '[^', (footnote at: 'id'), ']' - ] - ]. - cleanedFootnotesText := '' writeStream. - footnotes keysAndValuesDo: [:k :v | - cleanedFootnotesText - nextPutAll: '[^', k, ']: '; - nextPutAll: (v at: 'data-value'), String lf, String lf. - ]. - self body: (sanitized copyReplaceAll: self pubPubFootnotesText with: cleanedFootnotesText contents) -] - -{ #category : #accessing } -Markdeep >> pubPubImageLinks [ - ^ self selectPubPubLinksWithSize: 3 -] - -{ #category : #accessing } -Markdeep >> pubPubImagesToMarkdeep [ - | sanitized parsedLinks | - - parsedLinks := self pubPubImageLinks. - parsedLinks ifEmpty: [ ^self ]. - sanitized := self body. - parsedLinks do: [:link | - sanitized := sanitized copyReplaceAll: '{', link third, '}' with: '' - ]. - self body: sanitized -] - -{ #category : #accessing } -Markdeep >> pubPubRawLinks [ - | parser | - parser := PubPubGrammar new document. - ^ (parser parse: self body) -] - -{ #category : #accessing } -Markdeep >> replaceBackslashBreaklines [ - self bodyReplaceAll: '\ -' with: '
-' -] - -{ #category : #accessing } -Markdeep >> selectPubPubLinksWithSize: naturalNumber [ - ^ self pubPubRawLinks select: [ :each | each size = naturalNumber ] -] - -{ #category : #accessing } -Markdeep >> tail [ - "I enable the document tail, which, in turn, enables a Markdeep document" - | output | - output := '' writeStream. - output - nextPutAll: ''; lf; lf; - nextPutAll: ''; lf; - nextPutAll: ''; lf; - nextPutAll: self markdeepScriptTag; lf; - nextPutAll: ''. - ^ output contents -] - -{ #category : #accessing } -Markdeep >> tail: anObject [ - tail := anObject -] - -{ #category : #accessing } -Markdeep >> title [ - - ^ title ifNil: [ title := self metadata at: 'title' ] -] - -{ #category : #accessing } -Markdeep >> title: anObject [ - - title := anObject -] - -{ #category : #accessing } -Markdeep >> tocStyle [ - ^ self options at: 'tocStyle' ifAbsent: [ 'short' ] -] - -{ #category : #accessing } -Markdeep >> tocStyle: aString [ - "A string can be: 'auto' 'none' 'short' 'medium' or 'long'" - self options at: 'tocStyle' put: aString -] - -{ #category : #'instance creation' } -Markdeep >> version [ - self metadata at: 'version' ifPresent: [:value | ^ 'v',value ]. - ^ '' -] +" +I model a Mardeep file as described in https://casual-effects.com/markdeep/ +" +Class { + #name : #Markdeep, + #superclass : #Object, + #instVars : [ + 'title', + 'body', + 'comments', + 'tail', + 'language', + 'config', + 'metadata', + 'head', + 'navTop', + 'options' + ], + #category : #MiniDocs +} + +{ #category : #'as yet unclassified' } +Markdeep class >> fromMarkdownFile: aFileReference [ + ^ self new fromMarkdownFile: aFileReference. +] + +{ #category : #accessing } +Markdeep class >> fromPubPubTOC: orderedDictionary folder: folder index: ordinalPossitive [ + | contentSection testFile | + contentSection := orderedDictionary associations at: ordinalPossitive. + testFile := folder / (contentSection key,'--', contentSection value),'md'. + ^ self new fromMarkdownFile: testFile. +] + +{ #category : #'instance creation' } +Markdeep >> authors [ + self metadata at: 'authors' ifPresent: [:k | ^ '**', k, '**' ]. + ^ ''. +] + +{ #category : #'instance creation' } +Markdeep >> authorsString [ + self authors + ifNil: [ ^ '' ] ifNotNil: [ ^ ' ', self authors ] +] + +{ #category : #accessing } +Markdeep >> body [ + ^ body +] + +{ #category : #accessing } +Markdeep >> body: anObject [ + body := anObject +] + +{ #category : #accessing } +Markdeep >> bodyReplaceAll: original with: replacement [ + self body: (self body copyReplaceAll: original with: replacement) +] + +{ #category : #accessing } +Markdeep >> commentPubPubDelimiters [ + | commented openners | + openners := #('::: {.pub-body-component}' '::: {.editor .Prosemirror}' '::: {.pub-notes}'). + commented := self body. + openners do: [:openner | + commented := commented copyReplaceAll: openner with: '' + ]. + commented := commented + copyReplaceAll: '::: +' with: ' +'. + self body: commented +] + +{ #category : #accessing } +Markdeep >> comments [ + ^ comments ifNil: [ ^ comments := true ] +] + +{ #category : #accessing } +Markdeep >> comments: aBoolean [ + "I tell if comments are enabled by default or not." + comments := aBoolean +] + +{ #category : #utilities } +Markdeep >> commentsProvider [ + "I return the url of the default service that provides annotation support. + I am used to add such support in the contents of the Markdeep page." + ^ 'https://hypothes.is' +] + +{ #category : #utilities } +Markdeep >> commentsProviderStrings [ + "I associate a comments service provider with the string that is required to be added + to the document to enable such provider." + | providers | + providers := Dictionary new. + providers at: 'https://hypothes.is' put: ' +'. + ^ providers + + +] + +{ #category : #utilities } +Markdeep >> commentsSupport [ + "I enable comments of the page." + + self comments ifFalse: [ ^ self ]. + ^ self commentsProviderStrings at: self commentsProvider +] + +{ #category : #accessing } +Markdeep >> config [ + + ^ config ifNil: [ config := Dictionary new] +] + +{ #category : #accessing } +Markdeep >> config: aDictionary [ + + config := aDictionary +] + +{ #category : #'instance creation' } +Markdeep >> contents [ + | output | + self title ifNil: [ ^ self body ]. + output := '' writeStream. + output + nextPutAll: self headContents; lf; lf; + nextPutAll: ' **', self title, '**'; lf; + nextPutAll: self authorsString ; lf; + nextPutAll: ' ', self version; lf; + nextPutAll: self navTop; lf; lf; + nextPutAll: self body; lf; lf; + nextPutAll: self tail; lf; lf; lf; lf; + nextPutAll: self commentsSupport. + ^ output contents. +] + +{ #category : #persistence } +Markdeep >> exportAsFile [ + | newFile | + self markdownFile ifNil: [ self inform: 'Define an input Markdown file or use #exportAsFileOn: instead.' ]. + newFile := (self markdownFile file fullName, '.html') asFileReference. + ^ self exportAsFileOn: newFile. +] + +{ #category : #persistence } +Markdeep >> exportAsFileOn: aFileReference [ + aFileReference ensureDelete. + aFileReference exists ifFalse: [ aFileReference ensureCreateFile ]. + aFileReference writeStreamDo: [ :stream | + stream nextPutAll: self contents ]. + self inform: 'Exported as: ', String cr, aFileReference fullName. + ^ aFileReference +] + +{ #category : #utilities } +Markdeep >> fontAwesomeHeader [ + "I enable the font awesome support in the document header" + + ^ '' +] + +{ #category : #'instance creation' } +Markdeep >> fromMarkdownFile: aFileReference [ + "I create a Markdeep document from a given Markdown file." + self processMarkdownFor: aFileReference. + ^ self. +] + +{ #category : #accessing } +Markdeep >> gtTextFor: aView [ + + ^ aView textEditor + title: 'Text'; + text: [ self contents ] +] + +{ #category : #accessing } +Markdeep >> head [ + ^ head ifNil: [ head := OrderedCollection new. + head add: self fontAwesomeHeader; yourself ] +] + +{ #category : #accessing } +Markdeep >> head: anOrderedCollection [ + head := anOrderedCollection +] + +{ #category : #'instance creation' } +Markdeep >> headContents [ + + ^ String streamContents: [ :stream | + stream + nextPutAll: ''; + nextPut: Character lf. + self head do: [ :line | + stream + nextPutAll: ' '; + nextPutAll: line; + nextPut: Character lf + ]. + stream + nextPutAll: ''; + nextPut: Character lf. + ]. +] + +{ #category : #accessing } +Markdeep >> language [ + ^ language +] + +{ #category : #accessing } +Markdeep >> language: anObject [ + language := anObject +] + +{ #category : #accessing } +Markdeep >> markdeepScriptTag [ + ^ ' +' +] + +{ #category : #accessing } +Markdeep >> markdownFile [ + ^ Markdown new fromFile: (self config at: 'markdownFile') +] + +{ #category : #accessing } +Markdeep >> markdownFile: aFileReference [ + "Where the Mardown file associated with me is stored. Used for sync. and import/export purposes." + self config at: 'markdownFile' put: aFileReference +] + +{ #category : #'instance creation' } +Markdeep >> metadata [ + ^ metadata ifNil: [ metadata := OrderedDictionary new ] +] + +{ #category : #accessing } +Markdeep >> metadata: anOrderedDictionary [ + metadata := anOrderedDictionary +] + +{ #category : #utilities } +Markdeep >> metadataFromXML: aXMLDocument [ + | metaDict | + + metaDict := OrderedDictionary new. + (aXMLDocument xpath: '//meta') do: [ :each | + metaDict at: (each @ 'name') stringValue put: (each @ 'content') stringValue ]. + ^ metaDict +] + +{ #category : #'instance creation' } +Markdeep >> navTop [ + ^ navTop ifNil: [ navTop := '' ] +] + +{ #category : #'as yet unclassified' } +Markdeep >> navTop: aString [ + navTop:= aString. +] + +{ #category : #accessing } +Markdeep >> options [ + ^ options ifNil: [ + "Setting defaults accoding to: https://casual-effects.com/markdeep/#api" + options := Dictionary new + at: 'mode' put: nil; + at: 'lang' put: nil; + at: 'tocStyle' put: 'auto'; + at: 'autoLinkImages' put: true; + yourself + ] +] + +{ #category : #printing } +Markdeep >> printOn: aStream [ + + super printOn: aStream. + aStream + nextPutAll: '( ', self title, ' )' +] + +{ #category : #'instance creation' } +Markdeep >> processMarkdownFor: aFileReference [ + "comment stating purpose of message" + | markdownContent | + self markdownFile: aFileReference. + markdownContent := Markdown fromFile: aFileReference. + self body: (markdownContent commentYAMLMetadata contents). + self metadata: markdownContent yamlMetadata +] + +{ #category : #accessing } +Markdeep >> pubPubFootnoteMetadataFromString: string [ + | sanitized footnoteData altLine altString id | + (string lines size <= 1) ifTrue: [ ^ nil ]. + sanitized := '' writeStream. + altString := string copyReplaceAll: '.footnote' with: ''. + altString := altString copyReplaceAll: ' node-type=' + with: ' +node-type= '. + altString lines allButFirstDo: [:line | + (line beginsWith: '>') + ifTrue: [ altLine := line allButFirst ] + ifFalse: [ altLine := line ]. + sanitized + nextPutAll: altLine trimBoth; + nextPutAll: String lf + ]. + sanitized := sanitized contents. + sanitized := sanitized copyReplaceAll: 'type=' with: 'type: '. + sanitized := sanitized copyReplaceAll: 'value=' with: 'value: '. + id := (altString lines first) allButFirst trimmed. + footnoteData := { 'id' -> id } asDictionary. + footnoteData addAll: (MiniDocs yamlToJson: sanitized trimmed). + ^ footnoteData +] + +{ #category : #accessing } +Markdeep >> pubPubFootnoteRawLinks [ + ^ self selectPubPubLinksWithSize: 2 +] + +{ #category : #accessing } +Markdeep >> pubPubFootnotesLinesRange [ + | beginningLine endingLine | + beginningLine := self contents lines size + 1. + self contents lines doWithIndex: [:line :i | + (line beginsWith: '::: {.pub-notes}') ifTrue: [ beginningLine := i ]. + (i > beginningLine and: [ line beginsWith: ':::' ]) + ifTrue: [ + endingLine := i. + ^ {beginningLine . endingLine} + ] + ] +] + +{ #category : #accessing } +Markdeep >> pubPubFootnotesText [ + | footnotesLines output | + footnotesLines := self contents lines + copyFrom: self pubPubFootnotesLinesRange first + 3 + to: self pubPubFootnotesLinesRange second - 1. + output := '' writeStream. + footnotesLines do: [:line | + output + nextPutAll: line; + nextPutAll: String crlf. + ]. + ^ output contents allButLast +] + +{ #category : #accessing } +Markdeep >> pubPubFootnotesToMarkdeep [ + | footnotes sanitized cleanedFootnotesText parsedLinks | + footnotes := OrderedDictionary new. + parsedLinks := self pubPubFootnoteRawLinks. + parsedLinks ifEmpty: [ ^self ]. + sanitized := self body. + parsedLinks do: [:link | | footnote | + footnote := self pubPubFootnoteMetadataFromString: link second. + footnote ifNotNil: [ | toReplace | + footnotes at: (footnote at: 'id') put: footnote. + toReplace := '[', link first, ']{', link second, '}'. + sanitized := sanitized copyReplaceAll: toReplace with: '[^', (footnote at: 'id'), ']' + ] + ]. + cleanedFootnotesText := '' writeStream. + footnotes keysAndValuesDo: [:k :v | + cleanedFootnotesText + nextPutAll: '[^', k, ']: '; + nextPutAll: (v at: 'data-value'), String lf, String lf. + ]. + self body: (sanitized copyReplaceAll: self pubPubFootnotesText with: cleanedFootnotesText contents) +] + +{ #category : #accessing } +Markdeep >> pubPubImageLinks [ + ^ self selectPubPubLinksWithSize: 3 +] + +{ #category : #accessing } +Markdeep >> pubPubImagesToMarkdeep [ + | sanitized parsedLinks | + + parsedLinks := self pubPubImageLinks. + parsedLinks ifEmpty: [ ^self ]. + sanitized := self body. + parsedLinks do: [:link | + sanitized := sanitized copyReplaceAll: '{', link third, '}' with: '' + ]. + self body: sanitized +] + +{ #category : #accessing } +Markdeep >> pubPubRawLinks [ + | parser | + parser := PubPubGrammar new document. + ^ (parser parse: self body) +] + +{ #category : #accessing } +Markdeep >> replaceBackslashBreaklines [ + self bodyReplaceAll: '\ +' with: '
+' +] + +{ #category : #accessing } +Markdeep >> selectPubPubLinksWithSize: naturalNumber [ + ^ self pubPubRawLinks select: [ :each | each size = naturalNumber ] +] + +{ #category : #accessing } +Markdeep >> tail [ + "I enable the document tail, which, in turn, enables a Markdeep document" + | output | + output := '' writeStream. + output + nextPutAll: ''; lf; lf; + nextPutAll: ''; lf; + nextPutAll: ''; lf; + nextPutAll: self markdeepScriptTag; lf; + nextPutAll: ''. + ^ output contents +] + +{ #category : #accessing } +Markdeep >> tail: anObject [ + tail := anObject +] + +{ #category : #accessing } +Markdeep >> title [ + + ^ title ifNil: [ title := self metadata at: 'title' ] +] + +{ #category : #accessing } +Markdeep >> title: anObject [ + + title := anObject +] + +{ #category : #accessing } +Markdeep >> tocStyle [ + ^ self options at: 'tocStyle' ifAbsent: [ 'short' ] +] + +{ #category : #accessing } +Markdeep >> tocStyle: aString [ + "A string can be: 'auto' 'none' 'short' 'medium' or 'long'" + self options at: 'tocStyle' put: aString +] + +{ #category : #'instance creation' } +Markdeep >> version [ + self metadata at: 'version' ifPresent: [:value | ^ 'v',value ]. + ^ '' +] diff --git a/src/MiniDocs/Markdown.class.st b/src/MiniDocs/Markdown.class.st index e9124ca..2e9b79a 100644 --- a/src/MiniDocs/Markdown.class.st +++ b/src/MiniDocs/Markdown.class.st @@ -1,185 +1,185 @@ -" -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 : #Object, - #instVars : [ - 'contents', - 'file' - ], - #category : #MiniDocs -} - -{ #category : #'instance creation' } -Markdown class >> fromFile: aFileReference [ - ^ self new fromFile: aFileReference -] - -{ #category : #utilities } -Markdown class >> yamlMetadataDelimiter [ - ^ '---' - -] - -{ #category : #operation } -Markdown >> commentYAMLMetadata [ - | newContents | - self detectYAMLMetadata ifFalse: [ ^ self ]. - newContents := '' writeStream. - newContents nextPutAll: ''; crlf. - (self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line | - newContents nextPutAll: line; crlf ]. - ^ newContents contents. -] - -{ #category : #utilities } -Markdown >> containsYAMLMetadataClosing [ - ^ self yamlMetadataClosingLineNumber > 0 -] - -{ #category : #accessing } -Markdown >> contents [ - ^ contents -] - -{ #category : #accessing } -Markdown >> contents: anObject [ - contents := anObject -] - -{ #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 : #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 -] - -{ #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 : #accessing } -Markdown >> gtTextFor: aView [ - - ^ aView textEditor - title: 'Text'; - text: [ self contents ] -] - -{ #category : #utilities } -Markdown >> lines [ - ^ self contents lines. -] - -{ #category : #accessing } -Markdown >> metadata [ - | rawMeta | - rawMeta := PPYAMLGrammar new parse: self yamlMetadataString. - rawMeta associationsDo: [ :assoc | - assoc value = 'false' ifTrue: [ assoc value: false ]. - assoc value = 'true' ifTrue: [ assoc value: true ] ]. - ^ rawMeta -] - -{ #category : #accessing } -Markdown >> printOn: aStream [ - super printOn: aStream. - aStream - nextPutAll: '( ', (self metadata at: 'title'), ' )' -] - -{ #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 cr. ]. - ^ 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. -] +" +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 : #Object, + #instVars : [ + 'contents', + 'file' + ], + #category : #MiniDocs +} + +{ #category : #'instance creation' } +Markdown class >> fromFile: aFileReference [ + ^ self new fromFile: aFileReference +] + +{ #category : #utilities } +Markdown class >> yamlMetadataDelimiter [ + ^ '---' + +] + +{ #category : #operation } +Markdown >> commentYAMLMetadata [ + | newContents | + self detectYAMLMetadata ifFalse: [ ^ self ]. + newContents := '' writeStream. + newContents nextPutAll: ''; crlf. + (self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line | + newContents nextPutAll: line; crlf ]. + ^ newContents contents. +] + +{ #category : #utilities } +Markdown >> containsYAMLMetadataClosing [ + ^ self yamlMetadataClosingLineNumber > 0 +] + +{ #category : #accessing } +Markdown >> contents [ + ^ contents +] + +{ #category : #accessing } +Markdown >> contents: anObject [ + contents := anObject +] + +{ #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 : #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 +] + +{ #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 : #accessing } +Markdown >> gtTextFor: aView [ + + ^ aView textEditor + title: 'Text'; + text: [ self contents ] +] + +{ #category : #utilities } +Markdown >> lines [ + ^ self contents lines. +] + +{ #category : #accessing } +Markdown >> metadata [ + | rawMeta | + rawMeta := PPYAMLGrammar new parse: self yamlMetadataString. + rawMeta associationsDo: [ :assoc | + assoc value = 'false' ifTrue: [ assoc value: false ]. + assoc value = 'true' ifTrue: [ assoc value: true ] ]. + ^ rawMeta +] + +{ #category : #accessing } +Markdown >> printOn: aStream [ + super printOn: aStream. + aStream + nextPutAll: '( ', (self metadata at: 'title'), ' )' +] + +{ #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 cr. ]. + ^ 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. +] diff --git a/src/MiniDocs/MarkupFile.class.st b/src/MiniDocs/MarkupFile.class.st index 6696cba..2369afb 100644 --- a/src/MiniDocs/MarkupFile.class.st +++ b/src/MiniDocs/MarkupFile.class.st @@ -1,37 +1,37 @@ -" -I model common operations made with several markup files. -" -Class { - #name : #MarkupFile, - #superclass : #Object, - #instVars : [ - 'file' - ], - #category : #MiniDocs -} - -{ #category : #persistence } -MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [ - | file | - file := aFileReferenceOrFileName asFileReference. - file ensureDelete. - file exists ifFalse: [ file ensureCreateFile ]. - file writeStreamDo: [ :stream | - stream nextPutAll: text withUnixLineEndings]. - self inform: 'Exported as: ', String cr, file fullName. - ^ file -] - -{ #category : #accessing } -MarkupFile class >> installTemplate: anUrl into: aFolder [ - - | fileName | - fileName := anUrl asUrl segments last. - (aFolder / fileName) exists - ifTrue: [ (aFolder / fileName) ensureDeleteFile ] - ifFalse: [ aFolder ensureCreateDirectory ]. - ZnClient new - url: anUrl; - downloadTo: aFolder. - ^ aFolder -] +" +I model common operations made with several markup files. +" +Class { + #name : #MarkupFile, + #superclass : #Object, + #instVars : [ + 'file' + ], + #category : #MiniDocs +} + +{ #category : #persistence } +MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [ + | file | + file := aFileReferenceOrFileName asFileReference. + file ensureDelete. + file exists ifFalse: [ file ensureCreateFile ]. + file writeStreamDo: [ :stream | + stream nextPutAll: text withUnixLineEndings]. + self inform: 'Exported as: ', String cr, file fullName. + ^ file +] + +{ #category : #accessing } +MarkupFile class >> installTemplate: anUrl into: aFolder [ + + | fileName | + fileName := anUrl asUrl segments last. + (aFolder / fileName) exists + ifTrue: [ (aFolder / fileName) ensureDeleteFile ] + ifFalse: [ aFolder ensureCreateDirectory ]. + ZnClient new + url: anUrl; + downloadTo: aFolder. + ^ aFolder +] diff --git a/src/MiniDocs/MiniDocs.class.st b/src/MiniDocs/MiniDocs.class.st index a1fc372..d03b3da 100644 --- a/src/MiniDocs/MiniDocs.class.st +++ b/src/MiniDocs/MiniDocs.class.st @@ -1,52 +1,55 @@ -Class { - #name : #MiniDocs, - #superclass : #Object, - #category : #MiniDocs -} - -{ #category : #accessing } -MiniDocs class >> appFolder [ - | tempFolder | - tempFolder := FileLocator userData / 'Mutabit' / 'MiniDocs'. - tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ]. - ^ tempFolder -] - -{ #category : #accessing } -MiniDocs class >> installYamlToJson [ - "For the moment, only Gnu/Linux and Mac are supported. - IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation." - self yamlToJsonBinary exists ifTrue: [ ^ MiniDocs appFolder ]. - Nimble install: 'commandeer'. - OSSUnixSubprocess new - command: 'nim'; - arguments: {'c'. self yamlToJsonSourceCode fullName}; - runAndWaitOnExitDo: [ :process :outString | - (self yamlToJsonSourceCode parent / self yamlToJsonSourceCode basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. - ^ MiniDocs appFolder ] -] - -{ #category : #accessing } -MiniDocs class >> yamlToJson: yamlString [ - "This method uses a external binary written in Nim, as the native Pharo parser for YAML, written in PetitParser, - was less robust and unable to parse correctly the same strings as the external one." - self yamlToJsonBinary exists ifFalse: [ self installYamlToJson ]. - - OSSUnixSubprocess new - command: self yamlToJsonBinary fullName; - arguments: {yamlString}; - redirectStdout; - runAndWaitOnExitDo: [ :process :outString | - ^ (STONJSON fromString: outString allButFirst) first - ] -] - -{ #category : #accessing } -MiniDocs class >> yamlToJsonBinary [ - ^ self appFolder / 'yamlToJson' -] - -{ #category : #accessing } -MiniDocs class >> yamlToJsonSourceCode [ - ^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/yamlToJson.nim' -] +Class { + #name : #MiniDocs, + #superclass : #Object, + #category : #MiniDocs +} + +{ #category : #accessing } +MiniDocs class >> appFolder [ + | tempFolder userDataFolder | + userDataFolder := Smalltalk os isWindows + ifTrue: [ FileLocator home / 'AppData' / 'Local' ] + ifFalse: [ FileLocator userData ]. + tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'. + tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ]. + ^ tempFolder +] + +{ #category : #accessing } +MiniDocs class >> installYamlToJson [ + "For the moment, only Gnu/Linux and Mac are supported. + IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation." + self yamlToJsonBinary exists ifTrue: [ ^ MiniDocs appFolder ]. + Nimble install: 'commandeer'. + OSSUnixSubprocess new + command: 'nim'; + arguments: {'c'. self yamlToJsonSourceCode fullName}; + runAndWaitOnExitDo: [ :process :outString | + (self yamlToJsonSourceCode parent / self yamlToJsonSourceCode basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. + ^ MiniDocs appFolder ] +] + +{ #category : #accessing } +MiniDocs class >> yamlToJson: yamlString [ + "This method uses a external binary written in Nim, as the native Pharo parser for YAML, written in PetitParser, + was less robust and unable to parse correctly the same strings as the external one." + self yamlToJsonBinary exists ifFalse: [ self installYamlToJson ]. + + OSSUnixSubprocess new + command: self yamlToJsonBinary fullName; + arguments: {yamlString}; + redirectStdout; + runAndWaitOnExitDo: [ :process :outString | + ^ (STONJSON fromString: outString allButFirst) first + ] +] + +{ #category : #accessing } +MiniDocs class >> yamlToJsonBinary [ + ^ self appFolder / 'yamlToJson' +] + +{ #category : #accessing } +MiniDocs class >> yamlToJsonSourceCode [ + ^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/yamlToJson.nim' +] diff --git a/src/MiniDocs/NanoID.class.st b/src/MiniDocs/NanoID.class.st index 4740b2f..c1c75c1 100644 --- a/src/MiniDocs/NanoID.class.st +++ b/src/MiniDocs/NanoID.class.st @@ -1,53 +1,53 @@ -" -I'm run an implementation of the [Nano ID](https://github.com/ai/nanoid) tiny, secure URL-friendly unique string ID generator via its [Nim implementation](https://github.com/icyphox/nanoid.nim). - -The Nim script has hard coded: - - * a [base 58 encoding](https://medium.com/concerning-pharo/understanding-base58-encoding-23e673e37ff6) alphabet to avoid similar looking letter and the use of non-alphanumeric characters. - * a 12 characters length output, which gives [a pretty low probability collision](https://zelark.github.io/nano-id-cc/) for the previous alphabet: - ~616 years needed, in order to have a 1% probability of at least one collision at a speed of 1000 IDs per hour. - This is more than enough for our unique IDs applications, mostly in the documentation context, - which consists of hand crafted and/or programmatically produced notes , - for example in data narratives, book(lets) and TiddlyWiki tiddlers of tens or hundreds of notes at most, - unevenly produced between hours, days and/or weeks.. - -" -Class { - #name : #NanoID, - #superclass : #Object, - #category : #'MiniDocs-MiniDocs' -} - -{ #category : #accessing } -NanoID class >> binaryFile [ - ^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension -] - -{ #category : #accessing } -NanoID class >> generate [ - self binaryFile exists ifFalse: [ NanoID install]. - OSSUnixSubprocess new - command: self binaryFile fullName; - redirectStdout; - redirectStdout; - runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ] -] - -{ #category : #accessing } -NanoID class >> install [ - "For the moment, only Gnu/Linux and Mac are supported. - IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation." - self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ]. - Nimble install: 'nanoid'. - OSSUnixSubprocess new - command: 'nim'; - arguments: {'c'. self scriptSourceCode fullName}; - runAndWaitOnExitDo: [ :process :outString | - (self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. - ^ MiniDocs appFolder ] -] - -{ #category : #accessing } -NanoID class >> scriptSourceCode [ - ^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim' -] +" +I'm run an implementation of the [Nano ID](https://github.com/ai/nanoid) tiny, secure URL-friendly unique string ID generator via its [Nim implementation](https://github.com/icyphox/nanoid.nim). + +The Nim script has hard coded: + + * a [base 58 encoding](https://medium.com/concerning-pharo/understanding-base58-encoding-23e673e37ff6) alphabet to avoid similar looking letter and the use of non-alphanumeric characters. + * a 12 characters length output, which gives [a pretty low probability collision](https://zelark.github.io/nano-id-cc/) for the previous alphabet: + ~616 years needed, in order to have a 1% probability of at least one collision at a speed of 1000 IDs per hour. + This is more than enough for our unique IDs applications, mostly in the documentation context, + which consists of hand crafted and/or programmatically produced notes , + for example in data narratives, book(lets) and TiddlyWiki tiddlers of tens or hundreds of notes at most, + unevenly produced between hours, days and/or weeks.. + +" +Class { + #name : #NanoID, + #superclass : #Object, + #category : #'MiniDocs-MiniDocs' +} + +{ #category : #accessing } +NanoID class >> binaryFile [ + ^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension +] + +{ #category : #accessing } +NanoID class >> generate [ + self binaryFile exists ifFalse: [ NanoID install]. + OSSUnixSubprocess new + command: self binaryFile fullName; + redirectStdout; + redirectStdout; + runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ] +] + +{ #category : #accessing } +NanoID class >> install [ + "For the moment, only Gnu/Linux and Mac are supported. + IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation." + self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ]. + Nimble install: 'nanoid'. + OSSUnixSubprocess new + command: 'nim'; + arguments: {'c'. self scriptSourceCode fullName}; + runAndWaitOnExitDo: [ :process :outString | + (self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. + ^ MiniDocs appFolder ] +] + +{ #category : #accessing } +NanoID class >> scriptSourceCode [ + ^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim' +] diff --git a/src/MiniDocs/Nimble.class.st b/src/MiniDocs/Nimble.class.st index a121ec5..572d652 100644 --- a/src/MiniDocs/Nimble.class.st +++ b/src/MiniDocs/Nimble.class.st @@ -1,66 +1,66 @@ -" -I'm a helper class modelling the common uses of the Nim's [Nimble package manager](https://github.com/nim-lang/nimble). -This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation. -" -Class { - #name : #Nimble, - #superclass : #Object, - #category : #'MiniDocs-MiniDocs' -} - -{ #category : #accessing } -Nimble class >> detect: packageName [ - ^ self installed - detect: [ :dependency | dependency beginsWith: packageName ] - ifFound: [ ^ true ] - ifNone: [ ^ false ] -] - -{ #category : #accessing } -Nimble class >> install: packageName [ - (self detect: packageName) ifTrue: [ ^ self ]. - self installPackagesList. - OSSUnixSubprocess new - command: 'nimble'; - arguments: {'install'. - packageName}; - redirectStdout; - runAndWaitOnExitDo: [ :process :outString | ^ outString ] -] - -{ #category : #accessing } -Nimble class >> installPackagesList [ - - (FileLocator home / '.nimble' / 'packages_official.json') exists - ifTrue: [ ^ self ]. - OSSUnixSubprocess new - command: 'nimble'; - arguments: #('refresh'); - redirectStdout; - runAndWaitOnExitDo: [ :process :outString | ^ outString ] -] - -{ #category : #accessing } -Nimble class >> installed [ - | installed | - OSSUnixSubprocess new - command: 'nimble'; - arguments: #('list' '--installed'); - redirectStdout; - redirectStderr; - runAndWaitOnExitDo: [ :process :outString :errString | - process isSuccess - ifTrue: [ ^ outString lines ]; - ifFalse: [ ^ nil ] - ] -] - -{ #category : #accessing } -Nimble class >> version [ - - OSSUnixSubprocess new - command: 'nimble'; - arguments: #('--version'); - redirectStdout; - runAndWaitOnExitDo: [ :process :outString | ^ outString ] -] +" +I'm a helper class modelling the common uses of the Nim's [Nimble package manager](https://github.com/nim-lang/nimble). +This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation. +" +Class { + #name : #Nimble, + #superclass : #Object, + #category : #'MiniDocs-MiniDocs' +} + +{ #category : #accessing } +Nimble class >> detect: packageName [ + ^ self installed + detect: [ :dependency | dependency beginsWith: packageName ] + ifFound: [ ^ true ] + ifNone: [ ^ false ] +] + +{ #category : #accessing } +Nimble class >> install: packageName [ + (self detect: packageName) ifTrue: [ ^ self ]. + self installPackagesList. + OSSUnixSubprocess new + command: 'nimble'; + arguments: {'install'. + packageName}; + redirectStdout; + runAndWaitOnExitDo: [ :process :outString | ^ outString ] +] + +{ #category : #accessing } +Nimble class >> installPackagesList [ + + (FileLocator home / '.nimble' / 'packages_official.json') exists + ifTrue: [ ^ self ]. + OSSUnixSubprocess new + command: 'nimble'; + arguments: #('refresh'); + redirectStdout; + runAndWaitOnExitDo: [ :process :outString | ^ outString ] +] + +{ #category : #accessing } +Nimble class >> installed [ + | installed | + OSSUnixSubprocess new + command: 'nimble'; + arguments: #('list' '--installed'); + redirectStdout; + redirectStderr; + runAndWaitOnExitDo: [ :process :outString :errString | + process isSuccess + ifTrue: [ ^ outString lines ]; + ifFalse: [ ^ nil ] + ] +] + +{ #category : #accessing } +Nimble class >> version [ + + OSSUnixSubprocess new + command: 'nimble'; + arguments: #('--version'); + redirectStdout; + runAndWaitOnExitDo: [ :process :outString | ^ outString ] +] diff --git a/src/MiniDocs/PubPubGrammar.class.st b/src/MiniDocs/PubPubGrammar.class.st index 7372c09..bb958fd 100644 --- a/src/MiniDocs/PubPubGrammar.class.st +++ b/src/MiniDocs/PubPubGrammar.class.st @@ -1,70 +1,70 @@ -Class { - #name : #PubPubGrammar, - #superclass : #PP2CompositeNode, - #instVars : [ - 'document', - 'link', - 'linkLabel', - 'linkContent', - 'imageLinkLabel', - 'imageLinkContent', - 'alternativeImages', - 'imageLink' - ], - #category : #'MiniDocs-Model' -} - -{ #category : #accessing } -PubPubGrammar >> alternativeImages [ - ^ self linkContent -] - -{ #category : #accessing } -PubPubGrammar >> document [ - ^ (link / imageLink ) islandInSea star -] - -{ #category : #links } -PubPubGrammar >> imageLink [ - ^ imageLinkLabel, imageLinkContent, alternativeImages -] - -{ #category : #links } -PubPubGrammar >> imageLinkContent [ - ^ '(' asPParser, #any asPParser starLazy flatten, ')' asPParser ==> #second -] - -{ #category : #links } -PubPubGrammar >> imageLinkLabel [ - ^ '![' asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second -] - -{ #category : #accessing } -PubPubGrammar >> imageLinkSea [ - ^ imageLink sea ==> #second -] - -{ #category : #links } -PubPubGrammar >> link [ - ^ linkLabel, linkContent -] - -{ #category : #links } -PubPubGrammar >> linkContent [ - ^ '{' asPParser, #any asPParser starLazy flatten, '}' asPParser ==> #second. -] - -{ #category : #links } -PubPubGrammar >> linkLabel [ - ^ $[ asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second. -] - -{ #category : #accessing } -PubPubGrammar >> linkSea [ - ^ link sea ==> #second -] - -{ #category : #accessing } -PubPubGrammar >> start [ - ^ document -] +Class { + #name : #PubPubGrammar, + #superclass : #PP2CompositeNode, + #instVars : [ + 'document', + 'link', + 'linkLabel', + 'linkContent', + 'imageLinkLabel', + 'imageLinkContent', + 'alternativeImages', + 'imageLink' + ], + #category : #'MiniDocs-Model' +} + +{ #category : #accessing } +PubPubGrammar >> alternativeImages [ + ^ self linkContent +] + +{ #category : #accessing } +PubPubGrammar >> document [ + ^ (link / imageLink ) islandInSea star +] + +{ #category : #links } +PubPubGrammar >> imageLink [ + ^ imageLinkLabel, imageLinkContent, alternativeImages +] + +{ #category : #links } +PubPubGrammar >> imageLinkContent [ + ^ '(' asPParser, #any asPParser starLazy flatten, ')' asPParser ==> #second +] + +{ #category : #links } +PubPubGrammar >> imageLinkLabel [ + ^ '![' asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second +] + +{ #category : #accessing } +PubPubGrammar >> imageLinkSea [ + ^ imageLink sea ==> #second +] + +{ #category : #links } +PubPubGrammar >> link [ + ^ linkLabel, linkContent +] + +{ #category : #links } +PubPubGrammar >> linkContent [ + ^ '{' asPParser, #any asPParser starLazy flatten, '}' asPParser ==> #second. +] + +{ #category : #links } +PubPubGrammar >> linkLabel [ + ^ $[ asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second. +] + +{ #category : #accessing } +PubPubGrammar >> linkSea [ + ^ link sea ==> #second +] + +{ #category : #accessing } +PubPubGrammar >> start [ + ^ document +] diff --git a/src/MiniDocs/PubPubGrammarTest.class.st b/src/MiniDocs/PubPubGrammarTest.class.st index 1f9c3ef..17e0bbf 100644 --- a/src/MiniDocs/PubPubGrammarTest.class.st +++ b/src/MiniDocs/PubPubGrammarTest.class.st @@ -1,24 +1,24 @@ -Class { - #name : #PubPubGrammarTest, - #superclass : #PP2CompositeNodeTest, - #category : #'MiniDocs-Model' -} - -{ #category : #accessing } -PubPubGrammarTest >> parserClass [ - ^ PubPubGrammar -] - -{ #category : #accessing } -PubPubGrammarTest >> testImageLink [ - self - parse: '![This is an image label](this/is/an/image/link){this are alternate image sizes}' - rule: #imageLink -] - -{ #category : #accessing } -PubPubGrammarTest >> testLink [ - self - parse: '[This is a label]{this/is/a/link}' - rule: #link -] +Class { + #name : #PubPubGrammarTest, + #superclass : #PP2CompositeNodeTest, + #category : #'MiniDocs-Model' +} + +{ #category : #accessing } +PubPubGrammarTest >> parserClass [ + ^ PubPubGrammar +] + +{ #category : #accessing } +PubPubGrammarTest >> testImageLink [ + self + parse: '![This is an image label](this/is/an/image/link){this are alternate image sizes}' + rule: #imageLink +] + +{ #category : #accessing } +PubPubGrammarTest >> testLink [ + self + parse: '[This is a label]{this/is/a/link}' + rule: #link +] diff --git a/src/MiniDocs/String.extension.st b/src/MiniDocs/String.extension.st index 021c838..484b1a7 100644 --- a/src/MiniDocs/String.extension.st +++ b/src/MiniDocs/String.extension.st @@ -1,8 +1,8 @@ -Extension { #name : #String } - -{ #category : #'*MiniDocs' } -String >> asDashedLowercase [ - "I convert phrases like 'This is a phrase' into 'this-is-a-phrase'." - - ^ '-' join: (self substrings collect: [:each | each asLowercase ]) -] +Extension { #name : #String } + +{ #category : #'*MiniDocs' } +String >> asDashedLowercase [ + "I convert phrases like 'This is a phrase' into 'this-is-a-phrase'." + + ^ '-' join: (self substrings collect: [:each | each asLowercase ]) +] diff --git a/src/MiniDocs/package.st b/src/MiniDocs/package.st index 34758ac..1ca7ecf 100644 --- a/src/MiniDocs/package.st +++ b/src/MiniDocs/package.st @@ -1 +1 @@ -Package { #name : #MiniDocs } +Package { #name : #MiniDocs }