Merge remote-tracking branch 'origin/master'

This commit is contained in:
Anton Makeev
2016-11-14 18:07:21 +01:00
61 changed files with 1201 additions and 555 deletions
@@ -245,7 +245,7 @@ public class BreakpointManager {
return null;
}
@NotNull
@Nullable
public ExceptionBreakpoint addExceptionBreakpoint(@NotNull final String exceptionClassName, final String packageName) {
ApplicationManager.getApplication().assertIsDispatchThread();
final JavaExceptionBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(JavaExceptionBreakpointType.class);
@@ -47,7 +47,6 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.ContainerUtil;
@@ -150,14 +149,17 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
}
AtomicReference<ProgressIndicator> indicatorRef = new AtomicReference<>();
ApplicationManager.getApplication().invokeAndWait(() -> indicatorRef.set(new ProgressWindowWithNotification(true, myProject)));
ApplicationManager.getApplication()
.invokeAndWait(() -> indicatorRef.set(new ProgressWindowWithNotification(true, false, myProject, "Cancel emulation")));
ProgressIndicator indicator = indicatorRef.get();
ProgressManager.getInstance().executeProcessUnderProgress(
() -> processPreparedSubTypes(baseType, subType -> createRequestForPreparedClassEmulated(debugProcess, subType, false), indicator),
indicator);
if (indicator.isCanceled()) {
ApplicationManager.getApplication().invokeLater(
() -> DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeBreakpoint(this));
ApplicationManager.getApplication().invokeLater(() -> {
getProperties().EMULATED = false;
fireBreakpointChanged();
});
}
}
@@ -493,7 +495,7 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
}
public boolean isEmulated() {
return getProperties().EMULATED && Registry.is("debugger.emulate.method.breakpoints");
return getProperties().EMULATED;
}
private boolean isWatchEntry() {
@@ -17,7 +17,6 @@
package com.intellij.debugger.ui.breakpoints;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.util.ui.DialogUtil;
import com.intellij.util.ui.JBUI;
@@ -98,8 +97,7 @@ public class MethodBreakpointPropertiesPanel extends XBreakpointCustomProperties
@Override
public void loadFrom(@NotNull XBreakpoint<JavaMethodBreakpointProperties> breakpoint) {
myEmulatedCheckBox.setVisible(
Registry.is("debugger.emulate.method.breakpoints") && breakpoint.getType() instanceof JavaMethodBreakpointType);
myEmulatedCheckBox.setVisible(breakpoint.getType() instanceof JavaMethodBreakpointType);
myEmulatedCheckBox.setSelected(breakpoint.getProperties().EMULATED);
myWatchEntryCheckBox.setSelected(breakpoint.getProperties().WATCH_ENTRY);
@@ -65,7 +65,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
public boolean ADD_APPLET_TO_ENTRIES = true;
public boolean ADD_SERVLET_TO_ENTRIES = true;
public boolean ADD_NONJAVA_TO_ENTRIES = true;
protected boolean TEST_ENTRY_POINTS = true;
private boolean TEST_ENTRY_POINTS = true;
public static final String DISPLAY_NAME = InspectionsBundle.message("inspection.dead.code.display.name");
public static final String SHORT_NAME = HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME;
@@ -290,7 +290,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
if (isSuppressed || !scope.contains(file)) {
getEntryPointsManager(globalContext).addEntryPoint(refElement, false);
}
return;
}
}
}
@@ -323,12 +322,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
String qualifiedName = psiClass != null ? psiClass.getQualifiedName() : null;
if (qualifiedName != null) {
final GlobalSearchScope projectScope = GlobalSearchScope.projectScope(globalContext.getProject());
final PsiNonJavaFileReferenceProcessor processor = new PsiNonJavaFileReferenceProcessor() {
@Override
public boolean process(PsiFile file, int startOffset, int endOffset) {
getEntryPointsManager(globalContext).addEntryPoint(refElement, false);
return false;
}
final PsiNonJavaFileReferenceProcessor processor = (file, startOffset, endOffset) -> {
getEntryPointsManager(globalContext).addEntryPoint(refElement, false);
return false;
};
final DelegatingGlobalSearchScope globalSearchScope = new DelegatingGlobalSearchScope(projectScope) {
@Override
@@ -450,9 +446,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
checkForReachableRefs(globalContext);
final RefFilter filter = myPhase == 1 ? new StrictUnreferencedFilter(this, globalContext) :
new RefUnreachableFilter(this, globalContext);
final boolean[] requestAdded = {false};
LOG.assertTrue(myProcessedSuspicious != null, "phase: " + myPhase);
final boolean[] requestAdded = {false};
globalContext.getRefManager().iterate(new RefJavaVisitor() {
@Override
public void visitElement(@NotNull RefEntity refEntity) {
@@ -469,12 +465,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
getEntryPointsManager(globalContext).addEntryPoint(refField, false);
}
else {
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueFieldUsagesProcessor(refField, new GlobalJavaInspectionContext.UsagesProcessor() {
@Override
public boolean process(PsiReference psiReference) {
getEntryPointsManager(globalContext).addEntryPoint(refField, false);
return false;
}
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueFieldUsagesProcessor(refField, psiReference -> {
getEntryPointsManager(globalContext).addEntryPoint(refField, false);
return false;
});
requestAdded[0] = true;
}
@@ -506,20 +499,14 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
public void visitClass(@NotNull final RefClass refClass) {
myProcessedSuspicious.add(refClass);
if (!refClass.isAnonymous()) {
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueDerivedClassesProcessor(refClass, new GlobalJavaInspectionContext.DerivedClassesProcessor() {
@Override
public boolean process(PsiClass inheritor) {
getEntryPointsManager(globalContext).addEntryPoint(refClass, false);
return false;
}
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueDerivedClassesProcessor(refClass, inheritor -> {
getEntryPointsManager(globalContext).addEntryPoint(refClass, false);
return false;
});
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueClassUsagesProcessor(refClass, new GlobalJavaInspectionContext.UsagesProcessor() {
@Override
public boolean process(PsiReference psiReference) {
getEntryPointsManager(globalContext).addEntryPoint(refClass, false);
return false;
}
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueClassUsagesProcessor(refClass, psiReference -> {
getEntryPointsManager(globalContext).addEntryPoint(refClass, false);
return false;
});
requestAdded[0] = true;
}
@@ -549,12 +536,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool {
private static void enqueueMethodUsages(GlobalInspectionContext globalContext, final RefMethod refMethod) {
if (refMethod.getSuperMethods().isEmpty()) {
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueMethodUsagesProcessor(refMethod, new GlobalJavaInspectionContext.UsagesProcessor() {
@Override
public boolean process(PsiReference psiReference) {
getEntryPointsManager(globalContext).addEntryPoint(refMethod, false);
return false;
}
globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueMethodUsagesProcessor(refMethod, psiReference -> {
getEntryPointsManager(globalContext).addEntryPoint(refMethod, false);
return false;
});
}
else {
@@ -25,7 +25,6 @@ import com.intellij.codeInspection.reference.*;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspectionBase;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.DefUseUtil;
import com.intellij.ui.ScrollPaneFactory;
@@ -34,7 +33,6 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTabbedPane;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -43,10 +41,11 @@ import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase {
private final UnusedParametersInspection myUnusedParameters = new UnusedParametersInspection();
@@ -155,12 +154,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
final ButtonGroup group = new ButtonGroup();
group.add(asEntryPoint);
group.add(asUnused);
final ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setTestEntryPoints(asEntryPoint.isSelected());
}
};
final ActionListener listener = e -> setTestEntryPoints(asEntryPoint.isSelected());
asEntryPoint.addActionListener(listener);
asUnused.addActionListener(listener);
@@ -187,12 +181,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
myMainsCheckbox = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.main"));
myMainsCheckbox.setSelected(ADD_MAINS_TO_ENTRIES);
myMainsCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ADD_MAINS_TO_ENTRIES = myMainsCheckbox.isSelected();
}
});
myMainsCheckbox.addActionListener(e -> ADD_MAINS_TO_ENTRIES = myMainsCheckbox.isSelected());
add(myMainsCheckbox, gc);
@@ -200,23 +189,13 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
myAppletToEntries = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.applet"));
myAppletToEntries.setSelected(ADD_APPLET_TO_ENTRIES);
myAppletToEntries.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ADD_APPLET_TO_ENTRIES = myAppletToEntries.isSelected();
}
});
myAppletToEntries.addActionListener(e -> ADD_APPLET_TO_ENTRIES = myAppletToEntries.isSelected());
add(myAppletToEntries, gc);
gc.gridy++;
myServletToEntries = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.servlet"));
myServletToEntries.setSelected(ADD_SERVLET_TO_ENTRIES);
myServletToEntries.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
ADD_SERVLET_TO_ENTRIES = myServletToEntries.isSelected();
}
});
myServletToEntries.addActionListener(e -> ADD_SERVLET_TO_ENTRIES = myServletToEntries.isSelected());
add(myServletToEntries, gc);
gc.gridy++;
@@ -224,12 +203,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
if (extension.showUI()) {
final JCheckBox extCheckbox = new JCheckBox(extension.getDisplayName());
extCheckbox.setSelected(extension.isSelected());
extCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
extension.setSelected(extCheckbox.isSelected());
}
});
extCheckbox.addActionListener(e -> extension.setSelected(extCheckbox.isSelected()));
add(extCheckbox, gc);
gc.gridy++;
}
@@ -238,12 +212,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
myNonJavaCheckbox =
new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.external"));
myNonJavaCheckbox.setSelected(ADD_NONJAVA_TO_ENTRIES);
myNonJavaCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ADD_NONJAVA_TO_ENTRIES = myNonJavaCheckbox.isSelected();
}
});
myNonJavaCheckbox.addActionListener(e -> ADD_NONJAVA_TO_ENTRIES = myNonJavaCheckbox.isSelected());
gc.weighty = 1;
add(myNonJavaCheckbox, gc);
@@ -267,10 +236,10 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase
private class UnusedVariablesGraphAnnotator extends RefGraphAnnotator {
private final InspectionManager myInspectionManager;
private GlobalInspectionContextImpl myContext;
private Map<String, Tools> myTools;
private final GlobalInspectionContextImpl myContext;
private final Map<String, Tools> myTools;
public UnusedVariablesGraphAnnotator(InspectionManager inspectionManager, RefManager refManager) {
UnusedVariablesGraphAnnotator(InspectionManager inspectionManager, RefManager refManager) {
myInspectionManager = inspectionManager;
myContext = (GlobalInspectionContextImpl)((RefManagerImpl)refManager).getContext();
myTools = myContext.getTools();
@@ -158,7 +158,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
}
@NotNull
private MostlySingularMultiMap<String, AnnotationData> getDataFromFile(@NotNull PsiFile file) {
MostlySingularMultiMap<String, AnnotationData> getDataFromFile(@NotNull PsiFile file) {
Pair<MostlySingularMultiMap<String, AnnotationData>, Long> cached = myAnnotationFileToDataAndModStampCache.get(file);
long fileModificationStamp = file.getModificationStamp();
if (cached != null && cached.getSecond() == fileModificationStamp) {
@@ -336,16 +336,16 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
throw new UnsupportedOperationException();
}
protected void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName,
@NotNull PsiFile fromFile,
@NotNull List<PsiFile> annotationFiles) {
void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName,
@NotNull PsiFile fromFile,
@NotNull List<PsiFile> annotationFiles) {
VirtualFile virtualFile = fromFile.getVirtualFile();
if (virtualFile != null) {
myExternalAnnotationsCache.put(virtualFile, annotationFiles);
}
}
private static class AnnotationData {
static class AnnotationData {
private final String annotationClassFqName;
private final String annotationParameters;
@@ -357,7 +357,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
}
@NotNull
private PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) {
PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) {
PsiAnnotation a = myAnnotation;
if (a == null) {
String text = "@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "(" + annotationParameters + ")");
@@ -0,0 +1,186 @@
/*
* 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.codeInsight;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.testFramework.IdeaTestCase;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class ExternalAnnotationsManagerTest extends IdeaTestCase {
@Override
protected Sdk getTestProjectJdk() {
Sdk jdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
Sdk sdk = PsiTestUtil.addJdkAnnotations(jdk);
String home = jdk.getHomeDirectory().getParent().getPath();
String toolsPath = home + "/lib/tools.jar!/";
VfsRootAccess.allowRootAccess(getTestRootDisposable(), home);
VirtualFile toolsJar = JarFileSystem.getInstance().findFileByPath(toolsPath);
Sdk plusTools = PsiTestUtil.addRootsToJdk(sdk, OrderRootType.CLASSES, toolsJar);
Collection<String> utilClassPath = PathManager.getUtilClassPath();
VirtualFile[] files = utilClassPath.stream()
.map(path -> path.endsWith(".jar") ?
JarFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path) + "!/") :
LocalFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path)))
.toArray(VirtualFile[]::new);
Sdk result = PsiTestUtil.addRootsToJdk(plusTools, OrderRootType.CLASSES, files);
return result;
}
public void testBundledAnnotationXmls() {
String root = PathManagerEx.getCommunityHomePath() + "/java/jdkAnnotations";
findAnnotationsXmlAndCheck(root);
}
private void findAnnotationsXmlAndCheck(String root) {
VirtualFile jdkAnnoRoot = LocalFileSystem.getInstance().findFileByPath(root);
VfsUtilCore.visitChildrenRecursively(jdkAnnoRoot, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (file.getName().equals("annotations.xml")) {
check(file);
}
return true;
}
});
}
// some android classes are missing in IDEA, e.g. android.support.annotation.NonNull
public void _testAndroidAnnotationsXml() {
VirtualFile lib = LocalFileSystem.getInstance().findFileByPath(PathManagerEx.getCommunityHomePath() + "/android/android/lib");
VirtualFile[] androidJars = Arrays.stream(lib.getChildren())
.map(file -> file.getName().endsWith(".jar") ?
JarFileSystem.getInstance().getJarRootForLocalFile(file) :
file)
.toArray(VirtualFile[]::new);
ApplicationManager.getApplication().runWriteAction(() -> ProjectRootManager.getInstance(getProject())
.setProjectSdk(PsiTestUtil.addRootsToJdk(getTestProjectJdk(), OrderRootType.CLASSES, androidJars)));
String root = PathManagerEx.getCommunityHomePath() + "/android/android/annotations";
findAnnotationsXmlAndCheck(root);
}
private void check(VirtualFile file) {
//System.out.println("file = " + file);
ExternalAnnotationsManagerImpl manager = (ExternalAnnotationsManagerImpl)ExternalAnnotationsManager.getInstance(getProject());
PsiFile psiFile = getPsiManager().findFile(file);
MostlySingularMultiMap<String, BaseExternalAnnotationsManager.AnnotationData> map = manager.getDataFromFile(psiFile);
for (String externalName : map.keySet()) {
checkExternalName(psiFile, externalName);
// 'annotation name="org.jetbrains.annotations.NotNull"' should have FQN
for (BaseExternalAnnotationsManager.AnnotationData annotationData : map.get(externalName)) {
PsiAnnotation annotation = annotationData.getAnnotation(manager);
String nameText = annotation.getNameReferenceElement().getText();
assertClassFqn(nameText, psiFile, externalName);
}
}
}
private PsiClass assertClassFqn(String text, PsiFile psiFile, String externalName) {
if (!PsiNameHelper.getInstance(getProject()).isQualifiedName(text) || !text.contains(".")) {
fail("'" + text + "' doesn't seem like a FQN", psiFile, externalName);
}
PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass(text, GlobalSearchScope.allScope(getProject()));
if (aClass == null) {
fail("'" + text + "' doesn't resolve to a class", psiFile, externalName);
}
return aClass;
}
private static void fail(String error, PsiFile psiFile, String externalName) {
int offset = psiFile.getText().indexOf(XmlUtil.escape(externalName));
int line = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile).getLineNumber(offset);
fail(error + "\nFile: " + psiFile.getVirtualFile().getPath() + ":" + (line+1) + " (offset: "+offset+")");
}
private void checkExternalName(PsiFile psiFile, String externalName) {
// 'item name="java.lang.ClassLoader java.net.URL getResource(java.lang.String) 0"' should have all FQNs
String unescaped = StringUtil.unescapeXml(externalName);
List<String> words = StringUtil.split(unescaped, " ");
String className = words.get(0);
PsiClass aClass = assertClassFqn(className, psiFile, externalName);
if (words.size() == 1) return;
String rest = unescaped.substring(className.length() + " ".length());
if (rest.indexOf('(') == -1) {
// field
String field = StringUtil.trim(rest);
PsiField psiField = aClass.findFieldByName(field, false);
if (psiField == null) {
fail("Field '"+field+"' not found in class '"+aClass.getQualifiedName()+"'", psiFile, externalName);
}
return;
}
String methodName = ContainerUtil.getLastItem(StringUtil.getWordsIn(rest.substring(0, rest.indexOf('('))));
String methodSignature = rest.substring(0, rest.indexOf(')') + 1);
String methodExternalName = className + " " + methodSignature;
List<PsiMethod> methods = Arrays.stream(aClass.getMethods())
.filter(method -> methodExternalName.equals(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE)))
.collect(Collectors.toList());
boolean found = !methods.isEmpty();
if (!found) {
List<String> candidates = Arrays.stream(aClass.findMethodsByName(methodName, false))
.map(method -> XmlUtil.escape(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE)))
.collect(Collectors.toList());
String additionalMsg = candidates.isEmpty() ? "" : "\nMaybe you have meant one of these methods instead:\n"+StringUtil.join(candidates, "\n")+"\n";
fail("This method was not found in class '"+aClass.getQualifiedName()+"':\n"+"'"+methodSignature+"'"+additionalMsg, psiFile, externalName);
}
String parameterNumberText = StringUtil.trim(rest.substring(rest.indexOf(')') + 1));
if (parameterNumberText.isEmpty()) return;
try {
int paramNumber = Integer.parseInt(parameterNumberText);
PsiMethod method = methods.get(0);
if (method.getParameterList().getParametersCount() <= paramNumber) {
fail("Parameter number '"+paramNumber+"' is too big for a method '"+methodSignature+"'", psiFile, externalName);
}
}
catch (NumberFormatException e) {
fail("Parameter number is not an integer: '"+parameterNumberText+"'", psiFile, externalName);
}
}
}
@@ -20,7 +20,9 @@ import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings
import com.intellij.openapi.editor.Inlay
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.DebugUtil
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.util.DocumentUtil
import org.assertj.core.api.Assertions.assertThat
@@ -88,7 +90,13 @@ class InlayAssert(private val file: PsiFile, val inlays: List<Inlay>) {
val hintOffsets = hints.map { it.first }
val hintNames = hints.map { it.second }
assertThat(hints.size).isEqualTo(expectedInlays.size)
val elements = hintOffsets.mapNotNull { file.findElementAt(it) }
assertThat(hints.size)
.withFailMessage("Expected ${expectedInlays.size} elements with hints, Actual elements count ${hints.size}" +
": ${elements.joinToString(", ")}, file text: \n\n ${file.text} \n\n isCommitted ${isCommitted(file)} \n\n" +
"Psi: \n ${DebugUtil.psiToString(file, true)}")
.isEqualTo(expectedInlays.size)
val expect = expectedInlays.map { it.substringBefore("->") to it.substringAfter("->") }
val expectedHintNames = expect.map { it.first }
@@ -96,9 +104,16 @@ class InlayAssert(private val file: PsiFile, val inlays: List<Inlay>) {
assertThat(hintNames).isEqualTo(expectedHintNames)
val wordsAfter = hintOffsets.mapNotNull { file.findElementAt(it) }.map { it.text }
val wordsAfter = elements.map { it.text }
assertThat(wordsAfter).isEqualTo(expectedWordsAfter)
}
private fun isCommitted(file: PsiFile): Boolean {
val manager = PsiDocumentManager.getInstance(file.project)
val document = manager.getDocument(file)
assertThat(document).isNotNull()
return manager.isCommitted(document!!)
}
}
@@ -708,13 +708,16 @@ class Key {
setup("""
class Test {
void test() {
xxx(100);
check(1 + 1);
int i=1; check(1 + 1 + 1);
}
void check(int isShow) {}
void xxx(int followTheSum) {}
}
""")
onLineStartingWith("xxx").assertInlays("followTheSum->100")
onLineStartingWith("check").assertInlays("isShow->1")
onLineStartingWith("int").assertInlays("isShow->1")
}
@@ -32,6 +32,8 @@ import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.*
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent
import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry
@@ -64,6 +66,7 @@ import com.intellij.util.FileContentUtil
import com.intellij.util.Processor
import com.intellij.util.indexing.*
import com.intellij.util.io.*
import groovy.transform.CompileStatic
import org.jetbrains.annotations.NotNull
/**
* @author Eugene Zhuravlev
@@ -566,14 +569,14 @@ class IndexTest extends JavaCodeInsightFixtureTestCase {
VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
@Override
boolean visitFile(@NotNull VirtualFile visitedFile) {
iterator.processFile(visitedFile);
return true;
iterator.processFile(visitedFile)
return true
}
});
})
}
protected void doInvalidateIndicesForFile(VirtualFile file, boolean contentChange) {
vfsEventMerger.recordBeforeFileEvent(((VirtualFileWithId)file).id, file, contentChange);
vfsEventMerger.recordBeforeFileEvent(((VirtualFileWithId)file).id, file, contentChange)
}
@Override
@@ -586,12 +589,12 @@ class IndexTest extends JavaCodeInsightFixtureTestCase {
vfsEventMerger.processChanges(new VfsEventsMerger.VfsEventProcessor() {
@Override
boolean process(VfsEventsMerger.ChangeInfo info) {
operation.set(info.toString());
operation.set(info.toString())
return true
}
})
StringUtil.replace(operation.get(), file.getPath(), file.getName());
StringUtil.replace(operation.get(), file.getPath(), file.getName())
}
}
@@ -601,7 +604,7 @@ class IndexTest extends JavaCodeInsightFixtureTestCase {
ApplicationManager.getApplication().getMessageBus().connect(getTestRootDisposable()).subscribe(
VirtualFileManager.VFS_CHANGES,
listener
);
)
def fileName = "test.txt"
final VirtualFile testFile = myFixture.addFileToProject(fileName, "test").getVirtualFile()
@@ -614,17 +617,17 @@ class IndexTest extends JavaCodeInsightFixtureTestCase {
assertEquals("file: $fileName\n" +
"operation: REMOVE ADD", listener.indexingOperation(testFile))
VfsUtil.saveText(testFile, "foo");
VfsUtil.saveText(testFile, "bar");
VfsUtil.saveText(testFile, "foo")
VfsUtil.saveText(testFile, "bar")
assertEquals("file: $fileName\n" +
"operation: UPDATE-REMOVE UPDATE", listener.indexingOperation(testFile));
"operation: UPDATE-REMOVE UPDATE", listener.indexingOperation(testFile))
VfsUtil.saveText(testFile, "baz")
testFile.delete(null)
assertEquals("file: $fileName\n" +
"operation: REMOVE", listener.indexingOperation(testFile));
"operation: REMOVE", listener.indexingOperation(testFile))
}
void "test files inside copied directory are indexed"() {
@@ -643,35 +646,35 @@ class IndexTest extends JavaCodeInsightFixtureTestCase {
assert JavaFileElementType.isInSourceContent(myFixture.tempDirFixture.getFile('another/doo/A.java'))
}
@CompileStatic
void "test Vfs Events Processing Performance"() {
def filename = 'A.java'
myFixture.addFileToProject('foo/bar/' + filename, 'class A {}')
PlatformTestUtil.startPerformanceTest("Vfs Event Processing By Index", 1000, {
def files = FilenameIndex.getFilesByName(project, filename, GlobalSearchScope.moduleScope(myModule))
assert files != null
assert files.length == 1
assert files?.length == 1
VirtualFile file = files[0].virtualFile
def filename2 = 'B.java'
def max = 100000
List<VFileEvent> eventList = new ArrayList<>(max);
def len = max / 2;
List<VFileEvent> eventList = new ArrayList<>(max)
def len = max / 2
for(int i = 0; i < len; ++i) {
eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename, filename2, true)) ;
eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename2, filename, true)) ;
eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename, filename2, true))
eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename2, filename, true))
eventList.add(new VFileDeleteEvent(null, file, true))
eventList.add(new VFileCreateEvent(null, file.parent, filename, false, true))
}
IndexedFilesListener indexedFilesListener = ((FileBasedIndexImpl)FileBasedIndex.instance).changedFilesCollector
indexedFilesListener.before(eventList);
indexedFilesListener.after(eventList);
indexedFilesListener.before(eventList)
indexedFilesListener.after(eventList)
files = FilenameIndex.getFilesByName(project, filename, GlobalSearchScope.moduleScope(myModule))
assert files != null
assert files.length == 1
}).cpuBound().ioBound().assertTiming();
assert files?.length == 1
}).cpuBound().ioBound().assertTiming()
}
}
@@ -65,9 +65,6 @@
<val name="intValues" val="{java.awt.BasicStroke.JOIN_BEVEL, java.awt.BasicStroke.JOIN_MITER, java.awt.BasicStroke.JOIN_ROUND}" />
</annotation>
</item>
<item name="java.awt.CardLayout void show(Container parent, java.lang.String) 1">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="java.awt.CardLayout void show(java.awt.Container, java.lang.String) 1">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
@@ -2,7 +2,7 @@
<item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String) 0'>
<annotation name='org.jetbrains.annotations.NonNls'/>
</item>
<item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String, java.lang.String,java.lang.ClassLoader) 0'>
<item name='java.awt.datatransfer.DataFlavor DataFlavor(java.lang.String, java.lang.String, java.lang.ClassLoader) 0'>
<annotation name='org.jetbrains.annotations.NonNls'/>
</item>
</root>
@@ -30,7 +30,7 @@
<val name="flags" val="{java.awt.event.HierarchyEvent.PARENT_CHANGED,java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED,java.awt.event.HierarchyEvent.SHOWING_CHANGED}" />
</annotation>
</item>
<item name="java.awt.event.HierarchyEvent int getChangeFlags()">
<item name="java.awt.event.HierarchyEvent long getChangeFlags()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="flags" val="{java.awt.event.HierarchyEvent.PARENT_CHANGED,java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED,java.awt.event.HierarchyEvent.SHOWING_CHANGED}" />
</annotation>
@@ -37,12 +37,6 @@
<item name="java.lang.Class java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class&lt;?&gt;...) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="java.lang.Class java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class...) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="java.lang.ClassLoader InputStream getResourceAsStream(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="java.lang.ClassLoader java.io.InputStream getResourceAsStream(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
@@ -70,9 +64,6 @@
<item name="java.lang.Readable int read(java.nio.CharBuffer) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.lang.Runtime Process exec(java.lang.String[]) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="java.lang.Runtime java.lang.Process exec(java.lang.String[]) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
@@ -1,5 +1,5 @@
<root>
<item name='java.security.MessageDigest MessageDigest java.security.getInstance(java.lang.String) 0'>
<item name='java.security.MessageDigest java.security.MessageDigest getInstance(java.lang.String) 0'>
<annotation name='org.jetbrains.annotations.NonNls'/>
</item>
</root>
+1 -1
View File
@@ -120,7 +120,7 @@
<val name="intValues" val="{java.sql.Statement.RETURN_GENERATED_KEYS, java.sql.Statement.NO_GENERATED_KEYS}" />
</annotation>
</item>
<item name="java.sql.Statement int execute(java.lang.String, int) 1">
<item name="java.sql.Statement boolean execute(java.lang.String, int) 1">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{java.sql.Statement.RETURN_GENERATED_KEYS, java.sql.Statement.NO_GENERATED_KEYS}" />
</annotation>
@@ -612,21 +612,12 @@
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.Collection boolean containsAll(java.util.Collection&lt;?&gt; c) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean containsAll(java.util.Collection&lt;?&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean removeAll(java.util.Collection&lt;?&gt; c) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean removeAll(java.util.Collection&lt;?&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean retainAll(java.util.Collection&lt;?&gt; c) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.Collection boolean retainAll(java.util.Collection&lt;?&gt;) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
@@ -981,12 +972,6 @@
<val name="targetIsContainer" val="true"/>
</annotation>
</item>
<item name="java.util.List List&lt;E&gt; subList(int fromIndex, int toIndex)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.List ListIterator&lt;E&gt; listIterator()">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="java.util.List T[] toArray(T[])">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
+10 -10
View File
@@ -322,6 +322,16 @@
<val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
</annotation>
</item>
<item name="javax.swing.JTabbedPane int getTabLayoutPolicy()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
</annotation>
</item>
<item name="javax.swing.JTabbedPane int getTabPlacement()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{javax.swing.SwingConstants.TOP, javax.swing.SwingConstants.BOTTOM, javax.swing.SwingConstants.LEFT, javax.swing.SwingConstants.RIGHT}" />
</annotation>
</item>
<item name="javax.swing.JTable JTable(java.lang.Object[][], java.lang.Object[]) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
@@ -331,16 +341,6 @@
<item name="javax.swing.JTable int columnAtPoint(java.awt.Point) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="javax.swing.JTable int getTabLayoutPolicy()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{javax.swing.JTabbedPane.WRAP_TAB_LAYOUT, javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT}" />
</annotation>
</item>
<item name="javax.swing.JTable int getTabPlacement()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{javax.swing.SwingConstants.TOP, javax.swing.SwingConstants.BOTTOM, javax.swing.SwingConstants.LEFT, javax.swing.SwingConstants.RIGHT}" />
</annotation>
</item>
<item name="javax.swing.JTable int rowAtPoint(java.awt.Point) 0">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
@@ -10,7 +10,7 @@
<val name="intValues" val="{javax.swing.SwingConstants.NORTH,javax.swing.SwingConstants.SOUTH,javax.swing.SwingConstants.EAST,javax.swing.SwingConstants.WEST}" />
</annotation>
</item>
<item name="javax.swing.plaf.basic.BasicArrowButton void getDirection()">
<item name="javax.swing.plaf.basic.BasicArrowButton int getDirection()">
<annotation name="org.intellij.lang.annotations.MagicConstant">
<val name="intValues" val="{javax.swing.SwingConstants.NORTH,javax.swing.SwingConstants.SOUTH,javax.swing.SwingConstants.EAST,javax.swing.SwingConstants.WEST}" />
</annotation>
+3 -3
View File
@@ -36,13 +36,13 @@
<item name="org.jdom.Element Element(java.lang.String) 0">
<annotation name="org.jetbrains.annotations.NonNls" />
</item>
<item name="org.jdom.Element java.util.List getChildren()">
<item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren()">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="org.jdom.Element java.util.List getChildren(java.lang.String)">
<item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren(java.lang.String)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name="org.jdom.Element java.util.List getChildren(java.lang.String, org.jdom.Namespace)">
<item name="org.jdom.Element java.util.List&lt;org.jdom.Element&gt; getChildren(java.lang.String, org.jdom.Namespace)">
<annotation name="org.jetbrains.annotations.NotNull" />
</item>
<item name='org.jdom.Element java.util.List&lt;org.jdom.Content&gt; getContent()'>
@@ -21,7 +21,6 @@ import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerManagerImpl;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.impl.PrioritizedTask;
import com.intellij.debugger.impl.SynchronizationBasedSemaphore;
@@ -47,9 +46,9 @@ import com.intellij.util.SmartList;
import com.intellij.util.TimeoutUtil;
import com.intellij.util.lang.CompoundRuntimeException;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
import com.sun.jdi.Method;
import com.sun.jdi.ThreadReference;
import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
import javax.swing.*;
import java.util.ArrayList;
@@ -379,33 +378,41 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
public void createBreakpoints(final PsiFile file) {
Runnable runnable = () -> {
BreakpointManager breakpointManager = DebuggerManagerImpl.getInstanceEx(myProject).getBreakpointManager();
BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
String text = document.getText();
int offset = -1;
for (; ;) {
offset = document.getText().indexOf("Breakpoint!", offset + 1);
while (true) {
offset = text.indexOf("Breakpoint!", offset + 1);
if (offset == -1) break;
int commentLine = document.getLineNumber(offset);
String comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine));
String comment = text.substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine));
Breakpoint breakpoint;
if (comment.indexOf("Method") != -1) {
if (comment.contains("Method")) {
breakpoint = breakpointManager.addMethodBreakpoint(document, commentLine + 1);
if (breakpoint != null) {
println("MethodBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2),
ProcessOutputTypes.SYSTEM);
String emulated = readValue(comment, "Emulated");
if (emulated != null) {
((JavaMethodBreakpointProperties)breakpoint.getXBreakpoint().getProperties()).EMULATED = Boolean.valueOf(emulated);
println("Emulated = " + emulated, ProcessOutputTypes.SYSTEM);
}
}
}
else if (comment.indexOf("Field") != -1) {
else if (comment.contains("Field")) {
breakpoint = breakpointManager.addFieldBreakpoint(document, commentLine + 1, readValue(comment, "Field"));
if (breakpoint != null) {
println("FieldBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM);
}
}
else if (comment.indexOf("Exception") != -1) {
else if (comment.contains("Exception")) {
breakpoint = breakpointManager.addExceptionBreakpoint(readValue(comment, "Exception"), "");
if (breakpoint != null) {
println("ExceptionBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2),
@@ -419,6 +426,11 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
}
}
if (breakpoint == null) {
LOG.error("Unable to set a breakpoint at line " + (commentLine + 1));
continue;
}
String suspendPolicy = readValue(comment, "suspendPolicy");
if (suspendPolicy != null) {
//breakpoint.setSuspend(!DebuggerSettings.SUSPEND_NONE.equals(suspendPolicy));
@@ -18,16 +18,16 @@ package com.jetbrains.jsonSchema;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.json.JsonFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ZipperUpdater;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.jsonSchema.impl.JsonSchemaServiceImpl;
@@ -53,23 +53,29 @@ public class JsonSchemaVfsListener extends BulkVirtualFileListenerAdapter {
if (scope.isEmpty()) return;
final DaemonCodeAnalyzer analyzer = DaemonCodeAnalyzer.getInstance(project);
ApplicationManager.getApplication().runReadAction(() -> {
FileTypeIndex.processFiles(JsonFileType.INSTANCE, jsonFile -> {
final Collection<Pair<VirtualFile, String>> collection = myService.getSchemaFilesByFile(jsonFile);
if (collection != null && !collection.isEmpty()) {
for (Pair<VirtualFile, String> pair : collection) {
if (scope.contains(pair.getFirst())) {
final PsiFile psiFile = PsiManager.getInstance(project).findFile(jsonFile);
if (psiFile != null) {
analyzer.restart(psiFile);
final PsiManager psiManager = PsiManager.getInstance(project);
final Editor[] editors = EditorFactory.getInstance().getAllEditors();
for (Editor editor : editors) {
if (editor instanceof EditorEx) {
final VirtualFile file = ((EditorEx)editor).getVirtualFile();
if (JsonFileType.INSTANCE.equals(file.getFileType())) {
final Collection<Pair<VirtualFile, String>> collection = myService.getSchemaFilesByFile(file);
if (collection != null && !collection.isEmpty()) {
for (Pair<VirtualFile, String> pair : collection) {
if (scope.contains(pair.getFirst())) {
ApplicationManager.getApplication().runReadAction(() -> {
final PsiFile psiFile = psiManager.findFile(file);
if (psiFile != null) {
analyzer.restart(psiFile);
}
});
break;
}
return true;
}
}
}
return true;
}, GlobalSearchScope.projectScope(project));
});
}
}
};
@Override
@@ -105,10 +111,8 @@ public class JsonSchemaVfsListener extends BulkVirtualFileListenerAdapter {
private void onFileChange(@NotNull final VirtualFile schemaFile) {
if (myMappingsProjectConfiguration.isRegisteredSchemaFile(schemaFile)) {
myService.dropProviderFromCache(schemaFile);
if (Registry.is("json.schema.fast.annotation.update")) {
myDirtySchemas.add(schemaFile);
myUpdater.queue(myRunnable);
}
myDirtySchemas.add(schemaFile);
myUpdater.queue(myRunnable);
}
}
});
@@ -1,11 +1,14 @@
package com.jetbrains.jsonSchema.impl;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.SLRUMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;
/**
@@ -16,7 +19,7 @@ public class JsonSchemaObject {
private Map<String, JsonSchemaObject> myDefinitions;
private Map<String, JsonSchemaObject> myProperties;
private Map<String, JsonSchemaObject> myPatternProperties;
private final SLRUMap<String, String> myCachedPatternProperties = new SLRUMap<>(100, 100);
private final PatternCalculator myPatternCalculator = new PatternCalculator();
private String myId;
private String mySchema;
@@ -80,7 +83,7 @@ public class JsonSchemaObject {
myProperties = other.myProperties;
myDefinitions = other.myDefinitions;
myPatternProperties = other.myPatternProperties;
myCachedPatternProperties.clear();
myPatternCalculator.clear();
myType = other.myType;
myDefault = other.myDefault;
@@ -123,7 +126,7 @@ public class JsonSchemaObject {
myProperties.putAll(other.myProperties);
myDefinitions = copyMap(myDefinitions, other.myDefinitions);
myPatternProperties = copyMap(myPatternProperties, other.myPatternProperties);
myCachedPatternProperties.clear();
myPatternCalculator.clear();
if (!StringUtil.isEmptyOrSpaces(other.myDescription)) {
myDescription = other.myDescription;
}
@@ -198,7 +201,7 @@ public class JsonSchemaObject {
public void setPatternProperties(Map<String, JsonSchemaObject> patternProperties) {
myPatternProperties = patternProperties;
myCachedPatternProperties.clear();
myPatternCalculator.clear();
}
public JsonSchemaType getType() {
@@ -503,20 +506,7 @@ public class JsonSchemaObject {
@Nullable
public JsonSchemaObject getMatchingPatternPropertySchema(@NotNull String name) {
if (myPatternProperties == null || myPatternProperties.isEmpty()) return null;
final String s = myCachedPatternProperties.get(name);
if (s != null) return myPatternProperties.get(s);
final List<String> strings = new ArrayList<>(myPatternProperties.keySet());
Collections.sort(strings);
for (final String pattern : strings) {
final boolean matches = Pattern.compile(adaptSchemaPattern(pattern)).matcher(StringUtil.newBombedCharSequence(name, 1000)).matches();
if (matches) {
myCachedPatternProperties.put(name, pattern);
return myPatternProperties.get(pattern);
}
}
myCachedPatternProperties.put(name, "");
return null;
return myPatternCalculator.getMatchingPatternPropertySchema(myPatternProperties, name);
}
@NotNull
@@ -525,4 +515,84 @@ public class JsonSchemaObject {
pattern = pattern.endsWith("+") || pattern.endsWith("*") ? pattern : (pattern + ".*");
return pattern;
}
private static class PatternCalculator {
private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock();
private Map<String, Pattern> myCachedPatterns;
private SLRUMap<String, String> myCachedPatternProperties;
@Nullable
public JsonSchemaObject getMatchingPatternPropertySchema(@Nullable final Map<String, JsonSchemaObject> patternProperties,
@NotNull final String name) {
if (patternProperties == null || patternProperties.isEmpty()) return null;
myLock.readLock().lock();
try {
if (myCachedPatterns == null) {
initPatternCache(patternProperties);
}
assert myCachedPatternProperties != null;
final String s = myCachedPatternProperties.get(name);
if (s != null) return patternProperties.get(s);
return matchPatternsToString(name, patternProperties);
} finally {
myLock.readLock().unlock();
}
}
public void clear() {
myLock.writeLock().lock();
try {
myCachedPatterns = null;
myCachedPatternProperties = null;
} finally {
myLock.writeLock().unlock();
}
}
private JsonSchemaObject matchPatternsToString(@NotNull final String name, @NotNull final Map<String, JsonSchemaObject> patternProperties) {
final List<String> strings = new ArrayList<>(patternProperties.keySet());
Collections.sort(strings);
return underWrite(() -> {
for (final String pattern : strings) {
try {
final Pattern compiledPattern = myCachedPatterns.get(pattern);
assert compiledPattern != null;
final boolean matches = compiledPattern.matcher(StringUtil.newBombedCharSequence(name, 300)).matches();
if (matches) {
myCachedPatternProperties.put(name, pattern);
return patternProperties.get(pattern);
}
} catch (ProcessCanceledException e) {
//ignored
}
}
myCachedPatternProperties.put(name, "");
return null;
});
}
private <T> T underWrite(@NotNull final Computable<T> computable) {
myLock.readLock().unlock();
myLock.writeLock().lock();
try {
final T t = computable.compute();
myLock.readLock().lock();
return t;
} finally {
myLock.writeLock().unlock();
}
}
private void initPatternCache(@NotNull final Map<String, JsonSchemaObject> patternProperties) {
underWrite(() -> {
myCachedPatterns = new HashMap<>(patternProperties.size(), 1.0f);
myCachedPatternProperties = new SLRUMap<>(100, 100);
for (String pattern : patternProperties.keySet()) {
myCachedPatterns.put(pattern, Pattern.compile(adaptSchemaPattern(pattern)));
}
return true;
});
}
}
}
@@ -148,7 +148,7 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry {
* usages of the same classes and methods, usage searches are not performed directly, but
* instead are queued for batch processing through
* {@link GlobalJavaInspectionContext#enqueueClassUsagesProcessor} and similar methods. The method
* can add new problems to <code>problemDescriptionsProcessor</code> or remove some of the problems
* can add new problems to {@code problemDescriptionsProcessor} or remove some of the problems
* collected by {@link #runInspection(AnalysisScope, InspectionManager, GlobalInspectionContext, ProblemDescriptionsProcessor)}
* by calling {@link ProblemDescriptionsProcessor#ignoreElement(RefEntity)}.
*
@@ -207,7 +207,6 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry {
* @return JobDescriptors array to show inspection progress correctly. TotalAmount should be set (e.g. in
* {@link #runInspection(AnalysisScope, InspectionManager, GlobalInspectionContext, ProblemDescriptionsProcessor)})
* ProgressIndicator should progress with {@link GlobalInspectionContext#incrementJobDoneAmount(JobDescriptor, String)}
* @param context
*/
@Nullable
public JobDescriptor[] getAdditionalJobs(GlobalInspectionContext context) {
@@ -311,8 +311,8 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool {
/**
* Read in settings from XML config.
* Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
* and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
* Default implementation uses XmlSerializer so you may use public fields (like {@code int TOOL_OPTION})
* and bean-style getters/setters (like {@code int getToolOption(), void setToolOption(int)}) to store your options.
*
* @param node to read settings from.
* @throws InvalidDataException if the loaded data was not valid.
@@ -334,8 +334,8 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool {
/**
* Store current settings in XML config.
* Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
* and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
* Default implementation uses XmlSerializer so you may use public fields (like {@code int TOOL_OPTION})
* and bean-style getters/setters (like {@code int getToolOption(), void setToolOption(int)}) to store your options.
*
* @param node to store settings to.
* @throws WriteExternalException if no data should be saved for this component.
@@ -366,17 +366,11 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool {
return;
}
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
try {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty()) ourBlackList.add(line);
}
}
finally {
reader.close();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty()) ourBlackList.add(line);
}
}
catch (IOException e) {
@@ -600,7 +600,8 @@ public class ByWord {
Range expanded = expandW(myText1, myText2, range);
Range trimmed = trim(myText1, myText2, expanded);
if (!trimmed.isEmpty()) {
if (!trimmed.isEmpty() &&
!isEqualsIW(myText1, myText2, trimmed)) {
myChanges.add(trimmed);
}
}
@@ -638,7 +639,8 @@ public class ByWord {
MergeRange expanded = expandW(myText1, myText2, myText3, range);
MergeRange trimmed = trim(myText1, myText2, myText3, expanded);
if (!trimmed.isEmpty()) {
if (!trimmed.isEmpty() &&
!isEqualsIW(myText1, myText2, myText3, trimmed)) {
myChanges.add(trimmed);
}
}
@@ -690,7 +692,8 @@ public class ByWord {
Range trimmed = new Range(start1, end1, start2, end2);
if (!trimmed.isEmpty()) {
if (!trimmed.isEmpty() &&
!isEquals(myText1, myText2, trimmed)) {
myChanges.add(trimmed);
}
}
@@ -753,7 +756,8 @@ public class ByWord {
MergeRange trimmed = new MergeRange(start1, end1, start2, end2, start3, end3);
if (!trimmed.isEmpty()) {
if (!trimmed.isEmpty() &&
!isEquals(myText1, myText2, myText3, trimmed)) {
myChanges.add(trimmed);
}
}
@@ -384,4 +384,40 @@ public class TrimUtil {
public static Range expandIW(@NotNull CharSequence text1, @NotNull CharSequence text2) {
return expandIW(text1, text2, 0, 0, text1.length(), text2.length());
}
//
// Equality
//
public static boolean isEquals(@NotNull CharSequence text1, @NotNull CharSequence text2,
@NotNull Range range) {
CharSequence sequence1 = text1.subSequence(range.start1, range.end1);
CharSequence sequence2 = text2.subSequence(range.start2, range.end2);
return ComparisonUtil.isEquals(sequence1, sequence2, ComparisonPolicy.DEFAULT);
}
public static boolean isEqualsIW(@NotNull CharSequence text1, @NotNull CharSequence text2,
@NotNull Range range) {
CharSequence sequence1 = text1.subSequence(range.start1, range.end1);
CharSequence sequence2 = text2.subSequence(range.start2, range.end2);
return ComparisonUtil.isEquals(sequence1, sequence2, ComparisonPolicy.IGNORE_WHITESPACES);
}
public static boolean isEquals(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull CharSequence text3,
@NotNull MergeRange range) {
CharSequence sequence1 = text1.subSequence(range.start1, range.end1);
CharSequence sequence2 = text2.subSequence(range.start2, range.end2);
CharSequence sequence3 = text3.subSequence(range.start3, range.end3);
return ComparisonUtil.isEquals(sequence2, sequence1, ComparisonPolicy.DEFAULT) &&
ComparisonUtil.isEquals(sequence2, sequence3, ComparisonPolicy.DEFAULT);
}
public static boolean isEqualsIW(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull CharSequence text3,
@NotNull MergeRange range) {
CharSequence sequence1 = text1.subSequence(range.start1, range.end1);
CharSequence sequence2 = text2.subSequence(range.start2, range.end2);
CharSequence sequence3 = text3.subSequence(range.start3, range.end3);
return ComparisonUtil.isEquals(sequence2, sequence1, ComparisonPolicy.IGNORE_WHITESPACES) &&
ComparisonUtil.isEquals(sequence2, sequence3, ComparisonPolicy.IGNORE_WHITESPACES);
}
}
@@ -21,7 +21,6 @@ import com.intellij.diff.util.ThreeSide
import com.intellij.openapi.editor.Document
import com.intellij.openapi.progress.DumbProgressIndicator
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.util.Couple
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.text.StringUtil
import com.intellij.testFramework.UsefulTestCase
@@ -66,6 +65,10 @@ abstract class DiffTestCase : UsefulTestCase() {
assertTrue(message, actual)
}
fun assertFalse(actual: Boolean, message: String = "") {
assertFalse(message, actual)
}
fun assertEquals(expected: Any?, actual: Any?, message: String = "") {
assertEquals(message, expected, actual)
}
@@ -75,17 +78,33 @@ abstract class DiffTestCase : UsefulTestCase() {
}
fun assertEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) {
if (skipLastNewline && !ignoreSpaces) {
assertTrue(StringUtil.equals(chunk1, chunk2) ||
StringUtil.equals(stripNewline(chunk1), chunk2) ||
StringUtil.equals(chunk1, stripNewline(chunk2)))
}
else {
assertTrue(isEqualsCharSequences(chunk1, chunk2, ignoreSpaces))
}
}
fun assertNotEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) {
if (skipLastNewline && !ignoreSpaces) {
assertTrue(!StringUtil.equals(chunk1, chunk2) ||
!StringUtil.equals(stripNewline(chunk1), chunk2) ||
!StringUtil.equals(chunk1, stripNewline(chunk2)))
}
else {
assertFalse(isEqualsCharSequences(chunk1, chunk2, ignoreSpaces))
}
}
fun isEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean): Boolean {
if (ignoreSpaces) {
assertTrue(StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2))
} else {
if (skipLastNewline) {
if (StringUtil.equals(chunk1, chunk2)) return
if (StringUtil.equals(stripNewline(chunk1), chunk2)) return
if (StringUtil.equals(chunk1, stripNewline(chunk2))) return
assertTrue(false)
} else {
assertTrue(StringUtil.equals(chunk1, chunk2))
}
return StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2)
}
else {
return StringUtil.equals(chunk1, chunk2)
}
}
@@ -202,14 +221,14 @@ abstract class DiffTestCase : UsefulTestCase() {
// Helpers
//
open class Trio<T : Any>(val data1: T, val data2: T, val data3: T) {
open class Trio<out T>(val data1: T, val data2: T, val data3: T) {
companion object {
fun <V : Any> from(f: (ThreeSide) -> V): Trio<V> = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT))
fun <V> from(f: (ThreeSide) -> V): Trio<V> = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT))
}
fun <V : Any> map(f: (T) -> V): Trio<V> = Trio(f(data1), f(data2), f(data3))
fun <V> map(f: (T) -> V): Trio<V> = Trio(f(data1), f(data2), f(data3))
fun <V : Any> map(f: (T, ThreeSide) -> V): Trio<V> = Trio(f(data1, ThreeSide.LEFT), f(data2, ThreeSide.BASE), f(data3, ThreeSide.RIGHT))
fun <V> map(f: (T, ThreeSide) -> V): Trio<V> = Trio(f(data1, ThreeSide.LEFT), f(data2, ThreeSide.BASE), f(data3, ThreeSide.RIGHT))
fun forEach(f: (T, ThreeSide) -> Unit): Unit {
f(data1, ThreeSide.LEFT)
@@ -228,7 +247,11 @@ abstract class DiffTestCase : UsefulTestCase() {
}
override fun hashCode(): Int {
return data1.hashCode() * 37 * 37 + data2.hashCode() * 37 + data3.hashCode()
var h = 0
if (data1 != null) h = h * 31 + data1.hashCode()
if (data2 != null) h = h * 31 + data2.hashCode()
if (data3 != null) h = h * 31 + data3.hashCode()
return h
}
}
}
@@ -26,28 +26,31 @@ import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.text.StringUtil
class ComparisonUtilAutoTest : DiffTestCase() {
val RUNS = 30
val MAX_LENGTH = 300
fun testChar() {
doTestChar(System.currentTimeMillis(), 30, 30)
doTestChar(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
fun testWord() {
doTestWord(System.currentTimeMillis(), 30, 300)
doTestWord(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
fun testLine() {
doTestLine(System.currentTimeMillis(), 30, 300)
doTestLine(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
fun testLineSquashed() {
doTestLineSquashed(System.currentTimeMillis(), 30, 300)
doTestLineSquashed(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
fun testLineTrimSquashed() {
doTestLineTrimSquashed(System.currentTimeMillis(), 30, 300)
doTestLineTrimSquashed(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
fun testMerge() {
doTestMerge(System.currentTimeMillis(), 30, 300)
doTestMerge(System.currentTimeMillis(), RUNS, MAX_LENGTH)
}
private fun doTestLine(seed: Long, runs: Int, maxLength: Int) {
@@ -141,8 +144,8 @@ class ComparisonUtilAutoTest : DiffTestCase() {
val chunk2 = DiffUtil.getLinesContent(text2, f.startLine2, f.endLine2)
val chunk3 = DiffUtil.getLinesContent(text3, f.startLine3, f.endLine3)
val wordFragments = ByWord.compare(chunk1, chunk2, chunk3, policy, INDICATOR);
MergeLineFragmentImpl(f, wordFragments);
val wordFragments = ByWord.compare(chunk1, chunk2, chunk3, policy, INDICATOR)
MergeLineFragmentImpl(f, wordFragments)
}
debugData.put("Fragments", fineFragments)
@@ -200,18 +203,18 @@ class ComparisonUtilAutoTest : DiffTestCase() {
}
}
checkUnchanged(text1.charsSequence, text2.charsSequence, fragments, policy, true)
checkValidRanges(text1.charsSequence, text2.charsSequence, fragments, policy, true)
checkCantTrimLines(text1, text2, fragments, policy, allowNonSquashed)
}
private fun checkResultWord(text1: CharSequence, text2: CharSequence, fragments: List<DiffFragment>, policy: ComparisonPolicy) {
checkDiffConsistency(fragments)
checkUnchanged(text1, text2, fragments, policy, false)
checkValidRanges(text1, text2, fragments, policy, false)
}
private fun checkResultChar(text1: CharSequence, text2: CharSequence, fragments: List<DiffFragment>, policy: ComparisonPolicy) {
checkDiffConsistency(fragments)
checkUnchanged(text1, text2, fragments, policy, false)
checkValidRanges(text1, text2, fragments, policy, false)
}
private fun checkResultMerge(text1: Document, text2: Document, text3: Document, fragments: List<MergeLineFragment>, policy: ComparisonPolicy) {
@@ -223,10 +226,10 @@ class ComparisonUtilAutoTest : DiffTestCase() {
val chunk3 = DiffUtil.getLinesContent(text3, f.startLine3, f.endLine3)
checkDiffConsistency3(f.innerFragments!!)
checkUnchanged3(chunk1, chunk2, chunk3, f.innerFragments!!, policy)
checkValidRanges3(chunk1, chunk2, chunk3, f.innerFragments!!, policy)
}
checkUnchanged3(text1, text2, text3, fragments, policy)
checkValidRanges3(text1, text2, text3, fragments, policy)
checkCantTrimLines3(text1, text2, text3, fragments, policy)
}
@@ -374,28 +377,39 @@ class ComparisonUtilAutoTest : DiffTestCase() {
}
}
private fun checkUnchanged(text1: CharSequence, text2: CharSequence, fragments: List<DiffFragment>, policy: ComparisonPolicy, skipNewline: Boolean) {
private fun checkValidRanges(text1: CharSequence, text2: CharSequence, fragments: List<DiffFragment>, policy: ComparisonPolicy, skipNewline: Boolean) {
// TODO: better check for Trim spaces case ?
val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT
val ignoreSpacesUnchanged = policy != ComparisonPolicy.DEFAULT
val ignoreSpacesChanged = policy == ComparisonPolicy.IGNORE_WHITESPACES
var last1 = 0
var last2 = 0
for (fragment in fragments) {
val chunk1 = text1.subSequence(last1, fragment.startOffset1)
val chunk2 = text2.subSequence(last2, fragment.startOffset2)
val start1 = fragment.startOffset1
val start2 = fragment.startOffset2
val end1 = fragment.endOffset1
val end2 = fragment.endOffset2
assertEqualsCharSequences(chunk1, chunk2, ignoreSpaces, skipNewline)
val chunk1 = text1.subSequence(last1, start1)
val chunk2 = text2.subSequence(last2, start2)
assertEqualsCharSequences(chunk1, chunk2, ignoreSpacesUnchanged, skipNewline)
val chunkContent1 = text1.subSequence(start1, end1)
val chunkContent2 = text2.subSequence(start2, end2)
if (!skipNewline) {
assertNotEqualsCharSequences(chunkContent1, chunkContent2, ignoreSpacesChanged, skipNewline)
}
last1 = fragment.endOffset1
last2 = fragment.endOffset2
}
val chunk1 = text1.subSequence(last1, text1.length)
val chunk2 = text2.subSequence(last2, text2.length)
assertEqualsCharSequences(chunk1, chunk2, ignoreSpaces, skipNewline)
assertEqualsCharSequences(chunk1, chunk2, ignoreSpacesUnchanged, skipNewline)
}
private fun checkUnchanged3(text1: Document, text2: Document, text3: Document, fragments: List<MergeLineFragment>, policy: ComparisonPolicy) {
val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT
private fun checkValidRanges3(text1: Document, text2: Document, text3: Document, fragments: List<MergeLineFragment>, policy: ComparisonPolicy) {
val ignoreSpaces = policy != ComparisonPolicy.DEFAULT
var last1 = 0
var last2 = 0
@@ -425,8 +439,9 @@ class ComparisonUtilAutoTest : DiffTestCase() {
assertEqualsCharSequences(content2, content3, ignoreSpaces, false)
}
private fun checkUnchanged3(text1: CharSequence, text2: CharSequence, text3: CharSequence, fragments: List<MergeWordFragment>, policy: ComparisonPolicy) {
val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT
private fun checkValidRanges3(text1: CharSequence, text2: CharSequence, text3: CharSequence, fragments: List<MergeWordFragment>, policy: ComparisonPolicy) {
val ignoreSpacesUnchanged = policy != ComparisonPolicy.DEFAULT
val ignoreSpacesChanged = policy == ComparisonPolicy.IGNORE_WHITESPACES
var last1 = 0
var last2 = 0
@@ -435,13 +450,21 @@ class ComparisonUtilAutoTest : DiffTestCase() {
val start1 = fragment.startOffset1
val start2 = fragment.startOffset2
val start3 = fragment.startOffset3
val end1 = fragment.endOffset1
val end2 = fragment.endOffset2
val end3 = fragment.endOffset3
val content1 = text1.subSequence(last1, start1)
val content2 = text2.subSequence(last2, start2)
val content3 = text3.subSequence(last3, start3)
assertEqualsCharSequences(content2, content1, ignoreSpacesUnchanged, false)
assertEqualsCharSequences(content2, content3, ignoreSpacesUnchanged, false)
assertEqualsCharSequences(content2, content1, ignoreSpaces, false)
assertEqualsCharSequences(content2, content3, ignoreSpaces, false)
val chunkContent1 = text1.subSequence(start1, end1)
val chunkContent2 = text2.subSequence(start2, end2)
val chunkContent3 = text3.subSequence(start3, end3)
assertFalse(isEqualsCharSequences(chunkContent2, chunkContent1, ignoreSpacesChanged) &&
isEqualsCharSequences(chunkContent2, chunkContent3, ignoreSpacesChanged))
last1 = fragment.endOffset1
last2 = fragment.endOffset2
@@ -452,8 +475,8 @@ class ComparisonUtilAutoTest : DiffTestCase() {
val content2 = text2.subSequence(last2, text2.length)
val content3 = text3.subSequence(last3, text3.length)
assertEqualsCharSequences(content2, content1, ignoreSpaces, false)
assertEqualsCharSequences(content2, content3, ignoreSpaces, false)
assertEqualsCharSequences(content2, content1, ignoreSpacesUnchanged, false)
assertEqualsCharSequences(content2, content3, ignoreSpacesUnchanged, false)
}
private fun checkCantTrimLines(text1: Document, text2: Document, fragments: List<LineFragment>, policy: ComparisonPolicy, allowNonSquashed: Boolean) {
@@ -491,11 +514,7 @@ class ComparisonUtilAutoTest : DiffTestCase() {
}
private fun countNonWhitespaceCharacters(line: CharSequence): Int {
var count = 0
for (i in 0 until line.length) {
if (!StringUtil.isWhiteSpace(line[i])) count++
}
return count
return (0 until line.length).count { !StringUtil.isWhiteSpace(line[it]) }
}
private fun getFirstLastLines(text: Document, start: Int, end: Int): Couple<CharSequence>? {
@@ -18,6 +18,9 @@ package com.intellij.diff.comparison
import com.intellij.diff.DiffTestCase
import com.intellij.diff.fragments.DiffFragment
import com.intellij.diff.fragments.LineFragment
import com.intellij.diff.fragments.MergeWordFragment
import com.intellij.diff.util.IntPair
import com.intellij.diff.util.ThreeSide
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.impl.DocumentImpl
import com.intellij.openapi.util.Couple
@@ -25,31 +28,64 @@ import com.intellij.util.containers.ContainerUtil
import java.util.*
abstract class ComparisonUtilTestBase : DiffTestCase() {
private fun doLineTest(before: Document, after: Document, matchings: Couple<BitSet>?, expected: List<Change>?, policy: ComparisonPolicy) {
private fun doLineTest(text: Couple<Document>, matchings: Couple<BitSet>?, expected: List<Couple<IntPair>>?, policy: ComparisonPolicy) {
val before = text.first
val after = text.second
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)
}
private fun doWordTest(before: Document, after: Document, matchings: Couple<BitSet>?, expected: List<Change>?, policy: ComparisonPolicy) {
private fun doLineInnerTest(text: Couple<Document>, matchings: Couple<BitSet>?, expected: List<Couple<IntPair>>?, policy: ComparisonPolicy) {
val before = text.first
val after = text.second
val rawFragments = MANAGER.compareLinesInner(before.charsSequence, after.charsSequence, policy, INDICATOR)
val fragments = MANAGER.squash(rawFragments)
checkConsistencyWord(fragments, before, after)
checkConsistencyLineInner(fragments, before, after)
val diffFragments = fragments[0].innerFragments!!
if (matchings != null) checkDiffMatching(diffFragments, matchings)
if (expected != null) checkDiffChanges(diffFragments, expected)
}
private fun doCharTest(before: Document, after: Document, matchings: Couple<BitSet>?, expected: List<Change>?, policy: ComparisonPolicy) {
private fun doWordTest(text: Couple<Document>, matchings: Couple<BitSet>?, expected: List<Couple<IntPair>>?, policy: ComparisonPolicy) {
val before = text.first
val after = text.second
val fragments = MANAGER.compareWords(before.charsSequence, after.charsSequence, policy, INDICATOR)
checkConsistency(fragments, before, after)
if (matchings != null) checkDiffMatching(fragments, matchings)
if (expected != null) checkDiffChanges(fragments, expected)
}
private fun doWordTest(text: Trio<Document>, matchings: Trio<BitSet>?, expected: List<Trio<IntPair>>?, policy: ComparisonPolicy) {
val before = text.data1
val base = text.data2
val after = text.data3
val fragments = ByWord.compare(before.charsSequence, base.charsSequence, after.charsSequence, policy, INDICATOR)
checkConsistency(fragments)
if (matchings != null) checkMergeMatching(fragments, matchings)
if (expected != null) checkMergeChanges(fragments, expected)
}
private fun doCharTest(text: Couple<Document>, matchings: Couple<BitSet>?, expected: List<Couple<IntPair>>?, policy: ComparisonPolicy) {
val before = text.first
val after = text.second
val fragments = MANAGER.compareChars(before.charsSequence, after.charsSequence, policy, INDICATOR)
checkConsistency(fragments, before, after)
if (matchings != null) checkDiffMatching(fragments, matchings)
if (expected != null) checkDiffChanges(fragments, expected)
}
private fun doSplitterTest(before: Document, after: Document, squash: Boolean, trim: Boolean, expected: List<Change>?, policy: ComparisonPolicy) {
private fun doSplitterTest(text: Couple<Document>,
squash: Boolean,
trim: Boolean,
expected: List<Couple<IntPair>>?,
policy: ComparisonPolicy) {
val before = text.first
val after = text.second
val text1 = before.charsSequence
val text2 = after.charsSequence
@@ -62,7 +98,7 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
if (expected != null) checkLineChanges(fragments, expected)
}
private fun checkConsistencyWord(fragments: List<LineFragment>, before: Document, after: Document) {
private fun checkConsistencyLineInner(fragments: List<LineFragment>, before: Document, after: Document) {
assertTrue(fragments.size == 1)
val fragment = fragments[0]
@@ -102,16 +138,33 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
}
}
private fun checkLineChanges(fragments: List<LineFragment>, expected: List<Change>) {
private fun checkConsistency(fragments: List<MergeWordFragment>) {
for (fragment in fragments) {
assertTrue(fragment.getStartOffset(ThreeSide.LEFT) <= fragment.getEndOffset(ThreeSide.LEFT))
assertTrue(fragment.getStartOffset(ThreeSide.BASE) <= fragment.getEndOffset(ThreeSide.BASE))
assertTrue(fragment.getStartOffset(ThreeSide.RIGHT) <= fragment.getEndOffset(ThreeSide.RIGHT))
assertTrue(fragment.getStartOffset(ThreeSide.LEFT) != fragment.getEndOffset(ThreeSide.LEFT) ||
fragment.getStartOffset(ThreeSide.BASE) != fragment.getEndOffset(ThreeSide.BASE) ||
fragment.getStartOffset(ThreeSide.RIGHT) != fragment.getEndOffset(ThreeSide.RIGHT))
}
}
private fun checkLineChanges(fragments: List<LineFragment>, expected: List<Couple<IntPair>>) {
val changes = convertLineFragments(fragments)
assertOrderedEquals(changes, expected)
}
private fun checkDiffChanges(fragments: List<DiffFragment>, expected: List<Change>) {
private fun checkDiffChanges(fragments: List<DiffFragment>, expected: List<Couple<IntPair>>) {
val changes = convertDiffFragments(fragments)
assertOrderedEquals(changes, expected)
}
private fun checkMergeChanges(fragments: List<MergeWordFragment>, expected: List<Trio<IntPair>>) {
val changes = convertMergeFragments(fragments)
assertOrderedEquals(changes, expected)
}
private fun checkLineMatching(fragments: List<LineFragment>, matchings: Couple<BitSet>) {
val set1 = BitSet()
val set2 = BitSet()
@@ -120,8 +173,8 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
set2.set(fragment.startLine2, fragment.endLine2)
}
assertEquals(matchings.first, set1)
assertEquals(matchings.second, set2)
assertEquals(matchings.first, set1, "Before")
assertEquals(matchings.second, set2, "After")
}
private fun checkDiffMatching(fragments: List<DiffFragment>, matchings: Couple<BitSet>) {
@@ -132,16 +185,39 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
set2.set(fragment.startOffset2, fragment.endOffset2)
}
assertEquals(matchings.first, set1)
assertEquals(matchings.second, set2)
assertEquals(matchings.first, set1, "Before")
assertEquals(matchings.second, set2, "After")
}
private fun convertDiffFragments(fragments: List<DiffFragment>): List<Change> {
return fragments.map { Change(it.startOffset1, it.endOffset1, it.startOffset2, it.endOffset2) }
private fun checkMergeMatching(fragments: List<MergeWordFragment>, matchings: Trio<BitSet>) {
val set1 = BitSet()
val set2 = BitSet()
val set3 = BitSet()
for (fragment in fragments) {
set1.set(fragment.getStartOffset(ThreeSide.LEFT), fragment.getEndOffset(ThreeSide.LEFT))
set2.set(fragment.getStartOffset(ThreeSide.BASE), fragment.getEndOffset(ThreeSide.BASE))
set3.set(fragment.getStartOffset(ThreeSide.RIGHT), fragment.getEndOffset(ThreeSide.RIGHT))
}
assertEquals(matchings.data1, set1, "Before")
assertEquals(matchings.data2, set2, "Base")
assertEquals(matchings.data3, set3, "After")
}
private fun convertLineFragments(fragments: List<LineFragment>): List<Change> {
return fragments.map { Change(it.startLine1, it.endLine1, it.startLine2, it.endLine2) }
private fun convertDiffFragments(fragments: List<DiffFragment>): List<Couple<IntPair>> {
return fragments.map { Couple(IntPair(it.startOffset1, it.endOffset1), IntPair(it.startOffset2, it.endOffset2)) }
}
private fun convertLineFragments(fragments: List<LineFragment>): List<Couple<IntPair>> {
return fragments.map { Couple(IntPair(it.startLine1, it.endLine1), IntPair(it.startLine2, it.endLine2)) }
}
private fun convertMergeFragments(fragments: List<MergeWordFragment>): List<Trio<IntPair>> {
return fragments.map {
Trio(IntPair(it.getStartOffset(ThreeSide.LEFT), it.getEndOffset(ThreeSide.LEFT)),
IntPair(it.getStartOffset(ThreeSide.BASE), it.getEndOffset(ThreeSide.BASE)),
IntPair(it.getStartOffset(ThreeSide.RIGHT), it.getEndOffset(ThreeSide.RIGHT)))
}
}
private fun checkLineOffsets(fragment: LineFragment, before: Document, after: Document) {
@@ -168,39 +244,51 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
// Test Builder
//
private 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
}
internal enum class TestType {
LINE, WORD, CHAR, SPLITTER
LINE, LINE_INNER, WORD, CHAR, SPLITTER
}
internal inner class TestBuilder(private val type: TestType) {
private var isExecuted: Boolean = false
private var before: Document? = null
private var after: Document? = null
private var defaultChanges: List<Change>? = null
private var trimChanges: List<Change>? = null
private var ignoreChanges: List<Change>? = null
private var defaultMatching: Couple<BitSet>? = null
private var trimMatching: Couple<BitSet>? = null
private var ignoreMatching: Couple<BitSet>? = null
private var text: Data<Document> = Data()
private var changes: PolicyData<List<Data<IntPair>>> = PolicyData()
private var matchings: PolicyData<Data<BitSet>> = PolicyData()
private var shouldSquash: Boolean = false
private var shouldTrim: Boolean = false
private fun changes(policy: ComparisonPolicy): List<Change>? = when (policy) {
ComparisonPolicy.IGNORE_WHITESPACES -> ignoreChanges ?: trimChanges ?: defaultChanges
ComparisonPolicy.TRIM_WHITESPACES -> trimChanges ?: defaultChanges
ComparisonPolicy.DEFAULT -> defaultChanges
}
private fun matchings(policy: ComparisonPolicy): Couple<BitSet>? = when (policy) {
ComparisonPolicy.IGNORE_WHITESPACES -> ignoreMatching ?: trimMatching ?: defaultMatching
ComparisonPolicy.TRIM_WHITESPACES -> trimMatching ?: defaultMatching
ComparisonPolicy.DEFAULT -> defaultMatching
}
fun assertExecuted() {
assertTrue(isExecuted)
}
@@ -209,17 +297,36 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
try {
isExecuted = true
val change = changes(policy)
val matchings = matchings(policy)
assertTrue(change != null || matchings != null)
if (text.isTwoSide()) {
val text = text.asCouple()
val changes = changes.get(policy)?.map { it.asCouple() }
val matchings = matchings.get(policy)?.asCouple()
assertTrue(changes != null || matchings != null)
when (type) {
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 -> {
assertNull(matchings)
doSplitterTest(before!!, after!!, shouldSquash, shouldTrim, change, policy)
when (type) {
TestType.LINE -> doLineTest(text, matchings, changes, policy)
TestType.LINE_INNER -> {
doLineInnerTest(text, matchings, changes, policy)
doWordTest(text, matchings, changes, policy)
}
TestType.WORD -> doWordTest(text, matchings, changes, policy)
TestType.CHAR -> doCharTest(text, matchings, changes, policy)
TestType.SPLITTER -> {
assertNull(matchings)
doSplitterTest(text, shouldSquash, shouldTrim, changes, policy)
}
else -> assert(false)
}
}
else {
val text = text.asTrio()
val changes = changes.get(policy)?.map { it.asTrio() }
val matchings = matchings.get(policy)?.asTrio()
assertTrue(changes != null || matchings != null)
when (type) {
TestType.WORD -> doWordTest(text, matchings, changes, policy)
else -> assert(false)
}
}
}
@@ -254,102 +361,84 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
return Helper(this, v)
}
inner class Helper(val before: String, val after: String) {
operator fun Helper.minus(v: String): Helper {
return Helper(before, v, after)
}
inner class Helper(val before: String, val after: String, val base: String? = null) {
init {
val builder = this@TestBuilder
if (builder.before == null && builder.after == null) {
builder.before = DocumentImpl(parseSource(before))
builder.after = DocumentImpl(parseSource(after))
if (builder.text.before == null && builder.text.after == null ||
base != null && builder.text.base == null) {
builder.text.before = DocumentImpl(parseSource(before))
builder.text.after = DocumentImpl(parseSource(after))
if (base != null) builder.text.base = DocumentImpl(parseSource(base))
}
}
fun plainSource() {
val builder = this@TestBuilder
builder.before = DocumentImpl(before)
builder.after = DocumentImpl(after)
builder.text.before = DocumentImpl(before)
builder.text.after = DocumentImpl(after)
if (base != null) {
builder.text.base = DocumentImpl(base)
}
}
fun default() {
defaultMatching = parseMatching(before, after)
matchings.default = parseMatching(before, after, base)
}
fun trim() {
trimMatching = parseMatching(before, after)
matchings.trim = parseMatching(before, after, base)
}
fun ignore() {
ignoreMatching = parseMatching(before, after)
matchings.ignore = parseMatching(before, after, base)
}
private fun parseMatching(before: String, after: String): Couple<BitSet> {
private fun parseMatching(before: String, after: String, base: String?): Data<BitSet> {
if (type == TestType.LINE) {
val builder = this@TestBuilder
return Couple.of(parseLineMatching(before, builder.before!!), parseLineMatching(after, builder.after!!))
return Data(parseLineMatching(before, builder.text.before!!),
if (base != null) parseLineMatching(base, builder.text.base!!) else null,
parseLineMatching(after, builder.text.after!!))
}
else {
return Couple.of(parseMatching(before), parseMatching(after))
return Data(parseMatching(before),
if (base != null) parseMatching(base) else null,
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
}
}
fun default(vararg expected: Change): Unit {
defaultChanges = ContainerUtil.list(*expected)
fun default(vararg expected: Couple<IntPair>): Unit {
changes.default = ContainerUtil.list(*expected).map { Data(it.first, it.second) }
}
fun trim(vararg expected: Change): Unit {
trimChanges = ContainerUtil.list(*expected)
fun trim(vararg expected: Couple<IntPair>): Unit {
changes.trim = ContainerUtil.list(*expected).map { Data(it.first, it.second) }
}
fun ignore(vararg expected: Change): Unit {
ignoreChanges = ContainerUtil.list(*expected)
fun ignore(vararg expected: Couple<IntPair>): Unit {
changes.ignore = ContainerUtil.list(*expected).map { Data(it.first, it.second) }
}
fun mod(line1: Int, line2: Int, count1: Int, count2: Int): Change {
fun mod(line1: Int, line2: Int, count1: Int, count2: Int): Couple<IntPair> {
assert(count1 != 0)
assert(count2 != 0)
return Change(line1, line1 + count1, line2, line2 + count2)
return Couple(IntPair(line1, line1 + count1), IntPair(line2, line2 + count2))
}
fun del(line1: Int, line2: Int, count1: Int): Change {
fun del(line1: Int, line2: Int, count1: Int): Couple<IntPair> {
assert(count1 != 0)
return Change(line1, line1 + count1, line2, line2)
return Couple(IntPair(line1, line1 + count1), IntPair(line2, line2))
}
fun ins(line1: Int, line2: Int, count2: Int): Change {
fun ins(line1: Int, line2: Int, count2: Int): Couple<IntPair> {
assert(count2 != 0)
return Change(line1, line1, line2, line2 + count2)
return Couple(IntPair(line1, line1), IntPair(line2, line2 + count2))
}
@@ -361,6 +450,8 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
internal fun lines(f: TestBuilder.() -> Unit): Unit = doTest(TestType.LINE, f)
internal fun lines_inner(f: TestBuilder.() -> Unit): Unit = doTest(TestType.LINE_INNER, f)
internal fun words(f: TestBuilder.() -> Unit): Unit = doTest(TestType.WORD, f)
internal fun chars(f: TestBuilder.() -> Unit): Unit = doTest(TestType.CHAR, f)
@@ -382,9 +473,28 @@ abstract class ComparisonUtilTestBase : DiffTestCase() {
// Helpers
//
data class Change(val start1: Int, val end1: Int, val start2: Int, val end2: Int) {
override fun toString(): String {
return "($start1, $end1) - ($start2, $end2)"
private data class Data<T>(var before: T?, var base: T?, var after: T?) {
constructor() : this(null, null, null)
constructor(before: T?, after : T?) : this(before, null, after)
fun isTwoSide(): Boolean = before != null && after != null && base == null
fun isThreeSide(): Boolean = before != null && after != null && base != null
fun asCouple(): Couple<T> {
assert(isTwoSide())
return Couple(before!!, after!!)
}
fun asTrio(): Trio<T> {
assert(isThreeSide())
return Trio(before!!, base!!, after!!)
}
}
private data class PolicyData<T>(var default: T? = null, var trim: T? = null, var ignore: T? = null) {
fun get(policy: ComparisonPolicy): T? =
when (policy) {
ComparisonPolicy.IGNORE_WHITESPACES -> ignore ?: trim ?: default
ComparisonPolicy.TRIM_WHITESPACES -> trim ?: default
ComparisonPolicy.DEFAULT -> default
}
}
}
@@ -187,25 +187,31 @@ class MergeResolveUtilTest : DiffTestCase() {
)
}
fun testRegressions() {
test(
"i\n",
"i",
"\ni",
"i\n",
"i"
)
}
private fun testGreedy(base: String, left: String, right: String, expected: String?) {
test(base, left, right, expected, true);
test(base, left, right, expected, true)
}
private fun test(base: String, left: String, right: String, expected: String?, isGreedy: Boolean = false) {
val simpleResult = MergeResolveUtil.tryResolve(left, base, right)
val magicResult = MergeResolveUtil.tryGreedyResolve(left, base, right);
val expectedSimple = if (isGreedy) null else expected
val expectedGreedy = expected
test(base, left, right, expectedSimple, expectedGreedy)
}
if (expected == null) {
assertNull(simpleResult)
assertNull(magicResult)
}
else if (isGreedy) {
assertNull(simpleResult)
assertEquals(expected, magicResult)
}
else {
assertEquals(expected, simpleResult)
assertEquals(expected, magicResult)
}
private fun test(base: String, left: String, right: String, expectedSimple: String?, expectedGreedy: String?) {
val simpleResult = MergeResolveUtil.tryResolve(left, base, right)
val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right)
assertEquals(expectedSimple, simpleResult, "Simple")
assertEquals(expectedGreedy, greedyResult, "Greedy")
}
}
@@ -17,72 +17,72 @@ package com.intellij.diff.comparison
class WordComparisonUtilTest : ComparisonUtilTestBase() {
fun testSimpleCases() {
words {
lines_inner {
("x z" - "y z")
("- " - "- ").default()
testAll()
}
words {
lines_inner {
("x z" - "y z")
("- " - "- ").default()
testAll()
}
words {
lines_inner {
(" x z" - "y z")
("-- " - "- ").default()
(" - " - "- ").trim()
testAll()
}
words {
lines_inner {
("x z " - "y z")
("- -" - "- ").default()
("- " - "- ").trim()
testAll()
}
words {
lines_inner {
("x z " - "y z")
("- -" - "- ").default()
("- " - "- ").trim()
testAll()
}
words {
lines_inner {
("x z" - " y z ")
("- " - "-- -").default()
("- " - " - ").trim()
testAll()
}
words {
lines_inner {
("x y" - "x z ")
(" -" - " --").default()
(" -" - " - ").trim()
testAll()
}
words {
lines_inner {
("x,y" - "x")
(" --" - " ").default()
testAll()
}
words {
lines_inner {
("x,y" - "y")
("-- " - " ").default()
testAll()
}
words {
lines_inner {
(".x=" - ".!=")
(" - " - " - ").default()
testAll()
}
words {
lines_inner {
("X xyz1 Z" - "X xyz2 Z")
(" ---- " - " ---- ").default()
testAll()
@@ -90,52 +90,52 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testPunctuation() {
words {
lines_inner {
(" x.z.x " - "x..x")
("- - -" - " ").default()
(" - " - " ").trim()
testAll()
}
words {
lines_inner {
("x..x" - " x.z.x ")
(" " - "- - -").default()
(" " - " - ").trim()
testAll()
}
words {
lines_inner {
("x ... z" - "y ... z")
("- " - "- ").default()
testAll()
}
words {
lines_inner {
("x ... z" - "x ... y")
(" -" - " -").default()
testAll()
}
words {
lines_inner {
("x ,... z" - "x ... y")
(" - -" - " -").default()
testAll()
}
words {
lines_inner {
("x . , .. z" - "x ... y")
(" --- -" - " -").default()
(" - -" - " -").ignore()
testAll()
}
words {
lines_inner {
("x==y==z" - "x====z")
(" - " - " ").default()
testAll()
}
words {
lines_inner {
("x====z" - "x==t==z")
(" " - " - ").default()
testAll()
@@ -143,13 +143,13 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testOldDiffBug() {
words {
lines_inner {
("x'y'>" - "x'>")
(" -- " - " ").default()
testAll()
}
words {
lines_inner {
("x'>" - "x'y'>")
(" " - " -- ").default()
testAll()
@@ -157,14 +157,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testWhitespaceOnlyChanges() {
words {
lines_inner {
("x =z" - "x= z")
(" -- " - " -- ").default()
testDefault()
testTrim()
}
words {
lines_inner {
("x =" - "x= z")
(" -- " - " ---").default()
(" " - " -").ignore()
@@ -173,7 +173,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testNewlines() {
words {
lines_inner {
(" x _ y _ z " - "x z")
("- ------ -" - " ").default()
(" - " - " ").trim()
@@ -181,17 +181,48 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words {
lines_inner {
("x z" - " x _ y _ z ")
(" " - "- ------ -").default()
(" " - " - ").trim()
(" " - " - ").ignore()
testAll()
}
words {
("_i" - "i_")
("- " - " -").default()
(" " - " ").trim()
testAll()
}
words {
("i_" - "_i")
("- " - " -").default() // TODO
testAll()
}
words {
("x_y" - "xy")
(" " - " ").ignore()
testIgnore()
}
words {
("A x_y B" - "a xy b")
("-------" - "------").ignore()
testIgnore()
}
words {
("A xy B" - "a xy b")
("- -" - "- -").ignore()
testIgnore()
}
}
fun testFixedBugs() {
words {
lines_inner {
(".! " - ". y!")
(" -" - " --- ").default()
(" " - " --- ").trim()
@@ -199,7 +230,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words {
lines_inner {
(" x n" - " y_ x m")
(" -" - "---- -").default()
(" -" - " - -").trim()
@@ -207,7 +238,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words {
lines_inner {
("x_" - "x! ")
(" -" - " ---").default()
(" " - " - ").trim()
@@ -217,35 +248,35 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testInnerWhitespaces() {
words {
lines_inner {
("<< x >>" - "<.<>.>")
(" --- " - " - - ").default()
(" - " - " - - ").ignore()
testAll()
}
words {
lines_inner {
("<< x >>" - "y<<x>>y")
(" - - " - "- -").default()
(" " - "- -").ignore()
testAll()
}
words {
lines_inner {
("x .. z" - "x y .. z")
(" " - " -- ").default() // TODO: looks wrong
(" " - " -- ").default()
(" " - " - ").ignore()
testAll()
}
words {
lines_inner {
(" x..z" - "x..y ")
("-- -" - " ---").default()
(" -" - " - ").trim()
testAll()
}
words {
lines_inner {
(" x y x _ x z x " - "x x_x x")
("- -- - - -- -" - " ").default()
(" -- -- " - " ").trim()
@@ -256,28 +287,28 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
fun testAlgorithmSpecific() {
// prefer words over punctuation
words {
lines_inner {
("...x" - "x...")
("--- " - " ---").default()
testAll()
}
// prefer longer words sequences
words {
lines_inner {
("x x y" - "x y")
("-- " - " ").default()
("- " - " ").ignore()
testAll()
}
words {
lines_inner {
("y x x" - "y x")
(" --" - " ").default()
(" -" - " ").ignore()
testAll()
}
words {
lines_inner {
("A X A B" - "A B")
("---- " - " ").default()
("--- " - " ").ignore()
@@ -285,14 +316,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
// prefer less modified 'sentences'
words {
lines_inner {
("A.X A.Z" - "A.X A.Y A.Z")
(" " - " ---- ").default()
(" " - " --- ").ignore()
testAll()
}
words {
lines_inner {
("X.A Z.A" - "X.A Y.A Z.A")
(" " - " ---- ").default()
(" " - " --- ").ignore()
@@ -300,7 +331,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
// prefer punctuation over whitespaces
words {
lines_inner {
(". " - " .")
(" ---" - "--- ").default()
testDefault()
@@ -308,26 +339,26 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun `test legacy cases from ByWordTest`() {
words {
lines_inner {
("abc def, 123" - "ab def, 12")
("--- ---" - "-- --").default()
testAll()
}
words {
lines_inner {
(" a[xy]+1" - ",a[]+1")
("- -- " - "- ").default()
(" -- " - "- ").trim()
testAll()
}
words {
lines_inner {
("0987_ a.g();_" - "yyyy_")
("------------- " - "---- ").default()
testAll()
}
words {
lines_inner {
(" abc_2222_" - " x = abc_zzzz_")
//(" ---- " - "-- ---- ---- ").legacy()
(" ---- " - " ------ ---- ").default()
@@ -335,7 +366,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words { // Idea58505
lines_inner { // Idea58505
(" if (eventMerger!=null && !dataSelection.getValueIsAdjusting()) {" -
" if (eventMerger!=null && (dataSelection==null || !dataSelection.getValueIsAdjusting())) {")
//(" - " -
@@ -347,7 +378,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words { // Idea56428
lines_inner { // Idea56428
("messageInsertStatement = connection.prepareStatement(\"INSERT INTO AUDIT (AUDIT_TYPE_ID, STATUS, SERVER_ID, INSTANCE_ID, REQUEST_ID) VALUES (?, ?, ?, ?, ?)\");" -
"messageInsertStatement = connection.prepareStatement(\"INSERT INTO AUDIT (AUDIT_TYPE_ID, CREATION_TIMESTAMP, STATUS, SERVER_ID, INSTANCE_ID, REQUEST_ID) VALUES (?, ?, ?, ?, ?, ?)\");").plainSource()
//(" . . " -
@@ -359,14 +390,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words {
lines_inner {
("f(a, b);" - "f(a,_ b);")
(" " - " -- ").default()
(" " - " ").trim()
testAll()
}
words {
lines_inner {
(" o.f(a)" - "o. f( b)")
("- - " - " - -- ").default()
(" - " - " - -- ").trim()
@@ -374,7 +405,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
testAll()
}
words {
lines_inner {
(" 123 " - "xyz")
(" --- " - "---").trim()
testTrim()
@@ -382,14 +413,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() {
}
fun testEmptyRangePositions() {
words {
lines_inner {
("x? y" - "x y")
(" - " - " ").default()
default(del(1, 1, 1))
testAll()
}
words {
lines_inner {
("x ?y" - "x y")
(" - " - " ").default()
default(del(2, 2, 1))
@@ -0,0 +1,108 @@
/*
* 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.diff.comparison
class WordMergeComparisonUtilTest : ComparisonUtilTestBase() {
fun testSimple() {
words {
("" - "" - "")
("" - "" - "").default()
testAll()
}
words {
("" - "X" - "")
("" - "-" - "").default()
testAll()
}
words {
("X" - "" - "")
("-" - "" - "").default()
testAll()
}
words {
("a b" - "a b" - "a b")
(" " - " " - " ").default()
testAll()
}
words {
("A b c" - "a b c" - "a b C")
("- -" - "- -" - "- -").default()
testAll()
}
words {
("a c" - "a c" - "a X c")
(" " - " " - " -- ").default()
(" " - " " - " - ").ignore()
testAll()
}
words {
("a X c" - "a X c" - "a c")
(" -- " - " -- " - " ").default()
(" - " - " - " - " ").ignore()
testAll()
}
words {
("a X c" - "a c" - "a Y c")
(" -- " - " " - " -- ").default()
(" - " - " " - " - ").ignore()
testAll()
}
words {
("a c" - "a X c" - "a Y c")
(" " - " -- " - " -- ").default()
(" " - " - " - " - ").ignore()
testAll()
}
}
fun testNewlines() {
words {
("i" - "i_" - "_i")
("-" - "--" - "--").default() // TODO
(" " - " " - " ").trim()
testAll()
}
words {
("_i" - "i_" - "i")
("--" - "--" - "-").default()
(" " - " " - " ").trim()
testAll()
}
words {
("i" - "_i" - "i_")
(" " - "- " - " -").default()
(" " - " " - " ").trim()
testAll()
}
words {
("_i" - "i" - "i_")
("- " - " " - " -").default()
(" " - " " - " ").trim()
testAll()
}
}
}
@@ -32,7 +32,7 @@ class MergeAutoTest : MergeTestBase() {
private fun doUndoTest(seed: Long, runs: Int, maxLength: Int) {
doTest(seed, runs, maxLength) { text1, text2, text3, debugData ->
testN(text1, text2, text3) {
if (changes.size == 0) {
if (changes.isEmpty()) {
assertEquals(text1, text2)
assertEquals(text1, text3)
assertEquals(text2, text3)
@@ -99,7 +99,7 @@ abstract class MergeTestBase : DiffTestCase() {
val editor: EditorEx = viewer.editor
val document: Document = editor.document
private val textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
private val textEditor = TextEditorProvider.getInstance().getTextEditor(editor)
private val undoManager = UndoManager.getInstance(project!!)
fun change(num: Int): TextMergeChange {
@@ -114,7 +114,7 @@ abstract class MergeTestBase : DiffTestCase() {
//
fun runActionByTitle(name: String): Boolean {
val action = actions.filter { name.equals(it.templatePresentation.text) }
val action = actions.filter { name == it.templatePresentation.text }
assertTrue(action.size == 1, action.toString())
return runAction(action[0])
}
@@ -396,7 +396,7 @@ abstract class MergeTestBase : DiffTestCase() {
if (other !is ViewerState) return false
if (!StringUtil.equals(content, other.content)) return false
if (!changes.equals(other.changes)) return false
if (changes != other.changes) return false
return true
}
@@ -411,9 +411,9 @@ abstract class MergeTestBase : DiffTestCase() {
if (other !is ChangeState) return false
if (!StringUtil.equals(content, other.content)) return false
if (!starts.equals(other.starts)) return false
if (!ends.equals(other.ends)) return false
if (!resolved.equals(other.resolved)) return false
if (starts != other.starts) return false
if (ends != other.ends) return false
if (resolved != other.resolved) return false
return true
}
@@ -175,6 +175,7 @@ class LineNumberConvertorCorrectorTest : UsefulTestCase() {
assertEquals(minimumMatched2, counter2)
}
@Suppress("unused")
fun printMatchings() {
for (i in 0..length * 2 - 1) {
val value = convertor.convert1(i)
@@ -34,8 +34,8 @@ class UnifiedFragmentBuilderAutoTest : DiffTestCase() {
doAutoTest(seed, runs) { debugData ->
debugData.put("MaxLength", maxLength)
var text1 = DocumentImpl(generateText(maxLength))
var text2 = DocumentImpl(generateText(maxLength))
val text1 = DocumentImpl(generateText(maxLength))
val text2 = DocumentImpl(generateText(maxLength))
debugData.put("Text1", textToReadableFormat(text1.charsSequence))
debugData.put("Text2", textToReadableFormat(text2.charsSequence))
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,6 +95,30 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority
@NotNull String presentationText,
@NotNull List<ProblemDescriptor> descriptions,
@Nullable Class quickfixClass) {
sortDescriptions(descriptions);
return applyFixesNoSort(project, presentationText, descriptions, quickfixClass);
}
public static AbstractPerformFixesTask applyFixesNoSort(@NotNull Project project,
@NotNull String presentationText,
@NotNull List<ProblemDescriptor> descriptions,
@Nullable Class quickfixClass) {
final SequentialModalProgressTask progressTask =
new SequentialModalProgressTask(project, presentationText, true);
final boolean isBatch = quickfixClass != null && BatchQuickFix.class.isAssignableFrom(quickfixClass);
final AbstractPerformFixesTask fixesTask = isBatch ?
new PerformBatchFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass) :
new PerformFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass);
CommandProcessor.getInstance().executeCommand(project, () -> {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
progressTask.setMinIterationTime(200);
progressTask.setTask(fixesTask);
ProgressManager.getInstance().run(progressTask);
}, presentationText, null);
return fixesTask;
}
public static void sortDescriptions(@NotNull List<ProblemDescriptor> descriptions) {
Collections.sort(descriptions, (o1, o2) -> {
final ProblemDescriptorBase d1 = (ProblemDescriptorBase)o1;
final ProblemDescriptorBase d2 = (ProblemDescriptorBase)o2;
@@ -104,20 +128,6 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority
}
return -elementsDiff;
});
final SequentialModalProgressTask progressTask =
new SequentialModalProgressTask(project, presentationText, true);
final boolean isBatch = quickfixClass != null && BatchQuickFix.class.isAssignableFrom(quickfixClass);
final AbstractPerformFixesTask fixesTask = isBatch ?
new PerformBatchFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass) :
new PerformFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass);
CommandProcessor.getInstance().executeCommand(project, () -> {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
progressTask.setMinIterationTime(200);
progressTask.setTask(fixesTask);
ProgressManager.getInstance().run(progressTask);
}, presentationText, null);
return fixesTask;
}
@Override
@@ -938,6 +938,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
}
}
if (!localDescriptors.isEmpty()) {
CleanupInspectionIntention.sortDescriptions(localDescriptors);
descriptors.addAll(localDescriptors);
files.add(file);
}
@@ -962,7 +963,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
}
Runnable runnable = () -> {
if (!FileModificationService.getInstance().preparePsiElementsForWrite(files)) return;
CleanupInspectionIntention.applyFixes(getProject(), "Code Cleanup", descriptors, null);
CleanupInspectionIntention.applyFixesNoSort(getProject(), "Code Cleanup", descriptors, null);
};
TransactionGuard.submitTransaction(getProject(), runnable);
}
@@ -326,6 +326,12 @@ public final class LanguageConsoleBuilder {
super.dispose();
}
@Override
public void scrollToEnd() {
getComponent().validate();
super.scrollToEnd();
}
private final class GutterUpdateScheduler extends DocumentAdapter implements DocumentBulkUpdateListener {
private final ConsoleGutterComponent lineStartGutter;
private final ConsoleGutterComponent lineEndGutter;
@@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable;
import static com.intellij.formatting.Indent.Type.CONTINUATION;
import static com.intellij.formatting.Indent.Type.NORMAL;
class IndentCalculator {
public class IndentCalculator {
private @NotNull final Project myProject;
private @NotNull final Editor myEditor;
@@ -39,7 +39,6 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.*;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdaterImpl;
@@ -2003,11 +2002,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
private void processFilesInReadActionWithYieldingToWriteAction() {
try {
while (myVfsEventsMerger.hasChanges()) {
boolean result = ProgressIndicatorUtils
.runInReadActionWithWriteActionPriority(this::processFilesInReadAction, null);
if (!result) {
ProgressIndicatorUtils.yieldToPendingWriteActions();
}
ProgressManager.getInstance().runInReadActionWithWriteActionPriority(this::processFilesInReadAction);
}
}
finally {
@@ -23,7 +23,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
/**
* Created by Maxim.Mossienko on 11/10/2016.
* @author Maxim.Mossienko on 11/10/2016.
*/
public class VfsEventsMerger {
public void recordFileEvent(int fileId, VirtualFile file, boolean contentChange) {
@@ -35,19 +35,10 @@ public class VfsEventsMerger {
}
private void updateChange(int fileId, VirtualFile file, short mask) {
ChangeInfo newChangeInfo = new ChangeInfo(file, mask, null);
ChangeInfo existingChangeInfo = myChangeInfos.cacheOrGet(fileId, newChangeInfo);
if (existingChangeInfo != newChangeInfo) {
while (true) {
newChangeInfo = new ChangeInfo(file, mask, existingChangeInfo);
if (existingChangeInfo == null) {
existingChangeInfo = myChangeInfos.cacheOrGet(fileId, newChangeInfo);
} else {
if (myChangeInfos.replace(fileId, existingChangeInfo, newChangeInfo)) break;
existingChangeInfo = myChangeInfos.get(fileId);
}
}
while (true) {
ChangeInfo existingChangeInfo = myChangeInfos.get(fileId);
ChangeInfo newChangeInfo = new ChangeInfo(file, mask, existingChangeInfo);
if(myChangeInfos.put(fileId, newChangeInfo) == existingChangeInfo) break;
}
}
@@ -59,6 +50,7 @@ public class VfsEventsMerger {
// 2. Method processes snapshot of available events at the time of the invokation, it does mean that if events are produced concurrently
// with the processing then set of events will be not empty
// 3. Method regularly checks for cancellations (thus can finish with PCEs) but event processor should process the change info atomically
// (without PCE)
public boolean processChanges(VfsEventProcessor eventProcessor) {
if (!myChangeInfos.isEmpty()) {
int[] fileIds = myChangeInfos.keys(); // snapshot of the keys
@@ -148,5 +140,4 @@ public class VfsEventsMerger {
return fileId;
}
}
}
@@ -86,6 +86,7 @@ class ReadMostlyRWLock {
boolean isReadLockedByThisThread() {
checkReadThreadAccess();
Reader status = R.get();
throwIfImpatient(status);
return status.readRequested;
}
@@ -107,9 +108,7 @@ class ReadMostlyRWLock {
if (iteration > SPIN_TO_WAIT_FOR_LOCK) {
status.blocked = true;
try {
if (status.impatientReads) {
throw new ApplicationUtil.CannotRunReadActionException();
}
throwIfImpatient(status);
LockSupport.parkNanos(this, 1000000); // unparked by writeUnlock
}
finally {
@@ -121,6 +120,13 @@ class ReadMostlyRWLock {
}
}
private void throwIfImpatient(Reader status) {
// when client explicitly runs in non-cancelable block do not throw from within nested read actions
if (status.impatientReads && writeRequested && !ProgressManager.getInstance().isInNonCancelableSection()) {
throw new ApplicationUtil.CannotRunReadActionException();
}
}
/**
* Executes a {@code runnable} in an "impatient" mode.
* In this mode any attempt to grab read lock
@@ -163,7 +163,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
runStartupActivity();
}
else {
myStartupManager.registerStartupActivity(() -> runStartupActivity());
myStartupManager.registerStartupActivity(this::runStartupActivity);
}
}
@@ -591,7 +591,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
if (refs.size() <= FREE_QUEUES_LIMIT) return;
DocumentReference[] backSorted = refs.toArray(new DocumentReference[refs.size()]);
Arrays.sort(backSorted, (a, b) -> getLastCommandTimestamp(a) - getLastCommandTimestamp(b));
Arrays.sort(backSorted, Comparator.comparingInt(this::getLastCommandTimestamp));
for (int i = 0; i < backSorted.length - FREE_QUEUES_LIMIT; i++) {
DocumentReference each = backSorted[i];
@@ -634,7 +634,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
@TestOnly
public void dropHistoryInTests() {
flushMergers();
LOG.assertTrue(myCommandLevel == 0);
LOG.assertTrue(myCommandLevel == 0, myCommandLevel);
myUndoStacksHolder.clearAllStacksInTests();
myRedoStacksHolder.clearAllStacksInTests();
@@ -643,8 +643,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
@TestOnly
private void flushMergers() {
// Run dummy command in order to flush all mergers...
CommandProcessor.getInstance()
.executeCommand(myProject, EmptyRunnable.getInstance(), CommonBundle.message("drop.undo.history.command.name"), null);
CommandProcessor.getInstance().executeCommand(myProject, EmptyRunnable.getInstance(), CommonBundle.message("drop.undo.history.command.name"), null);
}
@TestOnly
@@ -2,7 +2,7 @@
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog.PluginUpdateInfoPanel">
<grid id="18751" binding="myPanel" layout-manager="GridBagLayout">
<constraints>
<xy x="63" y="44" width="604" height="510"/>
<xy x="63" y="44" width="604" height="522"/>
</constraints>
<properties/>
<border type="none"/>
@@ -13,7 +13,6 @@
<gridbag weightx="1.0" weighty="0.0"/>
</constraints>
<properties>
<preferredSize width="268" height="30"/>
<text resource-bundle="messages/IdeBundle" key="updates.plugins.ready.header"/>
</properties>
</component>
@@ -21,4 +21,5 @@ public class SSHUtil {
public static final Pattern PASSPHRASE_PROMPT = Pattern.compile("Enter passphrase for key \\'(.*)\\':\\s?");
public static final Pattern PASSWORD_PROMPT = Pattern.compile("(.*)\\'s password:\\s?");
public static final String PASSWORD_PROMPT_SUFFIX = "password:";
public static final String CONFIRM_CONNECTION_PROMPT = "Are you sure you want to continue connecting";
}
@@ -23,6 +23,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
// unless stated otherwise, the behavior described in cases is true for 162+
class UpdateStrategyTest {
@Test fun `channel contains no builds`() {
val result = check("IU-145.258", ChannelStatus.RELEASE, """<channel id="IDEA_Release" status="release" licensing="release"/>""")
@@ -150,6 +151,7 @@ class UpdateStrategyTest {
assertBuild("143.2332", result.newBuild)
}
// since 163
@Test fun `updates from the same baseline are preferred (per-release channels)`() {
val result = check("IU-143.2287", ChannelStatus.EAP, """
<channel id="IDEA_143_EAP" status="eap" licensing="eap">
@@ -714,4 +714,47 @@ public class ApplicationImplTest extends LightPlatformTestCase {
readAction2.get();
readAction1.get();
}
public void testReadActionInImpatientModeMustNotThrowWhenThereIsAPendingWriteAndWeAreUnderNonCancelableSection() throws Exception {
AtomicBoolean stopRead = new AtomicBoolean();
AtomicBoolean readAcquired = new AtomicBoolean();
ApplicationImpl app = (ApplicationImpl)ApplicationManager.getApplication();
Future<?> readAction1 = app.executeOnPooledThread(() ->
app.runReadAction(() -> {
readAcquired.set(true);
try {
while (!stopRead.get()) ;
}
finally {
readAcquired.set(false);
}
})
);
while (!readAcquired.get());
AtomicBoolean executingImpatientReader = new AtomicBoolean();
Future<?> readAction2 = app.executeOnPooledThread(() -> {
// wait for write action attempt to start
while (!app.isWriteActionPending());
ProgressManager.getInstance().executeNonCancelableSection(()->{
app.executeByImpatientReader(() -> {
executingImpatientReader.set(true);
app.runReadAction(EmptyRunnable.getInstance());
// must not throw
});
});
});
Future<?> readAction1Canceler = app.executeOnPooledThread(() -> {
while (!executingImpatientReader.get());
TimeoutUtil.sleep(300); // make sure readAction2 does call runReadAction()
stopRead.set(true);
});
app.runWriteAction(EmptyRunnable.getInstance());
readAction1Canceler.get();
readAction2.get();
readAction1.get();
}
}
@@ -373,6 +373,14 @@ public class PsiTestUtil {
public static Sdk addJdkAnnotations(@NotNull Sdk sdk) {
String path = FileUtil.toSystemIndependentName(PlatformTestUtil.getCommunityPath()) + "/java/jdkAnnotations";
VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path);
return addRootsToJdk(sdk, AnnotationOrderRootType.getInstance(), root);
}
@NotNull
@Contract(pure=true)
public static Sdk addRootsToJdk(@NotNull Sdk sdk,
@NotNull OrderRootType rootType,
@NotNull VirtualFile... roots) {
Sdk clone;
try {
clone = (Sdk)sdk.clone();
@@ -381,7 +389,9 @@ public class PsiTestUtil {
throw new RuntimeException(e);
}
SdkModificator sdkModificator = clone.getSdkModificator();
sdkModificator.addRoot(root, AnnotationOrderRootType.getInstance());
for (VirtualFile root : roots) {
sdkModificator.addRoot(root, rootType);
}
sdkModificator.commitChanges();
return clone;
}
@@ -242,7 +242,7 @@ debugger.renderers.file.description=Enable file object renderer
debugger.single.smart.step.force=true
debugger.single.smart.step.force.description=Do force step into on single variant smart step into
debugger.emulate.method.breakpoints=false
debugger.emulate.method.breakpoints.description=Emulate method breakpoints with line breakpoints
debugger.emulate.method.breakpoints.description=Emulate method breakpoints with line breakpoints by default
debugger.intern.string.literals=false
debugger.intern.string.literals.description=Make string literal refer to the same instance of class String
@@ -913,5 +913,3 @@ build.manager.async.fs.events.description=Experimental: TranslatingCompilerFiles
batch.inspections.use.psi.as.ref.table.key=false
batch.inspections.use.psi.as.ref.table.key.description=Use PsiElement as key for RefManager's table
json.schema.fast.annotation.update=false
json.schema.fast.annotation.update.description=Restart highlighting for all json files matching json schema on json schema fiel change
@@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author Konstantin Kolosovsky.
@@ -212,4 +214,13 @@ public abstract class BaseDataReader {
LOG.error(e);
}
}
}
public void waitFor(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
try {
myFinishedFuture.get(timeout, unit);
}
catch (ExecutionException e) {
LOG.error(e);
}
}
}
@@ -90,6 +90,12 @@ public class ChangeDiffRequestProducer implements DiffRequestProducer {
}
public static boolean isEquals(@NotNull Change change1, @NotNull Change change2) {
if (!Comparing.equal(ChangesUtil.getBeforePath(change1), ChangesUtil.getBeforePath(change2)) ||
!Comparing.equal(ChangesUtil.getAfterPath(change1), ChangesUtil.getAfterPath(change2))) {
// we use Change.hashCode(), so removing this check might violate comparison contract
return false;
}
for (ChangeDiffViewerWrapperProvider provider : ChangeDiffViewerWrapperProvider.EP_NAME.getExtensions()) {
ThreeState equals = provider.isEquals(change1, change2);
if (equals == ThreeState.NO) return false;
@@ -24,7 +24,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.SystemInfo
import java.io.File
import kotlin.reflect.KProperty
import kotlin.reflect.*
/**
* @author traff
@@ -39,18 +39,19 @@ class TerminalProjectOptionsProvider(private val myProject: Project) : Persisten
}
override fun loadState(state: State) {
shellPath = state.myShellPath
myState.myShellPath = state.myShellPath
myState.myStartingDirectory = state.myStartingDirectory
}
class State {
var myShellPath: String? = null
var myStartingDirectory: String? = null
}
var shellPath: String? by ValueWithDefault { defaultShellPath }
var shellPath: String? by ValueWithDefault(State::myShellPath, myState) { defaultShellPath }
var startingDirectory: String? by ValueWithDefault { defaultStartingDirectory }
var startingDirectory: String? by ValueWithDefault(State::myStartingDirectory, myState) { defaultStartingDirectory }
val defaultStartingDirectory: String?
get() {
@@ -114,15 +115,15 @@ class TerminalProjectOptionsProvider(private val myProject: Project) : Persisten
}
class ValueWithDefault(val default: () -> String?) {
private var _value: String? = null
// TODO: In Kotlin 1.1 it will be possible to pass references to instance properties. Until then we need 'state' argument as a reciever for
// to property to apply
class ValueWithDefault(val prop: KMutableProperty1<TerminalProjectOptionsProvider.State, String?>, val state: TerminalProjectOptionsProvider.State, val default: () -> String?) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String? {
return if (_value !== null) _value else default()
return if (prop.get(state) !== null) prop.get(state) else default()
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
_value = if (value == default() || value.isNullOrEmpty()) null else value
prop.set(state, if (value == default() || value.isNullOrEmpty()) null else value)
}
}
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.ui.JBColor;
import com.intellij.util.Alarm;
import com.intellij.util.PlatformUtils;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,6 +52,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
private static final Logger LOG = Logger.getInstance(IpnbFilePanel.class);
private final DocumentAdapter myDocumentListener;
private final Document myDocument;
private final MessageBusConnection myBusConnection;
private IpnbFile myIpnbFile;
private final Project myProject;
@NotNull private final IpnbFileEditor myParent;
@@ -98,19 +100,22 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
}, 10, ModalityState.stateForComponent(this));
UIUtil.requestFocus(this);
ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectEx.ProjectSaved.TOPIC,
new ProjectEx.ProjectSaved() {
@Override
public void saved(@NotNull Project project) {
CommandProcessor.getInstance().runUndoTransparentAction(
() -> ApplicationManager.getApplication()
.runWriteAction(() -> saveToFile(false)));
}
});
myBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
myBusConnection.subscribe(ProjectEx.ProjectSaved.TOPIC,
new ProjectEx.ProjectSaved() {
@Override
public void saved(@NotNull Project project) {
CommandProcessor.getInstance().runUndoTransparentAction(
() -> ApplicationManager.getApplication()
.runWriteAction(() -> saveToFile(false)));
}
});
}
@Override
public void dispose() {
myDocument.removeDocumentListener(myDocumentListener);
Disposer.dispose(myBusConnection);
}
private void readFromFile(boolean showError) {
@@ -391,9 +396,11 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
myIpnbFile.removeCell(index);
remove(index);
int indexToSelect = index < myIpnbPanels.size() ? index : index-1;
final IpnbEditablePanel panel = myIpnbPanels.get(indexToSelect);
setSelectedCell(panel, false);
if (!myIpnbPanels.isEmpty()) {
int indexToSelect = index < myIpnbPanels.size() ? index : index - 1;
final IpnbEditablePanel panel = myIpnbPanels.get(indexToSelect);
setSelectedCell(panel, false);
}
}
public void saveToFile(boolean synchronize) {
@@ -620,7 +627,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
createAndAddCell(true, IpnbCodeCell.createEmptyCodeCell());
CommandProcessor.getInstance().executeCommand(getProject(),
() -> ApplicationManager.getApplication().runWriteAction(
() -> saveToFile(false)), "Ipnb.runCell", new Object());
() -> saveToFile(false)), "Ipnb.runCell", new Object());
}
}
@@ -308,7 +308,10 @@ public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
}
if (outputContent != null) {
myCell.addCellOutput(outputContent);
addOutputPanel(myViewPanel, outputContent, outputContent instanceof IpnbOutOutputCell);
final JComponent component = myHideableOutputPanel.getSecondComponent();
if (component != null) {
addOutputPanel(component, outputContent, outputContent instanceof IpnbOutOutputCell);
}
}
final IpnbFilePanel filePanel = myParent.getIpnbFilePanel();
filePanel.revalidateAndRepaint();
@@ -28,7 +28,10 @@ import org.jetbrains.plugins.ipnb.format.cells.output.*;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class IpnbParser {
private static final Logger LOG = Logger.getInstance(IpnbParser.class);
@@ -57,7 +60,7 @@ public class IpnbParser {
IpnbFileRaw rawFile = gson.fromJson(fileText.toString(), IpnbFileRaw.class);
if (rawFile == null) {
int nbformat = isIpythonNewFormat(virtualFile) ? 4 : 3;
return new IpnbFile(Collections.emptyMap(), nbformat, Lists.newArrayList(), path);
return new IpnbFile(new HashMap<>(), nbformat, Lists.newArrayList(), path);
}
List<IpnbCell> cells = new ArrayList<>();
final IpnbWorksheet[] worksheets = rawFile.worksheets;
@@ -256,7 +259,7 @@ public class IpnbParser {
public IpnbCell createCell(boolean isValidSource) {
final IpnbCell cell;
if (cell_type.equals("markdown")) {
cell = new IpnbMarkdownCell(isValidSource ? source : Collections.emptyList(), metadata);
cell = new IpnbMarkdownCell(isValidSource ? source : new ArrayList<>(), metadata);
}
else if (cell_type.equals("code")) {
final List<IpnbOutputCell> outputCells = new ArrayList<>();
@@ -265,14 +268,14 @@ public class IpnbParser {
}
final Integer prompt = prompt_number != null ? prompt_number : execution_count;
cell = new IpnbCodeCell(language == null ? "python" : language,
input == null ? (isValidSource ? source : Collections.emptyList()) : input,
input == null ? (isValidSource ? source : new ArrayList<>()) : input,
prompt, outputCells, metadata);
}
else if (cell_type.equals("raw")) {
cell = new IpnbRawCell(isValidSource ? source : Collections.emptyList());
cell = new IpnbRawCell(isValidSource ? source : new ArrayList<>());
}
else if (cell_type.equals("heading")) {
cell = new IpnbHeadingCell(isValidSource ? source : Collections.emptyList(), level, metadata);
cell = new IpnbHeadingCell(isValidSource ? source : new ArrayList<>(), level, metadata);
}
else {
cell = null;
@@ -303,7 +306,7 @@ public class IpnbParser {
final CellOutputRaw raw = new CellOutputRaw();
raw.metadata = outputCell.getMetadata();
if (raw.metadata == null && !(outputCell instanceof IpnbStreamOutputCell) && !(outputCell instanceof IpnbErrorOutputCell)) {
raw.metadata = Collections.emptyMap();
raw.metadata = new HashMap<>();
}
if (outputCell instanceof IpnbPngOutputCell) {
@@ -5,7 +5,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -61,7 +61,7 @@ public class IpnbCodeCell extends IpnbEditableCell {
@NotNull
public static IpnbCodeCell createEmptyCodeCell() {
return new IpnbCodeCell("python", Collections.emptyList(), null, new ArrayList<>(),
Collections.emptyMap());
return new IpnbCodeCell("python", new ArrayList<>(), null, new ArrayList<>(),
new HashMap<>());
}
}
+8 -4
View File
@@ -18,8 +18,8 @@ envs {
conda "django18", "2.7", ["django==1.6"], false
textfile "django18/tags.txt", "django"
// conda "django17", "2.7", ["django==1.7"], false
// textfile "django17/tags.txt", "django\nskeletons"
conda "django17", "2.7", ["django==1.7"], false
textfile "django17/tags.txt", "django\nskeletons"
conda "python34", "3.4", ["ipython==2.1", "django==1.6", "behave", "jinja2", "tox>=2.0", "pandas"], true
textfile "python34/tags.txt", "python3\nipython\nipython200\nskeletons\ndjango\nbehave\ntox\njinja2\npython34\npackaging\npandas"
@@ -43,8 +43,12 @@ if (new File(envs.envsDirectory, "django_latest").exists() &&
delete new File(envs.envsDirectory, "django_latest")
}
if (System.getenv("NO_CLEAN") == null && envs.envsDirectory.exists() &&
envs.envsDirectory.lastModified()<project.buildscript.sourceFile.lastModified()) {
def bootstrapDirNotEmpty = envs.bootstrapDirectory.exists() &&
(new File(envs.bootstrapDirectory, "latest_64/bin/conda").exists() ||
new File(envs.bootstrapDirectory, "latest_64/Scripts/conda.exe").exists())
if (!bootstrapDirNotEmpty || (System.getenv("NO_CLEAN") == null && envs.envsDirectory.exists() &&
envs.envsDirectory.lastModified() < project.buildscript.sourceFile.lastModified())) {
// clean the cache if the build script if modified later
println "Cleaning cached environments at " + envs.envsDirectory
delete envs.envsDirectory
@@ -151,7 +151,7 @@ public interface PydevConsoleRunner {
}
}));
return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_AND_PY_PATHS_ENV, path);
return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_AND_PYTHON_PATHS, path);
}
static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs, @NotNull Project project) {
@@ -127,9 +127,9 @@ import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.re
* @author traff, oleg
*/
public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
public static final String WORKING_DIR_AND_PY_PATHS_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
public static final String WORKING_DIR_AND_PYTHON_PATHS = "WORKING_DIR_AND_PYTHON_PATHS";
public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
"sys.path.extend([" + WORKING_DIR_AND_PY_PATHS_ENV + "])\n";
"sys.path.extend([" + WORKING_DIR_AND_PYTHON_PATHS + "])\n";
private static final Logger LOG = Logger.getInstance(PydevConsoleRunnerImpl.class.getName());
@SuppressWarnings("SpellCheckingInspection")
public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";