Skip to content

Commit d90c367

Browse files
hadimctrueden
authored andcommitted
Add autocompletion framework
The default autocompletion works by inspecting attributes/methods of an object. Signed-off-by: Curtis Rueden <ctrueden@wisc.edu>
1 parent 743deaf commit d90c367

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.script;
33+
34+
import java.lang.reflect.Field;
35+
import java.util.ArrayList;
36+
import java.util.Arrays;
37+
import java.util.Collections;
38+
import java.util.Comparator;
39+
import java.util.List;
40+
import java.util.stream.Collectors;
41+
42+
import javax.script.Bindings;
43+
import javax.script.ScriptContext;
44+
import javax.script.ScriptEngine;
45+
46+
/**
47+
* Abstract base class for {@link AutoCompleter} implementations.
48+
*
49+
* @author Hadrien Mary
50+
*/
51+
public abstract class AbstractAutoCompleter implements AutoCompleter {
52+
53+
@SuppressWarnings("unused")
54+
private ScriptLanguage scriptLanguage;
55+
56+
public AbstractAutoCompleter(final ScriptLanguage scriptLanguage) {
57+
this.scriptLanguage = scriptLanguage;
58+
}
59+
60+
@Override
61+
public AutoCompletionResult autocomplete(final String code, final int index,
62+
final ScriptEngine engine)
63+
{
64+
65+
final List<String> matches = new ArrayList<>();
66+
final int startIndex = 0;
67+
68+
if (code.endsWith(".")) {
69+
// Autocompletion with all the attributes of the object
70+
matches.addAll(engineAttributesCompleter(code, index, engine));
71+
72+
}
73+
else if (code.contains(".")) {
74+
final List<String> codeList = Arrays.asList(code.split("\\."));
75+
final String objectString = codeList.get(codeList.size() - 2);
76+
final String fieldBeginWith = codeList.get(codeList.size() - 1);
77+
matches.addAll(engineAttributesCompleter(objectString + ".",
78+
fieldBeginWith, index, engine));
79+
80+
}
81+
else {
82+
// Autocompletion with variables in the engine scope
83+
matches.addAll(engineVariablesCompleter(code, index, engine));
84+
}
85+
86+
// Remove duplicates
87+
matches.stream().distinct().collect(Collectors.toList());
88+
89+
// Sort alphabetically, ignoring case
90+
Collections.sort(matches, new Comparator<Object>() {
91+
92+
@Override
93+
public int compare(final Object o1, final Object o2) {
94+
final String s1 = (String) o1;
95+
final String s2 = (String) o2;
96+
return s1.toLowerCase().compareTo(s2.toLowerCase());
97+
}
98+
});
99+
100+
// Return results. For now we ignore index and startIndex.
101+
return new AutoCompletionResult(matches, startIndex);
102+
}
103+
104+
private List<String> engineVariablesCompleter(final String code,
105+
@SuppressWarnings("unused") final int index, final ScriptEngine engine)
106+
{
107+
final List<String> matches = new ArrayList<>();
108+
109+
final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
110+
111+
for (final String key : bindings.keySet()) {
112+
if (key.toLowerCase().startsWith(code.toLowerCase())) {
113+
matches.add(key);
114+
}
115+
}
116+
return matches;
117+
118+
}
119+
120+
private List<String> engineAttributesCompleter(final String objectString,
121+
final int index, final ScriptEngine engine)
122+
{
123+
return engineAttributesCompleter(objectString, "", index, engine);
124+
}
125+
126+
private List<String> engineAttributesCompleter(final String objectString,
127+
final String fieldBeginWith, @SuppressWarnings("unused") final int index,
128+
final ScriptEngine engine)
129+
{
130+
final List<String> matches = new ArrayList<>();
131+
132+
final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
133+
134+
for (final String key : bindings.keySet()) {
135+
if (objectString.endsWith(key + ".")) {
136+
final Object obj = bindings.get(key);
137+
for (final Field field : obj.getClass().getDeclaredFields()) {
138+
if (field.getName().toLowerCase().startsWith(fieldBeginWith
139+
.toLowerCase()))
140+
{
141+
matches.add(objectString + field.getName());
142+
}
143+
}
144+
}
145+
}
146+
147+
return matches;
148+
}
149+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
package org.scijava.script;
32+
33+
import javax.script.ScriptEngine;
34+
35+
/**
36+
* Helper for generating autocomplete suggestions for code fragments.
37+
*
38+
* @author Hadrien Mary
39+
*/
40+
public interface AutoCompleter {
41+
42+
default AutoCompletionResult autocomplete(final String code,
43+
final ScriptEngine engine)
44+
{
45+
return autocomplete(code, 0, engine);
46+
}
47+
48+
AutoCompletionResult autocomplete(String code, int startIndex,
49+
ScriptEngine engine);
50+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
7+
package org.scijava.script;
8+
9+
import java.util.List;
10+
11+
/**
12+
* Data structure housing suggestions given by {@link AutoCompleter} helpers.
13+
*
14+
* @author Hadrien Mary
15+
*/
16+
public class AutoCompletionResult {
17+
18+
private List<String> matches;
19+
private int startIndex;
20+
21+
public AutoCompletionResult(final List<String> matches) {
22+
this(matches, 0);
23+
}
24+
25+
public AutoCompletionResult(final List<String> matches,
26+
final int startIndex)
27+
{
28+
this.matches = matches;
29+
this.startIndex = startIndex;
30+
}
31+
32+
public int getStartIndex() {
33+
return startIndex;
34+
}
35+
36+
public List<String> getMatches() {
37+
return matches;
38+
}
39+
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.script;
33+
34+
/**
35+
* Default implementation of {@link AutoCompleter}.
36+
*
37+
* @author Hadrien Mary
38+
*/
39+
public class DefaultAutoCompleter extends AbstractAutoCompleter {
40+
41+
public DefaultAutoCompleter(final ScriptLanguage scriptLanguage) {
42+
super(scriptLanguage);
43+
}
44+
}

src/main/java/org/scijava/script/ScriptLanguage.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
* </p>
6666
*
6767
* @author Johannes Schindelin
68+
* @author Curtis Rueden
69+
* @author Hadrien Mary
6870
*/
6971
public interface ScriptLanguage extends ScriptEngineFactory, RichPlugin,
7072
SingletonPlugin
@@ -86,6 +88,14 @@ default Object decode(final Object object) {
8688
return object;
8789
}
8890

91+
/**
92+
* Gets a helper object capable of generating autocomplete suggestions for a
93+
* code fragment.
94+
*/
95+
default AutoCompleter getAutoCompleter() {
96+
return new DefaultAutoCompleter(this);
97+
}
98+
8999
// -- ScriptEngineFactory methods --
90100

91101
@Override

0 commit comments

Comments
 (0)