IDEA-186962 Allow user defined Async annotations

This commit is contained in:
Egor Ushakov
2018-08-01 19:40:07 +03:00
parent 8245f7afb3
commit 41f02ffc3a
10 changed files with 419 additions and 248 deletions
@@ -13,6 +13,8 @@ import com.intellij.debugger.settings.ThreadsViewSettings;
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.util.registry.Registry;
@@ -51,7 +53,9 @@ public class StackFrameItem {
public static final XDebuggerTreeNodeHyperlink CAPTURE_SETTINGS_OPENER = new XDebuggerTreeNodeHyperlink(" settings") {
@Override
public void onClick(MouseEvent event) {
ShowSettingsUtil.getInstance().showSettingsDialog(null, CaptureConfigurable.class);
ShowSettingsUtil.getInstance().showSettingsDialog(
CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(event.getComponent())),
CaptureConfigurable.class);
event.consume();
}
};
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.debugger.settings.CaptureConfigurable">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="95385" class="com.intellij.ui.components.JBCheckBox" binding="myDebuggerAgent">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/DebuggerBundle" key="label.capture.configurable.debugger.agent"/>
</properties>
</component>
<hspacer id="728e7">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="a8e4" class="javax.swing.JButton" binding="myConfigureAnnotationsButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/DebuggerBundle" key="label.capture.configurable.annotations.configure"/>
</properties>
</component>
<grid id="5f9b3" binding="myCapturePanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="99787" class="com.intellij.ui.components.JBCheckBox" binding="myCaptureVariables">
<constraints border-constraint="South"/>
<properties>
<text resource-bundle="messages/DebuggerBundle" key="label.capture.configurable.capture.variables"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.settings;
import com.intellij.codeInsight.AnnotationsPanel;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.jdi.DecompiledLocalVariable;
@@ -22,7 +23,9 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
@@ -37,7 +40,6 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.ui.ItemRemovable;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.components.BorderLayoutPanel;
import com.intellij.util.xmlb.XmlSerializer;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
@@ -51,8 +53,10 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
@@ -61,10 +65,18 @@ import java.util.function.BiConsumer;
*/
public class CaptureConfigurable implements SearchableConfigurable {
private static final Logger LOG = Logger.getInstance(CaptureConfigurable.class);
private final Project myProject;
private JCheckBox myDebuggerAgent;
private JButton myConfigureAnnotationsButton;
private JPanel myCapturePanel;
private MyTableModel myTableModel;
private JCheckBox myCaptureVariables;
private JPanel myPanel;
public CaptureConfigurable(Project project) {
myProject = project;
}
@NotNull
@Override
@@ -246,19 +258,12 @@ public class CaptureConfigurable implements SearchableConfigurable {
}
});
BorderLayoutPanel panel = JBUI.Panels.simplePanel();
myDebuggerAgent = new JCheckBox(DebuggerBundle.message("label.capture.configurable.debugger.agent"));
panel.addToTop(myDebuggerAgent);
myConfigureAnnotationsButton.addActionListener(e -> new AsyncAnnotationsDialog(myProject).show());
BorderLayoutPanel debuggerPanel = JBUI.Panels.simplePanel();
debuggerPanel.setBorder(IdeBorderFactory.createTitledBorder("Breakpoints based", false));
debuggerPanel.addToCenter(decorator.createPanel());
myCapturePanel.setBorder(IdeBorderFactory.createTitledBorder("Breakpoints based", false));
myCapturePanel.add(decorator.createPanel(), BorderLayout.CENTER);
myCaptureVariables = new JCheckBox(DebuggerBundle.message("label.capture.configurable.capture.variables"));
debuggerPanel.addToBottom(myCaptureVariables);
panel.addToCenter(debuggerPanel);
return panel;
return myPanel;
}
private StreamEx<CapturePoint> selectedCapturePoints(JBTable table) {
@@ -506,21 +511,27 @@ public class CaptureConfigurable implements SearchableConfigurable {
static void processCaptureAnnotations(BiConsumer<Boolean, PsiModifierListOwner> consumer) {
ApplicationManager.getApplication().assertReadAccessAllowed();
scanPointsInt(true, consumer);
scanPointsInt(false, consumer);
Project project = JavaDebuggerSupport.getContextProjectForEditorFieldsInDebuggerConfigurables();
DebuggerProjectSettings debuggerProjectSettings = DebuggerProjectSettings.getInstance(project);
scanPointsInt(project, debuggerProjectSettings, true, consumer);
scanPointsInt(project, debuggerProjectSettings, false, consumer);
}
private static void scanPointsInt(boolean capture, BiConsumer<Boolean, PsiModifierListOwner> consumer) {
private static void scanPointsInt(Project project,
DebuggerProjectSettings debuggerProjectSettings,
boolean capture,
BiConsumer<Boolean, PsiModifierListOwner> consumer) {
try {
String annotationName = getAnnotationName(capture);
Project project = JavaDebuggerSupport.getContextProjectForEditorFieldsInDebuggerConfigurables();
GlobalSearchScope allScope = GlobalSearchScope.allScope(project);
PsiClass annotationClass = JavaPsiFacade.getInstance(project).findClass(annotationName, allScope);
if (annotationClass != null) {
AnnotatedElementsSearch.searchElements(annotationClass, allScope, PsiMethod.class, PsiParameter.class)
.forEach(e -> {
consumer.accept(capture, e);
});
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
for (String annotationName : getAsyncAnnotations(debuggerProjectSettings, capture)) {
PsiClass annotationClass = psiFacade.findClass(annotationName, allScope);
if (annotationClass != null) {
AnnotatedElementsSearch.searchElements(annotationClass, allScope, PsiMethod.class, PsiParameter.class)
.forEach(e -> {
consumer.accept(capture, e);
});
}
}
}
catch (IndexNotReadyException | ProcessCanceledException ignore) {
@@ -533,4 +544,57 @@ public class CaptureConfigurable implements SearchableConfigurable {
static String getAnnotationName(boolean capture) {
return (capture ? Async.Schedule.class : Async.Execute.class).getName().replace("$", ".");
}
private static List<String> getAsyncAnnotations(DebuggerProjectSettings debuggerProjectSettings, boolean capture) {
return StreamEx.of(capture ? debuggerProjectSettings.myAsyncScheduleAnnotations : debuggerProjectSettings.myAsyncExecuteAnnotations)
.prepend(getAnnotationName(capture))
.toList();
}
private class AsyncAnnotationsDialog extends DialogWrapper {
private final AnnotationsPanel myAsyncSchedulePanel;
private final AnnotationsPanel myAsyncExecutePanel;
private final DebuggerProjectSettings mySettings;
private AsyncAnnotationsDialog(@NotNull Project project) {
super(project, true);
mySettings = DebuggerProjectSettings.getInstance(myProject);
myAsyncSchedulePanel = new AnnotationsPanel(project,
"Async Schedule",
"",
getAsyncAnnotations(mySettings, true),
new String[]{getAnnotationName(true)},
Collections.emptySet(), false, false);
myAsyncExecutePanel = new AnnotationsPanel(project,
"Async Execute",
"",
getAsyncAnnotations(mySettings, false),
new String[]{getAnnotationName(false)},
Collections.emptySet(), false, false);
init();
setTitle("Async Annotations Configuration");
}
@Override
protected JComponent createCenterPanel() {
final Splitter splitter = new Splitter(true);
splitter.setFirstComponent(myAsyncSchedulePanel.getComponent());
splitter.setSecondComponent(myAsyncExecutePanel.getComponent());
splitter.setHonorComponentsMinimumSize(true);
splitter.setPreferredSize(JBUI.size(300, 400));
return splitter;
}
@SuppressWarnings("SSBasedInspection")
@Override
protected void doOKAction() {
mySettings.myAsyncScheduleAnnotations = StreamEx.of(myAsyncSchedulePanel.getAnnotations())
.filter(e -> !e.equals(getAnnotationName(true)))
.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
mySettings.myAsyncExecuteAnnotations = StreamEx.of(myAsyncExecutePanel.getAnnotations())
.filter(e -> !e.equals(getAnnotationName(false)))
.toArray(ArrayUtil.EMPTY_STRING_ARRAY);
super.doOKAction();
}
}
}
@@ -0,0 +1,40 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.settings;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.util.ArrayUtil;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.intellij.util.xmlb.annotations.XCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author egor
*/
@State(name = "DebuggerSettings", storages = @Storage("debugger.xml"))
public class DebuggerProjectSettings implements PersistentStateComponent<DebuggerProjectSettings> {
@XCollection(propertyElementName = "async-schedule-annotations", elementName = "annotation", valueAttributeName = "name")
public String[] myAsyncScheduleAnnotations = ArrayUtil.EMPTY_STRING_ARRAY;
@XCollection(propertyElementName = "async-execute-annotations", elementName = "annotation", valueAttributeName = "name")
public String[] myAsyncExecuteAnnotations = ArrayUtil.EMPTY_STRING_ARRAY;
public static DebuggerProjectSettings getInstance(@NotNull Project project) {
return ServiceManager.getService(project, DebuggerProjectSettings.class);
}
@Nullable
@Override
public DebuggerProjectSettings getState() {
return this;
}
@Override
public void loadState(@NotNull DebuggerProjectSettings state) {
XmlSerializerUtil.copyBean(state, this);
}
}
@@ -45,8 +45,6 @@ public class JavaDebuggerSettings extends XDebuggerSettings<Element> {
case HOTSWAP:
return singletonList(SimpleConfigurable.create("reference.idesettings.debugger.hotswap", OptionsBundle.message("options.java.display.name"),
JavaHotSwapConfigurableUi.class, DebuggerSettings::getInstance));
case ROOT:
return singletonList(new CaptureConfigurable());
}
return Collections.emptyList();
}
@@ -0,0 +1,225 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.ClassFilter;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeClassChooserFactory;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import javax.swing.*;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.util.List;
import java.util.*;
public class AnnotationsPanel {
private final Project myProject;
private String myDefaultAnnotation;
private final Set<String> myDefaultAnnotations;
private final JBTable myTable;
private final JPanel myComponent;
protected final DefaultTableModel myTableModel;
public AnnotationsPanel(Project project,
String name,
String defaultAnnotation,
List<String> annotations,
String[] defaultAnnotations,
Set<String> checkedAnnotations,
boolean showInstrumentationOptions,
boolean showDefaultActions) {
myProject = project;
myDefaultAnnotation = defaultAnnotation;
myDefaultAnnotations = new HashSet<>(Arrays.asList(defaultAnnotations));
myTableModel = new DefaultTableModel() {
@Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
};
myTableModel.setColumnCount(showInstrumentationOptions ? 2 : 1);
for (String annotation : annotations) {
addRow(annotation, checkedAnnotations.contains(annotation));
}
DefaultTableColumnModel columnModel = new DefaultTableColumnModel();
columnModel.addColumn(new TableColumn(0, 100, new ColoredTableCellRenderer() {
@Override
public void acquireState(JTable table, boolean isSelected, boolean hasFocus, int row, int column) {
super.acquireState(table, isSelected, false, row, column);
}
@Override
protected void customizeCellRenderer(JTable table,
Object value,
boolean selected,
boolean hasFocus,
int row,
int column) {
append((String)value, SimpleTextAttributes.REGULAR_ATTRIBUTES);
if (value.equals(myDefaultAnnotation)) {
setIcon(AllIcons.Diff.CurrentLine);
}
else {
setIcon(EmptyIcon.ICON_16);
}
}
}, null));
myTable = new JBTable(myTableModel, columnModel);
if (showInstrumentationOptions) {
columnModel.getColumn(0).setHeaderValue("Annotation");
TableColumn checkColumn = new TableColumn(1, 100, new BooleanTableCellRenderer(), new BooleanTableCellEditor());
columnModel.addColumn(checkColumn);
checkColumn.setHeaderValue(" Instrument ");
TableCellRenderer defaultRenderer = myTable.getTableHeader().getDefaultRenderer();
TableCellRenderer headerRenderer = new TableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
Component component = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (component instanceof JComponent) {
((JComponent)component)
.setToolTipText(column == 1 ? "Add runtime assertions for notnull-annotated methods and parameters" : null);
}
return component;
}
};
myTable.getTableHeader().setDefaultRenderer(headerRenderer);
checkColumn.setHeaderRenderer(headerRenderer);
checkColumn.sizeWidthToFit();
}
final AnActionButton selectButton =
new AnActionButton("Select annotation used for code generation", AllIcons.Actions.Checked) {
@Override
public void actionPerformed(AnActionEvent e) {
String selectedValue = getSelectedAnnotation();
if (selectedValue == null) return;
myDefaultAnnotation = selectedValue;
// to show the new default value in the ui
myTableModel.fireTableRowsUpdated(myTable.getSelectedRow(), myTable.getSelectedRow());
}
@Override
public void updateButton(AnActionEvent e) {
String selectedValue = getSelectedAnnotation();
e.getPresentation().setEnabled(selectedValue != null && !selectedValue.equals(myDefaultAnnotation));
}
};
final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTable).disableUpDownActions()
.setAddAction(b -> chooseAnnotation(name))
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
String selectedValue = getSelectedAnnotation();
if (selectedValue == null) return;
if (myDefaultAnnotation.equals(selectedValue)) myDefaultAnnotation = (String)myTable.getValueAt(0, 0);
myTableModel.removeRow(myTable.getSelectedRow());
}
})
.setRemoveActionUpdater(e -> !myDefaultAnnotations.contains(getSelectedAnnotation()));
if (showDefaultActions) {
toolbarDecorator.addExtraAction(selectButton);
}
final JPanel panel = toolbarDecorator.createPanel();
myComponent = new JPanel(new BorderLayout());
myComponent.setBorder(IdeBorderFactory.createTitledBorder(name + " annotations", false, JBUI.insetsTop(10)));
myComponent.add(panel);
myComponent.setPreferredSize(new JBDimension(myComponent.getPreferredSize().width, 200));
myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.setRowSelectionAllowed(true);
myTable.setShowGrid(false);
selectAnnotation(myDefaultAnnotation);
}
private void addRow(String annotation, boolean checked) {
myTableModel.addRow(new Object[]{annotation, checked});
}
private Integer selectAnnotation(String annotation) {
for (int i = 0; i < myTable.getRowCount(); i++) {
if (annotation.equals(myTable.getValueAt(i, 0))) {
myTable.setRowSelectionInterval(i, i);
return i;
}
}
return null;
}
private String getSelectedAnnotation() {
int selectedRow = myTable.getSelectedRow();
return selectedRow < 0 ? null : (String)myTable.getValueAt(selectedRow, 0);
}
private void chooseAnnotation(String title) {
final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject)
.createNoInnerClassesScopeChooser("Choose " + title + " annotation", GlobalSearchScope.allScope(myProject), new ClassFilter() {
@Override
public boolean isAccepted(PsiClass aClass) {
return aClass.isAnnotationType();
}
}, null);
chooser.showDialog();
final PsiClass selected = chooser.getSelected();
if (selected == null) {
return;
}
final String qualifiedName = selected.getQualifiedName();
if (selectAnnotation(qualifiedName) == null) {
addRow(qualifiedName, false);
}
}
public JComponent getComponent() {
return myComponent;
}
String getDefaultAnnotation() {
return myDefaultAnnotation;
}
public String[] getAnnotations() {
int size = myTable.getRowCount();
String[] result = new String[size];
for (int i = 0; i < size; i++) {
result[i] = (String)myTable.getValueAt(i, 0);
}
return result;
}
List<String> getCheckedAnnotations() {
List<String> result = new ArrayList<>();
for (int i = 0; i < myTable.getRowCount(); i++) {
if (Boolean.TRUE.equals(myTable.getValueAt(i, 1))) {
result.add((String)myTable.getValueAt(i, 0));
}
}
return result;
}
}
@@ -1,50 +1,22 @@
// Copyright 2000-2017 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.
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.ClassFilter;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeClassChooserFactory;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Splitter;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
import java.util.Collections;
public class NullableNotNullDialog extends DialogWrapper {
private final Project myProject;
@@ -62,14 +34,16 @@ public class NullableNotNullDialog extends DialogWrapper {
myShowInstrumentationOptions = showInstrumentationOptions;
NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject);
myNullablePanel = new AnnotationsPanel("Nullable",
myNullablePanel = new AnnotationsPanel(project,
"Nullable",
manager.getDefaultNullable(),
manager.getNullables(), NullableNotNullManager.DEFAULT_NULLABLES,
Collections.emptySet(), false);
myNotNullPanel = new AnnotationsPanel("NotNull",
Collections.emptySet(), false, true);
myNotNullPanel = new AnnotationsPanel(project,
"NotNull",
manager.getDefaultNotNull(),
manager.getNotNulls(), NullableNotNullManager.DEFAULT_NOT_NULLS,
ContainerUtil.newHashSet(manager.getInstrumentedNotNulls()), showInstrumentationOptions);
ContainerUtil.newHashSet(manager.getInstrumentedNotNulls()), showInstrumentationOptions, true);
init();
setTitle("Nullable/NotNull Configuration");
@@ -132,192 +106,4 @@ public class NullableNotNullDialog extends DialogWrapper {
super.doOKAction();
}
private class AnnotationsPanel {
private String myDefaultAnnotation;
private final Set<String> myDefaultAnnotations;
private final JBTable myTable;
private final JPanel myComponent;
private final DefaultTableModel myTableModel;
private AnnotationsPanel(String name, String defaultAnnotation, List<String> annotations, String[] defaultAnnotations, Set<String> checkedAnnotations, boolean showInstrumentationOptions) {
myDefaultAnnotation = defaultAnnotation;
myDefaultAnnotations = new HashSet<>(Arrays.asList(defaultAnnotations));
myTableModel = new DefaultTableModel() {
@Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
};
myTableModel.setColumnCount(showInstrumentationOptions ? 2 : 1);
for (String annotation : annotations) {
addRow(annotation, checkedAnnotations.contains(annotation));
}
DefaultTableColumnModel columnModel = new DefaultTableColumnModel();
columnModel.addColumn(new TableColumn(0, 100, new ColoredTableCellRenderer() {
@Override
public void acquireState(JTable table, boolean isSelected, boolean hasFocus, int row, int column) {
super.acquireState(table, isSelected, false, row, column);
}
@Override
protected void customizeCellRenderer(JTable table,
Object value,
boolean selected,
boolean hasFocus,
int row,
int column) {
append((String)value, SimpleTextAttributes.REGULAR_ATTRIBUTES);
if (value.equals(myDefaultAnnotation)) {
setIcon(AllIcons.Diff.CurrentLine);
} else {
setIcon(EmptyIcon.ICON_16);
}
}
}, null));
myTable = new JBTable(myTableModel, columnModel);
if (showInstrumentationOptions) {
columnModel.getColumn(0).setHeaderValue("Annotation");
TableColumn checkColumn = new TableColumn(1, 100, new BooleanTableCellRenderer(), new BooleanTableCellEditor());
columnModel.addColumn(checkColumn);
checkColumn.setHeaderValue(" Instrument ");
TableCellRenderer defaultRenderer = myTable.getTableHeader().getDefaultRenderer();
TableCellRenderer headerRenderer = new TableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
Component component = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (component instanceof JComponent) {
((JComponent)component)
.setToolTipText(column == 1 ? "Add runtime assertions for notnull-annotated methods and parameters" : null);
}
return component;
}
};
myTable.getTableHeader().setDefaultRenderer(headerRenderer);
checkColumn.setHeaderRenderer(headerRenderer);
checkColumn.sizeWidthToFit();
}
final AnActionButton selectButton =
new AnActionButton("Select annotation used for code generation", AllIcons.Actions.Checked) {
@Override
public void actionPerformed(AnActionEvent e) {
String selectedValue = getSelectedAnnotation();
if (selectedValue == null) return;
myDefaultAnnotation = selectedValue;
// to show the new default value in the ui
myTableModel.fireTableRowsUpdated(myTable.getSelectedRow(), myTable.getSelectedRow());
}
@Override
public void updateButton(AnActionEvent e) {
String selectedValue = getSelectedAnnotation();
e.getPresentation().setEnabled(selectedValue != null && !selectedValue.equals(myDefaultAnnotation));
}
};
final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTable).disableUpDownActions()
.setAddAction(b -> chooseAnnotation(name))
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
String selectedValue = getSelectedAnnotation();
if (selectedValue == null) return;
if (myDefaultAnnotation.equals(selectedValue)) myDefaultAnnotation = (String)myTable.getValueAt(0, 0);
myTableModel.removeRow(myTable.getSelectedRow());
}
})
.setRemoveActionUpdater(e -> !myDefaultAnnotations.contains(getSelectedAnnotation()))
.addExtraAction(selectButton);
final JPanel panel = toolbarDecorator.createPanel();
myComponent = new JPanel(new BorderLayout());
myComponent.setBorder(IdeBorderFactory.createTitledBorder(name + " annotations", false, JBUI.insetsTop(10)));
myComponent.add(panel);
myComponent.setPreferredSize(new JBDimension(myComponent.getPreferredSize().width, 200));
myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.setRowSelectionAllowed(true);
myTable.setShowGrid(false);
selectAnnotation(myDefaultAnnotation);
}
private void addRow(String annotation, boolean checked) {
myTableModel.addRow(new Object[]{annotation, checked});
}
private Integer selectAnnotation(String annotation) {
for (int i = 0; i < myTable.getRowCount(); i++) {
if (annotation.equals(myTable.getValueAt(i, 0))) {
myTable.setRowSelectionInterval(i, i);
return i;
}
}
return null;
}
private String getSelectedAnnotation() {
int selectedRow = myTable.getSelectedRow();
return selectedRow <0 ? null : (String)myTable.getValueAt(selectedRow, 0);
}
private void chooseAnnotation(String title) {
final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject)
.createNoInnerClassesScopeChooser("Choose " + title + " annotation", GlobalSearchScope.allScope(myProject), new ClassFilter() {
@Override
public boolean isAccepted(PsiClass aClass) {
return aClass.isAnnotationType();
}
}, null);
chooser.showDialog();
final PsiClass selected = chooser.getSelected();
if (selected == null) {
return;
}
final String qualifiedName = selected.getQualifiedName();
if (selectAnnotation(qualifiedName) == null) {
addRow(qualifiedName, false);
}
}
JComponent getComponent() {
return myComponent;
}
String getDefaultAnnotation() {
return myDefaultAnnotation;
}
String[] getAnnotations() {
int size = myTable.getRowCount();
String[] result = new String[size];
for (int i = 0; i < size; i++) {
result[i] = (String)myTable.getValueAt(i, 0);
}
return result;
}
List<String> getCheckedAnnotations() {
List<String> result = new ArrayList<>();
for (int i = 0; i < myTable.getRowCount(); i++) {
if (Boolean.TRUE.equals(myTable.getValueAt(i, 1))) {
result.add((String)myTable.getValueAt(i, 0));
}
}
return result;
}
}
}
@@ -262,6 +262,7 @@ label.threads.view.configurable.show.stack.frames.for.synthetic.methods=Show sta
label.threads.view.configurable.show.thread.groups=Show thread &groups
label.capture.configurable.capture.variables=Capture local variables (may greatly slow down the execution)
label.capture.configurable.debugger.agent=Instrumenting agent (requires debugger restart)
label.capture.configurable.annotations.configure=Configure annotations...
threads.view.configurable.display.name=Customize Threads View
user.renderers.configurable.display.name=Java Type Renderers
async.stacktraces.configurable.display.name=Async Stack Traces
+3
View File
@@ -281,6 +281,9 @@
<xdebugger.debuggerSupport implementation="com.intellij.debugger.ui.JavaDebuggerSupport"/>
<xdebugger.settings implementation="com.intellij.debugger.settings.JavaDebuggerSettings"/>
<projectConfigurable parentId="project.propDebugger" instance="com.intellij.debugger.settings.CaptureConfigurable"/>
<xdebugger.localAttachDebuggerProvider implementation="com.intellij.debugger.engine.JavaAttachDebuggerProvider"/>
<xdebugger.breakpointType implementation="com.intellij.debugger.ui.breakpoints.JavaMethodBreakpointType"/>
@@ -149,6 +149,7 @@
<applicationService serviceImplementation="com.intellij.debugger.settings.NodeRendererSettings"/>
<applicationService serviceImplementation="com.intellij.debugger.settings.ViewsGeneralSettings"/>
<applicationService serviceImplementation="com.intellij.debugger.settings.DebuggerSettings"/>
<projectService serviceImplementation="com.intellij.debugger.settings.DebuggerProjectSettings"/>
<applicationService serviceImplementation="com.intellij.debugger.settings.ThreadsViewSettings"/>
<applicationService serviceInterface="com.intellij.packaging.elements.PackagingElementFactory"