mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-02 00:57:59 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
Generated
+1
@@ -65,6 +65,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/devkit/jps-plugin/devkit-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/devkit/jps-plugin/devkit-jps-plugin.iml" group="plugins" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/diff-api/diff-api.iml" filepath="$PROJECT_DIR$/platform/diff-api/diff-api.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/diff-impl/diff-impl.iml" filepath="$PROJECT_DIR$/platform/diff-impl/diff-impl.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/diff-impl/diff-tests.iml" filepath="$PROJECT_DIR$/platform/diff-impl/diff-tests.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/dom-impl/dom-impl.iml" filepath="$PROJECT_DIR$/xml/dom-impl/dom-impl.iml" group="xml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/dom-openapi/dom-openapi.iml" filepath="$PROJECT_DIR$/xml/dom-openapi/dom-openapi.iml" group="xml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/xml/dom-tests/dom-tests.iml" filepath="$PROJECT_DIR$/xml/dom-tests/dom-tests.iml" group="xml" />
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+15
-11
@@ -136,18 +136,22 @@ if [ -n "$@@product_uc@@_PROPERTIES" ]; then
|
||||
IDE_PROPERTIES_PROPERTY="-Didea.properties.file=$@@product_uc@@_PROPERTIES"
|
||||
fi
|
||||
|
||||
vm_options_file="$IDE_BIN_HOME/@@vm_options@@$BITS.vmoptions"
|
||||
user_vm_options_file="$HOME/.@@system_selector@@/@@vm_options@@$BITS.vmoptions"
|
||||
if [ -r "$user_vm_options_file" ]; then
|
||||
vm_options_file="$user_vm_options_file"
|
||||
fi
|
||||
if [ -n "$@@product_uc@@_VM_OPTIONS" ]; then
|
||||
vm_options_file="$@@product_uc@@_VM_OPTIONS"
|
||||
fi
|
||||
|
||||
VM_OPTIONS=""
|
||||
VM_OPTIONS_FILES_USED=""
|
||||
for vm_opts_file in "$IDE_BIN_HOME/@@vm_options@@$BITS.vmoptions" "$OS_SPECIFIC_BIN_DIR/@@vm_options@@$BITS.vmoptions" "$HOME/.@@system_selector@@/@@vm_options@@$BITS.vmoptions" "$@@product_uc@@_VM_OPTIONS"; do
|
||||
if [ -r "$vm_opts_file" ]; then
|
||||
VM_OPTIONS_DATA=`"$CAT" "$vm_opts_file" | "$GREP" -v "^#.*" | "$TR" '\n' ' '`
|
||||
if [ -r "$vm_options_file" ]; then
|
||||
VM_OPTIONS_DATA=`"$CAT" "$vm_options_file" | "$GREP" -v "^#.*" | "$TR" '\n' ' '`
|
||||
VM_OPTIONS="$VM_OPTIONS $VM_OPTIONS_DATA"
|
||||
if [ -n "$VM_OPTIONS_FILES_USED" ]; then
|
||||
VM_OPTIONS_FILES_USED="$VM_OPTIONS_FILES_USED,"
|
||||
fi
|
||||
VM_OPTIONS_FILES_USED="$VM_OPTIONS_FILES_USED$vm_opts_file"
|
||||
fi
|
||||
done
|
||||
else
|
||||
message "Cannot find VM options file."
|
||||
fi
|
||||
|
||||
IS_EAP="@@isEap@@"
|
||||
if [ "$IS_EAP" = "true" ]; then
|
||||
@@ -172,7 +176,7 @@ LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JDK/jre/bin/java" \
|
||||
$AGENT \
|
||||
"-Xbootclasspath/a:$IDE_HOME/lib/boot.jar" \
|
||||
-classpath "$CLASSPATH" \
|
||||
$VM_OPTIONS "-Djb.vmOptionsFile=$VM_OPTIONS_FILES_USED" \
|
||||
$VM_OPTIONS "-Djb.vmOptionsFile=$vm_options_file" \
|
||||
"-XX:ErrorFile=$HOME/java_error_in_@@product_uc@@_%p.log" \
|
||||
-Djb.restart.code=88 -Didea.paths.selector=@@system_selector@@ \
|
||||
$IDE_PROPERTIES_PROPERTY \
|
||||
|
||||
Binary file not shown.
+7
-20
@@ -17,11 +17,11 @@
|
||||
package org.jetbrains.testme.instrumentation;
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
|
||||
public class Instrumenter extends ClassVisitor {
|
||||
private static final int ADDED_CODE_STACK_SIZE = 6;
|
||||
protected final ClassVisitor myClassVisitor;
|
||||
private final String myClassName;
|
||||
private final String myInternalClassName;
|
||||
@@ -88,7 +88,7 @@ public class Instrumenter extends ClassVisitor {
|
||||
mv = new StaticBlockMethodVisitor(mv);
|
||||
mv.visitCode();
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(myMethodNames.length + 2, 1);
|
||||
mv.visitMaxs(ADDED_CODE_STACK_SIZE, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
super.visitEnd();
|
||||
@@ -102,12 +102,11 @@ public class Instrumenter extends ClassVisitor {
|
||||
public void visitCode() {
|
||||
super.visitCode();
|
||||
|
||||
visitLdcInsn(myClassName);
|
||||
pushInstruction(this, myMethodNames.length);
|
||||
visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BOOLEAN);
|
||||
visitFieldInsn(Opcodes.PUTSTATIC, myInternalClassName, METHODS_VISITED, METHODS_VISITED_CLASS);
|
||||
|
||||
pushInstruction(this, myMethodNames.length);
|
||||
|
||||
visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
|
||||
|
||||
for(int i = 0; i < myMethodNames.length; ++i) {
|
||||
@@ -117,28 +116,16 @@ public class Instrumenter extends ClassVisitor {
|
||||
visitInsn(Opcodes.AASTORE);
|
||||
}
|
||||
|
||||
visitVarInsn(Opcodes.ASTORE, 0);
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC, ProjectData.PROJECT_DATA_OWNER, "trace", "(Ljava/lang/String;[Z[Ljava/lang/String;)[Z", false);
|
||||
visitFieldInsn(Opcodes.PUTSTATIC, myInternalClassName, METHODS_VISITED, METHODS_VISITED_CLASS);
|
||||
|
||||
Label startLabel = new Label();
|
||||
visitLabel(startLabel);
|
||||
|
||||
visitLdcInsn(myClassName);
|
||||
visitFieldInsn(Opcodes.GETSTATIC, myInternalClassName, METHODS_VISITED, METHODS_VISITED_CLASS);
|
||||
visitVarInsn(Opcodes.ALOAD, 0);
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC, ProjectData.PROJECT_DATA_OWNER, "trace", "(Ljava/lang/String;[Z[Ljava/lang/String;)V", false);
|
||||
|
||||
Label endLabel = new Label();
|
||||
visitLabel(endLabel);
|
||||
|
||||
visitLocalVariable("methodNames", "[Ljava/lang/String;", null, startLabel, endLabel, 0);
|
||||
// no return here
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
final int ourMaxStack = myMethodNames.length + 2;
|
||||
final int ourMaxLocals = 1;
|
||||
final int ourMaxStack = ADDED_CODE_STACK_SIZE;
|
||||
|
||||
super.visitMaxs(Math.max(ourMaxStack, maxStack), Math.max(ourMaxLocals, maxLocals));
|
||||
super.visitMaxs(Math.max(ourMaxStack, maxStack), maxLocals);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-44
@@ -17,42 +17,31 @@ public class ProjectData {
|
||||
myTraceDir = traceDir;
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, Set<String>> myTrace;
|
||||
private final ConcurrentMap<String, boolean[]> myTrace2 = new ConcurrentHashMap<String, boolean[]>();
|
||||
private final ConcurrentMap<String, String[]> myTrace3 = new ConcurrentHashMap<String, String[]>();
|
||||
|
||||
public static ProjectData getProjectData() {
|
||||
return ourData;
|
||||
}
|
||||
|
||||
public static void trace(String className, String methodSignature) {
|
||||
ourData.traceLines(className, methodSignature);
|
||||
}
|
||||
|
||||
// called from instrumented code during class's static init
|
||||
public static void trace(String className, boolean[] methodFlags, String[] methodNames) {
|
||||
ourData.traceLines(className, methodFlags, methodNames);
|
||||
}
|
||||
|
||||
public void traceLines(String className, String methodSignature) {
|
||||
if (myTrace != null) {
|
||||
Set<String> methods = myTrace.get(className);
|
||||
if (methods == null) {
|
||||
methods = new HashSet<String>();
|
||||
Set<String> previousMethods = myTrace.putIfAbsent(className, methods);
|
||||
if (previousMethods != null) methods = previousMethods;
|
||||
}
|
||||
synchronized (methods) {
|
||||
methods.add(methodSignature);
|
||||
}
|
||||
}
|
||||
public static boolean[] trace(String className, boolean[] methodFlags, String[] methodNames) {
|
||||
return ourData.traceLines(className, methodFlags, methodNames);
|
||||
}
|
||||
|
||||
public synchronized void traceLines(String className, boolean[] methodFlags, String[] methodNames) {
|
||||
private synchronized boolean[] traceLines(String className, boolean[] methodFlags, String[] methodNames) {
|
||||
//System.out.println("Registering " + className);
|
||||
assert methodFlags.length == methodNames.length;
|
||||
myTrace2.put(className, methodFlags);
|
||||
myTrace3.put(className, methodNames);
|
||||
final boolean[] previousMethodFlags = myTrace2.putIfAbsent(className, methodFlags);
|
||||
|
||||
if (previousMethodFlags != null) {
|
||||
assert previousMethodFlags.length == methodFlags.length;
|
||||
final String[] previousMethodNames = myTrace3.get(className);
|
||||
assert previousMethodNames != null && previousMethodNames.length == methodNames.length;
|
||||
} else {
|
||||
myTrace3.put(className, methodNames);
|
||||
}
|
||||
return previousMethodFlags != null ? previousMethodFlags : methodFlags;
|
||||
}
|
||||
|
||||
private static final int STRING_LENGTH_THRESHOLD = 255;
|
||||
@@ -163,22 +152,6 @@ public class ProjectData {
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
myTrace = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOldTrace(DataOutputStream os) throws IOException {
|
||||
os.writeInt(myTrace.size());
|
||||
for (Iterator<String> it = myTrace.keySet().iterator(); it.hasNext();) {
|
||||
final String classData = it.next();
|
||||
os.writeUTF(classData);
|
||||
final Set<String> methods = myTrace.get(classData);
|
||||
os.writeInt(methods.size());
|
||||
for (Iterator<String> iterator = methods.iterator(); iterator.hasNext(); ) {
|
||||
os.writeUTF(iterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void testStarted(final String name) {
|
||||
@@ -190,8 +163,4 @@ public class ProjectData {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearOldTrace() {
|
||||
myTrace = new ConcurrentHashMap<String, Set<String>>();
|
||||
}
|
||||
}
|
||||
|
||||
+23
-9
@@ -446,16 +446,30 @@ public class HighlightMethodUtil {
|
||||
final PsiMethod containerMethod = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true, PsiLambdaExpression.class);
|
||||
if (containerMethod != null) {
|
||||
final PsiMethod method = candidate.getElement();
|
||||
final List<PsiType> list = ContainerUtil.map(method.getParameterList().getParameters(),
|
||||
new Function<PsiParameter, PsiType>() {
|
||||
@Override
|
||||
public PsiType fun(PsiParameter parameter) {
|
||||
return parameter.getType();
|
||||
}
|
||||
});
|
||||
PsiType[] leftTypes = list.toArray(new PsiType[list.size()]);
|
||||
final PsiType[] expressionTypes = methodCall.getArgumentList().getExpressionTypes();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final int max = Math.max(parameters.length, expressionTypes.length);
|
||||
PsiType[] leftTypes = PsiType.createArray(max);
|
||||
PsiType[] rightTypes = PsiType.createArray(max);
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (i < parameters.length) {
|
||||
leftTypes[i] = parameters[i].getType();
|
||||
rightTypes[i] = i < expressionTypes.length ? expressionTypes[i] : null;
|
||||
|
||||
if (leftTypes[i] instanceof PsiEllipsisType &&
|
||||
!(max == expressionTypes.length &&
|
||||
expressionTypes[i] instanceof PsiArrayType &&
|
||||
expressionTypes[i].getArrayDimensions() == leftTypes[i].getArrayDimensions())) {
|
||||
leftTypes[i] = ((PsiEllipsisType)leftTypes[i]).getComponentType();
|
||||
}
|
||||
}
|
||||
else {
|
||||
leftTypes[i] = null;
|
||||
rightTypes[i] = i < expressionTypes.length ? expressionTypes[i] : null;
|
||||
}
|
||||
}
|
||||
final PsiSubstitutor substitutor = resolveHelper
|
||||
.inferTypeArguments(method.getTypeParameters(), leftTypes, methodCall.getArgumentList().getExpressionTypes(),
|
||||
.inferTypeArguments(method.getTypeParameters(), leftTypes, rightTypes,
|
||||
PsiUtil.getLanguageLevel(methodCall));
|
||||
PsiType methodCallTypeByArgs = substitutor.substitute(methodCall.getType());
|
||||
//ensure type params are not included
|
||||
|
||||
@@ -227,7 +227,7 @@ public class JavaKeywordCompletion {
|
||||
|
||||
if (psiElement().withText(";").withSuperParent(2, PsiIfStatement.class).accepts(prevLeaf) ||
|
||||
psiElement().withText("}").withSuperParent(3, PsiIfStatement.class).accepts(prevLeaf)) {
|
||||
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ELSE), TailTypes.SYNCHRONIZED_LPARENTH));
|
||||
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ELSE), TailType.HUMBLE_SPACE_BEFORE_WORD));
|
||||
}
|
||||
|
||||
if (psiElement().withText("}").withParent(psiElement(PsiCodeBlock.class).withParent(or(psiElement(PsiTryStatement.class), psiElement(PsiCatchSection.class)))).accepts(prevLeaf)) {
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBase
|
||||
if (declarationScope instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)declarationScope;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel7OrHigher(method))) {
|
||||
if (containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel8OrHigher(method))) {
|
||||
if (containingClass.findMethodBySignature(generateMethodPrototype(method, parameter), false) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -971,13 +971,16 @@ public class JavaDocInfoGenerator {
|
||||
|
||||
if (method instanceof PsiMethod) {
|
||||
PsiMethod psiMethod = (PsiMethod)method;
|
||||
final PsiDocComment docComment = getDocComment(psiMethod);
|
||||
final PsiDocTag[] localTags = docComment != null ? docComment.getTags() : PsiDocTag.EMPTY_ARRAY;
|
||||
int parameterIndex = psiMethod.getParameterList().getParameterIndex(parameter);
|
||||
final ParamInfo tagInfoProvider = findDocTag(localTags, parameterName, psiMethod, parameterLocator(parameterIndex));
|
||||
PsiParameterList parameterList = psiMethod.getParameterList();
|
||||
if (parameter.getParent() == parameterList) { // this can also be a parameter in foreach statement or in catch clause
|
||||
final PsiDocComment docComment = getDocComment(psiMethod);
|
||||
final PsiDocTag[] localTags = docComment != null ? docComment.getTags() : PsiDocTag.EMPTY_ARRAY;
|
||||
int parameterIndex = parameterList.getParameterIndex(parameter);
|
||||
final ParamInfo tagInfoProvider = findDocTag(localTags, parameterName, psiMethod, parameterLocator(parameterIndex));
|
||||
|
||||
if (tagInfoProvider != null) {
|
||||
generateOneParameter(buffer, tagInfoProvider);
|
||||
if (tagInfoProvider != null) {
|
||||
generateOneParameter(buffer, tagInfoProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.intellij.psi.util;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -64,11 +65,11 @@ public class ClassUtil {
|
||||
buf.append(qName);
|
||||
}
|
||||
else {
|
||||
final PsiClass parentClass = getContainingClass(aClass);
|
||||
final PsiClass parentClass = PsiTreeUtil.getContextOfType(aClass, PsiClass.class, true);
|
||||
if (parentClass != null) {
|
||||
formatClassName(parentClass, buf);
|
||||
buf.append("$");
|
||||
buf.append(getNonQualifiedClassIdx(aClass));
|
||||
buf.append(getNonQualifiedClassIdx(aClass, parentClass));
|
||||
final String name = aClass.getName();
|
||||
if (name != null) {
|
||||
buf.append(name);
|
||||
@@ -77,42 +78,24 @@ public class ClassUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getContainingClass(@NotNull PsiClass aClass) {
|
||||
PsiElement parent = aClass.getContext();
|
||||
while (parent != null && !(parent instanceof PsiClass)) {
|
||||
parent = parent.getContext();
|
||||
}
|
||||
return (PsiClass)parent;
|
||||
}
|
||||
|
||||
public static int getNonQualifiedClassIdx(@NotNull final PsiClass psiClass) {
|
||||
final int[] result = {-1};
|
||||
final PsiClass containingClass = getContainingClass(psiClass);
|
||||
if (containingClass != null) {
|
||||
containingClass.accept(new JavaRecursiveElementVisitor() {
|
||||
private int myCurrentIdx = 0;
|
||||
|
||||
private static int getNonQualifiedClassIdx(@NotNull final PsiClass psiClass, @NotNull final PsiClass containingClass) {
|
||||
TObjectIntHashMap<PsiClass> indices =
|
||||
CachedValuesManager.getCachedValue(containingClass, new CachedValueProvider<TObjectIntHashMap<PsiClass>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (result[0] == -1) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {
|
||||
super.visitClass(aClass);
|
||||
if (aClass.getQualifiedName() == null) {
|
||||
myCurrentIdx++;
|
||||
if (psiClass == aClass) {
|
||||
result[0] = myCurrentIdx;
|
||||
public Result<TObjectIntHashMap<PsiClass>> compute() {
|
||||
final TObjectIntHashMap<PsiClass> map = new TObjectIntHashMap<PsiClass>();
|
||||
int index = 0;
|
||||
for (PsiClass aClass : SyntaxTraverser.psiTraverser().withRoot(containingClass).postOrderDfsTraversal().filter(PsiClass.class)) {
|
||||
if (aClass.getQualifiedName() == null) {
|
||||
map.put(aClass, ++index);
|
||||
}
|
||||
}
|
||||
return Result.create(map, containingClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result[0];
|
||||
|
||||
return indices.get(psiClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
public class A {
|
||||
public void method() {
|
||||
if (true) { foo(); }
|
||||
<caret>
|
||||
el<caret>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
public class A {
|
||||
public void method() {
|
||||
if (true) { foo(); }
|
||||
else <caret>
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import java.util.List;
|
||||
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> bar() {
|
||||
return foo("");
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import java.util.List;
|
||||
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> bar() {
|
||||
return foo("", "");
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import java.util.List;
|
||||
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> bar() {
|
||||
return foo(new String[] {""});
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String bar() {
|
||||
return fo<caret>o("");
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String bar() {
|
||||
return fo<caret>o("", "");
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "false"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String bar() {
|
||||
return fo<caret>o();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Make 'bar' return 'java.util.List<java.lang.String>'" "true"
|
||||
public class Foo {
|
||||
<T> java.util.List<T> foo(T... t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String bar() {
|
||||
return fo<caret>o(new String[] {""});
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -76,8 +76,8 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
|
||||
public void testMethodScope3() throws Exception { doTest(1, "final", "public", "static", "volatile", "abstract", "throws", "instanceof"); }
|
||||
public void testMethodScope4() throws Exception { doTest(6, "final", "try", "for", "while", "return", "throw"); }
|
||||
public void testMethodScope5() throws Exception { doTest(false); }
|
||||
public void testElseAfterSemicolon() throws Exception { doTest(1, "else"); }
|
||||
public void testElseAfterRBrace() throws Exception { doTest(1, "else"); }
|
||||
public void testElseAfterSemicolon() { doTest(1, "else"); }
|
||||
public void testElseAfterRBrace() { doTest(false); }
|
||||
public void testExtraBracketAfterFinally1() throws Exception { doTest(false); }
|
||||
public void testExtraBracketAfterFinally2() throws Exception { doTest(false); }
|
||||
public void testExtendsInCastTypeParameters() throws Exception { doTest(false); }
|
||||
@@ -137,7 +137,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
|
||||
assertEquals("this", myItems[1].getLookupString());
|
||||
}
|
||||
|
||||
private void doTest(boolean select) throws Exception {
|
||||
private void doTest(boolean select) {
|
||||
configureByFile(BASE_PATH + "/" + getTestName(true) + ".java");
|
||||
if (select) {
|
||||
selectItem(myItems[0]);
|
||||
|
||||
+13
-13
@@ -1,12 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: Anna.Kozlova
|
||||
* Date: 06-Sep-2006
|
||||
* Time: 13:56:11
|
||||
* Copyright 2000-2015 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.daemon.quickFix;
|
||||
|
||||
@@ -27,11 +32,6 @@ public class JavadocInspectionQuickFixTest extends LightQuickFixParameterizedTes
|
||||
return new LocalInspectionTool[]{javaDocLocalInspection};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,8 +33,12 @@ public class UnscrambleDialogTest extends JavaCodeInsightFixtureTestCase {
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
Disposer.dispose(myContent);
|
||||
super.tearDown();
|
||||
try {
|
||||
Disposer.dispose(myContent);
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testStacktrace() throws Exception {
|
||||
@@ -47,9 +51,9 @@ public class UnscrambleDialogTest extends JavaCodeInsightFixtureTestCase {
|
||||
|
||||
public void testException() throws Exception {
|
||||
showText("java.lang.NullPointerException\n" +
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.findOneStyleSheet(XhtmlFileInfo.java:291)\n" +
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.getStylesheets(XhtmlFileInfo.java:174)\n" +
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.initStylesheets(XhtmlFileInfo.java:119)");
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.findOneStyleSheet(XhtmlFileInfo.java:291)\n" +
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.getStylesheets(XhtmlFileInfo.java:174)\n" +
|
||||
"\tat com.intellij.psi.css.resolve.impl.XhtmlFileInfo.initStylesheets(XhtmlFileInfo.java:119)");
|
||||
assertIcon("exception.png", myContent.getIcon());
|
||||
assertEquals("NPE", myContent.getDisplayName());
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
package com.intellij.testFramework;
|
||||
|
||||
import com.intellij.ide.highlighter.ModuleFileType;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.components.ComponentsPackage;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -160,15 +158,13 @@ public abstract class ModuleTestCase extends IdeaTestCase {
|
||||
}
|
||||
|
||||
protected final void loadModuleComponentState(@NotNull Module module, @NotNull Object component) {
|
||||
AccessToken token = WriteAction.start();
|
||||
ProjectEx project = (ProjectEx)myProject;
|
||||
project.setOptimiseTestLoadSpeed(false);
|
||||
try {
|
||||
ProjectEx project = (ProjectEx)myProject;
|
||||
project.setOptimiseTestLoadSpeed(false);
|
||||
ComponentsPackage.getStateStore(module).initComponent(component, false);
|
||||
project.setOptimiseTestLoadSpeed(true);
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
project.setOptimiseTestLoadSpeed(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<root>
|
||||
<item name='org.junit.runner.Description T getAnnotation(java.lang.Class<T>)'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -375,23 +375,28 @@ NSString *getOverrideVMOptionsPath() {
|
||||
}
|
||||
|
||||
NSArray *parseVMOptions() {
|
||||
NSArray *files = @[getApplicationVMOptionsPath(),
|
||||
getUserVMOptionsPath(),
|
||||
getOverrideVMOptionsPath()];
|
||||
NSString *vmOptionsFile = getApplicationVMOptionsPath();
|
||||
NSString *userVMOptiosFile = getUserVMOptionsPath();
|
||||
NSString *envVarVMOptiosFile = getOverrideVMOptionsPath();
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:userVMOptiosFile]) {
|
||||
vmOptionsFile = userVMOptiosFile;
|
||||
}
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:envVarVMOptiosFile]) {
|
||||
vmOptionsFile = envVarVMOptiosFile;
|
||||
}
|
||||
|
||||
NSMutableArray *options = [NSMutableArray array];
|
||||
NSMutableArray *used = [NSMutableArray array];
|
||||
|
||||
for (NSString *file in files) {
|
||||
NSLog(@"Processing VMOptions file at %@", file);
|
||||
NSArray *contents = [VMOptionsReader readFile:file];
|
||||
if (contents != nil) {
|
||||
NSLog(@"Done");
|
||||
[used addObject:file];
|
||||
[options addObjectsFromArray:contents];
|
||||
} else {
|
||||
NSLog(@"No content found");
|
||||
}
|
||||
NSLog(@"Processing VMOptions file at %@", vmOptionsFile);
|
||||
NSArray *contents = [VMOptionsReader readFile:vmOptionsFile];
|
||||
if (contents != nil) {
|
||||
NSLog(@"Done");
|
||||
[used addObject:vmOptionsFile];
|
||||
[options addObjectsFromArray:contents];
|
||||
} else {
|
||||
NSLog(@"No content found at %@ ", vmOptionsFile);
|
||||
}
|
||||
[options addObject:[NSString stringWithFormat:@"-Djb.vmOptionsFile=%@", [used componentsJoinedByString:@","]]];
|
||||
|
||||
|
||||
@@ -374,6 +374,29 @@ bool AddClassPathOptions(std::vector<std::string>& vmOptionLines)
|
||||
return true;
|
||||
}
|
||||
|
||||
//return VMOptions from one of the files in the following order:
|
||||
//$<IDE-NAME>_VM_OPTIONS
|
||||
//$CONFIG_DIRECTORY/<ide-name>[64][.exe].vmoptions
|
||||
//bin/<ide-name>[64][.exe].vmoptions
|
||||
bool FindValidVMOptions(std::vector<std::wstring> files, std::wstring& used, std::vector<std::string>& vmOptionLines)
|
||||
{
|
||||
if (files.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < files.size(); i++)
|
||||
{
|
||||
if (GetFileAttributes(files[i].c_str()) != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
if (LoadVMOptionsFile(files[i].c_str(), vmOptionLines))
|
||||
{
|
||||
used += (used.size() ? L"," : L"") + files[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddPredefinedVMOptions(std::vector<std::string>& vmOptionLines)
|
||||
{
|
||||
std::string vmOptions = LoadStdString(IDS_VM_OPTIONS);
|
||||
@@ -403,8 +426,13 @@ bool LoadVMOptions()
|
||||
GetModuleFileName(NULL, buffer, _MAX_PATH);
|
||||
std::wstring module(buffer);
|
||||
|
||||
files.push_back(module + L".vmoptions");
|
||||
|
||||
if (LoadString(hInst, IDS_VM_OPTIONS_ENV_VAR, buffer, _MAX_PATH))
|
||||
{
|
||||
if (GetEnvironmentVariableW(buffer, copy, _MAX_PATH)) {
|
||||
ExpandEnvironmentStrings(copy, buffer, _MAX_PATH);
|
||||
files.push_back(std::wstring(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
if (LoadString(hInst, IDS_VM_OPTIONS_PATH, buffer, _MAX_PATH))
|
||||
{
|
||||
@@ -413,33 +441,17 @@ bool LoadVMOptions()
|
||||
files.push_back(selector + module.substr(module.find_last_of('\\')) + L".vmoptions");
|
||||
}
|
||||
|
||||
if (LoadString(hInst, IDS_VM_OPTIONS_ENV_VAR, buffer, _MAX_PATH))
|
||||
{
|
||||
if (GetEnvironmentVariableW(buffer, copy, _MAX_PATH)) {
|
||||
ExpandEnvironmentStrings(copy, buffer, _MAX_PATH);
|
||||
files.push_back(std::wstring(buffer));
|
||||
}
|
||||
}
|
||||
files.push_back(module + L".vmoptions");
|
||||
std::wstring used;
|
||||
std::vector<std::string> vmOptionLines;
|
||||
|
||||
if (files.size() == 0) {
|
||||
if (!FindValidVMOptions(files, used, vmOptionLines))
|
||||
{
|
||||
std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
|
||||
MessageBoxA(NULL, "Cannot find VM options file", error.c_str(), MB_OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring used;
|
||||
std::vector<std::string> vmOptionLines;
|
||||
for (int i = 0; i < files.size(); i++)
|
||||
{
|
||||
if (GetFileAttributes(files[i].c_str()) != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
if (LoadVMOptionsFile(files[i].c_str(), vmOptionLines))
|
||||
{
|
||||
used += (used.size() ? L"," : L"") + files[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vmOptionLines.push_back(std::string("-Djb.vmOptions=") + EncodeWideACP(used));
|
||||
|
||||
if (!AddClassPathOptions(vmOptionLines)) return false;
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract class RefEntityImpl implements RefEntity {
|
||||
|
||||
RefEntityImpl(@NotNull String name, @NotNull RefManager manager) {
|
||||
myManager = (RefManagerImpl)manager;
|
||||
myName = name;
|
||||
myName = myManager.internName(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.StringInterner;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -85,6 +86,7 @@ public class RefManagerImpl extends RefManager {
|
||||
|
||||
private final Map<Key, RefManagerExtension> myExtensions = new THashMap<Key, RefManagerExtension>();
|
||||
private final Map<Language, RefManagerExtension> myLanguageExtensions = new HashMap<Language, RefManagerExtension>();
|
||||
private final StringInterner myNameInterner = new StringInterner();
|
||||
|
||||
public RefManagerImpl(@NotNull Project project, @Nullable AnalysisScope scope, @NotNull GlobalInspectionContext context) {
|
||||
myProject = project;
|
||||
@@ -106,6 +108,12 @@ public class RefManagerImpl extends RefManager {
|
||||
}
|
||||
}
|
||||
|
||||
public String internName(@NotNull String name) {
|
||||
synchronized (myNameInterner) {
|
||||
return myNameInterner.intern(name);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GlobalInspectionContext getContext() {
|
||||
return myContext;
|
||||
|
||||
@@ -76,6 +76,4 @@ class ApplicationStoreImpl(private val application: ApplicationImpl, pathMacroMa
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessageBus() = application.getMessageBus()
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.application.ex.DecodeDefaultsUtil
|
||||
import com.intellij.openapi.application.runBatchUpdate
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.components.StateStorage.SaveSession
|
||||
import com.intellij.openapi.components.StateStorageChooserEx.Resolution
|
||||
@@ -53,6 +54,9 @@ import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
private val LOG = Logger.getInstance(javaClass<ComponentStoreImpl>())
|
||||
|
||||
/**
|
||||
* <b>Note:</b> this class is used in upsource, please notify upsource team in case you change its API.
|
||||
*/
|
||||
public abstract class ComponentStoreImpl : IComponentStore {
|
||||
private val myComponents = Collections.synchronizedMap(THashMap<String, Any>())
|
||||
private val mySettingsSavingComponents = CopyOnWriteArrayList<SettingsSavingComponent>()
|
||||
@@ -346,8 +350,10 @@ public abstract class ComponentStoreImpl : IComponentStore {
|
||||
return notReloadableComponents ?: emptySet<String>()
|
||||
}
|
||||
|
||||
override final fun reloadStates(componentNames: MutableSet<String>) {
|
||||
reinitComponents(componentNames)
|
||||
override final fun reloadStates(componentNames: MutableSet<String>, messageBus: MessageBus) {
|
||||
runBatchUpdate(messageBus) {
|
||||
reinitComponents(componentNames)
|
||||
}
|
||||
}
|
||||
|
||||
override final fun reloadState(componentClass: Class<out PersistentStateComponent<*>>) {
|
||||
@@ -370,8 +376,6 @@ public abstract class ComponentStoreImpl : IComponentStore {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getMessageBus(): MessageBus
|
||||
|
||||
/**
|
||||
* null if reloaded
|
||||
* empty list if nothing to reload
|
||||
@@ -404,19 +408,15 @@ public abstract class ComponentStoreImpl : IComponentStore {
|
||||
}
|
||||
|
||||
// used in settings repository plugin
|
||||
/**
|
||||
* You must call it in batch mode (use runBatchUpdate)
|
||||
*/
|
||||
public fun reinitComponents(componentNames: Set<String>, changedStorages: Set<StateStorage> = emptySet(), notReloadableComponents: Collection<String> = emptySet()) {
|
||||
val messageBus = getMessageBus()
|
||||
messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateStarted()
|
||||
try {
|
||||
for (componentName in componentNames) {
|
||||
if (!notReloadableComponents.contains(componentName)) {
|
||||
reloadState(componentName, changedStorages)
|
||||
}
|
||||
for (componentName in componentNames) {
|
||||
if (!notReloadableComponents.contains(componentName)) {
|
||||
reloadState(componentName, changedStorages)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -85,9 +85,7 @@ class DefaultProjectStoreImpl(override val project: ProjectImpl, private val pat
|
||||
|
||||
// don't want to optimize and use already loaded data - it will add unnecessary complexity and implementation-lock (currently we store loaded archived state in memory, but later implementation can be changed)
|
||||
fun getStateCopy() = storage.loadLocalData()
|
||||
|
||||
override fun getMessageBus() = project.getMessageBus()
|
||||
|
||||
|
||||
override final fun getPathMacroManagerForDefaults() = pathMacroManager
|
||||
|
||||
override fun selectDefaultStorages(storages: Array<Storage>, operation: StateStorageOperation) = selectDefaultStorages(storages, operation, StorageScheme.DEFAULT)
|
||||
|
||||
@@ -18,22 +18,18 @@ package com.intellij.configurationStore
|
||||
import com.intellij.openapi.components.PathMacroManager
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ex.ProjectEx
|
||||
|
||||
class ModuleStoreImpl(private val module: Module, private val pathMacroManager: PathMacroManager) : ComponentStoreImpl() {
|
||||
override val storageManager = ModuleStateStorageManager(pathMacroManager.createTrackingSubstitutor(), module)
|
||||
class ModuleStoreImpl(module: Module, private val pathMacroManager: PathMacroManager) : ComponentStoreImpl() {
|
||||
override val project = module.getProject()
|
||||
|
||||
override val project: Project?
|
||||
get() = module.getProject()
|
||||
override val storageManager = ModuleStateStorageManager(pathMacroManager.createTrackingSubstitutor(), module)
|
||||
|
||||
override fun setPath(path: String) {
|
||||
storageManager.addMacro(StoragePathMacros.MODULE_FILE, path)
|
||||
}
|
||||
|
||||
override fun optimizeTestLoading() = (module.getProject() as ProjectEx).isOptimiseTestLoadSpeed()
|
||||
|
||||
override fun getMessageBus() = module.getMessageBus()
|
||||
override fun optimizeTestLoading() = (project as ProjectEx).isOptimiseTestLoadSpeed()
|
||||
|
||||
override final fun getPathMacroManagerForDefaults() = pathMacroManager
|
||||
}
|
||||
@@ -247,8 +247,6 @@ open class ProjectStoreImpl(override val project: ProjectImpl, private val pathM
|
||||
protected open fun beforeSave(readonlyFiles: List<Pair<SaveSession, VirtualFile>>) {
|
||||
}
|
||||
|
||||
override fun getMessageBus() = project.getMessageBus()
|
||||
|
||||
override fun <T> getStorageSpecs(component: PersistentStateComponent<T>, stateSpec: State, operation: StateStorageOperation): Array<Storage> {
|
||||
// if we create project from default, component state written not to own storage file, but to project file,
|
||||
// we don't have time to fix it properly, so, ancient hack restored.
|
||||
|
||||
@@ -46,6 +46,8 @@ import kotlin.reflect.jvm.java
|
||||
|
||||
/**
|
||||
* If componentManager not specified, storage will not add file tracker (see VirtualFileTracker)
|
||||
* <p/>
|
||||
* <b>Note:</b> this class is used in upsource, please notify upsource team in case you change its API.
|
||||
*/
|
||||
open class StateStorageManagerImpl(private val rootTagName: String,
|
||||
private val pathMacroSubstitutor: TrackingPathMacroSubstitutor? = null,
|
||||
|
||||
@@ -18,8 +18,8 @@ package com.intellij.configurationStore
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||
import com.intellij.openapi.application.runBatchUpdate
|
||||
import com.intellij.openapi.components.ComponentManager
|
||||
import com.intellij.openapi.components.StateStorage
|
||||
import com.intellij.openapi.components.impl.stores.*
|
||||
@@ -65,10 +65,12 @@ class StoreAwareProjectManager(virtualFileManager: VirtualFileManager, progressM
|
||||
val changes = CHANGED_FILES_KEY.get(project) ?: continue
|
||||
CHANGED_FILES_KEY.set(project, null)
|
||||
if (!changes.isEmpty()) {
|
||||
for ((store, storages) in changes.entrySet()) {
|
||||
@suppress("UNCHECKED_CAST")
|
||||
if (reloadStore(storages as Set<StateStorage>, store, false) == ReloadComponentStoreStatus.RESTART_AGREED) {
|
||||
projectsToReload.add(project)
|
||||
runBatchUpdate(project.getMessageBus()) {
|
||||
for ((store, storages) in changes.entrySet()) {
|
||||
@suppress("UNCHECKED_CAST")
|
||||
if (reloadStore(storages as Set<StateStorage>, store, false) == ReloadComponentStoreStatus.RESTART_AGREED) {
|
||||
projectsToReload.add(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,7 +218,6 @@ private fun reloadStore(changedStorages: Set<StateStorage>, store: ComponentStor
|
||||
val notReloadableComponents: Collection<String>?
|
||||
var willBeReloaded = false
|
||||
try {
|
||||
val token = WriteAction.start()
|
||||
try {
|
||||
notReloadableComponents = store.reload(changedStorages)
|
||||
}
|
||||
@@ -225,9 +226,6 @@ private fun reloadStore(changedStorages: Set<StateStorage>, store: ComponentStor
|
||||
Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title"))
|
||||
return ReloadComponentStoreStatus.ERROR
|
||||
}
|
||||
finally {
|
||||
token.finish()
|
||||
}
|
||||
|
||||
if (notReloadableComponents == null || notReloadableComponents.isEmpty()) {
|
||||
return ReloadComponentStoreStatus.SUCCESS
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.application.options.PathMacrosImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.components.impl.stores.StreamProvider
|
||||
@@ -149,8 +148,6 @@ class ApplicationStoreTest {
|
||||
override fun setPath(path: String) {
|
||||
storageManager.addMacro(StoragePathMacros.APP_CONFIG, path)
|
||||
}
|
||||
|
||||
override fun getMessageBus() = ApplicationManager.getApplication().getMessageBus()
|
||||
}
|
||||
|
||||
abstract class Foo {
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.components.impl.stores.BatchUpdateListener
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleTypeId
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ex.ProjectEx
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.impl.storage.ClasspathStorage
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.testFramework.*
|
||||
import gnu.trove.TObjectIntHashMap
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
@RunsInEdt class ModuleStoreTest {
|
||||
@RunsInEdt
|
||||
@RunsInActiveStoreMode
|
||||
class ModuleStoreTest {
|
||||
companion object {
|
||||
ClassRule val projectRule = ProjectRule()
|
||||
|
||||
@@ -39,7 +46,7 @@ import java.nio.file.Path
|
||||
|
||||
private val tempDirManager = TemporaryDirectory()
|
||||
|
||||
private val ruleChain = RuleChain(tempDirManager, EdtRule())
|
||||
private val ruleChain = RuleChain(tempDirManager, EdtRule(), ActiveStoreRule(projectRule), DisposeModulesRule(projectRule))
|
||||
|
||||
public Rule fun getChain(): RuleChain = ruleChain
|
||||
|
||||
@@ -49,53 +56,107 @@ import java.nio.file.Path
|
||||
"<module type=\"JAVA_MODULE\" foo=\"bar\" version=\"4\" />")
|
||||
}
|
||||
|
||||
projectRule.project.runInStoreLoadMode {
|
||||
moduleFile.loadModule().useAndDispose {
|
||||
assertThat(getOptionValue("foo")).isEqualTo("bar")
|
||||
moduleFile.loadModule().useAndDispose {
|
||||
assertThat(getOptionValue("foo")).isEqualTo("bar")
|
||||
|
||||
setOption("foo", "not bar")
|
||||
saveStore()
|
||||
}
|
||||
setOption("foo", "not bar")
|
||||
saveStore()
|
||||
}
|
||||
|
||||
moduleFile.loadModule().useAndDispose {
|
||||
assertThat(getOptionValue("foo")).isEqualTo("not bar")
|
||||
moduleFile.loadModule().useAndDispose {
|
||||
assertThat(getOptionValue("foo")).isEqualTo("not bar")
|
||||
|
||||
setOption("foo", "not bar")
|
||||
saveStore()
|
||||
}
|
||||
setOption("foo", "not bar")
|
||||
saveStore()
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `must be empty if classpath storage`() {
|
||||
// we must not use VFS here, file must not be created
|
||||
val moduleFile = tempDirManager.newPath("module").resolve("test.iml")
|
||||
projectRule.project.runInStoreLoadMode {
|
||||
moduleFile.createModule().useAndDispose {
|
||||
ModuleRootModificationUtil.addContentRoot(this, moduleFile.parentSystemIndependentPath)
|
||||
saveStore()
|
||||
assertThat(moduleFile).isRegularFile()
|
||||
assertThat(moduleFile.readText()).startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">")
|
||||
moduleFile.createModule().useAndDispose {
|
||||
ModuleRootModificationUtil.addContentRoot(this, moduleFile.parentSystemIndependentPath)
|
||||
saveStore()
|
||||
assertThat(moduleFile).isRegularFile()
|
||||
assertThat(moduleFile.readText()).startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">")
|
||||
|
||||
ClasspathStorage.setStorageType(ModuleRootManager.getInstance(this), "eclipse")
|
||||
saveStore()
|
||||
assertThat(moduleFile).hasContent("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
ClasspathStorage.setStorageType(ModuleRootManager.getInstance(this), "eclipse")
|
||||
saveStore()
|
||||
assertThat(moduleFile).hasContent("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="eclipse" classpath-dir="$MODULE_DIR" type="JAVA_MODULE" version="4" />""")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `one batch update session if several modules changed`() {
|
||||
val nameToCount = TObjectIntHashMap<String>()
|
||||
val root = tempDirManager.newPath()
|
||||
|
||||
fun Module.addContentRoot() {
|
||||
val moduleName = getName()
|
||||
var batchUpdateCount = 0
|
||||
nameToCount.put(moduleName, batchUpdateCount)
|
||||
|
||||
getMessageBus().connect().subscribe(BatchUpdateListener.TOPIC, object : BatchUpdateListener {
|
||||
override fun onBatchUpdateStarted() {
|
||||
nameToCount.put(moduleName, ++batchUpdateCount)
|
||||
}
|
||||
|
||||
override fun onBatchUpdateFinished() {
|
||||
}
|
||||
})
|
||||
|
||||
//
|
||||
ModuleRootModificationUtil.addContentRoot(this, root.resolve(moduleName).systemIndependentPath)
|
||||
assertThat(contentRootUrls).hasSize(1)
|
||||
saveStore()
|
||||
}
|
||||
|
||||
fun Module.removeContentRoot() {
|
||||
val modulePath = stateStore.getStateStorageManager().expandMacros(StoragePathMacros.MODULE_FILE)
|
||||
val moduleFile = Paths.get(modulePath)
|
||||
assertThat(moduleFile).isRegularFile()
|
||||
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByPath(modulePath)!!
|
||||
val newData = moduleFile.readText().replace("<content url=\"file://\$MODULE_DIR$/${getName()}\" />\n", "").toByteArray()
|
||||
runWriteAction {
|
||||
virtualFile.setBinaryContent(newData)
|
||||
}
|
||||
}
|
||||
|
||||
fun Module.assertChangesApplied() {
|
||||
assertThat(contentRootUrls).isEmpty()
|
||||
}
|
||||
|
||||
val m1 = root.resolve("m1.iml").createModule()
|
||||
val m2 = root.resolve("m2.iml").createModule()
|
||||
|
||||
var projectBatchUpdateCount = 0
|
||||
projectRule.project.getMessageBus().connect(m1).subscribe(BatchUpdateListener.TOPIC, object : BatchUpdateListener {
|
||||
override fun onBatchUpdateStarted() {
|
||||
nameToCount.put("p", ++projectBatchUpdateCount)
|
||||
}
|
||||
|
||||
override fun onBatchUpdateFinished() {
|
||||
}
|
||||
})
|
||||
|
||||
m1.addContentRoot()
|
||||
m2.addContentRoot()
|
||||
|
||||
m1.removeContentRoot()
|
||||
m2.removeContentRoot()
|
||||
|
||||
(ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedAlarm()
|
||||
|
||||
m1.assertChangesApplied()
|
||||
m2.assertChangesApplied()
|
||||
|
||||
assertThat(nameToCount.size()).isEqualTo(3)
|
||||
assertThat(nameToCount.get("p")).isEqualTo(1)
|
||||
assertThat(nameToCount.get("m1")).isEqualTo(1)
|
||||
assertThat(nameToCount.get("m1")).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Project.runInStoreLoadMode(task: () -> T): T {
|
||||
val isModeDisabled = (this as ProjectEx).isOptimiseTestLoadSpeed()
|
||||
if (isModeDisabled) {
|
||||
setOptimiseTestLoadSpeed(false)
|
||||
}
|
||||
try {
|
||||
return task()
|
||||
}
|
||||
finally {
|
||||
if (isModeDisabled) {
|
||||
setOptimiseTestLoadSpeed(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
val Module.contentRootUrls: Array<String>
|
||||
get() = ModuleRootManager.getInstance(this).getContentRootUrls()
|
||||
@@ -486,23 +486,48 @@ public class PluginManagerCore {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<URL> getClassLoaderUrls() {
|
||||
Collection<URL> urls = null;
|
||||
final ClassLoader classLoader = PluginManagerCore.class.getClassLoader();
|
||||
final Class<? extends ClassLoader> aClass = classLoader.getClass();
|
||||
try {
|
||||
@SuppressWarnings("unchecked") List<URL> urls = (List<URL>)aClass.getMethod("getUrls").invoke(classLoader);
|
||||
return urls;
|
||||
//noinspection unchecked
|
||||
urls = (List<URL>)aClass.getMethod("getUrls").invoke(classLoader);
|
||||
}
|
||||
catch (IllegalAccessException ignored) { }
|
||||
catch (InvocationTargetException ignored) { }
|
||||
catch (NoSuchMethodException ignored) { }
|
||||
|
||||
if (classLoader instanceof URLClassLoader) {
|
||||
return Arrays.asList(((URLClassLoader)classLoader).getURLs());
|
||||
if (urls == null && classLoader instanceof URLClassLoader) {
|
||||
urls = Arrays.asList(((URLClassLoader)classLoader).getURLs());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
return urls == null ? Collections.<URL>emptyList() : getClassLoaderUrls(urls);
|
||||
}
|
||||
|
||||
public static Collection<URL> getClassLoaderUrls(Collection<URL> urls) {
|
||||
List<URL> additionalRefs = new ArrayList<URL>();
|
||||
for (URL url : urls) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
File file = new File(decodeUrl(url.getFile()));
|
||||
final String[] referencedJars = ClassPath.loadManifestClasspath(file);
|
||||
if (referencedJars != null) {
|
||||
for (String referencedJar : referencedJars) {
|
||||
try {
|
||||
additionalRefs.add(new URL(referencedJar));
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!additionalRefs.isEmpty()) {
|
||||
Collection<URL> result = new ArrayList<URL>(urls);
|
||||
result.addAll(additionalRefs);
|
||||
return result;
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private static void prepareLoadingPluginsErrorMessage(@NotNull List<String> errors) {
|
||||
@@ -907,21 +932,6 @@ public class PluginManagerCore {
|
||||
if (progress != null && !pluginDescriptor.getName().equals(SPECIAL_IDEA_PLUGIN)) {
|
||||
progress.showProgress("Plugin loaded: " + pluginDescriptor.getName(), PLUGINS_PROGRESS_MAX_VALUE * (float)i / urls.size());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final String[] referencedJars = ClassPath.loadManifestClasspath(file);
|
||||
if (referencedJars != null) {
|
||||
for (String referencedJar : referencedJars) {
|
||||
final File referencedFile;
|
||||
try {
|
||||
referencedFile = new File(new URI(referencedJar));
|
||||
}
|
||||
catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
loadDescriptorFromClassPath(referencedFile, result, platformPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.intellij.diff;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.contents.EmptyContent;
|
||||
import com.intellij.diff.contents.FileContent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
@@ -64,6 +65,9 @@ public abstract class DiffContentFactory {
|
||||
@Nullable
|
||||
public abstract DocumentContent createDocument(@Nullable Project project, @NotNull VirtualFile file);
|
||||
|
||||
@Nullable
|
||||
public abstract FileContent createFile(@Nullable Project project, @NotNull VirtualFile file);
|
||||
|
||||
@NotNull
|
||||
public abstract DiffContent createClipboardContent();
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
package com.intellij.diff;
|
||||
|
||||
import com.intellij.openapi.ui.WindowWrapper;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class DiffDialogHints {
|
||||
@@ -32,7 +34,9 @@ public class DiffDialogHints {
|
||||
//
|
||||
|
||||
@Nullable private final WindowWrapper.Mode myMode;
|
||||
@Nullable private final Component myParent;
|
||||
@Nullable private Component myParent;
|
||||
@Nullable private BooleanGetter myOkAction;
|
||||
@Nullable private BooleanGetter myCancelAction;
|
||||
|
||||
public DiffDialogHints(@Nullable WindowWrapper.Mode mode) {
|
||||
this(mode, null);
|
||||
@@ -43,6 +47,18 @@ public class DiffDialogHints {
|
||||
myParent = parent;
|
||||
}
|
||||
|
||||
public void setParent(@Nullable Component value) {
|
||||
myParent = value;
|
||||
}
|
||||
|
||||
public void setOkAction(@Nullable BooleanGetter value) {
|
||||
myOkAction = value;
|
||||
}
|
||||
|
||||
public void setCancelAction(@Nullable BooleanGetter value) {
|
||||
myCancelAction = value;
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
@@ -56,4 +72,14 @@ public class DiffDialogHints {
|
||||
public Component getParent() {
|
||||
return myParent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BooleanGetter getOkAction() {
|
||||
return myOkAction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BooleanGetter getCancelAction() {
|
||||
return myCancelAction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.diff;
|
||||
|
||||
import com.intellij.diff.chains.DiffRequestChain;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -47,4 +48,7 @@ public abstract class DiffManager {
|
||||
|
||||
@NotNull
|
||||
public abstract DiffRequestPanel createRequestPanel(@Nullable Project project, @NotNull Disposable parent, @Nullable Window window);
|
||||
|
||||
@CalledInAwt
|
||||
public abstract void showMerge(@Nullable Project project, @NotNull MergeRequest request);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.intellij.diff;
|
||||
|
||||
import com.intellij.diff.chains.DiffRequestChain;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.merge.MergeTool;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -45,6 +47,12 @@ public abstract class DiffManagerEx extends DiffManager {
|
||||
@CalledInAwt
|
||||
public abstract void showDiffBuiltin(@Nullable Project project, @NotNull DiffRequestChain requests, @NotNull DiffDialogHints hints);
|
||||
|
||||
@CalledInAwt
|
||||
public abstract void showMergeBuiltin(@Nullable Project project, @NotNull MergeRequest request);
|
||||
|
||||
@NotNull
|
||||
public abstract List<DiffTool> getDiffTools();
|
||||
|
||||
@NotNull
|
||||
public abstract List<MergeTool> getMergeTools();
|
||||
}
|
||||
|
||||
@@ -15,13 +15,21 @@
|
||||
*/
|
||||
package com.intellij.diff;
|
||||
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.diff.merge.TextMergeRequest;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* Use ProgressManager.executeProcessUnderProgress() to pass modality state if needed
|
||||
*/
|
||||
@@ -31,12 +39,19 @@ public abstract class DiffRequestFactory {
|
||||
return ServiceManager.getService(DiffRequestFactory.class);
|
||||
}
|
||||
|
||||
//
|
||||
// Diff
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public abstract ContentDiffRequest createFromFiles(@Nullable Project project, @NotNull VirtualFile file1, @NotNull VirtualFile file2);
|
||||
|
||||
@NotNull
|
||||
public abstract ContentDiffRequest createClipboardVsValue(@NotNull String value);
|
||||
|
||||
//
|
||||
// Titles
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public abstract String getContentTitle(@NotNull VirtualFile file);
|
||||
@@ -46,4 +61,63 @@ public abstract class DiffRequestFactory {
|
||||
|
||||
@NotNull
|
||||
public abstract String getTitle(@NotNull VirtualFile file);
|
||||
|
||||
//
|
||||
// Merge
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public abstract MergeRequest createMergeRequest(@Nullable Project project,
|
||||
@Nullable FileType fileType,
|
||||
@NotNull Document output,
|
||||
@NotNull List<String> textContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> titles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract MergeRequest createMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract TextMergeRequest createTextMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract MergeRequest createBinaryMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract MergeRequest createMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> contents,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract MergeRequest createMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> contents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
|
||||
@NotNull
|
||||
public abstract TextMergeRequest createTextMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> contents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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;
|
||||
|
||||
public class InvalidDiffRequestException extends Exception {
|
||||
public InvalidDiffRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidDiffRequestException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidDiffRequestException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,12 @@ public interface DiffRequestProducer {
|
||||
@NotNull
|
||||
String getName();
|
||||
|
||||
/*
|
||||
* Should be called either in EDT or without ReadLock.
|
||||
* Some implementors might need WriteLock, so usage of Application.invokeAndWait() is possible.
|
||||
*
|
||||
* Valid ModalityState should be passed with ProgressIndicator.getModalityState().
|
||||
*/
|
||||
@CalledInBackground
|
||||
@NotNull
|
||||
DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class MergeContext implements UserDataHolder {
|
||||
protected final UserDataHolderBase myUserDataHolder = new UserDataHolderBase();
|
||||
|
||||
@Nullable
|
||||
public abstract Project getProject();
|
||||
|
||||
public abstract boolean isFocused();
|
||||
|
||||
public abstract void requestFocus();
|
||||
|
||||
@CalledInAwt
|
||||
public abstract void finishMerge(@NotNull MergeResult result);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getUserData(@NotNull Key<T> key) {
|
||||
return myUserDataHolder.getUserData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
myUserDataHolder.putUserData(key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class MergeRequest implements UserDataHolder {
|
||||
protected final UserDataHolderBase myUserDataHolder = new UserDataHolderBase();
|
||||
|
||||
@Nullable
|
||||
public abstract String getTitle();
|
||||
|
||||
/*
|
||||
* Called on conflict resolve end.
|
||||
*/
|
||||
@CalledInAwt
|
||||
public abstract void applyResult(@NotNull MergeResult result);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getUserData(@NotNull Key<T> key) {
|
||||
return myUserDataHolder.getUserData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
myUserDataHolder.putUserData(key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
public enum MergeResult {CANCEL, LEFT, RIGHT, RESOLVED}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public interface MergeTool {
|
||||
ExtensionPointName<MergeTool> EP_NAME = ExtensionPointName.create("com.intellij.diff.merge.MergeTool");
|
||||
|
||||
@CalledInAwt
|
||||
@NotNull
|
||||
MergeViewer createComponent(@NotNull MergeContext context, @NotNull MergeRequest request);
|
||||
|
||||
boolean canShow(@NotNull MergeContext context, @NotNull MergeRequest request);
|
||||
|
||||
/*
|
||||
* Merge viewer should call MergeContext.finishMerge(MergeResult) when processing is over.
|
||||
*
|
||||
* MergeRequest.applyResult() will be performed by the caller, so it shouldn't be called by MergeViewer directly.
|
||||
*/
|
||||
interface MergeViewer extends Disposable {
|
||||
@NotNull
|
||||
JComponent getComponent();
|
||||
|
||||
@Nullable
|
||||
JComponent getPreferredFocusedComponent();
|
||||
|
||||
@Nullable
|
||||
Action getResolveAction(@NotNull MergeResult result);
|
||||
|
||||
@CalledInAwt
|
||||
ToolbarComponents init();
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
void dispose();
|
||||
}
|
||||
|
||||
class ToolbarComponents {
|
||||
@Nullable public List<AnAction> toolbarActions;
|
||||
@Nullable public JComponent statusPanel;
|
||||
@Nullable public BooleanGetter closeHandler; // return false if merge window should be prevented from closing and canceling resolve.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class TextMergeRequest extends ThreesideMergeRequest {
|
||||
@Override
|
||||
@NotNull
|
||||
public abstract List<DocumentContent> getContents();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public abstract DocumentContent getOutputContent();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ThreesideMergeRequest extends MergeRequest {
|
||||
/*
|
||||
* 3 contents: left - middle - right (local - base - server)
|
||||
*/
|
||||
@NotNull
|
||||
public abstract List<? extends DiffContent> getContents();
|
||||
|
||||
@NotNull
|
||||
public abstract DiffContent getOutputContent();
|
||||
|
||||
/**
|
||||
* @return contents names. Should have same length as {@link #getContents()}
|
||||
* Titles could be null.
|
||||
*/
|
||||
@NotNull
|
||||
public abstract List<String> getContentTitles();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.requests;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public abstract class ComponentDiffRequest extends DiffRequest {
|
||||
@NotNull
|
||||
public abstract JComponent getComponent(@NotNull DiffContext context);
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public interface DiffUserDataKeys {
|
||||
@@ -52,6 +53,7 @@ public interface DiffUserDataKeys {
|
||||
// Both data from DiffContext / DiffRequest will be used. Data from DiffRequest will be used first.
|
||||
//
|
||||
|
||||
Key<List<JComponent>> NOTIFICATIONS = Key.create("Diff.Notifications");
|
||||
Key<List<AnAction>> CONTEXT_ACTIONS = Key.create("Diff.ContextActions");
|
||||
Key<DataProvider> DATA_PROVIDER = Key.create("Diff.DataProvider");
|
||||
Key<Boolean> GO_TO_SOURCE_DISABLE = Key.create("Diff.GoToSourceDisable");
|
||||
|
||||
@@ -34,6 +34,13 @@ public enum Side {
|
||||
myIndex = index;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Side fromIndex(int index) {
|
||||
if (index == 0) return LEFT;
|
||||
if (index == 1) return RIGHT;
|
||||
throw new IndexOutOfBoundsException("index: " + index);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Side fromLeft(boolean isLeft) {
|
||||
return isLeft ? LEFT : RIGHT;
|
||||
|
||||
@@ -32,6 +32,14 @@ public enum ThreeSide {
|
||||
myIndex = index;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ThreeSide fromIndex(int index) {
|
||||
if (index == 0) return LEFT;
|
||||
if (index == 1) return BASE;
|
||||
if (index == 2) return RIGHT;
|
||||
throw new IndexOutOfBoundsException("index: " + index);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return myIndex;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<content url="file://$MODULE_DIR$/src">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="core-api" />
|
||||
<orderEntry type="module" module-name="diff-api" exported="" />
|
||||
<orderEntry type="module" module-name="testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="platform-api" />
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="vcs-api-core" />
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/tests">
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="core-api" />
|
||||
<orderEntry type="module" module-name="diff-api" exported="" />
|
||||
<orderEntry type="module" module-name="testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="platform-api" />
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="vcs-api-core" />
|
||||
<orderEntry type="module" module-name="diff-impl" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -17,7 +17,6 @@ package com.intellij.diff;
|
||||
|
||||
import com.intellij.diff.contents.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
@@ -119,6 +118,13 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
return new FileDocumentContentImpl(project, document, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FileContent createFile(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
if (file.isDirectory()) return null;
|
||||
return (FileContent)create(project, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DiffContent createClipboardContent() {
|
||||
|
||||
@@ -18,8 +18,17 @@ package com.intellij.diff;
|
||||
public abstract class DiffContextEx extends DiffContext {
|
||||
/*
|
||||
* Reopen current DiffRequest.
|
||||
*
|
||||
* perform the same procedure as on switching between DiffRequests or between DiffViewers.
|
||||
* this can be used, if some change in request or settings was made, and we need to reopen DiffViewer to apply them.
|
||||
*/
|
||||
public abstract void reopenDiffRequest();
|
||||
|
||||
/*
|
||||
* Drop cached DiffRequest version (if any) and reopen current DiffRequest.
|
||||
*
|
||||
* perform the same procedure as on opening DiffRequests for the first time.
|
||||
* this can be used, if some change in request or settings was made, and we need to reload DiffRequest to apply them.
|
||||
*/
|
||||
public abstract void reloadDiffRequest();
|
||||
}
|
||||
|
||||
@@ -19,15 +19,18 @@ import com.intellij.diff.chains.DiffRequestChain;
|
||||
import com.intellij.diff.chains.SimpleDiffRequestChain;
|
||||
import com.intellij.diff.impl.DiffRequestPanelImpl;
|
||||
import com.intellij.diff.impl.DiffWindow;
|
||||
import com.intellij.diff.merge.*;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.tools.binary.BinaryDiffTool;
|
||||
import com.intellij.diff.tools.dir.DirDiffTool;
|
||||
import com.intellij.diff.tools.external.ExternalDiffTool;
|
||||
import com.intellij.diff.tools.external.ExternalMergeTool;
|
||||
import com.intellij.diff.tools.fragmented.UnifiedDiffTool;
|
||||
import com.intellij.diff.tools.simple.SimpleDiffTool;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -93,4 +96,29 @@ public class DiffManagerImpl extends DiffManagerEx {
|
||||
Collections.addAll(result, DiffTool.EP_NAME.getExtensions());
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<MergeTool> getMergeTools() {
|
||||
List<MergeTool> result = new ArrayList<MergeTool>();
|
||||
Collections.addAll(result, MergeTool.EP_NAME.getExtensions());
|
||||
result.add(TextMergeTool.INSTANCE);
|
||||
result.add(BinaryMergeTool.INSTANCE);
|
||||
return result;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void showMerge(@Nullable Project project, @NotNull MergeRequest request) {
|
||||
if (ExternalMergeTool.isDefault()) {
|
||||
ExternalMergeTool.show(project, request);
|
||||
return;
|
||||
}
|
||||
|
||||
showMergeBuiltin(project, request);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void showMergeBuiltin(@Nullable Project project, @NotNull MergeRequest request) {
|
||||
new MergeWindow(project, request).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,40 @@
|
||||
package com.intellij.diff;
|
||||
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.contents.FileAwareDocumentContent;
|
||||
import com.intellij.diff.contents.FileContent;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.diff.merge.TextMergeRequest;
|
||||
import com.intellij.diff.requests.BinaryMergeRequestImpl;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.requests.SimpleDiffRequest;
|
||||
import com.intellij.diff.requests.TextMergeRequestImpl;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DiffRequestFactoryImpl extends DiffRequestFactory {
|
||||
private DiffContentFactory myContentFactory = DiffContentFactory.getInstance();
|
||||
private final DiffContentFactory myContentFactory = DiffContentFactory.getInstance();
|
||||
|
||||
//
|
||||
// Diff
|
||||
//
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@@ -57,6 +79,10 @@ public class DiffRequestFactoryImpl extends DiffRequestFactory {
|
||||
return new SimpleDiffRequest(title, content1, content2, title1, title2);
|
||||
}
|
||||
|
||||
//
|
||||
// Titles
|
||||
//
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getContentTitle(@NotNull VirtualFile file) {
|
||||
@@ -147,4 +173,195 @@ public class DiffRequestFactoryImpl extends DiffRequestFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Merge
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public MergeRequest createMergeRequest(@Nullable Project project,
|
||||
@Nullable FileType fileType,
|
||||
@NotNull Document outputDocument,
|
||||
@NotNull List<String> textContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> titles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (textContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (titles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
if (!DiffUtil.canMakeWritable(outputDocument)) throw new InvalidDiffRequestException("Output is read only");
|
||||
|
||||
DocumentContent outputContent = myContentFactory.create(project, outputDocument, fileType);
|
||||
CharSequence originalContent = outputDocument.getImmutableCharSequence();
|
||||
|
||||
List<DocumentContent> contents = new ArrayList<DocumentContent>(3);
|
||||
for (String text : textContents) {
|
||||
contents.add(myContentFactory.create(text, fileType));
|
||||
}
|
||||
|
||||
return new TextMergeRequestImpl(project, outputContent, originalContent, contents, title, titles, applyCallback);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeRequest createMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (byteContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
try {
|
||||
return createTextMergeRequest(project, output, byteContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
catch (InvalidDiffRequestException e) {
|
||||
return createBinaryMergeRequest(project, output, byteContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TextMergeRequest createTextMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (byteContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
final Document outputDocument = FileDocumentManager.getInstance().getDocument(output);
|
||||
if (outputDocument == null) throw new InvalidDiffRequestException("Can't get output document: " + output.getPresentableUrl());
|
||||
if (!DiffUtil.canMakeWritable(outputDocument)) throw new InvalidDiffRequestException("Output is read only: " + output.getPresentableUrl());
|
||||
|
||||
DocumentContent outputContent = myContentFactory.create(project, outputDocument);
|
||||
CharSequence originalContent = outputDocument.getImmutableCharSequence();
|
||||
|
||||
List<DocumentContent> contents = new ArrayList<DocumentContent>(3);
|
||||
for (byte[] bytes : byteContents) {
|
||||
contents.add(FileAwareDocumentContent.create(project, bytes, output));
|
||||
}
|
||||
|
||||
return new TextMergeRequestImpl(project, outputContent, originalContent, contents, title, contentTitles, applyCallback);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeRequest createBinaryMergeRequest(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (byteContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
try {
|
||||
FileContent outputContent = myContentFactory.createFile(project, output);
|
||||
if (outputContent == null) throw new InvalidDiffRequestException("Can't create output content: " + output);
|
||||
byte[] originalContent = output.contentsToByteArray();
|
||||
|
||||
List<DiffContent> contents = new ArrayList<DiffContent>(3);
|
||||
for (byte[] bytes : byteContents) {
|
||||
contents.add(myContentFactory.createFromBytes(project, output, bytes));
|
||||
}
|
||||
|
||||
return new BinaryMergeRequestImpl(outputContent, originalContent, contents, byteContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new InvalidDiffRequestException("Can't read from file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeRequest createMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> fileContents,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
String title = "Merge " + output.getPresentableUrl();
|
||||
List<String> titles = ContainerUtil.list("Your Version", "Base Version", "Their Version");
|
||||
return createMergeRequestFromFiles(project, output, fileContents, title, titles, applyCallback);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeRequest createMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> fileContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (fileContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
try {
|
||||
return createTextMergeRequestFromFiles(project, output, fileContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
catch (InvalidDiffRequestException e) {
|
||||
return createBinaryMergeRequestFromFiles(project, output, fileContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TextMergeRequest createTextMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> fileContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (fileContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
final Document outputDocument = FileDocumentManager.getInstance().getDocument(output);
|
||||
if (outputDocument == null) throw new InvalidDiffRequestException("Can't get output document: " + output.getPresentableUrl());
|
||||
if (!DiffUtil.canMakeWritable(outputDocument)) throw new InvalidDiffRequestException("Output is read only: " + output.getPresentableUrl());
|
||||
|
||||
DocumentContent outputContent = myContentFactory.create(project, outputDocument);
|
||||
CharSequence originalContent = outputDocument.getImmutableCharSequence();
|
||||
|
||||
List<DocumentContent> contents = new ArrayList<DocumentContent>(3);
|
||||
for (VirtualFile file : fileContents) {
|
||||
DocumentContent document = myContentFactory.createDocument(project, file);
|
||||
if (document == null) throw new InvalidDiffRequestException("Can't get text content: " + file.getPresentableUrl());
|
||||
contents.add(document);
|
||||
}
|
||||
|
||||
return new TextMergeRequestImpl(project, outputContent, originalContent, contents, title, contentTitles, applyCallback);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MergeRequest createBinaryMergeRequestFromFiles(@Nullable Project project,
|
||||
@NotNull VirtualFile output,
|
||||
@NotNull List<VirtualFile> fileContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) throws InvalidDiffRequestException {
|
||||
if (fileContents.size() != 3) throw new IllegalArgumentException();
|
||||
if (contentTitles.size() != 3) throw new IllegalArgumentException();
|
||||
|
||||
|
||||
try {
|
||||
FileContent outputContent = myContentFactory.createFile(project, output);
|
||||
if (outputContent == null) throw new InvalidDiffRequestException("Can't create output content: " + output.getPresentableUrl());
|
||||
byte[] originalContent = output.contentsToByteArray();
|
||||
|
||||
List<DiffContent> contents = new ArrayList<DiffContent>(3);
|
||||
List<byte[]> byteContents = new ArrayList<byte[]>(3);
|
||||
for (VirtualFile file : fileContents) {
|
||||
FileContent content = myContentFactory.createFile(project, file);
|
||||
if (content == null) throw new InvalidDiffRequestException("Can't create content: " + file.getPresentableUrl());
|
||||
contents.add(content);
|
||||
byteContents.add(file.contentsToByteArray()); // TODO: we can read contents from file when needed
|
||||
}
|
||||
|
||||
return new BinaryMergeRequestImpl(outputContent, originalContent, contents, byteContents, title, contentTitles, applyCallback);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new InvalidDiffRequestException("Can't read from file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.diff.tools.util.base.TextDiffSettingsHolder;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -39,6 +40,10 @@ public class SetEditorSettingsAction extends ActionGroup implements DumbAware {
|
||||
myTextSettings = settings;
|
||||
myEditors = editors;
|
||||
|
||||
for (Editor editor : myEditors) {
|
||||
((EditorGutterComponentEx)editor.getGutter()).setGutterPopupGroup(this);
|
||||
}
|
||||
|
||||
myActions = new EditorSettingToggleAction[]{
|
||||
new EditorSettingToggleAction("EditorToggleShowWhitespaces") {
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ApplicationStarterEx;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
@@ -125,4 +126,9 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx {
|
||||
public boolean canProcessExternalCommandLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Project getProject() {
|
||||
return null; // TODO: try to guess project
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.diff.DiffRequestFactory;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.project.DefaultProjectFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -47,8 +46,6 @@ public class DiffApplication extends ApplicationStarterBase {
|
||||
}
|
||||
|
||||
public void processCommand(@NotNull String[] args, @Nullable String currentDirectory) throws Exception {
|
||||
// TODO: try to guess 'right' project ?
|
||||
|
||||
final String path1 = args[1];
|
||||
final String path2 = args[2];
|
||||
|
||||
@@ -59,9 +56,10 @@ public class DiffApplication extends ApplicationStarterBase {
|
||||
if (file2 == null) throw new Exception("Can't find file " + path2);
|
||||
|
||||
VfsUtil.markDirtyAndRefresh(false, false, false, file1, file2);
|
||||
DiffRequest request = DiffRequestFactory.getInstance().createFromFiles(null, file1, file2);
|
||||
|
||||
Project project = DefaultProjectFactory.getInstance().getDefaultProject();
|
||||
Project project = getProject();
|
||||
|
||||
DiffRequest request = DiffRequestFactory.getInstance().createFromFiles(project, file1, file2);
|
||||
DiffManagerEx.getInstance().showDiffBuiltin(project, request, DiffDialogHints.MODAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.applications;
|
||||
|
||||
import com.intellij.diff.DiffManagerEx;
|
||||
import com.intellij.diff.DiffRequestFactory;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"})
|
||||
public class MergeApplication extends ApplicationStarterBase {
|
||||
@Override
|
||||
protected boolean checkArguments(@NotNull String[] args) {
|
||||
return (args.length == 4 || args.length == 5) && "merge".equals(args[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "merge";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getUsageMessage() {
|
||||
final String scriptName = ApplicationNamesInfo.getInstance().getScriptName();
|
||||
return DiffBundle.message("merge.application.usage.parameters.and.description", scriptName);
|
||||
}
|
||||
|
||||
public void processCommand(@NotNull String[] args, @Nullable String currentDirectory) throws Exception {
|
||||
// TODO: try to guess 'right' project ?
|
||||
|
||||
final String path1 = args[1];
|
||||
final String path2 = args[2];
|
||||
final String path3 = args[3];
|
||||
final String path4 = args.length == 5 ? args[4] : args[3];
|
||||
|
||||
final VirtualFile file1 = findFile(path1, currentDirectory);
|
||||
final VirtualFile file2 = findFile(path2, currentDirectory);
|
||||
final VirtualFile file3 = findFile(path3, currentDirectory);
|
||||
final VirtualFile file4 = findFile(path4, currentDirectory);
|
||||
|
||||
if (file1 == null) throw new Exception("Can't find file " + path1);
|
||||
if (file2 == null) throw new Exception("Can't find file " + path2);
|
||||
if (file3 == null) throw new Exception("Can't find file " + path3);
|
||||
if (file4 == null) throw new Exception("Can't find file " + path4);
|
||||
|
||||
file1.refresh(false, true);
|
||||
file2.refresh(false, true);
|
||||
file3.refresh(false, true);
|
||||
file4.refresh(false, true);
|
||||
|
||||
Project project = getProject();
|
||||
|
||||
List<VirtualFile> contents = ContainerUtil.list(file1, file3, file2); // left, base, right
|
||||
MergeRequest request = DiffRequestFactory.getInstance().createMergeRequestFromFiles(project, file4, contents, null);
|
||||
|
||||
DiffManagerEx.getInstance().showMergeBuiltin(project, request);
|
||||
|
||||
Document document = FileDocumentManager.getInstance().getCachedDocument(file4);
|
||||
if (document != null) FileDocumentManager.getInstance().saveDocument(document);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressWindow;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -64,8 +63,18 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso
|
||||
// Update
|
||||
//
|
||||
|
||||
@Override
|
||||
protected void reloadRequest() {
|
||||
updateRequest(true, false, null);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void updateRequest(final boolean force, @Nullable final ScrollToPolicy scrollToChangePolicy) {
|
||||
updateRequest(force, true, scrollToChangePolicy);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void updateRequest(final boolean force, boolean useCache, @Nullable final ScrollToPolicy scrollToChangePolicy) {
|
||||
List<? extends DiffRequestProducer> requests = myRequestChain.getRequests();
|
||||
int index = myRequestChain.getIndex();
|
||||
if (index < 0 || index >= requests.size()) {
|
||||
@@ -75,7 +84,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso
|
||||
|
||||
final DiffRequestProducer producer = requests.get(index);
|
||||
|
||||
DiffRequest request = loadRequestFast(producer);
|
||||
DiffRequest request = loadRequestFast(producer, useCache);
|
||||
if (request != null) {
|
||||
applyRequest(request, force, scrollToChangePolicy);
|
||||
return;
|
||||
@@ -107,7 +116,8 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected DiffRequest loadRequestFast(@NotNull DiffRequestProducer producer) {
|
||||
protected DiffRequest loadRequestFast(@NotNull DiffRequestProducer producer, boolean useCache) {
|
||||
if (!useCache) return null;
|
||||
return myRequestCache.get(producer);
|
||||
}
|
||||
|
||||
@@ -126,6 +136,7 @@ public abstract class CacheDiffRequestChainProcessor extends DiffRequestProcesso
|
||||
return new ErrorDiffRequest(producer, e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn(e);
|
||||
return new ErrorDiffRequest(producer, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ import java.awt.*;
|
||||
|
||||
public class DiffRequestPanelImpl implements DiffRequestPanel {
|
||||
@NotNull private final JPanel myPanel;
|
||||
@NotNull private final MyCacheDiffRequestChainProcessor myProcessor;
|
||||
@NotNull private final MyDiffRequestProcessor myProcessor;
|
||||
|
||||
public DiffRequestPanelImpl(@Nullable Project project, @Nullable Window window) {
|
||||
myProcessor = new MyCacheDiffRequestChainProcessor(project, window);
|
||||
myProcessor = new MyDiffRequestProcessor(project, window);
|
||||
myProcessor.putContextUserData(DiffUserDataKeys.DO_NOT_CHANGE_WINDOW_TITLE, true);
|
||||
|
||||
myPanel = new JPanel(new BorderLayout()) {
|
||||
@@ -81,12 +81,12 @@ public class DiffRequestPanelImpl implements DiffRequestPanel {
|
||||
Disposer.dispose(myProcessor);
|
||||
}
|
||||
|
||||
private static class MyCacheDiffRequestChainProcessor extends DiffRequestProcessor {
|
||||
private static class MyDiffRequestProcessor extends DiffRequestProcessor {
|
||||
@Nullable private final Window myWindow;
|
||||
|
||||
@NotNull private DiffRequest myRequest = NoDiffRequest.INSTANCE;
|
||||
|
||||
public MyCacheDiffRequestChainProcessor(@Nullable Project project, @Nullable Window window) {
|
||||
public MyDiffRequestProcessor(@Nullable Project project, @Nullable Window window) {
|
||||
super(project);
|
||||
myWindow = window;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,11 @@ public abstract class DiffRequestProcessor implements Disposable {
|
||||
// Update
|
||||
//
|
||||
|
||||
@CalledInAwt
|
||||
protected void reloadRequest() {
|
||||
updateRequest(true);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void updateRequest() {
|
||||
updateRequest(false);
|
||||
@@ -445,13 +450,13 @@ public abstract class DiffRequestProcessor implements Disposable {
|
||||
|
||||
myToolbarPanel.setContent(toolbar.getComponent());
|
||||
for (AnAction action : group.getChildren(null)) {
|
||||
action.registerCustomShortcutSet(action.getShortcutSet(), myMainPanel);
|
||||
DiffUtil.registerAction(action, myMainPanel);
|
||||
}
|
||||
}
|
||||
|
||||
protected void buildActionPopup(@Nullable List<AnAction> viewerActions) {
|
||||
ShowActionGroupPopupAction action = new ShowActionGroupPopupAction();
|
||||
action.registerCustomShortcutSet(action.getShortcutSet(), myMainPanel);
|
||||
DiffUtil.registerAction(action, myMainPanel);
|
||||
|
||||
myPopupActionGroup = collectPopupActions(viewerActions);
|
||||
}
|
||||
@@ -902,10 +907,15 @@ public abstract class DiffRequestProcessor implements Disposable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDiffRequest() {
|
||||
public void reopenDiffRequest() {
|
||||
updateRequest(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDiffRequest() {
|
||||
reloadRequest();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Project getProject() {
|
||||
|
||||
@@ -63,6 +63,8 @@ public abstract class DiffWindowBase {
|
||||
myProcessor.requestFocus(); // TODO: not needed for modal dialogs. Make a flag in WindowWrapperBuilder ?
|
||||
}
|
||||
})
|
||||
.setOkAction(myHints.getOkAction())
|
||||
.setCancelAction(myHints.getCancelAction())
|
||||
.build();
|
||||
myWrapper.setImage(ImageLoader.loadFromResource("/diff/Diff.png"));
|
||||
Disposer.register(myWrapper, myProcessor);
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.FrameDiffTool;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.requests.SimpleDiffRequest;
|
||||
import com.intellij.diff.tools.binary.ThreesideBinaryDiffViewer;
|
||||
import com.intellij.diff.tools.holders.BinaryEditorHolder;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.List;
|
||||
|
||||
public class BinaryMergeTool implements MergeTool {
|
||||
public static final BinaryMergeTool INSTANCE = new BinaryMergeTool();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeViewer createComponent(@NotNull MergeContext context, @NotNull MergeRequest request) {
|
||||
return new BinaryMergeViewer(context, (ThreesideMergeRequest)request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow(@NotNull MergeContext context, @NotNull MergeRequest request) {
|
||||
if (!(request instanceof ThreesideMergeRequest)) return false;
|
||||
|
||||
MergeUtil.ProxyDiffContext diffContext = new MergeUtil.ProxyDiffContext(context);
|
||||
for (DiffContent diffContent : ((ThreesideMergeRequest)request).getContents()) {
|
||||
if (!BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE.canShowContent(diffContent, diffContext)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class BinaryMergeViewer implements MergeViewer {
|
||||
@NotNull private final MergeContext myMergeContext;
|
||||
@NotNull private final ThreesideMergeRequest myMergeRequest;
|
||||
|
||||
@NotNull private final DiffContext myDiffContext;
|
||||
@NotNull private final ContentDiffRequest myDiffRequest;
|
||||
|
||||
@NotNull private final MyThreesideViewer myViewer;
|
||||
|
||||
public BinaryMergeViewer(@NotNull MergeContext context, @NotNull ThreesideMergeRequest request) {
|
||||
myMergeContext = context;
|
||||
myMergeRequest = request;
|
||||
|
||||
myDiffContext = new MergeUtil.ProxyDiffContext(myMergeContext);
|
||||
myDiffRequest = new SimpleDiffRequest(myMergeRequest.getTitle(),
|
||||
getDiffContents(myMergeRequest),
|
||||
getDiffContentTitles(myMergeRequest));
|
||||
|
||||
myViewer = new MyThreesideViewer(myDiffContext, myDiffRequest);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<DiffContent> getDiffContents(@NotNull ThreesideMergeRequest mergeRequest) {
|
||||
return ContainerUtil.newArrayList(mergeRequest.getContents());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> getDiffContentTitles(@NotNull ThreesideMergeRequest mergeRequest) {
|
||||
return MergeUtil.notNullizeContentTitles(mergeRequest.getContentTitles());
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myViewer.getComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myViewer.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ToolbarComponents init() {
|
||||
ToolbarComponents components = new ToolbarComponents();
|
||||
|
||||
FrameDiffTool.ToolbarComponents init = myViewer.init();
|
||||
components.statusPanel = init.statusPanel;
|
||||
components.toolbarActions = init.toolbarActions;
|
||||
|
||||
components.closeHandler = new BooleanGetter() {
|
||||
@Override
|
||||
public boolean get() {
|
||||
return Messages.showYesNoDialog(getComponent().getRootPane(),
|
||||
DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"),
|
||||
DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) == Messages.YES;
|
||||
}
|
||||
};
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Action getResolveAction(@NotNull final MergeResult result) {
|
||||
if (result == MergeResult.RESOLVED) return null;
|
||||
|
||||
String caption = MergeUtil.getResolveActionTitle(result, myMergeRequest, myMergeContext);
|
||||
return new AbstractAction(caption) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (result == MergeResult.CANCEL) {
|
||||
if (Messages.showYesNoDialog(getComponent().getRootPane(),
|
||||
DiffBundle.message("merge.dialog.exit.without.applying.changes.confirmation.message"),
|
||||
DiffBundle.message("cancel.visual.merge.dialog.title"), Messages.getQuestionIcon()) != Messages.YES) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
myMergeContext.finishMerge(result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Disposer.dispose(myViewer);
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public MyThreesideViewer getViewer() {
|
||||
return myViewer;
|
||||
}
|
||||
|
||||
//
|
||||
// Viewer
|
||||
//
|
||||
|
||||
private static class MyThreesideViewer extends ThreesideBinaryDiffViewer {
|
||||
public MyThreesideViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
super(context, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
public void rediff(boolean trySync) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public class ErrorMergeTool implements MergeTool {
|
||||
public static final ErrorMergeTool INSTANCE = new ErrorMergeTool();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MergeViewer createComponent(@NotNull MergeContext context, @NotNull MergeRequest request) {
|
||||
return new MyViewer(context, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow(@NotNull MergeContext context, @NotNull MergeRequest request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class MyViewer implements MergeViewer {
|
||||
@NotNull private final MergeContext myMergeContext;
|
||||
@NotNull private final MergeRequest myMergeRequest;
|
||||
|
||||
@NotNull private final JPanel myPanel;
|
||||
|
||||
public MyViewer(@NotNull MergeContext context, @NotNull MergeRequest request) {
|
||||
myMergeContext = context;
|
||||
myMergeRequest = request;
|
||||
|
||||
myPanel = new JPanel(new BorderLayout());
|
||||
myPanel.add(createComponent(), BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JComponent createComponent() {
|
||||
return DiffUtil.createMessagePanel("Can't show diff");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ToolbarComponents init() {
|
||||
return new ToolbarComponents();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Action getResolveAction(@NotNull final MergeResult result) {
|
||||
if (result == MergeResult.RESOLVED) return null;
|
||||
|
||||
String caption = MergeUtil.getResolveActionTitle(result, myMergeRequest, myMergeContext);
|
||||
return new AbstractAction(caption) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
myMergeContext.finishMerge(result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.DiffManagerEx;
|
||||
import com.intellij.diff.actions.impl.NextDifferenceAction;
|
||||
import com.intellij.diff.actions.impl.PrevDifferenceAction;
|
||||
import com.intellij.diff.tools.util.DiffDataKeys;
|
||||
import com.intellij.diff.tools.util.PrevNextDifferenceIterable;
|
||||
import com.intellij.diff.util.DiffPlaces;
|
||||
import com.intellij.diff.util.DiffUserDataKeys;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.impl.DataManagerImpl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.BooleanGetter;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
|
||||
import com.intellij.ui.components.panels.Wrapper;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: support merge request chains
|
||||
// idea - to keep in memory all viewers that were modified (so binary conflict is not the case and OOM shouldn't be too often)
|
||||
// suspend() / resume() methods for viewers? To not interfere with MergeRequest lifecycle: single request -> single viewer -> single applyResult()
|
||||
public abstract class MergeRequestProcessor implements Disposable {
|
||||
private static final Logger LOG = Logger.getInstance(MergeRequestProcessor.class);
|
||||
|
||||
private boolean myDisposed;
|
||||
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final MergeContext myContext;
|
||||
|
||||
@NotNull private final List<MergeTool> myAvailableTools;
|
||||
|
||||
@NotNull private final JPanel myPanel;
|
||||
@NotNull private final MyPanel myMainPanel;
|
||||
@NotNull private final Wrapper myContentPanel;
|
||||
@NotNull private final Wrapper myToolbarPanel;
|
||||
@NotNull private final Wrapper myToolbarStatusPanel;
|
||||
|
||||
@NotNull private final MergeRequest myRequest;
|
||||
@NotNull private final MergeTool.MergeViewer myViewer;
|
||||
|
||||
@Nullable private BooleanGetter myCloseHandler;
|
||||
private boolean myConflictResolved = false;
|
||||
|
||||
public MergeRequestProcessor(@Nullable Project project, @NotNull MergeRequest request) {
|
||||
myProject = project;
|
||||
myRequest = request;
|
||||
|
||||
myContext = new MyDiffContext();
|
||||
myContext.putUserData(DiffUserDataKeysEx.PLACE, DiffPlaces.MERGE);
|
||||
|
||||
myAvailableTools = DiffManagerEx.getInstance().getMergeTools();
|
||||
|
||||
myPanel = new JPanel(new BorderLayout());
|
||||
myMainPanel = new MyPanel();
|
||||
myContentPanel = new Wrapper();
|
||||
myToolbarPanel = new Wrapper();
|
||||
myToolbarPanel.setFocusable(true);
|
||||
myToolbarStatusPanel = new Wrapper();
|
||||
|
||||
myPanel.add(myMainPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel topPanel = new JPanel(new BorderLayout());
|
||||
topPanel.add(myToolbarPanel, BorderLayout.CENTER);
|
||||
topPanel.add(myToolbarStatusPanel, BorderLayout.EAST);
|
||||
|
||||
|
||||
myMainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
myMainPanel.add(myContentPanel, BorderLayout.CENTER);
|
||||
|
||||
myMainPanel.setFocusTraversalPolicyProvider(true);
|
||||
myMainPanel.setFocusTraversalPolicy(new MyFocusTraversalPolicy());
|
||||
|
||||
MergeTool.MergeViewer viewer;
|
||||
try {
|
||||
MergeTool tool = getFittedTool();
|
||||
viewer = tool.createComponent(myContext, myRequest);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
viewer = ErrorMergeTool.INSTANCE.createComponent(myContext, myRequest);
|
||||
}
|
||||
myViewer = viewer;
|
||||
}
|
||||
|
||||
//
|
||||
// Update
|
||||
//
|
||||
|
||||
public void init() {
|
||||
myContentPanel.setContent(myViewer.getComponent());
|
||||
setTitle(myRequest.getTitle());
|
||||
|
||||
MergeTool.ToolbarComponents toolbarComponents = myViewer.init();
|
||||
|
||||
buildToolbar(toolbarComponents.toolbarActions);
|
||||
myToolbarStatusPanel.setContent(toolbarComponents.statusPanel);
|
||||
myCloseHandler = toolbarComponents.closeHandler;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public BottomActions getBottomActions() {
|
||||
BottomActions actions = new BottomActions();
|
||||
actions.applyLeft = myViewer.getResolveAction(MergeResult.LEFT);
|
||||
actions.applyRight = myViewer.getResolveAction(MergeResult.RIGHT);
|
||||
actions.resolveAction = myViewer.getResolveAction(MergeResult.RESOLVED);
|
||||
actions.cancelAction = myViewer.getResolveAction(MergeResult.CANCEL);
|
||||
return actions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected DefaultActionGroup collectToolbarActions(@Nullable List<AnAction> viewerActions) {
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
|
||||
List<AnAction> navigationActions = ContainerUtil.<AnAction>list(new MyPrevDifferenceAction(),
|
||||
new MyNextDifferenceAction());
|
||||
DiffUtil.addActionBlock(group, navigationActions);
|
||||
|
||||
DiffUtil.addActionBlock(group, viewerActions);
|
||||
|
||||
List<AnAction> requestContextActions = myRequest.getUserData(DiffUserDataKeys.CONTEXT_ACTIONS);
|
||||
DiffUtil.addActionBlock(group, requestContextActions);
|
||||
|
||||
List<AnAction> contextActions = myContext.getUserData(DiffUserDataKeys.CONTEXT_ACTIONS);
|
||||
DiffUtil.addActionBlock(group, contextActions);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
protected void buildToolbar(@Nullable List<AnAction> viewerActions) {
|
||||
ActionGroup group = collectToolbarActions(viewerActions);
|
||||
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.DIFF_TOOLBAR, group, true);
|
||||
|
||||
DataManager.registerDataProvider(toolbar.getComponent(), myMainPanel);
|
||||
toolbar.setTargetComponent(toolbar.getComponent());
|
||||
|
||||
myToolbarPanel.setContent(toolbar.getComponent());
|
||||
for (AnAction action : group.getChildren(null)) {
|
||||
DiffUtil.registerAction(action, myMainPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MergeTool getFittedTool() {
|
||||
for (MergeTool tool : myAvailableTools) {
|
||||
try {
|
||||
if (tool.canShow(myContext, myRequest)) return tool;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorMergeTool.INSTANCE;
|
||||
}
|
||||
|
||||
private void setTitle(@Nullable String title) {
|
||||
if (title == null) title = "Merge";
|
||||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (myDisposed) return;
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myDisposed) return;
|
||||
myDisposed = true;
|
||||
|
||||
onDispose();
|
||||
|
||||
Disposer.dispose(myViewer);
|
||||
|
||||
myToolbarStatusPanel.setContent(null);
|
||||
myToolbarPanel.setContent(null);
|
||||
myContentPanel.setContent(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
private void applyRequestResult(@NotNull MergeResult result) {
|
||||
if (myConflictResolved) return;
|
||||
myConflictResolved = true;
|
||||
try {
|
||||
myRequest.applyResult(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Abstract
|
||||
//
|
||||
|
||||
@CalledInAwt
|
||||
protected void onDispose() {
|
||||
applyRequestResult(MergeResult.CANCEL);
|
||||
}
|
||||
|
||||
protected void setWindowTitle(@NotNull String title) {
|
||||
}
|
||||
|
||||
public abstract void closeDialog();
|
||||
|
||||
@Nullable
|
||||
public <T> T getContextUserData(@NotNull Key<T> key) {
|
||||
return myContext.getUserData(key);
|
||||
}
|
||||
|
||||
public <T> void putContextUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
myContext.putUserData(key, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public JComponent getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
JComponent component = myViewer.getPreferredFocusedComponent();
|
||||
return component != null ? component : myToolbarPanel.getTargetComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MergeContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public boolean checkCloseAction() {
|
||||
return myConflictResolved || myCloseHandler == null || myCloseHandler.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getHelpId() {
|
||||
return PlatformDataKeys.HELP_ID.getData(myMainPanel);
|
||||
}
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
|
||||
public boolean isFocused() {
|
||||
return DiffUtil.isFocusedComponent(myProject, myPanel);
|
||||
}
|
||||
|
||||
public void requestFocus() {
|
||||
DiffUtil.requestFocus(myProject, getPreferredFocusedComponent());
|
||||
}
|
||||
|
||||
protected void requestFocusInternal() {
|
||||
JComponent component = getPreferredFocusedComponent();
|
||||
if (component != null) component.requestFocus();
|
||||
}
|
||||
|
||||
//
|
||||
// Navigation
|
||||
//
|
||||
|
||||
private static class MyNextDifferenceAction extends NextDifferenceAction {
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
|
||||
e.getPresentation().setEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
|
||||
if (iterable != null && iterable.canGoNext()) {
|
||||
e.getPresentation().setEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
e.getPresentation().setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
|
||||
if (iterable != null && iterable.canGoNext()) {
|
||||
iterable.goNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyPrevDifferenceAction extends PrevDifferenceAction {
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
|
||||
e.getPresentation().setEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
|
||||
if (iterable != null && iterable.canGoPrev()) {
|
||||
e.getPresentation().setEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
e.getPresentation().setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
|
||||
if (iterable != null && iterable.canGoPrev()) {
|
||||
iterable.goPrev();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
private class MyPanel extends JPanel implements DataProvider {
|
||||
public MyPanel() {
|
||||
super(new BorderLayout());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
Object data;
|
||||
|
||||
DataProvider contentProvider = DataManagerImpl.getDataProviderEx(myContentPanel.getTargetComponent());
|
||||
if (contentProvider != null) {
|
||||
data = contentProvider.getData(dataId);
|
||||
if (data != null) return data;
|
||||
}
|
||||
|
||||
if (CommonDataKeys.PROJECT.is(dataId)) {
|
||||
return myProject;
|
||||
}
|
||||
else if (PlatformDataKeys.HELP_ID.is(dataId)) {
|
||||
if (myRequest.getUserData(DiffUserDataKeys.HELP_ID) != null) {
|
||||
return myRequest.getUserData(DiffUserDataKeys.HELP_ID);
|
||||
}
|
||||
else {
|
||||
return "procedures.vcWithIDEA.commonVcsOps.integrateDiffs.resolveConflict";
|
||||
}
|
||||
}
|
||||
|
||||
DataProvider requestProvider = myRequest.getUserData(DiffUserDataKeys.DATA_PROVIDER);
|
||||
if (requestProvider != null) {
|
||||
data = requestProvider.getData(dataId);
|
||||
if (data != null) return data;
|
||||
}
|
||||
|
||||
DataProvider contextProvider = myContext.getUserData(DiffUserDataKeys.DATA_PROVIDER);
|
||||
if (contextProvider != null) {
|
||||
data = contextProvider.getData(dataId);
|
||||
if (data != null) return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy {
|
||||
@Override
|
||||
public final Component getDefaultComponentImpl(final Container focusCycleRoot) {
|
||||
JComponent component = MergeRequestProcessor.this.getPreferredFocusedComponent();
|
||||
if (component == null) return null;
|
||||
return IdeFocusTraversalPolicy.getPreferredFocusedComponent(component, this);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDiffContext extends MergeContext {
|
||||
@Nullable
|
||||
@Override
|
||||
public Project getProject() {
|
||||
return MergeRequestProcessor.this.getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return MergeRequestProcessor.this.isFocused();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
MergeRequestProcessor.this.requestFocusInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishMerge(@NotNull MergeResult result) {
|
||||
applyRequestResult(result);
|
||||
MergeRequestProcessor.this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
public static class BottomActions {
|
||||
@Nullable public Action applyLeft;
|
||||
@Nullable public Action applyRight;
|
||||
@Nullable public Action resolveAction;
|
||||
@Nullable public Action cancelAction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MergeUtil {
|
||||
@NotNull
|
||||
public static String getResolveActionTitle(@NotNull MergeResult result, @NotNull MergeRequest request, @NotNull MergeContext context) {
|
||||
switch (result) {
|
||||
case CANCEL:
|
||||
return "Abort";
|
||||
case LEFT:
|
||||
return "Accept Left";
|
||||
case RIGHT:
|
||||
return "Accept Right";
|
||||
case RESOLVED:
|
||||
return "Apply";
|
||||
default:
|
||||
throw new IllegalArgumentException(result.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> notNullizeContentTitles(@NotNull List<String> mergeContentTitles) {
|
||||
String left = StringUtil.notNullize(ThreeSide.LEFT.select(mergeContentTitles), "Your Version");
|
||||
String base = StringUtil.notNullize(ThreeSide.BASE.select(mergeContentTitles), "Base Version");
|
||||
String right = StringUtil.notNullize(ThreeSide.RIGHT.select(mergeContentTitles), "Server Version");
|
||||
return ContainerUtil.list(left, base, right);
|
||||
}
|
||||
|
||||
public static class ProxyDiffContext extends DiffContext {
|
||||
@NotNull private final MergeContext myMergeContext;
|
||||
|
||||
public ProxyDiffContext(@NotNull MergeContext mergeContext) {
|
||||
myMergeContext = mergeContext;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Project getProject() {
|
||||
return myMergeContext.getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWindowFocused() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return myMergeContext.isFocused();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
myMergeContext.requestFocus();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getUserData(@NotNull Key<T> key) {
|
||||
return myMergeContext.getUserData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
myMergeContext.putUserData(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.util.DiffUserDataKeys;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.List;
|
||||
|
||||
public class MergeWindow {
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final MergeRequest myMergeRequest;
|
||||
|
||||
private MyDialog myWrapper;
|
||||
|
||||
public MergeWindow(@Nullable Project project, @NotNull MergeRequest mergeRequest) {
|
||||
myProject = project;
|
||||
myMergeRequest = mergeRequest;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
MergeRequestProcessor processor = new MergeRequestProcessor(myProject, myMergeRequest) {
|
||||
@Override
|
||||
public void closeDialog() {
|
||||
myWrapper.doCancelAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setWindowTitle(@NotNull String title) {
|
||||
myWrapper.setTitle(title);
|
||||
}
|
||||
};
|
||||
|
||||
myWrapper = new MyDialog(processor);
|
||||
myWrapper.init();
|
||||
}
|
||||
|
||||
public void show() {
|
||||
init();
|
||||
myWrapper.show();
|
||||
}
|
||||
|
||||
// TODO: use WindowWrapper
|
||||
private static class MyDialog extends DialogWrapper {
|
||||
@NotNull private final MergeRequestProcessor myProcessor;
|
||||
@NotNull private final MergeRequestProcessor.BottomActions myBottomActions;
|
||||
|
||||
public MyDialog(@NotNull MergeRequestProcessor processor) {
|
||||
super(processor.getProject(), true);
|
||||
myProcessor = processor;
|
||||
myBottomActions = myProcessor.getBottomActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
Disposer.register(getDisposable(), myProcessor);
|
||||
getWindow().addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
myProcessor.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return new MyPanel(myProcessor.getComponent());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myProcessor.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getDimensionServiceKey() {
|
||||
return StringUtil.notNullize(myProcessor.getContextUserData(DiffUserDataKeys.DIALOG_GROUP_KEY), "MergeDialog");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Action[] createActions() {
|
||||
List<Action> actions = ContainerUtil.skipNulls(ContainerUtil.list(myBottomActions.resolveAction, myBottomActions.cancelAction));
|
||||
if (myBottomActions.resolveAction != null) {
|
||||
myBottomActions.resolveAction.putValue(DialogWrapper.DEFAULT_ACTION, true);
|
||||
}
|
||||
return actions.toArray(new Action[actions.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Action[] createLeftSideActions() {
|
||||
List<Action> actions = ContainerUtil.skipNulls(ContainerUtil.list(myBottomActions.applyLeft, myBottomActions.applyRight));
|
||||
return actions.toArray(new Action[actions.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Action getOKAction() {
|
||||
if (myBottomActions.resolveAction != null) return myBottomActions.resolveAction;
|
||||
return super.getOKAction();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Action getCancelAction() {
|
||||
if (myBottomActions.cancelAction != null) return myBottomActions.cancelAction;
|
||||
return super.getCancelAction();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return myProcessor.getHelpId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCancelAction() {
|
||||
if (!myProcessor.checkCloseAction()) return;
|
||||
super.doCancelAction();
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyPanel extends JPanel {
|
||||
public MyPanel(@NotNull JComponent content) {
|
||||
super(new BorderLayout());
|
||||
add(content, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension windowSize = DiffUtil.getDefaultDiffWindowSize();
|
||||
Dimension size = super.getPreferredSize();
|
||||
return new Dimension(Math.max(windowSize.width, size.width), Math.max(windowSize.height, size.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.merge;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.fragments.MergeLineFragment;
|
||||
import com.intellij.diff.tools.simple.ThreesideDiffChangeBase;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffUtil.UpdatedLineRange;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TextMergeChange extends ThreesideDiffChangeBase {
|
||||
@NotNull private final TextMergeTool.TextMergeViewer myMergeViewer;
|
||||
@NotNull private final TextMergeTool.TextMergeViewer.MyThreesideViewer myViewer;
|
||||
@NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
|
||||
|
||||
@NotNull private final List<MyGutterOperation> myOperations = new ArrayList<MyGutterOperation>();
|
||||
|
||||
private final int[] myStartLines = new int[3];
|
||||
private final int[] myEndLines = new int[3];
|
||||
private final boolean[] myResolved = new boolean[2];
|
||||
private boolean myOnesideAppliedConflict;
|
||||
|
||||
@CalledInAwt
|
||||
public TextMergeChange(@NotNull MergeLineFragment fragment, @NotNull TextMergeTool.TextMergeViewer viewer) {
|
||||
super(fragment, viewer.getViewer().getEditors(), ComparisonPolicy.DEFAULT);
|
||||
myMergeViewer = viewer;
|
||||
myViewer = viewer.getViewer();
|
||||
|
||||
for (ThreeSide side : ThreeSide.values()) {
|
||||
myStartLines[side.getIndex()] = fragment.getStartLine(side);
|
||||
myEndLines[side.getIndex()] = fragment.getEndLine(side);
|
||||
}
|
||||
|
||||
installHighlighter();
|
||||
}
|
||||
|
||||
void installHighlighter() {
|
||||
assert myHighlighters.isEmpty();
|
||||
|
||||
createHighlighter(ThreeSide.BASE);
|
||||
if (getType().isLeftChange()) createHighlighter(ThreeSide.LEFT);
|
||||
if (getType().isRightChange()) createHighlighter(ThreeSide.RIGHT);
|
||||
|
||||
doInstallActionHighlighters();
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
void destroyHighlighter() {
|
||||
for (RangeHighlighter highlighter : myHighlighters) {
|
||||
highlighter.dispose();
|
||||
}
|
||||
myHighlighters.clear();
|
||||
|
||||
for (MyGutterOperation operation : myOperations) {
|
||||
operation.dispose();
|
||||
}
|
||||
myOperations.clear();
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
void doReinstallHighlighter() {
|
||||
destroyHighlighter();
|
||||
installHighlighter();
|
||||
myViewer.repaintDividers();
|
||||
}
|
||||
|
||||
private void createHighlighter(@NotNull ThreeSide side) {
|
||||
Editor editor = side.select(myViewer.getEditors());
|
||||
Document document = editor.getDocument();
|
||||
|
||||
TextDiffType type = getDiffType();
|
||||
boolean resolved = isResolved(side);
|
||||
int startLine = getStartLine(side);
|
||||
int endLine = getEndLine(side);
|
||||
|
||||
int start;
|
||||
int end;
|
||||
if (startLine == endLine) {
|
||||
start = end = startLine < DiffUtil.getLineCount(document) ? document.getLineStartOffset(startLine) : document.getTextLength();
|
||||
}
|
||||
else {
|
||||
start = document.getLineStartOffset(startLine);
|
||||
end = document.getLineEndOffset(endLine - 1);
|
||||
if (end < document.getTextLength()) end++;
|
||||
}
|
||||
|
||||
myHighlighters.addAll(DiffDrawUtil.createHighlighter(editor, start, end, type, false, HighlighterTargetArea.EXACT_RANGE, resolved));
|
||||
|
||||
if (startLine == endLine) {
|
||||
if (startLine != 0) {
|
||||
myHighlighters.addAll(DiffDrawUtil.createLineMarker(editor, endLine - 1, type, SeparatorPlacement.BOTTOM, true, resolved));
|
||||
}
|
||||
}
|
||||
else {
|
||||
myHighlighters.addAll(DiffDrawUtil.createLineMarker(editor, startLine, type, SeparatorPlacement.TOP, false, resolved));
|
||||
myHighlighters.addAll(DiffDrawUtil.createLineMarker(editor, endLine - 1, type, SeparatorPlacement.BOTTOM, false, resolved));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
@CalledInAwt
|
||||
void setResolved(@NotNull Side side, boolean value) {
|
||||
myResolved[side.getIndex()] = value;
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return myResolved[0] && myResolved[1];
|
||||
}
|
||||
|
||||
public boolean isResolved(@NotNull Side side) {
|
||||
return side.select(myResolved);
|
||||
}
|
||||
|
||||
public boolean isOnesideAppliedConflict() {
|
||||
return myOnesideAppliedConflict;
|
||||
}
|
||||
|
||||
public void markOnesideAppliedConflict() {
|
||||
myOnesideAppliedConflict = true;
|
||||
}
|
||||
|
||||
public boolean isResolved(@NotNull ThreeSide side) {
|
||||
switch (side) {
|
||||
case LEFT:
|
||||
return isResolved(Side.LEFT);
|
||||
case BASE:
|
||||
return isResolved();
|
||||
case RIGHT:
|
||||
return isResolved(Side.RIGHT);
|
||||
default:
|
||||
throw new IllegalArgumentException(side.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public int getStartLine(@NotNull ThreeSide side) {
|
||||
return side.select(myStartLines);
|
||||
}
|
||||
|
||||
public int getEndLine(@NotNull ThreeSide side) {
|
||||
return side.select(myEndLines);
|
||||
}
|
||||
|
||||
public void setStartLine(@NotNull ThreeSide side, int value) {
|
||||
myStartLines[side.getIndex()] = value;
|
||||
}
|
||||
|
||||
public void setEndLine(@NotNull ThreeSide side, int value) {
|
||||
myEndLines[side.getIndex()] = value;
|
||||
}
|
||||
|
||||
//
|
||||
// Shift
|
||||
//
|
||||
|
||||
@Nullable
|
||||
State processBaseChange(int oldLine1, int oldLine2, int shift) {
|
||||
int line1 = getStartLine(ThreeSide.BASE);
|
||||
int line2 = getEndLine(ThreeSide.BASE);
|
||||
|
||||
UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(line1, line2, oldLine1, oldLine2, shift);
|
||||
|
||||
boolean rangeAffected = newRange.damaged ||
|
||||
(oldLine2 >= line1 && oldLine1 <= line2); // RangeMarker can be updated in a different way
|
||||
State oldState = rangeAffected ? storeState() : null;
|
||||
|
||||
if (newRange.startLine == newRange.endLine && getDiffType() == TextDiffType.DELETED && !isResolved()) {
|
||||
if (oldState == null) oldState = storeState();
|
||||
myViewer.markChangeResolved(this);
|
||||
}
|
||||
|
||||
setStartLine(ThreeSide.BASE, newRange.startLine);
|
||||
setEndLine(ThreeSide.BASE, newRange.endLine);
|
||||
|
||||
return oldState;
|
||||
}
|
||||
|
||||
//
|
||||
// Gutter actions
|
||||
//
|
||||
|
||||
private void doInstallActionHighlighters() {
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.LEFT, OperationType.APPLY));
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.LEFT, OperationType.IGNORE));
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.RIGHT, OperationType.APPLY));
|
||||
ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.RIGHT, OperationType.IGNORE));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MyGutterOperation createOperation(@NotNull ThreeSide side, @NotNull OperationType type) {
|
||||
if (isResolved(side)) return null;
|
||||
|
||||
EditorEx editor = myViewer.getEditor(side);
|
||||
Document document = editor.getDocument();
|
||||
|
||||
int line = getStartLine(side);
|
||||
int offset = line == DiffUtil.getLineCount(document) ? document.getTextLength() : document.getLineStartOffset(line);
|
||||
|
||||
RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset,
|
||||
HighlighterLayer.ADDITIONAL_SYNTAX,
|
||||
null,
|
||||
HighlighterTargetArea.LINES_IN_RANGE);
|
||||
return new MyGutterOperation(side, highlighter, type);
|
||||
}
|
||||
|
||||
public void updateGutterActions(boolean force) {
|
||||
for (MyGutterOperation operation : myOperations) {
|
||||
operation.update(force);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyGutterOperation {
|
||||
@NotNull private final ThreeSide mySide;
|
||||
@NotNull private final RangeHighlighter myHighlighter;
|
||||
@NotNull private final OperationType myType;
|
||||
|
||||
private boolean myCtrlPressed;
|
||||
private boolean myShiftPressed;
|
||||
|
||||
private MyGutterOperation(@NotNull ThreeSide side, @NotNull RangeHighlighter highlighter, @NotNull OperationType type) {
|
||||
mySide = side;
|
||||
myHighlighter = highlighter;
|
||||
myType = type;
|
||||
|
||||
update(true);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
myHighlighter.dispose();
|
||||
}
|
||||
|
||||
public void update(boolean force) {
|
||||
if (!force && !areModifiersChanged()) {
|
||||
return;
|
||||
}
|
||||
if (myHighlighter.isValid()) myHighlighter.setGutterIconRenderer(createRenderer());
|
||||
}
|
||||
|
||||
private boolean areModifiersChanged() {
|
||||
return myCtrlPressed != myViewer.getModifierProvider().isCtrlPressed() ||
|
||||
myShiftPressed != myViewer.getModifierProvider().isShiftPressed();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GutterIconRenderer createRenderer() {
|
||||
if (mySide == ThreeSide.BASE) return null;
|
||||
Side versionSide = mySide.select(Side.LEFT, null, Side.RIGHT);
|
||||
assert versionSide != null;
|
||||
|
||||
myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed();
|
||||
myShiftPressed = myViewer.getModifierProvider().isShiftPressed();
|
||||
|
||||
if (!isChange(versionSide)) return null;
|
||||
|
||||
switch (myType) {
|
||||
case APPLY:
|
||||
return createApplyRenderer(versionSide, myCtrlPressed);
|
||||
case IGNORE:
|
||||
return createIgnoreRenderer(versionSide, myCtrlPressed);
|
||||
default:
|
||||
throw new IllegalArgumentException(myType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GutterIconRenderer createApplyRenderer(@NotNull final Side side, final boolean modifier) {
|
||||
if (isResolved(side)) return null;
|
||||
Icon icon = isOnesideAppliedConflict() ? AllIcons.Diff.ArrowLeftDown : AllIcons.Diff.Arrow;
|
||||
return createIconRenderer(DiffBundle.message("merge.dialog.apply.change.action.name"), icon, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myViewer.executeMergeCommand("Apply change", Collections.singletonList(TextMergeChange.this), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myViewer.replaceChange(TextMergeChange.this, side, modifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GutterIconRenderer createIgnoreRenderer(@NotNull final Side side, final boolean modifier) {
|
||||
if (isResolved(side)) return null;
|
||||
return createIconRenderer(DiffBundle.message("merge.dialog.ignore.change.action.name"), AllIcons.Diff.Remove, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myViewer.executeMergeCommand(null, Collections.singletonList(TextMergeChange.this), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myViewer.ignoreChange(TextMergeChange.this, side, modifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GutterIconRenderer createIconRenderer(@NotNull final String tooltipText,
|
||||
@NotNull final Icon icon,
|
||||
@NotNull final Runnable perform) {
|
||||
return new GutterIconRenderer() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public boolean isNavigateAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AnAction getClickAction() {
|
||||
return new DumbAwareAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
perform.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTooltipText() {
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private enum OperationType {
|
||||
APPLY, IGNORE
|
||||
}
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
@NotNull
|
||||
State storeState() {
|
||||
return new State(
|
||||
myStartLines[0],
|
||||
myStartLines[1],
|
||||
myStartLines[2],
|
||||
|
||||
myEndLines[0],
|
||||
myEndLines[1],
|
||||
myEndLines[2],
|
||||
|
||||
myResolved[0],
|
||||
myResolved[1],
|
||||
|
||||
myOnesideAppliedConflict);
|
||||
}
|
||||
|
||||
void restoreState(@NotNull State state) {
|
||||
myStartLines[0] = state.myStartLine1;
|
||||
myStartLines[1] = state.myStartLine2;
|
||||
myStartLines[2] = state.myStartLine3;
|
||||
|
||||
myEndLines[0] = state.myEndLine1;
|
||||
myEndLines[1] = state.myEndLine2;
|
||||
myEndLines[2] = state.myEndLine3;
|
||||
|
||||
myResolved[0] = state.myResolved1;
|
||||
myResolved[1] = state.myResolved2;
|
||||
|
||||
myOnesideAppliedConflict = state.myOnesideAppliedConflict;
|
||||
}
|
||||
|
||||
public static class State {
|
||||
private final int myStartLine1;
|
||||
private final int myStartLine2;
|
||||
private final int myStartLine3;
|
||||
|
||||
private final int myEndLine1;
|
||||
private final int myEndLine2;
|
||||
private final int myEndLine3;
|
||||
|
||||
private final boolean myResolved1;
|
||||
private final boolean myResolved2;
|
||||
|
||||
private final boolean myOnesideAppliedConflict;
|
||||
|
||||
public State(int startLine1,
|
||||
int startLine2,
|
||||
int startLine3,
|
||||
int endLine1,
|
||||
int endLine2,
|
||||
int endLine3,
|
||||
boolean resolved1,
|
||||
boolean resolved2,
|
||||
boolean onesideAppliedConflict) {
|
||||
myStartLine1 = startLine1;
|
||||
myStartLine2 = startLine2;
|
||||
myStartLine3 = startLine3;
|
||||
myEndLine1 = endLine1;
|
||||
myEndLine2 = endLine2;
|
||||
myEndLine3 = endLine3;
|
||||
myResolved1 = resolved1;
|
||||
myResolved2 = resolved2;
|
||||
myOnesideAppliedConflict = onesideAppliedConflict;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.requests;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.FileContent;
|
||||
import com.intellij.diff.merge.ThreesideMergeRequest;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class BinaryMergeRequestImpl extends ThreesideMergeRequest {
|
||||
private static final Logger LOG = Logger.getInstance(BinaryMergeRequestImpl.class);
|
||||
|
||||
@NotNull private final FileContent myFile;
|
||||
@NotNull private final List<DiffContent> myContents;
|
||||
|
||||
@NotNull private final List<byte[]> myByteContents;
|
||||
@NotNull private final byte[] myOriginalContent;
|
||||
|
||||
@Nullable private final String myTitle;
|
||||
@NotNull private final List<String> myTitles;
|
||||
|
||||
@Nullable private final Consumer<MergeResult> myApplyCallback;
|
||||
|
||||
public BinaryMergeRequestImpl(@NotNull FileContent file,
|
||||
@NotNull byte[] originalContent,
|
||||
@NotNull List<DiffContent> contents,
|
||||
@NotNull List<byte[]> byteContents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) {
|
||||
assert byteContents.size() == 3;
|
||||
assert contents.size() == 3;
|
||||
assert contentTitles.size() == 3;
|
||||
|
||||
myFile = file;
|
||||
myOriginalContent = originalContent;
|
||||
|
||||
myByteContents = byteContents;
|
||||
myContents = contents;
|
||||
myTitle = title;
|
||||
myTitles = contentTitles;
|
||||
|
||||
myApplyCallback = applyCallback;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileContent getOutputContent() {
|
||||
return myFile;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<DiffContent> getContents() {
|
||||
return myContents;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getContentTitles() {
|
||||
return myTitles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyResult(@NotNull MergeResult result) {
|
||||
final byte[] applyContent;
|
||||
switch (result) {
|
||||
case CANCEL:
|
||||
applyContent = myOriginalContent;
|
||||
break;
|
||||
case LEFT:
|
||||
applyContent = ThreeSide.LEFT.select(myByteContents);
|
||||
break;
|
||||
case RIGHT:
|
||||
applyContent = ThreeSide.RIGHT.select(myByteContents);
|
||||
break;
|
||||
case RESOLVED:
|
||||
applyContent = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(result.toString());
|
||||
}
|
||||
|
||||
if (applyContent != null) {
|
||||
new WriteCommandAction.Simple(null) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
try {
|
||||
myFile.getFile().setBinaryContent(applyContent);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
Messages.showErrorDialog((Project)null, "Can't apply result", CommonBundle.getErrorTitle());
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
if (myApplyCallback != null) myApplyCallback.consume(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.requests;
|
||||
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.diff.merge.TextMergeRequest;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TextMergeRequestImpl extends TextMergeRequest {
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final DocumentContent myOutput;
|
||||
@NotNull private final List<DocumentContent> myContents;
|
||||
|
||||
@NotNull private final CharSequence myOriginalContent;
|
||||
|
||||
@Nullable private final String myTitle;
|
||||
@NotNull private final List<String> myTitles;
|
||||
|
||||
@Nullable private final Consumer<MergeResult> myApplyCallback;
|
||||
|
||||
public TextMergeRequestImpl(@Nullable Project project,
|
||||
@NotNull DocumentContent output,
|
||||
@NotNull CharSequence originalContent,
|
||||
@NotNull List<DocumentContent> contents,
|
||||
@Nullable String title,
|
||||
@NotNull List<String> contentTitles,
|
||||
@Nullable Consumer<MergeResult> applyCallback) {
|
||||
assert contents.size() == 3;
|
||||
assert contentTitles.size() == 3;
|
||||
myProject = project;
|
||||
|
||||
myOutput = output;
|
||||
myOriginalContent = originalContent;
|
||||
|
||||
myContents = contents;
|
||||
myTitles = contentTitles;
|
||||
myTitle = title;
|
||||
|
||||
myApplyCallback = applyCallback;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DocumentContent getOutputContent() {
|
||||
return myOutput;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<DocumentContent> getContents() {
|
||||
return myContents;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getContentTitles() {
|
||||
return myTitles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyResult(@NotNull MergeResult result) {
|
||||
final CharSequence applyContent;
|
||||
switch (result) {
|
||||
case CANCEL:
|
||||
applyContent = myOriginalContent;
|
||||
break;
|
||||
case LEFT:
|
||||
CharSequence leftContent = ThreeSide.LEFT.select(getContents()).getDocument().getImmutableCharSequence();
|
||||
applyContent = StringUtil.convertLineSeparators(leftContent.toString());
|
||||
break;
|
||||
case RIGHT:
|
||||
CharSequence rightContent = ThreeSide.RIGHT.select(getContents()).getDocument().getImmutableCharSequence();
|
||||
applyContent = StringUtil.convertLineSeparators(rightContent.toString());
|
||||
break;
|
||||
case RESOLVED:
|
||||
applyContent = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(result.toString());
|
||||
}
|
||||
|
||||
if (applyContent != null) {
|
||||
new WriteCommandAction.Simple(myProject) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
myOutput.getDocument().setText(applyContent);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
if (myApplyCallback != null) myApplyCallback.consume(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.requests;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.DiffContextEx;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.fileTypes.UnknownFileType;
|
||||
import com.intellij.openapi.fileTypes.ex.FileTypeChooser;
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class UnknownFileTypeDiffRequest extends ComponentDiffRequest {
|
||||
@Nullable private final String myFileName;
|
||||
@Nullable private final String myTitle;
|
||||
|
||||
public UnknownFileTypeDiffRequest(@NotNull VirtualFile file, @Nullable String title) {
|
||||
this(file.getName(), title);
|
||||
}
|
||||
|
||||
public UnknownFileTypeDiffRequest(@NotNull String fileName, @Nullable String title) {
|
||||
boolean knownFileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName) != UnknownFileType.INSTANCE;
|
||||
myFileName = knownFileType ? null : fileName;
|
||||
myTitle = title;
|
||||
}
|
||||
|
||||
public UnknownFileTypeDiffRequest(@Nullable String title) {
|
||||
myFileName = null;
|
||||
myTitle = title;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent(@NotNull final DiffContext context) {
|
||||
final SimpleColoredComponent label = new SimpleColoredComponent();
|
||||
label.append("Can't show diff for unknown file type. ",
|
||||
new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getInactiveTextColor()));
|
||||
if (myFileName != null) {
|
||||
label.append("Associate", SimpleTextAttributes.LINK_ATTRIBUTES, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FileType type = FileTypeChooser.associateFileType(myFileName);
|
||||
if (type != null) onSuccess(context);
|
||||
}
|
||||
});
|
||||
LinkMouseListenerBase.installSingleTagOn(label);
|
||||
}
|
||||
return DiffUtil.createMessagePanel(label);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFileName() {
|
||||
return myFileName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
protected void onSuccess(@NotNull DiffContext context) {
|
||||
if (context instanceof DiffContextEx) ((DiffContextEx)context).reloadDiffRequest();
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="d5bc7" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="d5bc7" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
@@ -103,9 +103,18 @@
|
||||
</component>
|
||||
<hspacer id="3caba">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<component id="5eb41" class="javax.swing.JCheckBox" binding="myRespectExitCodeCheckbox">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<margin top="4" left="2" bottom="0" right="2"/>
|
||||
<text value="Trust process exit code"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<component id="7e9c5" class="javax.swing.JLabel">
|
||||
|
||||
@@ -50,6 +50,7 @@ public class ExternalDiffSettingsPanel {
|
||||
private TextFieldWithBrowseButton myMergePath;
|
||||
private JTextField myMergeParameters;
|
||||
private JLabel myDescriptionLabel;
|
||||
private JCheckBox myRespectExitCodeCheckbox;
|
||||
|
||||
public ExternalDiffSettingsPanel() {
|
||||
myDescriptionLabel.setText(DESCRIPTION_TEXT);
|
||||
@@ -87,6 +88,7 @@ public class ExternalDiffSettingsPanel {
|
||||
if (mySettings.isMergeEnabled() != myMergeEnabled.isSelected()) return true;
|
||||
if (!mySettings.getMergeExePath().equals(myMergePath.getText())) return true;
|
||||
if (!mySettings.getMergeParameters().equals(myMergeParameters.getText())) return true;
|
||||
if (mySettings.isMergeTrustExitCode() != myRespectExitCodeCheckbox.isSelected()) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -100,6 +102,7 @@ public class ExternalDiffSettingsPanel {
|
||||
mySettings.setMergeEnabled(myMergeEnabled.isSelected());
|
||||
mySettings.setMergeExePath(myMergePath.getText());
|
||||
mySettings.setMergeParameters(myMergeParameters.getText());
|
||||
mySettings.setMergeTrustExitCode(myRespectExitCodeCheckbox.isSelected());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@@ -111,6 +114,7 @@ public class ExternalDiffSettingsPanel {
|
||||
myMergePath.setText(mySettings.getMergeExePath());
|
||||
myMergeEnabled.setSelected(mySettings.isMergeEnabled());
|
||||
myMergeParameters.setText(mySettings.getMergeParameters());
|
||||
myRespectExitCodeCheckbox.setSelected(mySettings.isMergeTrustExitCode());
|
||||
|
||||
updateEnabledEffect();
|
||||
}
|
||||
@@ -122,6 +126,7 @@ public class ExternalDiffSettingsPanel {
|
||||
|
||||
UIUtil.setEnabled(myMergePath, myMergeEnabled.isSelected(), true);
|
||||
UIUtil.setEnabled(myMergeParameters, myMergeEnabled.isSelected(), true);
|
||||
UIUtil.setEnabled(myRespectExitCodeCheckbox, myMergeEnabled.isSelected(), true);
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
|
||||
@@ -20,19 +20,15 @@ import com.intellij.diff.DiffContextEx;
|
||||
import com.intellij.diff.FrameDiffTool;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.FileContent;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.requests.MessageDiffRequest;
|
||||
import com.intellij.diff.requests.*;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.fileTypes.FileTypes;
|
||||
import com.intellij.openapi.fileTypes.ex.FileTypeChooser;
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase;
|
||||
import com.intellij.openapi.fileTypes.UnknownFileType;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -79,26 +75,17 @@ public class ErrorDiffTool implements FrameDiffTool {
|
||||
String message = ((MessageDiffRequest)request).getMessage();
|
||||
return DiffUtil.createMessagePanel(message);
|
||||
}
|
||||
if (request instanceof ComponentDiffRequest) {
|
||||
return ((ComponentDiffRequest)request).getComponent(myContext);
|
||||
}
|
||||
if (request instanceof ContentDiffRequest) {
|
||||
List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
|
||||
for (DiffContent content : contents) {
|
||||
for (final DiffContent content : contents) {
|
||||
if (content instanceof FileContent && FileTypes.UNKNOWN.equals(content.getContentType())) {
|
||||
final VirtualFile file = ((FileContent)content).getFile();
|
||||
|
||||
final SimpleColoredComponent label = new SimpleColoredComponent();
|
||||
label.append("Can't show diff for unknown file type. ",
|
||||
new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getInactiveTextColor()));
|
||||
label.append("Associate", SimpleTextAttributes.LINK_ATTRIBUTES, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FileType type = FileTypeChooser.associateFileType(file.getName());
|
||||
if (type != null && myContext instanceof DiffContextEx) {
|
||||
((DiffContextEx)myContext).reloadDiffRequest();
|
||||
}
|
||||
}
|
||||
});
|
||||
LinkMouseListenerBase.installSingleTagOn(label);
|
||||
return DiffUtil.createMessagePanel(label);
|
||||
UnknownFileTypeDiffRequest unknownFileTypeRequest = new UnknownFileTypeDiffRequest(file, myRequest.getTitle());
|
||||
return unknownFileTypeRequest.getComponent(myContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,6 +108,21 @@ public class ErrorDiffTool implements FrameDiffTool {
|
||||
@NotNull
|
||||
@Override
|
||||
public ToolbarComponents init() {
|
||||
if (myRequest instanceof UnknownFileTypeDiffRequest) {
|
||||
String fileName = ((UnknownFileTypeDiffRequest)myRequest).getFileName();
|
||||
if (fileName != null && FileTypeManager.getInstance().getFileTypeByFileName(fileName) != UnknownFileType.INSTANCE) {
|
||||
// FileType was assigned elsewhere (ex: by other UnknownFileTypeDiffRequest). We should reload request.
|
||||
if (myContext instanceof DiffContextEx) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((DiffContextEx)myContext).reloadDiffRequest();
|
||||
}
|
||||
}, ModalityState.current());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ToolbarComponents();
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -91,6 +91,7 @@ public class ExternalDiffSettings implements PersistentStateComponent<ExternalDi
|
||||
@Nullable public Boolean MERGE_ENABLED = null;
|
||||
@Nullable public String MERGE_EXE_PATH = null;
|
||||
@Nullable public String MERGE_PARAMETERS = null;
|
||||
public boolean MERGE_TRUST_EXIT_CODE = false;
|
||||
}
|
||||
|
||||
public boolean isDiffEnabled() {
|
||||
@@ -158,4 +159,12 @@ public class ExternalDiffSettings implements PersistentStateComponent<ExternalDi
|
||||
myState.MERGE_PARAMETERS = path;
|
||||
setProperty(DiffManagerImpl.MERGE_TOOL_PARAMETERS, path);
|
||||
}
|
||||
|
||||
public boolean isMergeTrustExitCode() {
|
||||
return myState.MERGE_TRUST_EXIT_CODE;
|
||||
}
|
||||
|
||||
public void setMergeTrustExitCode(boolean value) {
|
||||
myState.MERGE_TRUST_EXIT_CODE = value;
|
||||
}
|
||||
}
|
||||
|
||||
+257
-24
@@ -16,18 +16,28 @@
|
||||
package com.intellij.diff.tools.external;
|
||||
|
||||
import com.intellij.diff.contents.*;
|
||||
import com.intellij.diff.merge.MergeResult;
|
||||
import com.intellij.diff.merge.ThreesideMergeRequest;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.CommandLineTokenizer;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
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.VirtualFile;
|
||||
import com.intellij.util.LineSeparator;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -36,6 +46,8 @@ import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ExternalDiffToolUtil {
|
||||
public static boolean canCreateFile(@NotNull DiffContent content) {
|
||||
@@ -59,31 +71,12 @@ public class ExternalDiffToolUtil {
|
||||
return file.getPath();
|
||||
}
|
||||
|
||||
byte[] bytes = file.contentsToByteArray();
|
||||
return createFile(bytes, getFileName(title, windowTitle, content.getContentType())).getPath();
|
||||
String tempFileName = getFileName(title, windowTitle, content.getContentType());
|
||||
return createTempFile(file, tempFileName).getPath();
|
||||
}
|
||||
else if (content instanceof DocumentContent) {
|
||||
final DocumentContent documentContent = (DocumentContent)content;
|
||||
FileDocumentManager.getInstance().saveDocument(documentContent.getDocument());
|
||||
|
||||
LineSeparator separator = documentContent.getLineSeparator();
|
||||
if (separator == null) separator = LineSeparator.getSystemLineSeparator();
|
||||
|
||||
Charset charset = documentContent.getCharset();
|
||||
if (charset == null) charset = Charset.defaultCharset();
|
||||
|
||||
String contentData = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
return documentContent.getDocument().getText();
|
||||
}
|
||||
});
|
||||
if (separator != LineSeparator.LF) {
|
||||
contentData = StringUtil.convertLineSeparators(contentData, separator.getSeparatorString());
|
||||
}
|
||||
|
||||
byte[] bytes = contentData.getBytes(charset);
|
||||
return createFile(bytes, getFileName(title, windowTitle, content.getContentType())).getPath();
|
||||
String tempFileName = getFileName(title, windowTitle, content.getContentType());
|
||||
return createTempFile(((DocumentContent)content), tempFileName).getPath();
|
||||
}
|
||||
else if (content instanceof DirectoryContent) {
|
||||
VirtualFile file = ((DirectoryContent)content).getFile();
|
||||
@@ -97,6 +90,56 @@ public class ExternalDiffToolUtil {
|
||||
throw new IllegalArgumentException(content.toString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File createTempFile(@NotNull final DocumentContent content, @NotNull String tempFileName) throws IOException {
|
||||
FileDocumentManager.getInstance().saveDocument(content.getDocument());
|
||||
|
||||
LineSeparator separator = content.getLineSeparator();
|
||||
if (separator == null) separator = LineSeparator.getSystemLineSeparator();
|
||||
|
||||
Charset charset = content.getCharset();
|
||||
if (charset == null) charset = Charset.defaultCharset();
|
||||
|
||||
String contentData = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
return content.getDocument().getText();
|
||||
}
|
||||
});
|
||||
if (separator != LineSeparator.LF) {
|
||||
contentData = StringUtil.convertLineSeparators(contentData, separator.getSeparatorString());
|
||||
}
|
||||
|
||||
byte[] bytes = contentData.getBytes(charset);
|
||||
return createFile(bytes, tempFileName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File createTempFile(@NotNull VirtualFile file, @NotNull String tempFileName) throws IOException {
|
||||
byte[] bytes = file.contentsToByteArray();
|
||||
return createFile(bytes, tempFileName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static OutputFile createOutputFile(@NotNull DiffContent content, @Nullable String windowTitle) throws IOException {
|
||||
if (content instanceof FileContent) {
|
||||
FileContent fileContent = (FileContent)content;
|
||||
if (fileContent.getFile().isInLocalFileSystem()) {
|
||||
return new LocalOutputFile(fileContent.getFile());
|
||||
}
|
||||
|
||||
String tempFileName = getFileName(null, windowTitle, content.getContentType());
|
||||
File tempFile = createTempFile(fileContent.getFile(), tempFileName);
|
||||
return new NonLocalOutputFile(fileContent.getFile(), tempFile);
|
||||
}
|
||||
else if (content instanceof DocumentContent) {
|
||||
String tempFileName = getFileName(null, windowTitle, content.getContentType());
|
||||
File tempFile = createTempFile(((DocumentContent)content), tempFileName);
|
||||
return new DocumentOutputFile(((DocumentContent)content).getDocument(), ((DocumentContent)content).getCharset(), tempFile);
|
||||
}
|
||||
throw new IllegalArgumentException(content.toString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getFileName(@Nullable String title, @Nullable String windowTitle, @Nullable FileType fileType) {
|
||||
String prefix = "";
|
||||
@@ -120,7 +163,7 @@ public class ExternalDiffToolUtil {
|
||||
}
|
||||
|
||||
public static void execute(@NotNull ExternalDiffSettings settings,
|
||||
@NotNull List<DiffContent> contents,
|
||||
@NotNull List<? extends DiffContent> contents,
|
||||
@NotNull List<String> titles,
|
||||
@Nullable String windowTitle)
|
||||
throws IOException, ExecutionException {
|
||||
@@ -162,4 +205,194 @@ public class ExternalDiffToolUtil {
|
||||
commandLine.addParameters(args);
|
||||
commandLine.createProcess();
|
||||
}
|
||||
|
||||
public static void executeMerge(@Nullable Project project,
|
||||
@NotNull ExternalDiffSettings settings,
|
||||
@NotNull ThreesideMergeRequest request)
|
||||
throws IOException, ExecutionException {
|
||||
boolean success = false;
|
||||
try {
|
||||
DiffContent outputContent = request.getOutputContent();
|
||||
List<? extends DiffContent> contents = request.getContents();
|
||||
List<String> titles = request.getContentTitles();
|
||||
String windowTitle = request.getTitle();
|
||||
|
||||
assert contents.size() == 3;
|
||||
assert titles.size() == contents.size();
|
||||
|
||||
List<String> files = new ArrayList<String>();
|
||||
for (int i = 0; i < contents.size(); i++) {
|
||||
files.add(createFile(contents.get(i), titles.get(i), windowTitle));
|
||||
}
|
||||
|
||||
OutputFile outputFile = createOutputFile(outputContent, windowTitle);
|
||||
|
||||
CommandLineTokenizer parameterTokenizer = new CommandLineTokenizer(settings.getMergeParameters(), true);
|
||||
|
||||
List<String> args = new ArrayList<String>();
|
||||
while (parameterTokenizer.hasMoreTokens()) {
|
||||
String arg = parameterTokenizer.nextToken();
|
||||
if ("%1".equals(arg)) {
|
||||
args.add(files.get(0));
|
||||
}
|
||||
else if ("%2".equals(arg)) {
|
||||
args.add(files.get(2));
|
||||
}
|
||||
else if ("%3".equals(arg)) {
|
||||
args.add(files.get(1));
|
||||
}
|
||||
else if ("%4".equals(arg)) {
|
||||
args.add(outputFile.getPath());
|
||||
}
|
||||
else {
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
GeneralCommandLine commandLine = new GeneralCommandLine();
|
||||
commandLine.setExePath(settings.getMergeExePath());
|
||||
|
||||
commandLine.addParameters(args);
|
||||
final Process process = commandLine.createProcess();
|
||||
|
||||
if (settings.isMergeTrustExitCode()) {
|
||||
final Ref<Boolean> resultRef = new Ref<Boolean>();
|
||||
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Waiting for external tool", true) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
|
||||
final Thread waiter = new Thread("external process waiter") {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
resultRef.set(process.waitFor() == 0);
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
finally {
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
};
|
||||
waiter.start();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
indicator.checkCanceled();
|
||||
if (semaphore.tryAcquire(200, TimeUnit.MILLISECONDS)) break;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
finally {
|
||||
waiter.interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
success = resultRef.get() == Boolean.TRUE;
|
||||
}
|
||||
else {
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Launching external tool", false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(true);
|
||||
TimeoutUtil.sleep(1000);
|
||||
}
|
||||
});
|
||||
|
||||
success = Messages.showYesNoDialog(project,
|
||||
"Press \"Mark as Resolved\" when you finish resolving conflicts in the external tool",
|
||||
"Merge In External Tool", "Mark as Resolved", "Revert", null) == Messages.YES;
|
||||
}
|
||||
|
||||
if (success) outputFile.finish();
|
||||
}
|
||||
finally {
|
||||
request.applyResult(success ? MergeResult.RESOLVED : MergeResult.CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
private interface OutputFile {
|
||||
@NotNull
|
||||
String getPath();
|
||||
|
||||
void finish() throws IOException;
|
||||
}
|
||||
|
||||
private static class LocalOutputFile implements OutputFile {
|
||||
@NotNull private final VirtualFile myFile;
|
||||
|
||||
public LocalOutputFile(@NotNull VirtualFile file) {
|
||||
myFile = file;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPath() {
|
||||
return myFile.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
myFile.refresh(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NonLocalOutputFile implements OutputFile {
|
||||
@NotNull private final VirtualFile myFile;
|
||||
@NotNull private final File myLocalFile;
|
||||
|
||||
public NonLocalOutputFile(@NotNull VirtualFile file, @NotNull File localFile) {
|
||||
myFile = file;
|
||||
myLocalFile = localFile;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPath() {
|
||||
return myLocalFile.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() throws IOException {
|
||||
myFile.setBinaryContent(FileUtil.loadFileBytes(myLocalFile));
|
||||
}
|
||||
}
|
||||
|
||||
private static class DocumentOutputFile implements OutputFile {
|
||||
@NotNull private final Document myDocument;
|
||||
@NotNull private final File myLocalFile;
|
||||
@NotNull private final Charset myCharset;
|
||||
|
||||
public DocumentOutputFile(@NotNull Document document, @Nullable Charset charset, @NotNull File localFile) {
|
||||
myDocument = document;
|
||||
myLocalFile = localFile;
|
||||
// TODO: potentially dangerous operation - we're using default charset
|
||||
myCharset = charset != null ? charset : Charset.defaultCharset();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPath() {
|
||||
return myLocalFile.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() throws IOException {
|
||||
final String content = StringUtil.convertLineSeparators(FileUtil.loadFile(myLocalFile, myCharset));
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myDocument.setText(content);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.tools.external;
|
||||
|
||||
import com.intellij.diff.DiffManagerEx;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.contents.FileContent;
|
||||
import com.intellij.diff.merge.MergeRequest;
|
||||
import com.intellij.diff.merge.ThreesideMergeRequest;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ExternalMergeTool {
|
||||
public static final Logger LOG = Logger.getInstance(ExternalMergeTool.class);
|
||||
|
||||
public static boolean isDefault() {
|
||||
return ExternalDiffSettings.getInstance().isMergeEnabled();
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return ExternalDiffSettings.getInstance().isMergeEnabled();
|
||||
}
|
||||
|
||||
public static void show(@Nullable final Project project,
|
||||
@NotNull final MergeRequest request) {
|
||||
try {
|
||||
if (canShow(request)) {
|
||||
showRequest(project, request);
|
||||
}
|
||||
else {
|
||||
DiffManagerEx.getInstance().showMergeBuiltin(project, request);
|
||||
}
|
||||
}
|
||||
catch (ProcessCanceledException ignore) {
|
||||
}
|
||||
catch (Throwable e) {
|
||||
LOG.error(e);
|
||||
Messages.showErrorDialog(project, e.getMessage(), "Can't Show Merge In External Tool");
|
||||
}
|
||||
}
|
||||
|
||||
public static void showRequest(@Nullable Project project, @NotNull MergeRequest request)
|
||||
throws ExecutionException, IOException {
|
||||
ExternalDiffSettings settings = ExternalDiffSettings.getInstance();
|
||||
|
||||
ExternalDiffToolUtil.executeMerge(project, settings, (ThreesideMergeRequest)request);
|
||||
}
|
||||
|
||||
public static boolean canShow(@NotNull MergeRequest request) {
|
||||
if (request instanceof ThreesideMergeRequest) {
|
||||
DiffContent outputContent = ((ThreesideMergeRequest)request).getOutputContent();
|
||||
if (!canProcessOutputContent(outputContent)) return false;
|
||||
|
||||
List<? extends DiffContent> contents = ((ThreesideMergeRequest)request).getContents();
|
||||
if (contents.size() != 3) return false;
|
||||
for (DiffContent content : contents) {
|
||||
if (!ExternalDiffToolUtil.canCreateFile(content)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canProcessOutputContent(@NotNull DiffContent content) {
|
||||
if (content instanceof DocumentContent) return true;
|
||||
if (content instanceof FileContent && ((FileContent)content).getFile().isInLocalFileSystem()) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -257,13 +257,17 @@ public class UnifiedDiffChange {
|
||||
return new DumbAwareAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
if (myViewer.isStateIsOutOfDate()) return;
|
||||
if (!myViewer.isEditable(sourceSide.other(), true)) return;
|
||||
|
||||
final Project project = e.getProject();
|
||||
final Document document = myViewer.getDocument(sourceSide.other());
|
||||
|
||||
DiffUtil.executeWriteCommand(document, project, "Replace change", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myViewer.applyChange(UnifiedDiffChange.this, sourceSide);
|
||||
myViewer.replaceChange(UnifiedDiffChange.this, sourceSide);
|
||||
myViewer.scheduleRediff();
|
||||
}
|
||||
});
|
||||
// applyChange() will schedule rediff, but we want to try to do it in sync
|
||||
|
||||
@@ -30,10 +30,9 @@ import com.intellij.diff.tools.util.base.*;
|
||||
import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.Separator;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -50,6 +49,7 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -60,10 +60,7 @@ import gnu.trove.TIntFunction;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.diff.util.DiffUtil.getLineCount;
|
||||
|
||||
@@ -124,6 +121,11 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
new MyOpenInEditorWithMouseAction().register(getEditors());
|
||||
|
||||
TextDiffViewerUtil.checkDifferentDocuments(myRequest);
|
||||
|
||||
DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
DiffUtil.registerAction(new AppendSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.RIGHT, true), myPanel);
|
||||
DiffUtil.registerAction(new AppendSelectedChangesAction(Side.RIGHT, true), myPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,6 +135,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
installEditorListeners();
|
||||
installTypingSupport();
|
||||
myPanel.setLoadingContent(); // We need loading panel only for initial rediff()
|
||||
myPanel.setPersistentNotifications(DiffUtil.getCustomNotifications(myContext, myRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,7 +211,19 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
|
||||
@NotNull
|
||||
protected List<AnAction> createEditorPopupActions() {
|
||||
return TextDiffViewerUtil.createEditorPopupActions();
|
||||
List<AnAction> group = new ArrayList<AnAction>();
|
||||
|
||||
group.add(new ReplaceSelectedChangesAction(Side.LEFT, false));
|
||||
group.add(new AppendSelectedChangesAction(Side.LEFT, false));
|
||||
group.add(new ReplaceSelectedChangesAction(Side.RIGHT, false));
|
||||
group.add(new AppendSelectedChangesAction(Side.RIGHT, false));
|
||||
group.add(new RevertSelectedChangesAction(Side.LEFT));
|
||||
group.add(new RevertSelectedChangesAction(Side.RIGHT));
|
||||
group.add(Separator.getInstance());
|
||||
|
||||
group.addAll(TextDiffViewerUtil.createEditorPopupActions());
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
@@ -621,26 +636,163 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
scheduleRediff();
|
||||
}
|
||||
|
||||
@CalledWithWriteLock
|
||||
public void applyChange(@NotNull UnifiedDiffChange change, @NotNull Side sourceSide) {
|
||||
if (myStateIsOutOfDate || myChangedBlockData == null) return;
|
||||
//
|
||||
// Modification operations
|
||||
//
|
||||
|
||||
Side affectedSide = sourceSide.other();
|
||||
if (!isEditable(affectedSide, true)) return;
|
||||
private abstract class ApplySelectedChangesActionBase extends AnAction implements DumbAware {
|
||||
@NotNull protected final Side myModifiedSide;
|
||||
private final boolean myShortcut;
|
||||
|
||||
public ApplySelectedChangesActionBase(@NotNull Side modifiedSide, boolean shortcut) {
|
||||
myModifiedSide = modifiedSide;
|
||||
myShortcut = shortcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
if (myShortcut) {
|
||||
// consume shortcut even if there are nothing to do - avoid calling some other action
|
||||
e.getPresentation().setEnabledAndVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor != getEditor()) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEditable(myModifiedSide, true) || isStateIsOutOfDate()) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
e.getPresentation().setVisible(true);
|
||||
e.getPresentation().setEnabled(isSomeChangeSelected());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
final List<UnifiedDiffChange> selectedChanges = getSelectedChanges();
|
||||
if (selectedChanges.isEmpty()) return;
|
||||
|
||||
if (!isEditable(myModifiedSide, true)) return;
|
||||
if (isStateIsOutOfDate()) return;
|
||||
|
||||
String title = e.getPresentation().getText() + " selected changes";
|
||||
DiffUtil.executeWriteCommand(getDocument(myModifiedSide), e.getProject(), title, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// state is invalidated during apply(), but changes are in reverse order, so they should not conflict with each other
|
||||
apply(selectedChanges);
|
||||
scheduleRediff();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean isSomeChangeSelected() {
|
||||
if (myChangedBlockData == null) return false;
|
||||
List<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
|
||||
if (changes.isEmpty()) return false;
|
||||
|
||||
List<Caret> carets = getEditor().getCaretModel().getAllCarets();
|
||||
if (carets.size() != 1) return true;
|
||||
Caret caret = carets.get(0);
|
||||
if (caret.hasSelection()) return true;
|
||||
int line = getEditor().getDocument().getLineNumber(getEditor().getExpectedCaretOffset());
|
||||
|
||||
for (UnifiedDiffChange change : changes) {
|
||||
if (DiffUtil.isSelectedByLine(line, change.getLine1(), change.getLine2())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@CalledWithWriteLock
|
||||
protected abstract void apply(@NotNull List<UnifiedDiffChange> changes);
|
||||
}
|
||||
|
||||
private class ReplaceSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public ReplaceSelectedChangesAction(@NotNull Side focusedSide, boolean shortcut) {
|
||||
super(focusedSide.other(), shortcut);
|
||||
|
||||
setShortcutSet(ActionManager.getInstance().getAction(focusedSide.select("Diff.ApplyLeftSide", "Diff.ApplyRightSide")).getShortcutSet());
|
||||
getTemplatePresentation().setText("Replace");
|
||||
getTemplatePresentation().setIcon(focusedSide.select(AllIcons.Diff.ArrowRight, AllIcons.Diff.Arrow));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<UnifiedDiffChange> changes) {
|
||||
for (UnifiedDiffChange change : changes) {
|
||||
replaceChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AppendSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public AppendSelectedChangesAction(@NotNull Side focusedSide, boolean shortcut) {
|
||||
super(focusedSide.other(), shortcut);
|
||||
|
||||
setShortcutSet(ActionManager.getInstance().getAction(focusedSide.select("Diff.AppendLeftSide", "Diff.AppendRightSide")).getShortcutSet());
|
||||
getTemplatePresentation().setText("Insert");
|
||||
getTemplatePresentation().setIcon(focusedSide.select(AllIcons.Diff.ArrowRightDown, AllIcons.Diff.ArrowLeftDown));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<UnifiedDiffChange> changes) {
|
||||
for (UnifiedDiffChange change : changes) {
|
||||
appendChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RevertSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public RevertSelectedChangesAction(@NotNull Side focusedSide) {
|
||||
super(focusedSide, false);
|
||||
getTemplatePresentation().setText("Revert");
|
||||
getTemplatePresentation().setIcon(AllIcons.Diff.Remove);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<UnifiedDiffChange> changes) {
|
||||
for (UnifiedDiffChange change : changes) {
|
||||
replaceChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CalledWithWriteLock
|
||||
public void replaceChange(@NotNull UnifiedDiffChange change, @NotNull Side sourceSide) {
|
||||
Side outputSide = sourceSide.other();
|
||||
|
||||
Document document1 = getDocument(Side.LEFT);
|
||||
Document document2 = getDocument(Side.RIGHT);
|
||||
|
||||
LineFragment lineFragment = change.getLineFragment();
|
||||
|
||||
DiffUtil.applyModification(affectedSide.select(document1, document2),
|
||||
affectedSide.getStartLine(lineFragment), affectedSide.getEndLine(lineFragment),
|
||||
DiffUtil.applyModification(outputSide.select(document1, document2),
|
||||
outputSide.getStartLine(lineFragment), outputSide.getEndLine(lineFragment),
|
||||
sourceSide.select(document1, document2),
|
||||
sourceSide.getStartLine(lineFragment), sourceSide.getEndLine(lineFragment));
|
||||
|
||||
// no need to mark myStateIsOutOfDate - it will be made by DocumentListener
|
||||
// TODO: we can apply change manually, without marking state out-of-date. But we'll have to schedule rediff anyway.
|
||||
scheduleRediff();
|
||||
}
|
||||
|
||||
@CalledWithWriteLock
|
||||
public void appendChange(@NotNull UnifiedDiffChange change, @NotNull final Side sourceSide) {
|
||||
Side outputSide = sourceSide.other();
|
||||
|
||||
Document document1 = getDocument(Side.LEFT);
|
||||
Document document2 = getDocument(Side.RIGHT);
|
||||
|
||||
LineFragment lineFragment = change.getLineFragment();
|
||||
if (sourceSide.getStartLine(lineFragment) == sourceSide.getEndLine(lineFragment)) return;
|
||||
|
||||
DiffUtil.applyModification(outputSide.select(document1, document2),
|
||||
outputSide.getEndLine(lineFragment), outputSide.getEndLine(lineFragment),
|
||||
sourceSide.select(document1, document2),
|
||||
sourceSide.getStartLine(lineFragment), sourceSide.getEndLine(lineFragment));
|
||||
}
|
||||
|
||||
//
|
||||
@@ -775,6 +927,26 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@CalledInAwt
|
||||
private List<UnifiedDiffChange> getSelectedChanges() {
|
||||
if (myChangedBlockData == null) return Collections.emptyList();
|
||||
final BitSet lines = DiffUtil.getSelectedLines(myEditor);
|
||||
List<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
|
||||
|
||||
List<UnifiedDiffChange> affectedChanges = new ArrayList<UnifiedDiffChange>();
|
||||
for (int i = changes.size() - 1; i >= 0; i--) {
|
||||
UnifiedDiffChange change = changes.get(i);
|
||||
int line1 = change.getLine1();
|
||||
int line2 = change.getLine2();
|
||||
|
||||
if (DiffUtil.isSelectedByLine(lines, line1, line2)) {
|
||||
affectedChanges.add(change);
|
||||
}
|
||||
}
|
||||
return affectedChanges;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
@Nullable
|
||||
protected OpenFileDescriptor getOpenFileDescriptor(int offset) {
|
||||
|
||||
@@ -30,10 +30,7 @@ import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.Separator;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.DiffNavigationContext;
|
||||
@@ -84,6 +81,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
myFoldingModel = new MyFoldingModel(getEditors(), this);
|
||||
|
||||
myModifierProvider = new ModifierProvider();
|
||||
|
||||
DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
DiffUtil.registerAction(new AppendSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.RIGHT, true), myPanel);
|
||||
DiffUtil.registerAction(new AppendSelectedChangesAction(Side.RIGHT, true), myPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,9 +139,12 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
protected List<AnAction> createEditorPopupActions() {
|
||||
List<AnAction> group = new ArrayList<AnAction>();
|
||||
|
||||
group.add(new ReplaceSelectedChangesAction());
|
||||
group.add(new AppendSelectedChangesAction());
|
||||
group.add(new RevertSelectedChangesAction());
|
||||
group.add(new ReplaceSelectedChangesAction(Side.LEFT, false));
|
||||
group.add(new AppendSelectedChangesAction(Side.LEFT, false));
|
||||
group.add(new ReplaceSelectedChangesAction(Side.RIGHT, false));
|
||||
group.add(new AppendSelectedChangesAction(Side.RIGHT, false));
|
||||
group.add(new RevertSelectedChangesAction(Side.LEFT));
|
||||
group.add(new RevertSelectedChangesAction(Side.RIGHT));
|
||||
group.add(Separator.getInstance());
|
||||
|
||||
group.addAll(super.createEditorPopupActions());
|
||||
@@ -491,18 +496,22 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
//
|
||||
|
||||
private abstract class ApplySelectedChangesActionBase extends AnAction implements DumbAware {
|
||||
private final boolean myModifyOpposite;
|
||||
@NotNull protected final Side myModifiedSide;
|
||||
private final boolean myShortcut;
|
||||
|
||||
public ApplySelectedChangesActionBase(@Nullable String text,
|
||||
@Nullable String description,
|
||||
@Nullable Icon icon,
|
||||
boolean modifyOpposite) {
|
||||
super(text, description, icon);
|
||||
myModifyOpposite = modifyOpposite;
|
||||
public ApplySelectedChangesActionBase(@NotNull Side modifiedSide, boolean shortcut) {
|
||||
myModifiedSide = modifiedSide;
|
||||
myShortcut = shortcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
if (myShortcut) {
|
||||
// consume shortcut even if there are nothing to do - avoid calling some other action
|
||||
e.getPresentation().setEnabledAndVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor != getEditor1() && editor != getEditor2()) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
@@ -510,30 +519,38 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
}
|
||||
|
||||
Side side = Side.fromLeft(editor == getEditor(Side.LEFT));
|
||||
Editor modifiedEditor = getEditor(side.other(myModifyOpposite));
|
||||
if (!isVisible(side)) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Editor modifiedEditor = getEditor(myModifiedSide);
|
||||
if (!DiffUtil.isEditable(modifiedEditor)) {
|
||||
e.getPresentation().setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
e.getPresentation().setIcon(getIcon(side));
|
||||
e.getPresentation().setVisible(true);
|
||||
e.getPresentation().setEnabled(isSomeChangeSelected(side));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor != getEditor1() && editor != getEditor2()) return;
|
||||
|
||||
final Side side = Side.fromLeft(editor == getEditor(Side.LEFT));
|
||||
final List<SimpleDiffChange> selectedChanges = getSelectedChanges(side);
|
||||
if (selectedChanges.isEmpty()) return;
|
||||
|
||||
Editor modifiedEditor = getEditor(myModifiedSide);
|
||||
if (!DiffUtil.isEditable(modifiedEditor)) return;
|
||||
|
||||
Editor modifiedEditor = getEditor(side.other(myModifyOpposite));
|
||||
String title = e.getPresentation().getText() + " selected changes";
|
||||
DiffUtil.executeWriteCommand(modifiedEditor.getDocument(), e.getProject(), title, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
apply(side, selectedChanges);
|
||||
apply(selectedChanges);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -554,66 +571,72 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract Icon getIcon(@NotNull Side side);
|
||||
protected abstract boolean isVisible(@NotNull Side side);
|
||||
|
||||
@CalledWithWriteLock
|
||||
protected abstract void apply(@NotNull Side side, @NotNull List<SimpleDiffChange> changes);
|
||||
protected abstract void apply(@NotNull List<SimpleDiffChange> changes);
|
||||
}
|
||||
|
||||
private class ReplaceSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public ReplaceSelectedChangesAction() {
|
||||
super("Replace", null, AllIcons.Diff.Arrow, true);
|
||||
}
|
||||
public ReplaceSelectedChangesAction(@NotNull Side focusedSide, boolean shortcut) {
|
||||
super(focusedSide.other(), shortcut);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Icon getIcon(@NotNull Side side) {
|
||||
return side.isLeft() ? AllIcons.Diff.ArrowRight : AllIcons.Diff.Arrow;
|
||||
setShortcutSet(ActionManager.getInstance().getAction(focusedSide.select("Diff.ApplyLeftSide", "Diff.ApplyRightSide")).getShortcutSet());
|
||||
getTemplatePresentation().setText("Replace");
|
||||
getTemplatePresentation().setIcon(focusedSide.select(AllIcons.Diff.ArrowRight, AllIcons.Diff.Arrow));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull Side side, @NotNull List<SimpleDiffChange> changes) {
|
||||
protected boolean isVisible(@NotNull Side side) {
|
||||
return side == myModifiedSide.other();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<SimpleDiffChange> changes) {
|
||||
for (SimpleDiffChange change : changes) {
|
||||
replaceChange(change, side);
|
||||
replaceChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AppendSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public AppendSelectedChangesAction() {
|
||||
super("Insert", null, AllIcons.Diff.ArrowLeftDown, true);
|
||||
}
|
||||
public AppendSelectedChangesAction(@NotNull Side focusedSide, boolean shortcut) {
|
||||
super(focusedSide.other(), shortcut);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Icon getIcon(@NotNull Side side) {
|
||||
return side.isLeft() ? AllIcons.Diff.ArrowRightDown : AllIcons.Diff.ArrowLeftDown;
|
||||
setShortcutSet(ActionManager.getInstance().getAction(focusedSide.select("Diff.AppendLeftSide", "Diff.AppendRightSide")).getShortcutSet());
|
||||
getTemplatePresentation().setText("Insert");
|
||||
getTemplatePresentation().setIcon(focusedSide.select(AllIcons.Diff.ArrowRightDown, AllIcons.Diff.ArrowLeftDown));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull Side side, @NotNull List<SimpleDiffChange> changes) {
|
||||
protected boolean isVisible(@NotNull Side side) {
|
||||
return side == myModifiedSide.other();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<SimpleDiffChange> changes) {
|
||||
for (SimpleDiffChange change : changes) {
|
||||
appendChange(change, side);
|
||||
appendChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RevertSelectedChangesAction extends ApplySelectedChangesActionBase {
|
||||
public RevertSelectedChangesAction() {
|
||||
super("Revert", null, AllIcons.Diff.Remove, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Icon getIcon(@NotNull Side side) {
|
||||
return AllIcons.Diff.Remove;
|
||||
public RevertSelectedChangesAction(@NotNull Side focusedSide) {
|
||||
super(focusedSide, false);
|
||||
getTemplatePresentation().setText("Revert");
|
||||
getTemplatePresentation().setIcon(AllIcons.Diff.Remove);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull Side side, @NotNull List<SimpleDiffChange> changes) {
|
||||
protected boolean isVisible(@NotNull Side side) {
|
||||
return side == myModifiedSide;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull List<SimpleDiffChange> changes) {
|
||||
for (SimpleDiffChange change : changes) {
|
||||
replaceChange(change, side.other());
|
||||
replaceChange(change, myModifiedSide.other());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-145
@@ -15,28 +15,25 @@
|
||||
*/
|
||||
package com.intellij.diff.tools.simple;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonManager;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.fragments.MergeLineFragment;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffDrawUtil;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.TextDiffType;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.SeparatorPlacement;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleThreesideDiffChange {
|
||||
@NotNull private final MergeLineFragment myFragment;
|
||||
public class SimpleThreesideDiffChange extends ThreesideDiffChangeBase {
|
||||
@NotNull private final List<? extends EditorEx> myEditors;
|
||||
|
||||
@NotNull private ConflictType myType;
|
||||
@NotNull private final MergeLineFragment myFragment;
|
||||
|
||||
@NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
|
||||
|
||||
@@ -46,10 +43,9 @@ public class SimpleThreesideDiffChange {
|
||||
public SimpleThreesideDiffChange(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
myFragment = fragment;
|
||||
super(fragment, editors, policy);
|
||||
myEditors = editors;
|
||||
|
||||
myType = calcType(fragment, editors, policy);
|
||||
myFragment = fragment;
|
||||
|
||||
installHighlighter();
|
||||
}
|
||||
@@ -58,8 +54,8 @@ public class SimpleThreesideDiffChange {
|
||||
assert myHighlighters.isEmpty();
|
||||
|
||||
createHighlighter(ThreeSide.BASE);
|
||||
if (myType.isLeftChange()) createHighlighter(ThreeSide.LEFT);
|
||||
if (myType.isRightChange()) createHighlighter(ThreeSide.RIGHT);
|
||||
if (getType().isLeftChange()) createHighlighter(ThreeSide.LEFT);
|
||||
if (getType().isRightChange()) createHighlighter(ThreeSide.RIGHT);
|
||||
}
|
||||
|
||||
public void destroyHighlighter() {
|
||||
@@ -107,24 +103,16 @@ public class SimpleThreesideDiffChange {
|
||||
// Getters
|
||||
//
|
||||
|
||||
@Override
|
||||
public int getStartLine(@NotNull ThreeSide side) {
|
||||
return myFragment.getStartLine(side) + side.select(myLineStartShifts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndLine(@NotNull ThreeSide side) {
|
||||
return myFragment.getEndLine(side) + side.select(myLineEndShifts);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextDiffType getDiffType() {
|
||||
return myType.getDiffType();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConflictType getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
//
|
||||
// Shift
|
||||
//
|
||||
@@ -140,125 +128,4 @@ public class SimpleThreesideDiffChange {
|
||||
|
||||
return newRange.damaged;
|
||||
}
|
||||
|
||||
//
|
||||
// Type
|
||||
//
|
||||
|
||||
@NotNull
|
||||
private static ConflictType calcType(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
boolean isLeftEmpty = isIntervalEmpty(fragment, ThreeSide.LEFT);
|
||||
boolean isBaseEmpty = isIntervalEmpty(fragment, ThreeSide.BASE);
|
||||
boolean isRightEmpty = isIntervalEmpty(fragment, ThreeSide.RIGHT);
|
||||
assert !isLeftEmpty || !isBaseEmpty || !isRightEmpty;
|
||||
|
||||
if (isBaseEmpty) {
|
||||
if (isLeftEmpty) { // --=
|
||||
return new ConflictType(TextDiffType.INSERTED, false, true);
|
||||
}
|
||||
else if (isRightEmpty) { // =--
|
||||
return new ConflictType(TextDiffType.INSERTED, true, false);
|
||||
}
|
||||
else { // =-=
|
||||
boolean equalModifications = compareLeftAndRight(fragment, editors, policy);
|
||||
return new ConflictType(equalModifications ? TextDiffType.INSERTED : TextDiffType.CONFLICT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isLeftEmpty && isRightEmpty) { // -=-
|
||||
return new ConflictType(TextDiffType.DELETED);
|
||||
}
|
||||
else { // -==, ==-, ===
|
||||
boolean unchangedLeft = compareWithBase(fragment, editors, ThreeSide.LEFT);
|
||||
boolean unchangedRight = compareWithBase(fragment, editors, ThreeSide.RIGHT);
|
||||
assert !unchangedLeft || !unchangedRight;
|
||||
|
||||
if (unchangedLeft) return new ConflictType(isRightEmpty ? TextDiffType.DELETED : TextDiffType.MODIFIED, false, true);
|
||||
if (unchangedRight) return new ConflictType(isLeftEmpty ? TextDiffType.DELETED : TextDiffType.MODIFIED, true, false);
|
||||
|
||||
boolean equalModifications = compareLeftAndRight(fragment, editors, policy);
|
||||
return new ConflictType(equalModifications ? TextDiffType.MODIFIED : TextDiffType.CONFLICT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean compareLeftAndRight(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
CharSequence content1 = getRangeContent(fragment, editors, ThreeSide.LEFT);
|
||||
CharSequence content2 = getRangeContent(fragment, editors, ThreeSide.RIGHT);
|
||||
|
||||
if (policy == ComparisonPolicy.IGNORE_WHITESPACES) {
|
||||
if (content1 == null) content1 = "";
|
||||
if (content2 == null) content2 = "";
|
||||
}
|
||||
|
||||
if (content1 == null && content2 == null) return true;
|
||||
if (content1 == null ^ content2 == null) return false;
|
||||
|
||||
return ComparisonManager.getInstance().isEquals(content1, content2, policy);
|
||||
}
|
||||
|
||||
private static boolean compareWithBase(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ThreeSide side) {
|
||||
CharSequence content1 = getRangeContent(fragment, editors, ThreeSide.BASE);
|
||||
CharSequence content2 = getRangeContent(fragment, editors, side);
|
||||
|
||||
return StringUtil.equals(content1, content2);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CharSequence getRangeContent(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ThreeSide side) {
|
||||
DocumentEx document = side.select(editors).getDocument();
|
||||
int line1 = fragment.getStartLine(side);
|
||||
int line2 = fragment.getEndLine(side);
|
||||
if (line1 == line2) return null;
|
||||
return DiffUtil.getLinesContent(document, line1, line2);
|
||||
}
|
||||
|
||||
private static boolean isIntervalEmpty(@NotNull MergeLineFragment fragment, @NotNull ThreeSide side) {
|
||||
return fragment.getStartLine(side) == fragment.getEndLine(side);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
public static class ConflictType {
|
||||
@NotNull private final TextDiffType myType;
|
||||
private final boolean myLeftChange;
|
||||
private final boolean myRightChange;
|
||||
|
||||
public ConflictType(@NotNull TextDiffType type) {
|
||||
this(type, true, true);
|
||||
}
|
||||
|
||||
public ConflictType(@NotNull TextDiffType type, boolean leftChange, boolean rightChange) {
|
||||
myType = type;
|
||||
myLeftChange = leftChange;
|
||||
myRightChange = rightChange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextDiffType getDiffType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public boolean isLeftChange() {
|
||||
return myLeftChange;
|
||||
}
|
||||
|
||||
public boolean isRightChange() {
|
||||
return myRightChange;
|
||||
}
|
||||
|
||||
public boolean isChange(@NotNull Side side) {
|
||||
return side.isLeft() ? myLeftChange : myRightChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-342
@@ -26,7 +26,7 @@ import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.fragments.MergeLineFragment;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.tools.util.*;
|
||||
import com.intellij.diff.tools.util.DiffNotifications;
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy;
|
||||
import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
|
||||
import com.intellij.diff.tools.util.side.ThreesideTextDiffViewer;
|
||||
@@ -34,79 +34,31 @@ import com.intellij.diff.util.DiffDividerDrawUtil;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.Separator;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.ButtonlessScrollBarUI;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: extract common methods with Twoside
|
||||
public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx {
|
||||
public static final Logger LOG = Logger.getInstance(SimpleThreesideDiffViewer.class);
|
||||
|
||||
@NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable1;
|
||||
@NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable2;
|
||||
|
||||
@NotNull private final PrevNextDifferenceIterable myPrevNextDifferenceIterable;
|
||||
@NotNull private final MyStatusPanel myStatusPanel;
|
||||
|
||||
@NotNull private final List<SimpleThreesideDiffChange> myDiffChanges = new ArrayList<SimpleThreesideDiffChange>();
|
||||
@NotNull private final List<SimpleThreesideDiffChange> myInvalidDiffChanges = new ArrayList<SimpleThreesideDiffChange>();
|
||||
private int myChangesCount = -1;
|
||||
private int myConflictsCount = -1;
|
||||
|
||||
@NotNull private final MyFoldingModel myFoldingModel;
|
||||
@NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
|
||||
|
||||
public SimpleThreesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
super(context, (ContentDiffRequest)request);
|
||||
|
||||
mySyncScrollable1 = new MySyncScrollable(Side.LEFT);
|
||||
mySyncScrollable2 = new MySyncScrollable(Side.RIGHT);
|
||||
myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
|
||||
myStatusPanel = new MyStatusPanel();
|
||||
myFoldingModel = new MyFoldingModel(getEditors().toArray(new EditorEx[3]), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
myContentPanel.setPainter(new MyDividerPainter(Side.LEFT), Side.LEFT);
|
||||
myContentPanel.setPainter(new MyDividerPainter(Side.RIGHT), Side.RIGHT);
|
||||
myContentPanel.setScrollbarPainter(new MyScrollbarPainter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onDispose() {
|
||||
destroyChangedBlocks();
|
||||
super.onDispose();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -145,30 +97,10 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void processContextHints() {
|
||||
super.processContextHints();
|
||||
myInitialScrollHelper.processContext(myRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void updateContextHints() {
|
||||
super.updateContextHints();
|
||||
myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
|
||||
myInitialScrollHelper.updateContext(myRequest);
|
||||
}
|
||||
|
||||
//
|
||||
// Diff
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public FoldingModelSupport.Settings getFoldingModelSettings() {
|
||||
return TextDiffViewerUtil.getFoldingModelSettings(myContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSlowRediff() {
|
||||
super.onSlowRediff();
|
||||
@@ -227,13 +159,11 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
|
||||
clearDiffPresentation();
|
||||
|
||||
myChangesCount = 0;
|
||||
myConflictsCount = 0;
|
||||
resetChangeCounters();
|
||||
for (MergeLineFragment fragment : fragments) {
|
||||
SimpleThreesideDiffChange change = new SimpleThreesideDiffChange(fragment, getEditors(), comparisonPolicy);
|
||||
myDiffChanges.add(change);
|
||||
if (change.getDiffType() != TextDiffType.CONFLICT) myChangesCount++;
|
||||
if (change.getDiffType() == TextDiffType.CONFLICT) myConflictsCount++;
|
||||
onChangeAdded(change);
|
||||
}
|
||||
|
||||
myFoldingModel.install(fragments, myRequest, getFoldingModelSettings());
|
||||
@@ -246,24 +176,8 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Runnable applyNotification(@Nullable final JComponent notification) {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearDiffPresentation();
|
||||
if (notification != null) myPanel.addNotification(notification);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void clearDiffPresentation() {
|
||||
myStatusPanel.setBusy(false);
|
||||
myPanel.resetNotifications();
|
||||
destroyChangedBlocks();
|
||||
}
|
||||
|
||||
private void destroyChangedBlocks() {
|
||||
protected void destroyChangedBlocks() {
|
||||
super.destroyChangedBlocks();
|
||||
for (SimpleThreesideDiffChange change : myDiffChanges) {
|
||||
change.destroyHighlighter();
|
||||
}
|
||||
@@ -273,14 +187,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
change.destroyHighlighter();
|
||||
}
|
||||
myInvalidDiffChanges.clear();
|
||||
|
||||
myChangesCount = -1;
|
||||
myConflictsCount = -1;
|
||||
|
||||
myFoldingModel.destroy();
|
||||
|
||||
myContentPanel.repaintDividers();
|
||||
myStatusPanel.update();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -319,28 +225,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDocumentChange(@NotNull DocumentEvent e) {
|
||||
super.onDocumentChange(e);
|
||||
myFoldingModel.onDocumentChanged(e);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) {
|
||||
SimpleThreesideDiffChange targetChange = scrollToPolicy.select(myDiffChanges);
|
||||
if (targetChange == null) return false;
|
||||
|
||||
doScrollToChange(targetChange, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doScrollToChange(@NotNull SimpleThreesideDiffChange change, boolean animated) {
|
||||
// TODO: use anchors to fix scrolling issue at the start/end of file
|
||||
EditorEx editor = getCurrentEditor();
|
||||
int line = change.getStartLine(getCurrentSide());
|
||||
DiffUtil.scrollEditor(editor, line, animated);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private IgnorePolicy getIgnorePolicy() {
|
||||
IgnorePolicy policy = getTextSettings().getIgnorePolicy();
|
||||
@@ -353,41 +237,20 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
//
|
||||
|
||||
@NotNull
|
||||
protected List<SimpleThreesideDiffChange> getDiffChanges() {
|
||||
public List<SimpleThreesideDiffChange> getChanges() {
|
||||
return myDiffChanges;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected SyncScrollSupport.SyncScrollable getSyncScrollable(@NotNull Side side) {
|
||||
return side.select(mySyncScrollable1, mySyncScrollable2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected JComponent getStatusPanel() {
|
||||
return myStatusPanel;
|
||||
protected DiffDividerDrawUtil.DividerPaintable getDividerPaintable(@NotNull Side side) {
|
||||
return new MyDividerPaintable(side);
|
||||
}
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
|
||||
@Nullable
|
||||
@CalledInAwt
|
||||
private SimpleThreesideDiffChange getSelectedChange(@NotNull ThreeSide side) {
|
||||
EditorEx editor = getEditor(side);
|
||||
int caretLine = editor.getCaretModel().getLogicalPosition().line;
|
||||
|
||||
for (SimpleThreesideDiffChange change : myDiffChanges) {
|
||||
int line1 = change.getStartLine(side);
|
||||
int line2 = change.getEndLine(side);
|
||||
|
||||
if (DiffUtil.isSelectedByLine(caretLine, line1, line2)) return change;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
|
||||
public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
return ThreesideTextDiffViewer.canShowRequest(context, request);
|
||||
@@ -397,46 +260,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
// Actions
|
||||
//
|
||||
|
||||
private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<SimpleThreesideDiffChange> {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<SimpleThreesideDiffChange> getChanges() {
|
||||
return myDiffChanges;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected EditorEx getEditor() {
|
||||
return getCurrentEditor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStartLine(@NotNull SimpleThreesideDiffChange change) {
|
||||
return change.getStartLine(getCurrentSide());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getEndLine(@NotNull SimpleThreesideDiffChange change) {
|
||||
return change.getEndLine(getCurrentSide());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollToChange(@NotNull SimpleThreesideDiffChange change) {
|
||||
doScrollToChange(change, true);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction {
|
||||
public MyToggleExpandByDefaultAction() {
|
||||
super(getTextSettings());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void expandAll(boolean expand) {
|
||||
myFoldingModel.expandAll(expand);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
|
||||
public MyIgnorePolicySettingAction() {
|
||||
super(getTextSettings());
|
||||
@@ -472,48 +295,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
// Helpers
|
||||
//
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
if (DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.is(dataId)) {
|
||||
return myPrevNextDifferenceIterable;
|
||||
}
|
||||
else if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
|
||||
SimpleThreesideDiffChange change = getSelectedChange(getCurrentSide());
|
||||
if (change != null) {
|
||||
return new LineRange(change.getStartLine(getCurrentSide()), change.getEndLine(getCurrentSide()));
|
||||
}
|
||||
}
|
||||
|
||||
return super.getData(dataId);
|
||||
}
|
||||
|
||||
private class MySyncScrollable extends BaseSyncScrollable {
|
||||
@NotNull private final Side mySide;
|
||||
|
||||
public MySyncScrollable(@NotNull Side side) {
|
||||
mySide = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSyncScrollEnabled() {
|
||||
return getTextSettings().isEnableSyncScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processHelper(@NotNull ScrollHelper helper) {
|
||||
ThreeSide left = mySide.select(ThreeSide.LEFT, ThreeSide.BASE);
|
||||
ThreeSide right = mySide.select(ThreeSide.BASE, ThreeSide.RIGHT);
|
||||
|
||||
if (!helper.process(0, 0)) return;
|
||||
for (SimpleThreesideDiffChange diffChange : myDiffChanges) {
|
||||
if (!helper.process(diffChange.getStartLine(left), diffChange.getStartLine(right))) return;
|
||||
if (!helper.process(diffChange.getEndLine(left), diffChange.getEndLine(right))) return;
|
||||
}
|
||||
helper.process(getEditor(left).getDocument().getLineCount(), getEditor(right).getDocument().getLineCount());
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDividerPaintable implements DiffDividerDrawUtil.DividerPaintable {
|
||||
@NotNull private final Side mySide;
|
||||
|
||||
@@ -527,7 +308,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
ThreeSide right = mySide.select(ThreeSide.BASE, ThreeSide.RIGHT);
|
||||
|
||||
for (SimpleThreesideDiffChange diffChange : myDiffChanges) {
|
||||
if (!diffChange.getType().isChange(mySide)) continue;
|
||||
if (!diffChange.isChange(mySide)) continue;
|
||||
if (!handler.process(diffChange.getStartLine(left), diffChange.getEndLine(left),
|
||||
diffChange.getStartLine(right), diffChange.getEndLine(right),
|
||||
diffChange.getDiffType().getColor(getEditor(ThreeSide.BASE)))) {
|
||||
@@ -536,117 +317,4 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDividerPainter implements DiffSplitter.Painter {
|
||||
@NotNull private final Side mySide;
|
||||
@NotNull private final MyDividerPaintable myPaintable;
|
||||
|
||||
public MyDividerPainter(@NotNull Side side) {
|
||||
mySide = side;
|
||||
myPaintable = new MyDividerPaintable(side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(@NotNull Graphics g, @NotNull JComponent divider) {
|
||||
Graphics2D gg = DiffDividerDrawUtil.getDividerGraphics(g, divider, getEditor(ThreeSide.BASE).getComponent());
|
||||
|
||||
gg.setColor(DiffDrawUtil.getDividerColor(getEditor(ThreeSide.BASE)));
|
||||
gg.fill(gg.getClipBounds());
|
||||
|
||||
Editor editor1 = mySide.select(getEditor(ThreeSide.LEFT), getEditor(ThreeSide.BASE));
|
||||
Editor editor2 = mySide.select(getEditor(ThreeSide.BASE), getEditor(ThreeSide.RIGHT));
|
||||
|
||||
//DividerPolygonUtil.paintSimplePolygons(gg, divider.getWidth(), editor1, editor2, myPaintable);
|
||||
DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), editor1, editor2, myPaintable);
|
||||
|
||||
myFoldingModel.paintOnDivider(gg, divider, mySide);
|
||||
|
||||
gg.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyScrollbarPainter implements ButtonlessScrollBarUI.ScrollbarRepaintCallback {
|
||||
@NotNull private final MyDividerPaintable myPaintable = new MyDividerPaintable(Side.RIGHT);
|
||||
|
||||
@Override
|
||||
public void call(Graphics g) {
|
||||
EditorEx editor1 = getEditor(ThreeSide.BASE);
|
||||
EditorEx editor2 = getEditor(ThreeSide.RIGHT);
|
||||
|
||||
int width = editor1.getScrollPane().getVerticalScrollBar().getWidth();
|
||||
DiffDividerDrawUtil.paintPolygonsOnScrollbar((Graphics2D)g, width, editor1, editor2, myPaintable);
|
||||
|
||||
myFoldingModel.paintOnScrollbar((Graphics2D)g, width);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyStatusPanel extends StatusPanel {
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getMessage() {
|
||||
if (myChangesCount < 0 || myConflictsCount < 0) return null;
|
||||
if (myChangesCount == 0 && myConflictsCount == 0) {
|
||||
return DiffBundle.message("merge.dialog.all.conflicts.resolved.message.text");
|
||||
}
|
||||
return makeCounterWord(myChangesCount, "change") + ". " + makeCounterWord(myConflictsCount, "conflict");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String makeCounterWord(int number, @NotNull String word) {
|
||||
if (number == 0) {
|
||||
return "No " + StringUtil.pluralize(word);
|
||||
}
|
||||
return number + " " + StringUtil.pluralize(word, number);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyFoldingModel extends FoldingModelSupport {
|
||||
private final MyPaintable myPaintable1 = new MyPaintable(0, 1);
|
||||
private final MyPaintable myPaintable2 = new MyPaintable(1, 2);
|
||||
|
||||
public MyFoldingModel(@NotNull EditorEx[] editors, @NotNull Disposable disposable) {
|
||||
super(editors, disposable);
|
||||
assert editors.length == 3;
|
||||
}
|
||||
|
||||
public void install(@Nullable List<MergeLineFragment> fragments,
|
||||
@NotNull UserDataHolder context,
|
||||
@NotNull FoldingModelSupport.Settings settings) {
|
||||
Iterator<int[]> it = map(fragments, new Function<MergeLineFragment, int[]>() {
|
||||
@Override
|
||||
public int[] fun(MergeLineFragment fragment) {
|
||||
return new int[]{
|
||||
fragment.getStartLine(ThreeSide.LEFT),
|
||||
fragment.getEndLine(ThreeSide.LEFT),
|
||||
fragment.getStartLine(ThreeSide.BASE),
|
||||
fragment.getEndLine(ThreeSide.BASE),
|
||||
fragment.getStartLine(ThreeSide.RIGHT),
|
||||
fragment.getEndLine(ThreeSide.RIGHT)};
|
||||
}
|
||||
});
|
||||
install(it, context, settings);
|
||||
}
|
||||
|
||||
public void paintOnDivider(@NotNull Graphics2D gg, @NotNull Component divider, @NotNull Side side) {
|
||||
MyPaintable paintable = side.select(myPaintable1, myPaintable2);
|
||||
paintable.paintOnDivider(gg, divider);
|
||||
}
|
||||
|
||||
public void paintOnScrollbar(@NotNull Graphics2D gg, int width) {
|
||||
myPaintable2.paintOnScrollbar(gg, width);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyInitialScrollHelper extends MyInitialScrollPositionHelper {
|
||||
@Override
|
||||
protected boolean doScrollToChange() {
|
||||
if (myScrollToChange == null) return false;
|
||||
return SimpleThreesideDiffViewer.this.doScrollToChange(myScrollToChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doScrollToFirstChange() {
|
||||
return SimpleThreesideDiffViewer.this.doScrollToChange(ScrollToPolicy.FIRST_CHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.tools.simple;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonManager;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.fragments.MergeLineFragment;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.diff.util.TextDiffType;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ThreesideDiffChangeBase {
|
||||
@NotNull private ConflictType myType;
|
||||
|
||||
public ThreesideDiffChangeBase(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
myType = calcType(fragment, editors, policy);
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
public abstract int getStartLine(@NotNull ThreeSide side);
|
||||
|
||||
public abstract int getEndLine(@NotNull ThreeSide side);
|
||||
|
||||
@NotNull
|
||||
public TextDiffType getDiffType() {
|
||||
return myType.getDiffType();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConflictType getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public boolean isConflict() {
|
||||
return getDiffType() == TextDiffType.CONFLICT;
|
||||
}
|
||||
|
||||
public boolean isChange(@NotNull Side side) {
|
||||
return myType.isChange(side);
|
||||
}
|
||||
|
||||
//
|
||||
// Type
|
||||
//
|
||||
|
||||
@NotNull
|
||||
private static ConflictType calcType(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
boolean isLeftEmpty = isIntervalEmpty(fragment, ThreeSide.LEFT);
|
||||
boolean isBaseEmpty = isIntervalEmpty(fragment, ThreeSide.BASE);
|
||||
boolean isRightEmpty = isIntervalEmpty(fragment, ThreeSide.RIGHT);
|
||||
assert !isLeftEmpty || !isBaseEmpty || !isRightEmpty;
|
||||
|
||||
if (isBaseEmpty) {
|
||||
if (isLeftEmpty) { // --=
|
||||
return new ConflictType(TextDiffType.INSERTED, false, true);
|
||||
}
|
||||
else if (isRightEmpty) { // =--
|
||||
return new ConflictType(TextDiffType.INSERTED, true, false);
|
||||
}
|
||||
else { // =-=
|
||||
boolean equalModifications = compareLeftAndRight(fragment, editors, policy);
|
||||
return new ConflictType(equalModifications ? TextDiffType.INSERTED : TextDiffType.CONFLICT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isLeftEmpty && isRightEmpty) { // -=-
|
||||
return new ConflictType(TextDiffType.DELETED);
|
||||
}
|
||||
else { // -==, ==-, ===
|
||||
boolean unchangedLeft = compareWithBase(fragment, editors, ThreeSide.LEFT);
|
||||
boolean unchangedRight = compareWithBase(fragment, editors, ThreeSide.RIGHT);
|
||||
assert !unchangedLeft || !unchangedRight;
|
||||
|
||||
if (unchangedLeft) return new ConflictType(isRightEmpty ? TextDiffType.DELETED : TextDiffType.MODIFIED, false, true);
|
||||
if (unchangedRight) return new ConflictType(isLeftEmpty ? TextDiffType.DELETED : TextDiffType.MODIFIED, true, false);
|
||||
|
||||
boolean equalModifications = compareLeftAndRight(fragment, editors, policy);
|
||||
return new ConflictType(equalModifications ? TextDiffType.MODIFIED : TextDiffType.CONFLICT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean compareLeftAndRight(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ComparisonPolicy policy) {
|
||||
CharSequence content1 = getRangeContent(fragment, editors, ThreeSide.LEFT);
|
||||
CharSequence content2 = getRangeContent(fragment, editors, ThreeSide.RIGHT);
|
||||
|
||||
if (policy == ComparisonPolicy.IGNORE_WHITESPACES) {
|
||||
if (content1 == null) content1 = "";
|
||||
if (content2 == null) content2 = "";
|
||||
}
|
||||
|
||||
if (content1 == null && content2 == null) return true;
|
||||
if (content1 == null ^ content2 == null) return false;
|
||||
|
||||
return ComparisonManager.getInstance().isEquals(content1, content2, policy);
|
||||
}
|
||||
|
||||
private static boolean compareWithBase(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ThreeSide side) {
|
||||
CharSequence content1 = getRangeContent(fragment, editors, ThreeSide.BASE);
|
||||
CharSequence content2 = getRangeContent(fragment, editors, side);
|
||||
|
||||
return StringUtil.equals(content1, content2);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CharSequence getRangeContent(@NotNull MergeLineFragment fragment,
|
||||
@NotNull List<? extends EditorEx> editors,
|
||||
@NotNull ThreeSide side) {
|
||||
DocumentEx document = side.select(editors).getDocument();
|
||||
int line1 = fragment.getStartLine(side);
|
||||
int line2 = fragment.getEndLine(side);
|
||||
if (line1 == line2) return null;
|
||||
return DiffUtil.getLinesContent(document, line1, line2);
|
||||
}
|
||||
|
||||
private static boolean isIntervalEmpty(@NotNull MergeLineFragment fragment, @NotNull ThreeSide side) {
|
||||
return fragment.getStartLine(side) == fragment.getEndLine(side);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
public static class ConflictType {
|
||||
@NotNull private final TextDiffType myType;
|
||||
private final boolean myLeftChange;
|
||||
private final boolean myRightChange;
|
||||
|
||||
public ConflictType(@NotNull TextDiffType type) {
|
||||
this(type, true, true);
|
||||
}
|
||||
|
||||
public ConflictType(@NotNull TextDiffType type, boolean leftChange, boolean rightChange) {
|
||||
myType = type;
|
||||
myLeftChange = leftChange;
|
||||
myRightChange = rightChange;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextDiffType getDiffType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public boolean isLeftChange() {
|
||||
return myLeftChange;
|
||||
}
|
||||
|
||||
public boolean isRightChange() {
|
||||
return myRightChange;
|
||||
}
|
||||
|
||||
public boolean isChange(@NotNull Side side) {
|
||||
return side.isLeft() ? myLeftChange : myRightChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.tools.simple;
|
||||
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.diff.fragments.MergeLineFragment;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.tools.util.*;
|
||||
import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
|
||||
import com.intellij.diff.tools.util.side.ThreesideTextDiffViewer;
|
||||
import com.intellij.diff.util.*;
|
||||
import com.intellij.diff.util.DiffDividerDrawUtil.DividerPaintable;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ui.ButtonlessScrollBarUI;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer {
|
||||
public static final Logger LOG = Logger.getInstance(ThreesideTextDiffViewerEx.class);
|
||||
|
||||
@NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable1;
|
||||
@NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable2;
|
||||
|
||||
@NotNull protected final PrevNextDifferenceIterable myPrevNextDifferenceIterable;
|
||||
@NotNull protected final MyStatusPanel myStatusPanel;
|
||||
|
||||
@NotNull protected final MyFoldingModel myFoldingModel;
|
||||
@NotNull protected final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
|
||||
|
||||
private int myChangesCount = -1;
|
||||
private int myConflictsCount = -1;
|
||||
|
||||
public ThreesideTextDiffViewerEx(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
|
||||
super(context, request);
|
||||
|
||||
mySyncScrollable1 = new MySyncScrollable(Side.LEFT);
|
||||
mySyncScrollable2 = new MySyncScrollable(Side.RIGHT);
|
||||
myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
|
||||
myStatusPanel = new MyStatusPanel();
|
||||
myFoldingModel = new MyFoldingModel(getEditors().toArray(new EditorEx[3]), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
myContentPanel.setPainter(new MyDividerPainter(Side.LEFT), Side.LEFT);
|
||||
myContentPanel.setPainter(new MyDividerPainter(Side.RIGHT), Side.RIGHT);
|
||||
myContentPanel.setScrollbarPainter(new MyScrollbarPainter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onDispose() {
|
||||
destroyChangedBlocks();
|
||||
super.onDispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void processContextHints() {
|
||||
super.processContextHints();
|
||||
myInitialScrollHelper.processContext(myRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void updateContextHints() {
|
||||
super.updateContextHints();
|
||||
myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
|
||||
myInitialScrollHelper.updateContext(myRequest);
|
||||
}
|
||||
|
||||
//
|
||||
// Diff
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public FoldingModelSupport.Settings getFoldingModelSettings() {
|
||||
return TextDiffViewerUtil.getFoldingModelSettings(myContext);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Runnable applyNotification(@Nullable final JComponent notification) {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearDiffPresentation();
|
||||
if (notification != null) myPanel.addNotification(notification);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void clearDiffPresentation() {
|
||||
myStatusPanel.setBusy(false);
|
||||
myPanel.resetNotifications();
|
||||
destroyChangedBlocks();
|
||||
|
||||
myContentPanel.repaintDividers();
|
||||
myStatusPanel.update();
|
||||
}
|
||||
|
||||
protected void destroyChangedBlocks() {
|
||||
myFoldingModel.destroy();
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
@Override
|
||||
protected void onDocumentChange(@NotNull DocumentEvent e) {
|
||||
super.onDocumentChange(e);
|
||||
myFoldingModel.onDocumentChanged(e);
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) {
|
||||
ThreesideDiffChangeBase targetChange = scrollToPolicy.select(getChanges());
|
||||
if (targetChange == null) return false;
|
||||
|
||||
doScrollToChange(targetChange, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void doScrollToChange(@NotNull ThreesideDiffChangeBase change, boolean animated) {
|
||||
int[] startLines = new int[3];
|
||||
int[] endLines = new int[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ThreeSide side = ThreeSide.fromIndex(i);
|
||||
startLines[i] = change.getStartLine(side);
|
||||
endLines[i] = change.getEndLine(side);
|
||||
DiffUtil.moveCaret(getEditor(side), startLines[i]);
|
||||
}
|
||||
|
||||
getSyncScrollSupport().makeVisible(getCurrentSide(), startLines, endLines, animated);
|
||||
}
|
||||
|
||||
//
|
||||
// Counters
|
||||
//
|
||||
|
||||
public int getChangesCount() {
|
||||
return myChangesCount;
|
||||
}
|
||||
|
||||
public int getConflictsCount() {
|
||||
return myConflictsCount;
|
||||
}
|
||||
|
||||
protected void resetChangeCounters() {
|
||||
myChangesCount = 0;
|
||||
myConflictsCount = 0;
|
||||
}
|
||||
|
||||
protected void onChangeAdded(@NotNull ThreesideDiffChangeBase change) {
|
||||
if (change.isConflict()) {
|
||||
myConflictsCount++;
|
||||
}
|
||||
else {
|
||||
myChangesCount++;
|
||||
}
|
||||
myStatusPanel.update();
|
||||
}
|
||||
|
||||
protected void onChangeRemoved(@NotNull ThreesideDiffChangeBase change) {
|
||||
if (change.isConflict()) {
|
||||
myConflictsCount--;
|
||||
}
|
||||
else {
|
||||
myChangesCount--;
|
||||
}
|
||||
myStatusPanel.update();
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
@NotNull
|
||||
protected abstract DividerPaintable getDividerPaintable(@NotNull Side side);
|
||||
|
||||
/*
|
||||
* Some changes (ex: applied ones) can be excluded from general processing, but should be painted/used for synchronized scrolling
|
||||
*/
|
||||
@NotNull
|
||||
protected List<? extends ThreesideDiffChangeBase> getAllChanges() {
|
||||
return getChanges();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract List<? extends ThreesideDiffChangeBase> getChanges();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected SyncScrollSupport.SyncScrollable getSyncScrollable(@NotNull Side side) {
|
||||
return side.select(mySyncScrollable1, mySyncScrollable2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected JComponent getStatusPanel() {
|
||||
return myStatusPanel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SyncScrollSupport.ThreesideSyncScrollSupport getSyncScrollSupport() {
|
||||
//noinspection ConstantConditions
|
||||
return mySyncScrollSupport;
|
||||
}
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
|
||||
@Nullable
|
||||
@CalledInAwt
|
||||
protected ThreesideDiffChangeBase getSelectedChange(@NotNull ThreeSide side) {
|
||||
int caretLine = getEditor(side).getCaretModel().getLogicalPosition().line;
|
||||
|
||||
for (ThreesideDiffChangeBase change : getChanges()) {
|
||||
int line1 = change.getStartLine(side);
|
||||
int line2 = change.getEndLine(side);
|
||||
|
||||
if (DiffUtil.isSelectedByLine(caretLine, line1, line2)) return change;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// Actions
|
||||
//
|
||||
|
||||
protected class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<ThreesideDiffChangeBase> {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<? extends ThreesideDiffChangeBase> getChanges() {
|
||||
return ThreesideTextDiffViewerEx.this.getChanges();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected EditorEx getEditor() {
|
||||
return getCurrentEditor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStartLine(@NotNull ThreesideDiffChangeBase change) {
|
||||
return change.getStartLine(getCurrentSide());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getEndLine(@NotNull ThreesideDiffChangeBase change) {
|
||||
return change.getEndLine(getCurrentSide());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scrollToChange(@NotNull ThreesideDiffChangeBase change) {
|
||||
doScrollToChange(change, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction {
|
||||
public MyToggleExpandByDefaultAction() {
|
||||
super(getTextSettings());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void expandAll(boolean expand) {
|
||||
myFoldingModel.expandAll(expand);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
if (DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.is(dataId)) {
|
||||
return myPrevNextDifferenceIterable;
|
||||
}
|
||||
else if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
|
||||
ThreesideDiffChangeBase change = getSelectedChange(getCurrentSide());
|
||||
if (change != null) {
|
||||
return new LineRange(change.getStartLine(getCurrentSide()), change.getEndLine(getCurrentSide()));
|
||||
}
|
||||
}
|
||||
return super.getData(dataId);
|
||||
}
|
||||
|
||||
protected class MySyncScrollable extends BaseSyncScrollable {
|
||||
@NotNull private final Side mySide;
|
||||
|
||||
public MySyncScrollable(@NotNull Side side) {
|
||||
mySide = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSyncScrollEnabled() {
|
||||
return getTextSettings().isEnableSyncScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processHelper(@NotNull ScrollHelper helper) {
|
||||
ThreeSide left = mySide.select(ThreeSide.LEFT, ThreeSide.BASE);
|
||||
ThreeSide right = mySide.select(ThreeSide.BASE, ThreeSide.RIGHT);
|
||||
|
||||
if (!helper.process(0, 0)) return;
|
||||
for (ThreesideDiffChangeBase diffChange : getAllChanges()) {
|
||||
if (!helper.process(diffChange.getStartLine(left), diffChange.getStartLine(right))) return;
|
||||
if (!helper.process(diffChange.getEndLine(left), diffChange.getEndLine(right))) return;
|
||||
}
|
||||
helper.process(getEditor(left).getDocument().getLineCount(), getEditor(right).getDocument().getLineCount());
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyDividerPainter implements DiffSplitter.Painter {
|
||||
@NotNull private final Side mySide;
|
||||
@NotNull private final DividerPaintable myPaintable;
|
||||
|
||||
public MyDividerPainter(@NotNull Side side) {
|
||||
mySide = side;
|
||||
myPaintable = getDividerPaintable(side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(@NotNull Graphics g, @NotNull JComponent divider) {
|
||||
Graphics2D gg = DiffDividerDrawUtil.getDividerGraphics(g, divider, getEditor(ThreeSide.BASE).getComponent());
|
||||
|
||||
gg.setColor(DiffDrawUtil.getDividerColor(getEditor(ThreeSide.BASE)));
|
||||
gg.fill(gg.getClipBounds());
|
||||
|
||||
Editor editor1 = mySide.select(getEditor(ThreeSide.LEFT), getEditor(ThreeSide.BASE));
|
||||
Editor editor2 = mySide.select(getEditor(ThreeSide.BASE), getEditor(ThreeSide.RIGHT));
|
||||
|
||||
//DividerPolygonUtil.paintSimplePolygons(gg, divider.getWidth(), editor1, editor2, myPaintable);
|
||||
DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), editor1, editor2, myPaintable);
|
||||
|
||||
myFoldingModel.paintOnDivider(gg, divider, mySide);
|
||||
|
||||
gg.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyScrollbarPainter implements ButtonlessScrollBarUI.ScrollbarRepaintCallback {
|
||||
@NotNull private final DividerPaintable myPaintable = getDividerPaintable(Side.RIGHT);
|
||||
|
||||
@Override
|
||||
public void call(Graphics g) {
|
||||
EditorEx editor1 = getEditor(ThreeSide.BASE);
|
||||
EditorEx editor2 = getEditor(ThreeSide.RIGHT);
|
||||
|
||||
int width = editor1.getScrollPane().getVerticalScrollBar().getWidth();
|
||||
DiffDividerDrawUtil.paintPolygonsOnScrollbar((Graphics2D)g, width, editor1, editor2, myPaintable);
|
||||
|
||||
myFoldingModel.paintOnScrollbar((Graphics2D)g, width);
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyStatusPanel extends StatusPanel {
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getMessage() {
|
||||
if (myChangesCount < 0 || myConflictsCount < 0) return null;
|
||||
if (myChangesCount == 0 && myConflictsCount == 0) {
|
||||
return DiffBundle.message("merge.dialog.all.conflicts.resolved.message.text");
|
||||
}
|
||||
return makeCounterWord(myChangesCount, "change") + ". " + makeCounterWord(myConflictsCount, "conflict");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String makeCounterWord(int number, @NotNull String word) {
|
||||
if (number == 0) {
|
||||
return "No " + StringUtil.pluralize(word);
|
||||
}
|
||||
return number + " " + StringUtil.pluralize(word, number);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MyFoldingModel extends FoldingModelSupport {
|
||||
private final MyPaintable myPaintable1 = new MyPaintable(0, 1);
|
||||
private final MyPaintable myPaintable2 = new MyPaintable(1, 2);
|
||||
|
||||
public MyFoldingModel(@NotNull EditorEx[] editors, @NotNull Disposable disposable) {
|
||||
super(editors, disposable);
|
||||
assert editors.length == 3;
|
||||
}
|
||||
|
||||
public void install(@Nullable List<MergeLineFragment> fragments,
|
||||
@NotNull UserDataHolder context,
|
||||
@NotNull FoldingModelSupport.Settings settings) {
|
||||
Iterator<int[]> it = map(fragments, new Function<MergeLineFragment, int[]>() {
|
||||
@Override
|
||||
public int[] fun(MergeLineFragment fragment) {
|
||||
return new int[]{
|
||||
fragment.getStartLine(ThreeSide.LEFT),
|
||||
fragment.getEndLine(ThreeSide.LEFT),
|
||||
fragment.getStartLine(ThreeSide.BASE),
|
||||
fragment.getEndLine(ThreeSide.BASE),
|
||||
fragment.getStartLine(ThreeSide.RIGHT),
|
||||
fragment.getEndLine(ThreeSide.RIGHT)};
|
||||
}
|
||||
});
|
||||
install(it, context, settings);
|
||||
}
|
||||
|
||||
public void paintOnDivider(@NotNull Graphics2D gg, @NotNull Component divider, @NotNull Side side) {
|
||||
MyPaintable paintable = side.select(myPaintable1, myPaintable2);
|
||||
paintable.paintOnDivider(gg, divider);
|
||||
}
|
||||
|
||||
public void paintOnScrollbar(@NotNull Graphics2D gg, int width) {
|
||||
myPaintable2.paintOnScrollbar(gg, width);
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyInitialScrollHelper extends MyInitialScrollPositionHelper {
|
||||
@Override
|
||||
protected boolean doScrollToChange() {
|
||||
if (myScrollToChange == null) return false;
|
||||
return ThreesideTextDiffViewerEx.this.doScrollToChange(myScrollToChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doScrollToFirstChange() {
|
||||
return ThreesideTextDiffViewerEx.this.doScrollToChange(ScrollToPolicy.FIRST_CHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -22,7 +22,7 @@ import java.util.List;
|
||||
|
||||
public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDifferenceIterable {
|
||||
@NotNull
|
||||
protected abstract List<T> getChanges();
|
||||
protected abstract List<? extends T> getChanges();
|
||||
|
||||
@NotNull
|
||||
protected abstract EditorEx getEditor();
|
||||
@@ -35,7 +35,7 @@ public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDiffe
|
||||
|
||||
@Override
|
||||
public boolean canGoNext() {
|
||||
List<T> changes = getChanges();
|
||||
List<? extends T> changes = getChanges();
|
||||
if (changes.isEmpty()) return false;
|
||||
|
||||
EditorEx editor = getEditor();
|
||||
@@ -50,7 +50,7 @@ public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDiffe
|
||||
|
||||
@Override
|
||||
public void goNext() {
|
||||
List<T> changes = getChanges();
|
||||
List<? extends T> changes = getChanges();
|
||||
int line = getEditor().getCaretModel().getLogicalPosition().line;
|
||||
|
||||
T next = null;
|
||||
@@ -68,7 +68,7 @@ public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDiffe
|
||||
|
||||
@Override
|
||||
public boolean canGoPrev() {
|
||||
List<T> changes = getChanges();
|
||||
List<? extends T> changes = getChanges();
|
||||
if (changes.isEmpty()) return false;
|
||||
|
||||
int line = getEditor().getCaretModel().getLogicalPosition().line;
|
||||
@@ -83,7 +83,7 @@ public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDiffe
|
||||
|
||||
@Override
|
||||
public void goPrev() {
|
||||
List<T> changes = getChanges();
|
||||
List<? extends T> changes = getChanges();
|
||||
int line = getEditor().getCaretModel().getLogicalPosition().line;
|
||||
|
||||
T prev = null;
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package com.intellij.diff.tools.util;
|
||||
|
||||
import com.intellij.diff.util.IntPair;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.diff.util.ThreeSide;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
@@ -24,7 +24,7 @@ import com.intellij.openapi.editor.ScrollingModel;
|
||||
import com.intellij.openapi.editor.event.VisibleAreaEvent;
|
||||
import com.intellij.openapi.editor.event.VisibleAreaListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import gnu.trove.TIntFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.CalledInAwt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -44,37 +44,31 @@ public class SyncScrollSupport {
|
||||
int transfer(@NotNull Side baseSide, int line);
|
||||
}
|
||||
|
||||
public static class TwosideSyncScrollSupport {
|
||||
@NotNull private final Editor myEditor1;
|
||||
@NotNull private final Editor myEditor2;
|
||||
public static class TwosideSyncScrollSupport extends SyncScrollSupportBase {
|
||||
@NotNull private final List<? extends Editor> myEditors;
|
||||
@NotNull private final SyncScrollable myScrollable;
|
||||
|
||||
@NotNull private final MyScrollHelper myHelper1;
|
||||
@NotNull private final MyScrollHelper myHelper2;
|
||||
|
||||
private boolean myDisabled = false;
|
||||
private boolean myDuringSyncScroll = false;
|
||||
@NotNull private final ScrollHelper myHelper1;
|
||||
@NotNull private final ScrollHelper myHelper2;
|
||||
|
||||
public TwosideSyncScrollSupport(@NotNull List<? extends Editor> editors, @NotNull SyncScrollable scrollable) {
|
||||
this(Side.LEFT.select(editors), Side.RIGHT.select(editors), scrollable);
|
||||
}
|
||||
|
||||
public TwosideSyncScrollSupport(@NotNull Editor editor1, @NotNull Editor editor2, @NotNull SyncScrollable scrollable) {
|
||||
myEditor1 = editor1;
|
||||
myEditor2 = editor2;
|
||||
myEditors = editors;
|
||||
myScrollable = scrollable;
|
||||
|
||||
myHelper1 = create(myEditor1, myEditor2, myScrollable, Side.LEFT);
|
||||
myHelper2 = create(myEditor2, myEditor1, myScrollable, Side.RIGHT);
|
||||
myHelper1 = create(Side.LEFT);
|
||||
myHelper2 = create(Side.RIGHT);
|
||||
}
|
||||
|
||||
public boolean isDuringSyncScroll() {
|
||||
return myDuringSyncScroll;
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<? extends Editor> getEditors() {
|
||||
return myEditors;
|
||||
}
|
||||
|
||||
public void setDisabled(boolean value) {
|
||||
if (myDisabled == value) LOG.warn(new Throwable("myDisabled == value: " + myDisabled + " - " + value));
|
||||
myDisabled = value;
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<? extends ScrollHelper> getScrollHelpers() {
|
||||
return ContainerUtil.list(myHelper1, myHelper2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -83,41 +77,176 @@ public class SyncScrollSupport {
|
||||
}
|
||||
|
||||
public void visibleAreaChanged(VisibleAreaEvent e) {
|
||||
if (!myScrollable.isSyncScrollEnabled() || myDuringSyncScroll || myDisabled) return;
|
||||
if (!myScrollable.isSyncScrollEnabled() || isDuringSyncScroll()) return;
|
||||
|
||||
myDuringSyncScroll = true;
|
||||
enterDisableScrollSection();
|
||||
try {
|
||||
if (e.getEditor() == myEditor1) {
|
||||
if (e.getEditor() == Side.LEFT.select(myEditors)) {
|
||||
myHelper1.visibleAreaChanged(e);
|
||||
}
|
||||
else if (e.getEditor() == myEditor2) {
|
||||
else if (e.getEditor() == Side.RIGHT.select(myEditors)) {
|
||||
myHelper2.visibleAreaChanged(e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myDuringSyncScroll = false;
|
||||
exitDisableScrollSection();
|
||||
}
|
||||
}
|
||||
|
||||
public void makeVisible(@NotNull Side masterSide,
|
||||
int startLine1, int endLine1, int startLine2, int endLine2,
|
||||
final boolean animate) {
|
||||
Side slaveSide = masterSide.other();
|
||||
doMakeVisible(masterSide.getIndex(), new int[]{startLine1, startLine2}, new int[]{endLine1, endLine2}, animate);
|
||||
}
|
||||
|
||||
final IntPair offsets = getTargetOffsets(myEditor1, myEditor2, startLine1, endLine1, startLine2, endLine2);
|
||||
@NotNull
|
||||
private ScrollHelper create(@NotNull Side side) {
|
||||
return ScrollHelper.create(myEditors, side.getIndex(), side.other().getIndex(), myScrollable, side);
|
||||
}
|
||||
}
|
||||
|
||||
final Editor masterEditor = masterSide.select(myEditor1, myEditor2);
|
||||
final Editor slaveEditor = slaveSide.select(myEditor1, myEditor2);
|
||||
public static class ThreesideSyncScrollSupport extends SyncScrollSupportBase {
|
||||
@NotNull private final List<? extends Editor> myEditors;
|
||||
@NotNull private final SyncScrollable myScrollable12;
|
||||
@NotNull private final SyncScrollable myScrollable23;
|
||||
|
||||
final int masterOffset = masterSide.select(offsets.val1, offsets.val2);
|
||||
final int slaveOffset = slaveSide.select(offsets.val1, offsets.val2);
|
||||
@NotNull private final ScrollHelper myHelper12;
|
||||
@NotNull private final ScrollHelper myHelper21;
|
||||
@NotNull private final ScrollHelper myHelper23;
|
||||
@NotNull private final ScrollHelper myHelper32;
|
||||
|
||||
int startOffset1 = myEditor1.getScrollingModel().getVisibleArea().y;
|
||||
int startOffset2 = myEditor2.getScrollingModel().getVisibleArea().y;
|
||||
final int masterStartOffset = masterSide.select(startOffset1, startOffset2);
|
||||
public ThreesideSyncScrollSupport(@NotNull List<? extends Editor> editors,
|
||||
@NotNull SyncScrollable scrollable12,
|
||||
@NotNull SyncScrollable scrollable23) {
|
||||
assert editors.size() == 3;
|
||||
|
||||
myHelper1.setAnchor(startOffset1, offsets.val1, startOffset2, offsets.val2);
|
||||
myHelper2.setAnchor(startOffset2, offsets.val2, startOffset1, offsets.val1);
|
||||
myEditors = editors;
|
||||
myScrollable12 = scrollable12;
|
||||
myScrollable23 = scrollable23;
|
||||
|
||||
myHelper12 = create(ThreeSide.LEFT, ThreeSide.BASE);
|
||||
myHelper21 = create(ThreeSide.BASE, ThreeSide.LEFT);
|
||||
|
||||
myHelper23 = create(ThreeSide.BASE, ThreeSide.RIGHT);
|
||||
myHelper32 = create(ThreeSide.RIGHT, ThreeSide.BASE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<? extends Editor> getEditors() {
|
||||
return myEditors;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<? extends ScrollHelper> getScrollHelpers() {
|
||||
return ContainerUtil.list(myHelper12, myHelper21, myHelper23, myHelper32);
|
||||
}
|
||||
|
||||
public void visibleAreaChanged(VisibleAreaEvent e) {
|
||||
if (isDuringSyncScroll()) return;
|
||||
|
||||
enterDisableScrollSection();
|
||||
try {
|
||||
if (e.getEditor() == ThreeSide.LEFT.select(myEditors)) {
|
||||
if (myScrollable12.isSyncScrollEnabled()) {
|
||||
myHelper12.visibleAreaChanged(e);
|
||||
if (myScrollable23.isSyncScrollEnabled()) myHelper23.visibleAreaChanged(e);
|
||||
}
|
||||
}
|
||||
else if (e.getEditor() == ThreeSide.BASE.select(myEditors)) {
|
||||
if (myScrollable12.isSyncScrollEnabled()) myHelper21.visibleAreaChanged(e);
|
||||
if (myScrollable23.isSyncScrollEnabled()) myHelper23.visibleAreaChanged(e);
|
||||
}
|
||||
else if (e.getEditor() == ThreeSide.RIGHT.select(myEditors)) {
|
||||
if (myScrollable23.isSyncScrollEnabled()) {
|
||||
myHelper32.visibleAreaChanged(e);
|
||||
if (myScrollable12.isSyncScrollEnabled()) myHelper21.visibleAreaChanged(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
exitDisableScrollSection();
|
||||
}
|
||||
}
|
||||
|
||||
public void makeVisible(@NotNull ThreeSide masterSide, int[] startLines, int[] endLines, boolean animate) {
|
||||
doMakeVisible(masterSide.getIndex(), startLines, endLines, animate);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ScrollHelper create(@NotNull ThreeSide master, @NotNull ThreeSide slave) {
|
||||
assert master != slave;
|
||||
assert master == ThreeSide.BASE || slave == ThreeSide.BASE;
|
||||
|
||||
boolean leftSide = master == ThreeSide.LEFT || slave == ThreeSide.LEFT;
|
||||
SyncScrollable scrollable = leftSide ? myScrollable12 : myScrollable23;
|
||||
|
||||
Side side;
|
||||
if (leftSide) {
|
||||
// LEFT - BASE -> LEFT
|
||||
// BASE - LEFT -> RIGHT
|
||||
side = Side.fromLeft(master == ThreeSide.LEFT);
|
||||
}
|
||||
else {
|
||||
// BASE - RIGHT -> LEFT
|
||||
// RIGHT - BASE -> RIGHT
|
||||
side = Side.fromLeft(master == ThreeSide.BASE);
|
||||
}
|
||||
|
||||
return ScrollHelper.create(myEditors, master.getIndex(), slave.getIndex(), scrollable, side);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
private abstract static class SyncScrollSupportBase {
|
||||
private int myDuringSyncScrollDepth = 0;
|
||||
|
||||
public boolean isDuringSyncScroll() {
|
||||
return myDuringSyncScrollDepth > 0;
|
||||
}
|
||||
|
||||
public void enterDisableScrollSection() {
|
||||
myDuringSyncScrollDepth++;
|
||||
}
|
||||
|
||||
public void exitDisableScrollSection() {
|
||||
myDuringSyncScrollDepth--;
|
||||
assert myDuringSyncScrollDepth >= 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract List<? extends Editor> getEditors();
|
||||
|
||||
@NotNull
|
||||
protected abstract List<? extends ScrollHelper> getScrollHelpers();
|
||||
|
||||
protected void doMakeVisible(final int masterIndex, int[] startLines, int[] endLines, final boolean animate) {
|
||||
final List<? extends Editor> editors = getEditors();
|
||||
final List<? extends ScrollHelper> helpers = getScrollHelpers();
|
||||
|
||||
final int count = editors.size();
|
||||
assert startLines.length == count;
|
||||
assert endLines.length == count;
|
||||
|
||||
final int[] offsets = getTargetOffsets(editors.toArray(new Editor[count]), startLines, endLines);
|
||||
|
||||
final int[] startOffsets = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
startOffsets[i] = editors.get(i).getScrollingModel().getVisibleArea().y;
|
||||
}
|
||||
|
||||
final Editor masterEditor = editors.get(masterIndex);
|
||||
final int masterOffset = offsets[masterIndex];
|
||||
final int masterStartOffset = startOffsets[masterIndex];
|
||||
|
||||
for (ScrollHelper helper : helpers) {
|
||||
helper.setAnchor(startOffsets[helper.getMasterIndex()], offsets[helper.getMasterIndex()],
|
||||
startOffsets[helper.getSlaveIndex()], offsets[helper.getSlaveIndex()]);
|
||||
}
|
||||
|
||||
doScrollHorizontally(masterEditor, 0, false); // animation will be canceled by "scroll vertically" anyway
|
||||
doScrollVertically(masterEditor, masterOffset, animate);
|
||||
@@ -125,117 +254,64 @@ public class SyncScrollSupport {
|
||||
masterEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myHelper1.removeAnchor();
|
||||
myHelper2.removeAnchor();
|
||||
for (ScrollHelper helper : helpers) {
|
||||
helper.removeAnchor();
|
||||
}
|
||||
|
||||
int masterFinalOffset = masterEditor.getScrollingModel().getVisibleArea().y;
|
||||
int slaveFinalOffset = slaveEditor.getScrollingModel().getVisibleArea().y;
|
||||
if (slaveFinalOffset != slaveOffset) {
|
||||
myDuringSyncScroll = true;
|
||||
boolean animateSlaves = animate && masterFinalOffset == masterStartOffset;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i == masterIndex) continue;
|
||||
Editor editor = editors.get(i);
|
||||
|
||||
doScrollVertically(slaveEditor, slaveOffset, animate && masterFinalOffset == masterStartOffset);
|
||||
int finalOffset = editor.getScrollingModel().getVisibleArea().y;
|
||||
if (finalOffset != offsets[i]) {
|
||||
enterDisableScrollSection();
|
||||
|
||||
slaveEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myDuringSyncScroll = false;
|
||||
}
|
||||
});
|
||||
doScrollVertically(editor, offsets[i], animateSlaves);
|
||||
|
||||
editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
exitDisableScrollSection();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class ThreesideSyncScrollSupport {
|
||||
private static abstract class ScrollHelper implements VisibleAreaListener {
|
||||
@NotNull private final List<? extends Editor> myEditors;
|
||||
@NotNull private final SyncScrollable myScrollable1;
|
||||
@NotNull private final SyncScrollable myScrollable2;
|
||||
|
||||
@NotNull private final MyScrollHelper myHelper11;
|
||||
@NotNull private final MyScrollHelper myHelper12;
|
||||
@NotNull private final MyScrollHelper myHelper21;
|
||||
@NotNull private final MyScrollHelper myHelper22;
|
||||
|
||||
private boolean myDisabled = false;
|
||||
private boolean myDuringSyncScroll = false;
|
||||
|
||||
public ThreesideSyncScrollSupport(@NotNull List<? extends Editor> editors,
|
||||
@NotNull SyncScrollable scrollable1,
|
||||
@NotNull SyncScrollable scrollable2) {
|
||||
assert editors.size() == 3;
|
||||
|
||||
myEditors = editors;
|
||||
myScrollable1 = scrollable1;
|
||||
myScrollable2 = scrollable2;
|
||||
|
||||
myHelper11 = create(editors.get(0), editors.get(1), myScrollable1, Side.LEFT);
|
||||
myHelper12 = create(editors.get(1), editors.get(0), myScrollable1, Side.RIGHT);
|
||||
|
||||
myHelper21 = create(editors.get(1), editors.get(2), myScrollable2, Side.LEFT);
|
||||
myHelper22 = create(editors.get(2), editors.get(1), myScrollable2, Side.RIGHT);
|
||||
}
|
||||
|
||||
public void setDisabled(boolean value) {
|
||||
if (myDisabled == value) LOG.warn(new Throwable("myDisabled == value: " + myDisabled + " - " + value));
|
||||
myDisabled = value;
|
||||
}
|
||||
|
||||
public void visibleAreaChanged(VisibleAreaEvent e) {
|
||||
if (myDuringSyncScroll || myDisabled) return;
|
||||
|
||||
myDuringSyncScroll = true;
|
||||
try {
|
||||
if (e.getEditor() == myEditors.get(0)) {
|
||||
if (myScrollable1.isSyncScrollEnabled()) myHelper11.visibleAreaChanged(e);
|
||||
if (myScrollable1.isSyncScrollEnabled() && myScrollable2.isSyncScrollEnabled()) myHelper21.visibleAreaChanged(e);
|
||||
}
|
||||
else if (e.getEditor() == myEditors.get(1)) {
|
||||
if (myScrollable1.isSyncScrollEnabled()) myHelper12.visibleAreaChanged(e);
|
||||
if (myScrollable2.isSyncScrollEnabled()) myHelper21.visibleAreaChanged(e);
|
||||
}
|
||||
else if (e.getEditor() == myEditors.get(2)) {
|
||||
if (myScrollable2.isSyncScrollEnabled()) myHelper22.visibleAreaChanged(e);
|
||||
if (myScrollable2.isSyncScrollEnabled() && myScrollable1.isSyncScrollEnabled()) myHelper12.visibleAreaChanged(e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myDuringSyncScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MyScrollHelper create(@NotNull Editor master,
|
||||
@NotNull Editor slave,
|
||||
@NotNull final SyncScrollable scrollable,
|
||||
@NotNull final Side side) {
|
||||
return new MyScrollHelper(master, slave, new TIntFunction() {
|
||||
@Override
|
||||
public int execute(int value) {
|
||||
return scrollable.transfer(side, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
|
||||
private static class MyScrollHelper implements VisibleAreaListener {
|
||||
@NotNull private final Editor myMaster;
|
||||
@NotNull private final Editor mySlave;
|
||||
@NotNull private final TIntFunction myConvertor;
|
||||
private final int myMasterIndex;
|
||||
private final int mySlaveIndex;
|
||||
|
||||
@Nullable private Anchor myAnchor;
|
||||
|
||||
public MyScrollHelper(@NotNull Editor master, @NotNull Editor slave, @NotNull TIntFunction convertor) {
|
||||
myMaster = master;
|
||||
mySlave = slave;
|
||||
myConvertor = convertor;
|
||||
public ScrollHelper(@NotNull List<? extends Editor> editors, int masterIndex, int slaveIndex) {
|
||||
myEditors = editors;
|
||||
myMasterIndex = masterIndex;
|
||||
mySlaveIndex = slaveIndex;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ScrollHelper create(@NotNull List<? extends Editor> editors,
|
||||
int masterIndex,
|
||||
int slaveIndex,
|
||||
@NotNull final SyncScrollable scrollable,
|
||||
@NotNull final Side side) {
|
||||
return new ScrollHelper(editors, masterIndex, slaveIndex) {
|
||||
@Override
|
||||
protected int convertLine(int value) {
|
||||
return scrollable.transfer(side, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract int convertLine(int value);
|
||||
|
||||
public void setAnchor(int masterStartOffset, int masterEndOffset, int slaveStartOffset, int slaveEndOffset) {
|
||||
myAnchor = new Anchor(masterStartOffset, masterEndOffset, slaveStartOffset, slaveEndOffset);
|
||||
}
|
||||
@@ -254,20 +330,38 @@ public class SyncScrollSupport {
|
||||
if (newRectangle.y != oldRectangle.y) syncVerticalScroll(false);
|
||||
}
|
||||
|
||||
private void syncVerticalScroll(boolean animated) {
|
||||
if (myMaster.getDocument().getTextLength() == 0) return;
|
||||
public int getMasterIndex() {
|
||||
return myMasterIndex;
|
||||
}
|
||||
|
||||
Rectangle viewRect = myMaster.getScrollingModel().getVisibleArea();
|
||||
public int getSlaveIndex() {
|
||||
return mySlaveIndex;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Editor getMaster() {
|
||||
return myEditors.get(myMasterIndex);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Editor getSlave() {
|
||||
return myEditors.get(mySlaveIndex);
|
||||
}
|
||||
|
||||
private void syncVerticalScroll(boolean animated) {
|
||||
if (getMaster().getDocument().getTextLength() == 0) return;
|
||||
|
||||
Rectangle viewRect = getMaster().getScrollingModel().getVisibleArea();
|
||||
int middleY = viewRect.height / 3;
|
||||
|
||||
int offset;
|
||||
if (myAnchor == null) {
|
||||
LogicalPosition masterPos = myMaster.xyToLogicalPosition(new Point(viewRect.x, viewRect.y + middleY));
|
||||
LogicalPosition masterPos = getMaster().xyToLogicalPosition(new Point(viewRect.x, viewRect.y + middleY));
|
||||
int masterCenterLine = masterPos.line;
|
||||
int convertedCenterLine = myConvertor.execute(masterCenterLine);
|
||||
int convertedCenterLine = convertLine(masterCenterLine);
|
||||
|
||||
Point point = mySlave.logicalPositionToXY(new LogicalPosition(convertedCenterLine, masterPos.column));
|
||||
int correction = (viewRect.y + middleY) % myMaster.getLineHeight();
|
||||
Point point = getSlave().logicalPositionToXY(new LogicalPosition(convertedCenterLine, masterPos.column));
|
||||
int correction = (viewRect.y + middleY) % getMaster().getLineHeight();
|
||||
offset = point.y - middleY + correction;
|
||||
}
|
||||
else {
|
||||
@@ -277,13 +371,13 @@ public class SyncScrollSupport {
|
||||
offset = myAnchor.slaveStartOffset + (int)((myAnchor.slaveEndOffset - myAnchor.slaveStartOffset) * progress);
|
||||
}
|
||||
|
||||
int deltaHeaderOffset = getHeaderOffset(mySlave) - getHeaderOffset(myMaster);
|
||||
doScrollVertically(mySlave, offset + deltaHeaderOffset, animated);
|
||||
int deltaHeaderOffset = getHeaderOffset(getSlave()) - getHeaderOffset(getMaster());
|
||||
doScrollVertically(getSlave(), offset + deltaHeaderOffset, animated);
|
||||
}
|
||||
|
||||
private void syncHorizontalScroll(boolean animated) {
|
||||
int offset = myMaster.getScrollingModel().getVisibleArea().x;
|
||||
doScrollHorizontally(mySlave, offset, animated);
|
||||
int offset = getMaster().getScrollingModel().getVisibleArea().x;
|
||||
doScrollHorizontally(getSlave(), offset, animated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,56 +401,83 @@ public class SyncScrollSupport {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static IntPair getTargetOffsets(@NotNull Editor editor1, @NotNull Editor editor2,
|
||||
int startLine1, int endLine1, int startLine2, int endLine2) {
|
||||
int topOffset1 = editor1.logicalPositionToXY(new LogicalPosition(startLine1, 0)).y;
|
||||
int bottomOffset1 = editor1.logicalPositionToXY(new LogicalPosition(endLine1 + 1, 0)).y;
|
||||
int topOffset2 = editor2.logicalPositionToXY(new LogicalPosition(startLine2, 0)).y;
|
||||
int bottomOffset2 = editor2.logicalPositionToXY(new LogicalPosition(endLine2 + 1, 0)).y;
|
||||
private static int[] getTargetOffsets(@NotNull Editor editor1, @NotNull Editor editor2,
|
||||
int startLine1, int endLine1, int startLine2, int endLine2) {
|
||||
return getTargetOffsets(new Editor[]{editor1, editor2},
|
||||
new int[]{startLine1, startLine2},
|
||||
new int[]{endLine1, endLine2});
|
||||
}
|
||||
|
||||
int rangeHeight1 = bottomOffset1 - topOffset1;
|
||||
int rangeHeight2 = bottomOffset2 - topOffset2;
|
||||
@NotNull
|
||||
private static int[] getTargetOffsets(@NotNull Editor[] editors, int[] startLines, int[] endLines) {
|
||||
int count = editors.length;
|
||||
assert startLines.length == count;
|
||||
assert endLines.length == count;
|
||||
|
||||
int gapLines1 = 2 * editor1.getLineHeight();
|
||||
int gapLines2 = 2 * editor2.getLineHeight();
|
||||
int[] topOffsets = new int[count];
|
||||
int[] bottomOffsets = new int[count];
|
||||
int[] rangeHeights = new int[count];
|
||||
int[] gapLines = new int[count];
|
||||
int[] editorHeights = new int[count];
|
||||
int[] maximumOffsets = new int[count];
|
||||
int[] topShifts = new int[count];
|
||||
|
||||
int editorHeight1 = editor1.getScrollingModel().getVisibleArea().height;
|
||||
int editorHeight2 = editor2.getScrollingModel().getVisibleArea().height;
|
||||
for (int i = 0; i < count; i++) {
|
||||
topOffsets[i] = editors[i].logicalPositionToXY(new LogicalPosition(startLines[i], 0)).y;
|
||||
bottomOffsets[i] = editors[i].logicalPositionToXY(new LogicalPosition(endLines[i] + 1, 0)).y;
|
||||
rangeHeights[i] = bottomOffsets[i] - topOffsets[i];
|
||||
|
||||
int maximumOffset1 = ((EditorEx)editor1).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight1;
|
||||
int maximumOffset2 = ((EditorEx)editor2).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight2;
|
||||
gapLines[i] = 2 * editors[i].getLineHeight();
|
||||
editorHeights[i] = editors[i].getScrollingModel().getVisibleArea().height;
|
||||
|
||||
// 'shift' here - distance between editor's top and first line of range
|
||||
maximumOffsets[i] = ((EditorEx)editors[i]).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeights[i];
|
||||
|
||||
// make whole range visible. If possible, locate it at 'center' (1/3 of height)
|
||||
// If can't show whole range - show as much as we can
|
||||
boolean canShow1 = 2 * gapLines1 + rangeHeight1 <= editorHeight1;
|
||||
boolean canShow2 = 2 * gapLines2 + rangeHeight2 <= editorHeight2;
|
||||
|
||||
int topShift1 = canShow1 ? Math.min(editorHeight1 - gapLines1 - rangeHeight1, editorHeight1 / 3) : gapLines1;
|
||||
int topShift2 = canShow2 ? Math.min(editorHeight2 - gapLines2 - rangeHeight2, editorHeight2 / 3) : gapLines2;
|
||||
// 'shift' here - distance between editor's top and first line of range
|
||||
|
||||
int topShift = Math.min(topShift1, topShift2);
|
||||
// make whole range visible. If possible, locate it at 'center' (1/3 of height)
|
||||
// If can't show whole range - show as much as we can
|
||||
boolean canShow = 2 * gapLines[i] + rangeHeights[i] <= editorHeights[i];
|
||||
|
||||
topShifts[i] = canShow ? Math.min(editorHeights[i] - gapLines[i] - rangeHeights[i], editorHeights[i] / 3) : gapLines[i];
|
||||
}
|
||||
|
||||
int topShift = min(topShifts);
|
||||
|
||||
// check if we're at the top of file
|
||||
topShift = Math.min(topShift, Math.min(topOffset1, topOffset2));
|
||||
topShift = Math.min(topShift, min(topOffsets));
|
||||
|
||||
int offset1 = topOffset1 - topShift;
|
||||
int offset2 = topOffset2 - topShift;
|
||||
if (maximumOffset1 > offset1 && maximumOffset2 > offset2) return new IntPair(offset1, offset2);
|
||||
int[] offsets = new int[count];
|
||||
boolean haveEnoughSpace = true;
|
||||
for (int i = 0; i < count; i++) {
|
||||
offsets[i] = topOffsets[i] - topShift;
|
||||
haveEnoughSpace &= maximumOffsets[i] > offsets[i];
|
||||
}
|
||||
|
||||
if (haveEnoughSpace) return offsets;
|
||||
|
||||
// One of the ranges is at end of file - we can't scroll where we want to.
|
||||
topShift = Math.max(topOffset1 - maximumOffset1, topOffset2 - maximumOffset2);
|
||||
topShift = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
topShift = Math.max(topOffsets[i] - maximumOffsets[i], topShift);
|
||||
}
|
||||
|
||||
// Try to show as much of range as we can (even if it breaks alignment)
|
||||
offset1 = topOffset1 - topShift + Math.max(topShift + rangeHeight1 + gapLines1 - editorHeight1, 0);
|
||||
offset2 = topOffset2 - topShift + Math.max(topShift + rangeHeight2 + gapLines2 - editorHeight2, 0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Try to show as much of range as we can (even if it breaks alignment)
|
||||
offsets[i] = topOffsets[i] - topShift + Math.max(topShift + rangeHeights[i] + gapLines[i] - editorHeights[i], 0);
|
||||
|
||||
// always show top of the range
|
||||
offset1 = Math.min(offset1, topOffset1 - gapLines1);
|
||||
offset2 = Math.min(offset2, topOffset2 - gapLines2);
|
||||
// always show top of the range
|
||||
offsets[i] = Math.min(offsets[i], topOffsets[i] - gapLines[i]);
|
||||
}
|
||||
|
||||
return new IntPair(offset1, offset2);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
private static int min(int[] values) {
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (int value : values) {
|
||||
if (value < min) min = value;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
private static class Anchor {
|
||||
|
||||
@@ -18,21 +18,29 @@ package com.intellij.diff.tools.util.base;
|
||||
import com.intellij.diff.DiffContext;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.components.panels.Wrapper;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class DiffPanelBase extends JPanel implements DataProvider {
|
||||
@Nullable protected final Project myProject;
|
||||
@NotNull private final DataProvider myDataProvider;
|
||||
@NotNull protected final DiffContext myContext;
|
||||
|
||||
@NotNull private final List<JComponent> myPersistentNotifications = new ArrayList<JComponent>();
|
||||
|
||||
@NotNull protected final JPanel myContentPanel;
|
||||
@NotNull protected final JPanel myNotificationsPanel;
|
||||
|
||||
@NotNull private final Wrapper myNorthPanel;
|
||||
@NotNull private final Wrapper mySouthPanel;
|
||||
|
||||
@NotNull protected final CardLayout myCardLayout;
|
||||
|
||||
@NotNull protected String myCurrentCard;
|
||||
@@ -51,23 +59,20 @@ public abstract class DiffPanelBase extends JPanel implements DataProvider {
|
||||
myNotificationsPanel = new JPanel();
|
||||
myNotificationsPanel.setLayout(new BoxLayout(myNotificationsPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
myNorthPanel = new Wrapper();
|
||||
mySouthPanel = new Wrapper();
|
||||
|
||||
add(myContentPanel, BorderLayout.CENTER);
|
||||
|
||||
JComponent topPanel = createTopPanel();
|
||||
if (topPanel != null) add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
JComponent bottomPanel = createBottomPanel();
|
||||
if (bottomPanel != null) add(bottomPanel, BorderLayout.SOUTH);
|
||||
add(myNorthPanel, BorderLayout.NORTH);
|
||||
add(mySouthPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent createTopPanel() {
|
||||
return null;
|
||||
public void setTopPanel(@Nullable JComponent component) {
|
||||
myNorthPanel.setContent(component);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent createBottomPanel() {
|
||||
return null;
|
||||
public void setBottomPanel(@Nullable JComponent component) {
|
||||
mySouthPanel.setContent(component);
|
||||
}
|
||||
|
||||
protected void setCurrentCard(@NotNull String card) {
|
||||
@@ -94,8 +99,25 @@ public abstract class DiffPanelBase extends JPanel implements DataProvider {
|
||||
// Notifications
|
||||
//
|
||||
|
||||
public void setPersistentNotifications(@NotNull List<JComponent> components) {
|
||||
for (JComponent notification : myPersistentNotifications) {
|
||||
myNotificationsPanel.remove(notification);
|
||||
}
|
||||
|
||||
myPersistentNotifications.clear();
|
||||
myPersistentNotifications.addAll(components);
|
||||
|
||||
for (JComponent notification : myPersistentNotifications) {
|
||||
myNotificationsPanel.add(notification);
|
||||
}
|
||||
myNotificationsPanel.revalidate();
|
||||
}
|
||||
|
||||
public void resetNotifications() {
|
||||
myNotificationsPanel.removeAll();
|
||||
for (JComponent notification : myPersistentNotifications) {
|
||||
myNotificationsPanel.add(notification);
|
||||
}
|
||||
myNotificationsPanel.revalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider {
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public final void rediff(boolean trySync) {
|
||||
public void rediff(boolean trySync) {
|
||||
if (isDisposed()) return;
|
||||
abortRediff();
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ public abstract class OnesideDiffViewer<T extends EditorHolder> extends Listener
|
||||
myPanel = new SimpleDiffPanel(myContentPanel, this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
myPanel.setPersistentNotifications(DiffUtil.getCustomNotifications(myContext, myRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onDispose() {
|
||||
|
||||
@@ -66,6 +66,12 @@ public abstract class ThreesideDiffViewer<T extends EditorHolder> extends Listen
|
||||
myPanel = new SimpleDiffPanel(myContentPanel, this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
myPanel.setPersistentNotifications(DiffUtil.getCustomNotifications(myContext, myRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
@CalledInAwt
|
||||
protected void onDispose() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user