Extract Function: Improve container selection UI
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<root>
|
||||
<item
|
||||
name='com.intellij.codeInsight.unwrap.RangeSplitter java.util.List<com.intellij.openapi.util.TextRange> split(com.intellij.openapi.util.TextRange, java.util.List<com.intellij.openapi.util.TextRange>)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item
|
||||
name='com.intellij.codeInsight.unwrap.ScopeHighlighter ScopeHighlighter(com.intellij.openapi.editor.Editor, com.intellij.util.NotNullFunction<com.intellij.psi.PsiElement,com.intellij.openapi.util.TextRange>)'>
|
||||
<annotation name='kotlin.jvm.KotlinSignature'>
|
||||
<val name="value" val=""fun ScopeHighlighter(editor: Editor, ranger: NotNullFunction<PsiElement, TextRange>?)""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item
|
||||
name='com.intellij.codeInsight.unwrap.ScopeHighlighter void highlight(com.intellij.psi.PsiElement, java.util.List<com.intellij.psi.PsiElement>) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item
|
||||
name='com.intellij.codeInsight.unwrap.ScopeHighlighter void highlight(com.intellij.psi.PsiElement, java.util.List<com.intellij.psi.PsiElement>) 1'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -5,4 +5,7 @@
|
||||
<item name='com.intellij.ide.util.FileStructurePopup com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder getTreeBuilder()'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='com.intellij.ide.util.PsiElementListCellRenderer java.lang.String getElementText(T) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -0,0 +1,5 @@
|
||||
<root>
|
||||
<item name='com.intellij.openapi.editor.colors.EditorColorsManager com.intellij.openapi.editor.colors.EditorColorsManager getInstance()'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -123,6 +123,9 @@ public class JetIconProvider extends IconProvider {
|
||||
return JetIcons.FUNCTION;
|
||||
}
|
||||
}
|
||||
|
||||
if (psiElement instanceof JetFunctionLiteral) return JetIcons.LAMBDA;
|
||||
|
||||
if (psiElement instanceof JetClass) {
|
||||
JetClass jetClass = (JetClass) psiElement;
|
||||
if (jetClass.isTrait()) {
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.jetbrains.jet.plugin.JetBundle;
|
||||
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils;
|
||||
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil;
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||
import org.jetbrains.jet.plugin.util.UtilPackage;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -509,11 +510,7 @@ public class JetRefactoringUtil {
|
||||
}
|
||||
|
||||
public static String getExpressionShortText(@NotNull JetElement element) { //todo: write appropriate implementation
|
||||
String expressionText = element.getText();
|
||||
if (expressionText.length() > 20) {
|
||||
expressionText = expressionText.substring(0, 17) + "...";
|
||||
}
|
||||
return expressionText;
|
||||
return UtilPackage.collapseSpaces(StringUtil.shortenTextWithEllipsis(element.getText(), 53, 0));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+60
-23
@@ -32,8 +32,6 @@ import org.jetbrains.jet.lang.psi.JetFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.plugin.refactoring.getAllExtractionContainers
|
||||
import com.intellij.refactoring.IntroduceTargetChooser
|
||||
import com.intellij.openapi.util.Pass
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getOutermostParentContainedIn
|
||||
import org.jetbrains.jet.plugin.refactoring.checkConflictsInteractively
|
||||
@@ -56,6 +54,17 @@ import org.jetbrains.jet.lang.psi.JetProperty
|
||||
import org.jetbrains.jet.lang.psi.JetParameterList
|
||||
import org.jetbrains.jet.lang.psi.JetClassInitializer
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import javax.swing.Icon
|
||||
import org.jetbrains.jet.plugin.refactoring.getPsiElementPopup
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.jet.plugin.util.collapseSpaces
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.lang.psi.JetPropertyAccessor
|
||||
import org.jetbrains.jet.lang.psi.JetClassOrObject
|
||||
|
||||
public class ExtractKotlinFunctionHandler(public val allContainersEnabled: Boolean = false) : RefactoringActionHandler {
|
||||
@@ -222,33 +231,61 @@ fun selectElements(
|
||||
return
|
||||
}
|
||||
|
||||
IntroduceTargetChooser.showChooser(
|
||||
getPsiElementPopup(
|
||||
editor,
|
||||
containers,
|
||||
object: Pass<JetElement>() {
|
||||
override fun pass(targetContainer: JetElement?) {
|
||||
if (targetContainer == null) {
|
||||
noContainerError()
|
||||
return
|
||||
containers.copyToArray(),
|
||||
object: PsiElementListCellRenderer<JetElement>() {
|
||||
private fun JetElement.renderName(): String? {
|
||||
if (this is JetPropertyAccessor) {
|
||||
return (getParent() as JetProperty).renderName() + if (isGetter()) ".get" else ".set"
|
||||
}
|
||||
return (this as? PsiNamedElement)?.getName() ?: "<anonymous>"
|
||||
}
|
||||
|
||||
onSelectionComplete(parent, elements, targetContainer)
|
||||
private fun JetElement.renderDeclaration(): String? {
|
||||
val name = renderName()
|
||||
val descriptor = AnalyzerFacadeWithCache.getContextForElement(this)[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
|
||||
val params = (descriptor as? FunctionDescriptor)?.let { descriptor ->
|
||||
descriptor.getValueParameters()
|
||||
.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) }
|
||||
.joinToString(", ", "(", ")")
|
||||
} ?: ""
|
||||
return "$name$params"
|
||||
}
|
||||
|
||||
private fun JetElement.renderText(): String {
|
||||
return StringUtil.shortenTextWithEllipsis(getText()!!.collapseSpaces(), 53, 0)
|
||||
}
|
||||
|
||||
private fun JetElement.getRepresentativeElement(): JetElement {
|
||||
return when (this) {
|
||||
is JetBlockExpression -> (getParent() as? JetDeclarationWithBody) ?: this
|
||||
is JetClassBody -> getParent() as JetClassOrObject
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementText(element: JetElement): String? {
|
||||
val representativeElement = element.getRepresentativeElement()
|
||||
return when (representativeElement) {
|
||||
is JetFile, is JetDeclarationWithBody, is JetClassOrObject -> representativeElement.renderDeclaration()
|
||||
else -> representativeElement.renderText()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContainerText(element: JetElement?, name: String?): String? = null
|
||||
|
||||
override fun getIconFlags(): Int = 0
|
||||
|
||||
override fun getIcon(element: PsiElement?): Icon? =
|
||||
super.getIcon((element as? JetElement)?.getRepresentativeElement())
|
||||
},
|
||||
"Select target code block",
|
||||
{
|
||||
when (it) {
|
||||
is JetFile -> "File: ${it.getName()}"
|
||||
is JetBlockExpression -> {
|
||||
(it.getParent() as? JetDeclarationWithBody)?.getText() ?: "...${it.getStatements().first?.getText()}"
|
||||
}
|
||||
is JetClassBody -> {
|
||||
(it.getParent() as? JetClassOrObject)?.getText()
|
||||
}
|
||||
else -> it?.getText()
|
||||
}
|
||||
},
|
||||
"Select target code block"
|
||||
)
|
||||
onSelectionComplete(parent, elements, it)
|
||||
true
|
||||
}
|
||||
).showInBestPositionFor(editor)
|
||||
}
|
||||
|
||||
fun selectMultipleExpressions() {
|
||||
|
||||
@@ -46,7 +46,22 @@ import com.intellij.refactoring.ui.ConflictsDialog
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import org.jetbrains.jet.lang.psi.codeFragmentUtil.skipVisibilityCheck
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.ui.popup.JBPopup
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.codeInsight.unwrap.RangeSplitter
|
||||
import com.intellij.codeInsight.unwrap.UnwrapHandler
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import java.util.Collections
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.openapi.ui.popup.JBPopupAdapter
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent
|
||||
|
||||
/**
|
||||
* Replace [[JetSimpleNameExpression]] (and its enclosing qualifier) with qualified element given by FqName
|
||||
@@ -165,3 +180,73 @@ public fun Project.executeWriteCommand(name: String, command: () -> Unit) {
|
||||
this, { ApplicationManager.getApplication()!!.runWriteAction(command) }, name, null
|
||||
)
|
||||
}
|
||||
|
||||
public fun <T : PsiElement> getPsiElementPopup(
|
||||
editor: Editor,
|
||||
elements: Array<T>,
|
||||
renderer: PsiElementListCellRenderer<T>,
|
||||
title: String? = null,
|
||||
processor: (T) -> Boolean): JBPopup {
|
||||
val highlighter = SelectionAwareScopeHighlighter(editor)
|
||||
|
||||
val list = JBList(elements.toList())
|
||||
list.setCellRenderer(renderer)
|
||||
list.addListSelectionListener { e ->
|
||||
highlighter.dropHighlight()
|
||||
val index = list.getSelectedIndex()
|
||||
if (index >= 0) {
|
||||
highlighter.highlight(list.getModel()!!.getElementAt(index) as PsiElement)
|
||||
}
|
||||
}
|
||||
|
||||
return with(PopupChooserBuilder(list)) {
|
||||
title?.let { setTitle(it) }
|
||||
renderer.installSpeedSearch(this, true)
|
||||
setItemChoosenCallback {
|
||||
for (element in list.getSelectedValues()) {
|
||||
element.let {
|
||||
[suppress("UNCHECKED_CAST")]
|
||||
processor(it as T)
|
||||
}
|
||||
}
|
||||
}
|
||||
addListener(object: JBPopupAdapter() {
|
||||
override fun onClosed(event: LightweightWindowEvent?) {
|
||||
highlighter.dropHighlight();
|
||||
}
|
||||
})
|
||||
|
||||
createPopup()
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectionAwareScopeHighlighter(val editor: Editor) {
|
||||
private val highlighters = ArrayList<RangeHighlighter>()
|
||||
|
||||
private fun addHighlighter(r: TextRange, attr: TextAttributes) {
|
||||
highlighters.add(
|
||||
editor.getMarkupModel().addRangeHighlighter(
|
||||
r.getStartOffset(),
|
||||
r.getEndOffset(),
|
||||
UnwrapHandler.HIGHLIGHTER_LEVEL,
|
||||
attr,
|
||||
HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public fun highlight(wholeAffected: PsiElement) {
|
||||
dropHighlight()
|
||||
|
||||
val attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!!
|
||||
val selectedRange = with(editor.getSelectionModel()) { TextRange(getSelectionStart(), getSelectionEnd()) }
|
||||
for (r in RangeSplitter.split(wholeAffected.getTextRange()!!, Collections.singletonList(selectedRange))) {
|
||||
addHighlighter(r, attributes)
|
||||
}
|
||||
}
|
||||
|
||||
public fun dropHighlight() {
|
||||
highlighters.forEach { it.dispose() }
|
||||
highlighters.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.plugin.util
|
||||
|
||||
fun String.collapseSpaces(): String {
|
||||
val builder = StringBuilder()
|
||||
var haveSpaces = false
|
||||
for (c in this) {
|
||||
if (c.isWhitespace()) {
|
||||
haveSpaces = true
|
||||
}
|
||||
else {
|
||||
if (haveSpaces) {
|
||||
builder.append(" ")
|
||||
haveSpaces = false
|
||||
}
|
||||
builder.append(c)
|
||||
}
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
Reference in New Issue
Block a user