diff --git a/src/Microdown-BookTester-Tests/MiCheckerEngineTest.class.st b/src/Microdown-BookTester-Tests/MiCheckerEngineTest.class.st deleted file mode 100644 index 776658e7..00000000 --- a/src/Microdown-BookTester-Tests/MiCheckerEngineTest.class.st +++ /dev/null @@ -1,99 +0,0 @@ -Class { - #name : 'MiCheckerEngineTest', - #superclass : 'TestCase', - #instVars : [ - 'fileSystem', - 'engine', - 'file' - ], - #category : 'Microdown-BookTester-Tests', - #package : 'Microdown-BookTester-Tests' -} - -{ #category : 'running' } -MiCheckerEngineTest >> generateFilesystemExample [ - file := fileSystem workingDirectory / 'test.md'. - file writeStreamDo: [ :stream | - stream nextPutAll: 'The colour of the sky is blue. -## writing books in pharo' - ]. -] - -{ #category : 'running' } -MiCheckerEngineTest >> setUp [ - super setUp. - - fileSystem := FileSystem memory. - engine := MicCheckerEngine new. - self generateFilesystemExample. -] - -{ #category : 'running' } -MiCheckerEngineTest >> testCheckersFromConfiguration [ - | config checkers | - config := OrderedDictionary new. - config at: 'EnglishTypography' put: nil. - config at: 'HeaderCapitalization' put: 'lowercase'. - checkers := engine checkersFromConfiguration: config. - self assert: checkers size equals: 2. - self assert: (checkers anySatisfy: [:c | c isKindOf: MicEnglishTypographyChecker]). - self assert: (checkers anySatisfy: [:c | c isKindOf: MicRuleHeaderChecker]). -] - -{ #category : 'running' } -MiCheckerEngineTest >> testCheckersFromConfigurationAllCheckers [ - | config checkers | - config := OrderedDictionary new. - config at: 'EnglishTypography' put: nil. - config at: 'FrenchTypography' put: nil. - config at: 'HeaderCapitalization' put: 'lowercase'. - config at: 'Vocabulary' put: { #('colour' 'color')}. - config at: 'CodeIndentation' put: nil. - config at: 'MethodReferenceUsingHash' put: nil. - config at: 'MethodStructure' put: nil. - config at: 'NotTheFigureX' put: nil. - config at: 'CodeLastPeriod' put: 'true'. - config at: 'ParagraphLabelEndPeriod' put: 'true'. - config at: 'CodeLastPeriodInCaption' put: nil. - config at: 'LastPeriodInCode' put: nil. - checkers := engine checkersFromConfiguration: config. - self assert: checkers size equals: 12. -] - -{ #category : 'running' } -MiCheckerEngineTest >> testEngineDetectsHeaderMistake [ - | config results | - config := OrderedDictionary new. - config at: 'HeaderCapitalization' put: 'uppercase'. - results := engine runOn: file withConfiguration: config. - self deny: results isEmpty. - self assert: results size equals: 3. -] - -{ #category : 'running' } -MiCheckerEngineTest >> testEngineRunsMultipleCheckers [ - | config results | - config := OrderedDictionary new. - config at: 'Vocabulary' put: { #('colour' 'color') }. - config at: 'HeaderCapitalization' put: 'uppercase'. - results := engine runOn: file withConfiguration: config. - self deny: results isEmpty. - self assert: results size > 1. -] - -{ #category : 'running' } -MiCheckerEngineTest >> testEngineWithEmptyConfiguration [ - | config results | - config := OrderedDictionary new. - results := engine runOn: file withConfiguration: config. - self assert: results isEmpty. -] - -{ #category : 'running' } -MiCheckerEngineTest >> testEngineWithUnknownChecker [ - | config results | - config := OrderedDictionary new. - config at: 'UnknownChecker' put: nil. - results := engine runOn: file withConfiguration: config. - self assert: results isEmpty. -] diff --git a/src/Microdown-BookTester/MicAnalysisReportWriter.class.st b/src/Microdown-BookTester/MicAnalysisReportWriter.class.st index ff64d733..cfb816ab 100644 --- a/src/Microdown-BookTester/MicAnalysisReportWriter.class.st +++ b/src/Microdown-BookTester/MicAnalysisReportWriter.class.st @@ -19,7 +19,7 @@ MicAnalysisReportWriter class >> checkersFromConfiguration: aConfiguration [ | availableCheckers configuredCheckers onlyCheckers | availableCheckers := Dictionary new. - (MicChecker allSubclasses copyWithoutFirst: MicFrenchTypoChecker) + MicChecker allSubclasses do: [ :aClass | availableCheckers at: aClass checkerName diff --git a/src/Microdown-BookTester/MicCheckerDashboard.class.st b/src/Microdown-BookTester/MicCheckerDashboard.class.st new file mode 100644 index 00000000..2efcf09b --- /dev/null +++ b/src/Microdown-BookTester/MicCheckerDashboard.class.st @@ -0,0 +1,172 @@ +Class { + #name : 'MicCheckerDashboard', + #superclass : 'SpPresenter', + #instVars : [ + 'checkerList', + 'explanation', + 'filePathInput', + 'runButton', + 'results', + 'browseButton', + 'exportButton', + 'reporter', + 'file', + 'currentFile', + 'currentReporter', + 'fileContent' + ], + #category : 'Microdown-BookTester-CheckerDashboard', + #package : 'Microdown-BookTester', + #tag : 'CheckerDashboard' +} + +{ #category : 'initialization' } +MicCheckerDashboard >> connectPresenters [ + checkerList presenters do: [ :cb | + cb whenChangedDo: [ + cb state ifTrue: [ + | name checker | + name := cb label. + checker := MicChecker allSubclasses + detect: [ :c | c checkerName = name ] + ifNone: [ nil ]. + checker ifNotNil: [ + explanation text: checker explanationForConfiguration. + name = 'FrenchTypography' ifTrue: [ + checkerList presenters + detect: [ :c | c label = 'EnglishTypography' ] + ifFound: [ :c | c state: false ] ]. + name = 'EnglishTypography' ifTrue: [ + checkerList presenters + detect: [ :c | c label = 'FrenchTypography' ] + ifFound: [ :c | c state: false ] ] ] ] ] ]. + + runButton action: [ + | config output selectedCheckers | + filePathInput text isEmpty + ifTrue: [ results text: 'Please enter a file path.' ] + ifFalse: [ + currentFile := filePathInput text asFileReference. + currentFile exists + ifFalse: [ results text: 'File not found: ' , currentFile fullName ] + ifTrue: [ + selectedCheckers := (checkerList presenters + select: [ :cb | cb state ]) + collect: [ :cb | cb label ]. + selectedCheckers isEmpty + ifTrue: [ results text: 'Please select at least one checker.' ] + ifFalse: [ + results text: 'Checking... please wait.'. + config := ConfigurationForPillar newFromDictionary: + (Dictionary new + at: 'onlyCheckers' put: selectedCheckers; + yourself). + currentReporter := MicAnalysisReportWriter + reportForFolder: currentFile parent + startingFrom: currentFile + configuration: config. + currentReporter isOkay + ifTrue: [ results text: 'No problems found!' ] + ifFalse: [ + | numberedContent lines | + output := String streamContents: [ :str | + currentReporter buildReportOn: str ]. + results text: output. + exportButton enabled: true. + + lines := currentFile contents lines. + numberedContent := String streamContents: [ :s | + lines doWithIndex: [ :line :i | + s nextPutAll: i printString. + s nextPutAll: ' | '. + s nextPutAll: line. + s nextPut: Character lf ] ]. + fileContent text: numberedContent ] ] ] ] ]. + + browseButton action: [ + | chosen | + chosen := UIManager default + chooseExistingFileReference: 'Select index.md' + extensions: #('md') + path: FileSystem workingDirectory. + chosen ifNotNil: [ filePathInput text: chosen fullName ] ]. + + exportButton action: [ + | htmlReporter outputFile | + htmlReporter := MicCheckerHTMLReporter new. + htmlReporter reporter: currentReporter. + htmlReporter fileReference: currentFile. + outputFile := htmlReporter generateReport. + WebBrowser openOn: outputFile fullName. + results text: results text , String cr , 'HTML report exported to: ' , outputFile fullName ]. +] + +{ #category : 'initialization' } +MicCheckerDashboard >> defaultLayout [ + | notebook topSection | + notebook := self newNotebook. + notebook addPage: (self newNotebookPage + title: 'Results'; + presenterProvider: [ results ]; + yourself). + notebook addPage: (self newNotebookPage + title: 'File Content'; + presenterProvider: [ fileContent ]; + yourself). + topSection := SpBoxLayout newTopToBottom + add: (SpPanedLayout newLeftToRight + add: checkerList; + add: explanation; + yourself); + addLast: (SpBoxLayout newLeftToRight + add: filePathInput; + add: browseButton expand: false; + yourself) expand: false; + addLast: (SpBoxLayout newLeftToRight + add: runButton; + add: exportButton; + yourself) expand: false; + yourself. + ^ SpPanedLayout newTopToBottom + add: topSection; + add: notebook; + yourself +] + +{ #category : 'initialization' } +MicCheckerDashboard >> initializePresenters [ + checkerList := self newComponentList. + checkerList presenters: (MicChecker allSubclasses collect: [ :c | + | cb | + cb := self newCheckBox. + cb label: c checkerName. + cb ]). + explanation := self newText. + explanation text: 'Select a checker to see its explanation.'. + filePathInput := self newTextInput. + filePathInput placeholder: 'Path to index.md...'. + results := self newText. + results text: 'Results will appear here.'. + results beNotEditable. + fileContent := self newText. + fileContent beNotEditable. + runButton := self newButton. + runButton label: 'Run Checkers'. + exportButton := self newButton. + exportButton label: 'Export HTML Report'. + exportButton enabled: false. + browseButton := self newButton. + browseButton label: 'Browse'. +] + +{ #category : 'initialization' } +MicCheckerDashboard >> initializeWindow: aWindowPresenter [ + aWindowPresenter title: 'Microdown Checker Dashboard'; + initialExtent: 900 @ 700. +] + +{ #category : 'accessing' } +MicCheckerDashboard >> runButton [ + + ^ runButton +] diff --git a/src/Microdown-BookTester/MicCheckerEngine.class.st b/src/Microdown-BookTester/MicCheckerEngine.class.st deleted file mode 100644 index fd770e3a..00000000 --- a/src/Microdown-BookTester/MicCheckerEngine.class.st +++ /dev/null @@ -1,52 +0,0 @@ -" -I am a generic engine that reads a configuration dictionary and instantiates, configures and runs the appropriate checkers. -I discover all available checkers by looking at all subclasses of MicChecker that respond to #checkerName. - -Usage example: - | engine config reslts | - config := OrderedDictionary new. - config at: 'Vocabulary' put: { ('colour' -> 'color') }. - config at: 'HeaderCapitalization' put: 'uppercase'. - config at: 'EnglishTypographic' put:nil. - engine := MicCheckerEngine new. - results := engine runOn: 'myFile.md' asFileReference withConfiguration: config. - results do: [ :r | Transcript showCr: r explanation ]. -" -Class { - #name : 'MicCheckerEngine', - #superclass : 'Object', - #category : 'Microdown-BookTester-Core', - #package : 'Microdown-BookTester', - #tag : 'Core' -} - -{ #category : 'as yet unclassified' } -MicCheckerEngine >> checkersFromConfiguration: aConfiguration [ - | availableCheckers activeCheckers | - availableCheckers := Dictionary new. - MicChecker allSubclasses do: [ :aClass | - availableCheckers at: aClass checkerName put: aClass ]. - activeCheckers := OrderedCollection new. - aConfiguration keysAndValuesDo: [ :key :value | - | checkerClass checker | - checkerClass := availableCheckers at: key ifAbsent: [ nil ]. - checkerClass ifNotNil: [ - checker := checkerClass new. - checker configureFrom: value. - activeCheckers add: checker - ] - ]. - ^ activeCheckers -] - -{ #category : 'as yet unclassified' } -MicCheckerEngine >> runOn: aFileReference withConfiguration: aConfiguration [ - | activeCheckers results | - activeCheckers := self checkersFromConfiguration: aConfiguration. - results := OrderedCollection new. - activeCheckers do: [ :checker | - checker checkProject: aFileReference. - results addAll: checker results - ]. - ^ results -] diff --git a/src/Microdown-BookTester/MicCheckerHTMLReporter.class.st b/src/Microdown-BookTester/MicCheckerHTMLReporter.class.st new file mode 100644 index 00000000..e8b878cd --- /dev/null +++ b/src/Microdown-BookTester/MicCheckerHTMLReporter.class.st @@ -0,0 +1,116 @@ +Class { + #name : 'MicCheckerHTMLReporter', + #superclass : 'Object', + #instVars : [ + 'reporter', + 'fileReference' + ], + #category : 'Microdown-BookTester-CheckerDashboard', + #package : 'Microdown-BookTester', + #tag : 'CheckerDashboard' +} + +{ #category : 'accessing' } +MicCheckerHTMLReporter >> buildHTML [ + ^ String streamContents: [ :s | + s nextPutAll: '
+ +✓ No problems found!
' ] + ifFalse: [ self buildResultsOn: s]. + s nextPutAll: '' + ] +] + +{ #category : 'accessing' } +MicCheckerHTMLReporter >> buildResultsOn: aStream [ + | grouped | + grouped := reporter results groupedBy: [:r | r headerString]. + grouped keysAndValuesDo: [:header :results | + aStream nextPutAll: '' , context , '' ]. + aStream nextPutAll: '