Watching child dirs.
This commit is contained in:
parent
47947a90b9
commit
fecde624c1
@ -4,6 +4,7 @@ import com.jessebrault.ssg.buildscript.GroovyBuildScriptRunner
|
|||||||
import com.jessebrault.ssg.part.GspPartRenderer
|
import com.jessebrault.ssg.part.GspPartRenderer
|
||||||
import com.jessebrault.ssg.part.PartFilePartsProvider
|
import com.jessebrault.ssg.part.PartFilePartsProvider
|
||||||
import com.jessebrault.ssg.part.PartType
|
import com.jessebrault.ssg.part.PartType
|
||||||
|
import com.jessebrault.ssg.provider.WithWatchableDir
|
||||||
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
import com.jessebrault.ssg.specialpage.GspSpecialPageRenderer
|
||||||
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
import com.jessebrault.ssg.specialpage.SpecialPageFileSpecialPagesProvider
|
||||||
import com.jessebrault.ssg.specialpage.SpecialPageType
|
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.MarkdownTextRenderer
|
||||||
import com.jessebrault.ssg.text.TextFileTextsProvider
|
import com.jessebrault.ssg.text.TextFileTextsProvider
|
||||||
import com.jessebrault.ssg.text.TextType
|
import com.jessebrault.ssg.text.TextType
|
||||||
|
import groovy.io.FileType
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
@ -21,7 +23,11 @@ import org.apache.logging.log4j.core.LoggerContext
|
|||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
|
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardWatchEventKinds
|
import java.nio.file.StandardWatchEventKinds
|
||||||
|
import java.nio.file.WatchEvent
|
||||||
|
import java.nio.file.WatchKey
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
@CommandLine.Command(
|
@CommandLine.Command(
|
||||||
@ -149,50 +155,62 @@ class StaticSiteGeneratorCli implements Callable<Integer> {
|
|||||||
private static Integer watch(Collection<Build> builds, StaticSiteGenerator ssg) {
|
private static Integer watch(Collection<Build> builds, StaticSiteGenerator ssg) {
|
||||||
logger.traceEntry('builds: {}, ssg: {}', builds, ssg)
|
logger.traceEntry('builds: {}, ssg: {}', builds, ssg)
|
||||||
|
|
||||||
Collection<WatchableProvider> watchableProviders = []
|
// Setup watchService and watchKeys
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def watchService = FileSystems.getDefault().newWatchService()
|
def watchService = FileSystems.getDefault().newWatchService()
|
||||||
|
Map<WatchKey, Path> watchKeys = [:]
|
||||||
|
|
||||||
watchableProviders.each {
|
// Our Closure to register a path
|
||||||
it.watchableDir.toPath().register(
|
def registerPath = { Path path ->
|
||||||
|
def watchKey = path.register(
|
||||||
watchService,
|
watchService,
|
||||||
StandardWatchEventKinds.ENTRY_CREATE,
|
StandardWatchEventKinds.ENTRY_CREATE,
|
||||||
StandardWatchEventKinds.ENTRY_DELETE,
|
StandardWatchEventKinds.ENTRY_DELETE,
|
||||||
StandardWatchEventKinds.ENTRY_MODIFY
|
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
|
//noinspection GroovyInfiniteLoopStatement
|
||||||
while (true) {
|
while (true) {
|
||||||
def watchKey = watchService.take()
|
def watchKey = watchService.take()
|
||||||
logger.debug('watchKey: {}', watchKey)
|
def path = watchKeys[watchKey]
|
||||||
watchKey.pollEvents().each {
|
if (path == null) {
|
||||||
logger.debug('watchEvent: {}', it)
|
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
|
//noinspection GroovyUnreachableStatement
|
||||||
|
@ -26,7 +26,7 @@ class SimpleStaticSiteGenerator implements StaticSiteGenerator {
|
|||||||
def config = build.config
|
def config = build.config
|
||||||
|
|
||||||
// Get all texts, templates, parts, and specialPages
|
// 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 templates = config.templatesProviders.collectMany { it.getTemplates() }
|
||||||
def parts = config.partsProviders.collectMany { it.getParts() }
|
def parts = config.partsProviders.collectMany { it.getParts() }
|
||||||
def specialPages = config.specialPagesProviders.collectMany { it.getSpecialPages() }
|
def specialPages = config.specialPagesProviders.collectMany { it.getSpecialPages() }
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.jessebrault.ssg.provider
|
||||||
|
|
||||||
|
interface Provider<T> {
|
||||||
|
T provide()
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.jessebrault.ssg.provider
|
||||||
|
|
||||||
|
trait WithWatchableDir {
|
||||||
|
File watchableDir
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package com.jessebrault.ssg.text
|
package com.jessebrault.ssg.text
|
||||||
|
|
||||||
|
import com.jessebrault.ssg.provider.WithWatchableDir
|
||||||
import com.jessebrault.ssg.util.FileNameHandler
|
import com.jessebrault.ssg.util.FileNameHandler
|
||||||
import com.jessebrault.ssg.util.RelativePathHandler
|
import com.jessebrault.ssg.util.RelativePathHandler
|
||||||
import com.jessebrault.ssg.WatchableProvider
|
|
||||||
import groovy.io.FileType
|
import groovy.io.FileType
|
||||||
import groovy.transform.EqualsAndHashCode
|
import groovy.transform.EqualsAndHashCode
|
||||||
import groovy.transform.NullCheck
|
import groovy.transform.NullCheck
|
||||||
@ -11,7 +11,7 @@ import org.slf4j.LoggerFactory
|
|||||||
|
|
||||||
@NullCheck
|
@NullCheck
|
||||||
@EqualsAndHashCode(includeFields = true)
|
@EqualsAndHashCode(includeFields = true)
|
||||||
class TextFileTextsProvider implements TextsProvider, WatchableProvider {
|
class TextFileTextsProvider implements TextsProvider, WithWatchableDir {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(TextFileTextsProvider)
|
private static final Logger logger = LoggerFactory.getLogger(TextFileTextsProvider)
|
||||||
|
|
||||||
@ -21,6 +21,9 @@ class TextFileTextsProvider implements TextsProvider, WatchableProvider {
|
|||||||
TextFileTextsProvider(Collection<TextType> textTypes, File textsDir) {
|
TextFileTextsProvider(Collection<TextType> textTypes, File textsDir) {
|
||||||
this.textTypes = textTypes
|
this.textTypes = textTypes
|
||||||
this.textsDir = textsDir
|
this.textsDir = textsDir
|
||||||
|
if (!this.textsDir.isDirectory()) {
|
||||||
|
throw new IllegalArgumentException('textsDir must be a directory, given: ' + this.textsDir)
|
||||||
|
}
|
||||||
this.watchableDir = this.textsDir
|
this.watchableDir = this.textsDir
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,11 +34,7 @@ class TextFileTextsProvider implements TextsProvider, WatchableProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Collection<Text> getTextFiles() {
|
Collection<Text> provide() {
|
||||||
if (!this.textsDir.isDirectory()) {
|
|
||||||
throw new IllegalArgumentException('textsDir must be a directory')
|
|
||||||
}
|
|
||||||
|
|
||||||
def textFiles = []
|
def textFiles = []
|
||||||
this.textsDir.eachFileRecurse(FileType.FILES) {
|
this.textsDir.eachFileRecurse(FileType.FILES) {
|
||||||
def type = this.getTextType(it)
|
def type = this.getTextType(it)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.jessebrault.ssg.text
|
package com.jessebrault.ssg.text
|
||||||
|
|
||||||
interface TextsProvider {
|
import com.jessebrault.ssg.provider.Provider
|
||||||
Collection<Text> getTextFiles()
|
|
||||||
|
interface TextsProvider extends Provider<Collection<Text>> {
|
||||||
Collection<TextType> getTextTypes()
|
Collection<TextType> getTextTypes()
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class TextFileTextsProviderTests {
|
|||||||
void findsFile() {
|
void findsFile() {
|
||||||
new FileTreeBuilder(this.textsDir).file('test.md', '**Hello, World!**')
|
new FileTreeBuilder(this.textsDir).file('test.md', '**Hello, World!**')
|
||||||
|
|
||||||
def r = this.textsProvider.getTextFiles()
|
def r = this.textsProvider.provide()
|
||||||
assertEquals(1, r.size())
|
assertEquals(1, r.size())
|
||||||
def f0 = r[0]
|
def f0 = r[0]
|
||||||
assertEquals('test', f0.path)
|
assertEquals('test', f0.path)
|
||||||
@ -36,7 +36,7 @@ class TextFileTextsProviderTests {
|
|||||||
file('nested.md', '**Hello!**')
|
file('nested.md', '**Hello!**')
|
||||||
}
|
}
|
||||||
|
|
||||||
def r = this.textsProvider.getTextFiles()
|
def r = this.textsProvider.provide()
|
||||||
assertEquals(1, r.size())
|
assertEquals(1, r.size())
|
||||||
def f0 = r[0]
|
def f0 = r[0]
|
||||||
assertEquals('nested/nested', f0.path)
|
assertEquals('nested/nested', f0.path)
|
||||||
@ -48,7 +48,7 @@ class TextFileTextsProviderTests {
|
|||||||
void ignoresUnsupportedFile() {
|
void ignoresUnsupportedFile() {
|
||||||
new FileTreeBuilder(this.textsDir).file('.ignored', 'Ignored!')
|
new FileTreeBuilder(this.textsDir).file('.ignored', 'Ignored!')
|
||||||
|
|
||||||
def r = this.textsProvider.getTextFiles()
|
def r = this.textsProvider.provide()
|
||||||
assertEquals(0, r.size())
|
assertEquals(0, r.size())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user