/* * Copyright 2014 TWO SIGMA OPEN SOURCE, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.twosigma.beaker.groovy.autocomplete; import com.twosigma.beaker.autocomplete.AutocompleteCandidate; import com.twosigma.beaker.autocomplete.AutocompleteRegistry; import com.twosigma.beaker.autocomplete.AutocompleteResult; import com.twosigma.beaker.autocomplete.ClassUtils; import com.twosigma.beaker.groovy.autocomplete.GroovyParser.ClassNameExpressionContext; import com.twosigma.beaker.groovy.autocomplete.GroovyParser.CommandExpressionStatementContext; import com.twosigma.beaker.groovy.autocomplete.GroovyParser.CompilationUnitContext; import com.twosigma.beaker.groovy.autocomplete.GroovyParser.PathExpressionContext; import com.twosigma.beaker.groovy.autocomplete.GroovyParser.StatementContext; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.tree.ErrorNode; import org.antlr.v4.runtime.tree.ParseTree; public class GroovyNodeCompletion extends GroovyAbstractListener { private AutocompleteRegistry registry; private int cursor; @SuppressWarnings("unused") private String text; private ClassUtils classUtils; public GroovyNodeCompletion(String t, int c, AutocompleteRegistry r, ClassUtils cu) { cursor = c; text = t; registry = r; classUtils = cu; } @Override public void exitClassNameExpression(ClassNameExpressionContext ctx) { if(ctx.getStart().getStartIndex() < cursor && ctx.getStop().getStopIndex()+1 >= cursor) { if(ctx.getText().contains(".")) { addQuery(classUtils.expandExpression(ctx.getText(), registry, classUtils.DO_STATIC), AutocompleteResult.getStartIndex(ctx)); // complete with standard groovy extension functions AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, ctx.getText().substring(ctx.getText().lastIndexOf(".")+1)); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } } } @Override public void exitPathExpression(PathExpressionContext ctx) { if(ctx.getStart().getStartIndex() < cursor && ctx.getStop().getStopIndex()+1 >= cursor) { // is this the start of a compilation unit? // it might be package if(ctx.getParent().getParent()!=null && ctx.getParent().getParent() instanceof StatementContext && ctx.getParent().getParent().getChildCount()==1 && ctx.getParent().getParent().getParent()!=null && ctx.getParent().getParent().getParent() instanceof CompilationUnitContext && ctx.getParent().getParent().getParent().getChild(0).equals(ctx.getParent().getParent())) { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.INITIAL, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } // is this leftmost part of an statement? // it might be import if(ctx.getParent().getChild(0).equals(ctx) && ctx.getParent().getParent()!=null && ctx.getParent().getParent() instanceof StatementContext && ctx.getParent().getParent().getChild(0).equals(ctx.getParent())) { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } // check out for implements and/or extends CommandExpressionStatementContext st = (CommandExpressionStatementContext) findParentNode(ctx,CommandExpressionStatementContext.class); if(st!=null && st.getParent()!=null) { ParseTree left = findLeftSibling(st); if(left!=null && left.getText().trim().equals("class")) { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.CLASSLEVEL, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } } // try to expand this as a path expression if(ctx.getText().contains(".")) { addQuery(classUtils.expandExpression(ctx.getText(), registry, classUtils.DO_NON_STATIC), AutocompleteResult.getStartIndex(ctx)); // complete with standard groovy extension functions AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, ctx.getText().substring(ctx.getText().lastIndexOf(".")+1)); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, ctx.getText()); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } } } @Override public void visitErrorNode(ErrorNode arg0) { if(arg0.getSymbol().getStartIndex() < cursor && arg0.getSymbol().getStopIndex()+1 >= cursor) { //System.out.println("ERR: "+arg0.getSymbol().getStartIndex()+" "+arg0.getSymbol().getStopIndex()+" "+arg0.getSymbol().getText()); ParseTree cuc = arg0.getParent(); if(cuc.getChild(0).equals(arg0)) { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.INITIAL, arg0.getText()); addQuery(c, 0); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, arg0.getText()); addQuery(c, 0); } if(cuc instanceof StatementContext && ((StatementContext) cuc).getStop().getStopIndex()+1 == cursor) { if(cuc.getText().contains(".")) { addQuery(classUtils.expandExpression(cuc.getText(), registry, classUtils.DO_ALL), 0); // complete with standard groovy extension functions AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, cuc.getText().substring(cuc.getText().lastIndexOf(".")+1)); addQuery(c,0); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, cuc.getText()); addQuery(c,0); c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, cuc.getText()); addQuery(c,0); } } } } private RuleContext findParentNode(RuleContext ctx, Class<?> classtype) { RuleContext p = ctx.getParent(); while(p!=null) { if(p.getClass().equals(classtype)) { return p; } p = p.getParent(); } return null; } private ParseTree findLeftSibling(RuleContext ctx) { RuleContext p = ctx.getParent(); if(p!=null) { for(int i=0; i<p.getChildCount(); i++) { if(p.getChild(i).equals(ctx)) { if(i>0) return p.getChild(i-1); break; } } } return null; } @Override public void exitCompilationUnit(CompilationUnitContext ctx) { if(ctx.getStop().getStopIndex()+1 == cursor) { if(ctx.getChildCount()>2) { String t = ctx.getChild(ctx.getChildCount()-2).getText(); if(t.endsWith("\n")) { String txt = ctx.getChild(ctx.getChildCount()-1).getText(); if(txt.contains(".")) { addQuery(classUtils.expandExpression(txt, registry, classUtils.DO_ALL), AutocompleteResult.getStartIndex(ctx)); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, txt); addQuery(c, AutocompleteResult.getStartIndex(ctx)); c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, txt); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } } else { for (int i=ctx.getChildCount()-1; i>0; i--) { if(!ctx.getChild(i).getText().isEmpty() && !ctx.getChild(i).getText().equals("<EOF>")) { String txt = ctx.getChild(i).getText(); if(txt.contains(".")) { addQuery(classUtils.expandExpression(txt, registry, classUtils.DO_ALL), AutocompleteResult.getStartIndex(ctx)); } else { AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, txt); addQuery(c, AutocompleteResult.getStartIndex(ctx)); } break; } } } } } } }