My use case - an lsp for the language that I’m building a plugin for requires a json file with all the available source files that is generated by another cli tool. So I setup a service that uses AsyncFileListener
to regenerate the json when files are moved and deleted, but the create event is not tracked.
Should I use BulkFileListener
instead and do something like
project.messageBus.connect(fileListenerDisposable).subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun after(events: MutableList<out VFileEvent>) {
val index = ProjectFileIndex.getInstance(project)
val luaFileEvents =
events
.filter { it is VFileMoveEvent || it is VFileCopyEvent || it is VFileCreateEvent || it is VFileDeleteEvent }
.filter {
it.file?.let { file -> file.fileType == LuauFileType && index.isInContent(file) } ?: false
}
if (luaFileEvents.isEmpty()) return
queueSourcemapRegeneration()
}
})
Or there is a better way?
I also saw there is PluggableFileWatcher
, but given its API documentation I doubt it’s the thing I need.
From AsyncFileListener
javadoc:
Note that this listener can only observe the state of the system before VFS events, and so
it can’t work with anything that would be after them, e.g. there will be no file in
VFileCreateEvent
, com.intellij.openapi.roots.FileIndexFacade
won’t know anything about the updated state after VFS change, and so on.
- Do you not receive any
VFileCreateEvent
in your listener?
- Or does
index.isInContent(file)
fail here?
I don’t get any VFileCreateEvent
in the AsyncFileListener
at all, and it’s according to the docs as far as I understand, so AsyncFileListener
doesn’t suit my needs. The question is: is the BulkFileListener
a good enough solution here, or there is a better one?
BulkFileListener
and AsyncFileListener
operate on the same set of events, the difference is only in when they are called. For your case, the latter suits just fine.
The most probable reason for missing VFileCreateEvent
is that no one has called VirtualFile#getChildren
on the parent directory yet.
Better avoid calling VFileEvent#getFile
on VFileCreateEvent
– it is time-consuming and might return null
. I’d suggest using VFileEvent#getFile
together with FileTypeRegistry#getFileTypeByFileName
.
You also need to handle renames (VFilePropertyChangeEvent#getPropertyName == VirtualFile#PROP_NAME
).
If you ask me, Lua is such a simple language that coding a proper language support is simpler than dealing with LSP and that another CLI tool 
I doublechecked and I see the VFCreateEvent
, indeed. The problem was that file doesn’t exist, and this is exactly what is said in the docs, but I’ve read it as “the event doesn’t exist”
It’s time to learn how to read.
I found that renames are missing today, so that’s also fixed, thank you for mentioning.
I did this in the end, because all the CLI stuff doesn’t support complex setups anyway and just path checks should be fine
val eventsOfInterest =
events
.filter { it is VFileMoveEvent || it is VFileCopyEvent || it is VFileCreateEvent || it is VFileDeleteEvent || (it is VFilePropertyChangeEvent && it.propertyName == VirtualFile.PROP_NAME) }
.filter {
// Should be fine to feed the whole path into getFileTypeByFileName, it searches for the last extension inside
FileTypeRegistry.getInstance().getFileTypeByFileName(it.path) == LuauFileType && it.path.startsWith(projectDir.path)
}
As for the lua simplicity, I kinda agree, but while I dive into the full language support the quick one via lsp makes sense. And the plugin is for luau
it’s almost lua, but with types, and building the type inference myself is not that straightforward.
Thank you so much for the help!
1 Like