Skip to content

Commit eaa73cf

Browse files
authored
Merge pull request #637 from tonihele/feature/issue-635
GLSL auto-completion
2 parents 82affd9 + 789dab4 commit eaa73cf

13 files changed

+575
-7
lines changed

jme3-glsl-highlighter/nbproject/project.xml

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@
5050
<specification-version>1.39.1.55</specification-version>
5151
</run-dependency>
5252
</dependency>
53+
<dependency>
54+
<code-name-base>org.netbeans.modules.editor.completion</code-name-base>
55+
<build-prerequisite/>
56+
<compile-dependency/>
57+
<run-dependency>
58+
<release-version>1</release-version>
59+
<specification-version>1.71.0.2</specification-version>
60+
</run-dependency>
61+
</dependency>
5362
<dependency>
5463
<code-name-base>org.netbeans.modules.editor.indent</code-name-base>
5564
<build-prerequisite/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright (c) 2003-2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.gde.glsl.highlighter.editor;
33+
34+
import com.jme3.gde.glsl.highlighter.editor.completion.FunctionCompletionItem;
35+
import com.jme3.gde.glsl.highlighter.editor.completion.KeywordCompletionItem;
36+
import com.jme3.gde.glsl.highlighter.editor.completion.TypeCompletionItem;
37+
import com.jme3.gde.glsl.highlighter.editor.completion.VariableCompletionItem;
38+
import com.jme3.gde.glsl.highlighter.lexer.GlslKeywordLibrary;
39+
import java.util.List;
40+
import javax.swing.text.BadLocationException;
41+
import javax.swing.text.Document;
42+
import javax.swing.text.Element;
43+
import javax.swing.text.JTextComponent;
44+
import javax.swing.text.StyledDocument;
45+
import org.netbeans.api.editor.mimelookup.MimeRegistration;
46+
import org.netbeans.spi.editor.completion.CompletionItem;
47+
import org.netbeans.spi.editor.completion.CompletionProvider;
48+
import org.netbeans.spi.editor.completion.CompletionResultSet;
49+
import org.netbeans.spi.editor.completion.CompletionTask;
50+
import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
51+
import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
52+
import org.openide.util.Exceptions;
53+
54+
@MimeRegistration(mimeType = "text/x-glsl", service = CompletionProvider.class)
55+
public class GlslCompletionProvider implements CompletionProvider {
56+
57+
@Override
58+
public CompletionTask createTask(int queryType, JTextComponent component) {
59+
if (queryType != CompletionProvider.COMPLETION_QUERY_TYPE) {
60+
return null;
61+
}
62+
63+
return new AsyncCompletionTask(new AsyncCompletionQuery() {
64+
@Override
65+
protected void query(CompletionResultSet completionResultSet,
66+
Document document, int caretOffset) {
67+
68+
String filter = "";
69+
int startOffset = caretOffset - 1;
70+
71+
try {
72+
final StyledDocument bDoc = (StyledDocument) document;
73+
final int lineStartOffset = getRowFirstNonWhite(bDoc, caretOffset);
74+
final char[] line = bDoc.getText(lineStartOffset, caretOffset - lineStartOffset).toCharArray();
75+
final int whiteOffset = indexOfWhite(line);
76+
filter = new String(line, whiteOffset + 1, line.length - whiteOffset - 1);
77+
if (whiteOffset > 0) {
78+
startOffset = lineStartOffset + whiteOffset + 1;
79+
} else {
80+
startOffset = lineStartOffset;
81+
}
82+
} catch (BadLocationException ex) {
83+
Exceptions.printStackTrace(ex);
84+
}
85+
86+
setCompletionItems(filter, completionResultSet, startOffset, caretOffset);
87+
88+
completionResultSet.finish();
89+
}
90+
91+
private void setCompletionItems(String filter, CompletionResultSet completionResultSet, int startOffset, int caretOffset) {
92+
List<GlslKeywordLibrary.Keyword> keywords = GlslKeywordLibrary.lookupAll(filter);
93+
completionResultSet.addAllItems(keywords.stream().map((keyword) -> createCompletionItem(keyword, startOffset, caretOffset)).toList());
94+
}
95+
96+
private CompletionItem createCompletionItem(GlslKeywordLibrary.Keyword keyword, int dotOffset, int caretOffset) {
97+
return switch (keyword.keywordType()) {
98+
case KEYWORD ->
99+
new KeywordCompletionItem(keyword.keyword(), dotOffset, caretOffset);
100+
case BUILTIN_FUNCTION ->
101+
new FunctionCompletionItem(keyword.keyword(), dotOffset, caretOffset);
102+
case BUILTIN_VARIABLE ->
103+
new VariableCompletionItem(keyword.keyword(), dotOffset, caretOffset);
104+
case BASIC_TYPE ->
105+
new TypeCompletionItem(keyword.keyword(), dotOffset, caretOffset);
106+
case UNFINISHED ->
107+
throw new AssertionError("Keyword type invalid");
108+
default ->
109+
throw new AssertionError("Keyword type not implemented");
110+
};
111+
}
112+
}, component);
113+
}
114+
115+
private static int getRowFirstNonWhite(StyledDocument doc, int offset)
116+
throws BadLocationException {
117+
Element lineElement = doc.getParagraphElement(offset);
118+
int start = lineElement.getStartOffset();
119+
while (start + 1 < lineElement.getEndOffset()) {
120+
try {
121+
if (doc.getText(start, 1).charAt(0) != ' ') {
122+
break;
123+
}
124+
} catch (BadLocationException ex) {
125+
throw (BadLocationException) new BadLocationException(
126+
"calling getText(" + start + ", " + (start + 1)
127+
+ ") on doc of length: " + doc.getLength(), start
128+
).initCause(ex);
129+
}
130+
start++;
131+
}
132+
return start;
133+
}
134+
135+
private static int indexOfWhite(char[] line) {
136+
int i = line.length;
137+
while (--i > -1) {
138+
final char c = line[i];
139+
if (Character.isWhitespace(c)) {
140+
return i;
141+
}
142+
}
143+
return -1;
144+
}
145+
146+
@Override
147+
public int getAutoQueryTypes(JTextComponent jtc, String string) {
148+
return 0;
149+
}
150+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2003-2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.gde.glsl.highlighter.editor.completion;
33+
34+
import java.awt.Color;
35+
import java.awt.Font;
36+
import java.awt.Graphics;
37+
import java.awt.event.KeyEvent;
38+
import javax.swing.ImageIcon;
39+
import javax.swing.text.BadLocationException;
40+
import javax.swing.text.JTextComponent;
41+
import javax.swing.text.StyledDocument;
42+
import org.netbeans.api.editor.completion.Completion;
43+
import org.netbeans.spi.editor.completion.CompletionItem;
44+
import org.netbeans.spi.editor.completion.CompletionTask;
45+
import org.netbeans.spi.editor.completion.support.CompletionUtilities;
46+
import org.openide.util.Exceptions;
47+
48+
public abstract class DefaultCompletionItem implements CompletionItem {
49+
50+
private final String keyword;
51+
private final int caretOffset;
52+
private final int dotOffset;
53+
54+
public DefaultCompletionItem(String keyword, int dotOffset, int caretOffset) {
55+
this.keyword = keyword;
56+
this.dotOffset = dotOffset;
57+
this.caretOffset = caretOffset;
58+
}
59+
60+
@Override
61+
public void defaultAction(JTextComponent component) {
62+
try {
63+
StyledDocument doc = (StyledDocument) component.getDocument();
64+
//Here we remove the characters starting at the start offset
65+
//and ending at the point where the caret is currently found:
66+
doc.remove(dotOffset, caretOffset - dotOffset);
67+
doc.insertString(dotOffset, keyword, null);
68+
Completion.get().hideAll();
69+
} catch (BadLocationException ex) {
70+
Exceptions.printStackTrace(ex);
71+
}
72+
}
73+
74+
@Override
75+
public void processKeyEvent(KeyEvent ke) {
76+
77+
}
78+
79+
@Override
80+
public int getPreferredWidth(Graphics graphics, Font font) {
81+
return CompletionUtilities.getPreferredWidth(keyword, null, graphics, font);
82+
}
83+
84+
@Override
85+
public void render(Graphics graphics, Font font, Color defaultColor,
86+
Color backgroundColor, int width, int height, boolean selected) {
87+
CompletionUtilities.renderHtml(getIcon(), keyword, null, graphics, font,
88+
(selected ? Color.white : null), width, height, selected);
89+
}
90+
91+
@Override
92+
public CompletionTask createDocumentationTask() {
93+
return null;
94+
}
95+
96+
@Override
97+
public CompletionTask createToolTipTask() {
98+
return null;
99+
}
100+
101+
@Override
102+
public boolean instantSubstitution(JTextComponent jtc) {
103+
return false;
104+
}
105+
106+
@Override
107+
public int getSortPriority() {
108+
return 0;
109+
}
110+
111+
@Override
112+
public CharSequence getSortText() {
113+
return keyword;
114+
}
115+
116+
@Override
117+
public CharSequence getInsertPrefix() {
118+
return keyword;
119+
}
120+
121+
protected abstract ImageIcon getIcon();
122+
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2003-2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.gde.glsl.highlighter.editor.completion;
33+
34+
import javax.swing.ImageIcon;
35+
import org.openide.util.ImageUtilities;
36+
37+
public class FunctionCompletionItem extends DefaultCompletionItem {
38+
39+
private static final ImageIcon fieldIcon
40+
= new ImageIcon(ImageUtilities.loadImage("com/jme3/gde/glsl/highlighter/editor/completion/method_16.png"));
41+
42+
public FunctionCompletionItem(String keyword, int dotOffset, int caretOffset) {
43+
super(keyword, dotOffset, caretOffset);
44+
}
45+
46+
@Override
47+
protected ImageIcon getIcon() {
48+
return fieldIcon;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2003-2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.gde.glsl.highlighter.editor.completion;
33+
34+
import javax.swing.ImageIcon;
35+
import org.openide.util.ImageUtilities;
36+
37+
public class KeywordCompletionItem extends DefaultCompletionItem {
38+
39+
private static final ImageIcon fieldIcon
40+
= new ImageIcon(ImageUtilities.loadImage("com/jme3/gde/glsl/highlighter/editor/completion/method_static_protected_16.png"));
41+
42+
public KeywordCompletionItem(String keyword, int dotOffset, int caretOffset) {
43+
super(keyword, dotOffset, caretOffset);
44+
}
45+
46+
@Override
47+
protected ImageIcon getIcon() {
48+
return fieldIcon;
49+
}
50+
}

0 commit comments

Comments
 (0)