Compare commits
104 Commits
gt-unsync1
...
master
Author | SHA1 | Date | |
---|---|---|---|
60294a40b9 | |||
6825d9f242 | |||
738697bc63 | |||
d822926612 | |||
af064db451 | |||
f85ed1803b | |||
92760a54a9 | |||
e73cbbb72a | |||
b89b3755f7 | |||
d2959856b9 | |||
ca3550a3f4 | |||
d470dd533c | |||
babbd48934 | |||
5ccff44fc9 | |||
33f3a9a0c2 | |||
96903d8627 | |||
4beca346be | |||
37d6000702 | |||
0ce6b89b6c | |||
629a9e32ca | |||
e26334425f | |||
58ec9eed76 | |||
bb7bab403e | |||
cea7ce5e6c | |||
65fb92964f | |||
cccf81e89b | |||
fa6303b762 | |||
02403b5ae3 | |||
e4d0880fea | |||
ee80105f44 | |||
ecb4321551 | |||
56ef3869ca | |||
8fe49106bb | |||
ff9fbb92f9 | |||
a6e49448fa | |||
45c4762201 | |||
8634047be9 | |||
f3929ceece | |||
b4d6940ec6 | |||
7bbf399ae1 | |||
fb64d5c1ae | |||
554fb9000e | |||
47eaafaf5c | |||
78900cc64d | |||
78f1b1474f | |||
94fdf3052f | |||
6072fd8971 | |||
6e58c5631d | |||
8cadf004dd | |||
2bc0b44fa2 | |||
882f33859c | |||
968bfff3bb | |||
f67a24c94c | |||
3c01731fdc | |||
1b66316d03 | |||
62a5c398d1 | |||
23335a7727 | |||
3202717cea | |||
27a28ce543 | |||
47aabb1d4f | |||
eeaf28127b | |||
141942ce3e | |||
c9b214f633 | |||
59b8387728 | |||
6433da508a | |||
5cf1d7bcc6 | |||
28a3d22911 | |||
da459b9d26 | |||
31e27acdff | |||
|
f592e29eef | ||
|
de61e736fa | ||
a22005da27 | |||
5e4db00352 | |||
ef982eb6a1 | |||
089eb8c2eb | |||
5ec6ea6377 | |||
7dc0bdaac0 | |||
0c45ccb39e | |||
573c929845 | |||
13f9b8050e | |||
53910fa840 | |||
bf0ea4b46d | |||
977922d7a3 | |||
51e84c2404 | |||
8fb6373a83 | |||
9ae7a6ec62 | |||
7979007091 | |||
a264070d5c | |||
642712cdfd | |||
34d77ecedd | |||
3a2d096025 | |||
7cd3f30216 | |||
5c897886e0 | |||
2037af37a3 | |||
82405165e1 | |||
b8262d00ca | |||
6832d9024d | |||
da4c9bf9c4 | |||
2ca24978bc | |||
bba3daebfa | |||
0c5ede8498 | |||
87fba41704 | |||
0305a68aca | |||
b2a4dc1839 |
@ -13,8 +13,7 @@ BaselineOfMiniDocs >> baseline: spec [
|
|||||||
"Dependencies"
|
"Dependencies"
|
||||||
self setUpTeapot: spec.
|
self setUpTeapot: spec.
|
||||||
self setUpPetitParser: spec.
|
self setUpPetitParser: spec.
|
||||||
"LepiterBuildingBlocs commented while resolving the conflict with the internal gtoolkit renaming."
|
self setUpLepiterBuildingBlocs: spec. "working in v1.0.993"
|
||||||
"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' ];
|
||||||
@ -32,7 +31,8 @@ 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,10 +60,7 @@ BaselineOfMiniDocs >> semanticVersion [
|
|||||||
BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [
|
BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [
|
||||||
spec
|
spec
|
||||||
baseline: 'LepiterBuildingBlocs'
|
baseline: 'LepiterBuildingBlocs'
|
||||||
with: [spec
|
with: [spec repository: 'github://botwhytho/LepiterBuildingBlocs:main/src']
|
||||||
repository: 'github://botwhytho/LepiterBuildingBlocs:main/src';
|
|
||||||
loads: #('ALL')];
|
|
||||||
import: 'LepiterBuildingBlocs'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
|
18
src/MiniDocs/AcroReport.class.st
Normal file
18
src/MiniDocs/AcroReport.class.st
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"
|
||||||
|
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
|
||||||
|
]
|
57
src/MiniDocs/AlphanumCounter.class.st
Normal file
57
src/MiniDocs/AlphanumCounter.class.st
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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 ]
|
||||||
|
]
|
@ -26,3 +26,20 @@ 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
|
||||||
|
]
|
||||||
|
12
src/MiniDocs/ByteString.extension.st
Normal file
12
src/MiniDocs/ByteString.extension.st
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
]
|
6
src/MiniDocs/Dictionary.extension.st
Normal file
6
src/MiniDocs/Dictionary.extension.st
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Extension { #name : #Dictionary }
|
||||||
|
|
||||||
|
{ #category : #'*MiniDocs' }
|
||||||
|
Dictionary >> treeView [
|
||||||
|
^ self asOrderedDictionary treeView
|
||||||
|
]
|
53
src/MiniDocs/FileLocator.extension.st
Normal file
53
src/MiniDocs/FileLocator.extension.st
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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'
|
||||||
|
]
|
@ -1,5 +1,51 @@
|
|||||||
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 |
|
||||||
@ -7,7 +53,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 uuid;
|
at: 'parent' put: self parent uid asString36;
|
||||||
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;
|
||||||
|
@ -29,15 +29,36 @@ HedgeDoc class >> newDefault [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
HedgeDoc >> asLePage [
|
HedgeDoc >> asLePage [
|
||||||
| newPage snippet |
|
| newPage sanitizedMarkdown |
|
||||||
snippet := LeTextSnippet new
|
sanitizedMarkdown := self bodyWithoutTitleHeader promoteMarkdownHeaders.
|
||||||
string: self bodyWithoutTitleHeader promoteMarkdownHeaders.
|
|
||||||
newPage := LePage new
|
newPage := LePage new
|
||||||
initializeTitle: self title;
|
initializeTitle: self title.
|
||||||
addSnippet: snippet;
|
sanitizedMarkdown := sanitizedMarkdown markdownSplitted.
|
||||||
yourself.
|
sanitizedMarkdown class = OrderedCollection ifTrue: [
|
||||||
newPage incomingLinks.
|
sanitizedMarkdown do: [:lines | | snippet |
|
||||||
newPage metadata addAll: self metadata.
|
snippet := LeTextSnippet new
|
||||||
|
string: lines asStringWithCr;
|
||||||
|
uid: LeUID new.
|
||||||
|
newPage
|
||||||
|
addSnippet: snippet;
|
||||||
|
yourself
|
||||||
|
]
|
||||||
|
].
|
||||||
|
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
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -151,7 +172,7 @@ HedgeDoc >> server: aUrlString [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
HedgeDoc >> url [
|
HedgeDoc >> url [
|
||||||
^ url asUrl
|
url ifNotNil: [ ^ url asUrl ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
@ -162,7 +183,6 @@ 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
|
||||||
]
|
]
|
||||||
|
@ -37,40 +37,39 @@ LeDatabase >> addPageCopy: aLePage [
|
|||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
|
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
|
||||||
| remoteMetadata divSnippets dataSnippets snippets page |
|
| remoteMetadata divSnippets dataSnippets 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
|
page fromDictionary: remoteMetadata.
|
||||||
title: (remoteMetadata at: 'title' ifAbsent: [ page detectMarkdeepTitleFrom: markdeepDocTree ]);
|
dataSnippets do: [:each | | snippet|
|
||||||
basicUid: (UUID fromString36: (remoteMetadata at: 'id' ifAbsent: [UUID new asString36]));
|
snippet := each asLepiterSnippet.
|
||||||
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 ]
|
||||||
self importErrorForLocal: existingPage withRemote: externalDocLocation.
|
ifFound: [
|
||||||
^ self ]
|
self importErrorForLocal: existingPage withRemote: externalDocLocation.
|
||||||
ifFalse: [ snippet database: self.
|
^ self
|
||||||
self registerSnippet: snippet ] ].
|
]
|
||||||
|
ifNone: [ snippet database: self ].
|
||||||
|
]
|
||||||
|
ifFalse: [ snippet database: self ]
|
||||||
|
].
|
||||||
self addPage: page.
|
self addPage: page.
|
||||||
^ page
|
^ page
|
||||||
]
|
]
|
||||||
|
@ -3,7 +3,7 @@ Extension { #name : #LePage }
|
|||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LePage >> asHtmlFile [
|
LePage >> asHtmlFile [
|
||||||
|
|
||||||
self asMarkdownFile.
|
self asMarkdownFileWithMetadataWrappers.
|
||||||
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,6 +21,7 @@ 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
|
||||||
@ -47,6 +48,7 @@ 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
|
||||||
@ -54,13 +56,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 demoteMarkdownHeaders;
|
contents: bodyStream contents promoteMarkdownHeaders;
|
||||||
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
|
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
|
||||||
^ markdown
|
^ markdown
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LePage >> asMarkdownFile [
|
LePage >> asMarkdownFileWithMetadataWrappers [
|
||||||
| 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
|
||||||
@ -70,13 +72,15 @@ LePage >> asMarkdownFile [
|
|||||||
LePage >> asMarkdownWithMetadataWrappers [
|
LePage >> asMarkdownWithMetadataWrappers [
|
||||||
| bodyStream markdown |
|
| bodyStream markdown |
|
||||||
bodyStream := '' writeStream.
|
bodyStream := '' writeStream.
|
||||||
bodyStream
|
"bodyStream
|
||||||
nextPutAll: '# ', self title; cr; cr.
|
nextPut: Character lf;
|
||||||
|
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 demoteMarkdownHeaders;
|
contents: bodyStream contents promoteMarkdownHeaders;
|
||||||
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
|
title: self title;
|
||||||
|
metadata: self metadata.
|
||||||
^ markdown
|
^ markdown
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -95,14 +99,6 @@ 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 ].
|
||||||
@ -124,11 +120,33 @@ LePage >> exportMetadataToHead: markdeep [
|
|||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LePage >> exportedFileName [
|
LePage >> exportedFileName [
|
||||||
| sanitized |
|
| sanitized titleWords shortTitle |
|
||||||
sanitized := self title asDashedLowercase romanizeAccents copyWithoutAll: #($/ $: $🢒).
|
titleWords := self title splitOn: Character space.
|
||||||
|
(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 |
|
||||||
@ -162,7 +180,10 @@ 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 := MiniDocsServer teapot staticRouter prefix joinUsing: '/'.
|
route := self storage path segments 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
|
||||||
]
|
]
|
||||||
@ -206,6 +227,17 @@ 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.
|
||||||
@ -265,6 +297,25 @@ 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 |
|
||||||
|
@ -8,6 +8,17 @@ 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 [
|
||||||
|
|
||||||
@ -17,7 +28,7 @@ LePharoSnippet >> fromMarkdeep: markdeepDiv [
|
|||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LePharoSnippet >> fromString: aString [
|
LePharoSnippet >> fromString: aString [
|
||||||
|
|
||||||
self code: aString
|
[ self coder forSource: aString ] onErrorDo: [ ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
|
@ -41,22 +41,37 @@ 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: aString [
|
LePictureSnippet >> fromString: aStringArray [
|
||||||
"aString should be a valid Markdown/Markdeep image string"
|
"aStringArray should contain as first element the sanitized string and
|
||||||
| args captionTemp urlTemp |
|
as second the full original image Link string, which may contains links in the description."
|
||||||
|
| args urlTemp |
|
||||||
|
|
||||||
args := aString splitOn: ']('.
|
args := aStringArray second splitOn: ']('.
|
||||||
captionTemp := args first.
|
urlTemp := args last.
|
||||||
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: captionTemp.
|
self caption: aStringArray first.
|
||||||
self urlString: urlTemp.
|
self urlString: urlTemp.
|
||||||
^ self
|
^ self
|
||||||
]
|
]
|
||||||
|
@ -13,7 +13,18 @@ LeSnippet class >> fromMetaMarkdeep: div [
|
|||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeSnippet >> metadata [
|
LeSnippet >> metadata [
|
||||||
^ self metadataUpdate
|
| createEmailSanitized editEmailSanitized |
|
||||||
|
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' }
|
||||||
|
@ -1,5 +1,25 @@
|
|||||||
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 |
|
||||||
|
@ -18,6 +18,17 @@ 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 [
|
||||||
|
|
||||||
@ -27,7 +38,9 @@ LeTextSnippet >> fromMarkdeep: markdeepDiv [
|
|||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeTextSnippet >> fromString: aString [
|
LeTextSnippet >> fromString: aString [
|
||||||
|
|
||||||
self string: aString
|
self
|
||||||
|
string: aString;
|
||||||
|
uid: LeUID new.
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
@ -49,7 +62,18 @@ LeTextSnippet >> parentId [
|
|||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeTextSnippet >> taggedWith: aString [
|
LeTextSnippet >> tagWith: aString [
|
||||||
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
|
self tags add: aString.
|
||||||
^ 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
|
||||||
]
|
]
|
||||||
|
@ -2,16 +2,11 @@ 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: '<div st-class="' , self class greaseString , '"';
|
nextPutAll: self metadataDiv;
|
||||||
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;
|
||||||
@ -39,44 +34,18 @@ LeTextualSnippet >> asMarkdownWithMetadataWrappers [
|
|||||||
| output |
|
| output |
|
||||||
output := '' writeStream.
|
output := '' writeStream.
|
||||||
output
|
output
|
||||||
nextPutAll: '<div st-class="', self class asString, '"'; lf;
|
nextPutAll: self metadataDiv;
|
||||||
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
|
|
||||||
nextPutAll: self markdownCustomOpener;
|
nextPutAll: self markdownCustomOpener;
|
||||||
nextPutAll: self contentAsStringCustomized; lf;
|
nextPutAll: self contentAsStringCustomized; lf;
|
||||||
nextPutAll: self markdownCustomCloser;
|
nextPutAll: self markdownCustomCloser;
|
||||||
nextPutAll: '</div>'; lf; lf.
|
nextPutAll: '</div>'; lf; lf.
|
||||||
^ output contents
|
^ output contents withInternetLineEndings
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeTextualSnippet >> contentAsStringAnnotated [
|
LeTextualSnippet >> contentAsStringAnnotated [
|
||||||
| annotations substitutions exported pageConfig|
|
self ast ifNotNil: [ ^ self processSnippetAnnotations ].
|
||||||
self ast ifNil: [ ^ self contentAsString ].
|
^ 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' }
|
||||||
@ -86,6 +55,11 @@ 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 [
|
||||||
^ ''
|
^ ''
|
||||||
@ -112,6 +86,21 @@ 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 |
|
||||||
@ -119,14 +108,46 @@ 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 uidString;
|
at: 'parent' put: (self parent ifNotNil: [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 ].
|
||||||
@ -141,5 +162,5 @@ LeTextualSnippet >> sanitizeMetadata [
|
|||||||
|
|
||||||
{ #category : #'*MiniDocs' }
|
{ #category : #'*MiniDocs' }
|
||||||
LeTextualSnippet >> tags [
|
LeTextualSnippet >> tags [
|
||||||
^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
|
^ self extra at: 'tags' ifAbsentPut: [ Set new ]
|
||||||
]
|
]
|
||||||
|
@ -4,7 +4,7 @@ Class {
|
|||||||
#instVars : [
|
#instVars : [
|
||||||
'folder'
|
'folder'
|
||||||
],
|
],
|
||||||
#category : #MiniDocs
|
#category : #'MiniDocs-Model'
|
||||||
}
|
}
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
@ -29,5 +29,5 @@ Logseq >> journals [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
Logseq >> pages [
|
Logseq >> pages [
|
||||||
self folder/ 'pages'
|
^self folder/ 'pages'
|
||||||
]
|
]
|
||||||
|
@ -27,6 +27,15 @@ 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
|
||||||
@ -40,7 +49,7 @@ Markdown >> body: aString [
|
|||||||
{ #category : #operation }
|
{ #category : #operation }
|
||||||
Markdown >> commentYAMLMetadata [
|
Markdown >> commentYAMLMetadata [
|
||||||
| newContents |
|
| newContents |
|
||||||
self detectYAMLMetadata ifFalse: [ ^ self ].
|
self contents detectYAMLMetadata ifFalse: [ ^ self ].
|
||||||
newContents := '' writeStream.
|
newContents := '' writeStream.
|
||||||
newContents nextPutAll: '<!--@yaml'; lf.
|
newContents nextPutAll: '<!--@yaml'; lf.
|
||||||
newContents nextPutAll: self yamlMetadataString.
|
newContents nextPutAll: self yamlMetadataString.
|
||||||
@ -57,14 +66,15 @@ Markdown >> containsYAMLMetadataClosing [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
Markdown >> contents [
|
Markdown >> contents [
|
||||||
| response |
|
| response metadataString |
|
||||||
response := WriteStream on: ''.
|
response := WriteStream on: ''.
|
||||||
|
metadataString := self metadataAsYAML
|
||||||
|
ifEmpty: [ '' ]
|
||||||
|
ifNotEmpty: [ '---', String cr, self metadataAsYAML, String cr, '---', String cr ].
|
||||||
response
|
response
|
||||||
nextPutAll: '---'; cr;
|
nextPutAll: metadataString;
|
||||||
nextPutAll: self metadataAsYAML; cr;
|
nextPutAll: (self body ifNil: [ '' ]).
|
||||||
nextPutAll: '---'; cr;
|
^ response contents withInternetLineEndings
|
||||||
nextPutAll: self body.
|
|
||||||
^ response contents
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
@ -132,7 +142,7 @@ Markdown >> exportMetadataAsYaml [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
Markdown >> file [
|
Markdown >> file [
|
||||||
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md.html' ]
|
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md' ]
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
@ -149,8 +159,18 @@ Markdown >> fromFile: aFileReference [
|
|||||||
|
|
||||||
{ #category : #'instance creation' }
|
{ #category : #'instance creation' }
|
||||||
Markdown >> fromString: markdownString [
|
Markdown >> fromString: markdownString [
|
||||||
(self metadata) at: 'original' put: markdownString yamlMetadata.
|
| yamlMetadataRaw bodyTemp |
|
||||||
self body: markdownString contentsWithoutYAMLMetadata
|
yamlMetadataRaw := (YamlHeaderParser parse: markdownString).
|
||||||
|
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 }
|
||||||
@ -179,6 +199,7 @@ Markdown >> lines [
|
|||||||
Markdown >> metadata [
|
Markdown >> metadata [
|
||||||
|
|
||||||
^ metadata ifNil: [ metadata := Dictionary new].
|
^ metadata ifNil: [ metadata := Dictionary new].
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -219,3 +240,8 @@ Markdown >> printOn: aStream [
|
|||||||
Markdown >> title [
|
Markdown >> title [
|
||||||
^ title ifNil: [ title:= self headerAsTitle ]
|
^ title ifNil: [ title:= self headerAsTitle ]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
{ #category : #accessing }
|
||||||
|
Markdown >> title: aString [
|
||||||
|
title := aString
|
||||||
|
]
|
||||||
|
@ -34,11 +34,8 @@ MiniDocs class >> altShiftRightCombo [
|
|||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
MiniDocs class >> appFolder [
|
MiniDocs class >> appFolder [
|
||||||
| tempFolder userDataFolder |
|
| tempFolder |
|
||||||
userDataFolder := Smalltalk os isWindows
|
tempFolder := ExoRepo userDataFolder / 'Mutabit' / 'MiniDocs'.
|
||||||
ifTrue: [ FileLocator home / 'AppData' / 'Local' ]
|
|
||||||
ifFalse: [ FileLocator userData ].
|
|
||||||
tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'.
|
|
||||||
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
|
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
|
||||||
^ tempFolder
|
^ tempFolder
|
||||||
]
|
]
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
"
|
|
||||||
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'
|
|
||||||
]
|
|
@ -1,89 +0,0 @@
|
|||||||
"
|
|
||||||
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 ]
|
|
||||||
]
|
|
@ -10,19 +10,18 @@ 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 fromString: (self at: 'content').
|
[ response fromDictionary: self ] onErrorDo: [ ].
|
||||||
response
|
[ response fromString: (self at: 'content') ] onErrorDo: [ ].
|
||||||
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]
|
||||||
@ -33,3 +32,69 @@ 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
|
||||||
|
]
|
||||||
|
@ -147,17 +147,11 @@ Pandoc class >> markdownToHtmlOnUnix: inputFile [
|
|||||||
outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html').
|
outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html').
|
||||||
outputFile ensureDelete.
|
outputFile ensureDelete.
|
||||||
outputFile ensureCreateFile.
|
outputFile ensureCreateFile.
|
||||||
OSSUnixSubprocess new
|
GtSubprocessWithInMemoryOutput new
|
||||||
command: 'pandoc';
|
shellCommand: 'pandoc -f markdown+startnum+task_lists --standalone -t html ', inputFile fullName, ' --output ', outputFile fullName;
|
||||||
arguments: {'-f'. 'markdown+startnum+task_lists'. '--standalone'. '-t'. 'html'. inputFile fullName.
|
runAndWait;
|
||||||
'--output'. outputFile fullName };
|
stdout.
|
||||||
redirectStdout;
|
^ outputFile.
|
||||||
redirectStderr;
|
|
||||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
|
||||||
process isSuccess
|
|
||||||
ifTrue: [ ^ outputFile ]
|
|
||||||
ifFalse: [ ^ inputFile ]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{ #category : #converters }
|
{ #category : #converters }
|
||||||
|
@ -11,7 +11,7 @@ Class {
|
|||||||
'footnoteLabel',
|
'footnoteLabel',
|
||||||
'footnoteContent'
|
'footnoteContent'
|
||||||
],
|
],
|
||||||
#category : #MiniDocs
|
#category : #'MiniDocs-Model'
|
||||||
}
|
}
|
||||||
|
|
||||||
{ #category : #accessing }
|
{ #category : #accessing }
|
||||||
|
@ -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,6 +13,41 @@ 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'."
|
||||||
@ -70,6 +105,16 @@ 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 |
|
||||||
@ -82,6 +127,38 @@ 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 |
|
||||||
@ -104,6 +181,12 @@ 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].
|
||||||
@ -116,11 +199,6 @@ 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."
|
||||||
|
9
src/MiniDocs/XMLDocument.extension.st
Normal file
9
src/MiniDocs/XMLDocument.extension.st
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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 = $* ]
|
||||||
|
]
|
@ -1,52 +0,0 @@
|
|||||||
"
|
|
||||||
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
|
|
||||||
]
|
|
10
src/MiniDocs/ZnConstants.extension.st
Normal file
10
src/MiniDocs/ZnConstants.extension.st
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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"
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user