diff --git a/Common/src/main/java/com/tabnineCommon/binary/requests/fileLifecycle/Workspace.kt b/Common/src/main/java/com/tabnineCommon/binary/requests/fileLifecycle/Workspace.kt new file mode 100644 index 00000000..c61d063e --- /dev/null +++ b/Common/src/main/java/com/tabnineCommon/binary/requests/fileLifecycle/Workspace.kt @@ -0,0 +1,17 @@ +package com.tabnineCommon.binary.requests.fileLifecycle + +import com.google.gson.annotations.SerializedName +import com.tabnineCommon.binary.BinaryRequest +import com.tabnineCommon.binary.requests.EmptyResponse + +data class Workspace( + @SerializedName("root_paths") private val rootPaths: List +) : BinaryRequest { + override fun response(): Class { + return EmptyResponse::class.java + } + + override fun serialize(): Any { + return mapOf("Workspace" to this) + } +} diff --git a/Common/src/main/java/com/tabnineCommon/chat/ChatMessagesRouter.kt b/Common/src/main/java/com/tabnineCommon/chat/ChatMessagesRouter.kt index 22365f3a..d27a378c 100644 --- a/Common/src/main/java/com/tabnineCommon/chat/ChatMessagesRouter.kt +++ b/Common/src/main/java/com/tabnineCommon/chat/ChatMessagesRouter.kt @@ -2,6 +2,7 @@ package com.tabnineCommon.chat import InitHandler import InsertAtCursorHandler +import WorkspaceFoldersHandler import com.google.gson.JsonElement import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project @@ -39,7 +40,8 @@ object ChatMessagesRouter { "get_settings" to GetChatSettingsHandler(gson), "update_settings" to UpdateChatSettingsHandler(gson), "get_server_url" to GetServerUrlHandler(gson), - "navigate_to_location" to NavigateToLocationHandler(gson) + "navigate_to_location" to NavigateToLocationHandler(gson), + "workspace_folders" to WorkspaceFoldersHandler(gson) ) fun handleRawMessage(rawRequest: String, project: Project): String { diff --git a/Common/src/main/java/com/tabnineCommon/chat/commandHandlers/WorkspaceFoldersHandler.kt b/Common/src/main/java/com/tabnineCommon/chat/commandHandlers/WorkspaceFoldersHandler.kt new file mode 100644 index 00000000..48aeaf5a --- /dev/null +++ b/Common/src/main/java/com/tabnineCommon/chat/commandHandlers/WorkspaceFoldersHandler.kt @@ -0,0 +1,20 @@ +import com.google.gson.Gson +import com.google.gson.JsonElement +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import com.tabnineCommon.chat.commandHandlers.ChatMessageHandler +import com.tabnineCommon.lifecycle.WorkspaceListenerService + +data class WorkspaceFoldersPayload(private val rootPaths: List) + +class WorkspaceFoldersHandler(gson: Gson) : ChatMessageHandler(gson) { + private val workspaceService = ServiceManager.getService(WorkspaceListenerService::class.java) + + override fun handle(payload: Unit?, project: Project): WorkspaceFoldersPayload? { + val rootPaths = workspaceService.getWorkspaceRootPaths() ?: return null + return WorkspaceFoldersPayload(rootPaths) + } + + override fun deserializeRequest(data: JsonElement?) { + } +} diff --git a/Common/src/main/java/com/tabnineCommon/lifecycle/LifecycleInitializer.kt b/Common/src/main/java/com/tabnineCommon/lifecycle/LifecycleInitializer.kt index f1e3f38f..49f1fbef 100644 --- a/Common/src/main/java/com/tabnineCommon/lifecycle/LifecycleInitializer.kt +++ b/Common/src/main/java/com/tabnineCommon/lifecycle/LifecycleInitializer.kt @@ -4,5 +4,6 @@ import com.intellij.openapi.components.ServiceManager fun initializeLifecycleEndpoints() { ServiceManager.getService(BinaryStateService::class.java).startUpdateLoop() + ServiceManager.getService(WorkspaceListenerService::class.java).start() TabnineFileEditorListener.registerListener() } diff --git a/Common/src/main/java/com/tabnineCommon/lifecycle/WorkspaceListenerService.kt b/Common/src/main/java/com/tabnineCommon/lifecycle/WorkspaceListenerService.kt new file mode 100644 index 00000000..6e777f34 --- /dev/null +++ b/Common/src/main/java/com/tabnineCommon/lifecycle/WorkspaceListenerService.kt @@ -0,0 +1,52 @@ +package com.tabnineCommon.lifecycle + +import com.intellij.openapi.components.Service +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.ProjectManager +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.util.concurrency.AppExecutorUtil +import com.tabnineCommon.binary.requests.fileLifecycle.Workspace +import com.tabnineCommon.general.DependencyContainer +import java.net.URL +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicBoolean + +@Service +class WorkspaceListenerService { + private val binaryRequestFacade = DependencyContainer.instanceOfBinaryRequestFacade() + private val scheduler = AppExecutorUtil.getAppScheduledExecutorService() + private val started = AtomicBoolean(false) + + fun start() { + if (started.getAndSet(true)) return + + scheduler.scheduleWithFixedDelay( + { + val rootPaths = getWorkspaceRootPaths() ?: return@scheduleWithFixedDelay + binaryRequestFacade.executeRequest(Workspace(rootPaths)) + }, + 5, 30, TimeUnit.SECONDS + ) + } + + fun getWorkspaceRootPaths(): List? { + val project = ProjectManager.getInstance().openProjects.firstOrNull() ?: return null + if (project.isDisposed) { + Logger.getInstance(javaClass).warn("Project ${project.name} is disposed, skipping workspace update") + return null + } + + val rootPaths = mutableListOf() + for (contentRootUrl in ProjectRootManager.getInstance(project).contentRootUrls) { + val url = URL(contentRootUrl) + if (url.protocol != "file") { + Logger.getInstance(javaClass).debug("Skipping workspace update for project ${project.name}, unsupported protocol ${url.protocol}") + continue + } + rootPaths.add(url.path) + } + + Logger.getInstance(javaClass).debug("Root paths for project ${project.name} found: $rootPaths") + return rootPaths + } +}