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);
}
}