summaryrefslogtreecommitdiff
path: root/src/main/kotlin/me
diff options
context:
space:
mode:
authorKyren223 <ulmliad223@gmail.com>2024-05-09 00:41:36 +0300
committerKyren223 <ulmliad223@gmail.com>2024-05-09 00:41:36 +0300
commit1e30fa7ded9939fa45a45f82f84c1e8cd95d68aa (patch)
treef2b60d48330937603a8b068dbb5a983be33d8fb8 /src/main/kotlin/me
parent5da72c7ec98edfab7fbebc335e1a9fba116988a5 (diff)
Refactored a lot of the code and added a bunch of features and settings. Not finished yet but committing, so I can push to GitHub
Diffstat (limited to 'src/main/kotlin/me')
-rw-r--r--src/main/kotlin/me/kyren223/trident/PathMappingsMenu.kt63
-rw-r--r--src/main/kotlin/me/kyren223/trident/QuickMenu.kt75
-rw-r--r--src/main/kotlin/me/kyren223/trident/QuickMenuSelectAction.kt11
-rw-r--r--src/main/kotlin/me/kyren223/trident/SettingsData.kt58
-rw-r--r--src/main/kotlin/me/kyren223/trident/TridentState.kt185
-rw-r--r--src/main/kotlin/me/kyren223/trident/Utils.kt12
-rw-r--r--src/main/kotlin/me/kyren223/trident/actions/AppendAction.kt (renamed from src/main/kotlin/me/kyren223/trident/AppendAction.kt)5
-rw-r--r--src/main/kotlin/me/kyren223/trident/actions/SelectAction.kt (renamed from src/main/kotlin/me/kyren223/trident/SelectAction.kt)19
-rw-r--r--src/main/kotlin/me/kyren223/trident/actions/TridentListAction.kt (renamed from src/main/kotlin/me/kyren223/trident/ToggleQuickMenuAction.kt)12
-rw-r--r--src/main/kotlin/me/kyren223/trident/actions/TridentListSelectAction.kt14
-rw-r--r--src/main/kotlin/me/kyren223/trident/actions/TridentMappingsAction.kt (renamed from src/main/kotlin/me/kyren223/trident/PathMappingsMenuAction.kt)7
-rw-r--r--src/main/kotlin/me/kyren223/trident/data/SettingsData.kt124
-rw-r--r--src/main/kotlin/me/kyren223/trident/data/SettingsState.kt (renamed from src/main/kotlin/me/kyren223/trident/SettingsState.kt)9
-rw-r--r--src/main/kotlin/me/kyren223/trident/data/TridentState.kt181
-rw-r--r--src/main/kotlin/me/kyren223/trident/tests/MappingsExpansionTest.kt42
-rw-r--r--src/main/kotlin/me/kyren223/trident/ui/SettingsConfigurable.kt (renamed from src/main/kotlin/me/kyren223/trident/SettingsConfigurable.kt)15
-rw-r--r--src/main/kotlin/me/kyren223/trident/ui/TridentListMenu.kt94
-rw-r--r--src/main/kotlin/me/kyren223/trident/ui/TridentMappingsMenu.kt84
-rw-r--r--src/main/kotlin/me/kyren223/trident/utils/TridentUtils.kt106
-rw-r--r--src/main/kotlin/me/kyren223/trident/utils/Utils.kt16
20 files changed, 705 insertions, 427 deletions
diff --git a/src/main/kotlin/me/kyren223/trident/PathMappingsMenu.kt b/src/main/kotlin/me/kyren223/trident/PathMappingsMenu.kt
deleted file mode 100644
index d098323..0000000
--- a/src/main/kotlin/me/kyren223/trident/PathMappingsMenu.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package me.kyren223.trident
-
-import com.intellij.openapi.editor.LogicalPosition
-import com.intellij.openapi.ui.DialogWrapper
-import com.intellij.ui.EditorTextField
-import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.newapi.vim
-import java.awt.event.FocusEvent
-import java.awt.event.FocusListener
-import javax.swing.JComponent
-
-class PathMappingsMenu(private val content: String) : DialogWrapper(true) {
- private lateinit var editorTextField: EditorTextField
-
- init {
- val settings = SettingsState.instance
- this.title = "Trident Path Mappings Menu"
- setSize(settings.width, settings.height)
- super.init()
- }
-
- override fun createCenterPanel(): JComponent {
- val settings = SettingsState.instance
- editorTextField = EditorTextField(this.content)
- editorTextField.setOneLineMode(false)
- editorTextField.addSettingsProvider {
- it.setFontSize(settings.fontSize)
- it.isInsertMode = true
- it.caretModel.moveToLogicalPosition(LogicalPosition(0, 0))
- it.settings.isLineNumbersShown = true
- }
-
- editorTextField.addFocusListener(object : FocusListener {
- override fun focusGained(e: FocusEvent?) {
- val editor = editorTextField.editor!!.vim
- val context = injector.executionContextManager.onEditor(editor, null)
- simulateKeyPress("<ESC>", editor, context)
- }
-
- override fun focusLost(e: FocusEvent?) { /* Do nothing */ }
- })
-
- return editorTextField
- }
-
- override fun getPreferredFocusedComponent(): JComponent {
- return this.editorTextField
- }
-
- override fun doOKAction() {
- TridentState.pathMappingsContent = editorTextField.text
- super.doOKAction()
- }
-
- override fun doCancelAction() {
- TridentState.pathMappingsContent = editorTextField.text
- super.doCancelAction()
- }
-
- override fun createSouthPanel(): JComponent? {
- return null
- }
-}
diff --git a/src/main/kotlin/me/kyren223/trident/QuickMenu.kt b/src/main/kotlin/me/kyren223/trident/QuickMenu.kt
deleted file mode 100644
index 3ef3871..0000000
--- a/src/main/kotlin/me/kyren223/trident/QuickMenu.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-@file:Suppress("SameParameterValue")
-
-package me.kyren223.trident
-
-import com.intellij.openapi.editor.LogicalPosition
-import com.intellij.openapi.ui.DialogWrapper
-import com.intellij.ui.EditorTextField
-import com.maddyhome.idea.vim.KeyHandler
-import com.maddyhome.idea.vim.api.ExecutionContext
-import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.newapi.vim
-import java.awt.event.FocusEvent
-import java.awt.event.FocusListener
-import javax.swing.JComponent
-
-class QuickMenu(private val content: String) : DialogWrapper(true) {
- private lateinit var editorTextField: EditorTextField
-
- init {
- val settings = SettingsState.instance
- this.title = "Trident Quick Menu"
- setSize(settings.width, settings.height)
- super.init()
- }
-
- override fun createCenterPanel(): JComponent {
- val settings = SettingsState.instance
- editorTextField = EditorTextField(this.content)
- editorTextField.setOneLineMode(false)
- editorTextField.addSettingsProvider {
- it.setFontSize(settings.fontSize)
- it.isInsertMode = true
- it.caretModel.moveToLogicalPosition(LogicalPosition(0, 0))
- it.settings.isLineNumbersShown = true
- }
-
- editorTextField.addFocusListener(object : FocusListener {
- override fun focusGained(e: FocusEvent?) {
- val editor = editorTextField.editor!!.vim
- val context = injector.executionContextManager.onEditor(editor, null)
- simulateKeyPress("<ESC>", editor, context)
- }
-
- override fun focusLost(e: FocusEvent?) { /* Do nothing */ }
- })
-
- return editorTextField
- }
-
-
- override fun getPreferredFocusedComponent(): JComponent {
- return this.editorTextField
- }
-
- fun select() {
- if (this.editorTextField.editor == null) return
- TridentState.quickMenuSelectedIndex = this.editorTextField.editor!!.caretModel.logicalPosition.line
- doOKAction()
- }
-
- override fun doOKAction() {
- TridentState.quickMenuContent = editorTextField.text
- super.doOKAction()
- }
-
- override fun doCancelAction() {
- TridentState.quickMenuContent = editorTextField.text
- super.doCancelAction()
- }
-
- override fun createSouthPanel(): JComponent? {
- return null
- }
-}
diff --git a/src/main/kotlin/me/kyren223/trident/QuickMenuSelectAction.kt b/src/main/kotlin/me/kyren223/trident/QuickMenuSelectAction.kt
deleted file mode 100644
index 0ae320a..0000000
--- a/src/main/kotlin/me/kyren223/trident/QuickMenuSelectAction.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package me.kyren223.trident
-
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-
-class QuickMenuSelectAction : AnAction() {
- override fun actionPerformed(e: AnActionEvent) {
- TridentState.quickMenuSelect()
- }
-
-}
diff --git a/src/main/kotlin/me/kyren223/trident/SettingsData.kt b/src/main/kotlin/me/kyren223/trident/SettingsData.kt
deleted file mode 100644
index 89ca91a..0000000
--- a/src/main/kotlin/me/kyren223/trident/SettingsData.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package me.kyren223.trident
-
-import com.intellij.ui.JBIntSpinner
-import com.intellij.ui.components.JBCheckBox
-import com.intellij.ui.components.JBLabel
-import com.intellij.util.ui.FormBuilder
-import javax.swing.JPanel
-
-class SettingsData {
-
- val panel: JPanel
- private val width: JBIntSpinner = JBIntSpinner(800, 1, 5000, 10)
- private val height: JBIntSpinner = JBIntSpinner(400, 1, 5000, 10)
- private val fontSize: JBIntSpinner = JBIntSpinner(20, 1, 100)
- private val enterToSelect: JBCheckBox = JBCheckBox()
-
- init {
- panel = FormBuilder.createFormBuilder()
- .addLabeledComponent(JBLabel("QuickMenu width"), width, 1, false)
- .addLabeledComponent(JBLabel("QuickMenu height"), height, 1, false)
- .addLabeledComponent(JBLabel("QuickMenu font size"), fontSize, 1, false)
- .addLabeledComponent(JBLabel("Enter to select item in the QuickMenu"), enterToSelect, 1, false)
- .addComponentFillVertically(JPanel(), 0)
- .panel
- }
-
- fun getWidth(): Int {
- return width.number
- }
-
- fun getHeight(): Int {
- return height.number
- }
-
- fun getFontSize(): Int {
- return fontSize.number
- }
-
- fun getEnterToSelect(): Boolean {
- return enterToSelect.isSelected
- }
-
- fun setWidth(width: Int) {
- this.width.number = width
- }
-
- fun setHeight(height: Int) {
- this.height.number = height
- }
-
- fun setFontSize(fontSize: Int) {
- this.fontSize.number = fontSize
- }
-
- fun setEnterToSelect(enter: Boolean) {
- this.enterToSelect.isSelected = enter
- }
-}
diff --git a/src/main/kotlin/me/kyren223/trident/TridentState.kt b/src/main/kotlin/me/kyren223/trident/TridentState.kt
deleted file mode 100644
index 1163789..0000000
--- a/src/main/kotlin/me/kyren223/trident/TridentState.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-package me.kyren223.trident
-
-import com.intellij.ide.util.PropertiesComponent
-import com.intellij.openapi.fileEditor.FileEditorManager
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vfs.LocalFileSystem
-import com.intellij.openapi.vfs.VirtualFile
-
-object TridentState {
- private var projectFiles: MutableMap<String, MutableList<VirtualFile>> = mutableMapOf()
- private var pathMappings: MutableMap<String, MutableMap<String, String>> = mutableMapOf()
- private const val TRIDENT_LIST_KEY = "TridentList"
- private const val PATH_MAPPINGS_LIST_KEY = "PathMappings"
- private var quickMenu: QuickMenu? = null
- var quickMenuSelectedIndex: Int? = null
- var quickMenuContent: String = ""
- private var pathMappingsMenu: PathMappingsMenu? = null
- var pathMappingsContent: String = ""
-
- fun appendFile(project: Project, file: VirtualFile) {
- if (!file.isValid) return
- loadProject(project)
- val files = projectFiles[project.name]
- files!!.add(file)
- saveProject(project)
- }
-
- fun selectFile(project: Project, index: Int): VirtualFile? {
- if (index < 0) return null
- loadProject(project)
- val files = projectFiles[project.name]
- if (files!!.size <= index) return null
- return files[index]
- }
-
- private fun loadProject(project: Project) {
- if (project.name in projectFiles) return
- val properties = PropertiesComponent.getInstance(project)
-
- val pathMappingsList = properties.getList(PATH_MAPPINGS_LIST_KEY)
- var map = mutableMapOf<String, String>()
- if (pathMappingsList == null) {
- map["..."] = project.basePath!!
- } else {
- for (entry in pathMappingsList) {
- val split = entry.split("=")
- if (split.size != 2) continue
- map[split[0]] = split[1]
- }
- }
- this.pathMappings[project.name] = map
-
- val list = properties.getList(TRIDENT_LIST_KEY)
- if (list == null) {
- projectFiles[project.name] = mutableListOf()
- } else {
- val fs = LocalFileSystem.getInstance()
- val files = list.stream()
- .map { fs.findFileByPath(it) }
- .toList()
- .filterNotNull()
- .filter { it.isValid }
- .toMutableList()
- projectFiles[project.name] = files
- }
- }
-
- private fun saveProject(project: Project) {
- if (project.name !in projectFiles) return
- val files = projectFiles[project.name]
- val properties = PropertiesComponent.getInstance(project)
-
- val stringList = files!!.stream()
- .filter { it.isValid }
- .map { it.path }
- .toList()
- properties.setList(TRIDENT_LIST_KEY, stringList)
-
- val pathMappingsString = pathMappings[project.name]!!.entries
- .joinToString("\n") { "${it.key}=${it.value}" }
- .split("\n")
- properties.setList(PATH_MAPPINGS_LIST_KEY, pathMappingsString)
- }
-
- fun getIndexOfFile(project: Project, data: VirtualFile?): Int {
- loadProject(project)
- val files = projectFiles[project.name]
- if (files == null || data == null) return -1
- return files.indexOfFirst { it.path == data.path }
- }
-
- fun getFileCount(project: Project): Int {
- loadProject(project)
- return projectFiles[project.name]!!.size
- }
-
- fun toggleQuickMenu(project: Project) {
- if (quickMenu != null && quickMenu!!.isShowing) {
- quickMenu!!.doCancelAction()
- return
- }
-
- loadProject(project)
- val content = getProjectContent(project)
- quickMenu = QuickMenu(content)
- val result = quickMenu!!.showAndGet()
-
- if (content != quickMenuContent) {
- retrieveProjectFiles(project, quickMenuContent)
- saveProject(project)
- }
-
- if (!result) return
- val index = quickMenuSelectedIndex ?: return
- quickMenuSelectedIndex = null
- val file = selectFile(project, index) ?: return
- FileEditorManager.getInstance(project).openFile(file, true)
- }
-
- fun quickMenuSelect() {
- if (quickMenu == null || !quickMenu!!.isShowing) return
- quickMenu!!.select()
- }
-
- private fun getProjectContent(project: Project): String {
- loadProject(project)
- val map = pathMappings[project.name] ?: throw IllegalStateException("Path mappings not found")
- println("map: $map")
- var content = projectFiles[project.name]!!
- .joinToString("\n") { it.path }
- .trim()
- println("content: $content")
- for ((key, value) in map) {
- println("key: $key | value: $value")
- content = content.replace(value, key)
- }
- println("content: $content")
- return content
- }
-
- private fun retrieveProjectFiles(project: Project, content: String) {
- loadProject(project)
- val files = content
- .split("\n")
- .asSequence()
- .map { it.trim() }
- .map { line ->
- pathMappings[project.name]!!.entries.fold(line) { acc, (key, value) ->
- acc.replace(key, value)
- }
- }
-// .map {
-// pathMappings[project.name]!!.entries.forEach { (key, value) -> it.replace(key, value) }
-// it
-// }
- .filter { it.isNotBlank() }
- .map { LocalFileSystem.getInstance().findFileByPath(it) }
- .filterNotNull()
- .filter { it.isValid }
- .toMutableList()
- projectFiles[project.name] = files
- }
-
- fun togglePathMappingsMenu(project: Project) {
- if (pathMappingsMenu != null && pathMappingsMenu!!.isShowing) {
- pathMappingsMenu!!.doCancelAction()
- return
- }
-
- loadProject(project)
- val content = pathMappings[project.name]!!.entries
- .joinToString("\n") { "${it.key}=${it.value}" }
- pathMappingsMenu = PathMappingsMenu(content)
- pathMappingsMenu!!.showAndGet()
-
- if (content != pathMappingsContent) {
- pathMappingsContent.split("\n").forEach {
- val split = it.split("=")
- if (split.size != 2) return@forEach
- this.pathMappings[project.name]!![split[0]] = split[1]
- }
- saveProject(project)
- }
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/me/kyren223/trident/Utils.kt b/src/main/kotlin/me/kyren223/trident/Utils.kt
deleted file mode 100644
index 7ff2d58..0000000
--- a/src/main/kotlin/me/kyren223/trident/Utils.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package me.kyren223.trident
-
-import com.maddyhome.idea.vim.KeyHandler
-import com.maddyhome.idea.vim.api.ExecutionContext
-import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.api.injector
-
-fun simulateKeyPress(key: String, editor: VimEditor, context: ExecutionContext.Editor) {
- KeyHandler.getInstance().handleKey(editor, injector.parser.parseKeys(key)[0], context)
-}
-
-
diff --git a/src/main/kotlin/me/kyren223/trident/AppendAction.kt b/src/main/kotlin/me/kyren223/trident/actions/AppendAction.kt
index f058f86..1b9a2b4 100644
--- a/src/main/kotlin/me/kyren223/trident/AppendAction.kt
+++ b/src/main/kotlin/me/kyren223/trident/actions/AppendAction.kt
@@ -1,15 +1,16 @@
-package me.kyren223.trident
+package me.kyren223.trident.actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
+import me.kyren223.trident.utils.TridentList
class AppendAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project: Project = e.project ?: return
val file: VirtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return
- TridentState.appendFile(project, file)
+ TridentList.append(project, file)
}
}
diff --git a/src/main/kotlin/me/kyren223/trident/SelectAction.kt b/src/main/kotlin/me/kyren223/trident/actions/SelectAction.kt
index 2e09bb9..37617c4 100644
--- a/src/main/kotlin/me/kyren223/trident/SelectAction.kt
+++ b/src/main/kotlin/me/kyren223/trident/actions/SelectAction.kt
@@ -1,32 +1,33 @@
-package me.kyren223.trident
+package me.kyren223.trident.actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.fileEditor.FileEditorManager
+import me.kyren223.trident.utils.TridentList
class SelectAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
var index = when (val actionId = e.actionManager.getId(this)) {
NEXT -> {
- val current = TridentState.getIndexOfFile(project, e.getData(PlatformDataKeys.VIRTUAL_FILE))
- if (current == -1) return
+ val file = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return
+ val current = TridentList.getIndexOfFile(project, file) ?: return
current + 1
}
+
PREV -> {
- val current = TridentState.getIndexOfFile(project, e.getData(PlatformDataKeys.VIRTUAL_FILE))
- if (current == -1) return
+ val file = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return
+ val current = TridentList.getIndexOfFile(project, file) ?: return
current - 1
}
+
else -> {
- getIndex(actionId)
+ getIndex(actionId!!)
}
}
- val count = TridentState.getFileCount(project)
- index = ((index % count) + count) % count
- val file = TridentState.selectFile(project, index) ?: return
+ val file = TridentList.select(project, index) ?: return
FileEditorManager.getInstance(project).openFile(file, true)
}
diff --git a/src/main/kotlin/me/kyren223/trident/ToggleQuickMenuAction.kt b/src/main/kotlin/me/kyren223/trident/actions/TridentListAction.kt
index a8a78ab..cef0bf2 100644
--- a/src/main/kotlin/me/kyren223/trident/ToggleQuickMenuAction.kt
+++ b/src/main/kotlin/me/kyren223/trident/actions/TridentListAction.kt
@@ -1,19 +1,21 @@
-package me.kyren223.trident
+package me.kyren223.trident.actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.key.MappingOwner
+import me.kyren223.trident.data.SettingsState
+import me.kyren223.trident.ui.TridentListMenu
-class ToggleQuickMenuAction : AnAction() {
+class TridentListAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
if (!remapped) remap()
- if (e.project != null) {
- TridentState.toggleQuickMenu(e.project!!)
- }
+ val project = e.project ?: return
+ TridentListMenu.open(project)
}
+ // TODO move the remap to somewhere else
private fun remap() {
if (!SettingsState.instance.enterToSelect) return
val keys = injector.parser.parseKeys(":action TridentQuickMenuSelect<cr>")
diff --git a/src/main/kotlin/me/kyren223/trident/actions/TridentListSelectAction.kt b/src/main/kotlin/me/kyren223/trident/actions/TridentListSelectAction.kt
new file mode 100644
index 0000000..d1dfd2d
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/actions/TridentListSelectAction.kt
@@ -0,0 +1,14 @@
+package me.kyren223.trident.actions
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import me.kyren223.trident.ui.TridentListMenu
+import me.kyren223.trident.utils.TridentList
+
+class TridentListSelectAction : AnAction() {
+ override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
+ TridentList.select(project, TridentListMenu.line)
+ }
+
+}
diff --git a/src/main/kotlin/me/kyren223/trident/PathMappingsMenuAction.kt b/src/main/kotlin/me/kyren223/trident/actions/TridentMappingsAction.kt
index 6b46f6a..b303c9e 100644
--- a/src/main/kotlin/me/kyren223/trident/PathMappingsMenuAction.kt
+++ b/src/main/kotlin/me/kyren223/trident/actions/TridentMappingsAction.kt
@@ -1,11 +1,12 @@
-package me.kyren223.trident
+package me.kyren223.trident.actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
+import me.kyren223.trident.ui.TridentMappingsMenu
-class PathMappingsMenuAction : AnAction() {
+class TridentMappingsAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
- TridentState.togglePathMappingsMenu(project)
+ TridentMappingsMenu.open(project)
}
}
diff --git a/src/main/kotlin/me/kyren223/trident/data/SettingsData.kt b/src/main/kotlin/me/kyren223/trident/data/SettingsData.kt
new file mode 100644
index 0000000..2c5c467
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/data/SettingsData.kt
@@ -0,0 +1,124 @@
+package me.kyren223.trident.data
+
+import com.intellij.ui.JBColor
+import com.intellij.ui.JBIntSpinner
+import com.intellij.ui.components.JBCheckBox
+import com.intellij.ui.components.JBLabel
+import com.intellij.util.ui.FormBuilder
+import com.intellij.util.ui.JBFont
+import javax.swing.JPanel
+
+class SettingsData {
+
+ private val minValue = 1
+ private val maxValue = 5000
+ private val step = 10
+
+ val panel: JPanel
+
+ private val width = JBIntSpinner(SettingsState.instance.width, minValue, maxValue, step)
+ private val height = JBIntSpinner(SettingsState.instance.height, minValue, maxValue, step)
+ private val fontSize = JBIntSpinner(SettingsState.instance.fontSize, minValue, maxValue, step)
+
+ private val enterToSelect = JBCheckBox(null, SettingsState.instance.enterToSelect)
+ private val rememberLine = JBCheckBox(null, SettingsState.instance.rememberLine)
+ private val automaticMapping = JBCheckBox(null, SettingsState.instance.automaticMapping)
+ private val recursiveMapping = JBCheckBox(null, SettingsState.instance.recursiveMapping)
+
+ init {
+ panel = FormBuilder.createFormBuilder()
+ .addComponent(JBLabel("Popup settings").withFont(JBFont.h4()))
+ .addLabeledComponent("Popup width", width)
+ .addLabeledComponent("Popup height", height)
+ .addLabeledComponent("Popup font size", fontSize)
+ .addSeparator()
+
+ .addComponent(JBLabel("Trident settings").withFont(JBFont.h4()))
+ .addLabeledComponent("Enter to select item", enterToSelect)
+ .addComponent(desc("If enabled while the TridentList is open, " +
+ "pressing enter will select the item in the current line and open the file (if it exists)."))
+ .addVerticalGap(step)
+
+ .addLabeledComponent("Automatic mapping (when possible)", automaticMapping)
+ .addComponent(desc("If enabled, when using the Append action, " +
+ "the full file path will be added to the Trident Mappings."))
+ .addComponent(desc("Mapping Entry: $<file>=<path>/<file>.<extension>"))
+ .addVerticalGap(step)
+
+ .addLabeledComponent("Recursive mapping", recursiveMapping)
+ .addComponent(desc("If enabled, when expanding a mapping for a path, " +
+ "each entry will be expanded recursively."))
+ .addComponent(desc("This allows mapping entries to be nested (include other mappings)."))
+ .addVerticalGap(step)
+
+ .addLabeledComponent("Remember last line", rememberLine)
+ .addComponent(desc("If enabled, the current line of the TridentList will be remembered " +
+ "and will be selected when the TridentList is opened."))
+ .addComponent(desc("Note, this does not persist across restarts, it's run-time only."))
+
+ .addComponentFillVertically(JPanel(), 0)
+ .panel
+ }
+
+ private fun desc(text: String): JBLabel {
+ val label = JBLabel(text).withFont(JBFont.small())
+ label.foreground = JBColor.GRAY
+ return label
+ }
+
+ fun getWidth(): Int {
+ return width.number
+ }
+
+ fun getHeight(): Int {
+ return height.number
+ }
+
+ fun getFontSize(): Int {
+ return fontSize.number
+ }
+
+ fun getEnterToSelect(): Boolean {
+ return enterToSelect.isSelected
+ }
+
+ fun setWidth(width: Int) {
+ this.width.number = width
+ }
+
+ fun setHeight(height: Int) {
+ this.height.number = height
+ }
+
+ fun setFontSize(fontSize: Int) {
+ this.fontSize.number = fontSize
+ }
+
+ fun setEnterToSelect(enter: Boolean) {
+ this.enterToSelect.isSelected = enter
+ }
+
+ fun getAutomaticMapping(): Boolean {
+ return automaticMapping.isSelected
+ }
+
+ fun setAutomaticMapping(automaticMapping: Boolean) {
+ this.automaticMapping.isSelected = automaticMapping
+ }
+
+ fun getRememberLine(): Boolean {
+ return rememberLine.isSelected
+ }
+
+ fun setRememberLine(rememberLine: Boolean) {
+ this.rememberLine.isSelected = rememberLine
+ }
+
+ fun getRecursiveMapping(): Boolean {
+ return recursiveMapping.isSelected
+ }
+
+ fun setRecursiveMapping(recursiveMapping: Boolean) {
+ this.recursiveMapping.isSelected = recursiveMapping
+ }
+}
diff --git a/src/main/kotlin/me/kyren223/trident/SettingsState.kt b/src/main/kotlin/me/kyren223/trident/data/SettingsState.kt
index b0544f4..16ec372 100644
--- a/src/main/kotlin/me/kyren223/trident/SettingsState.kt
+++ b/src/main/kotlin/me/kyren223/trident/data/SettingsState.kt
@@ -1,4 +1,4 @@
-package me.kyren223.trident
+package me.kyren223.trident.data
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.PersistentStateComponent
@@ -7,13 +7,16 @@ import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil
// Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-@State(name = "me.kyren223.trident.SettingsState", storages = [Storage("SdkSettingsPlugin.xml")])
+@State(name = "me.kyren223.trident.data.SettingsState", storages = [Storage("SdkSettingsPlugin.xml")])
class SettingsState : PersistentStateComponent<SettingsState?> {
- var enterToSelect = true
var width = 800
var height = 400
var fontSize = 20
+ var enterToSelect = true
+ var automaticMapping = false
+ var recursiveMapping = false
+ var rememberLine = false
override fun getState(): SettingsState {
return this
diff --git a/src/main/kotlin/me/kyren223/trident/data/TridentState.kt b/src/main/kotlin/me/kyren223/trident/data/TridentState.kt
new file mode 100644
index 0000000..54529e9
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/data/TridentState.kt
@@ -0,0 +1,181 @@
+package me.kyren223.trident.data
+
+//object TridentState {
+// private const val TRIDENT_LIST_KEY = "TridentList"
+// private const val TRIDENT_MAPPINGS_KEY = "TridentMappings"
+//
+// private var list: MutableMap<String, MutableList<VirtualFile>> = mutableMapOf()
+// private var mappings: MutableMap<String, MutableMap<String, String>> = mutableMapOf()
+//
+// private var quickMenu: QuickMenu? = null
+// var quickMenuSelectedIndex: Int? = null
+// var quickMenuContent: String = ""
+// private var pathMappingsMenu: PathMappingsMenu? = null
+// var pathMappingsContent: String = ""
+//
+//// fun appendFile(project: Project, file: VirtualFile) {
+//// if (!file.isValid) return
+//// loadProject(project)
+//// val files = list[project.name]
+//// files!!.add(file)
+//// saveProject(project)
+//// }
+//
+//// fun selectFile(project: Project, index: Int): VirtualFile? {
+//// if (index < 0) return null
+//// loadProject(project)
+//// val files = list[project.name]
+//// if (files!!.size <= index) return null
+//// return files[index]
+//// }
+//
+//// private fun loadProject(project: Project) {
+//// if (project.name in list) return
+//// val properties = PropertiesComponent.getInstance(project)
+////
+//// val pathMappingsList = properties.getList(TRIDENT_MAPPINGS_KEY)
+//// var map = mutableMapOf<String, String>()
+//// if (pathMappingsList == null) {
+//// map["..."] = project.basePath!!
+//// } else {
+//// for (entry in pathMappingsList) {
+//// val split = entry.split("=")
+//// if (split.size != 2) continue
+//// map[split[0]] = split[1]
+//// }
+//// }
+//// mappings[project.name] = map
+////
+//// val list = properties.getList(TRIDENT_LIST_KEY)
+//// if (list == null) {
+//// this.list[project.name] = mutableListOf()
+//// } else {
+//// val fs = LocalFileSystem.getInstance()
+//// val files = list.stream()
+//// .map { fs.findFileByPath(it) }
+//// .toList()
+//// .filterNotNull()
+//// .filter { it.isValid }
+//// .toMutableList()
+//// this.list[project.name] = files
+//// }
+//// }
+//
+//// private fun saveProject(project: Project) {
+//// if (project.name !in list) return
+//// val files = list[project.name]
+//// val properties = PropertiesComponent.getInstance(project)
+////
+//// val stringList = files!!.stream()
+//// .filter { it.isValid }
+//// .map { it.path }
+//// .toList()
+//// properties.setList(TRIDENT_LIST_KEY, stringList)
+////
+//// val pathMappingsString = mappings[project.name]!!.entries
+//// .joinToString("\n") { "${it.key}=${it.value}" }
+//// .split("\n")
+//// properties.setList(TRIDENT_MAPPINGS_KEY, pathMappingsString)
+//// }
+//
+//// fun getIndexOfFile(project: Project, data: VirtualFile?): Int {
+//// loadProject(project)
+//// val files = list[project.name]
+//// if (files == null || data == null) return -1
+//// return files.indexOfFirst { it.path == data.path }
+//// }
+//
+//// fun getFileCount(project: Project): Int {
+//// loadProject(project)
+//// return list[project.name]!!.size
+//// }
+//
+// fun toggleQuickMenu(project: Project) {
+// if (quickMenu != null && quickMenu!!.isShowing) {
+// quickMenu!!.doCancelAction()
+// return
+// }
+//
+//// loadProject(project)
+// val content = TridentList.get(project).joinToString("\n")
+// quickMenu = QuickMenu(content)
+// val result = quickMenu!!.showAndGet()
+//
+// if (content != quickMenuContent) {
+// retrieveProjectFiles(project, quickMenuContent)
+//// saveProject(project)
+// }
+//
+// if (!result) return
+// val index = quickMenuSelectedIndex ?: return
+// quickMenuSelectedIndex = null
+// val file = TridentList.select(project, index) ?: return
+// FileEditorManager.getInstance(project).openFile(file, true)
+// }
+//
+// fun quickMenuSelect() {
+// if (quickMenu == null || !quickMenu!!.isShowing) return
+// quickMenu!!.select()
+// }
+//
+//// private fun getProjectContent(project: Project): String {
+//// loadProject(project)
+//// val map = mappings[project.name] ?: throw IllegalStateException("Path mappings not found")
+//// println("map: $map")
+//// var content = list[project.name]!!
+//// .joinToString("\n") { it.path }
+//// .trim()
+//// println("content: $content")
+//// for ((key, value) in map) {
+//// println("key: $key | value: $value")
+//// content = content.replace(value, key)
+//// }
+//// println("content: $content")
+//// return content
+//// }
+//
+//// private fun retrieveProjectFiles(project: Project, content: String) {
+//// loadProject(project)
+//// val files = content
+//// .split("\n")
+//// .asSequence()
+//// .map { it.trim() }
+//// .map { line ->
+//// mappings[project.name]!!.entries.fold(line) { acc, (key, value) ->
+//// acc.replace(key, value)
+//// }
+//// }
+////// .map {
+////// pathMappings[project.name]!!.entries.forEach { (key, value) -> it.replace(key, value) }
+////// it
+////// }
+//// .filter { it.isNotBlank() }
+//// .map { LocalFileSystem.getInstance().findFileByPath(it) }
+//// .filterNotNull()
+//// .filter { it.isValid }
+//// .toMutableList()
+//// list[project.name] = files
+//// }
+//
+// fun togglePathMappingsMenu(project: Project) {
+// if (pathMappingsMenu != null && pathMappingsMenu!!.isShowing) {
+// pathMappingsMenu!!.doCancelAction()
+// return
+// }
+//
+//// loadProject(project)
+// val content = mappings[project.name]!!.entries
+// .joinToString("\n") { "${it.key}=${it.value}" }
+// pathMappingsMenu = PathMappingsMenu(content)
+// pathMappingsMenu!!.showAndGet()
+//
+// if (content != pathMappingsContent) {
+// pathMappingsContent.split("\n").forEach {
+// val split = it.split("=")
+// if (split.size != 2) return@forEach
+// mappings[project.name]!![split[0]] = split[1]
+// }
+//// saveProject(project)
+// }
+// }
+//} \ No newline at end of file
diff --git a/src/main/kotlin/me/kyren223/trident/tests/MappingsExpansionTest.kt b/src/main/kotlin/me/kyren223/trident/tests/MappingsExpansionTest.kt
new file mode 100644
index 0000000..0b66c4b
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/tests/MappingsExpansionTest.kt
@@ -0,0 +1,42 @@
+package me.kyren223.trident.tests
+
+import org.junit.Test
+
+class MappingsExpansionTest {
+ @Test
+ fun testExpand() {
+ val mappings = mapOf(
+ "\$A" to "A",
+ "\$BB" to "B",
+ "\$CCC" to "C",
+ "\$DDD" to "D",
+ "\$EEE" to "E",
+ "\$FFFFF" to "F",
+ "\$GG" to "G",
+ "\$UUU" to "U",
+ "\$VVVV" to "V",
+ "\$LLLLLL" to "L",
+ "\$MM" to "M",
+ "\$NN" to "N",
+ "\$OOO" to "O",
+ "\$PPP" to "P",
+ "\$QQQQ" to "Q",
+ "\$R" to "R",
+ "\$WWW" to "W",
+ "\$XX" to "X",
+ "\$HI" to "H",
+ "\$IJ" to "I",
+ "\$JetBrains" to "J",
+ "\$Krypton" to "K",
+ "\$Soft" to "S",
+ "\$Tnt" to "T",
+ "\$Ycombinator" to "Y",
+ "\$Zig" to "Z",
+ ).toList().sortedByDescending { it.first.length }
+
+ mappings.forEach { (key, value) ->
+ println("$key -> $value")
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/me/kyren223/trident/SettingsConfigurable.kt b/src/main/kotlin/me/kyren223/trident/ui/SettingsConfigurable.kt
index 3caaf4b..6283a29 100644
--- a/src/main/kotlin/me/kyren223/trident/SettingsConfigurable.kt
+++ b/src/main/kotlin/me/kyren223/trident/ui/SettingsConfigurable.kt
@@ -1,12 +1,16 @@
// Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package me.kyren223.trident
+package me.kyren223.trident.ui
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.util.NlsContexts.ConfigurableName
+import me.kyren223.trident.data.SettingsData
+import me.kyren223.trident.data.SettingsState
import javax.swing.JComponent
class SettingsConfigurable : Configurable {
+
private var settings: SettingsData? = null
+
override fun getDisplayName(): @ConfigurableName String {
return "Trident Settings"
}
@@ -22,6 +26,9 @@ class SettingsConfigurable : Configurable {
if (settings.height != this.settings!!.getHeight()) return true
if (settings.fontSize != this.settings!!.getFontSize()) return true
if (settings.enterToSelect != this.settings!!.getEnterToSelect()) return true
+ if (settings.automaticMapping != this.settings!!.getAutomaticMapping()) return true
+ if (settings.recursiveMapping != this.settings!!.getRecursiveMapping()) return true
+ if (settings.rememberLine != this.settings!!.getRememberLine()) return true
return false
}
@@ -31,6 +38,9 @@ class SettingsConfigurable : Configurable {
settings.height = this.settings!!.getHeight()
settings.fontSize = this.settings!!.getFontSize()
settings.enterToSelect = this.settings!!.getEnterToSelect()
+ settings.automaticMapping = this.settings!!.getAutomaticMapping()
+ settings.recursiveMapping = this.settings!!.getRecursiveMapping()
+ settings.rememberLine = this.settings!!.getRememberLine()
}
override fun reset() {
@@ -42,6 +52,9 @@ class SettingsConfigurable : Configurable {
this.settings!!.setHeight(settings.height)
this.settings!!.setFontSize(settings.fontSize)
this.settings!!.setEnterToSelect(settings.enterToSelect)
+ this.settings!!.setAutomaticMapping(settings.automaticMapping)
+ this.settings!!.setRecursiveMapping(settings.recursiveMapping)
+ this.settings!!.setRememberLine(settings.rememberLine)
}
override fun disposeUIResources() {
diff --git a/src/main/kotlin/me/kyren223/trident/ui/TridentListMenu.kt b/src/main/kotlin/me/kyren223/trident/ui/TridentListMenu.kt
new file mode 100644
index 0000000..4062837
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/ui/TridentListMenu.kt
@@ -0,0 +1,94 @@
+@file:Suppress("SameParameterValue")
+
+package me.kyren223.trident.ui
+
+import com.intellij.openapi.editor.EditorSettings
+import com.intellij.openapi.editor.LogicalPosition
+import com.intellij.openapi.fileEditor.FileEditorManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.ui.EditorTextField
+import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.Mode
+import me.kyren223.trident.data.SettingsState
+import me.kyren223.trident.utils.TridentList
+import me.kyren223.trident.utils.setEditorMode
+import java.awt.event.FocusEvent
+import java.awt.event.FocusListener
+import javax.swing.JComponent
+
+class TridentListMenu(private val content: String) : DialogWrapper(true) {
+
+ private lateinit var editor: EditorTextField
+
+ init {
+ this.title = "Trident List"
+ val settings = SettingsState.instance
+ setSize(settings.width, settings.height)
+ super.init()
+ }
+
+ override fun createCenterPanel(): JComponent {
+ editor = EditorTextField(this.content)
+ editor.setOneLineMode(false)
+ editor.addSettingsProvider {
+ it.setFontSize(SettingsState.instance.fontSize)
+ it.isInsertMode = false
+ val lineNumber = if (SettingsState.instance.rememberLine) line else 0
+ it.caretModel.moveToLogicalPosition(LogicalPosition(lineNumber, 0))
+ it.settings.isLineNumbersShown = true
+ it.settings.lineNumerationType = EditorSettings.LineNumerationType.ABSOLUTE
+ }
+
+ editor.addFocusListener(object : FocusListener {
+ override fun focusGained(e: FocusEvent?) {
+ val editor = editor.editor!!.vim
+ setEditorMode(editor, Mode.NORMAL())
+ }
+
+ override fun focusLost(e: FocusEvent?) { /* Do nothing */ }
+ })
+
+ return editor
+ }
+
+
+ override fun getPreferredFocusedComponent(): JComponent {
+ return this.editor
+ }
+
+ fun select() {
+ val project = editor.project
+ doOKAction()
+ val file = TridentList.select(project, line) ?: return
+ FileEditorManager.getInstance(project).openFile(file, true)
+ }
+
+ override fun doOKAction() {
+ save()
+ super.doOKAction()
+ }
+
+ override fun doCancelAction() {
+ save()
+ super.doCancelAction()
+ }
+
+ private fun save() {
+ TridentList.set(editor.project, editor.text.split("\n"))
+ line = editor.editor!!.caretModel.logicalPosition.line
+ }
+
+ override fun createSouthPanel(): JComponent? {
+ return null
+ }
+
+ companion object {
+ var line = 0
+ fun open(project: Project) {
+ val content = TridentList.get(project).joinToString("\n")
+ val menu = TridentListMenu(content)
+ menu.show()
+ }
+ }
+}
diff --git a/src/main/kotlin/me/kyren223/trident/ui/TridentMappingsMenu.kt b/src/main/kotlin/me/kyren223/trident/ui/TridentMappingsMenu.kt
new file mode 100644
index 0000000..a84b561
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/ui/TridentMappingsMenu.kt
@@ -0,0 +1,84 @@
+package me.kyren223.trident.ui
+
+import com.intellij.openapi.editor.EditorSettings
+import com.intellij.openapi.editor.LogicalPosition
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.ui.EditorTextField
+import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.Mode
+import me.kyren223.trident.data.SettingsState
+import me.kyren223.trident.utils.TridentMappings
+import me.kyren223.trident.utils.setEditorMode
+import java.awt.event.FocusEvent
+import java.awt.event.FocusListener
+import javax.swing.JComponent
+
+class TridentMappingsMenu(private val content: String) : DialogWrapper(true) {
+ private lateinit var editor: EditorTextField
+
+ init {
+ val settings = SettingsState.instance
+ this.title = "Trident Mappings"
+ setSize(settings.width, settings.height)
+ super.init()
+ }
+
+ override fun createCenterPanel(): JComponent {
+ editor = EditorTextField(this.content)
+ editor.setOneLineMode(false)
+ editor.addSettingsProvider {
+ it.setFontSize(SettingsState.instance.fontSize)
+ it.isInsertMode = false
+ it.caretModel.moveToLogicalPosition(LogicalPosition(0, 0))
+ it.settings.isLineNumbersShown = true
+ it.settings.lineNumerationType = EditorSettings.LineNumerationType.ABSOLUTE
+ }
+
+ editor.addFocusListener(object : FocusListener {
+ override fun focusGained(e: FocusEvent?) {
+ val editor = editor.editor!!.vim
+ setEditorMode(editor, Mode.NORMAL())
+ }
+
+ override fun focusLost(e: FocusEvent?) { /* Do nothing */ }
+ })
+
+ return editor
+ }
+
+ override fun getPreferredFocusedComponent(): JComponent {
+ return this.editor
+ }
+
+ override fun doOKAction() {
+ save()
+ super.doOKAction()
+ }
+
+ override fun doCancelAction() {
+ save()
+ super.doCancelAction()
+ }
+
+ private fun save() {
+ val map = editor.text.split("\n")
+ .map { entry -> entry.split("=") }
+ .filter { entry -> entry.size == 2 }
+ .associate { it[0] to it[1] }
+ TridentMappings.set(editor.project, map)
+ }
+
+ override fun createSouthPanel(): JComponent? {
+ return null
+ }
+
+ companion object {
+ fun open(project: Project) {
+ val content = TridentMappings.get(project).entries
+ .joinToString("\n") { "${it.key}=${it.value}" }
+ val menu = TridentMappingsMenu(content)
+ menu.show()
+ }
+ }
+}
diff --git a/src/main/kotlin/me/kyren223/trident/utils/TridentUtils.kt b/src/main/kotlin/me/kyren223/trident/utils/TridentUtils.kt
new file mode 100644
index 0000000..b7e19ac
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/utils/TridentUtils.kt
@@ -0,0 +1,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
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/me/kyren223/trident/utils/Utils.kt b/src/main/kotlin/me/kyren223/trident/utils/Utils.kt
new file mode 100644
index 0000000..7e6b80c
--- /dev/null
+++ b/src/main/kotlin/me/kyren223/trident/utils/Utils.kt
@@ -0,0 +1,16 @@
+package me.kyren223.trident.utils
+
+import com.maddyhome.idea.vim.api.VimEditor
+import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.command.OperatorArguments
+import com.maddyhome.idea.vim.state.mode.Mode
+
+fun setEditorMode(editor: VimEditor, mode: Mode) {
+ val context = injector.executionContextManager.onEditor(editor, null)
+ val operatorArguments = OperatorArguments(false, 0, mode)
+ editor.exitInsertMode(context, operatorArguments)
+ editor.vimChangeActionSwitchMode = Mode.NORMAL()
+}
+
+
+