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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
package me.kyren223.trident.utils
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import me.kyren223.trident.data.Settings
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
}
private fun getShortPath(path: String): String? {
// For path `/path/to/file.ext` the result should be `$file`
// Unless for dot files, in which case it should be `$ext`
// Examples:
// - `/path/to/TridentUtils.kt` -> `$TridentUtils`
// - `/path/to/someBinary` -> `$someBinary`
// - `/path/to/.ideavimrc` -> `$ideavimrc`
return path.split("/")
.lastOrNull()
?.let {
it.split(".")
.let { parts ->
when (parts.size) {
0 -> it
1, 2 -> parts.firstOrNull()
else -> return null
}
}
}
?.let { "\$${it}" }
}
fun append(project: Project, file: VirtualFile) {
val files = get(project).toMutableList()
val automaticMapping = Settings.state.automaticMapping
val path = if (automaticMapping) {
val shortPath = getShortPath(file.path) ?: file.path
if (TridentMappings.exists(project, shortPath)) file.path else {
TridentMappings.append(project, shortPath, file.path)
shortPath
}
} else file.path
files.add(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)
.map { TridentMappings.expand(project, it) }
.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? {
val cycle = Settings.state.indexCycling
val count = getFiles(project).size
val i = if (cycle) ((index % count) + count) % count else index
val files = getFiles(project)
return files.getOrNull(i)
}
}
object TridentMappings {
private const val PROPERTIES_KEY = "TridentMappings"
private fun isValidEntry(key: String, value: String): Boolean {
// Requirements:
// - Key and value must not be blank
// - key must only contain alphanumeric characters and start with $
// - 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()
val map = list
.map { it.split("=") }
.associate { it[0] to it[1] }
.toMutableMap()
project.guessProjectDir()?.let {
map["..."] = it.path
map["\$project"] = it.path
}
return map
}
fun expand(project: Project, path: String): String {
val recursive = Settings.state.recursiveMapping
val mappings = get(project).toList().sortedByDescending { it.first.length }
var expandedPath = path
while (true) {
var expanded = false
mappings.forEach { (key, value) ->
val expansion = expandedPath.replace(key, value)
if (expansion != expandedPath) {
expandedPath = expansion
expanded = true
}
}
if (!recursive || !expanded) break
}
return expandedPath
}
fun exists(project: Project, key: String): Boolean {
return get(project).containsKey(key)
}
}
|