/*******************************************************************************
* Copyright (c) 2013, 2015 Ericsson AB 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
*
* Contributors:
* Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865)
*******************************************************************************/
package org.eclipse.cdt.dsf.debug.ui.actions;
import org.eclipse.cdt.core.model.ICLanguageKeywords;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.debug.core.model.IStepIntoSelectionHandler;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.internal.ui.Messages;
import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver;
import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver.LineLocation;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.text.CWordFinder;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.ui.DebugUITools;
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.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* @since 2.4
*
*/
public class DsfStepIntoSelectionHyperlinkDetector extends AbstractHyperlinkDetector {
private class DsfStepIntoSelectionHyperlink implements IHyperlink {
private ITextSelection fSelection = null;
private IDsfStepIntoSelection fStepIntoSelectionCommand = null;
private DsfSourceSelectionResolver fSelectionResolver = null;
/**
* Constructor
*
* @param stepIntoSelectionCommand
* @param region
*/
public DsfStepIntoSelectionHyperlink(DsfSourceSelectionResolver selectionResolver, IDsfStepIntoSelection stepIntoSelectionCommand) {
fSelection = selectionResolver.resolveSelection();
fStepIntoSelectionCommand = stepIntoSelectionCommand;
fSelectionResolver = selectionResolver;
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(fSelection.getOffset(), fSelection.getLength());
}
@Override
public String getHyperlinkText() {
return Messages.DsfUIStepIntoEditorSelection;
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public void open() {
// Resolve the debug context
final IExecutionDMContext dmc = resolveDebugContext();
if (fSelectionResolver.isSuccessful() && dmc != null) {
LineLocation location = fSelectionResolver.getLineLocation();
fStepIntoSelectionCommand.runToSelection(location.getFileName(), location.getLineNumber(), fSelectionResolver.getFunction(), dmc);
} else {
String message = null;
if (dmc == null) {
message = "DSfStepIntoSelection: Unable to resolve the debug context"; //$NON-NLS-1$
} else {
message = "DSfStepIntoSelection: Unable to resolve a selected function"; //$NON-NLS-1$
}
DsfUIPlugin.debug(message);
}
}
}
@Override
public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region, boolean canShowMultipleHyperlinks) {
// Only valid in the context of a selection within the CEditor
ITextEditor editor = getAdapter(ITextEditor.class);
if (editor == null || region == null || !(editor instanceof CEditor))
return null;
ITextSelection selection = resolveSelection(editor, region);
if (selection == null) {
return null;
}
// Shall only enable hyper link step into selection within a cdt debug execution context
IExecutionDMContext dmc = resolveDebugContext();
if (dmc == null) {
return null;
}
final DsfSession session = DsfSession.getSession(dmc.getSessionId());
if (session == null || !session.isActive()) {
return null;
}
IDsfStepIntoSelection stepIntoSelectionCommand = null;
IStepIntoSelectionHandler stepIntoSelectionHandler = (IStepIntoSelectionHandler) session.getModelAdapter(IStepIntoSelectionHandler.class);
if (stepIntoSelectionHandler instanceof IDsfStepIntoSelection) {
stepIntoSelectionCommand = (IDsfStepIntoSelection)stepIntoSelectionHandler;
} else {
return null;
}
if (!stepIntoSelectionCommand.isExecutable(dmc)) {
return null;
}
DsfSourceSelectionResolver functionResolver = new DsfSourceSelectionResolver(editor, selection);
functionResolver.run();
// Resolve to a selected function
if (!functionResolver.isSuccessful()) {
// We are not pointing to a valid function
return null;
}
return new IHyperlink[] { new DsfStepIntoSelectionHyperlink(functionResolver, stepIntoSelectionCommand) };
}
private ITextSelection resolveSelection(ITextEditor editor, IRegion region) {
ITextSelection selection = null;
if (editor != null) {
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
final IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
if (document != null && workingCopy != null) {
// Check partition type.
String partitionType;
try {
partitionType = TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, region.getOffset(), false);
if (IDocument.DEFAULT_CONTENT_TYPE.equals(partitionType)) {
// Regular code i.e. Not a Preprocessor directive.
IRegion wregion = getIdentifier(document, region.getOffset(), workingCopy.getLanguage());
if (wregion != null) {
selection = new TextSelection(document, wregion.getOffset(), wregion.getLength());
}
}
} catch (BadLocationException e) {
// Ignore to return null
} catch (CoreException e) {
// Ignore to return null
}
}
}
return selection;
}
/**
* Returns the identifier at the given offset, or {@code null} if the there is no identifier at the offset.
*/
private static IRegion getIdentifier(IDocument document, int offset, ILanguage language) throws BadLocationException {
IRegion wordRegion = CWordFinder.findWord(document, offset);
if (wordRegion != null && wordRegion.getLength() > 0) {
String word = document.get(wordRegion.getOffset(), wordRegion.getLength());
if (!Character.isDigit(word.charAt(0)) && !isLanguageKeyword(language, word)) {
return wordRegion;
}
}
return null;
}
private static boolean isLanguageKeyword(ILanguage lang, String word) {
ICLanguageKeywords keywords = lang.getAdapter(ICLanguageKeywords.class);
if (keywords != null) {
for (String keyword : keywords.getKeywords()) {
if (keyword.equals(word))
return true;
}
for (String type : keywords.getBuiltinTypes()) {
if (type.equals(word))
return true;
}
for (String keyword : keywords.getPreprocessorKeywords()) {
if (keyword.charAt(0) == '#' && keyword.length() == word.length() + 1 && keyword.regionMatches(1, word, 0, word.length())) {
return true;
}
}
}
return false;
}
private IExecutionDMContext resolveDebugContext() {
IExecutionDMContext execContext = null;
IAdaptable adaptableContext = DebugUITools.getDebugContext();
IDMContext debugContext = null;
if (adaptableContext instanceof IDMVMContext) {
debugContext = ((IDMVMContext) adaptableContext).getDMContext();
}
if (debugContext != null) {
execContext = DMContexts.getAncestorOfType(debugContext, IExecutionDMContext.class);
}
return execContext;
}
}