mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
correctly stop at the breakpoint in groovy classes not in source (IDEA-64023)
This commit is contained in:
+5
-68
@@ -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>>() {
|
||||
|
||||
+2
-2
@@ -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()]);
|
||||
}
|
||||
|
||||
|
||||
+19
-7
@@ -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) {
|
||||
|
||||
+66
-5
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user