Fixing data folder for Windows.

This commit is contained in:
Offray Vladimir Luna Cardenas 2022-11-03 16:19:56 -05:00
parent 77404b9b31
commit 06b18c4e50
19 changed files with 1628 additions and 1625 deletions

View File

@ -1,164 +1,164 @@
Extension { #name : #LeDatabase } Extension { #name : #LeDatabase }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [ LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets snippets page | | remoteMetadata divSnippets snippets page |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ]. collect: [ :xmlElement | xmlElement postCopy ].
snippets := divSnippets snippets := divSnippets
collect: [ :xmlElement | collect: [ :xmlElement |
(xmlElement attributes at: 'st-class') = 'LeTextSnippet' (xmlElement attributes at: 'st-class') = 'LeTextSnippet'
ifTrue: [ LeTextSnippet new contentFrom: xmlElement ] ifTrue: [ LeTextSnippet new contentFrom: xmlElement ]
ifFalse: [ (xmlElement attributes at: 'st-class') = 'LePharoSnippet' ifFalse: [ (xmlElement attributes at: 'st-class') = 'LePharoSnippet'
ifTrue: [ LePharoSnippet new contentFrom: xmlElement ] ] ]. ifTrue: [ LePharoSnippet new contentFrom: xmlElement ] ] ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree. remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
page := LePage new page := LePage new
title: (remoteMetadata at: 'title'); title: (remoteMetadata at: 'title');
basicUid: (UUID fromString36: (remoteMetadata at: 'id')); basicUid: (UUID fromString36: (remoteMetadata at: 'id'));
createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime); createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime);
editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime); latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
createEmail: (LeEmail new email: (remoteMetadata at: 'creator')); createEmail: (LeEmail new email: (remoteMetadata at: 'creator'));
editEmail: (LeEmail new email: (remoteMetadata at: 'modifier')). editEmail: (LeEmail new email: (remoteMetadata at: 'modifier')).
snippets do: [ :snippet | page addSnippet: snippet ]. snippets do: [ :snippet | page addSnippet: snippet ].
page children page children
do: [ :snippet | do: [ :snippet |
(self hasBlockUID: snippet uid) (self hasBlockUID: snippet uid)
ifTrue: [ | existingPage | ifTrue: [ | existingPage |
existingPage := self pages existingPage := self pages
detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ]. detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ].
self importErrorForLocal: existingPage withRemote: externalDocLocation. self importErrorForLocal: existingPage withRemote: externalDocLocation.
^ self ] ^ self ]
ifFalse: [ snippet database: self. ifFalse: [ snippet database: self.
self registerSnippet: snippet ] ]. self registerSnippet: snippet ] ].
self addPage: page. self addPage: page.
^ page ^ page
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> addPageFromMarkdeepUrl: aString [ LeDatabase >> addPageFromMarkdeepUrl: aString [
| page | | page |
page := self detectLocalPageForRemote: aString. page := self detectLocalPageForRemote: aString.
page page
ifNotNil: [ :arg | ifNotNil: [ :arg |
self importErrorForLocal: page withRemote: aString. self importErrorForLocal: page withRemote: aString.
^ self ]. ^ self ].
^ self addPageFromMarkdeep: (self docTreeForLink: aString) withRemote: aString ^ self addPageFromMarkdeep: (self docTreeForLink: aString) withRemote: aString
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> detectLocalPageForRemote: markdeepDocUrl [ LeDatabase >> detectLocalPageForRemote: markdeepDocUrl [
| markdeepHelper id remoteMetadata docTree | | markdeepHelper id remoteMetadata docTree |
markdeepHelper := Markdeep new. markdeepHelper := Markdeep new.
docTree := self docTreeForLink: markdeepDocUrl. docTree := self docTreeForLink: markdeepDocUrl.
remoteMetadata := markdeepHelper metadataFromXML: docTree. remoteMetadata := markdeepHelper metadataFromXML: docTree.
id := remoteMetadata at: 'id' ifAbsent: [ nil ]. id := remoteMetadata at: 'id' ifAbsent: [ nil ].
^ self pageWithID: id ifAbsent: [ ^ nil ]. ^ self pageWithID: id ifAbsent: [ ^ nil ].
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> docTreeForLink: aString [ LeDatabase >> docTreeForLink: aString [
^ (XMLHTMLParser on: aString asUrl retrieveContents) parseDocument ^ (XMLHTMLParser on: aString asUrl retrieveContents) parseDocument
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> errorCardFor: error [ LeDatabase >> errorCardFor: error [
| keepButton overwriteButton backupButton errorMessageUI localPage errorKey | | keepButton overwriteButton backupButton errorMessageUI localPage errorKey |
errorKey := error keys first. errorKey := error keys first.
localPage := self pageWithID: errorKey. localPage := self pageWithID: errorKey.
keepButton := BrButton new keepButton := BrButton new
aptitude: BrGlamorousButtonWithIconAndLabelAptitude; aptitude: BrGlamorousButtonWithIconAndLabelAptitude;
label: 'Keep existing local page'; label: 'Keep existing local page';
icon: BrGlamorousVectorIcons cancel; icon: BrGlamorousVectorIcons cancel;
margin: (BlInsets left: 10); margin: (BlInsets left: 10);
action: [ :aButton | action: [ :aButton |
aButton phlow spawnObject: localPage. aButton phlow spawnObject: localPage.
self errors removeKey: errorKey self errors removeKey: errorKey
]. ].
overwriteButton := BrButton new overwriteButton := BrButton new
aptitude: BrGlamorousButtonWithIconAndLabelAptitude; aptitude: BrGlamorousButtonWithIconAndLabelAptitude;
label: 'Overwrite with remote page'; label: 'Overwrite with remote page';
icon: BrGlamorousVectorIcons edit; icon: BrGlamorousVectorIcons edit;
action: [ :aButton | action: [ :aButton |
self removePage: localPage. self removePage: localPage.
aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (error at: errorKey at: 'remote')). aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (error at: errorKey at: 'remote')).
self errors removeKey: errorKey self errors removeKey: errorKey
]; ];
margin: (BlInsets left: 10). margin: (BlInsets left: 10).
backupButton := BrButton new backupButton := BrButton new
aptitude: BrGlamorousButtonWithIconAndLabelAptitude; aptitude: BrGlamorousButtonWithIconAndLabelAptitude;
label: 'Backup local page'; label: 'Backup local page';
icon: BrGlamorousVectorIcons changes; icon: BrGlamorousVectorIcons changes;
action: [ :aButton | ]; action: [ :aButton | ];
margin: (BlInsets left: 10). margin: (BlInsets left: 10).
errorMessageUI := BrEditor new errorMessageUI := BrEditor new
aptitude: BrGlamorousRegularEditorAptitude new ; aptitude: BrGlamorousRegularEditorAptitude new ;
text: (error at: errorKey at: 'message'); text: (error at: errorKey at: 'message');
vFitContent. vFitContent.
^ { errorMessageUI. keepButton. overwriteButton. backupButton } ^ { errorMessageUI. keepButton. overwriteButton. backupButton }
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> errors [ LeDatabase >> errors [
^ self optionAt: 'errors' ifAbsentPut: [ Dictionary new ] ^ self optionAt: 'errors' ifAbsentPut: [ Dictionary new ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> gtViewErrorDetailsOn: aView [ LeDatabase >> gtViewErrorDetailsOn: aView [
<gtView> <gtView>
^ aView explicit ^ aView explicit
title: 'Errors' translated; title: 'Errors' translated;
priority: 5; priority: 5;
stencil: [ | container | stencil: [ | container |
container := BlElement new container := BlElement new
layout: BlFlowLayout new; layout: BlFlowLayout new;
constraintsDo: [ :c | constraintsDo: [ :c |
c vertical fitContent. c vertical fitContent.
c horizontal matchParent ]; c horizontal matchParent ];
padding: (BlInsets all: 10). padding: (BlInsets all: 10).
container container
addChildren: (self errorCardFor: self errors) addChildren: (self errorCardFor: self errors)
]. ].
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [ LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [
| message id error | | message id error |
id := page uidString. id := page uidString.
message := String streamContents: [ :stream | message := String streamContents: [ :stream |
stream stream
nextPutAll: 'IMPORTATION ERROR: A page with nextPutAll: 'IMPORTATION ERROR: A page with
'; ';
nextPut: Character lf; nextPut: Character lf;
nextPutAll: ' id: ' , id; nextPutAll: ' id: ' , id;
nextPut: Character lf; nextPut: Character lf;
nextPutAll: ' title: ' , page contentAsString; nextPutAll: ' title: ' , page contentAsString;
nextPut: Character lf; nextPut: Character lf;
nextPut: Character lf; nextPut: Character lf;
nextPutAll: 'already exists in this database and includes overlapping contents'; nextPutAll: 'already exists in this database and includes overlapping contents';
nextPut: Character lf; nextPut: Character lf;
nextPutAll: 'with the page you are trying to import from: nextPutAll: 'with the page you are trying to import from:
'; ';
nextPut: Character lf; nextPut: Character lf;
nextPutAll: externalDocLocation; nextPutAll: externalDocLocation;
nextPut: Character lf; nextPut: Character lf;
nextPut: Character lf; nextPut: Character lf;
nextPutAll: nextPutAll:
'Please choose one of the following options to addres the issue: 'Please choose one of the following options to addres the issue:
' ]. ' ].
error := Dictionary new error := Dictionary new
at: 'remote' put: externalDocLocation; at: 'remote' put: externalDocLocation;
at: 'message' put: message ; at: 'message' put: message ;
yourself. yourself.
self errors at: id put: error self errors at: id put: error
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> options [ LeDatabase >> options [
^ options ^ options
] ]

View File

@ -1,218 +1,218 @@
Extension { #name : #LePage } Extension { #name : #LePage }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asHtmlFile [ LePage >> asHtmlFile [
self asMarkdownFile. self asMarkdownFile.
self defaultPandocTemplate exists self defaultPandocTemplate exists
ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ]. ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ].
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'pandoc' ; command: 'pandoc' ;
arguments: { arguments: {
self markdownFileName. '-o'. self htmlFileName . self markdownFileName. '-o'. self htmlFileName .
'--toc' . '--toc' .
'--template=', self defaultPandocTemplate basenameWithoutExtension }; '--template=', self defaultPandocTemplate basenameWithoutExtension };
workingDirectory: self storage fullName; workingDirectory: self storage fullName;
runAndWaitOnExitDo: [ :process :outString | ^ self storage / self htmlFileName]. runAndWaitOnExitDo: [ :process :outString | ^ self storage / self htmlFileName].
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdeep [ LePage >> asMarkdeep [
| bodyStream markdeep | | bodyStream markdeep |
bodyStream := '' writeStream. bodyStream := '' writeStream.
self preorderTraversal do: [:snippet | self preorderTraversal do: [:snippet |
bodyStream nextPutAll: snippet asMarkdeep bodyStream nextPutAll: snippet asMarkdeep
]. ].
markdeep := Markdeep new markdeep := Markdeep new
title: self title; title: self title;
body: bodyStream contents; body: bodyStream contents;
navTop: self navTop. navTop: self navTop.
self metadata keysAndValuesDo: [:k :v | self metadata keysAndValuesDo: [:k :v |
k = 'lang' k = 'lang'
ifTrue: [ ifTrue: [
markdeep head markdeep head
add: '<meta lang="', v,'">'; add: '<meta lang="', v,'">';
yourself. yourself.
] ]
ifFalse: [ ifFalse: [
markdeep head markdeep head
add: '<meta name="', k, '" content="', v,'">'; add: '<meta name="', k, '" content="', v,'">';
yourself. yourself.
] ]
]. ].
self metadata at: 'authors' ifPresent: [:author | markdeep metadata at: 'authors' put: author ]. self metadata at: 'authors' ifPresent: [:author | markdeep metadata at: 'authors' put: author ].
self metadata at: 'version' ifPresent: [:version | markdeep metadata at: 'version' put: version ]. self metadata at: 'version' ifPresent: [:version | markdeep metadata at: 'version' put: version ].
^ markdeep. ^ markdeep.
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdeepFile [ LePage >> asMarkdeepFile [
| folder | | folder |
folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. folder := self options at: 'storage' ifAbsent: [ FileLocator temp ].
^ self asMarkdeep exportAsFileOn: folder / self markdeepFileName ^ self asMarkdeep exportAsFileOn: folder / self markdeepFileName
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdown [ LePage >> asMarkdown [
| bodyStream markdown | | bodyStream markdown |
bodyStream := '' writeStream. bodyStream := '' writeStream.
bodyStream bodyStream
nextPutAll: '---'; nextPutAll: '---';
nextPutAll: String lf. nextPutAll: String lf.
self metadata keysAndValuesDo: [ :k :v | self metadata keysAndValuesDo: [ :k :v |
bodyStream bodyStream
nextPutAll: k , ': "' , v, '"'; nextPutAll: k , ': "' , v, '"';
nextPutAll: String lf ]. nextPutAll: String lf ].
bodyStream nextPutAll: '---' , String lf , String lf. bodyStream nextPutAll: '---' , String lf , String lf.
self preorderTraversal self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ]. do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ].
markdown := Markdown new contents: bodyStream contents. markdown := Markdown new contents: bodyStream contents.
^ markdown ^ markdown
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdownFile [ LePage >> asMarkdownFile [
| folder | | folder |
folder := self options at: 'storage' ifAbsent: [ FileLocator temp ]. folder := self options at: 'storage' ifAbsent: [ FileLocator temp ].
^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdown contents ^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdown contents
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> defaultPandocTemplate [ LePage >> defaultPandocTemplate [
^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html' ^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html'
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> detectParentSnippetWithUid: uidString [ LePage >> detectParentSnippetWithUid: uidString [
"Answer a boolean indicating whether the supplied uid is present" "Answer a boolean indicating whether the supplied uid is present"
^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ] ifNone: [ ^ self ] ^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ] ifNone: [ ^ self ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> exportedFileName [ LePage >> exportedFileName [
| sanitized | | sanitized |
sanitized := self title asDashedLowercase copyWithoutAll: #($/). sanitized := self title asDashedLowercase copyWithoutAll: #($/).
^ sanitized , '--' , (self uidString copyFrom: 1 to: 5) ^ sanitized , '--' , (self uidString copyFrom: 1 to: 5)
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> fromMarkdeepUrl: aString [ LePage >> fromMarkdeepUrl: aString [
| docTree pageMetadata | | docTree pageMetadata |
docTree := GrafoscopioUtils xmlFromUrl: aString. docTree := GrafoscopioUtils xmlFromUrl: aString.
pageMetadata := Markdeep new metadataFromXML: docTree. pageMetadata := Markdeep new metadataFromXML: docTree.
self self
basicUid: (pageMetadata at: 'id'); basicUid: (pageMetadata at: 'id');
title: (pageMetadata at: 'title'); title: (pageMetadata at: 'title');
createTime: (pageMetadata at: 'created') asDateAndTime; createTime: (pageMetadata at: 'created') asDateAndTime;
editTime: (pageMetadata at: 'modified') asDateAndTime; editTime: (pageMetadata at: 'modified') asDateAndTime;
createEmail: (pageMetadata at: 'creator'); createEmail: (pageMetadata at: 'creator');
editEmail: (pageMetadata at: 'modifier'); editEmail: (pageMetadata at: 'modifier');
optionAt: 'metadata' put: pageMetadata. optionAt: 'metadata' put: pageMetadata.
self populateChildrenFrom: (docTree xpath: '//div') self populateChildrenFrom: (docTree xpath: '//div')
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> htmlFileName [ LePage >> htmlFileName [
^ self exportedFileName, '.html' ^ self exportedFileName, '.html'
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> latestEditTime: aLeTime [ LePage >> latestEditTime: aLeTime [
"Used for adding a LePage to database from a shared markdeep LePage version." "Used for adding a LePage to database from a shared markdeep LePage version."
latestEditTime := aLeTime latestEditTime := aLeTime
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> markdeepFileName [ LePage >> markdeepFileName [
^ self markdownFileName , '.html' ^ self markdownFileName , '.html'
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> markdownFileName [ LePage >> markdownFileName [
^ self exportedFileName, '.md' ^ self exportedFileName, '.md'
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> metadata [ LePage >> metadata [
^ self options at: 'metadata' ifAbsentPut: [ self metadataInit] ^ self options at: 'metadata' ifAbsentPut: [ self metadataInit]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> metadataInit [ LePage >> metadataInit [
^ OrderedDictionary new ^ OrderedDictionary new
at: 'id' put: self uidString; at: 'id' put: self uidString;
at: 'title' put: self contentAsString; at: 'title' put: self contentAsString;
at: 'created' put: self createTime greaseString; at: 'created' put: self createTime greaseString;
at: 'modified' put: self latestEditTime greaseString; at: 'modified' put: self latestEditTime greaseString;
at: 'creator' put: self createEmail greaseString; at: 'creator' put: self createEmail greaseString;
at: 'modifier' put: self editEmail greaseString; at: 'modifier' put: self editEmail greaseString;
yourself yourself
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> navTop [ LePage >> navTop [
| topNavFile | | topNavFile |
topNavFile := ((self optionAt: 'storage' ifAbsentPut: [ FileLocator temp ]) / '_navtop.html'). topNavFile := ((self optionAt: 'storage' ifAbsentPut: [ FileLocator temp ]) / '_navtop.html').
topNavFile exists topNavFile exists
ifFalse: [ ^ '' ] ifFalse: [ ^ '' ]
ifTrue: [ ^ topNavFile contents ] ifTrue: [ ^ topNavFile contents ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> options [ LePage >> options [
^ options ^ options
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> preorderTraversal [ LePage >> preorderTraversal [
| output | | output |
output := OrderedCollection new. output := OrderedCollection new.
self withDeepCollect: [:each | each allChildrenBreadthFirstDo: [:child | output add: child]]. self withDeepCollect: [:each | each allChildrenBreadthFirstDo: [:child | output add: child]].
^ output. ^ output.
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> removeSnippetsMetadata [ LePage >> removeSnippetsMetadata [
self preorderTraversal do: [ :snippet | self preorderTraversal do: [ :snippet |
(snippet options isNotNil and: [ snippet options includesKey: 'metadata' ]) (snippet options isNotNil and: [ snippet options includesKey: 'metadata' ])
ifTrue: [ snippet options removeKey: 'metadata' ] ] ifTrue: [ snippet options removeKey: 'metadata' ] ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> sharedVariablesBindings [ LePage >> sharedVariablesBindings [
| codeSnippets shared | | codeSnippets shared |
codeSnippets := self preorderTraversal select: [:snippet | codeSnippets := self preorderTraversal select: [:snippet |
snippet class = LePharoSnippet and: [ snippet code includesSubstring: ':='] snippet class = LePharoSnippet and: [ snippet code includesSubstring: ':=']
]. ].
codeSnippets first in: [:snippet | | context | codeSnippets first in: [:snippet | | context |
context := snippet coder evaluationContext. context := snippet coder evaluationContext.
snippet coder doItInContext: context. snippet coder doItInContext: context.
shared := context bindingStrategy bindings detect: [:each | shared := context bindingStrategy bindings detect: [:each |
each isKindOf: GtSharedVariablesBindings each isKindOf: GtSharedVariablesBindings
] ]
]. ].
codeSnippets asArray allButFirstDo: [:snippet| | context| codeSnippets asArray allButFirstDo: [:snippet| | context|
context := snippet coder evaluationContext. context := snippet coder evaluationContext.
context addBindings: shared. context addBindings: shared.
snippet coder doItInContext: context snippet coder doItInContext: context
]. ].
^ shared asDictionary ^ shared asDictionary
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> storage [ LePage >> storage [
^ self optionAt: 'storage' ^ self optionAt: 'storage'
ifAbsent: [ ^ FileLocator temp ] ifAbsent: [ ^ FileLocator temp ]
] ]

View File

@ -1,15 +1,15 @@
Extension { #name : #LePageHeaderBuilder } Extension { #name : #LePageHeaderBuilder }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePageHeaderBuilder >> addExportPageButton [ LePageHeaderBuilder >> addExportPageButton [
<leHeaderAction> <leHeaderAction>
| newButton | | newButton |
newButton := BrButton new newButton := BrButton new
aptitude: BrGlamorousButtonWithIconAptitude; aptitude: BrGlamorousButtonWithIconAptitude;
label: 'Export Page'; label: 'Export Page';
icon: BrGlamorousVectorIcons down; icon: BrGlamorousVectorIcons down;
action: [ :aButton | action: [ :aButton |
aButton phlow spawnObject: self page asMarkdeepFile ]. aButton phlow spawnObject: self page asMarkdeepFile ].
self toolbarElement addItem: newButton. self toolbarElement addItem: newButton.
] ]

View File

@ -1,64 +1,64 @@
Extension { #name : #LePharoSnippet } Extension { #name : #LePharoSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> contentAsStringCustomized [ LePharoSnippet >> contentAsStringCustomized [
| thisObject | | thisObject |
(self tags includes: 'output') ifFalse: [ ^ self contentAsString ]. (self tags includes: 'output') ifFalse: [ ^ self contentAsString ].
thisObject := ((self page sharedVariablesBindings) at: self detectObject) value. thisObject := ((self page sharedVariablesBindings) at: self detectObject) value.
^ thisObject perform: self detectMessage trimmed asSymbol. ^ thisObject perform: self detectMessage trimmed asSymbol.
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> contentFrom: markdeepDiv [ LePharoSnippet >> contentFrom: markdeepDiv [
| sanitizedStringText metadata joinedText | | sanitizedStringText metadata joinedText |
metadata := STON fromString: (markdeepDiv attributes at: 'st-data'). metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
sanitizedStringText := markdeepDiv contentString lines. sanitizedStringText := markdeepDiv contentString lines.
sanitizedStringText := sanitizedStringText copyFrom: 4 to: sanitizedStringText size -2. sanitizedStringText := sanitizedStringText copyFrom: 4 to: sanitizedStringText size -2.
joinedText := '' writeStream. joinedText := '' writeStream.
sanitizedStringText do: [ :line | joinedText nextPutAll: line; nextPut: Character lf ]. sanitizedStringText do: [ :line | joinedText nextPutAll: line; nextPut: Character lf ].
self code: joinedText contents allButLast; self code: joinedText contents allButLast;
uid: (LeUID new uidString: (metadata at: 'id')); uid: (LeUID new uidString: (metadata at: 'id'));
parent: (metadata at: 'parent'); parent: (metadata at: 'parent');
createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime)); createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime));
editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime)); editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime));
editEmail: (metadata at: 'modifier'); editEmail: (metadata at: 'modifier');
createEmail: (metadata at: 'creator') createEmail: (metadata at: 'creator')
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> markdeepCustomCloser [ LePharoSnippet >> markdeepCustomCloser [
^ String streamContents: [ :stream | ^ String streamContents: [ :stream |
stream stream
nextPutAll: '~~~'; lf; nextPutAll: '~~~'; lf;
nextPutAll: '</script>'; lf. nextPutAll: '</script>'; lf.
] ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> markdeepCustomOpener [ LePharoSnippet >> markdeepCustomOpener [
^ String streamContents: [ :stream | ^ String streamContents: [ :stream |
stream stream
nextPutAll: '<script type="preformatted">'; lf; nextPutAll: '<script type="preformatted">'; lf;
nextPutAll: '~~~ Smalltalk'; lf nextPutAll: '~~~ Smalltalk'; lf
] ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> markdownCustomCloser [ LePharoSnippet >> markdownCustomCloser [
(self tags includes: 'output') ifTrue: [^ String with: Character lf]. (self tags includes: 'output') ifTrue: [^ String with: Character lf].
^ String streamContents: [:stream | ^ String streamContents: [:stream |
stream stream
nextPutAll: '~~~'; lf nextPutAll: '~~~'; lf
] ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> markdownCustomOpener [ LePharoSnippet >> markdownCustomOpener [
(self tags includes: 'output') ifTrue: [ ^ String with: Character lf ]. (self tags includes: 'output') ifTrue: [ ^ String with: Character lf ].
^ String ^ String
streamContents: [ :stream | streamContents: [ :stream |
stream stream
nextPutAll: '~~~ Smalltalk'; nextPutAll: '~~~ Smalltalk';
lf ] lf ]
] ]

View File

@ -1,15 +1,15 @@
Extension { #name : #LePictureSnippet } Extension { #name : #LePictureSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePictureSnippet >> asMarkdeep [ LePictureSnippet >> asMarkdeep [
| output | | output |
output := WriteStream on: ''. output := WriteStream on: ''.
output output
nextPutAll: self metadataDiv; nextPutAll: self metadataDiv;
nextPutAll: self centeredFigure; nextPutAll: self centeredFigure;
nextPut: Character lf; nextPut: Character lf;
nextPutAll: '</div>'; nextPutAll: '</div>';
nextPut: Character lf; nextPut: Character lf;
nextPut: Character lf. nextPut: Character lf.
^ output contents ^ output contents
] ]

View File

@ -1,12 +1,12 @@
Extension { #name : #LeSnippet } Extension { #name : #LeSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeSnippet class >> fromMetaMarkdeep: div [ LeSnippet class >> fromMetaMarkdeep: div [
| className metadata snippet | | className metadata snippet |
className := (div xpath: '@st-class') stringValue. className := (div xpath: '@st-class') stringValue.
metadata := STON fromString:(div xpath: '@st-data') stringValue. metadata := STON fromString:(div xpath: '@st-data') stringValue.
snippet := className asClass new. snippet := className asClass new.
snippet injectMetadataFrom: metadata. snippet injectMetadataFrom: metadata.
snippet contentFrom: div. snippet contentFrom: div.
^ snippet. ^ snippet.
] ]

View File

@ -1,54 +1,54 @@
Extension { #name : #LeTextSnippet } Extension { #name : #LeTextSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> contentFrom: markdeepDiv [ LeTextSnippet >> contentFrom: markdeepDiv [
| sanitizedStringText metadata | | sanitizedStringText metadata |
metadata := STON fromString: (markdeepDiv attributes at: 'st-data'). metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
sanitizedStringText := markdeepDiv contentString. sanitizedStringText := markdeepDiv contentString.
sanitizedStringText := sanitizedStringText allButFirst. sanitizedStringText := sanitizedStringText allButFirst.
sanitizedStringText := sanitizedStringText allButLast. sanitizedStringText := sanitizedStringText allButLast.
self string: sanitizedStringText; self string: sanitizedStringText;
uid: (LeUID new uidString: (metadata at: 'id')); uid: (LeUID new uidString: (metadata at: 'id'));
parent: (metadata at: 'parent'); parent: (metadata at: 'parent');
createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime)); createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime));
editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime)); editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime));
editEmail: (metadata at: 'modifier'); editEmail: (metadata at: 'modifier');
createEmail: (metadata at: 'creator') createEmail: (metadata at: 'creator')
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> metadata [ LeTextSnippet >> metadata [
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ] ^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> metadataInit [ LeTextSnippet >> metadataInit [
^ OrderedDictionary new ^ OrderedDictionary new
at: 'id' put: self uidString; at: 'id' put: self uidString;
at: 'parent' put: self parentId; at: 'parent' put: self parentId;
at: 'created' put: self createTime asString; at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString; at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString; at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString; at: 'modifier' put: self editEmail asString;
yourself yourself
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> options [ LeTextSnippet >> options [
^ options ^ options
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> parentId [ LeTextSnippet >> parentId [
self parent ifNil: [ ^ self ]. self parent ifNil: [ ^ self ].
^ self parent uidString. ^ self parent uidString.
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> taggedWith: aString [ LeTextSnippet >> taggedWith: aString [
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ]. self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
^ self metadata at: 'tags' ^ self metadata at: 'tags'
] ]

View File

@ -1,91 +1,91 @@
Extension { #name : #LeTextualSnippet } Extension { #name : #LeTextualSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdeep [ LeTextualSnippet >> asMarkdeep [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use "Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents." 'st-' properties as a way to extend divs metadata regarding its contents."
| output | | output |
output := WriteStream on: ''. output := WriteStream on: ''.
output output
nextPutAll: '<div st-class="' , self class greaseString , '"'; nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf; nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">'; nextPutAll: ' st-data="' , (STON toString: self metadata) , '">';
nextPut: Character lf; nextPut: Character lf;
nextPutAll: self markdeepCustomOpener; nextPutAll: self markdeepCustomOpener;
nextPutAll: self contentAsString; nextPutAll: self contentAsString;
nextPut: Character lf; nextPut: Character lf;
nextPutAll: self markdeepCustomCloser; nextPutAll: self markdeepCustomCloser;
nextPutAll: '</div>'; nextPutAll: '</div>';
nextPut: Character lf; nextPut: Character lf;
nextPut: Character lf. nextPut: Character lf.
^ output contents ^ output contents
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdown [ LeTextualSnippet >> asMarkdown [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use "Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents." 'st-' properties as a way to extend divs metadata regarding its contents."
| output | | output |
output := '' writeStream. output := '' writeStream.
output output
nextPutAll: '<div st-class="', self class asString, '"'; lf; nextPutAll: '<div st-class="', self class asString, '"'; lf;
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf; nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
nextPutAll: self markdownCustomOpener; nextPutAll: self markdownCustomOpener;
nextPutAll: self contentAsStringCustomized; lf; nextPutAll: self contentAsStringCustomized; lf;
nextPutAll: self markdownCustomCloser; nextPutAll: self markdownCustomCloser;
nextPutAll: '</div>'; lf; lf. nextPutAll: '</div>'; lf; lf.
^ output contents ^ output contents
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> contentAsStringCustomized [ LeTextualSnippet >> contentAsStringCustomized [
^ self contentAsString ^ self contentAsString
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> markdeepCustomCloser [ LeTextualSnippet >> markdeepCustomCloser [
^ '' ^ ''
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> markdeepCustomOpener [ LeTextualSnippet >> markdeepCustomOpener [
^ '' ^ ''
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> markdownCustomCloser [ LeTextualSnippet >> markdownCustomCloser [
^ self markdeepCustomCloser ^ self markdeepCustomCloser
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> markdownCustomOpener [ LeTextualSnippet >> markdownCustomOpener [
^ self markdeepCustomOpener ^ self markdeepCustomOpener
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> metadata [ LeTextualSnippet >> metadata [
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ] ^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> metadataInit [ LeTextualSnippet >> metadataInit [
| surrogate | | surrogate |
self parent self parent
ifNil: [ surrogate := nil] ifNil: [ surrogate := nil]
ifNotNil: [ surrogate := self parent uidString ]. ifNotNil: [ surrogate := self parent uidString ].
^ OrderedDictionary new ^ OrderedDictionary new
at: 'id' put: self uidString; at: 'id' put: self uidString;
at: 'parent' put: surrogate; at: 'parent' put: surrogate;
at: 'created' put: self createTime asString; at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString; at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString; at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString; at: 'modifier' put: self editEmail asString;
yourself yourself
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> tags [ LeTextualSnippet >> tags [
^ self metadata at: 'tags' ifAbsentPut: [ Set new ] ^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
] ]

View File

@ -1,23 +1,23 @@
" "
Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser
" "
Class { Class {
#name : #ManifestMiniDocs, #name : #ManifestMiniDocs,
#superclass : #PackageManifest, #superclass : #PackageManifest,
#category : #'MiniDocs-Manifest' #category : #'MiniDocs-Manifest'
} }
{ #category : #'code-critics' } { #category : #'code-critics' }
ManifestMiniDocs class >> ruleCascadedNextPutAllsRuleV1FalsePositive [ ManifestMiniDocs class >> ruleCascadedNextPutAllsRuleV1FalsePositive [
^ #(#(#(#RGMethodDefinition #(#LeTextualSnippet #asMarkdeep #false)) #'2022-09-09T12:31:08.106585-05:00') ) ^ #(#(#(#RGMethodDefinition #(#LeTextualSnippet #asMarkdeep #false)) #'2022-09-09T12:31:08.106585-05:00') )
] ]
{ #category : #'code-critics' } { #category : #'code-critics' }
ManifestMiniDocs class >> ruleExcessiveVariablesRuleV1FalsePositive [ ManifestMiniDocs class >> ruleExcessiveVariablesRuleV1FalsePositive [
^ #(#(#(#RGClassDefinition #(#Markdeep)) #'2022-07-16T12:24:34.695032-05:00') ) ^ #(#(#(#RGClassDefinition #(#Markdeep)) #'2022-07-16T12:24:34.695032-05:00') )
] ]
{ #category : #'code-critics' } { #category : #'code-critics' }
ManifestMiniDocs class >> ruleParseTreeLintRuleV1FalsePositive [ ManifestMiniDocs class >> ruleParseTreeLintRuleV1FalsePositive [
^ #(#(#(#RGPackageDefinition #(#MiniDocs)) #'2022-07-25T09:28:50.156394-05:00') ) ^ #(#(#(#RGPackageDefinition #(#MiniDocs)) #'2022-07-25T09:28:50.156394-05:00') )
] ]

View File

@ -1,473 +1,473 @@
" "
I model a Mardeep file as described in https://casual-effects.com/markdeep/ I model a Mardeep file as described in https://casual-effects.com/markdeep/
" "
Class { Class {
#name : #Markdeep, #name : #Markdeep,
#superclass : #Object, #superclass : #Object,
#instVars : [ #instVars : [
'title', 'title',
'body', 'body',
'comments', 'comments',
'tail', 'tail',
'language', 'language',
'config', 'config',
'metadata', 'metadata',
'head', 'head',
'navTop', 'navTop',
'options' 'options'
], ],
#category : #MiniDocs #category : #MiniDocs
} }
{ #category : #'as yet unclassified' } { #category : #'as yet unclassified' }
Markdeep class >> fromMarkdownFile: aFileReference [ Markdeep class >> fromMarkdownFile: aFileReference [
^ self new fromMarkdownFile: aFileReference. ^ self new fromMarkdownFile: aFileReference.
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep class >> fromPubPubTOC: orderedDictionary folder: folder index: ordinalPossitive [ Markdeep class >> fromPubPubTOC: orderedDictionary folder: folder index: ordinalPossitive [
| contentSection testFile | | contentSection testFile |
contentSection := orderedDictionary associations at: ordinalPossitive. contentSection := orderedDictionary associations at: ordinalPossitive.
testFile := folder / (contentSection key,'--', contentSection value),'md'. testFile := folder / (contentSection key,'--', contentSection value),'md'.
^ self new fromMarkdownFile: testFile. ^ self new fromMarkdownFile: testFile.
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> authors [ Markdeep >> authors [
self metadata at: 'authors' ifPresent: [:k | ^ '**', k, '**' ]. self metadata at: 'authors' ifPresent: [:k | ^ '**', k, '**' ].
^ ''. ^ ''.
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> authorsString [ Markdeep >> authorsString [
self authors self authors
ifNil: [ ^ '' ] ifNotNil: [ ^ ' ', self authors ] ifNil: [ ^ '' ] ifNotNil: [ ^ ' ', self authors ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> body [ Markdeep >> body [
^ body ^ body
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> body: anObject [ Markdeep >> body: anObject [
body := anObject body := anObject
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> bodyReplaceAll: original with: replacement [ Markdeep >> bodyReplaceAll: original with: replacement [
self body: (self body copyReplaceAll: original with: replacement) self body: (self body copyReplaceAll: original with: replacement)
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> commentPubPubDelimiters [ Markdeep >> commentPubPubDelimiters [
| commented openners | | commented openners |
openners := #('::: {.pub-body-component}' '::: {.editor .Prosemirror}' '::: {.pub-notes}'). openners := #('::: {.pub-body-component}' '::: {.editor .Prosemirror}' '::: {.pub-notes}').
commented := self body. commented := self body.
openners do: [:openner | openners do: [:openner |
commented := commented copyReplaceAll: openner with: '<!--@div-open ', openner, '-->' commented := commented copyReplaceAll: openner with: '<!--@div-open ', openner, '-->'
]. ].
commented := commented commented := commented
copyReplaceAll: '::: copyReplaceAll: ':::
' with: '<!--@div-close ::: --> ' with: '<!--@div-close ::: -->
'. '.
self body: commented self body: commented
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> comments [ Markdeep >> comments [
^ comments ifNil: [ ^ comments := true ] ^ comments ifNil: [ ^ comments := true ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> comments: aBoolean [ Markdeep >> comments: aBoolean [
"I tell if comments are enabled by default or not." "I tell if comments are enabled by default or not."
comments := aBoolean comments := aBoolean
] ]
{ #category : #utilities } { #category : #utilities }
Markdeep >> commentsProvider [ Markdeep >> commentsProvider [
"I return the url of the default service that provides annotation support. "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." I am used to add such support in the contents of the Markdeep page."
^ 'https://hypothes.is' ^ 'https://hypothes.is'
] ]
{ #category : #utilities } { #category : #utilities }
Markdeep >> commentsProviderStrings [ Markdeep >> commentsProviderStrings [
"I associate a comments service provider with the string that is required to be added "I associate a comments service provider with the string that is required to be added
to the document to enable such provider." to the document to enable such provider."
| providers | | providers |
providers := Dictionary new. providers := Dictionary new.
providers at: 'https://hypothes.is' put: '<!-- Hypothesis --> providers at: 'https://hypothes.is' put: '<!-- Hypothesis -->
<script src="https://hypothes.is/embed.js" async></script>'. <script src="https://hypothes.is/embed.js" async></script>'.
^ providers ^ providers
] ]
{ #category : #utilities } { #category : #utilities }
Markdeep >> commentsSupport [ Markdeep >> commentsSupport [
"I enable comments of the page." "I enable comments of the page."
self comments ifFalse: [ ^ self ]. self comments ifFalse: [ ^ self ].
^ self commentsProviderStrings at: self commentsProvider ^ self commentsProviderStrings at: self commentsProvider
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> config [ Markdeep >> config [
^ config ifNil: [ config := Dictionary new] ^ config ifNil: [ config := Dictionary new]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> config: aDictionary [ Markdeep >> config: aDictionary [
config := aDictionary config := aDictionary
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> contents [ Markdeep >> contents [
| output | | output |
self title ifNil: [ ^ self body ]. self title ifNil: [ ^ self body ].
output := '' writeStream. output := '' writeStream.
output output
nextPutAll: self headContents; lf; lf; nextPutAll: self headContents; lf; lf;
nextPutAll: ' **', self title, '**'; lf; nextPutAll: ' **', self title, '**'; lf;
nextPutAll: self authorsString ; lf; nextPutAll: self authorsString ; lf;
nextPutAll: ' ', self version; lf; nextPutAll: ' ', self version; lf;
nextPutAll: self navTop; lf; lf; nextPutAll: self navTop; lf; lf;
nextPutAll: self body; lf; lf; nextPutAll: self body; lf; lf;
nextPutAll: self tail; lf; lf; lf; lf; nextPutAll: self tail; lf; lf; lf; lf;
nextPutAll: self commentsSupport. nextPutAll: self commentsSupport.
^ output contents. ^ output contents.
] ]
{ #category : #persistence } { #category : #persistence }
Markdeep >> exportAsFile [ Markdeep >> exportAsFile [
| newFile | | newFile |
self markdownFile ifNil: [ self inform: 'Define an input Markdown file or use #exportAsFileOn: instead.' ]. self markdownFile ifNil: [ self inform: 'Define an input Markdown file or use #exportAsFileOn: instead.' ].
newFile := (self markdownFile file fullName, '.html') asFileReference. newFile := (self markdownFile file fullName, '.html') asFileReference.
^ self exportAsFileOn: newFile. ^ self exportAsFileOn: newFile.
] ]
{ #category : #persistence } { #category : #persistence }
Markdeep >> exportAsFileOn: aFileReference [ Markdeep >> exportAsFileOn: aFileReference [
aFileReference ensureDelete. aFileReference ensureDelete.
aFileReference exists ifFalse: [ aFileReference ensureCreateFile ]. aFileReference exists ifFalse: [ aFileReference ensureCreateFile ].
aFileReference writeStreamDo: [ :stream | aFileReference writeStreamDo: [ :stream |
stream nextPutAll: self contents ]. stream nextPutAll: self contents ].
self inform: 'Exported as: ', String cr, aFileReference fullName. self inform: 'Exported as: ', String cr, aFileReference fullName.
^ aFileReference ^ aFileReference
] ]
{ #category : #utilities } { #category : #utilities }
Markdeep >> fontAwesomeHeader [ Markdeep >> fontAwesomeHeader [
"I enable the font awesome support in the document header" "I enable the font awesome support in the document header"
^ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">' ^ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> fromMarkdownFile: aFileReference [ Markdeep >> fromMarkdownFile: aFileReference [
"I create a Markdeep document from a given Markdown file." "I create a Markdeep document from a given Markdown file."
self processMarkdownFor: aFileReference. self processMarkdownFor: aFileReference.
^ self. ^ self.
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> gtTextFor: aView [ Markdeep >> gtTextFor: aView [
<gtView> <gtView>
^ aView textEditor ^ aView textEditor
title: 'Text'; title: 'Text';
text: [ self contents ] text: [ self contents ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> head [ Markdeep >> head [
^ head ifNil: [ head := OrderedCollection new. ^ head ifNil: [ head := OrderedCollection new.
head add: self fontAwesomeHeader; yourself ] head add: self fontAwesomeHeader; yourself ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> head: anOrderedCollection [ Markdeep >> head: anOrderedCollection [
head := anOrderedCollection head := anOrderedCollection
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> headContents [ Markdeep >> headContents [
^ String streamContents: [ :stream | ^ String streamContents: [ :stream |
stream stream
nextPutAll: '<head>'; nextPutAll: '<head>';
nextPut: Character lf. nextPut: Character lf.
self head do: [ :line | self head do: [ :line |
stream stream
nextPutAll: ' '; nextPutAll: ' ';
nextPutAll: line; nextPutAll: line;
nextPut: Character lf nextPut: Character lf
]. ].
stream stream
nextPutAll: '</head>'; nextPutAll: '</head>';
nextPut: Character lf. nextPut: Character lf.
]. ].
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> language [ Markdeep >> language [
^ language ^ language
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> language: anObject [ Markdeep >> language: anObject [
language := anObject language := anObject
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> markdeepScriptTag [ Markdeep >> markdeepScriptTag [
^ '<script src="markdeep.min.js" charset="utf-8"></script> ^ '<script src="markdeep.min.js" charset="utf-8"></script>
<script <script
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?" src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
charset="utf-8"> charset="utf-8">
</script>' </script>'
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> markdownFile [ Markdeep >> markdownFile [
^ Markdown new fromFile: (self config at: 'markdownFile') ^ Markdown new fromFile: (self config at: 'markdownFile')
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> markdownFile: aFileReference [ Markdeep >> markdownFile: aFileReference [
"Where the Mardown file associated with me is stored. Used for sync. and import/export purposes." "Where the Mardown file associated with me is stored. Used for sync. and import/export purposes."
self config at: 'markdownFile' put: aFileReference self config at: 'markdownFile' put: aFileReference
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> metadata [ Markdeep >> metadata [
^ metadata ifNil: [ metadata := OrderedDictionary new ] ^ metadata ifNil: [ metadata := OrderedDictionary new ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> metadata: anOrderedDictionary [ Markdeep >> metadata: anOrderedDictionary [
metadata := anOrderedDictionary metadata := anOrderedDictionary
] ]
{ #category : #utilities } { #category : #utilities }
Markdeep >> metadataFromXML: aXMLDocument [ Markdeep >> metadataFromXML: aXMLDocument [
| metaDict | | metaDict |
metaDict := OrderedDictionary new. metaDict := OrderedDictionary new.
(aXMLDocument xpath: '//meta') do: [ :each | (aXMLDocument xpath: '//meta') do: [ :each |
metaDict at: (each @ 'name') stringValue put: (each @ 'content') stringValue ]. metaDict at: (each @ 'name') stringValue put: (each @ 'content') stringValue ].
^ metaDict ^ metaDict
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> navTop [ Markdeep >> navTop [
^ navTop ifNil: [ navTop := '' ] ^ navTop ifNil: [ navTop := '' ]
] ]
{ #category : #'as yet unclassified' } { #category : #'as yet unclassified' }
Markdeep >> navTop: aString [ Markdeep >> navTop: aString [
navTop:= aString. navTop:= aString.
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> options [ Markdeep >> options [
^ options ifNil: [ ^ options ifNil: [
"Setting defaults accoding to: https://casual-effects.com/markdeep/#api" "Setting defaults accoding to: https://casual-effects.com/markdeep/#api"
options := Dictionary new options := Dictionary new
at: 'mode' put: nil; at: 'mode' put: nil;
at: 'lang' put: nil; at: 'lang' put: nil;
at: 'tocStyle' put: 'auto'; at: 'tocStyle' put: 'auto';
at: 'autoLinkImages' put: true; at: 'autoLinkImages' put: true;
yourself yourself
] ]
] ]
{ #category : #printing } { #category : #printing }
Markdeep >> printOn: aStream [ Markdeep >> printOn: aStream [
super printOn: aStream. super printOn: aStream.
aStream aStream
nextPutAll: '( ', self title, ' )' nextPutAll: '( ', self title, ' )'
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> processMarkdownFor: aFileReference [ Markdeep >> processMarkdownFor: aFileReference [
"comment stating purpose of message" "comment stating purpose of message"
| markdownContent | | markdownContent |
self markdownFile: aFileReference. self markdownFile: aFileReference.
markdownContent := Markdown fromFile: aFileReference. markdownContent := Markdown fromFile: aFileReference.
self body: (markdownContent commentYAMLMetadata contents). self body: (markdownContent commentYAMLMetadata contents).
self metadata: markdownContent yamlMetadata self metadata: markdownContent yamlMetadata
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubFootnoteMetadataFromString: string [ Markdeep >> pubPubFootnoteMetadataFromString: string [
| sanitized footnoteData altLine altString id | | sanitized footnoteData altLine altString id |
(string lines size <= 1) ifTrue: [ ^ nil ]. (string lines size <= 1) ifTrue: [ ^ nil ].
sanitized := '' writeStream. sanitized := '' writeStream.
altString := string copyReplaceAll: '.footnote' with: ''. altString := string copyReplaceAll: '.footnote' with: ''.
altString := altString copyReplaceAll: ' node-type=' altString := altString copyReplaceAll: ' node-type='
with: ' with: '
node-type= '. node-type= '.
altString lines allButFirstDo: [:line | altString lines allButFirstDo: [:line |
(line beginsWith: '>') (line beginsWith: '>')
ifTrue: [ altLine := line allButFirst ] ifTrue: [ altLine := line allButFirst ]
ifFalse: [ altLine := line ]. ifFalse: [ altLine := line ].
sanitized sanitized
nextPutAll: altLine trimBoth; nextPutAll: altLine trimBoth;
nextPutAll: String lf nextPutAll: String lf
]. ].
sanitized := sanitized contents. sanitized := sanitized contents.
sanitized := sanitized copyReplaceAll: 'type=' with: 'type: '. sanitized := sanitized copyReplaceAll: 'type=' with: 'type: '.
sanitized := sanitized copyReplaceAll: 'value=' with: 'value: '. sanitized := sanitized copyReplaceAll: 'value=' with: 'value: '.
id := (altString lines first) allButFirst trimmed. id := (altString lines first) allButFirst trimmed.
footnoteData := { 'id' -> id } asDictionary. footnoteData := { 'id' -> id } asDictionary.
footnoteData addAll: (MiniDocs yamlToJson: sanitized trimmed). footnoteData addAll: (MiniDocs yamlToJson: sanitized trimmed).
^ footnoteData ^ footnoteData
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubFootnoteRawLinks [ Markdeep >> pubPubFootnoteRawLinks [
^ self selectPubPubLinksWithSize: 2 ^ self selectPubPubLinksWithSize: 2
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubFootnotesLinesRange [ Markdeep >> pubPubFootnotesLinesRange [
| beginningLine endingLine | | beginningLine endingLine |
beginningLine := self contents lines size + 1. beginningLine := self contents lines size + 1.
self contents lines doWithIndex: [:line :i | self contents lines doWithIndex: [:line :i |
(line beginsWith: '::: {.pub-notes}') ifTrue: [ beginningLine := i ]. (line beginsWith: '::: {.pub-notes}') ifTrue: [ beginningLine := i ].
(i > beginningLine and: [ line beginsWith: ':::' ]) (i > beginningLine and: [ line beginsWith: ':::' ])
ifTrue: [ ifTrue: [
endingLine := i. endingLine := i.
^ {beginningLine . endingLine} ^ {beginningLine . endingLine}
] ]
] ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubFootnotesText [ Markdeep >> pubPubFootnotesText [
| footnotesLines output | | footnotesLines output |
footnotesLines := self contents lines footnotesLines := self contents lines
copyFrom: self pubPubFootnotesLinesRange first + 3 copyFrom: self pubPubFootnotesLinesRange first + 3
to: self pubPubFootnotesLinesRange second - 1. to: self pubPubFootnotesLinesRange second - 1.
output := '' writeStream. output := '' writeStream.
footnotesLines do: [:line | footnotesLines do: [:line |
output output
nextPutAll: line; nextPutAll: line;
nextPutAll: String crlf. nextPutAll: String crlf.
]. ].
^ output contents allButLast ^ output contents allButLast
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubFootnotesToMarkdeep [ Markdeep >> pubPubFootnotesToMarkdeep [
| footnotes sanitized cleanedFootnotesText parsedLinks | | footnotes sanitized cleanedFootnotesText parsedLinks |
footnotes := OrderedDictionary new. footnotes := OrderedDictionary new.
parsedLinks := self pubPubFootnoteRawLinks. parsedLinks := self pubPubFootnoteRawLinks.
parsedLinks ifEmpty: [ ^self ]. parsedLinks ifEmpty: [ ^self ].
sanitized := self body. sanitized := self body.
parsedLinks do: [:link | | footnote | parsedLinks do: [:link | | footnote |
footnote := self pubPubFootnoteMetadataFromString: link second. footnote := self pubPubFootnoteMetadataFromString: link second.
footnote ifNotNil: [ | toReplace | footnote ifNotNil: [ | toReplace |
footnotes at: (footnote at: 'id') put: footnote. footnotes at: (footnote at: 'id') put: footnote.
toReplace := '[', link first, ']{', link second, '}'. toReplace := '[', link first, ']{', link second, '}'.
sanitized := sanitized copyReplaceAll: toReplace with: '[^', (footnote at: 'id'), ']' sanitized := sanitized copyReplaceAll: toReplace with: '[^', (footnote at: 'id'), ']'
] ]
]. ].
cleanedFootnotesText := '' writeStream. cleanedFootnotesText := '' writeStream.
footnotes keysAndValuesDo: [:k :v | footnotes keysAndValuesDo: [:k :v |
cleanedFootnotesText cleanedFootnotesText
nextPutAll: '[^', k, ']: '; nextPutAll: '[^', k, ']: ';
nextPutAll: (v at: 'data-value'), String lf, String lf. nextPutAll: (v at: 'data-value'), String lf, String lf.
]. ].
self body: (sanitized copyReplaceAll: self pubPubFootnotesText with: cleanedFootnotesText contents) self body: (sanitized copyReplaceAll: self pubPubFootnotesText with: cleanedFootnotesText contents)
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubImageLinks [ Markdeep >> pubPubImageLinks [
^ self selectPubPubLinksWithSize: 3 ^ self selectPubPubLinksWithSize: 3
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubImagesToMarkdeep [ Markdeep >> pubPubImagesToMarkdeep [
| sanitized parsedLinks | | sanitized parsedLinks |
parsedLinks := self pubPubImageLinks. parsedLinks := self pubPubImageLinks.
parsedLinks ifEmpty: [ ^self ]. parsedLinks ifEmpty: [ ^self ].
sanitized := self body. sanitized := self body.
parsedLinks do: [:link | parsedLinks do: [:link |
sanitized := sanitized copyReplaceAll: '{', link third, '}' with: '' sanitized := sanitized copyReplaceAll: '{', link third, '}' with: ''
]. ].
self body: sanitized self body: sanitized
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> pubPubRawLinks [ Markdeep >> pubPubRawLinks [
| parser | | parser |
parser := PubPubGrammar new document. parser := PubPubGrammar new document.
^ (parser parse: self body) ^ (parser parse: self body)
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> replaceBackslashBreaklines [ Markdeep >> replaceBackslashBreaklines [
self bodyReplaceAll: '\ self bodyReplaceAll: '\
' with: '<br> ' with: '<br>
' '
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> selectPubPubLinksWithSize: naturalNumber [ Markdeep >> selectPubPubLinksWithSize: naturalNumber [
^ self pubPubRawLinks select: [ :each | each size = naturalNumber ] ^ self pubPubRawLinks select: [ :each | each size = naturalNumber ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> tail [ Markdeep >> tail [
"I enable the document tail, which, in turn, enables a Markdeep document" "I enable the document tail, which, in turn, enables a Markdeep document"
| output | | output |
output := '' writeStream. output := '' writeStream.
output output
nextPutAll: '<!-- Markdeep: -->'; lf; lf; nextPutAll: '<!-- Markdeep: -->'; lf; lf;
nextPutAll: '<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style>'; lf; nextPutAll: '<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style>'; lf;
nextPutAll: '<script>window.markdeepOptions = {tocStyle: "', self tocStyle,'"}</script>'; lf; nextPutAll: '<script>window.markdeepOptions = {tocStyle: "', self tocStyle,'"}</script>'; lf;
nextPutAll: self markdeepScriptTag; lf; nextPutAll: self markdeepScriptTag; lf;
nextPutAll: '<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>-->'. nextPutAll: '<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>-->'.
^ output contents ^ output contents
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> tail: anObject [ Markdeep >> tail: anObject [
tail := anObject tail := anObject
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> title [ Markdeep >> title [
^ title ifNil: [ title := self metadata at: 'title' ] ^ title ifNil: [ title := self metadata at: 'title' ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> title: anObject [ Markdeep >> title: anObject [
title := anObject title := anObject
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> tocStyle [ Markdeep >> tocStyle [
^ self options at: 'tocStyle' ifAbsent: [ 'short' ] ^ self options at: 'tocStyle' ifAbsent: [ 'short' ]
] ]
{ #category : #accessing } { #category : #accessing }
Markdeep >> tocStyle: aString [ Markdeep >> tocStyle: aString [
"A string can be: 'auto' 'none' 'short' 'medium' or 'long'" "A string can be: 'auto' 'none' 'short' 'medium' or 'long'"
self options at: 'tocStyle' put: aString self options at: 'tocStyle' put: aString
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdeep >> version [ Markdeep >> version [
self metadata at: 'version' ifPresent: [:value | ^ 'v',value ]. self metadata at: 'version' ifPresent: [:value | ^ 'v',value ].
^ '' ^ ''
] ]

View File

@ -1,185 +1,185 @@
" "
I model a Markdown document. I model a Markdown document.
At some point the idea is to have a full native parser implemented to deal 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, with my syntax, but meanwhile I will be collaborating with external parsers,
particularly the ones provided by Pandoc and/or Lunamark. particularly the ones provided by Pandoc and/or Lunamark.
" "
Class { Class {
#name : #Markdown, #name : #Markdown,
#superclass : #Object, #superclass : #Object,
#instVars : [ #instVars : [
'contents', 'contents',
'file' 'file'
], ],
#category : #MiniDocs #category : #MiniDocs
} }
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdown class >> fromFile: aFileReference [ Markdown class >> fromFile: aFileReference [
^ self new fromFile: aFileReference ^ self new fromFile: aFileReference
] ]
{ #category : #utilities } { #category : #utilities }
Markdown class >> yamlMetadataDelimiter [ Markdown class >> yamlMetadataDelimiter [
^ '---' ^ '---'
] ]
{ #category : #operation } { #category : #operation }
Markdown >> commentYAMLMetadata [ Markdown >> commentYAMLMetadata [
| newContents | | newContents |
self detectYAMLMetadata ifFalse: [ ^ self ]. self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream. newContents := '' writeStream.
newContents nextPutAll: '<!--@yaml:'; crlf. newContents nextPutAll: '<!--@yaml:'; crlf.
newContents nextPutAll: self yamlMetadataString. newContents nextPutAll: self yamlMetadataString.
newContents nextPutAll: String cr. newContents nextPutAll: String cr.
newContents nextPutAll: '-->'; crlf. newContents nextPutAll: '-->'; crlf.
(self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line | (self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line |
newContents nextPutAll: line; crlf ]. newContents nextPutAll: line; crlf ].
^ newContents contents. ^ newContents contents.
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> containsYAMLMetadataClosing [ Markdown >> containsYAMLMetadataClosing [
^ self yamlMetadataClosingLineNumber > 0 ^ self yamlMetadataClosingLineNumber > 0
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> contents [ Markdown >> contents [
^ contents ^ contents
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> contents: anObject [ Markdown >> contents: anObject [
contents := anObject contents := anObject
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> detectYAMLMetadata [ Markdown >> detectYAMLMetadata [
| lines | | lines |
lines := self lines. lines := self lines.
^ self startsWithYAMLMetadataDelimiter ^ self startsWithYAMLMetadataDelimiter
and: [ lines allButFirst and: [ lines allButFirst
detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ] detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ]
ifFound: [ ^ true ] ifNone: [ ^ false ] ] ifFound: [ ^ true ] ifNone: [ ^ false ] ]
] ]
{ #category : #operation } { #category : #operation }
Markdown >> exportMetadataAsJson [ Markdown >> exportMetadataAsJson [
"TBD: Lua scripts should be checked and installed when missing. Maybe a shared location "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." in '.local/share/Grafoscopio/Scripts' should be developed in the near future."
| output luaScript | | output luaScript |
luaScript := FileLocator home / '.local/share/Brea/scripts/meta-to-json.lua'. luaScript := FileLocator home / '.local/share/Brea/scripts/meta-to-json.lua'.
Smalltalk platformName = 'unix' ifTrue: [ Smalltalk platformName = 'unix' ifTrue: [
OSSUnixSubprocess new OSSUnixSubprocess new
workingDirectory: self file parent fullName; workingDirectory: self file parent fullName;
command: 'pandoc'; command: 'pandoc';
arguments: { '--lua-filter=', luaScript fullName . self file basename }; arguments: { '--lua-filter=', luaScript fullName . self file basename };
redirectStdout; redirectStdout;
redirectStdin; redirectStdin;
runAndWaitOnExitDo: [ :process :outString :errString | runAndWaitOnExitDo: [ :process :outString :errString |
output := process isSuccess output := process isSuccess
ifTrue: [ outString ] ifTrue: [ outString ]
ifFalse: [ errString ] ifFalse: [ errString ]
]]. ]].
^ output correctAccentedCharacters ^ output correctAccentedCharacters
] ]
{ #category : #operation } { #category : #operation }
Markdown >> exportMetadataAsYaml [ Markdown >> exportMetadataAsYaml [
| exportedFile | | exportedFile |
exportedFile := FileLocator temp / 'metadata.yaml'. exportedFile := FileLocator temp / 'metadata.yaml'.
MarkupFile exportAsFileOn: exportedFile containing: self yamlMetadataStringWithDelimiters. MarkupFile exportAsFileOn: exportedFile containing: self yamlMetadataStringWithDelimiters.
^ exportedFile ^ exportedFile
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> file [ Markdown >> file [
^ file ^ file
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> file: aFileReference [ Markdown >> file: aFileReference [
"I store the origen/destination of the Markdown contents." "I store the origen/destination of the Markdown contents."
file := aFileReference file := aFileReference
] ]
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdown >> fromFile: aFileReference [ Markdown >> fromFile: aFileReference [
self contents: aFileReference contents. self contents: aFileReference contents.
self file: aFileReference. self file: aFileReference.
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> gtTextFor: aView [ Markdown >> gtTextFor: aView [
<gtView> <gtView>
^ aView textEditor ^ aView textEditor
title: 'Text'; title: 'Text';
text: [ self contents ] text: [ self contents ]
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> lines [ Markdown >> lines [
^ self contents lines. ^ self contents lines.
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> metadata [ Markdown >> metadata [
| rawMeta | | rawMeta |
rawMeta := PPYAMLGrammar new parse: self yamlMetadataString. rawMeta := PPYAMLGrammar new parse: self yamlMetadataString.
rawMeta associationsDo: [ :assoc | rawMeta associationsDo: [ :assoc |
assoc value = 'false' ifTrue: [ assoc value: false ]. assoc value = 'false' ifTrue: [ assoc value: false ].
assoc value = 'true' ifTrue: [ assoc value: true ] ]. assoc value = 'true' ifTrue: [ assoc value: true ] ].
^ rawMeta ^ rawMeta
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> printOn: aStream [ Markdown >> printOn: aStream [
super printOn: aStream. super printOn: aStream.
aStream aStream
nextPutAll: '( ', (self metadata at: 'title'), ' )' nextPutAll: '( ', (self metadata at: 'title'), ' )'
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> startsWithYAMLMetadataDelimiter [ Markdown >> startsWithYAMLMetadataDelimiter [
^ self lines first beginsWith: self class yamlMetadataDelimiter ^ self lines first beginsWith: self class yamlMetadataDelimiter
] ]
{ #category : #accessing } { #category : #accessing }
Markdown >> yamlMetadata [ Markdown >> yamlMetadata [
^ MiniDocs yamlToJson: self yamlMetadataString ^ MiniDocs yamlToJson: self yamlMetadataString
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> yamlMetadataClosingLineNumber [ Markdown >> yamlMetadataClosingLineNumber [
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found." "I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."
self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ]. self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ].
self lines allButFirst doWithIndex: [ :currentLine :i | self lines allButFirst doWithIndex: [ :currentLine :i |
(currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ ^ i + 1 ]] (currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ ^ i + 1 ]]
] ]
{ #category : #operation } { #category : #operation }
Markdown >> yamlMetadataString [ Markdown >> yamlMetadataString [
| output yamlLines | | output yamlLines |
self detectYAMLMetadata ifFalse: [ ^ nil ]. self detectYAMLMetadata ifFalse: [ ^ nil ].
yamlLines := self lines copyFrom: 2 to: self yamlMetadataClosingLineNumber - 1. yamlLines := self lines copyFrom: 2 to: self yamlMetadataClosingLineNumber - 1.
output := '' writeStream. output := '' writeStream.
yamlLines do: [ :line | yamlLines do: [ :line |
output output
nextPutAll: line; nextPutAll: line;
nextPut: Character cr. ]. nextPut: Character cr. ].
^ output contents ^ output contents
] ]
{ #category : #utilities } { #category : #utilities }
Markdown >> yamlMetadataStringWithDelimiters [ Markdown >> yamlMetadataStringWithDelimiters [
| output | | output |
self yamlMetadataString ifNil: [ ^ nil ]. self yamlMetadataString ifNil: [ ^ nil ].
output := String new writeStream. output := String new writeStream.
output nextPutAll: self class yamlMetadataDelimiter; cr. output nextPutAll: self class yamlMetadataDelimiter; cr.
output nextPutAll: self yamlMetadataString. output nextPutAll: self yamlMetadataString.
output nextPutAll: self class yamlMetadataDelimiter; cr. output nextPutAll: self class yamlMetadataDelimiter; cr.
^ output contents. ^ output contents.
] ]

View File

@ -1,37 +1,37 @@
" "
I model common operations made with several markup files. I model common operations made with several markup files.
" "
Class { Class {
#name : #MarkupFile, #name : #MarkupFile,
#superclass : #Object, #superclass : #Object,
#instVars : [ #instVars : [
'file' 'file'
], ],
#category : #MiniDocs #category : #MiniDocs
} }
{ #category : #persistence } { #category : #persistence }
MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [ MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [
| file | | file |
file := aFileReferenceOrFileName asFileReference. file := aFileReferenceOrFileName asFileReference.
file ensureDelete. file ensureDelete.
file exists ifFalse: [ file ensureCreateFile ]. file exists ifFalse: [ file ensureCreateFile ].
file writeStreamDo: [ :stream | file writeStreamDo: [ :stream |
stream nextPutAll: text withUnixLineEndings]. stream nextPutAll: text withUnixLineEndings].
self inform: 'Exported as: ', String cr, file fullName. self inform: 'Exported as: ', String cr, file fullName.
^ file ^ file
] ]
{ #category : #accessing } { #category : #accessing }
MarkupFile class >> installTemplate: anUrl into: aFolder [ MarkupFile class >> installTemplate: anUrl into: aFolder [
| fileName | | fileName |
fileName := anUrl asUrl segments last. fileName := anUrl asUrl segments last.
(aFolder / fileName) exists (aFolder / fileName) exists
ifTrue: [ (aFolder / fileName) ensureDeleteFile ] ifTrue: [ (aFolder / fileName) ensureDeleteFile ]
ifFalse: [ aFolder ensureCreateDirectory ]. ifFalse: [ aFolder ensureCreateDirectory ].
ZnClient new ZnClient new
url: anUrl; url: anUrl;
downloadTo: aFolder. downloadTo: aFolder.
^ aFolder ^ aFolder
] ]

View File

@ -1,52 +1,55 @@
Class { Class {
#name : #MiniDocs, #name : #MiniDocs,
#superclass : #Object, #superclass : #Object,
#category : #MiniDocs #category : #MiniDocs
} }
{ #category : #accessing } { #category : #accessing }
MiniDocs class >> appFolder [ MiniDocs class >> appFolder [
| tempFolder | | tempFolder userDataFolder |
tempFolder := FileLocator userData / 'Mutabit' / 'MiniDocs'. userDataFolder := Smalltalk os isWindows
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ]. ifTrue: [ FileLocator home / 'AppData' / 'Local' ]
^ tempFolder ifFalse: [ FileLocator userData ].
] tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'.
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
{ #category : #accessing } ^ tempFolder
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." { #category : #accessing }
self yamlToJsonBinary exists ifTrue: [ ^ MiniDocs appFolder ]. MiniDocs class >> installYamlToJson [
Nimble install: 'commandeer'. "For the moment, only Gnu/Linux and Mac are supported.
OSSUnixSubprocess new IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation."
command: 'nim'; self yamlToJsonBinary exists ifTrue: [ ^ MiniDocs appFolder ].
arguments: {'c'. self yamlToJsonSourceCode fullName}; Nimble install: 'commandeer'.
runAndWaitOnExitDo: [ :process :outString | OSSUnixSubprocess new
(self yamlToJsonSourceCode parent / self yamlToJsonSourceCode basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. command: 'nim';
^ MiniDocs appFolder ] arguments: {'c'. self yamlToJsonSourceCode fullName};
] runAndWaitOnExitDo: [ :process :outString |
(self yamlToJsonSourceCode parent / self yamlToJsonSourceCode basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference.
{ #category : #accessing } ^ MiniDocs appFolder ]
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." { #category : #accessing }
self yamlToJsonBinary exists ifFalse: [ self installYamlToJson ]. MiniDocs class >> yamlToJson: yamlString [
"This method uses a external binary written in Nim, as the native Pharo parser for YAML, written in PetitParser,
OSSUnixSubprocess new was less robust and unable to parse correctly the same strings as the external one."
command: self yamlToJsonBinary fullName; self yamlToJsonBinary exists ifFalse: [ self installYamlToJson ].
arguments: {yamlString};
redirectStdout; OSSUnixSubprocess new
runAndWaitOnExitDo: [ :process :outString | command: self yamlToJsonBinary fullName;
^ (STONJSON fromString: outString allButFirst) first arguments: {yamlString};
] redirectStdout;
] runAndWaitOnExitDo: [ :process :outString |
^ (STONJSON fromString: outString allButFirst) first
{ #category : #accessing } ]
MiniDocs class >> yamlToJsonBinary [ ]
^ self appFolder / 'yamlToJson'
] { #category : #accessing }
MiniDocs class >> yamlToJsonBinary [
{ #category : #accessing } ^ self appFolder / 'yamlToJson'
MiniDocs class >> yamlToJsonSourceCode [ ]
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/yamlToJson.nim'
] { #category : #accessing }
MiniDocs class >> yamlToJsonSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/yamlToJson.nim'
]

View File

@ -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). 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: 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 [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: * 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. ~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, 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 , 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, 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.. unevenly produced between hours, days and/or weeks..
" "
Class { Class {
#name : #NanoID, #name : #NanoID,
#superclass : #Object, #superclass : #Object,
#category : #'MiniDocs-MiniDocs' #category : #'MiniDocs-MiniDocs'
} }
{ #category : #accessing } { #category : #accessing }
NanoID class >> binaryFile [ NanoID class >> binaryFile [
^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension ^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension
] ]
{ #category : #accessing } { #category : #accessing }
NanoID class >> generate [ NanoID class >> generate [
self binaryFile exists ifFalse: [ NanoID install]. self binaryFile exists ifFalse: [ NanoID install].
OSSUnixSubprocess new OSSUnixSubprocess new
command: self binaryFile fullName; command: self binaryFile fullName;
redirectStdout; redirectStdout;
redirectStdout; redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ] runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ]
] ]
{ #category : #accessing } { #category : #accessing }
NanoID class >> install [ NanoID class >> install [
"For the moment, only Gnu/Linux and Mac are supported. "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." IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation."
self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ]. self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ].
Nimble install: 'nanoid'. Nimble install: 'nanoid'.
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'nim'; command: 'nim';
arguments: {'c'. self scriptSourceCode fullName}; arguments: {'c'. self scriptSourceCode fullName};
runAndWaitOnExitDo: [ :process :outString | runAndWaitOnExitDo: [ :process :outString |
(self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference. (self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveTo: MiniDocs appFolder asFileReference.
^ MiniDocs appFolder ] ^ MiniDocs appFolder ]
] ]
{ #category : #accessing } { #category : #accessing }
NanoID class >> scriptSourceCode [ NanoID class >> scriptSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim' ^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim'
] ]

View File

@ -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). 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. This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation.
" "
Class { Class {
#name : #Nimble, #name : #Nimble,
#superclass : #Object, #superclass : #Object,
#category : #'MiniDocs-MiniDocs' #category : #'MiniDocs-MiniDocs'
} }
{ #category : #accessing } { #category : #accessing }
Nimble class >> detect: packageName [ Nimble class >> detect: packageName [
^ self installed ^ self installed
detect: [ :dependency | dependency beginsWith: packageName ] detect: [ :dependency | dependency beginsWith: packageName ]
ifFound: [ ^ true ] ifFound: [ ^ true ]
ifNone: [ ^ false ] ifNone: [ ^ false ]
] ]
{ #category : #accessing } { #category : #accessing }
Nimble class >> install: packageName [ Nimble class >> install: packageName [
(self detect: packageName) ifTrue: [ ^ self ]. (self detect: packageName) ifTrue: [ ^ self ].
self installPackagesList. self installPackagesList.
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'nimble'; command: 'nimble';
arguments: {'install'. arguments: {'install'.
packageName}; packageName};
redirectStdout; redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ] runAndWaitOnExitDo: [ :process :outString | ^ outString ]
] ]
{ #category : #accessing } { #category : #accessing }
Nimble class >> installPackagesList [ Nimble class >> installPackagesList [
(FileLocator home / '.nimble' / 'packages_official.json') exists (FileLocator home / '.nimble' / 'packages_official.json') exists
ifTrue: [ ^ self ]. ifTrue: [ ^ self ].
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'nimble'; command: 'nimble';
arguments: #('refresh'); arguments: #('refresh');
redirectStdout; redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ] runAndWaitOnExitDo: [ :process :outString | ^ outString ]
] ]
{ #category : #accessing } { #category : #accessing }
Nimble class >> installed [ Nimble class >> installed [
| installed | | installed |
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'nimble'; command: 'nimble';
arguments: #('list' '--installed'); arguments: #('list' '--installed');
redirectStdout; redirectStdout;
redirectStderr; redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString | runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess process isSuccess
ifTrue: [ ^ outString lines ]; ifTrue: [ ^ outString lines ];
ifFalse: [ ^ nil ] ifFalse: [ ^ nil ]
] ]
] ]
{ #category : #accessing } { #category : #accessing }
Nimble class >> version [ Nimble class >> version [
OSSUnixSubprocess new OSSUnixSubprocess new
command: 'nimble'; command: 'nimble';
arguments: #('--version'); arguments: #('--version');
redirectStdout; redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ] runAndWaitOnExitDo: [ :process :outString | ^ outString ]
] ]

View File

@ -1,70 +1,70 @@
Class { Class {
#name : #PubPubGrammar, #name : #PubPubGrammar,
#superclass : #PP2CompositeNode, #superclass : #PP2CompositeNode,
#instVars : [ #instVars : [
'document', 'document',
'link', 'link',
'linkLabel', 'linkLabel',
'linkContent', 'linkContent',
'imageLinkLabel', 'imageLinkLabel',
'imageLinkContent', 'imageLinkContent',
'alternativeImages', 'alternativeImages',
'imageLink' 'imageLink'
], ],
#category : #'MiniDocs-Model' #category : #'MiniDocs-Model'
} }
{ #category : #accessing } { #category : #accessing }
PubPubGrammar >> alternativeImages [ PubPubGrammar >> alternativeImages [
^ self linkContent ^ self linkContent
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammar >> document [ PubPubGrammar >> document [
^ (link / imageLink ) islandInSea star ^ (link / imageLink ) islandInSea star
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> imageLink [ PubPubGrammar >> imageLink [
^ imageLinkLabel, imageLinkContent, alternativeImages ^ imageLinkLabel, imageLinkContent, alternativeImages
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> imageLinkContent [ PubPubGrammar >> imageLinkContent [
^ '(' asPParser, #any asPParser starLazy flatten, ')' asPParser ==> #second ^ '(' asPParser, #any asPParser starLazy flatten, ')' asPParser ==> #second
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> imageLinkLabel [ PubPubGrammar >> imageLinkLabel [
^ '![' asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second ^ '![' asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammar >> imageLinkSea [ PubPubGrammar >> imageLinkSea [
^ imageLink sea ==> #second ^ imageLink sea ==> #second
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> link [ PubPubGrammar >> link [
^ linkLabel, linkContent ^ linkLabel, linkContent
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> linkContent [ PubPubGrammar >> linkContent [
^ '{' asPParser, #any asPParser starLazy flatten, '}' asPParser ==> #second. ^ '{' asPParser, #any asPParser starLazy flatten, '}' asPParser ==> #second.
] ]
{ #category : #links } { #category : #links }
PubPubGrammar >> linkLabel [ PubPubGrammar >> linkLabel [
^ $[ asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second. ^ $[ asPParser, #any asPParser starLazy flatten, $] asPParser ==> #second.
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammar >> linkSea [ PubPubGrammar >> linkSea [
^ link sea ==> #second ^ link sea ==> #second
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammar >> start [ PubPubGrammar >> start [
^ document ^ document
] ]

View File

@ -1,24 +1,24 @@
Class { Class {
#name : #PubPubGrammarTest, #name : #PubPubGrammarTest,
#superclass : #PP2CompositeNodeTest, #superclass : #PP2CompositeNodeTest,
#category : #'MiniDocs-Model' #category : #'MiniDocs-Model'
} }
{ #category : #accessing } { #category : #accessing }
PubPubGrammarTest >> parserClass [ PubPubGrammarTest >> parserClass [
^ PubPubGrammar ^ PubPubGrammar
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammarTest >> testImageLink [ PubPubGrammarTest >> testImageLink [
self self
parse: '![This is an image label](this/is/an/image/link){this are alternate image sizes}' parse: '![This is an image label](this/is/an/image/link){this are alternate image sizes}'
rule: #imageLink rule: #imageLink
] ]
{ #category : #accessing } { #category : #accessing }
PubPubGrammarTest >> testLink [ PubPubGrammarTest >> testLink [
self self
parse: '[This is a label]{this/is/a/link}' parse: '[This is a label]{this/is/a/link}'
rule: #link rule: #link
] ]

View File

@ -1,8 +1,8 @@
Extension { #name : #String } Extension { #name : #String }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> asDashedLowercase [ String >> asDashedLowercase [
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'." "I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
^ '-' join: (self substrings collect: [:each | each asLowercase ]) ^ '-' join: (self substrings collect: [:each | each asLowercase ])
] ]

View File

@ -1 +1 @@
Package { #name : #MiniDocs } Package { #name : #MiniDocs }