Introduce Variable: Support extraction of string template fragments
#KT-2089 Fixed
This commit is contained in:
@@ -55,15 +55,25 @@ public class KtPsiFactory(private val project: Project) {
|
||||
return (createExpression("a?.b") as KtSafeQualifiedExpression).getOperationTokenNode()
|
||||
}
|
||||
|
||||
public fun createExpression(text: String): KtExpression {
|
||||
private fun doCreateExpression(text: String): KtExpression {
|
||||
//TODO: '\n' below if important - some strange code indenting problems appear without it
|
||||
val expression = createProperty("val x =\n$text").getInitializer() ?: error("Failed to create expression from text: '$text'")
|
||||
return expression
|
||||
}
|
||||
|
||||
public fun createExpression(text: String): KtExpression {
|
||||
val expression = doCreateExpression(text)
|
||||
assert(expression.getText() == text) {
|
||||
"Failed to create expression from text: '$text', resulting expression's text was: '${expression.getText()}'"
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
public fun createExpressionIfPossible(text: String): KtExpression? {
|
||||
val expression = doCreateExpression(text)
|
||||
return if (expression.getText() == text) expression else null
|
||||
}
|
||||
|
||||
public fun createClassLiteral(className: String): KtClassLiteralExpression =
|
||||
createExpression("$className::class") as KtClassLiteralExpression
|
||||
|
||||
@@ -291,6 +301,8 @@ public class KtPsiFactory(private val project: Project) {
|
||||
return stringTemplateExpression.getEntries()[0] as KtStringTemplateEntryWithExpression
|
||||
}
|
||||
|
||||
public fun createStringTemplate(content: String) = createExpression("\"$content\"") as KtStringTemplateExpression
|
||||
|
||||
public fun createPackageDirective(fqName: FqName): KtPackageDirective {
|
||||
return createFile("package ${fqName.asString()}").getPackageDirective()!!
|
||||
}
|
||||
|
||||
@@ -138,6 +138,8 @@ public fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(): PsiElement?
|
||||
return siblings(withItself = false).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull()
|
||||
}
|
||||
|
||||
inline public fun <reified T : PsiElement> T.nextSiblingOfSameType() = PsiTreeUtil.getNextSiblingOfType(this, T::class.java)
|
||||
|
||||
public fun PsiElement?.isAncestor(element: PsiElement, strict: Boolean = false): Boolean {
|
||||
return PsiTreeUtil.isAncestor(this, element, strict)
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde;
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.IntroduceUtilKt;
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
|
||||
import org.jetbrains.kotlin.idea.util.string.StringUtilKt;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
@@ -489,7 +489,7 @@ public class KotlinRefactoringUtil {
|
||||
private static KtExpression findExpression(
|
||||
@NotNull KtFile file, int startOffset, int endOffset, boolean failOnNoExpression
|
||||
) throws IntroduceRefactoringException {
|
||||
KtExpression element = CodeInsightUtils.findExpression(file, startOffset, endOffset);
|
||||
KtExpression element = IntroduceUtilKt.findExpressionOrStringFragment(file, startOffset, endOffset);
|
||||
if (element == null) {
|
||||
//todo: if it's infix expression => add (), then commit document then return new created expression
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.introduce
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.nextSiblingOfSameType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
class ExtractableSubstringInfo(
|
||||
val startEntry: KtStringTemplateEntry,
|
||||
val endEntry: KtStringTemplateEntry,
|
||||
val prefix: String,
|
||||
val suffix: String,
|
||||
type: KotlinType? = null
|
||||
) {
|
||||
private fun guessLiteralType(literal: String): KotlinType {
|
||||
val facade = template.getResolutionFacade()
|
||||
val builtIns = facade.moduleDescriptor.builtIns
|
||||
val stringType = builtIns.stringType
|
||||
|
||||
if (startEntry != endEntry || startEntry !is KtLiteralStringTemplateEntry) return stringType
|
||||
|
||||
val expr = KtPsiFactory(startEntry).createExpressionIfPossible(literal) ?: return stringType
|
||||
|
||||
val context = facade.analyze(template, BodyResolveMode.PARTIAL)
|
||||
val scope = template.getResolutionScope(context, facade)
|
||||
|
||||
val tempContext = expr.analyzeInContext(scope, template)
|
||||
val trace = DelegatingBindingTrace(tempContext, "Evaluate '$literal'")
|
||||
val value = ConstantExpressionEvaluator(builtIns).evaluateExpression(expr, trace)
|
||||
if (value == null || value.isError) return stringType
|
||||
|
||||
return value.toConstantValue(TypeUtils.NO_EXPECTED_TYPE).type
|
||||
}
|
||||
|
||||
val template: KtStringTemplateExpression = startEntry.parent as KtStringTemplateExpression
|
||||
|
||||
val content = with(entries.map { it.text }.joinToString(separator = "")) { substring(prefix.length, length - suffix.length) }
|
||||
|
||||
val type = type ?: guessLiteralType(content)
|
||||
|
||||
val contentRange: TextRange
|
||||
get() = TextRange(startEntry.startOffset + prefix.length, endEntry.endOffset - suffix.length)
|
||||
|
||||
val entries: Sequence<KtStringTemplateEntry>
|
||||
get() = sequence(startEntry) { if (it != endEntry) it.nextSiblingOfSameType() else null }
|
||||
|
||||
fun createExpression(): KtExpression {
|
||||
val quote = template.firstChild.text
|
||||
val literalValue = if (KotlinBuiltIns.isString(type)) "$quote$content$quote" else content
|
||||
return KtPsiFactory(startEntry).createExpression(literalValue).apply { extractableSubstringInfo = this@ExtractableSubstringInfo }
|
||||
}
|
||||
|
||||
fun copy(newTemplate: KtStringTemplateExpression): ExtractableSubstringInfo {
|
||||
val oldEntries = template.entries
|
||||
val newEntries = newTemplate.entries
|
||||
val startIndex = oldEntries.indexOf(startEntry)
|
||||
val endIndex = oldEntries.indexOf(endEntry)
|
||||
if (startIndex < 0 || startIndex >= newEntries.size || endIndex < 0 || endIndex >= newEntries.size) {
|
||||
throw AssertionError("Old template($startIndex..$endIndex): ${template.text}, new template: ${newTemplate.text}")
|
||||
}
|
||||
return ExtractableSubstringInfo(newEntries[startIndex], newEntries[endIndex], prefix, suffix, type)
|
||||
}
|
||||
}
|
||||
|
||||
var KtExpression.extractableSubstringInfo: ExtractableSubstringInfo? by UserDataProperty(Key.create("EXTRACTED_SUBSTRING_INFO"))
|
||||
|
||||
val KtExpression.substringContextOrThis: KtExpression
|
||||
get() = extractableSubstringInfo?.template ?: this
|
||||
|
||||
val PsiElement.substringContextOrThis: PsiElement
|
||||
get() = (this as? KtExpression)?.extractableSubstringInfo?.template ?: this
|
||||
@@ -20,17 +20,16 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getOutermostParentContainedIn
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
|
||||
fun showErrorHint(project: Project, editor: Editor, message: String, title: String) {
|
||||
CodeInsightUtils.showErrorHint(project, editor, message, title, null)
|
||||
@@ -48,8 +47,9 @@ fun selectElementsWithTargetSibling(
|
||||
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
|
||||
) {
|
||||
fun onSelectionComplete(elements: List<PsiElement>, targetContainer: PsiElement) {
|
||||
val parent = PsiTreeUtil.findCommonParent(elements)
|
||||
?: throw AssertionError("Should have at least one parent: ${elements.joinToString("\n")}")
|
||||
val physicalElements = elements.map { it.substringContextOrThis }
|
||||
val parent = PsiTreeUtil.findCommonParent(physicalElements)
|
||||
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
|
||||
|
||||
if (parent == targetContainer) {
|
||||
continuation(elements, elements.first())
|
||||
@@ -80,8 +80,9 @@ fun selectElementsWithTargetParent(
|
||||
}
|
||||
|
||||
fun selectTargetContainer(elements: List<PsiElement>) {
|
||||
val parent = PsiTreeUtil.findCommonParent(elements)
|
||||
?: throw AssertionError("Should have at least one parent: ${elements.joinToString("\n")}")
|
||||
val physicalElements = elements.map { it.substringContextOrThis }
|
||||
val parent = PsiTreeUtil.findCommonParent(physicalElements)
|
||||
?: throw AssertionError("Should have at least one parent: ${physicalElements.joinToString("\n")}")
|
||||
|
||||
val containers = getContainers(elements, parent)
|
||||
if (containers.isEmpty()) {
|
||||
@@ -147,3 +148,29 @@ fun PsiElement.findExpressionsByCopyableDataAndClearIt(key: Key<Boolean>): List<
|
||||
results.forEach { it.putCopyableUserData(key, null) }
|
||||
return results
|
||||
}
|
||||
|
||||
fun findExpressionOrStringFragment(file: KtFile, startOffset: Int, endOffset: Int): KtExpression? {
|
||||
CodeInsightUtils.findExpression(file, startOffset, endOffset)?.let { return it }
|
||||
|
||||
val entry1 = file.findElementAt(startOffset)?.getNonStrictParentOfType<KtStringTemplateEntry>() ?: return null
|
||||
val entry2 = file.findElementAt(endOffset - 1)?.getNonStrictParentOfType<KtStringTemplateEntry>() ?: return null
|
||||
|
||||
if (entry1 == entry2 && entry1 is KtStringTemplateEntryWithExpression) return entry1.expression
|
||||
|
||||
val stringTemplate = entry1.parent as? KtStringTemplateExpression ?: return null
|
||||
if (entry2.parent != stringTemplate) return null
|
||||
|
||||
val templateOffset = stringTemplate.startOffset
|
||||
if (stringTemplate.getContentRange().equalsToRange(startOffset - templateOffset, endOffset - templateOffset)) return stringTemplate
|
||||
|
||||
val prefixOffset = startOffset - entry1.startOffset
|
||||
if (entry1 !is KtLiteralStringTemplateEntry && prefixOffset > 0) return null
|
||||
|
||||
val suffixOffset = endOffset - entry2.startOffset
|
||||
if (entry2 !is KtLiteralStringTemplateEntry && suffixOffset < entry2.textLength) return null
|
||||
|
||||
val prefix = entry1.text.substring(0, prefixOffset)
|
||||
val suffix = entry2.text.substring(suffixOffset)
|
||||
|
||||
return ExtractableSubstringInfo(entry1, entry2, prefix, suffix).createExpression()
|
||||
}
|
||||
+75
-33
@@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
@@ -45,10 +46,7 @@ import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceHandlerBase
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.findElementByCopyableDataAndClearIt
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionByCopyableDataAndClearIt
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionsByCopyableDataAndClearIt
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.*
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
@@ -105,17 +103,34 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
return newContainer.findElementAt(offset)?.parentsWithSelf?.firstOrNull { (it as? KtExpression)?.text == text }
|
||||
}
|
||||
|
||||
private fun replaceExpression(replace: KtExpression, addToReferences: Boolean): KtExpression {
|
||||
val isActualExpression = expression == replace
|
||||
private fun replaceExpression(expressionToReplace: KtExpression, addToReferences: Boolean): KtExpression {
|
||||
val isActualExpression = expression == expressionToReplace
|
||||
|
||||
val replacement = psiFactory.createExpression(nameSuggestion)
|
||||
var result = if (replace.isFunctionLiteralOutsideParentheses()) {
|
||||
val functionLiteralArgument = replace.getStrictParentOfType<KtFunctionLiteralArgument>()!!
|
||||
val substringInfo = expressionToReplace.extractableSubstringInfo
|
||||
var result = if (expressionToReplace.isFunctionLiteralOutsideParentheses()) {
|
||||
val functionLiteralArgument = expressionToReplace.getStrictParentOfType<KtFunctionLiteralArgument>()!!
|
||||
val newCallExpression = functionLiteralArgument.moveInsideParenthesesAndReplaceWith(replacement, bindingContext)
|
||||
newCallExpression.valueArguments.last().getArgumentExpression()!!
|
||||
}
|
||||
else if (substringInfo != null) {
|
||||
with(substringInfo) {
|
||||
val parent = startEntry.parent
|
||||
|
||||
psiFactory.createStringTemplate(prefix).entries.singleOrNull()?.let { parent.addBefore(it, startEntry) }
|
||||
|
||||
val refEntry = psiFactory.createBlockStringTemplateEntry(replacement)
|
||||
val addedRefEntry = parent.addBefore(refEntry, startEntry) as KtStringTemplateEntryWithExpression
|
||||
|
||||
psiFactory.createStringTemplate(suffix).entries.singleOrNull()?.let { parent.addAfter(it, endEntry) }
|
||||
|
||||
parent.deleteChildRange(startEntry, endEntry)
|
||||
|
||||
addedRefEntry.expression!!
|
||||
}
|
||||
}
|
||||
else {
|
||||
replace.replace(replacement) as KtExpression
|
||||
expressionToReplace.replace(replacement) as KtExpression
|
||||
}
|
||||
|
||||
val parent = result.parent
|
||||
@@ -279,7 +294,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
|
||||
expression.putCopyableUserData(EXPRESSION_KEY, true)
|
||||
for (replace in allReplaces) {
|
||||
replace.putCopyableUserData(REPLACE_KEY, true)
|
||||
replace.substringContextOrThis.putCopyableUserData(REPLACE_KEY, true)
|
||||
}
|
||||
commonParent.putCopyableUserData(COMMON_PARENT_KEY, true)
|
||||
|
||||
@@ -290,7 +305,12 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
|
||||
val newExpression = newCommonContainer.findExpressionByCopyableDataAndClearIt(EXPRESSION_KEY)
|
||||
val newCommonParent = newCommonContainer.findElementByCopyableDataAndClearIt(COMMON_PARENT_KEY)
|
||||
val newAllReplaces = newCommonContainer.findExpressionsByCopyableDataAndClearIt(REPLACE_KEY)
|
||||
val newAllReplaces = (allReplaces zip newCommonContainer.findExpressionsByCopyableDataAndClearIt(REPLACE_KEY)).map {
|
||||
val (originalReplace, newReplace) = it
|
||||
originalReplace.extractableSubstringInfo?.let {
|
||||
originalReplace.apply { extractableSubstringInfo = it.copy(newReplace as KtStringTemplateExpression) }
|
||||
} ?: newReplace
|
||||
}
|
||||
|
||||
runRefactoring(newExpression, newCommonContainer, newCommonParent, newAllReplaces)
|
||||
}
|
||||
@@ -299,7 +319,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
private fun calculateAnchor(commonParent: PsiElement, commonContainer: PsiElement, allReplaces: List<KtExpression>): PsiElement? {
|
||||
if (commonParent != commonContainer) return commonParent.parentsWithSelf.firstOrNull { it.parent == commonContainer }
|
||||
|
||||
val startOffset = allReplaces.fold(commonContainer.endOffset) { offset, expr -> Math.min(offset, expr.startOffset) }
|
||||
val startOffset = allReplaces.fold(commonContainer.endOffset) { offset, expr -> Math.min(offset, expr.substringContextOrThis.startOffset) }
|
||||
return commonContainer.allChildren.lastOrNull { it.textRange.contains(startOffset) } ?: return null
|
||||
}
|
||||
|
||||
@@ -376,38 +396,42 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
occurrencesToReplace: List<KtExpression>?,
|
||||
onNonInteractiveFinish: ((KtProperty) -> Unit)?
|
||||
) {
|
||||
val parent = expression.parent
|
||||
val substringInfo = expression.extractableSubstringInfo
|
||||
val physicalExpression = expression.substringContextOrThis
|
||||
|
||||
val parent = physicalExpression.parent
|
||||
|
||||
when {
|
||||
parent is KtQualifiedExpression -> {
|
||||
if (parent.receiverExpression != expression) {
|
||||
if (parent.receiverExpression != physicalExpression) {
|
||||
return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
||||
}
|
||||
}
|
||||
expression is KtStatementExpression ->
|
||||
physicalExpression is KtStatementExpression ->
|
||||
return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
||||
parent is KtOperationExpression && parent.operationReference == expression ->
|
||||
parent is KtOperationExpression && parent.operationReference == physicalExpression ->
|
||||
return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
||||
}
|
||||
|
||||
PsiTreeUtil.getNonStrictParentOfType(expression,
|
||||
PsiTreeUtil.getNonStrictParentOfType(physicalExpression,
|
||||
KtTypeReference::class.java,
|
||||
KtConstructorCalleeExpression::class.java,
|
||||
KtSuperExpression::class.java)?.let {
|
||||
return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.container"))
|
||||
}
|
||||
|
||||
val expressionType = bindingContext.getType(expression) //can be null or error type
|
||||
val scope = expression.getResolutionScope(bindingContext, resolutionFacade)
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||
val expressionType = substringInfo?.type ?: bindingContext.getType(physicalExpression) //can be null or error type
|
||||
val scope = physicalExpression.getResolutionScope(bindingContext, resolutionFacade)
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(physicalExpression)
|
||||
|
||||
val bindingTrace = ObservableBindingTrace(BindingTraceContext())
|
||||
val typeNoExpectedType = expression.computeTypeInfoInContext(scope, expression, bindingTrace, dataFlowInfo).type
|
||||
val typeNoExpectedType = substringInfo?.type
|
||||
?: physicalExpression.computeTypeInfoInContext(scope, physicalExpression, bindingTrace, dataFlowInfo).type
|
||||
val noTypeInference = expressionType != null
|
||||
&& typeNoExpectedType != null
|
||||
&& !KotlinTypeChecker.DEFAULT.equalTypes(expressionType, typeNoExpectedType)
|
||||
|
||||
if (expressionType == null && bindingContext.get(BindingContext.QUALIFIER, expression) != null) {
|
||||
if (expressionType == null && bindingContext.get(BindingContext.QUALIFIER, physicalExpression) != null) {
|
||||
return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.package.expression"))
|
||||
}
|
||||
|
||||
@@ -426,9 +450,11 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
OccurrencesChooser.ReplaceChoice.ALL -> allOccurrences
|
||||
else -> listOf(expression)
|
||||
}
|
||||
val replaceOccurrence = expression.shouldReplaceOccurrence(bindingContext, container) || allReplaces.size > 1
|
||||
val replaceOccurrence = substringInfo != null
|
||||
|| expression.shouldReplaceOccurrence(bindingContext, container)
|
||||
|| allReplaces.size > 1
|
||||
|
||||
val commonParent = PsiTreeUtil.findCommonParent(allReplaces) as KtElement
|
||||
val commonParent = PsiTreeUtil.findCommonParent(allReplaces.map { it.substringContextOrThis }) as KtElement
|
||||
var commonContainer = commonParent.getContainer()!!
|
||||
if (commonContainer != container && container.isAncestor(commonContainer, true)) {
|
||||
commonContainer = container
|
||||
@@ -439,7 +465,11 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
calculateAnchor(commonParent, commonContainer, allReplaces),
|
||||
NewDeclarationNameValidator.Target.VARIABLES
|
||||
)
|
||||
val suggestedNames = KotlinNameSuggester.suggestNamesByExpressionAndType(expression, null, bindingContext, validator, "value")
|
||||
val suggestedNames = KotlinNameSuggester.suggestNamesByExpressionAndType(expression,
|
||||
substringInfo?.type,
|
||||
bindingContext,
|
||||
validator,
|
||||
"value")
|
||||
val introduceVariableContext = IntroduceVariableContext(
|
||||
expression, suggestedNames.iterator().next(), allReplaces, commonContainer, commonParent,
|
||||
replaceOccurrence, noTypeInference, expressionType, bindingContext, resolutionFacade
|
||||
@@ -477,7 +507,12 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
}
|
||||
|
||||
if (isInplaceAvailable && occurrencesToReplace == null) {
|
||||
OccurrencesChooser.simpleChooser<KtExpression>(editor).showChooser(expression, allOccurrences, callback)
|
||||
val chooser = object: OccurrencesChooser<KtExpression>(editor) {
|
||||
override fun getOccurrenceRange(occurrence: KtExpression): TextRange? {
|
||||
return occurrence.extractableSubstringInfo?.contentRange ?: occurrence.textRange
|
||||
}
|
||||
}
|
||||
chooser.showChooser(expression, allOccurrences, callback)
|
||||
}
|
||||
else {
|
||||
callback.pass(OccurrencesChooser.ReplaceChoice.ALL)
|
||||
@@ -494,13 +529,17 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
resolutionFacade: ResolutionFacade,
|
||||
originalContext: BindingContext
|
||||
): List<Pair<KtElement, KtElement>> {
|
||||
val file = getContainingKtFile()
|
||||
val physicalExpression = substringContextOrThis
|
||||
val contentRange = extractableSubstringInfo?.contentRange
|
||||
|
||||
val references = collectDescendantsOfType<KtReferenceExpression>()
|
||||
val file = physicalExpression.getContainingKtFile()
|
||||
|
||||
val references = physicalExpression
|
||||
.collectDescendantsOfType<KtReferenceExpression> { contentRange == null || contentRange.contains(it.textRange) }
|
||||
|
||||
fun isResolvableNextTo(neighbour: KtExpression): Boolean {
|
||||
val scope = neighbour.getResolutionScope(originalContext, resolutionFacade)
|
||||
val newContext = analyzeInContext(scope, neighbour)
|
||||
val newContext = physicalExpression.analyzeInContext(scope, neighbour)
|
||||
val project = file.project
|
||||
return references.all {
|
||||
val originalDescriptor = originalContext[BindingContext.REFERENCE_TARGET, it]
|
||||
@@ -515,8 +554,8 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
}
|
||||
}
|
||||
|
||||
val firstContainer = getContainer() ?: return emptyList()
|
||||
val firstOccurrenceContainer = getOccurrenceContainer() ?: return emptyList()
|
||||
val firstContainer = physicalExpression.getContainer() ?: return emptyList()
|
||||
val firstOccurrenceContainer = physicalExpression.getOccurrenceContainer() ?: return emptyList()
|
||||
|
||||
val containers = SmartList(firstContainer)
|
||||
val occurrenceContainers = SmartList(firstOccurrenceContainer)
|
||||
@@ -554,8 +593,10 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
val expression = expressionToExtract?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||
?: return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression"))
|
||||
|
||||
val resolutionFacade = expression.getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.FULL)
|
||||
val physicalExpression = expression.substringContextOrThis
|
||||
|
||||
val resolutionFacade = physicalExpression.getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(physicalExpression, BodyResolveMode.FULL)
|
||||
|
||||
fun runWithChosenContainers(container: KtElement, occurrenceContainer: KtElement) {
|
||||
doRefactoring(project, editor, expression, container, occurrenceContainer, resolutionFacade, bindingContext, occurrencesToReplace, onNonInteractiveFinish)
|
||||
@@ -580,6 +621,7 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext) {
|
||||
if (file !is KtFile) return
|
||||
|
||||
try {
|
||||
KotlinRefactoringUtil.selectExpression(editor, file) { doRefactoring(project, editor, it, null, null) }
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.getContextForContainingDeclarationBody
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.ExtractableSubstringInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractableSubstringInfo
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange.Empty
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.*
|
||||
@@ -60,7 +62,7 @@ public interface UnificationResult {
|
||||
override fun and(other: Status): Status = this
|
||||
};
|
||||
|
||||
public abstract fun and(other: Status): Status
|
||||
public abstract infix fun and(other: Status): Status
|
||||
}
|
||||
|
||||
object Unmatched : UnificationResult {
|
||||
@@ -111,6 +113,7 @@ public class KotlinPsiUnifier(
|
||||
val declarationPatternsToTargets = MultiMap<DeclarationDescriptor, DeclarationDescriptor>()
|
||||
val weakMatches = HashMap<KtElement, KtElement>()
|
||||
var checkEquivalence: Boolean = false
|
||||
var targetSubstringInfo: ExtractableSubstringInfo? = null
|
||||
|
||||
private fun KotlinPsiRange.getBindingContext(): BindingContext {
|
||||
val element = (this as? KotlinPsiRange.ListRange)?.startElement as? KtElement
|
||||
@@ -702,7 +705,73 @@ public class KotlinPsiUnifier(
|
||||
return targetElementType != null && KotlinTypeChecker.DEFAULT.isSubtypeOf(targetElementType, parameter.expectedType)
|
||||
}
|
||||
|
||||
private fun doUnifyStringTemplateFragments(target: KtStringTemplateExpression, pattern: ExtractableSubstringInfo): Status {
|
||||
val prefixLength = pattern.prefix.length
|
||||
val suffixLength = pattern.suffix.length
|
||||
val targetEntries = target.entries
|
||||
val patternEntries = pattern.entries.toList()
|
||||
for ((index, targetEntry) in targetEntries.withIndex()) {
|
||||
if (index + patternEntries.size > targetEntries.size) return UNMATCHED
|
||||
|
||||
val targetEntryText = targetEntry.text
|
||||
|
||||
if (pattern.startEntry == pattern.endEntry && (prefixLength > 0 || suffixLength > 0)) {
|
||||
if (targetEntry !is KtLiteralStringTemplateEntry) continue
|
||||
|
||||
val patternText = with(pattern.startEntry.text) { substring(prefixLength, length - suffixLength) }
|
||||
val i = targetEntryText.indexOf(patternText)
|
||||
if (i < 0) continue
|
||||
val targetPrefix = targetEntryText.substring(0, i)
|
||||
val targetSuffix = targetEntryText.substring(i + patternText.length)
|
||||
targetSubstringInfo = ExtractableSubstringInfo(targetEntry, targetEntry, targetPrefix, targetSuffix, pattern.type)
|
||||
return MATCHED
|
||||
}
|
||||
|
||||
val matchStartByText = pattern.startEntry is KtLiteralStringTemplateEntry
|
||||
val matchEndByText = pattern.endEntry is KtLiteralStringTemplateEntry
|
||||
|
||||
val targetPrefix = if (matchStartByText) {
|
||||
if (targetEntry !is KtLiteralStringTemplateEntry) continue
|
||||
|
||||
val patternText = pattern.startEntry.text.substring(prefixLength)
|
||||
if (!targetEntryText.endsWith(patternText)) continue
|
||||
targetEntryText.substring(0, targetEntryText.length - patternText.length)
|
||||
}
|
||||
else ""
|
||||
|
||||
val lastTargetEntry = targetEntries[index + patternEntries.lastIndex]
|
||||
|
||||
val targetSuffix = if (matchEndByText) {
|
||||
if (lastTargetEntry !is KtLiteralStringTemplateEntry) continue
|
||||
|
||||
val patternText = with(pattern.endEntry.text) { substring(0, length - suffixLength) }
|
||||
val lastTargetEntryText = lastTargetEntry.text
|
||||
if (!lastTargetEntryText.startsWith(patternText)) continue
|
||||
lastTargetEntryText.substring(patternText.length)
|
||||
}
|
||||
else ""
|
||||
|
||||
val fromIndex = if (matchStartByText) 1 else 0
|
||||
val toIndex = if (matchEndByText) patternEntries.lastIndex - 1 else patternEntries.lastIndex
|
||||
val status = (fromIndex..toIndex).fold(MATCHED) { s, patternEntryIndex ->
|
||||
val targetEntryToUnify = targetEntries[index + patternEntryIndex]
|
||||
val patternEntryToUnify = patternEntries[patternEntryIndex]
|
||||
if (s != UNMATCHED) s and doUnify(targetEntryToUnify, patternEntryToUnify) else s
|
||||
}
|
||||
if (status == UNMATCHED) continue
|
||||
targetSubstringInfo = ExtractableSubstringInfo(targetEntry, lastTargetEntry, targetPrefix, targetSuffix, pattern.type)
|
||||
return status
|
||||
}
|
||||
|
||||
return UNMATCHED
|
||||
}
|
||||
|
||||
fun doUnify(target: KotlinPsiRange, pattern: KotlinPsiRange): Status {
|
||||
(pattern.elements.singleOrNull() as? KtExpression)?.extractableSubstringInfo?.let {
|
||||
val targetTemplate = target.elements.singleOrNull() as? KtStringTemplateExpression ?: return UNMATCHED
|
||||
return doUnifyStringTemplateFragments(targetTemplate, it)
|
||||
}
|
||||
|
||||
val targetElements = target.elements
|
||||
val patternElements = pattern.elements
|
||||
if (targetElements.size() != patternElements.size()) return UNMATCHED
|
||||
@@ -828,8 +897,14 @@ public class KotlinPsiUnifier(
|
||||
when {
|
||||
substitution.size() != descriptorToParameter.size() ->
|
||||
Unmatched
|
||||
status == MATCHED ->
|
||||
if (weakMatches.isEmpty()) StronglyMatched(target, substitution) else WeaklyMatched(target, substitution, weakMatches)
|
||||
status == MATCHED -> {
|
||||
val targetRange = targetSubstringInfo?.createExpression()?.toRange() ?: target
|
||||
if (weakMatches.isEmpty()) {
|
||||
StronglyMatched(targetRange, substitution)
|
||||
} else {
|
||||
WeaklyMatched(targetRange, substitution, weakMatches)
|
||||
}
|
||||
}
|
||||
else ->
|
||||
Unmatched
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc${pa</selection>ram}def"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot find an expression to introduce
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(param: Int): String {
|
||||
return "<selection>abc$pa</selection>ram"
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Cannot find an expression to introduce
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(a: Int): String {
|
||||
return "<selection>abc$a\</selection>ndef"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Cannot find an expression to introduce
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "xyfalsez"
|
||||
val y = "xyFalsez"
|
||||
val z = false
|
||||
return "ab<selection>false</selection>def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: Int): String {
|
||||
val b = false
|
||||
val x = "xy${b}z"
|
||||
val y = "xyFalsez"
|
||||
val z = false
|
||||
return "ab${b}def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "a1234_"
|
||||
val y = "-4123a"
|
||||
val z = "+1243a"
|
||||
val u = 123
|
||||
return "ab<selection>123</selection>def"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun foo(param: Int): String {
|
||||
val i = 123
|
||||
val x = "a${i}4_"
|
||||
val y = "-4${i}a"
|
||||
val z = "+1243a"
|
||||
val u = 123
|
||||
return "ab${i}def"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(param: Int): String {
|
||||
val x = "atrue123"
|
||||
val x = "aTRUE123"
|
||||
val z = true
|
||||
return "ab<selection>true</selection>def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(param: Int): String {
|
||||
val b = true
|
||||
val x = "a${b}123"
|
||||
val x = "aTRUE123"
|
||||
val z = true
|
||||
return "ab${b}def"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "abc$a"
|
||||
val y = "abc${a}"
|
||||
val z = "abc{$a}def"
|
||||
return "<selection>abc$a</selection>" + "def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "abc$a"
|
||||
val x = s
|
||||
val y = s
|
||||
val z = "abc{$a}def"
|
||||
return s + "def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "-${a + 1}"
|
||||
val y = "x${a + 1}y"
|
||||
val z = "x${a - 1}y"
|
||||
return "abc<selection>${a + 1}</selection>def"
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val i = a + 1
|
||||
val x = "-$i"
|
||||
val y = "x${i}y"
|
||||
val z = "x${a - 1}y"
|
||||
return "abc${i}def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "-$a"
|
||||
val y = "x${a}y"
|
||||
val z = "x$ay"
|
||||
return "abc<selection>${a}</selection>def"
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val a1 = a
|
||||
val x = "-$a1"
|
||||
val y = "x${a1}y"
|
||||
val z = "x$ay"
|
||||
return "abc${a1}def"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "+cd$a:${a + 1}efg"
|
||||
val y = "+cd$a${a + 1}efg"
|
||||
return "ab<selection>cd$a:${a + 1}ef</selection>"
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "cd$a:${a + 1}ef"
|
||||
val x = "+${s}g"
|
||||
val y = "+cd$a${a + 1}efg"
|
||||
return "ab$s"
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "_c$a:${a + 1}d_"
|
||||
val y = "_$a:${a + 1}d_"
|
||||
return "ab<selection>c$a:${a + 1}d</selection>ef"
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "c$a:${a + 1}d"
|
||||
val x = "_${s}_"
|
||||
val y = "_$a:${a + 1}d_"
|
||||
return "ab${s}ef"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "_ab$a:${a + 1}cd__"
|
||||
val y = "_a$a:${a + 1}cd__"
|
||||
return "<selection>ab$a:${a + 1}cd</selection>ef"
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "ab$a:${a + 1}cd"
|
||||
val x = "_${s}__"
|
||||
val y = "_a$a:${a + 1}cd__"
|
||||
return "${s}ef"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = """_c$a
|
||||
:${a + 1}d_"""
|
||||
val y = "_$a:${a + 1}d_"
|
||||
val z = """_c$a:${a + 1}d_"""
|
||||
val u = "_c$a\n:${a + 1}d_"
|
||||
return """ab<selection>c$a
|
||||
:${a + 1}d</selection>ef"""
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = """c$a
|
||||
:${a + 1}d"""
|
||||
val x = """_${s}_"""
|
||||
val y = "_$a:${a + 1}d_"
|
||||
val z = """_c$a:${a + 1}d_"""
|
||||
val u = "_c$a\n:${a + 1}d_"
|
||||
return """ab${s}ef"""
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xabc$a"
|
||||
val y = "${a}abcx"
|
||||
val z = "xacb$a"
|
||||
return "<selection>abc</selection>def"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "abc"
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xacb$a"
|
||||
return "${s}def"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xcd$a"
|
||||
val y = "${a}cdx"
|
||||
val z = "xcf$a"
|
||||
return "ab<selection>cd</selection>ef"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "cd"
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xcf$a"
|
||||
return "ab${s}ef"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(a: Int): String {
|
||||
val x = "xdef$a"
|
||||
val y = "${a}defx"
|
||||
val z = "xddf$a"
|
||||
return "abc<selection>def</selection>"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(a: Int): String {
|
||||
val s = "def"
|
||||
val x = "x$s$a"
|
||||
val y = "${a}${s}x"
|
||||
val z = "xddf$a"
|
||||
return "abc$s"
|
||||
}
|
||||
+105
@@ -507,6 +507,111 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/introduceVariable/stringTemplates")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StringTemplates extends AbstractExtractionTest {
|
||||
public void testAllFilesPresentInStringTemplates() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceVariable/stringTemplates"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEntryWithBlockExpr.kt")
|
||||
public void testBrokenEntryWithBlockExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/brokenEntryWithBlockExpr.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEntryWithExpr.kt")
|
||||
public void testBrokenEntryWithExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/brokenEntryWithExpr.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("brokenEscapeEntry.kt")
|
||||
public void testBrokenEscapeEntry() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/brokenEscapeEntry.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractFalse.kt")
|
||||
public void testExtractFalse() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/extractFalse.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractIntegerLiteral.kt")
|
||||
public void testExtractIntegerLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/extractIntegerLiteral.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extractTrue.kt")
|
||||
public void testExtractTrue() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/extractTrue.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullContent.kt")
|
||||
public void testFullContent() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/fullContent.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullEntryWithBlockExpr.kt")
|
||||
public void testFullEntryWithBlockExpr() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/fullEntryWithBlockExpr.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fullEntryWithSimpleName.kt")
|
||||
public void testFullEntryWithSimpleName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/fullEntryWithSimpleName.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithPrefix.kt")
|
||||
public void testMultipleEntriesWithPrefix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/multipleEntriesWithPrefix.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithSubstring.kt")
|
||||
public void testMultipleEntriesWithSubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/multipleEntriesWithSubstring.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleEntriesWithSuffix.kt")
|
||||
public void testMultipleEntriesWithSuffix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/multipleEntriesWithSuffix.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rawTemplateWithSubstring.kt")
|
||||
public void testRawTemplateWithSubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/rawTemplateWithSubstring.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntryPrefix.kt")
|
||||
public void testSingleEntryPrefix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/singleEntryPrefix.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntrySubstring.kt")
|
||||
public void testSingleEntrySubstring() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/singleEntrySubstring.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleEntrySuffix.kt")
|
||||
public void testSingleEntrySuffix() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/singleEntrySuffix.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction")
|
||||
|
||||
Reference in New Issue
Block a user