1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-13 19:25:38 +02:00

Bug 72809 - Improve content assist inside inactive code

Change-Id: If24e354d00aaf886da1571fc525b556e08c94897
This commit is contained in:
Nathan Ridge 2017-01-12 23:04:43 -05:00
parent d45ff124e3
commit 8956d2c7e6
14 changed files with 224 additions and 7 deletions

View file

@ -19,6 +19,7 @@ import org.eclipse.cdt.core.dom.ast.gnu.IGCCASTAttributeList;
import org.eclipse.cdt.core.dom.ast.gnu.IGNUASTCompoundStatementExpression;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
/**
* Factory for creating AST nodes. This interface contains factory methods
@ -133,6 +134,12 @@ public interface INodeFactory {
public IASTIfStatement newIfStatement(IASTExpression condition, IASTStatement then, IASTStatement elseClause);
/**
* @since 6.3
* @noreference This method is not intended to be referenced by clients.
*/
public IASTInactiveCompletionName newInactiveCompletionName(char[] name, IASTTranslationUnit ast);
public IASTInitializerList newInitializerList();
public IASTLabelStatement newLabelStatement(IASTName name, IASTStatement nestedStatement);

View file

@ -295,7 +295,23 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
} catch (OffsetLimitReachedException olre) {
if (mode != ParserMode.COMPLETION_PARSE)
throw new EndOfFileException(olre.getEndOffset());
createCompletionNode(olre.getFinalToken());
IToken completionToken = olre.getFinalToken();
createCompletionNode(completionToken);
if (olre.getOriginator() == OffsetLimitReachedException.ORIGIN_INACTIVE_CODE) {
// If completion is invoked inside inactive code, there is no AST from which
// to give the completion node a completion name, so we invent a name.
// The invented name is not hooked up to the AST, but does have an offset
// and length, so it can provide an accurate point of reference in
// declaredBefore().
IASTName completionName = nodeFactory.newInactiveCompletionName(
completionToken.getCharImage(), getTranslationUnit());
((ASTNode) completionName).setOffsetAndLength(completionToken.getOffset(),
completionToken.getLength());
completionNode.addName(completionName);
// Consume the completion token so we don't try to parse an AST fragment from it.
consume();
}
throw olre;
}
}

View file

@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2017 Nathan Ridge.
* 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
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTName;
/**
* Interface for a name representing a prefix being completed inside inactive code.
*/
public interface IASTInactiveCompletionName extends IASTName, IASTCompletionContext {
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2017 Nathan Ridge.
* 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
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.c;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
public class CASTInactiveCompletionName extends CASTName implements IASTInactiveCompletionName {
private IASTTranslationUnit fAst;
public CASTInactiveCompletionName(char[] name, IASTTranslationUnit ast) {
super(name);
fAst = ast;
}
@Override
public IASTCompletionContext getCompletionContext() {
return this;
}
@Override
public IBinding[] findBindings(IASTName name, boolean isPrefix) {
// 'name' (which is the same as 'this') is not hooked up to the AST, but it
// does have a location (offset and length) which we use to compute the
// containing scope.
IASTNodeSelector sel = fAst.getNodeSelector(null);
IASTNode node = sel.findEnclosingNode(getOffset(), getLength());
IScope lookupScope = CVisitor.getContainingScope(node);
if (lookupScope == null) {
lookupScope = fAst.getScope();
}
IBinding[] result = null;
try {
if (isPrefix) {
result = CVisitor.lookupPrefix(lookupScope, name);
} else {
result = new IBinding[] { CVisitor.lookup(lookupScope, name) };
}
} catch (DOMException e) {
}
return ArrayUtil.trim(IBinding.class, result);
}
}

View file

@ -87,6 +87,7 @@ import org.eclipse.cdt.core.dom.ast.gnu.c.IGCCASTArrayRangeDesignator;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.internal.core.dom.parser.ASTToken;
import org.eclipse.cdt.internal.core.dom.parser.ASTTokenList;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
import org.eclipse.cdt.internal.core.dom.parser.NodeFactory;
import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
@ -328,6 +329,11 @@ public class CNodeFactory extends NodeFactory implements ICNodeFactory {
return new CASTIfStatement(expr, thenStat, elseClause);
}
@Override
public IASTInactiveCompletionName newInactiveCompletionName(char[] name, IASTTranslationUnit ast) {
return new CASTInactiveCompletionName(name, ast);
}
@Override
@Deprecated
public org.eclipse.cdt.core.dom.ast.IASTInitializerExpression newInitializerExpression(IASTExpression expression) {

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2017 Nathan Ridge.
* 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
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
public class CPPASTInactiveCompletionName extends CPPASTName implements IASTInactiveCompletionName {
private IASTTranslationUnit fAst;
public CPPASTInactiveCompletionName(char[] name, IASTTranslationUnit ast) {
super(name);
fAst = ast;
}
@Override
public IASTCompletionContext getCompletionContext() {
return this;
}
@Override
public IBinding[] findBindings(IASTName name, boolean isPrefix) {
// 'name' (which is the same as 'this') is not hooked up to the AST, but it
// does have a location (offset and length) which we use to compute the
// containing scope.
IASTNodeSelector sel = fAst.getNodeSelector(null);
IASTNode node = sel.findEnclosingNode(getOffset(), getLength());
IScope lookupScope = CPPVisitor.getContainingScope(node);
if (lookupScope == null) {
lookupScope = fAst.getScope();
}
return CPPSemantics.findBindingsForContentAssist(name.getLookupKey(), isPrefix, lookupScope, name);
}
}

View file

@ -52,6 +52,7 @@ import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTToken;
import org.eclipse.cdt.core.dom.ast.IASTTokenList;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTTypeIdInitializerExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
@ -138,6 +139,7 @@ import org.eclipse.cdt.core.dom.parser.cpp.ICPPASTAttributeSpecifier;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.internal.core.dom.parser.ASTToken;
import org.eclipse.cdt.internal.core.dom.parser.ASTTokenList;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
import org.eclipse.cdt.internal.core.dom.parser.NodeFactory;
import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
@ -511,6 +513,11 @@ public class CPPNodeFactory extends NodeFactory implements ICPPNodeFactory {
return new CPPASTIfStatement(condition, then, elseClause);
}
@Override
public IASTInactiveCompletionName newInactiveCompletionName(char[] name, IASTTranslationUnit ast) {
return new CPPASTInactiveCompletionName(name, ast);
}
@Override
@Deprecated
public org.eclipse.cdt.core.dom.ast.IASTInitializerExpression newInitializerExpression(IASTExpression expression) {

View file

@ -3934,6 +3934,26 @@ public class CPPSemantics {
return contentAssistLookup(data, nsScopes);
}
/**
* Similar to {@link CPPSemantics#findBindingsForContentAssist(IASTName, boolean, String[])},
* but in lieu of a name hooked up to the AST, accepts just a string, a position in the file
* (represented as an IASTNode, and used to serve as the point of reference for the lookup),
* and a starting scope (which is required).
*/
public static IBinding[] findBindingsForContentAssist(char[] name, boolean prefixLookup,
IScope lookupScope, IASTNode point) {
LookupData data = new LookupData(name, null, point);
data.contentAssist = true;
data.fHeuristicBaseLookup = true;
data.setPrefixLookup(prefixLookup);
data.foundItems = new CharArrayObjectMap<>(2);
try {
CPPSemantics.lookup(data, lookupScope);
} catch (DOMException e) {
}
return collectContentAssistBindings(data);
}
private static IScope getLookupScope(IASTNode node) {
if (node == null)
return null;
@ -4082,6 +4102,10 @@ public class CPPSemantics {
}
} catch (DOMException e) {
}
return collectContentAssistBindings(data);
}
private static IBinding[] collectContentAssistBindings(LookupData data) {
@SuppressWarnings("unchecked")
CharArrayObjectMap<Object> map = (CharArrayObjectMap<Object>) data.foundItems;
IBinding[] result = IBinding.EMPTY_BINDING_ARRAY;

View file

@ -244,7 +244,7 @@ final public class Lexer implements ITokenSequence {
t0= t1;
t1= fetchToken();
final int tt1 = t1.getType();
if (tt1 == IToken.tEND_OF_INPUT)
if (tt1 == IToken.tEND_OF_INPUT || tt1 == IToken.tCOMPLETION)
break;
if (tt1 == IToken.tPOUND) {
final int tt0= t0.getType();

View file

@ -484,6 +484,16 @@ public class CompletionTests extends CompletionTestBase {
assertCompletionResults(fCursorOffset, expected, ID);
}
// int waldo;
// void foo() {
// #ifdef SOME_UNDEFINED_MACRO
// wald/*cursor*/
// #endif
// }
public void testInactiveCodeBlock_72809() throws Exception {
assertCompletionResults(new String[] { "waldo" });
}
//void gfunc(){TClass<int> t(0); t.a/*cursor*/
public void testTemplateClassMethod() throws Exception {
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=172436

View file

@ -387,6 +387,16 @@ public class CompletionTests_PlainC extends AbstractContentAssistTest {
assertCompletionResults(expected);
}
// int waldo;
// void foo() {
// #ifdef SOME_UNDEFINED_MACRO
// wald/*cursor*/
// #endif
// }
public void testInactiveCodeBlock_72809() throws Exception {
assertCompletionResults(new String[] { "waldo" });
}
//void fooFunction()
//{
// AStructType* c;

View file

@ -70,4 +70,13 @@ public class HelpProposalTests extends CompletionTestBase {
String[] expected = new String[] { "Waldo(const Waldo &)", "Waldo(int, int)" };
assertCompletionResults(fCursorOffset, expected, ID);
}
// void foo() {
// #ifdef MYMACRO
// setv/*cursor*/
// #endif
// }
public void testInactiveCodeBlock_72809() throws Exception {
assertCompletionResults(new String[] { "setvbuf()" });
}
}

View file

@ -97,6 +97,7 @@ import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
import org.eclipse.cdt.internal.core.dom.parser.c.CBuiltinParameter;
import org.eclipse.cdt.internal.core.dom.parser.c.CBuiltinVariable;
import org.eclipse.cdt.internal.core.dom.parser.c.CImplicitFunction;
@ -162,8 +163,11 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
IASTName[] names = completionNode.getNames();
for (IASTName name : names) {
if (name.getTranslationUnit() == null) {
if (name.getTranslationUnit() == null && !(name instanceof IASTInactiveCompletionName)) {
// The node isn't properly hooked up, must have backtracked out of this node.
// Inactive completion names are special in that they are not hooked up
// (because there is no AST for the inactive code), but we still want to
// attempt completion for them.
continue;
}

View file

@ -29,6 +29,8 @@ import org.eclipse.cdt.ui.IFunctionSummary;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.ui.text.IContentAssistHelpInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.IASTInactiveCompletionName;
import org.eclipse.cdt.internal.ui.CHelpProviderManager;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
@ -46,8 +48,10 @@ public class HelpCompletionProposalComputer extends ParsingBasedProposalComputer
for (int i = 0; i < names.length; ++i) {
IASTName name = names[i];
// ignore if not connected
if (name.getTranslationUnit() == null)
// Ignore if not connected.
// See the corresponding code in DOMCompletionProposalComputer for why
// inactive completion names are special.
if (name.getTranslationUnit() == null && !(name instanceof IASTInactiveCompletionName))
continue;
// ignore if this is a member access