package net.java.nboglpack.glsleditor;
import java.util.Map.Entry;
import java.util.Set;
import org.netbeans.api.editor.completion.Completion;
import org.openide.ErrorManager;
import org.netbeans.editor.BaseDocument;
import org.netbeans.spi.editor.completion.CompletionDocumentation;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionProvider;
import org.netbeans.spi.editor.completion.CompletionResultSet;
import org.netbeans.spi.editor.completion.CompletionTask;
import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
import javax.swing.Action;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import net.java.nboglpack.glsleditor.dataobject.GlslFragmentShaderDataLoader;
import net.java.nboglpack.glsleditor.dataobject.GlslGeometryShaderDataLoader;
import net.java.nboglpack.glsleditor.dataobject.GlslVertexShaderDataLoader;
import net.java.nboglpack.glsleditor.glsl.Glsl;
import net.java.nboglpack.glsleditor.vocabulary.GLSLElementDescriptor;
import org.netbeans.spi.editor.completion.support.CompletionUtilities;
/**
* Completion provider for the OpenGL Shading Language editor.
*
* @author Mathias Henze
* @author Michael Bien
*/
public class GlslCompletionProvider implements CompletionProvider {
private static final ErrorManager LOGGER = ErrorManager.getDefault().getInstance(GlslCompletionProvider.class.getName());
private final String mimeType;
private GlslCompletionProvider(String mimeType) {
this.mimeType = mimeType;
}
public CompletionTask createTask(int queryType, JTextComponent component) {
return new AsyncCompletionTask(new GlslCompletionQuery(mimeType), component);
}
public int getAutoQueryTypes(JTextComponent component, String typedText) {
return 0;
}
public static GlslCompletionProvider createVSCompletionProvider(){
return new GlslCompletionProvider(GlslVertexShaderDataLoader.REQUIRED_MIME);
}
public static GlslCompletionProvider createGSCompletionProvider(){
return new GlslCompletionProvider(GlslGeometryShaderDataLoader.REQUIRED_MIME);
}
public static GlslCompletionProvider createFSCompletionProvider(){
return new GlslCompletionProvider(GlslFragmentShaderDataLoader.REQUIRED_MIME);
}
private static class GlslCompletionQuery extends AsyncCompletionQuery {
private GlslVocabularyManager vocabulary;
public GlslCompletionQuery(String mimetype) {
vocabulary = GlslVocabularyManager.getInstance(mimetype);
}
protected void query(CompletionResultSet completionResultSet, Document document, int pos) {
fillResultset(completionResultSet, document, pos);
completionResultSet.finish();
}
private void fillResultset(CompletionResultSet completionResultSet, Document doc, int pos) {
Element paragraph = ((BaseDocument) doc).getParagraphElement(pos);
String prefix;
try {
prefix = doc.getText(paragraph.getStartOffset(), pos - paragraph.getStartOffset());
// daf�r sorgen, dass wir in den meisten f�llen einen korrekten prefix haben
// TODO: besser machen, ist ne hau-ruck-methode
// problem: bei leerzeichen in strings werden auch dort funktoren als autocomplete angeboten...
prefix = prefix.replaceAll(".*?([\\w-\"]*)$", "$1");
} catch (BadLocationException e) {
LOGGER.notify(e);
prefix = "";
}
// add user declared functions first
synchronized(Glsl.declaredFunctions) {
Set<Entry<String, GLSLElementDescriptor>> entrySet = Glsl.declaredFunctions.entrySet();
for (Entry<String, GLSLElementDescriptor> entry : entrySet) {
String name = entry.getKey();
GLSLElementDescriptor desc = entry.getValue();
if (name.regionMatches(true, 0, prefix, 0, prefix.length())) {
completionResultSet.addItem(new GlslCompletionItem(name.substring(0, name.indexOf('(')), desc, prefix, pos));
}
}
}
// add core GLSL completion items
Iterator it = vocabulary.getKeys().iterator();
while (it.hasNext()) {
String name = (String) it.next();
if (name.regionMatches(true, 0, prefix, 0, prefix.length())) {
GLSLElementDescriptor[] elements = vocabulary.getDesc(name);
if (elements != null) {
for (GLSLElementDescriptor element : elements)
completionResultSet.addItem(new GlslCompletionItem(name, element, prefix, pos));
}
}
}
}
}
private static class GlslCompletionItem implements CompletionItem {
private String key;
private GLSLElementDescriptor content;
private int carretPosition = 0;
private String prefix;
private String leftText;
private String rightText;
private String ARGUMENTS_COLOR = "<font color=#b28b00>";
private String BUILD_IN_VAR_COLOR = "<font color=#ce7b00>";
private String KEYWORD_COLOR = "<font color=#000099>";
private int priority;
public GlslCompletionItem(String key, GLSLElementDescriptor content, String prefix, int carretPosition) {
this.key = key;
this.content = content;
this.prefix = prefix;
this.carretPosition = carretPosition;
leftText = createLeftText();
rightText = content.type;
// low prority is first in completion list
switch (content.category) {
case TYPE:
priority = 10;
break;
case JUMP:
priority = 9;
break;
case SELECTION:
priority = 8;
break;
case ITERATION:
priority = 7;
break;
case KEYWORD:
priority = 6;
break;
case QUALIFIER:
priority = 5;
break;
case BUILD_IN_FUNC:
priority = 4;
break;
case BUILD_IN_VAR:
priority = 3;
break;
}
}
private String createLeftText() {
StringBuilder text = new StringBuilder();
switch (content.category) {
case TYPE:
case JUMP:
case SELECTION:
case ITERATION:
case KEYWORD:
case QUALIFIER:
text.append(KEYWORD_COLOR);
break;
case BUILD_IN_VAR:
text.append(BUILD_IN_VAR_COLOR);
break;
}
text.append("<b>");
text.append(key);
text.append("</b></font>");
if (content.arguments != null) {
text.append(ARGUMENTS_COLOR);
text.append(content.arguments);
text.append("</font>");
}
return text.toString();
}
public void defaultAction(JTextComponent component) {
Completion.get().hideAll();
// replace prefix with key
try {
component.getDocument().remove(carretPosition-prefix.length(), prefix.length());
component.getDocument().insertString(carretPosition-prefix.length(), key, null);
} catch (BadLocationException e) {
LOGGER.notify(e);
}
}
public void processKeyEvent(KeyEvent evt) {
// TODO: if "." then Completion.get().showCompletion()
}
public int getPreferredWidth(Graphics g, Font defaultFont) {
return CompletionUtilities.getPreferredWidth(leftText, rightText, g, defaultFont);
}
public void render(Graphics g, Font defaultFont, Color defaultColor, Color backgroundColor, int width, int height, boolean selected) {
CompletionUtilities.renderHtml(null, leftText, rightText, g, defaultFont, defaultColor, width, height, selected);
}
public CompletionTask createDocumentationTask() {
if (content.doc == null) {
return null;
}
return new AsyncCompletionTask(new AsyncCompletionQuery() {
private GlslDocItem item = new GlslDocItem(key, content);
protected void query(CompletionResultSet completionResultSet, Document document, int i) {
completionResultSet.setDocumentation(item);
completionResultSet.finish();
}
});
}
public CompletionTask createToolTipTask() {
return null;
}
public boolean instantSubstitution(JTextComponent component) {
defaultAction(component);
return true;
}
public int getSortPriority() {
return priority;
}
public CharSequence getSortText() {
return key;
}
public CharSequence getInsertPrefix() {
return prefix;
}
}
private static class GlslDocItem implements CompletionDocumentation {
private String text;
public GlslDocItem(String item, GLSLElementDescriptor content) {
StringBuilder sb = new StringBuilder();
sb.append("<code>");
if(content.type != null) {
sb.append(content.type);
sb.append(" ");
}
sb.append("<b>");
sb.append(item);
sb.append("</b>");
if(content.arguments != null) {
sb.append(content.arguments);
}
sb.append("</code><p>");
sb.append(content.doc);
sb.append("</p>");
text = sb.toString();
}
public String getText() {
return text;
}
public URL getURL() {
return null;
}
public CompletionDocumentation resolveLink(String link) {
return null;
}
public Action getGotoSourceAction() {
return null;
}
}
}