Merge remote-tracking branch 'origin/master'
@@ -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;
|
||||
|
||||
|
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 236 B |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 233 B |
|
Before Width: | Height: | Size: 216 B After Width: | Height: | Size: 227 B |
|
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?
|
||||
|
||||
|
Before Width: | Height: | Size: 132 B |
|
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(); }
|
||||
}
|
||||
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 234 KiB |
|
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());
|
||||
|
||||