blob: 48a04ecc5d4f70a04836455e1e535589aaec443d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.debug.ui.actions;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension2;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.statet.ecommons.text.IMarkerPositionResolver;
import org.eclipse.statet.ecommons.text.ui.AnnotationMarkerPositionResolver;
import org.eclipse.statet.ltk.model.core.element.WorkspaceSourceUnit;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.r.core.model.RSourceUnit;
import org.eclipse.statet.r.core.model.RWorkspaceSourceUnit;
import org.eclipse.statet.r.debug.core.RDebugModel;
import org.eclipse.statet.r.debug.core.breakpoints.IRBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.IRLineBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RLineBreakpointValidator;
import org.eclipse.statet.r.ui.RUI;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
/**
* Toggles a line breakpoint in a R editor.
*/
public class RToggleBreakpointAdapter implements IToggleBreakpointsTargetExtension,
IToggleBreakpointsTargetExtension2 {
private static class Data {
private final IRSourceEditor fEditor;
private final RWorkspaceSourceUnit fSourceUnit;
private AbstractDocument fDocument;
Data(final IRSourceEditor editor, final RWorkspaceSourceUnit su) {
fEditor = editor;
fSourceUnit = su;
}
public IRSourceEditor getEditor() {
return fEditor;
}
public RWorkspaceSourceUnit getSourceUnit() {
return fSourceUnit;
}
public void init2(final IProgressMonitor monitor) {
fDocument = fSourceUnit.getDocument(monitor);
}
public AbstractDocument getDocument() {
return fDocument;
}
}
private IRBreakpoint fLastBreakpoint;
private long fLastStamp;
public RToggleBreakpointAdapter() {
}
private Data createData(final IRSourceEditor editor) {
if (editor == null) {
return null;
}
final RSourceUnit su = editor.getSourceUnit();
if (!(su instanceof RWorkspaceSourceUnit)) {
return null;
}
return new Data(editor, (RWorkspaceSourceUnit)su);
}
@Override
public boolean canToggleLineBreakpoints(final IWorkbenchPart part, final ISelection selection) {
final IRSourceEditor editor = getREditor(part, selection);
return (editor != null && editor.getSourceUnit() instanceof WorkspaceSourceUnit
&& selection instanceof ITextSelection );
}
@Override
public void toggleLineBreakpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
final Data data = createData(getREditor(part, selection));
if (data == null) {
return;
}
final IProgressService progressService = part.getSite().getWorkbenchWindow().getService(IProgressService.class);
try {
progressService.busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
data.init2(monitor);
if (selection instanceof ITextSelection) {
final ITextSelection textSelection = (ITextSelection) selection;
doToggleLineBreakpoint(data, textSelection.getOffset(), monitor);
}
}
catch (final BadLocationException e) {}
catch (final CoreException e) {
log(data, e);
}
}
});
}
catch (final InvocationTargetException e) {}
catch (final InterruptedException e) {}
}
@Override
public boolean canToggleMethodBreakpoints(final IWorkbenchPart part, final ISelection selection) {
final IRSourceEditor editor = getREditor(part, selection);
return (editor != null && editor.getSourceUnit() instanceof WorkspaceSourceUnit
&& selection instanceof ITextSelection );
}
@Override
public void toggleMethodBreakpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
final Data data = createData(getREditor(part, selection));
if (data == null) {
return;
}
final IProgressService progressService = part.getSite().getWorkbenchWindow().getService(IProgressService.class);
try {
progressService.busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
data.init2(monitor);
if (selection instanceof ITextSelection) {
final ITextSelection textSelection = (ITextSelection) selection;
doToggleMethodBreakpoint(data, textSelection.getOffset(), monitor);
}
}
catch (final BadLocationException e) {}
catch (final CoreException e) {
log(data, e);
}
}
});
}
catch (final InvocationTargetException e) {}
catch (final InterruptedException e) {}
}
@Override
public boolean canToggleWatchpoints(final IWorkbenchPart part, final ISelection selection) {
return false;
}
@Override
public void toggleWatchpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
}
@Override
public boolean canToggleBreakpoints(final IWorkbenchPart part, final ISelection selection) {
final IRSourceEditor editor = getREditor(part, selection);
return (editor != null && editor.getSourceUnit() instanceof WorkspaceSourceUnit
&& selection instanceof ITextSelection );
}
@Override
public void toggleBreakpoints(final IWorkbenchPart part, final ISelection selection) throws CoreException {
final Data data = createData(getREditor(part, selection));
final IProgressService progressService = part.getSite().getWorkbenchWindow().getService(IProgressService.class);
try {
progressService.busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
data.init2(monitor);
if (selection instanceof ITextSelection) {
final ITextSelection textSelection = (ITextSelection) selection;
doToggleBestBreakpoint(data, textSelection.getOffset(), monitor);
}
}
catch (final BadLocationException e) {}
catch (final CoreException e) {
log(data, e);
}
}
});
}
catch (final InvocationTargetException e) {}
catch (final InterruptedException e) {}
}
@Override
public boolean canToggleBreakpointsWithEvent(final IWorkbenchPart part,
final ISelection selection, final Event event) {
return canToggleBreakpoints(part, selection);
}
@Override
public void toggleBreakpointsWithEvent(final IWorkbenchPart part,
final ISelection selection, final Event event) throws CoreException {
if (event != null) {
if ((event.stateMask & SWT.MOD2) > 0) {
final IRSourceEditor rSourceEditor = getREditor(part, selection);
if (rSourceEditor != null && selection instanceof ITextSelection) {
try {
final Data data = createData(rSourceEditor);
if (data == null) {
return;
}
final ITextSelection textSelection = (ITextSelection) selection;
data.init2(null);
final int lineNumber = data.getDocument()
.getLineOfOffset(textSelection.getOffset()) + 1;
final IRLineBreakpoint breakpoint = findFirst(data, lineNumber, null, null);
if (breakpoint != null) {
breakpoint.setEnabled(!breakpoint.isEnabled());
}
}
catch (final BadLocationException e) {
}
return;
}
}
}
toggleBreakpoints(part, selection);
}
public boolean removeBreakpoints(final IWorkbenchPart part, final ISelection selection,
final IProgressMonitor monitor) throws CoreException {
final Data data = createData(getREditor(part, selection));
if (data == null) {
return false;
}
if (selection instanceof ITextSelection) {
try {
data.init2(null);
if (selection instanceof ITextSelection) {
final ITextSelection textSelection = (ITextSelection) selection;
return checkSelectedLine(data, textSelection.getOffset(), null, monitor);
}
}
catch (final BadLocationException e) {}
catch (final CoreException e) {
log(data, e);
}
}
return false;
}
private void doToggleLineBreakpoint(final Data data, final int offset, final IProgressMonitor monitor)
throws BadLocationException, CoreException {
if (checkSelectedLine(data, offset, RDebugModel.R_LINE_BREAKPOINT_TYPE_ID, monitor)) {
return;
}
final RLineBreakpointValidator validator = new RLineBreakpointValidator(data.getSourceUnit(),
RDebugModel.R_LINE_BREAKPOINT_TYPE_ID, offset, monitor );
if (checkNewLine(data, validator, RDebugModel.R_LINE_BREAKPOINT_TYPE_ID, monitor)) {
return;
}
createNew(validator, monitor);
}
private void doToggleMethodBreakpoint(final Data data, final int offset, final IProgressMonitor monitor)
throws BadLocationException, CoreException {
if (checkSelectedLine(data, offset, RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID, monitor)) {
return;
}
final RLineBreakpointValidator validator = new RLineBreakpointValidator(data.getSourceUnit(),
RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID, offset, monitor );
if (checkNewLine(data, validator, RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID, monitor)) {
return;
}
createNew(validator, monitor);
}
private void doToggleBestBreakpoint(final Data data, final int offset, final IProgressMonitor monitor)
throws BadLocationException, CoreException {
if (checkSelectedLine(data, offset, null, monitor)) {
return;
}
final RLineBreakpointValidator validator = new RLineBreakpointValidator(data.getSourceUnit(),
null, offset, monitor);
if (checkNewLine(data, validator, null, monitor)) {
return;
}
createNew(validator, monitor);
}
private boolean checkSelectedLine(final Data data, final int offset, final String type,
final IProgressMonitor monitor)
throws BadLocationException, CoreException {
final int lineNumber = data.getDocument().getLineOfOffset(offset) + 1;
final IRLineBreakpoint breakpoint = findFirst(data, lineNumber,
type, fLastBreakpoint);
if (breakpoint != null) {
DebugUITools.deleteBreakpoints(new IBreakpoint[] { breakpoint },
getShell(data), monitor);
return true;
}
return false;
}
private boolean checkNewLine(final Data data, final RLineBreakpointValidator validator, final String type,
final IProgressMonitor monitor)
throws CoreException {
if (validator.getLineNumber() >= 0) {
final int lineCorr = Math.abs(validator.getLineNumber() - validator.getOriginalLineNumber());
if (lineCorr > 0) {
final IRLineBreakpoint breakpoint = findFirst(data, validator.getLineNumber(),
type, fLastBreakpoint);
if (breakpoint != null) {
if (breakpoint == fLastBreakpoint
|| type == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID
|| lineCorr <= 2) {
DebugUITools.deleteBreakpoints(new IBreakpoint[] { breakpoint },
getShell(data), monitor);
}
return true;
}
}
}
return false;
}
private IRLineBreakpoint findFirst(final Data data, final int lineNumber, final String type,
final IRBreakpoint last)
throws CoreException {
final List<IRLineBreakpoint> breakpoints = RDebugModel.getLineBreakpoints(
(IFile) data.getSourceUnit().getResource() );
if (breakpoints.isEmpty()) {
return null;
}
final IMarkerPositionResolver resolver = getMarkerPositionResolver(data);
if (last != null
&& (type == null || last.getBreakpointType() == type) ) {
for (final IRLineBreakpoint breakpoint : breakpoints) {
if (breakpoint == last
&& ((resolver != null) ? resolver.getLine(breakpoint.getMarker()) : breakpoint.getLineNumber()) == lineNumber ) {
return breakpoint;
}
}
}
for (final IRLineBreakpoint breakpoint : breakpoints) {
if ((type == null || breakpoint.getBreakpointType() == type)
&& ((resolver != null) ? resolver.getLine(breakpoint.getMarker()) : breakpoint.getLineNumber()) == lineNumber ) {
return breakpoint;
}
}
return null;
}
private boolean createNew(final RLineBreakpointValidator validator, final IProgressMonitor monitor) {
final IRBreakpoint breakpoint = validator.createBreakpoint(monitor);
if (breakpoint != null) {
fLastBreakpoint = breakpoint;
fLastStamp = System.currentTimeMillis();
return true;
}
return false;
}
private IRSourceEditor getREditor(final IWorkbenchPart part, final ISelection selection) {
if (part instanceof IRSourceEditor) {
return (IRSourceEditor) part;
}
final Object adapter = part.getAdapter(ISourceEditor.class);
if (adapter instanceof IRSourceEditor) {
return (IRSourceEditor) adapter;
}
return null;
}
protected IMarkerPositionResolver getMarkerPositionResolver(final Data data) {
final AbstractDocument document = data.getDocument();
final AbstractMarkerAnnotationModel model = getAnnotationModel(data.getEditor());
if (document != null && model != null) {
return new AnnotationMarkerPositionResolver(document, model);
}
return null;
}
protected AbstractMarkerAnnotationModel getAnnotationModel(final IRSourceEditor editor) {
if (editor instanceof AbstractTextEditor) {
final AbstractTextEditor textEditor = (AbstractTextEditor) editor;
final IDocumentProvider provider = textEditor.getDocumentProvider();
final IAnnotationModel model = provider.getAnnotationModel(textEditor.getEditorInput());
if (model instanceof AbstractMarkerAnnotationModel) {
return (AbstractMarkerAnnotationModel) model;
}
}
return null;
}
protected void log(final Data data, final CoreException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, 0,
NLS.bind("An error occurred when toggling an R method breakpoint in ''{0}''.",
data.getSourceUnit().getElementName().getDisplayName() ), e ));
}
private Shell getShell(final Data data) {
{ final IWorkbenchPart part = data.getEditor().getWorkbenchPart();
if (part != null) {
return part.getSite().getShell();
}
}
return null;
}
}