Skip to content

Commit

Permalink
1.3.25 #97 兼容VIC3中的定义引用 P1
Browse files Browse the repository at this point in the history
  • Loading branch information
DragonKnightOfBreeze committed Nov 21, 2024
1 parent 80a3d41 commit 6167503
Show file tree
Hide file tree
Showing 18 changed files with 249 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

* [X] 兼容脚本文件中的内联模版表达式(如,`has_ethic = ethic_[[fanatic]fanatic_]pacifist`
* [X] #96
* [ ] #97 兼容VIC3中的定义引用(定义引用表达式,对应新的规则类型`$define_reference`

***

* [X] Compatible with inline template expressions in script files (e.g., `has_ethic = ethic_[[fanatic]fanatic_]pacifist`)
* [X] #96
* [ ] #97 Support Vic3 define references (Define reference expressions, corresponding to new data type `$define_reference`)

## 1.3.24

Expand Down
3 changes: 0 additions & 3 deletions src/main/kotlin/icu/windea/pls/config/CwtDataTypeGroups.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ object CwtDataTypeGroups {
CwtDataTypes.IntVariableField,
CwtDataTypes.VariableField,
)
val DatabaseObject = arrayOf(
CwtDataTypes.DatabaseObject
)

val ConstantLike = arrayOf(
CwtDataTypes.Constant,
Expand Down
10 changes: 8 additions & 2 deletions src/main/kotlin/icu/windea/pls/config/CwtDataTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,21 @@ object CwtDataTypes {
val ParameterValue = CwtDataType("ParameterValue")
val LocalisationParameter = CwtDataType("LocalisationParameter")
val ShaderEffect = CwtDataType("ShaderEffect") //effects in .shader files
val DatabaseObject = CwtDataType("DatabaseObject")

/** @since 1.3.9 */
//@WithGameType(ParadoxGameType.Stellaris) //not limited yet
val DatabaseObject = CwtDataType("DatabaseObject") //e.g. civic:xxx:xxx
/** @since 1.3.25 */
//@WithGameType(ParadoxGameType.Vic3) //not limited yet
val DefineReference = CwtDataType("DefineReference") //e.g. define:NPortrait|GRACEFUL_AGING_START

@WithGameType(ParadoxGameType.Stellaris)
val StellarisNameFormat = CwtDataType("StellarisNameFormat")
@WithGameType(ParadoxGameType.Stellaris)
val TechnologyWithLevel = CwtDataType("TechnologyWithLevel")

val Constant = CwtDataType("Constant")
val TemplateExpression = CwtDataType("TemplateExpression") //e.g a_<b>_enum[c]_value[]
val TemplateExpression = CwtDataType("TemplateExpression") //e.g. a_<b>_enum[c]_value[]
/** @since 1.3.6 */
val AntExpression = CwtDataType("AntExpression") //e.g. /foo/bar?/*
/** @since 1.3.6 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package icu.windea.pls.ep.dataExpression

import icu.windea.pls.config.configGroup.*
import icu.windea.pls.config.expression.*
import icu.windea.pls.core.collections.*
import icu.windea.pls.config.CwtDataTypeGroups as TypeGroups
import icu.windea.pls.config.CwtDataTypes as Types

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ class CoreCwtDataExpressionResolver : CwtDataExpressionResolver {
return CwtDataExpression.create(expressionString, isKey, CwtDataTypes.DatabaseObject)
}

if (expressionString == "\$define_reference") {
return CwtDataExpression.create(expressionString, isKey, CwtDataTypes.DefineReference)
}

expressionString.removeSurroundingOrNull("stellaris_name_format[", "]")?.let { v ->
val value = v.orNull()
return CwtDataExpression.create(expressionString, isKey, CwtDataTypes.StellarisNameFormat, value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,22 @@ class CoreParadoxScriptExpressionMatcher : ParadoxScriptExpressionMatcher {
val r = variableFieldExpression != null
Result.of(r)
}
configExpression.type in CwtDataTypeGroups.DatabaseObject -> {
configExpression.type == CwtDataTypes.DatabaseObject -> {
if (!expression.type.isStringType()) return Result.NotMatch
if (expression.isParameterized()) return Result.ParameterizedMatch
val textRange = TextRange.create(0, expression.text.length)
val databaseObjectExpression = ParadoxDatabaseObjectExpression.resolve(expression.text, textRange, configGroup)
val r = databaseObjectExpression != null
Result.of(r)
}
configExpression.type == CwtDataTypes.DefineReference -> {
if (!expression.type.isStringType()) return Result.NotMatch
if (expression.isParameterized()) return Result.ParameterizedMatch
val textRange = TextRange.create(0, expression.text.length)
val defineReferenceExpression = ParadoxDefineReferenceExpression.resolve(expression.text, textRange, configGroup)
val r = defineReferenceExpression != null
Result.of(r)
}
configExpression.type == CwtDataTypes.Modifier -> {
if (!expression.type.isStringType()) return Result.NotMatch
if (!expression.text.isParameterAwareIdentifier()) return Result.NotMatch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class ParadoxScriptVariableFieldExpressionSupport : ParadoxScriptExpressionSuppo

class ParadoxScriptDatabaseObjectExpressionSupport : ParadoxScriptExpressionSupport {
override fun supports(config: CwtConfig<*>): Boolean {
return config.expression?.type in CwtDataTypeGroups.DatabaseObject
return config.expression?.type == CwtDataTypes.DatabaseObject
}

override fun annotate(element: ParadoxScriptExpressionElement, rangeInElement: TextRange?, expressionText: String, holder: AnnotationHolder, config: CwtConfig<*>) {
Expand All @@ -191,3 +191,30 @@ class ParadoxScriptDatabaseObjectExpressionSupport : ParadoxScriptExpressionSupp
ParadoxCompletionManager.completeDatabaseObjectExpression(context, result)
}
}

class ParadoxScriptDefineReferenceExpressionSupport : ParadoxScriptExpressionSupport {
override fun supports(config: CwtConfig<*>): Boolean {
return config.expression?.type == CwtDataTypes.DefineReference
}

override fun annotate(element: ParadoxScriptExpressionElement, rangeInElement: TextRange?, expressionText: String, holder: AnnotationHolder, config: CwtConfig<*>) {
if (element !is ParadoxScriptStringExpressionElement) return
val configGroup = config.configGroup
val range = TextRange.create(0, expressionText.length)
val defineReferenceExpression = ParadoxDefineReferenceExpression.resolve(expressionText, range, configGroup) ?: return
ParadoxExpressionManager.annotateComplexExpression(element, defineReferenceExpression, holder, config)
}

override fun getReferences(element: ParadoxScriptExpressionElement, rangeInElement: TextRange?, expressionText: String, config: CwtConfig<*>, isKey: Boolean?): Array<out PsiReference>? {
if (element !is ParadoxScriptStringExpressionElement) return PsiReference.EMPTY_ARRAY
val configGroup = config.configGroup
val range = TextRange.create(0, expressionText.length)
val defineReferenceExpression = ParadoxDefineReferenceExpression.resolve(expressionText, range, configGroup)
if (defineReferenceExpression == null) return PsiReference.EMPTY_ARRAY
return defineReferenceExpression.getReferences(element)
}

override fun complete(context: ProcessingContext, result: CompletionResultSet) {
ParadoxCompletionManager.completeDefineReferenceExpression(context, result)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,7 @@ object ParadoxCompletionManager {
return maxCount == null || actualCount < maxCount
}

fun getExpressionTailText(
context: ProcessingContext,
config: CwtConfig<*>?,
withConfigExpression: Boolean = true,
withFileName: Boolean = true,
): String {
fun getExpressionTailText(context: ProcessingContext, config: CwtConfig<*>?, withConfigExpression: Boolean = true, withFileName: Boolean = true): String {
context.expressionTailText?.let { return it }

return buildString {
Expand Down Expand Up @@ -1047,6 +1042,32 @@ object ParadoxCompletionManager {
context.isKey = isKey
}

fun completeDefineReferenceExpression(context: ProcessingContext, result: CompletionResultSet) {
ProgressManager.checkCanceled()
val keyword = context.keyword
val keywordOffset = context.keywordOffset
val configGroup = context.configGroup ?: return

val textRange = TextRange.create(keywordOffset, keywordOffset + keyword.length)
val expression = markIncomplete { ParadoxDefineReferenceExpression.resolve(keyword, textRange, configGroup) } ?: return

val oldNode = context.node
val isKey = context.isKey
context.isKey = null

val offset = context.offsetInParent!! - context.expressionOffset
if (offset < 0) return //unexpected
for (node in expression.nodes) {
val inRange = offset >= node.rangeInExpression.startOffset && offset <= node.rangeInExpression.endOffset
//TODO 1.3.25
}

context.keyword = keyword
context.keywordOffset = keywordOffset
context.node = oldNode
context.isKey = isKey
}

/**
* @return 是否已经输入了前缀。
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ interface ParadoxComplexExpression : ParadoxComplexExpressionNode {
dataType in CwtDataTypeGroups.ScopeField -> ParadoxScopeFieldExpression.resolve(expressionString, range, configGroup)
dataType in CwtDataTypeGroups.ValueField -> ParadoxValueFieldExpression.resolve(expressionString, range, configGroup)
dataType in CwtDataTypeGroups.VariableField -> ParadoxVariableFieldExpression.resolve(expressionString, range, configGroup)
dataType in CwtDataTypeGroups.DatabaseObject -> ParadoxDatabaseObjectExpression.resolve(expressionString, range, configGroup)
dataType == CwtDataTypes.DatabaseObject -> ParadoxDatabaseObjectExpression.resolve(expressionString, range, configGroup)
dataType == CwtDataTypes.DefineReference -> ParadoxDefineReferenceExpression.resolve(expressionString, range, configGroup)
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ object ParadoxComplexExpressionErrorCodes {
const val MalformedVariableFieldExpression = 102
const val MalformedDynamicValueExpression = 103
const val MalformedScriptValueExpression = 104
const val MalformedGameObjectExpression = 105
const val MalformedLocalisationCommandExpression = 106
const val MalformedDatabaseObjectExpression = 105
const val MalformedDefineReferenceExpression = 106
const val MalformedLocalisationCommandExpression = 150

const val UnresolvedScopeLink = 200
const val UnresolvedValueField = 201
Expand Down Expand Up @@ -85,9 +86,14 @@ object ParadoxComplexExpressionErrors {
return ParadoxComplexExpressionError(code, rangeInExpression, PlsBundle.message("script.expression.malformedScriptValueExpression", text))
}

fun malformedGameObjectExpression(rangeInExpression: TextRange, text: String): ParadoxComplexExpressionError {
val code = ParadoxComplexExpressionErrorCodes.MalformedGameObjectExpression
return ParadoxComplexExpressionError(code, rangeInExpression, PlsBundle.message("script.expression.malformedGameObjectExpression", text))
fun malformedDatabaseObjectExpression(rangeInExpression: TextRange, text: String): ParadoxComplexExpressionError {
val code = ParadoxComplexExpressionErrorCodes.MalformedDatabaseObjectExpression
return ParadoxComplexExpressionError(code, rangeInExpression, PlsBundle.message("script.expression.malformedDatabaseObjectExpression", text))
}

fun malformedDefineReferenceExpression(rangeInExpression: TextRange, text: String): ParadoxComplexExpressionError {
val code = ParadoxComplexExpressionErrorCodes.MalformedDefineReferenceExpression
return ParadoxComplexExpressionError(code, rangeInExpression, PlsBundle.message("script.expression.malformedDefineReferenceExpression", text))
}

fun malformedLocalisationCommandExpression(rangeInExpression: TextRange, text: String): ParadoxComplexExpressionError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import icu.windea.pls.core.collections.*
import icu.windea.pls.lang.expression.complex.nodes.*

/**
* 数据库对象表达式。对应的CWT规则类型为[CwtDataTypeGroups.DatabaseObject]。
* 数据库对象表达式。对应的CWT规则类型为[CwtDataTypes.DatabaseObject]。
*
* 也可以在本地化文件中作为概念名称使用。(如,`['civic:some_civic', ...]`)
*
Expand Down Expand Up @@ -97,10 +97,9 @@ class ParadoxDatabaseObjectExpression private constructor(
}
}
if (malformed) {
errors += ParadoxComplexExpressionErrors.malformedGameObjectExpression(rangeInExpression, text)
errors += ParadoxComplexExpressionErrors.malformedDatabaseObjectExpression(rangeInExpression, text)
}
return errors.pinned { it.isMalformedError() }
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package icu.windea.pls.lang.expression.complex

import com.intellij.openapi.util.*
import icu.windea.pls.*
import icu.windea.pls.config.configGroup.*
import icu.windea.pls.config.*
import icu.windea.pls.core.collections.*
import icu.windea.pls.lang.expression.complex.nodes.*

/**
* 定义引用表达式。对应的CWT规则类型为[CwtDataTypes.DefineReference]。
*
* 语法:
*
* ```bnf
* database_object_expression ::= database_object_type ":" database_object (":" swapped_database_object)?
* database_object_type ::= TOKEN //predefined by CWT Config (see database_object_types.cwt)
* database_object ::= TOKEN //predefined by CWT Config (see database_object_types.cwt)
* swapped_database_object ::= TOKEN //predefined by CWT Config (see database_object_types.cwt)
* ```
*
* 示例:
*
* * `define:NPortrait|GRACEFUL_AGING_START`
*/
class ParadoxDefineReferenceExpression private constructor(
override val text: String,
override val rangeInExpression: TextRange,
override val nodes: List<ParadoxComplexExpressionNode>,
override val configGroup: CwtConfigGroup
) : ParadoxComplexExpression.Base() {
override val errors by lazy { validate() }

companion object Resolver {
fun resolve(expressionString: String, range: TextRange, configGroup: CwtConfigGroup): ParadoxDefineReferenceExpression? {
if (expressionString.isEmpty()) return null

val incomplete = PlsStates.incompleteComplexExpression.get() ?: false

val nodes = mutableListOf<ParadoxComplexExpressionNode>()
val expression = ParadoxDefineReferenceExpression(expressionString, range, nodes, configGroup)
////TODO 1.3.25
return expression
}

private fun ParadoxDefineReferenceExpression.validate(): List<ParadoxComplexExpressionError> {
val errors = mutableListOf<ParadoxComplexExpressionError>()
var malformed = false
for (node in nodes) {
if (node.text.isEmpty()) {
malformed = true
break
}
}
if (malformed) {
errors += ParadoxComplexExpressionErrors.malformedDefineReferenceExpression(rangeInExpression, text)
}
return errors.pinned { it.isMalformedError() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class IncorrectDatabaseObjectExpressionInspection : LocalInspectionTool() {
private fun visitStringExpressionElement(element: ParadoxScriptStringExpressionElement) {
val config = ParadoxExpressionManager.getConfigs(element).firstOrNull() ?: return
val dataType = config.expression.type
if (dataType !in CwtDataTypeGroups.DatabaseObject) return
if (dataType != CwtDataTypes.DatabaseObject) return
val value = element.value
val textRange = TextRange.create(0, value.length)
val expression = ParadoxDatabaseObjectExpression.resolve(value, textRange, configGroup) ?: return
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package icu.windea.pls.lang.inspections.script.expression

import com.intellij.codeInspection.*
import com.intellij.openapi.progress.*
import com.intellij.openapi.util.*
import com.intellij.psi.*
import com.intellij.ui.dsl.builder.*
import icu.windea.pls.*
import icu.windea.pls.config.*
import icu.windea.pls.lang.*
import icu.windea.pls.lang.expression.complex.*
import icu.windea.pls.lang.util.*
import icu.windea.pls.script.psi.*
import javax.swing.*

/**
* 不正确的[ParadoxDefineReferenceExpression]的检查。
*
* @property reportsUnresolved 是否报告无法解析的引用。
*/
class IncorrectDefineReferenceExpressionInspection : LocalInspectionTool() {
@JvmField
var reportsUnresolved = true

override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
if (!shouldCheckFile(holder.file)) return PsiElementVisitor.EMPTY_VISITOR
val configGroup = getConfigGroup(holder.project, selectGameType(holder.file))
return object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
ProgressManager.checkCanceled()
if (element is ParadoxScriptStringExpressionElement) visitStringExpressionElement(element)
}

private fun visitStringExpressionElement(element: ParadoxScriptStringExpressionElement) {
val config = ParadoxExpressionManager.getConfigs(element).firstOrNull() ?: return
val dataType = config.expression.type
if (dataType != CwtDataTypes.DefineReference) return
val value = element.value
val textRange = TextRange.create(0, value.length)
val expression = ParadoxDefineReferenceExpression.resolve(value, textRange, configGroup) ?: return
handleErrors(element, expression)
}

private fun handleErrors(element: ParadoxScriptStringExpressionElement, expression: ParadoxDefineReferenceExpression) {
expression.errors.forEach { error -> handleError(element, error) }
expression.processAllNodes { node -> node.getUnresolvedError(element)?.let { error -> handleError(element, error) }.let { true } }
}

private fun handleError(element: ParadoxScriptStringExpressionElement, error: ParadoxComplexExpressionError) {
if (!reportsUnresolved && error.isUnresolvedError()) return
holder.registerExpressionError(error, element)
}
}
}

private fun shouldCheckFile(file: PsiFile): Boolean {
if (selectRootFile(file) == null) return false
return true
}

override fun createOptionsPanel(): JComponent {
return panel {
//reportsUnresolved
row {
checkBox(PlsBundle.message("inspection.script.incorrectExpression.option.reportsUnresolved"))
.bindSelected(::reportsUnresolved)
.actionListener { _, component -> reportsUnresolved = component.isSelected }
}
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/pls-ep.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptValueFieldExpressionSupport"/>
<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptVariableFieldExpressionSupport"/>
<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptDatabaseObjectExpressionSupport"/>
<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptDefineReferenceExpressionSupport"/>

<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptParameterExpressionSupport"/>
<scriptExpressionSupport implementation="icu.windea.pls.ep.expression.ParadoxScriptLocalisationParameterExpressionSupport"/>
Expand Down
Loading

0 comments on commit 6167503

Please sign in to comment.