Phase 5a: add configurable Jai module roots
This commit is contained in:
119
src/main/kotlin/dev/hgh/jai/settings/JaiProjectSettings.kt
Normal file
119
src/main/kotlin/dev/hgh/jai/settings/JaiProjectSettings.kt
Normal file
@@ -0,0 +1,119 @@
|
||||
package dev.hgh.jai.settings
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.RootsChangeRescanningInfo
|
||||
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import java.io.File
|
||||
|
||||
/** Project-level paths used to find Jai modules and external Jai source files. */
|
||||
@State(
|
||||
name = "JaiProjectSettings",
|
||||
storages = [Storage(StoragePathMacros.WORKSPACE_FILE)],
|
||||
)
|
||||
class JaiProjectSettings(
|
||||
private val project: Project,
|
||||
) : PersistentStateComponent<JaiProjectSettings.State> {
|
||||
/** The persisted form deliberately contains paths, not VirtualFiles. */
|
||||
class State {
|
||||
@JvmField
|
||||
var rootPaths: MutableList<String> = mutableListOf()
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is State && rootPaths == other.rootPaths
|
||||
|
||||
override fun hashCode(): Int = rootPaths.hashCode()
|
||||
}
|
||||
|
||||
private var state = State()
|
||||
|
||||
/** Configured roots in precedence order. Invalid paths remain visible in Settings. */
|
||||
fun rootPaths(): List<String> = state.rootPaths.toList()
|
||||
|
||||
/** Existing configured directories, in the same order as their persisted paths. */
|
||||
fun configuredRoots(): List<VirtualFile> =
|
||||
rootPaths()
|
||||
.mapNotNull { LocalFileSystem.getInstance().findFileByPath(it) }
|
||||
.filter { it.isValid && it.isDirectory }
|
||||
|
||||
fun setRootPaths(paths: Collection<String>) {
|
||||
val normalized = paths.mapNotNull(::normalizePath).distinct()
|
||||
if (normalized == state.rootPaths) return
|
||||
|
||||
state = State().also { it.rootPaths.addAll(normalized) }
|
||||
}
|
||||
|
||||
override fun getState(): State = State().also { it.rootPaths.addAll(state.rootPaths) }
|
||||
|
||||
override fun loadState(loadedState: State) {
|
||||
val normalized = loadedState.rootPaths.mapNotNull(::normalizePath).distinct()
|
||||
if (normalized == state.rootPaths) return
|
||||
|
||||
state = State().also { it.rootPaths.addAll(normalized) }
|
||||
}
|
||||
|
||||
private fun normalizePath(rawPath: String): String? {
|
||||
val trimmed = rawPath.trim()
|
||||
if (trimmed.isEmpty()) return null
|
||||
|
||||
val expanded =
|
||||
when {
|
||||
trimmed == "~" -> {
|
||||
System.getProperty("user.home")
|
||||
}
|
||||
|
||||
trimmed.startsWith("~/") -> {
|
||||
System.getProperty("user.home") + trimmed.removePrefix("~")
|
||||
}
|
||||
|
||||
else -> {
|
||||
trimmed
|
||||
}
|
||||
}
|
||||
val file = File(expanded)
|
||||
val absolute =
|
||||
if (file.isAbsolute) {
|
||||
file
|
||||
} else {
|
||||
project.basePath?.let { File(it, expanded) } ?: file.absoluteFile
|
||||
}
|
||||
return FileUtil.toSystemIndependentName(
|
||||
absolute
|
||||
.toPath()
|
||||
.normalize()
|
||||
.toAbsolutePath()
|
||||
.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
/** Rebuilds the project/index roots after Settings has been applied. */
|
||||
fun refreshProjectRoots() {
|
||||
if (!project.isInitialized || project.isDisposed) return
|
||||
|
||||
val refresh =
|
||||
Runnable {
|
||||
if (!project.isDisposed) {
|
||||
ProjectRootManagerEx
|
||||
.getInstanceEx(project)
|
||||
.makeRootsChange(Runnable {}, RootsChangeRescanningInfo.TOTAL_RESCAN)
|
||||
}
|
||||
}
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isWriteAccessAllowed) {
|
||||
refresh.run()
|
||||
} else {
|
||||
application.runWriteAction(refresh)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): JaiProjectSettings = project.getService(JaiProjectSettings::class.java)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user