Skip to content

Commit 5768490

Browse files
committed
Merge branch 'autocompletion-framework'
This branch adds autocompletion support to the ScriptLanguage API, courtesy of Hadrien Mary. Closes #264.
2 parents dbcdc4f + 5147970 commit 5768490

File tree

8 files changed

+349
-6
lines changed

8 files changed

+349
-6
lines changed

src/main/java/org/scijava/parse/DefaultParseService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.util.List;
3737
import java.util.Map;
3838

39-
import org.scijava.parse.Variable;
4039
import org.scijava.parse.eval.DefaultEvaluator;
4140
import org.scijava.plugin.Plugin;
4241
import org.scijava.service.AbstractService;
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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.lang.reflect.Method;
36+
import java.util.ArrayList;
37+
import java.util.Arrays;
38+
import java.util.Collections;
39+
import java.util.Comparator;
40+
import java.util.List;
41+
import java.util.stream.Collectors;
42+
43+
import javax.script.Bindings;
44+
import javax.script.ScriptContext;
45+
import javax.script.ScriptEngine;
46+
47+
/**
48+
* Abstract base class for {@link AutoCompleter} implementations.
49+
*
50+
* @author Hadrien Mary
51+
*/
52+
public abstract class AbstractAutoCompleter implements AutoCompleter {
53+
54+
@SuppressWarnings("unused")
55+
private ScriptLanguage scriptLanguage;
56+
57+
public AbstractAutoCompleter(final ScriptLanguage scriptLanguage) {
58+
this.scriptLanguage = scriptLanguage;
59+
}
60+
61+
@Override
62+
public AutoCompletionResult autocomplete(final String code, final int index,
63+
final ScriptEngine engine)
64+
{
65+
66+
final List<String> matches = new ArrayList<>();
67+
final int startIndex = 0;
68+
69+
if (code.endsWith(".")) {
70+
// Autocompletion with all the attributes of the object
71+
matches.addAll(engineAttributesCompleter(code, index, engine));
72+
73+
}
74+
else if (code.contains(".")) {
75+
final List<String> codeList = Arrays.asList(code.split("\\."));
76+
final String objectString = codeList.get(codeList.size() - 2);
77+
final String prefix = codeList.get(codeList.size() - 1);
78+
matches.addAll(engineAttributesCompleter(objectString + ".",
79+
prefix, index, engine));
80+
81+
}
82+
else {
83+
// Autocompletion with variables in the engine scope
84+
matches.addAll(engineVariablesCompleter(code, index, engine));
85+
}
86+
87+
// Remove duplicates
88+
final List<String> unique = //
89+
matches.stream().distinct().collect(Collectors.toList());
90+
91+
// Sort alphabetically, ignoring case
92+
Collections.sort(unique, new Comparator<Object>() {
93+
94+
@Override
95+
public int compare(final Object o1, final Object o2) {
96+
final String s1 = (String) o1;
97+
final String s2 = (String) o2;
98+
return s1.toLowerCase().compareTo(s2.toLowerCase());
99+
}
100+
});
101+
102+
// Return results. For now we ignore index and startIndex.
103+
return new AutoCompletionResult(unique, startIndex);
104+
}
105+
106+
private List<String> engineVariablesCompleter(final String code,
107+
@SuppressWarnings("unused") final int index, final ScriptEngine engine)
108+
{
109+
final List<String> matches = new ArrayList<>();
110+
111+
final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
112+
113+
for (final String key : bindings.keySet()) {
114+
if (key.toLowerCase().startsWith(code.toLowerCase())) {
115+
matches.add(key);
116+
}
117+
}
118+
return matches;
119+
120+
}
121+
122+
private List<String> engineAttributesCompleter(final String objectString,
123+
final int index, final ScriptEngine engine)
124+
{
125+
return engineAttributesCompleter(objectString, "", index, engine);
126+
}
127+
128+
private List<String> engineAttributesCompleter(final String objectString,
129+
final String prefix, @SuppressWarnings("unused") final int index,
130+
final ScriptEngine engine)
131+
{
132+
final List<String> matches = new ArrayList<>();
133+
final String lPrefix = prefix.toLowerCase();
134+
135+
final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
136+
137+
for (final String key : bindings.keySet()) {
138+
if (objectString.endsWith(key + ".")) {
139+
final Object obj = bindings.get(key);
140+
// check for public field completions
141+
for (final Field field : obj.getClass().getFields()) {
142+
if (field.getName().toLowerCase().startsWith(lPrefix)) {
143+
matches.add(objectString + field.getName());
144+
}
145+
}
146+
// check for public method completions
147+
for (final Method method : obj.getClass().getMethods()) {
148+
if (method.getName().toLowerCase().startsWith(lPrefix)) {
149+
matches.add(objectString + method.getName() + "(");
150+
}
151+
}
152+
}
153+
}
154+
155+
return matches;
156+
}
157+
}
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

src/test/java/org/scijava/command/CommandInfoTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.scijava.command.CommandInfoTest.CommandWithEnumParam.Choice;
4848
import org.scijava.module.ModuleItem;
4949
import org.scijava.plugin.Parameter;
50-
import org.scijava.plugin.Plugin;
5150

5251
/**
5352
* Tests {@link CommandInfo}.

0 commit comments

Comments
 (0)