package org.nodeclipse.ui.contentassist; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.CompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.swt.graphics.Image; //import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.nodeclipse.ui.Activator; import org.nodeclipse.ui.preferences.PreferenceConstants; import org.nodeclipse.ui.util.Constants; import org.nodeclipse.ui.util.NodeclipseConsole; /** * Code completion (Content Assist) that works in TextEditor based Editor * proposals are sorted inside Model.</br> * TODO make this class JSON independent - remove JSON processing in addCompletionProposalFromCompletionJson() * @see method computeCompletionProposals(ITextViewer viewer, int offset) * @see JSDTProposalComputer wrapper for JSDT, that calls computeCompletionProposals() * * @author Lamb Gao * @author Paul Verest * * see grepcode.com/file/repository.grepcode.com/java/eclipse.org/4.3/org.eclipse.ui.workbench/texteditor/3.8.100/org/eclipse/ui/texteditor/HippieProposalProcessor.java * */ public class NodeContentAssistant implements IContentAssistProcessor { private static final ICompletionProposal[] NO_PROPOSALS= new ICompletionProposal[0]; private static final IContextInformation[] NO_CONTEXTS= new IContextInformation[0]; public static final Image MODULE = Activator.getImageDescriptor(Constants.MODULE_ICON).createImage(); public static final Image METHOD = Activator.getImageDescriptor(Constants.METHOD_ICON).createImage(); public static final Image CLASS = Activator.getImageDescriptor(Constants.CLASS_ICON).createImage(); public static final Image PROPERTY = Activator.getImageDescriptor(Constants.PROPERTY_ICON).createImage(); public static final Image UNKNOWN_BLUE = Activator.getImageDescriptor(Constants.UNKNOWN_BLUE_ICON).createImage(); private boolean indexFilesInitialized = false; private IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); boolean useNodejsBaseModuleDefinitions = preferenceStore.getBoolean(PreferenceConstants.USE_NODEJS_BASE_MODULE_DEFINITIONS);//@since 0.12 boolean useOrionIndexFiles = preferenceStore.getBoolean(PreferenceConstants.USE_ORION_INDEX_FILES);//@since 0.12 boolean useCompletionJson = preferenceStore.getBoolean(PreferenceConstants.USE_COMPLETIONS_JSON);//@since 0.12 private Model model = null ; //new Model(); public String getInputString(IDocument doc, int offset) { StringBuffer buf = new StringBuffer(); while (true) { try { char charOffset = doc.getChar(--offset); if (Character.isWhitespace(charOffset)) break; buf.append(charOffset); } catch (BadLocationException e) { break; } } return buf.reverse().toString(); } // the very first way // private void addCompletionProposalFromNodejsSources( // List<CompletionProposal> list, String input, int offset) { // try { // for (int i = 0; i < ContentFromSources.METHODS.length(); i++) { // JSONObject method = (JSONObject) ContentFromSources.METHODS.get(i); // String trigger = method.getString("textRaw"); // if (trigger != null && trigger.startsWith(input)) { // int length = input.length(); // list.add(new CompletionProposal(trigger, offset - length, length, trigger.length(), // METHOD, null, null, method.getString("desc") )); // //method.getString("name") // } // } // } catch (JSONException e) { // //e.printStackTrace(); // NodeclipseConsole.write(e.getLocalizedMessage()+"\n"); // } // } // // /** old first way, see addCompletionProposalFromModel() below (that most of code moved to ContentFromSources.populateModel() ) // */ // @Deprecated // private void addCompletionProposalFromNodejsSources( // List<CompletionProposal> list, String input, int offset) { // int length = input.length(); // // modules30: timers(m8), module, addons, util(m13), Events(c1), domain(m1)(c1), buffer(c2), stream(c4), crypto(m18)(c7), // // tls_(ssl)(m5)(c4), stringdecoder(c1), fs(m67)(c4), path(m7), net(m10)(c2), dgram(m1)(c1), dns(m10), http(m4)(c4), https(m3)(c2), // // url(m3), querystring(m2), punycode(m4), readline(m1)(c1), repl(m1), vm(m5)(c1), child_process(m4)(c1), assert(m11), tty(m2)(c2), zlib(m14)(c8), os(m13), cluster(m3)(c1) // try { // JSONObject NodejsContext = ContentFromSources.defaultInstance.NodejsContext; // JSONArray modules = NodejsContext.getJSONArray("modules"); // log("modules"+modules.length()+" "); // for (int i = 0; i < modules.length(); i++) { // JSONObject module = (JSONObject) modules.get(i); // String moduleName = module.getString("name"); // debug( ", "+moduleName); // // if (module.has("methods")) { // JSONArray methods = module.getJSONArray("methods"); // debug("(m"+methods.length()+")"); // for (int j = 0; j < methods.length(); j++) { // JSONObject method = (JSONObject) methods.get(j); // // example: "textRaw": "http.createServer([requestListener])","type": "method","name": "createServer", // String trigger = method.getString("textRaw"); // if (trigger != null && trigger.startsWith(input)) { // String name = method.getString("name"); // String desc = formatedName(name,trigger)+method.getString("desc"); // list.add(new CompletionProposal(trigger, offset - length, length, trigger.length(), // METHOD, trigger, null, desc)); // } // } // } // // if (module.has("classes")){ // JSONArray classes = module.getJSONArray("classes"); // debug("(c"+classes.length()+")"); // for (int j = 0; j < classes.length(); j++) { // JSONObject clazz = (JSONObject) classes.get(j); // // example: "textRaw": "Class: Domain","type": "class","name": "Domain" // String trigger = clazz.getString("name"); // if (!trigger.startsWith(moduleName)) { // trigger=moduleName+'.'+trigger; // } // if (trigger != null && trigger.startsWith(input)) { // //String name = clazz.getString("name"); // String desc = formatedName(trigger,clazz.getString("textRaw"))+clazz.getString("desc"); // list.add(new CompletionProposal(trigger, offset - length, length, trigger.length(), // CLASS, trigger, null, desc )); // } // } // } // // } // } catch (JSONException e) { // log(e.getLocalizedMessage()+"\n"+e); // } // } // private String formatedName(String name) { // return "<b>"+name+"</b><br/>"; // } // private String formatedName(String name, String trigger) { // return formatedName(name)+"<code>"+trigger+"</code><br/>"; // } private void addCompletionProposalFromModel(List<CompletionProposal> list, String input, int offset) { int length = input.length(); //TODO create Model here and pass it as parameter if (useNodejsBaseModuleDefinitions){ model = ContentFromSources.getDefaultInstances().model; }else{ model = new Model(); } if (useOrionIndexFiles && !indexFilesInitialized){ //@since 0.12 context from Orion IndexFiles ContentFromOrionIndexFiles.initModel(model); indexFilesInitialized = true; } if (model==null){ log("Model is empty! (There should have been initialization error)"); } for(Entry entry: model.findMatchingEntries(input)){ String trigger = entry.trigger; String desc = entry.desc; //Image image = (entry.type == EntryType.clazz) ? CLASS : METHOD; Image image = null; switch (entry.type){ case module: image = MODULE; break; case method: image = METHOD; break; case clazz: image = CLASS; break; case property: image = PROPERTY; break; case unknown: image = UNKNOWN_BLUE; } list.add(new CompletionProposal(trigger, offset - length, length, trigger.length(), image, trigger, null, desc)); } } public void addCompletionProposalFromCompletionJson( List<CompletionProposal> list, String input, int offset) { //List<CompletionProposal> list = new ArrayList<CompletionProposal>(); int length = input.length(); try { for (int i = 0; i < ContentFromCompletionsJson.COMPLETIONS.length(); i++) { JSONObject method = (JSONObject) ContentFromCompletionsJson.COMPLETIONS.get(i); String trigger = method.getString("trigger"); if (trigger != null && trigger.startsWith(input)) { list.add(new CompletionProposal(trigger, offset - length, length, trigger.length(), //METHOD null, null, null, null)); } } } catch (JSONException e) { log(e.getLocalizedMessage()+"\n"); } //return list; } // Entry point for JSDT API call @Override public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { IDocument doc = viewer.getDocument(); String inputString = getInputString(doc, offset); List<CompletionProposal> list = new ArrayList<CompletionProposal>(); //list = getCompletionProposalFromCompletionJson(inputString , offset); //addCompletionProposalFromNodejsSources(list, inputString , offset); addCompletionProposalFromModel(list, inputString , offset); if (useCompletionJson && ContentFromCompletionsJson.COMPLETIONS!=null){ addCompletionProposalFromCompletionJson(list, inputString , offset); } return (CompletionProposal[]) list.toArray(new CompletionProposal[list.size()]); } @Override public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { return NO_CONTEXTS; } @Override public char[] getCompletionProposalAutoActivationCharacters() { // TODO Preferences // may be null return ".abcdefghijklmnopqrstuvwxyz".toCharArray(); } @Override public char[] getContextInformationAutoActivationCharacters() { return null; } @Override public String getErrorMessage() { return null; } @Override public IContextInformationValidator getContextInformationValidator() { return null; } // private static void debug(String s){ // //NodeclipseConsole.write(s); // } private static void log(String s){ NodeclipseConsole.write(s); } }