1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-09-10 12:03:16 +02:00

Bug 200418 - Mouse over struct/class member always fails

This commit is contained in:
Anton Leherbauer 2009-11-13 10:02:30 +00:00
parent f3955789de
commit 450bef3a2c
2 changed files with 318 additions and 280 deletions

View file

@ -1,5 +1,5 @@
############################################################################### ###############################################################################
# Copyright (c) 2003, 2007 QNX Software Systems and others. # Copyright (c) 2003, 2009 QNX Software Systems and others.
# All rights reserved. This program and the accompanying materials # All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0 # are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at # which accompanies this distribution, and is available at
@ -9,6 +9,7 @@
# QNX Software Systems - initial API and implementation # QNX Software Systems - initial API and implementation
############################################################################### ###############################################################################
AbstractDebugTextHover.jobName=Computing hover expression
CDebugModelPresentation.unknown_1=unknown CDebugModelPresentation.unknown_1=unknown
CDebugImageDescriptorRegistry.0=Allocating image for wrong display. CDebugImageDescriptorRegistry.0=Allocating image for wrong display.
CDebugModelPresentation.not_available_1=<not available> CDebugModelPresentation.not_available_1=<not available>

View file

@ -1,279 +1,316 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2000, 2007 QNX Software Systems and others. * Copyright (c) 2000, 2009 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
* *
* Contributors: * Contributors:
* QNX Software Systems - Initial API and implementation * QNX Software Systems - Initial API and implementation
* Nokia - Refactored from DebugTextHover to remove CDI dependency * Nokia - Refactored from DebugTextHover to remove CDI dependency
* * Wind River Systems - Bug 200418
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.debug.ui.editors; package org.eclipse.cdt.debug.ui.editors;
import java.util.regex.Matcher; import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import java.util.regex.Pattern; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils; import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.debug.ui.CDebugUIPlugin; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.ui.text.c.hover.ICEditorTextHover; import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.core.runtime.IAdaptable; import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.debug.core.DebugException; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.jface.text.BadLocationException; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.jface.text.IDocument; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression;
import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.jface.text.IRegion; import org.eclipse.cdt.core.dom.ast.gnu.IGNUASTCompoundStatementExpression;
import org.eclipse.jface.text.ITextHoverExtension; import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.jface.text.ITextViewer; import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.jface.viewers.ISelection; import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.cdt.debug.internal.ui.CDebugUIMessages;
import org.eclipse.ui.IEditorPart; import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils;
import org.eclipse.ui.IPartListener; import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
import org.eclipse.ui.ISelectionListener; import org.eclipse.cdt.ui.CDTUITools;
import org.eclipse.ui.IWorkbenchPage; import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.ui.IWorkbenchPart; import org.eclipse.cdt.ui.text.SharedASTJob;
import org.eclipse.cdt.ui.text.c.hover.ICEditorTextHover;
/* The class LanguageOperators protects some language specific import org.eclipse.core.runtime.CoreException;
* operator information used by the DebugTextHover class. import org.eclipse.core.runtime.IAdaptable;
*/ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
class LanguageOperators { import org.eclipse.core.runtime.jobs.Job;
public String getAssignmentOperator() { import org.eclipse.debug.core.DebugException;
return "="; //$NON-NLS-1$ import org.eclipse.debug.ui.DebugUITools;
} import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
public String getGreaterThanEqualToOperator() { import org.eclipse.jface.text.IInformationControlCreator;
return ">="; //$NON-NLS-1$ import org.eclipse.jface.text.IRegion;
} import org.eclipse.jface.text.ITextHoverExtension;
import org.eclipse.jface.text.ITextViewer;
public String getEqualToOperator() { import org.eclipse.jface.text.ITypedRegion;
return "=="; //$NON-NLS-1$ import org.eclipse.jface.text.Position;
} import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ui.IEditorPart;
public String getNotEqualToOperator() {
return "!="; //$NON-NLS-1$ /**
} * The text hovering support for C/C++ debugger.
*
public String getLessThenEqualToOperator() { * @since 6.1
return "<="; //$NON-NLS-1$ */
} public abstract class AbstractDebugTextHover implements ICEditorTextHover, ITextHoverExtension {
public String getValueChangeOperatorsRegex() { /**
return "(\\+\\+)|(\\-\\-)|(\\+\\=)|" //$NON-NLS-1$ * ASTVisitor checking for side-effect expressions.
+ "(\\-\\=)|(\\*\\=)|(/\\=)|(\\&\\=)" //$NON-NLS-1$ */
+ "(\\%\\=)|(\\^\\=)|(\\|\\=)|(\\<\\<\\=)|(\\>\\>\\=)"; //$NON-NLS-1$ private static class ExpressionChecker extends ASTVisitor {
} private boolean fValid;
private ExpressionChecker() {
public String getEqualToOperatorsRegex() { shouldVisitExpressions = true;
return "\\=\\=|\\<\\=|\\>\\=|!\\="; //$NON-NLS-1$ }
} private boolean check(IASTNode node) {
fValid = true;
public String getIdentifierRegex() { node.accept(this);
return "[_A-Za-z][_A-Za-z0-9]*"; //$NON-NLS-1$ return fValid;
} };
} @Override
public int visit(IASTExpression expression) {
/** if (expression instanceof IASTFunctionCallExpression) {
* The text hovering support for C/C++ debugger. fValid = false;
* } else if (expression instanceof IASTUnaryExpression) {
* @since 6.1 IASTUnaryExpression unaryExpression = (IASTUnaryExpression) expression;
*/ switch (unaryExpression.getOperator()) {
case IASTUnaryExpression.op_postFixDecr:
public abstract class AbstractDebugTextHover implements ICEditorTextHover, ITextHoverExtension, case IASTUnaryExpression.op_postFixIncr:
ISelectionListener, IPartListener { case IASTUnaryExpression.op_prefixIncr:
case IASTUnaryExpression.op_prefixDecr:
static final private int MAX_HOVER_INFO_SIZE = 100; fValid = false;
break;
private ISelection fSelection = null; }
} else if (expression instanceof IASTBinaryExpression) {
private IEditorPart fEditor; IASTBinaryExpression binaryExpression = (IASTBinaryExpression) expression;
switch (binaryExpression.getOperator()) {
protected abstract boolean canEvaluate(); case IASTBinaryExpression.op_binaryAndAssign:
case IASTBinaryExpression.op_binaryOrAssign:
protected abstract String evaluateExpression(String expression); case IASTBinaryExpression.op_binaryXorAssign:
case IASTBinaryExpression.op_divideAssign:
public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { case IASTBinaryExpression.op_minusAssign:
if (canEvaluate()) { case IASTBinaryExpression.op_moduloAssign:
try { case IASTBinaryExpression.op_multiplyAssign:
IDocument document = textViewer.getDocument(); case IASTBinaryExpression.op_plusAssign:
if (document == null) case IASTBinaryExpression.op_shiftLeftAssign:
return null; case IASTBinaryExpression.op_shiftRightAssign:
String expression = document.get(hoverRegion.getOffset(), fValid = false;
hoverRegion.getLength()); break;
if (expression == null) }
return null; } else if (expression instanceof ICPPASTNewExpression) {
expression = expression.trim(); fValid = false;
if (expression.length() == 0) } else if (expression instanceof ICPPASTDeleteExpression) {
return null; fValid = false;
LanguageOperators operatorsObj = new LanguageOperators(); } else if (expression instanceof IGNUASTCompoundStatementExpression) {
fValid = false;
Pattern pattern = Pattern.compile(operatorsObj }
.getValueChangeOperatorsRegex()); if (!fValid)
Matcher matcher = pattern.matcher(expression); return PROCESS_ABORT;
return PROCESS_CONTINUE;
boolean match_found = matcher.find(); }
// Get matching string }
// If the expression has some operators which can change the
// value of a variable, that expresssion should not be static final private int MAX_HOVER_INFO_SIZE = 100;
// evaluated.
if (match_found) { private IEditorPart fEditor;
return null;
} /**
pattern = Pattern.compile(operatorsObj * @return <code>true</code> if this hover can evaluate an expression
.getEqualToOperatorsRegex()); */
String[] tokens = pattern.split(expression); protected abstract boolean canEvaluate();
for (int i = 0; i < tokens.length; i++) {
//If the expression contains assignment operator that /**
// can change the value of a variable, the expression * Compute a value for given expression.
// should not be evaluated. *
if (tokens[i].indexOf(operatorsObj * @param expression
.getAssignmentOperator()) != -1) * @return a result string or <code>null</code> if the expression could not be evaluated
return null; */
} protected abstract String evaluateExpression(String expression);
//Supressing function calls from evaluation.
String functionCallRegex = operatorsObj.getIdentifierRegex() + "\\s*\\("; //$NON-NLS-1$ public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
pattern = Pattern.compile(functionCallRegex); if (canEvaluate()) {
matcher = pattern.matcher(expression); String expression = getExpressionText(textViewer, hoverRegion);
match_found = matcher.find(); if (expression != null) {
if (match_found) { String result = evaluateExpression(expression);
return null; if (result == null)
} return null;
StringBuffer buffer = new StringBuffer(); try {
String result = evaluateExpression(expression); StringBuilder buffer = new StringBuilder();
if (result == null) appendVariable(buffer, makeHTMLSafe(expression), makeHTMLSafe(result.trim()));
return null; if (buffer.length() > 0) {
try { return buffer.toString();
appendVariable(buffer, makeHTMLSafe(expression), makeHTMLSafe(result.trim())); }
} catch (DebugException x) { } catch (DebugException x) {
CDebugUIPlugin.log(x); CDebugUIPlugin.log(x);
} }
if (buffer.length() > 0) { }
return buffer.toString(); }
} return null;
} catch (BadLocationException x) { }
CDebugUIPlugin.log(x); public IRegion getHoverRegion(ITextViewer viewer, int offset) {
} /*
} * Point selectedRange = viewer.getSelectedRange(); if ( selectedRange.x >= 0 && selectedRange.y > 0 && offset >= selectedRange.x && offset <=
return null; * selectedRange.x + selectedRange.y ) return new Region( selectedRange.x, selectedRange.y );
} */
if (viewer != null)
public IRegion getHoverRegion(ITextViewer viewer, int offset) { return CDebugUIUtils.findWord(viewer.getDocument(), offset);
/* return null;
* Point selectedRange = viewer.getSelectedRange(); if ( selectedRange.x >= 0 && selectedRange.y > 0 && offset >= selectedRange.x && offset <= }
* selectedRange.x + selectedRange.y ) return new Region( selectedRange.x, selectedRange.y );
*/ public final void setEditor(IEditorPart editor) {
if (viewer != null) if (editor != null) {
return CDebugUIUtils.findWord(viewer.getDocument(), offset); fEditor = editor;
return null; }
} }
/** public IInformationControlCreator getHoverControlCreator() {
* Append HTML for the given variable to the given buffer return null;
*/ }
private static void appendVariable(StringBuffer buffer, String expression, String value) throws DebugException {
if (value.length() > MAX_HOVER_INFO_SIZE)
value = value.substring(0, MAX_HOVER_INFO_SIZE) + " ..."; //$NON-NLS-1$ /**
buffer.append("<p>"); //$NON-NLS-1$ * Compute the expression text to be evaluated by the debugger.
buffer.append("<pre>").append(expression).append("</pre>"); //$NON-NLS-1$ //$NON-NLS-2$ * <p>
buffer.append(" = "); //$NON-NLS-1$ * The default implementation uses an AST to compute a valid, side-effect free
buffer.append("<b><pre>").append(value).append("</pre></b>"); //$NON-NLS-1$ //$NON-NLS-2$ * expression.
buffer.append("</p>"); //$NON-NLS-1$ * </p>
} *
* @param textViewer the underlying text viewer
public void setEditor(IEditorPart editor) { * @param hoverRegion the hover region as returned by {@link #getHoverRegion(ITextViewer, int)}
if (editor != null) { * @return an expression string or <code>null</code> if no valid expression could be computed
fEditor = editor; */
final IWorkbenchPage page = editor.getSite().getPage(); protected String getExpressionText(ITextViewer textViewer, final IRegion hoverRegion) {
page.addSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, this); IDocument document = textViewer.getDocument();
page.addPartListener(this); if (document == null)
// initialize selection return null;
Runnable r = new Runnable() { ICElement cElement = CDTUITools.getEditorInputCElement(getEditor().getEditorInput());
if (cElement instanceof ITranslationUnit) {
public void run() { final Position expressionPosition = new Position(0);
fSelection = page SharedASTJob job = new SharedASTJob(CDebugUIMessages.getString("AbstractDebugTextHover.jobName"), (ITranslationUnit) cElement) { //$NON-NLS-1$
.getSelection(IDebugUIConstants.ID_DEBUG_VIEW); @Override
} public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
}; if (ast != null) {
CDebugUIPlugin.getStandardDisplay().asyncExec(r); int offset = hoverRegion.getOffset();
} int length = hoverRegion.getLength();
} IASTName name= ast.getNodeSelector(null).findEnclosingName(offset, length);
if (name != null) {
public void selectionChanged(IWorkbenchPart part, ISelection selection) { computeExpressionExtent(name, expressionPosition);
fSelection = selection; }
} }
return Status.OK_STATUS;
public void partActivated(IWorkbenchPart part) { }
} private void computeExpressionExtent(IASTName name, Position pos) {
IASTNode node = name;
public void partBroughtToTop(IWorkbenchPart part) { while (node != null && !(node instanceof IASTExpression) && !(node instanceof IASTDeclaration)) {
} node = node.getParent();
}
public void partClosed(IWorkbenchPart part) { if (node instanceof IASTExpression) {
if (part.equals(fEditor)) { ExpressionChecker checker = new ExpressionChecker();
IWorkbenchPage page = fEditor.getSite().getPage(); if (checker.check(node)) {
page.removeSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, this); IASTNodeLocation loc = node.getFileLocation();
page.removePartListener(this); pos.offset = loc.getNodeOffset();
fSelection = null; pos.length = loc.getNodeLength();
fEditor = null; }
} } else {
} // fallback: use simple name
IASTNodeLocation loc = name.getFileLocation();
public void partDeactivated(IWorkbenchPart part) { pos.offset = loc.getNodeOffset();
} pos.length = loc.getNodeLength();
}
public void partOpened(IWorkbenchPart part) { }
} };
job.setPriority(Job.DECORATE);
public IInformationControlCreator getHoverControlCreator() { job.setSystem(true);
return null; job.schedule();
} try {
job.join();
protected ISelection getSelection() { } catch (InterruptedException exc) {
return fSelection; job.cancel();
} Thread.currentThread().interrupt();
}
protected IAdaptable getSelectionAdaptable() { if (expressionPosition.getLength() > 0) {
if (fSelection instanceof IStructuredSelection) { try {
IStructuredSelection selection = (IStructuredSelection) fSelection; // Get expression text removing comments, obsolete whitespace, etc.
if (selection.size() == 1) { StringBuilder result = new StringBuilder();
Object element = selection.getFirstElement(); ITypedRegion[] partitions = TextUtilities.computePartitioning(document, ICPartitions.C_PARTITIONING,
if (element instanceof IAdaptable) { expressionPosition.offset, expressionPosition.length, false);
return (IAdaptable) element; for (ITypedRegion partition : partitions) {
} if (IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())
} || ICPartitions.C_CHARACTER.equals(partition.getType())
} || ICPartitions.C_STRING.equals(partition.getType())) {
return null; result.append(document.get(partition.getOffset(), partition.getLength()));
} } else {
result.append(' ');
}
protected IEditorPart getEditor() { }
return fEditor; String text = result.toString().replaceAll("(\\r\\n|\\n|\t| )+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$
} return text;
} catch (BadLocationException exc) {
/** }
* Replace any characters in the given String that would confuse an HTML parser with their escape sequences. }
*/ }
private static String makeHTMLSafe(String string) { return null;
StringBuffer buffer = new StringBuffer(string.length()); }
for (int i = 0; i != string.length(); i++) {
char ch = string.charAt(i); /**
switch (ch) { * Returns the debug context the expression should be evaluated against.
case '&': * The default implementation returns {@link DebugUITools#getDebugContext()}.
buffer.append("&amp;"); //$NON-NLS-1$ */
break; protected IAdaptable getSelectionAdaptable() {
case '<': return DebugUITools.getDebugContext();
buffer.append("&lt;"); //$NON-NLS-1$ }
break;
case '>': /**
buffer.append("&gt;"); //$NON-NLS-1$ * Returns the editor this hover is associated with.
break; */
default: protected final IEditorPart getEditor() {
buffer.append(ch); return fEditor;
break; }
}
} /**
return buffer.toString(); * Append HTML for the given variable to the given buffer
} */
} private static void appendVariable(StringBuilder buffer, String expression, String value) throws DebugException {
if (value.length() > MAX_HOVER_INFO_SIZE)
value = value.substring(0, MAX_HOVER_INFO_SIZE) + " ..."; //$NON-NLS-1$
buffer.append("<p>"); //$NON-NLS-1$
buffer.append("<pre>").append(expression).append("</pre>"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append(" = "); //$NON-NLS-1$
buffer.append("<b><pre>").append(value).append("</pre></b>"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append("</p>"); //$NON-NLS-1$
}
/**
* Replace any characters in the given String that would confuse an HTML parser with their escape sequences.
*/
private static String makeHTMLSafe(String string) {
StringBuilder buffer = new StringBuilder(string.length());
for (int i = 0; i != string.length(); i++) {
char ch = string.charAt(i);
switch (ch) {
case '&':
buffer.append("&amp;"); //$NON-NLS-1$
break;
case '<':
buffer.append("&lt;"); //$NON-NLS-1$
break;
case '>':
buffer.append("&gt;"); //$NON-NLS-1$
break;
case '"':
buffer.append("&quot;"); //$NON-NLS-1$
break;
default:
buffer.append(ch);
break;
}
}
return buffer.toString();
}
}