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:
parent
d45ff124e3
commit
8956d2c7e6
14 changed files with 224 additions and 7 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue