Fixing data folder for Windows.
This commit is contained in:
parent
77404b9b31
commit
06b18c4e50
@ -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 [
|
||||
<gtView>
|
||||
^ 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 [
|
||||
<gtView>
|
||||
^ 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
|
||||
]
|
||||
|
@ -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: '<meta lang="', v,'">';
|
||||
yourself.
|
||||
]
|
||||
ifFalse: [
|
||||
markdeep head
|
||||
add: '<meta name="', k, '" content="', v,'">';
|
||||
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: '<meta lang="', v,'">';
|
||||
yourself.
|
||||
]
|
||||
ifFalse: [
|
||||
markdeep head
|
||||
add: '<meta name="', k, '" content="', v,'">';
|
||||
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 ]
|
||||
]
|
||||
|
@ -1,15 +1,15 @@
|
||||
Extension { #name : #LePageHeaderBuilder }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePageHeaderBuilder >> addExportPageButton [
|
||||
<leHeaderAction>
|
||||
| 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 [
|
||||
<leHeaderAction>
|
||||
| newButton |
|
||||
|
||||
newButton := BrButton new
|
||||
aptitude: BrGlamorousButtonWithIconAptitude;
|
||||
label: 'Export Page';
|
||||
icon: BrGlamorousVectorIcons down;
|
||||
action: [ :aButton |
|
||||
aButton phlow spawnObject: self page asMarkdeepFile ].
|
||||
self toolbarElement addItem: newButton.
|
||||
]
|
||||
|
@ -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: '</script>'; lf.
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdeepCustomOpener [
|
||||
^ String streamContents: [ :stream |
|
||||
stream
|
||||
nextPutAll: '<script type="preformatted">'; lf;
|
||||
nextPutAll: '~~~ Smalltalk'; lf
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdownCustomCloser [
|
||||
(self tags includes: 'output') ifTrue: [^ String with: Character lf].
|
||||
^ String streamContents: [:stream |
|
||||
stream
|
||||
nextPutAll: '~~~'; lf
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdownCustomOpener [
|
||||
(self tags includes: 'output') ifTrue: [ ^ String with: Character lf ].
|
||||
^ String
|
||||
streamContents: [ :stream |
|
||||
stream
|
||||
nextPutAll: '~~~ Smalltalk';
|
||||
lf ]
|
||||
]
|
||||
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: '</script>'; lf.
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdeepCustomOpener [
|
||||
^ String streamContents: [ :stream |
|
||||
stream
|
||||
nextPutAll: '<script type="preformatted">'; lf;
|
||||
nextPutAll: '~~~ Smalltalk'; lf
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdownCustomCloser [
|
||||
(self tags includes: 'output') ifTrue: [^ String with: Character lf].
|
||||
^ String streamContents: [:stream |
|
||||
stream
|
||||
nextPutAll: '~~~'; lf
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePharoSnippet >> markdownCustomOpener [
|
||||
(self tags includes: 'output') ifTrue: [ ^ String with: Character lf ].
|
||||
^ String
|
||||
streamContents: [ :stream |
|
||||
stream
|
||||
nextPutAll: '~~~ Smalltalk';
|
||||
lf ]
|
||||
]
|
||||
|
@ -1,15 +1,15 @@
|
||||
Extension { #name : #LePictureSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePictureSnippet >> asMarkdeep [
|
||||
| output |
|
||||
output := WriteStream on: ''.
|
||||
output
|
||||
nextPutAll: self metadataDiv;
|
||||
nextPutAll: self centeredFigure;
|
||||
nextPut: Character lf;
|
||||
nextPutAll: '</div>';
|
||||
nextPut: Character lf;
|
||||
nextPut: Character lf.
|
||||
^ output contents
|
||||
]
|
||||
Extension { #name : #LePictureSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LePictureSnippet >> asMarkdeep [
|
||||
| output |
|
||||
output := WriteStream on: ''.
|
||||
output
|
||||
nextPutAll: self metadataDiv;
|
||||
nextPutAll: self centeredFigure;
|
||||
nextPut: Character lf;
|
||||
nextPutAll: '</div>';
|
||||
nextPut: Character lf;
|
||||
nextPut: Character lf.
|
||||
^ output contents
|
||||
]
|
||||
|
@ -1,12 +1,12 @@
|
||||
Extension { #name : #LeSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeSnippet class >> fromMetaMarkdeep: div [
|
||||
| className metadata snippet |
|
||||
className := (div xpath: '@st-class') stringValue.
|
||||
metadata := STON fromString:(div xpath: '@st-data') stringValue.
|
||||
snippet := className asClass new.
|
||||
snippet injectMetadataFrom: metadata.
|
||||
snippet contentFrom: div.
|
||||
^ snippet.
|
||||
]
|
||||
Extension { #name : #LeSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeSnippet class >> fromMetaMarkdeep: div [
|
||||
| className metadata snippet |
|
||||
className := (div xpath: '@st-class') stringValue.
|
||||
metadata := STON fromString:(div xpath: '@st-data') stringValue.
|
||||
snippet := className asClass new.
|
||||
snippet injectMetadataFrom: metadata.
|
||||
snippet contentFrom: div.
|
||||
^ snippet.
|
||||
]
|
||||
|
@ -1,54 +1,54 @@
|
||||
Extension { #name : #LeTextSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> contentFrom: markdeepDiv [
|
||||
|
||||
| sanitizedStringText metadata |
|
||||
metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
|
||||
sanitizedStringText := markdeepDiv contentString.
|
||||
sanitizedStringText := sanitizedStringText allButFirst.
|
||||
sanitizedStringText := sanitizedStringText allButLast.
|
||||
self string: sanitizedStringText;
|
||||
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' }
|
||||
LeTextSnippet >> metadata [
|
||||
|
||||
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> metadataInit [
|
||||
|
||||
^ OrderedDictionary new
|
||||
at: 'id' put: self uidString;
|
||||
at: 'parent' put: self parentId;
|
||||
at: 'created' put: self createTime asString;
|
||||
at: 'modified' put: self latestEditTime asString;
|
||||
at: 'creator' put: self createEmail asString;
|
||||
at: 'modifier' put: self editEmail asString;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> options [
|
||||
^ options
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> parentId [
|
||||
self parent ifNil: [ ^ self ].
|
||||
^ self parent uidString.
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> taggedWith: aString [
|
||||
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
|
||||
^ self metadata at: 'tags'
|
||||
]
|
||||
Extension { #name : #LeTextSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> contentFrom: markdeepDiv [
|
||||
|
||||
| sanitizedStringText metadata |
|
||||
metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
|
||||
sanitizedStringText := markdeepDiv contentString.
|
||||
sanitizedStringText := sanitizedStringText allButFirst.
|
||||
sanitizedStringText := sanitizedStringText allButLast.
|
||||
self string: sanitizedStringText;
|
||||
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' }
|
||||
LeTextSnippet >> metadata [
|
||||
|
||||
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> metadataInit [
|
||||
|
||||
^ OrderedDictionary new
|
||||
at: 'id' put: self uidString;
|
||||
at: 'parent' put: self parentId;
|
||||
at: 'created' put: self createTime asString;
|
||||
at: 'modified' put: self latestEditTime asString;
|
||||
at: 'creator' put: self createEmail asString;
|
||||
at: 'modifier' put: self editEmail asString;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> options [
|
||||
^ options
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> parentId [
|
||||
self parent ifNil: [ ^ self ].
|
||||
^ self parent uidString.
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextSnippet >> taggedWith: aString [
|
||||
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
|
||||
^ self metadata at: 'tags'
|
||||
]
|
||||
|
@ -1,91 +1,91 @@
|
||||
Extension { #name : #LeTextualSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> asMarkdeep [
|
||||
"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."
|
||||
|
||||
| output |
|
||||
output := WriteStream on: ''.
|
||||
output
|
||||
nextPutAll: '<div st-class="' , self class greaseString , '"';
|
||||
nextPut: Character lf;
|
||||
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">';
|
||||
nextPut: Character lf;
|
||||
nextPutAll: self markdeepCustomOpener;
|
||||
nextPutAll: self contentAsString;
|
||||
nextPut: Character lf;
|
||||
nextPutAll: self markdeepCustomCloser;
|
||||
nextPutAll: '</div>';
|
||||
nextPut: Character lf;
|
||||
nextPut: Character lf.
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> asMarkdown [
|
||||
"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."
|
||||
| output |
|
||||
output := '' writeStream.
|
||||
output
|
||||
nextPutAll: '<div st-class="', self class asString, '"'; lf;
|
||||
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
|
||||
nextPutAll: self markdownCustomOpener;
|
||||
nextPutAll: self contentAsStringCustomized; lf;
|
||||
nextPutAll: self markdownCustomCloser;
|
||||
nextPutAll: '</div>'; lf; lf.
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> contentAsStringCustomized [
|
||||
^ self contentAsString
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdeepCustomCloser [
|
||||
^ ''
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdeepCustomOpener [
|
||||
^ ''
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdownCustomCloser [
|
||||
^ self markdeepCustomCloser
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdownCustomOpener [
|
||||
^ self markdeepCustomOpener
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> metadata [
|
||||
|
||||
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> metadataInit [
|
||||
| surrogate |
|
||||
self parent
|
||||
ifNil: [ surrogate := nil]
|
||||
ifNotNil: [ surrogate := self parent uidString ].
|
||||
^ OrderedDictionary new
|
||||
at: 'id' put: self uidString;
|
||||
at: 'parent' put: surrogate;
|
||||
at: 'created' put: self createTime asString;
|
||||
at: 'modified' put: self latestEditTime asString;
|
||||
at: 'creator' put: self createEmail asString;
|
||||
at: 'modifier' put: self editEmail asString;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> tags [
|
||||
^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
|
||||
]
|
||||
Extension { #name : #LeTextualSnippet }
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> asMarkdeep [
|
||||
"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."
|
||||
|
||||
| output |
|
||||
output := WriteStream on: ''.
|
||||
output
|
||||
nextPutAll: '<div st-class="' , self class greaseString , '"';
|
||||
nextPut: Character lf;
|
||||
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">';
|
||||
nextPut: Character lf;
|
||||
nextPutAll: self markdeepCustomOpener;
|
||||
nextPutAll: self contentAsString;
|
||||
nextPut: Character lf;
|
||||
nextPutAll: self markdeepCustomCloser;
|
||||
nextPutAll: '</div>';
|
||||
nextPut: Character lf;
|
||||
nextPut: Character lf.
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> asMarkdown [
|
||||
"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."
|
||||
| output |
|
||||
output := '' writeStream.
|
||||
output
|
||||
nextPutAll: '<div st-class="', self class asString, '"'; lf;
|
||||
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
|
||||
nextPutAll: self markdownCustomOpener;
|
||||
nextPutAll: self contentAsStringCustomized; lf;
|
||||
nextPutAll: self markdownCustomCloser;
|
||||
nextPutAll: '</div>'; lf; lf.
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> contentAsStringCustomized [
|
||||
^ self contentAsString
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdeepCustomCloser [
|
||||
^ ''
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdeepCustomOpener [
|
||||
^ ''
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdownCustomCloser [
|
||||
^ self markdeepCustomCloser
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> markdownCustomOpener [
|
||||
^ self markdeepCustomOpener
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> metadata [
|
||||
|
||||
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> metadataInit [
|
||||
| surrogate |
|
||||
self parent
|
||||
ifNil: [ surrogate := nil]
|
||||
ifNotNil: [ surrogate := self parent uidString ].
|
||||
^ OrderedDictionary new
|
||||
at: 'id' put: self uidString;
|
||||
at: 'parent' put: surrogate;
|
||||
at: 'created' put: self createTime asString;
|
||||
at: 'modified' put: self latestEditTime asString;
|
||||
at: 'creator' put: self createEmail asString;
|
||||
at: 'modifier' put: self editEmail asString;
|
||||
yourself
|
||||
]
|
||||
|
||||
{ #category : #'*MiniDocs' }
|
||||
LeTextualSnippet >> tags [
|
||||
^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
|
||||
]
|
||||
|
@ -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
|
||||
"
|
||||
Class {
|
||||
#name : #ManifestMiniDocs,
|
||||
#superclass : #PackageManifest,
|
||||
#category : #'MiniDocs-Manifest'
|
||||
}
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleCascadedNextPutAllsRuleV1FalsePositive [
|
||||
^ #(#(#(#RGMethodDefinition #(#LeTextualSnippet #asMarkdeep #false)) #'2022-09-09T12:31:08.106585-05:00') )
|
||||
]
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleExcessiveVariablesRuleV1FalsePositive [
|
||||
^ #(#(#(#RGClassDefinition #(#Markdeep)) #'2022-07-16T12:24:34.695032-05:00') )
|
||||
]
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleParseTreeLintRuleV1FalsePositive [
|
||||
^ #(#(#(#RGPackageDefinition #(#MiniDocs)) #'2022-07-25T09:28:50.156394-05:00') )
|
||||
]
|
||||
"
|
||||
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 {
|
||||
#name : #ManifestMiniDocs,
|
||||
#superclass : #PackageManifest,
|
||||
#category : #'MiniDocs-Manifest'
|
||||
}
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleCascadedNextPutAllsRuleV1FalsePositive [
|
||||
^ #(#(#(#RGMethodDefinition #(#LeTextualSnippet #asMarkdeep #false)) #'2022-09-09T12:31:08.106585-05:00') )
|
||||
]
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleExcessiveVariablesRuleV1FalsePositive [
|
||||
^ #(#(#(#RGClassDefinition #(#Markdeep)) #'2022-07-16T12:24:34.695032-05:00') )
|
||||
]
|
||||
|
||||
{ #category : #'code-critics' }
|
||||
ManifestMiniDocs class >> ruleParseTreeLintRuleV1FalsePositive [
|
||||
^ #(#(#(#RGPackageDefinition #(#MiniDocs)) #'2022-07-25T09:28:50.156394-05:00') )
|
||||
]
|
||||
|
@ -1,473 +1,473 @@
|
||||
"
|
||||
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: '<!--@div-open ', openner, '-->'
|
||||
].
|
||||
commented := commented
|
||||
copyReplaceAll: ':::
|
||||
' with: '<!--@div-close ::: -->
|
||||
'.
|
||||
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: '<!-- Hypothesis -->
|
||||
<script src="https://hypothes.is/embed.js" async></script>'.
|
||||
^ 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"
|
||||
|
||||
^ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
|
||||
]
|
||||
|
||||
{ #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 [
|
||||
<gtView>
|
||||
^ 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: '<head>';
|
||||
nextPut: Character lf.
|
||||
self head do: [ :line |
|
||||
stream
|
||||
nextPutAll: ' ';
|
||||
nextPutAll: line;
|
||||
nextPut: Character lf
|
||||
].
|
||||
stream
|
||||
nextPutAll: '</head>';
|
||||
nextPut: Character lf.
|
||||
].
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language: anObject [
|
||||
language := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdeepScriptTag [
|
||||
^ '<script src="markdeep.min.js" charset="utf-8"></script>
|
||||
<script
|
||||
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
|
||||
charset="utf-8">
|
||||
</script>'
|
||||
]
|
||||
|
||||
{ #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: '<br>
|
||||
'
|
||||
]
|
||||
|
||||
{ #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: '<!-- Markdeep: -->'; lf; 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: self markdeepScriptTag; lf;
|
||||
nextPutAll: '<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>-->'.
|
||||
^ 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: '<!--@div-open ', openner, '-->'
|
||||
].
|
||||
commented := commented
|
||||
copyReplaceAll: ':::
|
||||
' with: '<!--@div-close ::: -->
|
||||
'.
|
||||
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: '<!-- Hypothesis -->
|
||||
<script src="https://hypothes.is/embed.js" async></script>'.
|
||||
^ 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"
|
||||
|
||||
^ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
|
||||
]
|
||||
|
||||
{ #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 [
|
||||
<gtView>
|
||||
^ 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: '<head>';
|
||||
nextPut: Character lf.
|
||||
self head do: [ :line |
|
||||
stream
|
||||
nextPutAll: ' ';
|
||||
nextPutAll: line;
|
||||
nextPut: Character lf
|
||||
].
|
||||
stream
|
||||
nextPutAll: '</head>';
|
||||
nextPut: Character lf.
|
||||
].
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language: anObject [
|
||||
language := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdeepScriptTag [
|
||||
^ '<script src="markdeep.min.js" charset="utf-8"></script>
|
||||
<script
|
||||
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
|
||||
charset="utf-8">
|
||||
</script>'
|
||||
]
|
||||
|
||||
{ #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: '<br>
|
||||
'
|
||||
]
|
||||
|
||||
{ #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: '<!-- Markdeep: -->'; lf; 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: self markdeepScriptTag; lf;
|
||||
nextPutAll: '<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>-->'.
|
||||
^ 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 ].
|
||||
^ ''
|
||||
]
|
||||
|
@ -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: '<!--@yaml:'; crlf.
|
||||
newContents nextPutAll: self yamlMetadataString.
|
||||
newContents nextPutAll: String cr.
|
||||
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 [
|
||||
<gtView>
|
||||
^ 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: '<!--@yaml:'; crlf.
|
||||
newContents nextPutAll: self yamlMetadataString.
|
||||
newContents nextPutAll: String cr.
|
||||
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 [
|
||||
<gtView>
|
||||
^ 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.
|
||||
]
|
||||
|
@ -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
|
||||
]
|
||||
|
@ -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'
|
||||
]
|
||||
|
@ -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'
|
||||
]
|
||||
|
@ -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 ]
|
||||
]
|
||||
|
@ -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
|
||||
]
|
||||
|
@ -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
|
||||
]
|
||||
|
@ -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 ])
|
||||
]
|
||||
|
@ -1 +1 @@
|
||||
Package { #name : #MiniDocs }
|
||||
Package { #name : #MiniDocs }
|
||||
|
Loading…
Reference in New Issue
Block a user