120 lines
4.1 KiB
Kotlin
120 lines
4.1 KiB
Kotlin
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)
|
|
}
|
|
}
|