delete the UI for editing Kotlin signatures in Java files

This commit is contained in:
Dmitry Jemerov
2015-08-20 21:12:28 +02:00
parent 482406b0d9
commit 301e79033d
9 changed files with 0 additions and 1064 deletions
-15
View File
@@ -27,9 +27,6 @@
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.ui.NonConfiguredKotlinProjectComponent</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.ktSignature.KotlinSignatureInJavaMarkerUpdater</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.js.KotlinJavaScriptLibraryManager</implementation-class>
</component>
@@ -84,11 +81,6 @@
<add-to-group group-id="CodeMenu" anchor="last"/>
</action>
<group id="EditorGutterKotlinPopupMenu">
<action id="ShowKotlinSignatures" class="org.jetbrains.kotlin.idea.ktSignature.ShowKotlinSignaturesAction"/>
<add-to-group group-id="EditorGutterPopupMenu" anchor="last"/>
</group>
<group id="KotlinToolsGroup" popup="true" text="Kotlin" icon="/org/jetbrains/kotlin/idea/icons/kotlin13.png"
class="org.jetbrains.kotlin.idea.actions.KotlinActionGroup">
<add-to-group group-id="ToolsMenu" anchor="last"/>
@@ -407,8 +399,6 @@
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinRunConfigurationProducer"/>
<codeInsight.lineMarkerProvider language="jet" implementationClass="org.jetbrains.kotlin.idea.highlighter.markers.KotlinLineMarkerProvider"/>
<codeInsight.lineMarkerProvider language="jet" implementationClass="org.jetbrains.kotlin.idea.highlighter.KotlinRecursiveCallLineMarkerProvider"/>
<codeInsight.lineMarkerProvider language="JAVA"
implementationClass="org.jetbrains.kotlin.idea.ktSignature.KotlinSignatureInJavaMarkerProvider"/>
<iconProvider implementation="org.jetbrains.kotlin.idea.JetIconProvider"/>
<itemPresentationProvider implementationClass="org.jetbrains.kotlin.idea.presentation.JetFunctionPresenter"
forClass="org.jetbrains.kotlin.psi.JetFunction"/>
@@ -576,11 +566,6 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.ktSignature.KotlinSignatureAnnotationIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToAssignmentIntention</className>
<category>Kotlin</category>
@@ -1,71 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiModifierListOwner;
import org.jetbrains.annotations.NotNull;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KOTLIN_SIGNATURE;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.OLD_KOTLIN_SIGNATURE;
public class DeleteSignatureAction extends AnAction {
private final PsiModifierListOwner annotationOwner;
public DeleteSignatureAction(@NotNull PsiModifierListOwner elementInEditor) {
super("Delete");
this.annotationOwner = KotlinSignatureUtil.getAnalyzableAnnotationOwner(elementInEditor);
getTemplatePresentation().setVisible(annotationOwner != null && KotlinSignatureUtil.isAnnotationEditable(elementInEditor));
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getProject();
assert project != null;
final PsiAnnotation annotation = KotlinSignatureUtil.findKotlinSignatureAnnotation(annotationOwner);
assert annotation != null;
if (annotation.getContainingFile() != annotationOwner.getContainingFile()) {
// external annotation
new WriteCommandAction(project) {
@Override
protected void run(@NotNull Result result) throws Throwable {
ExternalAnnotationsManager.getInstance(project).deannotate(annotationOwner, KOTLIN_SIGNATURE.asString());
ExternalAnnotationsManager.getInstance(project).deannotate(annotationOwner, OLD_KOTLIN_SIGNATURE.asString());
}
}.execute();
}
else {
new WriteCommandAction(project) {
@Override
protected void run(@NotNull Result result) throws Throwable {
annotation.delete();
}
}.execute();
}
KotlinSignatureUtil.refreshMarkers(project);
}
}
@@ -1,84 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
public class EditSignatureAction extends AnAction {
private final PsiModifierListOwner elementInEditor;
public EditSignatureAction(@NotNull PsiModifierListOwner elementInEditor) {
super(KotlinSignatureUtil.isAnnotationEditable(elementInEditor) ? "Edit" : "View");
this.elementInEditor = elementInEditor;
}
@Override
public void actionPerformed(AnActionEvent e) {
actionPerformed(e.getDataContext(), null);
}
public void actionPerformed(@NotNull DataContext dataContext, @Nullable Point point) {
Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
assert editor != null;
invokeEditSignature(elementInEditor, editor, point);
}
static void invokeEditSignature(@NotNull PsiElement elementInEditor, @NotNull Editor editor, @Nullable Point point) {
PsiAnnotation annotation = KotlinSignatureUtil.findKotlinSignatureAnnotation(elementInEditor);
assert annotation != null;
if (annotation.getContainingFile() == elementInEditor.getContainingFile()) {
// not external, go to
for (PsiNameValuePair pair : annotation.getParameterList().getAttributes()) {
if (pair.getName() == null || "value".equals(pair.getName())) {
PsiAnnotationMemberValue value = pair.getValue();
if (value != null) {
VirtualFile virtualFile = value.getContainingFile().getVirtualFile();
assert virtualFile != null;
PsiElement firstChild = value.getFirstChild();
if (firstChild != null && firstChild.getNode().getElementType() == JavaTokenType.STRING_LITERAL) {
new OpenFileDescriptor(value.getProject(), virtualFile, value.getTextOffset() + 1).navigate(true);
}
else {
NavigationUtil.activateFileWithPsiElement(value);
}
}
}
}
}
else {
PsiModifierListOwner annotationOwner = KotlinSignatureUtil.getAnalyzableAnnotationOwner(elementInEditor);
boolean editable = KotlinSignatureUtil.isAnnotationEditable(elementInEditor);
//noinspection ConstantConditions
EditSignatureBalloon balloon = new EditSignatureBalloon(annotationOwner, KotlinSignatureUtil.getKotlinSignature(annotation),
editable, annotation.getQualifiedName());
balloon.show(point, editor, elementInEditor);
}
}
}
@@ -1,246 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupAdapter;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.awt.RelativePoint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.JetFileType;
import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
class EditSignatureBalloon implements Disposable {
private final Editor editor;
private final PsiModifierListOwner annotatedElement;
private final Project project;
private final String previousSignature;
private final MyPanel panel;
private final Balloon balloon;
private final boolean editable;
private final String kotlinSignatureAnnotationFqName;
public EditSignatureBalloon(
@NotNull PsiModifierListOwner annotatedElement,
@NotNull String previousSignature,
boolean editable,
@NotNull String kotlinSignatureAnnotationFqName
) {
this.annotatedElement = annotatedElement;
this.previousSignature = previousSignature;
this.editable = editable;
this.kotlinSignatureAnnotationFqName = kotlinSignatureAnnotationFqName;
project = annotatedElement.getProject();
editor = createEditor();
panel = new MyPanel();
balloon = createBalloon();
}
private Balloon createBalloon() {
Balloon balloon = JBPopupFactory.getInstance().createDialogBalloonBuilder(panel, "Kotlin signature")
.setHideOnClickOutside(true)
.setHideOnKeyOutside(true)
.setBlockClicksThroughBalloon(true).createBalloon();
balloon.addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
Disposer.dispose(EditSignatureBalloon.this);
}
});
return balloon;
}
private Editor createEditor() {
EditorFactory editorFactory = EditorFactory.getInstance();
assert editorFactory != null;
LightVirtualFile virtualFile = new LightVirtualFile("signature.kt", JetFileType.INSTANCE, previousSignature);
virtualFile.setWritable(editable);
final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
assert document != null;
document.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent event) {
PsiDocumentManager psiDocManager = PsiDocumentManager.getInstance(project);
final PsiFile psiFile = psiDocManager.getPsiFile(document);
assert psiFile instanceof JetFile;
psiDocManager.performForCommittedDocument(document, new Runnable() {
@Override
public void run() {
panel.setSaveButtonEnabled(hasErrors((JetFile) psiFile));
}
});
psiDocManager.commitDocument(document);
}
}, this);
Editor editor = editorFactory.createEditor(document, project, JetFileType.INSTANCE, !editable);
EditorSettings settings = editor.getSettings();
settings.setVirtualSpace(false);
settings.setLineMarkerAreaShown(false);
settings.setFoldingOutlineShown(false);
settings.setRightMarginShown(false);
settings.setAdditionalPageAtBottom(false);
settings.setAdditionalLinesCount(2);
settings.setAdditionalColumnsCount(12);
assert editor instanceof EditorEx;
((EditorEx)editor).setEmbeddedIntoDialogWrapper(true);
editor.getColorsScheme().setColor(EditorColors.CARET_ROW_COLOR, editor.getColorsScheme().getDefaultBackground());
return editor;
}
private static int getLineY(@NotNull Editor editor, @NotNull PsiElement psiElementInEditor) {
LogicalPosition logicalPosition = editor.offsetToLogicalPosition(psiElementInEditor.getTextOffset());
return editor.logicalPositionToXY(logicalPosition).y;
}
public void show(@Nullable Point point, @NotNull final Editor mainEditor, @NotNull PsiElement psiElementInEditor) {
int lineY = getLineY(mainEditor, psiElementInEditor);
EditorGutterComponentEx gutter = (EditorGutterComponentEx) mainEditor.getGutter();
Point adjustedPoint;
if (point == null) {
adjustedPoint = new Point(gutter.getIconsAreaWidth() + gutter.getLineMarkerAreaOffset(), lineY);
}
else {
adjustedPoint = new Point(point.x, Math.min(lineY, point.y));
}
balloon.show(new RelativePoint(gutter, adjustedPoint), Balloon.Position.above);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
IdeFocusManager.getInstance(mainEditor.getProject()).requestFocus(editor.getContentComponent(), false);
}
});
}
@Override
public void dispose() {
EditorFactory editorFactory = EditorFactory.getInstance();
assert editorFactory != null;
editorFactory.releaseEditor(editor);
}
private void saveAndHide() {
balloon.hide();
final String newSignature = editor.getDocument().getText();
if (previousSignature.equals(newSignature)) {
return;
}
new WriteCommandAction(project) {
@Override
protected void run(@NotNull Result result) throws Throwable {
ExternalAnnotationsManager.getInstance(project).editExternalAnnotation(
annotatedElement, kotlinSignatureAnnotationFqName,
KotlinSignatureUtil.signatureToNameValuePairs(project, newSignature)
);
}
}.execute();
}
private static boolean hasErrors(@NotNull JetFile file) {
return AnalyzingUtils.getSyntaxErrorRanges(file).isEmpty();
}
private class MyPanel extends JPanel {
private final JButton saveButton;
MyPanel() {
super(new BorderLayout());
add(editor.getComponent(), BorderLayout.CENTER);
if (editable) {
JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.RIGHT));
saveButton = new JButton("Save") {
@Override
public boolean isDefaultButton() {
return true;
}
};
toolbar.add(saveButton);
add(toolbar, BorderLayout.SOUTH);
ActionListener saveAndHideListener = new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
saveAndHide();
}
};
saveButton.addActionListener(saveAndHideListener);
registerKeyboardAction(saveAndHideListener, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
}
else {
saveButton = null;
}
registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
balloon.hide();
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
}
void setSaveButtonEnabled(boolean enabled) {
if (saveButton != null) {
saveButton.setEnabled(enabled);
saveButton.setToolTipText(enabled ? null : "Please fix errors in signature to save it.");
}
}
}
}
@@ -1,225 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.ExternalAnnotationsListener;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.messages.MessageBusConnection;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.idea.JetBundle;
import org.jetbrains.kotlin.idea.JetIcons;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl;
import org.jetbrains.kotlin.load.java.structure.impl.JavaFieldImpl;
import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier;
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions;
import org.jetbrains.kotlin.renderer.NameShortness;
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver;
import javax.swing.*;
import java.util.Collections;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KOTLIN_SIGNATURE;
public class KotlinSignatureAnnotationIntention extends BaseIntentionAction implements Iconable {
private static final DescriptorRenderer RENDERER = DescriptorRenderer.Companion.withOptions(
new Function1<DescriptorRendererOptions, Unit>() {
@Override
public Unit invoke(DescriptorRendererOptions options) {
options.setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES);
options.setNameShortness(NameShortness.SHORT);
options.setModifiers(Collections.<DescriptorRendererModifier>emptySet());
options.setWithDefinedIn(false);
return Unit.INSTANCE$;
}
}
);
@NotNull
@Override
public String getFamilyName() {
return JetBundle.message("add.kotlin.signature.action.family.name");
}
@Override
public Icon getIcon(@IconFlags int flags) {
return JetIcons.SMALL_LOGO;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
PsiMember memberUnderCaret = findMemberUnderCaret(file, editor);
if (memberUnderCaret == null) {
return false;
}
PsiModifierListOwner annotationOwner = KotlinSignatureUtil.getAnalyzableAnnotationOwner(memberUnderCaret);
if (annotationOwner == null) {
return false;
}
if (!PsiUtil.isLanguageLevel5OrHigher(annotationOwner)) return false;
if (KotlinSignatureUtil.findKotlinSignatureAnnotation(annotationOwner) != null) {
if (KotlinSignatureUtil.isAnnotationEditable(annotationOwner)) {
setText(JetBundle.message("edit.kotlin.signature.action.text"));
}
else {
setText(JetBundle.message("view.kotlin.signature.action.text"));
}
}
else {
setText(JetBundle.message("add.kotlin.signature.action.text"));
}
return true;
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
final PsiMember annotatedElement = findMemberUnderCaret(file, editor);
assert annotatedElement != null;
if (KotlinSignatureUtil.findKotlinSignatureAnnotation(annotatedElement) != null) {
EditSignatureAction.invokeEditSignature(annotatedElement, editor, null);
return;
}
String signature = getDefaultSignature(project, annotatedElement);
final MessageBusConnection busConnection = project.getMessageBus().connect();
busConnection.subscribe(ExternalAnnotationsManager.TOPIC, new ExternalAnnotationsListener.Adapter() {
@Override
public void afterExternalAnnotationChanging(@NotNull PsiModifierListOwner owner, @NotNull String annotationFQName, boolean successful) {
busConnection.disconnect();
if (successful && owner == annotatedElement && KOTLIN_SIGNATURE.asString().equals(annotationFQName)) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
KotlinSignatureUtil.refreshMarkers(project);
EditSignatureAction.invokeEditSignature(annotatedElement, editor, null);
}
}, ModalityState.NON_MODAL);
}
}
});
AddAnnotationFix addAnnotationFix = new AddAnnotationFix(
KOTLIN_SIGNATURE.asString(), annotatedElement, KotlinSignatureUtil
.signatureToNameValuePairs(annotatedElement.getProject(), signature));
addAnnotationFix.invoke(project, editor, file);
}
private static String renderMember(PsiMember member) {
PsiClass containingClass = member.getContainingClass();
assert containingClass != null;
String qualifiedName = containingClass.getQualifiedName();
assert qualifiedName != null;
return member.getName() + " in " + qualifiedName;
}
@NotNull
private static String getDefaultSignature(@NotNull Project project, @NotNull PsiMember element) {
PsiMember analyzableAnnotationOwner = KotlinSignatureUtil.getAnalyzableAnnotationOwner(element);
assert analyzableAnnotationOwner != null;
JavaDescriptorResolver javaDescriptorResolver = ResolvePackage.getJavaDescriptorResolver(analyzableAnnotationOwner);
if (analyzableAnnotationOwner instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod) analyzableAnnotationOwner;
if (psiMethod.isConstructor()) {
ConstructorDescriptor constructorDescriptor =
ResolvePackage.resolveConstructor(javaDescriptorResolver, new JavaConstructorImpl(psiMethod));
assert constructorDescriptor != null: "Couldn't find constructor descriptor for " + renderMember(psiMethod);
return getDefaultConstructorAnnotation(constructorDescriptor);
}
else {
FunctionDescriptor functionDescriptor = ResolvePackage.resolveMethod(javaDescriptorResolver, new JavaMethodImpl(psiMethod));
assert functionDescriptor != null: "Couldn't find function descriptor for " + renderMember(psiMethod);
return RENDERER.render(functionDescriptor);
}
}
if (analyzableAnnotationOwner instanceof PsiField) {
VariableDescriptor variableDescriptor =
ResolvePackage.resolveField(javaDescriptorResolver, new JavaFieldImpl((PsiField) analyzableAnnotationOwner));
assert variableDescriptor != null : "Couldn't find variable descriptor for field " + renderMember(analyzableAnnotationOwner);
return RENDERER.render(variableDescriptor);
}
throw new IllegalStateException("PsiMethod or PsiField are expected");
}
private static String getDefaultConstructorAnnotation(ConstructorDescriptor constructorDescriptor) {
return String.format("fun %s%s", constructorDescriptor.getContainingDeclaration().getName(), RENDERER.renderFunctionParameters(constructorDescriptor));
}
@Nullable
private static PsiMember findMemberUnderCaret(@NotNull PsiElement file, Editor editor) {
int offset = editor.getCaretModel().getOffset();
PsiMember methodMember = findMethod(file, offset);
if (methodMember != null) {
return methodMember;
}
return findField(file, offset);
}
@Nullable
private static PsiMethod findMethod(@NotNull PsiElement file, int offset) {
PsiElement element = file.findElementAt(offset);
PsiMethod res = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (res == null) return null;
//Not available in method's body
PsiCodeBlock body = res.getBody();
if (body != null) {
TextRange bodyRange = body.getTextRange();
if (bodyRange != null && bodyRange.getStartOffset() <= offset) {
return null;
}
}
return res;
}
@Nullable
private static PsiField findField(@NotNull PsiElement file, int offset) {
return PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiField.class);
}
}
@@ -1,197 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.Function;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.idea.JetIcons;
import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil;
import org.jetbrains.kotlin.load.java.JavaBindingContext;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.List;
public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
private static final String SHOW_MARKERS_PROPERTY = "kotlin.signature.markers.enabled";
private static final Logger LOG = Logger.getInstance(KotlinSignatureInJavaMarkerProvider.class);
private static final GutterIconNavigationHandler<PsiModifierListOwner> NAVIGATION_HANDLER = new GutterIconNavigationHandler<PsiModifierListOwner>() {
@Override
public void navigate(MouseEvent e, PsiModifierListOwner element) {
if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED)) {
new EditSignatureAction(element).actionPerformed(DataManager.getInstance().getDataContext(e.getComponent()), e.getPoint());
}
}
};
@Override
@Nullable
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
return null;
}
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
if (elements.isEmpty()) {
return;
}
PsiElement firstElement = elements.get(0);
Project project = firstElement.getProject();
if (!isMarkersEnabled(project)) {
return;
}
if (!ProjectStructureUtil.hasJvmKotlinModules(project)) {
return;
}
Module module = ModuleUtilCore.findModuleForPsiElement(firstElement);
if (module != null && !ProjectStructureUtil.isUsedInKotlinJavaModule(module)) {
return;
}
markElements(elements, result, firstElement.getContainingFile());
}
private static void markElements(
@NotNull List<PsiElement> elements,
@NotNull Collection<LineMarkerInfo> result,
@NotNull PsiFile psiFile
) {
try {
for (PsiElement element : elements) {
markElement(element, result);
}
}
catch (AssertionError error) {
LOG.warn("Exception while collecting KotlinSignature markers:\n" + ExceptionUtil.getThrowableText(error));
}
}
private static void markElement(@NotNull PsiElement element, @NotNull Collection<LineMarkerInfo> result) {
Project project = element.getProject();
PsiModifierListOwner annotationOwner = KotlinSignatureUtil.getAnalyzableAnnotationOwner(element);
if (!(annotationOwner instanceof PsiMember)) {
return;
}
DeclarationDescriptor memberDescriptor = ResolvePackage.getJavaMemberDescriptor((PsiMember) annotationOwner);
if (memberDescriptor == null) return;
BindingContext bindingContext = KotlinCacheService.getInstance(project).getGlobalFacade(JvmPlatform.INSTANCE$)
.getFrontendService(annotationOwner, BindingTrace.class).getBindingContext();
List<String> errors = bindingContext.get(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, memberDescriptor);
boolean hasSignatureAnnotation = KotlinSignatureUtil.findKotlinSignatureAnnotation(annotationOwner) != null;
if (errors != null || hasSignatureAnnotation) {
result.add(new MyLineMarkerInfo((PsiModifierListOwner) element, errors, hasSignatureAnnotation));
}
}
public static boolean isMarkersEnabled(@NotNull Project project) {
return PropertiesComponent.getInstance(project).getBoolean(SHOW_MARKERS_PROPERTY, true);
}
public static void setMarkersEnabled(@NotNull Project project, boolean value) {
PropertiesComponent.getInstance(project).setValue(SHOW_MARKERS_PROPERTY, Boolean.toString(value));
KotlinSignatureUtil.refreshMarkers(project);
}
private static class MyLineMarkerInfo extends LineMarkerInfo<PsiModifierListOwner> {
public MyLineMarkerInfo(PsiModifierListOwner element, @Nullable List<String> errors, boolean hasAnnotation) {
super(element, element.getTextOffset(), errors != null ? AllIcons.Ide.Error : JetIcons.SMALL_LOGO, Pass.UPDATE_OVERRIDEN_MARKERS,
new TooltipProvider(errors), hasAnnotation ? NAVIGATION_HANDLER : null);
}
@Nullable
@Override
public GutterIconRenderer createGutterRenderer() {
return new LineMarkerGutterIconRenderer<PsiModifierListOwner>(this) {
@Nullable
@Override
public ActionGroup getPopupMenuActions() {
if (getNavigationHandler() == null) {
return null;
}
PsiModifierListOwner element = getElement();
assert element != null;
return new DefaultActionGroup(new EditSignatureAction(element), new DeleteSignatureAction(element));
}
};
}
}
private static class TooltipProvider implements Function<PsiElement, String> {
private final @Nullable List<String> errors;
private TooltipProvider(@Nullable List<String> errors) {
this.errors = errors;
}
@Nullable
@Override
public String fun(PsiElement element) {
PsiAnnotation annotation = KotlinSignatureUtil.findKotlinSignatureAnnotation(element);
if (annotation == null) return errorsString();
String signature = KotlinSignatureUtil.getKotlinSignature(annotation);
String text = "Alternative Kotlin signature is available for this method:\n" + StringUtil.escapeXml(signature);
if (errors == null) {
return text;
}
return text + "\nIt has the following " + StringUtil.pluralize("error", errors.size()) + ":\n" + errorsString();
}
@NotNull
private String errorsString() {
assert errors != null;
return StringUtil.escapeXml(StringUtil.join(errors, "\n"));
}
}
}
@@ -1,38 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.ExternalAnnotationsListener;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.project.Project;
public class KotlinSignatureInJavaMarkerUpdater extends AbstractProjectComponent {
protected KotlinSignatureInJavaMarkerUpdater(Project project) {
super(project);
}
@Override
public void initComponent() {
myProject.getMessageBus().connect().subscribe(ExternalAnnotationsManager.TOPIC, new ExternalAnnotationsListener.Adapter() {
@Override
public void externalAnnotationsChangedExternally() {
KotlinSignatureUtil.refreshMarkers(myProject);
}
});
}
}
@@ -1,135 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsElementImpl;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.load.java.components.PsiBasedExternalAnnotationResolver;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KOTLIN_SIGNATURE;
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.OLD_KOTLIN_SIGNATURE;
class KotlinSignatureUtil {
private KotlinSignatureUtil() {
}
/* For element from library sources or decompiled library sources return corresponding cls element */
@Nullable
static PsiMember getAnalyzableAnnotationOwner(@NotNull PsiElement element) {
if (!(element instanceof PsiField || element instanceof PsiMethod) || element instanceof PsiEnumConstant) {
return null;
}
PsiMember member = (PsiMember) element;
if (member.hasModifierProperty(PsiModifier.PRIVATE)) {
return null;
}
PsiClass containingClass = member.getContainingClass();
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
return null;
}
PsiMember annotationOwner = element.getOriginalElement() instanceof PsiMember
? (PsiMember) element.getOriginalElement()
: (PsiMember) element;
if (!annotationOwner.isPhysical()) {
// this is fake PsiFile which is mirror for ClsFile without sources
PsiCompiledElement compiledElement = element.getUserData(ClsElementImpl.COMPILED_ELEMENT);
if (compiledElement instanceof PsiMember) {
return (PsiMember) compiledElement;
}
}
return annotationOwner;
}
@NotNull
static String getKotlinSignature(@NotNull PsiAnnotation kotlinSignatureAnnotation) {
PsiNameValuePair pair = kotlinSignatureAnnotation.getParameterList().getAttributes()[0];
PsiAnnotationMemberValue value = pair.getValue();
if (value == null) {
return "null";
}
else if (value instanceof PsiLiteralExpression) {
Object valueObject = ((PsiLiteralExpression) value).getValue();
return valueObject == null ? "null" : StringUtil.unescapeStringCharacters(valueObject.toString());
}
else {
return value.getText();
}
}
@NotNull
static PsiNameValuePair[] signatureToNameValuePairs(@NotNull Project project, @NotNull String signature) {
return JavaPsiFacade.getElementFactory(project).createAnnotationFromText(
"@" + KOTLIN_SIGNATURE + "(value=\"" + StringUtil.escapeStringCharacters(signature) + "\")", null)
.getParameterList().getAttributes();
}
@Nullable
static PsiAnnotation findKotlinSignatureAnnotation(@NotNull PsiElement element) {
if (!(element instanceof PsiModifierListOwner)) return null;
PsiModifierListOwner annotationOwner = getAnalyzableAnnotationOwner(element);
if (annotationOwner == null) {
return null;
}
PsiModifierList list = annotationOwner.getModifierList();
PsiAnnotation annotation = list == null ? null : list.findAnnotation(KOTLIN_SIGNATURE.asString());
if (annotation == null) {
annotation = PsiBasedExternalAnnotationResolver.findExternalAnnotation(annotationOwner, KOTLIN_SIGNATURE);
if (annotation == null) {
annotation = list == null ? null : list.findAnnotation(OLD_KOTLIN_SIGNATURE.asString());
if (annotation == null) {
annotation = PsiBasedExternalAnnotationResolver.findExternalAnnotation(annotationOwner, OLD_KOTLIN_SIGNATURE);
}
}
}
if (annotation == null) return null;
if (annotation.getParameterList().getAttributes().length == 0) return null;
return annotation;
}
static void refreshMarkers(@NotNull Project project) {
DaemonCodeAnalyzer.getInstance(project).restart();
}
static boolean isAnnotationEditable(@NotNull PsiElement element) {
PsiModifierListOwner annotationOwner = getAnalyzableAnnotationOwner(element);
if (annotationOwner == null) {
return false;
}
PsiAnnotation annotation = findKotlinSignatureAnnotation(element);
assert annotation != null : "Annotation not found for " + element.getText();
if (annotation.getContainingFile() == annotationOwner.getContainingFile()) {
return annotation.isWritable();
} else {
ExternalAnnotationsManager annotationsManager = ExternalAnnotationsManager.getInstance(element.getProject());
//noinspection ConstantConditions
return annotationsManager.isExternalAnnotationWritable(annotationOwner, annotation.getQualifiedName());
}
}
}
@@ -1,53 +0,0 @@
/*
* 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.ktSignature;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
public class ShowKotlinSignaturesAction extends ToggleAction {
public ShowKotlinSignaturesAction() {
super("Show Kotlin Signatures");
}
@Override
public boolean isSelected(AnActionEvent e) {
Project project = e.getProject();
return project != null && KotlinSignatureInJavaMarkerProvider.isMarkersEnabled(project);
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setVisible(false);
PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
if (psiFile != null && psiFile.getLanguage() == JavaLanguage.INSTANCE) {
e.getPresentation().setVisible(true);
}
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
Project project = e.getProject();
assert project != null;
KotlinSignatureInJavaMarkerProvider.setMarkersEnabled(project, state);
}
}