mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -20,6 +20,7 @@ import com.intellij.openapi.compiler.CompileScope;
|
||||
import com.intellij.openapi.compiler.CompilerMessage;
|
||||
import com.intellij.openapi.compiler.CompilerMessageCategory;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.Navigatable;
|
||||
@@ -30,6 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
@@ -37,6 +39,8 @@ import java.util.StringTokenizer;
|
||||
*/
|
||||
public abstract class ProblemsView {
|
||||
|
||||
private final Project myProject;
|
||||
|
||||
public static class SERVICE {
|
||||
private SERVICE() {
|
||||
}
|
||||
@@ -46,29 +50,30 @@ public abstract class ProblemsView {
|
||||
}
|
||||
}
|
||||
|
||||
protected ProblemsView(Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
public abstract void clearMessages(CompileScope scope);
|
||||
|
||||
public abstract void clearMessages();
|
||||
|
||||
public abstract void addMessage(int type, @NotNull String[] text, @Nullable VirtualFile file, int line, int column, @Nullable Object data);
|
||||
public abstract void addMessage(int type, @NotNull String[] text, @Nullable String groupName, @NotNull Navigatable navigatable, @Nullable String exportTextPrefix, @Nullable String rendererTextPrefix, @NotNull UUID sessionId);
|
||||
|
||||
public abstract void addMessage(int type, @NotNull String[] text, @Nullable VirtualFile underFileGroup, @Nullable VirtualFile file, int line, int column, @Nullable Object data);
|
||||
|
||||
public abstract void addMessage(int type, @NotNull String[] text, @Nullable String groupName, @NotNull Navigatable navigatable, @Nullable String exportTextPrefix, @Nullable String rendererTextPrefix, @Nullable Object data);
|
||||
|
||||
public void addMessage(CompilerMessage message) {
|
||||
final Navigatable navigatable = message.getNavigatable();
|
||||
public void addMessage(CompilerMessage message, @NotNull UUID sessionId) {
|
||||
final VirtualFile file = message.getVirtualFile();
|
||||
Navigatable navigatable = message.getNavigatable();
|
||||
if (navigatable == null) {
|
||||
if (file == null) {
|
||||
return; // both navigatable and file must not be null
|
||||
}
|
||||
navigatable = new OpenFileDescriptor(myProject, file, -1, -1);
|
||||
}
|
||||
final CompilerMessageCategory category = message.getCategory();
|
||||
final int type = CompilerTask.translateCategory(category);
|
||||
final String[] text = convertMessage(message);
|
||||
if (navigatable != null) {
|
||||
final String groupName = file != null? file.getPresentableUrl() : category.getPresentableText();
|
||||
addMessage(type, text, groupName, navigatable, message.getExportTextPrefix(), message.getRenderTextPrefix(), message.getVirtualFile());
|
||||
}
|
||||
else {
|
||||
addMessage(type, text, file, -1, -1, message.getVirtualFile());
|
||||
}
|
||||
final String groupName = file != null? file.getPresentableUrl() : category.getPresentableText();
|
||||
addMessage(type, text, groupName, navigatable, message.getExportTextPrefix(), message.getRenderTextPrefix(), sessionId);
|
||||
}
|
||||
|
||||
public abstract void setProgress(String text, float fraction);
|
||||
|
||||
@@ -81,6 +81,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
|
||||
private final ProjectFileIndex myProjectFileIndex; // cached for performance reasons
|
||||
private final ProjectCompileScope myProjectCompileScope;
|
||||
private final long myStartCompilationStamp;
|
||||
private final UUID mySessionId = UUID.randomUUID();
|
||||
|
||||
public CompileContextImpl(final Project project,
|
||||
final CompilerTask compilerSession,
|
||||
@@ -254,7 +255,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
|
||||
myTask.addMessage(msg);
|
||||
}
|
||||
if (myShouldUpdateProblemsView && msg.getCategory() == CompilerMessageCategory.ERROR) {
|
||||
ProblemsViewImpl.SERVICE.getInstance(myProject).addMessage(msg);
|
||||
ProblemsViewImpl.SERVICE.getInstance(myProject).addMessage(msg, mySessionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -444,14 +444,14 @@ public class CompileDriver {
|
||||
buildManager.cancelAutoMakeTasks(myProject);
|
||||
return buildManager.scheduleBuild(myProject, compileContext.isRebuild(), compileContext.isMake(), scopes, paths, builderParams, new DefaultMessageHandler(myProject) {
|
||||
@Override
|
||||
public void buildStarted() {
|
||||
public void buildStarted(UUID sessionId) {
|
||||
final ProblemsView view = ProblemsViewImpl.SERVICE.getInstance(myProject);
|
||||
view.clearMessages(compileContext.getCompileScope());
|
||||
view.clearProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated() {
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -466,7 +466,7 @@ public class CompileDriver {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCompileMessage(CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
|
||||
protected void handleCompileMessage(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
|
||||
final CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind kind = message.getKind();
|
||||
//System.out.println(compilerMessage.getText());
|
||||
if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.PROGRESS) {
|
||||
@@ -494,7 +494,7 @@ public class CompileDriver {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBuildEvent(CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
|
||||
protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
|
||||
final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Type eventType = event.getEventType();
|
||||
switch (eventType) {
|
||||
case FILES_GENERATED:
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.compiler.CompileScope;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowAnchor;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
@@ -31,6 +30,8 @@ import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 9/18/12
|
||||
@@ -41,6 +42,7 @@ public class ProblemsViewImpl extends ProblemsView{
|
||||
private final ProblemsViewPanel myPanel;
|
||||
|
||||
public ProblemsViewImpl(final Project project, final ToolWindowManager wm) {
|
||||
super(project);
|
||||
myPanel = new ProblemsViewPanel(project);
|
||||
Disposer.register(project, new Disposable() {
|
||||
@Override
|
||||
@@ -86,28 +88,13 @@ public class ProblemsViewImpl extends ProblemsView{
|
||||
myPanel.clearMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessage(final int type, @NotNull final String[] text, @Nullable final VirtualFile file, final int line, final int column, @Nullable final Object data) {
|
||||
myPanel.addMessage(type, text, file, line, column, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessage(final int type,
|
||||
@NotNull final String[] text,
|
||||
@Nullable final VirtualFile underFileGroup,
|
||||
@Nullable final VirtualFile file,
|
||||
final int line,
|
||||
final int column, @Nullable final Object data) {
|
||||
myPanel.addMessage(type, text, underFileGroup, file, line, column, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessage(final int type,
|
||||
@NotNull final String[] text,
|
||||
@Nullable final String groupName,
|
||||
@NotNull final Navigatable navigatable,
|
||||
@Nullable final String exportTextPrefix, @Nullable final String rendererTextPrefix, @Nullable final Object data) {
|
||||
myPanel.addMessage(type, text, groupName, navigatable, exportTextPrefix, rendererTextPrefix, data);
|
||||
@Nullable final String exportTextPrefix, @Nullable final String rendererTextPrefix, @Nullable final UUID sessionId) {
|
||||
myPanel.addMessage(type, text, groupName, navigatable, exportTextPrefix, rendererTextPrefix, sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,14 +53,14 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildStarted() {
|
||||
public void buildStarted(UUID sessionId) {
|
||||
final ProblemsView view = ProblemsView.SERVICE.getInstance(myProject);
|
||||
view.clearMessages();
|
||||
view.clearProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBuildEvent(CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
|
||||
protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
|
||||
if (myProject.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCompileMessage(CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
|
||||
protected void handleCompileMessage(final UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
|
||||
if (myProject.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
|
||||
final VirtualFile vFile = sourceFilePath != null? LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(sourceFilePath)) : null;
|
||||
final long line = message.hasLine() ? message.getLine() : -1;
|
||||
final long column = message.hasColumn() ? message.getColumn() : -1;
|
||||
ProblemsView.SERVICE.getInstance(myProject).addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.ERROR, message.getText(), vFile, (int)line, (int)column, null));
|
||||
ProblemsView.SERVICE.getInstance(myProject).addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.ERROR, message.getText(), vFile, (int)line, (int)column, null), sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class AutoMakeMessageHandler extends DefaultMessageHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionTerminated() {
|
||||
public void sessionTerminated(UUID sessionId) {
|
||||
String statusMessage = null/*"Auto make completed"*/;
|
||||
switch (myBuildStatus) {
|
||||
case SUCCESS:
|
||||
|
||||
@@ -197,7 +197,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: probably we do not need this excessive filtering
|
||||
for (Project project : activeProjects) {
|
||||
if (!project.isInitialized() || ProjectRootManager.getInstance(project).getFileIndex().isInContent(eventFile)) {
|
||||
return true;
|
||||
@@ -451,7 +451,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
catch (Exception e) {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -469,7 +469,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
@Override
|
||||
public void run() {
|
||||
if (future.isCancelled() || project.isDisposed()) {
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
future.setDone();
|
||||
return;
|
||||
}
|
||||
@@ -516,7 +516,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
try {
|
||||
if (project.isDisposed()) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
return;
|
||||
}
|
||||
myBuildsInProgress.put(projectPath, future);
|
||||
@@ -533,7 +533,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
public void processTerminated(ProcessEvent event) {
|
||||
final BuilderMessageHandler handler = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
if (handler != null) {
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
catch (ExecutionException e) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
}
|
||||
finally {
|
||||
myBuildsInProgress.remove(projectPath);
|
||||
@@ -584,7 +584,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
catch (Throwable e) {
|
||||
myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
future.setDone();
|
||||
}
|
||||
}
|
||||
@@ -594,7 +594,7 @@ public class BuildManager implements ApplicationComponent{
|
||||
}
|
||||
catch (Throwable e) {
|
||||
handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionId);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -116,7 +116,7 @@ class BuildMessageDispatcher extends SimpleChannelHandler {
|
||||
if (msgType == CmdlineRemoteProto.Message.BuilderMessage.Type.PARAM_REQUEST) {
|
||||
final CmdlineRemoteProto.Message.ControllerMessage params = sessionData.params;
|
||||
if (params != null) {
|
||||
handler.buildStarted();
|
||||
handler.buildStarted(sessionId);
|
||||
sessionData.params = null;
|
||||
Channels.write(ctx.getChannel(), CmdlineProtoUtil.toMessage(sessionId, params));
|
||||
}
|
||||
@@ -146,7 +146,7 @@ class BuildMessageDispatcher extends SimpleChannelHandler {
|
||||
final BuilderMessageHandler handler = unregisterBuildMessageHandler(sessionData.sessionId);
|
||||
if (handler != null) {
|
||||
// notify the handler only if it has not been notified yet
|
||||
handler.sessionTerminated();
|
||||
handler.sessionTerminated(sessionData.sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ import java.util.UUID;
|
||||
* Date: 4/25/12
|
||||
*/
|
||||
public interface BuilderMessageHandler {
|
||||
void buildStarted();
|
||||
void buildStarted(UUID sessionId);
|
||||
|
||||
void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg);
|
||||
|
||||
void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure);
|
||||
|
||||
void sessionTerminated();
|
||||
void sessionTerminated(UUID sessionId);
|
||||
}
|
||||
|
||||
@@ -63,17 +63,17 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildStarted() {
|
||||
public void buildStarted(UUID sessionId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleBuildMessage(final Channel channel, final UUID sessionId, final CmdlineRemoteProto.Message.BuilderMessage msg) {
|
||||
switch (msg.getType()) {
|
||||
case BUILD_EVENT:
|
||||
handleBuildEvent(msg.getBuildEvent());
|
||||
handleBuildEvent(sessionId, msg.getBuildEvent());
|
||||
break;
|
||||
case COMPILE_MESSAGE:
|
||||
handleCompileMessage(msg.getCompileMessage());
|
||||
handleCompileMessage(sessionId, msg.getCompileMessage());
|
||||
break;
|
||||
case CONSTANT_SEARCH_TASK:
|
||||
final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task = msg.getConstantSearchTask();
|
||||
@@ -87,9 +87,9 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handleCompileMessage(CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message);
|
||||
protected abstract void handleCompileMessage(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message);
|
||||
|
||||
protected abstract void handleBuildEvent(CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
|
||||
protected abstract void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
|
||||
|
||||
private void handleConstantSearchTask(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
|
||||
final String ownerClassName = task.getOwnerClassName();
|
||||
|
||||
+1
@@ -375,6 +375,7 @@ public class JavaMemberNameCompletionContributor extends CompletionContributor {
|
||||
|
||||
for (final PsiField field : fields) {
|
||||
if (field == element) continue;
|
||||
assert field.isValid();
|
||||
final PsiModifierList modifierList = field.getModifierList();
|
||||
if (staticContext && (modifierList != null && !modifierList.hasModifierProperty(PsiModifier.STATIC))) continue;
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ public class JavaQualifiedNameProvider implements QualifiedNameProvider {
|
||||
catch (IncorrectOperationException e) {
|
||||
// failed to bind
|
||||
}
|
||||
if (!isReferencedTo(referenceExpression, targetElement)) {
|
||||
if (referenceExpression.isValid() && !isReferencedTo(referenceExpression, targetElement)) {
|
||||
toInsert = fqn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
@@ -391,6 +392,17 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<PsiElement, PsiComment> parseContext(@NotNull PsiElement startPoint) {
|
||||
for (PsiElement e = startPoint; e != null; e = e.getParent()) {
|
||||
if (e instanceof PsiDocCommentOwner) {
|
||||
return Pair.<PsiElement, PsiComment>create(e, ((PsiDocCommentOwner)e).getDocComment());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateDocumentationContentStub(PsiComment _comment) {
|
||||
final PsiDocCommentOwner commentOwner = ((PsiDocComment)_comment).getOwner();
|
||||
|
||||
@@ -93,7 +93,11 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
|
||||
|
||||
@Override
|
||||
public PsiType substitute(PsiType type) {
|
||||
if (type == null) return null;
|
||||
if (type == null) {
|
||||
//noinspection ConstantConditions
|
||||
return null;
|
||||
}
|
||||
assert type.isValid();
|
||||
PsiType substituted = type.accept(myAddingBoundsSubstitutionVisitor);
|
||||
return correctExternalSubstitution(substituted, type);
|
||||
}
|
||||
@@ -148,6 +152,7 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
|
||||
if (newBound == null) {
|
||||
return null;
|
||||
}
|
||||
assert newBound.isValid() : newBound.getClass();
|
||||
if (newBound instanceof PsiWildcardType) {
|
||||
return handleBoundComposition(wildcardType, (PsiWildcardType)newBound);
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.editorActions
|
||||
|
||||
import com.intellij.openapi.editor.impl.AbstractEditorProcessingOnDocumentModificationTest
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 9/20/12 6:17 PM
|
||||
*/
|
||||
class FixDocCommentTest extends AbstractEditorProcessingOnDocumentModificationTest {
|
||||
|
||||
void testGenerateMethodDoc() {
|
||||
doTest(
|
||||
initial: '''\
|
||||
class Test {
|
||||
String test(int i) {
|
||||
return "s";<caret>
|
||||
}
|
||||
}''',
|
||||
expected: '''\
|
||||
class Test {
|
||||
/**
|
||||
* <caret>
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
String test(int i) {
|
||||
return "s";
|
||||
}
|
||||
}'''
|
||||
)
|
||||
}
|
||||
|
||||
void testGenerateFieldDoc() {
|
||||
doTest(
|
||||
initial: '''\
|
||||
class Test {
|
||||
int <caret>i;
|
||||
}''',
|
||||
expected: '''\
|
||||
class Test {
|
||||
/**
|
||||
* <caret>
|
||||
*/
|
||||
int i;
|
||||
}'''
|
||||
)
|
||||
}
|
||||
|
||||
void testGenerateClassDoc() {
|
||||
doTest(
|
||||
initial: '''\
|
||||
class Test {
|
||||
void test1() {}
|
||||
<caret>
|
||||
void test2() {}
|
||||
}''',
|
||||
expected: '''\
|
||||
/**
|
||||
* <caret>
|
||||
*/
|
||||
class Test {
|
||||
void test1() {}
|
||||
|
||||
void test2() {}
|
||||
}'''
|
||||
)
|
||||
}
|
||||
|
||||
private def doTest(Map args) {
|
||||
configureFromFileText("${getTestName(false)}.java", args.initial)
|
||||
executeAction(FixDocCommentAction.ACTION_ID)
|
||||
checkResultByText(args.expected)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.intellij.lang.documentation;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -33,6 +36,28 @@ public interface CodeDocumentationProvider extends DocumentationProvider {
|
||||
@Nullable
|
||||
PsiComment findExistingDocComment(PsiComment contextElement);
|
||||
|
||||
/**
|
||||
* Examines PSI hierarchy identified by the given 'start' element trying to find element which can be documented
|
||||
* and it's doc comment (if any).
|
||||
* <p/>
|
||||
* Example:
|
||||
* <pre>
|
||||
* int test() {
|
||||
* return [caret] 1;
|
||||
* }
|
||||
* </pre>
|
||||
* PSI element at the caret (return element) is an entry point. This method is expected to return PSI method element
|
||||
* and <code>'null'</code> as the existing doc comment then.
|
||||
*
|
||||
* @param startPoint start traversal point
|
||||
* @return comment anchor which is a given element or its anchor if the one is found and its doc comment (if existing).
|
||||
* This method may return <code>'null'</code> as an indication that no doc comment anchor and existing comment
|
||||
* is available;
|
||||
* returned pair must have non-null PSI element and nullable existing comment references then
|
||||
*/
|
||||
@Nullable
|
||||
Pair<PsiElement, PsiComment> parseContext(@NotNull PsiElement startPoint);
|
||||
|
||||
/**
|
||||
* Generate documentation comment content for given context.
|
||||
* @param contextComment context psi comment
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.editorActions;
|
||||
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.lang.documentation.CodeDocumentationProvider;
|
||||
import com.intellij.lang.documentation.CompositeDocumentationProvider;
|
||||
import com.intellij.lang.documentation.DocumentationProvider;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.CaretModel;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorAction;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Creates documentation comment for the current context if it's not created yet (e.g. the caret is inside a method which
|
||||
* doesn't have a doc comment).
|
||||
* <p/>
|
||||
* Updates existing documentation comment if necessary if the one exists. E.g. we've changed method signature and want to remove all
|
||||
* outdated parameters and create stubs for the new ones.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 9/20/12 10:15 AM
|
||||
*/
|
||||
public class FixDocCommentAction extends EditorAction {
|
||||
|
||||
@NotNull @NonNls public static final String ACTION_ID = "FixDocComment";
|
||||
|
||||
public FixDocCommentAction() {
|
||||
super(new MyHandler());
|
||||
}
|
||||
|
||||
private static final class MyHandler extends EditorActionHandler {
|
||||
@Override
|
||||
public void execute(Editor editor, DataContext dataContext) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(dataContext);
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiFile psiFile = LangDataKeys.PSI_FILE.getData(dataContext);
|
||||
if (psiFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
process(psiFile, editor, project, editor.getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
private static void process(@NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Project project, int offset) {
|
||||
PsiElement elementAtOffset = file.findElementAt(offset);
|
||||
if (elementAtOffset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Language language = PsiUtilBase.getLanguageAtOffset(file, offset);
|
||||
final CodeDocumentationProvider docProvider;
|
||||
final DocumentationProvider langDocumentationProvider = LanguageDocumentation.INSTANCE.forLanguage(language);
|
||||
if (langDocumentationProvider instanceof CompositeDocumentationProvider) {
|
||||
docProvider = ((CompositeDocumentationProvider)langDocumentationProvider).getFirstCodeDocumentationProvider();
|
||||
}
|
||||
else if (langDocumentationProvider instanceof CodeDocumentationProvider) {
|
||||
docProvider = (CodeDocumentationProvider)langDocumentationProvider;
|
||||
}
|
||||
else {
|
||||
docProvider = null;
|
||||
}
|
||||
if (docProvider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Pair<PsiElement, PsiComment> pair = docProvider.parseContext(elementAtOffset);
|
||||
if (pair == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Commenter c = LanguageCommenters.INSTANCE.forLanguage(language);
|
||||
if (!(c instanceof CodeDocumentationAwareCommenter)) {
|
||||
return;
|
||||
}
|
||||
final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter)c;
|
||||
final Runnable command = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (pair.second == null || pair.second.getTextRange().isEmpty()) {
|
||||
generateComment(pair.first, editor, docProvider, commenter, project);
|
||||
}
|
||||
else {
|
||||
fixCommentIfNecessary(pair.second);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
CommandProcessor.getInstance().executeCommand(project, command, "Fix documentation", null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a comment if possible.
|
||||
* <p/>
|
||||
* It's assumed that this method {@link PsiDocumentManager#commitDocument(Document) syncs} all PSI-document
|
||||
* changes during the processing.
|
||||
*
|
||||
* @param anchor target element for which a comment should be generated
|
||||
* @param editor target editor
|
||||
* @param commenter commenter to use
|
||||
* @param project current project
|
||||
*/
|
||||
private static void generateComment(@NotNull PsiElement anchor,
|
||||
@NotNull Editor editor,
|
||||
@NotNull CodeDocumentationProvider documentationProvider,
|
||||
@NotNull CodeDocumentationAwareCommenter commenter,
|
||||
@NotNull Project project)
|
||||
{
|
||||
Document document = editor.getDocument();
|
||||
int commentStartOffset = anchor.getTextRange().getStartOffset();
|
||||
int lineStartOffset = document.getLineStartOffset(document.getLineNumber(commentStartOffset));
|
||||
if (lineStartOffset > 0 && lineStartOffset < commentStartOffset) {
|
||||
// Example:
|
||||
// void test1() {
|
||||
// }
|
||||
// void test2() {
|
||||
// <offset>
|
||||
// }
|
||||
// We want to insert the comment at the start of the line where 'test2()' is declared.
|
||||
int nonWhiteSpaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), commentStartOffset - 1, " \t");
|
||||
commentStartOffset = Math.max(nonWhiteSpaceOffset, lineStartOffset);
|
||||
}
|
||||
|
||||
int commentBodyRelativeOffset = 0;
|
||||
int caretOffsetToSet = -1;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String commentPrefix = commenter.getDocumentationCommentPrefix();
|
||||
if (commentPrefix != null) {
|
||||
buffer.append(commentPrefix).append("\n");
|
||||
commentBodyRelativeOffset += commentPrefix.length() + 1;
|
||||
}
|
||||
|
||||
String linePrefix = commenter.getDocumentationCommentLinePrefix();
|
||||
if (linePrefix != null) {
|
||||
buffer.append(linePrefix);
|
||||
commentBodyRelativeOffset += linePrefix.length();
|
||||
caretOffsetToSet = commentStartOffset + commentBodyRelativeOffset;
|
||||
}
|
||||
buffer.append("\n");
|
||||
commentBodyRelativeOffset++;
|
||||
|
||||
String commentSuffix = commenter.getDocumentationCommentSuffix();
|
||||
if (commentSuffix != null) {
|
||||
buffer.append(commentSuffix).append("\n");
|
||||
}
|
||||
|
||||
if (buffer.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.insertString(commentStartOffset, buffer);
|
||||
PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
|
||||
docManager.commitDocument(document);
|
||||
|
||||
Pair<PsiElement, PsiComment> pair = documentationProvider.parseContext(anchor);
|
||||
if (pair == null || pair.second == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String stub = documentationProvider.generateDocumentationContentStub(pair.second);
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
if (stub != null) {
|
||||
int insertionOffset = commentStartOffset + commentBodyRelativeOffset;
|
||||
//if (CodeStyleSettingsManager.getSettings(project).JD_ADD_BLANK_AFTER_DESCRIPTION) {
|
||||
// buffer.setLength(0);
|
||||
// if (linePrefix != null) {
|
||||
// buffer.append(linePrefix);
|
||||
// }
|
||||
// buffer.append("\n");
|
||||
// buffer.append(stub);
|
||||
// stub = buffer.toString();
|
||||
//}
|
||||
document.insertString(insertionOffset, stub);
|
||||
docManager.commitDocument(document);
|
||||
pair = documentationProvider.parseContext(anchor);
|
||||
}
|
||||
|
||||
if (caretOffsetToSet >= 0) {
|
||||
caretModel.moveToOffset(caretOffsetToSet);
|
||||
}
|
||||
|
||||
if (pair == null || pair.second == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int start = Math.min(calcStartReformatOffset(pair.first), calcStartReformatOffset(pair.second));
|
||||
int end = pair.second.getTextRange().getEndOffset();
|
||||
|
||||
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
codeStyleManager.reformatText(anchor.getContainingFile(), start, end);
|
||||
|
||||
int caretOffset = caretModel.getOffset();
|
||||
if (caretOffset > 0 && caretOffset <= document.getTextLength()) {
|
||||
char c = document.getCharsSequence().charAt(caretOffset - 1);
|
||||
if (!StringUtil.isWhiteSpace(c)) {
|
||||
document.insertString(caretOffset, " ");
|
||||
caretModel.moveToOffset(caretOffset + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int calcStartReformatOffset(@NotNull PsiElement element) {
|
||||
int result = element.getTextRange().getStartOffset();
|
||||
for (PsiElement e = element.getPrevSibling(); e != null; e = e.getPrevSibling()) {
|
||||
if (e instanceof PsiWhiteSpace) {
|
||||
result = e.getTextRange().getStartOffset();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void fixCommentIfNecessary(@NotNull PsiComment docComment) {
|
||||
// TODO den implement
|
||||
}
|
||||
}
|
||||
+11
-7
@@ -23,6 +23,7 @@ import com.intellij.ide.errorTreeView.impl.ErrorViewTextExporter;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
@@ -263,8 +264,7 @@ public class NewErrorTreeViewPanel extends JPanel implements DataProvider, Occur
|
||||
int line,
|
||||
int column,
|
||||
@Nullable Object data) {
|
||||
myErrorViewStructure
|
||||
.addMessage(ErrorTreeElementKind.convertMessageFromCompilerErrorType(type), text, underFileGroup, file, line, column, data);
|
||||
myErrorViewStructure.addMessage(ErrorTreeElementKind.convertMessageFromCompilerErrorType(type), text, underFileGroup, file, line, column, data);
|
||||
myBuilder.updateTree();
|
||||
}
|
||||
|
||||
@@ -275,11 +275,15 @@ public class NewErrorTreeViewPanel extends JPanel implements DataProvider, Occur
|
||||
@Nullable String exportTextPrefix,
|
||||
@Nullable String rendererTextPrefix,
|
||||
@Nullable Object data) {
|
||||
myErrorViewStructure.addNavigatableMessage(groupName, navigatable, ErrorTreeElementKind.convertMessageFromCompilerErrorType(type), text,
|
||||
data,
|
||||
exportTextPrefix == null ? "" : exportTextPrefix,
|
||||
rendererTextPrefix == null ? "" : rendererTextPrefix,
|
||||
data instanceof VirtualFile ? (VirtualFile)data : null);
|
||||
|
||||
VirtualFile file = data instanceof VirtualFile ? (VirtualFile)data : null;
|
||||
if (file == null && navigatable instanceof OpenFileDescriptor) {
|
||||
file = ((OpenFileDescriptor)navigatable).getFile();
|
||||
}
|
||||
final String exportPrefix = exportTextPrefix == null ? "" : exportTextPrefix;
|
||||
final String renderPrefix = rendererTextPrefix == null ? "" : rendererTextPrefix;
|
||||
final ErrorTreeElementKind kind = ErrorTreeElementKind.convertMessageFromCompilerErrorType(type);
|
||||
myErrorViewStructure.addNavigatableMessage(groupName, navigatable, kind, text, data, exportPrefix, renderPrefix, file);
|
||||
myBuilder.updateTree();
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,8 @@ action.ShowFilePath.text=File _Path
|
||||
action.ShowFilePath.description=Popup path of the current file for opening in external file manager
|
||||
action.SelectIn.text=Se_lect In...
|
||||
action.SelectIn.description=Select the current class or method in any view
|
||||
action.FixDocComment.text=Fix doc comment
|
||||
action.FixDocComment.description=Generate or fix doc comment for the current context
|
||||
action.QuickJavaDoc.text=Quick _Documentation
|
||||
action.QuickJavaDoc.description=Show a popup window with documentation for the symbol at caret
|
||||
action.QuickImplementations.text=Quic_k Definition
|
||||
|
||||
@@ -158,6 +158,8 @@
|
||||
|
||||
<add-to-group group-id="ViewMenu" relative-to-action="QuickActions" anchor="after"/>
|
||||
</group>
|
||||
|
||||
<action id="FixDocComment" class="com.intellij.codeInsight.editorActions.FixDocCommentAction"/>
|
||||
|
||||
<action id="ViewNavigationBar" class="com.intellij.ide.actions.ViewNavigationBarAction">
|
||||
<add-to-group group-id="UIToggleActions" relative-to-action="ViewStatusBar" anchor="after"/>
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class SaveCommittingDocumentsVetoer implements FileDocumentSynchronizatio
|
||||
return false;
|
||||
}
|
||||
if (beingCommitted instanceof Project) {
|
||||
boolean allowSave = myAdapter.showAllowSaveDialog((Project) beingCommitted, Collections.singletonList(document));
|
||||
boolean allowSave = myAdapter.showAllowSaveDialog(Collections.singletonMap(document, (Project)beingCommitted));
|
||||
if (!allowSave) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+29
-18
@@ -30,14 +30,15 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.UserDataHolderEx;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.changes.ui.CommitHelper;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class VetoSavingCommittingDocumentsAdapter implements ApplicationComponent {
|
||||
static final Object SAVE_DENIED = new Object();
|
||||
@@ -57,39 +58,49 @@ public class VetoSavingCommittingDocumentsAdapter implements ApplicationComponen
|
||||
ApplicationManager.getApplication().getMessageBus().connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
|
||||
@Override
|
||||
public void beforeAllDocumentsSaving() {
|
||||
List<Document> documentsToWarn = new ArrayList<Document>();
|
||||
final Document[] unsavedDocuments = myFileDocumentManager.getUnsavedDocuments();
|
||||
Project commitOwnerProject = null;
|
||||
for (Document unsavedDocument : unsavedDocuments) {
|
||||
final Object data = unsavedDocument.getUserData(CommitHelper.DOCUMENT_BEING_COMMITTED_KEY);
|
||||
if (data instanceof Project) {
|
||||
commitOwnerProject = (Project) data;
|
||||
documentsToWarn.add(unsavedDocument);
|
||||
}
|
||||
}
|
||||
Map<Document, Project> documentsToWarn = getDocumentsBeingCommitted();
|
||||
if (!documentsToWarn.isEmpty()) {
|
||||
boolean allowSave = showAllowSaveDialog(commitOwnerProject, documentsToWarn);
|
||||
for (Document document : documentsToWarn) {
|
||||
document.putUserData(CommitHelper.DOCUMENT_BEING_COMMITTED_KEY, allowSave ? null : SAVE_DENIED);
|
||||
}
|
||||
boolean allowSave = showAllowSaveDialog(documentsToWarn);
|
||||
updateSaveability(documentsToWarn, allowSave);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<Document, Project> getDocumentsBeingCommitted() {
|
||||
Map<Document, Project> documentsToWarn = ContainerUtil.newHashMap();
|
||||
for (Document unsavedDocument : myFileDocumentManager.getUnsavedDocuments()) {
|
||||
final Object data = unsavedDocument.getUserData(CommitHelper.DOCUMENT_BEING_COMMITTED_KEY);
|
||||
if (data instanceof Project) {
|
||||
documentsToWarn.put(unsavedDocument, (Project)data);
|
||||
}
|
||||
}
|
||||
return documentsToWarn;
|
||||
}
|
||||
|
||||
private static void updateSaveability(Map<Document, Project> documentsToWarn, boolean allowSave) {
|
||||
Object newValue = allowSave ? null : SAVE_DENIED;
|
||||
for (Document document : documentsToWarn.keySet()) {
|
||||
Project oldData = documentsToWarn.get(document);
|
||||
//the committing thread could have finished already and file is not being committed anymore
|
||||
((UserDataHolderEx)document).replace(CommitHelper.DOCUMENT_BEING_COMMITTED_KEY, oldData, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void disposeComponent() {
|
||||
}
|
||||
|
||||
boolean showAllowSaveDialog(Project project, List<Document> documentsToWarn) {
|
||||
boolean showAllowSaveDialog(Map<Document, Project> documentsToWarn) {
|
||||
StringBuilder messageBuilder = new StringBuilder("The following " + (documentsToWarn.size() == 1 ? "file is" : "files are") +
|
||||
" currently being committed to the VCS. " +
|
||||
"Saving now could cause inconsistent data to be committed.\n");
|
||||
for (Document document : documentsToWarn) {
|
||||
for (Document document : documentsToWarn.keySet()) {
|
||||
final VirtualFile file = myFileDocumentManager.getFile(document);
|
||||
messageBuilder.append(FileUtil.toSystemDependentName(file.getPath())).append("\n");
|
||||
}
|
||||
messageBuilder.append("Save the ").append(documentsToWarn.size() == 1 ? "file" : "files").append(" now?");
|
||||
|
||||
Project project = documentsToWarn.values().iterator().next();
|
||||
int rc = Messages.showOkCancelDialog(project, messageBuilder.toString(), "Save Files During Commit", "Save Now", "Postpone Save",
|
||||
Messages.getQuestionIcon());
|
||||
return rc == 0;
|
||||
|
||||
+13
@@ -25,6 +25,7 @@ import com.intellij.lang.documentation.CompositeDocumentationProvider;
|
||||
import com.intellij.lang.documentation.ExternalDocumentationProvider;
|
||||
import com.intellij.lang.java.JavaDocumentationProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
|
||||
@@ -37,6 +38,7 @@ import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.dsl.CustomMembersGenerator;
|
||||
import org.jetbrains.plugins.groovy.dsl.holders.NonCodeMembersHolder;
|
||||
@@ -459,6 +461,17 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<PsiElement, PsiComment> parseContext(@NotNull PsiElement startPoint) {
|
||||
for (PsiElement e = startPoint; e != null; e = e.getParent()) {
|
||||
if (e instanceof GrDocCommentOwner) {
|
||||
return Pair.<PsiElement, PsiComment>create(e, ((GrDocCommentOwner)e).getDocComment());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String generateDocumentationContentStub(PsiComment contextComment) {
|
||||
if (!(contextComment instanceof GrDocComment)) {
|
||||
return null;
|
||||
|
||||
@@ -103,7 +103,7 @@ public class DFAEngine<E> {
|
||||
|
||||
while (!workList.isEmpty()) {
|
||||
count++;
|
||||
if (timeout && count % 50 == 0 && tm.getCurrentThreadUserTime() - startTime > ourTimeLimit) return null;
|
||||
if (timeout && count % 512 == 0 && tm.getCurrentThreadUserTime() - startTime > ourTimeLimit) return null;
|
||||
|
||||
ProgressManager.checkCanceled();
|
||||
final Instruction curr = workList.remove();
|
||||
|
||||
@@ -88,6 +88,7 @@ public class DFAType {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof DFAType)) return false;
|
||||
|
||||
final DFAType other = (DFAType)obj;
|
||||
@@ -151,11 +152,13 @@ public class DFAType {
|
||||
}
|
||||
|
||||
private static boolean eq(PsiType t1, PsiType t2) {
|
||||
return Comparing.equal(TypeConversionUtil.erasure(t1), TypeConversionUtil.erasure(t2));
|
||||
return t1 == t2 || Comparing.equal(TypeConversionUtil.erasure(t1), TypeConversionUtil.erasure(t2));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DFAType create(DFAType t1, DFAType t2, PsiManager manager) {
|
||||
if (t1.equals(t2)) return t1;
|
||||
|
||||
final PsiType primary = TypesUtil.getLeastUpperBoundNullable(t1.primary, t2.primary, manager);
|
||||
final DFAType type = new DFAType(primary);
|
||||
|
||||
|
||||
+155
-36
@@ -114,7 +114,8 @@ public class TypeInferenceHelper {
|
||||
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(refExpr);
|
||||
if (scope == null) return null;
|
||||
|
||||
return inferVariableTypes(scope).getInferredType(refExpr.getReferenceName(), ControlFlowUtils.findRWInstruction(refExpr, scope.getControlFlow()));
|
||||
return getInferenceCache(scope).getInferredType(refExpr.getReferenceName(), ControlFlowUtils
|
||||
.findRWInstruction(refExpr, scope.getControlFlow()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -122,18 +123,16 @@ public class TypeInferenceHelper {
|
||||
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place);
|
||||
if (scope == null) return null;
|
||||
|
||||
return inferVariableTypes(scope).getInferredType(variableName, ControlFlowUtils.findNearestInstruction(place, scope.getControlFlow()));
|
||||
return getInferenceCache(scope).getInferredType(variableName, ControlFlowUtils.findNearestInstruction(place, scope.getControlFlow()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static InferenceResult inferVariableTypes(final GrControlFlowOwner scope) {
|
||||
return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider<InferenceResult>() {
|
||||
private static InferenceCache getInferenceCache(final GrControlFlowOwner scope) {
|
||||
return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider<InferenceCache>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<InferenceResult> compute() {
|
||||
Instruction[] flow = scope.getControlFlow();
|
||||
List<Map<String, PsiType>> list = performTypeDfa(scope, flow);
|
||||
return Result.create(new InferenceResult(flow, list), PsiModificationTracker.MODIFICATION_COUNT);
|
||||
public Result<InferenceCache> compute() {
|
||||
return Result.create(new InferenceCache(scope), PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -299,27 +298,17 @@ public class TypeInferenceHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<Map<String, PsiType>> performTypeDfa(GrControlFlowOwner owner, Instruction[] flow) {
|
||||
final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner, flow);
|
||||
final TypesSemilattice semilattice = new TypesSemilattice(owner.getManager());
|
||||
List<TypeDfaState> states = new DFAEngine<TypeDfaState>(flow, dfaInstance, semilattice).performDFAWithTimeout();
|
||||
if (states == null) return null;
|
||||
|
||||
List<Map<String, PsiType>> result = ContainerUtil.newArrayList();
|
||||
for (int i = 0; i < states.size(); i++) {
|
||||
result.add(states.get(i).getBindings(flow[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static class TypeDfaInstance implements DfaInstance<TypeDfaState> {
|
||||
private final GrControlFlowOwner myScope;
|
||||
private final Instruction[] myFlow;
|
||||
private final Set<Instruction> myInteresting;
|
||||
private final InferenceCache myCache;
|
||||
|
||||
TypeDfaInstance(GrControlFlowOwner scope, Instruction[] flow) {
|
||||
TypeDfaInstance(GrControlFlowOwner scope, Instruction[] flow, Set<Instruction> interesting, InferenceCache cache) {
|
||||
myScope = scope;
|
||||
myFlow = flow;
|
||||
myInteresting = interesting;
|
||||
myCache = cache;
|
||||
}
|
||||
|
||||
public void fun(final TypeDfaState state, final Instruction instruction) {
|
||||
@@ -335,31 +324,48 @@ public class TypeInferenceHelper {
|
||||
final String varName = instruction.getVariableName();
|
||||
if (varName == null) return;
|
||||
|
||||
state.putType(varName, doInference(state.getBindings(instruction), new NullableComputable<DFAType>() {
|
||||
updateVariableType(state, instruction, varName, new NullableComputable<DFAType>() {
|
||||
@Override
|
||||
public DFAType compute() {
|
||||
ReadWriteVariableInstruction originalInstr = instruction.getInstructionToMixin(myFlow);
|
||||
assert originalInstr != null && !originalInstr.isWrite();
|
||||
|
||||
DFAType original = state.getVariableType(varName).negate(originalInstr);
|
||||
DFAType original = state.getVariableType(varName);
|
||||
if (original == null) {
|
||||
original = DFAType.create(null);
|
||||
}
|
||||
original = original.negate(originalInstr);
|
||||
original.addMixin(instruction.inferMixinType(), instruction.getConditionInstruction());
|
||||
return original;
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleVariableWrite(TypeDfaState state, ReadWriteVariableInstruction instruction) {
|
||||
final PsiElement element = instruction.getElement();
|
||||
if (element != null && instruction.isWrite()) {
|
||||
state.putType(instruction.getVariableName(), doInference(state.getBindings(instruction), new Computable<DFAType>() {
|
||||
updateVariableType(state, instruction, instruction.getVariableName(), new Computable<DFAType>() {
|
||||
@Override
|
||||
public DFAType compute() {
|
||||
return DFAType.create(TypesUtil.boxPrimitiveType(getInitializerType(element), myScope.getManager(), myScope.getResolveScope()));
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVariableType(TypeDfaState state, Instruction instruction, String variableName, Computable<DFAType> computation) {
|
||||
if (!myInteresting.contains(instruction)) {
|
||||
state.removeBinding(variableName);
|
||||
return;
|
||||
}
|
||||
|
||||
DFAType type = myCache.getCachedInferredType(variableName, instruction);
|
||||
if (type == null) {
|
||||
type = doInference(state.getBindings(instruction), computation);
|
||||
}
|
||||
state.putType(variableName, type);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TypeDfaState initial() {
|
||||
return new TypeDfaState();
|
||||
@@ -371,22 +377,135 @@ public class TypeInferenceHelper {
|
||||
|
||||
}
|
||||
|
||||
private static class InferenceResult {
|
||||
private static class InferenceCache {
|
||||
final GrControlFlowOwner scope;
|
||||
final Instruction[] flow;
|
||||
final List<Map<String, PsiType>> varTypes;
|
||||
final AtomicReference<List<TypeDfaState>> varTypes;
|
||||
final ConcurrentHashSet<Instruction> tooComplex = new ConcurrentHashSet<Instruction>();
|
||||
|
||||
InferenceResult(Instruction[] flow, @Nullable List<Map<String, PsiType>> varTypes) {
|
||||
this.flow = flow;
|
||||
this.varTypes = varTypes;
|
||||
InferenceCache(final GrControlFlowOwner scope) {
|
||||
this.scope = scope;
|
||||
this.flow = scope.getControlFlow();
|
||||
List<TypeDfaState> noTypes = new ArrayList<TypeDfaState>();
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < flow.length; i++) {
|
||||
noTypes.add(new TypeDfaState());
|
||||
}
|
||||
varTypes = new AtomicReference<List<TypeDfaState>>(noTypes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiType getInferredType(String variableName, Instruction instruction) {
|
||||
if (instruction == null || varTypes == null) return null;
|
||||
private PsiType getInferredType(@Nullable String variableName, @Nullable Instruction instruction) {
|
||||
if (instruction == null || variableName == null) return null;
|
||||
if (tooComplex.contains(instruction)) return null;
|
||||
|
||||
return varTypes.get(instruction.num()).get(variableName);
|
||||
TypeDfaState cache = varTypes.get().get(instruction.num());
|
||||
if (!cache.containsVariable(variableName)) {
|
||||
Set<Instruction> interesting = collectRequiredInstructions(instruction, variableName);
|
||||
List<TypeDfaState> dfaResult = performTypeDfa(scope, flow, interesting);
|
||||
if (dfaResult == null) {
|
||||
tooComplex.addAll(interesting);
|
||||
} else {
|
||||
cacheDfaResult(dfaResult);
|
||||
}
|
||||
}
|
||||
DFAType dfaType = getCachedInferredType(variableName, instruction);
|
||||
return dfaType == null ? null : dfaType.getResultType();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<TypeDfaState> performTypeDfa(GrControlFlowOwner owner, Instruction[] flow, Set<Instruction> interesting) {
|
||||
final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner, flow, interesting, this);
|
||||
final TypesSemilattice semilattice = new TypesSemilattice(owner.getManager());
|
||||
return new DFAEngine<TypeDfaState>(flow, dfaInstance, semilattice).performDFAWithTimeout();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
DFAType getCachedInferredType(@NotNull String variableName, @NotNull Instruction instruction) {
|
||||
DFAType dfaType = varTypes.get().get(instruction.num()).getVariableType(variableName);
|
||||
return dfaType == null ? null : dfaType.negate(instruction);
|
||||
}
|
||||
|
||||
private Set<Instruction> collectRequiredInstructions(Instruction instruction, String variableName) {
|
||||
Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>> defUse = getDefUseMaps(scope);
|
||||
|
||||
Set<Instruction> interesting = ContainerUtil.newHashSet(instruction);
|
||||
LinkedList<Pair<Instruction,String>> queue = ContainerUtil.newLinkedList();
|
||||
queue.add(Pair.create(instruction, variableName));
|
||||
while (!queue.isEmpty()) {
|
||||
Pair<Instruction, String> pair = queue.removeFirst();
|
||||
for (Pair<Instruction, String> dep : findDependencies(defUse, pair.first, pair.second)) {
|
||||
if (interesting.add(dep.first)) {
|
||||
queue.addLast(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return interesting;
|
||||
}
|
||||
|
||||
private Set<Pair<Instruction,String>> findDependencies(Pair<ReachingDefinitionsDfaInstance, List<DefinitionMap>> defUse,
|
||||
Instruction insn,
|
||||
String varName) {
|
||||
int[] definitions = defUse.second.get(insn.num()).getDefinitions(defUse.first.getVarIndex(varName));
|
||||
if (definitions == null) return Collections.emptySet();
|
||||
|
||||
HashSet<Pair<Instruction, String>> pairs = ContainerUtil.newHashSet();
|
||||
for (int defIndex : definitions) {
|
||||
Instruction write = flow[defIndex];
|
||||
pairs.add(Pair.create(write, varName));
|
||||
PsiElement statement = findDependencyScope(write.getElement());
|
||||
pairs.addAll(findAllInstructionsInside(statement));
|
||||
}
|
||||
return pairs;
|
||||
}
|
||||
|
||||
private List<Pair<Instruction, String>> findAllInstructionsInside(PsiElement scope) {
|
||||
final List<Pair<Instruction, String>> result = ContainerUtil.newArrayList();
|
||||
scope.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (element instanceof GrReferenceExpression && !((GrReferenceExpression)element).isQualified()) {
|
||||
String varName = ((GrReferenceExpression)element).getReferenceName();
|
||||
if (varName != null) {
|
||||
for (Instruction dependency : ControlFlowUtils.findAllInstructions(element, flow)) {
|
||||
result.add(Pair.create(dependency, varName));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findDependencyScope(PsiElement element) {
|
||||
return PsiTreeUtil.findFirstParent(element, new Condition<PsiElement>() {
|
||||
@Override
|
||||
public boolean value(PsiElement element) {
|
||||
return org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isExpressionStatement(element) ||
|
||||
!(element.getParent() instanceof GrExpression);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cacheDfaResult(List<TypeDfaState> dfaResult) {
|
||||
while (true) {
|
||||
List<TypeDfaState> oldTypes = varTypes.get();
|
||||
if (varTypes.compareAndSet(oldTypes, addDfaResult(dfaResult, oldTypes))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TypeDfaState> addDfaResult(List<TypeDfaState> dfaResult, List<TypeDfaState> oldTypes) {
|
||||
List<TypeDfaState> newTypes = new ArrayList<TypeDfaState>(oldTypes);
|
||||
for (int i = 0; i < dfaResult.size(); i++) {
|
||||
newTypes.set(i, newTypes.get(i).mergeWith(dfaResult.get(i)));
|
||||
}
|
||||
return newTypes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-4
@@ -19,6 +19,7 @@ import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice;
|
||||
@@ -63,6 +64,15 @@ class TypeDfaState {
|
||||
myVarTypes = ContainerUtil.newHashMap(another.myVarTypes);
|
||||
}
|
||||
|
||||
TypeDfaState mergeWith(TypeDfaState another) {
|
||||
if (another.myVarTypes.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
TypeDfaState state = new TypeDfaState(this);
|
||||
state.myVarTypes.putAll(another.myVarTypes);
|
||||
return state;
|
||||
}
|
||||
|
||||
void joinState(TypeDfaState another, PsiManager manager) {
|
||||
for (Map.Entry<String, DFAType> entry : another.myVarTypes.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
@@ -83,10 +93,9 @@ class TypeDfaState {
|
||||
return myVarTypes.equals(another.myVarTypes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
DFAType getVariableType(String variableName) {
|
||||
DFAType type = myVarTypes.get(variableName);
|
||||
return type == null ? DFAType.create(null) : type;
|
||||
return myVarTypes.get(variableName);
|
||||
}
|
||||
|
||||
Map<String, PsiType> getBindings(Instruction instruction) {
|
||||
@@ -98,7 +107,7 @@ class TypeDfaState {
|
||||
return map;
|
||||
}
|
||||
|
||||
void putType(String variableName, DFAType type) {
|
||||
void putType(String variableName, @Nullable DFAType type) {
|
||||
myVarTypes.put(variableName, type);
|
||||
}
|
||||
|
||||
@@ -106,4 +115,12 @@ class TypeDfaState {
|
||||
public String toString() {
|
||||
return "TypeDfaState{" + myVarTypes + '}';
|
||||
}
|
||||
|
||||
public boolean containsVariable(@NotNull String variableName) {
|
||||
return myVarTypes.containsKey(variableName);
|
||||
}
|
||||
|
||||
public void removeBinding(String variableName) {
|
||||
myVarTypes.remove(variableName);
|
||||
}
|
||||
}
|
||||
+113
-59
@@ -20,7 +20,11 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.NameHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
@@ -36,6 +40,8 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
@@ -82,56 +88,115 @@ public class GrImportStatementImpl extends GroovyPsiElementImpl implements GrImp
|
||||
if (ref == null) return true;
|
||||
|
||||
if (isStatic()) {
|
||||
GrCodeReferenceElement qualifier = ref.getQualifier();
|
||||
if (qualifier == null) return true;
|
||||
PsiElement resolved = qualifier.resolve();
|
||||
if (!(resolved instanceof PsiClass)) return true;
|
||||
PsiClass clazz = (PsiClass)resolved;
|
||||
|
||||
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, this);
|
||||
|
||||
final String refName = ref.getReferenceName();
|
||||
if (nameHint == null || name.equals(nameHint.getName(state))) {
|
||||
final PsiField field = clazz.findFieldByName(refName, true);
|
||||
if (field != null && field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
if (!processor.execute(field, state)) return false;
|
||||
}
|
||||
|
||||
for (PsiMethod method : clazz.findMethodsByName(refName, true)) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
if (!processor.execute(method, state)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClass innerClass = clazz.findInnerClassByName(refName, true);
|
||||
if (innerClass != null && innerClass.hasModifierProperty(PsiModifier.STATIC) && !processor.execute(innerClass, state)) return false;
|
||||
}
|
||||
|
||||
final PsiMethod getter = GroovyPropertyUtils.findPropertyGetter(clazz, refName, true, true);
|
||||
if (getter != null &&
|
||||
(nameHint == null || name.equals(GroovyPropertyUtils.getPropertyNameByGetterName(nameHint.getName(state), true)))) {
|
||||
if (!processor.execute(getter, state)) return false;
|
||||
}
|
||||
|
||||
final PsiMethod setter = GroovyPropertyUtils.findPropertySetter(clazz, refName, true, true);
|
||||
if (setter != null &&
|
||||
(nameHint == null || name.equals(GroovyPropertyUtils.getPropertyNameBySetterName(nameHint.getName(state))))) {
|
||||
if (!processor.execute(setter, state)) return false;
|
||||
}
|
||||
return processSingleStaticImport(processor, state, name, nameHint, ref);
|
||||
}
|
||||
else { //class import statement
|
||||
if (nameHint == null || name.equals(nameHint.getName(state))) {
|
||||
final PsiElement resolved = ref.resolve();
|
||||
if (resolved instanceof PsiClass) {
|
||||
if (!isAliasedImport() && isFromSamePackage((PsiClass)resolved)) return true; //don't process classes from the same package because such import statements are ignored by compiler
|
||||
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, this);
|
||||
if (!processor.execute(resolved, state)) return false;
|
||||
}
|
||||
}
|
||||
if (nameHint == null || name.equals(nameHint.getName(state))) {
|
||||
return processSingleClassImport(processor, state, ref);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiClass resolveQualifier() {
|
||||
return CachedValuesManager.getManager(getProject()).getCachedValue(this, new CachedValueProvider<PsiClass>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<PsiClass> compute() {
|
||||
GrCodeReferenceElement reference = getImportReference();
|
||||
GrCodeReferenceElement qualifier = reference == null ? null : reference.getQualifier();
|
||||
PsiElement target = qualifier == null ? null : qualifier.resolve();
|
||||
PsiClass clazz = target instanceof PsiClass ? (PsiClass)target : null;
|
||||
return Result.create(clazz, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, GrImportStatementImpl.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<PsiMember> getAllStaticMembers(final PsiClass clazz) {
|
||||
return CachedValuesManager.getManager(clazz.getProject()).getCachedValue(clazz, new CachedValueProvider<List<PsiMember>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<List<PsiMember>> compute() {
|
||||
List<PsiMember> result = ContainerUtil.newArrayList();
|
||||
for (PsiMethod method : clazz.getAllMethods()) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
for (PsiField field : clazz.getAllFields()) {
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
for (PsiClass inner : clazz.getAllInnerClasses()) {
|
||||
if (inner.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
result.add(inner);
|
||||
}
|
||||
}
|
||||
return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, clazz);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean processSingleStaticImport(PsiScopeProcessor processor,
|
||||
ResolveState state,
|
||||
String importedName,
|
||||
NameHint nameHint,
|
||||
GrCodeReferenceElement ref) {
|
||||
PsiClass clazz = resolveQualifier();
|
||||
if (clazz == null) return true;
|
||||
|
||||
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, this);
|
||||
String hintName = nameHint == null ? null : nameHint.getName(state);
|
||||
|
||||
final String refName = ref.getReferenceName();
|
||||
if (hintName == null || importedName.equals(hintName)) {
|
||||
final PsiField field = clazz.findFieldByName(refName, true);
|
||||
if (field != null && field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
if (!processor.execute(field, state)) return false;
|
||||
}
|
||||
|
||||
for (PsiMethod method : clazz.findMethodsByName(refName, true)) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
if (!processor.execute(method, state)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClass innerClass = clazz.findInnerClassByName(refName, true);
|
||||
if (innerClass != null && innerClass.hasModifierProperty(PsiModifier.STATIC) && !processor.execute(innerClass, state)) return false;
|
||||
}
|
||||
|
||||
String propByGetter = hintName == null ? null : GroovyPropertyUtils.getPropertyNameByGetterName(hintName, true);
|
||||
String propBySetter = hintName == null ? null : GroovyPropertyUtils.getPropertyNameBySetterName(hintName);
|
||||
for (PsiMember member : getAllStaticMembers(clazz)) {
|
||||
if (!(member instanceof PsiMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiMethod method = (PsiMethod)member;
|
||||
if ((propByGetter == null || importedName.equals(propByGetter)) && GroovyPropertyUtils.isSimplePropertyGetter(method, refName) ||
|
||||
(propBySetter == null || importedName.equals(propBySetter)) && GroovyPropertyUtils.isSimplePropertySetter(method, refName)) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC) && !processor.execute(method, state)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean processSingleClassImport(PsiScopeProcessor processor, ResolveState state, GrCodeReferenceElement ref) {
|
||||
final PsiElement resolved = ref.resolve();
|
||||
if (!(resolved instanceof PsiClass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isAliasedImport() && isFromSamePackage((PsiClass)resolved)) {
|
||||
return true; //don't process classes from the same package because such import statements are ignored by compiler
|
||||
}
|
||||
|
||||
return processor.execute(resolved, state.put(ResolverProcessor.RESOLVE_CONTEXT, this));
|
||||
}
|
||||
|
||||
private boolean isFromSamePackage(PsiClass resolved) {
|
||||
final String qualifiedName = resolved.getQualifiedName();
|
||||
final String packageName = ((GroovyFile)getContainingFile()).getPackageName();
|
||||
@@ -168,18 +233,9 @@ public class GrImportStatementImpl extends GroovyPsiElementImpl implements GrImp
|
||||
}
|
||||
|
||||
private static boolean processAllMembers(PsiScopeProcessor processor, PsiClass clazz, ResolveState state) {
|
||||
for (PsiField field : clazz.getAllFields()) {
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC) && !ResolveUtil.processElement(processor, field, state)) return false;
|
||||
for (PsiMember member : getAllStaticMembers(clazz)) {
|
||||
if (!ResolveUtil.processElement(processor, (PsiNamedElement)member, state)) return false;
|
||||
}
|
||||
|
||||
for (PsiMethod method : clazz.getAllMethods()) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC) && !ResolveUtil.processElement(processor, method, state)) return false;
|
||||
}
|
||||
|
||||
for (PsiClass inner : clazz.getAllInnerClasses()) {
|
||||
if (inner.hasModifierProperty(PsiModifier.STATIC) && !ResolveUtil.processElement(processor, inner, state)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -228,9 +284,7 @@ public class GrImportStatementImpl extends GroovyPsiElementImpl implements GrImp
|
||||
resolved = ref.resolve();
|
||||
}
|
||||
else {
|
||||
final GrCodeReferenceElement qualifier = ref.getQualifier();
|
||||
if (qualifier == null) return null;
|
||||
resolved = qualifier.resolve();
|
||||
resolved = resolveQualifier();
|
||||
}
|
||||
|
||||
return resolved instanceof PsiClass ? (PsiClass)resolved : null;
|
||||
|
||||
@@ -26,9 +26,8 @@ import com.intellij.psi.impl.PsiClassImplUtil;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.scope.NameHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -207,11 +206,16 @@ public class GrClassImplUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiMethod[] getAllMethods(GrTypeDefinition grType) {
|
||||
List<PsiMethod> allMethods = new ArrayList<PsiMethod>();
|
||||
getAllMethodsInner(grType, allMethods, new HashSet<PsiClass>());
|
||||
|
||||
return allMethods.toArray(new PsiMethod[allMethods.size()]);
|
||||
public static PsiMethod[] getAllMethods(final GrTypeDefinition grType) {
|
||||
return CachedValuesManager.getManager(grType.getProject()).getCachedValue(grType, new CachedValueProvider<PsiMethod[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<PsiMethod[]> compute() {
|
||||
List<PsiMethod> list = ContainerUtil.newArrayList();
|
||||
getAllMethodsInner(grType, list, new HashSet<PsiClass>());
|
||||
return Result.create(list.toArray(new PsiMethod[list.size()]), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, grType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ class SomeClass {
|
||||
measureHighlighting(text, 8000)
|
||||
}
|
||||
|
||||
public void "_test infer only the variable types that are needed"() {
|
||||
public void "test infer only the variable types that are needed"() {
|
||||
addGdsl '''contribute(currentType(String.name)) {
|
||||
println 'sleeping'
|
||||
Thread.sleep(1000)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.lang.properties;
|
||||
|
||||
import com.intellij.openapi.application.PluginPathManager;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
|
||||
/**
|
||||
@@ -22,10 +23,9 @@ import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCa
|
||||
*/
|
||||
public class PropertiesFoldingTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
|
||||
private static final String BASE_PATH = "/plugins/properties/testData/propertiesFile/folding/";
|
||||
|
||||
protected String getBasePath() {
|
||||
return BASE_PATH;
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PluginPathManager.getPluginHomePath("properties") + "/testData/propertiesFile/folding/";
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
|
||||
Reference in New Issue
Block a user