diff --git a/src/main/java/org/scijava/script/AbstractScriptLanguage.java b/src/main/java/org/scijava/script/AbstractScriptLanguage.java index 5f52e9ac7..eb0c0b249 100644 --- a/src/main/java/org/scijava/script/AbstractScriptLanguage.java +++ b/src/main/java/org/scijava/script/AbstractScriptLanguage.java @@ -31,10 +31,13 @@ package org.scijava.script; +import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import org.scijava.plugin.AbstractRichPlugin; import org.scijava.plugin.PluginInfo; +import org.scijava.script.autocompletion.AutoCompleter; +import org.scijava.script.autocompletion.DefaultAutoCompleter; /** * Abstract superclass for {@link ScriptLanguage} implementations. diff --git a/src/main/java/org/scijava/script/AdaptedScriptLanguage.java b/src/main/java/org/scijava/script/AdaptedScriptLanguage.java index f0344cdb9..cb26e0241 100644 --- a/src/main/java/org/scijava/script/AdaptedScriptLanguage.java +++ b/src/main/java/org/scijava/script/AdaptedScriptLanguage.java @@ -31,6 +31,8 @@ package org.scijava.script; +import org.scijava.script.autocompletion.DefaultAutoCompleter; +import org.scijava.script.autocompletion.AutoCompleter; import java.util.List; import javax.script.ScriptEngine; diff --git a/src/main/java/org/scijava/script/ScriptLanguage.java b/src/main/java/org/scijava/script/ScriptLanguage.java index 14cf95866..cd229e1c7 100644 --- a/src/main/java/org/scijava/script/ScriptLanguage.java +++ b/src/main/java/org/scijava/script/ScriptLanguage.java @@ -31,6 +31,7 @@ package org.scijava.script; +import org.scijava.script.autocompletion.AutoCompleter; import java.util.Collections; import java.util.List; @@ -41,6 +42,7 @@ import org.scijava.plugin.Plugin; import org.scijava.plugin.RichPlugin; import org.scijava.plugin.SingletonPlugin; +import org.scijava.script.autocompletion.DefaultAutoCompleter; import org.scijava.util.VersionUtils; /** @@ -150,5 +152,9 @@ else if (key.equals(ScriptEngine.LANGUAGE_VERSION)) { default String getEngineVersion() { return "0.0"; } + + default AutoCompleter getAutoCompleter() { + return new DefaultAutoCompleter(this); + } } diff --git a/src/main/java/org/scijava/script/autocompletion/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/autocompletion/AbstractAutoCompleter.java new file mode 100644 index 000000000..96db64701 --- /dev/null +++ b/src/main/java/org/scijava/script/autocompletion/AbstractAutoCompleter.java @@ -0,0 +1,141 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 HOLDERS 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. + * #L% + */ +package org.scijava.script.autocompletion; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import org.scijava.script.ScriptLanguage; + +/** + * + * @author Hadrien Mary + */ +public abstract class AbstractAutoCompleter implements AutoCompleter { + + protected ScriptLanguage scriptLanguage = null; + + public AbstractAutoCompleter(ScriptLanguage scriptLanguage) { + this.scriptLanguage = scriptLanguage; + } + + @Override + public AutoCompletionResult autocomplete(String code, ScriptEngine engine) { + return autocomplete(code, 0, engine); + } + + @Override + public AutoCompletionResult autocomplete(String code, int index, ScriptEngine engine) { + + List matches = new ArrayList<>(); + int startIndex = 0; + + if (code.endsWith(".")) { + // Autocompletion with all the attributes of the object + matches.addAll(this.engineAttributesCompleter(code, index, engine)); + + } else if (code.contains(".")) { + List codeList = Arrays.asList(code.split("\\.")); + String objectString = (String) codeList.get(codeList.size() - 2); + String fieldBeginWith = (String) codeList.get(codeList.size() - 1); + matches.addAll(this.engineAttributesCompleter(objectString + ".", fieldBeginWith, index, engine)); + + } else { + // Autocompletion with variables in the engine scope + matches.addAll(this.engineVariablesCompleter(code, index, engine)); + } + + // Remove duplicates + matches.stream() + .distinct() + .collect(Collectors.toList()); + + // Sort alphabetcially + Collections.sort(matches, new SortIgnoreCase()); + + // Return results. For now we ignore index and startIndex. + return new AutoCompletionResult(matches, startIndex); + } + + private List engineVariablesCompleter(String code, int index, ScriptEngine engine) { + List matches = new ArrayList<>(); + + Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); + + for (String key : bindings.keySet()) { + if (key.toLowerCase().startsWith(code.toLowerCase())) { + matches.add(key); + }; + } + return matches; + + } + + private List engineAttributesCompleter(String objectString, int index, ScriptEngine engine) { + return this.engineAttributesCompleter(objectString, "", index, engine); + } + + private List engineAttributesCompleter(String objectString, String fieldBeginWith, int index, ScriptEngine engine) { + List matches = new ArrayList<>(); + + Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); + + for (String key : bindings.keySet()) { + if (objectString.endsWith(key + ".")) { + Object obj = bindings.get(key); + for (Field field : obj.getClass().getDeclaredFields()) { + if (field.getName().toLowerCase().startsWith(fieldBeginWith.toLowerCase())) { + matches.add(objectString + field.getName()); + } + } + } + } + + return matches; + } + + public class SortIgnoreCase implements Comparator { + + @Override + public int compare(Object o1, Object o2) { + String s1 = (String) o1; + String s2 = (String) o2; + return s1.toLowerCase().compareTo(s2.toLowerCase()); + } + } +} diff --git a/src/main/java/org/scijava/script/autocompletion/AutoCompleter.java b/src/main/java/org/scijava/script/autocompletion/AutoCompleter.java new file mode 100644 index 000000000..840f5cb74 --- /dev/null +++ b/src/main/java/org/scijava/script/autocompletion/AutoCompleter.java @@ -0,0 +1,44 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 HOLDERS 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. + * #L% + */ +package org.scijava.script.autocompletion; + +import javax.script.ScriptEngine; + +/** + * + * @author Hadrien Mary + */ +public interface AutoCompleter { + + public AutoCompletionResult autocomplete(String code, ScriptEngine engine); + + public AutoCompletionResult autocomplete(String code, int startIndex, ScriptEngine engine); +} diff --git a/src/main/java/org/scijava/script/autocompletion/AutoCompletionResult.java b/src/main/java/org/scijava/script/autocompletion/AutoCompletionResult.java new file mode 100644 index 000000000..3e90584e3 --- /dev/null +++ b/src/main/java/org/scijava/script/autocompletion/AutoCompletionResult.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.scijava.script.autocompletion; + +import java.util.List; + +/** + * + * @author hadim + */ +public class AutoCompletionResult { + + protected List matches; + protected int startIndex; + + public AutoCompletionResult(List matches) { + this(matches, 0); + } + + public AutoCompletionResult(List matches, int startIndex) { + this.matches = matches; + this.startIndex = startIndex; + } + + public int getStartIndex() { + return this.startIndex; + } + + public List getMatches() { + return this.matches; + } + +} diff --git a/src/main/java/org/scijava/script/autocompletion/DefaultAutoCompleter.java b/src/main/java/org/scijava/script/autocompletion/DefaultAutoCompleter.java new file mode 100644 index 000000000..902cd4375 --- /dev/null +++ b/src/main/java/org/scijava/script/autocompletion/DefaultAutoCompleter.java @@ -0,0 +1,45 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 HOLDERS 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. + * #L% + */ +package org.scijava.script.autocompletion; + +import org.scijava.script.ScriptLanguage; + +/** + * + * @author Hadrien Mary + */ +public class DefaultAutoCompleter extends AbstractAutoCompleter { + + public DefaultAutoCompleter(ScriptLanguage scriptLanguage) { + super(scriptLanguage); + } + +} diff --git a/src/test/java/org/scijava/script/ScriptEngineTest.java b/src/test/java/org/scijava/script/ScriptEngineTest.java index bce9fe6f2..de2f4f0f6 100644 --- a/src/test/java/org/scijava/script/ScriptEngineTest.java +++ b/src/test/java/org/scijava/script/ScriptEngineTest.java @@ -31,6 +31,8 @@ package org.scijava.script; +import org.scijava.script.autocompletion.DefaultAutoCompleter; +import org.scijava.script.autocompletion.AutoCompleter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/src/test/java/org/scijava/script/ScriptFinderTest.java b/src/test/java/org/scijava/script/ScriptFinderTest.java index c24fad50c..ee7ef9466 100644 --- a/src/test/java/org/scijava/script/ScriptFinderTest.java +++ b/src/test/java/org/scijava/script/ScriptFinderTest.java @@ -31,6 +31,8 @@ package org.scijava.script; +import org.scijava.script.autocompletion.DefaultAutoCompleter; +import org.scijava.script.autocompletion.AutoCompleter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/src/test/java/org/scijava/script/ScriptInfoTest.java b/src/test/java/org/scijava/script/ScriptInfoTest.java index 8d022f748..ea540f90b 100644 --- a/src/test/java/org/scijava/script/ScriptInfoTest.java +++ b/src/test/java/org/scijava/script/ScriptInfoTest.java @@ -31,6 +31,8 @@ package org.scijava.script; +import org.scijava.script.autocompletion.DefaultAutoCompleter; +import org.scijava.script.autocompletion.AutoCompleter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame;