Compare commits

..

No commits in common. "master" and "gt-crashes/2" have entirely different histories.

58 changed files with 628 additions and 2652 deletions

View File

@ -11,29 +11,20 @@ BaselineOfMiniDocs >> baseline: spec [
for: #common
do: [
"Dependencies"
self setUpTeapot: spec.
self setUpPetitParser: spec.
self setUpLepiterBuildingBlocs: spec. "working in v1.0.993"
spec
baseline: 'Mustache' with: [ spec repository: 'github://noha/mustache' ];
baseline: 'Temple' with: [ spec repository: 'github://astares/Pharo-Temple/src' ];
baseline: 'Tealight' with: [ spec repository: 'github://astares/Tealight:main/src' ];
baseline: 'DataFrame' with: [ spec repository: 'github://PolyMathOrg/DataFrame/src' ].
baseline: 'PetitParser' with: [
spec
loads: #('Minimal' 'Core' 'Tests' 'Islands');
repository: 'github://moosetechnology/PetitParser:v3.x.x/src'
].
"self fossil: spec."
self xmlParserHTML: spec.
"Packages"
spec
package: 'PetitMarkdown' with: [ spec requires: #('PetitParser')];
package: 'MiniDocs'
with: [ spec requires: #(
'Mustache' 'Temple' "Templating"
'Teapot' 'Tealight' "Web server"
'PetitMarkdown' 'PetitParser' "Parsers"
'DataFrame' "Tabular data"
'LepiterBuildingBlocs' "Lepiter utilities")].
.
package: 'MiniDocs' with: [ spec requires: #('Mustache' 'Tealight' 'PetitMarkdown')].
"Groups"
@ -46,7 +37,7 @@ BaselineOfMiniDocs >> baseline: spec [
BaselineOfMiniDocs >> fossil: spec [
| repo |
repo := ExoRepo new
repository: 'https://code.sustrato.red/Offray/Fossil'.
repository: 'https://code.tupale.co/Offray/Fossil'.
repo load.
spec baseline: 'Fossil' with: [ spec repository: 'gitlocal://', repo local fullName ]
]
@ -56,41 +47,15 @@ BaselineOfMiniDocs >> semanticVersion [
^ '0.2.0'
]
{ #category : #accessing }
BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [
spec
baseline: 'LepiterBuildingBlocs'
with: [spec repository: 'github://botwhytho/LepiterBuildingBlocs:main/src']
]
{ #category : #accessing }
BaselineOfMiniDocs >> setUpPetitParser: spec [
spec
baseline: 'PetitParser'
with: [ spec
repository: 'github://moosetechnology/PetitParser:v3.x.x/src';
loads: #('Minimal' 'Core' 'Tests' 'Islands')];
import: 'PetitParser'
]
{ #category : #accessing }
BaselineOfMiniDocs >> setUpTeapot: spec [
spec
baseline: 'Teapot'
with: [ spec
repository: 'github://zeroflag/Teapot/source';
loads: #('ALL') ];
import: 'Teapot'
]
{ #category : #accessing }
BaselineOfMiniDocs >> xmlParserHTML: spec [
spec
baseline: 'XMLParserHTML'
with: [ spec
repository: 'github://ruidajo/XML-XMLParserHTML/src';
loads: #('ALL')];
import: 'XMLParserHTML'
Metacello new
baseline: 'XMLParserHTML';
repository: 'github://pharo-contributions/XML-XMLParserHTML/src';
onConflict: [ :ex | ex useLoaded ];
onUpgrade: [ :ex | ex useLoaded ];
onDowngrade: [ :ex | ex useLoaded ];
onWarningLog;
load.
spec baseline: 'XMLParserHTML' with: [spec repository: 'github://pharo-contributions/XML-XMLParserHTML/src']
]

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

@ -1,45 +0,0 @@
Extension { #name : #Array }
{ #category : #'*MiniDocs' }
Array >> bagOfWordsFor: sentenceArray [
"An utility machine training little algorithm.
Inspired by https://youtu.be/8qwowmiXANQ?t=1144.
This should be moved probably to [Polyglot](https://github.com/pharo-ai/Polyglot),
but the repository is pretty innactive (with commits 2 or more years old and no reponse to issues).
Meanwhile, it will be in MiniDocs.
Given the sentence := #('hello' 'how' 'are' 'you')
and the testVocabulary := #('hi' 'hello' 'I' 'you' 'bye' 'thank' 'you')
then
testVocabulary bagOfWordsFor: sentence.
Should give: #(0 1 0 1 0 0 0)
"
| bagOfWords |
bagOfWords := Array new: self size.
bagOfWords doWithIndex: [:each :i | bagOfWords at: i put: 0 ].
sentenceArray do: [:token | |index|
index := self indexOf: token.
index > 0
ifTrue: [bagOfWords at: index put: 1]
].
^ 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,46 +0,0 @@
Extension { #name : #DataFrame }
{ #category : #'*MiniDocs' }
DataFrame >> asMarkdown [
| response |
response := '' writeStream.
self columnNames do: [ :name | response nextPutAll: '| ' , name , ' ' ].
response
nextPutAll: '|';
cr.
self columns size timesRepeat: [ response nextPutAll: '|---' ].
response
nextPutAll: '|';
cr.
self asArrayOfRows
do: [ :row |
row do: [ :cell | response nextPutAll: '| ' , cell asString , ' ' ].
response
nextPutAll: '|';
cr ].
^ response contents accentedCharactersCorrection withInternetLineEndings.
]
{ #category : #'*MiniDocs' }
DataFrame >> viewDataFor: aView [
<gtView>
| columnedList |
self numberOfRows >= 1 ifFalse: [ ^ aView empty ].
columnedList := aView columnedList
title: 'Data';
items: [ self transposed columns ];
priority: 40.
self columnNames
withIndexDo: [:aName :anIndex |
columnedList
column: aName
text: [:anItem | anItem at: anIndex ]
].
^ columnedList
]
{ #category : #'*MiniDocs' }
DataFrame >> webView [
^ Pandoc convertString: self asMarkdown from: 'markdown' to: 'html'
]

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

@ -27,14 +27,6 @@ GrafoscopioNode class >> fromFile: aFileReference [
^ (STON fromString: aFileReference contents) first parent
]
{ #category : #accessing }
GrafoscopioNode class >> fromLink: aStonLink [
| notebook |
notebook := (STON fromString: aStonLink asUrl retrieveContents utf8Decoded) first parent.
notebook addRemoteLocation: aStonLink.
^ notebook
]
{ #category : #accessing }
GrafoscopioNode >> addRemoteLocation: anURL [
self remoteLocations add: anURL
@ -61,11 +53,10 @@ GrafoscopioNode >> asLePage [
self root populateTimestamps.
page := LePage new
initializeTitle: 'Grafoscopio Notebook (imported)'.
self nodesInPreorder allButFirst
do: [:node | page addSnippet: node asSnippet ].
self nodesInPreorder allButFirst do: [:node |
page addSnippet: node asSnippet ].
page latestEditTime: self root latestEditionDate.
page createTime: self root earliestCreationDate.
page optionAt: 'remoteLocations' put: self remoteLocations.
^ page.
]

View File

@ -1,72 +0,0 @@
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' }
GtGQLSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
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' }
GtGQLSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -16,52 +16,12 @@ Class {
#category : #'MiniDocs-Core'
}
{ #category : #accessing }
HedgeDoc class >> fromLink: aUrl [
^ self new fromLink: aUrl
]
{ #category : #'as yet unclassified' }
HedgeDoc class >> newDefault [
^ self new
defaultServer.
]
{ #category : #accessing }
HedgeDoc >> asLePage [
| newPage sanitizedMarkdown |
sanitizedMarkdown := self bodyWithoutTitleHeader promoteMarkdownHeaders.
newPage := LePage new
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;
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
]
{ #category : #accessing }
HedgeDoc >> asMarkdeep [
^ Markdeep new
@ -80,18 +40,9 @@ HedgeDoc >> asMarkdownTiddler [
created: Tiddler nowLocal.
]
{ #category : #accessing }
HedgeDoc >> bodyWithoutTitleHeader [
| headerIndex |
headerIndex := self body lines
detectIndex: [ :line | line includesSubstring: self headerAsTitle ]
ifNone: [ ^ self body].
^ (self body lines copyWithoutIndex: headerIndex) asStringWithCr
]
{ #category : #accessing }
HedgeDoc >> contents [
^ super contents
^ body
]
{ #category : #accessing }
@ -104,12 +55,6 @@ HedgeDoc >> defaultServer [
self server: 'https://docutopia.tupale.co'.
]
{ #category : #accessing }
HedgeDoc >> fromLink: aString [
self url: aString.
self retrieveContents
]
{ #category : #'as yet unclassified' }
HedgeDoc >> htmlUrl [
| link |
@ -136,7 +81,7 @@ HedgeDoc >> pad: anObject [
{ #category : #accessing }
HedgeDoc >> retrieveContents [
self url ifNil: [ ^ self ].
self fromString: (self url addPathSegment: 'download') retrieveContents.
self contents: (self url addPathSegment: 'download') retrieveContents.
^ self.
]
@ -172,18 +117,11 @@ HedgeDoc >> server: aUrlString [
{ #category : #accessing }
HedgeDoc >> url [
url ifNotNil: [ ^ url asUrl ]
^ url asUrl
]
{ #category : #accessing }
HedgeDoc >> url: anObject [
| tempUrl html |
tempUrl := anObject asZnUrl.
html := XMLHTMLParser parse: tempUrl retrieveContents.
(html xpath: '//head/meta[@name="application-name"][@content = "HedgeDoc - Ideas grow better together"]') isEmpty
ifTrue: [ self inform: 'Not a hedgedoc url'.
url := nil ].
server := tempUrl host.
url := anObject
]

View File

@ -7,17 +7,17 @@ Class {
#category : #'MiniDocs-Model'
}
{ #category : #accessing }
HedgeDocGrammar >> metadataAsYAML [
"I parse the header of the hedgedoc document for YAML metadata."
^ '---' asPParser token, #any asPParser starLazy token, '---' asPParser token
]
{ #category : #accessing }
HedgeDocGrammar >> start [
| any |
any := #any asPParser.
^ (self metadataAsYAML / any starLazy), youtubeEmbeddedLink
^ (self yamlMetadata / any starLazy), youtubeEmbeddedLink
]
{ #category : #accessing }
HedgeDocGrammar >> yamlMetadata [
"I parse the header of the hedgedoc document for YAML metadata."
^ '---' asPParser token, #any asPParser starLazy token, '---' asPParser token
]
{ #category : #accessing }

View File

@ -1,26 +0,0 @@
Extension { #name : #LeChangesSnippet }
{ #category : #'*MiniDocs' }
LeChangesSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeChangesSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,21 +0,0 @@
Extension { #name : #LeCodeSnippet }
{ #category : #'*MiniDocs' }
LeCodeSnippet >> metadataUpdate [
| surrogate |
self parent
ifNil: [ surrogate := nil]
ifNotNil: [
self parent isString
ifTrue: [ surrogate := self parent]
ifFalse: [ surrogate := self parent uidString ]
].
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: surrogate;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString withoutXMLTagDelimiters;
at: 'modifier' put: self editEmail asString withoutXMLTagDelimiters;
yourself
]

View File

@ -1,17 +1,28 @@
Extension { #name : #LeDatabase }
{ #category : #'*MiniDocs' }
LeDatabase >> addPage2FromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| newPage |
"^ { snippets . page }"
"Rebulding partial subtrees"
"Adding unrooted subtrees to the page"
"^ newPage"
newPage := self
rebuildPageFromMarkdeep: markdeepDocTree
withRemote: externalDocLocation.
newPage
childrenDo: [ :snippet |
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets snippets page |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ].
snippets := divSnippets
collect: [ :xmlElement |
(xmlElement attributes at: 'st-class') = 'LeTextSnippet'
ifTrue: [ LeTextSnippet new contentFrom: xmlElement ]
ifFalse: [ (xmlElement attributes at: 'st-class') = 'LePharoSnippet'
ifTrue: [ LePharoSnippet new contentFrom: xmlElement ] ] ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
page := LePage new
title: (remoteMetadata at: 'title');
basicUid: (UUID fromString36: (remoteMetadata at: 'id'));
createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime);
editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
createEmail: (remoteMetadata at: 'creator');
editEmail: (remoteMetadata at: 'modifier').
snippets do: [ :snippet | page addSnippet: snippet ].
page children
do: [ :snippet |
(self hasBlockUID: snippet uid)
ifTrue: [ | existingPage |
existingPage := self pages
@ -20,56 +31,6 @@ LeDatabase >> addPage2FromMarkdeep: markdeepDocTree withRemote: externalDocLocat
^ self ]
ifFalse: [ snippet database: self.
self registerSnippet: snippet ] ].
self addPage: newPage.
^ newPage
]
{ #category : #'*MiniDocs' }
LeDatabase >> addPageCopy: aLePage [
| pageTitle timestamp shortID page |
timestamp := DateAndTime now asString.
pageTitle := 'Copy of ', aLePage title.
page := aLePage duplicatePageWithNewName: pageTitle, timestamp.
shortID := '(id: ', (page uid asString copyFrom: 1 to: 8), ')'.
page title: (page title copyReplaceAll: timestamp with: shortID).
^ page
]
{ #category : #'*MiniDocs' }
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets dataSnippets page |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
"Ensuring remote metadata has consistent data"
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.
page := LePage new.
page fromDictionary: remoteMetadata.
dataSnippets do: [:each | | snippet|
snippet := each asLepiterSnippet.
page addSnippet: snippet.
].
page children
do: [ :snippet |
(self hasBlockUID: snippet uid)
ifTrue: [ | existingPage |
existingPage := self pages
detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ]
ifFound: [
self importErrorForLocal: existingPage withRemote: externalDocLocation.
^ self
]
ifNone: [ snippet database: self ].
]
ifFalse: [ snippet database: self ]
].
self addPage: page.
^ page
]
@ -81,7 +42,7 @@ LeDatabase >> addPageFromMarkdeepUrl: aString [
page
ifNotNil: [ :arg |
self importErrorForLocal: page withRemote: aString.
^ self errorCardFor: page uidString ].
^ self ].
^ self addPageFromMarkdeep: (self docTreeForLink: aString) withRemote: aString
]
@ -101,10 +62,10 @@ LeDatabase >> docTreeForLink: aString [
]
{ #category : #'*MiniDocs' }
LeDatabase >> errorCardFor: errorKey [
LeDatabase >> errorCardFor: error [
| keepButton overwriteButton loadCopyButton errorMessageUI localPage |
| keepButton overwriteButton backupButton errorMessageUI localPage errorKey |
errorKey := error keys first.
localPage := self pageWithID: errorKey.
keepButton := BrButton new
aptitude: BrGlamorousButtonWithIconAndLabelAptitude;
@ -121,28 +82,22 @@ LeDatabase >> errorCardFor: errorKey [
icon: BrGlamorousVectorIcons edit;
action: [ :aButton |
self removePage: localPage.
aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (self errors at: errorKey at: 'remote')).
aButton phlow spawnObject: (self addPageFromMarkdeepUrl: (error at: errorKey at: 'remote')).
self errors removeKey: errorKey
];
margin: (BlInsets left: 10).
loadCopyButton := BrButton new
backupButton := BrButton new
aptitude: BrGlamorousButtonWithIconAndLabelAptitude;
label: 'Load remote page as a copy';
label: 'Backup local page';
icon: BrGlamorousVectorIcons changes;
action: [ :aButton | self ];
action: [ :aButton | ];
margin: (BlInsets left: 10).
errorMessageUI := BrEditor new
aptitude: BrGlamorousRegularEditorAptitude new ;
text: (self errors at: errorKey at: 'message');
text: (error at: errorKey at: 'message');
vFitContent.
^ BrHorizontalPane new
matchParent;
alignCenter;
addChild:errorMessageUI;
addChild: keepButton;
addChild: overwriteButton;
addChild: loadCopyButton
^ { errorMessageUI. keepButton. overwriteButton. backupButton }
]
{ #category : #'*MiniDocs' }
@ -169,34 +124,6 @@ LeDatabase >> gtViewErrorDetailsOn: aView [
].
]
{ #category : #'*MiniDocs' }
LeDatabase >> gtViewErrorDetailsOn: aView withKey: erroKey [
<gtView>
^ aView explicit
title: 'Errors beta' translated;
priority: 5;
stencil: [ | container |
container := BlElement new
layout: BlFlowLayout new;
constraintsDo: [ :c |
c vertical fitContent.
c horizontal matchParent ];
padding: (BlInsets all: 10).
container
addChildren: (self errorCardFor: erroKey)
].
]
{ #category : #'*MiniDocs' }
LeDatabase >> importDocumentFrom: aURL [
| doc |
"Using file extension in URL as a cheap (non-robuts) way of detecting the kind of document.
Better file type detection should be implemented in the future."
(aURL endsWith: '.md.html') ifTrue: [ ^ self addPageFromMarkdeepUrl: aURL ].
doc := HedgeDoc fromLink: aURL asString.
^ self addPage: doc asLePage
]
{ #category : #'*MiniDocs' }
LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [
@ -227,8 +154,7 @@ LeDatabase >> importErrorForLocal: page withRemote: externalDocLocation [
at: 'remote' put: externalDocLocation;
at: 'message' put: message ;
yourself.
self errors at: id put: error.
^ self errors at: id.
self errors at: id put: error
]
{ #category : #'*MiniDocs' }
@ -236,77 +162,3 @@ LeDatabase >> options [
^ options
]
{ #category : #'*MiniDocs' }
LeDatabase >> previewSanitizedPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets divSnippetsSanitized |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
remoteMetadata at: 'origin' put: externalDocLocation.
divSnippetsSanitized := self sanitizeMarkdeepSnippets: divSnippets withMetadata: remoteMetadata.
^ { divSnippets . divSnippetsSanitized . remoteMetadata }
]
{ #category : #'*MiniDocs' }
LeDatabase >> rebuildPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| newPage snippets divSnippets remoteMetadata dataSnippets |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
remoteMetadata at: 'origin' put: externalDocLocation.
dataSnippets := self
sanitizeMarkdeepSnippets: divSnippets
withMetadata: remoteMetadata.
snippets := dataSnippets collect: [ :each | each asLepiterSnippet ].
newPage := LePage new
title: (remoteMetadata at: 'title');
basicUid: (UUID fromString36: (remoteMetadata at: 'id'));
createTime: (LeTime new time: (remoteMetadata at: 'created') asDateAndTime);
editTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
latestEditTime: (LeTime new time: (remoteMetadata at: 'modified') asDateAndTime);
createEmail: (remoteMetadata at: 'creator');
editEmail: (remoteMetadata at: 'modifier'). "^ { snippets . page }" "Rebulding partial subtrees"
snippets
do: [ :currentSnippet |
| parentSnippet |
parentSnippet := snippets
detect: [ :item | item uid asString = currentSnippet parent ]
ifNone: [ parentSnippet := 'unrooted' ].
currentSnippet parent: parentSnippet.
parentSnippet class = ByteString
ifFalse: [ parentSnippet children addChild: currentSnippet ] ]. "Adding unrooted subtrees to the page"
"^ { unrooted . newPage }."
snippets
select: [ :each | each parent = 'unrooted' ]
thenDo: [ :unrooted | newPage addSnippet: unrooted ].
^ newPage
]
{ #category : #'*MiniDocs' }
LeDatabase >> sanitizeMarkdeepSnippets: divSnippets withMetadata: remoteMetadata [
^ divSnippets collectWithIndex: [:markdeepDiv :i | | snippetData creationTime modificationTime timestampWarning |
snippetData := markdeepDiv asSnippetDictionary.
creationTime := snippetData at: 'created'.
modificationTime := snippetData at: 'modified'.
timestampWarning := [:timestamp |
'Modified timestamps: ', timestamp ,' date and time was replaced instead of nil value. See "origin" metadata for more historical traceability information.'
].
(creationTime = 'nil' and: [ modificationTime ~= 'nil' ])
ifTrue: [
snippetData redefineTimestampsBefore: modificationTime.
snippetData addErrata: (timestampWarning value: 'creation').
snippetData at: 'origin' put: (remoteMetadata at: 'origin').
].
(creationTime = 'nil' and: [ modificationTime = 'nil' ])
ifTrue: [ | timeDiff |
timeDiff := divSnippets size - i. "Suggesting that last snippets were modified after the first ones."
modificationTime := (remoteMetadata at: 'created') asDateAndTime - timeDiff seconds.
snippetData redefineTimestampsBefore: modificationTime.
snippetData addErrata: (timestampWarning value: 'creation').
snippetData addErrata: (timestampWarning value: 'modification').
snippetData at: 'origin' put: (remoteMetadata at: 'origin').
].
snippetData.
]
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeDockerSnippet }
{ #category : #'*MiniDocs' }
LeDockerSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeDockerSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -5,28 +5,3 @@ LeExampleSnippet >> asMarkdeep [
^ (WriteStream on: '') contents
]
{ #category : #'*MiniDocs' }
LeExampleSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeExampleSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeGitHubSnippet }
{ #category : #'*MiniDocs' }
LeGitHubSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeGitHubSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,8 +0,0 @@
Extension { #name : #LeHeaderNode }
{ #category : #'*MiniDocs' }
LeHeaderNode >> headerFullName [
^ self topParent completeSource
copyFrom: self startPosition
to: self stopPosition
]

View File

@ -1,56 +0,0 @@
Extension { #name : #LeHomeDatabaseHeaderElement }
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> importMinidocsButtonElement [
^ self userData at: 'importMinidocsButtonElement' ifAbsentPut: [ self newImportMiniDocsButton]
]
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> initialize [
super initialize.
self initializeEditableTitleElement.
self initializeButtons.
self addChild: self toolbarElement as: #toolbar.
self toolbarElement
addItem: self editableTitleElement;
addItem: self newAddNewPageButton;
addItem: self removeButtonElement;
addItem: self importButtonElement;
addItem: self exportButtonElement;
addItem: self importMinidocsButtonElement.
self addAptitude: (BrLayoutResizerAptitude new
hInherit;
vAnyToFitContent;
hInherit: self toolbarElement;
vAnyToFitContent: self toolbarElement).
]
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> initializeButtons [
self initializeRemoveButton.
self initializeImportButton.
self initializeExportButton.
self initializeMiniDocsImportButton.
]
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> initializeMiniDocsImportButton [
self userData at: 'importMinidocsButtonElement' put: self newImportMiniDocsButton.
]
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> newImportMiniDocsButton [
^ LeMiniDocsImport new
tooltip: 'Import document from link';
contentExtent: 200 @ 30
]
{ #category : #'*MiniDocs' }
LeHomeDatabaseHeaderElement >> updateToolbarButtons [
self updateRemoveButtonElement.
self exportButtonElement database: self database.
self importButtonElement database: self database.
self importMinidocsButtonElement database: self database.
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeJenkinsSnippet }
{ #category : #'*MiniDocs' }
LeJenkinsSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeJenkinsSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,89 +0,0 @@
Class {
#name : #LeMiniDocsImport,
#superclass : #BrButton,
#instVars : [
'contentExtent',
'database'
],
#category : #'MiniDocs-UI'
}
{ #category : #accessing }
LeMiniDocsImport >> contentExtent [
^ contentExtent
]
{ #category : #accessing }
LeMiniDocsImport >> contentExtent: aPoint [
self
assert: [ aPoint isNotNil ]
description: [ 'Extent must be non-nil' ].
contentExtent := aPoint
]
{ #category : #accessing }
LeMiniDocsImport >> createDropdownExpandedHandleButton [
^ BrButton new
icon: BrGlamorousVectorIcons downwards;
label: self tooltip;
aptitude: BrGlamorousButtonWithIconAndLabelAptitude
]
{ #category : #accessing }
LeMiniDocsImport >> createURLeditable [
| base editable |
base := BlElement new
background: (Color white);
size: 200 @ 30;
margin: (BlInsets all: 10);
yourself.
editable := BrEditableLabel new
aptitude: BrGlamorousEditableLabelAptitude new glamorousRegularFontAndSize;
text: 'Document link';
switchToEditor.
editable when: BrEditorAcceptWish do: [ :aWish |
self importDocumentFrom: aWish text asString.
].
base addChild: editable.
^ base
]
{ #category : #accessing }
LeMiniDocsImport >> database [
^ database
]
{ #category : #accessing }
LeMiniDocsImport >> database: aLeDatabase [
database := aLeDatabase
]
{ #category : #accessing }
LeMiniDocsImport >> importDocumentFrom: aURL [
^ self database importDocumentFrom: aURL.
]
{ #category : #accessing }
LeMiniDocsImport >> initialize [
super initialize.
self
icon: BrGlamorousVectorIcons downwards;
label: 'Add MiniDocs';
aptitude: BrGlamorousButtonWithIconAndLabelAptitude.
self addAptitude: (BrGlamorousWithDropdownAptitude
handle: [ self createDropdownExpandedHandleButton ]
content: [ self createURLeditable ]).
self aptitude - BrGlamorousButtonExteriorAptitude.
]
{ #category : #accessing }
LeMiniDocsImport >> tooltip [
^ self label
]
{ #category : #accessing }
LeMiniDocsImport >> tooltip: aString [
self label: aString
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeMockedSnippet }
{ #category : #'*MiniDocs' }
LeMockedSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeMockedSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -3,7 +3,7 @@ Extension { #name : #LePage }
{ #category : #'*MiniDocs' }
LePage >> asHtmlFile [
self asMarkdownFileWithMetadataWrappers.
self asMarkdownFile.
self defaultPandocTemplate exists
ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ].
@ -21,23 +21,29 @@ LePage >> asHtmlFile [
LePage >> asMarkdeep [
| bodyStream markdeep |
bodyStream := '' writeStream.
bodyStream nextPutAll: self notebookMetadataSnippet asMarkdeep.
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdeep ].
self preorderTraversal do: [:snippet |
bodyStream nextPutAll: snippet asMarkdeep
].
markdeep := Markdeep new
title: self title;
body: bodyStream contents;
metadata: self metadata;
file: self storage / self markdeepFileName;
navTop: self navTop.
self metadata
at: 'authors'
ifPresent: [ :author | markdeep metadata at: 'authors' put: author ].
self metadata
at: 'version'
ifPresent: [ :version | markdeep metadata at: 'version' put: version ].
markdeep head: nil.
^ markdeep
title: self title;
body: bodyStream contents;
navTop: self navTop.
self metadata keysAndValuesDo: [:k :v |
k = 'lang'
ifTrue: [
markdeep head
add: '<meta lang="', v,'">';
yourself.
]
ifFalse: [
markdeep head
add: '<meta name="', k, '" content="', v,'">';
yourself.
]
].
self metadata at: 'authors' ifPresent: [:author | markdeep metadata at: 'authors' put: author ].
self metadata at: 'version' ifPresent: [:version | markdeep metadata at: 'version' put: version ].
^ markdeep.
]
{ #category : #'*MiniDocs' }
@ -48,49 +54,27 @@ LePage >> asMarkdeepFile [
{ #category : #'*MiniDocs' }
LePage >> asMarkdown [
"PENDING: to debug the output."
| bodyStream markdown |
bodyStream := '' writeStream.
bodyStream
nextPutAll: '# ', self title; cr; cr.
nextPutAll: '---';
nextPutAll: String lf.
self metadata keysAndValuesDo: [ :k :v |
bodyStream
nextPutAll: k , ': "' , v, '"';
nextPutAll: String lf ].
bodyStream nextPutAll: '---' , String lf , String lf.
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ].
markdown := Markdown new
contents: bodyStream contents promoteMarkdownHeaders;
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
markdown := Markdown new contents: bodyStream contents.
^ markdown
]
{ #category : #'*MiniDocs' }
LePage >> asMarkdownFileWithMetadataWrappers [
LePage >> asMarkdownFile [
| folder |
folder := self storage.
^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdownWithMetadataWrappers contents
]
{ #category : #'*MiniDocs' }
LePage >> asMarkdownWithMetadataWrappers [
| bodyStream markdown |
bodyStream := '' writeStream.
"bodyStream
nextPut: Character lf;
nextPutAll: '# ', self title; cr; cr."
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdownWithMetadataWrappers ].
markdown := Markdown new
contents: bodyStream contents promoteMarkdownHeaders;
title: self title;
metadata: self metadata.
^ markdown
]
{ #category : #'*MiniDocs' }
LePage >> config [
| configFile |
configFile := self storage / 'config.ston'.
configFile exists
ifTrue: [^ STON fromString: configFile contents ]
ifFalse: [ ^ nil ]
^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdown contents
]
{ #category : #'*MiniDocs' }
@ -101,52 +85,18 @@ LePage >> defaultPandocTemplate [
{ #category : #'*MiniDocs' }
LePage >> detectParentSnippetWithUid: uidString [
uidString = self uid asString36 ifTrue: [ ^ self ].
^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ]
]
"Answer a boolean indicating whether the supplied uid is present"
{ #category : #'*MiniDocs' }
LePage >> exportMetadataToHead: markdeep [
self metadata
keysAndValuesDo: [ :k :v |
k = 'lang'
ifTrue: [ markdeep head
add: '<meta lang="' , v , '">';
yourself ]
ifFalse: [ markdeep head
add: '<meta name="' , k , '" content="' , v , '">';
yourself ] ]
^ self preorderTraversal detect: [ :snippet | snippet uidString = uidString ] ifNone: [ ^ self ]
]
{ #category : #'*MiniDocs' }
LePage >> exportedFileName [
| sanitized titleWords shortTitle |
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 |
sanitized := self title asDashedLowercase romanizeAccents copyWithoutAll: #($/ $:).
^ 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' }
LePage >> fromMarkdeepUrl: aString [
| docTree pageMetadata |
@ -179,11 +129,8 @@ LePage >> latestEditTime: aLeTime [
{ #category : #'*MiniDocs' }
LePage >> localHostAddress [
| localUrl route |
MiniDocsServer teapot server isRunning ifFalse: [ MiniDocsServer restart ].
route := self storage path segments joinUsing: '/'.
MiniDocsServer teapot
serveStatic: ('/', route, '/', self markdeepFileName)
from: self storage / self markdeepFileName.
MiniDocsServer teapot server isRunning ifFalse: [ MiniDocsServer start ].
route := MiniDocsServer teapot staticRouter prefix joinUsing: '/'.
localUrl := MiniDocsServer teapot server localUrl asString.
^ localUrl, route, '/', self markdeepFileName
]
@ -202,17 +149,17 @@ LePage >> markdownFileName [
{ #category : #'*MiniDocs' }
LePage >> metadata [
^ self metadataUpdate
^ self options at: 'metadata' ifAbsentPut: [ self metadataInit]
]
{ #category : #'*MiniDocs' }
LePage >> metadataUpdate [
LePage >> metadataInit [
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'title' put: self contentAsString;
at: 'created' put: self createTime greaseString;
at: 'modified' put: self getLatestEditTime greaseString;
at: 'modified' put: self latestEditTime greaseString;
at: 'creator' put: self createEmail greaseString;
at: 'modifier' put: self editEmail greaseString;
yourself
@ -227,30 +174,6 @@ LePage >> navTop [
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' }
LePage >> olderChild [
"I provide the last edited child node.
I'm useful to recalculate the age of a notebook."
| response|
response := self preorderTraversal first.
self preorderTraversal do: [:current |
current editTime >= response editTime
ifTrue: [ response := current ]
].
^ response
]
{ #category : #'*MiniDocs' }
LePage >> options [
^ options
@ -268,11 +191,6 @@ LePage >> removeSnippetsMetadata [
ifTrue: [ snippet options removeKey: 'metadata' ] ]
]
{ #category : #'*MiniDocs' }
LePage >> sanitizeMetadata [
self allChildrenDepthFirst do: [:snippet | snippet sanitizeMetadata ]
]
{ #category : #'*MiniDocs' }
LePage >> sharedVariablesBindings [
| codeSnippets shared |
@ -297,25 +215,6 @@ LePage >> sharedVariablesBindings [
^ 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' }
LePage >> storage [
| current |
@ -325,15 +224,6 @@ LePage >> storage [
^ self optionAt: 'storage'
]
{ #category : #'*MiniDocs' }
LePage >> uiAddCopyButtonFor: anAction [
<lePageAction>
^ anAction button
tooltip: 'Export Page';
icon: BrGlamorousVectorIcons changes;
action: [:aButton | aButton phlow spawnObject: (self page database addPageCopy: self page) ]
]
{ #category : #'*MiniDocs' }
LePage >> uiDefineFolderFor: anAction [
<lePageAction>
@ -369,16 +259,3 @@ LePage >> uiRefreshWebPreviewButtonFor: anAction [
"TODO: If Chrome/Chromium are not installed, I should execute:"
"WebBrowser openOn: self page localHostAddress" ]
]
{ #category : #'*MiniDocs' }
LePage >> youngerChild [
"I provide the first create child node.
I'm useful to recalculate the age of a notebook."
| response|
response := self preorderTraversal first.
self preorderTraversal do: [:current |
current createTime <= response createTime
ifTrue: [ response := current ]
].
^ response
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LePharoRewriteSnippet }
{ #category : #'*MiniDocs' }
LePharoRewriteSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LePharoRewriteSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -9,26 +9,21 @@ LePharoSnippet >> contentAsStringCustomized [
]
{ #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').
]
LePharoSnippet >> contentFrom: markdeepDiv [
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromMarkdeep: markdeepDiv [
^ markdeepDiv asSnippetDictionary asLepiterSnippet
]
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromString: aString [
[ self coder forSource: aString ] onErrorDo: [ ]
| sanitizedStringText metadata joinedText |
metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
sanitizedStringText := markdeepDiv contentString lines.
sanitizedStringText := sanitizedStringText copyFrom: 4 to: sanitizedStringText size -2.
joinedText := '' writeStream.
sanitizedStringText do: [ :line | joinedText nextPutAll: line; nextPut: Character lf ].
self code: joinedText contents allButLast;
uid: (LeUID new uidString: (metadata at: 'id'));
parent: (metadata at: 'parent');
createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime));
editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime));
editEmail: (metadata at: 'modifier');
createEmail: (metadata at: 'creator')
]
{ #category : #'*MiniDocs' }

View File

@ -2,22 +2,11 @@ Extension { #name : #LePictureSnippet }
{ #category : #'*MiniDocs' }
LePictureSnippet >> asMarkdeep [
| output curatedCaption captionLines |
captionLines := self caption lines.
(captionLines size <= 1)
ifTrue: [ curatedCaption := caption ]
ifFalse: [
curatedCaption := WriteStream on: ''.
curatedCaption nextPutAll: captionLines first.
captionLines allButFirstDo: [:line |
curatedCaption nextPutAll: ' ', line.
curatedCaption := curatedCaption contents.
]
].
| output |
output := WriteStream on: ''.
output
nextPutAll: self metadataDiv;
nextPutAll: '![ ', curatedCaption ,' ](', self urlString, ')';
nextPutAll: '![ ', self caption ,' ](', self urlString, ')';
nextPut: Character lf;
nextPutAll: '</div>';
nextPut: Character lf;
@ -25,60 +14,9 @@ LePictureSnippet >> asMarkdeep [
^ output contents
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> asMarkdownWithMetadataWrappers [
^ self asMarkdeep
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> contentFrom: markdeepDiv [
| caption width |
caption := markdeepDiv contentString.
width := (markdeepDiv // 'img' @ 'width') stringValue.
self
optionAt: 'caption' put: caption;
optionAt: 'width' put: width.
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' }
LePictureSnippet >> fromMarkdeep: markdeepDiv [
^ markdeepDiv asSnippetDictionary asLepiterSnippet
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> fromString: aStringArray [
"aStringArray should contain as first element the sanitized string and
as second the full original image Link string, which may contains links in the description."
| args urlTemp |
args := aStringArray second splitOn: ']('.
urlTemp := args last.
urlTemp := urlTemp copyFrom: 1 to: urlTemp size - 1.
self caption: aStringArray first.
self urlString: urlTemp.
^ self
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> metadata [
^ self metadataInit
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
]
{ #category : #'*MiniDocs' }
@ -88,31 +26,13 @@ LePictureSnippet >> metadataDiv [
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf.
^ output contents withInternetLineEndings.
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">'.
^ output.
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> metadataInit [
| surrogate |
self parent
ifNil: [ surrogate := nil]
ifNotNil: [ surrogate := self parent uidString ].
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: surrogate;
at: 'url' put: '<!--',self contentAsString, '-->';
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString withoutXMLTagDelimiters;
at: 'modifier' put: self editEmail asString withoutXMLTagDelimiters;
yourself
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> metadataUpdate [
| surrogate |
self parent
ifNil: [ surrogate := nil]
@ -122,17 +42,7 @@ LePictureSnippet >> metadataUpdate [
at: 'parent' put: surrogate;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString withoutXMLTagDelimiters;
at: 'modifier' put: self editEmail asString withoutXMLTagDelimiters;
at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString;
yourself
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeSmaCCRewriteSnippet }
{ #category : #'*MiniDocs' }
LeSmaCCRewriteSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeSmaCCRewriteSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -7,26 +7,10 @@ LeSnippet class >> fromMetaMarkdeep: div [
metadata := STON fromString:(div xpath: '@st-data') stringValue.
snippet := className asClass new.
snippet injectMetadataFrom: metadata.
snippet fromMarkdeep: div.
snippet contentFrom: div.
^ snippet.
]
{ #category : #'*MiniDocs' }
LeSnippet >> metadata [
| 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' }
LeSnippet >> moveToPageTitled: pageName [
| db origin destination thisSnippet |

View File

@ -1,25 +1,5 @@
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' }
LeTextCoderSnippetElement >> moveToPageTitled: pageName [
| db origin destination |

View File

@ -2,14 +2,9 @@ Extension { #name : #LeTextSnippet }
{ #category : #'*MiniDocs' }
LeTextSnippet >> asLePage [
| page title currentSnippet |
title := self contentAsString markdownHeaders associations first value.
title := (title trimBoth: [:char | char = $# ]) trimmed.
| page |
page := LePage new
initializeTitle: title.
currentSnippet := LeTextSnippet new
string: self contentAsString.
page addSnippet: currentSnippet.
initializeTitle: self contentAsString.
self database addPage: page.
self childrenDo: [:child |
child moveToPageTitled: page title
@ -19,34 +14,39 @@ LeTextSnippet >> asLePage [
]
{ #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')
]
LeTextSnippet >> contentFrom: markdeepDiv [
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromMarkdeep: markdeepDiv [
^ markdeepDiv asSnippetDictionary asLepiterSnippet
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromString: aString [
self
string: aString;
uid: LeUID new.
| sanitizedStringText metadata |
metadata := STON fromString: (markdeepDiv attributes at: 'st-data').
sanitizedStringText := markdeepDiv contentString.
sanitizedStringText := sanitizedStringText allButFirst.
sanitizedStringText := sanitizedStringText allButLast.
self string: sanitizedStringText;
uid: (LeUID new uidString: (metadata at: 'id'));
parent: (metadata at: 'parent');
createTime: (LeTime new time: ((metadata at: 'created')asDateAndTime));
editTime: (LeTime new time: ((metadata at: 'modified') asDateAndTime));
editEmail: (metadata at: 'modifier');
createEmail: (metadata at: 'creator')
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> metadata [
^ self metadataUpdate
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> metadataInit [
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parentId;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString;
yourself
]
{ #category : #'*MiniDocs' }
@ -57,23 +57,11 @@ LeTextSnippet >> options [
{ #category : #'*MiniDocs' }
LeTextSnippet >> parentId [
self parent ifNil: [ ^ self ].
(self parent isString) ifTrue: [^ self parent].
^ self parent uidString.
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> tagWith: aString [
self tags add: aString.
]
{ #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
LeTextSnippet >> taggedWith: aString [
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
^ self metadata at: 'tags'
]

View File

@ -2,62 +2,45 @@ Extension { #name : #LeTextualSnippet }
{ #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdeep [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents."
| output |
output := WriteStream on: ''.
output
nextPutAll: self metadataDiv;
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">';
nextPut: Character lf;
nextPutAll: self markdeepCustomOpener;
nextPutAll: self contentAsStringAnnotated;
nextPutAll: self contentAsString;
nextPut: Character lf;
nextPutAll: self markdeepCustomCloser;
nextPutAll: '</div>';
nextPut: Character lf;
nextPut: Character lf.
^ output contents withInternetLineEndings
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdown [
| output |
output := '' writeStream.
output
nextPutAll: self contentAsStringCustomized; lf.
^ output contents
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdownWithMetadataWrappers [
LeTextualSnippet >> asMarkdown [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents."
| output |
output := '' writeStream.
output
nextPutAll: self metadataDiv;
nextPutAll: '<div st-class="', self class asString, '"'; lf;
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
nextPutAll: self markdownCustomOpener;
nextPutAll: self contentAsStringCustomized; lf;
nextPutAll: self markdownCustomCloser;
nextPutAll: '</div>'; lf; lf.
^ output contents withInternetLineEndings
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> contentAsStringAnnotated [
self ast ifNotNil: [ ^ self processSnippetAnnotations ].
^ self contentAsString
^ output contents
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> contentAsStringCustomized [
(self contentAsString beginsWith: '#')
ifTrue: [ ^ '#', self contentAsString ]
ifFalse: [ ^ self contentAsString ]
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> extra [
^ self optionAt: 'extra' ifAbsentPut: [ Dictionary new ]
^ self contentAsString
]
{ #category : #'*MiniDocs' }
@ -83,84 +66,26 @@ LeTextualSnippet >> markdownCustomOpener [
{ #category : #'*MiniDocs' }
LeTextualSnippet >> metadata [
^ self metadataUpdate
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
]
{ #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' }
LeTextualSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
LeTextualSnippet >> metadataInit [
| surrogate |
self parent
ifNil: [ surrogate := nil]
ifNotNil: [ surrogate := self parent uidString ].
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: (self parent ifNotNil: [self parent uidString ]);
at: 'parent' put: surrogate;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
at: 'extra' put: self extra;
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' }
LeTextualSnippet >> sanitizeMetadata [
self options ifNil: [^ self ].
self options removeKey: 'metadata' ifAbsent: [^ self ].
self metadata keysAndValuesDo: [:k :v |
(v asString includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v asString copyWithoutXMLDelimiters)
]
]
at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString;
yourself
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> tags [
^ self extra at: 'tags' ifAbsentPut: [ Set new ]
^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
]

View File

@ -1,21 +0,0 @@
Extension { #name : #LeUnknownSnippet }
{ #category : #'*MiniDocs' }
LeUnknownSnippet >> metadataUpdate [
| surrogate |
self parent
ifNil: [ surrogate := nil]
ifNotNil: [
self parent isString
ifTrue: [ surrogate := self parent]
ifFalse: [ surrogate := self parent uidString ]
].
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: surrogate;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: self createEmail asString;
at: 'modifier' put: self editEmail asString;
yourself
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeWardleyMapSnippet }
{ #category : #'*MiniDocs' }
LeWardleyMapSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeWardleyMapSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,26 +0,0 @@
Extension { #name : #LeWordSnippet }
{ #category : #'*MiniDocs' }
LeWordSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }
LeWordSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -16,7 +16,7 @@ LeYoutubeReferenceSnippet >> asMarkdeep [
{ #category : #'*MiniDocs' }
LeYoutubeReferenceSnippet >> metadata [
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataUpdate ]
^ self optionAt: 'metadata' ifAbsentPut: [ self metadataInit ]
]
{ #category : #'*MiniDocs' }
@ -26,12 +26,12 @@ LeYoutubeReferenceSnippet >> metadataDiv [
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">'.
^ output contents withInternetLineEndings.
nextPutAll: ' st-data="' , (STON toString: self metadata) , '">'.
^ output.
]
{ #category : #'*MiniDocs' }
LeYoutubeReferenceSnippet >> metadataUpdate [
LeYoutubeReferenceSnippet >> metadataInit [
| surrogate |
self parent
ifNil: [ surrogate := nil]
@ -45,13 +45,3 @@ LeYoutubeReferenceSnippet >> metadataUpdate [
at: 'modifier' put: self editEmail asString;
yourself
]
{ #category : #'*MiniDocs' }
LeYoutubeReferenceSnippet >> sanitizeMetadata [
self metadata keysAndValuesDo: [:k :v |
(v includesAny: #($< $>))
ifTrue: [
self metadata at: k put: (v copyWithoutAll: #($< $>))
]
]
]

View File

@ -1,33 +0,0 @@
Class {
#name : #Logseq,
#superclass : #Object,
#instVars : [
'folder'
],
#category : #'MiniDocs-Model'
}
{ #category : #accessing }
Logseq >> assets [
^ self folder / 'assets'
]
{ #category : #accessing }
Logseq >> folder [
^ folder
]
{ #category : #accessing }
Logseq >> folder: aFolder [
folder := aFolder
]
{ #category : #accessing }
Logseq >> journals [
self folder / 'journals'
]
{ #category : #accessing }
Logseq >> pages [
^self folder/ 'pages'
]

View File

@ -5,6 +5,7 @@ Class {
#name : #Markdeep,
#superclass : #Markdown,
#instVars : [
'title',
'comments',
'tail',
'language',
@ -29,25 +30,16 @@ Markdeep class >> fromPubPubTOC: orderedDictionary folder: folder index: ordina
^ self new fromMarkdownFile: testFile.
]
{ #category : #accessing }
Markdeep >> asMarkdownWithMetadataWrappers [
^ Markdown new
metadata: self metadata;
body: self body;
file: self markdownFile
]
{ #category : #'instance creation' }
Markdeep >> authors [
self metadata at: 'authors' ifAbsentPut: [ Dictionary new ].
"self metadata at: 'authors' ifNotEmpty: [:k | ^ '**', k, '**' ]
" ^ ''.
self metadata at: 'authors' ifPresent: [:k | ^ '**', k, '**' ].
^ ''.
]
{ #category : #'instance creation' }
Markdeep >> authorsString [
self authors
ifEmpty: [ ^ '' ] ifNotEmpty: [ ^ ' ', self authors ]
ifNil: [ ^ '' ] ifNotNil: [ ^ ' ', self authors ]
]
{ #category : #accessing }
@ -65,15 +57,10 @@ Markdeep >> bodyReplaceAll: original with: replacement [
self body: (self body copyReplaceAll: original with: replacement)
]
{ #category : #accessing }
Markdeep >> cleanMetadata [
metadata := nil
]
{ #category : #accessing }
Markdeep >> commentPubPubDelimiters [
| commented openners |
openners := #('::: {.pub-body-component}' '::: pub-body-component' '::: {.editor .Prosemirror}' '::: {.pub-notes}').
openners := #('::: {.pub-body-component}' '::: {.editor .Prosemirror}' '::: {.pub-notes}').
commented := self body.
openners do: [:openner |
commented := commented copyReplaceAll: openner with: '<!--@div-open ', openner, '-->'
@ -126,9 +113,7 @@ Markdeep >> commentsSupport [
{ #category : #accessing }
Markdeep >> config [
| configFile |
configFile := self folder / 'config.ston'.
configFile exists ifTrue: [ ^ config := STON fromString: configFile contents ].
^ config ifNil: [ config := Dictionary new]
]
@ -146,10 +131,10 @@ Markdeep >> contents [
output
nextPutAll: self headContents; lf; lf;
nextPutAll: ' **', self title trimmed accentedCharactersCorrection, '**'; lf;
nextPutAll: self authorsString ; lf;
nextPutAll: self authorsString ; lf;
nextPutAll: '', self version; lf;
nextPutAll: self navTop; lf; lf;
nextPutAll: self body; lf; lf;
nextPutAll: self body; lf; lf;
nextPutAll: self tail; lf; lf; lf; lf;
nextPutAll: self commentsSupport.
^ output contents.
@ -171,68 +156,6 @@ Markdeep >> converPubPubFootnoteBetween: footnote and: nextFootnote in: footnote
^ response contents
]
{ #category : #accessing }
Markdeep >> extractTitleFrom: docTree [
| tempTitle |
tempTitle := ((docTree children
detect: [ :node | node className = 'PPCMIndentedCode' ]) children
detect: [ :subnode | subnode text trimmed beginsWith: '**' ]) text trimmed.
self title: (tempTitle copyFrom: 3 to: tempTitle size - 2).
^ tempTitle
]
{ #category : #accessing }
Markdeep >> extractYamlMetadataFrom: documentTree [
| yamlComment response |
yamlComment := documentTree children
detect: [:node | node className = 'PPCMHtmlBlock' and: [node text trimmed beginsWith: '<!--@yaml']]
ifNone: [ ^ nil ].
response := '' writeStream.
yamlComment children allButFirst allButLast do: [:each |
response nextPutAll: each text; cr
].
^ {YAML2JSON fromString: response contents . yamlComment }
]
{ #category : #accessing }
Markdeep >> file: aFileReference [
file := aFileReference.
self fillInContentsFrom: aFileReference
]
{ #category : #accessing }
Markdeep >> fillInContentsFrom: aFileReference [
| docTree docTreeChildren headTree bodyStartLine bodyEndLine contentLines rawMetadata |
aFileReference exists ifFalse: [ ^ self ].
docTree := (Markdown new contents: aFileReference contents) documentTree.
docTreeChildren := docTree children.
headTree := docTreeChildren
detect: [ :node |
node className = 'PPCMParagraph'
and: [ (node children detect: [ :subnode | subnode text = '<head>' ]) isNotNil ] ]
ifNone: [ ^self ].
headTree children allButFirst allButLast
do: [ :node | node className = 'PPCMHtml' ifTrue: [ self head add: node text ] ].
self head: self head asSet asOrderedCollection.
rawMetadata := (self extractYamlMetadataFrom: docTree).
rawMetadata ifNotNil: [self metadata: rawMetadata first].
self title ifNil: [
self title: (self metadata at: 'title' ifAbsent: [self extractTitleFrom: docTree]).
self title: (self title trimBoth: [ :char | char = $" ]).
self metadata at: 'title' put: self title].
contentLines := self file contents lines.
bodyStartLine := (contentLines
detectIndex: [ :line | line includesSubstring: '<!--@yaml' ] ifNone: [ ^ self ]) + rawMetadata second children size.
bodyEndLine := contentLines detectIndex: [:line | line includesSubstring: '<!-- Markdeep'] ifNone: [ 0 ].
self body: (contentLines copyFrom: bodyStartLine to: bodyEndLine - 1 ) asStringWithCr.
^ self .
]
{ #category : #accessing }
Markdeep >> folder [
^ self file parent
]
{ #category : #utilities }
Markdeep >> fontAwesomeHeader [
"I enable the font awesome support in the document header"
@ -244,7 +167,6 @@ Markdeep >> fontAwesomeHeader [
Markdeep >> fromMarkdownFile: aFileReference [
"I create a Markdeep document from a given Markdown file."
self processMarkdownFor: aFileReference.
self file: aFileReference, 'html'.
^ self.
]
@ -269,11 +191,8 @@ Markdeep >> gtTextFor: aView [
{ #category : #accessing }
Markdeep >> head [
^ head ifNil: [
head := OrderedCollection new.
head add: self fontAwesomeHeader; yourself.
].
^ head ifNil: [ head := OrderedCollection new.
head add: self fontAwesomeHeader; yourself ]
]
{ #category : #accessing }
@ -294,11 +213,6 @@ Markdeep >> headContents [
nextPutAll: line;
nextPut: Character lf
].
self metadata keysAndValuesDo: [:k :v |
k = 'lang'
ifTrue: [ stream nextPutAll: ' <meta lang="', v,'">'; cr. ]
ifFalse: [ stream nextPutAll: ' <meta name="', k, '" content="', v,'">'; cr. ]
].
stream
nextPutAll: '</head>';
nextPut: Character lf.
@ -334,15 +248,12 @@ Markdeep >> markdownFile [
{ #category : #accessing }
Markdeep >> markdownFile: aFileReference [
"Where the Mardown file associated with me is stored. Used for sync. and import/export purposes."
self file: aFileReference, 'html'
self config at: 'markdownFile' put: aFileReference
]
{ #category : #'instance creation' }
Markdeep >> metadata [
metadata ifNil: [^ metadata := OrderedDictionary new ].
(metadata isNil and: [ self file contents isNil ])
ifTrue: [ metadata := OrderedDictionary new ].
^ metadata
^ metadata ifNil: [ metadata := OrderedDictionary new ]
]
{ #category : #accessing }
@ -383,13 +294,20 @@ Markdeep >> options [
]
]
{ #category : #printing }
Markdeep >> printOn: aStream [
super printOn: aStream.
aStream
nextPutAll: '( ', self title accentedCharactersCorrection, ' )'
]
{ #category : #'instance creation' }
Markdeep >> processMarkdownFor: aFileReference [
"comment stating purpose of message"
| markdownContent |
self file: aFileReference, 'html'.
markdownContent := Markdown fromFile: aFileReference.
self metadata: markdownContent metadataAsYAML.
self metadata: markdownContent yamlMetadata.
self body: (markdownContent commentYAMLMetadata contents).
]
@ -433,8 +351,7 @@ Markdeep >> pubPubFootnotesLinesRangeFor: contentSection [
| beginningLine endingLine |
beginningLine := contentSection lines size + 1.
contentSection lines doWithIndex: [:line :i |
((line includesSubstring: '::: {.pub-notes}') or: [line includesSubstring: '::: pub-notes'])
ifTrue: [ beginningLine := i ].
(line includesSubstring: '::: {.pub-notes}') ifTrue: [ beginningLine := i ].
(i > beginningLine and: [ line beginsWith: ':::' ])
ifTrue: [
endingLine := i.
@ -548,8 +465,7 @@ please visit the HTML version or download the PDF.
{ #category : #accessing }
Markdeep >> removeCCByLicenseDiv [
| licenseDiv|
licenseDiv := '
<div>
licenseDiv := '<div>
**License:** [Creative Commons Attribution 4.0 International License
(CC-BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
@ -649,7 +565,7 @@ Markdeep >> tail: anObject [
{ #category : #accessing }
Markdeep >> title [
^ title
^ title ifNil: [ title := self metadata at: 'title' ifAbsent: [ '' ] ]
]
{ #category : #accessing }

View File

@ -9,8 +9,7 @@ Class {
#superclass : #MarkupFile,
#instVars : [
'metadata',
'body',
'title'
'body'
],
#category : #'MiniDocs-Core'
}
@ -20,22 +19,20 @@ Markdown class >> fromFile: aFileReference [
^ self new fromFile: aFileReference
]
{ #category : #utilities }
Markdown class >> yamlMetadataDelimiter [
^ '---'
]
{ #category : #accessing }
Markdown >> asMarkdeep [
^ Markdeep new
body: self body;
markdownFile: self file;
commentYAMLMetadata
]
{ #category : #accessing }
Markdown >> asMarkdownTiddler [
^ Tiddler new
title: self title;
text: self contents;
type: 'text/x-markdown';
created: Tiddler nowLocal.
]
{ #category : #accessing }
Markdown >> body [
^ body
@ -49,7 +46,7 @@ Markdown >> body: aString [
{ #category : #operation }
Markdown >> commentYAMLMetadata [
| newContents |
self contents detectYAMLMetadata ifFalse: [ ^ self ].
self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
newContents nextPutAll: '<!--@yaml'; lf.
newContents nextPutAll: self yamlMetadataString.
@ -66,27 +63,48 @@ Markdown >> containsYAMLMetadataClosing [
{ #category : #accessing }
Markdown >> contents [
| response metadataString |
response := WriteStream on: ''.
metadataString := self metadataAsYAML
ifEmpty: [ '' ]
ifNotEmpty: [ '---', String cr, self metadataAsYAML, String cr, '---', String cr ].
response
nextPutAll: metadataString;
nextPutAll: (self body ifNil: [ '' ]).
^ response contents withInternetLineEndings
^ body
]
{ #category : #accessing }
Markdown >> contents: aString [
body := aString
Markdown >> contents: anObject [
body := anObject
]
{ #category : #accessing }
Markdown >> contentsWithoutYAMLMetadata [
| newContents |
self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
(self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line |
newContents nextPutAll: line; crlf ].
^ newContents contents.
]
{ #category : #operation }
Markdown >> deleteYAMLMetadata [
| newContents |
self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
(self lines copyFrom: self yamlMetadataClosingLineNumber + 1 to: self lines size) do: [ :line |
newContents nextPutAll: line; lf;lf ].
^ newContents contents.
]
{ #category : #utilities }
Markdown >> detectYAMLMetadata [
| lines |
lines := self lines.
^ self startsWithYAMLMetadataDelimiter
and: [ lines allButFirst
detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ]
ifFound: [ ^ true ] ifNone: [ ^ false ] ]
]
{ #category : #accessing }
Markdown >> documentTree [
| parser|
self contents ifNil: [^ nil].
parser := PPCommonMarkBlockParser new parse: self body.
parser := PPCommonMarkBlockParser new parse: self contents.
^ parser accept: CMBlockVisitor new
]
@ -103,12 +121,7 @@ Markdown >> exportAsFileOn: aFileReference [
aFileReference ensureDelete.
aFileReference exists ifFalse: [ aFileReference ensureCreateFile ].
aFileReference writeStreamDo: [ :stream |
stream nextPutAll: self contents withInternetLineEndings ].
]
{ #category : #accessing }
Markdown >> exportAsHTML [
^ Pandoc markdownToHtml: self file
stream nextPutAll: self contents ].
]
{ #category : #operation }
@ -142,7 +155,7 @@ Markdown >> exportMetadataAsYaml [
{ #category : #accessing }
Markdown >> file [
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md' ]
^ file ifNil: [ file := FileLocator temp / (NanoID generate asLowercase, '.md') ]
]
{ #category : #accessing }
@ -153,24 +166,15 @@ Markdown >> file: aFileReference [
{ #category : #'instance creation' }
Markdown >> fromFile: aFileReference [
self fromString: aFileReference contents.
self contents: aFileReference contents.
self file: aFileReference.
]
{ #category : #'instance creation' }
Markdown >> fromString: markdownString [
| yamlMetadataRaw bodyTemp |
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 ]].
self contents: markdownString.
self populateMetadata.
self contents: self contentsWithoutYAMLMetadata
]
{ #category : #accessing }
@ -181,17 +185,8 @@ Markdown >> gtTextFor: aView [
text: [ self contents ]
]
{ #category : #accessing }
Markdown >> headerAsTitle [
| headerNode |
headerNode := self documentTree children
detect: [ :node | node className = 'PPCMHeader' and: [ node level = 1 ] ] ifNone: [ ^ 'Untitled' ].
^ headerNode text
]
{ #category : #utilities }
Markdown >> lines [
self file ifNotNil: [^ self file contents lines ].
^ self contents lines.
]
@ -199,7 +194,6 @@ Markdown >> lines [
Markdown >> metadata [
^ metadata ifNil: [ metadata := Dictionary new].
]
@ -209,12 +203,6 @@ Markdown >> metadata: rawMeta [
metadata := rawMeta
]
{ #category : #accessing }
Markdown >> metadataAsYAML [
self metadata isEmptyOrNil ifTrue: [ ^ '' ].
^ (YQ jsonToYaml: self metadata) accentedCharactersCorrection
]
{ #category : #persistence }
Markdown >> notifyExportAsFileOn: aFileReference [
self exportAsFileOn: aFileReference.
@ -223,25 +211,62 @@ Markdown >> notifyExportAsFileOn: aFileReference [
]
{ #category : #accessing }
Markdown >> options [
^ self metadata at: 'options' ifAbsentPut: [ self defaultOptions]
Markdown >> populateMetadata [
| rawMeta |
rawMeta := MiniDocs yamlToJson: self yamlMetadataString.
rawMeta associationsDo: [ :assoc |
assoc value = 'false' ifTrue: [ assoc value: false ].
assoc value = 'true' ifTrue: [ assoc value: true ] ].
self metadata: rawMeta
]
{ #category : #accessing }
Markdown >> printOn: aStream [
| response |
super printOn: aStream.
response := self title ifNil: [ 'Untitled' ].
aStream
nextPutAll: '( ', response , ' )'
nextPutAll: '( ', (self metadata at: 'title' ifAbsent: ['untitled']), ' )'
]
{ #category : #utilities }
Markdown >> startsWithYAMLMetadataDelimiter [
^ self lines first beginsWith: self class yamlMetadataDelimiter
]
{ #category : #accessing }
Markdown >> title [
^ title ifNil: [ title:= self headerAsTitle ]
Markdown >> yamlMetadata [
^ MiniDocs yamlToJson: self yamlMetadataString
]
{ #category : #accessing }
Markdown >> title: aString [
title := aString
{ #category : #utilities }
Markdown >> yamlMetadataClosingLineNumber [
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."
self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ].
self lines allButFirst doWithIndex: [ :currentLine :i |
(currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ ^ i + 1 ]]
]
{ #category : #operation }
Markdown >> yamlMetadataString [
| output yamlLines |
self detectYAMLMetadata ifFalse: [ ^ nil ].
yamlLines := self lines copyFrom: 2 to: self yamlMetadataClosingLineNumber - 1.
output := '' writeStream.
yamlLines do: [ :line |
output
nextPutAll: line;
nextPut: Character lf. ].
^ output contents
]
{ #category : #utilities }
Markdown >> yamlMetadataStringWithDelimiters [
| output |
self yamlMetadataString ifNil: [ ^ nil ].
output := String new writeStream.
output nextPutAll: self class yamlMetadataDelimiter; cr.
output nextPutAll: self yamlMetadataString.
output nextPutAll: self class yamlMetadataDelimiter; cr.
^ output contents.
]

View File

@ -11,16 +11,13 @@ Class {
}
{ #category : #persistence }
MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: anObject [
| file preprocessed |
MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [
| file |
file := aFileReferenceOrFileName asFileReference.
file ensureDelete.
file exists ifFalse: [ file ensureCreateFile ].
(#('String' 'ByteString' 'WideString') includes: anObject className )
ifTrue: [ preprocessed := anObject ]
ifFalse: [preprocessed := STON toStringPretty: anObject ].
file writeStreamDo: [ :stream |
stream nextPutAll: preprocessed ].
stream nextPutAll: text withUnixLineEndings].
self inform: 'Exported as: ', String cr, file fullName.
^ file
]

View File

@ -10,7 +10,7 @@ Class {
{ #category : #accessing }
MiniDocs class >> altKeys [
^ BlAlternativeCombination new
combination: (BlSingleKeyCombination key:BlKeyboardKey altLeft)
key: (BlSingleKeyCombination key:BlKeyboardKey altLeft)
or: (BlSingleKeyCombination key:BlKeyboardKey altRight)
]
@ -34,17 +34,15 @@ MiniDocs class >> altShiftRightCombo [
{ #category : #accessing }
MiniDocs class >> appFolder [
| tempFolder |
tempFolder := ExoRepo userDataFolder / 'Mutabit' / 'MiniDocs'.
| tempFolder userDataFolder |
userDataFolder := Smalltalk os isWindows
ifTrue: [ FileLocator home / 'AppData' / 'Local' ]
ifFalse: [ FileLocator userData ].
tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'.
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
^ tempFolder
]
{ #category : #accessing }
MiniDocs class >> exportAsSton: anObject on: aFileReference [
MarkupFile exportAsFileOn: aFileReference containing: (STON toStringPretty: anObject) withInternetLineEndings
]
{ #category : #accessing }
MiniDocs class >> importGrafoscopioFile: aFileReference [
@ -89,7 +87,7 @@ MiniDocs class >> keyboardShortcutsRemapping [
{ #category : #accessing }
MiniDocs class >> shiftKeys [
^ BlAlternativeCombination new
combination: (BlSingleKeyCombination key:BlKeyboardKey shiftLeft)
key: (BlSingleKeyCombination key:BlKeyboardKey shiftLeft)
or: (BlSingleKeyCombination key:BlKeyboardKey shiftRight)
]
@ -97,7 +95,6 @@ MiniDocs class >> shiftKeys [
MiniDocs class >> yamlToJson: yamlString [
"This method uses a external binary written in Nim, as the native Pharo parser for YAML, written in PetitParser,
was less robust and unable to parse correctly the same strings as the external one."
yamlString ifNil: [ ^ Dictionary new ].
self yamlToJsonBinary exists ifFalse: [ self installYamlToJson ].
OSSUnixSubprocess new
@ -105,7 +102,7 @@ MiniDocs class >> yamlToJson: yamlString [
arguments: {yamlString};
redirectStdout;
runAndWaitOnExitDo: [ :process :outString |
^ (STONJSON fromString: outString allButFirst accentedCharactersCorrection) first
^ (STONJSON fromString: outString allButFirst) first
]
]

View File

@ -39,7 +39,7 @@ MiniDocsServer class >> restart [
{ #category : #accessing }
MiniDocsServer class >> singleton [
^ singleton ifNil: [ singleton := MiniDocsServer teapot ]
^ singleton
]
{ #category : #accessing }

View File

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

View File

@ -0,0 +1,82 @@
"
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.
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 ].
OSSUnixSubprocess new
command: 'nimble';
arguments: #('refresh');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ]
]
{ #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

@ -1,100 +0,0 @@
Extension { #name : #OrderedDictionary }
{ #category : #'*MiniDocs' }
OrderedDictionary >> addErrata: noteString [
self errata add: noteString
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> asLepiterSnippet [
| response |
self at: 'className' ifAbsent: [ ^ nil ].
response := (self at: 'className') asClass new.
[ response fromDictionary: self ] onErrorDo: [ ].
[ response fromString: (self at: 'content') ] onErrorDo: [ ].
self at: 'origin' ifPresent: [ response metadata at: 'origin' put: (self at: 'origin') ].
self at: 'errata' ifPresent: [ response metadata at: 'errata' put: (self at: 'errata') ].
^ response
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> asYAML [
^ (YQ jsonToYaml: self) accentedCharactersCorrection.
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> errata [
^ self at: 'errata' ifAbsentPut: [ OrderedCollection new]
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> redefineTimestampsBefore: dateAndTime [
self at: 'modified' put: dateAndTime asDateAndTime.
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

@ -1,162 +0,0 @@
"
I model the interaction between Pandoc and Grafoscopio.
"
Class {
#name : #Pandoc,
#superclass : #Object,
#classInstVars : [
'executable'
],
#category : #'MiniDocs-Core'
}
{ #category : #'*MiniDocs' }
Pandoc class >> convertString: aString from: inputFormat to: outputFormat [
OSSUnixSubprocess new
shellCommand: 'echo "', aString , '" | pandoc -f ', inputFormat,' -t ', outputFormat;
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
].
]
{ #category : #'as yet unclassified' }
Pandoc class >> downloadLuaFilters [
self luaFilters do: [ :filter | | filterUrl |
filterUrl := filter asUrl.
(FileLocator temp asFileReference / (filterUrl segments last)) exists
ifFalse: [
ZnClient new
url: filterUrl;
downloadTo: FileLocator temp ] ]
]
{ #category : #accessing }
Pandoc class >> executable [
^ executable ifNil: [ self executableLocation ]
]
{ #category : #accessing }
Pandoc class >> executable: aFileReference [
executable := aFileReference
]
{ #category : #accessing }
Pandoc class >> executableLocation [
| location |
location := '/usr/bin/pandoc'.
location asFileReference exists
ifTrue: [ ^ location ]
ifFalse: [ self definePandocExecutable ]
]
{ #category : #utility }
Pandoc class >> extractImagesInUnixFor: aFileReference withFilter: aLuaFilter [
"I use Pandoc Lua scripting capabilities to extract al images links in aFileReference"
OSSUnixSubprocess new
command: 'pandoc';
arguments: {aFileReference fullName . '--lua-filter=',aLuaFilter fullName };
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [
^ ((Soup fromString: outString) findAllTags: 'td') collect: [ :each | each next ] ]
ifFalse: [
"OSSUnixProcessExitStatus has a nice #printOn: "
Transcript show: 'Command exit with error status: ', process exitStatusInterpreter printString; cr.
Transcript show: 'Stderr contents: ', errString.
]
]
]
{ #category : #accessing }
Pandoc class >> htmlStringToMarkdown: aString [
OSSUnixSubprocess new
shellCommand: 'echo "', aString , '" | pandoc -f markdown -t html';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
].
]
{ #category : #converters }
Pandoc class >> htmlToMarkdown: inputFile [
| outputFile |
outputFile := FileLocator temp / 'body.md'.
outputFile ensureDelete.
outputFile ensureCreateFile.
OSSUnixSubprocess new
command: 'pandoc';
arguments: {'-f'. 'html'. '-t'. 'markdown'. '--atx-headers'. inputFile fullName.
'--output'. outputFile fullName };
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outputFile contents ]
ifFalse: [ ^inputFile contents ]
]
]
{ #category : #'as yet unclassified' }
Pandoc class >> listImagesFrom: aFileReference [
"I provide a list of all images contained in aFile."
| filter commandString outputString |
filter := FileLocator temp asFileReference / 'image-links.lua'.
filter exists
ifFalse: [ self downloadLuaFilters ].
commandString := 'pandoc ' , aFileReference fullName
, ' --lua-filter=' , filter fullName.
^ self extractImagesInUnixFor: aFileReference withFilter: filter
]
{ #category : #utility }
Pandoc class >> luaFilters [
"I define the location of set of scripts, that allows to change the default behaviour of Pandoc
and/or the processing of supported markup languages.
For more information about Lua filters see:
https://pandoc.org/lua-filters.html
"
| filters |
filters := OrderedCollection new.
filters
add: 'http://mutabit.com/repos.fossil/dataweek/doc/tip/Artefactos/Scripts/image-links.lua'.
^ filters
]
{ #category : #converters }
Pandoc class >> markdownToHtml: inputFile [
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ]) ifTrue: [ ^ self markdownToHtmlOnUnix: inputFile ].
Smalltalk os isWindows ifTrue: [ ^ self markdownToHtmlOnWindows: inputFile ].
]
{ #category : #converters }
Pandoc class >> markdownToHtmlOnUnix: inputFile [
| outputFile |
outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html').
outputFile ensureDelete.
outputFile ensureCreateFile.
GtSubprocessWithInMemoryOutput new
shellCommand: 'pandoc -f markdown+startnum+task_lists --standalone -t html ', inputFile fullName, ' --output ', outputFile fullName;
runAndWait;
stdout.
^ outputFile.
]
{ #category : #converters }
Pandoc class >> markdownToHtmlOnWindows: inputFile [
"ToDo: This command still doesn't receive any arguments."
^ (LibC resultOfCommand: 'pandoc ', inputFile fullName) correctAccentedCharacters.
]

View File

@ -1,11 +0,0 @@
Extension { #name : #Pandoc }
{ #category : #'*MiniDocs' }
Pandoc class >> convertString: aString from: inputFormat to: outputFormat [
OSSUnixSubprocess new
shellCommand: 'echo "', aString , '" | pandoc -f ', inputFormat,' -t ', outputFormat;
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
].
]

View File

@ -0,0 +1,139 @@
Class {
#name : #PubPub,
#superclass : #Object,
#instVars : [
'address',
'tableOfContents',
'titles',
'folder',
'currentLanguage',
'languages'
],
#category : #'MiniDocs-Model'
}
{ #category : #accessing }
PubPub >> addTableOfContents: anOrderedDictionary [
self tableOfContents
at: (self currentLanguage) put: anOrderedDictionary;
yourself
]
{ #category : #accessing }
PubPub >> addTitle: aString [
self titles
at: (self currentLanguage) put: aString
]
{ #category : #accessing }
PubPub >> address [
^ address
]
{ #category : #accessing }
PubPub >> address: anUrl [
address := anUrl
]
{ #category : #accessing }
PubPub >> currentLanguage [
^ currentLanguage
]
{ #category : #accessing }
PubPub >> currentLanguage: twoLettersInISO639_1 [
currentLanguage := twoLettersInISO639_1
]
{ #category : #accessing }
PubPub >> defaultTitle [
^ self titles associations first value
]
{ #category : #accessing }
PubPub >> downloadContents [
| workingDirectory |
workingDirectory := self folder / self currentLanguage / 'book'.
self tableOfContents keysAndValuesDo: [ :name :chapterAddress | |currentFileName|
currentFileName := name, '--', chapterAddress, '.md'.
(workingDirectory / currentFileName) asFileReference ensureDelete.
(workingDirectory / 'markdown') asFileReference ensureDelete.
ZnClient new
get: self address, 'pub/', chapterAddress, '/download/markdown';
downloadTo: workingDirectory .
workingDirectory / 'markdown' renameTo: currentFileName
].
^ workingDirectory
]
{ #category : #accessing }
PubPub >> exportMarkdeepFiles [
| markdownFiles markdeepDocs |
markdownFiles := self languageFolder allChildren select: [:file |
file basename endsWith: '.md'
].
markdeepDocs := markdownFiles collect: [:file |
(Markdeep fromMarkdownFile:file)
].
markdeepDocs do: [:each |
each fromPubPubToMarkdeep exportAsFile
].
^ self languageFolder
]
{ #category : #accessing }
PubPub >> extractAllContentsRaw [
^ self frontPage xpath: '//div[@class="layout-pubs-block"]'
]
{ #category : #accessing }
PubPub >> extractRawTableOfContents [
^ self extractAllContentsRaw first xpath: '//div[contains(concat(" ",normalize-space(@class)," "), " pub-preview-component ")]'
]
{ #category : #accessing }
PubPub >> extractTableOfContents [
^ self extractRawTableOfContents collect: [:each |
PubPubContent fromXML: each
]
]
{ #category : #accessing }
PubPub >> folder [
^ folder ensureCreateDirectory
]
{ #category : #accessing }
PubPub >> folder: localDirectory [
folder := localDirectory
]
{ #category : #accessing }
PubPub >> frontPage [
"This should scrap contents of the book's front-page and translate them into Markdeep,
according to our templates."
^ (XMLHTMLParser on: (self address asUrl retrieveContents)) parseDocument
]
{ #category : #accessing }
PubPub >> languageFolder [
^ self folder / self currentLanguage
]
{ #category : #accessing }
PubPub >> printOn: aStream [
super printOn: aStream.
aStream
nextPutAll: '(',self defaultTitle, ' | ', self address, ' )'
]
{ #category : #accessing }
PubPub >> tableOfContents [
tableOfContents ifNil: [^ tableOfContents := Dictionary new].
^ tableOfContents at: self currentLanguage
]
{ #category : #accessing }
PubPub >> titles [
^ titles ifNil: [titles := OrderedDictionary new]
]

View File

@ -5,9 +5,7 @@ Class {
'title',
'language',
'url',
'thumbnail',
'work',
'contents'
'thumbnail'
],
#category : #'MiniDocs-Model'
}
@ -17,40 +15,6 @@ PubPubContent class >> fromXML: anXMLElement [
^ self new fromXML: anXMLElement
]
{ #category : #accessing }
PubPubContent >> asMarkdeepFrontPageElement [
| response anchorName anchorLink markdeepFile |
response := '' writeStream.
anchorName := '[', self title,']'.
markdeepFile := './book/', self shortName,'--',self id,'.md.html'.
anchorLink := '(', markdeepFile,')'.
response
nextPutAll: '<big>', anchorName, anchorLink,'</big><br><br>';
nextPutAll: String lf.
self thumbnail ifNotNil: [ |image|
image := '
<img
src=', self thumbnail,
' width="55%"
style="width: 400px; height: 220px; object-fit: cover;"
/>'.
response nextPutAll: '<a href="',markdeepFile,'">', image, '</a>'
].
response
nextPutAll: String lf, String lf.
^ response contents
]
{ #category : #accessing }
PubPubContent >> contents: anObject [
contents := anObject
]
{ #category : #accessing }
PubPubContent >> fileName [
^ self shortName,'--', self id, '.md'
]
{ #category : #accessing }
PubPubContent >> fromXML: aXMLElement [
| image anchor|
@ -73,23 +37,6 @@ PubPubContent >> id [
^ (self url splitOn: $/) last
]
{ #category : #'as yet unclassified' }
PubPubContent >> language: aString [
language := aString
]
{ #category : #accessing }
PubPubContent >> next [
^ self nextInstance
]
{ #category : #accessing }
PubPubContent >> previous [
| index |
index := self work tableOfContents detectIndex: [:pubContent | pubContent = self ] ifNone: [ ^ nil ].
^ self work tableOfContents at: index - 1.
]
{ #category : #accessing }
PubPubContent >> printOn: aStream [
super printOn: aStream.
@ -107,11 +54,6 @@ PubPubContent >> shortName [
^ sanitized
]
{ #category : #accessing }
PubPubContent >> thumbnail [
^ thumbnail
]
{ #category : #accessing }
PubPubContent >> thumbnail: anURL [
thumbnail := anURL
@ -136,13 +78,3 @@ PubPubContent >> url [
PubPubContent >> url: anObject [
url := anObject
]
{ #category : #accessing }
PubPubContent >> work [
^ work
]
{ #category : #accessing }
PubPubContent >> work: aPubPubWork [
work := aPubPubWork
]

View File

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

View File

@ -1,240 +0,0 @@
Class {
#name : #PubPubWork,
#superclass : #Object,
#instVars : [
'address',
'tableOfContents',
'titles',
'folder',
'currentLanguage',
'languages'
],
#category : #'MiniDocs-Model'
}
{ #category : #accessing }
PubPubWork >> addTableOfContents: anOrderedDictionary [
self tableOfContents
at: (self currentLanguage) put: anOrderedDictionary;
yourself
]
{ #category : #accessing }
PubPubWork >> addTitle: aString [
self titles
at: (self currentLanguage) put: aString
]
{ #category : #accessing }
PubPubWork >> address [
^ address
]
{ #category : #accessing }
PubPubWork >> address: anUrl [
address := anUrl
]
{ #category : #accessing }
PubPubWork >> bookishFolder [
^ { 'en' -> 'book'.
'es' -> 'libro'} asDictionary
]
{ #category : #accessing }
PubPubWork >> currentLanguage [
^ currentLanguage
]
{ #category : #accessing }
PubPubWork >> currentLanguage: twoLettersInISO639_1 [
currentLanguage := twoLettersInISO639_1
]
{ #category : #accessing }
PubPubWork >> defaultOptions [
^ { 'sourceCodeLink' -> true .
'commentsProvider' -> 'Hypothesis' } asDictionary
]
{ #category : #accessing }
PubPubWork >> defaultTitle [
^ self titles associations first value
]
{ #category : #accessing }
PubPubWork >> downloadContents [
| workingDirectory |
workingDirectory := self workingDirectory.
self tableOfContentsDictionary
keysAndValuesDo: [ :name :chapterAddress |
| currentFileName |
currentFileName := name , '--' , chapterAddress , '.md'.
(workingDirectory / currentFileName) asFileReference ensureDelete.
(workingDirectory / 'markdown') asFileReference ensureDelete.
ZnClient new
get: self address , 'pub/' , chapterAddress , '/download/markdown';
downloadTo: workingDirectory.
workingDirectory / 'markdown' renameTo: currentFileName ].
^ workingDirectory
]
{ #category : #accessing }
PubPubWork >> downloadContents2 [
| workingDirectory |
workingDirectory := self folder / self currentLanguage / 'book'.
self tableOfContentsDictionary keysAndValuesDo: [ :name :chapterAddress | |currentFileName|
currentFileName := name, '--', chapterAddress, '.md'.
(workingDirectory / currentFileName) asFileReference ensureDelete.
(workingDirectory / 'markdown') asFileReference ensureDelete.
ZnClient new
get: self address, 'pub/', chapterAddress, '/download/markdown';
downloadTo: workingDirectory .
workingDirectory / 'markdown' renameTo: currentFileName
].
^ workingDirectory
]
{ #category : #accessing }
PubPubWork >> exportToHTML [
self markdownFiles
do: [ :file | | doc |
doc := Markdown new fromFile: file.
doc exportAsHTML ].
^ self markdownFiles first parent
]
{ #category : #accessing }
PubPubWork >> exportToMarkdeep [
| markdeepDocs |
markdeepDocs := self markdownFiles
collect: [ :file | Markdeep fromMarkdownFile: file ].
markdeepDocs do: [ :each | each fromPubPubToMarkdeep exportAsFile ].
^ self languageFolder
]
{ #category : #accessing }
PubPubWork >> extractAllContentsRaw [
^ self frontPage xpath: '//div[@class="layout-pubs-block"]'
]
{ #category : #accessing }
PubPubWork >> extractRawTableOfContents [
^ self extractAllContentsRaw first xpath: '//div[contains(concat(" ",normalize-space(@class)," "), " pub-preview-component ")]'
]
{ #category : #accessing }
PubPubWork >> folder [
^ folder ensureCreateDirectory
]
{ #category : #accessing }
PubPubWork >> folder: localDirectory [
folder := localDirectory
]
{ #category : #accessing }
PubPubWork >> frontPage [
"This should scrap contents of the book's front-page and translate them into Markdeep,
according to our templates."
^ (XMLHTMLParser on: (self address asUrl retrieveContents)) parseDocument
]
{ #category : #accessing }
PubPubWork >> languageFolder [
^ self folder / self currentLanguage
]
{ #category : #accessing }
PubPubWork >> markdeepFrontPage [
| frontPage markdeepIndex |
frontPage := Markdeep new.
frontPage
title: self defaultTitle;
file: self languageFolder / 'frontPage.md.html'.
markdeepIndex := '' writeStream.
self tableOfContents do: [:pubPubContent|
markdeepIndex
nextPutAll: pubPubContent asMarkdeepFrontPageElement
].
frontPage body: markdeepIndex contents.
^ frontPage
]
{ #category : #accessing }
PubPubWork >> markdownFiles [
^ self languageFolder allChildren
select: [ :file | file basename endsWith: '.md' ]
]
{ #category : #accessing }
PubPubWork >> populateContents [
self tableOfContents isEmptyOrNil
ifTrue: [ self populateTableOfContents ].
self workingDirectory children ifEmpty: [self downloadContents].
self tableOfContents do: [:pubPubContent | | contentFile|
contentFile := self workingDirectory / pubPubContent fileName.
contentFile exists
ifTrue: [ pubPubContent contents: (Markdown new fromFile: contentFile) ]
]
]
{ #category : #accessing }
PubPubWork >> populateTableOfContents [
| contentsCollection |
contentsCollection := self extractRawTableOfContents collect: [:each |
(PubPubContent fromXML: each)
language: self currentLanguage;
work: self
].
self addTableOfContents: contentsCollection asOrderedCollection
]
{ #category : #accessing }
PubPubWork >> printOn: aStream [
super printOn: aStream.
aStream
nextPutAll: '(',self defaultTitle, ' | ', self address, ' )'
]
{ #category : #accessing }
PubPubWork >> tableOfContents [
tableOfContents ifNil: [ ^ tableOfContents := Dictionary new].
^ tableOfContents at: self currentLanguage
]
{ #category : #accessing }
PubPubWork >> tableOfContents: anObject [
tableOfContents := anObject
]
{ #category : #accessing }
PubPubWork >> tableOfContentsDictionary [
| response |
response := OrderedDictionary new.
self tableOfContents do: [:content |
response
at: content shortName put: content id
].
^ response
]
{ #category : #accessing }
PubPubWork >> titles [
^ titles ifNil: [titles := OrderedDictionary new]
]
{ #category : #accessing }
PubPubWork >> viewContentsFor: aView [
<gtView>
^ aView list
title: 'Contents';
priority: 10;
items: [ self tableOfContents ]
]
{ #category : #accessing }
PubPubWork >> workingDirectory [
^ self folder / self currentLanguage / (self bookishFolder at: self currentLanguage)
]

View File

@ -4,7 +4,7 @@ Extension { #name : #String }
String >> accentedCharactersCorrection [
| modified corrections |
corrections := {
'ó' -> 'ó' . “' -> 'Ó' . º' -> 'ú' . 'ñ' -> 'ñ' . 'Ñ' -> 'Ñ' .
'ó' -> 'ó' . º' -> 'ú' . 'ñ' -> 'ñ' .
'í' -> 'í' . 'á' -> 'á' . 'é' -> 'é' . '’' -> $' asString} asDictionary.
modified := self copy.
corrections keysAndValuesDo: [ :k :v |
@ -13,41 +13,6 @@ String >> accentedCharactersCorrection [
^ 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' }
String >> asDashedLowercase [
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
@ -55,119 +20,6 @@ String >> asDashedLowercase [
^ '-' join: (self substrings collect: [:each | each asLowercase ])
]
{ #category : #'*MiniDocs' }
String >> asInteger [
"Return the integer present in the receiver, or nil. In case of float, returns the integer part."
"'1' asInteger >>> 1"
"'-1' asInteger >>> -1"
"'10' asInteger >>> 10"
"'a' asInteger >>> nil"
"'1.234' asInteger >>> 1"
^ (self copyWithoutAll: '_') asSignedInteger
]
{ #category : #'*MiniDocs' }
String >> contentsWithoutYAMLMetadata [
| newContents |
self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
(self lines copyFrom: self yamlMetadataClosingLineNumber + 2 to: self lines size) do: [ :line |
newContents nextPutAll: line; cr ].
^ newContents contents.
]
{ #category : #'*MiniDocs' }
String >> deleteYAMLMetadata [
| newContents |
self detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
(self lines copyFrom: self yamlMetadataClosingLineNumber + 1 to: self lines size) do: [ :line |
newContents nextPutAll: line; lf;lf ].
^ newContents contents.
]
{ #category : #'*MiniDocs' }
String >> demoteMarkdownHeaders [
| response |
response := self contents lines.
self markdownHeaders associations allButFirstDo: [ :assoc |
response at: assoc key put: '#', assoc value ].
^ response asStringWithCr withInternetLineEndings
]
{ #category : #'*MiniDocs' }
String >> detectYAMLMetadata [
| lines |
lines := self lines.
^ self startsWithYAMLMetadataDelimiter
and: [ lines allButFirst
detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ]
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' }
String >> markdownHeaders [
| response headers |
headers := (LeTextSnippet string: self contents) ast // #LeHeaderNode collect: [ :each | each headerFullName asString ].
response := OrderedDictionary new.
self lines doWithIndex: [:line :index |
(line beginsWithAnyOf: headers)
ifTrue: [ response at: index put: line ]
].
^ 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' }
String >> promoteMarkdownHeaders [
| response |
response := self contents lines.
self markdownHeaders associationsDo: [ :assoc |
response at: assoc key put: assoc value allButFirst ].
^ response asStringWithCr withInternetLineEndings
]
{ #category : #'*MiniDocs' }
String >> romanizeAccents [
| modified corrections |
@ -180,61 +32,3 @@ String >> romanizeAccents [
].
^ modified
]
{ #category : #'*MiniDocs' }
String >> startsWithMarkdownAdmonition [
self lines ifEmpty: [ ^ false ].
^ self admonitionBorders includes: self lines first trimBoth
]
{ #category : #'*MiniDocs' }
String >> startsWithYAMLMetadataDelimiter [
self lines ifEmpty: [^false].
^ self lines first beginsWith: self class yamlMetadataDelimiter
]
{ #category : #'*MiniDocs' }
String >> withoutXMLTagDelimiters [
^ self copyWithoutAll: #($< $>)
]
{ #category : #'*MiniDocs' }
String >> yamlMetadataClosingLineNumber [
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."
self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ].
self lines allButFirst doWithIndex: [ :currentLine :i |
(currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ ^ i + 1 ]]
]
{ #category : #'*MiniDocs' }
String class >> yamlMetadataDelimiter [
^ '---'
]
{ #category : #'*MiniDocs' }
String >> yamlMetadataString [
| output yamlLines |
self detectYAMLMetadata ifFalse: [ ^nil ].
self lines ifEmpty: [ ^nil ].
yamlLines := self lines copyFrom: 2 to: self yamlMetadataClosingLineNumber - 1.
output := '' writeStream.
yamlLines do: [ :line |
output
nextPutAll: line;
nextPut: Character lf. ].
^ output contents
]
{ #category : #'*MiniDocs' }
String >> yamlMetadataStringWithDelimiters [
| output |
self yamlMetadataString ifNil: [ ^ nil ].
output := String new writeStream.
output nextPutAll: self class yamlMetadataDelimiter; cr.
output nextPutAll: self yamlMetadataString.
output nextPutAll: self class yamlMetadataDelimiter; cr.
^ output contents.
]

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 = $* ]
]

View File

@ -1,53 +0,0 @@
Extension { #name : #XMLElement }
{ #category : #'*MiniDocs' }
XMLElement >> asSnippetDictionary [
| response |
response := STON fromString: (self attributes at: 'st-data').
response at: 'className' put: (self attributes at: 'st-class').
response at: 'content' put: self sanitizedContent.
^ response
]
{ #category : #'*MiniDocs' }
XMLElement >> extractMarkdownImageLinkData [
| linkParserNodes sanitizedText linkParser |
linkParser := (PPCommonMarkBlockParser parse: (self contentString trimBoth: [:each | each = Character lf]) allButFirst)
accept: CMBlockVisitor new.
linkParserNodes := linkParser children first children.
linkParserNodes size = 1
ifTrue: [ sanitizedText := linkParserNodes first label text ]
ifFalse: [ sanitizedText := '' writeStream.
linkParserNodes allButLast
do: [ :each |
each className = 'PPCMText'
ifTrue: [ sanitizedText nextPutAll: each text allButFirst ].
each className = 'PPCMLink'
ifTrue: [ sanitizedText nextPutAll: each printString ] ].
sanitizedText := sanitizedText contents ].
^ {sanitizedText . self contentString }
]
{ #category : #'*MiniDocs' }
XMLElement >> sanitizedContent [
| className sanitizedText |
className := self attributes at: 'st-class'.
className = 'LeTextSnippet'
ifTrue: [ sanitizedText := self contentString.
sanitizedText := sanitizedText allButFirst.
sanitizedText := sanitizedText allButLast ].
className = 'LePharoSnippet'
ifTrue: [ | joinedText |
sanitizedText := self contentString lines.
sanitizedText := sanitizedText copyFrom: 4 to: sanitizedText size - 2.
joinedText := '' writeStream.
sanitizedText
do: [ :line |
joinedText
nextPutAll: line;
nextPut: Character lf ].
sanitizedText := joinedText contents allButLast ].
className = 'LePictureSnippet'
ifTrue: [ sanitizedText := self extractMarkdownImageLinkData ].
^ sanitizedText
]

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

@ -0,0 +1,29 @@
"
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 >> install [
^ self lastReleaseData
]
{ #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"
]

View File

@ -23,8 +23,3 @@ PPCMBlockQuote >> initialize [
PPCMBlockQuote >> isBlockLevel [
^ true
]
{ #category : #accessing }
PPCMBlockQuote >> viewBody [
^ (self className ,' ', self text) asRopedText.
]

View File

@ -39,8 +39,9 @@ PPCMLink >> printOn: aStream [
super initialize.
^ aStream
nextPutAll:
'[',self label text,']',
'(',self destination,')'
self label text,
' -> ',
self destination
]
{ #category : #accessing }