summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Eidsness2013-02-27 08:16:19 (EST)
committer Doug Schaefer2013-02-28 18:19:59 (EST)
commit719982b23bd265699213650a6ec5e2d995db7f08 (patch)
tree1b713b7e0a60bd35be2eb9a0499ddf546eb0580c
parent5cad4cd8beda87c04cdbd3619813d0afe453f65c (diff)
downloadorg.eclipse.cdt-719982b23bd265699213650a6ec5e2d995db7f08.zip
org.eclipse.cdt-719982b23bd265699213650a6ec5e2d995db7f08.tar.gz
org.eclipse.cdt-719982b23bd265699213650a6ec5e2d995db7f08.tar.bz2
Recognize Q_SIGNAL and Q_SLOT on single functionsrefs/changes/01/10701/2
Qt allows signals and slots to be marked directly on the function, e.g., class T { Q_SIGNAL void some_signal(); Q_SLOT void some_slot(); }; This change modifies the Qt signal/slot tagger to look for these tags in addition to the previously implemented search for the visibility label. Change-Id: Ibf43df8d80d4ca9f8b62776e7a35a4fc067a289e Reviewed-on: https://git.eclipse.org/r/10701 Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com> Tested-by: Doug Schaefer <dschaefer@qnx.com>
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java21
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java275
-rw-r--r--qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/internal/ui/QtCompletionProposalComputer.java674
3 files changed, 547 insertions, 423 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
index 381655e..2e514eb 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
@@ -11,16 +11,13 @@ package org.eclipse.cdt.qt.core;
/**
* Declares constants related to tokens that are special in Qt applications.
*/
-public class QtKeywords
-{
- public static final String Q_SIGNALS = "Q_SIGNALS";
- public static final String Q_SLOTS = "Q_SLOTS";
- public static final String SIGNALS = "signals";
- public static final String SLOTS = "slots";
-
- public static final String SIGNAL = "SIGNAL";
- public static final String SLOT = "SLOT";
-
- public static final String QOBJECT = "QObject";
- public static final String CONNECT = "connect";
+public class QtKeywords {
+ public static final String CONNECT = "connect";
+ public static final String Q_SIGNAL = "Q_SIGNAL";
+ public static final String Q_SIGNALS = "Q_SIGNALS";
+ public static final String Q_SLOT = "Q_SLOT";
+ public static final String Q_SLOTS = "Q_SLOTS";
+ public static final String QOBJECT = "QObject";
+ public static final String SIGNALS = "signals";
+ public static final String SLOTS = "slots";
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java
index a82b3c1..06c2f6f 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java
@@ -9,11 +9,14 @@
package org.eclipse.cdt.qt.internal.core;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
+import org.eclipse.cdt.core.dom.ast.IASTProblemHolder;
+import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
@@ -25,77 +28,203 @@ import org.eclipse.cdt.core.dom.ast.tag.IWritableTag;
import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.QtPlugin;
-public class QtSignalSlotTagger implements IBindingTagger
-{
- private static ICPPASTVisibilityLabel findVisibilityLabel( ICPPMethod method, IASTNode ast )
- {
- // the visibility cannot be found without an ast
- if( ast == null )
- return null;
-
- IASTNode methodDecl = ast;
- ICPPASTCompositeTypeSpecifier classType = null;
- while( methodDecl != null
- && classType == null )
- {
- IASTNode parent = methodDecl.getParent();
- if( parent instanceof ICPPASTCompositeTypeSpecifier )
- classType = (ICPPASTCompositeTypeSpecifier)parent;
- else
- methodDecl = parent;
- }
-
- if( methodDecl == null
- || classType == null )
- return null;
-
- ICPPASTVisibilityLabel lastLabel = null;
- for( IASTDeclaration decl : classType.getMembers() )
- {
- if( decl instanceof ICPPASTVisibilityLabel )
- lastLabel = (ICPPASTVisibilityLabel)decl;
- else if( decl == methodDecl )
- return lastLabel;
- }
-
- return null;
- }
-
- @Override
- public ITag process( ITagWriter tagWriter, IBinding binding, IASTName ast )
- {
- // only methods a be signals or slots
- if( ! ( binding instanceof ICPPMethod ) )
- return null;
-
- // a visibility label is required in order to decide whether the method is a signal/slot
- ICPPMethod method = (ICPPMethod)binding;
- ICPPASTVisibilityLabel v = findVisibilityLabel( method, ast );
- if( v == null )
- return null;
-
- byte bitset = 0;
- for( IASTNodeLocation loc : v.getNodeLocations() )
- if( loc instanceof IASTMacroExpansionLocation )
- {
- IASTMacroExpansionLocation macroExpansion = (IASTMacroExpansionLocation)loc;
- IASTPreprocessorMacroExpansion exp = macroExpansion.getExpansion();
- String macro = exp.getMacroReference().toString();
-
- if( QtKeywords.SIGNALS.equals( macro ) || QtKeywords.Q_SIGNALS.equals( macro ) )
- bitset |= QtPlugin.SignalSlot_Mask_signal;
- else if( QtKeywords.SLOTS.equals( macro ) || QtKeywords.Q_SLOTS.equals( macro ) )
- bitset |= QtPlugin.SignalSlot_Mask_slot;
- }
-
- if( bitset != 0 )
- {
- IWritableTag tag = tagWriter.createTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID, 1 );
- if( tag != null
- && tag.putByte( 0, bitset ) )
- return tag;
- }
-
- return null;
- }
+/**
+ * Finds all functions that are marked as Qt signals or slots and tags them in
+ * the index. There are two ways that Qt understands for marking a function as a
+ * signal or slot: 1) With a macro in the function's visibility label 2) With a
+ * macro before the function itself E.g., both of these cases are valid:
+ *
+ * <pre>
+ * class T
+ * {
+ * private:
+ * Q_SLOT void some_slot();
+ *
+ * signals:
+ * void some_signal();
+ * };
+ * </pre>
+ *
+ * The 6 applicable macros are signals, Q_SIGNALS, Q_SIGNAL, slots, Q_SLOTS, and
+ * Q_SLOT.
+ */
+public class QtSignalSlotTagger implements IBindingTagger {
+ private static ICPPASTVisibilityLabel findVisibilityLabel(
+ ICPPMethod method, IASTNode ast) {
+ // the visibility cannot be found without an ast
+ if (ast == null)
+ return null;
+
+ IASTNode methodDecl = ast;
+ ICPPASTCompositeTypeSpecifier classType = null;
+ while (methodDecl != null && classType == null) {
+ IASTNode parent = methodDecl.getParent();
+ if (parent instanceof ICPPASTCompositeTypeSpecifier)
+ classType = (ICPPASTCompositeTypeSpecifier) parent;
+ else
+ methodDecl = parent;
+ }
+
+ if (methodDecl == null || classType == null)
+ return null;
+
+ ICPPASTVisibilityLabel lastLabel = null;
+ for (IASTDeclaration decl : classType.getMembers()) {
+ if (decl instanceof ICPPASTVisibilityLabel)
+ lastLabel = (ICPPASTVisibilityLabel) decl;
+ else if (decl == methodDecl)
+ return lastLabel;
+ }
+
+ return null;
+ }
+
+ private static byte getBitset(IASTNodeLocation... locations) {
+ for (IASTNodeLocation location : locations)
+ if (location instanceof IASTMacroExpansionLocation) {
+ IASTMacroExpansionLocation macroExpansion = (IASTMacroExpansionLocation) location;
+ IASTPreprocessorMacroExpansion exp = macroExpansion
+ .getExpansion();
+ String macro = exp.getMacroReference().toString();
+
+ if (QtKeywords.Q_SIGNAL.equals(macro)
+ || QtKeywords.Q_SIGNALS.equals(macro)
+ || QtKeywords.SIGNALS.equals(macro))
+ return QtPlugin.SignalSlot_Mask_signal;
+ if (QtKeywords.Q_SLOT.equals(macro)
+ || QtKeywords.Q_SLOTS.equals(macro)
+ || QtKeywords.SLOTS.equals(macro))
+ return QtPlugin.SignalSlot_Mask_slot;
+ }
+
+ return 0;
+ }
+
+ private static byte getBitset(IASTNode... nodes) {
+ byte bitset = 0;
+ for (IASTNode node : nodes)
+ if (node != null)
+ for (IASTNodeLocation loc : node.getNodeLocations())
+ bitset |= getBitset(loc);
+
+ return bitset;
+ }
+
+ private static IASTNode getSimpleDecl(IASTNode node) {
+ while (node != null && !(node instanceof IASTSimpleDeclaration))
+ node = node.getParent();
+ return node;
+ }
+
+ private byte getQtMarkers(ICPPMethod method, IASTName ast) {
+ byte bitset = 0;
+ if (ast == null)
+ return bitset;
+
+ // Look for macros on the previous visibility label.
+ bitset |= getBitset(findVisibilityLabel(method, ast));
+
+ // Look for macros on this function. See Bug 401696 for a better
+ // description of why it needs
+ // to work this why. Briefly, the parser does not associate empty macros
+ // with the function when
+ // they are the first thing in the declaration. E.g.,
+ // #define X
+ // void func1() {}
+ // X void func2() {}
+ // Could also look like:
+ // void func1() {} X
+ // void func2() {}
+ //
+ // The following code instead looks at the parents and children to find
+ // all node locations between
+ // the declarators.
+ //
+ // We first look at parents to find the closest SimpleDeclaration. We
+ // then look at that node's parent
+ // to find the node that is right before the target. Then we look at all
+ // node locations between the
+ // end of that previous node and the end of the target node.
+
+ // find the closest containing SimpleDecl
+ IASTNode simpleDecl = getSimpleDecl(ast);
+ IASTNode parent = simpleDecl == null ? null : simpleDecl.getParent();
+ if (parent == null)
+ return bitset;
+
+ // find the declaration before the target
+ IASTNode previous = null;
+ IASTNode[] children = parent.getChildren();
+ if (children.length > 1)
+ for (int i = 1; i < children.length; ++i) {
+ if (children[i] == simpleDecl) {
+ // if we haven't found a SimpleDecl, then find the nearest
+ // previous non-problem node
+ for (int j = i - 1; previous == null && j >= 0; --j)
+ if (!(children[j] instanceof IASTProblemHolder))
+ previous = children[j];
+ break;
+ }
+ if (children[i] instanceof IASTSimpleDeclaration)
+ previous = children[i];
+ }
+
+ // Signals/slots can only be declared inside of classes, so all cases we
+ // care about have a
+ // previous child, even if it is only the Base-class specifier.
+ if (previous == null)
+ return bitset;
+
+ IASTFileLocation prevLocation = previous.getFileLocation();
+ int prev_off = prevLocation.getNodeOffset();
+ int prev_end = prevLocation.getNodeOffset()
+ + prevLocation.getNodeLength();
+
+ // Figure out where the target node ends.
+ int end = ast.getFileLocation().getNodeOffset()
+ + ast.getFileLocation().getNodeLength();
+
+ // Examine all locations that appear after the previous node and before
+ // the target node.
+ boolean found_previous = false;
+ for (IASTNodeLocation loc : parent.getNodeLocations()) {
+ int o = loc.getNodeOffset();
+ int e = loc.getNodeOffset() + loc.getNodeLength();
+
+ // if the previous node has already been found, process this one
+ if (found_previous)
+ bitset |= getBitset(loc);
+
+ // otherwise see if this is the previous node
+ else if (o <= prev_off && e >= prev_end)
+ found_previous = true;
+
+ // stop processing when we're processed to the end of the target
+ if (e >= end)
+ break;
+ }
+
+ return bitset;
+ }
+
+ @Override
+ public ITag process(ITagWriter tagWriter, IBinding binding, IASTName ast) {
+ // only methods a be signals or slots
+ if (!(binding instanceof ICPPMethod))
+ return null;
+
+ // Find all qt marker macros associated with this node.
+ ICPPMethod method = (ICPPMethod) binding;
+ byte bitset = getQtMarkers(method, ast);
+
+ // create and store the bitset if needed
+ if (bitset != 0) {
+ IWritableTag tag = tagWriter.createTag(
+ QtPlugin.SIGNAL_SLOT_TAGGER_ID, 1);
+ if (tag != null && tag.putByte(0, bitset))
+ return tag;
+ }
+
+ return null;
+ }
}
diff --git a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/internal/ui/QtCompletionProposalComputer.java b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/internal/ui/QtCompletionProposalComputer.java
index 63d22a4..2e6e6d5 100644
--- a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/internal/ui/QtCompletionProposalComputer.java
+++ b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/internal/ui/QtCompletionProposalComputer.java
@@ -46,347 +46,345 @@ import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.QtNature;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.contentassist.ICEditorContentAssistInvocationContext;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
-@SuppressWarnings( "restriction" )
-public class QtCompletionProposalComputer extends ParsingBasedProposalComputer
-{
- private boolean isApplicable( CContentAssistInvocationContext context )
- {
- ITranslationUnit tu = context.getTranslationUnit();
- if( tu == null )
- return false;
-
- ICProject cProject = tu.getCProject();
- if( cProject == null )
- return false;
-
- IProject project = cProject.getProject();
- if( project == null )
- return false;
-
- try
- {
- return project.hasNature( QtNature.ID );
- }
- catch( CoreException e )
- {
- CUIPlugin.log( e );
- return false;
- }
- }
-
- private static boolean is_QObject_connect( CContentAssistInvocationContext context, IASTCompletionContext astContext, IASTName name )
- {
- IASTName connectName = name.getLastName();
- if( ! QtKeywords.CONNECT.equals( new String( connectName.getSimpleID() ) ) )
- return false;
-
- IBinding[] funcBindings = astContext.findBindings( connectName, ! context.isContextInformationStyle() );
- for( IBinding funcBinding : funcBindings )
- if( funcBinding instanceof ICPPFunction )
- {
- IBinding ownerBinding = ( (ICPPFunction)funcBinding ).getOwner();
- if( ownerBinding != null && QtKeywords.QOBJECT.equals( ownerBinding.getName() ) )
- return true;
- }
-
- return false;
- }
-
- private static class Completion
- {
- private final String replacement;
- private final String display;
- private final int cursorOffset;
-
- public static final Completion SIGNAL = new Completion( "SIGNAL()", "SIGNAL(a)", -1 );
- public static final Completion SLOT = new Completion( "SLOT()", "SLOT(a)", -1 );
-
- public Completion( String replacement )
- {
- this( replacement, replacement, 0 );
- }
-
- public Completion( String replacement, String display, int cursorOffset )
- {
- this.replacement = replacement;
- this.display = display;
- this.cursorOffset = cursorOffset;
- }
-
- public ICompletionProposal createProposal( CContentAssistInvocationContext context )
- {
- int repLength = replacement.length();
- int repOffset = context.getInvocationOffset();
- CCompletionProposal p = new CCompletionProposal( replacement, repOffset, repLength, null, display, RelevanceConstants.DEFAULT_TYPE_RELEVANCE, context.getViewer() );
- p.setCursorPosition( repLength + cursorOffset );
- return p;
- }
-
- @Override
- public String toString()
- {
- if( replacement == null )
- return super.toString();
- return replacement + '@' + cursorOffset;
- }
- }
-
- private static interface MethodFilter
- {
- public boolean keep( ICPPMethod method );
-
- public static class Qt
- {
- public static final MethodFilter Signal = new MethodFilter()
- {
- @Override
- public boolean keep( ICPPMethod method )
- {
- ITagReader tagReader = CCorePlugin.getTagService().findTagReader( method );
- if( tagReader == null )
- return false;
-
- ITag tag = tagReader.getTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID );
- if( tag == null )
- return false;
-
- int result = tag.getByte( 0 );
- return result != ITag.FAIL
- && ( ( result & QtPlugin.SignalSlot_Mask_signal ) == QtPlugin.SignalSlot_Mask_signal );
- }
- };
-
- public static final MethodFilter Slot = new MethodFilter()
- {
- @Override
- public boolean keep( ICPPMethod method )
- {
- ITagReader tagReader = CCorePlugin.getTagService().findTagReader( method );
- if( tagReader == null )
- return false;
-
- ITag tag = tagReader.getTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID );
- if( tag == null )
- return false;
-
- int result = tag.getByte( 0 );
- return result != ITag.FAIL
- && ( ( result & QtPlugin.SignalSlot_Mask_slot ) == QtPlugin.SignalSlot_Mask_slot );
- }
- };
- }
- }
-
- private static Iterable<ICPPMethod> filterMethods( final ICPPClassType cls, final MethodFilter filter )
- {
- return new Iterable<ICPPMethod>()
- {
- @Override
- public Iterator<ICPPMethod> iterator()
- {
- return new Iterator<ICPPMethod>()
- {
- private int index = 0;
- private final ICPPMethod[] methods = cls.getMethods();
-
- @Override
- public boolean hasNext()
- {
- for( ; index < methods.length; ++index )
- if( filter.keep( methods[index] ) )
- return true;
- return false;
- }
-
- @Override public ICPPMethod next() { return methods[index++]; }
- @Override public void remove() { }
- };
- }
- };
- }
-
- private static String getSignature( ICPPMethod method )
- {
- StringBuilder signature = new StringBuilder();
-
- signature.append( method.getName() );
- signature.append( '(' );
- boolean first = true;
- for( ICPPParameter param : method.getParameters() )
- {
- if( first )
- first = false;
- else
- signature.append( ", " );
- signature.append( ASTTypeUtil.getType( param.getType() ) );
- }
-
- signature.append( ')' );
- return signature.toString();
- }
-
- private static void addCompletionsFor( Collection<Completion> completions, IASTInitializerClause init, MethodFilter filter )
- {
- if( !( init instanceof ICPPASTInitializerClause ) )
- return;
-
- ICPPEvaluation eval = ( (ICPPASTInitializerClause)init ).getEvaluation();
- if( eval == null )
- return;
-
- IType type = eval.getTypeOrFunctionSet( init );
- while( type instanceof IPointerType )
- type = ( (IPointerType)type ).getType();
-
- if( type instanceof ICPPClassType )
- for( ICPPMethod signal : filterMethods( (ICPPClassType)type, filter ) )
- completions.add( new Completion( getSignature( signal ) ) );
- }
-
- // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
- private static int indexOfClosingPeer(String code, char left, char right, int pos) {
- int level= 0;
- final int length= code.length();
- while (pos < length) {
- char ch= code.charAt(pos);
- if (ch == left) {
- ++level;
- } else if (ch == right) {
- if (--level == 0) {
- return pos;
- }
- }
- ++pos;
- }
- return -1;
- }
-
- // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
- private static int[] computeCommaPositions(String code) {
- final int length= code.length();
- int pos= 0;
- List<Integer> positions= new ArrayList<Integer>();
- positions.add(new Integer(-1));
- while (pos < length && pos != -1) {
- char ch= code.charAt(pos);
- switch (ch) {
- case ',':
- positions.add(new Integer(pos));
- break;
- case '(':
- pos= indexOfClosingPeer(code, '(', ')', pos);
- break;
- case '<':
- pos= indexOfClosingPeer(code, '<', '>', pos);
- break;
- case '[':
- pos= indexOfClosingPeer(code, '[', ']', pos);
- break;
- default:
- break;
- }
- if (pos != -1)
- pos++;
- }
- positions.add(new Integer(length));
-
- int[] fields= new int[positions.size()];
- for (int i= 0; i < fields.length; i++)
- fields[i]= positions.get(i).intValue();
- return fields;
- }
-
-
- private void addConnectParameterCompletions( List<ICompletionProposal> proposals, CContentAssistInvocationContext context, IASTCompletionNode completionNode, String prefix )
- {
- IASTName[] names = completionNode.getNames();
- List<Completion> completions = new LinkedList<Completion>();
-
- for( IASTName name : names )
- {
- // The node isn't properly hooked up, must have backtracked out of this node
- if( name.getTranslationUnit() == null )
- continue;
-
- IASTCompletionContext astContext = name.getCompletionContext();
- if( astContext == null || ! ( astContext instanceof IASTNode ) )
- continue;
- IASTNode astNode = (IASTNode)astContext;
-
- if( is_QObject_connect( context, astContext, name ) )
- {
- int parseOffset = context.getParseOffset();
- int invocationOffset = context.getInvocationOffset();
-
- String unparsed = "";
- try { unparsed = context.getDocument().get( parseOffset, invocationOffset - parseOffset ); }
- catch( BadLocationException e ) { CCorePlugin.log( e ); }
-
- if( unparsed.length() > 0 && unparsed.charAt( 0 ) == '(' )
- unparsed = unparsed.substring( 1 );
-
- int[] commas = computeCommaPositions( unparsed );
- switch( commas.length )
- {
- case 3:
- completions.add( Completion.SIGNAL );
- break;
- case 5:
- completions.add( Completion.SLOT );
- break;
- }
- }
- else if( astNode.getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT )
- {
- IASTNode parent = astNode.getParent();
- if( ! ( parent instanceof IASTFunctionCallExpression ) )
- continue;
- IASTFunctionCallExpression call = (IASTFunctionCallExpression)parent;
- IASTExpression nameExpr = call.getFunctionNameExpression();
- if( !( nameExpr instanceof IASTIdExpression ) )
- continue;
- IASTIdExpression funcNameIdExpr = (IASTIdExpression)nameExpr;
- IASTName funcName = funcNameIdExpr.getName();
-
- if( !is_QObject_connect( context, astContext, funcName ) )
- continue;
-
- IASTInitializerClause[] args = call.getArguments();
- switch( args.length )
- {
- case 2:
- //if( QtKeywords.SIGNAL.equals( prefix ) )
- addCompletionsFor( completions, args[0], MethodFilter.Qt.Signal );
- break;
- case 4:
- if( QtKeywords.SLOT.equals( prefix ) )
- addCompletionsFor( completions, args[2], MethodFilter.Qt.Slot );
- break;
- }
- }
- }
-
- for( Completion completion : completions )
- {
- ICompletionProposal proposal = completion.createProposal( context );
- if( proposal != null )
- proposals.add( proposal );
- }
- }
-
- @Override
- protected List<ICompletionProposal> computeCompletionProposals( CContentAssistInvocationContext context, IASTCompletionNode completionNode, String prefix ) throws CoreException
- {
- if( !isApplicable( context ) )
- return Collections.emptyList();
-
- List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
- addConnectParameterCompletions( proposals, context, completionNode, prefix );
- return proposals;
- }
+@SuppressWarnings("restriction")
+public class QtCompletionProposalComputer extends ParsingBasedProposalComputer {
+ private boolean isApplicable(ICEditorContentAssistInvocationContext context) {
+ ITranslationUnit tu = context.getTranslationUnit();
+ if (tu == null)
+ return false;
+
+ ICProject cProject = tu.getCProject();
+ if (cProject == null)
+ return false;
+
+ IProject project = cProject.getProject();
+ if (project == null)
+ return false;
+
+ try {
+ return project.hasNature(QtNature.ID);
+ } catch (CoreException e) {
+ CUIPlugin.log(e);
+ return false;
+ }
+ }
+
+ private static boolean is_QObject_connect(
+ ICEditorContentAssistInvocationContext context,
+ IASTCompletionContext astContext, IASTName name) {
+ IASTName connectName = name.getLastName();
+ if (!QtKeywords.CONNECT.equals(new String(connectName.getSimpleID())))
+ return false;
+
+ IBinding[] funcBindings = astContext.findBindings(connectName,
+ !context.isContextInformationStyle());
+ for (IBinding funcBinding : funcBindings)
+ if (funcBinding instanceof ICPPFunction) {
+ IBinding ownerBinding = ((ICPPFunction) funcBinding).getOwner();
+ if (ownerBinding != null
+ && QtKeywords.QOBJECT.equals(ownerBinding.getName()))
+ return true;
+ }
+
+ return false;
+ }
+
+ private static class Completion {
+ private final String replacement;
+ private final String display;
+ private final int cursorOffset;
+
+ public static final Completion SIGNAL = new Completion("SIGNAL()",
+ "SIGNAL(a)", -1);
+ public static final Completion SLOT = new Completion("SLOT()",
+ "SLOT(a)", -1);
+
+ public Completion(String replacement) {
+ this(replacement, replacement, 0);
+ }
+
+ public Completion(String replacement, String display, int cursorOffset) {
+ this.replacement = replacement;
+ this.display = display;
+ this.cursorOffset = cursorOffset;
+ }
+
+ public ICompletionProposal createProposal(
+ ICEditorContentAssistInvocationContext context) {
+ int repLength = replacement.length();
+ int repOffset = context.getInvocationOffset();
+ CCompletionProposal p = new CCompletionProposal(replacement,
+ repOffset, repLength, null, display,
+ RelevanceConstants.DEFAULT_TYPE_RELEVANCE,
+ context.getViewer());
+ p.setCursorPosition(repLength + cursorOffset);
+ return p;
+ }
+
+ @Override
+ public String toString() {
+ if (replacement == null)
+ return super.toString();
+ return replacement + '@' + cursorOffset;
+ }
+ }
+
+ private static interface MethodFilter {
+ public boolean keep(ICPPMethod method);
+
+ public static class Qt {
+ public static final MethodFilter Signal = new MethodFilter() {
+ @Override
+ public boolean keep(ICPPMethod method) {
+ ITagReader tagReader = CCorePlugin.getTagService()
+ .findTagReader(method);
+ if (tagReader == null)
+ return false;
+
+ ITag tag = tagReader.getTag(QtPlugin.SIGNAL_SLOT_TAGGER_ID);
+ if (tag == null)
+ return false;
+
+ int result = tag.getByte(0);
+ return result != ITag.FAIL
+ && ((result & QtPlugin.SignalSlot_Mask_signal) == QtPlugin.SignalSlot_Mask_signal);
+ }
+ };
+
+ public static final MethodFilter Slot = new MethodFilter() {
+ @Override
+ public boolean keep(ICPPMethod method) {
+ ITagReader tagReader = CCorePlugin.getTagService()
+ .findTagReader(method);
+ if (tagReader == null)
+ return false;
+
+ ITag tag = tagReader.getTag(QtPlugin.SIGNAL_SLOT_TAGGER_ID);
+ if (tag == null)
+ return false;
+
+ int result = tag.getByte(0);
+ return result != ITag.FAIL
+ && ((result & QtPlugin.SignalSlot_Mask_slot) == QtPlugin.SignalSlot_Mask_slot);
+ }
+ };
+ }
+ }
+
+ private static Iterable<ICPPMethod> filterMethods(final ICPPClassType cls,
+ final MethodFilter filter) {
+ return new Iterable<ICPPMethod>() {
+ @Override
+ public Iterator<ICPPMethod> iterator() {
+ return new Iterator<ICPPMethod>() {
+ private int index = 0;
+ private final ICPPMethod[] methods = cls.getMethods();
+
+ @Override
+ public boolean hasNext() {
+ for (; index < methods.length; ++index)
+ if (filter.keep(methods[index]))
+ return true;
+ return false;
+ }
+
+ @Override
+ public ICPPMethod next() {
+ return methods[index++];
+ }
+
+ @Override
+ public void remove() {
+ }
+ };
+ }
+ };
+ }
+
+ private static String getSignature(ICPPMethod method) {
+ StringBuilder signature = new StringBuilder();
+
+ signature.append(method.getName());
+ signature.append('(');
+ boolean first = true;
+ for (ICPPParameter param : method.getParameters()) {
+ if (first)
+ first = false;
+ else
+ signature.append(", ");
+ signature.append(ASTTypeUtil.getType(param.getType()));
+ }
+
+ signature.append(')');
+ return signature.toString();
+ }
+
+ private static void addCompletionsFor(Collection<Completion> completions,
+ IASTInitializerClause init, MethodFilter filter) {
+ if (!(init instanceof ICPPASTInitializerClause))
+ return;
+
+ ICPPEvaluation eval = ((ICPPASTInitializerClause) init).getEvaluation();
+ if (eval == null)
+ return;
+
+ IType type = eval.getTypeOrFunctionSet(init);
+ while (type instanceof IPointerType)
+ type = ((IPointerType) type).getType();
+
+ if (type instanceof ICPPClassType)
+ for (ICPPMethod signal : filterMethods((ICPPClassType) type, filter))
+ completions.add(new Completion(getSignature(signal)));
+ }
+
+ // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
+ private static int indexOfClosingPeer(String code, char left, char right,
+ int pos) {
+ int level = 0;
+ final int length = code.length();
+ while (pos < length) {
+ char ch = code.charAt(pos);
+ if (ch == left) {
+ ++level;
+ } else if (ch == right) {
+ if (--level == 0) {
+ return pos;
+ }
+ }
+ ++pos;
+ }
+ return -1;
+ }
+
+ // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
+ private static int[] computeCommaPositions(String code) {
+ final int length = code.length();
+ int pos = 0;
+ List<Integer> positions = new ArrayList<Integer>();
+ positions.add(new Integer(-1));
+ while (pos < length && pos != -1) {
+ char ch = code.charAt(pos);
+ switch (ch) {
+ case ',':
+ positions.add(new Integer(pos));
+ break;
+ case '(':
+ pos = indexOfClosingPeer(code, '(', ')', pos);
+ break;
+ case '<':
+ pos = indexOfClosingPeer(code, '<', '>', pos);
+ break;
+ case '[':
+ pos = indexOfClosingPeer(code, '[', ']', pos);
+ break;
+ default:
+ break;
+ }
+ if (pos != -1)
+ pos++;
+ }
+ positions.add(new Integer(length));
+
+ int[] fields = new int[positions.size()];
+ for (int i = 0; i < fields.length; i++)
+ fields[i] = positions.get(i).intValue();
+ return fields;
+ }
+
+ private void addConnectParameterCompletions(
+ List<ICompletionProposal> proposals,
+ ICEditorContentAssistInvocationContext context,
+ IASTCompletionNode completionNode, String prefix) {
+ IASTName[] names = completionNode.getNames();
+ List<Completion> completions = new LinkedList<Completion>();
+
+ for (IASTName name : names) {
+ // The node isn't properly hooked up, must have backtracked out of
+ // this node
+ if (name.getTranslationUnit() == null)
+ continue;
+
+ IASTCompletionContext astContext = name.getCompletionContext();
+ if (astContext == null || !(astContext instanceof IASTNode))
+ continue;
+ IASTNode astNode = (IASTNode) astContext;
+
+ if (is_QObject_connect(context, astContext, name)) {
+ int parseOffset = context.getParseOffset();
+ int invocationOffset = context.getInvocationOffset();
+
+ String unparsed = "";
+ try {
+ unparsed = context.getDocument().get(parseOffset,
+ invocationOffset - parseOffset);
+ } catch (BadLocationException e) {
+ CCorePlugin.log(e);
+ }
+
+ if (unparsed.length() > 0 && unparsed.charAt(0) == '(')
+ unparsed = unparsed.substring(1);
+
+ int[] commas = computeCommaPositions(unparsed);
+ switch (commas.length) {
+ case 3:
+ completions.add(Completion.SIGNAL);
+ break;
+ case 5:
+ completions.add(Completion.SLOT);
+ break;
+ }
+ } else if (astNode.getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT) {
+ IASTNode parent = astNode.getParent();
+ if (!(parent instanceof IASTFunctionCallExpression))
+ continue;
+ IASTFunctionCallExpression call = (IASTFunctionCallExpression) parent;
+ IASTExpression nameExpr = call.getFunctionNameExpression();
+ if (!(nameExpr instanceof IASTIdExpression))
+ continue;
+ IASTIdExpression funcNameIdExpr = (IASTIdExpression) nameExpr;
+ IASTName funcName = funcNameIdExpr.getName();
+
+ if (!is_QObject_connect(context, astContext, funcName))
+ continue;
+
+ IASTInitializerClause[] args = call.getArguments();
+ switch (args.length) {
+ case 2:
+ addCompletionsFor(completions, args[0],
+ MethodFilter.Qt.Signal);
+ break;
+ case 4:
+ addCompletionsFor(completions, args[2],
+ MethodFilter.Qt.Slot);
+ break;
+ }
+ }
+ }
+
+ for (Completion completion : completions) {
+ ICompletionProposal proposal = completion.createProposal(context);
+ if (proposal != null)
+ proposals.add(proposal);
+ }
+ }
+
+ @Override
+ protected List<ICompletionProposal> computeCompletionProposals(
+ CContentAssistInvocationContext context,
+ IASTCompletionNode completionNode, String prefix)
+ throws CoreException {
+ if (!isApplicable(context))
+ return Collections.emptyList();
+
+ List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
+ addConnectParameterCompletions(proposals, context, completionNode,
+ prefix);
+ return proposals;
+ }
}