1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
package me.kyren223.trident.utils
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import me.kyren223.trident.data.SettingsState
object TridentList {
private const val PROPERTIES_KEY = "TridentList"
private fun isValidFile(project: Project, path: String): Boolean {
// Requirements:
// - File must exist on disk after expansion
val expandedPath = TridentMappings.expand(project, path)
return expandedPath.isNotBlank() && LocalFileSystem.getInstance().findFileByPath(expandedPath) != null
}
fun append(project: Project, file: VirtualFile) {
// TODO add a setting to automatically add an expansion to mappings if it doesn't exist
// Should be `$<file>` for `<path>/<file>.<extension>`
// Unless if the file starts with a dot, in which case it should be `$<extension>`
val files = get(project).toMutableList()
files.add(file.path)
set(project, files)
}
fun set(project: Project, files: List<String>) {
val properties = PropertiesComponent.getInstance(project)
val list = files.filter { isValidFile(project, it) }
properties.setList(PROPERTIES_KEY, list)
}
fun get(project: Project): List<String> {
val properties = PropertiesComponent.getInstance(project)
return properties.getList(PROPERTIES_KEY) ?: emptyList()
}
private fun getFiles(project: Project): List<VirtualFile> {
return get(project).mapNotNull { LocalFileSystem.getInstance().findFileByPath(it) }
}
fun getIndexOfFile(project: Project, file: VirtualFile): Int? {
val files = getFiles(project)
return files.indexOfFirst { it.path == file.path }.takeUnless { it == -1 }
}
fun select(project: Project, index: Int): VirtualFile? {
// TODO make this a setting if it should "cycle" or not
val count = getFiles(project).size
val i = ((index % count) + count) % count
val files = getFiles(project)
return files.getOrNull(i)
}
}
object TridentMappings {
private const val PROPERTIES_KEY = "TridentMappings"
private fun isValidEntry(key: String, value: String): Boolean {
// TODO change requirements, key should be alphanumeric and must only have 1 $ as the first character
// Requirements:
// - Key and value must not be blank
// - key must only contain alphanumeric characters and $
// - value must not contain =
return key.isNotBlank() && value.isNotBlank() &&
key.matches(Regex("^[a-zA-Z0-9\$]+$")) &&
value.matches(Regex("^[^=]+$"))
}
fun append(project: Project, key: String, value: String, overwrite: Boolean = false) {
val map = get(project).toMutableMap()
if (map.contains(key) && !overwrite) return
map[key] = value
set(project, map)
}
fun set(project: Project, mappings: Map<String, String>) {
val properties = PropertiesComponent.getInstance(project)
val list = mappings
.filter { isValidEntry(it.key, it.value) }
.map { "${it.key}=${it.value}" }
.toMutableList()
properties.setList(PROPERTIES_KEY, list)
}
fun get(project: Project): Map<String, String> {
val properties = PropertiesComponent.getInstance(project)
val list = properties.getList(PROPERTIES_KEY) ?: return emptyMap()
return list.map { it.split("=") }.associate { it[0] to it[1] }
}
fun expand(project: Project, path: String): String {
val recursive = SettingsState.instance.recursiveMapping
val mappings = get(project).toList().sortedByDescending { it.first.length }
var expanded = path
mappings.forEach { (key, value) ->
expanded = expanded.replace(key, if (recursive) expand(project, value) else value)
}
return expanded
}
}
|