mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 17:03:12 +07:00
Merge remote-tracking branch 'origin/master'
Conflicts: platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
+43
-22
@@ -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;
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -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;
|
||||
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
-45
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+135
-223
@@ -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;
|
||||
}
|
||||
+2
-2
@@ -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())) {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+6
-14
@@ -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
|
||||
|
||||
+22
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-3
@@ -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();
|
||||
|
||||
+13
-12
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -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++) {
|
||||
|
||||
+6
-8
@@ -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 void <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('}')
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* {@value FIELD}
|
||||
*/
|
||||
class Test {}
|
||||
+33
@@ -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('}')
|
||||
+6
@@ -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>
|
||||
}
|
||||
}
|
||||
+10
@@ -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);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
class Abc {
|
||||
void foo() {
|
||||
|
||||
new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
final String na<caret>me = "name";
|
||||
System.out.println(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
class Abc {
|
||||
void foo(final String name) {
|
||||
|
||||
new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -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"); }
|
||||
|
||||
+4
@@ -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'
|
||||
|
||||
|
||||
+2
@@ -150,6 +150,8 @@ public void testConvertToObjectStream() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testCollectionsEmptyMap() { doTest(true); }
|
||||
|
||||
private void doTest() {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
+5
-1
@@ -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(
|
||||
|
||||
+72
-7
@@ -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
|
||||
|
||||
+48
@@ -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() {
|
||||
|
||||
+23
-64
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-7
@@ -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() {
|
||||
|
||||
+19
-13
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+31
-5
@@ -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 {
|
||||
}
|
||||
-113
@@ -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;
|
||||
}
|
||||
}
|
||||
+20
-4
@@ -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);
|
||||
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user