Compare commits

..

No commits in common. "master" and "gt-unsync1" have entirely different histories.

29 changed files with 358 additions and 710 deletions

View File

@ -13,7 +13,8 @@ BaselineOfMiniDocs >> baseline: spec [
"Dependencies" "Dependencies"
self setUpTeapot: spec. self setUpTeapot: spec.
self setUpPetitParser: spec. self setUpPetitParser: spec.
self setUpLepiterBuildingBlocs: spec. "working in v1.0.993" "LepiterBuildingBlocs commented while resolving the conflict with the internal gtoolkit renaming."
"self setUpLepiterBuildingBlocs: spec"
spec spec
baseline: 'Mustache' with: [ spec repository: 'github://noha/mustache' ]; baseline: 'Mustache' with: [ spec repository: 'github://noha/mustache' ];
baseline: 'Temple' with: [ spec repository: 'github://astares/Pharo-Temple/src' ]; baseline: 'Temple' with: [ spec repository: 'github://astares/Pharo-Temple/src' ];
@ -31,8 +32,7 @@ BaselineOfMiniDocs >> baseline: spec [
'Mustache' 'Temple' "Templating" 'Mustache' 'Temple' "Templating"
'Teapot' 'Tealight' "Web server" 'Teapot' 'Tealight' "Web server"
'PetitMarkdown' 'PetitParser' "Parsers" 'PetitMarkdown' 'PetitParser' "Parsers"
'DataFrame' "Tabular data" 'DataFrame' "Tabular data")].
'LepiterBuildingBlocs' "Lepiter utilities")].
. .
"Groups" "Groups"
@ -60,7 +60,10 @@ BaselineOfMiniDocs >> semanticVersion [
BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [ BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [
spec spec
baseline: 'LepiterBuildingBlocs' baseline: 'LepiterBuildingBlocs'
with: [spec repository: 'github://botwhytho/LepiterBuildingBlocs:main/src'] with: [spec
repository: 'github://botwhytho/LepiterBuildingBlocs:main/src';
loads: #('ALL')];
import: 'LepiterBuildingBlocs'
] ]
{ #category : #accessing } { #category : #accessing }

View File

@ -1,18 +0,0 @@
"
I model a possible bridge between TaskWarrior and MiniDocs. (starting DRAFT).
"
Class {
#name : #AcroReport,
#superclass : #Object,
#category : #MiniDocs
}
{ #category : #accessing }
AcroReport class >> project: projectName [
| jsonReport |
jsonReport := (GtSubprocessWithInMemoryOutput new
shellCommand: 'task project:', projectName , ' export';
runAndWait;
stdout).
^ STONJSON fromString: jsonReport
]

View File

@ -1,57 +0,0 @@
Class {
#name : #AlphanumCounter,
#superclass : #Object,
#instVars : [
'letters',
'digits',
'currentLetter',
'currentDigit'
],
#category : #MiniDocs
}
{ #category : #accessing }
AlphanumCounter >> current [
^ self currentLetter asString, self currentDigit asString
]
{ #category : #accessing }
AlphanumCounter >> currentDigit [
^ currentDigit ifNil: [ currentDigit := self digits first ]
]
{ #category : #accessing }
AlphanumCounter >> currentLetter [
^ currentLetter ifNil: [ currentLetter := self letters first ]
]
{ #category : #accessing }
AlphanumCounter >> currentLetterIndex [
^ self letters detectIndex: [:n | n = self currentLetter]
]
{ #category : #accessing }
AlphanumCounter >> digits [
^ digits ifNil: [ digits := 1 to: 9 ]
]
{ #category : #accessing }
AlphanumCounter >> digits: aNumbersArray [
digits := aNumbersArray
]
{ #category : #accessing }
AlphanumCounter >> increase [
(self currentDigit < self digits last)
ifTrue: [ currentDigit := currentDigit + 1 ]
ifFalse: [
currentLetter := self letters at: (self currentLetterIndex + 1).
currentDigit := self digits first
]
]
{ #category : #accessing }
AlphanumCounter >> letters [
^ letters ifNil: [ letters := $A to: $Z ]
]

View File

@ -26,20 +26,3 @@ Array >> bagOfWordsFor: sentenceArray [
]. ].
^ bagOfWords ^ bagOfWords
] ]
{ #category : #'*MiniDocs' }
Array >> replaceWithUniqueNilsAndBooleans [
| response |
(self includesAny: #(true false nil))
ifFalse: [ response := self ]
ifTrue: [ | newItem |
response := OrderedCollection new.
self do: [:item |
(item isBoolean or: [ item isNil ])
ifTrue: [ newItem := item asString, '-', (NanoID generate copyFrom: 1 to: 3) ]
ifFalse: [ newItem := item ].
response add: newItem.
].
].
^ response
]

View File

@ -1,12 +0,0 @@
Extension { #name : #ByteString }
{ #category : #'*MiniDocs' }
ByteString >> asHTMLComment [
^ '<!-- ', self , ' -->'
]
{ #category : #'*MiniDocs' }
ByteString >> email [
"Quick fix for importing Lepiter pages that have a plain ByteString field as email."
^ self
]

View File

@ -1,6 +0,0 @@
Extension { #name : #Dictionary }
{ #category : #'*MiniDocs' }
Dictionary >> treeView [
^ self asOrderedDictionary treeView
]

View File

@ -1,53 +0,0 @@
Extension { #name : #FileLocator }
{ #category : #'*MiniDocs' }
FileLocator class >> aliases [
| fileAliases |
fileAliases := self fileAliases.
fileAliases exists
ifFalse: [ | initialConfig |
initialConfig := Dictionary new.
fileAliases ensureCreateFile.
MarkupFile exportAsFileOn: fileAliases containing: initialConfig
].
^ STON fromString: fileAliases contents
]
{ #category : #'*MiniDocs' }
FileLocator class >> atAlias: aString put: aFolderOrFile [
| updatedAliases |
updatedAliases:= self aliases
at: aString put: aFolderOrFile;
yourself.
MarkupFile exportAsFileOn: self fileAliases containing: updatedAliases.
^ updatedAliases
]
{ #category : #'*MiniDocs' }
FileLocator >> extractMetadata [
"I package the functionality from [[How to extract meta information using ExifTool]],
from the GToolkit Book.
I depend on the external tool ExifTool."
| process variablesList |
process := GtSubprocessWithInMemoryOutput new
command: 'exiftool';
arguments: { self fullName}.
process errorBlock: [ :proc | ^ self error: 'Failed to run exiftool' ].
process runAndWait.
variablesList := process stdout lines collect: [ :currentLine |
| separatorIndex name value |
separatorIndex := currentLine indexOf: $:.
name := (currentLine copyFrom: 1 to: separatorIndex - 1) trimBoth.
value := (currentLine
copyFrom: separatorIndex + 1
to: currentLine size) trimBoth.
name -> value
].
^ variablesList asOrderedDictionary
]
{ #category : #'*MiniDocs' }
FileLocator class >> fileAliases [
^ MiniDocs appFolder / 'fileAliases.ston'
]

View File

@ -1,51 +1,5 @@
Extension { #name : #GtGQLSnippet } Extension { #name : #GtGQLSnippet }
{ #category : #'*MiniDocs' }
GtGQLSnippet >> asMarkdeep [
| output |
output := WriteStream on: ''.
(self metadata)
at: 'operation' put: self operation;
at: 'input' put: self input;
at: 'context' put: self context;
yourself.
output
nextPutAll: self metadataDiv;
nextPutAll: self markdeepCustomOpener;
nextPutAll: self asMarkdownString;
nextPut: Character lf;
nextPutAll: self markdeepCustomCloser;
nextPut: Character lf;
nextPutAll: '</div>';
nextPut: Character lf;
nextPut: Character lf.
^ output contents withInternetLineEndings
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> markdeepCustomCloser [
^ self markdeepCustomOpener
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> markdeepCustomOpener [
^ '* * *'
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> metadataDiv [
"PENDING: Shared among several snippets. Should be abstracted further?"
| output |
output := WriteStream on: ''.
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf.
^ output contents withInternetLineEndings.
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
GtGQLSnippet >> metadataUpdate [ GtGQLSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized | | createEmailSanitized editEmailSanitized |
@ -53,7 +7,7 @@ GtGQLSnippet >> metadataUpdate [
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters. editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new ^ OrderedDictionary new
at: 'id' put: self uidString; at: 'id' put: self uidString;
at: 'parent' put: self parent uid asString36; at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString; at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString; at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized; at: 'creator' put: createEmailSanitized;

View File

@ -29,36 +29,15 @@ HedgeDoc class >> newDefault [
{ #category : #accessing } { #category : #accessing }
HedgeDoc >> asLePage [ HedgeDoc >> asLePage [
| newPage sanitizedMarkdown | | newPage snippet |
sanitizedMarkdown := self bodyWithoutTitleHeader promoteMarkdownHeaders. snippet := LeTextSnippet new
string: self bodyWithoutTitleHeader promoteMarkdownHeaders.
newPage := LePage new newPage := LePage new
initializeTitle: self title. initializeTitle: self title;
sanitizedMarkdown := sanitizedMarkdown markdownSplitted.
sanitizedMarkdown class = OrderedCollection ifTrue: [
sanitizedMarkdown do: [:lines | | snippet |
snippet := LeTextSnippet new
string: lines asStringWithCr;
uid: LeUID new.
newPage
addSnippet: snippet; addSnippet: snippet;
yourself yourself.
] newPage incomingLinks.
]. newPage metadata addAll: self metadata.
sanitizedMarkdown class = ByteString ifTrue: [ | snippet |
snippet := LeTextSnippet new
string: sanitizedMarkdown;
uid: LeUID new.
newPage
addSnippet: snippet;
yourself
].
newPage
incomingLinks;
splitAdmonitionSnippets.
newPage editTime: DateAndTime now.
newPage options
at: 'HedgeDoc' at: 'yamlFrontmatter' put: self metadata;
at: 'HedgeDoc' at: 'url' put: self url asString asHTMLComment.
^ newPage ^ newPage
] ]
@ -172,7 +151,7 @@ HedgeDoc >> server: aUrlString [
{ #category : #accessing } { #category : #accessing }
HedgeDoc >> url [ HedgeDoc >> url [
url ifNotNil: [ ^ url asUrl ] ^ url asUrl
] ]
{ #category : #accessing } { #category : #accessing }
@ -183,6 +162,7 @@ HedgeDoc >> url: anObject [
(html xpath: '//head/meta[@name="application-name"][@content = "HedgeDoc - Ideas grow better together"]') isEmpty (html xpath: '//head/meta[@name="application-name"][@content = "HedgeDoc - Ideas grow better together"]') isEmpty
ifTrue: [ self inform: 'Not a hedgedoc url'. ifTrue: [ self inform: 'Not a hedgedoc url'.
url := nil ]. url := nil ].
self metadata at: 'title' put: tempUrl firstPathSegment.
server := tempUrl host. server := tempUrl host.
url := anObject url := anObject
] ]

View File

@ -37,39 +37,40 @@ LeDatabase >> addPageCopy: aLePage [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [ LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets dataSnippets page | | remoteMetadata divSnippets dataSnippets snippets page |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ]. collect: [ :xmlElement | xmlElement postCopy ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree. remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
"Ensuring remote metadata has consistent data"
remoteMetadata at: 'origin' put: externalDocLocation. remoteMetadata at: 'origin' put: externalDocLocation.
remoteMetadata at: 'title' ifAbsentPut: [ markdeepDocTree detectMarkdeepTitle ].
remoteMetadata at: 'id' ifAbsentPut: [UUID new asString36].
remoteMetadata at: 'created' ifAbsentPut: [ DateAndTime now] .
remoteMetadata at: 'creator' ifAbsentPut: [ 'unknown' ].
remoteMetadata at: 'modified' ifAbsentPut: [ DateAndTime now].
remoteMetadata at: 'modifier' ifAbsentPut: [ 'unknown' ].
dataSnippets := self sanitizeMarkdeepSnippets: divSnippets withMetadata: remoteMetadata. dataSnippets := self sanitizeMarkdeepSnippets: divSnippets withMetadata: remoteMetadata.
snippets := dataSnippets collect: [ :each | each asLepiterSnippet ].
page := LePage new. page := LePage new.
page fromDictionary: remoteMetadata. page
dataSnippets do: [:each | | snippet| title: (remoteMetadata at: 'title' ifAbsent: [ page detectMarkdeepTitleFrom: markdeepDocTree ]);
snippet := each asLepiterSnippet. basicUid: (UUID fromString36: (remoteMetadata at: 'id' ifAbsent: [UUID new asString36]));
createTime: (LeTime new
time: (remoteMetadata at: 'created' ifAbsent: [ DateAndTime now]) asDateAndTime);
editTime: (LeTime new
time: (remoteMetadata at: 'modified' ifAbsent: [ DateAndTime now]) asDateAndTime);
latestEditTime: (LeTime new
time: (remoteMetadata at: 'modified' ifAbsent: [ DateAndTime now]) asDateAndTime);
createEmail: (remoteMetadata at: 'creator' ifAbsent: [ 'unknown' ]);
editEmail: (remoteMetadata at: 'modifier' ifAbsent: [ 'unknown' ]).
snippets do: [ :snippet | "| currentParent |"
page addSnippet: snippet. page addSnippet: snippet.
"currentParent := page detectParentSnippetWithUid: (snippet metadata at: 'parent').
snippet parent: currentParent."
]. ].
page children page children
do: [ :snippet | do: [ :snippet |
(self hasBlockUID: snippet uid) (self hasBlockUID: snippet uid)
ifTrue: [ | existingPage | ifTrue: [ | existingPage |
existingPage := self pages existingPage := self pages
detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ] detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ].
ifFound: [
self importErrorForLocal: existingPage withRemote: externalDocLocation. self importErrorForLocal: existingPage withRemote: externalDocLocation.
^ self ^ self ]
] ifFalse: [ snippet database: self.
ifNone: [ snippet database: self ]. self registerSnippet: snippet ] ].
]
ifFalse: [ snippet database: self ]
].
self addPage: page. self addPage: page.
^ page ^ page
] ]

View File

@ -3,7 +3,7 @@ Extension { #name : #LePage }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asHtmlFile [ LePage >> asHtmlFile [
self asMarkdownFileWithMetadataWrappers. self asMarkdownFile.
self defaultPandocTemplate exists self defaultPandocTemplate exists
ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ]. ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ].
@ -21,7 +21,6 @@ LePage >> asHtmlFile [
LePage >> asMarkdeep [ LePage >> asMarkdeep [
| bodyStream markdeep | | bodyStream markdeep |
bodyStream := '' writeStream. bodyStream := '' writeStream.
bodyStream nextPutAll: self notebookMetadataSnippet asMarkdeep.
self preorderTraversal self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdeep ]. do: [ :snippet | bodyStream nextPutAll: snippet asMarkdeep ].
markdeep := Markdeep new markdeep := Markdeep new
@ -48,7 +47,6 @@ LePage >> asMarkdeepFile [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdown [ LePage >> asMarkdown [
"PENDING: to debug the output."
| bodyStream markdown | | bodyStream markdown |
bodyStream := '' writeStream. bodyStream := '' writeStream.
bodyStream bodyStream
@ -56,13 +54,13 @@ LePage >> asMarkdown [
self preorderTraversal self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ]. do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ].
markdown := Markdown new markdown := Markdown new
contents: bodyStream contents promoteMarkdownHeaders; contents: bodyStream contents demoteMarkdownHeaders;
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new). metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
^ markdown ^ markdown
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> asMarkdownFileWithMetadataWrappers [ LePage >> asMarkdownFile [
| folder | | folder |
folder := self storage. folder := self storage.
^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdownWithMetadataWrappers contents ^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdownWithMetadataWrappers contents
@ -72,15 +70,13 @@ LePage >> asMarkdownFileWithMetadataWrappers [
LePage >> asMarkdownWithMetadataWrappers [ LePage >> asMarkdownWithMetadataWrappers [
| bodyStream markdown | | bodyStream markdown |
bodyStream := '' writeStream. bodyStream := '' writeStream.
"bodyStream bodyStream
nextPut: Character lf; nextPutAll: '# ', self title; cr; cr.
nextPutAll: '# ', self title; cr; cr."
self preorderTraversal self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdownWithMetadataWrappers ]. do: [ :snippet | bodyStream nextPutAll: snippet asMarkdownWithMetadataWrappers ].
markdown := Markdown new markdown := Markdown new
contents: bodyStream contents promoteMarkdownHeaders; contents: bodyStream contents demoteMarkdownHeaders;
title: self title; metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
metadata: self metadata.
^ markdown ^ markdown
] ]
@ -99,6 +95,14 @@ LePage >> defaultPandocTemplate [
^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html' ^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html'
] ]
{ #category : #'*MiniDocs' }
LePage >> detectMarkdeepTitleFrom: xmlSubtree [
| titleLine |
titleLine := (xmlSubtree nodesCollect: [:node | node contentString ]) first lines
detect: [:line | line includesSubstring: ' **'] ifNone: ['Untitled'].
^ titleLine trimmed trimBoth: [:char | char = $* ]
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> detectParentSnippetWithUid: uidString [ LePage >> detectParentSnippetWithUid: uidString [
uidString = self uid asString36 ifTrue: [ ^ self ]. uidString = self uid asString36 ifTrue: [ ^ self ].
@ -120,33 +124,11 @@ LePage >> exportMetadataToHead: markdeep [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> exportedFileName [ LePage >> exportedFileName [
| sanitized titleWords shortTitle | | sanitized |
titleWords := self title splitOn: Character space. sanitized := self title asDashedLowercase romanizeAccents copyWithoutAll: #($/ $: $🢒).
(titleWords size > 11)
ifTrue: [
titleWords := titleWords copyFrom: 1 to: 3.
shortTitle := titleWords joinUsing: Character space.
]
ifFalse: [shortTitle := self title].
sanitized := shortTitle asDashedLowercase romanizeAccents copyWithoutAll: #($/ $: $🢒 $,).
^ sanitized , '--' , (self uidString copyFrom: 1 to: 5) ^ sanitized , '--' , (self uidString copyFrom: 1 to: 5)
] ]
{ #category : #'*MiniDocs' }
LePage >> fromDictionary: aDictionary [
self
title: (aDictionary at: 'title');
basicUid: (UUID fromString36: (aDictionary at: 'id'));
createTime: (LeTime new
time: (aDictionary at: 'created') asDateAndTime);
editTime: (LeTime new
time: (aDictionary at: 'modified') asDateAndTime);
latestEditTime: (LeTime new
time: (aDictionary at: 'modified') asDateAndTime);
createEmail: (aDictionary at: 'creator');
editEmail: (aDictionary at: 'modifier').
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> fromMarkdeepUrl: aString [ LePage >> fromMarkdeepUrl: aString [
| docTree pageMetadata | | docTree pageMetadata |
@ -180,10 +162,7 @@ LePage >> latestEditTime: aLeTime [
LePage >> localHostAddress [ LePage >> localHostAddress [
| localUrl route | | localUrl route |
MiniDocsServer teapot server isRunning ifFalse: [ MiniDocsServer restart ]. MiniDocsServer teapot server isRunning ifFalse: [ MiniDocsServer restart ].
route := self storage path segments joinUsing: '/'. route := MiniDocsServer teapot staticRouter prefix joinUsing: '/'.
MiniDocsServer teapot
serveStatic: ('/', route, '/', self markdeepFileName)
from: self storage / self markdeepFileName.
localUrl := MiniDocsServer teapot server localUrl asString. localUrl := MiniDocsServer teapot server localUrl asString.
^ localUrl, route, '/', self markdeepFileName ^ localUrl, route, '/', self markdeepFileName
] ]
@ -227,17 +206,6 @@ LePage >> navTop [
ifTrue: [ ^ topNavFile contents ] ifTrue: [ ^ topNavFile contents ]
] ]
{ #category : #'*MiniDocs' }
LePage >> notebookMetadataSnippet [
| response |
response := LeTextSnippet new fromString: '<!-- See this snippet source code for this notebook''s metadata -->'.
response parent: self.
self optionAt: 'HedgeDoc' ifAbsent: [ ^ response ].
(response extra)
at: 'HedgeDoc' put: (self optionAt: 'HedgeDoc').
^ response
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> olderChild [ LePage >> olderChild [
"I provide the last edited child node. "I provide the last edited child node.
@ -297,25 +265,6 @@ LePage >> sharedVariablesBindings [
^ shared asDictionary ^ shared asDictionary
] ]
{ #category : #'*MiniDocs' }
LePage >> splitAdmonitionSnippets [
"I'm used to clean after importing from HedgeDoc to ensure that a snippet contains only admonitions and extra content is put in a new cell."
| admonitionSnippets |
admonitionSnippets := self children select: [:node | node string startsWithMarkdownAdmonition ].
admonitionSnippets ifEmpty: [ ^ self ].
admonitionSnippets do: [:node | | nodeContent |
node ifNotNil: [
nodeContent := node string.
nodeContent startsWithMarkdownAdmonition
ifTrue: [ | snippetCommand |
snippetCommand := node splitSnippetCommandAtPosition: nodeContent admonitionEndingPosition.
snippetCommand execute.
node tagWith: (nodeContent lines first trimBoth withoutPrefix: ':::')
]
]
]
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePage >> storage [ LePage >> storage [
| current | | current |

View File

@ -8,17 +8,6 @@ LePharoSnippet >> contentAsStringCustomized [
^ thisObject perform: self detectMessage trimmed asSymbol. ^ thisObject perform: self detectMessage trimmed asSymbol.
] ]
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromDictionary: anOrderedDictionary [
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator').
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> fromMarkdeep: markdeepDiv [ LePharoSnippet >> fromMarkdeep: markdeepDiv [
@ -28,7 +17,7 @@ LePharoSnippet >> fromMarkdeep: markdeepDiv [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePharoSnippet >> fromString: aString [ LePharoSnippet >> fromString: aString [
[ self coder forSource: aString ] onErrorDo: [ ] self code: aString
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }

View File

@ -41,37 +41,22 @@ LePictureSnippet >> contentFrom: markdeepDiv [
self urlString: (markdeepDiv // 'img' @ 'src') stringValue. self urlString: (markdeepDiv // 'img' @ 'src') stringValue.
] ]
{ #category : #'*MiniDocs' }
LePictureSnippet >> fromDictionary: anOrderedDictionary [
| sanitizedUrl|
sanitizedUrl := (anOrderedDictionary at: 'url').
sanitizedUrl := sanitizedUrl copyFrom: 5 to: sanitizedUrl size - 3.
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator');
urlString: sanitizedUrl;
caption: (anOrderedDictionary at: 'content') first
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePictureSnippet >> fromMarkdeep: markdeepDiv [ LePictureSnippet >> fromMarkdeep: markdeepDiv [
^ markdeepDiv asSnippetDictionary asLepiterSnippet ^ markdeepDiv asSnippetDictionary asLepiterSnippet
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LePictureSnippet >> fromString: aStringArray [ LePictureSnippet >> fromString: aString [
"aStringArray should contain as first element the sanitized string and "aString should be a valid Markdown/Markdeep image string"
as second the full original image Link string, which may contains links in the description." | args captionTemp urlTemp |
| args urlTemp |
args := aStringArray second splitOn: ']('. args := aString splitOn: ']('.
urlTemp := args last. captionTemp := args first.
captionTemp := captionTemp copyFrom: 3 to: captionTemp size.
urlTemp := args second.
urlTemp := urlTemp copyFrom: 1 to: urlTemp size - 1. urlTemp := urlTemp copyFrom: 1 to: urlTemp size - 1.
self caption: aStringArray first. self caption: captionTemp.
self urlString: urlTemp. self urlString: urlTemp.
^ self ^ self
] ]

View File

@ -13,18 +13,7 @@ LeSnippet class >> fromMetaMarkdeep: div [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeSnippet >> metadata [ LeSnippet >> metadata [
| createEmailSanitized editEmailSanitized | ^ self metadataUpdate
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
self optionAt: 'metadata' ifAbsentPut: [ OrderedDictionary new ].
^ (self optionAt: 'metadata')
at: 'id' put: self uidString;
at: 'parent' put: self parent uid asString36;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }

View File

@ -1,25 +1,5 @@
Extension { #name : #LeTextCoderSnippetElement } Extension { #name : #LeTextCoderSnippetElement }
{ #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> asLePage [
| currentSnippet newPage |
currentSnippet := self snippet.
newPage := LePage new.
newPage
title: (currentSnippet text asString trimLeft: [:char | char = $# ]) trim.
self page database
addPage: newPage.
currentSnippet allChildrenBreadthFirstDo: [:child |
child moveToPageTitled: newPage title.
].
^ newPage
]
{ #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> asSnippetViewModel [
^ self snippetContent
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> moveToPageTitled: pageName [ LeTextCoderSnippetElement >> moveToPageTitled: pageName [
| db origin destination | | db origin destination |

View File

@ -18,17 +18,6 @@ LeTextSnippet >> asLePage [
^ page. ^ page.
] ]
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromDictionary: anOrderedDictionary [
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator')
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> fromMarkdeep: markdeepDiv [ LeTextSnippet >> fromMarkdeep: markdeepDiv [
@ -38,9 +27,7 @@ LeTextSnippet >> fromMarkdeep: markdeepDiv [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> fromString: aString [ LeTextSnippet >> fromString: aString [
self self string: aString
string: aString;
uid: LeUID new.
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
@ -62,18 +49,7 @@ LeTextSnippet >> parentId [
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextSnippet >> tagWith: aString [ LeTextSnippet >> taggedWith: aString [
self tags add: aString. self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
] ^ self metadata at: 'tags'
{ #category : #'*MiniDocs' }
LeTextSnippet >> withFollowingSnippets [
"I'm the same method implemented for PharoSnippets,
but present also here as a way to improve moving prose snippets from pages.
"
| snippets stop start |
snippets := self parent children asArray.
start := snippets indexOf: self.
stop := snippets size.
^ snippets copyFrom: start to: stop
] ]

View File

@ -2,11 +2,16 @@ Extension { #name : #LeTextualSnippet }
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdeep [ LeTextualSnippet >> asMarkdeep [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents."
| output | | output |
output := WriteStream on: ''. output := WriteStream on: ''.
output output
nextPutAll: self metadataDiv; nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf;
nextPutAll: self markdeepCustomOpener; nextPutAll: self markdeepCustomOpener;
nextPutAll: self contentAsStringAnnotated; nextPutAll: self contentAsStringAnnotated;
nextPut: Character lf; nextPut: Character lf;
@ -34,18 +39,44 @@ LeTextualSnippet >> asMarkdownWithMetadataWrappers [
| output | | output |
output := '' writeStream. output := '' writeStream.
output output
nextPutAll: self metadataDiv; nextPutAll: '<div st-class="', self class asString, '"'; lf;
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
nextPutAll: self markdownCustomOpener; nextPutAll: self markdownCustomOpener;
nextPutAll: self contentAsStringCustomized; lf; nextPutAll: self contentAsStringCustomized; lf;
nextPutAll: self markdownCustomCloser; nextPutAll: self markdownCustomCloser;
nextPutAll: '</div>'; lf; lf. nextPutAll: '</div>'; lf; lf.
^ output contents withInternetLineEndings ^ output contents
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> contentAsStringAnnotated [ LeTextualSnippet >> contentAsStringAnnotated [
self ast ifNotNil: [ ^ self processSnippetAnnotations ]. | annotations substitutions exported pageConfig|
^ self contentAsString self ast ifNil: [ ^ self contentAsString ].
annotations := self ast parts select: [:each | each className includesSubstring: 'AnnotationNode' ].
annotations ifEmpty: [ ^ self contentAsString ].
substitutions := OrderedDictionary new.
pageConfig := self page config.
annotations do: [ :each | | key type value color |
key := each source.
type := (key splitOn: ':') first copyWithoutAll: '{{'.
value := key copyFrom: type size + 4 to: key size - 2.
pageConfig
ifNil: [ color := 'default' ]
ifNotNil: [ | colors |
colors := pageConfig at: 'annotationColors' ifAbsent: [ nil ].
colors ifNotNil: [
color := colors at: type ifAbsent: [ colors at: 'defaultColor' ifAbsentPut: ['default'] ]
]
].
substitutions
at: key
put: '<span st-class="',type,'" style="color:', color, '">', value,'</span>'.
].
exported := self contentAsString.
substitutions keysAndValuesDo: [:k :v |
exported := exported copyReplaceAll: k with: v.
].
^ exported
] ]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
@ -55,11 +86,6 @@ LeTextualSnippet >> contentAsStringCustomized [
ifFalse: [ ^ self contentAsString ] ifFalse: [ ^ self contentAsString ]
] ]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> extra [
^ self optionAt: 'extra' ifAbsentPut: [ Dictionary new ]
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> markdeepCustomCloser [ LeTextualSnippet >> markdeepCustomCloser [
^ '' ^ ''
@ -86,21 +112,6 @@ LeTextualSnippet >> metadata [
^ self metadataUpdate ^ self metadataUpdate
] ]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> metadataDiv [
"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."
"PENDING: this is repeated in several snippets. Can be abstracted up in a common object of the class hierarchy?"
| output |
output := WriteStream on: ''.
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf.
^ output contents withInternetLineEndings.
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> metadataUpdate [ LeTextualSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized | | createEmailSanitized editEmailSanitized |
@ -108,46 +119,14 @@ LeTextualSnippet >> metadataUpdate [
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters. editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new ^ OrderedDictionary new
at: 'id' put: self uidString; at: 'id' put: self uidString;
at: 'parent' put: (self parent ifNotNil: [self parent uidString ]); at: 'parent' put: self parent uidString;
at: 'created' put: self createTime asString; at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString; at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized; at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized; at: 'modifier' put: editEmailSanitized;
at: 'extra' put: self extra;
yourself yourself
] ]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> processSnippetAnnotations [
| exported substitutions annotations pageConfig |
annotations := self ast parts
select: [ :each | each className includesSubstring: 'AnnotationNode' ].
annotations ifEmpty: [ ^ self contentAsString ].
substitutions := OrderedDictionary new.
pageConfig := self page config.
annotations
do: [ :each |
| key type value color |
key := each source.
type := (key splitOn: ':') first copyWithoutAll: '{{'.
value := key copyFrom: type size + 4 to: key size - 2.
pageConfig
ifNil: [ color := 'default' ]
ifNotNil: [ | colors |
colors := pageConfig at: 'annotationColors' ifAbsent: [ nil ].
colors
ifNotNil: [ color := colors
at: type
ifAbsent: [ colors at: 'defaultColor' ifAbsentPut: [ 'default' ] ] ] ].
substitutions
at: key
put: '<span st-class="' , type , '" style="color:' , color , '">' , value , '</span>' ].
exported := self contentAsString.
substitutions
keysAndValuesDo: [ :k :v | exported := exported copyReplaceAll: k with: v ].
^ exported
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> sanitizeMetadata [ LeTextualSnippet >> sanitizeMetadata [
self options ifNil: [^ self ]. self options ifNil: [^ self ].
@ -162,5 +141,5 @@ LeTextualSnippet >> sanitizeMetadata [
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
LeTextualSnippet >> tags [ LeTextualSnippet >> tags [
^ self extra at: 'tags' ifAbsentPut: [ Set new ] ^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
] ]

View File

@ -4,7 +4,7 @@ Class {
#instVars : [ #instVars : [
'folder' 'folder'
], ],
#category : #'MiniDocs-Model' #category : #MiniDocs
} }
{ #category : #accessing } { #category : #accessing }
@ -29,5 +29,5 @@ Logseq >> journals [
{ #category : #accessing } { #category : #accessing }
Logseq >> pages [ Logseq >> pages [
^self folder/ 'pages' self folder/ 'pages'
] ]

View File

@ -27,15 +27,6 @@ Markdown >> asMarkdeep [
commentYAMLMetadata commentYAMLMetadata
] ]
{ #category : #accessing }
Markdown >> asMarkdownTiddler [
^ Tiddler new
title: self title;
text: self contents;
type: 'text/x-markdown';
created: Tiddler nowLocal.
]
{ #category : #accessing } { #category : #accessing }
Markdown >> body [ Markdown >> body [
^ body ^ body
@ -49,7 +40,7 @@ Markdown >> body: aString [
{ #category : #operation } { #category : #operation }
Markdown >> commentYAMLMetadata [ Markdown >> commentYAMLMetadata [
| newContents | | newContents |
self contents detectYAMLMetadata ifFalse: [ ^ self ]. self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream. newContents := '' writeStream.
newContents nextPutAll: '<!--@yaml'; lf. newContents nextPutAll: '<!--@yaml'; lf.
newContents nextPutAll: self yamlMetadataString. newContents nextPutAll: self yamlMetadataString.
@ -66,15 +57,14 @@ Markdown >> containsYAMLMetadataClosing [
{ #category : #accessing } { #category : #accessing }
Markdown >> contents [ Markdown >> contents [
| response metadataString | | response |
response := WriteStream on: ''. response := WriteStream on: ''.
metadataString := self metadataAsYAML
ifEmpty: [ '' ]
ifNotEmpty: [ '---', String cr, self metadataAsYAML, String cr, '---', String cr ].
response response
nextPutAll: metadataString; nextPutAll: '---'; cr;
nextPutAll: (self body ifNil: [ '' ]). nextPutAll: self metadataAsYAML; cr;
^ response contents withInternetLineEndings nextPutAll: '---'; cr;
nextPutAll: self body.
^ response contents
] ]
{ #category : #accessing } { #category : #accessing }
@ -142,7 +132,7 @@ Markdown >> exportMetadataAsYaml [
{ #category : #accessing } { #category : #accessing }
Markdown >> file [ Markdown >> file [
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md' ] ^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md.html' ]
] ]
{ #category : #accessing } { #category : #accessing }
@ -159,18 +149,8 @@ Markdown >> fromFile: aFileReference [
{ #category : #'instance creation' } { #category : #'instance creation' }
Markdown >> fromString: markdownString [ Markdown >> fromString: markdownString [
| yamlMetadataRaw bodyTemp | (self metadata) at: 'original' put: markdownString yamlMetadata.
yamlMetadataRaw := (YamlHeaderParser parse: markdownString). self body: markdownString contentsWithoutYAMLMetadata
bodyTemp := '' writeStream.
(yamlMetadataRaw removeKey: 'body') do: [:paragraph |
bodyTemp nextPutAll: paragraph; cr; cr
].
self body: bodyTemp contents withInternetLineEndings.
(yamlMetadataRaw sanitizeMultilineValuesWith: markdownString)
ifNotNil: [
self metadata
ifEmpty: [ self metadata: yamlMetadataRaw ]
ifNotEmpty: [ self metadata at: 'hedgeDoc' put: yamlMetadataRaw ]].
] ]
{ #category : #accessing } { #category : #accessing }
@ -200,7 +180,6 @@ Markdown >> metadata [
^ metadata ifNil: [ metadata := Dictionary new]. ^ metadata ifNil: [ metadata := Dictionary new].
] ]
{ #category : #accessing } { #category : #accessing }
@ -240,8 +219,3 @@ Markdown >> printOn: aStream [
Markdown >> title [ Markdown >> title [
^ title ifNil: [ title:= self headerAsTitle ] ^ title ifNil: [ title:= self headerAsTitle ]
] ]
{ #category : #accessing }
Markdown >> title: aString [
title := aString
]

View File

@ -34,8 +34,11 @@ MiniDocs class >> altShiftRightCombo [
{ #category : #accessing } { #category : #accessing }
MiniDocs class >> appFolder [ MiniDocs class >> appFolder [
| tempFolder | | tempFolder userDataFolder |
tempFolder := ExoRepo userDataFolder / 'Mutabit' / 'MiniDocs'. userDataFolder := Smalltalk os isWindows
ifTrue: [ FileLocator home / 'AppData' / 'Local' ]
ifFalse: [ FileLocator userData ].
tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'.
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ]. tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
^ tempFolder ^ tempFolder
] ]

View File

@ -0,0 +1,64 @@
"
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..
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #NanoID,
#superclass : #Object,
#category : #'MiniDocs-External'
}
{ #category : #accessing }
NanoID class >> binaryFile [
^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension
]
{ #category : #accessing }
NanoID class >> generate [
self binaryFile exists ifFalse: [ NanoID install].
Smalltalk os isWindows
ifTrue: [ ^ (LibC resultOfCommand:self binaryFile fullName) copyWithoutAll: (Character lf asString) ].
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'.
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nanoid c ',self scriptSourceCode fullName ].
OSSUnixSubprocess new
command: 'nim';
arguments: {'c'. self scriptSourceCode fullName};
runAndWaitOnExitDo: [ :process :outString |
(self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveToPageTitled: MiniDocs appFolder asFileReference.
^ MiniDocs appFolder ]
]
{ #category : #accessing }
NanoID class >> isInstalled [
^ self binaryFile exists
]
{ #category : #accessing }
NanoID class >> scriptSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim'
]

View File

@ -0,0 +1,89 @@
"
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-External'
}
{ #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.
Smalltalk os isWindows
ifTrue: [ ^ LibC runCommand: 'nimble install ', packageName ].
OSSUnixSubprocess new
command: 'nimble';
arguments: {'install'.
packageName};
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ Transcript show: 'Command exited correctly with output: ', outString. ]
ifFalse: [
^ 'Command exit with error status: ', process exitStatusInterpreter printString, String cr,
'Stderr contents: ', errString.
]
]
]
{ #category : #accessing }
Nimble class >> installPackagesList [
(FileLocator home / '.nimble' / 'packages_official.json') exists
ifTrue: [ ^ self ].
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('refresh');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ].
].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nimble refresh' ]
]
{ #category : #accessing }
Nimble class >> installed [
Smalltalk os isWindows
ifTrue: [ | process |
process := GtExternalProcessBuilder new
command: 'nimble.exe';
args: #('list' '--installed');
output.
^ process stdout lines ].
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 ]
]

View File

@ -10,18 +10,19 @@ OrderedDictionary >> asLepiterSnippet [
| response | | response |
self at: 'className' ifAbsent: [ ^ nil ]. self at: 'className' ifAbsent: [ ^ nil ].
response := (self at: 'className') asClass new. response := (self at: 'className') asClass new.
[ response fromDictionary: self ] onErrorDo: [ ]. response fromString: (self at: 'content').
[ response fromString: (self at: 'content') ] onErrorDo: [ ]. response
uid: (LeUID new uidString: (self at: 'id'));
parent: (self at: 'parent');
createTime: (LeTime new time: ((self at: 'created')asDateAndTime));
editTime: (LeTime new time: ((self at: 'modified') asDateAndTime));
editEmail: (self at: 'modifier');
createEmail: (self at: 'creator').
self at: 'origin' ifPresent: [ response metadata at: 'origin' put: (self at: 'origin') ]. self at: 'origin' ifPresent: [ response metadata at: 'origin' put: (self at: 'origin') ].
self at: 'errata' ifPresent: [ response metadata at: 'errata' put: (self at: 'errata') ]. self at: 'errata' ifPresent: [ response metadata at: 'errata' put: (self at: 'errata') ].
^ response ^ response
] ]
{ #category : #'*MiniDocs' }
OrderedDictionary >> asYAML [
^ (YQ jsonToYaml: self) accentedCharactersCorrection.
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
OrderedDictionary >> errata [ OrderedDictionary >> errata [
^ self at: 'errata' ifAbsentPut: [ OrderedCollection new] ^ self at: 'errata' ifAbsentPut: [ OrderedCollection new]
@ -32,69 +33,3 @@ OrderedDictionary >> redefineTimestampsBefore: dateAndTime [
self at: 'modified' put: dateAndTime asDateAndTime. self at: 'modified' put: dateAndTime asDateAndTime.
self at: 'created' put: dateAndTime asDateAndTime - 1 second. self at: 'created' put: dateAndTime asDateAndTime - 1 second.
] ]
{ #category : #'*MiniDocs' }
OrderedDictionary >> replaceNilsWith: aCharacter [
self associationsDo: [:each |
each value ifNil: [self at: each key put: aCharacter].
each value isDictionary ifTrue: [each value replaceNilsWith: aCharacter].
each value isArray ifTrue: [ | newArray|
newArray := (each value asDataSeries replaceNilsWith: aCharacter) asArray.
self at: each key put: newArray
]
]
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> replaceWithUniqueNilsAndBooleansStartingAt: anInteger [
| totalNils shortUID |
totalNils := self flattened asDataSeries countNils.
shortUID := [NanoID generate copyFrom: 1 to: 3].
self associations doWithIndex: [:assoc :i | | subIndex |
subIndex := anInteger asString, '-', i asString.
assoc value
ifNil: [ self at: assoc key put: 'nil-', subIndex ].
assoc value isBoolean
ifTrue: [ self at: assoc key put: assoc value asString, '-', subIndex ].
assoc value isDictionary ifTrue: [assoc replaceWithUniqueNilsAndBooleansStartingAt: i].
assoc value isArray
ifTrue: [ self at: assoc key put: (assoc value replaceWithUniqueNilsAndBooleans)]
]
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> sanitizeMultilineValuesWith: aString [
| toSanitize response |
toSanitize := OrderedCollection new.
response := OrderedCollection new.
self keysAndValuesDo: [:k :v |
(v isString and: [v lines size > 1])
ifTrue: [
aString lines
detect: [:line | line includesSubstring: k ]
ifFound: [:line | | sanitized|
sanitized := (line withoutPrefix: k, ':'), String cr,
v indentedWithExtraSpaces: 4.
self at: k put: sanitized ]
]
].
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> treeView [
| view |
view := GtMondrian new.
view nodes
stencil: [ :x |
BlElement new
border: (BlBorder paint: Color black);
geometry: BlEllipseGeometry new;
layout: (BlLinearLayout new alignCenter);
addChild: (BlTextElement text: (x asRopedText fontSize: 10)) ];
with: (self flatCollectAsSet: #yourself) , self keys.
view edges
stencil: [ :x | BlLineElement new border: (BlBorder paint: (Color blue alpha: 0.5) width: 4) ];
connect: self associations from: #key toAll: #value.
view layout tree.
^ view
]

View File

@ -147,11 +147,17 @@ Pandoc class >> markdownToHtmlOnUnix: inputFile [
outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html'). outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html').
outputFile ensureDelete. outputFile ensureDelete.
outputFile ensureCreateFile. outputFile ensureCreateFile.
GtSubprocessWithInMemoryOutput new OSSUnixSubprocess new
shellCommand: 'pandoc -f markdown+startnum+task_lists --standalone -t html ', inputFile fullName, ' --output ', outputFile fullName; command: 'pandoc';
runAndWait; arguments: {'-f'. 'markdown+startnum+task_lists'. '--standalone'. '-t'. 'html'. inputFile fullName.
stdout. '--output'. outputFile fullName };
^ outputFile. redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outputFile ]
ifFalse: [ ^ inputFile ]
]
] ]
{ #category : #converters } { #category : #converters }

View File

@ -11,7 +11,7 @@ Class {
'footnoteLabel', 'footnoteLabel',
'footnoteContent' 'footnoteContent'
], ],
#category : #'MiniDocs-Model' #category : #MiniDocs
} }
{ #category : #accessing } { #category : #accessing }

View File

@ -4,7 +4,7 @@ Extension { #name : #String }
String >> accentedCharactersCorrection [ String >> accentedCharactersCorrection [
| modified corrections | | modified corrections |
corrections := { corrections := {
'ó' -> 'ó' . “' -> 'Ó' . º' -> 'ú' . 'ñ' -> 'ñ' . 'Ñ' -> 'Ñ' . 'ó' -> 'ó' . º' -> 'ú' . 'ñ' -> 'ñ' .
'í' -> 'í' . 'á' -> 'á' . 'é' -> 'é' . '’' -> $' asString} asDictionary. 'í' -> 'í' . 'á' -> 'á' . 'é' -> 'é' . '’' -> $' asString} asDictionary.
modified := self copy. modified := self copy.
corrections keysAndValuesDo: [ :k :v | corrections keysAndValuesDo: [ :k :v |
@ -13,41 +13,6 @@ String >> accentedCharactersCorrection [
^ modified ^ modified
] ]
{ #category : #'*MiniDocs' }
String >> admonitionBorderLines [
| response |
response := OrderedDictionary new.
self lines doWithIndex: [:line :index |
(self admonitionBorders includes: line trimBoth)
ifTrue: [ response at: index put: line trimBoth ]
].
^ response
]
{ #category : #'*MiniDocs' }
String >> admonitionBorders [
"For the moment I only work with the admonition starting border
as adding the closing one would imply to redo the #markdownSplitted
method implementing a proper parser, which, ATM is overkill."
| response |
response := #('info' 'success' 'warning' 'danger') collect: [ :each | ':::', each ].
^ response "copyWith: ':::'"
]
{ #category : #'*MiniDocs' }
String >> admonitionEndingPosition [
| response |
response := 0.
self startsWithMarkdownAdmonition ifFalse: [ ^ response ].
self lines do: [:line |
response > 0 ifTrue: [ response := response + 1 ].
(line trimBoth = ':::')
ifFalse: [ response := response + line size ]
ifTrue: [ ^ response := response + line size. ]
].
^ response
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> asDashedLowercase [ String >> asDashedLowercase [
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'." "I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
@ -105,16 +70,6 @@ String >> detectYAMLMetadata [
ifFound: [ ^ true ] ifNone: [ ^ false ] ] ifFound: [ ^ true ] ifNone: [ ^ false ] ]
] ]
{ #category : #'*MiniDocs' }
String >> indentedWithExtraSpaces: spaceNumber [
| response indent |
response := '' writeStream.
indent := String new.
spaceNumber timesRepeat: [ indent := indent, ' ' ].
self lines do: [:line | response nextPutAll: indent, line, String lf ].
^ response contents
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> markdownHeaders [ String >> markdownHeaders [
| response headers | | response headers |
@ -127,38 +82,6 @@ String >> markdownHeaders [
^ response ^ response
] ]
{ #category : #'*MiniDocs' }
String >> markdownSplitLines [
"I'm useful for conversions between the HedgeDoc Markdown variant and Lepiter page snippets.
I provide broad places to where semantic breaks should be located in a page,
depending on headers or admonitions to create pages snippets with similar divisions.
Further page splits should be provided manually by the document author."
| response |
response := OrderedDictionary new.
response := response
addAll: self markdownHeaders;
addAll: self admonitionBorderLines;
yourself.
^ (response associations sorted: [ :x :y | x key < y key ]) asOrderedDictionary
]
{ #category : #'*MiniDocs' }
String >> markdownSplitted [
| response lastPart |
self markdownSplitLines ifEmpty: [ ^ self ].
response := OrderedCollection new.
self markdownSplitLines keys allButLast doWithIndex: [:key :index | | nextLine part |
nextLine := (self markdownSplitLines keys at: index + 1) - 1.
part := self lines copyFrom: key to: nextLine.
response add: part.
].
lastPart := self lines
copyFrom: self markdownSplitLines keys last
to: self lines size.
response add: lastPart.
^ response
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> promoteMarkdownHeaders [ String >> promoteMarkdownHeaders [
| response | | response |
@ -181,12 +104,6 @@ String >> romanizeAccents [
^ modified ^ modified
] ]
{ #category : #'*MiniDocs' }
String >> startsWithMarkdownAdmonition [
self lines ifEmpty: [ ^ false ].
^ self admonitionBorders includes: self lines first trimBoth
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> startsWithYAMLMetadataDelimiter [ String >> startsWithYAMLMetadataDelimiter [
self lines ifEmpty: [^false]. self lines ifEmpty: [^false].
@ -199,6 +116,11 @@ String >> withoutXMLTagDelimiters [
^ self copyWithoutAll: #($< $>) ^ self copyWithoutAll: #($< $>)
] ]
{ #category : #'*MiniDocs' }
String >> yamlMetadata [
^ (YAML2JSON fromString: self yamlMetadataString)
]
{ #category : #'*MiniDocs' } { #category : #'*MiniDocs' }
String >> yamlMetadataClosingLineNumber [ String >> yamlMetadataClosingLineNumber [
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found." "I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."

View File

@ -1,9 +0,0 @@
Extension { #name : #XMLDocument }
{ #category : #'*MiniDocs' }
XMLDocument >> detectMarkdeepTitle [
| titleLine |
titleLine := (self nodesCollect: [:node | node contentString ]) first lines
detect: [:line | line includesSubstring: ' **'] ifNone: ['Untitled'].
^ titleLine trimmed trimBoth: [:char | char = $* ]
]

52
src/MiniDocs/YQ.class.st Normal file
View File

@ -0,0 +1,52 @@
"
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #YQ,
#superclass : #Object,
#category : #'MiniDocs-External'
}
{ #category : #accessing }
YQ class >> binaryDownloadLinkFor: operativeSystem on: processor [
| binaryName binaryDownloadData |
binaryName := 'yq_', operativeSystem , '_', processor.
binaryDownloadData := ((self lastReleaseData at: 'assets')
select: [:each | (each at: 'name') beginsWith: binaryName ]) first.
^ binaryDownloadData at: 'browser_download_url'
]
{ #category : #accessing }
YQ class >> binaryFile [
"Starting with location on Arch Linux and its derivates. Multidistro and multiOS support should be added."
^ FileLocator root / 'usr/bin/yq'
]
{ #category : #accessing }
YQ class >> install [
^ self lastReleaseData
]
{ #category : #accessing }
YQ class >> jsonToYaml: aDictionary [
| jsonFile |
self binaryFile exists ifFalse: [ YQ install].
jsonFile := MarkupFile exportAsFileOn: FileLocator temp / 'data.json' containing: aDictionary.
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
shellCommand: 'cat ', jsonFile fullName,' | yq -y';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
]].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'yq -p=json ', jsonFile fullName ].
]
{ #category : #accessing }
YQ class >> lastReleaseData [
^ (STONJSON
fromString: 'https://api.github.com/repos/mikefarah/yq/releases' asUrl retrieveContents) first
]

View File

@ -1,10 +0,0 @@
Extension { #name : #ZnConstants }
{ #category : #'*MiniDocs' }
ZnConstants class >> maximumLineLength [
"Return the maximum line length to accept.
Used by ZnLineReader and thus for reading request/status lines as well as headers.
This helps to protect us from malicious content."
^ 5096 "8192"
]