Files
openide/python/src/com/jetbrains/python/PythonUiServiceImpl.java
T
Vladimir Krivosheevandintellij-monorepo-bot 2f5a9ccf5d cleanup - sort modifiers
GitOrigin-RevId: 45238c73a258f64d6ea1c5b802f34ed17b1419a1
2024-03-21 13:55:09 +00:00

316 lines
12 KiB
Java

// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.EditSourceUtil;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.messages.MessagesService;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.NlsContexts.*;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.usages.*;
import com.intellij.usages.rules.PsiElementUsage;
import com.intellij.util.Consumer;
import com.jetbrains.python.codeInsight.intentions.PyAnnotateTypesIntention;
import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix;
import com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix;
import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.ui.PyUiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public final class PythonUiServiceImpl extends PythonUiService {
@Override
public void showBalloonInfo(Project project, @PopupContent String message) {
PyUiUtil.showBalloon(project, message, MessageType.INFO);
}
@Override
public void showBalloonWarning(Project project, @PopupContent String message) {
PyUiUtil.showBalloon(project, message, MessageType.WARNING);
}
@Override
public void showBalloonError(Project project, @PopupContent String message) {
PyUiUtil.showBalloon(project, message, MessageType.ERROR);
}
@Override
public FileEditor getSelectedEditor(@NotNull Project project, VirtualFile virtualFile) {
return FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
}
@Override
public Editor openTextEditor(@NotNull Project project, PsiElement anchor) {
PsiFile file = InjectedLanguageManager.getInstance(project).getTopLevelFile(anchor);
return openTextEditor(project, file.getVirtualFile());
}
@Override
public Editor openTextEditor(@NotNull Project project, @NotNull VirtualFile virtualFile) {
return FileEditorManager.getInstance(project).openTextEditor(
new OpenFileDescriptor(project, virtualFile), true);
}
@Override
public Editor openTextEditor(@NotNull Project project, @NotNull VirtualFile virtualFile, int offset) {
return FileEditorManager.getInstance(project).openTextEditor(
new OpenFileDescriptor(project, virtualFile, offset), true);
}
@Override
public boolean showYesDialog(Project project, String message, String title) {
return MessageDialogBuilder.yesNo(title, message).ask(project);
}
@Override
public @Nullable LocalQuickFix createPyRenameElementQuickFix(@NotNull PsiElement element) {
return new PyRenameElementQuickFix(element);
}
//TODO: find a better place or, even better, port it to analysis module
@Override
public void runRenameProcessor(Project project,
PsiElement element,
String newName,
boolean searchInComments,
boolean searchTextOccurrences) {
new RenameProcessor(project, element, newName, searchInComments, searchTextOccurrences).run();
}
@Override
public LocalQuickFix createPyChangeSignatureQuickFixForMismatchingMethods(PyFunction function, PyFunction method) {
return PyChangeSignatureQuickFix.forMismatchingMethods(function, method);
}
@Override
public LocalQuickFix createPyChangeSignatureQuickFixForMismatchedCall(PyCallExpression.@NotNull PyArgumentsMapping mapping) {
return PyChangeSignatureQuickFix.forMismatchedCall(mapping);
}
@Override
public LocalQuickFix createPyImplementMethodsQuickFix(PyClass aClass, List<PyFunction> toImplement) {
return new PyImplementMethodsQuickFix(aClass, toImplement);
}
@Override
public void annotateTypesIntention(Editor editor, PyFunction function) {
PyAnnotateTypesIntention.annotateTypes(editor, function);
}
@Override
public void showPopup(Project project, List<String> items, String title, Consumer<String> callback) {
DataManager.getInstance().getDataContextFromFocus().doWhenDone((Consumer<DataContext>)dataContext ->
JBPopupFactory.getInstance().createPopupChooserBuilder(items)
.setTitle(title)
.setItemChosenCallback(callback)
.setNamerForFiltering(o -> o)
.createPopup()
.showInBestPositionFor(dataContext));
}
/**
* Shows a panel with name redefinition conflicts, if needed.
*
* @param conflicts what {@link #findDefinitions} would return
* @param obscured name or its topmost qualifier that is obscured, used at top of pane.
* @param name full name (maybe qualified) to show as obscured and display as qualifier in "would be" chunks.
* @return true iff conflicts is not empty and the panel is shown.
*/
@Override
public boolean showConflicts(Project project,
List<? extends Pair<PsiElement, PsiElement>> conflicts,
String obscured,
@Nullable String name) {
if (conflicts.size() > 0) {
Usage[] usages = new Usage[conflicts.size()];
int i = 0;
for (Pair<PsiElement, PsiElement> pair : conflicts) {
usages[i] = new NameUsage(pair.getFirst(), pair.getSecond(), name != null ? name : obscured, name != null);
i += 1;
}
UsageViewPresentation prsnt = new UsageViewPresentation();
prsnt.setTabText(PyBundle.message("CONFLICT.name.obscured.by.local.definitions", obscured));
prsnt.setCodeUsagesString(PyBundle.message("CONFLICT.name.obscured.cannot.convert", obscured));
prsnt.setUsagesString(PyBundle.message("CONFLICT.occurrence.pl"));
UsageViewManager.getInstance(project).showUsages(UsageTarget.EMPTY_ARRAY, usages, prsnt);
return true;
}
return false;
}
/**
* Simplistic usage object for demonstration of name clashes, etc.
*/
public static class NameUsage implements PsiElementUsage {
private final PsiElement myElement;
private final PsiElement myCulprit;
private static final TextAttributes SLANTED;
private final String myName;
private final boolean myIsPrefix;
static {
SLANTED = TextAttributes.ERASE_MARKER.clone();
SLANTED.setFontType(Font.ITALIC);
}
/**
* Creates a conflict search panel usage.
*
* @param element where conflict happens.
* @param culprit where name is redefined; usages with the same culprit are grouped.
* @param name redefinition of it is what the conflict is about.
* @param prefix if true, show name as a prefix to element's name in "would be" part.
*/
public NameUsage(PsiElement element, PsiElement culprit, String name, boolean prefix) {
myElement = element;
myCulprit = culprit;
myName = name;
myIsPrefix = prefix;
}
@Override
public FileEditorLocation getLocation() {
return null;
}
@Override
public @NotNull UsagePresentation getPresentation() {
return new UsagePresentation() {
@Override
public @Nullable Icon getIcon() {
PyPsiUtils.assertValid(myElement);
return myElement.isValid() ? myElement.getIcon(0) : null;
}
@Override
public TextChunk @NotNull [] getText() {
PyPsiUtils.assertValid(myElement);
if (myElement.isValid()) {
PsiFile file = myElement.getContainingFile();
String line_id = "...";
final Document document = file.getViewProvider().getDocument();
if (document != null) {
line_id = String.valueOf(document.getLineNumber(myElement.getTextOffset()));
}
TextChunk[] chunks = new TextChunk[3];
chunks[0] = new TextChunk(SLANTED, "(" + line_id + ") ");
chunks[1] = new TextChunk(TextAttributes.ERASE_MARKER, myElement.getText());
StringBuilder sb = new StringBuilder(" would become ").append(myName);
if (myIsPrefix) sb.append(".").append(myElement.getText());
chunks[2] = new TextChunk(SLANTED, sb.toString());
return chunks;
}
else {
return new TextChunk[]{new TextChunk(SLANTED, "?")};
}
}
@Override
public @NotNull String getPlainText() {
return myElement.getText();
}
@Override
public String getTooltipText() {
return myElement.getText();
}
};
}
@Override
public boolean isValid() {
return true;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public void selectInEditor() { }
@Override
public void highlightInEditor() { }
@Override
public void navigate(boolean requestFocus) {
Navigatable descr = EditSourceUtil.getDescriptor(myElement);
if (descr != null) descr.navigate(requestFocus);
}
@Override
public boolean canNavigate() {
return EditSourceUtil.canNavigate(myElement);
}
@Override
public PsiElement getElement() {
return myCulprit;
}
@Override
public boolean isNonCodeUsage() {
return false;
}
}
@Override
public @Nullable String showInputDialog(@Nullable Project project,
@DialogMessage String message,
@DialogTitle String title,
@Nullable String initialValue,
@Nullable InputValidator validator) {
return Messages.showInputDialog(project, message,
title, Messages.getQuestionIcon(), "", validator);
}
@Override
public void showErrorHint(Editor editor, @NotNull @HintText String message) {
HintManager.getInstance().showErrorHint(editor, message);
}
@Override
public int showChooseDialog(@Nullable Project project,
@Nullable Component parentComponent,
@DialogMessage String message,
@DialogTitle String title,
String @ListItem [] values,
@ListItem String initialValue,
@Nullable Icon icon) {
return MessagesService.getInstance().showChooseDialog(project, parentComponent, message, title, values, initialValue, icon);
}
}