| /******************************************************************************* |
| * Copyright (c) 2005, 2017 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.debug.ui.breakpoints; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.debug.core.model.IBreakpoint; |
| import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension; |
| import org.eclipse.dltk.core.IField; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.debug.core.model.IScriptVariable; |
| import org.eclipse.dltk.debug.ui.DLTKDebugUIPlugin; |
| import org.eclipse.dltk.internal.ui.editor.WorkingCopyManager; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.IWorkingCopyManager; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.texteditor.IEditorStatusLine; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| |
| public abstract class ScriptToggleBreakpointAdapter |
| implements IToggleBreakpointsTargetExtension { |
| |
| protected boolean isRemote(IWorkbenchPart part, ISelection selection) { |
| if (selection instanceof IStructuredSelection) { |
| IStructuredSelection ss = (IStructuredSelection) selection; |
| Object element = ss.getFirstElement(); |
| if (element instanceof IMember) { |
| IMember member = (IMember) element; |
| return !member.getScriptProject().getProject().exists(); |
| } |
| } |
| ITextEditor editor = getTextEditor(part); |
| if (editor != null) { |
| IEditorInput input = editor.getEditorInput(); |
| Object adapter = Platform.getAdapterManager().getAdapter(input, |
| "org.eclipse.team.core.history.IFileRevision"); //$NON-NLS-1$ |
| return adapter != null; |
| } |
| return false; |
| } |
| |
| protected String getSelectedFirstLine(ITextEditor editor, |
| ITextSelection selection) { |
| try { |
| IDocument doc = editor.getDocumentProvider().getDocument(null); |
| final int line = selection.getStartLine(); |
| IRegion region = doc.getLineInformation(line /* - 1 */); |
| return doc.get(region.getOffset(), region.getLength()); |
| } catch (BadLocationException e) { |
| DLTKUIPlugin.log(e); |
| } |
| |
| return null; |
| } |
| |
| protected String getSelectedText(ITextEditor editor, |
| ITextSelection selection) { |
| try { |
| IDocument doc = editor.getDocumentProvider().getDocument(null); |
| return doc.get(selection.getOffset(), selection.getLength()); |
| } catch (BadLocationException e) { |
| DLTKUIPlugin.log(e); |
| } |
| |
| return null; |
| } |
| |
| protected void report(final String message, final IWorkbenchPart part) { |
| DLTKDebugUIPlugin.getStandardDisplay().asyncExec(() -> { |
| IEditorStatusLine statusLine = part |
| .getAdapter(IEditorStatusLine.class); |
| if (statusLine != null) { |
| if (message != null) { |
| statusLine.setMessage(true, message, null); |
| } else { |
| statusLine.setMessage(true, null, null); |
| } |
| } |
| if (message != null |
| && DLTKDebugUIPlugin.getActiveWorkbenchShell() != null) { |
| DLTKDebugUIPlugin.getActiveWorkbenchShell().getDisplay().beep(); |
| } |
| }); |
| } |
| |
| protected ITextEditor getTextEditor(IWorkbenchPart part) { |
| if (part instanceof ITextEditor) { |
| return (ITextEditor) part; |
| } |
| return part.getAdapter(ITextEditor.class); |
| } |
| |
| protected static final int BREAKPOINT_LINE_NOT_FOUND = -1; |
| |
| protected static int findBreakpointLine(IDocument document, int startLine, |
| IScriptBreakpointLineValidator validator) { |
| String line = null; |
| int lineNumber = startLine; |
| int numberOfLines = document.getNumberOfLines(); |
| |
| if (lineNumber < 0 || lineNumber >= numberOfLines) { |
| return BREAKPOINT_LINE_NOT_FOUND; |
| } |
| |
| while (lineNumber < numberOfLines) { |
| try { |
| IRegion region = document.getLineInformation(lineNumber); |
| line = document.get(region.getOffset(), region.getLength()); |
| |
| if (validator.isValid(line, lineNumber)) { |
| return lineNumber; |
| } |
| |
| lineNumber++; |
| } catch (BadLocationException e) { |
| DLTKDebugUIPlugin.log(e); |
| } |
| } |
| |
| return BREAKPOINT_LINE_NOT_FOUND; |
| } |
| |
| public ScriptToggleBreakpointAdapter() { |
| |
| } |
| |
| @Override |
| public boolean canToggleLineBreakpoints(IWorkbenchPart part, |
| ISelection selection) { |
| if (isRemote(part, selection)) { |
| return false; |
| } |
| |
| return selection instanceof ITextSelection; |
| } |
| |
| /** |
| * Returns a selection of the member in the given text selection, or the |
| * original selection if none. |
| * |
| * @param part |
| * @param selection |
| * @return a structured selection of the member in the given text selection, |
| * or the original selection if none |
| * @exception CoreException |
| * if an exception occurs |
| */ |
| protected ISelection translateToMembers(IWorkbenchPart part, |
| ISelection selection) throws CoreException { |
| ITextEditor textEditor = getTextEditor(part); |
| if (textEditor != null && selection instanceof ITextSelection) { |
| ITextSelection textSelection = (ITextSelection) selection; |
| IEditorInput editorInput = textEditor.getEditorInput(); |
| IDocumentProvider documentProvider = textEditor |
| .getDocumentProvider(); |
| if (documentProvider == null) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| IDocument document = documentProvider.getDocument(editorInput); |
| int offset = textSelection.getOffset(); |
| if (document != null) { |
| try { |
| IRegion region = document |
| .getLineInformationOfOffset(offset); |
| int end = region.getOffset() + region.getLength(); |
| while (Character.isWhitespace(document.getChar(offset)) |
| && offset < end) { |
| offset++; |
| } |
| } catch (BadLocationException e) { |
| } |
| } |
| IMember m = null; |
| |
| IWorkingCopyManager manager = DLTKUIPlugin.getDefault() |
| .getWorkingCopyManager(); |
| ISourceModule unit = manager.getWorkingCopy(editorInput); |
| if (unit != null) { |
| synchronized (unit) { |
| unit.reconcile(false, null, null); |
| } |
| } |
| |
| IModelElement e = unit.getElementAt(offset); |
| if (e instanceof IMember) { |
| m = (IMember) e; |
| } |
| |
| if (m != null) { |
| return new StructuredSelection(m); |
| } |
| } |
| return selection; |
| } |
| |
| @Override |
| public void toggleLineBreakpoints(final IWorkbenchPart part, |
| final ISelection selection) throws CoreException { |
| |
| Job job = new Job("Script Toggle Line Breakpoint") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| final ITextEditor editor = getTextEditor(part); |
| if (editor != null && selection instanceof ITextSelection) { |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| try { |
| report(null, part); |
| |
| int lineNumber = ((ITextSelection) selection) |
| .getStartLine() + 1; |
| |
| final String debugModelId = getDebugModelId(); |
| final IBreakpoint breakpoint = BreakpointUtils |
| .findLineBreakpoint(editor, lineNumber, |
| debugModelId); |
| |
| if (breakpoint != null) { |
| // if breakpoint already exists, delete it |
| breakpoint.delete(); |
| } else { |
| final IDocumentProvider documentProvider = editor |
| .getDocumentProvider(); |
| if (documentProvider == null) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| final IDocument document = documentProvider |
| .getDocument(editor.getEditorInput()); |
| |
| lineNumber = findBreakpointLine(document, |
| lineNumber - 1, getValidator()) + 1; |
| |
| if (lineNumber != BREAKPOINT_LINE_NOT_FOUND) { |
| // Check if already breakpoint set to the same |
| // location |
| if (BreakpointUtils.findLineBreakpoint(editor, |
| lineNumber, debugModelId) == null) { |
| BreakpointUtils.addLineBreakpoint(editor, |
| lineNumber, debugModelId); |
| } else { |
| report(NLS.bind( |
| Messages.ScriptToggleBreakpointAdapter_breakpointAlreadySetAtLine, |
| lineNumber), part); |
| } |
| } else { |
| report(Messages.ScriptToggleBreakpointAdapter_invalidBreakpointPosition, |
| part); |
| } |
| } |
| } catch (CoreException e) { |
| DLTKDebugUIPlugin.log(e); |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| |
| }; |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * gets the <code>IJavaElement</code> from the editor input |
| * |
| * @param input |
| * the current editor input |
| * @return the corresponding <code>IJavaElement</code> |
| */ |
| protected IModelElement getModelElement(IEditorInput input) { |
| IModelElement je = DLTKUIPlugin.getEditorInputModelElement(input); |
| if (je != null) { |
| return je; |
| } |
| // try to get from the working copy manager |
| return ((WorkingCopyManager) DLTKUIPlugin.getDefault() |
| .getWorkingCopyManager()).getWorkingCopy(input, false); |
| } |
| |
| /** |
| * Returns if the text selection is a field selection or not |
| * |
| * @param selection |
| * the text selection |
| * @param part |
| * the associated workbench part |
| * @return true if the text selection is a valid field for a watchpoint, |
| * false otherwise |
| * @since 3.3 |
| */ |
| protected boolean isField(ITextSelection selection, IWorkbenchPart part) { |
| ITextEditor editor = getTextEditor(part); |
| if (editor != null) { |
| IModelElement element = getModelElement(editor.getEditorInput()); |
| if (element != null) { |
| try { |
| if (element instanceof ISourceModule) { |
| element = ((ISourceModule) element) |
| .getElementAt(selection.getOffset()); |
| } |
| return element != null |
| && element.getElementType() == IModelElement.FIELD; |
| } catch (ModelException e) { |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Determines if the selection is a field or not |
| * |
| * @param selection |
| * the current selection |
| * @return true if the selection is a field false otherwise |
| */ |
| protected boolean isFields(IStructuredSelection selection) { |
| if (!selection.isEmpty()) { |
| for (Iterator<?> i = selection.iterator(); i.hasNext();) { |
| Object thing = i.next(); |
| if (thing instanceof IField) { |
| return true; |
| } else if (thing instanceof IScriptVariable) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns a list of <code>IField</code> and <code>IJavaFieldVariable</code> |
| * in the given selection. When an <code>IField</code> can be resolved for |
| * an <code>IJavaFieldVariable</code>, it is returned in favour of the |
| * variable. |
| * |
| * @param selection |
| * @return list of <code>IField</code> and <code>IJavaFieldVariable</code>, |
| * possibly empty |
| * @throws CoreException |
| */ |
| protected List<?> getFields(IStructuredSelection selection) |
| throws CoreException { |
| if (selection.isEmpty()) { |
| return Collections.EMPTY_LIST; |
| } |
| List<Object> fields = new ArrayList<>(selection.size()); |
| Iterator<?> iterator = selection.iterator(); |
| while (iterator.hasNext()) { |
| Object thing = iterator.next(); |
| if (thing instanceof IField) { |
| fields.add(thing); |
| } else if (thing instanceof IScriptVariable) { |
| fields.add(thing); |
| } |
| } |
| return fields; |
| } |
| |
| protected abstract String getDebugModelId(); |
| |
| protected IScriptBreakpointLineValidator getValidator() { |
| return ScriptBreakpointLineValidatorFactory.NON_EMPTY_VALIDATOR; |
| } |
| } |