correctly stop at the breakpoint in groovy classes not in source (IDEA-64023)

This commit is contained in:
peter
2011-04-13 19:04:38 +02:00
parent 6888120224
commit b4e5f3d78f
4 changed files with 92 additions and 82 deletions
@@ -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<PsiClass> 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<VirtualFile> query = directoryIndex.getDirectoriesByPackageName(packageName, true);
final String fileNameWithoutExtension = dotIndex > 0 ? qName.substring(dotIndex + 1) : qName;
final Set<String> extensions = getAllGroovyFileExtensions();
final Ref<PsiFile> result = new Ref<PsiFile>();
query.forEach(new Processor<VirtualFile>() {
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<String> getAllGroovyFileExtensions() {
final Set<String> extensions = new HashSet<String>();
extensions.addAll(GroovyFileTypeLoader.getAllGroovyExtensions());
extensions.add("gvy");
extensions.add("gy");
extensions.add("gsh");
return extensions;
}
@NotNull
public List<ReferenceType> getAllClasses(final SourcePosition position) throws NoDataException {
List<ReferenceType> result = ApplicationManager.getApplication().runReadAction(new Computable<List<ReferenceType>>() {
@@ -38,13 +38,13 @@ public class GroovyClassFinder extends PsiElementFinder {
@Nullable
public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
final List<PsiClass> classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope);
final List<PsiClass> 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<PsiClass> classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope);
final Collection<PsiClass> classes = myGroovyPsiManager.getNamesCache().getScriptClassesByFQName(qualifiedName, scope, true);
return classes.isEmpty() ? PsiClass.EMPTY_ARRAY : classes.toArray(new PsiClass[classes.size()]);
}
@@ -57,8 +57,9 @@ public class GroovyShortNamesCache extends PsiShortNamesCache {
return allClasses.toArray(new PsiClass[allClasses.size()]);
}
public List<PsiClass> getScriptClassesByFQName(final String name, final GlobalSearchScope scope) {
final Collection<GroovyFile> files = StubIndex.getInstance().get(GrFullScriptNameIndex.KEY, name.hashCode(), myProject, new GrSourceFilterScope(scope));
public List<PsiClass> getScriptClassesByFQName(final String name, final GlobalSearchScope scope, final boolean srcOnly) {
GlobalSearchScope actualScope = srcOnly ? new GrSourceFilterScope(scope) : scope;
final Collection<GroovyFile> 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<PsiClass> result = new ArrayList<PsiClass>(getScriptClassesByFQName(name, scope));
public List<PsiClass> getClassesByFQName(@NotNull @NonNls String name, @NotNull GlobalSearchScope scope) {
final List<PsiClass> 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<? extends PsiElement> classes = StubIndex.getInstance().get(GrFullClassNameIndex.KEY, name.hashCode(), myProject, new GrSourceFilterScope(scope));
private List<PsiClass> addClasses(String name, GlobalSearchScope scope, boolean inSource) {
final List<PsiClass> result = new ArrayList<PsiClass>(getScriptClassesByFQName(name, scope, inSource));
final Collection<? extends PsiElement> 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<PsiClass> getAllScriptClasses(String shortName, GlobalSearchScope scope) {
@@ -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())
}
}