Watching child dirs.

This commit is contained in:
JesseBrault0709 2023-01-10 12:09:25 -06:00
parent 47947a90b9
commit fecde624c1
7 changed files with 72 additions and 44 deletions

View File

@ -4,6 +4,7 @@ import com.jessebrault.ssg.buildscript.GroovyBuildScriptRunner
import com.jessebrault.ssg.part.GspPartRenderer
import com.jessebrault.ssg.part.PartFilePartsProvider
import com.jessebrault.ssg.part.PartType
import com.jessebrault.ssg.provider.WithWatchableDir
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
import com.jessebrault.ssg.specialpage.SpecialPageType
@ -14,6 +15,7 @@ import com.jessebrault.ssg.text.MarkdownFrontMatterGetter
import com.jessebrault.ssg.text.MarkdownTextRenderer
import com.jessebrault.ssg.text.TextFileTextsProvider
import com.jessebrault.ssg.text.TextType
import groovy.io.FileType
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@ -21,7 +23,11 @@ import org.apache.logging.log4j.core.LoggerContext
import picocli.CommandLine
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
import java.nio.file.WatchEvent
import java.nio.file.WatchKey
import java.util.concurrent.Callable
@CommandLine.Command(
@ -149,50 +155,62 @@ class StaticSiteGeneratorCli implements Callable<Integer> {
private static Integer watch(Collection<Build> builds, StaticSiteGenerator ssg) {
logger.traceEntry('builds: {}, ssg: {}', builds, ssg)
Collection<WatchableProvider> watchableProviders = []
builds.each {
it.config.textProviders.each {
if (it instanceof WatchableProvider) {
watchableProviders << it
}
}
it.config.templatesProviders.each {
if (it instanceof WatchableProvider) {
watchableProviders << it
}
}
it.config.partsProviders.each {
if (it instanceof WatchableProvider) {
watchableProviders << it
}
}
it.config.specialPagesProviders.each {
if (it instanceof WatchableProvider) {
watchableProviders << it
}
}
}
// Setup watchService and watchKeys
def watchService = FileSystems.getDefault().newWatchService()
Map<WatchKey, Path> watchKeys = [:]
watchableProviders.each {
it.watchableDir.toPath().register(
// Our Closure to register a path
def registerPath = { Path path ->
def watchKey = path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
)
watchKeys[watchKey] = path
}
// Get all base watchableDirs
Collection<WithWatchableDir> watchableProviders = []
builds.each {
it.config.textProviders.each {
if (it instanceof WithWatchableDir) {
watchableProviders << it
}
}
}
// register them and their child directories using the Closure above
watchableProviders.each {
def baseDirFile = it.watchableDir
registerPath(baseDirFile.toPath())
baseDirFile.eachFile(FileType.DIRECTORIES) {
registerPath(it.toPath())
}
}
//noinspection GroovyInfiniteLoopStatement
while (true) {
def watchKey = watchService.take()
logger.debug('watchKey: {}', watchKey)
watchKey.pollEvents().each {
logger.debug('watchEvent: {}', it)
def path = watchKeys[watchKey]
if (path == null) {
logger.warn('unexpected watchKey: {}', watchKey)
} else {
watchKey.pollEvents().each {
assert it instanceof WatchEvent<Path>
def childName = it.context()
def childPath = path.resolve(childName)
logger.debug('childName: {}, childPath: {}', childName, childPath)
if (it.kind() == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(childPath)) {
logger.debug('registering dir with path: {}', childPath)
registerPath(childPath)
}
}
}
def valid = watchKey.reset()
if (!valid) {
def removedPath = watchKeys.remove(watchKey)
logger.debug('removed path: {}', removedPath)
}
watchKey.reset()
}
//noinspection GroovyUnreachableStatement

View File

@ -26,7 +26,7 @@ class SimpleStaticSiteGenerator implements StaticSiteGenerator {
def config = build.config
// Get all texts, templates, parts, and specialPages
def texts = config.textProviders.collectMany { it.getTextFiles() }
def texts = config.textProviders.collectMany { it.provide() }
def templates = config.templatesProviders.collectMany { it.getTemplates() }
def parts = config.partsProviders.collectMany { it.getParts() }
def specialPages = config.specialPagesProviders.collectMany { it.getSpecialPages() }

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.provider
interface Provider<T> {
T provide()
}

View File

@ -0,0 +1,5 @@
package com.jessebrault.ssg.provider
trait WithWatchableDir {
File watchableDir
}

View File

@ -1,8 +1,8 @@
package com.jessebrault.ssg.text
import com.jessebrault.ssg.provider.WithWatchableDir
import com.jessebrault.ssg.util.FileNameHandler
import com.jessebrault.ssg.util.RelativePathHandler
import com.jessebrault.ssg.WatchableProvider
import groovy.io.FileType
import groovy.transform.EqualsAndHashCode
import groovy.transform.NullCheck
@ -11,7 +11,7 @@ import org.slf4j.LoggerFactory
@NullCheck
@EqualsAndHashCode(includeFields = true)
class TextFileTextsProvider implements TextsProvider, WatchableProvider {
class TextFileTextsProvider implements TextsProvider, WithWatchableDir {
private static final Logger logger = LoggerFactory.getLogger(TextFileTextsProvider)
@ -21,6 +21,9 @@ class TextFileTextsProvider implements TextsProvider, WatchableProvider {
TextFileTextsProvider(Collection<TextType> textTypes, File textsDir) {
this.textTypes = textTypes
this.textsDir = textsDir
if (!this.textsDir.isDirectory()) {
throw new IllegalArgumentException('textsDir must be a directory, given: ' + this.textsDir)
}
this.watchableDir = this.textsDir
}
@ -31,11 +34,7 @@ class TextFileTextsProvider implements TextsProvider, WatchableProvider {
}
@Override
Collection<Text> getTextFiles() {
if (!this.textsDir.isDirectory()) {
throw new IllegalArgumentException('textsDir must be a directory')
}
Collection<Text> provide() {
def textFiles = []
this.textsDir.eachFileRecurse(FileType.FILES) {
def type = this.getTextType(it)

View File

@ -1,6 +1,7 @@
package com.jessebrault.ssg.text
interface TextsProvider {
Collection<Text> getTextFiles()
import com.jessebrault.ssg.provider.Provider
interface TextsProvider extends Provider<Collection<Text>> {
Collection<TextType> getTextTypes()
}

View File

@ -22,7 +22,7 @@ class TextFileTextsProviderTests {
void findsFile() {
new FileTreeBuilder(this.textsDir).file('test.md', '**Hello, World!**')
def r = this.textsProvider.getTextFiles()
def r = this.textsProvider.provide()
assertEquals(1, r.size())
def f0 = r[0]
assertEquals('test', f0.path)
@ -36,7 +36,7 @@ class TextFileTextsProviderTests {
file('nested.md', '**Hello!**')
}
def r = this.textsProvider.getTextFiles()
def r = this.textsProvider.provide()
assertEquals(1, r.size())
def f0 = r[0]
assertEquals('nested/nested', f0.path)
@ -48,7 +48,7 @@ class TextFileTextsProviderTests {
void ignoresUnsupportedFile() {
new FileTreeBuilder(this.textsDir).file('.ignored', 'Ignored!')
def r = this.textsProvider.getTextFiles()
def r = this.textsProvider.provide()
assertEquals(0, r.size())
}