2020
2121package com.demonwav.mcdev.translations.identification
2222
23+ import com.demonwav.mcdev.translations.TranslationConstants
2324import com.demonwav.mcdev.translations.identification.TranslationInstance.Companion.FormattingError
2425import com.demonwav.mcdev.translations.index.TranslationIndex
2526import com.demonwav.mcdev.translations.index.merge
27+ import com.demonwav.mcdev.util.constantStringValue
28+ import com.demonwav.mcdev.util.constantValue
29+ import com.demonwav.mcdev.util.extractVarArgs
30+ import com.demonwav.mcdev.util.referencedMethod
31+ import com.intellij.codeInspection.dataFlow.CommonDataflow
2632import com.intellij.openapi.project.Project
33+ import com.intellij.psi.CommonClassNames
34+ import com.intellij.psi.PsiCall
2735import com.intellij.psi.PsiCallExpression
2836import com.intellij.psi.PsiElement
37+ import com.intellij.psi.PsiEllipsisType
2938import com.intellij.psi.PsiExpression
3039import com.intellij.psi.PsiExpressionList
40+ import com.intellij.psi.PsiMethod
41+ import java.util.IllegalFormatException
3142import java.util.MissingFormatArgumentException
3243
3344abstract class TranslationIdentifier <T : PsiElement > {
@@ -49,57 +60,108 @@ abstract class TranslationIdentifier<T : PsiElement> {
4960 container : PsiElement ,
5061 referenceElement : PsiElement ,
5162 ): TranslationInstance ? {
52- if (container is PsiExpressionList && container.parent is PsiCallExpression ) {
53- val call = container.parent as PsiCallExpression
54- val index = container.expressions.indexOf(element)
55-
56- for (function in TranslationInstance .translationFunctions) {
57- if (function.matches(call, index)) {
58- val translationKey = function.getTranslationKey(call, referenceElement) ? : continue
59- val entries = TranslationIndex .getAllDefaultEntries(project).merge(" " )
60- val translation = entries[translationKey]?.text
61- if (translation != null ) {
62- val foldingElement = when (function.foldParameters) {
63- TranslationFunction .FoldingScope .CALL -> call
64- TranslationFunction .FoldingScope .PARAMETER -> element
65- TranslationFunction .FoldingScope .PARAMETERS -> container
66- }
67- try {
68- val (formatted, superfluousParams) = function.format(translation, call)
69- ? : (translation to - 1 )
70- return TranslationInstance (
71- foldingElement,
72- function.matchedIndex,
73- referenceElement,
74- TranslationInstance .Key (translationKey),
75- formatted,
76- if (superfluousParams >= 0 ) FormattingError .SUPERFLUOUS else null ,
77- superfluousParams,
78- )
79- } catch (ignored: MissingFormatArgumentException ) {
80- return TranslationInstance (
81- foldingElement,
82- function.matchedIndex,
83- referenceElement,
84- TranslationInstance .Key (translationKey),
85- translation,
86- FormattingError .MISSING ,
87- )
88- }
89- } else {
90- return TranslationInstance (
91- null ,
92- function.matchedIndex,
93- referenceElement,
94- TranslationInstance .Key (translationKey),
95- null ,
96- )
97- }
98- }
99- }
63+ if (container !is PsiExpressionList ) {
10064 return null
10165 }
102- return null
66+ val call = container.parent as ? PsiCallExpression ? : return null
67+ val index = container.expressions.indexOf(element)
68+
69+ val method = call.referencedMethod ? : return null
70+ val parameter = method.parameterList.getParameter(index) ? : return null
71+ val translatableAnnotation = parameter.getAnnotation(TranslationConstants .TRANSLATABLE_ANNOTATION )
72+ ? : return null
73+
74+ val prefix =
75+ translatableAnnotation.findAttributeValue(TranslationConstants .PREFIX )?.constantStringValue ? : " "
76+ val suffix =
77+ translatableAnnotation.findAttributeValue(TranslationConstants .SUFFIX )?.constantStringValue ? : " "
78+ val required =
79+ translatableAnnotation.findAttributeValue(TranslationConstants .REQUIRED )?.constantValue as ? Boolean
80+ ? : true
81+
82+ val translationKey = CommonDataflow .computeValue(element) as ? String ? : return null
83+
84+ val entries = TranslationIndex .getAllDefaultEntries(project).merge(" " )
85+ val translation = entries[prefix + translationKey + suffix]?.text
86+ ? : return TranslationInstance ( // translation doesn't exist
87+ null ,
88+ index,
89+ referenceElement,
90+ TranslationInstance .Key (prefix, translationKey, suffix),
91+ null ,
92+ required
93+ )
94+
95+ val foldMethod =
96+ translatableAnnotation.findAttributeValue(TranslationConstants .FOLD_METHOD )?.constantValue as ? Boolean
97+ ? : false
98+
99+ val formatting =
100+ (method.parameterList.parameters.last().type as ? PsiEllipsisType )
101+ ?.componentType?.equalsToText(CommonClassNames .JAVA_LANG_OBJECT ) == true
102+
103+ val foldingElement = if (foldMethod) {
104+ call
105+ } else if (
106+ index == 0 &&
107+ container.expressionCount > 1 &&
108+ method.parameterList.parametersCount == 2 &&
109+ formatting
110+ ) {
111+ container
112+ } else {
113+ element
114+ }
115+ try {
116+ val (formatted, superfluousParams) = if (formatting) {
117+ format(method, translation, call) ? : (translation to - 1 )
118+ } else {
119+ (translation to - 1 )
120+ }
121+ return TranslationInstance (
122+ foldingElement,
123+ index,
124+ referenceElement,
125+ TranslationInstance .Key (prefix, translationKey, suffix),
126+ formatted,
127+ required,
128+ if (superfluousParams >= 0 ) FormattingError .SUPERFLUOUS else null ,
129+ superfluousParams,
130+ )
131+ } catch (ignored: MissingFormatArgumentException ) {
132+ return TranslationInstance (
133+ foldingElement,
134+ index,
135+ referenceElement,
136+ TranslationInstance .Key (prefix, translationKey, suffix),
137+ translation,
138+ required,
139+ FormattingError .MISSING ,
140+ )
141+ }
142+ }
143+
144+ private fun format (method : PsiMethod , translation : String , call : PsiCall ): Pair <String , Int >? {
145+ val format = NUMBER_FORMATTING_PATTERN .replace(translation, " %$1s" )
146+ val paramCount = STRING_FORMATTING_PATTERN .findAll(format).count()
147+
148+ val varargs = call.extractVarArgs(method.parameterList.parametersCount - 1 , true , true )
149+ val varargStart = if (varargs.size > paramCount) {
150+ method.parameterList.parametersCount - 1 + paramCount
151+ } else {
152+ - 1
153+ }
154+ return try {
155+ String .format(format, * varargs) to varargStart
156+ } catch (e: MissingFormatArgumentException ) {
157+ // rethrow this specific exception to be handled by the caller
158+ throw e
159+ } catch (e: IllegalFormatException ) {
160+ null
161+ }
103162 }
163+
164+ private val NUMBER_FORMATTING_PATTERN = Regex (" %(\\ d+\\ $)?[\\ d.]*[df]" )
165+ private val STRING_FORMATTING_PATTERN = Regex (" [^%]?%(?:\\ d+\\ $)?s" )
104166 }
105167}
0 commit comments