1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-04 23:05:47 +02:00

Recognize Q_SIGNAL and Q_SLOT on single functions

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>
This commit is contained in:
Andrew Eidsness 2013-02-27 08:16:19 -05:00 committed by Doug Schaefer
parent 5cad4cd8be
commit 719982b23b
3 changed files with 492 additions and 368 deletions

View file

@ -11,16 +11,13 @@ package org.eclipse.cdt.qt.core;
/** /**
* Declares constants related to tokens that are special in Qt applications. * Declares constants related to tokens that are special in Qt applications.
*/ */
public class QtKeywords public class QtKeywords {
{ public static final String CONNECT = "connect";
public static final String Q_SIGNALS = "Q_SIGNALS"; public static final String Q_SIGNAL = "Q_SIGNAL";
public static final String Q_SLOTS = "Q_SLOTS"; public static final String Q_SIGNALS = "Q_SIGNALS";
public static final String SIGNALS = "signals"; public static final String Q_SLOT = "Q_SLOT";
public static final String SLOTS = "slots"; public static final String Q_SLOTS = "Q_SLOTS";
public static final String QOBJECT = "QObject";
public static final String SIGNAL = "SIGNAL"; public static final String SIGNALS = "signals";
public static final String SLOT = "SLOT"; public static final String SLOTS = "slots";
public static final String QOBJECT = "QObject";
public static final String CONNECT = "connect";
} }

View file

@ -9,11 +9,14 @@
package org.eclipse.cdt.qt.internal.core; package org.eclipse.cdt.qt.internal.core;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration; 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.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; 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.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; 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.QtKeywords;
import org.eclipse.cdt.qt.core.QtPlugin; import org.eclipse.cdt.qt.core.QtPlugin;
public class QtSignalSlotTagger implements IBindingTagger /**
{ * Finds all functions that are marked as Qt signals or slots and tags them in
private static ICPPASTVisibilityLabel findVisibilityLabel( ICPPMethod method, IASTNode ast ) * 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
// the visibility cannot be found without an ast * macro before the function itself E.g., both of these cases are valid:
if( ast == null ) *
return null; * <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; IASTNode methodDecl = ast;
ICPPASTCompositeTypeSpecifier classType = null; ICPPASTCompositeTypeSpecifier classType = null;
while( methodDecl != null while (methodDecl != null && classType == null) {
&& classType == null ) IASTNode parent = methodDecl.getParent();
{ if (parent instanceof ICPPASTCompositeTypeSpecifier)
IASTNode parent = methodDecl.getParent(); classType = (ICPPASTCompositeTypeSpecifier) parent;
if( parent instanceof ICPPASTCompositeTypeSpecifier ) else
classType = (ICPPASTCompositeTypeSpecifier)parent; methodDecl = parent;
else }
methodDecl = parent;
}
if( methodDecl == null if (methodDecl == null || classType == null)
|| classType == null ) return null;
return null;
ICPPASTVisibilityLabel lastLabel = null; ICPPASTVisibilityLabel lastLabel = null;
for( IASTDeclaration decl : classType.getMembers() ) for (IASTDeclaration decl : classType.getMembers()) {
{ if (decl instanceof ICPPASTVisibilityLabel)
if( decl instanceof ICPPASTVisibilityLabel ) lastLabel = (ICPPASTVisibilityLabel) decl;
lastLabel = (ICPPASTVisibilityLabel)decl; else if (decl == methodDecl)
else if( decl == methodDecl ) return lastLabel;
return lastLabel; }
}
return null; return null;
} }
@Override private static byte getBitset(IASTNodeLocation... locations) {
public ITag process( ITagWriter tagWriter, IBinding binding, IASTName ast ) for (IASTNodeLocation location : locations)
{ if (location instanceof IASTMacroExpansionLocation) {
// only methods a be signals or slots IASTMacroExpansionLocation macroExpansion = (IASTMacroExpansionLocation) location;
if( ! ( binding instanceof ICPPMethod ) ) IASTPreprocessorMacroExpansion exp = macroExpansion
return null; .getExpansion();
String macro = exp.getMacroReference().toString();
// a visibility label is required in order to decide whether the method is a signal/slot if (QtKeywords.Q_SIGNAL.equals(macro)
ICPPMethod method = (ICPPMethod)binding; || QtKeywords.Q_SIGNALS.equals(macro)
ICPPASTVisibilityLabel v = findVisibilityLabel( method, ast ); || QtKeywords.SIGNALS.equals(macro))
if( v == null ) return QtPlugin.SignalSlot_Mask_signal;
return null; if (QtKeywords.Q_SLOT.equals(macro)
|| QtKeywords.Q_SLOTS.equals(macro)
|| QtKeywords.SLOTS.equals(macro))
return QtPlugin.SignalSlot_Mask_slot;
}
byte bitset = 0; return 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 ) ) private static byte getBitset(IASTNode... nodes) {
bitset |= QtPlugin.SignalSlot_Mask_signal; byte bitset = 0;
else if( QtKeywords.SLOTS.equals( macro ) || QtKeywords.Q_SLOTS.equals( macro ) ) for (IASTNode node : nodes)
bitset |= QtPlugin.SignalSlot_Mask_slot; if (node != null)
} for (IASTNodeLocation loc : node.getNodeLocations())
bitset |= getBitset(loc);
if( bitset != 0 ) return bitset;
{ }
IWritableTag tag = tagWriter.createTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID, 1 );
if( tag != null
&& tag.putByte( 0, bitset ) )
return tag;
}
return null; 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;
}
} }

View file

@ -46,347 +46,345 @@ import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.QtNature; import org.eclipse.cdt.qt.core.QtNature;
import org.eclipse.cdt.qt.core.QtPlugin; import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.contentassist.ICEditorContentAssistInvocationContext;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal;
@SuppressWarnings( "restriction" ) @SuppressWarnings("restriction")
public class QtCompletionProposalComputer extends ParsingBasedProposalComputer public class QtCompletionProposalComputer extends ParsingBasedProposalComputer {
{ private boolean isApplicable(ICEditorContentAssistInvocationContext context) {
private boolean isApplicable( CContentAssistInvocationContext context ) ITranslationUnit tu = context.getTranslationUnit();
{ if (tu == null)
ITranslationUnit tu = context.getTranslationUnit(); return false;
if( tu == null )
return false;
ICProject cProject = tu.getCProject(); ICProject cProject = tu.getCProject();
if( cProject == null ) if (cProject == null)
return false; return false;
IProject project = cProject.getProject(); IProject project = cProject.getProject();
if( project == null ) if (project == null)
return false; return false;
try try {
{ return project.hasNature(QtNature.ID);
return project.hasNature( QtNature.ID ); } catch (CoreException e) {
} CUIPlugin.log(e);
catch( CoreException e ) return false;
{ }
CUIPlugin.log( e ); }
return false;
}
}
private static boolean is_QObject_connect( CContentAssistInvocationContext context, IASTCompletionContext astContext, IASTName name ) private static boolean is_QObject_connect(
{ ICEditorContentAssistInvocationContext context,
IASTName connectName = name.getLastName(); IASTCompletionContext astContext, IASTName name) {
if( ! QtKeywords.CONNECT.equals( new String( connectName.getSimpleID() ) ) ) IASTName connectName = name.getLastName();
return false; if (!QtKeywords.CONNECT.equals(new String(connectName.getSimpleID())))
return false;
IBinding[] funcBindings = astContext.findBindings( connectName, ! context.isContextInformationStyle() ); IBinding[] funcBindings = astContext.findBindings(connectName,
for( IBinding funcBinding : funcBindings ) !context.isContextInformationStyle());
if( funcBinding instanceof ICPPFunction ) for (IBinding funcBinding : funcBindings)
{ if (funcBinding instanceof ICPPFunction) {
IBinding ownerBinding = ( (ICPPFunction)funcBinding ).getOwner(); IBinding ownerBinding = ((ICPPFunction) funcBinding).getOwner();
if( ownerBinding != null && QtKeywords.QOBJECT.equals( ownerBinding.getName() ) ) if (ownerBinding != null
return true; && QtKeywords.QOBJECT.equals(ownerBinding.getName()))
} return true;
}
return false; return false;
} }
private static class Completion private static class Completion {
{ private final String replacement;
private final String replacement; private final String display;
private final String display; private final int cursorOffset;
private final int cursorOffset;
public static final Completion SIGNAL = new Completion( "SIGNAL()", "SIGNAL(a)", -1 ); public static final Completion SIGNAL = new Completion("SIGNAL()",
public static final Completion SLOT = new Completion( "SLOT()", "SLOT(a)", -1 ); "SIGNAL(a)", -1);
public static final Completion SLOT = new Completion("SLOT()",
"SLOT(a)", -1);
public Completion( String replacement ) public Completion(String replacement) {
{ this(replacement, replacement, 0);
this( replacement, replacement, 0 ); }
}
public Completion( String replacement, String display, int cursorOffset ) public Completion(String replacement, String display, int cursorOffset) {
{ this.replacement = replacement;
this.replacement = replacement; this.display = display;
this.display = display; this.cursorOffset = cursorOffset;
this.cursorOffset = cursorOffset; }
}
public ICompletionProposal createProposal( CContentAssistInvocationContext context ) public ICompletionProposal createProposal(
{ ICEditorContentAssistInvocationContext context) {
int repLength = replacement.length(); int repLength = replacement.length();
int repOffset = context.getInvocationOffset(); int repOffset = context.getInvocationOffset();
CCompletionProposal p = new CCompletionProposal( replacement, repOffset, repLength, null, display, RelevanceConstants.DEFAULT_TYPE_RELEVANCE, context.getViewer() ); CCompletionProposal p = new CCompletionProposal(replacement,
p.setCursorPosition( repLength + cursorOffset ); repOffset, repLength, null, display,
return p; RelevanceConstants.DEFAULT_TYPE_RELEVANCE,
} context.getViewer());
p.setCursorPosition(repLength + cursorOffset);
return p;
}
@Override @Override
public String toString() public String toString() {
{ if (replacement == null)
if( replacement == null ) return super.toString();
return super.toString(); return replacement + '@' + cursorOffset;
return replacement + '@' + cursorOffset; }
} }
}
private static interface MethodFilter private static interface MethodFilter {
{ public boolean keep(ICPPMethod method);
public boolean keep( ICPPMethod method );
public static class Qt public static class Qt {
{ public static final MethodFilter Signal = new MethodFilter() {
public static final MethodFilter Signal = new MethodFilter() @Override
{ public boolean keep(ICPPMethod method) {
@Override ITagReader tagReader = CCorePlugin.getTagService()
public boolean keep( ICPPMethod method ) .findTagReader(method);
{ if (tagReader == null)
ITagReader tagReader = CCorePlugin.getTagService().findTagReader( method ); return false;
if( tagReader == null )
return false;
ITag tag = tagReader.getTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID ); ITag tag = tagReader.getTag(QtPlugin.SIGNAL_SLOT_TAGGER_ID);
if( tag == null ) if (tag == null)
return false; return false;
int result = tag.getByte( 0 ); int result = tag.getByte(0);
return result != ITag.FAIL return result != ITag.FAIL
&& ( ( result & QtPlugin.SignalSlot_Mask_signal ) == QtPlugin.SignalSlot_Mask_signal ); && ((result & QtPlugin.SignalSlot_Mask_signal) == QtPlugin.SignalSlot_Mask_signal);
} }
}; };
public static final MethodFilter Slot = new MethodFilter() public static final MethodFilter Slot = new MethodFilter() {
{ @Override
@Override public boolean keep(ICPPMethod method) {
public boolean keep( ICPPMethod method ) ITagReader tagReader = CCorePlugin.getTagService()
{ .findTagReader(method);
ITagReader tagReader = CCorePlugin.getTagService().findTagReader( method ); if (tagReader == null)
if( tagReader == null ) return false;
return false;
ITag tag = tagReader.getTag( QtPlugin.SIGNAL_SLOT_TAGGER_ID ); ITag tag = tagReader.getTag(QtPlugin.SIGNAL_SLOT_TAGGER_ID);
if( tag == null ) if (tag == null)
return false; return false;
int result = tag.getByte( 0 ); int result = tag.getByte(0);
return result != ITag.FAIL return result != ITag.FAIL
&& ( ( result & QtPlugin.SignalSlot_Mask_slot ) == QtPlugin.SignalSlot_Mask_slot ); && ((result & QtPlugin.SignalSlot_Mask_slot) == QtPlugin.SignalSlot_Mask_slot);
} }
}; };
} }
} }
private static Iterable<ICPPMethod> filterMethods( final ICPPClassType cls, final MethodFilter filter ) private static Iterable<ICPPMethod> filterMethods(final ICPPClassType cls,
{ final MethodFilter filter) {
return new Iterable<ICPPMethod>() return new Iterable<ICPPMethod>() {
{ @Override
@Override public Iterator<ICPPMethod> iterator() {
public Iterator<ICPPMethod> iterator() return new Iterator<ICPPMethod>() {
{ private int index = 0;
return new Iterator<ICPPMethod>() private final ICPPMethod[] methods = cls.getMethods();
{
private int index = 0;
private final ICPPMethod[] methods = cls.getMethods();
@Override @Override
public boolean hasNext() public boolean hasNext() {
{ for (; index < methods.length; ++index)
for( ; index < methods.length; ++index ) if (filter.keep(methods[index]))
if( filter.keep( methods[index] ) ) return true;
return true; return false;
return false; }
}
@Override public ICPPMethod next() { return methods[index++]; } @Override
@Override public void remove() { } public ICPPMethod next() {
}; return methods[index++];
} }
};
}
private static String getSignature( ICPPMethod method ) @Override
{ public void remove() {
StringBuilder signature = new StringBuilder(); }
};
}
};
}
signature.append( method.getName() ); private static String getSignature(ICPPMethod method) {
signature.append( '(' ); StringBuilder signature = new StringBuilder();
boolean first = true;
for( ICPPParameter param : method.getParameters() )
{
if( first )
first = false;
else
signature.append( ", " );
signature.append( ASTTypeUtil.getType( param.getType() ) );
}
signature.append( ')' ); signature.append(method.getName());
return signature.toString(); signature.append('(');
} boolean first = true;
for (ICPPParameter param : method.getParameters()) {
if (first)
first = false;
else
signature.append(", ");
signature.append(ASTTypeUtil.getType(param.getType()));
}
private static void addCompletionsFor( Collection<Completion> completions, IASTInitializerClause init, MethodFilter filter ) signature.append(')');
{ return signature.toString();
if( !( init instanceof ICPPASTInitializerClause ) ) }
return;
ICPPEvaluation eval = ( (ICPPASTInitializerClause)init ).getEvaluation(); private static void addCompletionsFor(Collection<Completion> completions,
if( eval == null ) IASTInitializerClause init, MethodFilter filter) {
return; if (!(init instanceof ICPPASTInitializerClause))
return;
IType type = eval.getTypeOrFunctionSet( init ); ICPPEvaluation eval = ((ICPPASTInitializerClause) init).getEvaluation();
while( type instanceof IPointerType ) if (eval == null)
type = ( (IPointerType)type ).getType(); return;
if( type instanceof ICPPClassType ) IType type = eval.getTypeOrFunctionSet(init);
for( ICPPMethod signal : filterMethods( (ICPPClassType)type, filter ) ) while (type instanceof IPointerType)
completions.add( new Completion( getSignature( signal ) ) ); type = ((IPointerType) type).getType();
}
// Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator if (type instanceof ICPPClassType)
private static int indexOfClosingPeer(String code, char left, char right, int pos) { for (ICPPMethod signal : filterMethods((ICPPClassType) type, filter))
int level= 0; completions.add(new Completion(getSignature(signal)));
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 // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
private static int[] computeCommaPositions(String code) { private static int indexOfClosingPeer(String code, char left, char right,
final int length= code.length(); int pos) {
int pos= 0; int level = 0;
List<Integer> positions= new ArrayList<Integer>(); final int length = code.length();
positions.add(new Integer(-1)); while (pos < length) {
while (pos < length && pos != -1) { char ch = code.charAt(pos);
char ch= code.charAt(pos); if (ch == left) {
switch (ch) { ++level;
case ',': } else if (ch == right) {
positions.add(new Integer(pos)); if (--level == 0) {
break; return pos;
case '(': }
pos= indexOfClosingPeer(code, '(', ')', pos); }
break; ++pos;
case '<': }
pos= indexOfClosingPeer(code, '<', '>', pos); return -1;
break; }
case '[':
pos= indexOfClosingPeer(code, '[', ']', pos);
break;
default:
break;
}
if (pos != -1)
pos++;
}
positions.add(new Integer(length));
int[] fields= new int[positions.size()]; // Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
for (int i= 0; i < fields.length; i++) private static int[] computeCommaPositions(String code) {
fields[i]= positions.get(i).intValue(); final int length = code.length();
return fields; 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 ) private void addConnectParameterCompletions(
{ List<ICompletionProposal> proposals,
IASTName[] names = completionNode.getNames(); ICEditorContentAssistInvocationContext context,
List<Completion> completions = new LinkedList<Completion>(); IASTCompletionNode completionNode, String prefix) {
IASTName[] names = completionNode.getNames();
List<Completion> completions = new LinkedList<Completion>();
for( IASTName name : names ) for (IASTName name : names) {
{ // The node isn't properly hooked up, must have backtracked out of
// The node isn't properly hooked up, must have backtracked out of this node // this node
if( name.getTranslationUnit() == null ) if (name.getTranslationUnit() == null)
continue; continue;
IASTCompletionContext astContext = name.getCompletionContext(); IASTCompletionContext astContext = name.getCompletionContext();
if( astContext == null || ! ( astContext instanceof IASTNode ) ) if (astContext == null || !(astContext instanceof IASTNode))
continue; continue;
IASTNode astNode = (IASTNode)astContext; IASTNode astNode = (IASTNode) astContext;
if( is_QObject_connect( context, astContext, name ) ) if (is_QObject_connect(context, astContext, name)) {
{ int parseOffset = context.getParseOffset();
int parseOffset = context.getParseOffset(); int invocationOffset = context.getInvocationOffset();
int invocationOffset = context.getInvocationOffset();
String unparsed = ""; String unparsed = "";
try { unparsed = context.getDocument().get( parseOffset, invocationOffset - parseOffset ); } try {
catch( BadLocationException e ) { CCorePlugin.log( e ); } unparsed = context.getDocument().get(parseOffset,
invocationOffset - parseOffset);
} catch (BadLocationException e) {
CCorePlugin.log(e);
}
if( unparsed.length() > 0 && unparsed.charAt( 0 ) == '(' ) if (unparsed.length() > 0 && unparsed.charAt(0) == '(')
unparsed = unparsed.substring( 1 ); unparsed = unparsed.substring(1);
int[] commas = computeCommaPositions( unparsed ); int[] commas = computeCommaPositions(unparsed);
switch( commas.length ) switch (commas.length) {
{ case 3:
case 3: completions.add(Completion.SIGNAL);
completions.add( Completion.SIGNAL ); break;
break; case 5:
case 5: completions.add(Completion.SLOT);
completions.add( Completion.SLOT ); break;
break; }
} } else if (astNode.getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT) {
} IASTNode parent = astNode.getParent();
else if( astNode.getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT ) if (!(parent instanceof IASTFunctionCallExpression))
{ continue;
IASTNode parent = astNode.getParent(); IASTFunctionCallExpression call = (IASTFunctionCallExpression) parent;
if( ! ( parent instanceof IASTFunctionCallExpression ) ) IASTExpression nameExpr = call.getFunctionNameExpression();
continue; if (!(nameExpr instanceof IASTIdExpression))
IASTFunctionCallExpression call = (IASTFunctionCallExpression)parent; continue;
IASTExpression nameExpr = call.getFunctionNameExpression(); IASTIdExpression funcNameIdExpr = (IASTIdExpression) nameExpr;
if( !( nameExpr instanceof IASTIdExpression ) ) IASTName funcName = funcNameIdExpr.getName();
continue;
IASTIdExpression funcNameIdExpr = (IASTIdExpression)nameExpr;
IASTName funcName = funcNameIdExpr.getName();
if( !is_QObject_connect( context, astContext, funcName ) ) if (!is_QObject_connect(context, astContext, funcName))
continue; continue;
IASTInitializerClause[] args = call.getArguments(); IASTInitializerClause[] args = call.getArguments();
switch( args.length ) switch (args.length) {
{ case 2:
case 2: addCompletionsFor(completions, args[0],
//if( QtKeywords.SIGNAL.equals( prefix ) ) MethodFilter.Qt.Signal);
addCompletionsFor( completions, args[0], MethodFilter.Qt.Signal ); break;
break; case 4:
case 4: addCompletionsFor(completions, args[2],
if( QtKeywords.SLOT.equals( prefix ) ) MethodFilter.Qt.Slot);
addCompletionsFor( completions, args[2], MethodFilter.Qt.Slot ); break;
break; }
} }
} }
}
for( Completion completion : completions ) for (Completion completion : completions) {
{ ICompletionProposal proposal = completion.createProposal(context);
ICompletionProposal proposal = completion.createProposal( context ); if (proposal != null)
if( proposal != null ) proposals.add(proposal);
proposals.add( proposal ); }
} }
}
@Override @Override
protected List<ICompletionProposal> computeCompletionProposals( CContentAssistInvocationContext context, IASTCompletionNode completionNode, String prefix ) throws CoreException protected List<ICompletionProposal> computeCompletionProposals(
{ CContentAssistInvocationContext context,
if( !isApplicable( context ) ) IASTCompletionNode completionNode, String prefix)
return Collections.emptyList(); throws CoreException {
if (!isApplicable(context))
return Collections.emptyList();
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
addConnectParameterCompletions( proposals, context, completionNode, prefix ); addConnectParameterCompletions(proposals, context, completionNode,
return proposals; prefix);
} return proposals;
}
} }