/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package ca.weblite.netbeans.mirah.cc; import ca.weblite.netbeans.mirah.lexer.MirahParser; import ca.weblite.netbeans.mirah.lexer.MirahTokenId; import java.util.ArrayList; import java.util.List; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import mirah.impl.Tokens; import mirah.lang.ast.FieldDeclaration; import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.editor.BaseDocument; import org.netbeans.modules.parsing.api.Snapshot; import org.netbeans.modules.parsing.api.Source; import org.netbeans.modules.parsing.spi.ParseException; import org.netbeans.spi.editor.completion.CompletionResultSet; import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery; import org.openide.util.Exceptions; /** * * @author shannah */ public class PropertyCompletionQuery extends AsyncCompletionQuery{ final int tAtPos; private String filter; List<FieldDeclaration> matches = new ArrayList<FieldDeclaration>(); public PropertyCompletionQuery(int tAtPos){ this.tAtPos = tAtPos; } /* @Override protected boolean canFilter(JTextComponent component) { int currentPos = component.getCaretPosition(); if ( currentPos <= tAtPos){ return false; } try { char c = component.getText(currentPos, 1).charAt(0); switch ( c){ case '.': case ':': case '(': case ';': return false; } } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } try { filter = component.getText(tAtPos, currentPos-tAtPos).trim(); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } return filter != null; } @Override protected void filter(CompletionResultSet resultSet) { for ( FieldDeclaration field : matches){ if ( field.name().identifier().toLowerCase().indexOf(filter.toLowerCase()) == 0 ){ resultSet.addItem(new MirahPropertyCompletionItem(field, tAtPos, filter.length())); } } resultSet.finish(); } */ @Override protected void query(CompletionResultSet crs, Document doc, int caretOffset) { BaseDocument bdoc = (BaseDocument)doc; MirahParser.DocumentDebugger dbg = MirahParser.getDocumentDebugger(doc); if ( dbg != null ){ try { int p = caretOffset-1; if ( p < 0 ){ return; } String lastChar = doc.getText(p, 1); TokenSequence<MirahTokenId> toks = mirahTokenSequence(doc, caretOffset, true); MirahTokenId tInstanceVar = MirahTokenId.get(Tokens.tInstVar.ordinal()); MirahTokenId tClassVar = MirahTokenId.get(Tokens.tClassVar.ordinal()); MirahTokenId tDot = MirahTokenId.get(Tokens.tDot.ordinal()); MirahTokenId tAt = MirahTokenId.get(Tokens.tAt.ordinal()); MirahTokenId tId = MirahTokenId.get(Tokens.tIDENTIFIER.ordinal()); MirahTokenId tNL = MirahTokenId.get(Tokens.tNL.ordinal()); MirahTokenId tWS = MirahTokenId.WHITESPACE; int bol = MirahCodeCompleter.getBeginningOfLine(doc, caretOffset); int eol = MirahCodeCompleter.getEndOfLine(doc, caretOffset); Token<MirahTokenId> foundToken = null; int tokenStart = -1; int tokenLen = -1; filter = null; boolean isClassVar = false; while ( toks.offset() >= tAtPos ){ Token<MirahTokenId> tok = toks.token(); if ( tok.id() == tInstanceVar ){ foundToken = tok; tokenStart = toks.offset(); tokenLen = tok.length(); filter = doc.getText(tokenStart+1, caretOffset-tokenStart-1); break; } if ( tok.id() == tClassVar ){ isClassVar = true; foundToken = tok; tokenStart = toks.offset(); tokenLen = tok.length(); filter = doc.getText(tokenStart+2, caretOffset-tokenStart-2); break; } if ( tok.id() != tId && tok.id() != tAt ){ cancel(crs); return; } if ( !toks.movePrevious() ){ cancel(crs); return; } if ( tok.id() == tAt ){ foundToken = tok; tokenStart = tAtPos+1; // Not sure why we need to do +1. May be a bug in lexer tokenLen = 1; if ( "@".equals(doc.getText(tokenStart-1,1)) ){ isClassVar = true; tokenStart--; tokenLen = 2; } break; } } if ( foundToken == null ){ cancel(crs); return; } FieldDeclaration[] fields = MirahCodeCompleter.findFields(dbg, tAtPos, isClassVar); if ( fields.length == 0 ){ Source src = Source.create(doc); MirahParser parser = new MirahParser(); try { Snapshot snapshot = src.createSnapshot(); String text = snapshot.getText().toString(); StringBuilder sb = new StringBuilder(); sb.append(text.substring(0, tokenStart)); for ( int i=tokenStart; i<eol; i++){ sb.append(' '); } sb.append(text.substring(eol)); parser.reparse(snapshot, sb.toString()); } catch (ParseException ex){ Exceptions.printStackTrace(ex); } dbg = MirahParser.getDocumentDebugger(doc); //printNodes(dbg.compiler.compiler(), rightEdgeFinal); fields = MirahCodeCompleter.findFields(dbg, tAtPos, isClassVar); } if ( fields.length == 0 ){ cancel(crs); } else { matches.clear(); for ( FieldDeclaration dec : fields ){ if ( filter == null || dec.name().identifier().startsWith(filter)){ crs.addItem(new MirahPropertyCompletionItem(dec, tokenStart, tokenLen, isClassVar)); } matches.add(dec); } crs.finish(); } return; } catch ( BadLocationException ble){ cancel(crs); } } else { cancel(crs); } } private void cancel(CompletionResultSet crs){ crs.finish(); } private static TokenSequence<MirahTokenId> mirahTokenSequence(Document doc, int caretOffset, boolean backwardBias) { BaseDocument bd = (BaseDocument)doc; bd.readLock(); try { TokenHierarchy<?> hi = TokenHierarchy.get(doc); List<TokenSequence<?>> tsList = hi.embeddedTokenSequences(caretOffset, backwardBias); // Go from inner to outer TSes for (int i = tsList.size() - 1; i >= 0; i--) { TokenSequence<?> ts = tsList.get(i); if (ts.languagePath().innerLanguage() == MirahTokenId.getLanguage()) { TokenSequence<MirahTokenId> javaInnerTS = (TokenSequence<MirahTokenId>) ts; //bd.readUnlock(); return javaInnerTS; } } return null; } finally { bd.readUnlock(); } } }