From b4e5f3d78febbad4e6da175b2bf0d8c8ada3c0c0 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 12 Apr 2011 15:54:49 +0200 Subject: [PATCH] correctly stop at the breakpoint in groovy classes not in source (IDEA-64023) --- .../debugger/GroovyPositionManager.java | 73 ++----------------- .../psi/impl/javaView/GroovyClassFinder.java | 4 +- .../lang/stubs/GroovyShortNamesCache.java | 26 +++++-- .../groovy/compiler/GroovyDebuggerTest.groovy | 71 ++++++++++++++++-- 4 files changed, 92 insertions(+), 82 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java index f0b807067088..26d8d5037a93 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java @@ -30,28 +30,20 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; -import com.intellij.psi.search.FilenameIndex; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Function; -import com.intellij.util.Processor; -import com.intellij.util.Query; -import com.intellij.util.containers.HashSet; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.Location; import com.sun.jdi.ReferenceType; import com.sun.jdi.request.ClassPrepareRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.GroovyFileTypeLoader; import org.jetbrains.plugins.groovy.extensions.debugger.ScriptPositionManagerHelper; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; @@ -63,7 +55,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; public class GroovyPositionManager implements PositionManager { private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.PositionManagerImpl"); @@ -250,8 +241,8 @@ public class GroovyPositionManager implements PositionManager { final GlobalSearchScope searchScope = myDebugProcess.getSearchScope(); try { - final PsiClass[] classes = GroovyPsiManager.getInstance(project).getNamesCache().getClassesByFQName(qName, searchScope); - PsiClass clazz = classes.length == 1 ? classes[0] : null; + final List classes = GroovyPsiManager.getInstance(project).getNamesCache().getClassesByFQName(qName, searchScope); + PsiClass clazz = classes.size() == 1 ? classes.get(0) : null; if (clazz != null) return clazz.getContainingFile(); } catch (ProcessCanceledException e) { @@ -261,51 +252,6 @@ public class GroovyPositionManager implements PositionManager { return null; } - DirectoryIndex directoryIndex = DirectoryIndex.getInstance(project); - int dotIndex = qName.lastIndexOf("."); - String packageName = dotIndex > 0 ? qName.substring(0, dotIndex) : ""; - Query query = directoryIndex.getDirectoriesByPackageName(packageName, true); - final String fileNameWithoutExtension = dotIndex > 0 ? qName.substring(dotIndex + 1) : qName; - final Set extensions = getAllGroovyFileExtensions(); - final Ref result = new Ref(); - query.forEach(new Processor() { - public boolean process(VirtualFile vDir) { - for (final String extension : extensions) { - VirtualFile vFile = vDir.findChild(fileNameWithoutExtension + "." + extension); - if (vFile != null) { - PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile); - if (psiFile instanceof GroovyFileBase) { - result.set(psiFile); - return false; - } - } - } - return true; - } - }); - - PsiFile res = result.get(); - if (res != null) { - return res; - } - - if (StringUtil.isEmpty(packageName)) { - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); - for (final String extension : extensions) { - for (final PsiFile file : FilenameIndex.getFilesByName(project, runtimeName + "." + extension, GlobalSearchScope.projectScope(project))) { - final VirtualFile vFile = file.getVirtualFile(); - if (file instanceof GroovyFile && vFile != null && !fileIndex.isInSource(vFile)) { - for (PsiClass aClass : ((GroovyFile)file).getClasses()) { - if (qName.equals(aClass.getQualifiedName())) { - return file; - } - } - } - } - } - } - - for (ScriptPositionManagerHelper helper : ScriptPositionManagerHelper.EP_NAME.getExtensions()) { if (helper.isAppropriateRuntimeName(runtimeName)) { PsiFile file = helper.getExtraScriptIfNotFound(refType, runtimeName, project); @@ -315,15 +261,6 @@ public class GroovyPositionManager implements PositionManager { return null; } - private static Set getAllGroovyFileExtensions() { - final Set extensions = new HashSet(); - extensions.addAll(GroovyFileTypeLoader.getAllGroovyExtensions()); - extensions.add("gvy"); - extensions.add("gy"); - extensions.add("gsh"); - return extensions; - } - @NotNull public List getAllClasses(final SourcePosition position) throws NoDataException { List result = ApplicationManager.getApplication().runReadAction(new Computable>() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/javaView/GroovyClassFinder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/javaView/GroovyClassFinder.java index 48903bcfa376..03c1e4b41869 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/javaView/GroovyClassFinder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/javaView/GroovyClassFinder.java @@ -38,13 +38,13 @@ public class GroovyClassFinder extends PsiElementFinder { @Nullable public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { - final List classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope); + final List classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope, true); return classes.isEmpty() ? null : classes.get(0); } @NotNull public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { - final Collection classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope); + final Collection classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope, true); return classes.isEmpty() ? PsiClass.EMPTY_ARRAY : classes.toArray(new PsiClass[classes.size()]); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyShortNamesCache.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyShortNamesCache.java index 789e8fe0f609..ddb8dca5117e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyShortNamesCache.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyShortNamesCache.java @@ -57,8 +57,9 @@ public class GroovyShortNamesCache extends PsiShortNamesCache { return allClasses.toArray(new PsiClass[allClasses.size()]); } - public List getScriptClassesByFQName(final String name, final GlobalSearchScope scope) { - final Collection files = StubIndex.getInstance().get(GrFullScriptNameIndex.KEY, name.hashCode(), myProject, new GrSourceFilterScope(scope)); + public List getScriptClassesByFQName(final String name, final GlobalSearchScope scope, final boolean srcOnly) { + GlobalSearchScope actualScope = srcOnly ? new GrSourceFilterScope(scope) : scope; + final Collection files = StubIndex.getInstance().get(GrFullScriptNameIndex.KEY, name.hashCode(), myProject, actualScope); if (files.isEmpty()) { return Collections.emptyList(); } @@ -76,10 +77,22 @@ public class GroovyShortNamesCache extends PsiShortNamesCache { } @NotNull - public PsiClass[] getClassesByFQName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) { - final Collection result = new ArrayList(getScriptClassesByFQName(name, scope)); + public List getClassesByFQName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) { + final List result = addClasses(name, scope, true); + if (result.isEmpty()) { + result.addAll(addClasses(name, scope, false)); + } + if (result.isEmpty()) { + result.addAll(addClasses(name, GlobalSearchScope.projectScope(myProject), false)); + } + return result; + } - final Collection classes = StubIndex.getInstance().get(GrFullClassNameIndex.KEY, name.hashCode(), myProject, new GrSourceFilterScope(scope)); + private List addClasses(String name, GlobalSearchScope scope, boolean inSource) { + final List result = new ArrayList(getScriptClassesByFQName(name, scope, inSource)); + + final Collection classes = StubIndex + .getInstance().get(GrFullClassNameIndex.KEY, name.hashCode(), myProject, inSource ? new GrSourceFilterScope(scope) : scope); if (!classes.isEmpty()) { //hashcode doesn't guarantee equals for (PsiElement psiClass : classes) { @@ -88,8 +101,7 @@ public class GroovyShortNamesCache extends PsiShortNamesCache { } } } - - return result.isEmpty() ? PsiClass.EMPTY_ARRAY : result.toArray(new PsiClass[result.size()]); + return result; } private Collection getAllScriptClasses(String shortName, GlobalSearchScope scope) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy index 2d86c68d3bfd..ee610af0a2b4 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy @@ -37,6 +37,13 @@ import com.intellij.util.concurrency.Semaphore import org.jetbrains.plugins.groovy.debugger.GroovyPositionManager import com.intellij.execution.runners.ProgramRunner import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.Disposable +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.application.ApplicationManager +import com.intellij.debugger.engine.ContextUtil +import com.intellij.debugger.SourcePosition /** * @author peter @@ -136,9 +143,61 @@ println 2""") } } + public void testClassOutOfSourceRoots() { + def tempDir = new TempDirTestFixtureImpl() + edt { + tempDir.setUp() + disposeOnTearDown({ tempDir.tearDown() } as Disposable) + ApplicationManager.application.runWriteAction { + def model = ModuleRootManager.getInstance(myModule).modifiableModel + model.addContentEntry(tempDir.getFile('')) + model.commit() + } + } + + VirtualFile myClass = null + + def mcText = """ +package foo //1 + +class MyClass { //3 +static def foo(def a) { + println a //5 +} +} +""" + + + edt { + myClass = tempDir.createFile("MyClass.groovy", mcText) + } + + addBreakpoint(myClass, 5) + + myFixture.addFileToProject("Foo.groovy", """ +def cl = new GroovyClassLoader() +cl.parseClass('''$mcText''', 'MyClass.groovy').foo(2) + """) + make() + + runDebugger 'Foo', { + waitForBreakpoint() + SourcePosition position = managed { ContextUtil.getSourcePosition(evaluationContext()) } + assert myClass == position.file.virtualFile + eval 'a', '2' + } + } + private def addBreakpoint(String fileName, int line) { + VirtualFile file = null + edt { + file = myFixture.tempDirFixture.getFile(fileName) + } + addBreakpoint(file, line) + } + + private def addBreakpoint(VirtualFile file, int line) { edt { - def file = myFixture.tempDirFixture.getFile(fileName) DebuggerManagerImpl.getInstanceEx(project).breakpointManager.addLineBreakpoint(FileDocumentManager.instance.getDocument(file), line) } } @@ -180,7 +239,6 @@ println 2""") } private String eval(final String codeText, String expected) throws EvaluateException { - final SuspendContextImpl suspendContext = debugProcess.suspendManager.pausedContext Semaphore semaphore = new Semaphore() semaphore.down() @@ -189,15 +247,18 @@ println 2""") EvaluationContextImpl ctx def item = new WatchItemDescriptor(project, new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, codeText)) managed { - ctx = new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject()) + ctx = evaluationContext() item.setContext(ctx) item.updateRepresentation(ctx, { semaphore.up() } as DescriptorLabelListener) } - semaphore.waitFor() + assert semaphore.waitFor(10000): "too long evaluation: $item.label" String result = managed { DebuggerUtils.getValueAsString(ctx, item.value) } assert result == expected } - + private EvaluationContextImpl evaluationContext() { + final SuspendContextImpl suspendContext = debugProcess.suspendManager.pausedContext + new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject()) + } }