mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' into dsl
This commit is contained in:
@@ -21,6 +21,7 @@ import com.intellij.execution.junit2.info.MethodLocation;
|
||||
import com.intellij.execution.testframework.SourceScope;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.*;
|
||||
@@ -109,6 +110,7 @@ public class JUnitUtil {
|
||||
if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
|
||||
|
||||
for (final PsiMethod method : psiClass.getAllMethods()) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (isSuiteMethod(method)) return true;
|
||||
if (isTestAnnotated(method)) return true;
|
||||
}
|
||||
@@ -131,6 +133,7 @@ public class JUnitUtil {
|
||||
if (modifierList == null) return false;
|
||||
if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
|
||||
for (final PsiMethod method : psiClass.getAllMethods()) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (isTestAnnotated(method)) return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.ILazyParseableElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -43,6 +44,9 @@ public class DeclarationParser {
|
||||
private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create(
|
||||
JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD);
|
||||
|
||||
private static final String WHITESPACES = "\n\r \t";
|
||||
private static final String LINE_ENDS = "\n\r";
|
||||
|
||||
private DeclarationParser() { }
|
||||
|
||||
public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) {
|
||||
@@ -209,6 +213,7 @@ public class DeclarationParser {
|
||||
}
|
||||
|
||||
final PsiBuilder.Marker declaration = builder.mark();
|
||||
final int declarationStart = builder.getCurrentOffset();
|
||||
|
||||
final Pair<PsiBuilder.Marker, Boolean> modListInfo = parseModifierList(builder);
|
||||
final PsiBuilder.Marker modList = modListInfo.first;
|
||||
@@ -225,7 +230,7 @@ public class DeclarationParser {
|
||||
else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) {
|
||||
final PsiBuilder.Marker root = parseClassFromKeyword(builder, declaration, false);
|
||||
if (context == Context.FILE) {
|
||||
// todo: append following declarations to root
|
||||
// todo: append following declarations to root (?)
|
||||
boolean declarationsAfterEnd = false;
|
||||
|
||||
while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) {
|
||||
@@ -344,7 +349,7 @@ public class DeclarationParser {
|
||||
if (typeParams != null) {
|
||||
typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type);
|
||||
}
|
||||
return parseFieldOrLocalVariable(builder, declaration, context);
|
||||
return parseFieldOrLocalVariable(builder, declaration, declarationStart, context);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -538,7 +543,7 @@ public class DeclarationParser {
|
||||
|
||||
@Nullable
|
||||
private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration,
|
||||
final Context context) {
|
||||
final int declarationStart, final Context context) {
|
||||
final IElementType varType;
|
||||
if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) {
|
||||
varType = JavaElementType.FIELD;
|
||||
@@ -553,46 +558,66 @@ public class DeclarationParser {
|
||||
}
|
||||
|
||||
PsiBuilder.Marker variable = declaration;
|
||||
boolean openMarker = true;
|
||||
boolean unclosed = false;
|
||||
boolean eatSemicolon = true;
|
||||
boolean expectSemicolon = true;
|
||||
boolean shouldRollback;
|
||||
boolean openMarker = true;
|
||||
while (true) {
|
||||
shouldRollback = true;
|
||||
|
||||
if (!eatBrackets(builder)) {
|
||||
expectSemicolon = false;
|
||||
unclosed = true;
|
||||
}
|
||||
|
||||
if (expect(builder, JavaTokenType.EQ)) {
|
||||
final PsiBuilder.Marker expr = ExpressionParser.parse(builder);
|
||||
if (expr == null) {
|
||||
if (expr != null) {
|
||||
shouldRollback = false;
|
||||
}
|
||||
else {
|
||||
error(builder, JavaErrorMessages.message("expected.expression"));
|
||||
expectSemicolon = false;
|
||||
unclosed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.getTokenType() == JavaTokenType.COMMA) {
|
||||
variable.done(varType);
|
||||
builder.advanceLexer();
|
||||
variable = builder.mark();
|
||||
}
|
||||
else {
|
||||
if (builder.getTokenType() != JavaTokenType.COMMA) break;
|
||||
variable.done(varType);
|
||||
builder.advanceLexer();
|
||||
|
||||
if (builder.getTokenType() != JavaTokenType.IDENTIFIER) {
|
||||
error(builder, JavaErrorMessages.message("expected.identifier"));
|
||||
unclosed = true;
|
||||
eatSemicolon = false;
|
||||
openMarker = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!expect(builder, JavaTokenType.IDENTIFIER)) {
|
||||
variable.drop();
|
||||
error(builder, JavaErrorMessages.message("expected.identifier"));
|
||||
openMarker = false;
|
||||
eatSemicolon = false;
|
||||
break;
|
||||
}
|
||||
variable = builder.mark();
|
||||
builder.advanceLexer();
|
||||
}
|
||||
|
||||
if (eatSemicolon) {
|
||||
if (!expect(builder, JavaTokenType.SEMICOLON) && expectSemicolon) {
|
||||
if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) {
|
||||
builder.advanceLexer();
|
||||
}
|
||||
else {
|
||||
// special treatment (see DeclarationParserTest.testMultiLineUnclosed())
|
||||
if (!builder.eof() && shouldRollback) {
|
||||
final CharSequence text = builder.getOriginalText();
|
||||
final int spaceEnd = builder.getCurrentOffset();
|
||||
final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES);
|
||||
final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS);
|
||||
|
||||
if (declarationStart < lineStart && lineStart < spaceStart) {
|
||||
final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES);
|
||||
declaration.rollbackTo();
|
||||
return parse(stoppingBuilder(builder, newBufferEnd), context);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unclosed) {
|
||||
error(builder, JavaErrorMessages.message("expected.semicolon"));
|
||||
}
|
||||
// todo: special treatment - see DeclarationParserTest.testMultiLineUnclosed()
|
||||
}
|
||||
|
||||
if (openMarker) {
|
||||
|
||||
@@ -107,6 +107,25 @@ public class JavaParserUtil {
|
||||
}
|
||||
return (braceCount == 0 ? null : tokenType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eof() {
|
||||
return braceCount == 0 || super.eof();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final int stopAt) {
|
||||
return new PsiBuilderAdapter(builder) {
|
||||
@Override
|
||||
public IElementType getTokenType() {
|
||||
return getCurrentOffset() < stopAt ? super.getTokenType() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eof() {
|
||||
return getCurrentOffset() < stopAt || super.eof();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||
import com.intellij.psi.filters.OrFilter;
|
||||
import com.intellij.psi.impl.compiled.ClsElementImpl;
|
||||
import com.intellij.psi.impl.source.PsiImmediateClassType;
|
||||
@@ -39,10 +38,7 @@ import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.ui.IconDeferrer;
|
||||
import com.intellij.ui.RowIcon;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ReflectionCache;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -864,6 +860,7 @@ public class PsiClassImplUtil {
|
||||
}
|
||||
|
||||
public static boolean isClassEquivalentTo(PsiClass aClass, PsiElement another) {
|
||||
if (aClass == another) return true;
|
||||
if (!(another instanceof PsiClass)) return false;
|
||||
String name1 = aClass.getName();
|
||||
if (name1 == null) return false;
|
||||
@@ -900,17 +897,13 @@ public class PsiClassImplUtil {
|
||||
|
||||
final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
|
||||
final PsiFile file2 = another.getContainingFile().getOriginalFile();
|
||||
if (file1.equals(file2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//see com.intellij.openapi.vcs.changes.PsiChangeTracker
|
||||
//see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
|
||||
final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
|
||||
final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
|
||||
if (original1 == original2 && original1 != null
|
||||
|| original1 == file2 || original2 == file1) {
|
||||
return true;
|
||||
if (original1 == original2 && original1 != null || original1 == file2 || original2 == file1 || file1 == file2) {
|
||||
return compareClassSeqNumber(aClass, (PsiClass)another);
|
||||
}
|
||||
|
||||
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(aClass.getProject()).getFileIndex();
|
||||
@@ -920,6 +913,28 @@ public class PsiClassImplUtil {
|
||||
(fileIndex.isInSource(vfile2) || fileIndex.isInLibraryClasses(vfile2));
|
||||
}
|
||||
|
||||
private static boolean compareClassSeqNumber(PsiClass aClass, PsiClass another) {
|
||||
// there may be several classes in one file, they must not be equal
|
||||
int index1 = getSeqNumber(aClass);
|
||||
if (index1 == -1) return true;
|
||||
int index2 = getSeqNumber(another);
|
||||
return index1 == index2;
|
||||
}
|
||||
|
||||
private static int getSeqNumber(PsiClass aClass) {
|
||||
// sequence number of this class among its parent' child classes named the same
|
||||
PsiElement parent = aClass.getParent();
|
||||
if (parent == null) return -1;
|
||||
int seqNo = 0;
|
||||
for (PsiElement child : parent.getChildren()) {
|
||||
if (child == aClass) return seqNo;
|
||||
if (child instanceof PsiClass && Comparing.strEqual(aClass.getName(), ((PsiClass)child).getName())) {
|
||||
seqNo++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static PsiElement originalElement(PsiClass aClass) {
|
||||
final PsiElement originalElement = aClass.getOriginalElement();
|
||||
final PsiCompiledElement compiled = originalElement.getUserData(ClsElementImpl.COMPILED_ELEMENT);
|
||||
|
||||
+10
-1
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.PsiSubstitutorImpl;
|
||||
import com.intellij.psi.impl.search.JavaDirectInheritorsSearcher;
|
||||
import com.intellij.psi.impl.source.resolve.ResolveCache;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.impl.source.tree.TreeElement;
|
||||
@@ -167,7 +168,15 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
|
||||
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable((PsiTypeParameterListOwner)element)) {
|
||||
if (myQualifiedName.equals(parameter.getName())) return parameter;
|
||||
}
|
||||
return JavaPsiFacade.getInstance(getProject()).findClass(myQualifiedName, getResolveScope());
|
||||
return resolveClassPreferringMyJar();
|
||||
}
|
||||
|
||||
private PsiClass resolveClassPreferringMyJar() {
|
||||
PsiClass[] classes = JavaPsiFacade.getInstance(getProject()).findClasses(myQualifiedName, getResolveScope());
|
||||
for (PsiClass aClass : classes) {
|
||||
if (JavaDirectInheritorsSearcher.isFromTheSameJar(aClass, this)) return aClass;
|
||||
}
|
||||
return classes.length == 0 ? null : classes[0];
|
||||
}
|
||||
|
||||
public void processVariants(PsiScopeProcessor processor) {
|
||||
|
||||
@@ -48,6 +48,7 @@ public class LightVariableBuilder extends LightElement implements PsiVariable {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiModifierList getModifierList() {
|
||||
return myModifierList;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaAnonymousClassBaseRefOccurenceIndex;
|
||||
@@ -19,14 +21,17 @@ import com.intellij.psi.search.searches.AllClassesSearch;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.QueryExecutor;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, DirectClassInheritorsSearch.SearchParameters> {
|
||||
|
||||
public boolean execute(final DirectClassInheritorsSearch.SearchParameters p, final Processor<PsiClass> consumer) {
|
||||
final PsiClass aClass = p.getClassToProcess();
|
||||
final PsiManagerImpl psiManager = (PsiManagerImpl)PsiManager.getInstance(aClass.getProject());
|
||||
@@ -73,10 +78,26 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, List<PsiClass>> classes = new HashMap<String, List<PsiClass>>();
|
||||
|
||||
for (PsiReferenceList referenceList : candidates) {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiClass candidate = (PsiClass)referenceList.getParent();
|
||||
if (!consumer.process(candidate)) return false;
|
||||
final PsiClass candidate = (PsiClass)referenceList.getParent();
|
||||
String fqn = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
public String compute() {
|
||||
return candidate.getQualifiedName();
|
||||
}
|
||||
});
|
||||
List<PsiClass> list = classes.get(fqn);
|
||||
if (list == null) {
|
||||
list = new ArrayList<PsiClass>();
|
||||
classes.put(fqn, list);
|
||||
}
|
||||
list.add(candidate);
|
||||
}
|
||||
|
||||
for (List<PsiClass> sameNamedClasses : classes.values()) {
|
||||
if (!processSameNamedClasses(consumer, aClass, sameNamedClasses)) return false;
|
||||
}
|
||||
|
||||
if (p.includeAnonymous()) {
|
||||
@@ -116,4 +137,37 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean processSameNamedClasses(Processor<PsiClass> consumer, PsiClass aClass, List<PsiClass> sameNamedClasses) {
|
||||
// if there is a class from the same jar, prefer it
|
||||
boolean sameJarClassFound = false;
|
||||
for (PsiClass sameNamedClass : sameNamedClasses) {
|
||||
boolean fromSameJar = isFromTheSameJar(sameNamedClass, aClass);
|
||||
if (fromSameJar) {
|
||||
sameJarClassFound = true;
|
||||
if (!consumer.process(sameNamedClass)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sameJarClassFound) {
|
||||
for (PsiClass sameNamedClass : sameNamedClasses) {
|
||||
if (!consumer.process(sameNamedClass)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static VirtualFile getJarFile(PsiElement candidate) {
|
||||
VirtualFile file = candidate.getContainingFile().getVirtualFile();
|
||||
if (file != null && file.getFileSystem() instanceof JarFileSystem) {
|
||||
return JarFileSystem.getInstance().getVirtualFileForJar(file);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
public static boolean isFromTheSameJar(PsiElement candidate, PsiElement other) {
|
||||
VirtualFile c1 = getJarFile(candidate);
|
||||
VirtualFile c2 = getJarFile(other);
|
||||
return c1 != null && c1 == c2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ PsiJavaFile:MultiLineUnclosed.java
|
||||
PsiKeyword:int('int')
|
||||
PsiErrorElement:Identifier expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' \n ')
|
||||
PsiWhiteSpace(' \n ')
|
||||
PsiField:o
|
||||
PsiModifierList:
|
||||
<empty list>
|
||||
|
||||
+1
-2
@@ -21,7 +21,6 @@ import com.intellij.lang.java.parser.JavaParsingTestCase;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
|
||||
|
||||
// todo: fix parser and uncomment tests
|
||||
public class DeclarationParserTest extends JavaParsingTestCase {
|
||||
public DeclarationParserTest() {
|
||||
super("parser-partial/declarations");
|
||||
@@ -78,7 +77,7 @@ public class DeclarationParserTest extends JavaParsingTestCase {
|
||||
public void testUnclosedComma() { doParserTest("{ int field, }", false, false); }
|
||||
public void testUnclosedSemicolon() { doParserTest("{ int field }", false, false); }
|
||||
public void testMissingInitializerExpression() { doParserTest("{ int field=; }", false, false); }
|
||||
//public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); }
|
||||
public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); }
|
||||
|
||||
public void testMethodNormal0() { doParserTest("{ void f() {} }", false, false); }
|
||||
public void testMethodNormal1() { doParserTest("{ void f(); }", false, false); }
|
||||
|
||||
@@ -919,8 +919,9 @@ public class TypeConversionUtil {
|
||||
substitutor = getSuperClassSubstitutorInner(superClass, derivedClass, derivedSubstitutor, visited, manager);
|
||||
}
|
||||
if (substitutor == null) {
|
||||
LOG.error("Not inheritor: " + derivedClass + "(" + derivedClass.getClass().getName() + ");" +
|
||||
" super: " + superClass + "(" + derivedClass.getClass().getName() + ")");
|
||||
LOG.error(
|
||||
"Not inheritor: " + derivedClass + "(" + derivedClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(derivedClass) + ");" +
|
||||
"\n super: " + superClass + "(" + superClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(superClass) + ")");
|
||||
}
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
|
||||
}
|
||||
|
||||
boolean allLineCommented = true;
|
||||
boolean commentWithIndent = CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN;
|
||||
boolean commentWithIndent = !CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN;
|
||||
|
||||
for (int line = myStartLine; line <= myEndLine; line++) {
|
||||
Commenter commenter = blockSuitableCommenter != null ? blockSuitableCommenter : findCommenter(line);
|
||||
|
||||
@@ -76,7 +76,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
|
||||
protected PsiFile myOriginalFile = null;
|
||||
private final FileViewProvider myViewProvider;
|
||||
private static final Key<Document> HARD_REFERENCE_TO_DOCUMENT = new Key<Document>("HARD_REFERENCE_TO_DOCUMENT");
|
||||
private final Object myStubLock = new String("file's stub lock");
|
||||
private final Object myStubLock = new Object();
|
||||
private SoftReference<StubTree> myStub;
|
||||
protected final PsiManagerEx myManager;
|
||||
private volatile Object myTreeElementPointer; // SoftReference/WeakReference to RepositoryTreeElement when has repository id, RepositoryTreeElement otherwise
|
||||
|
||||
@@ -40,9 +40,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CodeEditUtil {
|
||||
private static final Key<Boolean> GENERATED_FLAG = new Key<Boolean>("CREATED BY IDEA");
|
||||
private static final Key<Integer> INDENT_INFO = new Key<Integer>("INDENTATION");
|
||||
private static final Key<Boolean> REFORMAT_KEY = new Key<Boolean>("REFORMAT BEFORE THIS ELEMENT");
|
||||
private static final Key<Boolean> GENERATED_FLAG = new Key<Boolean>("GENERATED_FLAG");
|
||||
private static final Key<Integer> INDENT_INFO = new Key<Integer>("INDENT_INFO");
|
||||
private static final Key<Boolean> REFORMAT_KEY = new Key<Boolean>("REFORMAT_KEY");
|
||||
|
||||
public static final Key<Boolean> OUTER_OK = new Key<Boolean>("OUTER_OK");
|
||||
|
||||
@@ -247,9 +247,15 @@ public class CodeEditUtil {
|
||||
final int leftBlankLines = getBlankLines(left.getText());
|
||||
final int rightBlankLines = getBlankLines(right.getText());
|
||||
final boolean leaveRightText = leftBlankLines < rightBlankLines;
|
||||
if (leftBlankLines == 0 && rightBlankLines == 0) text = left.getText() + right.getText();
|
||||
else if (leaveRightText) text = right.getText();
|
||||
else text = left.getText();
|
||||
if (leftBlankLines == 0 && rightBlankLines == 0) {
|
||||
text = left.getText() + right.getText();
|
||||
}
|
||||
else if (leaveRightText) {
|
||||
text = right.getText();
|
||||
}
|
||||
else {
|
||||
text = left.getText();
|
||||
}
|
||||
if(leaveRightText || forceReformat){
|
||||
final LeafElement merged = ASTFactory.whitespace(text);
|
||||
if(!leaveRightText){
|
||||
@@ -373,9 +379,13 @@ public class CodeEditUtil {
|
||||
}
|
||||
|
||||
public static void setNodeGenerated(final ASTNode next, final boolean value) {
|
||||
if(next == null) return;
|
||||
if(value) next.putCopyableUserData(GENERATED_FLAG, true);
|
||||
else next.putCopyableUserData(GENERATED_FLAG, null);
|
||||
if (next == null) return;
|
||||
if (value) {
|
||||
next.putCopyableUserData(GENERATED_FLAG, true);
|
||||
}
|
||||
else {
|
||||
next.putCopyableUserData(GENERATED_FLAG, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setOldIndentation(final TreeElement treeElement, final int oldIndentation) {
|
||||
|
||||
@@ -38,10 +38,9 @@ public abstract class KeyedExtensionCollector<T, KeyT> {
|
||||
|
||||
private final Map<String, List<T>> myExplicitExtensions = new THashMap<String, List<T>>();
|
||||
private final Map<String, List<T>> myCache = new HashMap<String, List<T>>();
|
||||
private final JBReentrantReadWriteLock mutex = LockFactory.createReadWriteLock();
|
||||
|
||||
private final JBLock r = mutex.readLock();
|
||||
private final JBLock w = mutex.writeLock();
|
||||
private final JBLock w;
|
||||
private final JBLock r;
|
||||
|
||||
private ExtensionPoint<KeyedLazyInstance<T>> myPoint;
|
||||
private final String myEpName;
|
||||
@@ -49,6 +48,9 @@ public abstract class KeyedExtensionCollector<T, KeyT> {
|
||||
private final List<ExtensionPointListener<T>> myListeners = ContainerUtil.createEmptyCOWList();
|
||||
|
||||
public KeyedExtensionCollector(@NonNls String epName) {
|
||||
JBReentrantReadWriteLock mutex = LockFactory.createReadWriteLock();
|
||||
r = mutex.readLock();
|
||||
w = mutex.writeLock();
|
||||
myEpName = epName;
|
||||
resetAreaListener();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ public class DuplicateAction extends EditorAction {
|
||||
VisualPosition caret = editor.getCaretModel().getVisualPosition();
|
||||
LogicalPosition lineStart = editor.visualToLogicalPosition(new VisualPosition(caret.line, 0));
|
||||
LogicalPosition nextLineStart = editor.visualToLogicalPosition(new VisualPosition(caret.line + 1, 0));
|
||||
if (nextLineStart.line == lineStart.line) {
|
||||
nextLineStart = new LogicalPosition(lineStart.line+1, 0);
|
||||
}
|
||||
|
||||
int start = editor.logicalPositionToOffset(lineStart);
|
||||
int end = editor.logicalPositionToOffset(nextLineStart);
|
||||
@@ -86,4 +89,4 @@ public class DuplicateAction extends EditorAction {
|
||||
presentation.setText(EditorBundle.message("action.duplicate.line"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public class EditorUtil {
|
||||
int fontType = state.getMergedAttributes().getFontType();
|
||||
int column = currentColumn.get();
|
||||
int spaceSize = getSpaceWidth(fontType, editorImpl);
|
||||
for (; column <= columnNumber && offset < end; offset++) {
|
||||
for (; column < columnNumber && offset < end; offset++) {
|
||||
if (offset >= state.getEndOffset()) {
|
||||
state.advance();
|
||||
fontType = state.getMergedAttributes().getFontType();
|
||||
@@ -407,6 +407,9 @@ public class EditorUtil {
|
||||
}
|
||||
|
||||
public static int nextTabStop(int x, Editor editor, int tabSize) {
|
||||
if (tabSize <= 0) {
|
||||
return x + getSpaceWidth(Font.PLAIN, editor);
|
||||
}
|
||||
tabSize *= getSpaceWidth(Font.PLAIN, editor);
|
||||
|
||||
int nTabs = x / tabSize;
|
||||
|
||||
@@ -480,11 +480,11 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
int y = myEditor.visualPositionToXY(visualPosition).y;
|
||||
int height = myEditor.getLineHeight();
|
||||
int visualLine = visualPosition.line + 1;
|
||||
while (true) {
|
||||
int lastVisualLine = myEditor.offsetToVisualPosition(document.getTextLength() - 1).line;
|
||||
for (; visualLine <= lastVisualLine; visualLine++) {
|
||||
LogicalPosition logical = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0));
|
||||
if (logical.line == logicalLine) {
|
||||
height += myEditor.getLineHeight();
|
||||
visualLine++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
||||
@@ -105,6 +105,7 @@ import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public final class EditorImpl extends UserDataHolderBase implements EditorEx, HighlighterClient, Queryable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorImpl");
|
||||
@@ -122,7 +123,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
ComplementaryFontsRegistry registry; // load costly font info
|
||||
}
|
||||
|
||||
private final CommandProcessor myCommandProcessor;
|
||||
private final CommandProcessor myCommandProcessor;
|
||||
private final MyScrollBar myVerticalScrollBar;
|
||||
|
||||
private final CopyOnWriteArrayList<EditorMouseListener> myMouseListeners = ContainerUtil.createEmptyCOWList();
|
||||
@@ -1318,8 +1319,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
void paint(Graphics g) {
|
||||
//TODO den remove
|
||||
System.out.printf("EditorImpl.paint(): (%d; %d) - [%d; %d]%n", g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height);
|
||||
startOptimizedScrolling();
|
||||
|
||||
if (myCursorUpdater != null) {
|
||||
@@ -1750,7 +1749,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
if (backColor != null && !backColor.equals(defaultBackground) && clip.intersects(position.x, position.y, w, getLineHeight())) {
|
||||
if (backColor.equals(myLastBackgroundColor) && myLastBackgroundPosition.y == position.y &&
|
||||
myLastBackgroundPosition.x + myLastBackgroundWidth == position.x) {
|
||||
myLastBackgroundPosition.x + myLastBackgroundWidth == position.x) {
|
||||
myLastBackgroundWidth += w;
|
||||
}
|
||||
else {
|
||||
@@ -1795,8 +1794,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
int visibleLineNumber = clip.y / lineHeight;
|
||||
|
||||
LogicalPosition logicalPosition = xyToLogicalPosition(new Point(0, clip.y));
|
||||
int startLineNumber = logicalPosition.line;
|
||||
// We use AtomicReference here just as a holder for LogicalPosition
|
||||
// The main idea is that there is a possible case that we need to perform painting starting from soft-wrapped logical line.
|
||||
// We may want to skip necessary of visual lines then. Hence, we remember logical position that corresponds to the starting
|
||||
// visual line in order to use it for further processing. As soon as necessary number of visual lines is skipped, logical
|
||||
// position is expected to be set to null as an indication that no soft wrap-introduced visual lines should be skipped on
|
||||
// current painting iteration.
|
||||
AtomicReference<LogicalPosition> logicalPosition = new AtomicReference<LogicalPosition>(xyToLogicalPosition(new Point(0, clip.y)));
|
||||
int startLineNumber = logicalPosition.get().line;
|
||||
|
||||
Point position = new Point(0, visibleLineNumber * lineHeight);
|
||||
if (startLineNumber == 0 && myPrefixText != null) {
|
||||
@@ -1851,10 +1856,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
else {
|
||||
FoldRegion collapsedFolderAt = iterationState.getCurrentFold();
|
||||
if (collapsedFolderAt != null) {
|
||||
TextChange softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset());
|
||||
if (softWrap != null && logicalPosition.get() != null) {
|
||||
position.x = drawStringWithSoftWraps(
|
||||
g, chars, collapsedFolderAt.getStartOffset(), collapsedFolderAt.getStartOffset(), position, clip, effectColor, effectType,
|
||||
fontType, currentColor, logicalPosition
|
||||
);
|
||||
}
|
||||
int foldingXStart = position.x;
|
||||
position.x =
|
||||
drawStringWithSoftWraps(g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType,
|
||||
fontType, currentColor, logicalPosition);
|
||||
position.x = drawString(
|
||||
g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType, fontType, currentColor
|
||||
);
|
||||
//drawStringWithSoftWraps(g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType,
|
||||
// fontType, currentColor, logicalPosition);
|
||||
BorderEffect.paintFoldedEffect(g, foldingXStart, position.y, position.x, getLineHeight(), effectColor, effectType);
|
||||
|
||||
}
|
||||
@@ -1964,7 +1978,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
final int lastCount = count - 1;
|
||||
final Color lastColor = color[lastCount];
|
||||
if (_data == myLastData && _start == ends[lastCount] && (_color == null || lastColor == null || _color == lastColor)
|
||||
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
|
||||
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
|
||||
{
|
||||
ends[lastCount] = _end;
|
||||
if (lastColor == null) color[lastCount] = _color;
|
||||
@@ -1997,7 +2011,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
private void paintCaretCursor(Graphics g) {
|
||||
// There is a possible case that visual caret position is changed because of newly added or removed soft wraps.
|
||||
// We check if that's the case and ask caret model to recalculate visual position if necessary.
|
||||
|
||||
|
||||
myCaretCursor.paint(g);
|
||||
}
|
||||
|
||||
@@ -2047,7 +2061,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
EffectType effectType,
|
||||
int fontType,
|
||||
Color fontColor,
|
||||
LogicalPosition startDrawingLogicalPosition)
|
||||
AtomicReference<LogicalPosition> startDrawingLogicalPosition)
|
||||
{
|
||||
return drawStringWithSoftWraps(g, text.toCharArray(), 0, text.length(), position, clip, effectColor, effectType,
|
||||
fontType, fontColor, startDrawingLogicalPosition);
|
||||
@@ -2063,16 +2077,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
EffectType effectType,
|
||||
int fontType,
|
||||
Color fontColor,
|
||||
LogicalPosition startDrawingLogicalPosition)
|
||||
AtomicReference<LogicalPosition> startDrawingLogicalPosition)
|
||||
{
|
||||
int startToUse = start;
|
||||
|
||||
// There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn.
|
||||
// We need to skip necessary number of visual lines then.
|
||||
int softWrapLinesToSkip = startDrawingLogicalPosition.softWrapLinesOnCurrentLogicalLine;
|
||||
int softWrapLinesToSkip = 0;
|
||||
if (startDrawingLogicalPosition.get() != null) {
|
||||
softWrapLinesToSkip = startDrawingLogicalPosition.get().softWrapLinesOnCurrentLogicalLine;
|
||||
}
|
||||
TextChange lastSkippedSoftWrap = null;
|
||||
if (softWrapLinesToSkip > 0) {
|
||||
List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.line);
|
||||
List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.get().line);
|
||||
for (TextChange softWrap : softWraps) {
|
||||
softWrapLinesToSkip -= StringUtil.countNewLines(softWrap.getText());
|
||||
if (softWrapLinesToSkip <= 0) {
|
||||
@@ -2081,13 +2098,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
startToUse = Math.max(startToUse, start);
|
||||
|
||||
if (startToUse >= end) {
|
||||
return position.x;
|
||||
}
|
||||
startDrawingLogicalPosition.set(null);
|
||||
|
||||
outer:
|
||||
for (TextChange softWrap : getSoftWrapModel().getSoftWrapsForRange(startToUse, end)) {
|
||||
@@ -2741,7 +2759,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int column = EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
|
||||
|
||||
if (softWrapAware) {
|
||||
int line = calcLogicalLineNumber(offset, false);
|
||||
int line = calcLogicalLineNumber(offset, false);
|
||||
return mySoftWrapModel.adjustLogicalPosition(new LogicalPosition(line, column), offset).column;
|
||||
}
|
||||
else {
|
||||
@@ -2881,22 +2899,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int x = myGutterComponent.convertX(e.getX());
|
||||
|
||||
if (x >= myGutterComponent.getLineNumberAreaOffset() &&
|
||||
x < myGutterComponent.getLineNumberAreaOffset() + myGutterComponent.getLineNumberAreaWidth()) {
|
||||
x < myGutterComponent.getLineNumberAreaOffset() + myGutterComponent.getLineNumberAreaWidth()) {
|
||||
return EditorMouseEventArea.LINE_NUMBERS_AREA;
|
||||
}
|
||||
|
||||
if (x >= myGutterComponent.getAnnotationsAreaOffset() &&
|
||||
x <= myGutterComponent.getAnnotationsAreaOffset() + myGutterComponent.getAnnotationsAreaWidth()) {
|
||||
x <= myGutterComponent.getAnnotationsAreaOffset() + myGutterComponent.getAnnotationsAreaWidth()) {
|
||||
return EditorMouseEventArea.ANNOTATIONS_AREA;
|
||||
}
|
||||
|
||||
if (x >= myGutterComponent.getLineMarkerAreaOffset() &&
|
||||
x < myGutterComponent.getLineMarkerAreaOffset() + myGutterComponent.getLineMarkerAreaWidth()) {
|
||||
x < myGutterComponent.getLineMarkerAreaOffset() + myGutterComponent.getLineMarkerAreaWidth()) {
|
||||
return EditorMouseEventArea.LINE_MARKERS_AREA;
|
||||
}
|
||||
|
||||
if (x >= myGutterComponent.getFoldingAreaOffset() &&
|
||||
x < myGutterComponent.getFoldingAreaOffset() + myGutterComponent.getFoldingAreaWidth()) {
|
||||
x < myGutterComponent.getFoldingAreaOffset() + myGutterComponent.getFoldingAreaWidth()) {
|
||||
return EditorMouseEventArea.FOLDING_OUTLINE_AREA;
|
||||
}
|
||||
|
||||
@@ -2988,7 +3006,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
int caretShift = newCaretOffset - mySavedSelectionStart;
|
||||
|
||||
if (myMousePressedEvent != null && getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.EDITING_AREA &&
|
||||
getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.LINE_NUMBERS_AREA) {
|
||||
getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.LINE_NUMBERS_AREA) {
|
||||
selectionModel.setSelection(oldSelectionStart, newCaretOffset);
|
||||
}
|
||||
else {
|
||||
@@ -4020,8 +4038,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
runMouseReleasedCommand(e);
|
||||
if (!e.isConsumed() && myMousePressedEvent != null && !myMousePressedEvent.isConsumed() &&
|
||||
Math.abs(e.getX() - myMousePressedEvent.getX()) < EditorUtil.getSpaceWidth(Font.PLAIN, EditorImpl.this) &&
|
||||
Math.abs(e.getY() - myMousePressedEvent.getY()) < getLineHeight()) {
|
||||
Math.abs(e.getX() - myMousePressedEvent.getX()) < EditorUtil.getSpaceWidth(Font.PLAIN, EditorImpl.this) &&
|
||||
Math.abs(e.getY() - myMousePressedEvent.getY()) < getLineHeight()) {
|
||||
runMouseClickedCommand(e);
|
||||
}
|
||||
myMousePressedEvent = null;
|
||||
@@ -4177,7 +4195,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
myMouseSelectedRegion = myFoldingModel.getFoldingPlaceholderAt(new Point(x, y));
|
||||
myMousePressedInsideSelection = mySelectionModel.hasSelection() && caretOffset >= mySelectionModel.getSelectionStart() &&
|
||||
caretOffset <= mySelectionModel.getSelectionEnd();
|
||||
caretOffset <= mySelectionModel.getSelectionEnd();
|
||||
|
||||
if (!myMousePressedInsideSelection && mySelectionModel.hasBlockSelection()) {
|
||||
int[] starts = mySelectionModel.getBlockSelectionStarts();
|
||||
@@ -4563,7 +4581,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
final Editor editor = getEditor(source);
|
||||
if (action == MOVE && !editor.isViewer()) {
|
||||
if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), editor.getProject())) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
CommandProcessor.getInstance().executeCommand(((EditorImpl)editor).myProject, new Runnable() {
|
||||
public void run() {
|
||||
@@ -4636,7 +4654,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myMaxWidth = mySize != null ? mySize.width : -1;
|
||||
}
|
||||
|
||||
myOldEndLine = getVisualPositionLine(e.getOffset() + e.getOldLength());
|
||||
myOldEndLine = offsetToLogicalPosition(e.getOffset() + e.getOldLength()).line;
|
||||
}
|
||||
|
||||
private int getVisualPositionLine(int offset) {
|
||||
@@ -4667,15 +4685,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myLineWidths.insert(oldEndLine + 1, delta);
|
||||
}
|
||||
else if (oldEndLine > newEndLine && !toAddNewLines && newEndLine + 1 < lineWidthSize) {
|
||||
myLineWidths.remove(newEndLine + 1, Math.min(oldEndLine, lineWidthSize) - newEndLine);
|
||||
myLineWidths.remove(newEndLine + 1, Math.min(oldEndLine, lineWidthSize) - newEndLine - 1);
|
||||
}
|
||||
myIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void changedUpdate(DocumentEvent e) {
|
||||
int startLine = e.getOldLength() == 0 ? myOldEndLine : getVisualPositionLine(e.getOffset());
|
||||
int newEndLine = e.getNewLength() == 0 ? startLine : getVisualPositionLine(e.getOffset() + e.getNewLength());
|
||||
int startLine = e.getOldLength() == 0 ? myOldEndLine : offsetToLogicalPosition(e.getOffset()).line;
|
||||
int newEndLine = e.getNewLength() == 0 ? startLine : offsetToLogicalPosition(e.getOffset() + e.getNewLength()).line;
|
||||
int oldEndLine = myOldEndLine;
|
||||
|
||||
update(startLine, newEndLine, oldEndLine);
|
||||
@@ -4877,7 +4895,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
: e.isControlDown() && !e.isMetaDown() && !e.isAltDown() && !e.isShiftDown();
|
||||
if (changeFontSize) {
|
||||
setFontSize(myScheme.getEditorFontSize() + e.getWheelRotation());
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-23
@@ -28,25 +28,29 @@ import com.intellij.openapi.editor.event.DocumentListener;
|
||||
*/
|
||||
public abstract class LineOrientedDocumentChangeAdapter implements DocumentListener {
|
||||
|
||||
private enum ChangeType {
|
||||
BEFORE, AFTER
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
onChange(event, ChangeType.BEFORE);
|
||||
Document document = event.getDocument();
|
||||
int startLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset()));
|
||||
int endLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset() + event.getOldLength()));
|
||||
int symbolsDifference = event.getNewLength() - event.getOldLength();
|
||||
beforeDocumentChange(startLine, endLine, symbolsDifference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
onChange(event, ChangeType.AFTER);
|
||||
Document document = event.getDocument();
|
||||
int startLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset()));
|
||||
int endLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset() + event.getNewLength()));
|
||||
int symbolsDifference = event.getNewLength() - event.getOldLength();
|
||||
afterDocumentChange(startLine, endLine, symbolsDifference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback adapter method for {@link DocumentListener#beforeDocumentChange(DocumentEvent)} event.
|
||||
*
|
||||
* @param startLine first logical document line affected by the target event (inclusive)
|
||||
* @param endLine last logical document line affected by the target event (inclusive)
|
||||
* @param endLine old last logical document line affected by the target event (inclusive)
|
||||
* @param symbolsDifference difference in number in symbols applied to the target document
|
||||
*/
|
||||
public abstract void beforeDocumentChange(int startLine, int endLine, int symbolsDifference);
|
||||
@@ -55,26 +59,11 @@ public abstract class LineOrientedDocumentChangeAdapter implements DocumentListe
|
||||
* Callback adapter method for {@link DocumentListener#documentChanged(DocumentEvent)} event.
|
||||
*
|
||||
* @param startLine first logical document line affected by the target event (inclusive)
|
||||
* @param endLine last logical document line affected by the target event (inclusive)
|
||||
* @param endLine new last logical document line affected by the target event (inclusive)
|
||||
* @param symbolsDifference difference in number in symbols applied to the target document
|
||||
*/
|
||||
public abstract void afterDocumentChange(int startLine, int endLine, int symbolsDifference);
|
||||
|
||||
private void onChange(DocumentEvent event, ChangeType type) {
|
||||
Document document = event.getDocument();
|
||||
int startLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset()));
|
||||
int endLine = document.getLineNumber(
|
||||
normalize(event.getDocument(), Math.max(event.getOffset() + event.getNewLength(), event.getOffset() + event.getOldLength()))
|
||||
);
|
||||
|
||||
int symbolsDifference = event.getNewLength() - event.getOldLength();
|
||||
switch (type) {
|
||||
case AFTER: beforeDocumentChange(startLine, endLine, symbolsDifference); break;
|
||||
case BEFORE: afterDocumentChange(startLine, endLine, symbolsDifference); break;
|
||||
default: throw new IllegalStateException("Unsupported event change type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private static int normalize(Document document, int offset) {
|
||||
if (offset < 0) {
|
||||
return 0;
|
||||
|
||||
+8
@@ -240,6 +240,10 @@ public class SoftWrapDataMapper {
|
||||
|
||||
@Nullable
|
||||
private LogicalPosition process(@NotNull FoldRegion region) {
|
||||
int endDocumentOffset = myEditor.getDocument().getTextLength();
|
||||
if (region.getEndOffset() >= endDocumentOffset) {
|
||||
return advanceToOffset(endDocumentOffset).build();
|
||||
}
|
||||
if (region.getStartOffset() > context.offset) {
|
||||
Context newContext = advanceToOffset(region.getStartOffset());
|
||||
if (strategy.exceeds(newContext)) {
|
||||
@@ -289,6 +293,10 @@ public class SoftWrapDataMapper {
|
||||
|
||||
@Nullable
|
||||
private LogicalPosition process(@NotNull TextChange softWrap) {
|
||||
int endDocumentOffset = myEditor.getDocument().getTextLength();
|
||||
if (softWrap.getStart() >= endDocumentOffset) {
|
||||
return advanceToOffset(endDocumentOffset).build();
|
||||
}
|
||||
Context newContext = advanceToOffset(softWrap.getStart());
|
||||
if (strategy.exceeds(newContext)) {
|
||||
return strategy.build(context);
|
||||
|
||||
+14
-31
@@ -32,16 +32,6 @@ import java.util.List;
|
||||
*/
|
||||
public class SoftWrapDocumentChangeManager {
|
||||
|
||||
/**
|
||||
* Holds logical lines where soft wraps should be removed.
|
||||
* <p/>
|
||||
* The general idea is to do the following:
|
||||
* <ul>
|
||||
* <li>listen for document changes, mark all soft wraps that belong to modified logical line as <code>'dirty'</code>;</li>
|
||||
* <li>remove soft wraps marked as 'dirty' on repaint;</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final TIntHashSet myDirtyLines = new TIntHashSet();
|
||||
private final List<DeferredChange> myDeferredChanges = new ArrayList<DeferredChange>();
|
||||
|
||||
private final SoftWrapsStorage myStorage;
|
||||
@@ -76,20 +66,19 @@ public class SoftWrapDocumentChangeManager {
|
||||
// Update offsets for soft wraps that remain after the changed line(s).
|
||||
List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
|
||||
for (DeferredChange change : myDeferredChanges) {
|
||||
if (change.startLine >= document.getLineCount()) {
|
||||
if (change.startOffset >= document.getTextLength()) {
|
||||
continue;
|
||||
}
|
||||
int index = myStorage.getSoftWrapIndex(document.getLineStartOffset(change.startLine));
|
||||
int index = myStorage.getSoftWrapIndex(change.startOffset);
|
||||
if (index < 0) {
|
||||
index = -index -1;
|
||||
}
|
||||
for (int i = index; i < softWraps.size(); i++) {
|
||||
TextChangeImpl softWrap = softWraps.get(i);
|
||||
if (softWrap.getStart() >= document.getTextLength()) {
|
||||
if (softWrapsToRemoveIndices.contains(i)) {
|
||||
continue;
|
||||
}
|
||||
int softWrapLine = document.getLineNumber(softWrap.getStart());
|
||||
if (myDirtyLines.contains(softWrapLine)) {
|
||||
if (softWrap.getStart() < change.endOffset || softWrap.getStart() >= document.getTextLength()) {
|
||||
softWrapsToRemoveIndices.add(i);
|
||||
continue;
|
||||
}
|
||||
@@ -106,44 +95,38 @@ public class SoftWrapDocumentChangeManager {
|
||||
}
|
||||
});
|
||||
|
||||
myDirtyLines.clear();
|
||||
myDeferredChanges.clear();
|
||||
}
|
||||
|
||||
private void init(Document document) {
|
||||
private void init(final Document document) {
|
||||
document.addDocumentListener(new LineOrientedDocumentChangeAdapter() {
|
||||
@Override
|
||||
public void beforeDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
myDeferredChanges.add(
|
||||
new DeferredChange(document.getLineStartOffset(startLine), document.getLineEndOffset(endLine), symbolsDifference)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
updateDeferredData(startLine, endLine, symbolsDifference);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDeferredData(int startLine, int endLine, int symbolsDifference) {
|
||||
for (int i = startLine; i <= endLine; i++) {
|
||||
myDirtyLines.add(i);
|
||||
}
|
||||
myDeferredChanges.add(new DeferredChange(startLine, endLine, symbolsDifference));
|
||||
}
|
||||
|
||||
private static class DeferredChange {
|
||||
final int startLine;
|
||||
final int endLine;
|
||||
final int startOffset;
|
||||
final int endOffset;
|
||||
final int symbolsDifference;
|
||||
|
||||
DeferredChange(int startLine, int endLine, int symbolsDifference) {
|
||||
this.startLine = startLine;
|
||||
this.endLine = endLine;
|
||||
DeferredChange(int startOffset, int endOffset, int symbolsDifference) {
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.symbolsDifference = symbolsDifference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return startLine + "-" + endLine + ": " + symbolsDifference;
|
||||
return startOffset + "-" + endOffset + ": " + symbolsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,11 +224,11 @@ javadoc.external.fetch.error.message=Cannot fetch remote documentation: {0}
|
||||
searching.for.implementations=Searching For Implementations...
|
||||
|
||||
goto.implementation.chooserTitle=<html><body>Choose Implementation of <b>{0}</b> ({1} found)</body></html>
|
||||
goto.implementation.notFound=Not implementations found
|
||||
goto.implementation.notFound=No implementations found
|
||||
|
||||
goto.test.chooserTitle.test=<html><body>Choose Test for <b>{0}</b> ({1} found)</body></html>
|
||||
goto.test.chooserTitle.subject=<html><body>Choose Test Subject for <b>{0}</b> ({1} found)</body></html>
|
||||
goto.test.notFound=Not test subjects found
|
||||
goto.test.notFound=No test subjects found
|
||||
|
||||
incremental.search.tooltip.prefix=Search for:
|
||||
goto.super.method.chooser.title=Choose super method
|
||||
|
||||
@@ -32,14 +32,14 @@ el.lparen.expected=( expected
|
||||
el.rparen.expected=) expected
|
||||
el.colon.expected=: expected
|
||||
el.value.expected=Value expected
|
||||
el.cannot.resolve.function=Cannot resolve function {0} #loc
|
||||
el.cannot.resolve.namespace=Cannot resolve namespace {0} #loc
|
||||
el.cannot.resolve.property=Cannot resolve property or method {0} (dynamic property?) #loc
|
||||
el.cannot.resolve.function=Cannot resolve function ''{0}'' #loc
|
||||
el.cannot.resolve.namespace=Cannot resolve namespace ''{0}'' #loc
|
||||
el.cannot.resolve.property=Cannot resolve property or method ''{0}'' (dynamic property?) #loc
|
||||
el.mismatched.parameters.count=Different number of formal and actual parameters
|
||||
el.declare.variable.intention.family=Declare Variable As External Data
|
||||
el.declare.variable.via.usebean.intention.name=Declare external variable as <jsp:useBean />
|
||||
el.declare.variable.via.comment.annotation.intention.name=Declare external variable in comment annotation
|
||||
el.cannot.resolve.variable=Cannot resolve variable {0} #loc
|
||||
el.cannot.resolve.variable=Cannot resolve variable ''{0}'' #loc
|
||||
jsf.el.out.of.attribute=JSF EL out of attribute #loc
|
||||
jsf.method.call.is.nonstd.extension=Method call is nonstandard extension #loc
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ public class LockPoolSynchronizedMap<K, V> extends THashMap<K, V> implements Con
|
||||
private static final JBReentrantReadWriteLock[] ourLocks = new JBReentrantReadWriteLock[NUM_LOCKS];
|
||||
private static int ourLockAllocationCounter = 0;
|
||||
|
||||
private final JBReentrantReadWriteLock mutex = allocateLock();
|
||||
private final JBLock r = mutex.readLock();
|
||||
private final JBLock w = mutex.writeLock();
|
||||
private final JBLock r;
|
||||
private final JBLock w;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < ourLocks.length; i++) {
|
||||
@@ -45,6 +44,11 @@ public class LockPoolSynchronizedMap<K, V> extends THashMap<K, V> implements Con
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final JBReentrantReadWriteLock mutex = allocateLock();
|
||||
r = mutex.readLock();
|
||||
w = mutex.writeLock();
|
||||
}
|
||||
public LockPoolSynchronizedMap() {
|
||||
}
|
||||
|
||||
@@ -250,14 +254,11 @@ public class LockPoolSynchronizedMap<K, V> extends THashMap<K, V> implements Con
|
||||
public boolean remove(Object key, Object oldValue) {
|
||||
w.lock();
|
||||
try {
|
||||
if (!Comparing.equal(oldValue, get(key))) {
|
||||
return false;
|
||||
}
|
||||
return super.remove(key) != null;
|
||||
V currentValue = get(key);
|
||||
return Comparing.equal(oldValue, currentValue) && super.remove(key) != null;
|
||||
}
|
||||
finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -33,7 +34,7 @@ public class ConcurrentTasks<T> {
|
||||
private volatile T myResult;
|
||||
private volatile int myCntAlive;
|
||||
private final ProgressIndicator myParentIndicator;
|
||||
private final Consumer<Consumer<T>>[] myTasks;
|
||||
private final List<Consumer<Consumer<T>>> myTasks;
|
||||
|
||||
public void compute() {
|
||||
final EmptyProgressIndicator pi = new EmptyProgressIndicator() {
|
||||
@@ -45,7 +46,7 @@ public class ConcurrentTasks<T> {
|
||||
super.checkCanceled();
|
||||
}
|
||||
};
|
||||
myCntAlive = myTasks.length;
|
||||
myCntAlive = myTasks.size();
|
||||
mySemaphore.down();
|
||||
|
||||
final List<Future<?>> futures = new LinkedList<Future<?>>();
|
||||
@@ -85,7 +86,7 @@ public class ConcurrentTasks<T> {
|
||||
if (myResultKnown) break;
|
||||
if (myCntAlive <= 0) break;
|
||||
pi.checkCanceled();
|
||||
mySemaphore.waitFor(1000);
|
||||
mySemaphore.waitFor(300);
|
||||
}
|
||||
// in it possible to even interrupt() threads involved, but at the moment it's better for tasks themselves to check cancel status
|
||||
for (Future<?> future : futures) {
|
||||
@@ -104,6 +105,12 @@ public class ConcurrentTasks<T> {
|
||||
}
|
||||
|
||||
public ConcurrentTasks(final ProgressIndicator parentIndicator, final Consumer<Consumer<T>>... tasks) {
|
||||
myParentIndicator = parentIndicator;
|
||||
myTasks = Arrays.asList(tasks);
|
||||
mySemaphore = new Semaphore();
|
||||
}
|
||||
|
||||
public ConcurrentTasks(final ProgressIndicator parentIndicator, final List<Consumer<Consumer<T>>> tasks) {
|
||||
myParentIndicator = parentIndicator;
|
||||
myTasks = tasks;
|
||||
mySemaphore = new Semaphore();
|
||||
|
||||
@@ -84,6 +84,6 @@ public class VcsDescriptor implements Comparable<VcsDescriptor> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myDisplayName;
|
||||
return getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.intellij.ui.EditorNotifications;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.EventDispatcher;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.messages.Topic;
|
||||
import org.jdom.Element;
|
||||
@@ -852,7 +853,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
|
||||
private boolean doCommit(final LocalChangeList changeList, final List<Change> changes, final boolean synchronously) {
|
||||
return new CommitHelper(myProject, changeList, changes, changeList.getName(),
|
||||
changeList.getComment(), new ArrayList<CheckinHandler>(), false, synchronously, null).doCommit();
|
||||
changeList.getComment(), new ArrayList<CheckinHandler>(), false, synchronously, NullableFunction.NULL).doCommit();
|
||||
}
|
||||
|
||||
public void commitChangesSynchronously(LocalChangeList changeList, List<Change> changes) {
|
||||
|
||||
+2
@@ -24,6 +24,7 @@ package com.intellij.openapi.vcs.changes.committed;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vcs.*;
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentProvider;
|
||||
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
|
||||
@@ -89,6 +90,7 @@ public class CommittedChangesViewManager implements ChangesViewContentProvider {
|
||||
public void disposeContent() {
|
||||
myVcsManager.removeVcsListener(myVcsListener);
|
||||
myConnection.disconnect();
|
||||
Disposer.dispose(myComponent);
|
||||
myComponent = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,10 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
|
||||
myBuffer.flush();
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (mySession == null) {
|
||||
// nothing to be done, exit
|
||||
return;
|
||||
}
|
||||
ensureHistoryPanelCreated().getHistoryPanelRefresh().finished();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -232,6 +232,9 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
|
||||
Set<String> unchecked = new HashSet<String>();
|
||||
uncheckedCommits.put(r.root, unchecked);
|
||||
if (r.commits.size() == 0) {
|
||||
if (r.remoteCommits > 0) {
|
||||
roots.add(r.root);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
boolean seenCheckedNode = false;
|
||||
|
||||
+4
-3
@@ -89,7 +89,7 @@ public class GitBranchConfigurationChangedDialog extends DialogWrapper {
|
||||
myTable.setModel(new DescriptorTableModel());
|
||||
myNameTextField.setText(config.getName());
|
||||
myNewAction = new DialogWrapperExitAction("New Configuration", NEW_CONFIGURATION);
|
||||
myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
final DocumentAdapter l = new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
String text = myNameTextField.getText().trim();
|
||||
@@ -113,8 +113,9 @@ public class GitBranchConfigurationChangedDialog extends DialogWrapper {
|
||||
setOKActionEnabled(s == null);
|
||||
myNewAction.setEnabled(s == null);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
myNameTextField.getDocument().addDocumentListener(l);
|
||||
l.changedUpdate(null);
|
||||
setOKButtonText("Update");
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.util.EventDispatcher;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitRevisionNumber;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.commands.GitCommand;
|
||||
@@ -525,7 +526,21 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
|
||||
if (myCurrentConfiguration == null) {
|
||||
// the configuration does not matches any standard, there could be no configurations with spaces at this point
|
||||
// since it is not allowed branch name.
|
||||
String name = "Unknown 1";
|
||||
String name = "untitled";
|
||||
if (locals.contains(name)) {
|
||||
String p = name;
|
||||
name = null;
|
||||
for (int i = 0; i < Integer.MAX_VALUE; i++) {
|
||||
final String c = name + i;
|
||||
if (!locals.contains(c)) {
|
||||
name = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = "untitled 1";
|
||||
}
|
||||
}
|
||||
GitBranchConfiguration c = createConfiguration(name);
|
||||
for (VirtualFile root : myGitRoots) {
|
||||
c.setBranch(root.getPath(), describeRoot(root));
|
||||
@@ -562,7 +577,7 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
|
||||
GitBranch current = GitBranch.current(myProject, root);
|
||||
if (current == null) {
|
||||
// It is on the tag or specific commit. In future, support for submodules should be added.
|
||||
return detectTag(root);
|
||||
return detectTag(root, "HEAD");
|
||||
}
|
||||
else {
|
||||
return current.getName();
|
||||
@@ -573,12 +588,13 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
|
||||
* Get tag name for the head
|
||||
*
|
||||
* @param root the root to describe
|
||||
* @param ref the ref to detect
|
||||
* @return the commit expression that describes root state
|
||||
*/
|
||||
private String detectTag(VirtualFile root) {
|
||||
String detectTag(VirtualFile root, final String ref) {
|
||||
try {
|
||||
GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.DESCRIBE);
|
||||
h.addParameters("--tags", "--exact", "HEAD");
|
||||
h.addParameters("--tags", "--exact", ref);
|
||||
h.setNoSSH(true);
|
||||
return h.run().trim();
|
||||
}
|
||||
@@ -586,11 +602,8 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("describe HEAD failed for root: " + root.getPath());
|
||||
}
|
||||
GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.SHOW);
|
||||
h.setNoSSH(true);
|
||||
h.addParameters("--pretty=format:%H", "HEAD");
|
||||
try {
|
||||
return h.run().trim();
|
||||
return GitRevisionNumber.resolve(myProject, root, ref).asString();
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
throw new RuntimeException("Unexpected exception at this time, the failure should have been detected at current(): ", e1);
|
||||
@@ -601,7 +614,7 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
|
||||
/**
|
||||
* Detect possible configurations
|
||||
*
|
||||
* @param roots
|
||||
* @param roots the vcs roots used to detect configuraitons
|
||||
* @return a sorted list of branches
|
||||
* @throws VcsException if there is a problem with running git
|
||||
*/
|
||||
|
||||
@@ -69,7 +69,6 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
* The logger
|
||||
*/
|
||||
private static final Logger LOG = Logger.getInstance(GitBranchesWidget.class.getName());
|
||||
|
||||
/**
|
||||
* The ID of the widget
|
||||
*/
|
||||
@@ -94,6 +93,10 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
* The selectable configurations. Null if invalidated or non-initialized
|
||||
*/
|
||||
private AnAction[] mySelectableConfigurations;
|
||||
/**
|
||||
* The selectable configurations. Null if invalidated or non-initialized
|
||||
*/
|
||||
private AnAction[] mySelectableWithChangesConfigurations;
|
||||
/**
|
||||
* The candidate remote configurations. Null if invalidated or non-initialized
|
||||
*/
|
||||
@@ -230,7 +233,7 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
if (myRemoveConfigurations == null) {
|
||||
ArrayList<AnAction> rc = new ArrayList<AnAction>();
|
||||
for (final String c : myConfigurations.getRemotesCandidates()) {
|
||||
rc.add(new DumbAwareAction(c) {
|
||||
rc.add(new DumbAwareAction(escapeActionText(c)) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
myConfigurations.startCheckout(null, c, false);
|
||||
@@ -273,13 +276,13 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that action for checking out configurations are crated
|
||||
*
|
||||
* @return get or create selectable configuration group
|
||||
*/
|
||||
private AnAction[] getSelectable() {
|
||||
private AnAction[] ensureSelectableCreated() {
|
||||
assert myPopupEnabled : "pop should be enabled";
|
||||
if (mySelectableConfigurations == null) {
|
||||
ArrayList<AnAction> rc = new ArrayList<AnAction>();
|
||||
|
||||
if (mySelectableConfigurations == null || mySelectableWithChangesConfigurations == null) {
|
||||
GitBranchConfiguration current;
|
||||
try {
|
||||
current = myConfigurations.getCurrentConfiguration();
|
||||
@@ -287,35 +290,50 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
catch (VcsException e) {
|
||||
LOG.error("Unexpected error at this point", e);
|
||||
mySelectableConfigurations = new AnAction[0];
|
||||
mySelectableWithChangesConfigurations = new AnAction[0];
|
||||
|
||||
return mySelectableConfigurations;
|
||||
}
|
||||
String name = current == null ? "" : current.getName();
|
||||
for (final String c : myConfigurations.getConfigurationNames()) {
|
||||
if (name.equals(c)) {
|
||||
// skip current config
|
||||
continue;
|
||||
}
|
||||
rc.add(new DumbAwareAction(c) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
try {
|
||||
final GitBranchConfiguration toCheckout = myConfigurations.getConfiguration(c);
|
||||
if (toCheckout == null) {
|
||||
throw new VcsException("The configuration " + c + " cannot be found.");
|
||||
}
|
||||
myConfigurations.startCheckout(toCheckout, null, true);
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
GitUIUtil.showOperationError(myProject, e1, "Unable to load: " + c);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
mySelectableConfigurations = rc.toArray(new AnAction[rc.size()]);
|
||||
mySelectableConfigurations = checkoutActions(name, true);
|
||||
mySelectableWithChangesConfigurations = checkoutActions(name, false);
|
||||
}
|
||||
return mySelectableConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout actions
|
||||
*
|
||||
* @param name the excluded name
|
||||
* @param quick true, if quick checkout actions
|
||||
* @return an array of actions for the configurations
|
||||
*/
|
||||
private AnAction[] checkoutActions(String name, final boolean quick) {
|
||||
ArrayList<AnAction> rc = new ArrayList<AnAction>();
|
||||
for (final String c : myConfigurations.getConfigurationNames()) {
|
||||
if (name.equals(c)) {
|
||||
// skip current config
|
||||
continue;
|
||||
}
|
||||
rc.add(new DumbAwareAction(escapeActionText(c)) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
try {
|
||||
final GitBranchConfiguration toCheckout = myConfigurations.getConfiguration(c);
|
||||
if (toCheckout == null) {
|
||||
throw new VcsException("The configuration " + c + " cannot be found.");
|
||||
}
|
||||
myConfigurations.startCheckout(toCheckout, null, quick);
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
GitUIUtil.showOperationError(myProject, e1, "Unable to load: " + c);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return rc.toArray(new AnAction[rc.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the action group for popup
|
||||
*/
|
||||
@@ -347,12 +365,24 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
}
|
||||
});
|
||||
myPopupActionGroup.add(new MyRemotesActionGroup());
|
||||
myPopupActionGroup.add(new MySelectableWithChangesActionGroup());
|
||||
myPopupActionGroup.addSeparator("Branch Configurations");
|
||||
myPopupActionGroup.add(new MySelectableActionGroup());
|
||||
}
|
||||
return myPopupActionGroup;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape action text (underscores)
|
||||
*
|
||||
* @param t the text to escape
|
||||
* @return escaped text
|
||||
*/
|
||||
private static String escapeActionText(String t) {
|
||||
return t.replaceAll("_", "__");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update label on the widget
|
||||
*/
|
||||
@@ -466,7 +496,29 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
return getSelectable();
|
||||
return ensureSelectableCreated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remotes action group
|
||||
*/
|
||||
class MySelectableWithChangesActionGroup extends ActionGroup {
|
||||
/**
|
||||
* The constructor
|
||||
*/
|
||||
public MySelectableWithChangesActionGroup() {
|
||||
super("Check out with Selected Changes...", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
ensureSelectableCreated();
|
||||
return mySelectableWithChangesConfigurations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,6 +556,7 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
@Override
|
||||
public void run() {
|
||||
mySelectableConfigurations = null;
|
||||
mySelectableWithChangesConfigurations = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -535,6 +588,7 @@ public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidge
|
||||
@Override
|
||||
public void run() {
|
||||
mySelectableConfigurations = null;
|
||||
mySelectableWithChangesConfigurations = null;
|
||||
updateLabel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -210,7 +210,7 @@ public class GitCheckoutProcess {
|
||||
// preparation phase finished. do actual checkout.
|
||||
assert myNewConfiguration != null;
|
||||
List<VirtualFile> checkoutRoots = rootsToCheckout();
|
||||
if (checkoutRoots.size() > 0) {
|
||||
if (myNewConfiguration != oldConfiguration || checkoutRoots.size() > 0) {
|
||||
// TODO disable saving
|
||||
myProgress.setText("Shelving changes...");
|
||||
Pair<BranchChanges, BranchChanges> changesPair = shelveChanges(myProgress, oldConfiguration.getName(), selected);
|
||||
@@ -218,15 +218,20 @@ public class GitCheckoutProcess {
|
||||
// save changes in old root, it also may be aliased with new root
|
||||
oldConfiguration.setChanges(changesPair.first);
|
||||
try {
|
||||
HashSet<VirtualFile> startedRoots = new HashSet<VirtualFile>();
|
||||
boolean failed = !checkoutAndRefreshRoots(checkoutRoots, startedRoots);
|
||||
myProgress.setText2("");
|
||||
if (!failed) {
|
||||
myConfig.setCurrentConfiguration(myNewConfiguration);
|
||||
if (checkoutRoots.size() > 0) {
|
||||
HashSet<VirtualFile> startedRoots = new HashSet<VirtualFile>();
|
||||
boolean failed = !checkoutAndRefreshRoots(checkoutRoots, startedRoots);
|
||||
myProgress.setText2("");
|
||||
if (!failed) {
|
||||
myConfig.setCurrentConfiguration(myNewConfiguration);
|
||||
}
|
||||
else {
|
||||
myNewConfiguration = oldConfiguration;
|
||||
rollbackRootCheckout(startedRoots);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myNewConfiguration = oldConfiguration;
|
||||
rollbackRootCheckout(startedRoots);
|
||||
myConfig.setCurrentConfiguration(myNewConfiguration);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -241,9 +246,6 @@ public class GitCheckoutProcess {
|
||||
}
|
||||
// TODO enable saving
|
||||
}
|
||||
else {
|
||||
myConfig.setCurrentConfiguration(myNewConfiguration);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myProjectManager.unblockReloadingProjectOnExternalChanges();
|
||||
@@ -253,6 +255,9 @@ public class GitCheckoutProcess {
|
||||
catch (VcsException e) {
|
||||
myExceptions.add(e);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
myExceptions.add(new VcsException("The checkout process failed: " + e.getMessage(), e));
|
||||
}
|
||||
finally {
|
||||
saveAll(); // saves configuration changes
|
||||
}
|
||||
|
||||
@@ -168,12 +168,12 @@ public class GitManageConfigurationsDialog extends DialogWrapper {
|
||||
myShelveNameLabel.setText(d);
|
||||
myShelveNameLabel.setToolTipText("<html><table><tr><td>Shelve Path:</td><td>" +
|
||||
StringUtil.escapeXml(ch.SHELVE_PATH) +
|
||||
"</td></tr><tr><td>Shelve Path:</td><td>" +
|
||||
"</td></tr><tr><td>Shelve Name:</td><td>" +
|
||||
StringUtil.escapeXml(d) +
|
||||
"</td></tr></html>");
|
||||
}
|
||||
}
|
||||
boolean isNonCurrent = selected != null || selected != current;
|
||||
boolean isNonCurrent = selected != null && selected != current;
|
||||
myDeleteButton.setEnabled(isNonCurrent);
|
||||
setOKActionEnabled(isNonCurrent && myConfigurations.getSpecialStatus() == GitBranchConfigurations.SpecialStatus.NORMAL);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.intellij.ui.table.JBTable;
|
||||
import com.intellij.util.ui.AbstractTableCellEditor;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitRevisionNumber;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.validators.GitBranchNameValidator;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -58,6 +59,10 @@ import java.util.List;
|
||||
* The switch branches dialog
|
||||
*/
|
||||
public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
/**
|
||||
* The prefix for remote references
|
||||
*/
|
||||
public static final String REMOTES_PREFIX = "remotes/";
|
||||
/**
|
||||
* The branch configuration name text field
|
||||
*/
|
||||
@@ -168,6 +173,12 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
else {
|
||||
myBranches = prepareBranchDescriptors(target, roots);
|
||||
}
|
||||
Collections.sort(myBranches, new Comparator<BranchDescriptor>() {
|
||||
@Override
|
||||
public int compare(BranchDescriptor o1, BranchDescriptor o2) {
|
||||
return o1.getRoot().compareTo(o2.getRoot());
|
||||
}
|
||||
});
|
||||
if (target == null) {
|
||||
myNameTextField.setText(generateNewConfigurationName());
|
||||
}
|
||||
@@ -205,6 +216,7 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
verify();
|
||||
}
|
||||
});
|
||||
verify();
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -218,7 +230,7 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
* @param remoteBranch the remote branch
|
||||
* @param config the configuration
|
||||
* @param isModify the modify mode flag
|
||||
* @return the pair of selected changes and
|
||||
* @return the dialog result object
|
||||
* @throws VcsException if there is a problem with accessing git
|
||||
*/
|
||||
@Nullable
|
||||
@@ -256,7 +268,11 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
for (BranchDescriptor d : myBranches) {
|
||||
if (d.root != null) {
|
||||
if (!StringUtil.isEmpty(d.newBranchName)) {
|
||||
rc.referencesToUse.put(d.root, d.referenceToCheckout.trim());
|
||||
String ref = d.referenceToCheckout.trim();
|
||||
if (!d.existingBranches.contains(ref)) {
|
||||
ref = myConfig.detectTag(d.root, ref);
|
||||
}
|
||||
rc.referencesToUse.put(d.root, ref);
|
||||
rc.target.setBranch(d.root.getPath(), d.newBranchName.trim());
|
||||
rc.checkoutNeeded.add(d.root);
|
||||
}
|
||||
@@ -334,11 +350,11 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = "Unnamed";
|
||||
name = "untitled";
|
||||
}
|
||||
if (myExistingConfigNames.contains(name)) {
|
||||
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
||||
String t = name + " " + i;
|
||||
String t = name + i;
|
||||
if (!myExistingConfigNames.contains(t)) {
|
||||
name = t;
|
||||
break;
|
||||
@@ -361,19 +377,31 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
assert roots.size() > 0;
|
||||
List<BranchDescriptor> rc = new ArrayList<BranchDescriptor>();
|
||||
HashSet<String> allBranches = new HashSet<String>();
|
||||
allBranches.addAll(myConfig.getConfigurationNames());
|
||||
final String qualifiedBranch = "remotes/" + remoteBranch;
|
||||
String firstRemote = remoteBranch.endsWith("/HEAD") ? null : qualifiedBranch;
|
||||
for (VirtualFile root : roots) {
|
||||
BranchDescriptor d = new BranchDescriptor();
|
||||
d.root = root;
|
||||
d.currentReference = myConfig.describeRoot(root);
|
||||
d.referenceToCheckout = "remotes/" + remoteBranch;
|
||||
if (firstRemote == null) {
|
||||
firstRemote = resolveHead(qualifiedBranch, d.root.getPath());
|
||||
}
|
||||
d.referenceToCheckout = qualifiedBranch;
|
||||
GitBranch.listAsStrings(myProject, root, false, true, d.existingBranches, null);
|
||||
GitBranch.listAsStrings(myProject, root, true, true, d.referencesToSelect, null);
|
||||
allBranches.addAll(d.existingBranches);
|
||||
rc.add(d);
|
||||
}
|
||||
int p = remoteBranch.indexOf('/');
|
||||
assert p > 0 && p < remoteBranch.length() - 1 : "Unexpected format for remote branch: " + remoteBranch;
|
||||
String candidate = remoteBranch.substring(p + 1);
|
||||
String candidate;
|
||||
if (firstRemote == null) {
|
||||
candidate = "untitled";
|
||||
}
|
||||
else {
|
||||
int p = firstRemote.indexOf('/', REMOTES_PREFIX.length() + 1);
|
||||
assert p > 0 && p < firstRemote.length() - 1 : "Unexpected format for remote branch: " + firstRemote;
|
||||
candidate = firstRemote.substring(p + 1);
|
||||
}
|
||||
String actual = null;
|
||||
if (!allBranches.contains(candidate)) {
|
||||
actual = candidate;
|
||||
@@ -395,6 +423,73 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get candidate new branch name from remote branch
|
||||
*
|
||||
* @param value the value used to guess new reference
|
||||
* @param d a description to use
|
||||
* @return the candidate branch name
|
||||
*/
|
||||
@Nullable
|
||||
private String getCandidateLocal(String value, BranchDescriptor d) {
|
||||
if (StringUtil.isEmpty(value) || !value.startsWith(REMOTES_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
int p = value.indexOf('/', REMOTES_PREFIX.length() + 1);
|
||||
String candidate = null;
|
||||
if (p != -1) {
|
||||
String c = value.substring(p + 1);
|
||||
if (!d.existingBranches.contains(c)) {
|
||||
candidate = c;
|
||||
}
|
||||
else {
|
||||
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
||||
String cn = c + i;
|
||||
if (!d.existingBranches.contains(cn)) {
|
||||
candidate = cn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ("HEAD".equals(candidate)) {
|
||||
final String rootPath = d.root.getPath();
|
||||
String newRef = resolveHead(value, rootPath);
|
||||
candidate = newRef == null ? null : getCandidateLocal(newRef, d);
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve remote had reference
|
||||
*
|
||||
* @param value the reference to resolve
|
||||
* @param rootPath the root path
|
||||
* @return the resolved reference or null
|
||||
*/
|
||||
@Nullable
|
||||
private static String resolveHead(String value, String rootPath) {
|
||||
if (!value.startsWith("remotes/")) {
|
||||
return null;
|
||||
}
|
||||
String newRef;
|
||||
try {
|
||||
final String refText =
|
||||
new String(FileUtil.loadFileText(new File(rootPath, ".git/refs/" + value), GitUtil.UTF8_ENCODING)).trim();
|
||||
String refsPrefix = "ref: refs/";
|
||||
if (refText.endsWith("/HEAD") || !refText.startsWith(refsPrefix)) {
|
||||
newRef = null;
|
||||
}
|
||||
else {
|
||||
newRef = refText.substring(refsPrefix.length());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
newRef = null;
|
||||
}
|
||||
return newRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare branch descriptors for existing configuration
|
||||
*
|
||||
@@ -441,7 +536,9 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myRoot;
|
||||
@@ -472,16 +569,25 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
*/
|
||||
static final int COLUMNS = STATUS_COLUMN + 1;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return myBranches.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMNS;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
BranchDescriptor d = myBranches.get(rowIndex);
|
||||
@@ -491,6 +597,9 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
return columnIndex == REVISION_COLUMN || columnIndex == NEW_BRANCH_COLUMN;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
String t = (String)aValue;
|
||||
@@ -499,24 +608,15 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
return;
|
||||
}
|
||||
if (columnIndex == REVISION_COLUMN) {
|
||||
String currentCandidate = getCandidateLocal(d.referenceToCheckout, d);
|
||||
boolean isCurrentMatchCandidate = currentCandidate != null && currentCandidate.equals(d.newBranchName);
|
||||
d.referenceToCheckout = t;
|
||||
String remotesPrefix = "remotes/";
|
||||
if (StringUtil.isEmpty(d.newBranchName) && t.startsWith(remotesPrefix) && d.referencesToSelect.contains(t)) {
|
||||
int p = t.indexOf(t.indexOf('/'), remotesPrefix.length() + 1);
|
||||
if (p != -1) {
|
||||
String c = t.substring(p);
|
||||
if (!d.existingBranches.contains(c)) {
|
||||
d.newBranchName = c;
|
||||
}
|
||||
else {
|
||||
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
||||
String candidate = c + i;
|
||||
if (!d.existingBranches.contains(c)) {
|
||||
d.newBranchName = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((StringUtil.isEmpty(d.newBranchName) || isCurrentMatchCandidate) &&
|
||||
t.startsWith(REMOTES_PREFIX) &&
|
||||
d.referencesToSelect.contains(t)) {
|
||||
String candidate = getCandidateLocal(t, d);
|
||||
if (candidate != null) {
|
||||
d.newBranchName = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,6 +627,10 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
fireTableRowsUpdated(rowIndex, rowIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
BranchDescriptor d = myBranches.get(rowIndex);
|
||||
@@ -561,6 +665,9 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
switch (column) {
|
||||
@@ -819,8 +926,7 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
String s = myTextField.getText();
|
||||
if (s.length() == 0 &&
|
||||
(myInvalidValues == null || !myInvalidValues.contains(s)) &&
|
||||
if ((myInvalidValues == null || !myInvalidValues.contains(s)) &&
|
||||
(s.length() == 0 || GitBranchNameValidator.INSTANCE.checkInput(s))) {
|
||||
myTextField.setForeground(myDefaultForeground);
|
||||
}
|
||||
|
||||
+33
-30
@@ -17,12 +17,16 @@ package org.jetbrains.plugins.groovy.actions.generate.constructors;
|
||||
|
||||
import com.intellij.codeInsight.generation.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.compiler.generator.GroovyToJavaGenerator;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
|
||||
@@ -35,55 +39,50 @@ import java.util.List;
|
||||
*/
|
||||
public class ConstructorGenerateHandler extends GenerateConstructorHandler {
|
||||
|
||||
private static final String DEF_PSEUDO_ANNO = "_____intellij_idea_rulez_def_";
|
||||
|
||||
@Nullable
|
||||
protected ClassMember[] chooseOriginalMembers(PsiClass aClass, Project project) {
|
||||
final ClassMember[] classMembers = super.chooseOriginalMembers(aClass, project);
|
||||
|
||||
final ClassMember[] classMembers = chooseOriginalMembersImpl(aClass, project);
|
||||
if (classMembers == null) return null;
|
||||
|
||||
List<ClassMember> res = new ArrayList<ClassMember>();
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
|
||||
String text;
|
||||
|
||||
for (ClassMember classMember : classMembers) {
|
||||
|
||||
if (classMember instanceof PsiMethodMember) {
|
||||
PsiMethod constructorImpl;
|
||||
final PsiMethod method = ((PsiMethodMember) classMember).getElement();
|
||||
|
||||
//TODO: rewrite it like fine java method
|
||||
text = method.getText();
|
||||
try {
|
||||
constructorImpl = factory.createMethodFromText(text, aClass);
|
||||
res.add(new PsiMethodMember(constructorImpl));
|
||||
} catch (IncorrectOperationException e) {
|
||||
e.printStackTrace();
|
||||
final PsiMethod method = ((PsiMethodMember)classMember).getElement();
|
||||
final PsiMethod copy = (PsiMethod)method.copy();
|
||||
if (copy instanceof GrMethod) {
|
||||
for (GrParameter parameter : ((GrMethod)copy).getParameterList().getParameters()) {
|
||||
if (parameter.getTypeElementGroovy() == null) {
|
||||
parameter.setName(DEF_PSEUDO_ANNO + parameter.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.add(new PsiMethodMember(factory.createMethodFromText(GroovyToJavaGenerator.generateMethodStub(copy), aClass)));
|
||||
} else if (classMember instanceof PsiFieldMember) {
|
||||
final PsiFieldMember fieldMember = (PsiFieldMember) classMember;
|
||||
PsiField fieldImpl;
|
||||
final PsiField field = ((PsiFieldMember) classMember).getElement();
|
||||
|
||||
final PsiField field = fieldMember.getElement();
|
||||
try {
|
||||
fieldImpl = factory.createFieldFromText(field.getType().getCanonicalText() + " " + field.getName(), aClass);
|
||||
res.add(new PsiFieldMember(fieldImpl));
|
||||
} catch (IncorrectOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String prefix = field instanceof GrField && ((GrField)field).getTypeElementGroovy() == null ? DEF_PSEUDO_ANNO : "";
|
||||
res.add(new PsiFieldMember(factory.createFieldFromText(field.getType().getCanonicalText() + " " + prefix + field.getName(), aClass)));
|
||||
}
|
||||
}
|
||||
|
||||
return res.toArray(new ClassMember[res.size()]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ClassMember[] chooseOriginalMembersImpl(PsiClass aClass, Project project) {
|
||||
return super.chooseOriginalMembers(aClass, project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected List<? extends GenerationInfo> generateMemberPrototypes(PsiClass aClass, ClassMember[] members) throws IncorrectOperationException {
|
||||
final List<? extends GenerationInfo> list = super.generateMemberPrototypes(aClass, members);
|
||||
|
||||
List<PsiGenerationInfo<GrMethod>> grConstructors = new ArrayList<PsiGenerationInfo<GrMethod>>();
|
||||
|
||||
GrMethod grConstructor;
|
||||
for (GenerationInfo generationInfo : list) {
|
||||
final PsiMember constructorMember = generationInfo.getPsiMember();
|
||||
assert constructorMember instanceof PsiMethod;
|
||||
@@ -93,22 +92,26 @@ public class ConstructorGenerateHandler extends GenerateConstructorHandler {
|
||||
assert block != null;
|
||||
|
||||
final String constructorName = aClass.getName();
|
||||
final String body = block.getText();
|
||||
final String body = StringUtil.replace(StringUtil.replace(block.getText(), DEF_PSEUDO_ANNO, ""), ";", "");
|
||||
final PsiParameterList list1 = constructor.getParameterList();
|
||||
|
||||
List<String> parametersNames = new ArrayList<String>();
|
||||
List<String> parametersTypes = new ArrayList<String>();
|
||||
for (PsiParameter parameter : list1.getParameters()) {
|
||||
parametersNames.add(parameter.getName());
|
||||
final String fullName = parameter.getName();
|
||||
parametersNames.add(StringUtil.trimStart(fullName, DEF_PSEUDO_ANNO));
|
||||
parametersTypes.add(fullName.startsWith(DEF_PSEUDO_ANNO) ? null : parameter.getType().getCanonicalText());
|
||||
}
|
||||
|
||||
final String[] paramNames = ArrayUtil.toStringArray(parametersNames);
|
||||
final String[] paramTypes = ArrayUtil.toStringArray(parametersTypes);
|
||||
assert constructorName != null;
|
||||
grConstructor = GroovyPsiElementFactory.getInstance(aClass.getProject()).createConstructorFromText(constructorName, null, paramNames, body);
|
||||
GrMethod grConstructor =
|
||||
GroovyPsiElementFactory.getInstance(aClass.getProject()).createConstructorFromText(constructorName, paramTypes, paramNames, body);
|
||||
|
||||
PsiUtil.shortenReferences(grConstructor);
|
||||
|
||||
final PsiGenerationInfo<GrMethod> psiGenerationInfo = new GroovyGenerationInfo<GrMethod>(grConstructor);
|
||||
grConstructors.add(psiGenerationInfo);
|
||||
grConstructors.add(new GroovyGenerationInfo<GrMethod>(grConstructor));
|
||||
}
|
||||
|
||||
return grConstructors;
|
||||
|
||||
+4
-5
@@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.codeInspection.assignment;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -31,6 +32,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
|
||||
@@ -109,11 +111,8 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
public void visitReturnStatement(GrReturnStatement returnStatement) {
|
||||
super.visitReturnStatement(returnStatement);
|
||||
|
||||
final PsiElement parent = returnStatement.getParent();
|
||||
if (!(parent instanceof GrOpenBlock)) return;
|
||||
final PsiElement element = parent.getParent();
|
||||
if (!(element instanceof GrMethod)) return;
|
||||
GrMethod method = (GrMethod)element;
|
||||
final GrMethod method = PsiTreeUtil.getParentOfType(returnStatement, GrMethod.class, true, GrClosableBlock.class);
|
||||
if (method == null) return;
|
||||
|
||||
final GrExpression value = returnStatement.getReturnValue();
|
||||
final PsiType expectedType = method.getReturnType();
|
||||
|
||||
+2
-3
@@ -123,15 +123,14 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase {
|
||||
if (element instanceof GrReferenceExpression) {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof GrAssignmentExpression) {
|
||||
toHighlight = ((GrAssignmentExpression)parent).getRValue();
|
||||
toHighlight = ((GrAssignmentExpression)parent).getLValue();
|
||||
}
|
||||
if (parent instanceof GrPostfixExpression) {
|
||||
toHighlight = parent;
|
||||
}
|
||||
}
|
||||
else if (element instanceof GrVariable) {
|
||||
toHighlight = ((GrVariable)element).getInitializerGroovy();
|
||||
if (toHighlight == null) toHighlight = ((GrVariable)element).getNameIdentifierGroovy();
|
||||
toHighlight = ((GrVariable)element).getNameIdentifierGroovy();
|
||||
}
|
||||
if (toHighlight == null) toHighlight = element;
|
||||
problemsHolder.registerProblem(toHighlight, GroovyInspectionBundle.message("unused.assignment.tooltip"),
|
||||
|
||||
+69
-27
@@ -38,6 +38,7 @@ import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.CollectionFactory;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
@@ -49,6 +50,7 @@ import org.jetbrains.plugins.groovy.compiler.GroovyCompilerConfiguration;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrClassSubstitutor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation;
|
||||
@@ -107,11 +109,13 @@ public class GroovyToJavaGenerator {
|
||||
|
||||
private static final CharSequence PREFIX_SEPARATOR = "/";
|
||||
private final CompileContext myContext;
|
||||
private final List<VirtualFile> myAllToCompile;
|
||||
private final Project myProject;
|
||||
|
||||
public GroovyToJavaGenerator(Project project, CompileContext context) {
|
||||
public GroovyToJavaGenerator(Project project, CompileContext context, List<VirtualFile> allToCompile) {
|
||||
myProject = project;
|
||||
myContext = context;
|
||||
myAllToCompile = allToCompile;
|
||||
}
|
||||
|
||||
public GenerationItem[] getGenerationItems(CompileContext context) {
|
||||
@@ -420,7 +424,7 @@ public class GroovyToJavaGenerator {
|
||||
text.append("}");
|
||||
}
|
||||
|
||||
private static void writeAllMethods(StringBuffer text, List<PsiMethod> methods, PsiClass aClass) {
|
||||
private void writeAllMethods(StringBuffer text, List<PsiMethod> methods, PsiClass aClass) {
|
||||
Set<MethodSignature> methodSignatures = new HashSet<MethodSignature>();
|
||||
for (PsiMethod method : methods) {
|
||||
if (LightMethodBuilder.isLightMethod(method, GrClassImplUtil.SYNTHETIC_METHOD_IMPLEMENTATION)) {
|
||||
@@ -508,7 +512,7 @@ public class GroovyToJavaGenerator {
|
||||
return psiClass != null && GrClassSubstitutor.getSubstitutedClass(psiClass).isInterface();
|
||||
}
|
||||
|
||||
private static void appendTypeParameters(StringBuffer text, PsiTypeParameterListOwner typeParameterListOwner) {
|
||||
private void appendTypeParameters(StringBuffer text, PsiTypeParameterListOwner typeParameterListOwner) {
|
||||
if (typeParameterListOwner.hasTypeParameters()) {
|
||||
text.append("<");
|
||||
PsiTypeParameter[] parameters = typeParameterListOwner.getTypeParameters();
|
||||
@@ -529,7 +533,7 @@ public class GroovyToJavaGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeEnumConstants(StringBuffer text, GrEnumTypeDefinition enumDefinition) {
|
||||
private void writeEnumConstants(StringBuffer text, GrEnumTypeDefinition enumDefinition) {
|
||||
text.append("\n ");
|
||||
GrEnumConstant[] enumConstants = enumDefinition.getEnumConstants();
|
||||
for (int i = 0; i < enumConstants.length; i++) {
|
||||
@@ -555,7 +559,7 @@ public class GroovyToJavaGenerator {
|
||||
text.append(";");
|
||||
}
|
||||
|
||||
private static void writeStubConstructorInvocation(StringBuffer text, PsiMethod constructor, PsiSubstitutor substitutor) {
|
||||
private void writeStubConstructorInvocation(StringBuffer text, PsiMethod constructor, PsiSubstitutor substitutor) {
|
||||
final PsiParameter[] superParams = constructor.getParameterList().getParameters();
|
||||
for (int j = 0; j < superParams.length; j++) {
|
||||
if (j > 0) text.append(", ");
|
||||
@@ -574,7 +578,7 @@ public class GroovyToJavaGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeConstructor(final StringBuffer text, final GrConstructor constructor, boolean isEnum) {
|
||||
private void writeConstructor(final StringBuffer text, final GrConstructor constructor, boolean isEnum) {
|
||||
text.append("\n");
|
||||
text.append(" ");
|
||||
if (!isEnum) {
|
||||
@@ -613,7 +617,7 @@ public class GroovyToJavaGenerator {
|
||||
text.append("\n }\n");
|
||||
}
|
||||
|
||||
private static Set<String> collectThrowsTypes(GrConstructor constructor, Set<PsiMethod> visited) {
|
||||
private Set<String> collectThrowsTypes(GrConstructor constructor, Set<PsiMethod> visited) {
|
||||
final GroovyResolveResult resolveResult = resolveChainingConstructor(constructor);
|
||||
if (resolveResult == null) {
|
||||
return Collections.emptySet();
|
||||
@@ -673,7 +677,7 @@ public class GroovyToJavaGenerator {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void writeVariableDeclarations(StringBuffer text, GrVariableDeclaration variableDeclaration) {
|
||||
private void writeVariableDeclarations(StringBuffer text, GrVariableDeclaration variableDeclaration) {
|
||||
GrTypeElement typeElement = variableDeclaration.getTypeElementGroovy();
|
||||
final String type = typeElement == null ? CommonClassNames.JAVA_LANG_OBJECT : getTypeText(typeElement.getType(), typeElement, false);
|
||||
final String initializer = getDefaultValueText(type);
|
||||
@@ -694,7 +698,23 @@ public class GroovyToJavaGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeMethod(StringBuffer text, PsiMethod method, final PsiParameter[] parameters) {
|
||||
public static String generateMethodStub(@NotNull PsiMethod method) {
|
||||
if (!(method instanceof GroovyPsiElement)) {
|
||||
return method.getText();
|
||||
}
|
||||
|
||||
final GroovyToJavaGenerator generator = new GroovyToJavaGenerator(method.getProject(), null, Collections.<VirtualFile>emptyList());
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
if (method instanceof GrConstructor) {
|
||||
generator.writeConstructor(buffer, (GrConstructor)method, false);
|
||||
}
|
||||
else {
|
||||
generator.writeMethod(buffer, method, method.getParameterList().getParameters());
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private void writeMethod(StringBuffer text, PsiMethod method, final PsiParameter[] parameters) {
|
||||
if (method == null) return;
|
||||
String name = method.getName();
|
||||
if (!JavaPsiFacade.getInstance(method.getProject()).getNameHelper().isIdentifier(name))
|
||||
@@ -739,7 +759,7 @@ public class GroovyToJavaGenerator {
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
private static void writeParameterList(StringBuffer text, PsiParameter[] parameters) {
|
||||
private void writeParameterList(StringBuffer text, PsiParameter[] parameters) {
|
||||
text.append("(");
|
||||
|
||||
//writes myParameters
|
||||
@@ -805,14 +825,7 @@ public class GroovyToJavaGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTypeText(@Nullable PsiType type, @Nullable PsiElement context, boolean allowVarargs) {
|
||||
if (context != null && type instanceof PsiClassType) {
|
||||
final String accessible = findAccessibleSuperClass(context, ((PsiClassType)type).resolve());
|
||||
if (accessible != null) {
|
||||
return accessible;
|
||||
}
|
||||
}
|
||||
|
||||
private String getTypeText(@Nullable PsiType type, @Nullable final PsiElement context, boolean allowVarargs) {
|
||||
if (type instanceof PsiArrayType) {
|
||||
String componentText = getTypeText(((PsiArrayType)type).getComponentType(), context, false);
|
||||
if (allowVarargs && type instanceof PsiEllipsisType) {
|
||||
@@ -825,28 +838,57 @@ public class GroovyToJavaGenerator {
|
||||
return CommonClassNames.JAVA_LANG_OBJECT;
|
||||
}
|
||||
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass raw = ((PsiClassType)type).resolve();
|
||||
if (raw != null) {
|
||||
final String qname = getClassQualifiedName(raw, context);
|
||||
if (qname != null) {
|
||||
final PsiType[] parameters = ((PsiClassType)type).getParameters();
|
||||
if (parameters.length > 0) {
|
||||
return qname + "<" + StringUtil.join(parameters, new Function<PsiType, String>() {
|
||||
@Override
|
||||
public String fun(PsiType type) {
|
||||
return getTypeText(type, context, false);
|
||||
}
|
||||
}, ", ") + ">";
|
||||
}
|
||||
return qname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String canonicalText = type.getCanonicalText();
|
||||
return canonicalText != null ? canonicalText : type.getPresentableText();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String findAccessibleSuperClass(PsiElement context, @Nullable PsiClass initialClass) {
|
||||
if (initialClass == null) {
|
||||
private String getClassQualifiedName(PsiClass psiClass, @Nullable PsiElement context) {
|
||||
if (context != null) {
|
||||
psiClass = findAccessibleSuperClass(context, psiClass);
|
||||
}
|
||||
if (psiClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (psiClass instanceof GrTypeDefinition) {
|
||||
if (!myAllToCompile.contains(psiClass.getContainingFile().getVirtualFile())) {
|
||||
final PsiClass container = psiClass.getContainingClass();
|
||||
if (container != null) {
|
||||
return getClassQualifiedName(container, null) + "$" + psiClass.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
return psiClass.getQualifiedName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass findAccessibleSuperClass(@NotNull PsiElement context, @NotNull PsiClass initialClass) {
|
||||
PsiClass curClass = initialClass;
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
|
||||
while (curClass != null && !resolveHelper.isAccessible(curClass, context, null)) {
|
||||
curClass = curClass.getSuperClass();
|
||||
}
|
||||
if (curClass != null && !initialClass.isEquivalentTo(curClass)) {
|
||||
final String qname = curClass.getQualifiedName();
|
||||
if (qname != null) {
|
||||
return qname;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return curClass;
|
||||
}
|
||||
|
||||
CharTrie myTrie = new CharTrie();
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ public class GroovycStubGenerator extends GroovyCompilerBase {
|
||||
if (GroovyCompilerConfiguration.getInstance(myProject).isUseGroovycStubs()) {
|
||||
runGroovycCompiler(compileContext, module, toCompile, true, tempOutput, sink, tests);
|
||||
} else {
|
||||
final GroovyToJavaGenerator generator = new GroovyToJavaGenerator(myProject, compileContext);
|
||||
final GroovyToJavaGenerator generator = new GroovyToJavaGenerator(myProject, compileContext, toCompile);
|
||||
for (VirtualFile file : toCompile) {
|
||||
final String outPath = tempOutput.getPath();
|
||||
final List<String> relPaths = generator.generateItems(file, tempOutput);
|
||||
|
||||
+85
-1
@@ -18,16 +18,27 @@ package org.jetbrains.plugins.groovy.config;
|
||||
import com.intellij.ide.util.frameworkSupport.FrameworkSupportConfigurable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.CollectionFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.config.ui.GroovyFacetEditor;
|
||||
import org.jetbrains.plugins.groovy.config.ui.ManagedLibrariesEditor;
|
||||
import org.jetbrains.plugins.groovy.util.LibrariesUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
@@ -48,11 +59,72 @@ public class GroovySupportConfigurable extends FrameworkSupportConfigurable {
|
||||
addGroovySupport(module, rootModel);
|
||||
}
|
||||
|
||||
private boolean cleanDuplicates(List<LibraryManager> managers, ModifiableRootModel rootModel, final LibrariesContainer container) {
|
||||
if (managers.isEmpty()) return true;
|
||||
|
||||
for (OrderEntry entry : rootModel.getOrderEntries()) {
|
||||
if (entry instanceof LibraryOrderEntry) {
|
||||
final Library library = ((LibraryOrderEntry)entry).getLibrary();
|
||||
if (library == null) {
|
||||
cleanUndefinedGroovyLibrary(rootModel, (LibraryOrderEntry)entry);
|
||||
} else {
|
||||
final LibraryManager manager = ManagedLibrariesEditor.findManagerFor(library, managers.toArray(new LibraryManager[managers.size()]), container);
|
||||
if (manager != null) {
|
||||
@SuppressWarnings({"NonConstantStringShouldBeStringBuffer"})
|
||||
String message = "There is already a " + manager.getLibraryCategoryName() + " library";
|
||||
final String version = manager.getLibraryVersion(library, container);
|
||||
if (StringUtil.isNotEmpty(version)) {
|
||||
message += " of version " + version;
|
||||
}
|
||||
message += ".\n Do you want to replace the existing one?";
|
||||
final String replace = "&Replace";
|
||||
final int result =
|
||||
Messages
|
||||
.showDialog(facetEditor.getComponent(), message, "Library already exists", new String[]{replace, "&Add", "&Cancel"}, 0,
|
||||
null);
|
||||
if (result == 2 || result < 0) {
|
||||
return false; //cancel or escape
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
rootModel.removeOrderEntry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void cleanUndefinedGroovyLibrary(ModifiableRootModel rootModel, LibraryOrderEntry entry) {
|
||||
final String libraryName = entry.getLibraryName();
|
||||
if (libraryName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (AbstractGroovyLibraryManager each : AbstractGroovyLibraryManager.EP_NAME.getExtensions()) {
|
||||
if (each.managesName(libraryName)) {
|
||||
rootModel.removeOrderEntry(entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addGroovySupport(final Module module, ModifiableRootModel rootModel) {
|
||||
final LibrariesContainer container = LibrariesContainerFactory.createContainer(rootModel);
|
||||
|
||||
if (!facetEditor.addNewSdk()) {
|
||||
final Library selectedLibrary = facetEditor.getSelectedLibrary();
|
||||
if (selectedLibrary != null) {
|
||||
LibrariesUtil.placeEntryToCorrectPlace(rootModel, rootModel.addLibraryEntry(selectedLibrary));
|
||||
List<LibraryManager> suitable = CollectionFactory.arrayList();
|
||||
for (final LibraryManager manager : AbstractGroovyLibraryManager.EP_NAME.getExtensions()) {
|
||||
if (manager.managesLibrary(selectedLibrary, container)) {
|
||||
suitable.add(manager);
|
||||
}
|
||||
}
|
||||
if (cleanDuplicates(suitable, rootModel, container)) {
|
||||
LibrariesUtil.placeEntryToCorrectPlace(rootModel, rootModel.addLibraryEntry(selectedLibrary));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -60,6 +132,18 @@ public class GroovySupportConfigurable extends FrameworkSupportConfigurable {
|
||||
final String path = facetEditor.getNewSdkPath();
|
||||
final AbstractGroovyLibraryManager libraryManager = facetEditor.getChosenManager();
|
||||
if (path != null && libraryManager != null) {
|
||||
List<LibraryManager> suitable = CollectionFactory.arrayList();
|
||||
suitable.add(libraryManager);
|
||||
final VirtualFile vfile = LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(path));
|
||||
if (vfile != null) {
|
||||
for (final LibraryManager manager : AbstractGroovyLibraryManager.EP_NAME.getExtensions()) {
|
||||
if (manager != libraryManager && manager.isSDKHome(vfile)) {
|
||||
suitable.add(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cleanDuplicates(suitable, rootModel, container)) return;
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (module.isDisposed()) {
|
||||
|
||||
@@ -148,7 +148,7 @@ public class GroovyPositionManager implements PositionManager {
|
||||
public String compute() {
|
||||
GrTypeDefinition typeDefinition = findEnclosingTypeDefinition(position);
|
||||
if (typeDefinition != null) {
|
||||
return typeDefinition.getQualifiedName();
|
||||
return getClassNameForJvm(typeDefinition);
|
||||
}
|
||||
return getScriptQualifiedName(position);
|
||||
}
|
||||
@@ -171,6 +171,7 @@ public class GroovyPositionManager implements PositionManager {
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getClassNameForJvm(final PsiClass typeDefinition) {
|
||||
final PsiClass psiClass = typeDefinition.getContainingClass();
|
||||
if (psiClass != null) {
|
||||
@@ -311,21 +312,15 @@ public class GroovyPositionManager implements PositionManager {
|
||||
List<ReferenceType> result = ApplicationManager.getApplication().runReadAction(new Computable<List<ReferenceType>>() {
|
||||
public List<ReferenceType> compute() {
|
||||
GroovyPsiElement sourceImage = findReferenceTypeSourceImage(position);
|
||||
final String scriptName = getScriptQualifiedName(position);
|
||||
|
||||
if (sourceImage instanceof GrTypeDefinition && !((GrTypeDefinition)sourceImage).isAnonymous()) {
|
||||
String qName = getClassNameForJvm((GrTypeDefinition)sourceImage);
|
||||
if (qName != null) return myDebugProcess.getVirtualMachineProxy().classesByName(qName);
|
||||
} else if (sourceImage == null) {
|
||||
final String scriptName = getScriptQualifiedName(position);
|
||||
if (scriptName != null) return myDebugProcess.getVirtualMachineProxy().classesByName(scriptName);
|
||||
} else {
|
||||
final GrTypeDefinition typeDefinition = findEnclosingTypeDefinition(position);
|
||||
String enclosingName;
|
||||
if (typeDefinition != null) {
|
||||
enclosingName = typeDefinition.getQualifiedName();
|
||||
} else {
|
||||
enclosingName = scriptName;
|
||||
}
|
||||
String enclosingName = findEnclosingName(position);
|
||||
if (enclosingName == null) return Collections.emptyList();
|
||||
|
||||
final List<ReferenceType> outers = myDebugProcess.getVirtualMachineProxy().classesByName(enclosingName);
|
||||
|
||||
+2
-2
@@ -47,9 +47,9 @@ public class GppExpectedTypesContributor extends GroovyExpectedTypesContributor
|
||||
assert argIndex >= 0;
|
||||
|
||||
final ArrayList<TypeConstraint> result = new ArrayList<TypeConstraint>();
|
||||
for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) {
|
||||
for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(list)) {
|
||||
if (type instanceof PsiClassType) {
|
||||
for (GroovyResolveResult resolveResult : PsiUtil.getConstructorCandidates((PsiClassType)type, argTypes, expression)) {
|
||||
for (GroovyResolveResult resolveResult : PsiUtil.getConstructorCandidates((PsiClassType)type, argTypes, list)) {
|
||||
final PsiElement method = resolveResult.getElement();
|
||||
if (method instanceof PsiMethod && ((PsiMethod)method).isConstructor()) {
|
||||
final PsiParameter[] constructorParameters = ((PsiMethod)method).getParameterList().getParameters();
|
||||
|
||||
+5
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.plugins.groovy.lang.psi.api.statements.params;
|
||||
|
||||
import com.intellij.psi.PsiModifierList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
@@ -35,5 +37,8 @@ public interface GrParameter extends PsiParameter, GrVariable {
|
||||
@Nullable
|
||||
GrExpression getDefaultInitializer();
|
||||
|
||||
@NotNull
|
||||
PsiModifierList getModifierList();
|
||||
|
||||
boolean isOptional();
|
||||
}
|
||||
|
||||
+5
-3
@@ -528,12 +528,14 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory {
|
||||
builder.append("(");
|
||||
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
String paramType = paramTypes == null ? "" : paramTypes[i];
|
||||
String paramType = paramTypes == null ? null : paramTypes[i];
|
||||
|
||||
if (i > 0) builder.append(", ");
|
||||
|
||||
builder.append(paramType);
|
||||
builder.append(" ");
|
||||
if (paramType != null) {
|
||||
builder.append(paramType);
|
||||
builder.append(" ");
|
||||
}
|
||||
builder.append(paramNames[i]);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -75,7 +75,12 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp
|
||||
argTypes[i] = argType;
|
||||
}
|
||||
|
||||
if (thisType instanceof GrTupleType &&
|
||||
if (thisType instanceof PsiArrayType &&
|
||||
argTypes.length == 1 &&
|
||||
TypesUtil.isAssignable(PsiType.INT, argTypes[0], getManager(), getResolveScope())) {
|
||||
return TypesUtil.boxPrimitiveType(((PsiArrayType)thisType).getComponentType(), getManager(), getResolveScope());
|
||||
}
|
||||
else if (thisType instanceof GrTupleType &&
|
||||
argTypes.length == 1 &&
|
||||
TypesUtil.isAssignable(PsiType.INT, argTypes[0], getManager(), getResolveScope())) {
|
||||
PsiType[] types = ((GrTupleType)thisType).getParameters();
|
||||
@@ -96,12 +101,7 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp
|
||||
}
|
||||
|
||||
PsiType componentType = null;
|
||||
if (thisType instanceof PsiArrayType &&
|
||||
argTypes.length == 1 &&
|
||||
TypesUtil.isAssignable(PsiType.INT, argTypes[0], getManager(), getResolveScope())) {
|
||||
componentType = TypesUtil.boxPrimitiveType(((PsiArrayType)thisType).getComponentType(), getManager(), getResolveScope());
|
||||
}
|
||||
else if (InheritanceUtil.isInheritor(thisType, CommonClassNames.JAVA_UTIL_MAP) && argTypes.length == 1) {
|
||||
if (InheritanceUtil.isInheritor(thisType, CommonClassNames.JAVA_UTIL_MAP) && argTypes.length == 1) {
|
||||
componentType = TypesUtil
|
||||
.boxPrimitiveType(substituteTypeParameter(thisType, CommonClassNames.JAVA_UTIL_MAP, 1, true), getManager(), getResolveScope());
|
||||
}
|
||||
|
||||
+2
-2
@@ -185,9 +185,9 @@ public class GrParameterImpl extends GrVariableImpl implements GrParameter {
|
||||
return getNameIdentifierGroovy().getTextRange().getStartOffset();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public GrModifierList getModifierList() {
|
||||
return findChildByClass(GrModifierList.class);
|
||||
return findNotNullChildByClass(GrModifierList.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+2
-1
@@ -27,6 +27,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -314,7 +315,7 @@ public class GroovyOverrideImplementUtil {
|
||||
JavaTemplateUtil.setClassAndMethodNameProperties(properties, method.getContainingClass(), resultMethod);
|
||||
|
||||
try {
|
||||
String bodyText = template.getText(properties);
|
||||
String bodyText = StringUtil.replace(template.getText(properties), ";", "");
|
||||
final GrCodeBlock newBody = GroovyPsiElementFactory.getInstance(project).createMethodBodyFromText("\n" + bodyText + "\n");
|
||||
|
||||
resultMethod.setBlock(newBody);
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.jetbrains.plugins.groovy.util.TestUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -131,7 +132,7 @@ public void testArrayType1() throws Throwable { doTest(); }
|
||||
private final String myFileContent;
|
||||
|
||||
public GroovyToJavaGeneratorTester(String relTestPath, String fileContent, Project project) {
|
||||
super(project, null);
|
||||
super(project, null, Collections.<VirtualFile>emptyList());
|
||||
myRelTestPath = relTestPath;
|
||||
myFileContent = fileContent;
|
||||
}
|
||||
|
||||
@@ -348,5 +348,46 @@ class Foo extends Bar {
|
||||
assertEmpty make()
|
||||
}
|
||||
|
||||
public void testDollarGroovyInnerClassUsagesInStubs() throws Exception {
|
||||
def javaFile = myFixture.addClass("""
|
||||
public class JavaClass {
|
||||
public static class InnerJavaClass {}
|
||||
}
|
||||
""")
|
||||
myFixture.addFileToProject("WithInner.groovy", """
|
||||
class WithInner {
|
||||
static class Inner {}
|
||||
}
|
||||
""")
|
||||
assertEmpty make()
|
||||
|
||||
myFixture.addFileToProject("Usage.groovy", """
|
||||
class Usage {
|
||||
def foo(WithInner.Inner i) {}
|
||||
def foo(JavaClass.InnerJavaClass i) {}
|
||||
}
|
||||
""")
|
||||
|
||||
touch(javaFile.containingFile.virtualFile)
|
||||
assertEmpty make()
|
||||
}
|
||||
|
||||
public void testDollarGroovyInnerClassUsagesInStubs2() throws Exception {
|
||||
myFixture.addClass(""" public class JavaClass { } """)
|
||||
myFixture.addFileToProject("WithInner.groovy", """
|
||||
class WithInner {
|
||||
static class Inner {}
|
||||
}
|
||||
""")
|
||||
|
||||
myFixture.addFileToProject("Usage.groovy", """
|
||||
class Usage {
|
||||
def foo(WithInner.Inner i) {}
|
||||
}
|
||||
""")
|
||||
|
||||
assertEmpty make()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -424,6 +424,23 @@ s.do<caret>Smth()
|
||||
@Typed File foo() { ['super':['a']] }
|
||||
@Typed File goo() { <warning descr="Cannot assign 'Map' to 'File'">[:]</warning> }
|
||||
File bar() { <warning descr="Cannot assign 'Map' to 'File'">[:]</warning> }
|
||||
"""
|
||||
myFixture.enableInspections new GroovyAssignabilityCheckInspection()
|
||||
myFixture.checkHighlighting(true, false, false)
|
||||
}
|
||||
|
||||
public void testNestedLiteralConstructors() throws Exception {
|
||||
configureGppScript """
|
||||
class Foo {
|
||||
def Foo(Bar b) { }
|
||||
}
|
||||
|
||||
class Bar {
|
||||
def Bar(int i) { }
|
||||
}
|
||||
|
||||
Foo x = [[2]]
|
||||
println x
|
||||
"""
|
||||
myFixture.enableInspections new GroovyAssignabilityCheckInspection()
|
||||
myFixture.checkHighlighting(true, false, false)
|
||||
|
||||
@@ -274,13 +274,10 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testCollectionAssignments() throws Exception {
|
||||
doTest(new GroovyAssignabilityCheckInspection());
|
||||
}
|
||||
public void testCollectionAssignments() {doTest(new GroovyAssignabilityCheckInspection()); }
|
||||
public void testReturnAssignability() {doTest(new GroovyAssignabilityCheckInspection()); }
|
||||
|
||||
public void testNumberDuplicatesInMaps() throws Exception {doTest();}
|
||||
|
||||
public void testMapNotAcceptedAsStringParameter() {
|
||||
doTest();
|
||||
}
|
||||
public void testMapNotAcceptedAsStringParameter() {doTest();}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.actions.generate;
|
||||
|
||||
|
||||
import com.intellij.codeInsight.generation.ClassMember
|
||||
import com.intellij.codeInsight.generation.PsiFieldMember
|
||||
import com.intellij.openapi.application.Result
|
||||
import com.intellij.openapi.application.RunResult
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.plugins.groovy.actions.generate.constructors.ConstructorGenerateHandler
|
||||
import org.jetbrains.plugins.groovy.util.TestUtils
|
||||
import com.intellij.codeInsight.generation.PsiMethodMember
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class GroovyGenerateMembersTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testConstructorAtOffset() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testConstructorAtEnd() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testLonelyConstructor() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testExplicitArgumentTypes() throws Exception {
|
||||
myFixture.configureByText("a.groovy", """
|
||||
class Super {
|
||||
def Super(a, int b) {}
|
||||
}
|
||||
|
||||
class Foo extends Super {
|
||||
int c
|
||||
Object d
|
||||
final e
|
||||
<caret>
|
||||
}
|
||||
""")
|
||||
generateConstructor()
|
||||
myFixture.checkResult """
|
||||
class Super {
|
||||
def Super(a, int b) {}
|
||||
}
|
||||
|
||||
class Foo extends Super {
|
||||
int c
|
||||
Object d
|
||||
final e
|
||||
|
||||
def Foo(a, int b, int c, Object d, e) {
|
||||
super(a, b)
|
||||
this.c = c
|
||||
this.d = d
|
||||
this.e = e
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
private void doTest() throws Throwable {
|
||||
myFixture.configureByFile(getTestName(false) + ".groovy");
|
||||
generateConstructor();
|
||||
myFixture.checkResultByFile(getTestName(false) + "_after.groovy");
|
||||
}
|
||||
|
||||
RunResult generateConstructor() {
|
||||
return new WriteCommandAction(getProject(), new PsiFile[0]) {
|
||||
protected void run(Result result) throws Throwable {
|
||||
new ConstructorGenerateHandler() {
|
||||
@Override protected ClassMember[] chooseOriginalMembersImpl(PsiClass aClass, Project project) {
|
||||
List<ClassMember> members = aClass.fields.collect { new PsiFieldMember(it) }
|
||||
members << new PsiMethodMember(aClass.superClass.constructors[0])
|
||||
return members as ClassMember[]
|
||||
}
|
||||
|
||||
}.invoke(getProject(), myFixture.getEditor(), myFixture.getFile());
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return TestUtils.getTestDataPath() + "generate";
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.actions.generate;
|
||||
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.plugins.groovy.actions.generate.constructors.ConstructorGenerateHandler;
|
||||
import org.jetbrains.plugins.groovy.util.TestUtils;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class GroovyGenerateMembersTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testConstructorAtOffset() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testConstructorAtEnd() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testLonelyConstructor() throws Throwable {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Throwable {
|
||||
myFixture.configureByFile(getTestName(false) + ".groovy");
|
||||
new WriteCommandAction(getProject()) {
|
||||
protected void run(Result result) throws Throwable {
|
||||
new ConstructorGenerateHandler().invoke(getProject(), myFixture.getEditor(), myFixture.getFile());
|
||||
}
|
||||
}.execute();
|
||||
myFixture.checkResultByFile(getTestName(false) + "_after.groovy");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return TestUtils.getTestDataPath() + "generate";
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -24,7 +24,7 @@ class Test {<caret>}
|
||||
class Test {
|
||||
@Override
|
||||
boolean equals(Object obj) {
|
||||
return super.equals(obj); //To change body of overridden methods use File | Settings | File Templates.
|
||||
return super.equals(obj) //To change body of overridden methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -38,7 +38,7 @@ class Test {<caret>}
|
||||
myFixture.checkResult """
|
||||
class Test {
|
||||
def Test() {
|
||||
super(); //To change body of overridden methods use File | Settings | File Templates.
|
||||
super() //To change body of overridden methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -59,7 +59,7 @@ class Test {<caret>}
|
||||
myFixture.checkResult """
|
||||
class Test {
|
||||
@Override def foo() {
|
||||
return super.foo(); //To change body of overridden methods use File | Settings | File Templates.
|
||||
return super.foo() //To change body of overridden methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.resolve;
|
||||
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiIntersectionType;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.util.TestUtils;
|
||||
import org.jmock.lib.CamelCaseNamingScheme;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
@@ -122,4 +125,9 @@ public class TypeInferenceTest extends GroovyResolveTestCase {
|
||||
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("arrayLikeAccessWithIntSequence/A.groovy").getElement();
|
||||
assertEquals("java.util.List", ref.getType().getCanonicalText());
|
||||
}
|
||||
|
||||
public void testArrayAccess() {
|
||||
final GrReferenceExpression ref = (GrReferenceExpression)configureByFile("arrayAccess/A.groovy");
|
||||
assertEquals(CommonClassNames.JAVA_LANG_STRING, ref.getType().getCanonicalText());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,4 @@ int numPermutationsPrinted = 1;
|
||||
|
||||
}
|
||||
|
||||
def x = <warning descr="Assignment is not used">2</warning>
|
||||
def <warning descr="Assignment is not used">x</warning> = 2
|
||||
@@ -1,5 +1,5 @@
|
||||
int idx
|
||||
idx = <warning descr="Assignment is not used">2</warning>
|
||||
<warning descr="Assignment is not used">idx</warning> = 2
|
||||
idx = 3
|
||||
if (++idx == 8) { //Assignment is used here
|
||||
idx = 33
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
File foo() {
|
||||
List<Integer> ints = []
|
||||
if (ints.empty) {
|
||||
print {return 42}
|
||||
for (x in ints) {
|
||||
<warning descr="Cannot assign 'Integer' to 'File'">return 43</warning>
|
||||
}
|
||||
}
|
||||
<warning descr="Cannot assign 'Integer' to 'File'">67</warning>
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
Class clazz = <warning descr="Assignment is not used">null</warning>
|
||||
Class <warning descr="Assignment is not used">clazz</warning> = null
|
||||
@@ -0,0 +1,7 @@
|
||||
def foo(String[] a) {
|
||||
def var = a[0]
|
||||
|
||||
print va<ref>r
|
||||
}
|
||||
|
||||
def bar(String a) {}
|
||||
@@ -60,9 +60,9 @@ public class HgExecutableValidator {
|
||||
}
|
||||
});
|
||||
} catch (InvocationTargetException e) {
|
||||
LOG.error(e);
|
||||
LOG.info(e);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error(e);
|
||||
LOG.info(e);
|
||||
}
|
||||
return myValidHgExecutable;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -47,10 +49,16 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
|
||||
|
||||
/**
|
||||
* Adds the information about visited URL.
|
||||
* @param stringUrl String representation of the URL.
|
||||
* @param username Login used to access the URL.
|
||||
* @param stringUrl String representation of the URL. If null or blank String is passed, nothing is saved.
|
||||
* @param username Login used to access the URL. If null is passed, a blank String is used.
|
||||
*/
|
||||
public void addRememberedUrl(@NotNull String stringUrl, @NotNull String username) {
|
||||
public void addRememberedUrl(@Nullable String stringUrl, @Nullable String username) {
|
||||
if (StringUtils.isBlank(stringUrl)) {
|
||||
return;
|
||||
}
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
List<String> list = myRememberedUrls.get(stringUrl);
|
||||
if (list == null) {
|
||||
list = new LinkedList<String>();
|
||||
|
||||
@@ -31,7 +31,11 @@ public class HgProjectSettings implements PersistentStateComponent<HgProjectSett
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return new State(myCheckIncoming, myCheckOutgoing, myGlobalSettings);
|
||||
final State s = new State();
|
||||
s.myCheckIncoming = myCheckIncoming;
|
||||
s.myCheckOutgoing = myCheckOutgoing;
|
||||
s.myGlobalSettings = myGlobalSettings;
|
||||
return s;
|
||||
}
|
||||
|
||||
public void loadState(State state) {
|
||||
@@ -76,10 +80,5 @@ public class HgProjectSettings implements PersistentStateComponent<HgProjectSett
|
||||
private boolean myCheckIncoming;
|
||||
private boolean myCheckOutgoing;
|
||||
private HgGlobalSettings myGlobalSettings;
|
||||
private State(boolean checkIncoming, boolean checkOutgoing, HgGlobalSettings globalSettings) {
|
||||
myCheckIncoming = checkIncoming;
|
||||
myCheckOutgoing = checkOutgoing;
|
||||
myGlobalSettings = globalSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +133,10 @@ abstract class HgAbstractFilesAction extends AnAction {
|
||||
HgUtil.markFileDirty(project, file);
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
LOG.error("Exception while marking files dirty", e);
|
||||
LOG.info("Exception while marking files dirty", e);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Exception while marking files dirty", e);
|
||||
LOG.info("Exception while marking files dirty", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ abstract class HgAbstractGlobalAction extends AnAction {
|
||||
}
|
||||
|
||||
private static void handleException(Project project, Exception e) {
|
||||
LOG.error(e);
|
||||
LOG.info(e);
|
||||
VcsUtil.showErrorMessage(project, e.getMessage(), "Error");
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class HgCommandAuthenticator {
|
||||
HgVcs.getInstance(project).getGlobalSettings().addRememberedUrl(runnable.getURL(), runnable.getUserName());
|
||||
}
|
||||
catch (PasswordSafeException e) {
|
||||
LOG.error("Couldn't store the password for key [" + key + "]", e);
|
||||
LOG.info("Couldn't store the password for key [" + key + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class HgCommandAuthenticator {
|
||||
try {
|
||||
password = PasswordSafe.getInstance().getPassword(project, HgCommandAuthenticator.class, key);
|
||||
} catch (PasswordSafeException e) {
|
||||
LOG.error("Couldn't get password for key [" + key + "]", e);
|
||||
LOG.info("Couldn't get password for key [" + key + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,9 +82,11 @@ public final class HgCommandService {
|
||||
return execute(repo, hgOptions, operation, arguments, charset, false);
|
||||
}
|
||||
|
||||
HgCommandResult execute(VirtualFile repo, List<String> hgOptions,
|
||||
String operation, List<String> arguments, Charset charset, boolean suppressCommandOutput) {
|
||||
|
||||
@Nullable
|
||||
HgCommandResult execute(VirtualFile repo, List<String> hgOptions, String operation, List<String> arguments, Charset charset, boolean suppressCommandOutput) {
|
||||
if (myProject.isDisposed()) {
|
||||
return null;
|
||||
}
|
||||
if (!validator.check(mySettings)) {
|
||||
return null;
|
||||
}
|
||||
@@ -117,7 +119,7 @@ public final class HgCommandService {
|
||||
cmdLine.add("extensions.mq=");
|
||||
} catch (IOException e) {
|
||||
showError(e);
|
||||
LOG.error("IOException during preparing command", e);
|
||||
LOG.info("IOException during preparing command", e);
|
||||
return null;
|
||||
}
|
||||
cmdLine.addAll(hgOptions);
|
||||
@@ -128,12 +130,14 @@ public final class HgCommandService {
|
||||
ShellCommand shellCommand = new ShellCommand();
|
||||
HgCommandResult result;
|
||||
try {
|
||||
LOG.debug(cmdLine.toString());
|
||||
String workingDir = repo != null ? repo.getPath() : null;
|
||||
result = shellCommand.execute(cmdLine, workingDir, charset);
|
||||
} catch (ShellCommandException e) {
|
||||
showError(e);
|
||||
LOG.error(e.getMessage(), e);
|
||||
LOG.info(e.getMessage(), e);
|
||||
return null;
|
||||
} catch (InterruptedException e) { // this may happen during project closing, no need to notify the user.
|
||||
LOG.info(e.getMessage(), e);
|
||||
return null;
|
||||
} finally {
|
||||
promptServer.stop();
|
||||
|
||||
@@ -67,7 +67,7 @@ public class HgCommitCommand {
|
||||
ensureSuccess(HgCommandService.getInstance(project).execute(repo, "commit", parameters));
|
||||
project.getMessageBus().syncPublisher(HgVcs.OUTGOING_TOPIC).update(project);
|
||||
} catch (IOException e) {
|
||||
LOG.error(e);
|
||||
LOG.info(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,14 @@ import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class HgErrorUtil {
|
||||
public final class HgErrorUtil {
|
||||
|
||||
private HgErrorUtil() { }
|
||||
|
||||
static boolean isAbort(HgCommandResult result) {
|
||||
public static boolean isAbort(HgCommandResult result) {
|
||||
if (result == null) {
|
||||
return true;
|
||||
}
|
||||
String line = getLastErrorLine(result);
|
||||
return StringUtils.isNotBlank(line) && StringUtils.contains(line, "abort:");
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public class HgWorkingCopyRevisionsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HgRevisionNumber tip(@NotNull VirtualFile repo) {
|
||||
List<HgRevisionNumber> tips = getRevisions(repo, "tip");
|
||||
if (tips.size() > 1) {
|
||||
@@ -58,13 +59,17 @@ public class HgWorkingCopyRevisionsCommand {
|
||||
else return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HgRevisionNumber identify(@NotNull VirtualFile repo) {
|
||||
HgCommandService commandService = HgCommandService.getInstance(project);
|
||||
HgCommandResult result = commandService.execute(
|
||||
repo, "identify", Arrays.asList("--num", "--id")
|
||||
);
|
||||
List<String> lines = result.getOutputLines();
|
||||
if (!lines.isEmpty()) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
final List<String> lines = result.getOutputLines();
|
||||
if (lines != null && !lines.isEmpty()) {
|
||||
String[] parts = StringUtils.split(lines.get(0), ' ');
|
||||
if (parts.length >= 2) {
|
||||
return HgRevisionNumber.getInstance(parts[1], parts[0]);
|
||||
@@ -73,11 +78,16 @@ public class HgWorkingCopyRevisionsCommand {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<HgRevisionNumber> getRevisions(VirtualFile repo, String command) {
|
||||
HgCommandService commandService = HgCommandService.getInstance(project);
|
||||
HgCommandResult result = commandService.execute(
|
||||
repo, command, Arrays.asList("--template", "{rev}|{node|short}\\n")
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return new ArrayList<HgRevisionNumber>(0);
|
||||
}
|
||||
List<String> lines = result.getOutputLines();
|
||||
List<HgRevisionNumber> revisions = new ArrayList<HgRevisionNumber>(lines.size());
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ final class ShellCommand {
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
public HgCommandResult execute(List<String> commandLine, String dir, Charset charset)
|
||||
throws ShellCommandException {
|
||||
public HgCommandResult execute(List<String> commandLine, String dir, Charset charset) throws ShellCommandException, InterruptedException {
|
||||
if (commandLine == null || commandLine.isEmpty()) {
|
||||
throw new IllegalArgumentException("commandLine is empty");
|
||||
}
|
||||
@@ -51,8 +50,6 @@ final class ShellCommand {
|
||||
return new HgCommandResult(out, err, exitValue );
|
||||
} catch (IOException e) {
|
||||
throw new ShellCommandException(e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new ShellCommandException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public class HgAnnotation implements FileAnnotation {
|
||||
try {
|
||||
return CurrentContentRevision.create(hgFile.toFilePath()).getContent();
|
||||
} catch (VcsException e) {
|
||||
LOG.error(e);
|
||||
LOG.info(e);
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ final class HgHeadMerger {
|
||||
|
||||
private static void throwException(Exception e) throws VcsException {
|
||||
String msg = "Exception during marking directory dirty: " + e;
|
||||
LOG.error(msg, e);
|
||||
LOG.info(msg, e);
|
||||
throw new VcsException(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.testFramework.AbstractVcsTestCase;
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
|
||||
import com.intellij.testFramework.fixtures.TempDirTestFixture;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.zmlx.hg4idea.HgFile;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
@@ -38,10 +39,6 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
|
||||
|
||||
public static final String HG_EXECUTABLE_PATH = "IDEA_TEST_HG_EXECUTABLE_PATH";
|
||||
|
||||
protected File myProjectRepo;
|
||||
protected TempDirTestFixture myTempDirTestFixture;
|
||||
protected TestChangeListManager myChangeListManager;
|
||||
|
||||
// some shortcuts to use in tests
|
||||
protected static final String AFILE = "a.txt";
|
||||
protected static final String BDIR = "b";
|
||||
@@ -50,83 +47,92 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
|
||||
protected static final String FILE_CONTENT = "Sample file content.";
|
||||
protected static final String FILE_CONTENT_2 = "some other file content";
|
||||
|
||||
protected File myProjectDir; // location of the project repository. Initialized differently in each test: by init or by clone.
|
||||
protected HgTestChangeListManager myChangeListManager;
|
||||
|
||||
@BeforeMethod
|
||||
protected void setUp() throws Exception {
|
||||
setHGExecutablePath();
|
||||
|
||||
myTempDirTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture();
|
||||
myTempDirTestFixture.setUp();
|
||||
myProjectRepo = new File(myTempDirTestFixture.getTempDirPath());
|
||||
|
||||
ProcessOutput processOutput = runHg(myProjectRepo, "init");
|
||||
verify(processOutput);
|
||||
initProject(myProjectRepo);
|
||||
activateVCS(HgVcs.VCS_NAME);
|
||||
|
||||
myChangeListManager = new TestChangeListManager(myProject);
|
||||
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
|
||||
}
|
||||
|
||||
protected void setHGExecutablePath() {
|
||||
// setting hg executable
|
||||
String exec = System.getenv(HG_EXECUTABLE_PATH);
|
||||
System.out.println("exec: " + exec);
|
||||
if (exec != null) {
|
||||
System.out.println("Using external");
|
||||
myClientBinaryPath = new File(exec);
|
||||
}
|
||||
if (exec == null || !myClientBinaryPath.exists()) {
|
||||
System.out.println("Using checked in");
|
||||
File pluginRoot = new File(PluginPathManager.getPluginHomePath(HgVcs.VCS_NAME));
|
||||
final File pluginRoot = new File(PluginPathManager.getPluginHomePath(HgVcs.VCS_NAME));
|
||||
myClientBinaryPath = new File(pluginRoot, "testData/bin");
|
||||
}
|
||||
|
||||
HgVcs.setTestHgExecutablePath(myClientBinaryPath.getPath());
|
||||
|
||||
myTraceClient = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Mercurial repository in a temporary test directory.
|
||||
* @return created repository
|
||||
*/
|
||||
protected HgTestRepository createRepository() throws Exception {
|
||||
final TempDirTestFixture dirFixture = createFixtureDir();
|
||||
final File repo = new File(dirFixture.getTempDirPath());
|
||||
ProcessOutput processOutput = runHg(repo, "init");
|
||||
verify(processOutput);
|
||||
return new HgTestRepository(this, dirFixture);
|
||||
}
|
||||
|
||||
protected static TempDirTestFixture createFixtureDir() throws Exception {
|
||||
final TempDirTestFixture fixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture();
|
||||
fixture.setUp();
|
||||
return fixture;
|
||||
}
|
||||
|
||||
protected void enableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
|
||||
setStandardConfirmation(HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY);
|
||||
}
|
||||
|
||||
protected void disableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
|
||||
setStandardConfirmation(HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the hg command.
|
||||
*
|
||||
* @param commandLine the name of the command and its arguments.
|
||||
*/
|
||||
protected ProcessOutput runHgOnProjectRepo(String... commandLine) throws IOException {
|
||||
return runHg(myProjectRepo, commandLine);
|
||||
return runHg(myProjectDir, commandLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the status of the file calling native 'hg status' command.
|
||||
*
|
||||
* @param status status as returned by {@link #added(java.lang.String)} and other methods.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void verifyStatus(String... status) throws IOException {
|
||||
verify(runHg(myProjectDir, "status"), status);
|
||||
}
|
||||
/**
|
||||
* Calls "hg add ." to add everything to the index.
|
||||
*/
|
||||
protected ProcessOutput addAll() throws IOException {
|
||||
return runHgOnProjectRepo("add", ".");
|
||||
return runHg(myProjectDir, "add", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls "hg commit -m <commitMessage>" to commit the index.
|
||||
*/
|
||||
protected ProcessOutput commitAll(String commitMessage) throws IOException {
|
||||
return runHgOnProjectRepo("commit", "-m", commitMessage);
|
||||
return runHg(myProjectDir, "commit", "-m", commitMessage);
|
||||
}
|
||||
|
||||
protected HgFile getHgFile(String... filepath) {
|
||||
File fileToInclude = myProjectRepo;
|
||||
File fileToInclude = myProjectDir;
|
||||
for (String path : filepath) {
|
||||
fileToInclude = new File(fileToInclude, path);
|
||||
}
|
||||
return new HgFile(myWorkingCopyDir, fileToInclude);
|
||||
}
|
||||
|
||||
|
||||
protected void enableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
|
||||
setStandardConfirmation(
|
||||
HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY
|
||||
);
|
||||
}
|
||||
|
||||
protected void disableSilentOperation(final VcsConfiguration.StandardConfirmation op) {
|
||||
setStandardConfirmation(
|
||||
HgVcs.VCS_NAME, op, VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY
|
||||
);
|
||||
}
|
||||
|
||||
protected VirtualFile makeFile(File file) throws IOException {
|
||||
file.createNewFile();
|
||||
@@ -135,8 +141,13 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
|
||||
return VcsUtil.getVirtualFile(file);
|
||||
}
|
||||
|
||||
protected ProcessOutput runHg(File aHgRepository, String... commandLine) throws IOException {
|
||||
return runClient(HgVcs.HG_EXECUTABLE_FILE_NAME, null, aHgRepository, commandLine);
|
||||
/**
|
||||
* Executes the given native Mercurial command with parameters in the given working directory.
|
||||
* @param workingDir working directory where the command will be executed. May be null.
|
||||
* @param commandLine command and parameters (e.g. 'status, -m').
|
||||
*/
|
||||
protected ProcessOutput runHg(@Nullable File workingDir, String... commandLine) throws IOException {
|
||||
return runClient(HgVcs.HG_EXECUTABLE_FILE_NAME, null, workingDir, commandLine);
|
||||
}
|
||||
|
||||
protected File fillFile(File aParentDir, String[] filePath, String fileContents) throws FileNotFoundException {
|
||||
@@ -157,49 +168,4 @@ public abstract class HgAbstractTestCase extends AbstractVcsTestCase {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the status of the file calling native 'hg status' command.
|
||||
* @param status status as returned by {@link #added(java.lang.String)} and other methods.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void verifyStatus(String... status) throws IOException {
|
||||
verify(runHgOnProjectRepo("status"), status);
|
||||
}
|
||||
|
||||
public static String added(String... path) {
|
||||
return "A " + path(path);
|
||||
}
|
||||
|
||||
public static String removed(String... path) {
|
||||
return "R " + path(path);
|
||||
}
|
||||
|
||||
public static String unknown(String... path) {
|
||||
return "? " + path(path);
|
||||
}
|
||||
|
||||
public static String modified(String... path) {
|
||||
return "M " + path(path);
|
||||
}
|
||||
|
||||
public static String missing(String... path) {
|
||||
return "! " + path(path);
|
||||
}
|
||||
|
||||
public static String path(String... line) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
int linePartCount = line.length;
|
||||
|
||||
for (int i = 0; i < linePartCount; i++) {
|
||||
String linePart = line[i];
|
||||
builder.append(linePart);
|
||||
|
||||
if (i < linePartCount - 1) {
|
||||
builder.append(File.separator);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ package org.zmlx.hg4idea.test;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.zmlx.hg4idea.test.HgTestOutputParser.added;
|
||||
|
||||
/**
|
||||
* Tests adding files to the Mercurial repository.
|
||||
*/
|
||||
public class HgAddTestCase extends HgAbstractTestCase {
|
||||
public class HgAddTestCase extends HgSingleUserTestCase {
|
||||
|
||||
/**
|
||||
* 1. Create a file in the file system.
|
||||
@@ -27,7 +29,7 @@ public class HgAddTestCase extends HgAbstractTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void fileAddedViaChangeListShouldBeAddedToHg() throws Exception {
|
||||
final VirtualFile vf = myTempDirTestFixture.createFile(AFILE);
|
||||
final VirtualFile vf = myRepo.getDirFixture().createFile(AFILE);
|
||||
myChangeListManager.addUnversionedFilesToVcs(vf);
|
||||
verifyStatus(added(AFILE));
|
||||
myChangeListManager.checkFilesAreInList(true, vf);
|
||||
@@ -51,9 +53,9 @@ public class HgAddTestCase extends HgAbstractTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void filesInDirsAddedViaChangeListShouldBeAddedToHg() throws Exception {
|
||||
final VirtualFile afile = myTempDirTestFixture.createFile(AFILE);
|
||||
final VirtualFile bdir = myTempDirTestFixture.findOrCreateDir(BDIR);
|
||||
final VirtualFile bfile = myTempDirTestFixture.createFile(BFILE_PATH);
|
||||
final VirtualFile afile = myRepo.getDirFixture().createFile(AFILE);
|
||||
final VirtualFile bdir = myRepo.getDirFixture().findOrCreateDir(BDIR);
|
||||
final VirtualFile bfile = myRepo.getDirFixture().createFile(BFILE_PATH);
|
||||
myChangeListManager.addUnversionedFilesToVcs(afile, bdir, bfile);
|
||||
verifyStatus(added(AFILE), added(BFILE_PATH));
|
||||
myChangeListManager.checkFilesAreInList(true, afile, bfile);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.vcs.VcsConfiguration;
|
||||
import com.intellij.testFramework.fixtures.TempDirTestFixture;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* The parent of all tests, where at least two repositories communicate with each other.
|
||||
* This is used to test collaborative tasks, such as push, pull, merge and others.
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class HgCollaborativeTestCase extends HgAbstractTestCase {
|
||||
|
||||
protected HgTestRepository myParentRepo;
|
||||
protected HgTestRepository myRepo;
|
||||
|
||||
@BeforeMethod
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
myParentRepo = createRepository();
|
||||
myRepo = cloneFrom(myParentRepo);
|
||||
|
||||
myProjectDir = new File(myRepo.getDirFixture().getTempDirPath());
|
||||
|
||||
initProject(myProjectDir);
|
||||
activateVCS(HgVcs.VCS_NAME);
|
||||
myChangeListManager = new HgTestChangeListManager(myProject);
|
||||
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a repository from the given one. New repository is located in a temporary test directory.
|
||||
* @param parent repository to clone from.
|
||||
* @return New repository cloned from the given parent.
|
||||
*/
|
||||
protected HgTestRepository cloneFrom(HgTestRepository parent) throws Exception {
|
||||
final TempDirTestFixture dirFixture = createFixtureDir();
|
||||
final ProcessOutput processOutput = runHg(null, "clone", parent.getDirFixture().getTempDirPath(), dirFixture.getTempDirPath());
|
||||
verify(processOutput);
|
||||
return new HgTestRepository(this, dirFixture);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,14 +17,14 @@ import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HgCopyTestCase extends HgAbstractTestCase {
|
||||
public class HgCopyTestCase extends HgSingleUserTestCase {
|
||||
|
||||
@Test
|
||||
public void testCopyUnmodifiedFile() throws Exception {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
copyFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("b.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -32,16 +32,16 @@ public class HgCopyTestCase extends HgAbstractTestCase {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
editFileInCommand(myProject, file, "newer content");
|
||||
verify(runHgOnProjectRepo("status"), modified("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.modified("a.txt"));
|
||||
copyFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), modified("a.txt"), added("b.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.modified("a.txt"), HgTestOutputParser.added("b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyUnversionedFile() throws Exception {
|
||||
VirtualFile file = makeFile(new File(myWorkingCopyDir.getPath(), "a.txt"));
|
||||
copyFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), unknown("a.txt"), unknown("b.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("a.txt"), HgTestOutputParser.unknown("b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -50,7 +50,7 @@ public class HgCopyTestCase extends HgAbstractTestCase {
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
copyFileInCommand(file, "b.txt");
|
||||
copyFileInCommand(file, "c.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("b.txt"), added("c.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("b.txt"), HgTestOutputParser.added("c.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +59,7 @@ public class HgCopyTestCase extends HgAbstractTestCase {
|
||||
createFileInCommand(parent, "a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
copyFileInCommand(parent, "org");
|
||||
verify(runHgOnProjectRepo("status"), added("org", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("org", "a.txt"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@ import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HgDeleteTestCase extends HgAbstractTestCase {
|
||||
public class HgDeleteTestCase extends HgSingleUserTestCase {
|
||||
|
||||
@Test
|
||||
public void testDeleteUnmodifiedFile() throws Exception {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
deleteFileInCommand(file);
|
||||
verify(runHgOnProjectRepo("status"), removed("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.removed("a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUnversionedFile() throws Exception {
|
||||
VirtualFile file = makeFile(new File(myWorkingCopyDir.getPath(), "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), unknown("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("a.txt"));
|
||||
deleteFileInCommand(file);
|
||||
Assert.assertFalse(file.exists());
|
||||
}
|
||||
@@ -48,9 +48,9 @@ public class HgDeleteTestCase extends HgAbstractTestCase {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
editFileInCommand(myProject, file, "even newer content");
|
||||
verify(runHgOnProjectRepo("status"), modified("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.modified("a.txt"));
|
||||
deleteFileInCommand(file);
|
||||
verify(runHgOnProjectRepo("status"), removed("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.removed("a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +59,7 @@ public class HgDeleteTestCase extends HgAbstractTestCase {
|
||||
createFileInCommand(parent, "a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
deleteFileInCommand(parent);
|
||||
verify(runHgOnProjectRepo("status"), removed("com", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.removed("com", "a.txt"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// limitations under the License.
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import com.intellij.openapi.vcs.VcsConfiguration;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
|
||||
import com.intellij.testFramework.fixtures.TempDirTestFixture;
|
||||
@@ -25,7 +24,10 @@ import java.io.File;
|
||||
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class HgFromClonedTestCase extends HgAbstractTestCase {
|
||||
/**
|
||||
* TODO: substitute by new interface from HgCollaborativeTestCase and HgTestRepository.
|
||||
*/
|
||||
public class HgFromClonedTestCase extends HgCollaborativeTestCase {
|
||||
|
||||
protected File remoteRepo;
|
||||
protected File projectRepo;
|
||||
@@ -34,8 +36,9 @@ public class HgFromClonedTestCase extends HgAbstractTestCase {
|
||||
protected VirtualFile projectRepoVirtualFile;
|
||||
|
||||
@BeforeMethod
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
setHGExecutablePath();
|
||||
super.setUp();
|
||||
|
||||
remoteRepoDir = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture();
|
||||
projectRepoDir = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture();
|
||||
@@ -47,9 +50,6 @@ public class HgFromClonedTestCase extends HgAbstractTestCase {
|
||||
activateVCS(HgVcs.VCS_NAME);
|
||||
|
||||
projectRepoVirtualFile = VcsUtil.getVirtualFile(projectRepo);
|
||||
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
|
||||
}
|
||||
|
||||
private File cloneRemoteRepository(TempDirTestFixture projectRepoDir, File remoteRepo, String destination) throws Exception {
|
||||
@@ -73,7 +73,7 @@ public class HgFromClonedTestCase extends HgAbstractTestCase {
|
||||
File aFile = fillFile(remoteRepo, new String[]{"com", "a.txt"}, "file contents");
|
||||
|
||||
verify(runHg(remoteRepo, "add", aFile.getPath()));
|
||||
verify(runHg(remoteRepo, "status"), added("com", "a.txt"));
|
||||
verify(runHg(remoteRepo, "status"), HgTestOutputParser.added("com", "a.txt"));
|
||||
verify(runHg(remoteRepo, "commit", "-m", "initial contents"));
|
||||
|
||||
return remoteRepo;
|
||||
|
||||
@@ -17,7 +17,7 @@ import static org.testng.Assert.assertTrue;
|
||||
/**
|
||||
* HgHistoryTestCase tests retrieving file history and specific revisions.
|
||||
*/
|
||||
public class HgHistoryTestCase extends HgAbstractTestCase {
|
||||
public class HgHistoryTestCase extends HgSingleUserTestCase {
|
||||
|
||||
/**
|
||||
* 1. Make two versions of a file (create, add, commit, modify, commit).
|
||||
@@ -27,11 +27,11 @@ public class HgHistoryTestCase extends HgAbstractTestCase {
|
||||
@Test
|
||||
public void testCurrentAndPreviousRevisions() throws Exception {
|
||||
int versions = 0;
|
||||
fillFile(myProjectRepo, new String[]{ AFILE }, FILE_CONTENT);
|
||||
fillFile(myProjectDir, new String[]{ AFILE }, FILE_CONTENT);
|
||||
addAll();
|
||||
commitAll("initial content");
|
||||
versions++;
|
||||
fillFile(myProjectRepo, new String[] { AFILE} , FILE_CONTENT_2);
|
||||
fillFile(myProjectDir, new String[] { AFILE} , FILE_CONTENT_2);
|
||||
commitAll("updated content");
|
||||
versions++;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class HgHistoryTestCase extends HgAbstractTestCase {
|
||||
public void renameShouldPreserveFileHistory() throws Exception {
|
||||
int versions = 0;
|
||||
|
||||
fillFile(myProjectRepo, new String[]{ AFILE }, FILE_CONTENT);
|
||||
fillFile(myProjectDir, new String[]{ AFILE }, FILE_CONTENT);
|
||||
addAll();
|
||||
commitAll("initial content");
|
||||
versions++;
|
||||
@@ -67,7 +67,7 @@ public class HgHistoryTestCase extends HgAbstractTestCase {
|
||||
commitAll("file renamed");
|
||||
versions++;
|
||||
|
||||
fillFile(myProjectRepo, new String[]{ BFILE }, FILE_CONTENT_2);
|
||||
fillFile(myProjectDir, new String[]{ BFILE }, FILE_CONTENT_2);
|
||||
commitAll("updated content");
|
||||
versions++;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class HgHistoryTestCase extends HgAbstractTestCase {
|
||||
}
|
||||
|
||||
private VcsHistorySession getHistorySession(String relativePath) throws VcsException {
|
||||
return HgVcs.getInstance(myProject).getVcsHistoryProvider().createSessionFor(VcsUtil.getFilePath(new File(myProjectRepo, relativePath), false));
|
||||
return HgVcs.getInstance(myProject).getVcsHistoryProvider().createSessionFor(VcsUtil.getFilePath(new File(myProjectDir, relativePath), false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ import java.util.List;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public class HgLogTestCase extends HgAbstractTestCase {
|
||||
public class HgLogTestCase extends HgSingleUserTestCase {
|
||||
|
||||
@Test
|
||||
public void testCommitMessagesWithMultipleLines() throws Exception {
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "initial contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "initial contents");
|
||||
runHgOnProjectRepo("add", ".");
|
||||
runHgOnProjectRepo("commit", "-m", "initial\ncontents");
|
||||
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "updated contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "updated contents");
|
||||
runHgOnProjectRepo("commit", "-m", "updated\ncontents");
|
||||
|
||||
List<HgFileRevision> fileLog = new HgLogCommand(myProject).execute(getHgFile("file.txt"), 10, false);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2008-2010 Victor Iacoban
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed under
|
||||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
// either express or implied. See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Tests adding files to the Mercurial repository.
|
||||
*/
|
||||
public class HgMergeTestCase extends HgCollaborativeTestCase {
|
||||
|
||||
@Test
|
||||
public void testRevertUncommittedMerge() throws Exception {
|
||||
HgTestRepository central = createRepository();
|
||||
HgTestRepository worker = cloneFrom(central);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HgMoveTestCase extends HgAbstractTestCase {
|
||||
public class HgMoveTestCase extends HgSingleUserTestCase {
|
||||
|
||||
@Test
|
||||
public void testMoveNewFile() throws Exception {
|
||||
@@ -27,7 +27,7 @@ public class HgMoveTestCase extends HgAbstractTestCase {
|
||||
VirtualFile parent2 = createDirInCommand(myWorkingCopyDir, "org");
|
||||
moveFileInCommand(file, parent2);
|
||||
|
||||
verify(runHgOnProjectRepo("status"), added("org", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("org", "a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -39,7 +39,7 @@ public class HgMoveTestCase extends HgAbstractTestCase {
|
||||
VirtualFile parent2 = createDirInCommand(myWorkingCopyDir, "org");
|
||||
moveFileInCommand(file, parent2);
|
||||
|
||||
verify(runHgOnProjectRepo("status"), added("org", "a.txt"), removed("com", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("org", "a.txt"), HgTestOutputParser.removed("com", "a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -52,7 +52,7 @@ public class HgMoveTestCase extends HgAbstractTestCase {
|
||||
VirtualFile parent2 = createDirInCommand(myWorkingCopyDir, "org");
|
||||
moveFileInCommand(dir, parent2);
|
||||
|
||||
verify(runHgOnProjectRepo("status"), added("org", "zzz", "a.txt"), removed("com", "zzz", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("org", "zzz", "a.txt"), HgTestOutputParser.removed("com", "zzz", "a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -62,12 +62,12 @@ public class HgMoveTestCase extends HgAbstractTestCase {
|
||||
File unversionedFile = new File(parent1.getPath(), "a.txt");
|
||||
VirtualFile file = makeFile(unversionedFile);
|
||||
|
||||
verify(runHgOnProjectRepo("status"), unknown("com", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("com", "a.txt"));
|
||||
|
||||
VirtualFile parent2 = createDirInCommand(myWorkingCopyDir, "org");
|
||||
moveFileInCommand(file, parent2);
|
||||
|
||||
verify(runHgOnProjectRepo("status"), unknown("org", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("org", "a.txt"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.testng.annotations.Test;
|
||||
import org.zmlx.hg4idea.command.HgCommandResult;
|
||||
import org.zmlx.hg4idea.command.HgErrorUtil;
|
||||
import org.zmlx.hg4idea.command.HgPushCommand;
|
||||
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class HgPushTestCase extends HgCollaborativeTestCase {
|
||||
|
||||
/**
|
||||
* Tests 'push' using only native commands.
|
||||
* This is to test the harness.
|
||||
*/
|
||||
@Test
|
||||
public void testNativeCommands() throws Exception {
|
||||
myRepo.getDirFixture().createFile(AFILE);
|
||||
myRepo.add();
|
||||
myRepo.commit();
|
||||
myRepo.push();
|
||||
|
||||
myParentRepo.update();
|
||||
assertNotNull(myParentRepo.getDirFixture().getFile(AFILE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing HgPushCommand:
|
||||
* 1. Create a file, add to the VCS and commit via ChangeListManager.
|
||||
* 2. Push via HgPushCommand.
|
||||
* 3. Natively update parent repository.
|
||||
* 4. Verify that the changes appeared there.
|
||||
*/
|
||||
@Test
|
||||
public void testHgPushCommand() throws Exception {
|
||||
final VirtualFile vf = myRepo.getDirFixture().createFile(AFILE);
|
||||
myChangeListManager.addUnversionedFilesToVcs(vf);
|
||||
myChangeListManager.checkFilesAreInList(true, vf);
|
||||
myChangeListManager.commitFiles(vf);
|
||||
|
||||
final HgPushCommand command = new HgPushCommand(myProject, myRepo.getDir(), myParentRepo.getDir().getUrl());
|
||||
final HgCommandResult result = command.execute();
|
||||
assertNotNull(result);
|
||||
assertFalse(HgErrorUtil.isAbort(result));
|
||||
|
||||
myParentRepo.update();
|
||||
assertNotNull(myParentRepo.getDirFixture().getFile(AFILE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,14 +17,14 @@ import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HgRenameTestCase extends HgAbstractTestCase {
|
||||
public class HgRenameTestCase extends HgSingleUserTestCase {
|
||||
|
||||
@Test
|
||||
public void testRenameUnmodifiedFile() throws Exception {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
renameFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("b.txt"), removed("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("b.txt"), HgTestOutputParser.removed("a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -32,16 +32,16 @@ public class HgRenameTestCase extends HgAbstractTestCase {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
editFileInCommand(myProject, file, "modified new file content");
|
||||
verify(runHgOnProjectRepo("status"), modified("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.modified("a.txt"));
|
||||
renameFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("b.txt"), removed("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("b.txt"), HgTestOutputParser.removed("a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameNewFile() throws Exception {
|
||||
VirtualFile file = createFileInCommand("a.txt", "new file content");
|
||||
renameFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("b.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -50,7 +50,7 @@ public class HgRenameTestCase extends HgAbstractTestCase {
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
renameFileInCommand(file, "b.txt");
|
||||
renameFileInCommand(file, "c.txt");
|
||||
verify(runHgOnProjectRepo("status"), added("c.txt"), removed("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("c.txt"), HgTestOutputParser.removed("a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +59,7 @@ public class HgRenameTestCase extends HgAbstractTestCase {
|
||||
createFileInCommand(parent, "a.txt", "new file content");
|
||||
runHgOnProjectRepo("commit", "-m", "added file");
|
||||
renameFileInCommand(parent, "org");
|
||||
verify(runHgOnProjectRepo("status"), added("org", "a.txt"), removed("com", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.added("org", "a.txt"), HgTestOutputParser.removed("com", "a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -68,20 +68,20 @@ public class HgRenameTestCase extends HgAbstractTestCase {
|
||||
|
||||
File unversionedFile = new File(parent.getPath(), "a.txt");
|
||||
makeFile(unversionedFile);
|
||||
verify(runHgOnProjectRepo("status"), unknown("com", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("com", "a.txt"));
|
||||
|
||||
renameFileInCommand(parent, "org");
|
||||
verify(runHgOnProjectRepo("status"), unknown("org", "a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("org", "a.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameUnversionedFile() throws Exception {
|
||||
File unversionedFile = new File(myWorkingCopyDir.getPath(), "a.txt");
|
||||
VirtualFile file = makeFile(unversionedFile);
|
||||
verify(runHgOnProjectRepo("status"), unknown("a.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("a.txt"));
|
||||
|
||||
renameFileInCommand(file, "b.txt");
|
||||
verify(runHgOnProjectRepo("status"), unknown("b.txt"));
|
||||
verify(runHgOnProjectRepo("status"), HgTestOutputParser.unknown("b.txt"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import org.zmlx.hg4idea.command.HgResolveCommand;
|
||||
|
||||
public class HgResolveConflictTestCase extends HgAbstractTestCase {
|
||||
public class HgResolveConflictTestCase extends HgSingleUserTestCase {
|
||||
|
||||
public static final String BASE = "one\n" +
|
||||
"conflicting\n" +
|
||||
@@ -47,7 +47,7 @@ public class HgResolveConflictTestCase extends HgAbstractTestCase {
|
||||
|
||||
runHgOnProjectRepo("--config", "ui.merge=internal:merge", "merge");
|
||||
|
||||
VirtualFile repoFile = makeFile(myProjectRepo);
|
||||
VirtualFile repoFile = makeFile(myProjectDir);
|
||||
HgResolveCommand.MergeData data = new HgResolveCommand(myProject).getResolveData(repoFile, repoFile.findChild("conflicting"));
|
||||
|
||||
Assert.assertEquals(data.getBase(), BASE.getBytes(),
|
||||
|
||||
@@ -9,14 +9,14 @@ import java.nio.charset.Charset;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public class HgRevertTestCase extends HgAbstractTestCase {
|
||||
public class HgRevertTestCase extends HgSingleUserTestCase {
|
||||
@Test
|
||||
public void testRevertToCurrentRevision() throws Exception {
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "initial contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "initial contents");
|
||||
runHgOnProjectRepo("add", ".");
|
||||
runHgOnProjectRepo("commit", "-m", "initial contents");
|
||||
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "new contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "new contents");
|
||||
|
||||
HgRevertCommand revertCommand = new HgRevertCommand(myProject);
|
||||
revertCommand.execute(getHgFile("file.txt"), null, false);
|
||||
@@ -30,11 +30,11 @@ public class HgRevertTestCase extends HgAbstractTestCase {
|
||||
|
||||
@Test
|
||||
public void testRevertToGivenRevision() throws Exception {
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "initial contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "initial contents");
|
||||
runHgOnProjectRepo("add", ".");
|
||||
runHgOnProjectRepo("commit", "-m", "initial contents");
|
||||
|
||||
fillFile(myProjectRepo, new String[]{"file.txt"}, "new contents");
|
||||
fillFile(myProjectDir, new String[]{"file.txt"}, "new contents");
|
||||
runHgOnProjectRepo("commit", "-m", "new contents");
|
||||
|
||||
HgRevertCommand revertCommand = new HgRevertCommand(myProject);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import com.intellij.openapi.vcs.VcsConfiguration;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.zmlx.hg4idea.HgVcs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* HgSingleUserTestCase is the parent of test cases for single user workflow.
|
||||
* It doesn't include collaborate tasks such as cloning, pushing, etc.
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class HgSingleUserTestCase extends HgAbstractTestCase {
|
||||
|
||||
protected HgTestRepository myRepo;
|
||||
|
||||
@BeforeMethod
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
myRepo = createRepository();
|
||||
myProjectDir = new File(myRepo.getDirFixture().getTempDirPath());
|
||||
|
||||
initProject(myProjectDir);
|
||||
activateVCS(HgVcs.VCS_NAME);
|
||||
myChangeListManager = new HgTestChangeListManager(myProject);
|
||||
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
|
||||
enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
|
||||
}
|
||||
|
||||
}
|
||||
+9
-3
@@ -19,6 +19,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl;
|
||||
import com.intellij.openapi.vcs.changes.LocalChangeList;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.testng.Assert;
|
||||
@@ -29,15 +30,17 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* The ChangeListManagerImpl extension with some useful helper methods for tests.
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class TestChangeListManager {
|
||||
public class HgTestChangeListManager {
|
||||
|
||||
private ChangeListManagerImpl peer;
|
||||
|
||||
public TestChangeListManager(Project project) {
|
||||
public HgTestChangeListManager(Project project) {
|
||||
peer = ChangeListManagerImpl.getInstanceImpl(project);
|
||||
}
|
||||
|
||||
@@ -79,7 +82,10 @@ public class TestChangeListManager {
|
||||
for (VirtualFile f : files) {
|
||||
changes.addAll(peer.getChangesIn(f));
|
||||
}
|
||||
Assert.assertTrue(peer.commitChangesSynchronouslyWithResult(peer.getDefaultChangeList(), changes));
|
||||
final LocalChangeList list = peer.getDefaultChangeList();
|
||||
assertNotNull(list);
|
||||
list.setComment("A comment to a commit");
|
||||
Assert.assertTrue(peer.commitChangesSynchronouslyWithResult(list, changes));
|
||||
}
|
||||
|
||||
public void removeFiles(final VirtualFile file) {
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Helper class for parsing outputs of mercurial commands.
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class HgTestOutputParser {
|
||||
|
||||
public static String added(String... path) {
|
||||
return "A " + path(path);
|
||||
}
|
||||
|
||||
public static String removed(String... path) {
|
||||
return "R " + path(path);
|
||||
}
|
||||
|
||||
public static String unknown(String... path) {
|
||||
return "? " + path(path);
|
||||
}
|
||||
|
||||
public static String modified(String... path) {
|
||||
return "M " + path(path);
|
||||
}
|
||||
|
||||
public static String missing(String... path) {
|
||||
return "! " + path(path);
|
||||
}
|
||||
|
||||
public static String path(String... line) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
int linePartCount = line.length;
|
||||
|
||||
for (int i = 0; i < linePartCount; i++) {
|
||||
String linePart = line[i];
|
||||
builder.append(linePart);
|
||||
|
||||
if (i < linePartCount - 1) {
|
||||
builder.append(File.separator);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.zmlx.hg4idea.test;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.testFramework.fixtures.TempDirTestFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Representation of a Mercurial repository for tests purposes.
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
class HgTestRepository {
|
||||
@NotNull private final HgAbstractTestCase myTest;
|
||||
@NotNull private final TempDirTestFixture myDirFixture;
|
||||
@Nullable private final HgTestRepository myParent; // cloned from
|
||||
|
||||
HgTestRepository(@NotNull HgAbstractTestCase test, @NotNull TempDirTestFixture dir) {
|
||||
this(test, dir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param test test instance
|
||||
* @param dir repository root
|
||||
* @param parent parent repository where this repository is cloned from, if one exists.
|
||||
*/
|
||||
HgTestRepository(@NotNull HgAbstractTestCase test, @NotNull TempDirTestFixture dir, @Nullable HgTestRepository parent) {
|
||||
myTest = test;
|
||||
myDirFixture = dir;
|
||||
myParent = parent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
TempDirTestFixture getDirFixture() {
|
||||
return myDirFixture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Natively executes the given mercurial command.
|
||||
* @param commandWithParameters Mercurial command with parameters. E.g. ["status", "-a"]
|
||||
*/
|
||||
void execute(String... commandWithParameters) throws IOException {
|
||||
myTest.runHg(new File(myDirFixture.getTempDirPath()), commandWithParameters);
|
||||
}
|
||||
|
||||
void add() throws IOException {
|
||||
execute("add");
|
||||
}
|
||||
|
||||
void commit() throws IOException {
|
||||
execute("commit", "-m", "Sample commit message");
|
||||
}
|
||||
|
||||
void update() throws IOException {
|
||||
execute("update");
|
||||
}
|
||||
|
||||
void push() throws IOException {
|
||||
execute("push");
|
||||
}
|
||||
|
||||
public VirtualFile getDir() {
|
||||
return myDirFixture.getFile(".");
|
||||
}
|
||||
}
|
||||
@@ -101,11 +101,11 @@ public class RngCompactLanguage extends Language {
|
||||
|
||||
public static class MyNamesValidator implements NamesValidator {
|
||||
public boolean isKeyword(String name, Project project) {
|
||||
return RenameUtil.isKeyword(PsiManager.getInstance(project), name);
|
||||
return RenameUtil.isKeyword(name);
|
||||
}
|
||||
|
||||
public boolean isIdentifier(String name, Project project) {
|
||||
return RenameUtil.isIdentifier(PsiManager.getInstance(project), name);
|
||||
return RenameUtil.isIdentifier(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,33 +17,44 @@
|
||||
package org.intellij.plugins.relaxNG.compact.psi.util;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LanguageParserDefinitions;
|
||||
import com.intellij.lang.ParserDefinition;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.sun.org.apache.xerces.internal.util.XML11Char;
|
||||
import org.intellij.plugins.relaxNG.compact.RncElementTypes;
|
||||
import org.intellij.plugins.relaxNG.compact.RncFileType;
|
||||
import org.intellij.plugins.relaxNG.compact.RncTokenTypes;
|
||||
import org.intellij.plugins.relaxNG.compact.RngCompactLanguage;
|
||||
import org.intellij.plugins.relaxNG.compact.psi.RncFile;
|
||||
import org.intellij.plugins.relaxNG.compact.psi.RncGrammar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: sweinreuter
|
||||
* Date: 14.08.2007
|
||||
*/
|
||||
public class RenameUtil {
|
||||
|
||||
private static Set<String> ourRncKeywords = new HashSet<String>();
|
||||
|
||||
static {
|
||||
Collections.addAll(ourRncKeywords, "attribute", "default", "datatypes", "div", "element", "empty", "external",
|
||||
"grammar", "include", "inherit", "list", "mixed", "namespace", "notAllowed", "parent", "start",
|
||||
"string", "text", "token");
|
||||
}
|
||||
|
||||
private RenameUtil() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ASTNode createIdentifierNode(PsiManager manager, String name) throws IncorrectOperationException {
|
||||
if (isKeyword(manager, name)) {
|
||||
if (isKeyword(name)) {
|
||||
name = "\\" + name;
|
||||
} else if (!isIdentifier(manager, name)) {
|
||||
} else if (!isIdentifier(name)) {
|
||||
throw new IncorrectOperationException("Illegal identifier: " + name);
|
||||
}
|
||||
|
||||
@@ -55,15 +66,23 @@ public class RenameUtil {
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public static boolean isIdentifier(PsiManager manager, String name) {
|
||||
return isTokenOfType(manager, name, RncTokenTypes.IDENTIFIER_OR_KEYWORD);
|
||||
public static boolean isIdentifier(String name) {
|
||||
//return isTokenOfType(manager, name, RncTokenTypes.IDENTIFIER_OR_KEYWORD);
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
if (XML11Char.isXML11ValidNCName(name)) {
|
||||
return true;
|
||||
}
|
||||
return name.length() >= 2 && name.startsWith("\\") && XML11Char.isXML11ValidNCName(name.substring(1));
|
||||
}
|
||||
|
||||
public static boolean isKeyword(PsiManager manager, String name) {
|
||||
return isTokenOfType(manager, name, RncTokenTypes.KEYWORDS);
|
||||
public static boolean isKeyword(String name) {
|
||||
//return isTokenOfType(manager, name, RncTokenTypes.KEYWORDS);
|
||||
return ourRncKeywords.contains(name);
|
||||
}
|
||||
|
||||
private static boolean isTokenOfType(PsiManager manager, String name, TokenSet set) {
|
||||
/*private static boolean isTokenOfType(PsiManager manager, String name, TokenSet set) {
|
||||
final ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(RngCompactLanguage.INSTANCE);
|
||||
assert definition != null;
|
||||
final Lexer lexer = definition.createLexer(manager.getProject());
|
||||
@@ -72,7 +91,7 @@ public class RenameUtil {
|
||||
final IElementType t = lexer.getTokenType();
|
||||
lexer.advance();
|
||||
return lexer.getTokenType() == null && set.contains(t);
|
||||
}
|
||||
}*/
|
||||
|
||||
public static ASTNode createPrefixedNode(PsiManager manager, String prefix, String localPart) {
|
||||
final PsiFileFactory f = PsiFileFactory.getInstance(manager.getProject());
|
||||
|
||||
@@ -16,22 +16,20 @@
|
||||
|
||||
package org.intellij.plugins.relaxNG;
|
||||
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
|
||||
import com.intellij.codeInsight.daemon.QuickFixProvider;
|
||||
import com.intellij.codeInsight.daemon.impl.*;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.InspectionToolProvider;
|
||||
import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspection;
|
||||
import com.intellij.javaee.ExternalResourceManagerEx;
|
||||
import com.intellij.mock.MockProgressIndicator;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PluginPathManager;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -44,7 +42,7 @@ import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
|
||||
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
|
||||
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
|
||||
import com.intellij.testFramework.fixtures.TestFixtureBuilder;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
|
||||
import junit.framework.TestCase;
|
||||
import org.intellij.plugins.relaxNG.inspections.RngDomInspection;
|
||||
import org.intellij.plugins.testUtil.IdeaCodeInsightTestCase;
|
||||
@@ -54,10 +52,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.io.FileUtil.delete;
|
||||
|
||||
@@ -196,44 +191,23 @@ public abstract class HighlightingTestBase extends TestCase implements IdeaCodeI
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Collection<HighlightInfo> doHighlighting(final Boolean includeExternalToolPass) {
|
||||
private Collection<HighlightInfo> doHighlighting(final Boolean externalToolPass) {
|
||||
final Project project = myTestFixture.getProject();
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
final Editor editor = myTestFixture.getEditor();
|
||||
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<Collection<HighlightInfo>>() {
|
||||
public Collection<HighlightInfo> compute() {
|
||||
final Editor editor = myTestFixture.getEditor();
|
||||
final List<TextEditorHighlightingPass> passes =
|
||||
TextEditorHighlightingPassRegistrarEx.getInstanceEx(project).instantiatePasses(myTestFixture.getFile(), editor, ArrayUtil.EMPTY_INT_ARRAY);
|
||||
final MockProgressIndicator progress = new MyMockProgressIndicator();
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
for (TextEditorHighlightingPass pass : passes) {
|
||||
if (includeExternalToolPass == null) {
|
||||
pass.collectInformation(progress);
|
||||
} else {
|
||||
if (includeExternalToolPass == pass instanceof ExternalToolPass) {
|
||||
pass.collectInformation(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (TextEditorHighlightingPass pass : passes) {
|
||||
if (includeExternalToolPass == null) {
|
||||
pass.applyInformationToEditor();
|
||||
} else {
|
||||
if (includeExternalToolPass == pass instanceof ExternalToolPass) {
|
||||
pass.applyInformationToEditor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ProgressManager.getInstance().runProcess(runnable, progress);
|
||||
List<HighlightInfo> infos = DaemonCodeAnalyzerImpl.getHighlights(editor.getDocument(), project);
|
||||
return infos == null ? Collections.<HighlightInfo>emptyList() : new ArrayList<HighlightInfo>(infos);
|
||||
}
|
||||
});
|
||||
int[] ignore = externalToolPass == null || externalToolPass ? new int[]{
|
||||
com.intellij.codeHighlighting.Pass.LINE_MARKERS,
|
||||
com.intellij.codeHighlighting.Pass.LOCAL_INSPECTIONS,
|
||||
com.intellij.codeHighlighting.Pass.POPUP_HINTS,
|
||||
com.intellij.codeHighlighting.Pass.POST_UPDATE_ALL,
|
||||
com.intellij.codeHighlighting.Pass.UPDATE_ALL,
|
||||
com.intellij.codeHighlighting.Pass.UPDATE_FOLDING,
|
||||
com.intellij.codeHighlighting.Pass.UPDATE_OVERRIDEN_MARKERS,
|
||||
com.intellij.codeHighlighting.Pass.UPDATE_VISIBLE,
|
||||
com.intellij.codeHighlighting.Pass.VISIBLE_LINE_MARKERS,
|
||||
} : new int[]{com.intellij.codeHighlighting.Pass.EXTERNAL_TOOLS};
|
||||
return CodeInsightTestFixtureImpl.instantiateAndRun(myTestFixture.getFile(), editor, ignore, false);
|
||||
}
|
||||
|
||||
protected void doTestCompletion(String name, String ext) throws Throwable {
|
||||
|
||||
@@ -16,17 +16,25 @@
|
||||
package org.jetbrains.idea.svn;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Getter;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.CalledInAwt;
|
||||
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
|
||||
import com.intellij.ui.GuiUtils;
|
||||
import com.intellij.util.containers.SoftHashMap;
|
||||
import com.intellij.util.EventDispatcher;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.svn.auth.ProviderType;
|
||||
import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction;
|
||||
import org.jetbrains.idea.svn.auth.SvnAuthenticationListener;
|
||||
import org.tmatesoft.svn.core.SVNErrorMessage;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNURL;
|
||||
@@ -37,43 +45,74 @@ import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author alex
|
||||
*/
|
||||
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager implements SvnAuthenticationListener {
|
||||
private static final Logger LOG = Logger.getInstance(SvnAuthenticationManager.class.getName());
|
||||
private final Project myProject;
|
||||
private final File myConfigDirectory;
|
||||
private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy;
|
||||
private SvnConfiguration myConfig;
|
||||
private AtomicBoolean myStoreInPlainTextAnyway = new AtomicBoolean(false);
|
||||
// instead of ThreadLocal
|
||||
private final Set<Thread> myPlainTextAllowed;
|
||||
private static final ThreadLocal<Boolean> ourJustEntered = new ThreadLocal<Boolean>();
|
||||
private SvnAuthenticationInteraction myInteraction;
|
||||
private final EventDispatcher<SvnAuthenticationListener> myListener;
|
||||
|
||||
public SvnAuthenticationManager(final Project project, final File configDirectory) {
|
||||
super(configDirectory, true, null, null);
|
||||
myProject = project;
|
||||
myConfigDirectory = configDirectory;
|
||||
myListener = EventDispatcher.create(SvnAuthenticationListener.class);
|
||||
myConfig = SvnConfiguration.getInstance(myProject);
|
||||
if (myPersistentAuthenticationProviderProxy != null) {
|
||||
myPersistentAuthenticationProviderProxy.setProject(myProject);
|
||||
}
|
||||
myPlainTextAllowed = Collections.synchronizedSet(new HashSet<Thread>());
|
||||
myInteraction = new MySvnAuthenticationInteraction(myProject);
|
||||
}
|
||||
|
||||
public void addListener(final SvnAuthenticationListener listener) {
|
||||
myListener.addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) {
|
||||
myListener.getMulticaster().actualSaveWillBeTried(type, url, realm, kind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) {
|
||||
if (ProviderType.interactive.equals(type) && (! canceled)) {
|
||||
ourJustEntered.set(true);
|
||||
}
|
||||
myListener.getMulticaster().requested(type, url, realm, kind, canceled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ISVNAuthenticationProvider createCacheAuthenticationProvider(File authDir, String userName) {
|
||||
myStoreInPlainTextAnyway = new AtomicBoolean(false);
|
||||
myPersistentAuthenticationProviderProxy = new PersistentAuthenticationProviderProxy(super.createCacheAuthenticationProvider(authDir, userName), authDir);
|
||||
return myPersistentAuthenticationProviderProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acknowledgeAuthentication(boolean accepted,
|
||||
String kind,
|
||||
String realm,
|
||||
SVNErrorMessage errorMessage,
|
||||
SVNAuthentication authentication) throws SVNException {
|
||||
try {
|
||||
super.acknowledgeAuthentication(accepted, kind, realm, errorMessage, authentication);
|
||||
} finally {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
myPlainTextAllowed.remove(currentThread);
|
||||
}
|
||||
}
|
||||
|
||||
private class PersistentAuthenticationProviderProxy implements ISVNAuthenticationProvider, IPersistentAuthenticationProvider {
|
||||
private final Map<SvnAuthWrapperEqualable, Long> myRewritePreventer;
|
||||
private static final long ourRefreshInterval = 6000 * 1000;
|
||||
private final ISVNAuthenticationProvider myDelegate;
|
||||
private final File myAuthDir;
|
||||
private Project myProject;
|
||||
@@ -81,7 +120,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
private PersistentAuthenticationProviderProxy(final ISVNAuthenticationProvider delegate, final File authDir) {
|
||||
myDelegate = delegate;
|
||||
myAuthDir = authDir;
|
||||
myRewritePreventer = new SoftHashMap<SvnAuthWrapperEqualable, Long>();
|
||||
}
|
||||
|
||||
public void setProject(Project project) {
|
||||
@@ -89,9 +127,11 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
}
|
||||
|
||||
public SVNAuthentication requestClientAuthentication(final String kind, final SVNURL url, final String realm, final SVNErrorMessage errorMessage,
|
||||
final SVNAuthentication previousAuth,
|
||||
final boolean authMayBeStored) {
|
||||
return myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, false);
|
||||
final SVNAuthentication previousAuth, final boolean authMayBeStored) {
|
||||
final SVNAuthentication svnAuthentication =
|
||||
myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
|
||||
myListener.getMulticaster().requested(ProviderType.persistent, url, realm, kind, svnAuthentication == null);
|
||||
return svnAuthentication;
|
||||
}
|
||||
|
||||
public int acceptServerAuthentication(final SVNURL url, final String realm, final Object certificate, final boolean resultMayBeStored) {
|
||||
@@ -99,44 +139,37 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
}
|
||||
|
||||
public void saveAuthentication(final SVNAuthentication auth, final String kind, final String realm) throws SVNException {
|
||||
try {
|
||||
final SvnAuthWrapperEqualable newKey = new SvnAuthWrapperEqualable(auth);
|
||||
final Long recent = myRewritePreventer.get(newKey);
|
||||
final long currTime = System.currentTimeMillis();
|
||||
File dir = new File(myAuthDir, kind);
|
||||
String fileName = SVNFileUtil.computeChecksum(realm);
|
||||
File authFile = new File(dir, fileName);
|
||||
final Boolean fromInteractive = ourJustEntered.get();
|
||||
ourJustEntered.set(null);
|
||||
if (! Boolean.TRUE.equals(fromInteractive)) {
|
||||
// not what user entered
|
||||
return;
|
||||
}
|
||||
|
||||
if ((! authFile.exists()) || recent == null || ((recent != null) && ((currTime - recent.longValue()) > ourRefreshInterval))) {
|
||||
if ((!auth.isStorageAllowed()) || (USERNAME.equals(kind))) return;
|
||||
|
||||
if (auth.isStorageAllowed() && ISVNAuthenticationManager.USERNAME != kind) {
|
||||
try {
|
||||
GuiUtils.runOrInvokeAndWait(new Runnable() {
|
||||
public void run() {
|
||||
checkContinueSaveCredentials(auth, kind, realm);
|
||||
saveCredentialsIfAllowed(auth, kind, realm, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
File dir = new File(myAuthDir, kind);
|
||||
String fileName = SVNFileUtil.computeChecksum(realm);
|
||||
File authFile = new File(dir, fileName);
|
||||
|
||||
myListener.getMulticaster().actualSaveWillBeTried(ProviderType.persistent, auth.getURL(), realm, kind);
|
||||
try {
|
||||
((IPersistentAuthenticationProvider) myDelegate).saveAuthentication(auth, kind, realm);
|
||||
}
|
||||
catch (SVNException e) {
|
||||
if (myProject != null) {
|
||||
ApplicationManager.getApplication().invokeLater(new ChangesViewBalloonProblemNotifier(myProject,
|
||||
"<b>Problem when storing Subversion credentials:</b> " + e.getMessage(), MessageType.ERROR));
|
||||
}
|
||||
});
|
||||
} catch (InvocationTargetException e) {
|
||||
LOG.error(e);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error(e);
|
||||
} finally {
|
||||
// do not make password file readonly
|
||||
setWriteable(authFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((DefaultSVNAuthenticationManager.IPersistentAuthenticationProvider)myDelegate).saveAuthentication(auth, kind, realm);
|
||||
|
||||
// do not make password file readonly
|
||||
setWriteable(authFile);
|
||||
|
||||
myRewritePreventer.put(newKey, currTime);
|
||||
}
|
||||
}
|
||||
catch (final SVNException e) {
|
||||
// show notification so that user was aware his credentials were not saved
|
||||
if (myProject == null) return;
|
||||
ApplicationManager.getApplication().invokeLater(new ChangesViewBalloonProblemNotifier(myProject,
|
||||
"<b>Problem when storing Subversion credentials:</b> " + e.getMessage(), MessageType.ERROR));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final static int maxAttempts = 10;
|
||||
@@ -163,13 +196,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean haveStoredCredentials(final String kind, final SVNURL url, final String realm, final SVNErrorMessage errorMessage,
|
||||
final SVNAuthentication previousAuth) {
|
||||
final SVNAuthentication result =
|
||||
myPersistentAuthenticationProviderProxy.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, false);
|
||||
return result != null;
|
||||
}
|
||||
|
||||
public ISVNProxyManager getProxyManager(SVNURL url) throws SVNException {
|
||||
// this code taken from default manager (changed for system properties reading)
|
||||
String host = url.getHost();
|
||||
@@ -358,27 +384,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class SvnAuthWrapperEqualable extends Wrapper<SVNAuthentication> {
|
||||
private SvnAuthWrapperEqualable(SVNAuthentication svnAuthentication) {
|
||||
super(svnAuthentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof SvnAuthWrapperEqualable) {
|
||||
return SvnAuthEquals.equals(this.getT(), ((SvnAuthWrapperEqualable) obj).getT());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return SvnAuthEquals.hashCode(getT());
|
||||
}
|
||||
}
|
||||
|
||||
private void setPropertyForHost(final String host, final String property, final String value) {
|
||||
final SVNConfigFile userConfig = new SVNConfigFile(new File(myConfigDirectory, "servers"));
|
||||
|
||||
@@ -418,84 +423,176 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager {
|
||||
return "yes".equalsIgnoreCase(storeAuthCreds) || "on".equalsIgnoreCase(storeAuthCreds) || "true".equalsIgnoreCase(storeAuthCreds);
|
||||
}
|
||||
|
||||
public boolean checkContinueSaveCredentials(final SVNAuthentication auth, final String kind, final String realm) {
|
||||
public void saveCredentialsIfAllowed(final SVNAuthentication auth, final String kind, final String realm,
|
||||
final Runnable saveRunnable) {
|
||||
final SVNURL url = auth.getURL();
|
||||
|
||||
final String storeCredentials = getConfigFile().getPropertyValue("auth", "store-auth-creds");
|
||||
if ((Boolean.FALSE.equals(isAuthStorageEnabledMy(url))) || (! isTurned(storeCredentials))) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store credentials: forbidden by \"store-auth-creds=no\"", MessageType.ERROR);
|
||||
return false;
|
||||
myInteraction.warnOnAuthStorageDisabled(url);
|
||||
return;
|
||||
}
|
||||
final boolean passwordStorageEnabled = isStorePasswords(url);
|
||||
// check can store
|
||||
if ((! ISVNAuthenticationManager.SSL.equals(kind)) && (! passwordStorageEnabled)) {
|
||||
// but it should be
|
||||
//userConfig.setPropertyValue("auth", "store-passwords", "yes", true);
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password: forbidden by \"store-passwords=no\"", MessageType.ERROR);
|
||||
return false;
|
||||
myInteraction.warnOnPasswordStorageDisabled(url);
|
||||
return;
|
||||
}
|
||||
if (ISVNAuthenticationManager.SSL.equals(kind) && (! isStoreSSLClientCertificatePassphrases(url))) {
|
||||
//setPropertyForHost(url.getHost(), "store-ssl-client-cert-pp", "yes");
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store passphrase: forbidden by \"store-ssl-client-cert-pp=no\"", MessageType.ERROR);
|
||||
return false;
|
||||
myInteraction.warnOnSSLPassphraseStorageDisabled(url);
|
||||
return;
|
||||
}
|
||||
|
||||
// check can encrypt
|
||||
if (!(SystemInfo.isWindows && SVNJNAUtil.isWinCryptEnabled())) {
|
||||
if (ISVNAuthenticationManager.SSL.equals(kind)) {
|
||||
try {
|
||||
try {
|
||||
if (ISVNAuthenticationManager.SSL.equals(kind)) {
|
||||
if (!isStorePlainTextPassphrases(realm, auth)) {
|
||||
return askToStoreUnencrypted("Store the passphrase in plaintext?",
|
||||
String.format("Your passphrase for client certificate:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
|
||||
((SVNSSLAuthentication)auth).getCertificateFile().getPath()));
|
||||
promptAndSaveWhenWeLackEncryption(saveRunnable,
|
||||
new Getter<Boolean>() {
|
||||
@Override
|
||||
public Boolean get() {
|
||||
return myInteraction.promptForSSLPlaintextPassphraseSaving(url, realm, ((SVNSSLAuthentication)auth).getCertificateFile());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (SVNException e) {
|
||||
LOG.error(e); // should not occur, anyway means not allowed
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
} else {
|
||||
if (!isStorePlainTextPasswords(realm, auth)) {
|
||||
return askToStoreUnencrypted("Store the password in plaintext?",
|
||||
String.format("Your password for authentication realm:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
|
||||
realm));
|
||||
promptAndSaveWhenWeLackEncryption(saveRunnable,
|
||||
new Getter<Boolean>() {
|
||||
@Override
|
||||
public Boolean get() {
|
||||
return myInteraction.promptForPlaintextPasswordSaving(url, realm);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (SVNException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
} catch (SVNException e) {
|
||||
LOG.info(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
saveRunnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isStorePlainTextPasswords(String realm, SVNAuthentication auth) throws SVNException {
|
||||
// normally check user preferences, but the user may override them via the dialog from checkContinueSaveCredentials()
|
||||
return myStoreInPlainTextAnyway.get() || super.isStorePlainTextPasswords(realm, auth);
|
||||
return myPlainTextAllowed.contains(Thread.currentThread()) || super.isStorePlainTextPasswords(realm, auth);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isStorePlainTextPassphrases(String realm, SVNAuthentication auth) throws SVNException {
|
||||
return myStoreInPlainTextAnyway.get() || super.isStorePlainTextPassphrases(realm, auth);
|
||||
return myPlainTextAllowed.contains(Thread.currentThread()) || super.isStorePlainTextPassphrases(realm, auth);
|
||||
}
|
||||
|
||||
private ModalityState getCurrent() {
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
return ModalityState.current();
|
||||
}
|
||||
final ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (pi == null) {
|
||||
return ModalityState.defaultModalityState();
|
||||
}
|
||||
return pi.getModalityState();
|
||||
}
|
||||
|
||||
private void promptAndSaveWhenWeLackEncryption(
|
||||
final Runnable saveRunnable,
|
||||
final Getter<Boolean> prompt) {
|
||||
|
||||
final Runnable actualSave = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
myPlainTextAllowed.add(currentThread);
|
||||
try {
|
||||
saveRunnable.run();
|
||||
}
|
||||
finally {
|
||||
myPlainTextAllowed.remove(currentThread);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (myInteraction.promptInAwt()) {
|
||||
new AbstractCalledLater(myProject, getCurrent()) {
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean saveOnce = Boolean.TRUE.equals(prompt.get());
|
||||
if (saveOnce) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(actualSave);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}.callMe();
|
||||
} else {
|
||||
final boolean saveOnce = Boolean.TRUE.equals(prompt.get());
|
||||
if (saveOnce) {
|
||||
actualSave.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a yes/no question whether user wants to store his password in plain text and returns his answer.
|
||||
* Also updates the 'myStoreInPlainTextAnyway' variable correspondingly.
|
||||
* This method shuld be called from the event dispatching thread.
|
||||
* @param title title of the questioning dialog.
|
||||
* @param message questioning message to be displayed.
|
||||
* @return true if user agrees to store his password in plaintext, false if he doesn't.
|
||||
*/
|
||||
@CalledInAwt
|
||||
private boolean askToStoreUnencrypted(String title, String message) {
|
||||
final int answer = Messages.showYesNoDialog(myProject, message, title, Messages.getQuestionIcon());
|
||||
if (answer == 0) {
|
||||
myStoreInPlainTextAnyway.set(true);
|
||||
} else {
|
||||
myStoreInPlainTextAnyway.set(false);
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password", MessageType.ERROR);
|
||||
}
|
||||
return myStoreInPlainTextAnyway.get();
|
||||
|
||||
return answer == 0;
|
||||
}
|
||||
|
||||
}
|
||||
public void setInteraction(SvnAuthenticationInteraction interaction) {
|
||||
myInteraction = interaction;
|
||||
}
|
||||
|
||||
private static class MySvnAuthenticationInteraction implements SvnAuthenticationInteraction {
|
||||
private final Project myProject;
|
||||
|
||||
private MySvnAuthenticationInteraction(Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warnOnAuthStorageDisabled(SVNURL url) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store credentials: forbidden by \"store-auth-creds=no\"", MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warnOnPasswordStorageDisabled(SVNURL url) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store password: forbidden by \"store-passwords=no\"", MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warnOnSSLPassphraseStorageDisabled(SVNURL url) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Cannot store passphrase: forbidden by \"store-ssl-client-cert-pp=no\"", MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptForPlaintextPasswordSaving(SVNURL url, String realm) {
|
||||
final int answer = Messages.showYesNoDialog(myProject, String.format("Your password for authentication realm:\n" +
|
||||
"%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?", realm),
|
||||
"Store the password in plaintext?", Messages.getQuestionIcon());
|
||||
return answer == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptInAwt() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptForSSLPlaintextPassphraseSaving(SVNURL url, String realm, File certificateFile) {
|
||||
final int answer = Messages.showYesNoDialog(myProject, String.format("Your passphrase for client certificate:\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
|
||||
certificateFile.getPath()),
|
||||
"Store the passphrase in plaintext?", Messages.getQuestionIcon());
|
||||
return answer == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
|
||||
private final Map<File, MergeRootInfo> myMergeRootInfos = new HashMap<File, MergeRootInfo>();
|
||||
private final Map<File, UpdateRootInfo> myUpdateRootInfos = new HashMap<File, UpdateRootInfo>();
|
||||
private final List<AnnotationListener> myAnnotationListeners;
|
||||
private SvnInteractiveAuthenticationProvider myInteractiveProvider;
|
||||
|
||||
public static SvnConfiguration getInstance(Project project) {
|
||||
return project.getComponent(SvnConfiguration.class);
|
||||
@@ -200,6 +201,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
|
||||
myAuthManager = null;
|
||||
myPassiveAuthManager = null;
|
||||
myInteractiveManager = null;
|
||||
myInteractiveProvider = null;
|
||||
RUNTIME_AUTH_CACHE.clear();
|
||||
}
|
||||
|
||||
@@ -209,6 +211,7 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
|
||||
myAuthManager = null;
|
||||
myPassiveAuthManager = null;
|
||||
myInteractiveManager = null;
|
||||
myInteractiveProvider = null;
|
||||
RUNTIME_AUTH_CACHE.clear();
|
||||
}
|
||||
|
||||
@@ -224,8 +227,10 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
|
||||
if (myAuthManager == null) {
|
||||
// reloaded when configuration directory changes
|
||||
myAuthManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
|
||||
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, getInteractiveManager(svnVcs)));
|
||||
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
|
||||
getInteractiveManager(svnVcs);
|
||||
// to init
|
||||
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider));
|
||||
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
|
||||
}
|
||||
return myAuthManager;
|
||||
}
|
||||
@@ -242,7 +247,8 @@ public class SvnConfiguration implements ProjectComponent, JDOMExternalizable {
|
||||
if (myInteractiveManager == null) {
|
||||
myInteractiveManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
|
||||
myInteractiveManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
|
||||
myInteractiveManager.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager));
|
||||
myInteractiveProvider = new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager);
|
||||
myInteractiveManager.setAuthenticationProvider(myInteractiveProvider);
|
||||
}
|
||||
return myInteractiveManager;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.idea.svn.auth;
|
||||
|
||||
public enum ProviderType {
|
||||
dumb,
|
||||
interactive,
|
||||
persistent,
|
||||
memory_cache
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.idea.svn.auth;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNURL;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface SvnAuthenticationInteraction {
|
||||
void warnOnAuthStorageDisabled(final SVNURL url);
|
||||
void warnOnPasswordStorageDisabled(final SVNURL url);
|
||||
void warnOnSSLPassphraseStorageDisabled(final SVNURL url);
|
||||
boolean promptForSSLPlaintextPassphraseSaving(final SVNURL url, String realm, File certificateFile);
|
||||
boolean promptForPlaintextPasswordSaving(final SVNURL url, String realm);
|
||||
boolean promptInAwt();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user