Merge remote-tracking branch 'origin/master'

Conflicts:
	platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java
This commit is contained in:
Konstantin Bulenkov
2015-08-17 14:43:41 +02:00
210 changed files with 3926 additions and 2319 deletions
+1 -1
View File
@@ -727,7 +727,7 @@ binding.setVariable("zipSources", { String home, String targetDir ->
ant.delete(file: sources)
ant.zip(destfile: sources) {
fileset(dir: home) {
["java", "groovy", "ipr", "iml", "form", "xml", "properties"].each {
["java", "groovy", "ipr", "iml", "form", "xml", "properties", "kt"].each {
include(name: "**/*.$it")
}
exclude(name: "**/testData/**")
@@ -26,10 +26,7 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.PrioritizedTask;
import com.intellij.debugger.impl.*;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
@@ -1601,23 +1598,28 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
applyThreadFilter(getContextThread());
final SuspendContextImpl context = getSuspendContext();
myRunToCursorBreakpoint.setSuspendPolicy(context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
DebugProcessImpl debugProcess = context.getDebugProcess();
final DebugProcessImpl debugProcess = context.getDebugProcess();
myRunToCursorBreakpoint.createRequest(debugProcess);
DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
if (debugProcess.getRequestsManager().getWarning(myRunToCursorBreakpoint) != null) {
if (debugProcess.getRequestsManager().getWarning(myRunToCursorBreakpoint) == null) {
super.contextAction();
}
else {
DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
@Override
public void run() {
SourcePosition position = myRunToCursorBreakpoint.getSourcePosition();
String name = position != null ? position.getFile().getName() : "<No File>";
Messages.showErrorDialog(
DebuggerBundle.message("error.running.to.cursor.no.executable.code", name, myRunToCursorBreakpoint.getLineIndex() + 1),
DebuggerBundle.message("error.running.to.cursor.no.executable.code",
myRunToCursorBreakpoint.getSourcePosition().getFile().getName(),
myRunToCursorBreakpoint.getLineIndex() + 1),
UIUtil.removeMnemonic(ActionsBundle.actionText(XDebuggerActions.RUN_TO_CURSOR)));
DebuggerSession session = debugProcess.getSession();
session.getContextManager().setState(DebuggerContextUtil.createDebuggerContext(session, context),
DebuggerSession.State.PAUSED, DebuggerSession.Event.CONTEXT, null);
}
});
}
super.contextAction();
}
}
@@ -628,7 +628,9 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder {
final String localName = psiVar.getName();
PsiClass variableClass = getContainingClass(psiVar);
if (getContextPsiClass() == null || getContextPsiClass().equals(variableClass)) {
final LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, ContextUtil.isJspImplicit(element));
PsiElement method = PsiTreeUtil.getContextOfType(expression, PsiMethod.class, PsiLambdaExpression.class);
boolean canScanFrames = method instanceof PsiLambdaExpression || ContextUtil.isJspImplicit(element);
LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, canScanFrames);
if (psiVar instanceof PsiParameter) {
final PsiParameter param = (PsiParameter)psiVar;
final PsiParameterList paramList = PsiTreeUtil.getParentOfType(param, PsiParameterList.class, true);
@@ -21,18 +21,27 @@
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.LambdaMethodFilter;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.jdi.StackFrameProxy;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.impl.SimpleStackFrameContext;
import com.intellij.debugger.jdi.LocalVariableProxyImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.ui.impl.watch.LocalVariableDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiVariable;
import com.sun.jdi.*;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -42,12 +51,12 @@ class LocalVariableEvaluator implements Evaluator {
private final String myLocalVariableName;
private EvaluationContextImpl myContext;
private LocalVariableProxyImpl myEvaluatedVariable;
private final boolean myIsJspSpecial;
private final boolean myCanScanFrames;
private int myParameterIndex = -1;
public LocalVariableEvaluator(String localVariableName, boolean isJspSpecial) {
public LocalVariableEvaluator(String localVariableName, boolean canScanFrames) {
myLocalVariableName = localVariableName;
myIsJspSpecial = isJspSpecial;
myCanScanFrames = canScanFrames;
}
public void setParameterIndex(int parameterIndex) {
@@ -64,23 +73,27 @@ class LocalVariableEvaluator implements Evaluator {
try {
ThreadReferenceProxyImpl threadProxy = null;
int lastFrameIndex = -1;
PsiVariable variable = null;
boolean anotherFrame = false;
boolean topFrame = true;
while (true) {
try {
LocalVariableProxyImpl local = frameProxy.visibleVariableByName(myLocalVariableName);
if (local != null) {
myEvaluatedVariable = local;
myContext = context;
return frameProxy.getValue(local);
if (topFrame ||
variable.equals(resolveVariable(frameProxy, myLocalVariableName, context.getProject(), context.getDebugProcess()))) {
myEvaluatedVariable = local;
myContext = context;
return frameProxy.getValue(local);
}
}
}
catch (EvaluateException e) {
if (!(e.getCause() instanceof AbsentInformationException)) {
throw e;
}
if (!anotherFrame) {
if (topFrame) {
if (myParameterIndex < 0) {
throw e;
}
@@ -92,7 +105,11 @@ class LocalVariableEvaluator implements Evaluator {
}
}
if (anotherFrame || needToSwitchFrames(frameProxy)) {
if (myCanScanFrames) {
if (topFrame) {
variable = resolveVariable(frameProxy, myLocalVariableName, context.getProject(), context.getDebugProcess());
if (variable == null) break;
}
if (threadProxy == null /* initialize it lazily */) {
threadProxy = frameProxy.threadProxy();
lastFrameIndex = threadProxy.frameCount() - 1;
@@ -101,7 +118,7 @@ class LocalVariableEvaluator implements Evaluator {
if (currentFrameIndex < lastFrameIndex) {
frameProxy = threadProxy.frame(currentFrameIndex + 1);
if (frameProxy != null) {
anotherFrame = true;
topFrame = false;
continue;
}
}
@@ -118,17 +135,6 @@ class LocalVariableEvaluator implements Evaluator {
}
}
private boolean needToSwitchFrames(StackFrameProxyImpl frameProxy) {
if (myIsJspSpecial) return true;
try {
Location location = frameProxy.location();
if (location == null) return false;
return LambdaMethodFilter.isLambdaName(location.method().name());
}
catch (EvaluateException ignored) {}
return false;
}
@Override
public Modifier getModifier() {
Modifier modifier = null;
@@ -176,6 +182,21 @@ class LocalVariableEvaluator implements Evaluator {
return modifier;
}
@Nullable
private static PsiVariable resolveVariable(final StackFrameProxy frame,
final String name,
final Project project,
final DebugProcess process) {
return ApplicationManager.getApplication().runReadAction(new Computable<PsiVariable>() {
@Override
public PsiVariable compute() {
PsiElement place = PositionUtil.getContextElement(new SimpleStackFrameContext(frame, process));
if (place == null) return null;
return JavaPsiFacade.getInstance(project).getResolveHelper().resolveReferencedVariable(name, place);
}
});
}
@Override
public String toString() {
return myLocalVariableName;
@@ -136,7 +136,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaLineB
return res;
}
public boolean matchesPosition(@NotNull LineBreakpoint breakpoint, @NotNull SourcePosition position) {
public boolean matchesPosition(@NotNull LineBreakpoint<?> breakpoint, @NotNull SourcePosition position) {
JavaBreakpointProperties properties = breakpoint.getProperties();
if (properties == null || properties instanceof JavaLineBreakpointProperties) {
if (properties != null && ((JavaLineBreakpointProperties)properties).getLambdaOrdinal() == null) return true;
@@ -148,7 +148,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaLineB
}
@Nullable
public PsiElement getContainingMethod(@NotNull LineBreakpoint breakpoint) {
public PsiElement getContainingMethod(@NotNull LineBreakpoint<?> breakpoint) {
SourcePosition position = breakpoint.getSourcePosition();
if (position == null) return null;
@@ -168,7 +168,7 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaLineB
public class JavaBreakpointVariant extends XLineBreakpointVariant {
protected final XSourcePosition mySourcePosition;
private JavaBreakpointVariant(XSourcePosition position) {
public JavaBreakpointVariant(XSourcePosition position) {
mySourcePosition = position;
}
@@ -552,11 +552,13 @@ public class TestDiscoveryIndex implements ProjectComponent {
}
}
}
for (int classQName : previousClassData.keys()) {
if (classData.containsKey(classQName)) continue;
if (classData != null) {
for (int classQName : previousClassData.keys()) {
if (classData.containsKey(classQName)) continue;
TIntArrayList previousMethods = previousClassData.get(classQName);
removedClassData.put(classQName, previousMethods);
TIntArrayList previousMethods = previousClassData.get(classQName);
removedClassData.put(classQName, previousMethods);
}
}
}
@@ -300,6 +300,10 @@ public class InputVariables {
}
}
public ParametersFolder getFolding() {
return myFolding;
}
public void setFoldingAvailable(boolean foldingAvailable) {
myFoldingAvailable = foldingAvailable;
@@ -57,13 +57,13 @@ public class BasicExpressionCompletionContributor {
final Consumer<LookupElement> result,
PrefixMatcher matcher) {
final PsiElement element = parameters.getPosition();
if (JavaCompletionData.isAfterTypeDot(element)) {
if (JavaKeywordCompletion.isAfterTypeDot(element)) {
addKeyword(result, element, PsiKeyword.CLASS);
addKeyword(result, element, PsiKeyword.THIS);
}
if (!JavaCompletionData.AFTER_DOT.accepts(element)) {
if (!JavaKeywordCompletion.AFTER_DOT.accepts(element)) {
if (parameters.getParameters().getInvocationCount() <= 1) {
new CollectionsUtilityMethodsProvider(parameters.getPosition(),
parameters.getExpectedType(),
@@ -1,56 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.TailType;
import com.intellij.psi.*;
import com.intellij.psi.filters.*;
import com.intellij.psi.filters.position.LeftNeighbour;
import com.intellij.psi.filters.position.SuperParentFilter;
/**
* @author ven
*/
public class Java15CompletionData extends JavaCompletionData {
@Override
protected void initVariantsInFileScope() {
super.initVariantsInFileScope();
//static keyword in static import
{
final CompletionVariant variant = new CompletionVariant(PsiImportList.class, new LeftNeighbour(new TextFilter (PsiKeyword.IMPORT)));
variant.addCompletion(PsiKeyword.STATIC, TailType.HUMBLE_SPACE_BEFORE_WORD);
registerVariant(variant);
}
{
final ElementFilter position = new AndFilter(new LeftNeighbour(new TextFilter("@")),
new NotFilter(new SuperParentFilter(
new OrFilter(new ClassFilter(PsiNameValuePair.class),
new ClassFilter(PsiParameterList.class))))
);
final CompletionVariant variant = new CompletionVariant(PsiJavaFile.class, position);
variant.includeScopeClass(PsiClass.class);
variant.addCompletion(PsiKeyword.INTERFACE, TailType.HUMBLE_SPACE_BEFORE_WORD);
registerVariant(variant);
}
}
}
@@ -1,62 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
public class Java18CompletionData extends Java15CompletionData {
private static final PsiElementPattern<PsiElement, ?> AFTER_DOUBLE_COLON = psiElement()
.afterLeaf(psiElement(JavaTokenType.DOUBLE_COLON));
@Override
public void fillCompletions(final CompletionParameters parameters, final Consumer<LookupElement> result) {
PsiElement position = parameters.getPosition();
if (!inComment(position)) {
if (AFTER_DOUBLE_COLON.accepts(position)) {
PsiMethodReferenceExpression parent = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiMethodReferenceExpression.class);
TailType tail = parent != null && !LambdaHighlightingUtil.insertSemicolon(parent.getParent()) ? TailType.SEMICOLON : TailType.NONE;
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), tail));
return;
}
if (isSuitableForClass(position)) {
PsiElement scope = position.getParent();
while (scope != null && !(scope instanceof PsiFile)) {
if (scope instanceof PsiClass && ((PsiClass)scope).isInterface()) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD));
break;
}
scope = scope.getParent();
}
}
}
super.fillCompletions(parameters, result);
}
private static boolean inComment(final PsiElement position) {
return PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null;
}
}
@@ -1,89 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupItemUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiKeyword;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Set;
/**
* @author peter
*/
public class JavaAwareCompletionData extends CompletionData{
@Override
protected void addLookupItem(Set<LookupElement> set, final TailType tailType, @NotNull Object completion, final PsiFile file, final CompletionVariant variant) {
if (completion instanceof LookupElement && !(completion instanceof LookupItem)) {
set.add((LookupElement)completion);
return;
}
LookupElement _ret = LookupItemUtil.objectToLookupItem(completion);
if(_ret == null || !(_ret instanceof LookupItem)) return;
LookupItem ret = (LookupItem)_ret;
ret.setInsertHandler(new InsertHandler<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
if (context.shouldAddCompletionChar()) {
return;
}
if (tailType != TailType.NONE && tailType.isApplicable(context)) {
tailType.processTail(context.getEditor(), context.getTailOffset());
}
}
});
final Map<Object, Object> itemProperties = variant.getItemProperties();
for (final Object key : itemProperties.keySet()) {
ret.setAttribute(key, itemProperties.get(key));
}
set.add(ret);
}
protected void addKeyword(Set<LookupElement> set,
final TailType tailType,
final Object comp,
final PrefixMatcher matcher,
final PsiFile file,
final CompletionVariant variant) {
final PsiElementFactory factory = JavaPsiFacade.getInstance(file.getProject()).getElementFactory();
for (final LookupElement item : set) {
if (item.getObject().toString().equals(comp.toString())) {
return;
}
}
try{
final PsiKeyword keyword = factory.createKeyword((String)comp);
addLookupItem(set, tailType, keyword, file, variant);
}
catch(IncorrectOperationException e){
addLookupItem(set, tailType, comp, file, variant);
}
}
}
@@ -38,7 +38,6 @@ import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.patterns.PsiNameValuePairPattern;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.filters.*;
@@ -54,7 +53,12 @@ import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Consumer;
import com.intellij.util.DocumentUtil;
import com.intellij.util.PairConsumer;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -69,28 +73,11 @@ import static com.intellij.util.ObjectUtils.assertNotNull;
public class JavaCompletionContributor extends CompletionContributor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionContributor");
private static final Map<LanguageLevel, JavaCompletionData> ourCompletionData;
static {
ourCompletionData = new LinkedHashMap<LanguageLevel, JavaCompletionData>();
ourCompletionData.put(LanguageLevel.JDK_1_8, new Java18CompletionData());
ourCompletionData.put(LanguageLevel.JDK_1_5, new Java15CompletionData());
ourCompletionData.put(LanguageLevel.JDK_1_3, new JavaCompletionData());
}
public static final ElementPattern<PsiElement> ANNOTATION_NAME = psiElement().
withParents(PsiJavaCodeReferenceElement.class, PsiAnnotation.class).afterLeaf("@");
private static final PsiJavaElementPattern.Capture<PsiElement> UNEXPECTED_REFERENCE_AFTER_DOT =
psiElement().afterLeaf(".").insideStarting(psiExpressionStatement());
private static JavaCompletionData getCompletionData(LanguageLevel level) {
final Set<Map.Entry<LanguageLevel, JavaCompletionData>> entries = ourCompletionData.entrySet();
for (Map.Entry<LanguageLevel, JavaCompletionData> entry : entries) {
if (entry.getKey().isAtLeast(level)) return entry.getValue();
}
return ourCompletionData.get(LanguageLevel.JDK_1_3);
}
private static final PsiNameValuePairPattern NAME_VALUE_PAIR =
psiNameValuePair().withSuperParent(2, psiElement(PsiAnnotation.class));
private static final ElementPattern<PsiElement> ANNOTATION_ATTRIBUTE_NAME =
@@ -125,8 +112,8 @@ public class JavaCompletionContributor extends CompletionContributor {
return new AnnotationTypeFilter();
}
if (JavaCompletionData.DECLARATION_START.getValue().accepts(position) ||
JavaCompletionData.isInsideParameterList(position) ||
if (JavaKeywordCompletion.DECLARATION_START.getValue().accepts(position) ||
JavaKeywordCompletion.isInsideParameterList(position) ||
psiElement().inside(psiElement(PsiJavaCodeReferenceElement.class).withParent(psiAnnotation())).accepts(position)) {
return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER);
}
@@ -135,18 +122,18 @@ public class JavaCompletionContributor extends CompletionContributor {
return new ElementExtractorFilter(ElementClassFilter.CLASS);
}
if (JavaCompletionData.VARIABLE_AFTER_FINAL.accepts(position)) {
if (JavaKeywordCompletion.VARIABLE_AFTER_FINAL.accepts(position)) {
return ElementClassFilter.CLASS;
}
if (JavaCompletionData.AFTER_TRY_BLOCK.isAcceptable(position, position) ||
JavaCompletionData.START_SWITCH.accepts(position) ||
JavaCompletionData.isInstanceofPlace(position) ||
JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
if (psiElement().afterLeaf(psiElement(JavaTokenType.RBRACE).withParents(PsiCodeBlock.class, PsiTryStatement.class)).accepts(position) ||
JavaKeywordCompletion.START_SWITCH.accepts(position) ||
JavaKeywordCompletion.isInstanceofPlace(position) ||
JavaKeywordCompletion.isAfterPrimitiveOrArrayType(position)) {
return null;
}
if (JavaCompletionData.START_FOR.accepts(position)) {
if (JavaKeywordCompletion.START_FOR.accepts(position)) {
return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.VARIABLE);
}
@@ -213,8 +200,8 @@ public class JavaCompletionContributor extends CompletionContributor {
final CompletionResultSet result = JavaCompletionSorting.addJavaSorting(parameters, _result);
if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
JavaCompletionData.addExpectedTypeMembers(parameters, result);
if (ANNOTATION_ATTRIBUTE_NAME.accepts(position) && !JavaKeywordCompletion.isAfterPrimitiveOrArrayType(position)) {
JavaKeywordCompletion.addExpectedTypeMembers(parameters, result);
completeAnnotationAttributeName(result, position, parameters);
result.stopHere();
return;
@@ -291,7 +278,7 @@ public class JavaCompletionContributor extends CompletionContributor {
result.stopHere();
}
private void registerClassFromTypeElement(LookupElement element, InheritorsHolder inheritors) {
private static void registerClassFromTypeElement(LookupElement element, InheritorsHolder inheritors) {
PsiType type = assertNotNull(element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY)).getPsiType();
PsiClass aClass =
type instanceof PsiClassType && ((PsiClassType)type).getParameterCount() == 0 ? ((PsiClassType)type).resolve() : null;
@@ -302,8 +289,8 @@ public class JavaCompletionContributor extends CompletionContributor {
private static void addExpressionVariants(@NotNull CompletionParameters parameters, PsiElement position, CompletionResultSet result) {
if (JavaSmartCompletionContributor.INSIDE_EXPRESSION.accepts(position) &&
!JavaCompletionData.AFTER_DOT.accepts(position) && !SmartCastProvider.shouldSuggestCast(parameters)) {
JavaCompletionData.addExpectedTypeMembers(parameters, result);
!JavaKeywordCompletion.AFTER_DOT.accepts(position) && !SmartCastProvider.shouldSuggestCast(parameters)) {
JavaKeywordCompletion.addExpectedTypeMembers(parameters, result);
if (SameSignatureCallParametersProvider.IN_CALL_ARGUMENT.accepts(position)) {
new SameSignatureCallParametersProvider().addCompletions(parameters, new ProcessingContext(), result);
}
@@ -348,6 +335,7 @@ public class JavaCompletionContributor extends CompletionContributor {
final boolean isSwitchLabel = SWITCH_LABEL.accepts(position);
final boolean isAfterNew = JavaClassNameCompletionContributor.AFTER_NEW.accepts(position);
final boolean pkgContext = JavaCompletionUtil.inSomePackage(position);
final PsiType[] expectedTypes = ExpectedTypesGetter.getExpectedTypes(parameters.getPosition(), true);
LegacyCompletionContributor.processReferences(parameters, result, new PairConsumer<PsiReference, CompletionResultSet>() {
@Override
public void consume(final PsiReference reference, final CompletionResultSet result) {
@@ -378,6 +366,22 @@ public class JavaCompletionContributor extends CompletionContributor {
!((PsiJavaCodeReferenceCodeFragment)originalFile).isClassesAccepted() && item != null) {
item.setTailType(TailType.NONE);
}
if (item instanceof JavaMethodCallElement) {
JavaMethodCallElement call = (JavaMethodCallElement)item;
final PsiMethod method = call.getObject();
if (method.getTypeParameters().length > 0) {
final PsiType returned = TypeConversionUtil.erasure(method.getReturnType());
PsiType matchingExpectation = returned == null ? null : ContainerUtil.find(expectedTypes, new Condition<PsiType>() {
@Override
public boolean value(PsiType type) {
return type.isAssignableFrom(returned);
}
});
if (matchingExpectation != null && SmartCompletionDecorator.hasUnboundTypeParams(method, matchingExpectation)) {
call.setInferenceSubstitutor(SmartCompletionDecorator.calculateMethodReturnTypeSubstitutor(method, matchingExpectation), position);
}
}
}
result.addElement(element);
}
@@ -438,24 +442,14 @@ public class JavaCompletionContributor extends CompletionContributor {
}
};
PsiElement position = parameters.getPosition();
final Set<LookupElement> lookupSet = new LinkedHashSet<LookupElement>();
final Set<CompletionVariant> keywordVariants = new HashSet<CompletionVariant>();
final JavaCompletionData completionData = getCompletionData(PsiUtil.getLanguageLevel(position));
completionData.addKeywordVariants(keywordVariants, position, parameters.getOriginalFile());
completionData.completeKeywordsBySet(lookupSet, keywordVariants, position, result.getPrefixMatcher(), parameters.getOriginalFile());
completionData.fillCompletions(parameters, noMiddleMatches);
for (final LookupElement item : lookupSet) {
noMiddleMatches.consume(item);
}
JavaKeywordCompletion.addKeywords(parameters, noMiddleMatches);
}
static boolean isClassNamePossible(CompletionParameters parameters) {
boolean isSecondCompletion = parameters.getInvocationCount() >= 2;
PsiElement position = parameters.getPosition();
if (JavaCompletionData.isInstanceofPlace(position)) return false;
if (JavaKeywordCompletion.isInstanceofPlace(position)) return false;
final PsiElement parent = position.getParent();
if (!(parent instanceof PsiJavaCodeReferenceElement)) return isSecondCompletion;
@@ -482,7 +476,7 @@ public class JavaCompletionContributor extends CompletionContributor {
return false;
}
if (JavaCompletionData.isAfterPrimitiveOrArrayType(position)) {
if (JavaKeywordCompletion.isAfterPrimitiveOrArrayType(position)) {
return false;
}
@@ -48,7 +48,7 @@ public class JavaGenerateMemberCompletionContributor {
PsiElement position = parameters.getPosition();
if (psiElement(PsiIdentifier.class).withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClass.class).
andNot(JavaCompletionData.AFTER_DOT).
andNot(JavaKeywordCompletion.AFTER_DOT).
andNot(psiElement().afterLeaf(psiElement().inside(PsiModifierList.class))).accepts(position)) {
suggestGeneratedMethods(result, position);
} else if (psiElement(PsiIdentifier.class)
@@ -19,16 +19,14 @@ import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.TailTypes;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil;
import com.intellij.codeInsight.lookup.*;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.filters.*;
import com.intellij.psi.filters.classes.EnumOrAnnotationTypeFilter;
import com.intellij.psi.filters.classes.InterfaceFilter;
import com.intellij.psi.filters.getters.JavaMembersGetter;
import com.intellij.psi.filters.position.*;
import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement;
@@ -38,28 +36,18 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.patterns.PsiJavaPatterns.*;
import static com.intellij.patterns.StandardPatterns.not;
public class JavaCompletionData extends JavaAwareCompletionData {
private static final @NonNls String[] BLOCK_FINALIZERS = {"{", "}", ";", ":", "else"};
public class JavaKeywordCompletion {
public static final ElementPattern<PsiElement> AFTER_DOT = psiElement().afterLeaf(".");
public static final PsiJavaElementPattern.Capture<PsiElement> VARIABLE_AFTER_FINAL =
psiElement().afterLeaf(PsiKeyword.FINAL).inside(PsiDeclarationStatement.class);
static final ElementPattern<PsiElement> VARIABLE_AFTER_FINAL = psiElement().afterLeaf(PsiKeyword.FINAL).inside(PsiDeclarationStatement.class);
public static final LeftNeighbour AFTER_TRY_BLOCK = new LeftNeighbour(new AndFilter(
new TextFilter("}"),
new ParentElementFilter(new AndFilter(
new LeftNeighbour(new TextFilter(PsiKeyword.TRY)),
new ParentElementFilter(new ClassFilter(PsiTryStatement.class)))
)));
private static final PsiJavaElementPattern.Capture<PsiElement> INSIDE_PARAMETER_LIST =
private static final ElementPattern<PsiElement> INSIDE_PARAMETER_LIST =
psiElement().withParent(
psiElement(PsiJavaCodeReferenceElement.class).insideStarting(
psiElement().withTreeParent(
@@ -98,7 +86,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
new LeftNeighbour(
new OrFilter(
new AndFilter (
new TextFilter(BLOCK_FINALIZERS),
new TextFilter("{", "}", ";", ":", "else"),
new NotFilter (
new SuperParentFilter(new ClassFilter(PsiAnnotation.class))
)
@@ -151,10 +139,10 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
};
public static final ElementPattern<PsiElement> START_FOR =
static final ElementPattern<PsiElement> START_FOR =
psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for")).withParents(PsiJavaCodeReferenceElement.class,
PsiExpressionStatement.class, PsiForStatement.class);
private static final PsiJavaElementPattern.Capture<PsiElement> CLASS_REFERENCE =
private static final ElementPattern<PsiElement> CLASS_REFERENCE =
psiElement().withParent(psiReferenceExpression().referencing(psiClass().andNot(psiElement(PsiTypeParameter.class))));
private static final ElementPattern<PsiElement> EXPR_KEYWORDS = and(
@@ -170,18 +158,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
not(psiElement().afterLeaf("."))
);
public JavaCompletionData(){
declareCompletionSpaces();
initVariantsInFileScope();
initVariantsInClassScope();
initVariantsInMethodScope();
defineScopeEquivalence(PsiMethod.class, PsiClassInitializer.class);
defineScopeEquivalence(PsiMethod.class, JavaCodeFragment.class);
}
public static final NotNullLazyValue<ElementPattern<PsiElement>> DECLARATION_START = new NotNullLazyValue<ElementPattern<PsiElement>>() {
static final NotNullLazyValue<ElementPattern<PsiElement>> DECLARATION_START = new NotNullLazyValue<ElementPattern<PsiElement>>() {
@NotNull
@Override
protected ElementPattern<PsiElement> compute() {
@@ -200,170 +177,6 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
};
private void declareCompletionSpaces() {
declareFinalScope(PsiFile.class);
{
// Class body
final CompletionVariant variant = new CompletionVariant(CLASS_BODY.getValue());
variant.includeScopeClass(PsiClass.class, true);
registerVariant(variant);
}
{
// Method body
final CompletionVariant variant = new CompletionVariant(new AndFilter(new InsideElementFilter(new ClassFilter(PsiCodeBlock.class)),
new NotFilter(new InsideElementFilter(new ClassFilter(JspClassLevelDeclarationStatement.class)))));
variant.includeScopeClass(PsiMethod.class, true);
variant.includeScopeClass(PsiClassInitializer.class, true);
registerVariant(variant);
}
{
// Field initializer
final CompletionVariant variant = new CompletionVariant(new AfterElementFilter(new TextFilter("=")));
variant.includeScopeClass(PsiField.class, true);
registerVariant(variant);
}
declareFinalScope(PsiLiteralExpression.class);
declareFinalScope(PsiComment.class);
}
protected void initVariantsInFileScope(){
}
/**
* aClass == null for JspDeclaration scope
*/
protected void initVariantsInClassScope() {
// Completion for extends keyword
// position
{
final ElementFilter position = new AndFilter(
new NotFilter(CLASS_BODY.getValue()),
new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))),
new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))),
new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))),
new NotFilter(new ScopeFilter(new EnumOrAnnotationTypeFilter())),
new LeftNeighbour(new OrFilter(
new ClassFilter(PsiIdentifier.class),
new TextFilter(">"))));
// completion
final CompletionVariant variant = new CompletionVariant(position);
variant.includeScopeClass(PsiClass.class, true);
variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD);
variant.excludeScopeClass(PsiAnonymousClass.class);
variant.excludeScopeClass(PsiTypeParameter.class);
registerVariant(variant);
}
// Completion for implements keyword
// position
{
final ElementFilter position = new AndFilter(
new NotFilter(CLASS_BODY.getValue()),
new NotFilter(new BeforeElementFilter(new ContentFilter(new TextFilter(PsiKeyword.EXTENDS)))),
new NotFilter(new AfterElementFilter(new ContentFilter(new TextFilter(PsiKeyword.IMPLEMENTS)))),
new NotFilter(new LeftNeighbour(new LeftNeighbour(new TextFilter("<", ",")))),
new LeftNeighbour(new OrFilter(
new ClassFilter(PsiIdentifier.class),
new TextFilter(">"))),
new NotFilter(new ScopeFilter(new InterfaceFilter())));
// completion
final CompletionVariant variant = new CompletionVariant(position);
variant.includeScopeClass(PsiClass.class, true);
variant.addCompletion(PsiKeyword.IMPLEMENTS, TailType.HUMBLE_SPACE_BEFORE_WORD);
variant.excludeScopeClass(PsiAnonymousClass.class);
registerVariant(variant);
}
{
final CompletionVariant variant = new CompletionVariant(PsiElement.class, psiElement().afterLeaf(
psiElement(PsiIdentifier.class).afterLeaf(
psiElement().withText(string().oneOf(",", "<")).withParent(PsiTypeParameterList.class))));
//variant.includeScopeClass(PsiClass.class, true);
variant.addCompletion(PsiKeyword.EXTENDS, TailType.HUMBLE_SPACE_BEFORE_WORD);
registerVariant(variant);
}
}
private void initVariantsInMethodScope() {
// Completion for classes in method throws section
// position
{
final ElementFilter position = new LeftNeighbour(new AndFilter(
new TextFilter(")"),
new ParentElementFilter(new ClassFilter(PsiParameterList.class))));
// completion
CompletionVariant variant = new CompletionVariant(PsiMethod.class, position);
variant.includeScopeClass(PsiClass.class); // for throws on separate line
variant.addCompletion(PsiKeyword.THROWS);
registerVariant(variant);
//in annotation methods
variant = new CompletionVariant(PsiAnnotationMethod.class, position);
variant.addCompletion(PsiKeyword.DEFAULT);
registerVariant(variant);
}
{
// Keyword completion in returns !!!!
final CompletionVariant variant = new CompletionVariant(PsiMethod.class, new LeftNeighbour(new TextFilter(PsiKeyword.RETURN)));
variant.addCompletion(PsiKeyword.TRUE, TailType.NONE);
variant.addCompletion(PsiKeyword.FALSE, TailType.NONE);
registerVariant(variant);
}
// Catch/Finally completion
{
final ElementFilter position = AFTER_TRY_BLOCK;
final CompletionVariant variant = new CompletionVariant(position);
variant.includeScopeClass(PsiCodeBlock.class, true);
variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH);
variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE);
registerVariant(variant);
}
// Catch/Finally completion
{
final ElementFilter position = new LeftNeighbour(new AndFilter(
new TextFilter("}"),
new ParentElementFilter(new AndFilter(
new LeftNeighbour(new NotFilter(new TextFilter(PsiKeyword.TRY))),
new OrFilter(
new ParentElementFilter(new ClassFilter(PsiTryStatement.class)),
new ParentElementFilter(new ClassFilter(PsiCatchSection.class)))
))));
final CompletionVariant variant = new CompletionVariant(position);
variant.includeScopeClass(PsiCodeBlock.class, false);
variant.addCompletion(PsiKeyword.CATCH, TailTypes.CATCH_LPARENTH);
variant.addCompletion(PsiKeyword.FINALLY, TailTypes.FINALLY_LBRACE);
registerVariant(variant);
}
// Completion for else expression
// completion
{
final ElementFilter position = new LeftNeighbour(
new OrFilter(
new AndFilter(new TextFilter("}"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 3)),
new AndFilter(new TextFilter(";"),new ParentElementFilter(new ClassFilter(PsiIfStatement.class), 2))
));
final CompletionVariant variant = new CompletionVariant(PsiMethod.class, position);
variant.addCompletion(PsiKeyword.ELSE);
registerVariant(variant);
}
}
private static TailType getReturnTail(PsiElement position) {
PsiElement scope = position;
while(true){
@@ -390,7 +203,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
}
private static void addStatementKeywords(Consumer<LookupElement> variant, PsiElement position) {
private static void addStatementKeywords(Consumer<LookupElement> variant, PsiElement position, @Nullable PsiElement prevLeaf) {
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SWITCH), TailTypes.SWITCH_LPARENTH));
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.WHILE), TailTypes.WHILE_LPARENTH));
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DO), TailTypes.DO_LBRACE));
@@ -411,19 +224,82 @@ public class JavaCompletionData extends JavaAwareCompletionData {
ret = new OverrideableSpace(ret, returnTail);
}
variant.consume(ret);
if (psiElement().withText(";").withSuperParent(2, PsiIfStatement.class).accepts(prevLeaf) ||
psiElement().withText("}").withSuperParent(3, PsiIfStatement.class).accepts(prevLeaf)) {
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ELSE), TailTypes.SYNCHRONIZED_LPARENTH));
}
if (psiElement().withText("}").withParent(psiElement(PsiCodeBlock.class).withParent(or(psiElement(PsiTryStatement.class), psiElement(PsiCatchSection.class)))).accepts(prevLeaf)) {
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CATCH), TailTypes.CATCH_LPARENTH));
variant.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.FINALLY), TailTypes.FINALLY_LBRACE));
}
}
public void fillCompletions(CompletionParameters parameters, final Consumer<LookupElement> result) {
static void addKeywords(CompletionParameters parameters, final Consumer<LookupElement> result) {
final PsiElement position = parameters.getPosition();
if (PsiTreeUtil.getParentOfType(position, PsiComment.class, false) != null) {
if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) {
return;
}
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(position);
addFinal(result, position, prevLeaf);
if (isStatementPosition(position)) {
addCaseDefault(result, position);
if (START_SWITCH.accepts(position)) {
return;
}
addBreakContinue(result, position);
addStatementKeywords(result, position, prevLeaf);
}
addThisSuper(result, position);
addExpressionKeywords(parameters, result, position, prevLeaf);
addFileHeaderKeywords(result, position, prevLeaf);
addInstanceof(result, position);
addClassKeywords(result, position, prevLeaf);
addMethodHeaderKeywords(result, position, prevLeaf);
addPrimitiveTypes(result, position);
addClassLiteral(result, position);
addUnfinishedMethodTypeParameters(position, result);
addExtendsSuperImplements(result, position, prevLeaf);
}
private static void addMethodHeaderKeywords(Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
if (psiElement().withText(")").withParents(PsiParameterList.class, PsiMethod.class).accepts(prevLeaf)) {
assert prevLeaf != null;
if (prevLeaf.getParent().getParent() instanceof PsiAnnotationMethod) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD));
} else {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.THROWS), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
}
private static void addCaseDefault(Consumer<LookupElement> result, PsiElement position) {
if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON));
}
}
private static void addFinal(Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class);
if (statement == null) {
statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class);
}
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(position);
if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) {
if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).afterLeaf("{").accepts(statement)) {
PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(prevLeaf, PsiTryStatement.class);
@@ -433,19 +309,15 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
}
if (isStatementPosition(position)) {
if (PsiTreeUtil.getParentOfType(position, PsiSwitchStatement.class, false, PsiMember.class) != null) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CASE), TailType.INSERT_SPACE));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.CASE_COLON));
if (START_SWITCH.accepts(position)) {
return;
}
}
addBreakContinue(result, position);
addStatementKeywords(result, position);
if ((isInsideParameterList(position) || isAtResourceVariableStart(position) || isAtCatchVariableStart(position)) &&
!psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) &&
!AFTER_DOT.accepts(position)) {
result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
private static void addThisSuper(Consumer<LookupElement> result, PsiElement position) {
if (SUPER_OR_THIS_PATTERN.accepts(position)) {
final boolean afterDot = AFTER_DOT.accepts(position);
final boolean insideQualifierClass = isInsideQualifierClass(position);
@@ -477,6 +349,15 @@ public class JavaCompletionData extends JavaAwareCompletionData {
result.consume(superItem);
}
}
}
private static void addExpressionKeywords(CompletionParameters parameters, Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
if (psiElement(JavaTokenType.DOUBLE_COLON).accepts(prevLeaf)) {
PsiMethodReferenceExpression parent = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiMethodReferenceExpression.class);
TailType tail = parent != null && !LambdaHighlightingUtil.insertSemicolon(parent.getParent()) ? TailType.SEMICOLON : TailType.NONE;
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.NEW), tail));
return;
}
if (isExpressionPosition(position)) {
if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) {
@@ -488,7 +369,9 @@ public class JavaCompletionData extends JavaAwareCompletionData {
result.consume(createKeyword(position, PsiKeyword.FALSE));
}
}
}
private static void addFileHeaderKeywords(Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
PsiFile file = position.getContainingFile();
if (!(file instanceof PsiExpressionCodeFragment) &&
!(file instanceof PsiJavaCodeReferenceCodeFragment) &&
@@ -502,12 +385,12 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
}
if ((isInsideParameterList(position) || isAtResourceVariableStart(position) || isAtCatchVariableStart(position)) &&
!psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) &&
!AFTER_DOT.accepts(position)) {
result.consume(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD));
if (PsiUtil.isLanguageLevel5OrHigher(position) && prevLeaf != null && prevLeaf.textMatches(PsiKeyword.IMPORT)) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.STATIC), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
private static void addInstanceof(Consumer<LookupElement> result, PsiElement position) {
if (isInstanceofPlace(position)) {
result.consume(LookupElementDecorator.withInsertHandler(
createKeyword(position, PsiKeyword.INSTANCEOF),
@@ -533,32 +416,65 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
}));
}
}
private static void addClassKeywords(Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
if (isSuitableForClass(position)) {
for (String s : ModifierChooser.getKeywords(position)) {
result.consume(new OverrideableSpace(createKeyword(position, s), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
if (PsiUtil.isLanguageLevel8OrHigher(position)) {
PsiClass containingClass = PsiTreeUtil.getParentOfType(position, PsiClass.class);
if (containingClass != null && containingClass.isInterface()) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.DEFAULT), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD));
if (PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) == null) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD));
if (PsiUtil.getLanguageLevel(position).isAtLeast(LanguageLevel.JDK_1_5)) {
if (PsiUtil.isLanguageLevel5OrHigher(position)) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.ENUM), TailType.INSERT_SPACE));
}
}
}
addPrimitiveTypes(result, position);
if (psiElement().withText("@").andNot(psiElement().inside(PsiParameterList.class)).andNot(psiElement().inside(psiNameValuePair()))
.accepts(prevLeaf)) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
private static void addClassLiteral(Consumer<LookupElement> result, PsiElement position) {
if (isAfterTypeDot(position)) {
result.consume(createKeyword(position, PsiKeyword.CLASS));
}
}
addUnfinishedMethodTypeParameters(position, result);
private static void addExtendsSuperImplements(Consumer<LookupElement> result, PsiElement position, @Nullable PsiElement prevLeaf) {
if (JavaMemberNameCompletionContributor.INSIDE_TYPE_PARAMS_PATTERN.accepts(position)) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.SUPER), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
if (prevLeaf == null || !(prevLeaf instanceof PsiIdentifier || prevLeaf.textMatches(">"))) return;
PsiClass psiClass = null;
if (prevLeaf instanceof PsiIdentifier && prevLeaf.getParent() instanceof PsiClass) {
psiClass = (PsiClass)prevLeaf.getParent();
} else {
PsiReferenceList referenceList = PsiTreeUtil.getParentOfType(prevLeaf, PsiReferenceList.class);
if (referenceList != null && referenceList.getParent() instanceof PsiClass) {
psiClass = (PsiClass)referenceList.getParent();
}
}
if (psiClass != null) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXTENDS), TailType.HUMBLE_SPACE_BEFORE_WORD));
if (!psiClass.isInterface()) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.IMPLEMENTS), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
}
private static boolean mayExpectBoolean(CompletionParameters parameters) {
@@ -736,10 +652,6 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
private static boolean isStatementPosition(PsiElement position) {
if (PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class) != null) {
return false;
}
if (psiElement().withSuperParent(2, PsiConditionalExpression.class).andNot(psiElement().insideStarting(psiElement(PsiConditionalExpression.class))).accepts(position)) {
return false;
}
@@ -269,7 +269,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
@Override
public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
final PsiElement element = parameters.getPosition();
final ElementPattern<? extends PsiElement> leftNeighbor = JavaCompletionData.AFTER_DOT;
final ElementPattern<? extends PsiElement> leftNeighbor = JavaKeywordCompletion.AFTER_DOT;
final boolean needQualify = leftNeighbor.accepts(element);
for (final PsiType type : ExpectedTypesGetter.getExpectedTypes(element, false)) {
@@ -382,7 +382,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
boolean quick,
Consumer<LookupElement> consumer) {
PsiElement position = params.getPosition();
if (!JavaCompletionData.AFTER_DOT.accepts(position)) {
if (!JavaKeywordCompletion.AFTER_DOT.accepts(position)) {
for (ExpectedTypeInfo info : mergedInfos) {
new JavaMembersGetter(info.getType(), params).addMembers(!quick, consumer);
if (!info.getDefaultType().equals(info.getType())) {
@@ -38,7 +38,7 @@ import static com.intellij.patterns.PsiJavaPatterns.psiElement;
class MethodReturnTypeProvider extends CompletionProvider<CompletionParameters> {
protected static final ElementPattern<PsiElement> IN_METHOD_RETURN_TYPE =
psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMethod.class)
.andNot(JavaCompletionData.AFTER_DOT);
.andNot(JavaKeywordCompletion.AFTER_DOT);
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
@@ -130,7 +130,7 @@ public class ModifierChooser {
PsiElement prev = FilterPositionUtil.searchNonSpaceNonCommentBack(element);
if (parent instanceof PsiJavaFile || parent instanceof PsiClass) {
if (prev == null || JavaCompletionData.END_OF_BLOCK.getValue().isAcceptable(element, prev.getParent())) {
if (prev == null || JavaKeywordCompletion.END_OF_BLOCK.getValue().isAcceptable(element, prev.getParent())) {
return true;
}
}
@@ -125,7 +125,7 @@ public class ReferenceExpressionCompletionContributor {
public static Runnable fillCompletionVariants(final JavaSmartCompletionParameters parameters, final Consumer<LookupElement> result) {
final PsiElement element = parameters.getPosition();
if (JavaSmartCompletionContributor.INSIDE_TYPECAST_EXPRESSION.accepts(element)) return null;
if (JavaCompletionData.isAfterPrimitiveOrArrayType(element)) return null;
if (JavaKeywordCompletion.isAfterPrimitiveOrArrayType(element)) return null;
final int offset = parameters.getParameters().getOffset();
final PsiReference reference = element.getContainingFile().findReferenceAt(offset);
@@ -23,9 +23,9 @@ import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.TailTypeDecorator;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -177,20 +177,12 @@ public class SmartCompletionDecorator extends TailTypeDecorator<LookupElement> {
return false;
}
public static PsiSubstitutor calculateMethodReturnTypeSubstitutor(PsiMethod method, final PsiType expected) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
final PsiTypeParameter[] typeParameters = method.getTypeParameters();
for (PsiTypeParameter typeParameter : typeParameters) {
PsiType substitution = helper.getSubstitutionForTypeParameter(typeParameter, method.getReturnType(), expected,
false, PsiUtil.getLanguageLevel(method));
if (PsiType.NULL.equals(substitution)) {
substitution = TypeConversionUtil.typeParameterErasure(typeParameter);
}
public static PsiSubstitutor calculateMethodReturnTypeSubstitutor(@NotNull PsiMethod method, @NotNull final PsiType expected) {
PsiType returnType = method.getReturnType();
if (returnType == null) return PsiSubstitutor.EMPTY;
substitutor = substitutor.put(typeParameter, substitution);
}
return substitutor;
PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
return helper.inferTypeArguments(method.getTypeParameters(), new PsiType[]{expected}, new PsiType[]{returnType}, LanguageLevel.HIGHEST);
}
@Nullable
@@ -16,18 +16,20 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.openapi.roots.ExternalLibraryDescriptor;
import com.intellij.codeInsight.daemon.quickFix.ExternalLibraryResolver;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.module.EffectiveLanguageLevelUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ExternalLibraryDescriptor;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.ThreeState;
import org.intellij.lang.annotations.Flow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -39,17 +41,34 @@ public class JetBrainsAnnotationsExternalLibraryResolver extends ExternalLibrary
@NotNull
@Override
public List<String> getLibraryClassesRoots() {
return Collections.singletonList(FileUtil.toSystemIndependentName(new File(PathManager.getLibPath(), "annotations.jar").getAbsolutePath()));
File annotationsJar = new File(PathManager.getLibPath(), "annotations.jar");
if (annotationsJar.exists()) {
return Collections.singletonList(FileUtil.toSystemIndependentName(annotationsJar.getAbsolutePath()));
}
return getPathsToAnnotationsDirectoriesInDevelopmentMode("annotations");
}
};
private static final ExternalLibraryDescriptor JAVA8 = new JetBrainsAnnotationsLibraryDescriptor() {
@NotNull
@Override
public List<String> getLibraryClassesRoots() {
return Collections.singletonList(FileUtil.toSystemIndependentName(new File(PathManager.getHomePath(), "redist/annotations-java8.jar").getAbsolutePath()));
File annotationsJar = new File(PathManager.getHomePath(), "redist/annotations-java8.jar");
if (annotationsJar.exists()) {
return Collections.singletonList(FileUtil.toSystemIndependentName(annotationsJar.getAbsolutePath()));
}
return getPathsToAnnotationsDirectoriesInDevelopmentMode("annotations-java8");
}
};
@NotNull
private static List<String> getPathsToAnnotationsDirectoriesInDevelopmentMode(final String moduleName) {
final String annotationsRoot = PathManager.getJarPathForClass(Flow.class);
if (annotationsRoot == null) return Collections.emptyList();
return Arrays.asList(annotationsRoot, FileUtil.toSystemIndependentName(new File(new File(annotationsRoot).getParentFile(),
moduleName).getAbsolutePath()));
}
@Nullable
@Override
public ExternalClassResolveResult resolveClass(@NotNull String shortClassName, @NotNull ThreeState isAnnotation, @NotNull Module contextModule) {
@@ -467,8 +467,6 @@ public class JavaDocInfoGenerator {
}
private void generateTypeParametersSection(final StringBuilder buffer, final PsiClass aClass) {
final PsiDocComment docComment = aClass.getDocComment();
if (docComment == null) return;
final LinkedList<Pair<PsiDocTag, InheritDocProvider<PsiDocTag>>> result =
new LinkedList<Pair<PsiDocTag, InheritDocProvider<PsiDocTag>>>();
final PsiTypeParameter[] typeParameters = aClass.getTypeParameters();
@@ -693,11 +691,9 @@ public class JavaDocInfoGenerator {
try {
final Document document = JDOMUtil.loadDocument(new ByteArrayInputStream(htmlText.getBytes(CharsetToolkit.UTF8_CHARSET)));
final Element rootTag = document.getRootElement();
if (rootTag != null) {
final Element subTag = rootTag.getChild("body");
if (subTag != null) {
htmlText = subTag.getValue();
}
final Element subTag = rootTag.getChild("body");
if (subTag != null) {
htmlText = subTag.getValue();
}
}
catch (JDOMException ignore) {}
@@ -1107,6 +1103,7 @@ public class JavaDocInfoGenerator {
}
private PsiDocComment loadSyntheticDocComment(final PsiMethod method, final String resourceName) {
//noinspection IOResourceOpenedButNotSafelyClosed
final InputStream commentStream = JavaDocInfoGenerator.class.getResourceAsStream(resourceName);
if (commentStream == null) {
return null;
@@ -1134,7 +1131,9 @@ public class JavaDocInfoGenerator {
}
String s = buffer.toString();
s = StringUtil.replace(s, "<ClassName>", method.getContainingClass().getName());
PsiClass containingClass = method.getContainingClass();
assert containingClass != null;
s = StringUtil.replace(s, "<ClassName>", containingClass.getName());
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory();
try {
return elementFactory.createDocCommentFromText(s);
@@ -1298,6 +1297,7 @@ public class JavaDocInfoGenerator {
return "";
}
//noinspection ReplaceAllDot
return "../" + ourNotDot.matcher(qName).replaceAll("").replaceAll(".", "../");
}
@@ -1415,6 +1415,9 @@ public class JavaDocInfoGenerator {
if (myElement instanceof PsiField) valueField = (PsiField) myElement;
}
else {
if (text.indexOf('#') == -1) {
text = "#" + text;
}
PsiElement target = JavaDocUtil.findReferenceTarget(PsiManager.getInstance(myProject), text, myElement);
if (target instanceof PsiField) {
valueField = (PsiField) target;
@@ -2012,7 +2015,7 @@ public class JavaDocInfoGenerator {
buffer.append(separator);
length += 3;
}
length += generateType(buffer, psiType, context, generateLink, useShortNames);
length += generateType(buffer, psiType, context, true, useShortNames);
}
return length;
}
@@ -15,7 +15,7 @@
*/
package com.intellij.codeInsight.template;
import com.intellij.codeInsight.completion.JavaCompletionData;
import com.intellij.codeInsight.completion.JavaKeywordCompletion;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.ide.highlighter.JavaFileHighlighter;
import com.intellij.lang.java.JavaLanguage;
@@ -136,7 +136,7 @@ public abstract class JavaCodeContextType extends TemplateContextType {
return false;
}
if (JavaCompletionData.isInsideParameterList(element)) {
if (JavaKeywordCompletion.isInsideParameterList(element)) {
return false;
}
@@ -167,7 +167,7 @@ public abstract class JavaCodeContextType extends TemplateContextType {
return false;
}
return JavaCompletionData.isSuitableForClass(element) || JavaCompletionData.isInsideParameterList(element);
return JavaKeywordCompletion.isSuitableForClass(element) || JavaKeywordCompletion.isInsideParameterList(element);
}
}
@@ -52,7 +52,7 @@ public class MagicCompletionContributor extends CompletionContributor {
PsiElement pos = parameters.getPosition();
MagicConstantInspection.AllowedValues allowedValues = null;
if (JavaCompletionData.AFTER_DOT.accepts(pos)) {
if (JavaKeywordCompletion.AFTER_DOT.accepts(pos)) {
return;
}
@@ -20,6 +20,7 @@ import com.intellij.formatting.alignment.AlignmentStrategy;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiSyntheticClass;
import com.intellij.psi.TokenType;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
@@ -122,7 +123,7 @@ public class CodeBlockBlock extends AbstractJavaBlock {
if (child.getElementType() == JavaElementType.SWITCH_LABEL_STATEMENT) {
child = processCaseAndStatementAfter(result, child, childAlignment, childWrap, indent);
}
else if (myNode.getElementType() == JavaElementType.CLASS && child.getElementType() == JavaTokenType.LBRACE) {
else if (myNode.getPsi() instanceof PsiClass && child.getElementType() == JavaTokenType.LBRACE) {
child = composeCodeBlock(result, child, getCodeBlockExternalIndent(), myChildrenIndent, null);
}
else if (myNode.getElementType() == JavaElementType.CODE_BLOCK && child.getElementType() == JavaTokenType.LBRACE
@@ -23,8 +23,6 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.AnonymousTargetClassPreselectionUtil;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
import com.intellij.codeInsight.intention.impl.AddNullableNotNullAnnotationFix;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.codeInspection.dataFlow.*;
@@ -1722,7 +1720,7 @@ public class ExtractMethodProcessor implements MatchProvider {
if (myExtractedMethod != null) {
final ExtractMethodSignatureSuggester suggester = new ExtractMethodSignatureSuggester(myProject, myExtractedMethod, myMethodCall, myVariableDatum);
duplicates = suggester.getDuplicates(myExtractedMethod, myMethodCall);
duplicates = suggester.getDuplicates(myExtractedMethod, myMethodCall, myInputVariables.getFolding());
if (duplicates != null && !duplicates.isEmpty()) {
myDuplicates = duplicates;
myExtractedMethod = suggester.getExtractedMethod();
@@ -89,8 +89,8 @@ public class ExtractMethodSignatureSuggester {
myVariableData = variableDatum;
}
public List<Match> getDuplicates(final PsiMethod method, final PsiMethodCallExpression methodCall) {
final List<Match> duplicates = findDuplicatesSignature(method);
public List<Match> getDuplicates(final PsiMethod method, final PsiMethodCallExpression methodCall, ParametersFolder folder) {
final List<Match> duplicates = findDuplicatesSignature(method, folder);
if (duplicates != null && !duplicates.isEmpty()) {
if (ApplicationManager.getApplication().isUnitTestMode() ||
new PreviewDialog(method, myExtractedMethod, methodCall, myMethodCall, duplicates.size()).showAndGet()) {
@@ -127,7 +127,7 @@ public class ExtractMethodSignatureSuggester {
}
@Nullable
public List<Match> findDuplicatesSignature(final PsiMethod method) {
public List<Match> findDuplicatesSignature(final PsiMethod method, ParametersFolder folder) {
final List<PsiExpression> copies = new ArrayList<PsiExpression>();
final InputVariables variables = detectTopLevelExpressionsToReplaceWithParameters(copies);
if (variables == null) {
@@ -148,7 +148,7 @@ public class ExtractMethodSignatureSuggester {
List<Match> duplicates = finder.findDuplicates(method.getContainingClass());
if (duplicates != null && !duplicates.isEmpty()) {
restoreRenamedParams(copies);
restoreRenamedParams(copies, folder);
if (!myMethodCall.isValid()) {
return null;
}
@@ -255,17 +255,18 @@ public class ExtractMethodSignatureSuggester {
return true;
}
private void restoreRenamedParams(List<PsiExpression> copies) {
final Map<String, PsiVariable> renameMap = new HashMap<String, PsiVariable>();
private void restoreRenamedParams(List<PsiExpression> copies, ParametersFolder folder) {
final Map<String, String> renameMap = new HashMap<String, String>();
for (VariableData data : myVariableData) {
if (!data.name.equals(data.variable.getName())) {
renameMap.put(data.name, data.variable);
final String replacement = folder.getGeneratedCallArgument(data);
if (!data.name.equals(replacement)) {
renameMap.put(data.name, replacement);
}
}
if (!renameMap.isEmpty()) {
for (PsiExpression currentExpression : copies) {
final Map<PsiReferenceExpression, PsiVariable> params = new HashMap<PsiReferenceExpression, PsiVariable>();
final Map<PsiReferenceExpression, String> params = new HashMap<PsiReferenceExpression, String>();
currentExpression.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
@@ -273,7 +274,7 @@ public class ExtractMethodSignatureSuggester {
final PsiElement resolve = expression.resolve();
if (resolve instanceof PsiParameter && myExtractedMethod.equals(((PsiParameter)resolve).getDeclarationScope())) {
final String name = ((PsiParameter)resolve).getName();
final PsiVariable variable = renameMap.get(name);
final String variable = renameMap.get(name);
if (renameMap.containsKey(name)) {
params.put(expression, variable);
}
@@ -281,8 +282,8 @@ public class ExtractMethodSignatureSuggester {
}
});
for (PsiReferenceExpression expression : params.keySet()) {
final PsiVariable var = params.get(expression);
expression.replace(myElementFactory.createExpressionFromText(var.getName(), expression));
final String var = params.get(expression);
expression.replace(myElementFactory.createExpressionFromText(var, expression));
}
}
}
@@ -128,8 +128,7 @@ public class InplaceIntroduceParameterPopup extends AbstractJavaInplaceIntroduce
@Override
protected PsiElement checkLocalScope() {
final PsiVariable variable = getLocalVariable();
return variable == null ? myMethod : PsiTreeUtil.getParentOfType(variable, PsiMethod.class);
return myMethod;
}
@Override
@@ -502,7 +502,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
public boolean process(final PsiReference reference) {
final PsiElement element = reference.getElement();
if (!isInside(element, allElementsToDelete)) {
if (!isInside(element, allElementsToDelete) && element instanceof PsiJavaCodeReferenceElement) {
PsiElement parent = element.getParent();
if (parent instanceof PsiReferenceList) {
final PsiElement pparent = parent.getParent();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -137,11 +137,11 @@ public class JavadocParser {
else if (TAG_VALUES_SET.contains(tokenType)) {
if (SEE_TAG.equals(tagName) && !isInline ||
LINK_TAG.equals(tagName) && isInline) {
parseSeeTagValue(builder);
parseSeeTagValue(builder, false);
}
else {
if (JavaParserUtil.getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_4) && LINK_PLAIN_TAG.equals(tagName) && isInline) {
parseSeeTagValue(builder);
parseSeeTagValue(builder, false);
}
else if (!isInline && (THROWS_TAG.equals(tagName) || EXCEPTION_TAG.equals(tagName))) {
final PsiBuilder.Marker tagValue = builder.mark();
@@ -154,7 +154,7 @@ public class JavadocParser {
}
else {
if (JavaParserUtil.getLanguageLevel(builder).isAtLeast(LanguageLevel.JDK_1_5) && VALUE_TAG.equals(tagName) && isInline) {
parseSeeTagValue(builder);
parseSeeTagValue(builder, true);
}
else {
parseSimpleTagValue(builder, false);
@@ -167,7 +167,7 @@ public class JavadocParser {
}
}
private static void parseSeeTagValue(@NotNull final PsiBuilder builder) {
private static void parseSeeTagValue(@NotNull final PsiBuilder builder, boolean allowBareFieldReference) {
final IElementType tokenType = getTokenType(builder);
if (tokenType == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN) {
parseMethodRef(builder, builder.mark());
@@ -180,6 +180,11 @@ public class JavadocParser {
if (getTokenType(builder) == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN) {
parseMethodRef(builder, refStart);
}
else if (allowBareFieldReference) {
refStart.rollbackTo();
builder.remapCurrentToken(JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
parseMethodRef(builder, builder.mark());
}
else {
refStart.drop();
}
@@ -192,8 +197,9 @@ public class JavadocParser {
}
private static void parseMethodRef(@NotNull final PsiBuilder builder, @NotNull final PsiBuilder.Marker refStart) {
builder.advanceLexer();
if (getTokenType(builder) == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN) {
builder.advanceLexer();
}
if (getTokenType(builder) != JavaDocTokenType.DOC_TAG_VALUE_TOKEN) {
refStart.done(JavaDocElementType.DOC_METHOD_OR_FIELD_REF);
return;
@@ -29,7 +29,7 @@ public class LightClassReference extends LightElement implements PsiJavaCodeRefe
private final String myText;
private final String myClassName;
private final PsiElement myContext;
private final GlobalSearchScope myResolveScope;
@NotNull private final GlobalSearchScope myResolveScope;
private final PsiClass myRefClass;
private final PsiSubstitutor mySubstitutor;
@@ -50,14 +50,13 @@ public class LightClassReference extends LightElement implements PsiJavaCodeRefe
this (manager, text, className, null, resolveScope);
}
public LightClassReference(@NotNull PsiManager manager, @NotNull @NonNls String text, @NotNull @NonNls String className, PsiSubstitutor substitutor, PsiElement context) {
public LightClassReference(@NotNull PsiManager manager, @NotNull @NonNls String text, @NotNull @NonNls String className, PsiSubstitutor substitutor, @NotNull PsiElement context) {
super(manager, JavaLanguage.INSTANCE);
myText = text;
myClassName = className;
mySubstitutor = substitutor;
myContext = context;
myResolveScope = null;
myResolveScope = context.getResolveScope();
myRefClass = null;
}
@@ -69,8 +68,7 @@ public class LightClassReference extends LightElement implements PsiJavaCodeRefe
super(manager, JavaLanguage.INSTANCE);
myText = text;
myRefClass = refClass;
myResolveScope = null;
myResolveScope = refClass.getResolveScope();
myClassName = null;
myContext = null;
mySubstitutor = substitutor;
@@ -179,11 +177,11 @@ public class LightClassReference extends LightElement implements PsiJavaCodeRefe
@NotNull
public String getCanonicalText() {
String name = getQualifiedName();
if (name == null) return null;
if (name == null) return "";
PsiType[] types = getTypeParameters();
if (types.length == 0) return name;
StringBuffer buf = new StringBuffer();
StringBuilder buf = new StringBuilder();
buf.append(name);
buf.append('<');
for (int i = 0; i < types.length; i++) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -155,8 +155,8 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
@Nullable
public PsiElement getNameElement() {
final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN);
return sharp != null ? SourceTreeToPsiMap.treeToPsiNotNull(sharp).getNextSibling() : null;
final ASTNode name = findChildByType(DOC_TAG_VALUE_TOKEN);
return name != null ? SourceTreeToPsiMap.treeToPsiNotNull(name) : null;
}
@Nullable
@@ -174,11 +174,9 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child.getNode().getElementType() == DOC_TYPE_HOLDER) {
final String[] typeStrings = child.getText().split("[, ]"); //avoid param types list parsing hmm method(paramType1, paramType2, ...) -> typeElement1, identifier2, ...
if (typeStrings != null) {
for (String type : typeStrings) {
if (!type.isEmpty()) {
types.add(type);
}
for (String type : typeStrings) {
if (!type.isEmpty()) {
types.add(type);
}
}
}
@@ -89,11 +89,14 @@ public class ComparisonFailureData {
if (actualFilePath != null) {
attrs.put("actualFile", actualFilePath);
}
final int expectedIdx = trace.indexOf("expected");
final int expectedIdx = trace.indexOf("expected:<");
final String comparisonFailureMessage;
if (expectedIdx > 0) {
comparisonFailureMessage = trace.substring(0, expectedIdx);
}
else if (failureIdx > -1) {
comparisonFailureMessage = trace.substring(0, failureIdx + failureMessageLength);
}
else {
comparisonFailureMessage = (failureMessageLength > 0 ? failureMessage + "\n" : "") + "Comparison Failure: ";
}
@@ -109,7 +112,8 @@ public class ComparisonFailureData {
if (!isAssertionError(throwable.getClass()) && !isAssertionError(throwableCause != null ? throwableCause.getClass() : null)) {
attrs.put("error", "true");
}
attrs.put("message", failureMessage != null ? failureMessage : "");
attrs.put("message", failureIdx > -1 ? trace.substring(0, failureIdx + failureMessageLength)
: failureMessage != null ? failureMessage : "");
}
}
@@ -0,0 +1,6 @@
public class A {
public void method() {
if (true) { foo(); }
<caret>
}
}
@@ -0,0 +1,6 @@
public class A {
public void method() {
if (true) foo();
<caret>
}
}
@@ -0,0 +1 @@
import <caret>
@@ -14,5 +14,5 @@
* limitations under the License.
*/
public class A{
public void method() <caret> {}
public void method() <caret>x {}
}
@@ -0,0 +1,26 @@
import java.lang.Exception;
/*
* Copyright 2000-2012 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.
*/
public class A{
public void method(){
try{
String str = "";
}
catch (Exception e) {}
<caret>
}
}
@@ -0,0 +1,16 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Foo {
Map<String, Integer> foo() {
return Bar.new<caret>
}
}
class Bar {
static <T, V> HashMap<T, V> newMap() {}
static <E> ArrayList<E> newList() {}
}
@@ -0,0 +1,9 @@
import java.util.Collections;
import java.util.Map;
class Foo {
void m() {
Map<String, Integer> m = Collections.emptyMap();<caret>
}
}
@@ -0,0 +1,8 @@
import java.util.Map;
class Foo {
void m() {
Map<String, Integer> m = emptyM<caret>
}
}
@@ -0,0 +1 @@
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://A"><code>A</code></a></b></small><PRE>public static&nbsp;void&nbsp;<b>JAVADOC_ME</b>()</PRE> The value of A is 23.</body></html>
@@ -0,0 +1,6 @@
class A {
public static int A = 23;
/** The value of A is {@value A}. */
public static void JAVADOC_ME() { }
}
@@ -0,0 +1,4 @@
/**
* {@value some.class#FIELD}
*/
class Test {}
@@ -0,0 +1,46 @@
PsiJavaFile:ValueQualified.java
PsiImportList
<empty list>
PsiClass:Test
PsiDocComment
PsiDocToken:DOC_COMMENT_START('/**')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('*')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiInlineDocTag:@value
PsiDocToken:DOC_INLINE_TAG_START('{')
PsiDocToken:DOC_TAG_NAME('@value')
PsiWhiteSpace(' ')
PsiElement(DOC_METHOD_OR_FIELD_REF)
PsiElement(DOC_REFERENCE_HOLDER)
PsiJavaCodeReferenceElement:some.
PsiJavaCodeReferenceElement:some
PsiIdentifier:some('some')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiErrorElement:Identifier expected
<empty list>
PsiReferenceParameterList
<empty list>
PsiKeyword:class('class')
PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#')
PsiDocToken:DOC_TAG_VALUE_TOKEN('FIELD')
PsiDocToken:DOC_INLINE_TAG_END('}')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_END('*/')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:Test('Test')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,4 @@
/**
* {@value #FIELD}
*/
class Test {}
@@ -0,0 +1,34 @@
PsiJavaFile:ValueUnqualifiedWithHash.java
PsiImportList
<empty list>
PsiClass:Test
PsiDocComment
PsiDocToken:DOC_COMMENT_START('/**')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('*')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiInlineDocTag:@value
PsiDocToken:DOC_INLINE_TAG_START('{')
PsiDocToken:DOC_TAG_NAME('@value')
PsiWhiteSpace(' ')
PsiElement(DOC_METHOD_OR_FIELD_REF)
PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#')
PsiDocToken:DOC_TAG_VALUE_TOKEN('FIELD')
PsiDocToken:DOC_INLINE_TAG_END('}')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_END('*/')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:Test('Test')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,4 @@
/**
* {@value FIELD}
*/
class Test {}
@@ -0,0 +1,33 @@
PsiJavaFile:ValueUnqualifiedWithoutHash.java
PsiImportList
<empty list>
PsiClass:Test
PsiDocComment
PsiDocToken:DOC_COMMENT_START('/**')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_LEADING_ASTERISKS('*')
PsiDocToken:DOC_COMMENT_DATA(' ')
PsiInlineDocTag:@value
PsiDocToken:DOC_INLINE_TAG_START('{')
PsiDocToken:DOC_TAG_NAME('@value')
PsiWhiteSpace(' ')
PsiElement(DOC_METHOD_OR_FIELD_REF)
PsiDocToken:DOC_TAG_VALUE_TOKEN('FIELD')
PsiDocToken:DOC_INLINE_TAG_END('}')
PsiWhiteSpace('\n ')
PsiDocToken:DOC_COMMENT_END('*/')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:Test('Test')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,6 @@
class Test {
public static void main(String[] args, int i) {
System.out.println("hi");
<selection>System.out.println("world, " + args[i]);</selection>
}
}
@@ -0,0 +1,10 @@
class Test {
public static void main(String[] args, int i) {
newMethod("hi");
newMethod("world, " + args[i]);
}
private static void newMethod(String x) {
System.out.println(x);
}
}
@@ -0,0 +1,13 @@
class Abc {
void foo() {
new Runnable(){
@Override
public void run() {
final String na<caret>me = "name";
System.out.println(name);
}
};
}
}
@@ -0,0 +1,12 @@
class Abc {
void foo(final String name) {
new Runnable(){
@Override
public void run() {
System.out.println(name);
}
};
}
}
@@ -76,6 +76,8 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
public void testMethodScope3() throws Exception { doTest(1, "final", "public", "static", "volatile", "abstract", "throws", "instanceof"); }
public void testMethodScope4() throws Exception { doTest(6, "final", "try", "for", "while", "return", "throw"); }
public void testMethodScope5() throws Exception { doTest(false); }
public void testElseAfterSemicolon() throws Exception { doTest(1, "else"); }
public void testElseAfterRBrace() throws Exception { doTest(1, "else"); }
public void testExtraBracketAfterFinally1() throws Exception { doTest(false); }
public void testExtraBracketAfterFinally2() throws Exception { doTest(false); }
public void testExtendsInCastTypeParameters() throws Exception { doTest(false); }
@@ -89,6 +91,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
public void testInstanceOf2() throws Exception { doTest(false); }
public void testInstanceOf3() throws Exception { doTest(false); }
public void testCatchFinally() throws Exception { doTest(2, "catch", "finally"); }
public void testSecondCatch() throws Exception { doTest(2, "catch", "finally"); }
public void testSuper1() throws Exception { doTest(1, "super"); }
public void testSuper2() throws Exception { doTest(0, "super"); }
public void testSuper3() throws Exception { doTest(true); }
@@ -98,7 +101,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
public void testDefaultInAnno() throws Exception { doTest(false); }
public void testNullInMethodCall() throws Exception { doTest(false); }
public void testNullInMethodCall2() throws Exception { doTest(false); }
public void testNewInMethodRefs() throws Exception { doTest(1, "new"); }
public void testNewInMethodRefs() throws Exception { doTest(1, "new", "null", "true", "false"); }
public void testSpaceAfterInstanceof() throws Exception { doTest(false); }
public void testInstanceofAfterUnresolved() throws Exception { doTest(1, "instanceof"); }
public void testInstanceofAfterStatementStart() throws Exception { doTest(1, "instanceof"); }
@@ -109,6 +112,7 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
checkResultByFile(BASE_PATH + "/" + getTestName(true) + "_after.java");
}
public void testImportStatic() throws Exception { doTest(1, "static"); }
public void testAbstractInInterface() throws Exception { doTest(1, "abstract"); }
public void testCharInAnnotatedParameter() throws Exception { doTest(1, "char"); }
public void testReturnInTernary() throws Exception { doTest(1, "return"); }
@@ -658,6 +658,10 @@ interface TxANotAnno {}
assertPreferredItems 0, 'newLinkedSet1', 'newLinkedSet0', 'newLinkedSet2'
}
public void testStaticMemberTypes() {
checkPreferredItems 0, 'newMap', 'newList'
}
public void testNoStatsInSuperInvocation() {
checkPreferredItems 0, 'put', 'putAll'
@@ -150,6 +150,8 @@ public void testConvertToObjectStream() {
doTest(false);
}
public void testCollectionsEmptyMap() { doTest(true); }
private void doTest() {
doTest(true);
}
@@ -48,6 +48,10 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase {
doTestMethod();
}
public void testValueInMethodNoHash() throws Exception {
doTestMethod();
}
public void testIdeadev2326() throws Exception {
doTestMethod();
}
@@ -256,7 +260,7 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase {
PsiDirectory dir = (PsiDirectory)psiClass.getParent().getParent();
PsiFile htmlFile = dir.findFile(psiClass.getName() + ".html");
assertNotNull(htmlFile);
assertEquals(StringUtil.convertLineSeparators(new String(htmlFile.getVirtualFile().contentsToByteArray()).trim()),
assertEquals(StringUtil.convertLineSeparators(new String(htmlFile.getVirtualFile().contentsToByteArray(), "UTF-8").trim()),
replaceEnvironmentDependentContent(doc));
}
@@ -87,4 +87,8 @@ public class JavadocParsingTest extends JavaParsingTestCase {
public void testLiteralTag() { doTest(true); }
public void testIDEADEV_41403() { doTest(true); }
public void testValueQualified() { doTest(true); }
public void testValueUnqualifiedWithHash() { doTest(true); }
public void testValueUnqualifiedWithoutHash() { doTest(true); }
}
@@ -330,6 +330,35 @@ public class JavaFormatterBracesTest extends AbstractJavaFormatterTest {
);
}
public void testAnonClassCodeBlock_BracesIndented() {
getSettings().CLASS_BRACE_STYLE = CommonCodeStyleSettings.NEXT_LINE_SHIFTED;
doTextTest(
"class X {\n" +
" public void run() {\n" +
" Runnable a = new Runnable() {\n" +
" @Override\n" +
" public void run() {\n" +
" \n" +
" }\n" +
" };\n" +
" }\n" +
"}",
"class X\n" +
" {\n" +
" public void run() {\n" +
" Runnable a = new Runnable()\n" +
" {\n" +
" @Override\n" +
" public void run() {\n" +
"\n" +
" }\n" +
" };\n" +
" }\n" +
" }"
);
}
public void testMethodIsSimple_IfCodeBlockHasNoLinefeeds() {
getSettings().KEEP_SIMPLE_METHODS_IN_ONE_LINE = true;
doClassTest(
@@ -27,6 +27,7 @@ import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.event.EditorEventMulticaster;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
@@ -670,23 +671,87 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
assertNotNull(node);
}
public void testLargeFileWithManyChanges() {
public void testLargeFileWithManyChangesPerformance() {
configureByText(PlainTextFileType.INSTANCE, StringUtil.repeat("foo foo \n", 50000));
final TextRange range = TextRange.from(10, 10);
final SmartPsiFileRange pointer = SmartPointerManager.getInstance(myProject).createSmartPsiFileRangePointer(myFile, range);
final Document document = myFile.getViewProvider().getDocument();
assertNotNull(document);
for (int i = 0; i < 10000; i++) {
document.insertString(i * 20 + 100, "x\n");
assertFalse(PsiDocumentManager.getInstance(myProject).isCommitted(document));
if (i % 500 == 0) {
PlatformTestUtil.startPerformanceTest("smart pointer range update", 25000, () -> {
for (int i = 0; i < 10000; i++) {
document.insertString(i * 20 + 100, "x\n");
assertFalse(PsiDocumentManager.getInstance(myProject).isCommitted(document));
assertEquals(range, pointer.getRange());
}
}
}).cpuBound().assertTiming();
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertEquals(range, pointer.getRange());
}
public void testConvergingRanges() {
configureByText(PlainTextFileType.INSTANCE, "aba");
final Document document = myFile.getViewProvider().getDocument();
assertNotNull(document);
SmartPsiFileRange range1 = SmartPointerManager.getInstance(myProject).createSmartPsiFileRangePointer(myFile, TextRange.create(0, 2));
SmartPsiFileRange range2 = SmartPointerManager.getInstance(myProject).createSmartPsiFileRangePointer(myFile, TextRange.create(1, 3));
document.deleteString(0, 1);
document.deleteString(1, 2);
assertEquals(TextRange.create(0, 1), range1.getRange());
assertEquals(TextRange.create(0, 1), range2.getRange());
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertEquals(TextRange.create(0, 1), range1.getRange());
assertEquals(TextRange.create(0, 1), range2.getRange());
document.insertString(0, "a");
assertEquals(TextRange.create(1, 2), range1.getRange());
assertEquals(TextRange.create(1, 2), range2.getRange());
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertEquals(TextRange.create(1, 2), range1.getRange());
assertEquals(TextRange.create(1, 2), range2.getRange());
}
public void testMoveText() {
PsiJavaFile file = (PsiJavaFile)configureByText(JavaFileType.INSTANCE, "class C1{}\nclass C2 {}");
DocumentEx document = (DocumentEx)file.getViewProvider().getDocument();
SmartPsiElementPointer<PsiClass> pointer1 =
SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(file.getClasses()[0]);
SmartPsiElementPointer<PsiClass> pointer2 =
SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(file.getClasses()[1]);
assertEquals("C1", pointer1.getElement().getName());
assertEquals("C2", pointer2.getElement().getName());
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(((SmartPointerEx) pointer1).getCachedElement());
assertNull(((SmartPointerEx) pointer2).getCachedElement());
TextRange range = file.getClasses()[1].getTextRange();
document.moveText(range.getStartOffset(), range.getEndOffset(), 0);
System.out.println(pointer1.getRange());
System.out.println(pointer2.getRange());
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertEquals("C1", pointer1.getElement().getName());
assertEquals("C2", pointer2.getElement().getName());
}
public void testNonPhysicalFile() {
PsiJavaFile file = (PsiJavaFile)myJavaFacade.findClass("AClass", GlobalSearchScope.allScope(getProject())).getContainingFile().copy();
SmartPsiFileRange pointer = SmartPointerManager.getInstance(myProject).createSmartPsiFileRangePointer(file, TextRange.create(1, 2));
file.getViewProvider().getDocument().insertString(0, " ");
assertEquals(TextRange.create(2, 3), pointer.getRange());
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertEquals(TextRange.create(2, 3), pointer.getRange());
}
}
@@ -672,6 +672,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doDuplicatesTest();
}
public void testSuggestChangeSignatureWithFolding() throws Exception {
doDuplicatesTest();
}
public void testSuggestChangeSignatureWithChangedParameterName() throws Exception {
configureByFile(BASE_PATH + getTestName(false) + ".java");
boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, false, "p");
@@ -15,6 +15,8 @@
*/
package com.intellij.refactoring;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pass;
@@ -22,6 +24,7 @@ import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler;
import com.intellij.testFramework.LightPlatformTestCase;
import org.jetbrains.annotations.NotNull;
/**
@@ -122,6 +125,34 @@ public class InplaceIntroduceParameterTest extends AbstractJavaInplaceIntroduceT
});
}
public void testLocalInsideAnonymous1() throws Exception {
final Pass<AbstractInplaceIntroducer> pass = new Pass<AbstractInplaceIntroducer>() {
@Override
public void pass(AbstractInplaceIntroducer inplaceIntroducePopup) {
}
};
String name = getTestName(true);
configureByFile(getBasePath() + name + getExtension());
final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
try {
TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
getEditor().getSettings().setVariableInplaceRenameEnabled(true);
//ensure extract local var
final MyIntroduceHandler introduceHandler = createIntroduceHandler();
introduceHandler.invokeImpl(LightPlatformTestCase.getProject(), getLocalVariableFromEditor(), getEditor());
final AbstractInplaceIntroducer introducer = introduceHandler.getInplaceIntroducer();
pass.pass(introducer);
TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
assert state != null;
state.gotoEnd(false);
checkResultByFile(getBasePath() + name + "_after" + getExtension());
}
finally {
getEditor().getSettings().setVariableInplaceRenameEnabled(enabled);
}
}
@Override
protected String getBasePath() {
return BASE_PATH;
@@ -24,7 +24,7 @@ import com.intellij.openapi.components.StateStorageOperation
import com.intellij.openapi.components.StoragePathMacros
import com.intellij.openapi.components.impl.BasePathMacroManager
import com.intellij.openapi.components.impl.ServiceManagerImpl
import com.intellij.openapi.components.impl.stores.DirectoryStorageData
import com.intellij.openapi.components.impl.stores.StateMap
import com.intellij.openapi.util.NamedJDOMExternalizable
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
@@ -35,21 +35,21 @@ class ApplicationStoreImpl(private val application: ApplicationImpl, pathMacroMa
override val storageManager = object : StateStorageManagerImpl("application", pathMacroManager.createTrackingSubstitutor(), application) {
override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String? {
if (component is NamedJDOMExternalizable) {
return "${StoragePathMacros.APP_CONFIG}/${component.getExternalFileName()}${DirectoryStorageData.DEFAULT_EXT}"
return "${StoragePathMacros.APP_CONFIG}/${component.getExternalFileName()}${StateMap.DEFAULT_EXT}"
}
else {
return DEFAULT_STORAGE_SPEC
}
}
override fun getMacroSubstitutor(fileSpec: String) = if (fileSpec == "${StoragePathMacros.APP_CONFIG}/${PathMacrosImpl.EXT_FILE_NAME}${DirectoryStorageData.DEFAULT_EXT}") null else super.getMacroSubstitutor(fileSpec)
override fun getMacroSubstitutor(fileSpec: String) = if (fileSpec == "${StoragePathMacros.APP_CONFIG}/${PathMacrosImpl.EXT_FILE_NAME}${StateMap.DEFAULT_EXT}") null else super.getMacroSubstitutor(fileSpec)
override protected val isUseXmlProlog: Boolean
get() = false
}
companion object {
private val DEFAULT_STORAGE_SPEC = "${StoragePathMacros.APP_CONFIG}/${PathManager.DEFAULT_OPTIONS_FILE_NAME}${DirectoryStorageData.DEFAULT_EXT}"
private val DEFAULT_STORAGE_SPEC = "${StoragePathMacros.APP_CONFIG}/${PathManager.DEFAULT_OPTIONS_FILE_NAME}${StateMap.DEFAULT_EXT}"
private val FILE_STORAGE_DIR = "options"
}
@@ -17,6 +17,7 @@ package com.intellij.configurationStore
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.*
import com.intellij.openapi.components.impl.stores.StateMap
import com.intellij.openapi.components.impl.stores.StateStorageManager
import com.intellij.openapi.components.impl.stores.StreamProvider
import com.intellij.openapi.project.Project
@@ -49,7 +50,7 @@ class DefaultProjectStoreImpl(override val project: ProjectImpl, private val pat
}
}
override fun createSaveSession(storageData: StorageData) = object : FileBasedStorage.FileSaveSession(storageData, this) {
override fun createSaveSession(states: StateMap) = object : FileBasedStorage.FileSaveSession(states, this) {
override fun saveLocally(element: Element?) {
super.saveLocally(Element("application").addContent(Element("component").setAttribute("name", "ProjectManager").addContent(element)))
}
@@ -96,11 +97,11 @@ class DefaultProjectStoreImpl(override val project: ProjectImpl, private val pat
private class MyExternalizationSession(val externalizationSession: StateStorage.ExternalizationSession) : StateStorageManager.ExternalizationSession {
override fun setState(storageSpecs: Array<Storage>, component: Any, componentName: String, state: Any) {
externalizationSession.setState(component, componentName, state, null)
externalizationSession.setState(component, componentName, state)
}
override fun setStateInOldStorage(component: Any, componentName: String, state: Any) {
externalizationSession.setState(component, componentName, state, null)
externalizationSession.setState(component, componentName, state)
}
override fun createSaveSessions() = ContainerUtil.createMaybeSingletonList(externalizationSession.createSaveSession())
@@ -22,22 +22,21 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.StoragePathMacros
import com.intellij.openapi.components.TrackingPathMacroSubstitutor
import com.intellij.openapi.components.impl.stores.*
import com.intellij.openapi.components.impl.stores.FileStorage
import com.intellij.openapi.components.impl.stores.StateMap
import com.intellij.openapi.components.impl.stores.StorageUtil
import com.intellij.openapi.components.impl.stores.StreamProvider
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.util.io.systemIndependentPath
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.LineSeparator
import org.jdom.Element
import org.jdom.JDOMException
import java.io.File
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.CharBuffer
open class FileBasedStorage(private volatile var file: File,
fileSpec: String,
@@ -65,9 +64,9 @@ open class FileBasedStorage(private volatile var file: File,
}
}
override fun createSaveSession(storageData: StorageData) = FileSaveSession(storageData, this)
override fun createSaveSession(states: StateMap) = FileSaveSession(states, this)
protected open class FileSaveSession(storageData: StorageData, storage: FileBasedStorage) : XmlElementStorage.XmlElementStorageSaveSession<FileBasedStorage>(storageData, storage) {
protected open class FileSaveSession(storageData: StateMap, storage: FileBasedStorage) : XmlElementStorage.XmlElementStorageSaveSession<FileBasedStorage>(storageData, storage) {
override fun save() {
if (!storage.blockSavingTheContent) {
super.save()
@@ -329,14 +329,14 @@ open class StateStorageManagerImpl(private val rootTagName: String,
continue
}
getExternalizationSession(storageManager.getStateStorage(storageSpec))?.setState(component, componentName, if (storageSpec.deprecated || resolution === Resolution.CLEAR) Element("empty") else state, storageSpec)
getExternalizationSession(storageManager.getStateStorage(storageSpec))?.setState(component, componentName, if (storageSpec.deprecated || resolution === Resolution.CLEAR) Element("empty") else state)
}
}
override fun setStateInOldStorage(component: Any, componentName: String, state: Any) {
val stateStorage = storageManager.getOldStorage(component, componentName, StateStorageOperation.WRITE)
if (stateStorage != null) {
getExternalizationSession(stateStorage)?.setState(component, componentName, state, null)
getExternalizationSession(stateStorage)?.setState(component, componentName, state)
}
}
@@ -1,164 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.configurationStore
import com.intellij.openapi.components.impl.stores.StateMap
import com.intellij.openapi.components.impl.stores.StateMap.getNewByteIfDiffers
import com.intellij.openapi.components.impl.stores.StorageDataBase
import com.intellij.openapi.util.JDOMUtil
import com.intellij.util.ArrayUtil
import com.intellij.util.containers.SmartHashSet
import org.jdom.Attribute
import org.jdom.Element
import java.util.Arrays
open class StorageData : StorageDataBase {
val states: StateMap
public constructor() {
states = StateMap()
}
protected constructor(storageData: StorageData) {
states = StateMap(storageData.states)
}
public open fun save(newLiveStates: Map<String, Element>, rootElementName: String): Element? {
if (states.isEmpty()) {
return null
}
val rootElement = Element(rootElementName)
val componentNames = ArrayUtil.toStringArray(states.keys())
Arrays.sort(componentNames)
for (componentName in componentNames) {
assert(componentName != null)
val element = states.getElement(componentName, newLiveStates)
// name attribute should be first
val elementAttributes = element.getAttributes()
if (elementAttributes.isEmpty()) {
element.setAttribute(StorageDataBase.NAME, componentName)
}
else {
var nameAttribute: Attribute? = element.getAttribute(StorageDataBase.NAME)
if (nameAttribute == null) {
nameAttribute = Attribute(StorageDataBase.NAME, componentName)
elementAttributes.add(0, nameAttribute)
}
else {
nameAttribute.setValue(componentName)
if (elementAttributes.get(0) != nameAttribute) {
elementAttributes.remove(nameAttribute)
elementAttributes.add(0, nameAttribute)
}
}
}
rootElement.addContent(element)
}
return rootElement
}
open fun clone(): StorageData = StorageData(this)
override fun hasState(componentName: String) = states.hasState(componentName)
}
fun setStateAndCloneIfNeed(componentName: String, newState: Element?, storageData: StorageData, newLiveStates: MutableMap<String, Element>): StorageData? {
val oldState = storageData.states.get(componentName)
if (newState == null || JDOMUtil.isEmpty(newState)) {
if (oldState == null) {
return null
}
val newStorageData = storageData.clone()
newStorageData.states.remove(componentName)
return newStorageData
}
prepareElement(newState)
newLiveStates.put(componentName, newState)
var newBytes: ByteArray? = null
if (oldState is Element) {
if (JDOMUtil.areElementsEqual(oldState as Element?, newState)) {
return null
}
}
else if (oldState != null) {
newBytes = getNewByteIfDiffers(componentName, newState, oldState as ByteArray)
if (newBytes == null) {
return null
}
}
val newStorageData = storageData.clone()
newStorageData.states.put(componentName, if (newBytes == null) newState else newBytes)
return newStorageData
}
fun prepareElement(state: Element) {
if (state.getParent() != null) {
LOG.warn("State element must not have parent ${JDOMUtil.writeElement(state)}")
state.detach()
}
state.setName(StorageDataBase.COMPONENT)
}
fun StateMap.setState(componentName: String, newState: Element?, newLiveStates: MutableMap<String, Element>): Any? {
if (newState == null || JDOMUtil.isEmpty(newState)) {
return remove(componentName)
}
prepareElement(newState)
newLiveStates.put(componentName, newState)
val oldState = get(componentName)
var newBytes: ByteArray? = null
if (oldState is Element) {
if (JDOMUtil.areElementsEqual(oldState as Element?, newState)) {
return null
}
}
else if (oldState != null) {
newBytes = getNewByteIfDiffers(componentName, newState, oldState as ByteArray)
if (newBytes == null) {
return null
}
}
put(componentName, if (newBytes == null) newState else newBytes)
return newState
}
// newStorageData - myStates contains only live (unarchived) states
fun StateMap.getChangedComponentNames(newStorageData: StorageData): Set<String> {
val bothStates = SmartHashSet(keys())
bothStates.retainAll(newStorageData.states.keys())
val diffs = SmartHashSet<String>()
diffs.addAll(newStorageData.states.keys())
diffs.addAll(keys())
diffs.removeAll(bothStates)
for (componentName in bothStates) {
compare(componentName, newStorageData.states, diffs)
}
return diffs
}
@@ -20,33 +20,37 @@ import com.intellij.openapi.components.StateStorage
import com.intellij.openapi.components.TrackingPathMacroSubstitutor
import com.intellij.openapi.components.impl.stores.*
import com.intellij.openapi.util.JDOMUtil
import com.intellij.util.ArrayUtil
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.SmartHashSet
import gnu.trove.THashMap
import org.jdom.Attribute
import org.jdom.Element
import org.jdom.JDOMException
import java.io.IOException
import java.util.Arrays
abstract class XmlElementStorage protected constructor(protected val fileSpec: String,
protected val rootElementName: String,
protected val pathMacroSubstitutor: TrackingPathMacroSubstitutor?,
roamingType: RoamingType?,
provider: StreamProvider?) : StateStorageBase<StorageData>() {
provider: StreamProvider?) : StateStorageBase<StateMap>() {
protected val roamingType: RoamingType = roamingType ?: RoamingType.PER_USER
private val provider: StreamProvider? = if (provider == null || roamingType == RoamingType.DISABLED || !provider.isApplicable(fileSpec, this.roamingType)) null else provider
protected abstract fun loadLocalData(): Element?
override fun getStateAndArchive(storageData: StorageData, component: Any, componentName: String) = storageData.states.getStateAndArchive(componentName)
override fun getStateAndArchive(storageData: StateMap, component: Any, componentName: String) = storageData.getStateAndArchive(componentName)
override fun loadData(): StorageData {
val storageData = StorageData()
override fun loadData(): StateMap {
val states = StateMap()
val element: Element?
// we don't use local data if has stream provider
if (provider != null && provider.enabled) {
try {
element = loadDataFromProvider()
if (element != null) {
storageData.loadState(element)
states.loadState(element)
}
}
catch (e: Exception) {
@@ -59,29 +63,29 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
}
if (element != null) {
storageData.loadState(element)
states.loadState(element)
}
return storageData
return states
}
throws(IOException::class, JDOMException::class)
private fun loadDataFromProvider() = JDOMUtil.load(provider!!.loadContent(fileSpec, roamingType))
private fun StorageData.loadState(element: Element) {
private fun StateMap.loadState(element: Element) {
beforeElementLoaded(element)
StorageDataBase.load(states, element, pathMacroSubstitutor, true)
StateMap.load(this, element, pathMacroSubstitutor, true)
}
fun setDefaultState(element: Element) {
element.setName(rootElementName)
val storageData = StorageData()
storageData.loadState(element)
storageDataRef.set(storageData)
val states = StateMap()
states.loadState(element)
storageDataRef.set(states)
}
override fun startExternalization() = if (checkIsSavingDisabled()) null else createSaveSession(getStorageData())
protected abstract fun createSaveSession(storageData: StorageData): StateStorage.ExternalizationSession
protected abstract fun createSaveSession(states: StateMap): StateStorage.ExternalizationSession
override fun analyzeExternalChangesAndUpdateIfNeed(componentNames: MutableSet<String>) {
val oldData = storageDataRef.get()
@@ -90,10 +94,10 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
if (LOG.isDebugEnabled()) {
LOG.debug("analyzeExternalChangesAndUpdateIfNeed: old data null, load new for ${toString()}")
}
componentNames.addAll(newData.states.keys())
componentNames.addAll(newData.keys())
}
else {
val changedComponentNames = oldData.states.getChangedComponentNames(newData)
val changedComponentNames = oldData.getChangedComponentNames(newData)
if (LOG.isDebugEnabled()) {
LOG.debug("analyzeExternalChangesAndUpdateIfNeed: changedComponentNames $changedComponentNames for ${toString()}")
}
@@ -103,31 +107,31 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
}
}
private fun setStorageData(oldStorageData: StorageData, newStorageData: StorageData?) {
private fun setStorageData(oldStorageData: StateMap, newStorageData: StateMap?) {
if (oldStorageData !== newStorageData && storageDataRef.getAndSet(newStorageData) !== oldStorageData) {
LOG.warn("Old storage data is not equal to current, new storage data was set anyway")
}
}
abstract class XmlElementStorageSaveSession<T : XmlElementStorage>(private val originalStorageData: StorageData, protected val storage: T) : SaveSessionBase() {
private var copiedStorageData: StorageData? = null
abstract class XmlElementStorageSaveSession<T : XmlElementStorage>(private val originalStates: StateMap, protected val storage: T) : SaveSessionBase() {
private var copiedStates: StateMap? = null
private val newLiveStates = THashMap<String, Element>()
override fun createSaveSession() = if (storage.checkIsSavingDisabled() || copiedStorageData == null) null else this
override fun createSaveSession() = if (storage.checkIsSavingDisabled() || copiedStates == null) null else this
override fun setSerializedState(component: Any, componentName: String, element: Element?) {
if (copiedStorageData == null) {
copiedStorageData = setStateAndCloneIfNeed(componentName, element, originalStorageData, newLiveStates)
if (copiedStates == null) {
copiedStates = setStateAndCloneIfNeed(componentName, element, originalStates, newLiveStates)
}
else {
copiedStorageData!!.states.setState(componentName, element, newLiveStates)
copiedStates!!.setState(componentName, element, newLiveStates)
}
}
override fun save() {
var storageData = copiedStorageData!!
var element = storageData.save(newLiveStates, storage.rootElementName)
var states = copiedStates!!
var element = save(states, newLiveStates, storage.rootElementName)
if (element == null || JDOMUtil.isEmpty(element)) {
element = null
}
@@ -149,7 +153,7 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
else {
saveLocally(element)
}
storage.setStorageData(originalStorageData, storageData)
storage.setStorageData(originalStates, states)
}
throws(IOException::class)
@@ -178,19 +182,19 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
try {
val newElement = if (deleted) null else loadDataFromProvider()
val storageData = storageDataRef.get()
val states = storageDataRef.get()
if (newElement == null) {
// if data was loaded, mark as changed all loaded components
if (storageData != null) {
changedComponentNames.addAll(storageData.states.keys())
setStorageData(storageData, null)
if (states != null) {
changedComponentNames.addAll(states.keys())
setStorageData(states, null)
}
}
else if (storageData != null) {
val newStorageData = StorageData()
else if (states != null) {
val newStorageData = StateMap()
newStorageData.loadState(newElement)
changedComponentNames.addAll(storageData.states.getChangedComponentNames(newStorageData))
setStorageData(storageData, newStorageData)
changedComponentNames.addAll(states.getChangedComponentNames(newStorageData))
setStorageData(states, newStorageData)
}
}
catch (e: Throwable) {
@@ -198,3 +202,125 @@ abstract class XmlElementStorage protected constructor(protected val fileSpec: S
}
}
}
private fun save(states: StateMap, newLiveStates: Map<String, Element>, rootElementName: String): Element? {
if (states.isEmpty()) {
return null
}
val rootElement = Element(rootElementName)
val componentNames = ArrayUtil.toStringArray(states.keys())
Arrays.sort(componentNames)
for (componentName in componentNames) {
assert(componentName != null)
val element = states.getElement(componentName, newLiveStates)
// name attribute should be first
val elementAttributes = element.getAttributes()
if (elementAttributes.isEmpty()) {
element.setAttribute(StateMap.NAME, componentName)
}
else {
var nameAttribute: Attribute? = element.getAttribute(StateMap.NAME)
if (nameAttribute == null) {
nameAttribute = Attribute(StateMap.NAME, componentName)
elementAttributes.add(0, nameAttribute)
}
else {
nameAttribute.setValue(componentName)
if (elementAttributes.get(0) != nameAttribute) {
elementAttributes.remove(nameAttribute)
elementAttributes.add(0, nameAttribute)
}
}
}
rootElement.addContent(element)
}
return rootElement
}
fun setStateAndCloneIfNeed(componentName: String, newState: Element?, oldStates: StateMap, newLiveStates: MutableMap<String, Element>): StateMap? {
val oldState = oldStates.get(componentName)
if (newState == null || JDOMUtil.isEmpty(newState)) {
if (oldState == null) {
return null
}
val newStates = StateMap(oldStates)
newStates.remove(componentName)
return newStates
}
prepareElement(newState)
newLiveStates.put(componentName, newState)
var newBytes: ByteArray? = null
if (oldState is Element) {
if (JDOMUtil.areElementsEqual(oldState as Element?, newState)) {
return null
}
}
else if (oldState != null) {
newBytes = StateMap.getNewByteIfDiffers(componentName, newState, oldState as ByteArray)
if (newBytes == null) {
return null
}
}
val newStates = StateMap(oldStates)
newStates.put(componentName, newBytes ?: newState)
return newStates
}
fun prepareElement(state: Element) {
if (state.getParent() != null) {
LOG.warn("State element must not have parent ${JDOMUtil.writeElement(state)}")
state.detach()
}
state.setName(StateMap.COMPONENT)
}
fun StateMap.setState(componentName: String, newState: Element?, newLiveStates: MutableMap<String, Element>): Any? {
if (newState == null || JDOMUtil.isEmpty(newState)) {
return remove(componentName)
}
prepareElement(newState)
newLiveStates.put(componentName, newState)
val oldState = get(componentName)
var newBytes: ByteArray? = null
if (oldState is Element) {
if (JDOMUtil.areElementsEqual(oldState as Element?, newState)) {
return null
}
}
else if (oldState != null) {
newBytes = StateMap.getNewByteIfDiffers(componentName, newState, oldState as ByteArray)
if (newBytes == null) {
return null
}
}
put(componentName, if (newBytes == null) newState else newBytes)
return newState
}
// newStorageData - myStates contains only live (unarchived) states
private fun StateMap.getChangedComponentNames(newStates: StateMap): Set<String> {
val bothStates = SmartHashSet(keys())
bothStates.retainAll(newStates.keys())
val diffs = SmartHashSet<String>()
diffs.addAll(newStates.keys())
diffs.addAll(keys())
diffs.removeAll(bothStates)
for (componentName in bothStates) {
compare(componentName, newStates, diffs)
}
return diffs
}
@@ -15,9 +15,13 @@
*/
package com.intellij.configurationStore
import com.intellij.openapi.components.impl.stores.StateMap
import com.intellij.openapi.util.JDOMBuilder.attr
import com.intellij.openapi.util.JDOMBuilder.tag
import junit.framework.TestCase
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.CoreMatchers.nullValue
import org.hamcrest.MatcherAssert.assertThat
import org.jdom.Element
import org.junit.Test
@@ -33,28 +37,28 @@ class XmlElementStorageTest {
public Test fun testGetStateNotSucceeded() {
val storage = MyXmlElementStorage(tag("root"))
val state = storage.getState(this, "test", javaClass<Element>())
TestCase.assertNull(state)
assertThat(state, nullValue())
}
public Test fun testSetStateOverridesOldState() {
public Test fun `set state overrides old state`() {
val storage = MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo"))))
val newState = tag("component", attr("name", "test"), tag("bar"))
val externalizationSession = storage.startExternalization()!!
externalizationSession.setState(this, "test", newState, null)
externalizationSession.setState(this, "test", newState)
externalizationSession.createSaveSession()!!.save()
TestCase.assertNotNull(storage.mySavedElement)
TestCase.assertNotNull(storage.mySavedElement!!.getChild("component").getChild("bar"))
TestCase.assertNull(storage.mySavedElement!!.getChild("component").getChild("foo"))
assertThat(storage.savedElement, notNullValue())
assertThat(storage.savedElement!!.getChild("component").getChild("bar"), notNullValue())
assertThat(storage.savedElement!!.getChild("component").getChild("foo"), nullValue())
}
private class MyXmlElementStorage(private val myElement: Element) : XmlElementStorage("", "root", null, null, null) {
var mySavedElement: Element? = null
var savedElement: Element? = null
override fun loadLocalData() = myElement
override fun createSaveSession(storageData: StorageData) = object : XmlElementStorage.XmlElementStorageSaveSession<MyXmlElementStorage>(storageData, this) {
override fun createSaveSession(states: StateMap) = object : XmlElementStorage.XmlElementStorageSaveSession<MyXmlElementStorage>(states, this) {
override fun saveLocally(element: Element?) {
mySavedElement = element?.clone()
savedElement = element?.clone()
}
}
}
@@ -205,7 +205,8 @@ public interface Application extends ComponentManager {
/**
* Causes {@code runnable.run()} to be executed asynchronously on the
* AWT event dispatching thread - unless the expiration condition is fulfilled.
* This will happen after all pending AWT events have been processed and in {@link ModalityState#defaultModalityState()} modality state.<p/>
* This will happen after all pending AWT events have been processed and in {@link ModalityState#defaultModalityState()} modality state
* (or a state with less modal dialogs open).<p/>
*
* Please use this method instead of {@link javax.swing.SwingUtilities#invokeLater(Runnable)} or {@link com.intellij.util.ui.UIUtil} methods
* for the reasons described in {@link ModalityState} documentation.
@@ -218,7 +219,7 @@ public interface Application extends ComponentManager {
/**
* Causes {@code runnable.run()} to be executed asynchronously on the
* AWT event dispatching thread, when IDEA is in the specified modality
* state.
* state (or a state with less modal dialogs open).
*
* Please use this method instead of {@link javax.swing.SwingUtilities#invokeLater(Runnable)} or {@link com.intellij.util.ui.UIUtil} methods
* for the reasons described in {@link ModalityState} documentation.
@@ -231,7 +232,7 @@ public interface Application extends ComponentManager {
/**
* Causes {@code runnable.run()} to be executed asynchronously on the
* AWT event dispatching thread, when IDEA is in the specified modality
* state - unless the expiration condition is fulfilled.
* state(or a state with less modal dialogs open) - unless the expiration condition is fulfilled.
* This will happen after all pending AWT events have been processed.
*
* Please use this method instead of {@link javax.swing.SwingUtilities#invokeLater(Runnable)} or {@link com.intellij.util.ui.UIUtil} methods
@@ -246,7 +247,7 @@ public interface Application extends ComponentManager {
/**
* <p>Causes {@code runnable.run()} to be executed synchronously on the
* AWT event dispatching thread, when the IDE is in the specified modality
* state. This call blocks until all pending AWT events have been processed and (then)
* state (or a state with less modal dialogs open). This call blocks until all pending AWT events have been processed and (then)
* {@code runnable.run()} returns.</p>
*
* <p>If current thread is an event dispatch thread then {@code runnable.run()}
@@ -21,7 +21,7 @@ import java.awt.*;
/**
* Represents the stack of active modal dialogs. Used in calls to {@link Application#invokeAndWait(Runnable, ModalityState)} to specify
* that the corresponding runnable is to be executed within the given modality state, i.e. when the same set modal dialogs is present.<p/>
* that the corresponding runnable is to be executed within the given modality state, i.e. when the same set modal dialogs is present, or its subset.<p/>
*
* Modality state is used to prevent the following scenario. Someone does SwingUtilities.invokeAndWait, but there are already other runnables in
* Swing queue, so they are executed before and show a dialog (e.g. asking a yes/no question). While this dialog is shown, further events are pumped
@@ -16,13 +16,15 @@
package com.intellij.openapi.editor.impl;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.event.RetargetRangeMarkers;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ProperTextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A range marker that has to be manually updated with {@link #applyEvent(DocumentEvent)}. Can hold PSI-based range and be updated when the document is committed.
* A range marker that has to be manually updated with {@link #getUpdatedRange(DocumentEvent)} and {@link #applyState(ManualRangeMarker)}.
* Can hold PSI-based range and be updated when the document is committed.
*/
public class ManualRangeMarker {
private static int ourCount = 0;
@@ -56,6 +58,14 @@ public class ManualRangeMarker {
@Nullable
private Pair<ProperTextRange, PersistentRangeMarker.LinesCols> getUpdatedState(@NotNull DocumentEvent event) {
if (event instanceof RetargetRangeMarkers) {
int start = ((RetargetRangeMarkers)event).getStartOffset();
if (myRange.getStartOffset() >= start && myRange.getEndOffset() <= ((RetargetRangeMarkers)event).getEndOffset()) {
ProperTextRange range = myRange.shiftRight(((RetargetRangeMarkers)event).getMoveDestinationOffset() - start);
return Pair.create(range, myLinesCols == null ? null : PersistentRangeMarker.storeLinesAndCols(range, event.getDocument()));
}
}
if (myLinesCols != null) {
return PersistentRangeMarker
.applyChange(event, myRange, myRange.getStartOffset(), myRange.getEndOffset(), myGreedyLeft, myGreedyRight, myLinesCols);
@@ -65,16 +75,26 @@ public class ManualRangeMarker {
return range == null ? null : new Pair<ProperTextRange, PersistentRangeMarker.LinesCols>(range, null);
}
public void applyEvent(@NotNull DocumentEvent event) {
if (!myValid) return;
Pair<ProperTextRange, PersistentRangeMarker.LinesCols> pair = getUpdatedState(event);
if (pair != null) {
myRange = pair.first;
myLinesCols = pair.second;
} else {
public void applyState(@Nullable ManualRangeMarker updated) {
if (updated == null || !updated.myValid) {
myValid = false;
}
if (!myValid) return;
myRange = updated.myRange;
myLinesCols = updated.myLinesCols;
}
public boolean isGreedyLeft() {
return myGreedyLeft;
}
public boolean isGreedyRight() {
return myGreedyRight;
}
public boolean isSurviveOnExternalChange() {
return myLinesCols != null;
}
@Nullable
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl.event;
import com.intellij.openapi.editor.Document;
import org.jetbrains.annotations.NotNull;
/**
* @author peter
*/
public class RetargetRangeMarkers extends DocumentEventImpl {
private final int myStartOffset;
private final int myEndOffset;
private final int myMoveDestinationOffset;
public RetargetRangeMarkers(@NotNull Document document,
int startOffset, int endOffset, int moveDestinationOffset) {
super(document, startOffset, "", "", 0, false);
myStartOffset = startOffset;
myEndOffset = endOffset;
myMoveDestinationOffset = moveDestinationOffset;
}
public int getStartOffset() {
return myStartOffset;
}
public int getEndOffset() {
return myEndOffset;
}
public int getMoveDestinationOffset() {
return myMoveDestinationOffset;
}
}
@@ -408,7 +408,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
}
ProgressIndicator indicator = createProgressIndicator();
CommitTask task = new CommitTask(document, project, indicator, "Sync commit", ModalityState.any());
CommitTask task = new CommitTask(document, project, indicator, "Sync commit", ModalityState.current());
synchronized (documentsToCommit) {
markRemovedFromDocsToCommit(task);
markRemovedCurrentTask(task);
@@ -16,19 +16,25 @@
package com.intellij.psi.impl;
import com.google.common.annotations.VisibleForTesting;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.PrioritizedInternalDocumentListener;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.editor.impl.EditorDocumentPriorities;
import com.intellij.openapi.editor.impl.FrozenDocument;
import com.intellij.openapi.editor.impl.event.RetargetRangeMarkers;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
@@ -56,7 +62,7 @@ import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.*;
public abstract class PsiDocumentManagerBase extends PsiDocumentManager implements DocumentListener {
public abstract class PsiDocumentManagerBase extends PsiDocumentManager implements DocumentListener, ProjectComponent {
static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiDocumentManagerImpl");
private static final Key<Document> HARD_REF_TO_DOCUMENT = Key.create("HARD_REFERENCE_TO_DOCUMENT");
private static final Key<PsiFile> HARD_REF_TO_PSI = Key.create("HARD_REFERENCE_TO_PSI");
@@ -624,13 +630,16 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
if (myStopTrackingDocuments || myProject.isDisposed()) return;
final Document document = event.getDocument();
if (document instanceof DocumentImpl && !myUncommittedInfos.containsKey(document)) {
myUncommittedInfos.put(document, new UncommittedInfo((DocumentImpl)document));
}
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
boolean isRelevant = virtualFile != null && isRelevant(virtualFile);
if (document instanceof DocumentImpl && !myUncommittedInfos.containsKey(document)) {
myUncommittedInfos.put(document, new UncommittedInfo((DocumentImpl)document));
if (isRelevant) {
((SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject)).fastenBelts(virtualFile);
}
}
final FileViewProvider viewProvider = getCachedViewProvider(document);
boolean inMyProject = viewProvider != null && viewProvider.getManager() == myPsiManager;
if (!isRelevant || !inMyProject) {
@@ -664,10 +673,6 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
if (myStopTrackingDocuments || myProject.isDisposed()) return;
final Document document = event.getDocument();
if (document instanceof DocumentImpl) {
myUncommittedInfos.get(document).myEvents.add(event);
}
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
boolean isRelevant = virtualFile != null && isRelevant(virtualFile);
@@ -764,6 +769,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
UncommittedInfo info = myUncommittedInfos.remove(document);
if (info != null) {
((SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject)).updatePointers(document, info.myFrozen, info.myEvents);
Disposer.dispose(info);
}
return info;
}
@@ -836,8 +842,11 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
return false;
}
@TestOnly
@VisibleForTesting
public void clearUncommittedDocuments() {
for (UncommittedInfo info : myUncommittedInfos.values()) {
Disposer.dispose(info);
}
myUncommittedInfos.clear();
myUncommittedDocuments.clear();
mySynchronizer.cleanupForNextTest();
@@ -855,12 +864,35 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
});
}
@Override
public void projectOpened() {
}
@Override
public void projectClosed() {
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
clearUncommittedDocuments();
}
@NotNull
@Override
public String getComponentName() {
return getClass().getSimpleName();
}
@NotNull
public PsiToDocumentSynchronizer getSynchronizer() {
return mySynchronizer;
}
private static class UncommittedInfo {
private static class UncommittedInfo extends DocumentAdapter implements PrioritizedInternalDocumentListener, Disposable {
final DocumentImpl myOriginal;
final FrozenDocument myFrozen;
final List<DocumentEvent> myEvents = ContainerUtil.newArrayList();
@@ -868,8 +900,27 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
public UncommittedInfo(DocumentImpl original) {
myOriginal = original;
myFrozen = original.freeze();
myOriginal.addDocumentListener(this, this);
}
@Override
public int getPriority() {
return EditorDocumentPriorities.RANGE_MARKER;
}
@Override
public void documentChanged(DocumentEvent e) {
myEvents.add(e);
}
@Override
public void moveTextHappened(int start, int end, int base) {
myEvents.add(new RetargetRangeMarkers(myOriginal, start, end, base));
}
@Override
public void dispose() {
}
}
}
@@ -0,0 +1,228 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.smartPointers;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.FrozenDocument;
import com.intellij.openapi.editor.impl.ManualRangeMarker;
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
import com.intellij.openapi.editor.impl.event.RetargetRangeMarkers;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Trinity;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.WeakHashMap;
import com.intellij.util.containers.WeakValueHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author peter
*/
class MarkerCache {
private final Set<ManualRangeMarker> myMarkerSet = Collections.newSetFromMap(new WeakHashMap<ManualRangeMarker, Boolean>());
private WeakValueHashMap<RangeKey, ManualRangeMarker> myByRange = new WeakValueHashMap<RangeKey, ManualRangeMarker>();
private volatile Trinity<Integer, Map<RangeKey, ManualRangeMarker>, FrozenDocument> myUpdatedRanges;
@Nullable
private static RangeKey keyOf(@NotNull ManualRangeMarker marker) {
ProperTextRange range = marker.getRange();
return range == null ? null : new RangeKey(range, marker.isGreedyLeft(), marker.isGreedyRight(), marker.isSurviveOnExternalChange());
}
@NotNull
synchronized ManualRangeMarker obtainMarker(@NotNull ProperTextRange range, @NotNull FrozenDocument frozen, boolean greedyLeft, boolean greedyRight, boolean persistent) {
WeakValueHashMap<RangeKey, ManualRangeMarker> byRange = getByRangeCache();
RangeKey key = new RangeKey(range, greedyLeft, greedyRight, persistent);
ManualRangeMarker marker = byRange.get(key);
if (marker == null) {
marker = new ManualRangeMarker(frozen, range, greedyLeft, greedyRight, persistent);
myMarkerSet.add(marker);
byRange.put(key, marker);
myUpdatedRanges = null;
}
return marker;
}
private WeakValueHashMap<RangeKey, ManualRangeMarker> getByRangeCache() {
if (myByRange == null) {
myByRange = new WeakValueHashMap<RangeKey, ManualRangeMarker>();
for (ManualRangeMarker marker : myMarkerSet) {
RangeKey key = keyOf(marker);
if (key != null) {
myByRange.put(key, marker);
}
}
}
return myByRange;
}
private Map<RangeKey, ManualRangeMarker> getUpdatedMarkers(@NotNull FrozenDocument frozen, @NotNull List<DocumentEvent> events) {
if (myMarkerSet.isEmpty()) return Collections.emptyMap();
int eventCount = events.size();
assert eventCount > 0;
Trinity<Integer, Map<RangeKey, ManualRangeMarker>, FrozenDocument> cache = myUpdatedRanges;
if (cache != null && cache.first.intValue() == eventCount) return cache.second;
//noinspection SynchronizeOnThis
synchronized (this) {
cache = myUpdatedRanges;
if (cache != null && cache.first.intValue() == eventCount) return cache.second;
Map<RangeKey, ManualRangeMarker> answer = ContainerUtil.newHashMap();
if (cache != null && cache.first < eventCount) {
// apply only the new events
answer.putAll(cache.second);
frozen = applyEvents(cache.third, events.subList(cache.first, eventCount), answer);
}
else {
for (ManualRangeMarker marker : myMarkerSet) {
RangeKey key = keyOf(marker);
if (key != null) {
answer.put(key, marker);
}
}
frozen = applyEvents(frozen, events, answer);
}
myUpdatedRanges = Trinity.create(eventCount, answer, frozen);
return answer;
}
}
private static FrozenDocument applyEvents(@NotNull FrozenDocument frozen,
@NotNull List<DocumentEvent> events,
Map<RangeKey, ManualRangeMarker> map) {
for (DocumentEvent event : events) {
DocumentEvent corrected;
if ((event instanceof RetargetRangeMarkers)) {
RetargetRangeMarkers retarget = (RetargetRangeMarkers)event;
corrected = new RetargetRangeMarkers(frozen, retarget.getStartOffset(), retarget.getEndOffset(), retarget.getMoveDestinationOffset());
}
else {
frozen = frozen.applyEvent(event, 0);
corrected = new DocumentEventImpl(frozen, event.getOffset(), event.getOldFragment(), event.getNewFragment(), event.getOldTimeStamp(),
event.isWholeTextReplaced());
}
for (Map.Entry<RangeKey, ManualRangeMarker> entry : map.entrySet()) {
ManualRangeMarker currentRange = entry.getValue();
if (currentRange != null) {
entry.setValue(currentRange.getUpdatedRange(corrected));
}
}
}
return frozen;
}
synchronized void updateMarkers(@NotNull FrozenDocument frozen, @NotNull List<DocumentEvent> events, @NotNull List<SmartPsiElementPointerImpl> pointers) {
List<SelfElementInfo> infos = ContainerUtil.findAll(ContainerUtil.map(pointers, new NullableFunction<SmartPsiElementPointerImpl, SmartPointerElementInfo>() {
@Nullable
@Override
public SmartPointerElementInfo fun(SmartPsiElementPointerImpl pointer) {
return pointer.getElementInfo();
}
}), SelfElementInfo.class);
Map<RangeKey, ManualRangeMarker> updated = getUpdatedMarkers(frozen, events);
Map<ManualRangeMarker, ManualRangeMarker> newStates = ContainerUtil.newHashMap();
for (SelfElementInfo info : infos) {
ManualRangeMarker marker = info.getRangeMarker();
RangeKey key = marker == null ? null : keyOf(marker);
if (key != null) {
newStates.put(marker, updated.get(key));
}
}
myMarkerSet.clear();
for (Map.Entry<ManualRangeMarker, ManualRangeMarker> entry : newStates.entrySet()) {
ManualRangeMarker marker = entry.getKey();
marker.applyState(entry.getValue());
if (marker.isValid()) {
myMarkerSet.add(marker); //re-add only alive markers
}
}
myByRange = null;
myUpdatedRanges = null;
for (SelfElementInfo info : infos) {
info.updateValidity();
}
}
@Nullable
ProperTextRange getUpdatedRange(@NotNull ManualRangeMarker marker, @NotNull FrozenDocument frozen, @NotNull List<DocumentEvent> events) {
ManualRangeMarker updated = getUpdatedMarkers(frozen, events).get(keyOf(marker));
return updated == null ? null : updated.getRange();
}
@TestOnly
synchronized int getMarkerCount() {
return myMarkerSet.size();
}
private static class RangeKey {
final int start;
final int end;
final int flags;
RangeKey(ProperTextRange range, boolean greedyLeft, boolean greedyRight, boolean persistent) {
start = range.getStartOffset();
end = range.getEndOffset();
flags = (persistent ? 4 : 0) + (greedyLeft ? 2 : 0) + (greedyRight ? 1 : 0);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RangeKey)) return false;
RangeKey key = (RangeKey)o;
if (start != key.start) return false;
if (end != key.end) return false;
if (flags != key.flags) return false;
return true;
}
@Override
public int hashCode() {
int result = start;
result = 31 * result + end;
result = 31 * result + flags;
return result;
}
@Override
public String toString() {
return "RangeKey{" +
"start=" + start +
", end=" + end +
", flags=" + flags +
'}';
}
}
}
@@ -17,33 +17,36 @@ package com.intellij.psi.impl.smartPointers;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.FrozenDocument;
import com.intellij.openapi.editor.impl.ManualRangeMarker;
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerBase;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.List;
/**
* User: cdr
*/
public class SelfElementInfo extends SmartPointerElementInfo {
private final VirtualFile myVirtualFile;
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SelfElementInfo");
private static final FileDocumentManager ourFileDocManager = FileDocumentManager.getInstance();
@NotNull private final VirtualFile myVirtualFile;
private final Class myType;
private final Project myProject;
private final Language myLanguage;
private final MarkerCache myMarkerCache;
@Nullable private ManualRangeMarker myRangeMarker;
@Nullable private ProperTextRange myPsiRange;
private final PsiDocumentManagerBase myPsiDocManager;
SelfElementInfo(@NotNull Project project,
@NotNull ProperTextRange range,
@@ -51,14 +54,16 @@ public class SelfElementInfo extends SmartPointerElementInfo {
@NotNull PsiFile containingFile,
@NotNull Language language) {
myLanguage = language;
myVirtualFile = PsiUtilCore.getVirtualFile(containingFile);
myVirtualFile = containingFile.getViewProvider().getVirtualFile();
myType = anchorClass;
assert !PsiFile.class.isAssignableFrom(anchorClass) : "FileElementInfo must be used for files";
myProject = project;
myPsiRange = range;
myMarkerCache = ((SmartPointerManagerImpl)SmartPointerManager.getInstance(project)).getMarkerCache(myVirtualFile);
myPsiDocManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject);
Document document = getDocumentManager().getCachedDocument(containingFile);
Document document = myPsiDocManager.getCachedDocument(containingFile);
if (document != null) {
setRange(range, document);
}
@@ -66,16 +71,13 @@ public class SelfElementInfo extends SmartPointerElementInfo {
void setRange(@NotNull TextRange range, @NotNull Document document) {
myPsiRange = null;
myRangeMarker = ((SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject)).obtainMarker(document, ProperTextRange.create(range));
}
private PsiDocumentManagerBase getDocumentManager() {
return (PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject);
FrozenDocument frozenDocument = myPsiDocManager.getLastCommittedDocument(document);
myRangeMarker = myMarkerCache.obtainMarker(ProperTextRange.create(range), frozenDocument, false, false, true);
}
@Override
public Document getDocumentToSynchronize() {
return myVirtualFile == null ? null : FileDocumentManager.getInstance().getCachedDocument(myVirtualFile);
return ourFileDocManager.getCachedDocument(myVirtualFile);
}
// before change
@@ -84,8 +86,8 @@ public class SelfElementInfo extends SmartPointerElementInfo {
if (myRangeMarker != null) return; // already tracks changes
if (myPsiRange == null) return; // invalid
Document document = myVirtualFile == null ? null : FileDocumentManager.getInstance().getDocument(myVirtualFile);
if (document == null || !getDocumentManager().isCommitted(document)) {
Document document = ourFileDocManager.getDocument(myVirtualFile);
if (document == null || !myPsiDocManager.isCommitted(document)) {
// we only have PSI range and now they say the document is uncommitted, so this PSI range is useless
// so, just invalidate
myPsiRange = null;
@@ -148,22 +150,19 @@ public class SelfElementInfo extends SmartPointerElementInfo {
myPsiRange = null;
}
public void updateRange(@NotNull DocumentEvent event, @NotNull Set<ManualRangeMarker> processedMarkers) {
assert myPsiRange == null;
if (myRangeMarker != null) {
if (processedMarkers.add(myRangeMarker)) {
myRangeMarker.applyEvent(event);
}
if (!myRangeMarker.isValid()) {
myRangeMarker = null;
}
void updateValidity() {
if (myPsiRange != null) {
LOG.error("Non-fastened smart pointer " + this + " " + myRangeMarker);
myPsiRange = null;
myRangeMarker = null;
}
if (myRangeMarker != null && !myRangeMarker.isValid()) {
myRangeMarker = null;
}
}
@Nullable
public static PsiFile restoreFileFromVirtual(final VirtualFile virtualFile, @NotNull final Project project, @Nullable final Language language) {
if (virtualFile == null) return null;
public static PsiFile restoreFileFromVirtual(@NotNull final VirtualFile virtualFile, @NotNull final Project project, @Nullable final Language language) {
return ApplicationManager.getApplication().runReadAction(new NullableComputable<PsiFile>() {
@Override
public PsiFile compute() {
@@ -216,8 +215,7 @@ public class SelfElementInfo extends SmartPointerElementInfo {
@Override
public int elementHashCode() {
VirtualFile virtualFile = myVirtualFile;
return virtualFile == null ? 0 : virtualFile.hashCode();
return myVirtualFile.hashCode();
}
@Override
@@ -243,6 +241,7 @@ public class SelfElementInfo extends SmartPointerElementInfo {
}
@Override
@NotNull
public VirtualFile getVirtualFile() {
return myVirtualFile;
}
@@ -253,25 +252,17 @@ public class SelfElementInfo extends SmartPointerElementInfo {
if (myRangeMarker != null) {
Document document = getDocumentToSynchronize();
if (document != null) {
FrozenDocument frozen = getDocumentManager().getLastCommittedDocument(document);
ManualRangeMarker marker = myRangeMarker;
for (DocumentEvent event : getDocumentManager().getEventsSinceCommit(document)) {
frozen = frozen.applyEvent(event, 0);
marker = marker.getUpdatedRange(withFrozen(frozen, event));
if (marker == null) return null;
PsiDocumentManagerBase documentManager = myPsiDocManager;
List<DocumentEvent> events = documentManager.getEventsSinceCommit(document);
if (!events.isEmpty()) {
return myMarkerCache.getUpdatedRange(myRangeMarker, documentManager.getLastCommittedDocument(document), events);
}
return marker.getRange();
}
return myRangeMarker.getRange();
}
return myPsiRange;
}
@NotNull
static DocumentEventImpl withFrozen(FrozenDocument frozen, DocumentEvent e) {
return new DocumentEventImpl(frozen, e.getOffset(), e.getOldFragment(), e.getNewFragment(), e.getOldTimeStamp(), e.isWholeTextReplaced());
}
@NotNull
@Override
public Project getProject() {
@@ -21,7 +21,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.FrozenDocument;
import com.intellij.openapi.editor.impl.ManualRangeMarker;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
@@ -30,14 +29,11 @@ import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -46,7 +42,6 @@ import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
public class SmartPointerManagerImpl extends SmartPointerManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl");
@@ -160,15 +155,21 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
private <E extends PsiElement> void initPointer(@NotNull SmartPsiElementPointerImpl<E> pointer, @NotNull VirtualFile containingFile) {
synchronized (lock) {
pointer.incrementAndGetReferenceCount(1);
getNotNullPointerList(containingFile).add(new PointerReference(pointer, containingFile, ourQueue, POINTERS_KEY));
}
}
@NotNull
private FilePointersList getNotNullPointerList(@NotNull VirtualFile containingFile) {
synchronized (lock) {
FilePointersList pointers = getPointers(containingFile);
if (pointers == null) {
pointers = new FilePointersList(); // we synchronise access anyway
containingFile.putUserData(POINTERS_KEY, pointers);
}
pointer.incrementAndGetReferenceCount(1);
pointers.add(new PointerReference(pointer, containingFile, ourQueue, POINTERS_KEY));
return pointers;
}
}
@@ -209,21 +210,8 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
@NotNull
ManualRangeMarker obtainMarker(@NotNull Document document, @NotNull ProperTextRange range) {
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
FilePointersList pointers = file == null ? null : getPointers(file);
ConcurrentMap<ProperTextRange, ManualRangeMarker> cache = pointers == null ? null : pointers.getMarkerCache();
ManualRangeMarker marker = cache == null ? null : cache.get(range);
if (marker != null) {
return marker;
}
FrozenDocument frozen = ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject)).getLastCommittedDocument(document);
marker = new ManualRangeMarker(frozen, range, false, false, true);
if (cache != null) {
marker = ConcurrencyUtil.cacheOrGet(cache, range, marker);
}
return marker;
MarkerCache getMarkerCache(@NotNull VirtualFile file) {
return getNotNullPointerList(file).markerCache;
}
@TestOnly
@@ -235,6 +223,14 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
}
@TestOnly
public int getMarkerCount(@NotNull Document document) {
synchronized (lock) {
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
return file == null ? 0 : getMarkerCache(file).getMarkerCount();
}
}
@Override
public boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) {
return SmartPsiElementPointerImpl.pointsToTheSameElementAs(pointer1, pointer2);
@@ -242,29 +238,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
public void updatePointers(Document document, FrozenDocument frozen, List<DocumentEvent> events) {
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
FilePointersList pointers = file == null ? null : getPointers(file);
if (pointers == null) return;
FilePointersList list = file == null ? null : getPointers(file);
if (list == null) return;
pointers.markerCache = null;
List<SelfElementInfo> infos = ContainerUtil.mapNotNull(pointers.getAlivePointers(), new Function<SmartPsiElementPointerImpl, SelfElementInfo>() {
@Override
public SelfElementInfo fun(SmartPsiElementPointerImpl pointer) {
final SmartPointerElementInfo info = pointer.getElementInfo();
return info instanceof SelfElementInfo ? (SelfElementInfo)info : null;
}
});
for (DocumentEvent event : events) {
THashSet<ManualRangeMarker> processedMarkers = ContainerUtil.newIdentityTroveSet();
frozen = frozen.applyEvent(event, 0);
final DocumentEvent corrected = SelfElementInfo.withFrozen(frozen, event);
for (SelfElementInfo info : infos) {
info.updateRange(corrected, processedMarkers);
}
}
list.markerCache.updateMarkers(frozen, events, list.getAlivePointers());
}
private static class PointerReference extends WeakReference<SmartPointerEx> {
@@ -285,7 +262,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
private int nextAvailableIndex;
private int size;
private PointerReference[] references = new PointerReference[10];
private volatile ConcurrentMap<ProperTextRange, ManualRangeMarker> markerCache;
private final MarkerCache markerCache = new MarkerCache();
private void add(@NotNull PointerReference reference) {
if (nextAvailableIndex >= references.length || nextAvailableIndex > size*2) { // overflow or too many dead refs
@@ -331,24 +308,6 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return size == 0;
}
@Nullable
private ConcurrentMap<ProperTextRange, ManualRangeMarker> getMarkerCache() {
ConcurrentMap<ProperTextRange, ManualRangeMarker> cache = markerCache;
if (cache == null) {
cache = ContainerUtil.newConcurrentMap();
for (SmartPsiElementPointerImpl pointer : getAlivePointers()) {
SmartPointerElementInfo info = pointer == null ? null : pointer.getElementInfo();
ManualRangeMarker marker = info instanceof SelfElementInfo ? ((SelfElementInfo)info).getRangeMarker() : null;
ProperTextRange key = marker == null ? null : marker.getRange();
if (key != null) {
cache.putIfAbsent(key, marker);
}
}
markerCache = cache;
}
return cache;
}
@NotNull
private List<SmartPsiElementPointerImpl> getAlivePointers() {
return ContainerUtil.mapNotNull(references, new Function<PointerReference, SmartPsiElementPointerImpl>() {
@@ -21,9 +21,11 @@ import com.intellij.diff.comparison.iterables.DiffIterableUtil.*;
import com.intellij.diff.comparison.iterables.FairDiffIterable;
import com.intellij.diff.fragments.DiffFragment;
import com.intellij.diff.util.Range;
import com.intellij.diff.util.Side;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.MergingCharSequence;
import org.jetbrains.annotations.NotNull;
@@ -48,9 +50,9 @@ public class ByWord {
List<InlineChunk> words2 = getInlineChunks(text2);
FairDiffIterable wordChanges = diff(words1, words2, indicator);
FairDiffIterable correctedWordChanges = preferBigChunks(words1, words2, wordChanges, indicator);
wordChanges = optimizeWordChunks(text1, text2, words1, words2, wordChanges, indicator);
FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(text1, text2, words1, words2, correctedWordChanges, indicator);
FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(text1, text2, words1, words2, wordChanges, indicator);
DiffIterable iterable = matchAdjustmentWhitespaces(text1, text2, delimitersIterable, policy, indicator);
return convertIntoFragments(iterable);
@@ -84,9 +86,9 @@ public class ByWord {
List<InlineChunk> words2 = getInlineChunks(text2);
FairDiffIterable wordChanges = diff(words1, words2, indicator);
FairDiffIterable correctedWordChanges = preferBigChunks(words1, words2, wordChanges, indicator);
wordChanges = optimizeWordChunks(text1, text2, words1, words2, wordChanges, indicator);
List<WordBlock> wordBlocks = new LineFragmentSplitter(text1, text2, words1, words2, correctedWordChanges, indicator).run();
List<WordBlock> wordBlocks = new LineFragmentSplitter(text1, text2, words1, words2, wordChanges, indicator).run();
List<LineBlock> lineBlocks = new ArrayList<LineBlock>(wordBlocks.size());
for (WordBlock block : wordBlocks) {
@@ -99,7 +101,7 @@ public class ByWord {
List<InlineChunk> subwords1 = words1.subList(words.start1, words.end1);
List<InlineChunk> subwords2 = words2.subList(words.start2, words.end2);
FairDiffIterable subiterable = fair(trim(correctedWordChanges, words.start1, words.end1, words.start2, words.end2));
FairDiffIterable subiterable = fair(trim(wordChanges, words.start1, words.end1, words.start2, words.end2));
FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(subtext1, subtext2, subwords1, subwords2, subiterable,
offsets.start1, offsets.start2, indicator);
@@ -121,65 +123,130 @@ public class ByWord {
//
/*
* Try to merge matched blocks to form a bigger ones
* 1. Minimise amount of chunks
* good: "AX[AB]" - "[AB]"
* bad: "[A]XA[B]" - "[A][B]"
*
* sample: "A X A B" - "A B" should be matched as "A X [A B]" - "[A B]"
* 2. Minimise amount of modified 'sentences', where sentence is a sequence of words, that are not separated by whitespace
* good: "[AX] [AZ]" - "[AX] AY [AZ]"
* bad: "[AX A][Z]" - "[AX A]Y A[Z]"
* ex: "1.0.123 1.0.155" vs "1.0.123 1.0.134 1.0.155"
*/
@NotNull
private static FairDiffIterable preferBigChunks(@NotNull List<InlineChunk> words1,
@NotNull List<InlineChunk> words2,
@NotNull FairDiffIterable iterable,
@NotNull ProgressIndicator indicator) {
private static FairDiffIterable optimizeWordChunks(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull List<InlineChunk> words1,
@NotNull List<InlineChunk> words2,
@NotNull FairDiffIterable iterable,
@NotNull ProgressIndicator indicator) {
List<Range> newRanges = new ArrayList<Range>();
for (Range range : iterable.iterateUnchanged()) {
if (newRanges.size() == 0) {
Range lastRange = ContainerUtil.getLastItem(newRanges);
if (lastRange == null ||
(lastRange.end1 != range.start1 && lastRange.end2 != range.start2)) {
// if changes do not touch and we still can perform one of these optimisations,
// it means that given DiffIterable is not LCS (because we can build a smaller one). This should not happen.
newRanges.add(range);
continue;
}
Range lastRange = newRanges.get(newRanges.size() - 1);
boolean canMergeLeft = true;
int count = range.end1 - range.start1;
for (int i = 0; i < count; i++) {
InlineChunk word1 = words1.get(lastRange.end1 + i);
InlineChunk word2 = words2.get(lastRange.end2 + i);
if (!word1.equals(word2)) {
canMergeLeft = false;
break;
}
}
int lastCount = lastRange.end1 - lastRange.start1;
if (canMergeLeft) {
// merge chunks left [A]B[B] -> [AB]B
int equalLeft = equalRanges(words1, words2, lastRange.end1, lastRange.end2, count, true);
if (equalLeft == count) {
newRanges.remove(newRanges.size() - 1);
newRanges.add(new Range(lastRange.start1, lastRange.end1 + count, lastRange.start2, lastRange.end2 + count));
continue;
}
boolean canMergeRight = true;
int lastCount = lastRange.end1 - lastRange.start1;
for (int i = 0; i < lastCount; i++) {
InlineChunk word1 = words1.get(range.start1 - i - 1);
InlineChunk word2 = words2.get(range.start2 - i - 1);
if (!word1.equals(word2)) {
canMergeRight = false;
break;
}
}
if (canMergeRight) {
// merge chunks right [A]A[B] -> A[AB]
int equalRight = equalRanges(words1, words2, range.start1 - lastCount, range.start2 - lastCount, lastCount, false);
if (equalRight == lastCount) {
newRanges.remove(newRanges.size() - 1);
newRanges.add(new Range(range.start1 - lastCount, range.end1, range.start2 - lastCount, range.end2));
continue;
}
Side touchSide = Side.fromLeft(lastRange.end1 == range.start1);
List<InlineChunk> touchWords = touchSide.select(words1, words2);
CharSequence touchText = touchSide.select(text1, text2);
int touchStart = touchSide.select(range.start1, range.start2);
// check if chunks are already separated by whitespaces
if (!isSeparatedWithWhitespace(touchText, touchWords.get(touchStart - 1), touchWords.get(touchStart))) {
// shift chunks left [X]A Y[A ZA] -> [XA] YA [ZA]
// [X][A ZA] -> [XA] [ZA]
int leftShift = findSequenceEdgeShift(touchText, touchWords, touchStart, equalLeft, true);
if (leftShift > 0) {
newRanges.remove(newRanges.size() - 1);
newRanges.add(new Range(lastRange.start1, lastRange.end1 + leftShift, lastRange.start2, lastRange.end2 + leftShift));
newRanges.add(new Range(range.start1 + leftShift, range.end1, range.start2 + leftShift, range.end2));
continue;
}
// shift chunks right [AX A]Y A[Z] -> [AX] AY [AZ]
// [AX A][Z] -> [AX] [AZ]
int rightShift = findSequenceEdgeShift(touchText, touchWords, touchStart - 1, equalRight, false);
if (rightShift > 0) {
newRanges.remove(newRanges.size() - 1);
newRanges.add(new Range(lastRange.start1, lastRange.end1 - rightShift, lastRange.start2, lastRange.end2 - rightShift));
newRanges.add(new Range(range.start1 - rightShift, range.end1, range.start2 - rightShift, range.end2));
continue;
}
}
// nothing to do
newRanges.add(range);
}
return fair(createUnchanged(newRanges, words1.size(), words2.size()));
}
private static <T> int equalRanges(@NotNull List<T> data1, @NotNull List<T> data2, int start1, int start2, int count,
boolean leftToRight) {
for (int i = 0; i < count; i++) {
int shift = leftToRight ? i : count - i - 1;
T val1 = data1.get(start1 + shift);
T val2 = data2.get(start2 + shift);
if (!val1.equals(val2)) return i;
}
return count;
}
private static int findSequenceEdgeShift(@NotNull CharSequence text, @NotNull List<InlineChunk> words, int offset, int count,
boolean leftToRight) {
for (int i = 0; i < count; i++) {
InlineChunk word1;
InlineChunk word2;
if (leftToRight) {
word1 = words.get(offset + i);
word2 = words.get(offset + i + 1);
}
else {
word1 = words.get(offset - i - 1);
word2 = words.get(offset - i);
}
if (isSeparatedWithWhitespace(text, word1, word2)) return i + 1;
}
return -1;
}
private static boolean isSeparatedWithWhitespace(@NotNull CharSequence text, @NotNull InlineChunk word1, @NotNull InlineChunk word2) {
if (word1 instanceof NewlineChunk || word2 instanceof NewlineChunk) return true;
int offset1 = word1.getOffset2();
int offset2 = word2.getOffset1();
for (int i = offset1; i < offset2; i++) {
if (isWhiteSpace(text.charAt(i))) return true;
}
return false;
}
@NotNull
private static FairDiffIterable matchAdjustmentDelimiters(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@@ -61,6 +61,10 @@ public class ComparisonUtilAutoTest extends AutoTestCase {
doTestChar(System.currentTimeMillis(), 30, 30);
}
public void testWord() throws Exception {
doTestWord(System.currentTimeMillis(), 300000, 300);
}
public void testLine() throws Exception {
doTestLine(System.currentTimeMillis(), 30, 300);
}
@@ -147,13 +151,30 @@ public class ComparisonUtilAutoTest extends AutoTestCase {
});
}
private void doTestWord(long seed, int runs, int maxLength) throws Exception {
ComparisonPolicy[] policies = {ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES};
doTest(seed, runs, maxLength, policies, new TestTask() {
@Override
public void run(@NotNull Document text1, @NotNull Document text2, @NotNull ComparisonPolicy policy, @NotNull Ref<Object> debugData) {
CharSequence sequence1 = text1.getCharsSequence();
CharSequence sequence2 = text2.getCharsSequence();
List<DiffFragment> fragments = myComparisonManager.compareWords(sequence1, sequence2, policy, INDICATOR);
debugData.set(fragments);
checkResultWord(sequence1, sequence2, fragments, policy);
}
});
}
private void doTest(long seed, int runs, int maxLength, @NotNull ComparisonPolicy[] policies, @NotNull TestTask test) throws Exception {
myRng.setSeed(seed);
ComparisonPolicy policy = null;
Ref<Object> debugData = new Ref<Object>();
for (int i = 0; i < runs; i++) {
for (int i = 1; i <= runs; i++) {
if (i % 1000 == 0) System.out.println(i);
Document text1 = null;
Document text2 = null;
@@ -188,8 +188,8 @@ public abstract class ComparisonUtilTestBase extends UsefulTestCase {
set2.set(fragment.getStartLine2(), fragment.getEndLine2());
}
assertEquals(policy.name(), set1, matchings.first);
assertEquals(policy.name(), set2, matchings.second);
assertEquals(policy.name(), matchings.first, set1);
assertEquals(policy.name(), matchings.second, set2);
}
private static void checkDiffMatching(@NotNull List<? extends DiffFragment> fragments,
@@ -204,8 +204,8 @@ public abstract class ComparisonUtilTestBase extends UsefulTestCase {
set2.set(fragment.getStartOffset2(), fragment.getEndOffset2());
}
assertEquals(policy.name(), set1, matchings.first);
assertEquals(policy.name(), set2, matchings.second);
assertEquals(policy.name(), matchings.first, set1);
assertEquals(policy.name(), matchings.second, set2);
}
@NotNull
@@ -183,10 +183,6 @@ public class WordComparisonUtilTest extends ComparisonUtilTestBase {
// TODO
}
public void testNonDeterministicCases() {
// TODO
}
public void testAlgorithmSpecific() {
// prefer words over punctuation
TestData.words("...x", "x...")
@@ -199,11 +195,30 @@ public class WordComparisonUtilTest extends ComparisonUtilTestBase {
.____Ignore_("- ", " ")
.all();
TestData.words("y x x", "y x")
._______Def_(" --", " ")
.____Ignore_(" -", " ")
.all();
TestData.words("A X A B", "A B")
._______Def_("---- ", " ")
.____Ignore_("--- ", " ")
.all();
// prefer less modified 'sentences'
TestData.words("A.X A.Z", "A.X A.Y A.Z")
._______Def_(" ", " ---- ")
.____Ignore_(" ", " --- ")
.all();
TestData.words("X.A Z.A", "X.A Y.A Z.A")
._______Def_(" ", " ---- ")
.____Ignore_(" ", " --- ")
.all();
// prefer punctuation over whitespaces
TestData.words(". ", " .")
._______Def_(" ---", "--- ")
.def();
// TODO
}
}
@@ -26,6 +26,6 @@ public interface LightDuplicateProfile {
void process(@NotNull LighterAST ast, @NotNull Callback callback);
interface Callback {
void process(int hash, @NotNull LighterAST ast, @NotNull LighterASTNode... nodes);
void process(int hash, int hash2, @NotNull LighterAST ast, @NotNull LighterASTNode... nodes);
}
}
@@ -56,7 +56,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
private static boolean ourEnabledOldProfiles = false;
@NonNls public static final ID<Integer, TIntArrayList> NAME = ID.create("DuplicatesIndex");
private static final int myBaseVersion = 21;
private static final int myBaseVersion = 23;
private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
@Override
@@ -71,14 +71,18 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
private final DataExternalizer<TIntArrayList> myValueExternalizer = new DataExternalizer<TIntArrayList>() {
@Override
public void save(@NotNull DataOutput out, TIntArrayList list) throws IOException {
if (list.size() == 1) DataInputOutputUtil.writeINT(out, list.getQuick(0));
if (list.size() == 2) {
DataInputOutputUtil.writeINT(out, list.getQuick(0));
DataInputOutputUtil.writeINT(out, list.getQuick(1));
}
else {
DataInputOutputUtil.writeINT(out, -list.size());
int prev = 0;
for (int i = 0, len = list.size(); i < len; ++i) {
for (int i = 0, len = list.size(); i < len; i+=2) {
int value = list.getQuick(i);
DataInputOutputUtil.writeINT(out, value - prev);
prev = value;
DataInputOutputUtil.writeINT(out, list.getQuick(i + 1));
}
}
}
@@ -87,17 +91,20 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
public TIntArrayList read(@NotNull DataInput in) throws IOException {
int capacityOrValue = DataInputOutputUtil.readINT(in);
if (capacityOrValue >= 0) {
TIntArrayList list = new TIntArrayList(1);
TIntArrayList list = new TIntArrayList(2);
list.add(capacityOrValue);
list.add(DataInputOutputUtil.readINT(in));
return list;
}
capacityOrValue = -capacityOrValue;
TIntArrayList list = new TIntArrayList(capacityOrValue);
int prev = 0;
while(capacityOrValue-- > 0) {
while(capacityOrValue > 0) {
int value = DataInputOutputUtil.readINT(in) + prev;
list.add(value);
prev = value;
list.add(DataInputOutputUtil.readINT(in));
capacityOrValue -= 2;
}
return list;
}
@@ -121,12 +128,13 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
((LightDuplicateProfile)profile).process(ast, new LightDuplicateProfile.Callback() {
@Override
public void process(int hash, @NotNull LighterAST ast, @NotNull LighterASTNode... nodes) {
public void process(int hash, int hash2, @NotNull LighterAST ast, @NotNull LighterASTNode... nodes) {
TIntArrayList list = result.get(hash);
if (list == null) {
result.put(hash, list = new TIntArrayList(1));
result.put(hash, list = new TIntArrayList(2));
}
list.add(nodes[0].getStartOffset());
list.add(hash2);
}
});
return result;
@@ -217,6 +225,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
TIntArrayList list = myMap.get(hash);
if (list == null) { myMap.put(hash, list = new TIntArrayList()); }
list.add(frag.getStartOffset());
list.add(0);
}
public THashMap<Integer,TIntArrayList> getMap() {
@@ -25,6 +25,7 @@ import com.intellij.util.SmartList;
import com.intellij.util.indexing.FileBasedIndex;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntLongHashMap;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -57,7 +58,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
((LightDuplicateProfile)profile).process(ast, new LightDuplicateProfile.Callback() {
DuplicatedCodeProcessor<LighterASTNode> myProcessor;
@Override
public void process(int hash, @NotNull final LighterAST ast, @NotNull final LighterASTNode... nodes) {
public void process(int hash, int hash2, @NotNull final LighterAST ast, @NotNull final LighterASTNode... nodes) {
class LightDuplicatedCodeProcessor extends DuplicatedCodeProcessor<LighterASTNode> {
LightDuplicatedCodeProcessor(VirtualFile file, Project project) {
@@ -93,7 +94,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
myProcessor = new LightDuplicatedCodeProcessor(virtualFile, psiFile.getProject());
myProcessorRef.set(myProcessor);
}
myProcessor.process(hash, nodes[0]);
myProcessor.process(hash, hash2, nodes[0]);
}
});
} else {
@@ -153,7 +154,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
myProcessor = new OldDuplicatedCodeProcessor(virtualFile, psiFile.getProject());
myProcessorRef.set(myProcessor);
}
myProcessor.process(hash, frag);
myProcessor.process(hash, 0, frag);
}
}, true).visitNode(psiFile);
}
@@ -174,9 +175,9 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
final int offsetInOtherFile = processor.reportedOffsetInOtherFiles.get(offset);
LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile);
int hash = processor.fragmentHash.get(offset);
long hash = processor.fragmentHash.get(offset);
LocalQuickFix viewAllDupesFix = hash != 0 ? createShowOtherDupesFix(virtualFile, offset, hash, psiFile.getProject()) : null;
LocalQuickFix viewAllDupesFix = hash != 0 ? createShowOtherDupesFix(virtualFile, offset, (int)hash, (int)(hash >> 32), psiFile.getProject()) : null;
ProblemDescriptor descriptor = manager
.createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix, viewAllDupesFix);
@@ -190,7 +191,7 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
protected LocalQuickFix createNavigateToDupeFix(@NotNull VirtualFile file, int offsetInOtherFile) {
return null;
}
protected LocalQuickFix createShowOtherDupesFix(VirtualFile file, int offset, int hash, Project project) {
protected LocalQuickFix createShowOtherDupesFix(VirtualFile file, int offset, int hash, int hash2, Project project) {
return null;
}
@@ -200,12 +201,13 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
final TIntObjectHashMap<PsiElement> reportedPsi = new TIntObjectHashMap<PsiElement>();
final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap();
final TIntIntHashMap fragmentSize = new TIntIntHashMap();
final TIntIntHashMap fragmentHash = new TIntIntHashMap();
final TIntLongHashMap fragmentHash = new TIntLongHashMap();
final VirtualFile virtualFile;
final Project project;
final ProjectFileIndex myProjectFileIndex;
T myNode;
int myHash;
int myHash2;
DuplicatedCodeProcessor(VirtualFile file, Project project) {
virtualFile = file;
@@ -213,25 +215,27 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
}
void process(int hash, T node) {
void process(int hash, int hash2, T node) {
ProgressManager.checkCanceled();
myNode = node;
myHash = hash;
myHash2 = hash2;
FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, this, GlobalSearchScope.projectScope(project));
}
@Override
public boolean process(VirtualFile file, TIntArrayList list) {
for(int i = 0, len = list.size(); i < len; ++i) {
for(int i = 0, len = list.size(); i < len; i+=2) {
ProgressManager.checkCanceled();
int value = list.getQuick(i);
if (list.getQuick(i + 1) != myHash2) continue;
int offset = list.getQuick(i);
if (myProjectFileIndex.isInSource(virtualFile) && !myProjectFileIndex.isInSource(file)) return true;
if (!myProjectFileIndex.isInSource(virtualFile) && myProjectFileIndex.isInSource(file)) return true;
final int startOffset = getStartOffset(myNode);
final int endOffset = getEndOffset(myNode);
if (file.equals(virtualFile) && value >= startOffset && value < endOffset) continue;
if (file.equals(virtualFile) && offset >= startOffset && offset < endOffset) continue;
PsiElement target = getPsi(myNode);
TextRange rangeInElement = getRangeInElement(myNode);
@@ -252,10 +256,12 @@ public class DuplicatesInspectionBase extends LocalInspectionTool {
reportedRanges.put(fragmentStartOffsetInteger, rangeInElement);
reportedFiles.put(fragmentStartOffsetInteger, file);
reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value);
reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, offset);
reportedPsi.put(fragmentStartOffsetInteger, target);
fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize);
if (newFragmentSize >= MIN_FRAGMENT_SIZE || isLightProfile()) fragmentHash.put(fragmentStartOffsetInteger, myHash);
if (newFragmentSize >= MIN_FRAGMENT_SIZE || isLightProfile()) {
fragmentHash.put(fragmentStartOffsetInteger, (myHash & 0xFFFFFFFFL) | ((long)myHash2 << 32));
}
return false;
}
return true;
@@ -65,4 +65,8 @@ public class CompletionResult {
return myMatcher.isStartMatch(myLookupElement);
}
@Override
public String toString() {
return myLookupElement.toString();
}
}
@@ -41,7 +41,7 @@ public abstract class LookupElementDecorator<T extends LookupElement> extends Lo
@Override
public boolean isValid() {
return super.isValid() && myDelegate.isValid();
return myDelegate.isValid() && super.isValid();
}
@Override
@@ -115,7 +115,7 @@ public abstract class RunManager {
* Returns the list of all temporary run configurations.
*
* @return the list of all temporary run configurations.
* @see com.intellij.execution.RunnerAndConfigurationSettings#isTemporary()
* @see RunnerAndConfigurationSettings#isTemporary()
*/
@NotNull
@Deprecated
@@ -125,7 +125,7 @@ public abstract class RunManager {
* Returns the list of all temporary run configurations settings.
*
* @return the list of all temporary run configurations settings.
* @see com.intellij.execution.RunnerAndConfigurationSettings#isTemporary()
* @see RunnerAndConfigurationSettings#isTemporary()
*/
@NotNull
public abstract List<RunnerAndConfigurationSettings> getTempConfigurationsList();
@@ -134,7 +134,7 @@ public abstract class RunManager {
* Checks if the specified run configuration is temporary and will be deleted when the temporary configurations limit is exceeded.
*
* @return true if the configuration is temporary, false otherwise.
* @see com.intellij.execution.RunnerAndConfigurationSettings#isTemporary()
* @see RunnerAndConfigurationSettings#isTemporary()
*/
@Deprecated
public abstract boolean isTemporary(@NotNull RunConfiguration configuration);
@@ -176,7 +176,7 @@ public abstract class RunManager {
* @param name the name of the configuration to create (should be unique and not equal to any other existing configuration)
* @param type the type of the configuration to create.
* @return the configuration settings object.
* @see RunManager#suggestUniqueName(String, java.util.ArrayList)
* @see RunManager#suggestUniqueName(String, Collection)
*/
@NotNull
public abstract RunnerAndConfigurationSettings createRunConfiguration(@NotNull String name, @NotNull ConfigurationFactory type);
@@ -43,6 +43,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -66,6 +67,7 @@ public class ConfigurationContext {
private List<RuntimeConfigurationProducer> myPreferredProducers;
private List<ConfigurationFromContext> myConfigurationsFromContext;
@NotNull
public static ConfigurationContext getFromContext(DataContext dataContext) {
final ConfigurationContext context = new ConfigurationContext(dataContext);
final DataManager dataManager = DataManager.getInstance();
@@ -231,4 +231,15 @@ public abstract class RunConfigurationProducer<T extends RunConfiguration> {
assert false : aClass;
return null;
}
@Nullable
public ConfigurationFromContext createLightConfiguration(@NotNull final ConfigurationContext context) {
RunConfiguration configuration = myConfigurationFactory.createTemplateConfiguration(context.getProject());
RunnerAndConfigurationSettings settings =
RunManager.getInstance(context.getProject()).createConfiguration(configuration, myConfigurationFactory);
if (!setupConfigurationFromContext((T)configuration, context, new Ref<PsiElement>(context.getPsiLocation()))) {
return null;
}
return new ConfigurationFromContextImpl(this, settings, context.getPsiLocation());
}
}
@@ -66,16 +66,16 @@ public class ExecutionEnvironment extends UserDataHolderBase implements Disposab
public ExecutionEnvironment(@NotNull Executor executor,
@NotNull ProgramRunner runner,
@NotNull RunnerAndConfigurationSettings configuration,
@NotNull RunnerAndConfigurationSettings settings,
@NotNull Project project) {
this(configuration.getConfiguration(),
this(settings.getConfiguration(),
executor,
DefaultExecutionTarget.INSTANCE,
project,
configuration.getRunnerSettings(runner),
configuration.getConfigurationSettings(runner),
null,
settings.getRunnerSettings(runner),
settings.getConfigurationSettings(runner),
null,
settings,
runner);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package com.intellij.lang.documentation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
@@ -29,6 +30,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class CompositeDocumentationProvider extends DocumentationProviderEx implements ExternalDocumentationProvider, ExternalDocumentationHandler {
private static final Logger LOG = Logger.getInstance(CompositeDocumentationProvider.class);
private final List<DocumentationProvider> myProviders;
@@ -66,6 +68,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationHandler &&
((ExternalDocumentationHandler)provider).handleExternal(element, originalElement)) {
LOG.debug("handleExternal: ", provider);
return true;
}
}
@@ -78,6 +81,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationHandler &&
((ExternalDocumentationHandler)provider).handleExternalLink(psiManager, link, context)) {
LOG.debug("handleExternalLink: ", provider);
return true;
}
}
@@ -89,6 +93,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public boolean canFetchDocumentationLink(String link) {
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)provider).canFetchDocumentationLink(link)) {
LOG.debug("canFetchDocumentationLink: ", provider);
return true;
}
}
@@ -101,6 +106,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public String fetchExternalDocumentation(@NotNull String link, @Nullable PsiElement element) {
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)provider).canFetchDocumentationLink(link)) {
LOG.debug("fetchExternalDocumentation: ", provider);
return ((ExternalDocumentationHandler)provider).fetchExternalDocumentation(link, element);
}
}
@@ -112,7 +118,10 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
for (DocumentationProvider provider : getAllProviders()) {
String result = provider.getQuickNavigateInfo(element, originalElement);
if (result != null) return result;
if (result != null) {
LOG.debug("getQuickNavigateInfo: ", provider);
return result;
}
}
return null;
}
@@ -122,6 +131,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
List<String> result = provider.getUrlFor(element, originalElement);
if (result != null) {
LOG.debug("getUrlFor: ", provider);
return result;
}
}
@@ -133,6 +143,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
String result = provider.generateDoc(element, originalElement);
if (result != null) {
LOG.debug("generateDoc: ", provider);
return result;
}
}
@@ -144,6 +155,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
PsiElement result = provider.getDocumentationElementForLookupItem(psiManager, object, element);
if (result != null) {
LOG.debug("getDocumentationElementForLookupItem: ", provider);
return result;
}
}
@@ -154,7 +166,10 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
for (DocumentationProvider provider : getAllProviders()) {
PsiElement result = provider.getDocumentationElementForLink(psiManager, link, context);
if (result != null) return result;
if (result != null) {
LOG.debug("getDocumentationElementForLink: ", provider);
return result;
}
}
return null;
}
@@ -164,6 +179,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public CodeDocumentationProvider getFirstCodeDocumentationProvider() {
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof CodeDocumentationProvider) {
LOG.debug("getFirstCodeDocumentationProvider: ", provider);
return (CodeDocumentationProvider)provider;
}
}
@@ -176,6 +192,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
if (provider instanceof ExternalDocumentationProvider) {
final String doc = ((ExternalDocumentationProvider)provider).fetchExternalDocumentation(project, element, docUrls);
if (doc != null) {
LOG.debug("fetchExternalDocumentation: ", provider);
return doc;
}
}
@@ -187,10 +204,16 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
public boolean hasDocumentationFor(PsiElement element, PsiElement originalElement) {
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationProvider) {
if (((ExternalDocumentationProvider)provider).hasDocumentationFor(element, originalElement)) return true;
if (((ExternalDocumentationProvider)provider).hasDocumentationFor(element, originalElement)) {
LOG.debug("hasDocumentationFor: ", provider);
return true;
}
}
else {
if (hasUrlsFor(provider, element, originalElement)) return true;
if (hasUrlsFor(provider, element, originalElement)) {
LOG.debug("handleExternal(hasUrlsFor): ", provider);
return true;
}
}
}
return false;
@@ -201,6 +224,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
for (DocumentationProvider provider : getAllProviders()) {
if (provider instanceof ExternalDocumentationProvider &&
((ExternalDocumentationProvider)provider).canPromptToConfigureDocumentation(element)) {
LOG.debug("canPromptToConfigureDocumentation: ", provider);
return true;
}
}
@@ -213,6 +237,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
if (provider instanceof ExternalDocumentationProvider &&
((ExternalDocumentationProvider)provider).canPromptToConfigureDocumentation(element)) {
((ExternalDocumentationProvider)provider).promptToConfigureDocumentation(element);
LOG.debug("promptToConfigureDocumentation: ", provider);
break;
}
}
@@ -233,6 +258,7 @@ public class CompositeDocumentationProvider extends DocumentationProviderEx impl
if (provider instanceof DocumentationProviderEx) {
PsiElement element = ((DocumentationProviderEx)provider).getCustomDocumentationElement(editor, file, contextElement);
if (element != null) {
LOG.debug("getCustomDocumentationElement: ", provider);
return element;
}
}
@@ -18,6 +18,7 @@ package com.intellij.psi.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionException;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
@@ -26,6 +27,7 @@ import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.Processor;
import com.intellij.util.SystemProperties;
@@ -637,9 +639,11 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea
return options;
}
}
FileIndentOptionsProvider[] providers = Extensions.getExtensions(FileIndentOptionsProvider.EP_NAME);
for (FileIndentOptionsProvider provider : providers) {
boolean committedDocumentNeeded = false;
for (FileIndentOptionsProvider provider : Extensions.getExtensions(FileIndentOptionsProvider.EP_NAME)) {
if (!isFullReformat || provider.useOnFullReformat()) {
committedDocumentNeeded |= provider instanceof ProviderForCommittedDocument;
IndentOptions indentOptions = provider.getIndentOptions(this, file);
if (indentOptions != null) {
if (providerProcessor != null) {
@@ -651,12 +655,29 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea
}
}
}
return getIndentOptions(file.getFileType());
IndentOptions options = getIndentOptions(file.getFileType());
if (committedDocumentNeeded) {
markOptionsInaccurateIfDocumentUncommitted(options, file);
}
return options;
}
else
return OTHER_INDENT_OPTIONS;
}
private static void markOptionsInaccurateIfDocumentUncommitted(@NotNull IndentOptions options, @NotNull PsiFile file) {
PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject());
Document document = manager.getDocument(file);
if (document != null && !manager.isCommitted(document)) {
options.setRecalculateForCommittedDocument(true);
}
}
public static boolean isRecalculateForCommittedDocument(@NotNull IndentOptions options) {
return options.isRecalculateForCommittedDocument();
}
private static boolean isFileFullyCoveredByRange(@NotNull PsiFile file, @Nullable TextRange formatRange) {
return
formatRange != null &&
@@ -922,6 +922,7 @@ public class CommonCodeStyleSettings {
private FileIndentOptionsProvider myFileIndentOptionsProvider;
private static final Key<CommonCodeStyleSettings.IndentOptions> INDENT_OPTIONS_KEY = Key.create("INDENT_OPTIONS_KEY");
private boolean myInaccurate;
@Override
public void readExternal(Element element) throws InvalidDataException {
@@ -1021,5 +1022,13 @@ public class CommonCodeStyleSettings {
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
return document != null ? document.getUserData(INDENT_OPTIONS_KEY) : null;
}
boolean isRecalculateForCommittedDocument() {
return myInaccurate;
}
void setRecalculateForCommittedDocument(boolean value) {
myInaccurate = value;
}
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle;
public interface ProviderForCommittedDocument {
}
@@ -1,113 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.event.EditorFactoryEvent;
import com.intellij.openapi.editor.event.EditorFactoryListener;
import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
public class SmartPointerEditorTracker extends AbstractProjectComponent implements DocumentListener, EditorFactoryListener, DocumentBulkUpdateListener {
private final SmartPointerManagerImpl mySmartPointerManager;
public SmartPointerEditorTracker(Project project,
EditorFactory editorFactory,
SmartPointerManager manager,
MessageBus bus) {
super(project);
mySmartPointerManager = (SmartPointerManagerImpl)manager;
editorFactory.getEventMulticaster().addDocumentListener(this, project);
editorFactory.addEditorFactoryListener(this, project);
bus.connect().subscribe(DocumentBulkUpdateListener.TOPIC, this);
}
@Override
public void beforeDocumentChange(DocumentEvent event) {
Document document = event.getDocument();
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
boolean isBulk = document instanceof DocumentEx && ((DocumentEx)document).isInBulkUpdate();
boolean isRelevant = virtualFile != null && isRelevant(virtualFile);
if (!isBulk && isRelevant && shouldNotifySmartPointers(virtualFile)) {
mySmartPointerManager.fastenBelts(virtualFile);
}
}
@Override
public void documentChanged(DocumentEvent event) {
}
@Override
public void editorCreated(@NotNull EditorFactoryEvent event) {
final Editor editor = event.getEditor();
if (editor.getProject() != null && editor.getProject() != myProject || myProject.isDisposed()) return;
final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
if (psiFile == null) return;
final VirtualFile virtualFile = psiFile.getVirtualFile();
// materialize all range markers and do not let them to be collected to improve responsiveness
if (virtualFile != null) {
mySmartPointerManager.fastenBelts(virtualFile);
}
}
@Override
public void editorReleased(@NotNull EditorFactoryEvent event) {
}
private boolean shouldNotifySmartPointers(@NotNull VirtualFile virtualFile) {
// for an open file do not do fasten/unfasten, they should always stay fastened to improve responsiveness
return !FileEditorManager.getInstance(myProject).isFileOpen(virtualFile);
}
private boolean isRelevant(@NotNull VirtualFile virtualFile) {
return !virtualFile.getFileType().isBinary() && !myProject.isDisposed();
}
@Override
public void updateStarted(@NotNull Document document) {
final VirtualFile virtualFile = getVirtualFile(document);
if (virtualFile != null && isRelevant(virtualFile) && shouldNotifySmartPointers(virtualFile)) {
mySmartPointerManager.fastenBelts(virtualFile);
}
}
@Override
public void updateFinished(@NotNull Document document) {
}
private static VirtualFile getVirtualFile(@NotNull Document document) {
final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
return virtualFile == null || !virtualFile.isValid() ? null : virtualFile;
}
}
@@ -29,10 +29,7 @@ import com.intellij.codeInsight.unwrap.ScopeHighlighter;
import com.intellij.codeInspection.SuppressIntentionActionFromFix;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
@@ -43,6 +40,7 @@ import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.event.EditorFactoryAdapter;
import com.intellij.openapi.editor.event.EditorFactoryEvent;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
@@ -60,6 +58,7 @@ import com.intellij.ui.LightweightHint;
import com.intellij.ui.PopupMenuListenerAdapter;
import com.intellij.ui.RowIcon;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.WizardPopup;
import com.intellij.util.Alarm;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ThreeState;
@@ -76,6 +75,7 @@ import javax.swing.event.ListSelectionListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
@@ -458,6 +458,22 @@ public class IntentionHintComponent implements Disposable, ScrollAwareHint {
Disposer.dispose(myPopup);
}
myPopup = JBPopupFactory.getInstance().createListPopup(step);
if (myPopup instanceof WizardPopup) {
Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_SHOW_INTENTION_ACTIONS);
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof KeyboardShortcut) {
KeyboardShortcut keyboardShortcut = (KeyboardShortcut)shortcut;
if (keyboardShortcut.getSecondKeyStroke() == null) {
((WizardPopup)myPopup).registerAction("activateSelectedElement", keyboardShortcut.getFirstKeyStroke(), new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
myPopup.handleSelect(true);
}
});
}
}
}
}
boolean committed = PsiDocumentManager.getInstance(myFile.getProject()).isCommitted(myEditor.getDocument());
final PsiFile injectedFile = committed ? InjectedLanguageUtil.findInjectedPsiNoCommit(myFile, myEditor.getCaretModel().getOffset()) : null;
@@ -242,7 +242,7 @@ public abstract class GotoTargetHandler implements CodeInsightActionHandler {
};
}
public static PsiElementListCellRenderer createRenderer(GotoData gotoData, PsiElement eachTarget) {
protected static PsiElementListCellRenderer createRenderer(GotoData gotoData, PsiElement eachTarget) {
PsiElementListCellRenderer renderer = null;
for (GotoTargetRendererProvider eachProvider : Extensions.getExtensions(GotoTargetRendererProvider.EP_NAME)) {
renderer = eachProvider.getRenderer(eachTarget, gotoData);
@@ -83,7 +83,7 @@ public class EnvironmentVariablesData {
return DEFAULT;
}
Map<String, String> envs = ImmutableMap.of();
String passParentEnvsStr = element.getAttributeValue(PASS_PARENT_ENVS);
String passParentEnvsStr = envsElement.getAttributeValue(PASS_PARENT_ENVS);
boolean passParentEnvs = passParentEnvsStr == null || Boolean.parseBoolean(passParentEnvsStr);
for (Element envElement : envsElement.getChildren(ENV)) {
String envName = envElement.getAttributeValue(NAME);
@@ -21,6 +21,11 @@ import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.Location;
import com.intellij.execution.PsiLocation;
import com.intellij.execution.actions.BaseRunConfigurationAction;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.ConfigurationFromContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.execution.configurations.LocatableConfiguration;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.util.UserDataHolderBase;
@@ -34,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -65,7 +71,7 @@ public class RunLineMarkerInfo extends LineMarkerInfo<PsiElement> {
new Function<Executor, AnAction>() {
@Override
public AnAction fun(Executor executor) {
return new ActionWrapper(ActionManager.getInstance().getAction(executor.getContextActionId()));
return new ActionWrapper(ActionManager.getInstance().getAction(executor.getContextActionId()), executor);
}
}));
actions.add(Separator.getInstance());
@@ -75,7 +81,7 @@ public class RunLineMarkerInfo extends LineMarkerInfo<PsiElement> {
@Override
public AnAction fun(RunLineMarkerContributor contributor) {
AnAction action = contributor.getAdditionalAction(myElement);
return action != null ? new ActionWrapper(action) : null;
return action != null ? new ActionWrapper(action, null) : null;
}
}));
@@ -92,26 +98,37 @@ public class RunLineMarkerInfo extends LineMarkerInfo<PsiElement> {
private class ActionWrapper extends AnAction {
private final AnAction myOrigin;
@Nullable
private final Executor myExecutor;
public ActionWrapper(@NotNull AnAction origin) {
public ActionWrapper(@NotNull AnAction origin, @Nullable Executor executor) {
myOrigin = origin;
myExecutor = executor;
copyFrom(origin);
}
@Override
public void update(AnActionEvent e) {
myOrigin.update(createEvent(e));
AnActionEvent event = wrapEvent(e);
if (myExecutor != null) {
String name = getActionName(event.getDataContext(), myExecutor);
e.getPresentation().setVisible(name != null);
e.getPresentation().setText(name);
}
else {
myOrigin.update(event);
}
}
@NotNull
private AnActionEvent createEvent(AnActionEvent e) {
private AnActionEvent wrapEvent(AnActionEvent e) {
return new AnActionEvent(
e.getInputEvent(), new MyDataContext(e.getDataContext()), e.getPlace(), e.getPresentation(), e.getActionManager(), e.getModifiers());
}
@Override
public void actionPerformed(AnActionEvent e) {
myOrigin.actionPerformed(createEvent(e));
myOrigin.actionPerformed(wrapEvent(e));
}
}
@@ -129,4 +146,21 @@ public class RunLineMarkerInfo extends LineMarkerInfo<PsiElement> {
return myDelegate.getData(dataId);
}
}
public String getActionName(DataContext dataContext, @NotNull Executor executor) {
final ConfigurationContext context = ConfigurationContext.getFromContext(dataContext);
List<RunConfigurationProducer<?>> producers = RunConfigurationProducer.getProducers(context.getProject());
List<ConfigurationFromContext> list = ContainerUtil.mapNotNull(producers,
new Function<RunConfigurationProducer<?>, ConfigurationFromContext>() {
@Override
public ConfigurationFromContext fun(RunConfigurationProducer<?> producer) {
return producer.createLightConfiguration(context);
}
}
);
if (list.isEmpty()) return null;
Collections.sort(list, ConfigurationFromContext.COMPARATOR);
String actionName = BaseRunConfigurationAction.suggestRunActionName((LocatableConfiguration)list.get(0).getConfiguration());
return executor.getStartActionText(actionName);
}
}
@@ -374,6 +374,7 @@ public class FindDialog extends DialogWrapper {
if(myResultsPreviewTable != null &&
myContent.getSelectedIndex() == RESULTS_PREVIEW_TAB_INDEX) {
newAction.run();
return;
}
if (previousAction != null) {

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