Gitnuro/src/main/kotlin/com/jetpackduba/gitnuro/git/FileChangesWatcher.kt

107 lines
4.1 KiB
Kotlin

package com.jetpackduba.gitnuro.git
import com.jetpackduba.gitnuro.extensions.systemSeparator
import com.jetpackduba.gitnuro.logging.printLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException
import java.nio.file.*
import java.nio.file.StandardWatchEventKinds.*
import java.nio.file.attribute.BasicFileAttributes
import javax.inject.Inject
private const val TAG = "FileChangesWatcher"
class FileChangesWatcher @Inject constructor() {
private val _changesNotifier = MutableSharedFlow<Boolean>()
val changesNotifier: SharedFlow<Boolean> = _changesNotifier
val keys = mutableMapOf<WatchKey, Path>()
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = withContext(Dispatchers.IO) {
println(ignoredDirsPath)
val watchService = FileSystems.getDefault().newWatchService()
val path = Paths.get(pathStr)
path.register(
watchService,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY
)
// register directory and subdirectories but ignore dirs by gitignore
Files.walkFileTree(path, object : SimpleFileVisitor<Path>() {
@Throws(IOException::class)
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
val isIgnoredDirectory = ignoredDirsPath.any { "$pathStr/$it" == dir.toString() }
return if (!isIgnoredDirectory) {
val watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
keys[watchKey] = dir
FileVisitResult.CONTINUE
} else {
FileVisitResult.SKIP_SUBTREE
}
}
})
var key: WatchKey
while (watchService.take().also { key = it } != null) {
val events = key.pollEvents()
val dir = keys[key] ?: return@withContext
val hasGitDirectoryChanged = dir.startsWith("$pathStr$systemSeparator.git$systemSeparator")
if (events.count() == 1) {
val fileChanged = events.first().context().toString()
val fullPathOfFileChanged = "$pathStr$systemSeparator.git$systemSeparator$fileChanged"
// Ignore COMMIT_EDITMSG changes
if (isGitMessageFile(pathStr, fullPathOfFileChanged)) {
printLog(TAG, "Ignored changes in $fullPathOfFileChanged")
return@withContext
}
}
printLog(TAG, "Has git dir changed: $hasGitDirectoryChanged")
_changesNotifier.emit(hasGitDirectoryChanged)
// Check if new directories have been added to add them to the watchService
launch(Dispatchers.IO) {
for (event in events) {
if (event.kind() == ENTRY_CREATE) {
try {
val eventFile = File(dir.toAbsolutePath().toString() + systemSeparator + event.context())
if (eventFile.isDirectory) {
val eventPath = eventFile.toPath()
printLog(TAG, "New directory $eventFile detected, adding it to watchService")
val watchKey =
eventPath.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
keys[watchKey] = eventPath
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
}
key.reset()
}
}
private fun isGitMessageFile(repoPath: String, fullPathOfFileChanged: String): Boolean {
val gitDir = "$repoPath$systemSeparator.git$systemSeparator"
return fullPathOfFileChanged == "${gitDir}COMMIT_EDITMSG" ||
fullPathOfFileChanged == "${gitDir}MERGE_MSG"
}
}