Skip to content

Commit

Permalink
1.2.2 优化与BUG修复
Browse files Browse the repository at this point in the history
  • Loading branch information
DragonKnightOfBreeze committed Nov 16, 2023
1 parent 2f89a6e commit b6dd6a1
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package icu.windea.pls.core.tool
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.project.*
import com.intellij.ui.*
import com.intellij.ui.table.*
import com.intellij.util.ui.*
import icu.windea.pls.*
import icu.windea.pls.core.*
import icu.windea.pls.core.settings.*
import icu.windea.pls.core.tool.actions.*
import java.awt.*
import java.awt.event.*
import javax.swing.*

Expand Down Expand Up @@ -124,25 +122,16 @@ class ParadoxModDependenciesTableModel(
@JvmStatic
fun createPanel(project: Project, settings: ParadoxGameOrModSettingsState, modDependencies: MutableList<ParadoxModDependencySettingsState>): JPanel {
val tableModel = ParadoxModDependenciesTableModel(settings, modDependencies)
val tableView = TableView(tableModel)
tableView.setShowGrid(false)
tableView.rowSelectionAllowed = true
tableView.columnSelectionAllowed = false
tableView.intercellSpacing = Dimension(0, 0)
tableView.selectionModel.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
//调整列的宽度
tableView.setFixedColumnWidth(EnabledItem.columnIndex, EnabledItem.name)
tableView.tableHeader.columnModel.getColumn(NameItem.columnIndex).preferredWidth = 10000 // consume all available space
tableView.setFixedColumnWidth(VersionItem.columnIndex, VersionItem.name)
tableView.setFixedColumnWidth(SupportedVersionItem.columnIndex, SupportedVersionItem.name)
val tableView = ParadoxModDependenciesTableView(tableModel)
//快速搜索
object : TableViewSpeedSearch<ParadoxModDependencySettingsState>(tableView, null) {
val speedSearch = object : TableViewSpeedSearch<ParadoxModDependencySettingsState>(tableView, null) {
override fun getItemText(element: ParadoxModDependencySettingsState): String {
val modDirectory = element.modDirectory.orEmpty()
val modDescriptorSettings = getProfilesSettings().modDescriptorSettings.getValue(modDirectory)
return modDescriptorSettings.name.orEmpty()
}
}
speedSearch.setupListeners()
//双击打开模组依赖信息对话框
object : DoubleClickListener() {
override fun onDoubleClick(event: MouseEvent): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package icu.windea.pls.core.tool

import com.intellij.ui.table.*
import com.intellij.util.ui.*
import icu.windea.pls.core.*
import icu.windea.pls.core.settings.*
import java.awt.*
import javax.swing.*
import javax.swing.event.*

class ParadoxModDependenciesTableView(
tableModel: ListTableModel<ParadoxModDependencySettingsState>
): TableView<ParadoxModDependencySettingsState>(tableModel) {
init {
setShowGrid(false)
rowSelectionAllowed = true
columnSelectionAllowed = false
intercellSpacing = Dimension(0, 0)
selectionModel.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
setColumnSizes()
}

override fun tableChanged(e: TableModelEvent) {
super.tableChanged(e)
if(e.type == TableModelEvent.INSERT) setColumnSizes() //防止插入条目到空表格后,列的宽度被重置
}

private fun setColumnSizes() {
//调整列的宽度
setFixedColumnWidth(ParadoxModDependenciesTableModel.EnabledItem.columnIndex, ParadoxModDependenciesTableModel.EnabledItem.name)
setFixedColumnWidth(ParadoxModDependenciesTableModel.VersionItem.columnIndex, ParadoxModDependenciesTableModel.VersionItem.name)
setFixedColumnWidth(ParadoxModDependenciesTableModel.SupportedVersionItem.columnIndex, ParadoxModDependenciesTableModel.SupportedVersionItem.name)
tableHeader.columnModel.getColumn(ParadoxModDependenciesTableModel.NameItem.columnIndex).preferredWidth = 10000 // consume all available space
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,12 @@ class EntryListTableModel<K, V>(
tableView.intercellSpacing = Dimension(0, 0)
tableView.selectionModel.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
//快速搜索
object : TableViewSpeedSearch<Entry<K, V>>(tableView, null) {
val speedSearch = object : TableViewSpeedSearch<Entry<K, V>>(tableView, null) {
override fun getItemText(element: Entry<K, V>): String {
return keyGetter(element.key)
}
}
speedSearch.setupListeners()
val decorator = ToolbarDecorator.createDecorator(tableView)
customizer(decorator)
val panel = decorator.createPanel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ class ExpandClauseTemplateDialog(
tableView.columnSelectionAllowed = true
tableView.selectionModel.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
//快速搜索
object : TableViewSpeedSearch<ElementDescriptor>(tableView, null) {
val speedSearch = object : TableViewSpeedSearch<ElementDescriptor>(tableView, null) {
override fun getItemText(element: ElementDescriptor): String {
return element.name
}
}
speedSearch.setupListeners()
val listTable = ElementsListTable(tableView, elementsTableModel, disposable, context, this)
val table = listTable.table
//add, remove, move up, move down, duplicate
Expand Down
40 changes: 24 additions & 16 deletions src/main/kotlin/icu/windea/pls/inject/CodeInjectorBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,29 @@ abstract class CodeInjectorBase : CodeInjector() {
}
val injectTargetName = injectTarget.value
val injectPluginId = injectTarget.pluginId
val injectMethods = mutableMapOf<String, Method>()
val injectMethodInfos = mutableMapOf<String, CodeInjectorInfo.MethodInfo>()
val functions = this::class.declaredFunctions
for(function in functions) {
val injectMethod = function.findAnnotation<InjectMethod>() ?: continue
val method = function.javaMethod ?: continue
val uuid = UUID.randomUUID().toString()
injectMethods.put(uuid, method)
val hasReceiver = function.extensionReceiverParameter != null
val injectMethodInfo = CodeInjectorInfo.MethodInfo(injectMethod.pointer, hasReceiver)
val hasReturnValue = method.returnType != Void.TYPE && (injectMethod.pointer == InjectMethod.Pointer.AFTER || injectMethod.pointer == InjectMethod.Pointer.AFTER_FINALLY)
val injectMethodInfo = CodeInjectorInfo.MethodInfo(method, injectMethod.pointer, hasReceiver, hasReturnValue, injectMethod.static)
injectMethodInfos.put(uuid, injectMethodInfo)
}
return CodeInjectorInfo(this, injectTargetName, injectPluginId, injectMethods, injectMethodInfos)
return CodeInjectorInfo(this, injectTargetName, injectPluginId, injectMethodInfos)
}

private fun doInjectMethods(targetClass: CtClass, codeInjectorInfo: CodeInjectorInfo) {
var addFields = true
codeInjectorInfo.injectMethods.forEach { (methodId, injectMethod) ->
val targetMethod = findCtMethod(targetClass, injectMethod)
codeInjectorInfo.injectMethodInfos.forEach { (methodId, injectMethodInfo) ->
val injectMethod = injectMethodInfo.method
val targetMethod = findCtMethod(targetClass, injectMethod, injectMethodInfo)
if(targetMethod == null) {
thisLogger().warn("Inject method ${injectMethod.name} cannot be applied to any method of ${targetClass.name}")
return@forEach
}
val injectMethodInfo = codeInjectorInfo.injectMethodInfos.get(methodId) ?: throw IllegalStateException()

if(addFields) {
val f1 = "private static volatile UserDataHolder __codeInjectorService__ = " +
Expand Down Expand Up @@ -116,16 +115,21 @@ abstract class CodeInjectorBase : CodeInjector() {
}
}

private fun findCtMethod(ctClass: CtClass, method: Method): CtMethod? {
private fun findCtMethod(ctClass: CtClass, method: Method, injectMethodInfo: CodeInjectorInfo.MethodInfo): CtMethod? {
val methodName = method.name
return ctClass.getDeclaredMethods(methodName).also { if(it.size == 1) return it[0] }
.filter { it.parameterTypes.size <= method.parameterCount }.also { if(it.size == 1) return it[0] }
.filter { it.parameterTypes.withIndex().all { (i, p) -> p.name == method.parameters[i].name } }.also { if(it.size == 1) return it[0] }
.firstOrNull()
}

protected fun continueInvocation(): Nothing {
throw CONTINUE_INVOCATION
var argSize = method.parameterCount
if(injectMethodInfo.hasReceiver) argSize--
if(injectMethodInfo.hasReturnValue) argSize--
if(argSize < 0) return null //unexpected
var argIndexOffset = 0
if(injectMethodInfo.hasReceiver) argIndexOffset++
return ctClass.getDeclaredMethods(methodName).find f@{ ctMethod ->
val isStatic = Modifier.isStatic(ctMethod.modifiers)
if((injectMethodInfo.static && !isStatic) || (!injectMethodInfo.static && isStatic)) return@f false
if(ctMethod.parameterTypes.size != argSize) return@f false
if(ctMethod.parameterTypes.withIndex().any { (i, p) -> p.name != method.parameterTypes[i + argIndexOffset].name }) return@f false
true
}
}

private fun applyCodeInjectorSupports() {
Expand All @@ -134,6 +138,10 @@ abstract class CodeInjectorBase : CodeInjector() {
}
}

protected fun continueInvocation(): Nothing {
throw CONTINUE_INVOCATION
}

companion object {
private val CONTINUE_INVOCATION = ContinueInvocationException()
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/kotlin/icu/windea/pls/inject/CodeInjectorInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ data class CodeInjectorInfo(
val codeInjector: CodeInjector,
val injectTargetName: String,
val injectPluginId: String,
val injectMethods: Map<String, Method>,
val injectMethodInfos: Map<String, MethodInfo>
) {
data class MethodInfo(
val method: Method,
val pointer: InjectMethod.Pointer,
val hasReceiver: Boolean
val hasReceiver: Boolean,
val hasReturnValue: Boolean,
val static: Boolean
)
}
5 changes: 2 additions & 3 deletions src/main/kotlin/icu/windea/pls/inject/CodeInjectorService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import icu.windea.pls.core.*
import icu.windea.pls.core.util.*
import javassist.*
import java.lang.reflect.*
import java.nio.file.*

@Service
class CodeInjectorService : UserDataHolderBase() {
Expand Down Expand Up @@ -88,8 +87,8 @@ class CodeInjectorService : UserDataHolderBase() {
//不要在声明和调用注入方法时加载目标类型(例如,将接收者的类型直接指定为目标类型)
val codeInjector = getUserData(codeInjectorsKey)?.get(codeInjectorId) ?: throw IllegalStateException()
val codeInjectorInfo = codeInjector.getUserData(codeInjectorInfoKey) ?: throw IllegalStateException()
val injectMethod = codeInjectorInfo.injectMethods[methodId] ?: throw IllegalStateException()
val injectMethodInfo = codeInjectorInfo.injectMethodInfos[methodId] ?: throw IllegalStateException()
val injectMethod = injectMethodInfo.method
val actualArgsSize = injectMethod.parameterCount
val finalArgs = when(actualArgsSize) {
args.size -> args
Expand All @@ -106,7 +105,7 @@ class CodeInjectorService : UserDataHolderBase() {
}.toTypedArray()
}
}
if(finalArgs.size != injectMethod.parameterCount) throw IllegalStateException()
if(finalArgs.size != actualArgsSize) throw IllegalStateException()
return injectMethod.invoke(codeInjector, *finalArgs)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import icu.windea.pls.inject.*
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
annotation class InjectMethod(
val pointer: Pointer = Pointer.BODY
val pointer: Pointer = Pointer.BODY,
val static: Boolean = false,
) {
enum class Pointer {
BODY, BEFORE, AFTER, AFTER_FINALLY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

package icu.windea.pls.inject.injectors

import com.intellij.openapi.diagnostic.*
import com.intellij.openapi.fileChooser.tree.*
import com.intellij.openapi.vfs.*
import com.intellij.ui.*
Expand All @@ -18,21 +17,35 @@ class FileRenderCodeInjector : CodeInjectorBase() {
//com.intellij.openapi.fileChooser.tree.FileRenderer
//com.intellij.openapi.fileChooser.tree.FileRenderer.customize

@InjectMethod(InjectMethod.Pointer.AFTER)
fun Any.customize(renderer: SimpleColoredComponent, value: Any, selected: Boolean, focused: Boolean) {
try {
val file = when {
value is FileNode -> value.file
value is VirtualFile -> value
else -> return
}
val rootInfo = file.rootInfo
if(rootInfo != null && rootInfo.rootFile == file) {
val comment = rootInfo.qualifiedName
renderer.append(" $comment", SimpleTextAttributes.GRAYED_ATTRIBUTES)
@InjectMethod(InjectMethod.Pointer.AFTER, static = true)
fun customize(renderer: SimpleColoredComponent, value: Any, selected: Boolean, focused: Boolean) {
doCustomizeCatching(value, renderer)
}

@InjectMethod(InjectMethod.Pointer.AFTER, static = true)
fun customize(renderer: SimpleColoredComponent, value: Any) {
doCustomizeCatching(value, renderer)
}

private fun doCustomizeCatching(value: Any, renderer: SimpleColoredComponent) {
disableLogger {
runCatchingCancelable {
if(doCustomize(value, renderer)) return
}
} catch(e: Exception) {
thisLogger().warn(e)
}
}

private fun doCustomize(value: Any, renderer: SimpleColoredComponent): Boolean {
val file = when {
value is FileNode -> value.file
value is VirtualFile -> value
else -> return true
}
val rootInfo = file.rootInfo
if(rootInfo != null && rootInfo.rootFile == file) {
val comment = rootInfo.qualifiedName
renderer.append(" $comment", SimpleTextAttributes.GRAYED_ATTRIBUTES)
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class IfsUtilCodeInjector : CodeInjectorBase() {
private val FORMAT_KEY by lazy { staticProperty<IfsUtil, Key<String>>("FORMAT_KEY").get() }
private val IMAGE_PROVIDER_REF_KEY by lazy { staticProperty<IfsUtil, Key<SoftReference<ImageDocument.ScaledImageProvider>>>("IMAGE_PROVIDER_REF_KEY").get() }

@InjectMethod(InjectMethod.Pointer.AFTER)
@InjectMethod(InjectMethod.Pointer.AFTER, static = true)
fun refresh(file: VirtualFile, returnValue: Boolean): Boolean {
if(returnValue) return true
if(file.fileType != DdsFileType) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import icu.windea.pls.inject.*
import icu.windea.pls.inject.annotations.*

/**
* 重写IDE低层检查字符串是否是标识符的代码逻辑,以便兼容本地化文件中的本地化图标引用(`£unity£`),从而可以正确地查找引用
* 重写IDE底层的检测字符串是否是标识符的代码逻辑,从而可以正确地查找特定类型的引用
*/
@InjectTarget("com.intellij.psi.impl.search.LowLevelSearchUtil")
class LowLevelSearchUtilCodeInjector : CodeInjectorBase() {
//com.intellij.psi.impl.search.LowLevelSearchUtil
//com.intellij.psi.impl.search.LowLevelSearchUtil.checkJavaIdentifier

//rewrite this method to compatible with Paradox localisation icon references (e.g. "£unity£")
@InjectMethod
//rewrite this method to compatible with:
//localisation icon references (e.g. "£unity£")

@InjectMethod(static = true)
fun checkJavaIdentifier(text: CharSequence, searcher: StringSearcher, index: Int): Boolean {
if(!searcher.isJavaIdentifier) {
return true
Expand Down

0 comments on commit b6dd6a1

Please sign in to comment.