Skip to content

Commit

Permalink
Merge pull request jMonkeyEngine#637 from tonihele/feature/issue-635
Browse files Browse the repository at this point in the history
GLSL auto-completion
  • Loading branch information
tonihele authored Dec 15, 2024
2 parents 82affd9 + 789dab4 commit eaa73cf
Show file tree
Hide file tree
Showing 13 changed files with 575 additions and 7 deletions.
9 changes: 9 additions & 0 deletions jme3-glsl-highlighter/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@
<specification-version>1.39.1.55</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.editor.completion</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>1.71.0.2</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.editor.indent</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2003-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.glsl.highlighter.editor;

import com.jme3.gde.glsl.highlighter.editor.completion.FunctionCompletionItem;
import com.jme3.gde.glsl.highlighter.editor.completion.KeywordCompletionItem;
import com.jme3.gde.glsl.highlighter.editor.completion.TypeCompletionItem;
import com.jme3.gde.glsl.highlighter.editor.completion.VariableCompletionItem;
import com.jme3.gde.glsl.highlighter.lexer.GlslKeywordLibrary;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyledDocument;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
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 org.openide.util.Exceptions;

@MimeRegistration(mimeType = "text/x-glsl", service = CompletionProvider.class)
public class GlslCompletionProvider implements CompletionProvider {

@Override
public CompletionTask createTask(int queryType, JTextComponent component) {
if (queryType != CompletionProvider.COMPLETION_QUERY_TYPE) {
return null;
}

return new AsyncCompletionTask(new AsyncCompletionQuery() {
@Override
protected void query(CompletionResultSet completionResultSet,
Document document, int caretOffset) {

String filter = "";
int startOffset = caretOffset - 1;

try {
final StyledDocument bDoc = (StyledDocument) document;
final int lineStartOffset = getRowFirstNonWhite(bDoc, caretOffset);
final char[] line = bDoc.getText(lineStartOffset, caretOffset - lineStartOffset).toCharArray();
final int whiteOffset = indexOfWhite(line);
filter = new String(line, whiteOffset + 1, line.length - whiteOffset - 1);
if (whiteOffset > 0) {
startOffset = lineStartOffset + whiteOffset + 1;
} else {
startOffset = lineStartOffset;
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}

setCompletionItems(filter, completionResultSet, startOffset, caretOffset);

completionResultSet.finish();
}

private void setCompletionItems(String filter, CompletionResultSet completionResultSet, int startOffset, int caretOffset) {
List<GlslKeywordLibrary.Keyword> keywords = GlslKeywordLibrary.lookupAll(filter);
completionResultSet.addAllItems(keywords.stream().map((keyword) -> createCompletionItem(keyword, startOffset, caretOffset)).toList());
}

private CompletionItem createCompletionItem(GlslKeywordLibrary.Keyword keyword, int dotOffset, int caretOffset) {
return switch (keyword.keywordType()) {
case KEYWORD ->
new KeywordCompletionItem(keyword.keyword(), dotOffset, caretOffset);
case BUILTIN_FUNCTION ->
new FunctionCompletionItem(keyword.keyword(), dotOffset, caretOffset);
case BUILTIN_VARIABLE ->
new VariableCompletionItem(keyword.keyword(), dotOffset, caretOffset);
case BASIC_TYPE ->
new TypeCompletionItem(keyword.keyword(), dotOffset, caretOffset);
case UNFINISHED ->
throw new AssertionError("Keyword type invalid");
default ->
throw new AssertionError("Keyword type not implemented");
};
}
}, component);
}

private static int getRowFirstNonWhite(StyledDocument doc, int offset)
throws BadLocationException {
Element lineElement = doc.getParagraphElement(offset);
int start = lineElement.getStartOffset();
while (start + 1 < lineElement.getEndOffset()) {
try {
if (doc.getText(start, 1).charAt(0) != ' ') {
break;
}
} catch (BadLocationException ex) {
throw (BadLocationException) new BadLocationException(
"calling getText(" + start + ", " + (start + 1)
+ ") on doc of length: " + doc.getLength(), start
).initCause(ex);
}
start++;
}
return start;
}

private static int indexOfWhite(char[] line) {
int i = line.length;
while (--i > -1) {
final char c = line[i];
if (Character.isWhitespace(c)) {
return i;
}
}
return -1;
}

@Override
public int getAutoQueryTypes(JTextComponent jtc, String string) {
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2003-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.glsl.highlighter.editor.completion;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyledDocument;
import org.netbeans.api.editor.completion.Completion;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionTask;
import org.netbeans.spi.editor.completion.support.CompletionUtilities;
import org.openide.util.Exceptions;

public abstract class DefaultCompletionItem implements CompletionItem {

private final String keyword;
private final int caretOffset;
private final int dotOffset;

public DefaultCompletionItem(String keyword, int dotOffset, int caretOffset) {
this.keyword = keyword;
this.dotOffset = dotOffset;
this.caretOffset = caretOffset;
}

@Override
public void defaultAction(JTextComponent component) {
try {
StyledDocument doc = (StyledDocument) component.getDocument();
//Here we remove the characters starting at the start offset
//and ending at the point where the caret is currently found:
doc.remove(dotOffset, caretOffset - dotOffset);
doc.insertString(dotOffset, keyword, null);
Completion.get().hideAll();
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}

@Override
public void processKeyEvent(KeyEvent ke) {

}

@Override
public int getPreferredWidth(Graphics graphics, Font font) {
return CompletionUtilities.getPreferredWidth(keyword, null, graphics, font);
}

@Override
public void render(Graphics graphics, Font font, Color defaultColor,
Color backgroundColor, int width, int height, boolean selected) {
CompletionUtilities.renderHtml(getIcon(), keyword, null, graphics, font,
(selected ? Color.white : null), width, height, selected);
}

@Override
public CompletionTask createDocumentationTask() {
return null;
}

@Override
public CompletionTask createToolTipTask() {
return null;
}

@Override
public boolean instantSubstitution(JTextComponent jtc) {
return false;
}

@Override
public int getSortPriority() {
return 0;
}

@Override
public CharSequence getSortText() {
return keyword;
}

@Override
public CharSequence getInsertPrefix() {
return keyword;
}

protected abstract ImageIcon getIcon();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2003-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.glsl.highlighter.editor.completion;

import javax.swing.ImageIcon;
import org.openide.util.ImageUtilities;

public class FunctionCompletionItem extends DefaultCompletionItem {

private static final ImageIcon fieldIcon
= new ImageIcon(ImageUtilities.loadImage("com/jme3/gde/glsl/highlighter/editor/completion/method_16.png"));

public FunctionCompletionItem(String keyword, int dotOffset, int caretOffset) {
super(keyword, dotOffset, caretOffset);
}

@Override
protected ImageIcon getIcon() {
return fieldIcon;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2003-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.gde.glsl.highlighter.editor.completion;

import javax.swing.ImageIcon;
import org.openide.util.ImageUtilities;

public class KeywordCompletionItem extends DefaultCompletionItem {

private static final ImageIcon fieldIcon
= new ImageIcon(ImageUtilities.loadImage("com/jme3/gde/glsl/highlighter/editor/completion/method_static_protected_16.png"));

public KeywordCompletionItem(String keyword, int dotOffset, int caretOffset) {
super(keyword, dotOffset, caretOffset);
}

@Override
protected ImageIcon getIcon() {
return fieldIcon;
}
}
Loading

0 comments on commit eaa73cf

Please sign in to comment.