Merge branch 'master' into dsl

This commit is contained in:
Maxim Medvedev
2010-08-04 12:06:50 +04:00
107 changed files with 2957 additions and 768 deletions
@@ -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);
@@ -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>
@@ -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;
}
@@ -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;
}
}
@@ -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;
@@ -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);
@@ -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) {
@@ -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;
@@ -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);
}
@@ -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;
@@ -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();
@@ -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"),
@@ -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();
@@ -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);
@@ -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);
@@ -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();
@@ -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();
}
@@ -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]);
}
@@ -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());
}
@@ -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
@@ -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();}
}
@@ -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";
}
}
@@ -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";
}
}
@@ -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
View File
@@ -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 &lt;commitMessage&gt;" 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);
}
}
@@ -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>&nbsp;" + 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>&nbsp;" + 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