blob: 429f3ae35bc0d0e45d0e92ce467ae72bb1dacfd6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
* 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.dltk.tcl.internal.core.codeassist.completion;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.expressions.StringLiteral;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.statements.Block;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.codeassist.complete.CompletionNodeFound;
import org.eclipse.dltk.tcl.ast.TclStatement;
import org.eclipse.dltk.tcl.ast.expressions.TclBlockExpression;
import org.eclipse.dltk.tcl.ast.expressions.TclExecuteExpression;
import org.eclipse.dltk.tcl.core.TclKeywordsManager;
import org.eclipse.dltk.tcl.core.TclParseUtil;
import org.eclipse.dltk.tcl.core.ast.TclAdvancedExecuteExpression;
import org.eclipse.dltk.tcl.core.ast.TclPackageDeclaration;
import org.eclipse.dltk.tcl.core.extensions.ICompletionExtension;
import org.eclipse.dltk.tcl.internal.core.codeassist.TclASTUtil;
import org.eclipse.dltk.tcl.internal.core.codeassist.TclAssistParser;
import org.eclipse.dltk.tcl.internal.parser.OldTclParserUtils;
public class TclCompletionParser extends TclAssistParser {
private static class TclEmptyCompleteStatement extends TclStatement {
public TclEmptyCompleteStatement(List expressions) {
super(expressions);
}
}
private ICompletionExtension[] extensions;
public TclCompletionParser(ICompletionExtension[] extensions) {
this.extensions = extensions;
}
/**
* Called then element could not be found.
*/
@Override
public void handleNotInElement(ASTNode node, int position) {
if (node != null && node.sourceStart() <= position
&& position <= node.sourceEnd()) {
// this is empty module case
if (node instanceof ModuleDeclaration) {
ModuleDeclaration unit = (ModuleDeclaration) node;
List exprs = new ArrayList();
exprs.add(new SimpleReference(position, position, ""));
TclEmptyCompleteStatement statement = new TclEmptyCompleteStatement(
exprs);
unit.addStatement(statement);
this.parseBlockStatements(statement, unit, position);
} else if (node instanceof MethodDeclaration) {
// empty keyword like completion.
MethodDeclaration method = (MethodDeclaration) node;
List exprs = new ArrayList();
exprs.add(new SimpleReference(position, position, ""));
TclEmptyCompleteStatement statement = new TclEmptyCompleteStatement(
exprs);
method.getStatements().add(statement);
this.parseBlockStatements(statement, method, position);
} else if (node instanceof TypeDeclaration) {
// empty keyword like completion.
TypeDeclaration type = (TypeDeclaration) node;
List exprs = new ArrayList();
exprs.add(new SimpleReference(position, position, ""));
TclEmptyCompleteStatement statement = new TclEmptyCompleteStatement(
exprs);
type.getStatements().add(statement);
// this.assistNodeParent = node;
this.parseBlockStatements(statement, type, position);
} else if (node instanceof TclExecuteExpression) {
// empty keyword like completion.
List exprs = new ArrayList();
exprs.add(new SimpleReference(position, position, ""));
TclEmptyCompleteStatement statement = new TclEmptyCompleteStatement(
exprs);
this.parseBlockStatements(statement, node, position);
}
}
}
@Override
public void parseBlockStatements(ASTNode node, ASTNode inNode,
int position) {
if (node instanceof TclStatement) {
TclStatement statement = (TclStatement) node;
List<ASTNode> expressions = statement.getExpressions();
int len = expressions.size();
boolean first = false;
String completionToken = null;
ASTNode completionNode = null;
for (int i = 0; i < len; ++i) {
ASTNode n = expressions.get(i);
if (n.sourceStart() <= position && n.sourceEnd() >= position
|| (node instanceof TclEmptyCompleteStatement)) {
if (i == 0) {
first = true;
}
completionNode = n;
}
}
if (completionNode == null) {
// TODO: Add inner completion here.
if (len > 0) {
// ASTNode firstNode = (ASTNode) expressions.get(0);
// if (position > firstNode.sourceEnd()) {
// // This could be variable completion.
// boolean provideDollar =
// !checkVariableWithoutDollarCompletion(
// statement, position);
// this.assistNodeParent = inNode;
// SimpleReference ref = new SimpleReference(position,
// position, "");
// ASTNode nde = new CompletionOnVariable("", ref, node,
// inNode, true, provideDollar);
// throw new CompletionNodeFound(nde, null);
// }
String[] keywords = checkKeywords(completionToken, MODULE);
ASTNode nde = new CompletionOnKeywordArgumentOrFunctionArgument(
"", (TclStatement) node, keywords, position);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
null/*
* ((TypeDeclaration) inNode).scope
*/);
} else {
completionToken = "";
}
} else if (completionNode instanceof SimpleReference) {
int maxLen = position - completionNode.sourceStart();
completionToken = ((SimpleReference) completionNode).getName();
// We need to cut some sumbols if node is begger then position.
if (completionToken.length() > maxLen && maxLen > 0) {
completionToken = completionToken.substring(0, maxLen);
}
}
if (completionNode instanceof StringLiteral) {
int maxLen = position - completionNode.sourceStart();
int pos = maxLen;
SimpleReference tok = OldTclParserUtils
.extractVariableFromString(
(StringLiteral) completionNode, pos);
if (tok != null) {
this.assistNodeParent = inNode;
ASTNode nde = new CompletionOnVariable(tok.getName(), tok,
node, inNode, false);
throw new CompletionNodeFound(nde, null);
} else {
this.assistNodeParent = inNode;
SimpleReference ref = new SimpleReference(position,
position, "");
ASTNode nde = new CompletionOnVariable("", ref, node,
inNode, true);
throw new CompletionNodeFound(nde, null);
}
}
if (completionNode instanceof TclExecuteExpression) {
TclExecuteExpression expr = (TclExecuteExpression) completionNode;
List exprs = expr.parseExpression();
for (int i = 0; i < exprs.size(); ++i) {
ASTNode n = (ASTNode) exprs.get(i);
if (n.sourceStart() <= position
&& n.sourceEnd() >= position) {
parseBlockStatements(n, expr, position);
}
}
handleNotInElement(expr, position);
}
if (completionNode instanceof TclAdvancedExecuteExpression) {
TclAdvancedExecuteExpression expr = (TclAdvancedExecuteExpression) completionNode;
List exprs = expr.getStatements();
for (int i = 0; i < exprs.size(); ++i) {
ASTNode n = (ASTNode) exprs.get(i);
if (n.sourceStart() <= position
&& n.sourceEnd() >= position) {
parseBlockStatements(n, expr, position);
}
}
}
if (completionNode instanceof TclBlockExpression) {
TclBlockExpression block = (TclBlockExpression) completionNode;
List s = block.parseBlockSimple();
if (s != null) {
int slen = s.size();
for (int u = 0; u < slen; ++u) {
ASTNode n = (ASTNode) s.get(u);
n.setStart(n.sourceStart() - block.sourceStart());
n.setEnd(n.sourceEnd() - block.sourceStart());
TclASTUtil.extendStatement(n, block.getBlock());
n.setStart(n.sourceStart() + block.sourceStart());
n.setEnd(n.sourceEnd() + block.sourceStart());
if (n.sourceStart() <= position
&& n.sourceEnd() >= position) {
parseBlockStatements(n, inNode, position);
}
}
}
handleNotInElement(inNode, position);
}
if (completionToken != null && completionToken.startsWith("$")) {
// Argument name completion...
this.assistNodeParent = inNode;
ASTNode nde = new CompletionOnVariable(completionToken,
completionNode, node, inNode, false);
throw new CompletionNodeFound(nde, null);
} else {
// This is keyword or function completion.
if (inNode instanceof ModuleDeclaration
&& completionNode != null && first) {
String[] keywords = checkKeywords(completionToken, MODULE);
ASTNode nde = new CompletionOnKeywordOrFunction(
completionToken, completionNode, node, keywords);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
((ModuleDeclaration) inNode).scope);
} else if (inNode instanceof MethodDeclaration
&& completionNode != null && first) {
String[] keywords = checkKeywords(completionToken,
FUNCTION);
ASTNode nde = new CompletionOnKeywordOrFunction(
completionToken, completionNode,
TclParseUtil.getScopeParent(getModule(), node),
keywords);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
((MethodDeclaration) inNode).scope);
} else if (inNode instanceof TypeDeclaration
&& completionNode != null && first) {
String[] keywords = checkKeywords(completionToken,
NAMESPACE);
ASTNode nde = new CompletionOnKeywordOrFunction(
completionToken, completionNode, node, keywords);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
null/*
* ((TypeDeclaration) inNode).scope
*/);
} else if (inNode instanceof TclExecuteExpression
&& completionNode != null && first) {
String[] keywords = checkKeywords(completionToken,
EXEC_EXPRESSION);
ASTNode nde = new CompletionOnKeywordOrFunction(
completionToken, completionNode, node, keywords);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
null/*
* ((TypeDeclaration) inNode).scope
*/);
} else {
if (completionNode != null) {
String[] keywords = checkKeywords(completionToken,
MODULE);
ASTNode nde = new CompletionOnKeywordArgumentOrFunctionArgument(
completionToken, completionNode,
(TclStatement) node, keywords);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
null/*
* ((TypeDeclaration ) inNode).scope
*/);
} else {
String[] keywords = checkKeywords(completionToken,
MODULE);
if (completionToken == null) {
completionToken = "";
}
ASTNode nde = new CompletionOnKeywordArgumentOrFunctionArgument(
completionToken, (TclStatement) node, keywords,
position);
this.assistNodeParent = inNode;
throw new CompletionNodeFound(nde,
null/*
* ((TypeDeclaration ) inNode).scope
*/);
}
}
}
// if (checkVariableWithoutDollarCompletion(statement, position)
// && completionToken != null) {
// this.assistNodeParent = inNode;
// SimpleReference ref = new SimpleReference(completionNode
// .sourceStart(), completionNode.sourceEnd(),
// completionToken);
// ASTNode nde = new CompletionOnVariable(completionToken, ref,
// node, inNode, true);
// throw new CompletionNodeFound(nde, null);
// }
} else if (node instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) node;
List statements = method.getStatements();
boolean inStatement = false;
if (statements != null) {
int length = statements.size();
for (int i = 0; i < length; i++) {
ASTNode nde = (ASTNode) statements.get(i);
if (nde.sourceStart() <= position
&& nde.sourceEnd() >= position) {
inStatement = true;
parseBlockStatements(nde, method, position);
}
}
}
if (!inStatement) {
this.handleNotInElement(method, position);
}
} else {
visitElements(node, position);
}
}
public class CompletionVisitor extends ASTVisitor {
protected int position;
protected ModuleDeclaration module;
public CompletionVisitor(int position, ModuleDeclaration module) {
this.position = position;
this.module = module;
}
@Override
public boolean visit(Statement s) throws Exception {
if (s.sourceStart() <= position && s.sourceEnd() >= position) {
for (int i = 0; i < extensions.length; i++) {
extensions[i].visit(s, TclCompletionParser.this, position);
}
if (s instanceof TclStatement) {
ASTNode inNode = TclParseUtil.getScopeParent(module, s);
TclCompletionParser.this.parseBlockStatements(s, inNode,
position);
}
if (s instanceof TclPackageDeclaration) {
TclPackageDeclaration decl = (TclPackageDeclaration) s;
if (decl.getNameStart() <= position
&& position <= decl.getNameEnd()) {
ASTNode inNode = TclParseUtil.getScopeParent(module, s);
assistNodeParent = inNode;
throw new CompletionNodeFound(decl,
null/*
* ((TypeDeclaration ) inNode).scope
*/);
}
}
}
return super.visit(s);
}
@Override
public boolean visit(Expression s) throws Exception {
if (s.sourceStart() <= position && s.sourceEnd() >= position) {
if (s instanceof TclBlockExpression) {
ASTNode inNode = TclParseUtil.getScopeParent(module, s);
TclBlockExpression block = (TclBlockExpression) s;
List ss = block.parseBlockSimple();
if (ss != null) {
int slen = ss.size();
for (int u = 0; u < slen; ++u) {
ASTNode n = (ASTNode) ss.get(u);
if (n != null && n.sourceStart() <= position
&& n.sourceEnd() >= position) {
parseBlockStatements(n, inNode, position);
}
}
}
}
for (int i = 0; i < extensions.length; i++) {
extensions[i].visit(s, TclCompletionParser.this, position);
}
}
return super.visit(s);
}
@Override
public boolean endvisit(Expression s) throws Exception {
if (s instanceof Block && s.sourceStart() <= position
&& s.sourceEnd() >= position) {
// We are in block, and no in node completion are done.
String[] keywords = checkKeywords("", MODULE);
ASTNode inNode = TclParseUtil.getScopeParent(module, s);
ASTNode nde = new CompletionOnKeywordOrFunction("", inNode, s,
keywords);
assistNodeParent = inNode;
throw new CompletionNodeFound(nde, null);
}
return super.endvisit(s);
}
};
protected CompletionVisitor createCompletionVisitor(int position) {
return new CompletionVisitor(position, this.getModule());
}
private void visitElements(ASTNode node, int position) {
if (!(node instanceof TclStatement)) {
CompletionVisitor visitor = createCompletionVisitor(position);
try {
node.traverse(visitor);
} catch (CompletionNodeFound e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
}
private boolean checkVariableWithoutDollarCompletion(TclStatement statement,
int position) {
// TODO: Add more compecated check.
Expression e = statement.getAt(0);
if (e instanceof SimpleReference) {
SimpleReference ref = (SimpleReference) e;
String name = ref.getName();
if (name.equals("set")) {
return true;
}
}
return false;
}
public String[] checkKeywords(String completionToken, int type) {
String[] keywords = TclKeywordsManager.getKeywords(type);
// TODO: Possible require cases.
if (type == MODULE || type == FUNCTION || type == NAMESPACE
|| type == EXEC_EXPRESSION) {
// Suppose we can handle all keywords.
String[] kw = new String[keywords.length];
for (int i = 0; i < keywords.length; ++i) {
kw[i] = keywords[i];
}
return kw;
}
return null;
}
public void setAssistNodeParent(ASTNode prevParent) {
this.assistNodeParent = prevParent;
}
}