Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2016-02-17 16:09:39 +01:00
126 changed files with 2339 additions and 1231 deletions
+1 -1
View File
@@ -1 +1 @@
144.SNAPSHOT
146.SNAPSHOT
@@ -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.
@@ -40,6 +40,7 @@ import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
@@ -100,9 +101,9 @@ public class HighlightMethodUtil {
int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList());
if (accessLevel < superAccessLevel) {
String description = JavaErrorMessages.message("weaker.privileges",
createClashMethodMessage(method, superMethod, true),
accessModifier,
PsiUtil.getAccessModifier(superAccessLevel));
createClashMethodMessage(method, superMethod, true),
VisibilityUtil.toPresentableText(accessModifier),
PsiUtil.getAccessModifier(superAccessLevel));
TextRange textRange;
if (includeRealPositionInfo) {
if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
@@ -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.
@@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Processor;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.fixes.ChangeModifierFix;
import com.siyeh.ig.psiutils.MethodUtils;
@@ -185,12 +186,14 @@ class AccessCanBeTightenedInspection extends BaseJavaBatchLocalInspectionTool {
log(member.getName() + " unused; ignore");
return; // do not propose private for unused method
}
int max = maxLevel.get();
if (max == PsiUtil.ACCESS_LEVEL_PRIVATE && memberClass == null) {
max = suggestPackageLocal(member);
}
log(member.getName()+": effective level is '" + PsiUtil.getAccessModifier(max) + "'");
String maxModifier = PsiUtil.getAccessModifier(max);
log(member.getName() + ": effective level is '" + maxModifier + "'");
if (max < currentLevel) {
if (max == PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL && member instanceof PsiClass && childMembersAreUsedOutsideMyPackage.contains(member)) {
@@ -205,7 +208,7 @@ class AccessCanBeTightenedInspection extends BaseJavaBatchLocalInspectionTool {
}
});
assert toHighlight != null : member +" ; " + ((PsiNameIdentifierOwner)member).getNameIdentifier() + "; "+ memberModifierList.getText();
myHolder.registerProblem(toHighlight, "Access can be "+PsiUtil.getAccessModifier(max), new ChangeModifierFix(PsiUtil.getAccessModifier(max)));
myHolder.registerProblem(toHighlight, "Access can be " + VisibilityUtil.toPresentableText(maxModifier), new ChangeModifierFix(maxModifier));
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInspection.deadCode;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.GlobalJavaInspectionContext;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.ex.*;
@@ -39,7 +40,7 @@ public class DummyEntryPointsPresentation extends UnusedDeclarationPresentation
}
@Override
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) {
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) {
if (myQuickFixActions == null) {
myQuickFixActions = new QuickFixAction[]{new MoveEntriesToSuspicious(getToolWrapper())};
}
@@ -145,7 +145,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta
}
@Override
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) {
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) {
return myQuickFixActions;
}
@@ -143,7 +143,7 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
}
protected static boolean hasMethod(PsiClass targetClass, PsiMethod method) {
PsiMethod[] targetClassMethods = targetClass.getMethods();
PsiMethod[] targetClassMethods = targetClass.findMethodsByName(method.getName(), true);
for (PsiMethod candidate : targetClassMethods) {
if (candidate != method &&
MethodSignatureUtil.areSignaturesEqual(method.getSignature(PsiSubstitutor.EMPTY),
@@ -155,15 +155,8 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
}
protected static boolean hasField(PsiClass targetClass, PsiField field) {
String fieldName = field.getName();
PsiField[] targetClassFields = targetClass.getFields();
for (PsiField candidate : targetClassFields) {
if (candidate != field &&
fieldName.equals(candidate.getName())) {
return true;
}
}
return false;
final PsiField fieldByName = targetClass.findFieldByName(field.getName(), true);
return fieldByName != null && fieldByName != field;
}
@Override
@@ -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.
@@ -24,6 +24,7 @@ import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
@@ -123,7 +124,11 @@ public final class PsiUtil extends PsiUtilCore {
if (type instanceof PsiCapturedWildcardType) {
final PsiType upperBound = ((PsiCapturedWildcardType)type).getUpperBound();
if (upperBound instanceof PsiClassType) {
String classText = "class I<T extends " + upperBound.getCanonicalText() + "> {}";
final PsiClass resolved = ((PsiClassType)upperBound).resolve();
final PsiFile containingFile = resolved != null ? resolved.getContainingFile() : null;
final String packageName = containingFile instanceof PsiClassOwner ? ((PsiClassOwner)containingFile).getPackageName() : null;
String classText = StringUtil.isEmptyOrSpaces(packageName) ? "" : "package " +packageName + ";\n ";
classText += "class I<T extends " + upperBound.getCanonicalText() + "> {}";
final PsiJavaFile file =
(PsiJavaFile)PsiFileFactory.getInstance(expression.getProject()).createFileFromText("inference_dummy.java", JavaLanguage.INSTANCE, classText);
final PsiTypeParameter freshParameter = file.getClasses()[0].getTypeParameters()[0];
@@ -348,10 +353,11 @@ public final class PsiUtil extends PsiUtilCore {
}
@PsiModifier.ModifierConstant
@Nullable
@NotNull
public static String getAccessModifier(@AccessLevel int accessLevel) {
assert accessLevel > 0 && accessLevel <= accessModifiers.length : accessLevel;
@SuppressWarnings("UnnecessaryLocalVariable") @PsiModifier.ModifierConstant
final String modifier = accessLevel > accessModifiers.length ? null : accessModifiers[accessLevel - 1];
final String modifier = accessModifiers[accessLevel - 1];
return modifier;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 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.
@@ -31,6 +31,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class VisibilityUtil {
@@ -107,7 +108,8 @@ public class VisibilityUtil {
}
@Nls
public static String getVisibilityStringToDisplay(PsiMember member) {
@NotNull
public static String getVisibilityStringToDisplay(@NotNull PsiMember member) {
if (member.hasModifierProperty(PsiModifier.PUBLIC)) {
return toPresentableText(PsiModifier.PUBLIC);
}
@@ -120,7 +122,8 @@ public class VisibilityUtil {
return toPresentableText(PsiModifier.PACKAGE_LOCAL);
}
public static String toPresentableText(@PsiModifier.ModifierConstant String modifier) {
@NotNull
public static String toPresentableText(@PsiModifier.ModifierConstant @NotNull String modifier) {
return PsiBundle.visibilityPresentation(modifier);
}
@@ -150,7 +150,8 @@ public class StrictSubtypingConstraint implements ConstraintFormula {
final PsiType tSubstituted = tSubstitutor.substitute(parameter);
final PsiType sSubstituted = sSubstitutor.substituteWithBoundsPromotion(parameter);
if (tSubstituted == null ^ sSubstituted == null) {
return false;
session.setErased();
return true;
}
constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted));
}
@@ -75,7 +75,7 @@ public class TypeCompatibilityConstraint implements ConstraintFormula {
final PsiClassType.ClassResolveResult sResult = ((PsiClassType)s).resolveGenerics();
final PsiClass tClass = tResult.getElement();
final PsiClass sClass = sResult.getElement();
if (tClass != null && sClass != null) {
if (tClass != null && sClass != null && !(sClass instanceof InferenceVariable)) {
final PsiSubstitutor sSubstitutor = TypeConversionUtil.getClassSubstitutor(tClass, sClass, sResult.getSubstitutor());
if (sSubstitutor != null) {
if (PsiUtil.isRawSubstitutor(tClass, sSubstitutor)) {
@@ -4,7 +4,7 @@ interface i {
}
public class a implements i {
void <error descr="'ff()' in 'a' clashes with 'ff()' in 'i'; attempting to assign weaker access privileges ('packageLocal'); was 'public'">ff</error>() {}
void <error descr="'ff()' in 'a' clashes with 'ff()' in 'i'; attempting to assign weaker access privileges ('package local'); was 'public'">ff</error>() {}
}
class ai implements i {
public <error descr="'ff()' in 'ai' clashes with 'ff()' in 'i'; attempting to use incompatible return type">int</error> ff() { return 0;}
@@ -34,7 +34,7 @@ interface i2 {
class weak {
void f1() {}
}
<error descr="'f1()' in 'weak' clashes with 'f1()' in 'i'; attempting to assign weaker access privileges ('packageLocal'); was 'public'">class a2 extends weak implements i</error> {
<error descr="'f1()' in 'weak' clashes with 'f1()' in 'i'; attempting to assign weaker access privileges ('package local'); was 'public'">class a2 extends weak implements i</error> {
}
class a3 {
@@ -0,0 +1,8 @@
import java.util.Map;
class Test {
void f() {}
void m(Map<String, ? extends Test> map, String name) {
map.get(name).f();
}
}
@@ -1,5 +1,5 @@
class Foo<T extends Enum> {
public T bar(Class<? extends T> type, String str) {
return Enum.valueOf<error descr="'valueOf(java.lang.Class<T>, java.lang.String)' in 'java.lang.Enum' cannot be applied to '(java.lang.Class<capture<? extends T>>, java.lang.String)'">(type, str)</error>;
<error descr="Incompatible types. Found: 'java.lang.Enum', required: 'T'">return Enum.valueOf(type, str);</error>
}
}
@@ -0,0 +1,11 @@
abstract class Issue {
private void enumUsage(Class<? extends Enum> enumClass) {
passEnumClass(enumClass);
}
abstract <E extends Enum<E>> void passEnumClass(Class<E> var1);
void a(Enum<?> enumValue) {
Enum constant = Enum.valueOf(enumValue.getClass(), "name");
}
}
@@ -0,0 +1,11 @@
import java.util.List;
class Test {
void m(Class<List> clazz){
List<Object[]> data = foo(clazz);
}
public static <T> T foo(Class<? super T> clazz) {
return null;
}
}
@@ -0,0 +1,21 @@
import java.util.List;
interface FacetId<T extends List> {}
abstract class One {
public abstract <F extends List<C>, C extends One> void findFacetType(FacetId<F> typeId);
private void addSubFacet(FacetId<?> underlyingType) {
findFacetType(underlyingType);
}
}
class Bar<X extends Foo> {}
class Foo<C> {}
class Main {
void foo(Bar<?> a) {
m(a);
}
<X extends Foo<C>, C> void m(Bar<X> a) {
}
}
@@ -12,7 +12,10 @@ class Test {
SuperFoo<String> ls = foo(<error descr="no instance(s) of type variable(s) exist so that String conforms to Number
inference variable R has incompatible bounds:
equality constraints: String
upper bounds: Object, Number">() -> new Foo<>()</error>);
upper bounds: Object, Number
inference variable X has incompatible bounds:
equality constraints: R
upper bounds: Number">() -> new Foo<>()</error>);
SuperFoo<Integer> li = foo(() -> new Foo<>());
SuperFoo<?> lw = foo(() -> new Foo<>());
}
@@ -0,0 +1,12 @@
class One {
static boolean truth = true;
static void important() {
System.out.println(1);
}
}
class A extends One {
Two() {
important();
System.out.println(truth);
}
}
@@ -0,0 +1,6 @@
public class B {
static boolean truth = false;
static void important() {
System.out.println(2);
}
}
@@ -0,0 +1,12 @@
class One {
static boolean truth = true;
static void important() {
System.out.println(1);
}
}
class A extends One {
Two() {
important();
System.out.println(truth);
}
}
@@ -0,0 +1,6 @@
public class B {
static boolean truth = false;
static void important() {
System.out.println(2);
}
}
@@ -966,4 +966,8 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
public void testCapturedWildcardWithPrimitiveTypesChecks() throws Exception {
doTest();
}
public void testCapturedWildcardPackageLocalAccess() throws Exception {
doTest();
}
}
@@ -379,6 +379,18 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testUncheckedWarningConvertingToInferenceVariable() throws Exception {
doTest();
}
public void testUncheckedWarningDuringStrictSubtyping() throws Exception {
doTest();
}
public void testIDEA150688() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}
@@ -154,6 +154,17 @@ public class MoveMembersTest extends MultiFileTestCase {
}
}
public void testExistingFieldInSuper() throws Exception {
try {
doTest("B", "A", 0, 1);
fail("conflict expected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals("Field <b><code>truth</code></b> already exists in the target class.\n" +
"Method <b><code>important()</code></b> already exists in the target class.", e.getMessage());
}
}
public void testInnerToInterface() throws Exception {
doTest("A", "B", 0);
}
@@ -19,7 +19,6 @@ import com.intellij.JavaTestUtil;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDocumentManager;
@@ -71,9 +70,9 @@ public class RenameMethodMultiTest extends MultiFileTestCase {
doTest("p.B", "void method()", "finalMethod");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
Assert.assertEquals("Renaming method will override final \"method <b><code>A.finalMethod()</code></b>\"\n" +
"Method finalMethod() will override \n" +
"a method of the base class <b><code>p.A</code></b>", e.getMessage());
Assert.assertEquals("Method finalMethod() will override \n" +
"a method of the base class <b><code>p.A</code></b>\n" +
"Renaming method will override final \"method <b><code>A.finalMethod()</code></b>\"", e.getMessage());
return;
}
fail("Conflicts were not found");
@@ -18,6 +18,7 @@ package org.jetbrains.jps.incremental;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Consumer;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -135,6 +136,7 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
Iterable<ExcludedJavaSourceRootProvider> excludedRootProviders = JpsServiceManager.getInstance().getExtensions(ExcludedJavaSourceRootProvider.class);
final Set<File> moduleExcludes = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
moduleExcludes.addAll(index.getModuleExcludes(myModule));
final JpsJavaCompilerConfiguration compilerConfig = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(myModule.getProject());
roots_loop:
for (JpsTypedModuleSourceRoot<JavaSourceRootProperties> sourceRoot : myModule.getSourceRoots(type)) {
@@ -147,8 +149,19 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
}
}
final String packagePrefix = sourceRoot.getProperties().getPackagePrefix();
roots.add(new JavaSourceRootDescriptor(sourceRoot.getFile(), this, false, false, packagePrefix,
computeRootExcludes(sourceRoot.getFile(), index)));
// consider annotation processors output for generated sources, if contained under some source root
Set<File> excludes = computeRootExcludes(sourceRoot.getFile(), index);
final ProcessorConfigProfile profile = compilerConfig.getAnnotationProcessingProfile(myModule);
if (profile.isEnabled()) {
final File outputDir = ProjectPaths.getAnnotationProcessorGeneratedSourcesOutputDir(myModule, JavaSourceRootType.TEST_SOURCE == sourceRoot.getRootType(), profile);
if (outputDir != null && FileUtil.isAncestor(sourceRoot.getFile(), outputDir, true)) {
excludes = ContainerUtil.newTroveSet(FileUtil.FILE_HASHING_STRATEGY, excludes);
excludes.add(outputDir);
}
}
roots.add(new JavaSourceRootDescriptor(sourceRoot.getFile(), this, false, false, packagePrefix, excludes));
}
return roots;
}
@@ -138,11 +138,18 @@ public class BuildFSState {
return false;
}
final CompileScope scope = context.getScope();
final BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex();
try {
delta.lockData();
for (Set<File> files : delta.getSourcesToRecompile().values()) {
files_loop:
for (File file : files) {
if ((getEventRegistrationStamp(file) > targetBuildStart || FileSystemUtil.lastModified(file) > targetBuildStart) && scope.isAffected(target, file)) {
for (BuildRootDescriptor rd : rootIndex.findAllParentDescriptors(file, context)) {
if (rd.isGenerated()) { // do not send notification for generated sources
continue files_loop;
}
}
return true;
}
}
@@ -37,3 +37,6 @@ quickfix.add.double.quotes.desc=Wrap with double quotes
surround.with.object.literal.desc=object literal
json.template.context.type=JSON
#json schema
json.schema.add.schema.chooser.title=Select JSON Schema File
@@ -2,11 +2,11 @@ package com.jetbrains.jsonSchema;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.CollectConsumer;
import com.jetbrains.jsonSchema.impl.JsonSchemaReader;
import org.jetbrains.annotations.Nls;
@@ -22,18 +22,18 @@ import java.io.IOException;
*/
public class JsonSchemaConfigurable extends NamedConfigurable<JsonSchemaMappingsConfigurationBase.SchemaInfo> {
private final Project myProject;
@NotNull private final VirtualFile myFile;
@NotNull private final String mySchemaFilePath;
@NotNull private final JsonSchemaMappingsConfigurationBase.SchemaInfo mySchema;
@Nullable private final Runnable myTree;
private JsonSchemaMappingsView myView;
private String myDisplayName;
public JsonSchemaConfigurable(Project project,
@NotNull VirtualFile schemaFile, @NotNull JsonSchemaMappingsConfigurationBase.SchemaInfo schema,
@NotNull String schemaFilePath, @NotNull JsonSchemaMappingsConfigurationBase.SchemaInfo schema,
@Nullable Runnable updateTree) {
super(true, updateTree);
myProject = project;
myFile = schemaFile;
mySchemaFilePath = schemaFilePath;
mySchema = schema;
myTree = updateTree;
myDisplayName = mySchema.getName();
@@ -98,27 +98,32 @@ public class JsonSchemaConfigurable extends NamedConfigurable<JsonSchemaMappings
private void doValidation() throws ConfigurationException {
if (StringUtil.isEmptyOrSpaces(myDisplayName)) throw new ConfigurationException("Schema name is empty");
if (StringUtil.isEmptyOrSpaces(myView.getSchemaSubPath())) throw new ConfigurationException("Schema path is empty");
if (!Comparing.equal(myView.getSchemaSubPath(), mySchema.getRelativePathToSchema())) {
final CollectConsumer<String> collectConsumer = new CollectConsumer<>();
final File file = new File(myProject.getBasePath(), myView.getSchemaSubPath());
try {
if (!JsonSchemaReader.isJsonSchema(FileUtil.loadFile(file), collectConsumer)) {
final String message;
if (collectConsumer.getResult().isEmpty()) message = "Can not read JSON schema from file (Unknown reason)";
else message = "Can not read JSON schema from file: " + StringUtil.join(collectConsumer.getResult(), "; ");
throw new ConfigurationException(message);
}
}
catch (IOException e) {
throw new ConfigurationException("Can not read JSON schema from file: " + e.getMessage());
final CollectConsumer<String> collectConsumer = new CollectConsumer<>();
final File file = new File(myProject.getBasePath(), myView.getSchemaSubPath());
try {
if (!JsonSchemaReader.isJsonSchema(FileUtil.loadFile(file), collectConsumer)) {
final String message;
if (collectConsumer.getResult().isEmpty()) message = "Can not read JSON schema from file (Unknown reason)";
else message = "Can not read JSON schema from file: " + StringUtil.join(collectConsumer.getResult(), "; ");
logErrorForUser(message);
throw new ConfigurationException(message);
}
}
catch (IOException e) {
final String message = "Can not read JSON schema from file: " + e.getMessage();
logErrorForUser(message);
throw new ConfigurationException(message);
}
}
private void logErrorForUser(@NotNull final String error) {
JsonSchemaReader.ERRORS_NOTIFICATION.createNotification(error, MessageType.ERROR).notify(myProject);
}
@Override
public void reset() {
if (myView == null) return;
myView.setItems(myFile, mySchema.getPatterns());
myView.setItems(mySchemaFilePath, mySchema.getPatterns());
setDisplayName(mySchema.getName());
}
@@ -1,5 +1,6 @@
package com.jetbrains.jsonSchema;
import com.intellij.json.JsonBundle;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -12,6 +13,7 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.MasterDetailsComponent;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -130,7 +132,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple
private void addProjectSchema() {
final VirtualFile file =
FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), myProject, null);
FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor().withTitle(
JsonBundle.message("json.schema.add.schema.chooser.title")), myProject, null);
if (file != null) {
final String relativePath = VfsUtil.getRelativePath(file, myProject.getBaseDir());
if (relativePath == null) {
@@ -140,6 +143,7 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple
final JsonSchemaChecker importer = new JsonSchemaChecker(file, true);
if (!importer.checkSchemaFile()) {
if (!StringUtil.isEmptyOrSpaces(importer.getError())) {
JsonSchemaReader.ERRORS_NOTIFICATION.createNotification(importer.getError(), MessageType.ERROR).notify(myProject);
Messages.showErrorDialog(myProject, importer.getError(), READ_JSON_SCHEMA);
}
return;
@@ -194,7 +198,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple
}*/
private void addCreatedMappings(@NotNull VirtualFile schemaFile, @NotNull final JsonSchemaMappingsConfigurationBase.SchemaInfo info) {
final MyNode node = new MyNode(new JsonSchemaConfigurable(myProject, schemaFile, info, myTreeUpdater), info.isApplicationLevel());
final MyNode node = new MyNode(new JsonSchemaConfigurable(myProject, FileUtil.toSystemDependentName(schemaFile.getPath()),
info, myTreeUpdater), info.isApplicationLevel());
addNode(node, myRoot);
selectNodeInTree(node, true);
}
@@ -206,11 +211,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple
final List<JsonSchemaMappingsConfigurationBase.SchemaInfo> list = getStoredList();
for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : list) {
final String[] parts = info.getRelativePathToSchema().replace('\\', '/').split("/");
final VirtualFile schemaFile = VfsUtil.findRelativeFile(myProject.getBaseDir(), parts);
if (schemaFile != null) {
myRoot.add(new MyNode(new JsonSchemaConfigurable(myProject, schemaFile, info, myTreeUpdater), info.isApplicationLevel()));
}
myRoot.add(new MyNode(new JsonSchemaConfigurable(myProject, new File(myProject.getBasePath(), info.getRelativePathToSchema()).getPath(),
info, myTreeUpdater), info.isApplicationLevel()));
}
((DefaultTreeModel) myTree.getModel()).reload(myRoot);
if (myRoot.children().hasMoreElements()) {
@@ -2,9 +2,16 @@ package com.jetbrains.jsonSchema;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.xmlb.annotations.Transient;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* @author Irina.Chernushina on 2/2/2016.
@@ -17,6 +24,8 @@ import java.io.File;
)
public class JsonSchemaMappingsProjectConfiguration extends JsonSchemaMappingsConfigurationBase {
private Project myProject;
@Transient
private final Map<VirtualFile, SchemaInfo> mySchemaFiles = new HashMap<>();
public static JsonSchemaMappingsProjectConfiguration getInstance(@NotNull final Project project) {
return ServiceManager.getService(project, JsonSchemaMappingsProjectConfiguration.class);
@@ -33,4 +42,48 @@ public class JsonSchemaMappingsProjectConfiguration extends JsonSchemaMappingsCo
public File convertToAbsoluteFile(@NotNull String path) {
return myProject.getBasePath() == null ? new File(path) : new File(myProject.getBasePath(), path);
}
public boolean isRegisteredSchemaFile(@NotNull VirtualFile file) {
return mySchemaFiles.containsKey(file);
}
@Override
public void setState(@NotNull Map<String, SchemaInfo> state) {
super.setState(state);
recalculateSchemaFiles();
}
@Override
public void addSchema(@NotNull SchemaInfo info) {
super.addSchema(info);
recalculateSchemaFiles();
}
@Override
public void removeSchema(@NotNull SchemaInfo info) {
super.removeSchema(info);
recalculateSchemaFiles();
}
@Override
public void loadState(JsonSchemaMappingsConfigurationBase state) {
super.loadState(state);
recalculateSchemaFiles();
}
private void recalculateSchemaFiles() {
mySchemaFiles.clear();
if (myProject == null || myProject.getBaseDir() == null) return;
for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : myState.values()) {
final String pathToSchema = FileUtil.toSystemIndependentName(info.getRelativePathToSchema());
final VirtualFile schemaFile = VfsUtil.findRelativeFile(myProject.getBaseDir(), pathToSchema);
if (schemaFile != null) mySchemaFiles.put(schemaFile, info);
}
}
@Nullable
public SchemaInfo getSchemaBySchemaFile(@NotNull final VirtualFile file) {
return mySchemaFiles.get(file);
}
}
@@ -1,5 +1,6 @@
package com.jetbrains.jsonSchema;
import com.intellij.json.JsonBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.application.ModalityState;
@@ -81,7 +82,7 @@ public class JsonSchemaMappingsView {
final JPanel wrapper = new JPanel(new BorderLayout());
final JPanel wrapper2 = new JPanel(new BorderLayout());
mySchemaField = new TextFieldWithBrowseButton();
SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, "Select JSON Schema File",
SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, JsonBundle.message("json.schema.add.schema.chooser.title"),
FileChooserDescriptorFactory.createSingleFileDescriptor());
attachNavigateToSchema();
final JBLabel label = new JBLabel("JSON schema file:");
@@ -127,7 +128,7 @@ public class JsonSchemaMappingsView {
return items;
}
public void setItems(VirtualFile schemaFile, final List<JsonSchemaMappingsConfigurationBase.Item> data) {
public void setItems(String schemaFilePath, final List<JsonSchemaMappingsConfigurationBase.Item> data) {
/*final ArrayList<JsonSchemaMappingsConfigurationBase.Item> copy = new ArrayList<JsonSchemaMappingsConfigurationBase.Item>();
if (myProject != null && myProject.getBasePath() != null) {
for (JsonSchemaMappingsConfigurationBase.Item item : copy) {
@@ -141,8 +142,9 @@ public class JsonSchemaMappingsView {
} else {
copy.addAll(data);
}*/
mySchemaField.setText(FileUtil.toSystemDependentName(schemaFile.getPath()));
myTableView.setModelAndUpdateColumns(new ListTableModel<JsonSchemaMappingsConfigurationBase.Item>(createColumns(), new ArrayList<JsonSchemaMappingsConfigurationBase.Item>(data)));
mySchemaField.setText(schemaFilePath);
myTableView.setModelAndUpdateColumns(
new ListTableModel<JsonSchemaMappingsConfigurationBase.Item>(createColumns(), new ArrayList<JsonSchemaMappingsConfigurationBase.Item>(data)));
}
public String getSchemaSubPath() {
@@ -0,0 +1,66 @@
/*
* 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.jetbrains.jsonSchema;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.listeners.RefactoringElementListenerProvider;
import com.intellij.refactoring.listeners.UndoRefactoringElementAdapter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Irina.Chernushina on 2/17/2016.
*/
public class JsonSchemaRefactoringListenerProvider implements RefactoringElementListenerProvider {
@Nullable
@Override
public RefactoringElementListener getListener(PsiElement element) {
if (element == null) {
return null;
}
VirtualFile fileAtElement = PsiUtilBase.asVirtualFile(element);
if (fileAtElement == null) {
return null;
}
Project project = element.getProject();
final JsonSchemaMappingsProjectConfiguration configuration = JsonSchemaMappingsProjectConfiguration.getInstance(project);
final JsonSchemaMappingsConfigurationBase.SchemaInfo schemaInfo = configuration.getSchemaBySchemaFile(fileAtElement);
if (schemaInfo != null && project.getBaseDir() != null) {
return new UndoRefactoringElementAdapter() {
@Override
protected void refactored(@NotNull PsiElement element, @Nullable String oldQualifiedName) {
VirtualFile newFile = PsiUtilBase.asVirtualFile(element);
if (newFile != null) {
final String relativePath = VfsUtil.getRelativePath(newFile, project.getBaseDir());
if (relativePath != null) {
configuration.removeSchema(schemaInfo);
final JsonSchemaMappingsConfigurationBase.SchemaInfo newSchema =
new JsonSchemaMappingsConfigurationBase.SchemaInfo(schemaInfo.getName(), relativePath, schemaInfo.isApplicationLevel(),
schemaInfo.getPatterns());
configuration.addSchema(newSchema);
}
}
}
};
}
return null;
}
}
@@ -18,11 +18,8 @@ package com.jetbrains.jsonSchema.extension;
import com.intellij.json.JsonFileType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ResourceUtil;
import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurationBase;
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,7 +28,6 @@ import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.Map;
/**
* @author Irina.Chernushina on 2/16/2016.
@@ -46,15 +42,7 @@ public class JsonSchemaSelfProviderFactory implements JsonSchemaProviderFactory
@Override
public boolean isAvailable(@NotNull VirtualFile file) {
if (project == null || !JsonFileType.INSTANCE.equals(file.getFileType())) return false;
final JsonSchemaMappingsProjectConfiguration configuration = JsonSchemaMappingsProjectConfiguration.getInstance(project);
final Map<String, JsonSchemaMappingsConfigurationBase.SchemaInfo> map = configuration.getStateMap();
for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : map.values()) {
final String pathToSchema = FileUtil.toSystemIndependentName(info.getRelativePathToSchema());
if (! file.getPath().endsWith(pathToSchema)) continue;
final VirtualFile schemaFile = VfsUtil.findRelativeFile(project.getBaseDir(), pathToSchema);
if (file.equals(schemaFile)) return true;
}
return false;
return JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file);
}
@Nullable
@@ -4,6 +4,7 @@ import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.intellij.notification.NotificationGroup;
import com.intellij.util.Consumer;
import com.intellij.util.ThrowablePairConsumer;
import org.jetbrains.annotations.NotNull;
@@ -17,6 +18,8 @@ import java.util.*;
* @author Irina.Chernushina on 8/27/2015.
*/
public class JsonSchemaReader {
public static final NotificationGroup ERRORS_NOTIFICATION = NotificationGroup.logOnlyGroup("JSON Schema");
public JsonSchemaObject read(@NotNull final Reader reader) throws IOException {
final JsonReader in = new JsonReader(reader);
@@ -180,7 +180,9 @@ public class HighlightDisplayLevel {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(getColor());
g.fillRect(x + 2, y + 2, 10, 10);
int shift = JBUI.scale(2);
int size = JBUI.scale(10);
g.fillRect(x + shift, y + shift, size, size);
}
@Override
@@ -100,6 +100,10 @@ public abstract class SuppressIntentionAction implements Iconable, IntentionActi
*/
public abstract boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element);
public boolean isSuppressAll() {
return false;
}
@Nullable
private static PsiElement getElement(@NotNull Editor editor, @NotNull PsiFile file) {
CaretModel caretModel = editor.getCaretModel();
@@ -22,4 +22,6 @@ import org.jetbrains.annotations.NotNull;
public interface SuppressQuickFix extends LocalQuickFix {
SuppressQuickFix[] EMPTY_ARRAY = new SuppressQuickFix[0];
boolean isAvailable(@NotNull final Project project, @NotNull final PsiElement context);
boolean isSuppressAll();
}
@@ -48,6 +48,7 @@ public interface RefEntity extends UserDataHolder {
*
* @return the list of children.
*/
@Nullable
List<RefEntity> getChildren();
/**
@@ -106,6 +106,11 @@ public abstract class AbstractBatchSuppressByNoInspectionCommentFix implements C
invoke(project, element);
}
@Override
public boolean isSuppressAll() {
return SuppressionUtil.ALL.equals(myID);
}
protected final void replaceSuppressionComment(@NotNull final PsiElement comment) {
SuppressionUtil.replaceSuppressionComment(comment, myID, myReplaceOtherSuppressionIds, getCommentLanguage(comment));
}
@@ -85,4 +85,8 @@ public class SuppressIntentionActionFromFix extends SuppressIntentionAction {
public String getFamilyName() {
return myFix.getFamilyName();
}
public boolean isSuppressAll() {
return myFix.isSuppressAll();
}
}
@@ -203,7 +203,7 @@ class StateMap private constructor(private val names: Array<String>, private val
}
val currentState = states.get(index)
LOG.assertTrue(currentState is Element)
LOG.assertTrue(currentState is Element, currentState?.let { it.javaClass.name } ?: "null")
states.set(index, if (state == null) null else archiveState(state).toByteArray())
}
}
@@ -56,7 +56,7 @@ class StateGetter<S : Any, T : Any>(private val component: PersistentStateCompon
try {
stateAfterLoad = component.state
}
catch(e: Throwable) {
catch (e: Throwable) {
LOG.error("Cannot get state after load", e)
stateAfterLoad = null
}
@@ -226,7 +226,7 @@ fun save(states: StateMap, rootElementName: String, newLiveStates: Map<String, E
return rootElement
}
fun Element.normalizeRootName(): Element {
internal fun Element.normalizeRootName(): Element {
if (parent != null) {
LOG.warn("State element must not have parent ${JDOMUtil.writeElement(this)}")
detach()
@@ -0,0 +1,63 @@
/*
* 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.openapi.editor;
/**
* Allows to suppress stripping spaces from some lines or from entire document when the document is being saved and
* "Strip spaces on Save" option is not "None".
*
* @see StripTrailingSpacesFilterFactory
*/
public interface StripTrailingSpacesFilter {
/**
* The filter which completely prohibits removing trailing spaces from a document regardless of other filters.
*/
StripTrailingSpacesFilter NOT_ALLOWED = new StripTrailingSpacesFilter() {
@Override
public boolean isStripSpacesAllowedForLine(int line) {
return false;
}
};
/**
* Tells that strip trailing spaces is not currently possible since some conditions are not met for proper document processing.
* The filter will force another attempt to strip trailing spaces later regardless of other filters.
*/
StripTrailingSpacesFilter POSTPONED = new StripTrailingSpacesFilter() {
@Override
public boolean isStripSpacesAllowedForLine(int line) {
return false;
}
};
/**
* The filter which does not put any restrictions on trailing spaces removal. Other filters may be taken into account.
*/
StripTrailingSpacesFilter ALL_LINES = new StripTrailingSpacesFilter() {
@Override
public boolean isStripSpacesAllowedForLine(int line) {
return true;
}
};
/**
* @param line The document line.
* @return True if trailing spaces can be removed from the line, false otherwise.
*/
boolean isStripSpacesAllowedForLine(int line);
}
@@ -0,0 +1,42 @@
/*
* 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.openapi.editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An extension point to create filters which may put restrictions on how trailing spaces will be handled in a document.
*/
public abstract class StripTrailingSpacesFilterFactory {
public static final ExtensionPointName<StripTrailingSpacesFilterFactory> EXTENSION_POINT
= new ExtensionPointName<StripTrailingSpacesFilterFactory>("com.intellij.stripTrailingSpacesFilterFactory");
/**
* Creates a filter which may restrict trailing spaces removal.
*
* @param project The current project or null if there is no project context.
* @param document The document to be processed.
* @return The filter as defined in {@link StripTrailingSpacesFilter}. The factory may return one of the several predefined filters:
* {@link StripTrailingSpacesFilter#NOT_ALLOWED}, {@link StripTrailingSpacesFilter#POSTPONED} or
* {@link StripTrailingSpacesFilter#ALL_LINES}. The latter can be returned, for example, if a language-specific logic is not
* applicable to the document.
*/
@NotNull
public abstract StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document);
}
@@ -1,53 +0,0 @@
/*
* 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.openapi.editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Allows to suppress stripping spaces from some lines when a document is being saved and "Strip spaces on Save" option is not "None".
*/
public abstract class StripTrailingSpacesLineFilter {
public static final ExtensionPointName<StripTrailingSpacesLineFilter> LINE_FILTER_EXTENSION_POINT
= new ExtensionPointName<StripTrailingSpacesLineFilter>("com.intellij.stripTrailingSpacesLineFilter");
/**
* Tells if spaces can be stripped from the document. Returns <code>false</code> if trailing spaces should be preserved regardless of
* the editor settings and other filters.
*
* @param project The current project or <code>null</code> if there is no project context.
* @param document The document.
* @return True if it's OK to strip spaces, false otherwise.
*/
public abstract boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document);
/**
* Processes a document and sets bits to 1 (true) for lines which should remain untouched when trailing spaces are removed.
*
* @param project The current project or null if there is no project context.
* @param document The document to process.
* @param disabledLinesBitSet The bit set which can be modified by the <code>apply()</code> method. Each bit index corresponds to a line
*
* @return True if successful, false if can't be applied now, probably later.
*/
public abstract boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet);
}
@@ -51,10 +51,9 @@ import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import static com.intellij.openapi.editor.StripTrailingSpacesLineFilter.LINE_FILTER_EXTENSION_POINT;
import static com.intellij.openapi.editor.StripTrailingSpacesFilterFactory.EXTENSION_POINT;
public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl");
@@ -199,13 +198,18 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
if (!isStripTrailingSpacesEnabled) {
return true;
}
StripTrailingSpacesLineFilter[] filters = LINE_FILTER_EXTENSION_POINT.getExtensions();
for (StripTrailingSpacesLineFilter filter : filters) {
if (!filter.isStripSpacesAllowed(project, this)) return true;
}
BitSet disabledLinesBitSet = new BitSet(getLineCount());
for (StripTrailingSpacesLineFilter filter : filters) {
if (!filter.apply(project, this, disabledLinesBitSet)) return false;
List<StripTrailingSpacesFilter> filters = new ArrayList<StripTrailingSpacesFilter>();
for (StripTrailingSpacesFilterFactory filterFactory : EXTENSION_POINT.getExtensions()) {
StripTrailingSpacesFilter filter = filterFactory.createFilter(project, this);
if (filter == StripTrailingSpacesFilter.NOT_ALLOWED) {
return true;
}
else if (filter == StripTrailingSpacesFilter.POSTPONED) {
return false;
}
else {
filters.add(filter);
}
}
boolean markAsNeedsStrippingLater = false;
@@ -230,7 +234,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
lineLoop:
for (int line = 0; line < getLineCount(); line++) {
LineSet lineSet = getLineSet();
if (inChangedLinesOnly && !lineSet.isModified(line) || disabledLinesBitSet.get(line)) continue;
if (inChangedLinesOnly && !lineSet.isModified(line) || !canStripSpacesFrom(line, filters)) continue;
int whiteSpaceStart = -1;
final int lineEnd = lineSet.getLineEnd(line) - lineSet.getSeparatorLength(line);
int lineStart = lineSet.getLineStart(line);
@@ -288,6 +292,13 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
return markAsNeedsStrippingLater;
}
private static boolean canStripSpacesFrom(int line, @NotNull List<StripTrailingSpacesFilter> filters) {
for (StripTrailingSpacesFilter filter : filters) {
if (!filter.isStripSpacesAllowedForLine(line)) return false;
}
return true;
}
@Override
public void setReadOnly(boolean isReadOnly) {
if (myIsReadOnly != isReadOnly) {
@@ -18,9 +18,11 @@ package com.intellij.openapi.editor.impl;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageUtil;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.StripTrailingSpacesLineFilter;
import com.intellij.openapi.editor.StripTrailingSpacesFilter;
import com.intellij.openapi.editor.StripTrailingSpacesFilterFactory;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
@@ -29,28 +31,45 @@ import org.jetbrains.annotations.Nullable;
import java.util.BitSet;
public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailingSpacesLineFilter {
@Override
public boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document) {
return true;
public abstract class PsiBasedStripTrailingSpacesFilter implements StripTrailingSpacesFilter {
private final @NotNull BitSet myDisabledLinesBitSet;
private final @NotNull Document myDocument;
public abstract static class Factory extends StripTrailingSpacesFilterFactory {
@NotNull
@Override
public final StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document) {
Language language = getDocumentLanguage(document);
if (language != null && isApplicableTo(language)) {
PsiFile psiFile = getPsiFile(project, document);
if (psiFile != null) {
PsiBasedStripTrailingSpacesFilter filter = createFilter(document);
filter.process(psiFile);
return filter;
}
return POSTPONED;
}
return ALL_LINES;
}
@NotNull
protected abstract PsiBasedStripTrailingSpacesFilter createFilter(@NotNull Document document);
protected abstract boolean isApplicableTo(@NotNull Language language);
}
protected PsiBasedStripTrailingSpacesFilter(@NotNull Document document) {
myDocument = document;
myDisabledLinesBitSet = new BitSet(document.getLineCount());
}
@Override
public final boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet) {
Language language = getDocumentLanguage(document);
if (language != null && isApplicableTo(language)) {
PsiFile psiFile = getPsiFile(project, document);
if (psiFile != null) {
return apply(document, psiFile, disabledLinesBitSet);
}
return false;
}
return true;
public boolean isStripSpacesAllowedForLine(int line) {
return !myDisabledLinesBitSet.get(line);
}
protected abstract boolean isApplicableTo(@NotNull Language language);
protected abstract boolean apply(@NotNull Document document, @NotNull PsiFile psiFile, BitSet disabledLinesBitSet);
protected abstract void process(@NotNull PsiFile psiFile);
@Nullable
private static Language getDocumentLanguage(@NotNull Document document) {
@@ -72,4 +91,10 @@ public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailin
}
return null;
}
protected final void disableRange(@NotNull TextRange range) {
int startLine = myDocument.getLineNumber(range.getStartOffset());
int endLine = myDocument.getLineNumber(range.getEndOffset());
myDisabledLinesBitSet.set(startLine, endLine);
}
}
@@ -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.
@@ -38,9 +38,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -406,66 +404,69 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
}
private void registerIndicatorAndRun(@NotNull ProgressIndicator indicator,
@NotNull Thread currentThread,
ProgressIndicator oldIndicator,
@NotNull Runnable process) {
Set<Thread> underIndicator;
boolean alreadyUnder;
boolean isStandard;
@NotNull Thread currentThread,
ProgressIndicator oldIndicator,
@NotNull Runnable process) {
List<Set<Thread>> threadsUnderThisIndicator = new ArrayList<Set<Thread>>();
synchronized (threadsUnderIndicator) {
underIndicator = threadsUnderIndicator.get(indicator);
if (underIndicator == null) {
underIndicator = new SmartHashSet<Thread>();
threadsUnderIndicator.put(indicator, underIndicator);
}
alreadyUnder = !underIndicator.add(currentThread);
isStandard = indicator instanceof StandardProgressIndicator;
if (!isStandard) {
nonStandardIndicators.add(indicator);
if (myCheckCancelledFuture == null) {
myCheckCancelledFuture = startBackgroundIndicatorPing();
for (ProgressIndicator thisIndicator = indicator; thisIndicator != null; thisIndicator = thisIndicator instanceof WrappedProgressIndicator ? ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator() : null) {
Set<Thread> underIndicator = threadsUnderIndicator.get(thisIndicator);
if (underIndicator == null) {
underIndicator = new SmartHashSet<Thread>();
threadsUnderIndicator.put(thisIndicator, underIndicator);
}
}
boolean alreadyUnder = !underIndicator.add(currentThread);
threadsUnderThisIndicator.add(alreadyUnder ? null : underIndicator);
if (indicator.isCanceled()) {
threadsUnderCanceledIndicator.add(currentThread);
}
else {
threadsUnderCanceledIndicator.remove(currentThread);
}
thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty();
}
try {
if (indicator instanceof WrappedProgressIndicator) {
ProgressIndicator wrappee = ((WrappedProgressIndicator)indicator).getOriginalProgressIndicator();
if (wrappee == indicator) throw new IllegalArgumentException(indicator + " wraps itself");
registerIndicatorAndRun(wrappee, currentThread, oldIndicator, process);
}
else {
process.run();
}
}
finally {
synchronized (threadsUnderIndicator) {
boolean removed = alreadyUnder || underIndicator.remove(currentThread);
if (removed && underIndicator.isEmpty()) {
threadsUnderIndicator.remove(indicator);
}
boolean isStandard = thisIndicator instanceof StandardProgressIndicator;
if (!isStandard) {
nonStandardIndicators.remove(indicator);
if (nonStandardIndicators.isEmpty()) {
myCheckCancelledFuture.cancel(true);
myCheckCancelledFuture = null;
nonStandardIndicators.add(thisIndicator);
if (myCheckCancelledFuture == null) {
myCheckCancelledFuture = startBackgroundIndicatorPing();
}
}
// by this time oldIndicator may have been canceled
if (oldIndicator != null && oldIndicator.isCanceled()) {
if (thisIndicator.isCanceled()) {
threadsUnderCanceledIndicator.add(currentThread);
}
else {
threadsUnderCanceledIndicator.remove(currentThread);
}
}
thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty();
}
try {
process.run();
}
finally {
synchronized (threadsUnderIndicator) {
ProgressIndicator thisIndicator = null;
// order doesn't matter
for (int i = 0; i < threadsUnderThisIndicator.size(); i++) {
thisIndicator = i == 0 ? indicator : ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator();
Set<Thread> underIndicator = threadsUnderThisIndicator.get(i);
boolean removed = underIndicator != null && underIndicator.remove(currentThread);
if (removed && underIndicator.isEmpty()) {
threadsUnderIndicator.remove(thisIndicator);
}
boolean isStandard = thisIndicator instanceof StandardProgressIndicator;
if (!isStandard) {
nonStandardIndicators.remove(thisIndicator);
if (nonStandardIndicators.isEmpty()) {
myCheckCancelledFuture.cancel(true);
myCheckCancelledFuture = null;
}
}
// by this time oldIndicator may have been canceled
if (oldIndicator != null && oldIndicator.isCanceled()) {
threadsUnderCanceledIndicator.add(currentThread);
}
else {
threadsUnderCanceledIndicator.remove(currentThread);
}
}
thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty();
}
}
@@ -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.
@@ -43,12 +43,13 @@ public class ProgressWrapper extends AbstractProgressIndicatorBase implements Wr
}
protected ProgressWrapper(@NotNull ProgressIndicator original, boolean checkCanceledForMe) {
assert original instanceof StandardProgressIndicator : "Original indicator must be StandardProcessIndicator";
myOriginal = original;
myCheckCanceledForMe = checkCanceledForMe;
nested = 1 + (original instanceof ProgressWrapper ? ((ProgressWrapper)original).nested : -1);
if (nested > 50) {
LOG.error("Too many wrapped indicators");
}
//if (nested > 50) {
// LOG.error("Too many wrapped indicators");
//}
ProgressManager.assertNotCircular(original);
}
@@ -60,13 +61,36 @@ public class ProgressWrapper extends AbstractProgressIndicatorBase implements Wr
@Override
public final boolean isCanceled() {
return myOriginal.isCanceled() || myCheckCanceledForMe && super.isCanceled();
ProgressWrapper current = this;
while (true) {
if (current.myCheckCanceledForMe && current.isCanceledRaw()) return true;
ProgressIndicator original = current.getOriginalProgressIndicator();
if (original instanceof ProgressWrapper) {
current = (ProgressWrapper)original;
}
else {
return original.isCanceled();
}
}
}
private boolean isCanceledRaw() { return super.isCanceled(); }
private void checkCanceledRaw() { super.checkCanceled(); }
@Override
public final void checkCanceled() {
myOriginal.checkCanceled();
super.checkCanceled();
ProgressWrapper current = this;
while (true) {
current.checkCanceledRaw();
ProgressIndicator original = current.getOriginalProgressIndicator();
if (original instanceof ProgressWrapper) {
current = (ProgressWrapper)original;
}
else {
original.checkCanceled();
break;
}
}
}
@Override
@@ -24,6 +24,7 @@ import com.intellij.diff.util.Side;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -82,6 +83,9 @@ abstract class ChunkOptimizer<T> {
int equalForward = expandForward(myData1, myData2, range1.end1, range1.end2, range1.end1 + count2, range1.end2 + count2);
int equalBackward = expandBackward(myData1, myData2, range2.start1 - count1, range2.start2 - count1, range2.start1, range2.start2);
// nothing to do
if (equalForward == 0 && equalBackward == 0) return;
// merge chunks left [A]B[B] -> [AB]B
if (equalForward == count2) {
myRanges.remove(myRanges.size() - 1);
@@ -223,31 +227,81 @@ abstract class ChunkOptimizer<T> {
@Override
protected int getShift(@NotNull Side touchSide, int equalForward, int equalBackward, @NotNull Range range1, @NotNull Range range2) {
Integer shift;
shift = getUnchangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, 0);
if (shift != null) return shift;
shift = getChangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, 0);
if (shift != null) return shift;
shift = getUnchangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, myThreshold);
if (shift != null) return shift;
shift = getChangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, myThreshold);
if (shift != null) return shift;
return 0;
}
/**
* search for an empty line boundary in unchanged lines
* ie: we want insertion/deletion to go right before/after of an empty line
*/
@Nullable
private Integer getUnchangedBoundaryShift(@NotNull Side touchSide,
int equalForward, int equalBackward,
@NotNull Range range1, @NotNull Range range2,
int threshold) {
List<Line> touchLines = touchSide.select(myData1, myData2);
int touchStart = touchSide.select(range2.start1, range2.start2);
int shiftForward = findUnimportantLineShift(touchLines, touchStart, equalForward, true, 0);
int shiftBackward = findUnimportantLineShift(touchLines, touchStart - 1, equalBackward, false, 0);
int shiftForward = findNextUnimportantLine(touchLines, touchStart, equalForward + 1, threshold);
int shiftBackward = findPrevUnimportantLine(touchLines, touchStart - 1, equalBackward + 1, threshold);
if (shiftForward == -1 && shiftBackward == -1 && myThreshold != 0) {
shiftForward = findUnimportantLineShift(touchLines, touchStart, equalForward, true, myThreshold);
shiftBackward = findUnimportantLineShift(touchLines, touchStart - 1, equalBackward, false, myThreshold);
}
if (shiftForward == 0 || shiftBackward == 0) return 0;
if (shiftForward == -1 && shiftBackward == -1) return 0;
return shiftForward != -1 ? shiftForward : -shiftBackward;
return getShift(shiftForward, shiftBackward);
}
private static int findUnimportantLineShift(@NotNull List<Line> lines, int offset, int count, boolean leftToRight, int threshold) {
/**
* search for an empty line boundary in changed lines
* ie: we want insertion/deletion to start/end with an empty line
*/
@Nullable
private Integer getChangedBoundaryShift(@NotNull Side touchSide,
int equalForward, int equalBackward,
@NotNull Range range1, @NotNull Range range2,
int threshold) {
Side nonTouchSide = touchSide.other();
List<Line> nonTouchLines = nonTouchSide.select(myData1, myData2);
int changeStart = nonTouchSide.select(range1.end1, range1.end2);
int changeEnd = nonTouchSide.select(range2.start1, range2.start2);
int shiftForward = findNextUnimportantLine(nonTouchLines, changeStart, equalForward + 1, threshold);
int shiftBackward = findPrevUnimportantLine(nonTouchLines, changeEnd - 1, equalBackward + 1, threshold);
return getShift(shiftForward, shiftBackward);
}
private static int findNextUnimportantLine(@NotNull List<Line> lines, int offset, int count, int threshold) {
for (int i = 0; i < count; i++) {
int index = leftToRight ? offset + i : offset - i;
if (lines.get(index).getNonSpaceChars() <= threshold) {
return i;
}
if (lines.get(offset + i).getNonSpaceChars() <= threshold) return i;
}
return -1;
}
private static int findPrevUnimportantLine(@NotNull List<Line> lines, int offset, int count, int threshold) {
for (int i = 0; i < count; i++) {
if (lines.get(offset - i).getNonSpaceChars() <= threshold) return i;
}
return -1;
}
@Nullable
private static Integer getShift(int shiftForward, int shiftBackward) {
if (shiftForward == -1 && shiftBackward == -1) return null;
if (shiftForward == 0 || shiftBackward == 0) return 0;
return shiftForward != -1 ? shiftForward : -shiftBackward;
}
}
}
@@ -100,10 +100,6 @@ abstract class DiffTestCase : UsefulTestCase() {
fun parseSource(string: CharSequence): String = string.toString().replace('_', '\n')
fun parseMatching(before: String, after: String): Couple<BitSet> {
return Couple.of(parseMatching(before), parseMatching(after))
}
fun parseMatching(matching: String): BitSet {
val set = BitSet()
matching.filterNot { it == '.' }.forEachIndexed { i, c -> if (c != ' ') set.set(i) }
@@ -25,9 +25,10 @@ import com.intellij.util.containers.ContainerUtil
import java.util.*
abstract class ComparisonUtilTestBase : DiffTestCase() {
private fun doLineTest(before: Document, after: Document, expected: List<Change>?, policy: ComparisonPolicy) {
private fun doLineTest(before: Document, after: Document, matchings: Couple<BitSet>?, expected: List<Change>?, policy: ComparisonPolicy) {
val fragments = MANAGER.compareLines(before.charsSequence, after.charsSequence, policy, INDICATOR)
checkConsistency(fragments, before, after)
if (matchings != null) checkLineMatching(fragments, matchings)
if (expected != null) checkLineChanges(fragments, expected)
}
@@ -111,6 +112,18 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
assertOrderedEquals(changes, expected)
}
private fun checkLineMatching(fragments: List<LineFragment>, matchings: Couple<BitSet>) {
val set1 = BitSet()
val set2 = BitSet()
for (fragment in fragments) {
set1.set(fragment.startLine1, fragment.endLine1)
set2.set(fragment.startLine2, fragment.endLine2)
}
assertEquals(matchings.first, set1)
assertEquals(matchings.second, set2)
}
private fun checkDiffMatching(fragments: List<DiffFragment>, matchings: Couple<BitSet>) {
val set1 = BitSet()
val set2 = BitSet()
@@ -201,7 +214,7 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
assertTrue(change != null || matchings != null)
when (type) {
TestType.LINE -> doLineTest(before!!, after!!, change, policy)
TestType.LINE -> doLineTest(before!!, after!!, matchings, change, policy)
TestType.WORD -> doWordTest(before!!, after!!, matchings, change, policy)
TestType.CHAR -> doCharTest(before!!, after!!, matchings, change, policy)
TestType.SPLITTER -> {
@@ -267,6 +280,47 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
fun ignore() {
ignoreMatching = parseMatching(before, after)
}
private fun parseMatching(before: String, after: String): Couple<BitSet> {
if (type == TestType.LINE) {
val builder = this@TestBuilder
return Couple.of(parseLineMatching(before, builder.before!!), parseLineMatching(after, builder.after!!))
}
else {
return Couple.of(parseMatching(before), parseMatching(after))
}
}
fun parseLineMatching(matching: String, document: Document): BitSet {
assertEquals(matching.length, document.textLength)
val lines1 = matching.split('_', '*')
val lines2 = document.charsSequence.split('\n')
assertEquals(lines1.size, lines2.size)
for (i in 0..lines1.size - 1) {
assertEquals(lines1[i].length, lines2[i].length, "line $i")
}
val set = BitSet()
var index = 0
var lineNumber = 0
while (index < matching.length) {
var end = matching.indexOfAny(listOf("_", "*"), index) + 1
if (end == 0) end = matching.length
val line = matching.subSequence(index, end)
if (line.find { it != ' ' && it != '_' } != null) {
assert(!line.contains(' '))
set.set(lineNumber)
}
lineNumber++
index = end
}
return set
}
}
@@ -257,7 +257,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test regression - shifted similar lines should be matched as a single change, not insertion-deletion`() {
lines {
(" X_ X" - " X_ X")
default(mod(0, 0, 2, 2))
("--_---" - "---_----").default()
testDefault()
}
}
@@ -265,19 +265,59 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test prefer chunks bounded by empty line`() {
lines {
("A_B_o_o_Y_Z_ _A_B_z_z_Y_Z" - "A_B_o_o_Y_Z_ _A_B_u_u_Y_Z_ _A_B_z_z_Y_Z")
default(ins(7, 7, 7))
(" _ _ _ _ _ _ _ _ _ _ _ _ " - " _ _ _ _ _ _ _-_-_-_-_-_-_-_ _ _ _ _ _ ").default()
testAll()
}
lines {
("A_B_o_o_Y_Z_ _A_B_ _ _Y_Z" - "A_B_o_o_Y_Z_ _A_B_u_u_Y_Z_ _A_B_ _ _Y_Z")
(" _ _ _ _ _ _ _ _ _ _ _ _ " - " _ _ _ _ _ _ _ _ _-_-_-_-_-_-_-_ _ _ _ ").default()
testAll()
}
lines {
("A_B_o_o_ _A_B_z_z" - "A_B_o_o_ _A_B_u_u_ _A_B_z_z")
default(ins(5, 5, 5))
(" _ _ _ _ _ _ _ _ " - " _ _ _ _ _-_-_-_-_-_ _ _ _ ").default()
testAll()
}
lines {
("o_o_Y_Z_ _z_z_Y_Z" - "o_o_Y_Z_ _u_u_Y_Z_ _z_z_Y_Z")
default(ins(5, 5, 5))
(" _ _ _ _ _ _ _ _ " - " _ _ _ _ _-_-_-_-_-_ _ _ _ ").default()
testAll()
}
}
fun `test prefer chunks bounded by short line`() {
lines {
("A====_B====_o====_o====_Y====_Z====_!_A====_B====_z====_z====_Y====_Z====" -
"A====_B====_o====_o====_Y====_Z====_!_A====_B====_u====_u====_Y====_Z====_!_A====_B====_z====_z====_Y====_Z====")
(" _ _ _ _ _ _ _ _ _ _ _ _ " -
" _ _ _ _ _ _ _-----_-----_-----_-----_-----_-----_-_ _ _ _ _ _ ").default()
testAll()
}
lines {
("A====_B====_o====_o====_Y====_Z====_ _A====_B====_!_!_Y====_Z====" -
"A====_B====_o====_o====_Y====_Z====_ _A====_B====_u====_u====_Y====_Z====_ _A====_B====_!_!_Y====_Z====")
(" _ _ _ _ _ _ _ _ _ _ _ _ " -
" _ _ _ _ _ _ _-----_-----_-----_-----_-----_-----_-_ _ _ _ _ _ ").default()
testAll()
}
lines {
("A====_B====_o====_o====_Y====_Z====_!_A====_B====_ _ _Y====_Z====" -
"A====_B====_o====_o====_Y====_Z====_!_A====_B====_u====_u====_Y====_Z====_!_A====_B====_ _ _Y====_Z====")
(" _ _ _ _ _ _ _ _ _ _ _ _ " -
" _ _ _ _ _ _ _ _ _-----_-----_-----_-----_-_-----_-----_ _ _ _ ").default()
testAll()
}
}
fun `test prefer chunks bounded by empty line - using inserted content`() {
lines {
("e_x_A_B_z" - "x_A_B_ _q_A_B_z")
("-_ _ _ _ " - " _ _ _-_-_-_-_ ").default()
testAll()
}
}
@@ -285,12 +325,13 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test prefer smaller amount of chunks`() {
lines() {
("X_A_X_Y_" - "X_Y_")
default(del(0, 0, 2))
("-_-_ _ _" - " _ _").default()
testAll()
}
lines() {
(" __x___y" - "__y")
("-**-*__ " - "__ ").default()
default(del(0, 0, 3))
testAll()
}
@@ -298,7 +339,8 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
lines {
("U======_X======_Y======_z======_X======_Y======_X======_U======_X======_z======" -
"U======_Y======_X======_U======_X======")
default(del(1, 1, 4), del(9, 5, 1))
(" _-------_-------_-------_-------_ _ _ _ _-------" -
" _ _ _ _ ").default()
testAll()
}
}
@@ -306,7 +348,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test regression - can trim chunks after 'compareTwoSteps'`() {
lines {
("q__7_ 6_ 7" - "_7")
default(del(0, 0, 1), del(3, 2, 2))
("-*_ _--*--" - "_ ").default()
testDefault()
}
}
@@ -314,7 +356,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test regression - can trim chunks after 'optimizeLineChunks'`() {
lines {
("A=====_ B=====_ }_}_B=====_" - "A=====_ }_}_B=====_")
default(del(1, 1, 1))
(" _-------_ _ _ _" - " _ _ _ _").default()
testAll()
}
}
@@ -322,26 +364,26 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test bad cases caused by 'compareTwoStep' logic`() {
lines {
("x_!" - "!_x_y")
default(del(0, 0, 1), ins(2, 1, 2))
("-_ " - " _-_-").default()
testAll()
}
lines {
("!_x_y" - "x_!")
default(del(0, 0, 1), mod(2, 1, 1, 1))
("-_ _-" - " _-").default()
testAll()
}
lines {
("x_! " - "!_x_y")
default(mod(0, 0, 2, 3))
trim(del(0, 0, 1), ins(2, 1, 2))
("-_--" - "-_-_-").default()
("-_ " - " _-_-").trim()
testAll()
}
lines {
("!_x_y" - "x_! ")
default(del(0, 0, 1), mod(2, 1, 1, 1))
("-_ _-" - " _--").default()
testAll()
}
@@ -356,16 +398,16 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test bad cases caused by 'compareSmart' logic`() {
lines {
("A=====_ B=====_ }_}_B=====" - "A=====_ }_}_B=====")
// TODO trim(del(1, 1, 1))
default(del(1, 1, 1))
trim(ins(1, 1, 2), del(2, 4, 3))
// TODO (" _-------_ _ _ " - " _ _ _ ").trim()
(" _-------_ _ _ " - " _ _ _ ").default()
(" _ _--_-_------" - " _--_-_ ").trim()
testAll()
}
lines {
("A=====_ B=====_X_ }_}_Z_B=====_" - "A=====_ }_}_B=====_")
// TODO default(del(1, 1, 2), del(5, 3, 1))
default(mod(1, 1, 5, 2))
// TODO (" _-------_-_ _ _-_ _" - " _ _ _ _").default()
(" _-------_-_--_-_-_ _" - " _--_-_ _").default()
testAll()
}
}
@@ -373,17 +415,16 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() {
fun `test trim changed blocks after second step correction`() {
lines() {
("====}_==== }_Y_====}" - "====}_Y_====}")
default(del(1, 1, 1)) // result after second step correction
ignore(mod(1, 1, 2, 1)) // result looks strange because of 'diff.unimportant.line.char.count'
testDefault()
testTrim()
(" _------_ _ " - " _ _ ").default() // result after second step correction
(" _ _-_-----" - " _-_ ").ignore() // result looks strange because of 'diff.unimportant.line.char.count'
testAll()
}
}
fun `test second step correction processes all confusing lines`() {
lines {
("====}_==== }_Y_==== }_====}" - "==== }_Y_==== }")
default(del(0, 0, 1), del(4, 3, 1))
("-----_ _ _ _-----" - " _ _ ").default()
testDefault()
}
}
@@ -916,7 +916,7 @@ public class CommonCodeStyleSettings {
public int WRAP_ON_TYPING = WrapOnTyping.DEFAULT.intValue;
//-------------------------Indent options-------------------------------------------------
public static class IndentOptions implements Cloneable {
public static class IndentOptions implements Cloneable, JDOMExternalizable {
public int INDENT_SIZE = 4;
public int CONTINUATION_INDENT_SIZE = 8;
public int TAB_SIZE = 4;
@@ -931,10 +931,12 @@ public class CommonCodeStyleSettings {
private static final Key<CommonCodeStyleSettings.IndentOptions> INDENT_OPTIONS_KEY = Key.create("INDENT_OPTIONS_KEY");
private boolean myInaccurate;
@Override
public void readExternal(Element element) throws InvalidDataException {
DefaultJDOMExternalizer.readExternal(this, element);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
DefaultJDOMExternalizer.writeExternal(this, element, field -> {
if ("KEEP_INDENTS_ON_EMPTY_LINES".equals(field.getName())) {
@@ -50,7 +50,7 @@ public class AnalysisUIOptions implements PersistentStateComponent<AnalysisUIOpt
public boolean SHOW_ONLY_DIFF = false;
public boolean SHOW_STRUCTURE = false;
public boolean ANALYSIS_IN_BACKGROUND = false;
public boolean ANALYSIS_IN_BACKGROUND = true;
public AnalysisUIOptions() {
myAutoScrollToSourceHandler = new AutoScrollToSourceHandler() {
@@ -377,9 +377,13 @@ public class IntentionListStep implements ListPopupStep<IntentionActionWithTextC
if (myCachedGutters.contains(action)) {
return 5;
}
if (action.getAction() instanceof EmptyIntentionAction) {
final IntentionAction underlyingAction = action.getAction();
if (underlyingAction instanceof EmptyIntentionAction) {
return -10;
}
if (underlyingAction instanceof SuppressIntentionActionFromFix && ((SuppressIntentionActionFromFix)underlyingAction).isSuppressAll()) {
return -15;
}
return 0;
}
@@ -80,7 +80,7 @@ public class IntentionDescriptionPanel {
}
}
// TODO 134099: see SingleInspectionProfilePanel#toHTML
// TODO 134099: see SingleInspectionProfilePanel#setHTML
private String toHTML(String text) {
final HintHint hintHint = new HintHint(myDescriptionBrowser, new Point(0, 0));
hintHint.setFont(UIUtil.getLabelFont());
@@ -75,13 +75,13 @@ public class DescriptorComposer extends HTMLComposerImpl {
}
public static String[] quickFixTexts(RefEntity where, @NotNull InspectionToolPresentation toolPresentation){
QuickFixAction[] quickFixes = toolPresentation.getQuickFixes(new RefEntity[] {where});
QuickFixAction[] quickFixes = toolPresentation.getQuickFixes(new RefEntity[] {where}, null);
if (quickFixes == null) {
return null;
}
List<String> texts = new ArrayList<String>();
for (QuickFixAction quickFix : quickFixes) {
String text = quickFix.getText(where);
String text = quickFix.getText();
if (text == null) continue;
texts.add(escapeQuickFixText(text));
}
@@ -83,7 +83,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider
public QuickFixAction[] getQuickFixes(@NotNull final InspectionToolWrapper toolWrapper, @NotNull final InspectionTree tree) {
final RefEntity[] refEntities = tree.getSelectedElements();
InspectionToolPresentation presentation = tree.getContext().getPresentation(toolWrapper);
return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities);
return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities, tree.getSelectedDescriptors());
}
@@ -40,28 +40,20 @@ import java.util.Set;
*/
public class LocalQuickFixWrapper extends QuickFixAction {
private final QuickFix myFix;
private String myText;
public LocalQuickFixWrapper(@NotNull QuickFix fix, @NotNull InspectionToolWrapper toolWrapper) {
super(fix.getName(), toolWrapper);
myFix = fix;
myText = myFix.getName();
setText(myFix.getName());
}
@Override
public void update(AnActionEvent e) {
super.update(e);
getTemplatePresentation().setText(myText);
e.getPresentation().setText(myText);
}
@Override
public String getText(RefEntity where) {
return myText;
}
public void setText(@NotNull String text) {
myText = text;
getTemplatePresentation().setText(text);
}
@@ -41,7 +41,6 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SequentialModalProgressTask;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -102,7 +101,7 @@ public class QuickFixAction extends AnAction {
return false;
}
public String getText(RefEntity where) {
public String getText() {
return getTemplatePresentation().getText();
}
@@ -110,15 +109,13 @@ public class QuickFixAction extends AnAction {
public void actionPerformed(final AnActionEvent e) {
final InspectionResultsView view = getInvoker(e);
final InspectionTree tree = view.getTree();
if (isProblemDescriptorsAcceptable()) {
final CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors();
if (descriptors.length > 0) {
doApplyFix(view.getProject(), descriptors, tree.getContext());
return;
}
final CommonProblemDescriptor[] descriptors;
if (isProblemDescriptorsAcceptable() && (descriptors = tree.getSelectedDescriptors()).length > 0) {
doApplyFix(view.getProject(), descriptors, tree.getContext());
} else {
doApplyFix(getSelectedElements(e), view);
}
doApplyFix(getSelectedElements(e), view);
view.updateRightPanel();
}
@@ -21,7 +21,6 @@
package com.intellij.codeInspection.offlineViewer;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.QuickFix;
import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.reference.RefElement;
@@ -31,7 +30,6 @@ import com.intellij.codeInspection.ui.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
@@ -69,7 +67,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
final TreePath[] treePaths = tree.getSelectionPaths();
if (treePaths == null) return QuickFixAction.EMPTY;
final List<RefEntity> selectedElements = new ArrayList<RefEntity>();
final Map<RefEntity, Set<QuickFix>> actions = new HashMap<RefEntity, Set<QuickFix>>();
final Map<RefEntity, CommonProblemDescriptor[]> actions = new HashMap<>();
for (TreePath selectionPath : treePaths) {
TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), new TreeUtil.Traverse() {
@Override
@@ -79,18 +77,10 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
final OfflineProblemDescriptorNode descriptorNode = (OfflineProblemDescriptorNode)node;
final RefEntity element = descriptorNode.getElement();
selectedElements.add(element);
Set<QuickFix> quickFixes = actions.get(element);
if (quickFixes == null) {
quickFixes = new HashSet<QuickFix>();
actions.put(element, quickFixes);
}
final CommonProblemDescriptor descriptor = descriptorNode.getDescriptor();
if (descriptor != null) {
final QuickFix[] fixes = descriptor.getFixes();
if (fixes != null) {
ContainerUtil.addAll(quickFixes, fixes);
}
}
CommonProblemDescriptor[] quickFixes = actions.get(element);
actions.put(element, quickFixes == null ?
new CommonProblemDescriptor[]{descriptorNode.getDescriptor()} :
DefaultInspectionToolPresentation.mergeDescriptors(quickFixes, new CommonProblemDescriptor[]{descriptorNode.getDescriptor()}));
}
else if (node instanceof RefElementNode) {
selectedElements.add(((RefElementNode)node).getElement());
@@ -106,7 +96,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
GlobalInspectionContextImpl context = tree.getContext();
InspectionToolPresentation presentation = context.getPresentation(toolWrapper);
return presentation.extractActiveFixes(selectedRefElements, actions);
return presentation.extractActiveFixes(selectedRefElements, actions, tree.getSelectedDescriptors());
}
@Override
@@ -1,532 +0,0 @@
/*
* Copyright 2000-2009 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.ui;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.actions.SuppressActionWrapper;
import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManagerImpl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.Document;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;
import java.awt.*;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
class Browser extends JPanel {
private static final String UNDER_CONSTRUCTION = InspectionsBundle.message("inspection.tool.description.under.construction.text");
private final List<ClickListener> myClickListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private RefEntity myCurrentEntity;
private JEditorPane myHTMLViewer;
private final InspectionResultsView myView;
private final HyperlinkListener myHyperLinkListener;
private CommonProblemDescriptor myCurrentDescriptor;
public static class ClickEvent {
public static final int REF_ELEMENT = 1;
public static final int FILE_OFFSET = 2;
private final VirtualFile myFile;
private final int myStartPosition;
private final int myEndPosition;
private final RefElement refElement;
private final int myEventType;
public ClickEvent(VirtualFile myFile, int myStartPosition, int myEndPosition) {
this.myFile = myFile;
this.myStartPosition = myStartPosition;
this.myEndPosition = myEndPosition;
myEventType = FILE_OFFSET;
refElement = null;
}
public int getEventType() {
return myEventType;
}
public VirtualFile getFile() {
return myFile;
}
public int getStartOffset() {
return myStartPosition;
}
public int getEndOffset() {
return myEndPosition;
}
public RefElement getClickedElement() {
return refElement;
}
}
public void dispose(){
removeAll();
if (myHTMLViewer != null) {
myHTMLViewer.removeHyperlinkListener(myHyperLinkListener);
myHTMLViewer = null;
}
myClickListeners.clear();
}
public interface ClickListener {
void referenceClicked(ClickEvent e);
}
private void showPageFromHistory(@NotNull RefEntity newEntity) {
InspectionToolWrapper toolWrapper = getToolWrapper(newEntity);
try {
String html = generateHTML(newEntity, toolWrapper);
myHTMLViewer.read(new StringReader(html), null);
setupStyle();
myHTMLViewer.setCaretPosition(0);
}
catch (Exception e) {
showEmpty();
}
finally {
myCurrentEntity = newEntity;
myCurrentDescriptor = null;
}
}
public void showPageFor(RefEntity refEntity, CommonProblemDescriptor descriptor) {
try {
String html = generateHTML(refEntity, descriptor);
myHTMLViewer.read(new StringReader(html), null);
setupStyle();
myHTMLViewer.setCaretPosition(0);
}
catch (Exception e) {
showEmpty();
}
finally {
myCurrentEntity = refEntity;
myCurrentDescriptor = descriptor;
}
}
public void showPageFor(RefEntity newEntity) {
if (newEntity == null) {
showEmpty();
return;
}
//multiple problems for one entity -> refresh browser
showPageFromHistory(newEntity.getRefManager().getRefinedElement(newEntity));
}
public Browser(@NotNull InspectionResultsView view) {
super(new BorderLayout());
myView = view;
myCurrentEntity = null;
myCurrentDescriptor = null;
myHTMLViewer = new JEditorPane(UIUtil.HTML_MIME, InspectionsBundle.message("inspection.offline.view.empty.browser.text"));
myHTMLViewer.setEditable(false);
myHyperLinkListener = new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
Browser.this.hyperlinkUpdate(e);
}
};
myHTMLViewer.addHyperlinkListener(myHyperLinkListener);
final JScrollPane pane = ScrollPaneFactory.createScrollPane(myHTMLViewer);
pane.setBorder(null);
add(pane, BorderLayout.CENTER);
setupStyle();
}
private void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
return;
}
JEditorPane pane = (JEditorPane)e.getSource();
if (e instanceof HTMLFrameHyperlinkEvent) {
HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
HTMLDocument doc = (HTMLDocument)pane.getDocument();
doc.processHTMLFrameHyperlinkEvent(evt);
return;
}
URL url = null;
try {
url = e.getURL();
@NonNls String ref = url.getRef();
if (ref.startsWith("pos:")) {
int delimeterPos = ref.indexOf(':', "pos:".length() + 1);
String startPosition = ref.substring("pos:".length(), delimeterPos);
String endPosition = ref.substring(delimeterPos + 1);
Integer textStartOffset = new Integer(startPosition);
Integer textEndOffset = new Integer(endPosition);
String fileURL = url.toExternalForm();
fileURL = fileURL.substring(0, fileURL.indexOf('#'));
VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(fileURL);
if (vFile != null) {
fireClickEvent(vFile, textStartOffset.intValue(), textEndOffset.intValue());
}
}
else if (ref.startsWith("descr:")) {
if (myCurrentDescriptor instanceof ProblemDescriptor) {
PsiElement psiElement = ((ProblemDescriptor)myCurrentDescriptor).getPsiElement();
if (psiElement == null) return;
VirtualFile vFile = psiElement.getContainingFile().getVirtualFile();
if (vFile != null) {
TextRange range = ((ProblemDescriptorBase)myCurrentDescriptor).getTextRange();
fireClickEvent(vFile, range.getStartOffset(), range.getEndOffset());
}
}
}
else if (ref.startsWith("invoke:")) {
int actionNumber = Integer.parseInt(ref.substring("invoke:".length()));
InspectionToolWrapper toolWrapper = getToolWrapper();
InspectionToolPresentation presentation = myView.getGlobalInspectionContext().getPresentation(toolWrapper);
QuickFixAction fixAction = presentation.getQuickFixes(new RefElement[]{(RefElement)myCurrentEntity})[actionNumber];
fixAction.doApplyFix(new RefElement[]{(RefElement)myCurrentEntity}, myView);
}
else if (ref.startsWith("invokelocal:")) {
int actionNumber = Integer.parseInt(ref.substring("invokelocal:".length()));
if (actionNumber > -1) {
invokeLocalFix(actionNumber);
}
} else if (ref.startsWith("suppress:")){
final SuppressActionWrapper.SuppressTreeAction[] suppressTreeActions =
new SuppressActionWrapper(myView.getProject(), getToolWrapper(), myView.getTree().getSelectionPaths()).getChildren(null);
final List<AnAction> activeActions = new ArrayList<AnAction>();
for (SuppressActionWrapper.SuppressTreeAction suppressTreeAction : suppressTreeActions) {
if (suppressTreeAction.isAvailable()) activeActions.add(suppressTreeAction);
}
if (!activeActions.isEmpty()) {
int actionNumber = Integer.parseInt(ref.substring("suppress:".length()));
if (actionNumber > -1 && activeActions.size() > actionNumber) {
activeActions.get(actionNumber).actionPerformed(null);
}
}
}
else {
int offset = Integer.parseInt(ref);
String fileURL = url.toExternalForm();
fileURL = fileURL.substring(0, fileURL.indexOf('#'));
VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(fileURL);
if (vFile == null) {
vFile = VfsUtil.findFileByURL(url);
}
if (vFile != null) {
fireClickEvent(vFile, offset, offset);
}
}
}
catch (Throwable t) {
if (url != null) {
BrowserUtil.browse(url);
}
}
}
private void setupStyle() {
Document document = myHTMLViewer.getDocument();
if (!(document instanceof StyledDocument)) {
return;
}
StyledDocument styledDocument = (StyledDocument)document;
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
EditorColorsScheme scheme = colorsManager.getGlobalScheme();
Style style = styledDocument.addStyle("active", null);
StyleConstants.setFontFamily(style, scheme.getEditorFontName());
StyleConstants.setFontSize(style, scheme.getEditorFontSize());
styledDocument.setCharacterAttributes(0, document.getLength(), style, false);
}
public void addClickListener(ClickListener listener) {
myClickListeners.add(listener);
}
private void fireClickEvent(VirtualFile file, int startPosition, int endPosition) {
ClickEvent e = new ClickEvent(file, startPosition, endPosition);
for (ClickListener listener : myClickListeners) {
listener.referenceClicked(e);
}
}
private String generateHTML(final RefEntity refEntity, @NotNull final InspectionToolWrapper toolWrapper) {
final StringBuffer buf = new StringBuffer();
final HTMLComposerImpl htmlComposer = getPresentation(toolWrapper).getComposer();
if (refEntity instanceof RefElement) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
htmlComposer.compose(buf, refEntity);
}
});
}
else {
htmlComposer.compose(buf, refEntity);
}
uppercaseFirstLetter(buf);
if (refEntity instanceof RefElement){
appendSuppressSection(buf);
}
insertHeaderFooter(buf);
return buf.toString();
}
private InspectionToolPresentation getPresentation(@NotNull InspectionToolWrapper toolWrapper) {
return myView.getGlobalInspectionContext().getPresentation(toolWrapper);
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static void insertHeaderFooter(final StringBuffer buf) {
buf.insert(0, "<HTML><BODY>");
buf.append("</BODY></HTML>");
}
private String generateHTML(final RefEntity refEntity, final CommonProblemDescriptor descriptor) {
final StringBuffer buf = new StringBuffer();
final Runnable action = new Runnable() {
@Override
public void run() {
InspectionToolWrapper toolWrapper = getToolWrapper(refEntity);
getPresentation(toolWrapper).getComposer().compose(buf, refEntity, descriptor);
}
};
ApplicationManager.getApplication().runReadAction(action);
uppercaseFirstLetter(buf);
if (refEntity instanceof RefElement) {
appendSuppressSection(buf);
}
insertHeaderFooter(buf);
return buf.toString();
}
private InspectionToolWrapper getToolWrapper(final RefEntity refEntity) {
InspectionToolWrapper toolWrapper = getToolWrapper();
assert toolWrapper != null;
final GlobalInspectionContextImpl context = myView.getGlobalInspectionContext();
if (refEntity instanceof RefElement){
PsiElement element = ((RefElement)refEntity).getElement();
if (element == null) return toolWrapper;
InspectionProfileWrapper profileWrapper = InspectionProjectProfileManagerImpl.getInstanceImpl(context.getProject()).getProfileWrapper();
toolWrapper = profileWrapper.getInspectionTool(toolWrapper.getShortName(), element);
}
return toolWrapper;
}
private void appendSuppressSection(final StringBuffer buf) {
final InspectionToolWrapper toolWrapper = getToolWrapper();
if (toolWrapper != null) {
final HighlightDisplayKey key = HighlightDisplayKey.find(toolWrapper.getShortName());
if (key != null){//dummy entry points
final SuppressActionWrapper.SuppressTreeAction[] suppressActions = new SuppressActionWrapper(myView.getProject(), toolWrapper, myView.getTree().getSelectionPaths()).getChildren(null);
if (suppressActions.length > 0) {
final List<AnAction> activeSuppressActions = new ArrayList<AnAction>();
for (SuppressActionWrapper.SuppressTreeAction suppressAction : suppressActions) {
if (suppressAction.isAvailable()) {
activeSuppressActions.add(suppressAction);
}
}
if (!activeSuppressActions.isEmpty()) {
int idx = 0;
@NonNls final String br = "<br>";
buf.append(br);
HTMLComposerImpl.appendHeading(buf, InspectionsBundle.message("inspection.export.results.suppress"));
for (AnAction suppressAction : activeSuppressActions) {
buf.append(br);
if (idx == activeSuppressActions.size() - 1) {
buf.append(br);
}
HTMLComposer.appendAfterHeaderIndention(buf);
@NonNls final String href = "<a HREF=\"file://bred.txt#suppress:" + idx + "\">" + suppressAction.getTemplatePresentation().getText() + "</a>";
buf.append(href);
idx++;
}
}
}
}
}
}
private static void uppercaseFirstLetter(final StringBuffer buf) {
if (buf.length() > 1) {
char[] firstLetter = new char[1];
buf.getChars(0, 1, firstLetter, 0);
buf.setCharAt(0, Character.toUpperCase(firstLetter[0]));
}
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void showEmpty() {
myCurrentEntity = null;
try {
myHTMLViewer.read(new StringReader(InspectionsBundle.message("inspection.offline.view.empty.browser.text")), null);
}
catch (IOException e) {
//can't be
}
}
public void showDescription(@NotNull InspectionToolWrapper toolWrapper){
if (toolWrapper.getShortName().isEmpty()){
showEmpty();
return;
}
@NonNls StringBuffer page = new StringBuffer();
page.append("<table border='0' cellspacing='0' cellpadding='0' width='100%'>");
page.append("<tr><td colspan='2'>");
HTMLComposer.appendHeading(page, InspectionsBundle.message("inspection.tool.in.browser.id.title"));
page.append("</td></tr>");
page.append("<tr><td width='37'></td>" +
"<td>");
page.append(toolWrapper.getShortName());
page.append("</td></tr>");
page.append("<tr height='10'></tr>");
page.append("<tr><td colspan='2'>");
HTMLComposer.appendHeading(page, InspectionsBundle.message("inspection.tool.in.browser.description.title"));
page.append("</td></tr>");
page.append("<tr><td width='37'></td>" +
"<td>");
@NonNls final String underConstruction = "<b>" + UNDER_CONSTRUCTION + "</b></html>";
try {
@NonNls String description = toolWrapper.loadDescription();
if (description == null) {
description = underConstruction;
}
page.append(DefaultInspectionToolPresentation.stripUIRefsFromInspectionDescription(UIUtil.getHtmlBody(description)));
page.append("</td></tr></table>");
myHTMLViewer.setText(XmlStringUtil.wrapInHtml(page));
setupStyle();
}
finally {
myCurrentEntity = null;
}
}
@Nullable
private InspectionToolWrapper getToolWrapper() {
return myView.getTree().getSelectedToolWrapper();
}
public void invokeLocalFix(int idx) {
if (myView.getTree().getSelectionCount() != 1) return;
final InspectionTreeNode node = (InspectionTreeNode)myView.getTree().getSelectionPath().getLastPathComponent();
if (node instanceof ProblemDescriptionNode) {
final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
final CommonProblemDescriptor descriptor = problemNode.getDescriptor();
final RefEntity element = problemNode.getElement();
invokeFix(element, descriptor, idx);
}
else if (node instanceof RefElementNode) {
RefElementNode elementNode = (RefElementNode)node;
RefEntity element = elementNode.getElement();
CommonProblemDescriptor descriptor = elementNode.getProblem();
if (descriptor != null) {
invokeFix(element, descriptor, idx);
}
}
}
private void invokeFix(final RefEntity element, final CommonProblemDescriptor descriptor, final int idx) {
final QuickFix[] fixes = descriptor.getFixes();
if (fixes != null && fixes.length > idx && fixes[idx] != null) {
if (element instanceof RefElement) {
PsiElement psiElement = ((RefElement)element).getElement();
if (psiElement != null && psiElement.isValid()) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(psiElement)) return;
performFix(element, descriptor, idx, fixes[idx]);
}
}
else {
performFix(element, descriptor, idx, fixes[idx]);
}
}
}
private void performFix(final RefEntity element, final CommonProblemDescriptor descriptor, final int idx, final QuickFix fix) {
final Runnable command = new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final PsiModificationTracker tracker = PsiManager.getInstance(myView.getProject()).getModificationTracker();
final long startCount = tracker.getModificationCount();
CommandProcessor.getInstance().markCurrentCommandAsGlobal(myView.getProject());
//CCE here means QuickFix was incorrectly inherited
fix.applyFix(myView.getProject(), descriptor);
if (startCount != tracker.getModificationCount()) {
InspectionToolWrapper toolWrapper = myView.getTree().getSelectedToolWrapper();
if (toolWrapper != null) {
InspectionToolPresentation presentation =
myView.getGlobalInspectionContext().getPresentation(toolWrapper);
presentation.ignoreProblem(element, descriptor, idx);
}
myView.updateView(false);
}
}
});
}
};
CommandProcessor.getInstance().executeCommand(myView.getProject(), command, fix.getName(), null);
}
}
@@ -267,7 +267,7 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro
}
@NotNull
private static CommonProblemDescriptor[] mergeDescriptors(@NotNull CommonProblemDescriptor[] problems1,
public static CommonProblemDescriptor[] mergeDescriptors(@NotNull CommonProblemDescriptor[] problems1,
@NotNull CommonProblemDescriptor[] problems2) {
CommonProblemDescriptor[] out = new CommonProblemDescriptor[problems1.length + problems2.length];
int o = problems1.length;
@@ -641,41 +641,77 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro
@Override
@Nullable
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) {
return extractActiveFixes(refElements, getQuickFixActions());
public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) {
return extractActiveFixes(refElements, getProblemElements(), allowedDescriptors);
}
@Override
@Nullable
public QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, @NotNull Map<RefEntity, Set<QuickFix>> actions) {
Map<Class, QuickFixAction> result = new com.intellij.util.containers.HashMap<Class, QuickFixAction>();
public QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements,
@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<>();
boolean isFirst = true;
for (RefEntity refElement : refElements) {
final Set<QuickFix> localQuickFixes = actions.get(refElement);
if (localQuickFixes == null) continue;
for (QuickFix fix : localQuickFixes) {
if (fix == null) continue;
final Class klass = fix instanceof ActionClassHolder ? ((ActionClassHolder ) fix).getActionClass() : fix.getClass();
final QuickFixAction quickFixAction = result.get(klass);
if (quickFixAction != null) {
try {
String familyName = fix.getFamilyName();
familyName = !familyName.isEmpty() ? "\'" + familyName + "\'" : familyName;
((LocalQuickFixWrapper)quickFixAction).setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", familyName));
}
catch (AbstractMethodError e) {
//for plugin compatibility
((LocalQuickFixWrapper)quickFixAction).setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", ""));
}
final CommonProblemDescriptor[] descriptors = descriptorMap.get(refElement);
if (descriptors == null) continue;
for (CommonProblemDescriptor d : descriptors) {
if (allowedDescriptorSet != null && !allowedDescriptorSet.contains(d)) {
continue;
}
else {
LocalQuickFixWrapper quickFixWrapper = new LocalQuickFixWrapper(fix, myToolWrapper);
result.put(klass, quickFixWrapper);
QuickFix[] fixes = d.getFixes();
if (fixes != null) {
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);
}
isFirst = false;
}
else {
for (Class clazz : new ArrayList<>(result.keySet())) {
boolean isFound = false;
for (QuickFix fix : fixes) {
if (fix == null) continue;
final Class klass = getFixClass(fix);
if (clazz.equals(klass)) {
isFound = true;
final QuickFixAction quickFixAction = result.get(clazz);
try {
String familyName = fix.getFamilyName();
familyName = !familyName.isEmpty() ? "\'" + familyName + "\'" : familyName;
((LocalQuickFixWrapper)quickFixAction)
.setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", familyName));
}
catch (AbstractMethodError e) {
//for plugin compatibility
((LocalQuickFixWrapper)quickFixAction)
.setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", ""));
}
break;
}
}
if (!isFound) {
result.remove(clazz);
if (result.isEmpty()) {
return QuickFixAction.EMPTY;
}
}
}
}
}
}
}
return result.values().isEmpty() ? null : result.values().toArray(new QuickFixAction[result.size()]);
}
private static Class getFixClass(QuickFix fix) {
return fix instanceof ActionClassHolder ? ((ActionClassHolder)fix).getActionClass() : fix.getClass();
}
@Override
public RefEntity getElement(@NotNull CommonProblemDescriptor descriptor) {
return getProblemToElements().get(descriptor);
@@ -23,7 +23,6 @@ import com.intellij.util.IconUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Enumeration;
/**
* @author max
@@ -51,19 +50,9 @@ public class InspectionNode extends InspectionTreeNode {
}
public boolean isTooBigForOnlineRefresh() {
if(!myTooBigForOnlineRefresh) myTooBigForOnlineRefresh = getProblemCount()>1000;
if (!myTooBigForOnlineRefresh) {
myTooBigForOnlineRefresh = getProblemCount() > 1000;
}
return myTooBigForOnlineRefresh;
}
@Override
public int getProblemCount() {
int sum = 0;
Enumeration children = children();
while (children.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)children.nextElement();
if (child instanceof InspectionNode) continue;
sum += child.getProblemCount();
}
return sum;
}
}
@@ -0,0 +1,109 @@
/*
* 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.ui;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.ex.DisableInspectionToolAction;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel;
import com.intellij.ui.*;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
/**
* @author Dmitry Batkovich
*/
public class InspectionNodeInfo extends JPanel {
private final static Logger LOG = Logger.getInstance(InspectionNodeInfo.class);
private final JButton myButton;
private final SimpleColoredComponent myTitle;
private final HighlightDisplayKey myKey;
private final InspectionProfileImpl myCurrentProfile;
private final String myName;
private final Project myProject;
public InspectionNodeInfo(final InspectionToolWrapper toolWrapper, Project project) {
setLayout(new GridBagLayout());
setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 0, 0));
myProject = project;
myTitle = new SimpleColoredComponent();
myCurrentProfile = (InspectionProfileImpl)InspectionProjectProfileManager.getInstance(project).getProjectProfileImpl();
myKey = HighlightDisplayKey.find(toolWrapper.getID());
myName = toolWrapper.getDisplayName();
myButton = new JButton();
add(myTitle,
new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new JBInsets(0, 2, 0, 0),
0, 0));
JEditorPane description = new JEditorPane();
description.setContentType(UIUtil.HTML_MIME);
description.setEditable(false);
description.setOpaque(false);
description.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE);
final String toolDescription = toolWrapper.loadDescription();
SingleInspectionProfilePanel.readHTML(description, SingleInspectionProfilePanel.toHTML(description, toolDescription == null ? "" : toolDescription, true));
add(ScrollPaneFactory.createScrollPane(description, true),
new GridBagConstraints(0, 1, 1, 1, 0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
new JBInsets(5, 5, 0, 0), 0, 0));
add(myButton,
new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
new JBInsets(15, 0, 0, 0), 0, 0));
updateEnableButtonText(false);
new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent event, int clickCount) {
updateEnableButtonText(true);
return true;
}
}.installOn(myButton);
}
private void updateEnableButtonText(boolean revert) {
boolean isEnabled = myCurrentProfile.isToolEnabled(myKey);
if (revert) {
final boolean isEnabledAsFinal = isEnabled;
DisableInspectionToolAction.modifyAndCommitProjectProfile(model -> {
if (isEnabledAsFinal) {
model.disableTool(myKey.getID(), myProject);
}
else {
((InspectionProfileImpl)model).enableTool(myKey.getID(), myProject);
}
}, myProject);
isEnabled = !isEnabled;
}
myButton.setText((isEnabled ? "Disable" : "Enable") + " inspection");
myTitle.clear();
myTitle.append(myName);
if (!isEnabled) {
myTitle.append(" Disabled", SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
}
}
@@ -21,7 +21,6 @@ import com.intellij.analysis.AnalysisScope;
import com.intellij.analysis.AnalysisUIOptions;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.reference.RefElement;
@@ -36,30 +35,31 @@ import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.pom.Navigatable;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.*;
import com.intellij.ui.components.JBLabel;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.impl.UsagePreviewPanel;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.OpenSourceUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.*;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
@@ -67,8 +67,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
@@ -77,9 +75,8 @@ import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
@@ -87,11 +84,13 @@ import java.util.concurrent.ConcurrentMap;
* @author max
*/
public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider {
private static final Logger LOG = Logger.getInstance(InspectionResultsView.class);
public static final DataKey<InspectionResultsView> DATA_KEY = DataKey.create("inspectionView");
private static final Key<Boolean> PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused.");
private final Project myProject;
private final InspectionTree myTree;
private final Browser myBrowser;
private final ConcurrentMap<HighlightDisplayLevel, ConcurrentMap<String, InspectionGroupNode>> myGroups = ContainerUtil.newConcurrentMap();
private final OccurenceNavigator myOccurenceNavigator;
private volatile InspectionProfile myInspectionProfile;
@@ -110,6 +109,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
private final InspectionRVContentProvider myProvider;
private AnAction myIncludeAction;
private AnAction myExcludeAction;
private Editor myPreviewEditor;
public InspectionResultsView(@NotNull final Project project,
final InspectionProfile inspectionProfile,
@@ -129,59 +129,26 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
myOccurenceNavigator = initOccurenceNavigator();
myBrowser = new Browser(this);
mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION);
mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT | SideBorder.RIGHT));
mySplitter.setSecondComponent(myBrowser);
mySplitter.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue());
}
mySplitter.addPropertyChangeListener(evt -> {
if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue());
}
});
add(mySplitter, BorderLayout.CENTER);
myBrowser.addClickListener(new Browser.ClickListener() {
@Override
public void referenceClicked(final Browser.ClickEvent e) {
if (e.getEventType() == Browser.ClickEvent.REF_ELEMENT) {
final RefElement refElement = e.getClickedElement();
final OpenFileDescriptor descriptor = getOpenFileDescriptor(refElement);
if (descriptor != null) {
FileEditorManager.getInstance(project).openTextEditor(descriptor, false);
}
}
else if (e.getEventType() == Browser.ClickEvent.FILE_OFFSET) {
final VirtualFile file = e.getFile();
final OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, e.getStartOffset());
final Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
if (editor != null) {
final TextAttributes selectionAttributes =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
HighlightManager.getInstance(project)
.addRangeHighlight(editor, e.getStartOffset(), e.getEndOffset(), selectionAttributes, true, null);
}
}
}
});
createActionsToolbar();
TreeUtil.selectFirstNode(myTree);
}
private void initTreeListeners() {
myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
syncBrowser();
if (isAutoScrollMode()) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
}
myTree.getSelectionModel().addTreeSelectionListener(e -> {
syncRightPanel();
if (isAutoScrollMode()) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
}
});
@@ -364,9 +331,9 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
}
@Override
public void dispose(){
public void dispose() {
releaseEditor(myPreviewEditor);
mySplitter.dispose();
myBrowser.dispose();
myInspectionProfile = null;
myDisposed = true;
}
@@ -406,9 +373,18 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
return null;
}
private void syncBrowser() {
private void syncRightPanel() {
final Editor oldEditor = myPreviewEditor;
if (myTree.getSelectionModel().getSelectionCount() != 1) {
myBrowser.showEmpty();
if (myTree.getSelectedToolWrapper() == null) {
final JLabel multipleSelectionLabel = new JBLabel(InspectionViewNavigationPanel.getTitleText(false, false));
multipleSelectionLabel.setVerticalAlignment(SwingConstants.TOP);
multipleSelectionLabel.setBorder(IdeBorderFactory.createEmptyBorder(5, 7, 0, 0));
mySplitter.setSecondComponent(multipleSelectionLabel);
}
else {
showInRightPanel(myTree.getCommonSelectedElement());
}
}
else {
TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
@@ -416,48 +392,111 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent();
if (node instanceof RefElementNode) {
final RefElementNode refElementNode = (RefElementNode)node;
final CommonProblemDescriptor problem = refElementNode.getProblem();
final RefEntity refSelected = refElementNode.getElement();
if (problem != null) {
showInBrowser(refSelected, problem);
}
else {
showInBrowser(refSelected);
}
showInRightPanel(refSelected);
}
else if (node instanceof ProblemDescriptionNode) {
final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
showInBrowser(problemNode.getElement(), problemNode.getDescriptor());
showInRightPanel(problemNode.getElement());
}
else if (node instanceof InspectionNode) {
showInBrowser(((InspectionNode)node).getToolWrapper());
else if (node instanceof InspectionNode || node instanceof InspectionPackageNode || node instanceof InspectionModuleNode) {
showInRightPanel(null);
}
else if (node instanceof InspectionRootNode || node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) {
mySplitter.setSecondComponent(new InspectionViewNavigationPanel(node, myTree));
}
else {
myBrowser.showEmpty();
LOG.error("Unexpected node: " + node.getClass());
}
}
}
if (oldEditor != null) {
if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) {
oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null);
}
else {
releaseEditor(oldEditor);
}
}
}
private void showInBrowser(final RefEntity refEntity) {
private void showInRightPanel(@Nullable final RefEntity refEntity) {
Cursor currentCursor = getCursor();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
myBrowser.showPageFor(refEntity);
setCursor(currentCursor);
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
final JPanel editorPanel = new JPanel();
editorPanel.setLayout(new BorderLayout());
CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors();
editorPanel.add(createBaseRightComponentFor(descriptors, refEntity), BorderLayout.CENTER);
if (descriptors.length > 0) {
final InspectionToolWrapper tool = myTree.getSelectedToolWrapper();
LOG.assertTrue(tool != null);
editorPanel.add(new QuickFixToolbar(myTree,
myProject,
myPreviewEditor,
myProvider.getQuickFixes(tool, myTree)),
BorderLayout.NORTH);
}
mySplitter.setSecondComponent(editorPanel);
}
finally {
setCursor(currentCursor);
}
}
private void showInBrowser(@NotNull InspectionToolWrapper toolWrapper) {
Cursor currentCursor = getCursor();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
myBrowser.showDescription(toolWrapper);
setCursor(currentCursor);
private boolean reuseEditorFor(Document document) {
return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document;
}
private void showInBrowser(final RefEntity refEntity, CommonProblemDescriptor descriptor) {
Cursor currentCursor = getCursor();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
myBrowser.showPageFor(refEntity, descriptor);
setCursor(currentCursor);
private JComponent createBaseRightComponentFor(CommonProblemDescriptor[] descriptors,
RefEntity selectedEntity) {
final int count = descriptors.length;
if (selectedEntity instanceof RefElement && !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) {
PsiElement selectedElement = ((RefElement)selectedEntity).getElement();
if (count == 1) {
final CommonProblemDescriptor descriptor = descriptors[0];
if (descriptor instanceof ProblemDescriptorBase) {
final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement();
if (element != null) {
selectedElement = element;
}
}
}
final PsiFile file = selectedElement.getContainingFile();
final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
if (reuseEditorFor(document)) {
myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true);
}
else {
myPreviewEditor = EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true);
final EditorSettings settings = myPreviewEditor.getSettings();
settings.setLineNumbersShown(false);
settings.setLineMarkerAreaShown(false);
settings.setAdditionalColumnsCount(0);
settings.setAdditionalLinesCount(0);
settings.setLeadingWhitespaceShown(true);
settings.setRightMarginShown(true);
settings.setRightMargin(60);
UsagePreviewPanel.highlight(Collections.emptyList(), myPreviewEditor, myProject);
}
if (count == 1) {
final PsiElement finalSelectedElement = selectedElement;
ApplicationManager.getApplication().invokeLater(() -> {
if (myPreviewEditor != null && !myPreviewEditor.isDisposed()) {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
myPreviewEditor.getCaretModel().moveToOffset(finalSelectedElement.getTextOffset());
myPreviewEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
}
}, ModalityState.NON_MODAL);
}
return myPreviewEditor.getComponent();
}
else if (selectedEntity == null) {
return new InspectionNodeInfo(myTree.getSelectedToolWrapper(), myProject);
}
return new JPanel();
}
@NotNull
@@ -477,7 +516,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
}
private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY);
final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null);
if (fixes != null) {
for (QuickFixAction fix : fixes) {
fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
@@ -833,6 +872,10 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
return myScope;
}
public void updateRightPanel() {
syncRightPanel();
}
private class CloseAction extends AnAction implements DumbAware {
private CloseAction() {
super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
@@ -905,4 +948,10 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren
}
}
}
private static void releaseEditor(@Nullable Editor editor) {
if (editor != null && !editor.isDisposed()) {
EditorFactory.getInstance().releaseEditor(editor);
}
}
}
@@ -67,7 +67,7 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor
@NotNull
Set<RefModule> getModuleProblems();
@Nullable
QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements);
QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, @Nullable CommonProblemDescriptor[] descriptors);
@NotNull
Map<RefEntity, CommonProblemDescriptor[]> getProblemElements();
@NotNull
@@ -86,6 +86,8 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor
GlobalInspectionContextImpl getContext();
void ignoreProblem(RefEntity refEntity, CommonProblemDescriptor problem, int idx);
@Nullable
QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, @NotNull Map<RefEntity, Set<QuickFix>> actions);
QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements,
@NotNull Map<RefEntity, CommonProblemDescriptor[]> descriptorMap,
@Nullable CommonProblemDescriptor[] allowedDescriptors);
void exportResults(@NotNull final Element parentNode);
}
@@ -109,6 +109,9 @@ public class InspectionTree extends Tree {
Object[] nodes = path.getPath();
for (int j = nodes.length - 1; j >= 0; j--) {
Object node = nodes[j];
if (node instanceof InspectionGroupNode) {
return null;
}
if (node instanceof InspectionNode) {
InspectionToolWrapper wrapper = ((InspectionNode)node).getToolWrapper();
if (toolWrapper == null) {
@@ -125,6 +128,36 @@ public class InspectionTree extends Tree {
return toolWrapper;
}
@Nullable
public RefEntity getCommonSelectedElement() {
final Object node = getCommonSelectedNode();
return node instanceof RefElementNode ? ((RefElementNode)node).getElement() : null;
}
@Nullable
private Object getCommonSelectedNode() {
final TreePath[] paths = getSelectionPaths();
if (paths == null) return null;
final Object[][] resolvedPaths = new Object[paths.length][];
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
resolvedPaths[i] = path.getPath();
}
Object currentCommonNode = null;
for (int i = 0; i < resolvedPaths[0].length; i++) {
final Object currentNode = resolvedPaths[0][i];
for (int j = 1; j < resolvedPaths.length; j++) {
final Object o = resolvedPaths[j][i];
if (!o.equals(currentNode)) {
return currentCommonNode;
}
}
currentCommonNode = currentNode;
}
return currentCommonNode;
}
@NotNull
public RefEntity[] getSelectedElements() {
TreePath[] selectionPaths = getSelectionPaths();
@@ -164,7 +197,6 @@ public class InspectionTree extends Tree {
}
public CommonProblemDescriptor[] getSelectedDescriptors() {
final InspectionToolWrapper toolWrapper = getSelectedToolWrapper();
if (getSelectionCount() == 0) return EMPTY_DESCRIPTORS;
final TreePath[] paths = getSelectionPaths();
final LinkedHashSet<CommonProblemDescriptor> descriptors = new LinkedHashSet<CommonProblemDescriptor>();
@@ -177,7 +209,9 @@ public class InspectionTree extends Tree {
private static void traverseDescriptors(InspectionTreeNode node, LinkedHashSet<CommonProblemDescriptor> descriptors){
if (node instanceof ProblemDescriptionNode) {
descriptors.add(((ProblemDescriptionNode)node).getDescriptor());
if (node.isValid() && !node.isResolved()) {
descriptors.add(((ProblemDescriptionNode)node).getDescriptor());
}
}
for(int i = node.getChildCount() - 1; i >= 0; i--){
traverseDescriptors((InspectionTreeNode)node.getChildAt(i), descriptors);
@@ -0,0 +1,60 @@
/*
* 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.ui;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.*;
/**
* @author Dmitry Batkovich
*/
public class InspectionViewNavigationPanel extends JPanel {
public InspectionViewNavigationPanel(InspectionTreeNode node, InspectionTree tree) {
setLayout(new BorderLayout());
setBorder(IdeBorderFactory.createEmptyBorder(5, 7, 0, 0));
final String titleLabelText = getTitleText(node instanceof InspectionRootNode, true);
add(new JBLabel(titleLabelText), BorderLayout.NORTH);
final JPanel links = new JPanel();
links.setLayout(new BoxLayout(links, BoxLayout.Y_AXIS));
links.add(Box.createVerticalStrut(JBUI.scale(10)));
add(BorderLayout.CENTER, links);
for (int i = 0; i < node.getChildCount(); i++) {
final TreeNode child = node.getChildAt(i);
final LinkLabel link = new LinkLabel(child.toString(), null) {
@Override
public void doClick() {
TreeUtil.selectInTree((DefaultMutableTreeNode)child, true, tree);
}
};
link.setBorder(IdeBorderFactory.createEmptyBorder(1, 17, 3, 1));
links.add(link);
}
}
@NotNull
public static String getTitleText(boolean addGroupWord, boolean addColon) {
return "Select inspection " + (addGroupWord ? "group" : "") + " to see problems" + (addColon ? ":" : ".");
}
}
@@ -0,0 +1,230 @@
/*
* 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.ui;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.QuickFix;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.QuickFixAction;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.ui.actions.SuppressActionWrapper;
import com.intellij.codeInspection.ui.actions.occurrences.GoToSubsequentOccurrenceAction;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.ui.ClickListener;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
import java.util.function.Supplier;
/**
* @author Dmitry Batkovich
*/
public class QuickFixToolbar extends JPanel {
private static final int MAX_FIX_COUNT = 2;
public QuickFixToolbar(@NotNull InspectionTree tree,
@NotNull Project project,
@Nullable Editor editor,
@Nullable QuickFixAction[] fixes) {
final boolean hasFixes = fixes != null && fixes.length != 0;
CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors();
int problemCount = descriptors.length;
final boolean multipleDescriptors = problemCount > 1;
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
List<JPanel> panels = new ArrayList<>();
for (int i = 0; i < (multipleDescriptors ? 2 : 1); i++) {
final JPanel line = new JPanel();
line.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
panels.add((JPanel)add(line));
}
if (multipleDescriptors || !hasFixes) {
panels.get(0).setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 0, 0));
}
//fill(getBulbPlacement(hasFixes), QuickFixToolbar::createBulbIcon, panels);
fill(getDescriptionLabelPlacement(multipleDescriptors),
() -> getLabel(fixes, tree.getSelectionCount() == 1 ? (InspectionTreeNode)tree.getSelectionPath().getLastPathComponent() : null, problemCount), panels);
fill(getFixesPlacement(hasFixes, multipleDescriptors), () -> createFixPanel(fixes), panels);
fill(getSuppressPlacement(multipleDescriptors), () -> createSuppressionCombo(tree.getSelectedToolWrapper()
, tree.getSelectionPath(), project), panels);
fill(multipleDescriptors && editor != null ? 1 : -1, () -> ActionManager.getInstance().createActionToolbar("", GoToSubsequentOccurrenceAction.createNextPreviousActions(
editor, descriptors), true).getComponent(), panels);
}
@NotNull
private static JComponent getLabel(QuickFixAction[] fixes, InspectionTreeNode targetNode, int problemsCount) {
final String targetName = targetNode instanceof RefElementNode ? ((RefElementNode)targetNode).getElement().getName() : null;
SimpleColoredComponent label = new SimpleColoredComponent();
boolean hasFixesNonIntersectedFixes = fixes != null && fixes.length == 0;
boolean hasFixes = fixes != null && fixes.length != 0;
label.append((hasFixes ? " Fix " : " ") + problemsCount + " problems" + (targetName == null ? "" : (" in " + targetName)) + (
hasFixesNonIntersectedFixes
? ":" : ""), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
if (hasFixesNonIntersectedFixes) {
label.append(" select a single problem to see its quick fixes");
}
if (!hasFixes) {
label.setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 6, 0));
}
return label;
}
@NotNull
private static JLabel createBulbIcon() {
final JLabel label = new JLabel(AllIcons.Actions.IntentionBulb);
label.setBorder(IdeBorderFactory.createEmptyBorder(0, 10, 0, 0));
return label;
}
private static JComponent createSuppressionCombo(@NotNull final InspectionToolWrapper toolWrapper,
@NotNull final TreePath path,
@NotNull final Project project) {
final ComboBoxAction action = new ComboBoxAction() {
{
getTemplatePresentation().setText("Suppress");
}
@NotNull
@Override
protected ActionGroup createPopupActionGroup(JComponent button) {
return new SuppressActionWrapper(project, toolWrapper, path);
}
};
action.setSmallVariant(false);
return action.createCustomComponent(action.getTemplatePresentation());
}
@NotNull
private static JPanel createFixPanel(QuickFixAction[] fixes) {
JPanel fixPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, JBUI.scale(3), JBUI.scale(5)));
if (fixes.length > MAX_FIX_COUNT) {
final ComboBoxAction fixComboBox = new ComboBoxAction() {
{
getTemplatePresentation().setText("Apply quick fixes");
getTemplatePresentation().setIcon(AllIcons.Actions.CreateFromUsage);
setSmallVariant(false);
}
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
final DefaultActionGroup actionGroup = new DefaultActionGroup();
for (QuickFixAction fix : fixes) {
actionGroup.add(fix);
}
return actionGroup;
}
};
fixPanel.add(fixComboBox.createCustomComponent(fixComboBox.getTemplatePresentation()));
}
else {
for (QuickFixAction fix : fixes) {
fixPanel.add(createQuickFixButton(fix));
}
}
return fixPanel;
}
private static void fill(int row,
@NotNull Supplier<JComponent> componentSupplier,
@NotNull List<JPanel> parent) {
if (row == -1) {
return;
}
final JPanel rowPanel = parent.get(row);
rowPanel.add(componentSupplier.get());
}
private static int getSuppressPlacement(boolean multipleDescriptors) {
return multipleDescriptors ? 1 : 0;
}
private static int getFixesPlacement(boolean hasQuickFixes, boolean multipleDescriptors) {
return hasQuickFixes ? multipleDescriptors ? 1 : 0 : -1;
}
private static int getDescriptionLabelPlacement(boolean multipleDescriptors) {
return multipleDescriptors ? 0 : -1;
}
private static int getBulbPlacement(boolean hasQuickFixes) {
return hasQuickFixes ? 0 : -1;
}
private static JComponent createQuickFixButton(@NotNull QuickFixAction fix) {
final MyCustomComponentLocalQuickFixWrapper action = new MyCustomComponentLocalQuickFixWrapper(fix);
return action.createCustomComponent(action.getTemplatePresentation());
}
private static class MyCustomComponentLocalQuickFixWrapper extends AnAction implements CustomComponentAction {
private QuickFixAction myUnderlying;
public MyCustomComponentLocalQuickFixWrapper(@NotNull QuickFixAction underlying) {
myUnderlying = underlying;
copyFrom(underlying);
}
@Override
public JComponent createCustomComponent(Presentation presentation) {
final JButton button = new JButton(presentation.getText());
button.setIcon(presentation.getIcon());
new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent event, int clickCount) {
actionPerformed(AnActionEvent.createFromAnAction(MyCustomComponentLocalQuickFixWrapper.this,
event,
"LOCAL_QUICK_FIX_WRAPPER_PANEL",
DataManager.getInstance().getDataContext(button)));
return true;
}
}.installOn(button);
return button;
}
@Override
public void update(AnActionEvent e) {
myUnderlying.update(e);
}
@Override
public void actionPerformed(AnActionEvent e) {
myUnderlying.actionPerformed(e);
}
}
}
@@ -71,11 +71,6 @@ public class RefElementNode extends InspectionTreeNode {
return myIcon.getIcon();
}
@Override
public int getProblemCount() {
return Math.max(1, super.getProblemCount());
}
public String toString() {
final RefEntity element = getElement();
if (element == null || !element.isValid()) {
@@ -32,6 +32,7 @@ import com.intellij.codeInspection.ui.RefElementNode;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Separator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
@@ -49,6 +50,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
public class SuppressActionWrapper extends ActionGroup {
@@ -60,7 +63,7 @@ public class SuppressActionWrapper extends ActionGroup {
public SuppressActionWrapper(@NotNull final Project project,
@NotNull final InspectionToolWrapper toolWrapper,
@NotNull final TreePath[] paths) {
@NotNull final TreePath... paths) {
super(InspectionsBundle.message("suppress.inspection.problem"), false);
myProject = project;
myManager = (InspectionManagerEx)InspectionManager.getInstance(myProject);
@@ -83,14 +86,25 @@ public class SuppressActionWrapper extends ActionGroup {
@Override
@NotNull
public SuppressTreeAction[] getChildren(@Nullable final AnActionEvent e) {
public AnAction[] getChildren(@Nullable final AnActionEvent e) {
final SuppressIntentionAction[] suppressActions = InspectionManagerEx.getSuppressActions(myToolWrapper);
if (suppressActions == null || suppressActions.length == 0) return new SuppressTreeAction[0];
final SuppressTreeAction[] actions = new SuppressTreeAction[suppressActions.length];
final AnAction[] actions = new AnAction[suppressActions.length + 1];
for (int i = 0; i < suppressActions.length; i++) {
final SuppressIntentionAction suppressAction = suppressActions[i];
actions[i] = new SuppressTreeAction(suppressAction);
}
actions[suppressActions.length] = Separator.getInstance();
Arrays.sort(actions, new Comparator<AnAction>() {
@Override
public int compare(AnAction a1, AnAction a2) {
return getWeight(a1) - getWeight(a2);
}
public int getWeight(AnAction a) {
return a instanceof Separator ? 0 : ((SuppressTreeAction)a).isSuppressAll() ? 1 : -1;
}
});
return actions;
}
@@ -219,5 +233,9 @@ public class SuppressActionWrapper extends ActionGroup {
}
return false;
}
public boolean isSuppressAll() {
return mySuppressAction.isSuppressAll();
}
}
}
@@ -0,0 +1,128 @@
/*
* 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.ui.actions.occurrences;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.ProblemDescriptorBase;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/**
* @author Dmitry Batkovich
*/
public class GoToSubsequentOccurrenceAction extends AnAction {
private final boolean myNext;
private OccurrencesManager myManager;
private GoToSubsequentOccurrenceAction(boolean next, OccurrencesManager manager) {
myNext = next;
myManager = manager;
getTemplatePresentation().setIcon(next ? AllIcons.Actions.NextOccurence : AllIcons.Actions.PreviousOccurence);
getTemplatePresentation().setDescription("Navigate to the " + (next ? "Next" : "Previous") + " Occurrence");
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabledAndVisible(myManager.hasValidSubsequent());
}
@Override
public void actionPerformed(AnActionEvent e) {
myManager.selectSubsequent(myNext);
}
public static ActionGroup createNextPreviousActions(@NotNull Editor editor, @NotNull CommonProblemDescriptor[] descriptors) {
OccurrencesManager manager = new OccurrencesManager(editor, descriptors);
return new DefaultActionGroup(new GoToSubsequentOccurrenceAction(false, manager), new GoToSubsequentOccurrenceAction(true, manager));
}
private static class OccurrencesManager {
private final Editor myEditor;
private final ProblemDescriptorBase[] myChildren;
private int mySelected = -1;
private OccurrencesManager(@NotNull Editor editor, @NotNull CommonProblemDescriptor[] descriptors) {
myEditor = editor;
myChildren = Arrays.stream(descriptors)
.filter(d -> d instanceof ProblemDescriptorBase)
.map(d -> (ProblemDescriptorBase) d)
.filter(d -> {
PsiElement element = d.getPsiElement();
return element != null && element.isValid();
})
.sorted((o1, o2) -> o1.getPsiElement().getTextOffset() - o2.getPsiElement().getTextOffset())
.toArray(ProblemDescriptorBase[]::new);
selectSubsequent(true);
}
public void selectSubsequent(boolean next) {
int subsequentIdz = mySelected;
while (true) {
subsequentIdz += next ? 1 : -1;
subsequentIdz += myChildren.length;
subsequentIdz %= myChildren.length;
if (mySelected == subsequentIdz) {
throw new IllegalStateException("Selection is unavailable");
}
ProblemDescriptorBase descriptorBase = myChildren[subsequentIdz];
if (descriptorBase.getPsiElement().isValid()) {
mySelected = subsequentIdz;
ApplicationManager.getApplication().invokeLater(() -> {
if (myEditor == null || myEditor.isDisposed()) return;
PsiElement toSelect = descriptorBase.getPsiElement();
PsiDocumentManager.getInstance(myEditor.getProject()).commitAllDocuments();
myEditor.getCaretModel().moveToOffset(toSelect.getTextOffset());
myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
myEditor.getSelectionModel().setSelection(toSelect.getTextRange().getStartOffset(), toSelect.getTextRange().getEndOffset());
}, ModalityState.NON_MODAL);
return;
}
}
}
public boolean hasValidSubsequent() {
if (myEditor.isDisposed()) {
return false;
}
int subsequentIdz = mySelected;
while (true) {
subsequentIdz += 1 + myChildren.length;
subsequentIdz %= myChildren.length;
if (mySelected == subsequentIdz) {
return false;
}
ProblemDescriptorBase descriptorBase = myChildren[subsequentIdz];
PsiElement element = descriptorBase.getPsiElement();
if (element != null && element.isValid()) {
return true;
}
}
}
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
@@ -67,6 +68,9 @@ public abstract class GotoActionBase extends AnAction {
myHistoryIndex = strings == null || strings.size() <= 1 || !ourLastStrings.containsKey(myInAction) ? 0 : 1;
gotoActionPerformed(e);
}
catch (ProcessCanceledException e1) {
myInAction = null;
}
catch (Throwable t) {
LOG.error(t);
myInAction = null;
@@ -57,8 +57,7 @@ public class AbstractNewProjectStep extends DefaultActionGroup implements DumbAw
NullableConsumer<ProjectSettingsStepBase> callback = customization.createCallback();
ProjectSpecificAction projectSpecificAction = customization.createProjectSpecificAction(callback);
addAll(projectSpecificAction.getChildren(null));
addProjectSpecificAction(projectSpecificAction);
DirectoryProjectGenerator[] generators = customization.getProjectGenerators();
customization.setUpBasicAction(projectSpecificAction, generators);
@@ -67,6 +66,10 @@ public class AbstractNewProjectStep extends DefaultActionGroup implements DumbAw
addAll(customization.getExtraActions(callback));
}
protected void addProjectSpecificAction(@NotNull final ProjectSpecificAction projectSpecificAction) {
addAll(projectSpecificAction.getChildren(null));
}
protected static abstract class Customization {
@NotNull
protected ProjectSpecificAction createProjectSpecificAction(@NotNull final NullableConsumer<ProjectSettingsStepBase> callback) {
@@ -92,8 +92,6 @@ import java.io.StringReader;
import java.util.*;
import java.util.List;
import com.intellij.util.containers.Queue;
/**
* User: anna
* Date: 31-May-2006
@@ -773,9 +771,9 @@ public class SingleInspectionProfilePanel extends JPanel {
}
// TODO 134099: see IntentionDescriptionPanel#readHTML
private boolean readHTML(String text) {
public static boolean readHTML(JEditorPane browser, String text) {
try {
myBrowser.read(new StringReader(text), null);
browser.read(new StringReader(text), null);
return true;
}
catch (IOException ignored) {
@@ -783,10 +781,10 @@ public class SingleInspectionProfilePanel extends JPanel {
}
}
// TODO 134099: see IntentionDescriptionPanel#toHTML
private String toHTML(String text) {
final HintHint hintHint = new HintHint(myBrowser, new Point(0, 0));
hintHint.setFont(UIUtil.getLabelFont());
// TODO 134099: see IntentionDescriptionPanel#setHTML
public static String toHTML(JEditorPane browser, String text, boolean miniFontSize) {
final HintHint hintHint = new HintHint(browser, new Point(0, 0));
hintHint.setFont(miniFontSize ? UIUtil.getLabelFont(UIUtil.FontSize.SMALL) : UIUtil.getLabelFont());
return HintUtil.prepareHintText(text, hintHint);
}
@@ -806,8 +804,8 @@ public class SingleInspectionProfilePanel extends JPanel {
final Descriptor defaultDescriptor = singleNode.getDefaultDescriptor();
final String description = defaultDescriptor.loadDescription();
try {
if (!readHTML(SearchUtil.markup(toHTML(description), myProfileFilter.getFilter()))) {
readHTML(toHTML("<b>" + UNDER_CONSTRUCTION + "</b>"));
if (!readHTML(myBrowser, SearchUtil.markup(toHTML(myBrowser, description, false), myProfileFilter.getFilter()))) {
readHTML(myBrowser, toHTML(myBrowser, "<b>" + UNDER_CONSTRUCTION + "</b>", false));
}
}
catch (Throwable t) {
@@ -819,11 +817,11 @@ public class SingleInspectionProfilePanel extends JPanel {
}
else {
readHTML(toHTML("Can't find inspection description."));
readHTML(myBrowser, toHTML(myBrowser, "Can't find inspection description.", false));
}
}
else {
readHTML(toHTML("Multiple inspections are selected. You can edit them as a single inspection."));
readHTML(myBrowser, toHTML(myBrowser, "Multiple inspections are selected. You can edit them as a single inspection.", false));
}
myOptionsPanel.removeAll();
@@ -1021,7 +1019,7 @@ public class SingleInspectionProfilePanel extends JPanel {
private void initOptionsAndDescriptionPanel() {
myOptionsPanel.removeAll();
readHTML(EMPTY_HTML);
readHTML(myBrowser, EMPTY_HTML);
myOptionsPanel.validate();
myOptionsPanel.repaint();
}
@@ -585,7 +585,9 @@ public abstract class BaseRefactoringProcessor implements Runnable {
@Override
public String getMessage() {
return StringUtil.join(messages, "\n");
List<String> result = new ArrayList<>(messages);
Collections.sort(result);
return StringUtil.join(result, "\n");
}
}
@@ -81,7 +81,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
DataContext context = e.getDataContext();
Project project = e.getProject();
if (project == null) return;
DefaultActionGroup group = createPopupActionGroup(button);
ActionGroup group = createPopupActionGroup(button);
ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(
myPopupTitle, group, context, false, shouldShowDisabledActions(), false, null, getMaxRows(), getPreselectCondition());
popup.setMinimumSize(new Dimension(getMinWidth(), getMinHeight()));
@@ -124,7 +124,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
@NotNull
protected abstract DefaultActionGroup createPopupActionGroup(JComponent button);
protected abstract ActionGroup createPopupActionGroup(JComponent button);
protected int getMaxRows() {
return 30;
@@ -152,7 +152,9 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
setModel(new MyButtonModel());
setHorizontalAlignment(LEFT);
setFocusable(false);
putClientProperty("styleCombo", Boolean.TRUE);
if (isSmallVariant()) {
putClientProperty("styleCombo", Boolean.TRUE);
}
Insets margins = getMargin();
setMargin(JBUI.insets(margins.top, 2, margins.bottom, 2));
if (isSmallVariant()) {
@@ -277,7 +279,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
}
protected JBPopup createPopup(Runnable onDispose) {
DefaultActionGroup group = createPopupActionGroup(this);
ActionGroup group = createPopupActionGroup(this);
DataContext context = getDataContext();
ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(
@@ -410,7 +412,6 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
final Dimension size = getSize();
if (SystemInfo.isMac && UIUtil.isUnderIntelliJLaF()) {
putClientProperty("styleCombo", Boolean.TRUE);
super.paint(g);
} else {
UISettings.setupAntialiasing(g);
@@ -570,7 +570,6 @@ public class JBScrollPane extends JScrollPane {
// so that if we don't ask after resetting the bounds we may have gotten the wrong answer.
if (viewport != null) {
viewport.setBounds(bounds);
pane.setComponentZOrder(viewport, pane.getComponentCount() - 1);
if (scrollable != null && hsbOpaque && vsbOpaque) {
viewTracksViewportWidth = scrollable.getScrollableTracksViewportWidth();
viewTracksViewportHeight = scrollable.getScrollableTracksViewportHeight();
@@ -655,7 +654,6 @@ public class JBScrollPane extends JScrollPane {
int overlapY = !hsbOnTop ? 0 : overlapHeight;
vsb.setBounds(vsbBounds.x, vsbBounds.y + overlapY, vsbBounds.width, vsbBounds.height - overlapHeight);
vsb.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.LEFT : Alignment.RIGHT);
pane.setComponentZOrder(vsb, 0);
}
// Modify the bounds of the translucent scroll bar.
if (!vsbOpaque) {
@@ -714,6 +712,20 @@ public class JBScrollPane extends JScrollPane {
vsbOnLeft ? rowHeadBounds.width : vsbBounds.width,
hsbOnTop ? hsbBounds.height : colHeadBounds.height);
}
if (!vsbOpaque && vsbNeeded || !hsbOpaque && hsbNeeded) {
fixComponentZOrder(vsb, 0);
fixComponentZOrder(viewport, -1);
}
}
private static void fixComponentZOrder(Component component, int index) {
if (component != null) {
Container parent = component.getParent();
synchronized (parent.getTreeLock()) {
if (index < 0) index += parent.getComponentCount();
parent.setComponentZOrder(component, index);
}
}
}
private void adjustForVSB(Rectangle bounds, Insets insets, Rectangle vsbBounds, boolean vsbOpaque, boolean vsbOnLeft) {
@@ -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.
@@ -96,7 +96,7 @@ public class JobLauncherImpl extends JobLauncher {
Boolean result = processImmediatelyIfTooFew(things, wrapper, runInReadAction, thingProcessor);
if (result != null) return result.booleanValue();
ApplierCompleter<T> applier = new ApplierCompleter<T>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null);
ApplierCompleter<T> applier = new ApplierCompleter<>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null);
try {
pool.invoke(applier);
if (applier.throwable != null) throw applier.throwable;
@@ -109,10 +109,7 @@ public class JobLauncherImpl extends JobLauncher {
// then task2 calls checkCancel() and get here
return false;
}
catch (RuntimeException e) {
throw e;
}
catch (Error e) {
catch (RuntimeException | Error e) {
throw e;
}
catch (Throwable e) {
@@ -125,7 +122,7 @@ public class JobLauncherImpl extends JobLauncher {
// if {@code things} are too few to be processed in the real pool, returns TRUE if processed successfully, FALSE if not
// returns null if things need to be processed in the real pool
private static <T> Boolean processImmediatelyIfTooFew(@NotNull final List<T> things,
final ProgressIndicator progress,
@NotNull final ProgressIndicator progress,
boolean runInReadAction,
@NotNull final Processor<? super T> thingProcessor) {
// commit can be invoked from within write action
@@ -136,24 +133,16 @@ public class JobLauncherImpl extends JobLauncher {
if (things.size() <= 1 || JobSchedulerImpl.CORES_COUNT <= CORES_FORK_THRESHOLD) {
final AtomicBoolean result = new AtomicBoolean(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() {
@Override
public void run() {
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < things.size(); i++) {
T thing = things.get(i);
if (!thingProcessor.process(thing)) {
result.set(false);
break;
}
}
}
}, progress);
Runnable runnable = () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> {
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < things.size(); i++) {
T thing = things.get(i);
if (!thingProcessor.process(thing)) {
result.set(false);
break;
}
}
};
}, progress);
if (runInReadAction) {
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(runnable)) return false;
}
@@ -318,35 +307,32 @@ public class JobLauncherImpl extends JobLauncher {
@Override
public Boolean call() throws Exception {
ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() {
@Override
public void run() {
try {
while (true) {
progress.checkCanceled();
T element = failedToProcess.poll();
if (element == null) element = things.take();
ProgressManager.getInstance().executeProcessUnderProgress(() -> {
try {
while (true) {
progress.checkCanceled();
T element = failedToProcess.poll();
if (element == null) element = things.take();
if (element == tombStone) {
things.offer(element);
result = true;
if (element == tombStone) {
things.offer(element);
result = true;
break;
}
try {
if (!thingProcessor.process(element)) {
result = false;
break;
}
try {
if (!thingProcessor.process(element)) {
result = false;
break;
}
}
catch (RuntimeException e) {
failedToProcess.add(element);
throw e;
}
}
catch (RuntimeException e) {
failedToProcess.add(element);
throw e;
}
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, progress);
return result;
@@ -372,7 +358,7 @@ public class JobLauncherImpl extends JobLauncher {
}
}
List<ForkJoinTask<Boolean>> tasks = new ArrayList<ForkJoinTask<Boolean>>();
List<ForkJoinTask<Boolean>> tasks = new ArrayList<>();
for (int i = 0; i < JobSchedulerImpl.CORES_COUNT; i++) {
tasks.add(pool.submit(new MyTask(i)));
}
@@ -218,6 +218,59 @@ public class Switcher extends AnAction implements DumbAware {
final Alarm myAlarm;
final SwitcherSpeedSearch mySpeedSearch;
final String myTitle;
private class MyFocusTraversalPolicy extends FocusTraversalPolicy {
@Override
public Component getComponentAfter(Container aContainer, Component aComponent) {
return aComponent == toolWindows ? files : toolWindows;
}
@Override
public Component getComponentBefore(Container aContainer, Component aComponent) {
return aComponent == toolWindows ? files : toolWindows;
}
@Override
public Component getFirstComponent(Container aContainer) {
return toolWindows;
}
@Override
public Component getLastComponent(Container aContainer) {
return files;
}
@Override
public Component getDefaultComponent(Container aContainer) {
return files;
}
}
private static void exchangeSelectionState (JBList toClear, JBList toSelect) {
if (toSelect.getModel().getSize() > 0) {
int index = Math.min(toClear.getSelectedIndex(), toSelect.getModel().getSize() - 1);
toSelect.setSelectedIndex(index);
toSelect.ensureIndexIsVisible(index);
toClear.clearSelection();
}
}
private class MyToolWindowsListFocusListener extends FocusAdapter {
@Override
public void focusGained(FocusEvent e) {
exchangeSelectionState(files, toolWindows);
}
}
private class MyFilesListFocusListener extends FocusAdapter {
@Override
public void focusGained(FocusEvent e) {
exchangeSelectionState(toolWindows, files);
}
}
final ClickListener myClickListener = new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent e, int clickCount) {
@@ -287,6 +340,7 @@ public class Switcher extends AnAction implements DumbAware {
}
toolWindows = new JBList(twModel);
toolWindows.addFocusListener(new MyToolWindowsListFocusListener());
if (pinned) {
new NameFilteringListModel<ToolWindow>(toolWindows, new Function<ToolWindow, String>() {
@NotNull
@@ -526,6 +580,7 @@ public class Switcher extends AnAction implements DumbAware {
ScrollingUtil.installActions(files);
files.addMouseListener(this);
files.addMouseMotionListener(this);
files.addFocusListener(new MyFilesListFocusListener());
myClickListener.installOn(files);
ScrollingUtil.ensureSelectionExists(files);
@@ -569,12 +624,6 @@ public class Switcher extends AnAction implements DumbAware {
}).createPopup();
if (isPinnedMode()) {
new AnAction(null, null, null) {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
changeSelection();
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), this, myPopup);
new AnAction(null, null, null) {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
@@ -600,6 +649,22 @@ public class Switcher extends AnAction implements DumbAware {
}
myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, myPopup);
myPopup.showInCenterOf(window);
Container popupFocusAncestor = myPopup.getContent().getFocusCycleRootAncestor();
popupFocusAncestor.setFocusTraversalPolicy(new MyFocusTraversalPolicy());
addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, "RIGHT");
addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, "LEFT");
}
private static void addFocusTraversalKeys (Container focusCycleRoot, int focusTraversalType, String keyStroke) {
Set<AWTKeyStroke> focusTraversalKeySet = focusCycleRoot.getFocusTraversalKeys(focusTraversalType);
Set<AWTKeyStroke> set = new HashSet<AWTKeyStroke>(focusTraversalKeySet);
set.add(KeyStroke.getKeyStroke(keyStroke));
focusCycleRoot.setFocusTraversalKeys(focusTraversalType, set);
}
@NotNull
@@ -653,12 +718,6 @@ public class Switcher extends AnAction implements DumbAware {
if (ctrl && isAutoHide() || enter) {
navigate(e);
}
else if (e.getKeyCode() == VK_LEFT) {
goLeft();
}
else if (e.getKeyCode() == VK_RIGHT) {
goRight();
}
}
KeyEvent lastEvent;
@@ -673,18 +732,6 @@ public class Switcher extends AnAction implements DumbAware {
closeTabOrToolWindow();
break;
}
if (e.getKeyCode() == ALT_KEY) {
changeSelection();
}
}
private void changeSelection() {
if (isFilesSelected()) {
goLeft();
}
else {
goRight();
}
}
private void closeTabOrToolWindow() {
@@ -721,7 +768,6 @@ public class Switcher extends AnAction implements DumbAware {
}
}, 300);
if (jList.getModel().getSize() == 1) {
goLeft();
removeElementAt(jList, selectedIndex);
this.remove(jList);
this.remove(separator);
@@ -793,38 +839,10 @@ public class Switcher extends AnAction implements DumbAware {
return getSelectedList() == toolWindows;
}
private void goRight() {
if ((isFilesSelected() || !isFilesVisible()) && isAutoHide()) {
cancel();
}
else {
if (files.getModel().getSize() > 0) {
final int index = Math.min(toolWindows.getSelectedIndex(), files.getModel().getSize() - 1);
files.setSelectedIndex(index);
files.ensureIndexIsVisible(index);
toolWindows.getSelectionModel().clearSelection();
IdeFocusManager.findInstanceByComponent(files).requestFocus(files, true);
}
}
}
private void cancel() {
myPopup.cancel();
}
private void goLeft() {
if (isToolWindowsSelected() && isAutoHide()) {
cancel();
}
else {
if (toolWindows.getModel().getSize() > 0) {
toolWindows.setSelectedIndex(Math.min(files.getSelectedIndex(), toolWindows.getModel().getSize() - 1));
files.getSelectionModel().clearSelection();
IdeFocusManager.findInstanceByComponent(toolWindows).requestFocus(toolWindows, true);
}
}
}
public void go(boolean forward) {
JBList selected = getSelectedList();
JList list = selected;
@@ -70,6 +70,7 @@
<completion.contributor order="last, before JsonSchemaCompletionContributor" language="JSON" implementationClass="com.jetbrains.jsonSchema.ide.JsonSchemaCompletionContributor"/>
<lang.documentationProvider language="JSON"
implementationClass="com.jetbrains.jsonSchema.ide.JsonSchemaDocumentationProvider"/>
<refactoring.elementListenerProvider implementation="com.jetbrains.jsonSchema.JsonSchemaRefactoringListenerProvider"/>
</extensions>
<extensions defaultExtensionNs="JavaScript">
<JsonSchema.ProviderFactory implementation="com.jetbrains.jsonSchema.extension.JsonSchemaImportedProviderFactory"/>
@@ -262,6 +262,6 @@
<extensionPoint name="remote.credentialsType" interface="com.intellij.remote.ext.CredentialsTypeEx"/>
<extensionPoint name="remote.credentialsLanguageContribution"
interface="com.intellij.remote.ext.CredentialsLanguageContribution"/>
<extensionPoint name="stripTrailingSpacesLineFilter" interface="com.intellij.openapi.editor.StripTrailingSpacesLineFilter"/>
<extensionPoint name="stripTrailingSpacesFilterFactory" interface="com.intellij.openapi.editor.StripTrailingSpacesFilterFactory"/>
</extensionPoints>
</idea-plugin>
@@ -27,4 +27,15 @@ SourceSafe
TFS
ro.redeul.google.go
com.intellij.plugins.html.instantEditing
com.jetbrains.intellij.api.watcher
com.jetbrains.intellij.api.watcher
com.jetbrains.php.framework
com.intellij.phing
com.intellij.apacheConfig
com.jetbrains.plugins.ini4idea
com.jetbrains.php.gae
com.jetbrains.php.drupal
com.jetbrains.php.wordPress
org.jetbrains.plugins.phpstorm-remote-interpreter
com.jetbrains.php.behat
com.jetbrains.php.blade
com.jetbrains.performancePlugin
@@ -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.
@@ -15,6 +15,8 @@
*/
package com.intellij.openapi.progress.impl;
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.concurrency.SensitiveProgressWrapper;
import com.intellij.ide.util.DelegatingProgressIndicator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
@@ -377,6 +379,19 @@ public class ProgressIndicatorTest extends LightPlatformTestCase {
}
}
public void testSOEUnderExtremelyNestedWrappedIndicator() {
ProgressIndicator indicator = new DaemonProgressIndicator();
for (int i=0;i<10000;i++) {
indicator = new SensitiveProgressWrapper(indicator);
}
ProgressManager.getInstance().executeProcessUnderProgress(() -> {
ProgressIndicator progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
assertTrue(progressIndicator instanceof SensitiveProgressWrapper);
progressIndicator.checkCanceled();
progressIndicator.isCanceled();
}, indicator);
}
public void testBombedIndicator() {
final int count = 10;
new BombedProgressIndicator(count).runBombed(() -> {
@@ -19,5 +19,5 @@ package org.jetbrains.debugger
* @author Konstantin.Ulitin
*/
interface WorkerManager {
val activeWorkers: Set<String>
val activeWorkers: Map<String, WebWorker>
}
@@ -21,5 +21,5 @@ import java.util.*
* @author Konstantin.Ulitin
*/
abstract class WorkerManagerBase : WorkerManager {
override val activeWorkers: Set<String> = Collections.emptySet()
override val activeWorkers: Map<String, WebWorker> = Collections.emptyMap()
}
@@ -0,0 +1,25 @@
/*
* 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 org.jetbrains.debugger
/**
* @author Konstantin.Ulitin
*/
interface WebWorker {
val id: String
val url: String
}
@@ -18,6 +18,7 @@ package com.intellij.usages.impl;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColors;
@@ -108,22 +109,30 @@ public class UsagePreviewPanel extends UsageContextPanelBase {
revalidate();
}
final Editor editor = myEditor;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (myProject.isDisposed()) return;
highlight(infos, editor);
}
});
highlight(infos, myEditor, myProject);
}
private static final Key<Boolean> IN_PREVIEW_USAGE_FLAG = Key.create("IN_PREVIEW_USAGE_FLAG");
private void highlight(@NotNull List<UsageInfo> infos, @NotNull Editor editor) {
if (editor != myEditor) return; //already disposed
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
MarkupModel markupModel = myEditor.getMarkupModel();
public static void highlight(@NotNull final List<UsageInfo> infos, @NotNull final Editor editor, @NotNull final Project project) {
@SuppressWarnings("UnnecessaryLocalVariable") final Editor editor1 = editor;
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (project.isDisposed()) return;
//noinspection ConstantConditions
if (editor1 == editor && !editor.isDisposed()) {
doHighlight(infos, editor, project);
}
}
}, ModalityState.NON_MODAL);
}
private static void doHighlight(@NotNull List<UsageInfo> infos, @NotNull Editor editor, @NotNull Project project) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
MarkupModel markupModel = editor.getMarkupModel();
for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) {
if (highlighter.getUserData(IN_PREVIEW_USAGE_FLAG) != null) {
highlighter.dispose();
@@ -154,15 +163,15 @@ public class UsagePreviewPanel extends UsageContextPanelBase {
}
}
// highlight injected element in host document textrange
textRange = InjectedLanguageManager.getInstance(myProject).injectedToHost(psiElement, textRange);
textRange = InjectedLanguageManager.getInstance(project).injectedToHost(psiElement, textRange);
RangeHighlighter highlighter = markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(),
HighlighterLayer.ADDITIONAL_SYNTAX, attributes,
HighlighterTargetArea.EXACT_RANGE);
highlighter.putUserData(IN_PREVIEW_USAGE_FLAG, Boolean.TRUE);
myEditor.getCaretModel().moveToOffset(textRange.getEndOffset());
editor.getCaretModel().moveToOffset(textRange.getEndOffset());
}
myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
}
private static final Key<UsagePreviewPanel> PREVIEW_EDITOR_FLAG = Key.create("PREVIEW_EDITOR_FLAG");
@@ -24,7 +24,6 @@ import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.CommonProcessors;
@@ -34,7 +33,10 @@ import com.intellij.util.io.IOUtil;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.Page;
import com.intellij.util.io.PersistentEnumerator;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.CommitId;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLogHashMap;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.impl.HashImpl;
import com.intellij.vcs.log.impl.VcsRootsRegistry;
import org.jetbrains.annotations.NotNull;
@@ -80,20 +82,19 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap {
private final PersistentEnumerator<CommitId> myPersistentEnumerator;
public VcsLogHashMapImpl(@NotNull final Project project, @NotNull Map<VirtualFile, VcsLogProvider> logProviders) throws IOException {
cleanupOldNaming(project, logProviders);
String logId = calcLogId(project, logProviders);
final File mapFile = new File(LOG_CACHE_APP_DIR, logId + "." + VERSION);
if (!mapFile.exists()) {
IOUtil.deleteAllFilesStartingWith(new File(LOG_CACHE_APP_DIR, logId));
}
Disposer.register(project, this);
myPersistentEnumerator = IOUtil.openCleanOrResetBroken(new ThrowableComputable<PersistentEnumerator<CommitId>, IOException>() {
@Override
public PersistentEnumerator<CommitId> compute() throws IOException {
return new PersistentEnumerator<CommitId>(mapFile, new MyCommitIdKeyDescriptor(project), Page.PAGE_SIZE);
}
}, mapFile);
Disposer.register(project, this);
}
@NotNull
@@ -102,13 +103,6 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap {
return project.getLocationHash() + "." + Integer.toHexString(hashcode);
}
// TODO remove in IDEA 15
private static void cleanupOldNaming(@NotNull Project project, @NotNull Map<VirtualFile, VcsLogProvider> providers) {
int hashcode = calcLogProvidersHash(providers);
String oldLogId = project.getName() + "." + hashcode;
FileUtil.delete(new File(LOG_CACHE, oldLogId));
}
private static int calcLogProvidersHash(@NotNull final Map<VirtualFile, VcsLogProvider> logProviders) {
List<VirtualFile> sortedRoots = ContainerUtil.sorted(logProviders.keySet(), new Comparator<VirtualFile>() {
@Override
@@ -196,7 +190,7 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap {
}
private static class MyCommitIdKeyDescriptor implements KeyDescriptor<CommitId> {
private final VcsRootsRegistry myRootsRegistry;
@NotNull private final VcsRootsRegistry myRootsRegistry;
public MyCommitIdKeyDescriptor(@NotNull Project project) {
myRootsRegistry = ServiceManager.getService(project, VcsRootsRegistry.class);
@@ -35,20 +35,20 @@ public class BranchesPanel extends JPanel {
private static final int TOP = 2;
private static final int BOTTOM = 3;
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final VcsLogUiImpl myUI;
@NotNull private final VcsLogUiImpl myUi;
@NotNull private final VcsRefPainter myReferencePainter;
@NotNull private LinkedHashMap<VirtualFile, List<RefGroup>> myRefGroups;
@Nullable private Collection<VirtualFile> myRoots = null;
public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI, @NotNull RefsModel initialRefsModel) {
public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl ui, @NotNull RefsModel initialRefsModel) {
super(new FlowLayout(FlowLayout.LEADING, BIG_ROOTS_GAP - 2 * SMALL_ROOTS_GAP, 0));
setBorder(new EmptyBorder(TOP, SMALL_ROOTS_GAP, BOTTOM, SMALL_ROOTS_GAP));
myDataHolder = dataHolder;
myUI = UI;
myUi = ui;
myRefGroups = getRefsToDisplayOnPanel(initialRefsModel);
myReferencePainter = new VcsRefPainter(myUI.getColorManager(), true);
myReferencePainter = new VcsRefPainter(myUi.getColorManager(), true);
recreateComponents();
}
@@ -129,7 +129,7 @@ public class BranchesPanel extends JPanel {
myGroups = groups;
for (RefGroup group : myGroups) {
add(new ReferenceGroupComponent(group, myReferencePainter, myUI, root));
add(new ReferenceGroupComponent(group, myReferencePainter, myUi, root));
}
}
}
@@ -83,7 +83,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
private static final int MAX_ROWS_TO_CALC_WIDTH = 1000;
private static final int MAX_ROWS_TO_CALC_OFFSET = 100;
@NotNull private final VcsLogUiImpl myUI;
@NotNull private final VcsLogUiImpl myUi;
private final VcsLogDataHolder myLogDataHolder;
private final MyDummyTableCellEditor myDummyEditor = new MyDummyTableCellEditor();
@NotNull private final TableCellRenderer myDummyRenderer = new DefaultTableCellRenderer();
@@ -104,13 +104,13 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
}
};
public VcsLogGraphTable(@NotNull VcsLogUiImpl UI, @NotNull final VcsLogDataHolder logDataHolder, @NotNull VisiblePack initialDataPack) {
public VcsLogGraphTable(@NotNull VcsLogUiImpl ui, @NotNull final VcsLogDataHolder logDataHolder, @NotNull VisiblePack initialDataPack) {
super();
myUI = UI;
myUi = ui;
myLogDataHolder = logDataHolder;
myGraphCommitCellRenderer = new GraphCommitCellRender(logDataHolder, myGraphCellPainter, this);
setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI));
setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUi));
setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRenderer);
setDefaultRenderer(String.class, new StringCellRenderer());
@@ -125,7 +125,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
PopupHandler.installPopupHandler(this, VcsLogActionPlaces.POPUP_ACTION_GROUP, VcsLogActionPlaces.VCS_LOG_TABLE_PLACE);
ScrollingUtil.installActions(this, false);
GraphTableModel model = new GraphTableModel(initialDataPack, myLogDataHolder, myUI);
GraphTableModel model = new GraphTableModel(initialDataPack, myLogDataHolder, myUi);
setModel(model);
initColumnSize();
}
@@ -193,10 +193,10 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
private void setRootColumnSize(TableColumn column) {
int rootWidth;
if (!myUI.isMultipleRoots()) {
if (!myUi.isMultipleRoots()) {
rootWidth = 0;
}
else if (!myUI.isShowRootNames()) {
else if (!myUi.isShowRootNames()) {
rootWidth = ROOT_INDICATOR_WIDTH;
}
else {
@@ -231,7 +231,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
return "<html><b>" +
((VirtualFile)at).getPresentableUrl() +
"</b><br/>Click to " +
(myUI.isShowRootNames() ? "collapse" : "expand") +
(myUi.isShowRootNames() ? "collapse" : "expand") +
"</html>";
}
}
@@ -363,7 +363,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
private boolean expandOrCollapseRoots(@NotNull MouseEvent e) {
TableColumn column = getRootColumnOrNull(e);
if (column != null) {
myUI.setShowRootNames(!myUI.isShowRootNames());
myUi.setShowRootNames(!myUi.isShowRootNames());
return true;
}
return false;
@@ -400,7 +400,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi
}
}
myUI.repaintUI(); // in case of repaintUI doing something more than just repainting this table in some distant future
myUi.repaintUI(); // in case of repaintUI doing something more than just repainting this table in some distant future
if (answer == null) {
return;
@@ -45,9 +45,9 @@ public class GraphTableModel extends AbstractTableModel {
private boolean myMoreRequested;
public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI) {
public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl ui) {
myLogDataHolder = dataHolder;
myUi = UI;
myUi = ui;
myDataPack = dataPack;
myDataHolder = dataHolder;
}
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.InspectionGadgetsFix;
import org.jetbrains.annotations.NonNls;
@@ -40,8 +41,7 @@ public class ChangeModifierFix extends InspectionGadgetsFix {
@Override
@NotNull
public String getName() {
return InspectionGadgetsBundle.message("change.modifier.quickfix",
modifierText);
return InspectionGadgetsBundle.message("change.modifier.quickfix", VisibilityUtil.toPresentableText(modifierText));
}
@NotNull
@@ -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.
@@ -15,13 +15,21 @@
*/
package com.siyeh.ig.j2me;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.psi.*;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.MethodCallUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class MethodCallInLoopConditionInspectionBase extends BaseInspection {
public boolean ignoreIterationMethods = true;
@Override
@NotNull
public String getDisplayName() {
@@ -39,12 +47,18 @@ public class MethodCallInLoopConditionInspectionBase extends BaseInspection {
return true;
}
@Nullable
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel("Ignore iteration method calls", this, "ignoreIterationMethods");
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new MethodCallInLoopConditionVisitor();
}
private static class MethodCallInLoopConditionVisitor extends BaseInspectionVisitor {
private class MethodCallInLoopConditionVisitor extends BaseInspectionVisitor {
@Override
public void visitForStatement(@NotNull PsiForStatement statement) {
@@ -80,9 +94,17 @@ public class MethodCallInLoopConditionInspectionBase extends BaseInspection {
final PsiElementVisitor visitor = new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitMethodCallExpression(
@NotNull PsiMethodCallExpression expression) {
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (ignoreIterationMethods) {
final PsiMethod method = expression.resolveMethod();
if (MethodCallUtils.isCallToMethod(expression, CommonClassNames.JAVA_UTIL_ITERATOR, PsiType.BOOLEAN, "hasNext") ||
MethodCallUtils.isCallToMethod(expression, "java.util.ListIterator", PsiType.BOOLEAN, "hasPrevious") ||
MethodCallUtils.isCallToMethod(expression, "java.sql.ResultSet", PsiType.BOOLEAN, "next") ||
MethodCallUtils.isCallToMethod(expression, "java.util.Enumeration", PsiType.BOOLEAN, "hasMoreElements")) {
return;
}
}
registerMethodCallError(expression);
}
};
@@ -1,22 +0,0 @@
package com.siyeh.igtest.j2me;
public class MethodCallInLoopConditionInspection {
public void foo() {
for (int i = 0; i < bar(); i++) {
}
while (bar() != 4) {
foo();
}
do {
foo();
}
while (bar() != 4);
}
private int bar() {
return 3;
}
}

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