Move: Implement Kotlin-specific editor for class name

#KT-13192 Fixed
This commit is contained in:
Alexey Sedunov
2017-03-27 20:37:47 +03:00
parent eaba215bbb
commit b5db50f429
6 changed files with 118 additions and 11 deletions
@@ -142,7 +142,7 @@ abstract class CompletionSession(
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter())
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter(), (file as? KtCodeFragment)?.extraCompletionFilter)
}
protected val searchScope: GlobalSearchScope = getResolveScope(parameters.originalFile as KtFile)
@@ -418,4 +418,6 @@ fun OffsetMap.tryGetOffset(key: OffsetKey): Int? {
catch(e: Exception) {
return null
}
}
}
var KtCodeFragment.extraCompletionFilter: ((LookupElement) -> Boolean)? by CopyableUserDataProperty(Key.create("EXTRA_COMPLETION_FILTER"))
@@ -159,6 +159,7 @@ object KeywordCompletion {
CommentFilter(),
ParentFilter(ClassFilter(KtLiteralStringTemplateEntry::class.java)),
ParentFilter(ClassFilter(KtConstantExpression::class.java)),
FileFilter(ClassFilter(KtTypeCodeFragment::class.java)),
LeftNeighbour(TextFilter(".")),
LeftNeighbour(TextFilter("?."))
))
@@ -182,6 +183,17 @@ object KeywordCompletion {
}
}
private class FileFilter(filter : ElementFilter) : PositionElementFilter() {
init {
setFilter(filter)
}
override fun isAcceptable(element : Any?, context : PsiElement?) : Boolean {
val file = (element as? PsiElement)?.containingFile
return file != null && (filter?.isAcceptable(file, context) ?: true)
}
}
private fun buildFilter(position: PsiElement): (KtKeywordToken) -> Boolean {
var parent = position.parent
var prevParent = position
@@ -32,7 +32,8 @@ class LookupElementsCollector(
private val prefixMatcher: PrefixMatcher,
private val completionParameters: CompletionParameters,
resultSet: CompletionResultSet,
sorter: CompletionSorter
sorter: CompletionSorter,
private val filter: ((LookupElement) -> Boolean)?
) {
var bestMatchingDegree = Int.MIN_VALUE
@@ -132,7 +133,9 @@ class LookupElementsCollector(
}
}
elements.add(result)
if (filter?.invoke(result) ?: true) {
elements.add(result)
}
val matchingDegree = RealPrefixMatchingWeigher.getBestMatchingDegree(result, prefixMatcher)
bestMatchingDegree = Math.max(bestMatchingDegree, matchingDegree)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.ide.util.ClassFilter;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeJavaClassChooserDialog;
@@ -23,9 +24,9 @@ import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaCodeFragment;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringBundle;
@@ -34,18 +35,21 @@ import com.intellij.refactoring.classMembers.MemberInfoChange;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration;
import org.jetbrains.kotlin.idea.completion.CompletionUtilsKt;
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject;
import org.jetbrains.kotlin.idea.projectView.KtClassOrObjectTreeNode;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinTypeReferenceEditorComboWithBrowseButton;
import org.jetbrains.kotlin.psi.*;
import javax.swing.*;
@@ -62,7 +66,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
private JPanel mainPanel;
private JTextField originalClassField;
private JPanel membersInfoPanel;
private ReferenceEditorComboWithBrowseButton targetClassChooser;
private KotlinTypeReferenceEditorComboWithBrowseButton targetClassChooser;
private JCheckBox openInEditorCheckBox;
private JPanel targetClassChooserPanel;
private KotlinMemberSelectionTable memberTable;
@@ -96,7 +100,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
originalClassField.setText(originalClass.getFqName().asString());
//noinspection ConstantConditions
targetClassChooser = new ReferenceEditorComboWithBrowseButton(
targetClassChooser = new KotlinTypeReferenceEditorComboWithBrowseButton(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -148,10 +152,24 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
}
},
initialTargetClass.getFqName().asString(),
myProject,
true,
JavaCodeFragment.VisibilityChecker.PROJECT_SCOPE_VISIBLE,
originalClass,
RECENTS_KEY);
KtTypeCodeFragment codeFragment = targetClassChooser.getCodeFragment();
if (codeFragment != null) {
CompletionUtilsKt.setExtraCompletionFilter(
codeFragment,
new Function1<LookupElement, Boolean>() {
@Override
public Boolean invoke(LookupElement lookupElement) {
Object lookupObject = lookupElement.getObject();
if (!(lookupObject instanceof DeclarationLookupObject)) return false;
PsiElement psiElement = ((DeclarationLookupObject) lookupObject).getPsiElement();
if (!(psiElement instanceof KtClassOrObject)) return false;
return KotlinRefactoringUtilKt.canRefactor(psiElement);
}
}
);
}
targetClassChooser.getChildComponent().getDocument().addDocumentListener(
new DocumentAdapter() {
@Override
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2009 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.ui
import com.intellij.openapi.editor.Document
import com.intellij.openapi.ui.ComponentWithBrowseButton
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.ui.EditorComboBox
import com.intellij.ui.RecentsManager
import com.intellij.ui.TextAccessor
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
import java.awt.event.ActionListener
class KotlinTypeReferenceEditorComboWithBrowseButton(
browseActionListener: ActionListener,
text: String?,
contextElement: PsiElement,
recentsKey: String
) : ComponentWithBrowseButton<EditorComboBox>(
EditorComboBox(createDocument(text, contextElement), contextElement.project, KotlinFileType.INSTANCE),
browseActionListener
), TextAccessor {
companion object {
private fun createDocument(text: String?, contextElement: PsiElement): Document? {
val codeFragment = KtPsiFactory(contextElement).createTypeCodeFragment(text ?: "", contextElement)
return PsiDocumentManager.getInstance(contextElement.project).getDocument(codeFragment)
}
}
private val project = contextElement.project
init {
RecentsManager.getInstance(contextElement.project).getRecentEntries(recentsKey)?.let {
childComponent.setHistory(ArrayUtil.toStringArray(it))
}
if (text != null) {
if (text.isNotEmpty()) {
childComponent.prependItem(text)
}
else {
childComponent.selectedItem = null
}
}
}
override fun getText() = childComponent.text.trim { it <= ' ' }
override fun setText(text: String) {
childComponent.text = text
}
val codeFragment: KtTypeCodeFragment?
get() = PsiDocumentManager.getInstance(project).getPsiFile(childComponent.document) as? KtTypeCodeFragment
}