Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2016-07-14 21:05:22 +02:00
102 changed files with 1648 additions and 815 deletions
@@ -16,19 +16,34 @@
package com.intellij.debugger.actions;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.MethodBytecodeUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.DocumentUtil;
import com.intellij.util.Range;
import com.intellij.util.containers.OrderedSet;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.sun.jdi.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.Collections;
import java.util.List;
@@ -39,6 +54,8 @@ import java.util.Stack;
* Date: 22.11.11
*/
public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
private static final Logger LOG = Logger.getInstance(JavaSmartStepIntoHandler.class);
@Override
public boolean isAvailable(final SourcePosition position) {
final PsiFile file = position.getFile();
@@ -46,8 +63,37 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
}
@Override
public boolean doSmartStep(SourcePosition position, DebuggerSession session, TextEditor fileEditor) {
session.getProcess().getManagerThread().schedule(new DebuggerContextCommandImpl(session.getContextManager().getContext()) {
@Override
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
List<SmartStepTarget> targets = ApplicationManager.getApplication().runReadAction(
(Computable<List<SmartStepTarget>>)() -> findSmartStepTargets(position, suspendContext));
DebuggerUIUtil.invokeLater(() -> {
if (targets.isEmpty()) {
doStepInto(session, Registry.is("debugger.single.smart.step.force"), null);
}
else {
handleTargets(position, session, fileEditor, targets);
}
});
}
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
});
return true;
}
@NotNull
public List<SmartStepTarget> findSmartStepTargets(final SourcePosition position) {
@Override
public List<SmartStepTarget> findSmartStepTargets(SourcePosition position) {
throw new IllegalStateException("Should not be used");
}
protected List<SmartStepTarget> findSmartStepTargets(final SourcePosition position, @Nullable SuspendContextImpl suspendContext) {
final int line = position.getLine();
if (line < 0) {
return Collections.emptyList(); // the document has been changed
@@ -82,7 +128,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
}
element = parent;
}
while(true);
while (true);
//noinspection unchecked
final List<SmartStepTarget> targets = new OrderedSet<>();
@@ -222,10 +268,42 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
Range<Integer> lines =
new Range<>(doc.getLineNumber(textRange.get().getStartOffset()), doc.getLineNumber(textRange.get().getEndOffset()));
for (SmartStepTarget target : targets) {
target.setCallingExpressionLines(lines);
targets.forEach(t -> t.setCallingExpressionLines(lines));
if (!targets.isEmpty()) {
StackFrameProxyImpl frameProxy = suspendContext != null ? suspendContext.getFrameProxy() : null;
if (frameProxy != null) {
try {
Location location = frameProxy.location();
MethodBytecodeUtil.visit(location.declaringType(), location.method(), location.codeIndex(), new MethodVisitor(Opcodes.ASM5) {
boolean myLineMatch = false;
@Override
public void visitLineNumber(int line, Label start) {
myLineMatch = lines.isWithin(line - 1);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (myLineMatch) {
targets.removeIf(t -> {
if (t instanceof MethodSmartStepTarget) {
return DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)t).getMethod(),
owner.replace("/", "."), name, desc, suspendContext.getDebugProcess());
}
return false;
});
}
}
});
}
catch (Exception e) {
LOG.info(e);
}
}
return targets;
}
return targets;
}
return Collections.emptyList();
}
@@ -17,6 +17,7 @@ package com.intellij.debugger.actions;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.MethodFilter;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
@@ -51,8 +52,12 @@ public class JvmSmartStepIntoActionHandler extends DebuggerActionHandler {
}
}
}
doStepInto(session, Registry.is("debugger.single.smart.step.force"), null);
}
static void doStepInto(DebuggerSession session, boolean force, MethodFilter filter) {
session.sessionResumed();
session.stepInto(Registry.is("debugger.single.smart.step.force"), null);
session.stepInto(force, filter);
}
public boolean isEnabled(@NotNull final Project project, final AnActionEvent event) {
@@ -57,21 +57,22 @@ public abstract class JvmSmartStepIntoHandler {
* @return false to continue for another handler or for default action (step into)
*/
public boolean doSmartStep(SourcePosition position, final DebuggerSession session, TextEditor fileEditor) {
final List<SmartStepTarget> targets = findSmartStepTargets(position);
return handleTargets(position, session, fileEditor, findSmartStepTargets(position));
}
protected final boolean handleTargets(SourcePosition position,
DebuggerSession session,
TextEditor fileEditor,
List<SmartStepTarget> targets) {
if (!targets.isEmpty()) {
final SmartStepTarget firstTarget = targets.get(0);
SmartStepTarget firstTarget = targets.get(0);
if (targets.size() == 1) {
session.sessionResumed();
session.stepInto(Registry.is("debugger.single.smart.step.force"), createMethodFilter(firstTarget));
doStepInto(session, Registry.is("debugger.single.smart.step.force"), firstTarget);
}
else {
final Editor editor = fileEditor.getEditor();
final PsiMethodListPopupStep popupStep = new PsiMethodListPopupStep(editor, targets, new PsiMethodListPopupStep.OnChooseRunnable() {
public void execute(SmartStepTarget chosenTarget) {
session.sessionResumed();
session.stepInto(true, createMethodFilter(chosenTarget));
}
});
Editor editor = fileEditor.getEditor();
PsiMethodListPopupStep popupStep =
new PsiMethodListPopupStep(editor, targets, chosenTarget -> doStepInto(session, true, chosenTarget));
ListPopupImpl popup = new ListPopupImpl(popupStep);
DebuggerUIUtil.registerExtraHandleShortcuts(popup, XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO);
popup.setAdText(DebuggerUIUtil.getSelectionShortcutsAdText(XDebuggerActions.STEP_INTO, XDebuggerActions.SMART_STEP_INTO));
@@ -95,6 +96,10 @@ public abstract class JvmSmartStepIntoHandler {
return false;
}
protected void doStepInto(DebuggerSession session, boolean force, SmartStepTarget target) {
JvmSmartStepIntoActionHandler.doStepInto(session, force, createMethodFilter(target));
}
private static void highlightTarget(PsiMethodListPopupStep popupStep, SmartStepTarget target) {
final PsiElement highlightElement = target.getHighlightElement();
if (highlightElement != null) {
@@ -918,6 +918,23 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
return false;
}
public static boolean methodMatches(@NotNull PsiMethod psiMethod,
String className,
String name,
String signature,
DebugProcessImpl process) {
PsiClass containingClass = psiMethod.getContainingClass();
try {
return containingClass != null && Objects.equals(containingClass.getQualifiedName(), className) &&
JVMNameUtil.getJVMMethodName(psiMethod).equals(name) &&
JVMNameUtil.getJVMSignature(psiMethod).getName(process).equals(signature);
}
catch (EvaluateException e) {
LOG.debug(e);
return false;
}
}
@Nullable
public static PsiElement getContainingMethod(@Nullable PsiElement elem) {
return PsiTreeUtil.getContextOfType(elem, PsiMethod.class, PsiLambdaExpression.class, PsiClassInitializer.class);
@@ -134,7 +134,7 @@ public class ConfigFileInfoSetImpl implements ConfigFileInfoSet {
final int id = Comparing.compare(o1.getMetaData().getId(), o2.getMetaData().getId());
return id != 0? id : Comparing.compare(o1.getUrl(), o2.getUrl());
});
sortedConfigFiles.addAll(myConfigFiles.collectValues());
sortedConfigFiles.addAll(myConfigFiles.values());
for (ConfigFileInfo configuration : sortedConfigFiles) {
final Element child = new Element(ELEMENT_NAME);
final ConfigFileMetaData metaData = configuration.getMetaData();
@@ -31,13 +31,14 @@ import com.intellij.openapi.extensions.impl.ExtensionPointImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.PsiDocCommentOwner;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.xmlb.SkipDefaultsSerializationFilter;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.*;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -50,6 +51,7 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
@NonNls private static final String[] STANDARD_ANNOS = {
"javax.ws.rs.*",
};
private static final String PATTERN_SUFFIX = ".*";
// null means uninitialized
private volatile List<String> ADDITIONAL_ANNOS;
@@ -72,6 +74,7 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
}
public JDOMExternalizableStringList ADDITIONAL_ANNOTATIONS = new JDOMExternalizableStringList();
private final Map<String, SmartRefElementPointer> myPersistentEntryPoints;
private final List<ClassPattern> myPatterns = new ArrayList<>();
private final Set<RefElement> myTemporaryEntryPoints;
private static final String VERSION = "2.0";
@NonNls private static final String VERSION_ATTR = "version";
@@ -135,6 +138,13 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
}
catch (Throwable ignored) {
}
getPatterns().clear();
for (Element pattern : element.getChildren("pattern")) {
final ClassPattern classPattern = new ClassPattern();
XmlSerializer.deserializeInto(classPattern, pattern);
getPatterns().add(classPattern);
}
}
@Override
@@ -142,6 +152,11 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
public Element getState() {
Element element = new Element("state");
writeExternal(element, myPersistentEntryPoints, ADDITIONAL_ANNOTATIONS);
if (!getPatterns().isEmpty()) {
for (ClassPattern pattern : getPatterns()) {
element.addContent(XmlSerializer.serialize(pattern, new SkipDefaultsSerializationFilter()));
}
}
return element;
}
@@ -177,6 +192,16 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
((RefElementImpl)refElement).setPermanentEntry(entryPoint.isPersistent());
}
}
for (ClassPattern pattern : myPatterns) {
final RefEntity refClass = manager.getReference(RefJavaManager.CLASS, pattern.pattern);
if (refClass != null) {
for (RefMethod constructor : ((RefClass)refClass).getConstructors()) {
((RefMethodImpl)constructor).setEntry(true);
((RefMethodImpl)constructor).setPermanentEntry(true);
}
}
}
});
}
}
@@ -192,6 +217,21 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
@Override
public void addEntryPoint(@NotNull RefElement newEntryPoint, boolean isPersistent) {
if (!newEntryPoint.isValid()) return;
if (isPersistent) {
if (newEntryPoint instanceof RefMethod && ((RefMethod)newEntryPoint).isConstructor() || newEntryPoint instanceof RefClass) {
final ClassPattern classPattern = new ClassPattern();
classPattern.pattern = new SmartRefElementPointerImpl(newEntryPoint, true).getFQName();
getPatterns().add(classPattern);
final EntryPointsManager entryPointsManager = getInstance(newEntryPoint.getElement().getProject());
if (this != entryPointsManager) {
entryPointsManager.addEntryPoint(newEntryPoint, true);
}
return;
}
}
if (newEntryPoint instanceof RefClass) {
RefClass refClass = (RefClass)newEntryPoint;
@@ -248,8 +288,8 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
if (key != null) {
myPersistentEntryPoints.remove(key);
((RefElementImpl)anEntryPoint).setEntry(false);
}
((RefElementImpl)anEntryPoint).setEntry(false);
if (anEntryPoint.isPermanentEntry() && anEntryPoint.isValid()) {
final Project project = anEntryPoint.getElement().getProject();
@@ -258,6 +298,17 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
entryPointsManager.removeEntryPoint(anEntryPoint);
}
}
if (anEntryPoint instanceof RefMethod && ((RefMethod)anEntryPoint).isConstructor() || anEntryPoint instanceof RefClass) {
final RefClass aClass = anEntryPoint instanceof RefClass ? (RefClass)anEntryPoint : ((RefMethod)anEntryPoint).getOwnerClass();
final String qualifiedName = aClass.getQualifiedName();
for (Iterator<ClassPattern> iterator = getPatterns().iterator(); iterator.hasNext(); ) {
if (Comparing.equal(iterator.next().pattern, qualifiedName)) {
//todo if inheritance or pattern?
iterator.remove();
}
}
}
}
@NotNull
@@ -321,6 +372,7 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
public void addAllPersistentEntries(EntryPointsManagerBase manager) {
myPersistentEntryPoints.putAll(manager.myPersistentEntryPoints);
myPatterns.addAll(manager.getPatterns());
}
public static void convert(Element element, final Map<String, SmartRefElementPointer> persistentEntryPoints) {
@@ -380,7 +432,74 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen
return true;
}
if (element instanceof PsiClass) {
final String qualifiedName = ((PsiClass)element).getQualifiedName();
if (qualifiedName != null) {
for (ClassPattern pattern : getPatterns()) {
if (isAcceptedByPattern((PsiClass)element, qualifiedName, pattern, new HashSet<>())) {
return true;
}
}
}
}
return AnnotationUtil.checkAnnotatedUsingPatterns(owner, ADDITIONAL_ANNOTATIONS) ||
AnnotationUtil.checkAnnotatedUsingPatterns(owner, getAdditionalAnnotations());
}
private static boolean isAcceptedByPattern(@NotNull PsiClass element, String qualifiedName, ClassPattern pattern, Set<PsiClass> visited) {
if (qualifiedName == null) {
return false;
}
if (qualifiedName.equals(pattern.pattern)) {
return true;
}
if (pattern.pattern.endsWith(PATTERN_SUFFIX) && qualifiedName.startsWith(StringUtil.trimEnd(pattern.pattern, PATTERN_SUFFIX))) {
return true;
}
if (pattern.hierarchically) {
for (PsiClass superClass : element.getSupers()) {
final String superClassQualifiedName = superClass.getQualifiedName();
if (visited.add(superClass) && isAcceptedByPattern(superClass, superClassQualifiedName, pattern, visited)) {
return true;
}
}
}
return false;
}
public List<ClassPattern> getPatterns() {
return myPatterns;
}
@Tag("pattern")
public static class ClassPattern {
@Attribute("value")
public String pattern;
@Attribute("hierarchically")
public boolean hierarchically = false;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassPattern otherPattern = (ClassPattern)o;
if (hierarchically != otherPattern.hierarchically) return false;
if (!pattern.equals(otherPattern.pattern)) return false;
return true;
}
@Override
public int hashCode() {
int result = pattern.hashCode();
result = 31 * result + (hierarchically ? 1 : 0);
return result;
}
}
}
@@ -372,6 +372,9 @@ public class RefJavaManagerImpl extends RefJavaManager {
}
@Override
public void configureEntryClassPatterns() {}
@Override
public JButton createConfigureAnnotationsBtn() {
return null;
@@ -128,10 +128,13 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
gc.gridy++;
add(myNonJavaCheckbox, gc);
JButton configureAnnotations = EntryPointsManagerImpl.createConfigureAnnotationsButton();
final JButton configureClassPatternsButton = EntryPointsManagerImpl.createConfigureClassPatternsButton();
gc.fill = GridBagConstraints.NONE;
gc.gridy++;
gc.insets.top = 10;
add(configureClassPatternsButton, gc);
JButton configureAnnotations = EntryPointsManagerImpl.createConfigureAnnotationsButton();
gc.gridy++;
gc.weighty = 1;
add(configureAnnotations, gc);
@@ -0,0 +1,172 @@
/*
* Copyright 2000-2016 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 com.intellij.codeInspection.ex;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.ItemRemovable;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
class ConfigureClassPatternsDialog extends DialogWrapper {
private final List<EntryPointsManagerBase.ClassPattern> myModifiedPatterns;
private final List<EntryPointsManagerBase.ClassPattern> myPatterns;
private final Project myProject;
public ConfigureClassPatternsDialog(List<EntryPointsManagerBase.ClassPattern> patterns, Project project) {
super(project);
myModifiedPatterns = new ArrayList<>(patterns);
myPatterns = patterns;
myProject = project;
init();
setTitle("Configure Class Patterns");
}
@Override
protected JComponent createCenterPanel() {
final JBTable table = createTableForPatterns();
final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(table)
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
myModifiedPatterns.add(new EntryPointsManagerBase.ClassPattern());
AbstractTableModel model = (AbstractTableModel)table.getModel();
final int row = myModifiedPatterns.size() - 1;
model.fireTableRowsInserted(row, row);
table.setRowSelectionInterval(row, row);
table.editCellAt(row, 1);
}
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
TableUtil.removeSelectedItems(table);
table.repaint();
}
})
.setRemoveActionUpdater(new AnActionButtonUpdater() {
@Override
public boolean isEnabled(AnActionEvent e) {
return table.getSelectedRow() >= 0;
}
})
.setButtonComparator("Add", "Remove");
JPanel panel = new JPanel(new BorderLayout());
panel.add(SeparatorFactory.createSeparator("Mark class as reachable if name matches", null), BorderLayout.NORTH);
panel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER);
return panel;
}
@Override
protected void doOKAction() {
myPatterns.clear();
myPatterns.addAll(myModifiedPatterns);
DaemonCodeAnalyzer.getInstance(myProject).restart();
super.doOKAction();
}
private JBTable createTableForPatterns() {
TableModel dataModel = new MyTableModel();
final JBTable result = new JBTable(dataModel);
result.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableCellEditor editor = result.getDefaultEditor(String.class);
if (editor instanceof DefaultCellEditor) {
((DefaultCellEditor)editor).setClickCountToStart(1);
}
final TableColumn column = result.getTableHeader().getColumnModel().getColumn(0);
column.setResizable(false);
final int width = 15 + result.getTableHeader().getFontMetrics(result.getTableHeader().getFont()).stringWidth(result.getColumnName(0));
column.setMaxWidth(width);
column.setMinWidth(width);
return result;
}
private class MyTableModel extends AbstractTableModel implements ItemRemovable {
private final String[] myNames;
public MyTableModel() {
myNames = new String[] {"With Subclasses", "Class"};
}
public int getColumnCount() {
return 2;
}
public int getRowCount() {
return myModifiedPatterns.size();
}
@Nullable
public Object getValueAt(int row, int col) {
if (row < 0) return null;
final EntryPointsManagerBase.ClassPattern classPattern = myModifiedPatterns.get(row);
if (classPattern == null) return null;
if (col == 0) {
return classPattern.hierarchically;
}
return classPattern.pattern;
}
public String getColumnName(int column) {
return myNames[column];
}
public Class getColumnClass(int col) {
if (col == 0) {
return Boolean.class;
}
if (col == 1) {
return String.class;
}
throw new IllegalArgumentException(String.valueOf(col));
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object aValue, int row, int col) {
EntryPointsManagerBase.ClassPattern classPattern = myModifiedPatterns.get(row);
if (classPattern == null) return;
if (col == 0) {
classPattern.hierarchically = (boolean)aValue;
}
else {
classPattern.pattern = (String)aValue;
}
fireTableRowsUpdated(row, row);
}
@Override
public void removeRow(int idx) {
myModifiedPatterns.remove(idx);
}
}
}
@@ -70,6 +70,11 @@ public class EntryPointsManagerImpl extends EntryPointsManagerBase implements Pe
}.show();
}
@Override
public void configureEntryClassPatterns() {
new ConfigureClassPatternsDialog(getPatterns(), myProject).show();
}
@Override
public JButton createConfigureAnnotationsBtn() {
return createConfigureAnnotationsButton();
@@ -85,4 +90,15 @@ public class EntryPointsManagerImpl extends EntryPointsManagerBase implements Pe
});
return configureAnnotations;
}
}
public static JButton createConfigureClassPatternsButton() {
final JButton configureAnnotations = new JButton("Configure class patterns...");
configureAnnotations.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
getInstance(ProjectUtil.guessCurrentProject(configureAnnotations)).configureEntryClassPatterns();
}
});
return configureAnnotations;
}
}
@@ -0,0 +1,85 @@
/*
* Copyright 2000-2016 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 com.intellij.codeInspection;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection
import com.intellij.codeInspection.ex.EntryPointsManagerBase
import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.codeInspection.reference.RefClass
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
class UnusedDeclarationClassPatternsTest : LightCodeInsightFixtureTestCase() {
fun testClassPattern() {
val unusedDeclarationInspection = UnusedDeclarationInspection(true)
myFixture.enableInspections(unusedDeclarationInspection)
val classPattern = EntryPointsManagerBase.ClassPattern()
classPattern.hierarchically = true;
classPattern.pattern = "java.lang.Runnable"
val patterns = EntryPointsManagerBase.getInstance(project).patterns
try {
patterns.add(classPattern)
myFixture.configureByText("C.java", "public abstract class C implements Runnable {}")
myFixture.checkHighlighting()
}
finally {
patterns.remove(classPattern)
myFixture.disableInspections(unusedDeclarationInspection)
}
}
fun testNoClassPattern() {
val unusedDeclarationInspection = UnusedDeclarationInspection(true)
try {
myFixture.enableInspections(unusedDeclarationInspection)
myFixture.configureByText("C.java", "public abstract class <warning descr=\"Class 'C' is never used\">C</warning> implements Runnable {}")
myFixture.checkHighlighting()
}
finally {
myFixture.disableInspections(unusedDeclarationInspection)
}
}
fun testAddEntryPoint() {
val aClass = myFixture.addClass("public class Foo {}")
val entryPointsManager = EntryPointsManagerBase.getInstance(project)
val context = (InspectionManager.getInstance(project) as InspectionManagerEx).createNewGlobalContext(false)
try {
val refClass = context.refManager.getReference(aClass)
assertNotNull(refClass)
val patterns = entryPointsManager.patterns
assertEmpty(patterns)
//add class as entry point
entryPointsManager.addEntryPoint(refClass!!, true)
assertSize(1, patterns)
assertEquals("Foo", patterns[0].pattern)
assertEmpty(entryPointsManager.entryPoints)
//remove class entry point with constructors - ensure nothing is left in the entries
entryPointsManager.removeEntryPoint(refClass)
for (constructor in (refClass as RefClass).constructors) {
entryPointsManager.removeEntryPoint(constructor)
}
assertEmpty(patterns)
assertEmpty(entryPointsManager.entryPoints)
}
finally {
context.cleanup()
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -102,6 +102,11 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
debugProcess.getManagerThread().schedule(debugProcess.createStepOverCommand(context, false));
}
protected void stepOut(SuspendContextImpl context) {
DebugProcessImpl debugProcess = context.getDebugProcess();
debugProcess.getManagerThread().schedule(debugProcess.createStepOutCommand(context));
}
protected void waitBreakpoints() {
myScriptRunnablesSema.down();
waitFor(() -> myScriptRunnablesSema.waitFor());
@@ -129,7 +134,7 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
myScriptRunnables.add(runnable);
}
protected void doWhenPausedThenResume(final SuspendContextRunnable runnable) {
protected void onStop(final SuspendContextRunnable runnable, final SuspendContextRunnable then){
onBreakpoint(new SuspendContextRunnable() {
@Override
public void run(SuspendContextImpl suspendContext) throws Exception {
@@ -137,12 +142,16 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
runnable.run(suspendContext);
}
finally {
resume(suspendContext);
then.run(suspendContext);
}
}
});
}
protected void doWhenPausedThenResume(final SuspendContextRunnable runnable) {
onStop(runnable, this::resume);
}
protected void addDefaultBreakpointListener() {
if (myPauseScriptListener == null) {
final DebugProcessImpl debugProcess = getDebugProcess();
@@ -52,6 +52,8 @@ public abstract class EntryPointsManager implements Disposable {
public abstract void configureAnnotations();
public abstract void configureEntryClassPatterns();
/**
* {@link com.intellij.codeInspection.ex.EntryPointsManagerImpl#createConfigureAnnotationsButton()} should be used instead
*/
@@ -16,8 +16,8 @@
package com.intellij.openapi.editor.markup;
public interface HighlighterLayer {
int CARET_ROW = 1000;
int SYNTAX = 2000;
int SYNTAX = 1000;
int CARET_ROW = 2000;
int ADDITIONAL_SYNTAX = 3000;
int GUARDED_BLOCKS = 3500;
int WARNING = 4000;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 252 B

@@ -24,12 +24,12 @@ import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class OfflineProblemDescriptor {
@@ -91,14 +91,16 @@ public class OfflineProblemDescriptor {
@Nullable
public RefEntity getRefElement(final RefManager refManager) {
final RefEntity refElement = refManager.getReference(myType, myFQName);
if (refElement instanceof RefElement) {
final PsiElement element = ((RefElement)refElement).getElement();
if (element != null && element.isValid()) {
UIUtil.invokeLaterIfNeeded(() -> PsiDocumentManager.getInstance(element.getProject()).commitAllDocuments());
return ReadAction.compute(() -> {
final RefEntity refElement = refManager.getReference(myType, myFQName);
if (refElement instanceof RefElement) {
final PsiElement element = ((RefElement)refElement).getElement();
if (element != null && element.isValid()) {
UIUtil.invokeLaterIfNeeded(() -> PsiDocumentManager.getInstance(element.getProject()).commitAllDocuments());
}
}
}
return refElement;
return refElement;
});
}
public boolean equals(final Object o) {
@@ -47,7 +47,6 @@ import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
@@ -314,7 +313,17 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass {
}
if (HighlightingLevelManager.getInstance(project).shouldInspect(hostFile)) {
collectIntentionsFromDoNotShowLeveledInspections(project, hostFile, psiElement, offset, intentions);
PsiElement adjustedElement = psiElement;
int adjustedOffset = offset;
if (psiElement instanceof PsiWhiteSpace) {
final PsiElement prev = psiElement.getPrevSibling();
if (prev != null) {
adjustedElement = prev;
final TextRange range = prev.getTextRange();
adjustedOffset = range.isEmpty() ? range.getEndOffset() : range.getEndOffset() - 1;
}
}
collectIntentionsFromDoNotShowLeveledInspections(project, hostFile, adjustedElement, adjustedOffset, intentions);
}
final int line = hostDocument.getLineNumber(offset);
@@ -27,6 +27,7 @@ import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefModule;
import com.intellij.codeInspection.ui.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
@@ -73,7 +74,7 @@ public abstract class InspectionRVContentProvider {
@NotNull
public RefElementNode createNode(@NotNull InspectionToolPresentation presentation) {
return new RefElementNode(myEntity, presentation);
return ReadAction.compute(() -> new RefElementNode(myEntity, presentation));
}
@Nullable
@@ -203,7 +204,7 @@ public abstract class InspectionRVContentProvider {
if (moduleNode == null) {
if (moduleName != null) {
final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleName);
final Module module = ReadAction.compute(() -> ModuleManager.getInstance(myProject).findModuleByName(moduleName));
if (module != null) {
moduleNode = new InspectionModuleNode(module);
moduleNodes.put(moduleName, moduleNode);
@@ -356,39 +357,41 @@ public abstract class InspectionRVContentProvider {
@SuppressWarnings({"ConstantConditions"}) //class cast suppression
public static InspectionTreeNode merge(InspectionTreeNode child, InspectionTreeNode parent, boolean merge) {
if (merge) {
for (int i = 0; i < parent.getChildCount(); i++) {
InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i);
if (child.getClass() != current.getClass()) {
continue;
}
if (current instanceof InspectionPackageNode) {
if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) {
processDepth(child, current);
return current;
return ReadAction.compute(() -> {
if (merge) {
for (int i = 0; i < parent.getChildCount(); i++) {
InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i);
if (child.getClass() != current.getClass()) {
continue;
}
}
else if (current instanceof RefElementNode) {
if (InspectionResultsViewComparator.getInstance().compare(current, child) == 0) {
processDepth(child, current);
return current;
if (current instanceof InspectionPackageNode) {
if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) {
processDepth(child, current);
return current;
}
}
}
else if (current instanceof InspectionNode) {
if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) {
processDepth(child, current);
return current;
else if (current instanceof RefElementNode) {
if (InspectionResultsViewComparator.getInstance().compare(current, child) == 0) {
processDepth(child, current);
return current;
}
}
}
else if (current instanceof InspectionModuleNode) {
if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) {
processDepth(child, current);
return current;
else if (current instanceof InspectionNode) {
if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) {
processDepth(child, current);
return current;
}
}
else if (current instanceof InspectionModuleNode) {
if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) {
processDepth(child, current);
return current;
}
}
}
}
}
return parent.insertByOrder(child, false);
return parent.insertByOrder(child, false);
});
}
private static void processDepth(final InspectionTreeNode child, final InspectionTreeNode current) {
@@ -21,8 +21,12 @@
package com.intellij.codeInspection.ex;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.reference.*;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefModule;
import com.intellij.codeInspection.reference.RefUtil;
import com.intellij.codeInspection.ui.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
@@ -125,7 +129,8 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider
final RefElementNode elemNode = addNodeToParent(container, presentation, pNode);
for (CommonProblemDescriptor problem : problems) {
assert problem != null;
elemNode.insertByOrder(new ProblemDescriptionNode(refElement, problem, toolWrapper,presentation), true);
elemNode
.insertByOrder(ReadAction.compute(() -> new ProblemDescriptionNode(refElement, problem, toolWrapper, presentation)), true);
if (problems.length == 1) {
elemNode.setProblem(problems[0]);
}
@@ -30,6 +30,7 @@ import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
@@ -71,7 +72,8 @@ class OfflineDescriptorResolveResult {
@NotNull InspectionToolWrapper wrapper,
@NotNull InspectionToolPresentation presentation) {
final RefEntity element = descriptor.getRefElement(presentation.getContext().getRefManager());
final CommonProblemDescriptor resolvedDescriptor = createDescriptor(element, descriptor, wrapper, presentation);
final CommonProblemDescriptor resolvedDescriptor =
ReadAction.compute(() -> createDescriptor(element, descriptor, wrapper, presentation));
return new OfflineDescriptorResolveResult(element, resolvedDescriptor);
}
@@ -26,6 +26,7 @@ import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.containers.FactoryMap;
@@ -190,7 +191,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
for (OfflineProblemDescriptor descriptor : ((RefEntityContainer<OfflineProblemDescriptor>)container).getDescriptors()) {
final OfflineDescriptorResolveResult resolveResult = myResolvedDescriptor.get(toolWrapper.getShortName())
.computeIfAbsent(descriptor, d -> OfflineDescriptorResolveResult.resolve(d, toolWrapper, presentation));
elemNode.insertByOrder(OfflineProblemDescriptorNode.create(descriptor, resolveResult, toolWrapper, presentation), true);
elemNode.insertByOrder(ReadAction.compute(() -> OfflineProblemDescriptorNode.create(descriptor, resolveResult, toolWrapper, presentation)), true);
}
}
}
@@ -618,7 +618,7 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro
@NotNull Map<RefEntity, CommonProblemDescriptor[]> descriptorMap,
@Nullable CommonProblemDescriptor[] allowedDescriptors) {
final Set<CommonProblemDescriptor> allowedDescriptorSet = allowedDescriptors == null ? null : ContainerUtil.newHashSet(allowedDescriptors);
Map<Class, QuickFixAction> result = new com.intellij.util.containers.HashMap<>();
Map<String, LocalQuickFixWrapper> result = new com.intellij.util.containers.HashMap<>();
boolean isFirst = true;
for (RefEntity refElement : refElements) {
final CommonProblemDescriptor[] descriptors = descriptorMap.get(refElement);
@@ -632,34 +632,34 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro
if (isFirst) {
for (QuickFix fix : fixes) {
if (fix == null) continue;
final Class klass = getFixClass(fix);
LocalQuickFixWrapper quickFixWrapper = new LocalQuickFixWrapper(fix, myToolWrapper);
result.put(klass, quickFixWrapper);
result.put(fix.getFamilyName(), quickFixWrapper);
}
isFirst = false;
}
else {
for (Class clazz : new ArrayList<>(result.keySet())) {
for (String familyName : new ArrayList<>(result.keySet())) {
boolean isFound = false;
for (QuickFix fix : fixes) {
if (fix == null) continue;
final Class klass = getFixClass(fix);
if (clazz.equals(klass)) {
if (familyName.equals(fix.getFamilyName())) {
isFound = true;
final QuickFixAction quickFixAction = result.get(clazz);
final LocalQuickFixWrapper quickFixAction = result.get(fix.getFamilyName());
LOG.assertTrue(getFixClass(fix).equals(getFixClass(quickFixAction.getFix())),
"QuickFix-es with the same getFamilyName() should be the same class instances. " +
"Please assign reported exception for the fix \"" + fix.getClass().getName() + "\" developer");
try {
String familyName = fix.getFamilyName();
((LocalQuickFixWrapper)quickFixAction).setText(StringUtil.escapeMnemonics(familyName));
quickFixAction.setText(StringUtil.escapeMnemonics(fix.getFamilyName()));
}
catch (AbstractMethodError e) {
//for plugin compatibility
((LocalQuickFixWrapper)quickFixAction).setText("Name is not available");
quickFixAction.setText("Name is not available");
}
break;
}
}
if (!isFound) {
result.remove(clazz);
result.remove(familyName);
if (result.isEmpty()) {
return QuickFixAction.EMPTY;
}
@@ -759,14 +759,16 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
for (ScopeToolState state : myProvider.getTools(currentTools)) {
InspectionToolWrapper toolWrapper = state.getTool();
ReadAction.run(() -> {
if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
addTool(toolWrapper,
profile.getErrorLevel(key, state.getScope(myProject), myProject),
isGroupedBySeverity,
singleInspectionRun);
}
});
if (ReadAction.compute(() -> myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper))) {
//ReadAction.run(
// () ->
addTool(toolWrapper,
profile.getErrorLevel(key, state.getScope(myProject), myProject),
isGroupedBySeverity,
singleInspectionRun)
//)
;
}
}
}
}
@@ -18,6 +18,7 @@ package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.ui.tree.TreeUtil;
@@ -101,18 +102,20 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
}
public InspectionTreeNode insertByOrder(InspectionTreeNode child, boolean allowDuplication) {
if (!allowDuplication) {
int index = getIndex(child);
if (index != -1) {
return ReadAction.compute(() -> {
if (!allowDuplication) {
int index = getIndex(child);
if (index != -1) {
return (InspectionTreeNode)getChildAt(index);
}
}
int index = TreeUtil.indexedBinarySearch(this, child, InspectionResultsViewComparator.getInstance());
if (!allowDuplication && index >= 0){
return (InspectionTreeNode)getChildAt(index);
}
}
int index = TreeUtil.indexedBinarySearch(this, child, InspectionResultsViewComparator.getInstance());
if (!allowDuplication && index >= 0){
return (InspectionTreeNode)getChildAt(index);
}
insert(child, Math.abs(index + 1));
return child;
insert(child, Math.abs(index + 1));
return child;
});
}
@Override
@@ -17,60 +17,63 @@
package com.intellij.execution;
import com.intellij.CommonBundle;
import com.intellij.ide.GeneralSettings;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;
import com.intellij.util.ArrayUtil;
import java.util.ArrayList;
import java.util.List;
public class TerminateRemoteProcessDialog {
public static int show(final Project project,
public static GeneralSettings.ProcessCloseConfirmation show(final Project project,
final String sessionName,
final TerminateOption option) {
final String message = option.myAlwaysUseDefault && !option.myDetach ?
ExecutionBundle.message("terminate.process.confirmation.text", sessionName) :
ExecutionBundle.message("disconnect.process.confirmation.text", sessionName);
final String okButtonText = option.myAlwaysUseDefault && !option.myDetach ?
ExecutionBundle.message("button.terminate") :
ExecutionBundle.message("button.disconnect");
final String[] options = new String[] {okButtonText, CommonBundle.getCancelButtonText()};
return Messages.showDialog(project, message, ExecutionBundle.message("process.is.running.dialog.title", sessionName),
options, 0, Messages.getWarningIcon(),
option);
boolean canDisconnect,
boolean defaultDisconnect) {
GeneralSettings.ProcessCloseConfirmation confirmation = GeneralSettings.getInstance().getProcessCloseConfirmation();
if (confirmation != GeneralSettings.ProcessCloseConfirmation.ASK) {
if (confirmation == GeneralSettings.ProcessCloseConfirmation.DISCONNECT && !canDisconnect) {
confirmation = GeneralSettings.ProcessCloseConfirmation.TERMINATE;
}
return confirmation;
}
List<String> options = new ArrayList<>(3);
options.add(ExecutionBundle.message("button.terminate"));
if (canDisconnect) {
options.add(ExecutionBundle.message("button.disconnect"));
}
options.add(CommonBundle.getCancelButtonText());
DialogWrapper.DoNotAskOption.Adapter doNotAskOption = new DialogWrapper.DoNotAskOption.Adapter() {
@Override
public void rememberChoice(boolean isSelected, int exitCode) {
if (isSelected) {
GeneralSettings.ProcessCloseConfirmation confirmation = getConfirmation(exitCode, canDisconnect);
if (confirmation != null) {
GeneralSettings.getInstance().setProcessCloseConfirmation(confirmation);
}
}
}
};
return getConfirmation(Messages.showDialog(project,
ExecutionBundle.message("terminate.process.confirmation.text", sessionName),
ExecutionBundle.message("process.is.running.dialog.title", sessionName),
ArrayUtil.toStringArray(options),
canDisconnect && defaultDisconnect ? 1 : 0,
Messages.getWarningIcon(),
doNotAskOption), canDisconnect);
}
public static class TerminateOption implements DialogWrapper.DoNotAskOption {
private boolean myDetach;
private final boolean myAlwaysUseDefault;
public TerminateOption(boolean detachIsDefault, boolean alwaysUseDefault) {
myDetach = detachIsDefault;
myAlwaysUseDefault = alwaysUseDefault;
}
@Override
public boolean isToBeShown() {
return myDetach;
}
@Override
public void setToBeShown(boolean value, int exitCode) {
myDetach = value;
}
@Override
public boolean canBeHidden() {
return !myAlwaysUseDefault;
}
@Override
public boolean shouldSaveOptionsOnCancel() {
return false;
}
@NotNull
@Override
public String getDoNotShowMessage() {
return ExecutionBundle.message("terminate.after.disconnect.checkbox");
private static GeneralSettings.ProcessCloseConfirmation getConfirmation(int button, boolean withDisconnect) {
switch (button) {
case 0:
return GeneralSettings.ProcessCloseConfirmation.TERMINATE;
case 1:
if (withDisconnect) {
return GeneralSettings.ProcessCloseConfirmation.DISCONNECT;
}
default:
return null;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -23,6 +23,7 @@ import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.execution.ui.layout.impl.DockableGridContainerFactory;
import com.intellij.ide.DataManager;
import com.intellij.ide.GeneralSettings;
import com.intellij.ide.impl.ContentManagerWatcher;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataProvider;
@@ -35,7 +36,6 @@ import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
@@ -621,11 +621,13 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
}
else {
//todo[nik] this is a temporary solution for the following problem: some configurations should not allow user to choose between 'terminating' and 'detaching'
final boolean useDefault = Boolean.TRUE.equals(processHandler.getUserData(ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY));
final TerminateRemoteProcessDialog.TerminateOption option = new TerminateRemoteProcessDialog.TerminateOption(processHandler.detachIsDefault(), useDefault);
final int rc = TerminateRemoteProcessDialog.show(myProject, descriptor.getDisplayName(), option);
if (rc != DialogWrapper.OK_EXIT_CODE) return false;
destroyProcess = !option.isToBeShown();
boolean canDisconnect = !Boolean.TRUE.equals(processHandler.getUserData(ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY));
GeneralSettings.ProcessCloseConfirmation rc =
TerminateRemoteProcessDialog.show(myProject, descriptor.getDisplayName(), canDisconnect, processHandler.detachIsDefault());
if (rc == null) { // cancel
return false;
}
destroyProcess = rc == GeneralSettings.ProcessCloseConfirmation.TERMINATE;
}
if (destroyProcess) {
processHandler.destroyProcess();
@@ -99,10 +99,12 @@ public final class MapIndexStorage<Key, Value> implements IndexStorage<Key, Valu
private void initMapAndCache() throws IOException {
final ValueContainerMap<Key, Value> map;
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(ourProgressManagerCheckCancelledIOCanceller);
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(Boolean.TRUE);
try {
map = new ValueContainerMap<Key, Value>(getStorageFile(), myKeyDescriptor, myDataExternalizer, myKeyIsUniqueForIndexedFile);
} finally {
PersistentHashMapValueStorage.CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.set(null);
PersistentHashMapValueStorage.CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.set(null);
}
myCache = new SLRUCache<Key, ChangeTrackingValueContainer<Value>>(myCacheSize, (int)(Math.ceil(myCacheSize * 0.25)) /* 25% from the main cache size*/) {
@Override
@@ -22,9 +22,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -37,7 +35,6 @@ public class AnsiEscapeDecoder {
private static final String CSI = ESC_CHAR + "["; // "Control Sequence Initiator"
private static final Pattern INNER_PATTERN = Pattern.compile(Pattern.quote("m" + CSI));
private final Map<String, Key> myCachedKeys = new HashMap<String, Key>();
private Key myCurrentTextAttributes;
/**
@@ -68,7 +65,7 @@ public class AnsiEscapeDecoder {
// this is a simple fix for RUBY-8996:
// we replace several consecutive escape sequences with one which contains all these sequences
String colorAttribute = INNER_PATTERN.matcher(escSeq).replaceAll(";");
myCurrentTextAttributes = getOutputKey(colorAttribute);
myCurrentTextAttributes = ColoredOutputTypeRegistry.getInstance().getOutputKey(colorAttribute);
}
pos = escSeqEndInd;
}
@@ -80,16 +77,6 @@ public class AnsiEscapeDecoder {
}
}
@NotNull
private Key getOutputKey(@NotNull String attribute) {
Key key = myCachedKeys.get(attribute);
if (key == null) {
key = ColoredOutputTypeRegistry.getInstance().getOutputKey(attribute);
myCachedKeys.put(attribute, key);
}
return key;
}
/*
* Selects all consecutive escape sequences and returns escape sequence end index (exclusive).
* If the escape sequence isn't finished, returns -1.
@@ -28,6 +28,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author yole
@@ -37,6 +39,8 @@ public class ColoredOutputTypeRegistry {
return ServiceManager.getService(ColoredOutputTypeRegistry.class);
}
private final Map<String, Key> myRegisteredKeys = new HashMap<String, Key>();
private static final TextAttributesKey[] myAnsiColorKeys = new TextAttributesKey[]{
ConsoleHighlighter.BLACK,
ConsoleHighlighter.RED,
@@ -89,6 +93,10 @@ public class ColoredOutputTypeRegistry {
@NotNull
public Key getOutputKey(@NonNls String attribute) {
final Key key = myRegisteredKeys.get(attribute);
if (key != null) {
return key;
}
final String completeAttribute = attribute;
if (attribute.startsWith("\u001B[")) {
attribute = attribute.substring(2);
@@ -100,82 +108,11 @@ public class ColoredOutputTypeRegistry {
if (attribute.equals("0")) {
return ProcessOutputTypes.STDOUT;
}
boolean inverse = false;
TextAttributes attrs = new TextAttributes();
final String[] strings = attribute.split(";");
for (String string : strings) {
int value;
try {
value = Integer.parseInt(string);
}
catch (NumberFormatException e) {
continue;
}
if (value == 1) {
attrs.setFontType(Font.BOLD);
}
else if (value == 4) {
attrs.setEffectType(EffectType.LINE_UNDERSCORE);
}
else if (value == 7) {
inverse = true;
}
else if (value == 22) {
attrs.setFontType(Font.PLAIN);
}
else if (value == 24) { //not underlined
attrs.setEffectType(null);
}
else if (value >= 30 && value <= 37) {
attrs.setForegroundColor(getAnsiColor(value - 30));
}
else if (value == 38) {
//TODO: 256 colors foreground
}
else if (value == 39) {
attrs.setForegroundColor(getDefaultForegroundColor());
}
else if (value >= 40 && value <= 47) {
attrs.setBackgroundColor(getAnsiColor(value - 40));
}
else if (value == 48) {
//TODO: 256 colors background
}
else if (value == 49) {
attrs.setBackgroundColor(getDefaultBackgroundColor());
}
else if (value >= 90 && value <= 97) {
attrs.setForegroundColor(
getAnsiColor(value - 82));
}
else if (value >= 100 && value <= 107) {
attrs.setBackgroundColor(
getAnsiColor(value - 92));
}
}
if (attrs.getEffectType() == EffectType.LINE_UNDERSCORE) {
Color foregroundColor = attrs.getForegroundColor();
if (foregroundColor == null) {
foregroundColor = getDefaultForegroundColor();
}
attrs.setEffectColor(foregroundColor);
}
if (inverse) {
Color foregroundColor = attrs.getForegroundColor();
if (foregroundColor == null) {
foregroundColor = getDefaultForegroundColor();
}
Color backgroundColor = attrs.getBackgroundColor();
if (backgroundColor == null) {
backgroundColor = getDefaultBackgroundColor();
}
attrs.setForegroundColor(backgroundColor);
attrs.setEffectColor(backgroundColor);
attrs.setBackgroundColor(foregroundColor);
}
TextAttributes attrs = new AnsiTextAttributes(attribute);
Key newKey = new Key(completeAttribute);
ConsoleViewContentType contentType = new ConsoleViewContentType(completeAttribute, attrs);
ConsoleViewContentType.registerNewConsoleViewType(newKey, contentType);
myRegisteredKeys.put(completeAttribute, newKey);
return newKey;
}
@@ -214,4 +151,80 @@ public class ColoredOutputTypeRegistry {
}
return myAnsiColorKeys[value];
}
private static class AnsiTextAttributes extends TextAttributes {
int myForegroundColor = -1;
int myBackgroundColor = -1;
boolean myInverse = false;
public AnsiTextAttributes(String attribute) {
setEffectType(null);
final String[] strings = attribute.split(";");
for (String string : strings) {
int value;
try {
value = Integer.parseInt(string);
}
catch (NumberFormatException e) {
continue;
}
if (value == 1) {
setFontType(Font.BOLD);
}
else if (value == 4) {
setEffectType(EffectType.LINE_UNDERSCORE);
}
else if (value == 7) {
myInverse = true;
}
else if (value == 22) {
setFontType(Font.PLAIN);
}
else if (value == 24) { //not underlined
setEffectType(null);
}
else if (value >= 30 && value <= 37) {
myForegroundColor = value - 30;
}
else if (value == 38) {
//TODO: 256 colors foreground
}
else if (value == 39) {
myForegroundColor = -1;
}
else if (value >= 40 && value <= 47) {
myBackgroundColor = value - 40;
}
else if (value == 48) {
//TODO: 256 colors background
}
else if (value == 49) {
myBackgroundColor = -1;
}
else if (value >= 90 && value <= 97) {
myForegroundColor = value - 82;
}
else if (value >= 100 && value <= 107) {
myBackgroundColor = value - 92;
}
}
}
@Override
public Color getForegroundColor() {
if (myInverse) return myBackgroundColor < 0 ? getDefaultBackgroundColor() : getAnsiColor(myBackgroundColor);
return myForegroundColor < 0 ? getDefaultForegroundColor() : getAnsiColor(myForegroundColor);
}
@Override
public Color getBackgroundColor() {
if (myInverse) return myForegroundColor < 0 ? getDefaultForegroundColor() : getAnsiColor(myForegroundColor);
return myBackgroundColor < 0 ? getDefaultBackgroundColor() : getAnsiColor(myBackgroundColor);
}
@Override
public Color getEffectColor() {
return getEffectType() != null ? getForegroundColor() : null;
}
}
}
@@ -39,6 +39,8 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
public static final int OPEN_PROJECT_NEW_WINDOW = 0;
public static final int OPEN_PROJECT_SAME_WINDOW = 1;
public enum ProcessCloseConfirmation {ASK, TERMINATE, DISCONNECT}
public static final String PROP_INACTIVE_TIMEOUT = "inactiveTimeout";
public static final String PROP_SUPPORT_SCREEN_READERS = "supportScreenReaders";
@@ -56,6 +58,7 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
private boolean mySearchInBackground;
private boolean myConfirmExit = true;
private int myConfirmOpenNewProject = OPEN_PROJECT_ASK;
private ProcessCloseConfirmation myProcessCloseConfirmation = ProcessCloseConfirmation.ASK;
public static GeneralSettings getInstance(){
return ServiceManager.getService(GeneralSettings.class);
@@ -142,6 +145,14 @@ public class GeneralSettings implements PersistentStateComponent<GeneralSettings
);
}
public ProcessCloseConfirmation getProcessCloseConfirmation() {
return myProcessCloseConfirmation;
}
public void setProcessCloseConfirmation(ProcessCloseConfirmation processCloseConfirmation) {
myProcessCloseConfirmation = processCloseConfirmation;
}
@OptionTag("autoSyncFiles")
public boolean isSyncOnFrameActivation() {
return mySyncOnFrameActivation;
@@ -59,6 +59,7 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
settings.setSaveOnFrameDeactivation(myComponent.myChkSaveOnFrameDeactivation.isSelected());
settings.setConfirmExit(myComponent.myConfirmExit.isSelected());
settings.setConfirmOpenNewProject(getConfirmOpenNewProject());
settings.setProcessCloseConfirmation(getProcessCloseConfirmation());
settings.setAutoSaveIfInactive(myComponent.myChkAutoSaveIfInactive.isSelected());
try {
@@ -71,6 +72,18 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
settings.setUseSafeWrite(myComponent.myChkUseSafeWrite.isSelected());
}
private GeneralSettings.ProcessCloseConfirmation getProcessCloseConfirmation() {
if (myComponent.myTerminateProcessJBRadioButton.isSelected()) {
return GeneralSettings.ProcessCloseConfirmation.TERMINATE;
}
else if (myComponent.myDisconnectJBRadioButton.isSelected()) {
return GeneralSettings.ProcessCloseConfirmation.DISCONNECT;
}
else {
return GeneralSettings.ProcessCloseConfirmation.ASK;
}
}
@GeneralSettings.OpenNewProjectOption
private int getConfirmOpenNewProject() {
if (myComponent.myConfirmWindowToOpenProject.isSelected()) {
@@ -95,6 +108,7 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
isModified |= settings.isAutoSaveIfInactive() != myComponent.myChkAutoSaveIfInactive.isSelected();
isModified |= settings.isConfirmExit() != myComponent.myConfirmExit.isSelected();
isModified |= settings.getConfirmOpenNewProject() != getConfirmOpenNewProject();
isModified |= settings.getProcessCloseConfirmation() != getProcessCloseConfirmation();
int inactiveTimeout = -1;
try {
@@ -157,6 +171,17 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
myComponent.myOpenProjectInSameWindow.setSelected(true);
break;
}
switch (settings.getProcessCloseConfirmation()) {
case TERMINATE:
myComponent.myTerminateProcessJBRadioButton.setSelected(true);
break;
case DISCONNECT:
myComponent.myDisconnectJBRadioButton.setSelected(true);
break;
case ASK:
myComponent.myAskJBRadioButton.setSelected(true);
break;
}
}
public void disposeUIResources() {
@@ -183,6 +208,9 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable<Searchabl
private JBRadioButton myOpenProjectInSameWindow;
private JBRadioButton myConfirmWindowToOpenProject;
private JCheckBox myChkSupportScreenReaders;
private JBRadioButton myTerminateProcessJBRadioButton;
private JBRadioButton myDisconnectJBRadioButton;
private JBRadioButton myAskJBRadioButton;
public MyComponent() { }
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.ide.GeneralSettingsConfigurable.MyComponent">
<grid id="9d13a" binding="myPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="9d13a" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="32" y="45" width="637" height="645"/>
@@ -10,7 +10,7 @@
<children>
<vspacer id="4bc17">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="5dbad" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@@ -167,7 +167,7 @@
</hspacer>
<grid id="857f8" binding="myPluginOptionsPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -215,6 +215,43 @@
</component>
</children>
</grid>
<grid id="ef6e1" layout-manager="GridLayoutManager" row-count="1" 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>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
<BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
</clientProperties>
<border type="etched" title="On Process Tab Close"/>
<children>
<component id="a998a" class="com.intellij.ui.components.JBRadioButton" binding="myTerminateProcessJBRadioButton" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="radio.process.close.terminate"/>
</properties>
</component>
<component id="9903d" class="com.intellij.ui.components.JBRadioButton" binding="myDisconnectJBRadioButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="radio.process.close.disaconnect"/>
</properties>
</component>
<component id="9c112" class="com.intellij.ui.components.JBRadioButton" binding="myAskJBRadioButton" default-binding="true">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="radio.process.close.ask"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<buttonGroups>
@@ -227,5 +264,10 @@
<member id="45c11"/>
<member id="a31cc"/>
</group>
<group name="myProjectCloseGroup">
<member id="a998a"/>
<member id="9903d"/>
<member id="9c112"/>
</group>
</buttonGroups>
</form>
@@ -20,6 +20,7 @@ import com.intellij.openapi.ui.GraphicsConfig;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.Gray;
import com.intellij.ui.paint.RectanglePainter;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
@@ -130,17 +131,19 @@ public class DarculaTextFieldUI extends TextFieldWithPopupHandlerUI {
}
protected void paintSearchField(Graphics2D g, JTextComponent c, Rectangle r) {
g.setColor(c.getBackground());
final boolean noBorder = c.getClientProperty("JTextField.Search.noBorderRing") == Boolean.TRUE;
int radius = r.height-1;
g.fillRoundRect(r.x, r.y+1, r.width, r.height - (noBorder ? 2 : 1), radius, radius);
g.setColor(c.isEnabled() ? Gray._100 : Gray._83);
if (!noBorder) {
if (c.hasFocus()) {
DarculaUIUtil.paintSearchFocusRing(g, r);
} else {
g.drawRoundRect(r.x, r.y, r.width, r.height-1, radius, radius);
}
if (noBorder) {
g.setColor(c.getBackground());
RectanglePainter.FILL.paint(g, r.x, r.y, r.width, r.height, radius);
}
else if (c.hasFocus()) {
g.setColor(c.getBackground());
RectanglePainter.FILL.paint(g, r.x, r.y, r.width, r.height, radius);
DarculaUIUtil.paintSearchFocusRing(g, r);
}
else {
RectanglePainter.paint(g, r.x, r.y, r.width, r.height, radius, c.getBackground(), c.isEnabled() ? Gray._100 : Gray._83);
}
Point p = getSearchIconCoord();
Icon searchIcon = myTextField.getClientProperty("JTextField.Search.FindPopup") instanceof JPopupMenu ? UIManager.getIcon("TextField.darcula.searchWithHistory.icon") : UIManager.getIcon("TextField.darcula.search.icon");
@@ -462,16 +462,16 @@ public final class IterationState {
guard = null;
}
if (syntax != null && highlighter.getLayer() < HighlighterLayer.SYNTAX) {
cachedAttributes.add(syntax);
syntax = null;
}
if (caret != null && highlighter.getLayer() < HighlighterLayer.CARET_ROW) {
cachedAttributes.add(caret);
caret = null;
}
if (syntax != null && highlighter.getLayer() < HighlighterLayer.SYNTAX) {
cachedAttributes.add(syntax);
syntax = null;
}
TextAttributes textAttributes = highlighter.getTextAttributes();
if (textAttributes != null && textAttributes != TextAttributes.ERASE_MARKER) {
cachedAttributes.add(textAttributes);
@@ -484,8 +484,8 @@ public final class IterationState {
if (selection != null) cachedAttributes.add(selection);
if (fold != null) cachedAttributes.add(fold);
if (guard != null) cachedAttributes.add(guard);
if (syntax != null) cachedAttributes.add(syntax);
if (caret != null) cachedAttributes.add(caret);
if (syntax != null) cachedAttributes.add(syntax);
Color fore = null;
Color back = isInGuardedBlock ? myReadOnlyColor : null;
@@ -516,16 +516,16 @@ public class IterationState {
guard = null;
}
if (syntax != null && highlighter.getLayer() < HighlighterLayer.SYNTAX) {
cachedAttributes.add(syntax);
syntax = null;
}
if (caret != null && highlighter.getLayer() < HighlighterLayer.CARET_ROW) {
cachedAttributes.add(caret);
caret = null;
}
if (syntax != null && highlighter.getLayer() < HighlighterLayer.SYNTAX) {
cachedAttributes.add(syntax);
syntax = null;
}
TextAttributes textAttributes = highlighter.getTextAttributes();
if (textAttributes != null && textAttributes != TextAttributes.ERASE_MARKER) {
cachedAttributes.add(textAttributes);
@@ -535,8 +535,8 @@ public class IterationState {
if (selection != null) cachedAttributes.add(selection);
if (fold != null) cachedAttributes.add(fold);
if (guard != null) cachedAttributes.add(guard);
if (syntax != null) cachedAttributes.add(syntax);
if (caret != null) cachedAttributes.add(caret);
if (syntax != null) cachedAttributes.add(syntax);
Color fore = null;
Color back = isInGuardedBlock ? myReadOnlyColor : null;
@@ -38,6 +38,9 @@ checkbox.save.files.on.frame.deactivation=Save files on frame deactivation
checkbox.synchronize.files.on.frame.activation=Synchronize files on frame or editor tab activation
checkbox.reopen.last.project.on.startup=Reopen last project on startup
checkbox.support.screen.readers=Support screen readers (requires restart)
radio.process.close.terminate=Terminate Process
radio.process.close.disaconnect=Disconnect (if available)
radio.process.close.ask=Ask
treenode.loading= loading...
action.clear.list=_Clear List
action.clear.list.message=Would you like to clear the list of recent projects?
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

@@ -69,9 +69,10 @@ public class EditorPaintingTest extends AbstractEditorTest {
checkResult();
}
public void testCaretRowHasLowerPriorityThanSyntax() throws Exception {
public void testCaretRowWinsOverSyntaxEvenInPresenceOfHighlighter() throws Exception {
initText("foo");
setUniformEditorHighlighter(new TextAttributes(null, Color.red, null, null, Font.PLAIN));
addRangeHighlighter(0, 3, 0, null, Color.blue);
checkResult();
}
@@ -17,12 +17,17 @@ package com.intellij.testFramework;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionEP;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.LocalInspectionEP;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.testFramework.fixtures.impl.GlobalInspectionContextForTests;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.ui.UIUtil;
import org.jdom.Document;
import org.jdom.Element;
@@ -32,8 +37,7 @@ import org.junit.Assert;
import java.io.CharArrayReader;
import java.io.File;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class InspectionTestUtil {
private InspectionTestUtil() {
@@ -164,4 +168,20 @@ expected:
}
while (!globalContext.isFinished());
}
@NotNull
public static <T extends InspectionProfileEntry> List<InspectionProfileEntry> instantiateTools(@NotNull Collection<Class<? extends T>> inspections) {
Set<String> classNames = JBIterable.from(inspections).transform(Class::getName).toSet();
List<InspectionProfileEntry> tools = JBIterable.of(LocalInspectionEP.LOCAL_INSPECTION, InspectionEP.GLOBAL_INSPECTION)
.flatten((o) -> Arrays.asList(o.getExtensions()))
.filter((o) -> classNames.contains(o.implementationClass))
.transform(InspectionEP::instantiateTool)
.toList();
if (tools.size() != classNames.size()) {
Set<String> missing = ContainerUtil.newTreeSet(classNames);
missing.removeAll(JBIterable.from(tools).transform((o) -> o.getClass().getName()).toSet());
throw new RuntimeException("Unregistered inspections requested: " + missing);
}
return tools;
}
}
@@ -21,7 +21,9 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
@@ -45,7 +47,6 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
import com.intellij.openapi.fileTypes.FileType;
@@ -346,26 +347,9 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da
((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
}
// todo: use Class<? extends InspectionProfileEntry> once on Java 7
protected void enableInspectionTools(@NotNull Class<?>... classes) {
final InspectionProfileEntry[] tools = new InspectionProfileEntry[classes.length];
final List<InspectionEP> eps = ContainerUtil.newArrayList();
ContainerUtil.addAll(eps, Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION));
ContainerUtil.addAll(eps, Extensions.getExtensions(InspectionEP.GLOBAL_INSPECTION));
next:
for (int i = 0; i < classes.length; i++) {
for (InspectionEP ep : eps) {
if (classes[i].getName().equals(ep.implementationClass)) {
tools[i] = ep.instantiateTool();
continue next;
}
}
throw new IllegalArgumentException("Unable to find extension point for " + classes[i].getName());
}
enableInspectionTools(tools);
protected void enableInspectionTools(@NotNull Class<? extends InspectionProfileEntry>... classes) {
List<InspectionProfileEntry> tools = InspectionTestUtil.instantiateTools(Arrays.asList(classes));
enableInspectionTools(tools.toArray(new InspectionProfileEntry[tools.size()]));
}
protected void enableInspectionTools(@NotNull InspectionProfileEntry... tools) {
@@ -115,6 +115,7 @@ import com.intellij.testFramework.fixtures.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.ui.UIUtil;
import junit.framework.ComparisonFailure;
@@ -418,16 +419,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
@Override
public void enableInspections(@NotNull final Collection<Class<? extends LocalInspectionTool>> inspections) {
List<LocalInspectionTool> tools = new ArrayList<LocalInspectionTool>();
for (Class<? extends LocalInspectionTool> clazz : inspections) {
try {
LocalInspectionTool inspection = clazz.getConstructor().newInstance();
tools.add(inspection);
}
catch (Exception e) {
throw new RuntimeException("Cannot instantiate " + clazz);
}
}
List<InspectionProfileEntry> tools = InspectionTestUtil.instantiateTools(inspections);
enableInspections(tools.toArray(new LocalInspectionTool[tools.size()]));
}
@@ -441,22 +433,13 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
@Override
public void enableInspections(@NotNull InspectionToolProvider... providers) {
List<LocalInspectionTool> tools = new ArrayList<LocalInspectionTool>();
for (InspectionToolProvider provider : providers) {
for (Class<?> clazz : provider.getInspectionClasses()) {
try {
Object o = clazz.getConstructor().newInstance();
if (o instanceof LocalInspectionTool) {
LocalInspectionTool inspection = (LocalInspectionTool)o;
tools.add(inspection);
}
}
catch (Exception e) {
throw new RuntimeException("Cannot instantiate " + clazz, e);
}
}
}
enableInspections(tools.toArray(new LocalInspectionTool[tools.size()]));
List<Class<? extends LocalInspectionTool>> classes = JBIterable.of(providers)
.flatten((o) -> Arrays.asList(o.getInspectionClasses()))
.transform((Function<Class, Class<? extends LocalInspectionTool>>)o ->
LocalInspectionTool.class.isAssignableFrom(o) ? (Class<? extends LocalInspectionTool>) o : null)
.filter(Conditions.notNull())
.toList();
enableInspections(classes);
}
@Override
@@ -105,6 +105,9 @@ public class MultiValuesMap<K, V>{
return myBaseMap.containsKey(key);
}
/**
* @deprecated this method will return unordered set for ordered map, use {@link #values()} instead
*/
@NotNull
public Collection<V> collectValues() {
Collection<V> result = new HashSet<V>();
@@ -526,17 +526,33 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
myEnumerator.unlockStorage();
}
PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(valueOffset);
final PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(valueOffset);
DataInputStream input = new DataInputStream(new UnsyncByteArrayInputStream(readResult.buffer));
final Value valueRead;
try {
valueRead = myValueExternalizer.read(input);
}
finally {
input.close();
}
if (myValueStorage.performChunksCompaction(readResult.chunksCount, readResult.buffer.length)) {
long newValueOffset = myValueStorage.compactChunks(new ValueDataAppender() {
@Override
public void append(DataOutput out) throws IOException {
myValueExternalizer.save(out, valueRead);
}
}, readResult);
if (readResult.offset != valueOffset) { // compacted several chunks produced during append
myEnumerator.lockStorage();
try {
myEnumerator.markDirty(true);
if (myDirectlyStoreLongFileOffsetMode) {
((PersistentBTreeEnumerator<Key>)myEnumerator).putNonnegativeValue(key, readResult.offset);
((PersistentBTreeEnumerator<Key>)myEnumerator).putNonnegativeValue(key, newValueOffset);
} else {
updateValueId(id, readResult.offset, valueOffset, key, 0);
updateValueId(id, newValueOffset, valueOffset, key, 0);
}
myLiveAndGarbageKeysCounter++;
myReadCompactionGarbageSize += readResult.buffer.length;
@@ -544,14 +560,7 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
myEnumerator.unlockStorage();
}
}
final DataInputStream input = new DataInputStream(new UnsyncByteArrayInputStream(readResult.buffer));
try {
return myValueExternalizer.read(input);
}
finally {
input.close();
}
return valueRead;
}
public final boolean containsMapping(Key key) throws IOException {
@@ -596,10 +605,12 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
if (myDirectlyStoreLongFileOffsetMode) {
assert !myIntMapping; // removal isn't supported
record = ((PersistentBTreeEnumerator<Key>)myEnumerator).getNonnegativeValue(key);
((PersistentBTreeEnumerator<Key>)myEnumerator).putNonnegativeValue(key, NULL_ADDR);
if (record != NULL_ADDR) {
((PersistentBTreeEnumerator<Key>)myEnumerator).putNonnegativeValue(key, NULL_ADDR);
}
} else {
final int id = tryEnumerate(key);
if (id == PersistentEnumerator.NULL_ID) {
if (id == PersistentEnumeratorBase.NULL_ID) {
return;
}
assert !myIntMapping; // removal isn't supported
@@ -38,6 +38,7 @@ public class PersistentHashMapValueStorage {
private final File myFile;
private final String myPath;
private final boolean myReadOnly;
private final boolean myCompactChunksWithValueDeserialization;
private final ExceptionalIOCancellationCallback myExceptionalIOCancellationCallback;
private boolean myCompactionMode = false;
@@ -47,6 +48,7 @@ public class PersistentHashMapValueStorage {
public static class CreationTimeOptions {
public static final ThreadLocal<ExceptionalIOCancellationCallback> EXCEPTIONAL_IO_CANCELLATION = new ThreadLocal<ExceptionalIOCancellationCallback>();
public static final ThreadLocal<Boolean> READONLY = new ThreadLocal<Boolean>();
public static final ThreadLocal<Boolean> COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION = new ThreadLocal<Boolean>();
}
public interface ExceptionalIOCancellationCallback {
@@ -102,6 +104,7 @@ public class PersistentHashMapValueStorage {
public PersistentHashMapValueStorage(String path) throws IOException {
myExceptionalIOCancellationCallback = CreationTimeOptions.EXCEPTIONAL_IO_CANCELLATION.get();
myReadOnly = CreationTimeOptions.READONLY.get() == Boolean.TRUE;
myCompactChunksWithValueDeserialization = CreationTimeOptions.COMPACT_CHUNKS_WITH_VALUE_DESERIALIZATION.get() == Boolean.TRUE;
myPath = path;
myFile = new File(path);
@@ -143,14 +146,14 @@ public class PersistentHashMapValueStorage {
}
public long appendBytes(byte[] data, int offset, int dataLength, long prevChunkAddress) throws IOException {
assert !myCompactionMode && !myReadOnly;
assert allowedToCompactChunks();
long result = mySize; // volatile read
final FileAccessorCache.Handle<DataOutputStream> appender = myCompressedAppendableFile != null? null : ourAppendersCache.get(myPath);
DataOutputStream dataOutputStream;
try {
if (myCompressedAppendableFile != null) {
BufferExposingByteArrayOutputStream stream = new BufferExposingByteArrayOutputStream();
BufferExposingByteArrayOutputStream stream = new BufferExposingByteArrayOutputStream(dataLength + 15);
DataOutputStream testStream = new DataOutputStream(stream);
saveData(data, offset, dataLength, prevChunkAddress, result, testStream);
myCompressedAppendableFile.append(stream.getInternalBuffer(), stream.size());
@@ -339,17 +342,21 @@ public class PersistentHashMapValueStorage {
}
public static class ReadResult {
public final long offset;
public final byte[] buffer;
public final int chunksCount;
public ReadResult(long offset, byte[] buffer) {
this.offset = offset;
public ReadResult(byte[] buffer, int chunksCount) {
this.buffer = buffer;
this.chunksCount = chunksCount;
}
}
private long myChunksRemovalTime;
private long myChunksReadingTime;
private int myChunks;
private long myChunksOriginalBytes;
private long myChunksBytesAfterRemoval;
private int myLastReportedChunksCount;
/**
* Reads bytes pointed by tailChunkAddress into result passed, returns new address if linked list compactification have been performed
@@ -428,22 +435,55 @@ public class PersistentHashMapValueStorage {
}
}
if (chunkCount > 1 && !myCompactionMode && !myReadOnly) {
if (chunkCount > 1) {
checkCancellation();
long endCompactionTime = ourDumpChunkRemovalTime ? System.nanoTime() : 0;
long diff = endCompactionTime - startedTime;
myChunksRemovalTime += diff;
myChunksReadingTime += (ourDumpChunkRemovalTime ? System.nanoTime() : 0) - startedTime;
myChunks += chunkCount;
if (ourDumpChunkRemovalTime && chunkCount > 2) {
System.out.println("Removed " + chunkCount + " chunks for " + (diff / 1000000) + "ms, bytes: " + result.length + ", total: " +
(myChunksRemovalTime / 1000000) + "ms for " + myChunks + " chunks in " + myPath);
}
long l = appendBytes(new ByteSequence(result), 0);
return new ReadResult(l, result);
myChunksOriginalBytes += result.length;
}
return new ReadResult(tailChunkAddress, result);
return new ReadResult(result, chunkCount);
}
private boolean allowedToCompactChunks() {
return !myCompactionMode && !myReadOnly;
}
boolean performChunksCompaction(int chunksCount, int chunksBytesSize) {
return chunksCount > 1 && allowedToCompactChunks();
}
long compactChunks(PersistentHashMap.ValueDataAppender appender, ReadResult result) throws IOException {
checkCancellation();
long startedTime = ourDumpChunkRemovalTime ? System.nanoTime() : 0;
long newValueOffset;
if (myCompactChunksWithValueDeserialization) {
final BufferExposingByteArrayOutputStream stream = new BufferExposingByteArrayOutputStream(result.buffer.length);
DataOutputStream testStream = new DataOutputStream(stream);
appender.append(testStream);
newValueOffset = appendBytes(stream.getInternalBuffer(), 0, stream.size(), 0);
myChunksBytesAfterRemoval += stream.size();
} else {
newValueOffset = appendBytes(new ByteSequence(result.buffer), 0);
myChunksBytesAfterRemoval += result.buffer.length;
}
if (ourDumpChunkRemovalTime) {
myChunksRemovalTime += System.nanoTime() - startedTime;
if (myChunks - myLastReportedChunksCount > 1000) {
myLastReportedChunksCount = myChunks;
System.out.println(myChunks + " chunks were read " + (myChunksReadingTime / 1000000) +
"ms, bytes: " + myChunksOriginalBytes +
(myChunksOriginalBytes != myChunksBytesAfterRemoval ? "->" + myChunksBytesAfterRemoval : "") +
" compaction:" + (myChunksRemovalTime / 1000000) + "ms in " + myPath);
}
}
return newValueOffset;
}
private static final boolean ourDumpChunkRemovalTime = SystemProperties.getBooleanProperty("idea.phmp.dump.chunk.removal.time", false);
@@ -18,7 +18,6 @@ package com.intellij.openapi.vcs.ex;
import com.intellij.diff.comparison.ByLine;
import com.intellij.diff.comparison.ComparisonPolicy;
import com.intellij.diff.comparison.TrimUtil;
import com.intellij.diff.comparison.iterables.DiffIterable;
import com.intellij.diff.comparison.iterables.DiffIterableUtil;
import com.intellij.diff.comparison.iterables.FairDiffIterable;
import com.intellij.diff.util.DiffUtil;
@@ -27,7 +26,6 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.progress.DumbProgressIndicator;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.ex.Range.InnerRange;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.diff.FilesTooBigForDiffException;
import org.jetbrains.annotations.NotNull;
@@ -69,7 +67,7 @@ public class RangesBuilder {
int vcsShift) throws FilesTooBigForDiffException {
FairDiffIterable iterable = ByLine.compare(vcs, current, ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE);
List<Range> result = new ArrayList<Range>();
List<Range> result = new ArrayList<>();
for (com.intellij.diff.util.Range range : iterable.iterateChanges()) {
int vcsLine1 = vcsShift + range.start1;
int vcsLine2 = vcsShift + range.end1;
@@ -106,7 +104,7 @@ public class RangesBuilder {
rangeBuilder.flushChange();
}
else {
rangeBuilder.markChangedWhitespaces(vcsIndex, currentIndex);
rangeBuilder.markChanged(vcsIndex, currentIndex);
}
}
}
@@ -124,10 +122,12 @@ public class RangesBuilder {
private final int myCurrentShift;
private final int myVcsShift;
@NotNull private final List<Range> myRanges = new ArrayList<>();
@NotNull private final List<Range> myResult = new ArrayList<>();
private com.intellij.diff.util.Range change;
private ArrayList<InnerRange> innerRanges;
private int vcsLine1 = -1;
private int vcsLine2 = -1;
private int currentLine1 = -1;
private int currentLine2 = -1;
public RangeBuilder(@NotNull List<String> current,
@NotNull List<String> vcs,
@@ -140,66 +140,70 @@ public class RangesBuilder {
}
public void flushChange() {
if (change == null) return;
if (vcsLine1 == -1) return;
for (InnerRange range : innerRanges) {
range.shift(myCurrentShift);
}
innerRanges.trimToSize();
int forward = TrimUtil.expandForward(myVcs, myCurrent, vcsLine1, currentLine1, vcsLine2, currentLine2);
vcsLine1 += forward;
currentLine1 += forward;
int currentLine1 = myCurrentShift + change.start2;
int currentLine2 = myCurrentShift + change.end2;
int vcsLine1 = myVcsShift + change.start1;
int vcsLine2 = myVcsShift + change.end1;
myRanges.add(new Range(currentLine1, currentLine2, vcsLine1, vcsLine2, innerRanges));
int backward = TrimUtil.expandBackward(myVcs, myCurrent, vcsLine1, currentLine1, vcsLine2, currentLine2);
vcsLine2 -= backward;
currentLine2 -= backward;
change = null;
innerRanges = null;
List<String> vcs = myVcs.subList(vcsLine1, vcsLine2);
List<String> current = myCurrent.subList(currentLine1, currentLine2);
List<InnerRange> inner = calcInnerRanges(vcs, current, myCurrentShift + currentLine1);
myResult.add(new Range(myCurrentShift + currentLine1, myCurrentShift + currentLine2,
myVcsShift + vcsLine1, myVcsShift + vcsLine2,
inner));
currentLine1 = -1;
currentLine2 = -1;
vcsLine1 = -1;
vcsLine2 = -1;
}
public void markChangedWhitespaces(int vcsIndex, int currentIndex) {
appendChangedLine(vcsIndex, vcsIndex + 1, currentIndex, currentIndex + 1);
appendInnerEquals(vcsIndex, vcsIndex + 1, currentIndex, currentIndex + 1);
public void markChanged(int vcsIndex, int currentIndex) {
markChanged(vcsIndex, vcsIndex + 1, currentIndex, currentIndex + 1);
}
public void markChanged(int vcsStart, int vcsEnd, int currentStart, int currentEnd) {
appendChangedLine(vcsStart, vcsEnd, currentStart, currentEnd);
appendInnerChange(vcsStart, vcsEnd, currentStart, currentEnd);
if (vcsLine1 == -1) {
vcsLine1 = vcsStart;
vcsLine2 = vcsEnd;
currentLine1 = currentStart;
currentLine2 = currentEnd;
}
else {
assert vcsStart == vcsLine2;
assert currentStart == currentLine2;
vcsLine2 = vcsEnd;
currentLine2 = currentEnd;
}
}
@NotNull
public List<Range> finish() {
flushChange();
return myRanges;
return myResult;
}
}
private void appendChangedLine(int vcsStart, int vcsEnd, int currentStart, int currentEnd) {
if (change == null) {
change = new com.intellij.diff.util.Range(vcsStart, vcsEnd, currentStart, currentEnd);
innerRanges = new ArrayList<>();
}
else {
assert vcsStart == change.end1;
assert currentStart == change.end2;
change = new com.intellij.diff.util.Range(change.start1, vcsEnd, change.start2, currentEnd);
}
}
@NotNull
private static List<InnerRange> calcInnerRanges(@NotNull List<String> vcs, @NotNull List<String> current, int startOffset) {
ArrayList<InnerRange> result = new ArrayList<>();
FairDiffIterable iwIterable = ByLine.compare(vcs, current, ComparisonPolicy.IGNORE_WHITESPACES, DumbProgressIndicator.INSTANCE);
for (Pair<com.intellij.diff.util.Range, Boolean> pair : DiffIterableUtil.iterateAll(iwIterable)) {
com.intellij.diff.util.Range range = pair.first;
Boolean equals = pair.second;
private void appendInnerChange(int vcsStart, int vcsEnd, int currentStart, int currentEnd) {
byte type = getChangeType(vcsStart, vcsEnd, currentStart, currentEnd);
innerRanges.add(new InnerRange(currentStart, currentEnd, type));
}
private void appendInnerEquals(int vcsStart, int vcsEnd, int currentStart, int currentEnd) {
InnerRange last = ContainerUtil.getLastItem(innerRanges);
if (last == null || last.getType() != Range.EQUAL) {
innerRanges.add(new InnerRange(currentStart, currentEnd, Range.EQUAL));
}
else {
assert currentStart == last.getLine2();
innerRanges.set(innerRanges.size() - 1, new InnerRange(last.getLine1(), currentEnd, Range.EQUAL));
}
byte type = equals ? Range.EQUAL : getChangeType(range.start1, range.end1, range.start2, range.end2);
result.add(new InnerRange(range.start2 + startOffset, range.end2 + startOffset, type));
}
result.trimToSize();
return result;
}
private static byte getChangeType(int vcsStart, int vcsEnd, int currentStart, int currentEnd) {
@@ -2387,6 +2387,10 @@
key="control.flow.statement.without.braces.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.style.ControlFlowStatementWithoutBracesInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="SingleStatementInBlock" bundle="com.siyeh.InspectionGadgetsBundle"
key="single.statement.in.block.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.style.SingleStatementInBlockInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="EqualsCalledOnEnumConstant" bundle="com.siyeh.InspectionGadgetsBundle"
key="equals.called.on.enum.constant.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="false" level="WARNING"
@@ -2202,4 +2202,6 @@ if.to.assertion.descriptor=Replace 'if' with 'assert' statement
replace.equality.with.equals.name=Replace Equality with Equals
replace.equality.with.equals.descriptor=Replace ''{0}'' with ''{1}equals()''
replace.equality.with.safe.equals.name=Replace Equality with Safe Equals
replace.equality.with.safe.equals.descriptor=Replace ''{0}'' with safe ''{1}equals()''
replace.equality.with.safe.equals.descriptor=Replace ''{0}'' with safe ''{1}equals()''
single.statement.in.block.name=Code Block Contains Single Statement
single.statement.in.block.descriptor=Remove braces from ''{0}'' statement
@@ -0,0 +1,117 @@
/*
* Copyright 2000-2016 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 com.siyeh.ig.style;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import org.jetbrains.annotations.Contract;
public abstract class ControlFlowStatementVisitorBase extends BaseInspectionVisitor {
private final HighlightDisplayKey myKey;
protected ControlFlowStatementVisitorBase(BaseInspection inspection) {
final String shortName = inspection.getShortName();
myKey = HighlightDisplayKey.find(shortName);
}
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
super.visitForeachStatement(statement);
if (isApplicable(statement.getBody())) {
registerKeywordOrStatementError(statement, PsiKeyword.FOR);
}
}
@Override
public void visitForStatement(PsiForStatement statement) {
super.visitForStatement(statement);
if (isApplicable(statement.getBody())) {
registerKeywordOrStatementError(statement, PsiKeyword.FOR);
}
}
@Override
public void visitWhileStatement(PsiWhileStatement statement) {
super.visitWhileStatement(statement);
if (isApplicable(statement.getBody())) {
registerKeywordOrStatementError(statement, PsiKeyword.WHILE);
}
}
@Override
public void visitDoWhileStatement(PsiDoWhileStatement statement) {
super.visitDoWhileStatement(statement);
if (isApplicable(statement.getBody())) {
registerKeywordOrStatementError(statement, PsiKeyword.DO);
}
}
@Override
public void visitIfStatement(PsiIfStatement statement) {
super.visitIfStatement(statement);
final PsiStatement thenBranch = statement.getThenBranch();
if (isApplicable(thenBranch)) {
registerKeywordOrStatementError(statement.getFirstChild(), thenBranch, PsiKeyword.IF);
}
final PsiStatement elseBranch = statement.getElseBranch();
if (isApplicable(elseBranch)) {
registerKeywordOrStatementError(statement.getElseElement(), elseBranch, PsiKeyword.ELSE);
}
}
@Contract("null->false")
protected abstract boolean isApplicable(PsiStatement body);
private void registerKeywordOrStatementError(PsiStatement statement, String text) {
boolean highlightOnlyKeyword = isHighlightOnlyKeyword(statement);
if (highlightOnlyKeyword) {
registerStatementError(statement, text);
}
else {
registerError(statement, text);
}
}
private void registerKeywordOrStatementError(PsiElement keyword, PsiStatement body, String text) {
boolean highlightOnlyKeyword = isHighlightOnlyKeyword(body);
if (highlightOnlyKeyword) {
registerError(keyword != null ? keyword : body, text);
}
else {
registerErrorAtRange(keyword != null ? keyword : body, body, text);
}
}
private boolean isHighlightOnlyKeyword(PsiElement element) {
if (!isOnTheFly()) {
return true;
}
if (myKey != null) {
final Project project = element.getProject();
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
final HighlightDisplayLevel errorLevel = profile.getErrorLevel(myKey, element);
return !HighlightDisplayLevel.DO_NOT_SHOW.equals(errorLevel);
}
return false;
}
}
@@ -15,12 +15,8 @@
*/
package com.siyeh.ig.style;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
@@ -28,18 +24,12 @@ import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
public class ControlFlowStatementWithoutBracesInspection
extends BaseInspection {
@NonNls private static final String DO_TEXT = "do";
@NonNls private static final String ELSE_TEXT = "else";
@NonNls private static final String FOR_TEXT = "for";
@NonNls private static final String IF_TEXT = "if";
@NonNls private static final String WHILE_TEXT = "while";
@Override
@NotNull
public String getDisplayName() {
@@ -58,11 +48,11 @@ public class ControlFlowStatementWithoutBracesInspection
public InspectionGadgetsFix buildFix(Object... infos) {
if (infos.length == 1 && infos[0] instanceof String) {
switch ((String)infos[0]) {
case DO_TEXT: return new DoBracesFix();
case ELSE_TEXT: return new ElseBracesFix();
case FOR_TEXT: return new ForBracesFix();
case IF_TEXT: return new IfBracesFix();
case WHILE_TEXT: return new WhileBracesFix();
case PsiKeyword.DO: return new DoBracesFix();
case PsiKeyword.ELSE: return new ElseBracesFix();
case PsiKeyword.FOR: return new ForBracesFix();
case PsiKeyword.IF: return new IfBracesFix();
case PsiKeyword.WHILE: return new WhileBracesFix();
}
}
return null;
@@ -138,122 +128,24 @@ public class ControlFlowStatementWithoutBracesInspection
@Override
public BaseInspectionVisitor buildVisitor() {
final String shortName = getShortName();
final HighlightDisplayKey key = HighlightDisplayKey.find(shortName);
return new ControlFlowStatementVisitor(key);
return new ControlFlowStatementVisitor(this);
}
private static class ControlFlowStatementVisitor
extends BaseInspectionVisitor {
private HighlightDisplayKey myKey;
public ControlFlowStatementVisitor(HighlightDisplayKey key) {
myKey = key;
private static class ControlFlowStatementVisitor extends ControlFlowStatementVisitorBase {
private ControlFlowStatementVisitor(BaseInspection inspection) {
super(inspection);
}
@Contract("null->false")
@Override
public void visitDoWhileStatement(PsiDoWhileStatement statement) {
super.visitDoWhileStatement(statement);
final PsiStatement body = statement.getBody();
if (body == null || body instanceof PsiBlockStatement) {
return;
}
registerKeywordOrStatementError(statement, DO_TEXT);
}
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
super.visitForeachStatement(statement);
final PsiStatement body = statement.getBody();
if (body == null || body instanceof PsiBlockStatement) {
return;
}
registerKeywordOrStatementError(statement, FOR_TEXT);
}
@Override
public void visitForStatement(PsiForStatement statement) {
super.visitForStatement(statement);
final PsiStatement body = statement.getBody();
if (body == null || body instanceof PsiBlockStatement) {
return;
}
registerKeywordOrStatementError(statement, FOR_TEXT);
}
@Override
public void visitIfStatement(PsiIfStatement statement) {
super.visitIfStatement(statement);
final PsiStatement thenBranch = statement.getThenBranch();
if (thenBranch == null) {
return;
}
boolean highlightOnlyKeyword = isHighlightOnlyKeyword(statement);
if (!(thenBranch instanceof PsiBlockStatement)) {
if (highlightOnlyKeyword) {
registerStatementError(statement, IF_TEXT);
}
else {
final PsiElement startElement = statement.getFirstChild();
registerErrorAtRange(startElement != null ? startElement : thenBranch, thenBranch, IF_TEXT);
}
}
final PsiStatement elseBranch = statement.getElseBranch();
if (elseBranch == null) {
return;
}
if (!(elseBranch instanceof PsiBlockStatement) &&
!(elseBranch instanceof PsiIfStatement)) {
final PsiKeyword elseKeyword = statement.getElseElement();
if (elseKeyword == null) {
return;
}
if (highlightOnlyKeyword) {
registerError(elseKeyword, ELSE_TEXT);
}
else {
registerErrorAtRange(elseKeyword, elseBranch, ELSE_TEXT);
}
}
}
@Override
public void visitWhileStatement(PsiWhileStatement statement) {
super.visitWhileStatement(statement);
final PsiStatement body = statement.getBody();
if (body == null || body instanceof PsiBlockStatement) {
return;
}
registerKeywordOrStatementError(statement, WHILE_TEXT);
}
private void registerKeywordOrStatementError(PsiStatement statement, String text) {
boolean highlightOnlyKeyword = isHighlightOnlyKeyword(statement);
if (highlightOnlyKeyword) {
registerStatementError(statement, text);
}
else {
registerError(statement, text);
}
}
private boolean isHighlightOnlyKeyword(PsiElement element) {
if (!isOnTheFly()) {
return true;
}
if (myKey != null) {
final Project project = element.getProject();
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
HighlightDisplayLevel errorLevel = profile.getErrorLevel(myKey, element);
return !HighlightDisplayLevel.DO_NOT_SHOW.equals(errorLevel);
}
return false;
protected boolean isApplicable(PsiStatement body) {
return body != null && !(body instanceof PsiBlockStatement);
}
}
private static class DoBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return DO_TEXT; } }
private static class ElseBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return ELSE_TEXT; } }
private static class ForBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return FOR_TEXT; } }
private static class IfBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return IF_TEXT; } }
private static class WhileBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return WHILE_TEXT; } }
private static class DoBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return PsiKeyword.DO; } }
private static class ElseBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return PsiKeyword.ELSE; } }
private static class ForBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return PsiKeyword.FOR; } }
private static class IfBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return PsiKeyword.IF; } }
private static class WhileBracesFix extends ControlFlowStatementFix { @Override String getKeywordText() { return PsiKeyword.WHILE; } }
}
@@ -0,0 +1,173 @@
/*
* Copyright 2000-2016 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 com.siyeh.ig.style;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.FileTypeUtils;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SingleStatementInBlockInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("single.statement.in.block.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("single.statement.in.block.descriptor", infos);
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new SingleStatementInBlockVisitor(this);
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
if (infos.length == 1 && infos[0] instanceof String) {
switch ((String)infos[0]) {
case PsiKeyword.DO:
return new RemoveDoBracesFix();
case PsiKeyword.ELSE:
return new RemoveElseBracesFix();
case PsiKeyword.FOR:
return new RemoveForBracesFix();
case PsiKeyword.IF:
return new RemoveIfBracesFix();
case PsiKeyword.WHILE:
return new RemoveWhileBracesFix();
}
}
return null;
}
private static void doFixImpl(@NotNull PsiBlockStatement blockStatement) {
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
final PsiStatement[] statements = codeBlock.getStatements();
final PsiStatement statement = statements[0];
handleComments(blockStatement, codeBlock);
final String text = statement.getText();
PsiReplacementUtil.replaceStatement(blockStatement, text);
}
private static void handleComments(PsiBlockStatement blockStatement, PsiCodeBlock codeBlock) {
final PsiElement parent = blockStatement.getParent();
assert parent != null;
final PsiElement grandParent = parent.getParent();
assert grandParent != null;
PsiElement sibling = codeBlock.getFirstChild();
assert sibling != null;
sibling = sibling.getNextSibling();
while (sibling != null) {
if (sibling instanceof PsiComment) {
grandParent.addBefore(sibling, parent);
}
sibling = sibling.getNextSibling();
}
final PsiElement lastChild = blockStatement.getLastChild();
if (lastChild instanceof PsiComment) {
final PsiElement nextSibling = parent.getNextSibling();
grandParent.addAfter(lastChild, nextSibling);
}
}
private static class SingleStatementInBlockVisitor extends ControlFlowStatementVisitorBase {
protected SingleStatementInBlockVisitor(BaseInspection inspection) {
super(inspection);
}
@Contract("null->false")
@Override
protected boolean isApplicable(PsiStatement body) {
if (body instanceof PsiBlockStatement) {
final PsiBlockStatement statement = (PsiBlockStatement)body;
final PsiStatement[] statements = statement.getCodeBlock().getStatements();
if (statements.length == 1 && !(statements[0] instanceof PsiDeclarationStatement)) {
final PsiFile file = statement.getContainingFile();
//this inspection doesn't work in JSP files, as it can't tell about tags
// inside the braces
if (!FileTypeUtils.isInServerPageFile(file)) {
return true;
}
}
}
return false;
}
}
private static abstract class SingleStatementInBlockFix extends InspectionGadgetsFix {
@Nls
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("single.statement.in.block.descriptor", getKeywordText());
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionGadgetsBundle.message("single.statement.in.block.name");
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
final PsiElement startElement = descriptor.getStartElement();
final PsiElement startParent = startElement.getParent();
final PsiElement body;
if (startElement instanceof PsiLoopStatement) {
body = ((PsiLoopStatement)startElement).getBody();
}
else if (startParent instanceof PsiLoopStatement) {
body = ((PsiLoopStatement)startParent).getBody();
}
else {
assert startElement instanceof PsiKeyword;
assert startParent instanceof PsiIfStatement;
PsiIfStatement ifStatement = (PsiIfStatement)startParent;
body = ((PsiKeyword)startElement).getTokenType() == JavaTokenType.IF_KEYWORD
? ifStatement.getThenBranch()
: ifStatement.getElseBranch();
}
assert body instanceof PsiBlockStatement;
doFixImpl((PsiBlockStatement)body);
}
abstract String getKeywordText();
}
private static class RemoveDoBracesFix extends SingleStatementInBlockFix { @Override String getKeywordText() { return PsiKeyword.DO; } }
private static class RemoveElseBracesFix extends SingleStatementInBlockFix { @Override String getKeywordText() { return PsiKeyword.ELSE; } }
private static class RemoveForBracesFix extends SingleStatementInBlockFix { @Override String getKeywordText() { return PsiKeyword.FOR; } }
private static class RemoveIfBracesFix extends SingleStatementInBlockFix { @Override String getKeywordText() { return PsiKeyword.IF; } }
private static class RemoveWhileBracesFix extends SingleStatementInBlockFix { @Override String getKeywordText() { return PsiKeyword.WHILE; } }
}
@@ -1,6 +1,6 @@
<html>
<body>
This intention removes braces from control flow statements with
This inspection reports control flow statements with
only a single statement in their code block.
</body>
</html>
@@ -0,0 +1,6 @@
class X {
{
do System.out.println();
while (true)<caret>;
}
}
@@ -0,0 +1,8 @@
class X {
{
do {
System.out.println();
}
while (true)<caret>;
}
}
@@ -0,0 +1,5 @@
class X {
void f(int[] a){
for(int i: a) System.out.println(i);
}
}
@@ -0,0 +1,7 @@
class X {
void f(int[] a){
for(int i: a) <caret> {
System.out.println(i);
}
}
}
@@ -0,0 +1,5 @@
class X {
void f(int[] a){
for(int i=0; i<a.length; i++) System.out.println(a[i]);
}
}
@@ -0,0 +1,7 @@
class X {
void f(int[] a){
for(int i=0; i<a.length; i++) {
System.out.println(a[i]);<caret>
}
}
}
@@ -2,7 +2,7 @@ class X {
{
if (true) {
System.out.println();
}<caret>
<caret>}
else {
System.out.println();
}
@@ -0,0 +1,5 @@
class X {
{
while (true) System.out.println();
}
}
@@ -0,0 +1,7 @@
class X {
{
while (true)<caret> {
System.out.println();
}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2016 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 com.siyeh.ig.fixes.braces;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.IGQuickFixesTestCase;
import com.siyeh.ig.style.SingleStatementInBlockInspection;
/**
* @author Bas Leijdekkers
* @author Pavel.Dolgov
*/
public class SingleStatementInBlockFixTest extends IGQuickFixesTestCase {
public void testBetweenIfAndElse() { assertQuickfixNotAvailable(getMessagePrefix());}
public void testIfElse() { doTest("if"); }
public void testIfElse2() { doTest("if"); }
public void testWhile() { doTest("while"); }
public void testForEach() { doTest("for"); }
public void testForIndex() { doTest("for"); }
public void testDoWhile() { doTest("do"); }
@Override
protected void setUp() throws Exception {
super.setUp();
myRelativePath = "single_statement_block";
}
@Override
protected BaseInspection getInspection() {
return new SingleStatementInBlockInspection();
}
protected void doTest(String keyword) {
super.doTest(getMessage(keyword));
}
private static String getMessage(String keyword) {
return InspectionGadgetsBundle.message("single.statement.in.block.descriptor", keyword);
}
private static String getMessagePrefix() {
final String message = InspectionGadgetsBundle.message("single.statement.in.block.descriptor", "@");
final int index = message.indexOf("@");
if (index >= 0) return message.substring(0, index);
return message;
}
}
@@ -351,11 +351,6 @@
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
<categoryKey>intention.category.control.flow</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.braces.RemoveBracesIntention</className>
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
<categoryKey>intention.category.control.flow</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.forloop.ReverseForLoopDirectionIntention</className>
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
@@ -89,7 +89,6 @@ demorgans.intention.family.name=DeMorgan Law
flip.comparison.intention.family.name=Flip Comparison
flip.conjunction.intention.family.name=Flip Conjunction Operands
negate.comparison.intention.family.name=Negate Comparison
remove.braces.intention.family.name=Remove Braces
flip.commutative.method.call.intention.family.name=Flip Commutative Method Call
constant.subexpression.intention.family.name=Compute Constant Value for Subexpression
extract.increment.intention.family.name=Extract Increment
@@ -171,7 +170,6 @@ flip.smth.intention.name1=Flip ''{0}'' (changes semantics)
flip.comparison.intention.name=Flip ''{0}'' to ''{1}''
negate.comparison.intention.name=Negate ''{0}''
negate.comparison.intention.name1=Negate ''{0}'' to ''{1}''
remove.braces.intention.name=Remove braces from ''{0}'' statement
flip.commutative.method.call.intention.name=Flip ''.{0}()''
flip.commutative.method.call.intention.name1=Flip ''.{0}()'' (may change semantics)
constant.expression.intention.name=Compute constant value of ''{0}''
@@ -1,76 +0,0 @@
/*
* Copyright 2000-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 com.siyeh.ipp.braces;
import com.intellij.psi.*;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.base.MutablyNamedIntention;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Danila Ponomarenko
*/
public abstract class BaseBracesIntention extends MutablyNamedIntention {
protected final String getTextForElement(PsiElement element) {
final PsiElement body = getSurroundingStatement(element);
if (body == null) {
return null;
}
return IntentionPowerPackBundle.message(getMessageKey(), getKeyword(body.getParent(), body));
}
@NotNull
protected abstract String getMessageKey();
@NotNull
private static String getKeyword(@NotNull PsiElement parent, @NotNull PsiElement element) {
if (parent instanceof PsiIfStatement) {
final PsiIfStatement ifStatement = (PsiIfStatement)parent;
final PsiStatement elseBranch = ifStatement.getElseBranch();
return element.equals(elseBranch) ? PsiKeyword.ELSE : PsiKeyword.IF;
}
final PsiElement firstChild = parent.getFirstChild();
assert firstChild != null;
return firstChild.getText();
}
@Nullable
protected static PsiStatement getSurroundingStatement(@NotNull PsiElement element) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiIfStatement) {
final PsiIfStatement ifStatement = (PsiIfStatement)parent;
final PsiStatement thenBranch = ifStatement.getThenBranch();
final int offset = element.getTextOffset();
if (thenBranch != null && offset > thenBranch.getTextOffset()) {
final PsiKeyword elseElement = ifStatement.getElseElement();
if (elseElement == null || offset < elseElement.getTextOffset()) {
// no 'else' branch or after 'then' branch but before 'else' keyword
return null;
}
return ifStatement.getElseBranch();
}
return thenBranch;
}
if (parent instanceof PsiLoopStatement) {
return ((PsiLoopStatement)parent).getBody();
}
return null;
}
}
@@ -1,91 +0,0 @@
/*
* Copyright 2003-2006 Dave Griffith, Bas Leijdekkers
*
* 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 com.siyeh.ipp.braces;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.psi.util.FileTypeUtils;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
public class RemoveBracesIntention extends BaseBracesIntention {
@NotNull
protected PsiElementPredicate getElementPredicate() {
return new PsiElementPredicate() {
@Override
public boolean satisfiedBy(PsiElement element) {
final PsiStatement statement = getSurroundingStatement(element);
if (statement == null || !(statement instanceof PsiBlockStatement)) {
return false;
}
final PsiStatement[] statements = ((PsiBlockStatement)statement).getCodeBlock().getStatements();
if (statements.length != 1 || statements[0] instanceof PsiDeclarationStatement) {
return false;
}
final PsiFile file = statement.getContainingFile();
//this intention doesn't work in JSP files, as it can't tell about tags
// inside the braces
return !FileTypeUtils.isInServerPageFile(file);
}
};
}
@NotNull
@Override
protected String getMessageKey() {
return "remove.braces.intention.name";
}
protected void processIntention(@NotNull PsiElement element)
throws IncorrectOperationException {
final PsiStatement body = getSurroundingStatement(element);
if (body == null || !(body instanceof PsiBlockStatement)) return;
final PsiBlockStatement blockStatement = (PsiBlockStatement)body;
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
final PsiStatement[] statements = codeBlock.getStatements();
final PsiStatement statement = statements[0];
handleComments(blockStatement, codeBlock);
final String text = statement.getText();
PsiReplacementUtil.replaceStatement(blockStatement, text);
}
private static void handleComments(PsiBlockStatement blockStatement, PsiCodeBlock codeBlock) {
final PsiElement parent = blockStatement.getParent();
assert parent != null;
final PsiElement grandParent = parent.getParent();
assert grandParent != null;
PsiElement sibling = codeBlock.getFirstChild();
assert sibling != null;
sibling = sibling.getNextSibling();
while (sibling != null) {
if (sibling instanceof PsiComment) {
grandParent.addBefore(sibling, parent);
}
sibling = sibling.getNextSibling();
}
final PsiElement lastChild = blockStatement.getLastChild();
if (lastChild instanceof PsiComment) {
final PsiElement nextSibling = parent.getNextSibling();
grandParent.addAfter(lastChild, nextSibling);
}
}
}
@@ -1,7 +0,0 @@
public class X {
int f(boolean b) {
if (b)
return 1;
return 0;
}
}
@@ -1,8 +0,0 @@
public class X {
int f(boolean b) {
if (b) <spot>{
return 1;
}</spot>
return 0;
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2000-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 com.siyeh.ipp.braces;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.IPPTestCase;
/**
* @see RemoveBracesIntention
* @author Bas Leijdekkers
*/
public class RemoveBracesIntentionTest extends IPPTestCase {
@Override
protected String getRelativePath() {
return "braces/remove";
}
@Override
protected String getIntentionName() {
return IntentionPowerPackBundle.message("remove.braces.intention.name", "if");
}
public void testBetweenIfAndElse() { assertIntentionNotAvailable(RemoveBracesIntention.class);}
public void testIfElse() { doTest(); }
public void testIfElse2() { doTest(); }
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

After

Width:  |  Height:  |  Size: 660 KiB

@@ -26,6 +26,7 @@ import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.impl.PyClassImpl;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.testing.PythonUnitTestUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -69,7 +70,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
if (containingClass == null) return;
final String name = node.getName();
if (name != null && name.startsWith("_")) return;
if (!isApplicable(containingClass)) {
if (!isApplicable(containingClass, myTypeEvalContext)) {
return;
}
@@ -111,7 +112,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
}
}
private static boolean isApplicable(@NotNull final PyClass containingClass) {
return !PythonUnitTestUtil.isUnitTestCaseClass(containingClass) && !containingClass.isSubclass("django.db.models.base.Model", null);
private static boolean isApplicable(@NotNull PyClass containingClass, @NotNull TypeEvalContext context) {
return !PythonUnitTestUtil.isUnitTestCaseClass(containingClass) && !containingClass.isSubclass("django.db.models.base.Model", context);
}
}
@@ -120,7 +120,8 @@ public class PyCallByClassInspection extends PyInspection {
if (method != null) {
PyClass calling_class = method.getContainingClass();
assert calling_class != null; // it's a method
if (first_arg_class.isSubclass(qual_class, null) && calling_class.isSubclass(qual_class, null)) {
if (first_arg_class.isSubclass(qual_class, myTypeEvalContext) &&
calling_class.isSubclass(qual_class, myTypeEvalContext)) {
break;
// TODO: might propose to switch to super() here
}
@@ -121,7 +121,7 @@ public class PyMethodParametersInspection extends PyInspection {
PsiElement zope_interface = findZopeInterface(node);
final PyClass cls = node.getContainingClass();
if (zope_interface instanceof PyClass) {
if (cls != null && cls.isSubclass((PyClass) zope_interface, null)) return; // it can have any params
if (cls != null && cls.isSubclass((PyClass) zope_interface, myTypeEvalContext)) return; // it can have any params
}
// analyze function itself
PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(node);
@@ -148,7 +148,7 @@ public class PyPropertyAccessInspection extends PyInspection {
.stream()
.filter(PyTargetExpression.class::isInstance)
.map(declaration -> ((PyTargetExpression)declaration).getContainingClass())
.filter(declaringClass -> declaringClass != null && !attributeIsWritable(qualifierClass, declaringClass, target))
.filter(declaringClass -> declaringClass != null && !attributeIsWritable(qualifierClass, declaringClass, targetName))
.findFirst()
.ifPresent(
cls -> registerProblem(target, String.format("'%s' object attribute '%s' is read-only", qualifierClass.getName(), targetName))
@@ -157,27 +157,28 @@ public class PyPropertyAccessInspection extends PyInspection {
}
}
private boolean attributeIsWritable(@NotNull PyClass qualifierClass,
@NotNull PyClass declaringClass,
@NotNull PyTargetExpression target) {
return attributeIsWritableInClass(qualifierClass, declaringClass, target) ||
private boolean attributeIsWritable(@NotNull PyClass qualifierClass, @NotNull PyClass declaringClass, @NotNull String targetName) {
return attributeIsWritableInClass(qualifierClass, declaringClass, targetName) ||
qualifierClass
.getAncestorClasses(myTypeEvalContext)
.stream()
.filter(ancestorClass -> !PyUtil.isObjectClass(ancestorClass))
.anyMatch(ancestorClass -> attributeIsWritableInClass(ancestorClass, declaringClass, target));
.anyMatch(ancestorClass -> attributeIsWritableInClass(ancestorClass, declaringClass, targetName));
}
private static boolean attributeIsWritableInClass(@NotNull PyClass cls,
@NotNull PyClass declaringClass,
@NotNull PyTargetExpression target) {
private boolean attributeIsWritableInClass(@NotNull PyClass cls, @NotNull PyClass declaringClass, @NotNull String targetName) {
final List<String> ownSlots = cls.getOwnSlots();
return ownSlots == null ||
ownSlots.contains(PyNames.DICT) ||
(LanguageLevel.forElement(target).isPy3K() &&
cls.equals(declaringClass) &&
ownSlots.contains(target.getName()));
if (ownSlots == null || ownSlots.contains(PyNames.DICT)) {
return true;
}
if (!cls.equals(declaringClass) || !ownSlots.contains(targetName)) {
return false;
}
return LanguageLevel.forElement(declaringClass).isAtLeast(LanguageLevel.PYTHON30) ||
declaringClass.findClassAttribute(targetName, false, myTypeEvalContext) == null;
}
}
}
@@ -28,7 +28,6 @@ import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.util.PsiElementFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.Processor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.inspections.quickfix.PyUpdatePropertySignatureQuickFix;
@@ -287,7 +286,7 @@ public class PyPropertyDefinitionInspection extends PyInspection {
private void checkForSelf(PyParameterList paramList) {
PyParameter[] parameters = paramList.getParameters();
final PyClass cls = PsiTreeUtil.getParentOfType(paramList, PyClass.class);
if (cls != null && cls.isSubclass("type", null)) return;
if (cls != null && cls.isSubclass("type", myTypeEvalContext)) return;
if (parameters.length > 0 && !PyNames.CANONICAL_SELF.equals(parameters[0].getName())) {
registerProblem(
parameters[0], PyBundle.message("INSP.accessor.first.param.is.$0", PyNames.CANONICAL_SELF), ProblemHighlightType.WEAK_WARNING,
@@ -141,12 +141,12 @@ public class PyProtectedMemberInspection extends PyInspection {
if (parentClass != null) {
if (PyTestUtil.isPyTestClass(parentClass, null) && ignoreTestFunctions) return;
if (parentClass.isSubclass(resolvedClass, null))
if (parentClass.isSubclass(resolvedClass, myTypeEvalContext))
return;
PyClass outerClass = getClassOwner(parentClass);
while (outerClass != null) {
if (outerClass.isSubclass(resolvedClass, null))
if (outerClass.isSubclass(resolvedClass, myTypeEvalContext))
return;
outerClass = getClassOwner(outerClass);
@@ -430,7 +430,7 @@ public class PyStringFormatInspection extends PyInspection {
else {
final PyClassType type = as(myTypeEvalContext.getType(rightExpression), PyClassType.class);
if (type != null) {
if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING, null)) {
if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING, myTypeEvalContext)) {
registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping"));
return;
}
@@ -65,7 +65,7 @@ public class PySuperArgumentsInspection extends PyInspection {
PyClass firstClass = findClassOf(arguments[0]);
PyClass secondClass = findClassOf(arguments[1]);
if (firstClass != null && secondClass != null) {
if (!secondClass.isSubclass(firstClass, null)) {
if (!secondClass.isSubclass(firstClass, myTypeEvalContext)) {
registerProblem(
node.getArgumentList(),
PyBundle.message("INSP.$0.is.not.superclass.of.$1",
@@ -96,8 +96,9 @@ public class AddMethodQuickFix implements LocalQuickFix {
boolean madeInstance = false;
if (callByClass) {
if (args.length > 0) {
PyType firstArgType = TypeEvalContext.userInitiated(cls.getProject(), cls.getContainingFile()).getType(args[0]);
if (firstArgType instanceof PyClassType && ((PyClassType)firstArgType).getPyClass().isSubclass(cls, null)) {
final TypeEvalContext context = TypeEvalContext.userInitiated(cls.getProject(), cls.getContainingFile());
final PyType firstArgType = context.getType(args[0]);
if (firstArgType instanceof PyClassType && ((PyClassType)firstArgType).getPyClass().isSubclass(cls, context)) {
// class, first arg ok: instance method
builder.parameter("self"); // NOTE: might use a name other than 'self', according to code style.
madeInstance = true;
@@ -16,7 +16,6 @@
package com.jetbrains.python.psi.impl;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
@@ -367,7 +366,7 @@ public class PyCallExpressionHelper {
PyClass resolvedParent = PsiTreeUtil.getStubOrPsiParentOfType(resolved, PyClass.class);
if (resolvedParent != null) {
final PyClass qualifierClass = ((PyClassType)qualifierType).getPyClass();
if ((qualifierClass.isSubclass(resolvedParent, null) || resolvedParent.isSubclass(qualifierClass, null))) {
if ((qualifierClass.isSubclass(resolvedParent, context) || resolvedParent.isSubclass(qualifierClass, context))) {
return true;
}
}
@@ -278,10 +278,6 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
return false;
}
public boolean isSubclass(PyClass parent) {
return isSubclass(parent, null);
}
@Override
public boolean isSubclass(@NotNull String superClassQName, @Nullable TypeEvalContext context) {
if (context == null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -176,16 +176,18 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
}
if (parent instanceof PyTupleExpression) {
PsiElement nextParent = parent.getParent();
while (nextParent instanceof PyParenthesizedExpression) {
while (nextParent instanceof PyParenthesizedExpression || nextParent instanceof PyTupleExpression) {
nextParent = nextParent.getParent();
}
if (nextParent instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)nextParent;
final PyExpression value = assignment.getAssignedValue();
if (value != null) {
final PyExpression lhs = assignment.getLeftHandSideExpression();
final PyTupleExpression targetTuple = PsiTreeUtil.findChildOfType(lhs, PyTupleExpression.class, false);
if (value != null && targetTuple != null) {
final PyType assignedType = PyTypeChecker.toNonWeakType(context.getType(value), context);
if (assignedType instanceof PyTupleType) {
final PyType t = PyTypeChecker.getTargetTypeFromTupleAssignment(this, (PyTupleExpression)parent, (PyTupleType)assignedType);
final PyType t = PyTypeChecker.getTargetTypeFromTupleAssignment(this, targetTuple, (PyTupleType)assignedType);
if (t != null) {
return t;
}
@@ -501,8 +501,7 @@ public class PyQualifiedReference extends PyReferenceImpl {
PyClass aClass = PsiTreeUtil.getParentOfType(resolveResult, PyClass.class);
PyClass bClass = PsiTreeUtil.getParentOfType(element, PyClass.class);
if (isSubclass(aClass, bClass)
|| (isSubclass(bClass, aClass))) {
if (aClass != null && bClass != null && bClass.isSubclass(aClass, myContext.getTypeEvalContext())) {
return true;
}
}
@@ -513,13 +512,6 @@ public class PyQualifiedReference extends PyReferenceImpl {
return false;
}
private static boolean isSubclass(@Nullable PyClass aClass, @Nullable PyClass bClass) {
if (aClass == null || bClass == null) {
return false;
}
return bClass.isSubclass(aClass, null);
}
private static boolean isLocalScope(PsiElement element) {
if (element instanceof PyParameter) {
return true;
@@ -530,7 +530,8 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
if (PyUtil.isClassAttribute(myElement) && (PyUtil.isClassAttribute(expr) || PyUtil.isInstanceAttribute(expr))) {
final PyClass c1 = PsiTreeUtil.getParentOfType(element, PyClass.class);
final PyClass c2 = PsiTreeUtil.getParentOfType(myElement, PyClass.class);
if (c1 != null && c2 != null && (c1.isSubclass(c2, null) || c2.isSubclass(c1, null))) {
final TypeEvalContext context = myContext.getTypeEvalContext();
if (c1 != null && c2 != null && (c1.isSubclass(c2, context) || c2.isSubclass(c1, context))) {
return true;
}
}
@@ -44,7 +44,7 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor<PsiElement, P
if (!queryParameters.isDeepSearch()) {
boolean isAlreadyFound = false;
for (PyClass alreadyFound : foundMethodContainingClasses) {
if (alreadyFound.isSubclass(superClass, null)) {
if (alreadyFound.isSubclass(superClass, context)) {
isAlreadyFound = true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -27,10 +27,10 @@ public class PyABCUtil {
private PyABCUtil() {
}
public static boolean isSubclass(@NotNull PyClass subClass, @NotNull PyClass superClass) {
public static boolean isSubclass(@NotNull PyClass subClass, @NotNull PyClass superClass, @Nullable TypeEvalContext context) {
final String superName = superClass.getName();
if (superName != null) {
return isSubclass(subClass, superName, true, null);
return isSubclass(subClass, superName, true, context);
}
return false;
}
@@ -107,11 +107,11 @@ public class PyABCUtil {
final PyClassLikeType metaClassType = classType.getMetaClassType(context, true);
if (metaClassType instanceof PyClassType) {
final PyClassType metaClass = (PyClassType)metaClassType;
return isSubclass(metaClass.getPyClass(), superClassName, true, null);
return isSubclass(metaClass.getPyClass(), superClassName, true, context);
}
}
else {
return isSubclass(pyClass, superClassName, true, null);
return isSubclass(pyClass, superClassName, true, context);
}
}
if (type instanceof PyUnionType) {
@@ -430,10 +430,11 @@ public class PyTypeChecker {
}
private static boolean matchClasses(@Nullable PyClass superClass, @Nullable PyClass subClass, @NotNull TypeEvalContext context) {
if (superClass == null || subClass == null || subClass.isSubclass(superClass, null) || PyABCUtil.isSubclass(subClass, superClass)) {
return true;
}
else if (PyUtil.hasUnresolvedAncestors(subClass, context)) {
if (superClass == null ||
subClass == null ||
subClass.isSubclass(superClass, context) ||
PyABCUtil.isSubclass(subClass, superClass, context) ||
PyUtil.hasUnresolvedAncestors(subClass, context)) {
return true;
}
else {
@@ -0,0 +1,5 @@
class Test(object):
__slots__ = 'test', 'test2'
def __init__(self):
self.test = 1
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -834,6 +834,30 @@ public class PyTypeTest extends PyTestCase {
"expr = (1, False) * 2");
}
public void testTupleDestructuring() {
doTest("str",
"_, expr = (1, 'val') ");
}
public void testParensTupleDestructuring() {
doTest("str",
"(_, expr) = (1, 'val') ");
}
// PY-19825
public void testSubTupleDestructuring() {
doTest("str",
"(a, (_, expr)) = (1, (2,'val')) ");
}
// PY-19825
public void testSubTupleIndirectDestructuring() {
doTest("str",
"xs = (2,'val')\n" +
"(a, (_, expr)) = (1, xs) ");
}
public void testConstructorUnification() {
doTest("C[int]",
"class C(object):\n" +
@@ -1065,6 +1089,20 @@ public class PyTypeTest extends PyTestCase {
"expr = func()");
}
// PY-19967
public void testInheritedNamedTupleReplace() {
PyExpression expr = parseExpr("from collections import namedtuple\n" +
"class MyClass(namedtuple('T', 'a b c')):\n" +
" def get_foo(self):\n" +
" return self.a\n" +
"\n" +
"inst = MyClass(1,2,3)\n" +
"expr = inst._replace(a=2)\n");
doTest("MyClass",
expr,
TypeEvalContext.userInitiated(expr.getProject(), expr.getContainingFile()));
}
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());

Some files were not shown because too many files have changed in this diff Show More