Skip to content

Commit

Permalink
Fix for issue #544: add breakpoint with double-click in class file ruler
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Apr 12, 2018
1 parent ac1d902 commit 090c6b7
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2017 the original author or authors.
* Copyright 2009-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -71,7 +71,7 @@ private static boolean isGroovyLikeSourceUnit(ICompilationUnit sourceUnit) {
return false;
}
private static final Pattern GROOVY_SOURCE_DISCRIMINATOR =
Pattern.compile("\\Apackage\\s+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\s*\\.\\s*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*\\s++(?!;)");
Pattern.compile("\\A(/\\*.*?\\*/\\s*)?package\\s+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(?:\\s*\\.\\s*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*\\s++(?!;)", Pattern.DOTALL);

@Override
public void reset() {
Expand Down
32 changes: 26 additions & 6 deletions ide/org.codehaus.groovy.eclipse.ui/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,14 @@
<adapter
type="org.codehaus.groovy.ast.ModuleNode"/>
</factory>
<factory
adaptableType="org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor"
class="org.codehaus.groovy.eclipse.adapters.ClassFileEditorAdapterFactory">
<adapter
type="org.codehaus.groovy.ast.ClassNode"/>
<adapter
type="org.codehaus.groovy.ast.ModuleNode"/>
</factory>
<factory
adaptableType="org.codehaus.groovy.eclipse.editor.GroovyEditor"
class="org.codehaus.groovy.eclipse.debug.ui.GroovyRetargettableActionAdapterFactory">
Expand All @@ -881,28 +889,40 @@
</extension>

<extension point="org.eclipse.ui.editorActions">
<!-- Ruler actions -->
<editorContribution
id="org.codehaus.groovy.editor.actions"
targetID="org.codehaus.groovy.eclipse.editor.GroovyEditor">
<!-- Ruler actions -->
<action
actionID="RulerDoubleClick"
class="org.codehaus.groovy.eclipse.debug.ui.GroovyBreakpointRulerActionDelegate"
id="org.groovy.codehaus.eclipse.ui.actions.ManageBreakpointRulerAction"
id="org.codehaus.groovy.editor.ruler.ToggleBreakpointRulerAction"
label="Toggle Breakpoint">
</action>
<action
actionID="RulerClick"
class="org.eclipse.jdt.internal.ui.javaeditor.JavaSelectRulerAction"
id="org.eclipse.jdt.internal.ui.javaeditor.JavaSelectRulerAction"
label="Select Annotation ruler">
id="org.codehaus.groovy.editor.ruler.SelectAnnotationRulerAction"
label="Select Annotation">
</action>
<action
class="org.codehaus.groovy.eclipse.editor.actions.FindOccurrencesAction"
definitionId="org.codehaus.groovy.eclipse.ui.findOccurrences"
id="org.codehaus.groovy.eclipse.ui.action.findOccurrences"
label="Find Occurrences in file"
style="push">
label="Find Occurrences in file">
</action>
</editorContribution>
<editorContribution
id="org.codehaus.groovy.class_editor.actions"
targetID="org.eclipse.jdt.ui.ClassFileEditor">
<action
actionID="RulerDoubleClick"
class="org.codehaus.groovy.eclipse.debug.ui.GroovyBreakpointRulerActionDelegate"
id="org.codehaus.groovy.class_editor.ruler.ToggleBreakpointRulerAction"
label="Toggle Breakpoint">
<!--enablement>
TODO: For now this applies to all .class files with source attachment
</enablement-->
</action>
</editorContribution>
</extension>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2009-2018 the original author or authors.
*
* 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 org.codehaus.groovy.eclipse.adapters;

import java.util.Arrays;
import java.util.List;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.ui.SharedASTProvider;

public class ClassFileEditorAdapterFactory implements IAdapterFactory {

@Override
public Class<?>[] getAdapterList() {
return new Class[] {ClassNode.class, ClassNode[].class, ModuleNode.class};
}

@Override @SuppressWarnings("unchecked")
public <T> T getAdapter(Object adaptable, Class<T> adapterType) {
T result = null;
if (adaptable instanceof ClassFileEditor && Arrays.asList(getAdapterList()).contains(adapterType)) {
ClassFileEditor editor = (ClassFileEditor) adaptable;
// trigger discriminator in MultiplexingCommentRecorderParser
ITypeRoot root = EditorUtility.getEditorInputJavaElement(editor, false);
CompilationUnit unit = SharedASTProvider.getAST(root, SharedASTProvider.WAIT_YES, null);
if (unit.getClass().getName().equals("org.codehaus.jdt.groovy.core.dom.GroovyCompilationUnit")) {
// TODO: Is there a better way to get from GroovyCompilationUnit to GroovyCompilationUnitDeclaration?
Object resolver = ReflectionUtils.executeNoArgPrivateMethod(org.eclipse.jdt.core.dom.AST.class, "getBindingResolver", unit.getAST());
CompilationUnitScope scope = (CompilationUnitScope) ReflectionUtils.executeNoArgPrivateMethod(resolver.getClass(), "scope", resolver);

ModuleNode moduleNode = ((GroovyCompilationUnitDeclaration) scope.referenceContext).getModuleNode();
if (adapterType.equals(ModuleNode.class)) {
result = (T) moduleNode;
} else if (moduleNode != null) {
List<ClassNode> classNodes = moduleNode.getClasses();
if (classNodes != null && !classNodes.isEmpty()) {
if (adapterType.equals(ClassNode.class)) {
result = (T) classNodes.get(0);
} else if (adapterType.equals(ClassNode[].class)) {
result = (T) classNodes.toArray();
}
}
}
}
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,7 @@ public class BreakpointLocationVerifierJob extends Job {
*/
private IResource fResource;


/**
* The status line to use to display errors
*/
private IEditorStatusLine fStatusLine;
private IEditorPart fEditorPart;

public BreakpointLocationVerifierJob(IJavaLineBreakpoint breakpoint, int lineNumber, String typeName, IType type, IResource resource, IEditorPart editorPart) {
super(ActionMessages.BreakpointLocationVerifierJob_breakpoint_location);
Expand All @@ -93,26 +89,26 @@ public BreakpointLocationVerifierJob(IJavaLineBreakpoint breakpoint, int lineNum
fTypeName = typeName;
fType = type;
fResource = resource;
fStatusLine = Adapters.adapt(editorPart, IEditorStatusLine.class);
fEditorPart = editorPart;
}

@Override
public IStatus run(IProgressMonitor monitor) {
try {
ModuleNode module = Adapters.adapt(fResource, ModuleNode.class);
if (module == null) {
return new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), ActionMessages.BreakpointLocationVerifierJob_not_valid_location);
}
if (fBreakpoint != null) {
DebugPlugin.getDefault().getBreakpointManager().removeBreakpoint(fBreakpoint, true);
}
ASTNode valid = new BreakpointLocationFinder(module).findBreakpointLocation(fLineNumber);
if (valid instanceof MethodNode && ((MethodNode) valid).getNameEnd() > 0) {
createNewMethodBreakpoint((MethodNode) valid, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), ActionMessages.BreakpointLocationVerifierJob_breakpoint_set);
} else if (valid != null) {
createNewLineBreakpoint(valid, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), ActionMessages.BreakpointLocationVerifierJob_breakpoint_set);

ModuleNode module = Adapters.adapt(fEditorPart, ModuleNode.class);
if (module != null) {
ASTNode found = new BreakpointLocationFinder(module).findBreakpointLocation(fLineNumber);
if (found instanceof MethodNode && ((MethodNode) found).getNameEnd() > 0) {
createNewMethodBreakpoint((MethodNode) found, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), ActionMessages.BreakpointLocationVerifierJob_breakpoint_set);
} else if (found != null) {
createNewLineBreakpoint(found, fTypeName);
return new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), ActionMessages.BreakpointLocationVerifierJob_breakpoint_set);
}
}
} catch (CoreException e) {
JDIDebugUIPlugin.log(new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), "Breakpoint location verification failed", e));
Expand Down Expand Up @@ -179,8 +175,9 @@ private void createNewLineBreakpoint(ASTNode node, String typeName) throws CoreE

protected void report(final String message) {
JDIDebugUIPlugin.getStandardDisplay().asyncExec(() -> {
if (fStatusLine != null) {
fStatusLine.setMessage(true, message, null);
IEditorStatusLine statusLine = Adapters.adapt(fEditorPart, IEditorStatusLine.class);
if (statusLine != null) {
statusLine.setMessage(true, message, null);
}
if (message != null && JDIDebugUIPlugin.getActiveWorkbenchShell() != null) {
Display.getCurrent().beep();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,74 +1,89 @@
/*******************************************************************************
* Copyright (c) 2000-2012 IBM Corporation, SpringSource and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
/*
* Copyright 2009-2018 the original author or authors.
*
* Contributors: IBM Corporation - initial version
* Andrew Eisenberg - convert for use with Groovy
******************************************************************************/
* 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 org.codehaus.groovy.eclipse.debug.ui;

import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IResource;
import static org.codehaus.jdt.groovy.model.GroovyNature.hasGroovyNature;

import org.codehaus.groovy.ast.ModuleNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;

public class GroovyBreakpointRulerActionDelegate extends RulerToggleBreakpointActionDelegate {

private IEditorPart fEditorPart;
private GroovyBreakpointRulerAction groovyDelegate;
/**
* @see IEditorActionDelegate#setActiveEditor(bIAction, IEditorPart)
*/
@Override
public void setActiveEditor(IAction callerAction, IEditorPart targetEditor) {
fEditorPart = targetEditor;
super.setActiveEditor(callerAction, targetEditor);
}
private GroovyBreakpointRulerAction delegate;


/**
* @see AbstractRulerActionDelegate#createAction()
*/
@Override
protected IAction createAction(ITextEditor editor,
IVerticalRulerInfo rulerInfo) {
IResource resource;
IEditorInput editorInput = editor.getEditorInput();
if (editorInput instanceof IFileEditorInput) {
resource = ((IFileEditorInput) editorInput).getFile();
if (GroovyNature.hasGroovyNature(resource.getProject())) {
groovyDelegate = new GroovyBreakpointRulerAction(rulerInfo, editor,
fEditorPart);
return groovyDelegate;
}
protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
IProject project = getProject(editor.getEditorInput());
if (project != null && hasGroovyNature(project) && hasGroovySource(editor)) {
delegate = new GroovyBreakpointRulerAction(rulerInfo, editor, getEditorPart());
return delegate;
}
// else: use jdt's action
return super.createAction(editor, rulerInfo);
}

/* (non-Javadoc)
* @see org.eclipse.ui.IActionDelegate2#runWithEvent(org.eclipse.jface.action.IAction, org.eclipse.swt.widgets.Event)
*/
@Override
public void runWithEvent(IAction action, Event event) {
if(groovyDelegate != null) {
groovyDelegate.runWithEvent(event);
if (delegate != null) {
delegate.runWithEvent(event);
} else {
super.runWithEvent(action, event);
}
}

@Override
public void dispose() {
groovyDelegate = null;
if (delegate != null) {
delegate.dispose();
delegate = null;
}
super.dispose();
}

//--------------------------------------------------------------------------

protected IEditorPart getEditorPart() {
return (IEditorPart) ReflectionUtils.getPrivateField(RulerToggleBreakpointActionDelegate.class, "fEditor", this);
}

protected IProject getProject(IEditorInput editorInput) {
IFile file = Adapters.adapt(editorInput, IFile.class);
if (file != null) {
return file.getProject();
}

IClassFile classFile = Adapters.adapt(editorInput, IClassFile.class);
if (classFile != null) {
return classFile.getJavaProject().getProject();
}

return null;
}

protected boolean hasGroovySource(ITextEditor editor) {
return (Adapters.adapt(editor, ModuleNode.class) != null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,21 +204,25 @@ protected IStatus run(IProgressMonitor monitor) {
resource = BreakpointUtils.getBreakpointResource(type);
try {
IRegion line = document.getLineInformation(lineNumber - 1);
int start = line.getOffset();
int end = start + line.getLength() - 1;
int start = line.getOffset(), end = start + line.getLength() - 1;
BreakpointUtils.addJavaBreakpointAttributesWithMemberDetails(attributes, type, start, end);
} catch (BadLocationException ble) {
JDIDebugUIPlugin.log(ble);
}
}

if (typeName != null && resource != null) {
IJavaLineBreakpoint existingBreakpoint = JDIDebugModel.lineBreakpointExists(resource, typeName, lineNumber);
if (typeName != null) {
IJavaLineBreakpoint existingBreakpoint = JDIDebugModel.lineBreakpointExists(typeName, lineNumber);
if (existingBreakpoint != null) {
removeBreakpoint(existingBreakpoint, true);
return Status.OK_STATUS;
}
createLineBreakpoint(resource, typeName, offset, lineNumber, -1, -1, 0, true, attributes, document, bestMatch, type, editorPart);
if (resource != null) {
int charStart = -1, charEnd = -1, hitCount = 0; boolean register = true;
IJavaLineBreakpoint breakpoint = JDIDebugModel.createLineBreakpoint(
resource, typeName, lineNumber, charStart, charEnd, hitCount, register, attributes);
new BreakpointLocationVerifierJob(breakpoint, lineNumber, typeName, type, resource, editorPart).schedule();
}
}
} catch (CoreException ce) {
return ce.getStatus();
Expand All @@ -240,11 +244,6 @@ public void toggleWatchpoints(IWorkbenchPart part, ISelection finalSelection) {

//--------------------------------------------------------------------------

private void createLineBreakpoint(IResource resource, String typeName, int offset, int lineNumber, int charStart, int charEnd, int hitCount, boolean register, Map<String, Object> attributes, IDocument document, boolean bestMatch, IType type, IEditorPart editorPart) throws CoreException {
IJavaLineBreakpoint breakpoint = JDIDebugModel.createLineBreakpoint(resource, typeName, lineNumber, charStart, charEnd, hitCount, register, attributes);
new BreakpointLocationVerifierJob(breakpoint, lineNumber, typeName, type, resource, editorPart).schedule();
}

protected IType getType(ITextSelection selection) {
IMember member = ActionDelegateHelper.getDefault().getCurrentMember(selection);
IType type = null;
Expand Down

0 comments on commit 090c6b7

Please sign in to comment.