Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3ef7091
Jython: Add auto-completion support for non-static methods!
tferr Dec 4, 2020
f7899f6
Warn user on invalid imports
tferr Dec 4, 2020
422f160
Add parameters and definitions to the description of completions
tferr Dec 4, 2020
092a86c
Fix visibility of field
tferr Dec 4, 2020
686f15b
auto-complete words that contain a term
haesleinhuepf Dec 4, 2020
b7d5201
added compatibility for import-as statements
haesleinhuepf Dec 4, 2020
77c3664
allow classes and aliases with lower case first character
haesleinhuepf Dec 4, 2020
d2a6256
Merge pull request #1 from haesleinhuepf/autocomplete_commands_that_c…
tferr Dec 4, 2020
121ad9c
Suggestion list: Ensure entries starting with seed remain on top of list
tferr Dec 4, 2020
1f3bea6
Fix formatting issues
tferr Dec 4, 2020
721fe91
Revert "added compatibility for import-as statements"
haesleinhuepf Dec 4, 2020
f9c6a69
Restrict allowed whitespace in variable declaration
tferr Dec 4, 2020
9a584df
Merge pull request #2 from haesleinhuepf/import-as_statment
tferr Dec 4, 2020
0d076ff
Merge remote-tracking branch 'tferr/autocompletion-vars' into autocom…
tferr Dec 4, 2020
daff927
Improve autocompletions
tferr Dec 4, 2020
e7870c0
Refactoring: move generic code to ClassUtil...
tferr Dec 5, 2020
89f5e08
Use frames when accessing javadocs
tferr Dec 5, 2020
b7c51fc
Revert "Merge pull request #2 from haesleinhuepf/import-as_statment"
tferr Dec 4, 2020
155c8aa
Revert "Merge pull request #2 from haesleinhuepf/import-as_statment"
tferr Dec 4, 2020
0050715
Merge remote-tracking branch 'tferr/autocompletion-vars' into autocom…
tferr Dec 5, 2020
96da72a
[maven-release-plugin] prepare release script-editor-0.5.9
frauzufall Dec 16, 2020
34a2e05
Jython: Add auto-completion support for non-static methods!
tferr Dec 4, 2020
b17cd7a
Warn user on invalid imports
tferr Dec 4, 2020
49d37b0
Add parameters and definitions to the description of completions
tferr Dec 4, 2020
ce3bb7a
Fix visibility of field
tferr Dec 4, 2020
92dfdb3
auto-complete words that contain a term
haesleinhuepf Dec 4, 2020
1a3ffaa
added compatibility for import-as statements
haesleinhuepf Dec 4, 2020
8d155fa
allow classes and aliases with lower case first character
haesleinhuepf Dec 4, 2020
8b4bf53
Suggestion list: Ensure entries starting with seed remain on top of list
tferr Dec 4, 2020
05ee489
Fix formatting issues
tferr Dec 4, 2020
0dfa760
Revert "added compatibility for import-as statements"
haesleinhuepf Dec 4, 2020
1d1f255
Restrict allowed whitespace in variable declaration
tferr Dec 4, 2020
1e46e56
Improve autocompletions
tferr Dec 4, 2020
ae945c0
Refactoring: move generic code to ClassUtil...
tferr Dec 5, 2020
3751e7c
Use frames when accessing javadocs
tferr Dec 5, 2020
2ce9f88
Revert "Merge pull request #2 from haesleinhuepf/import-as_statment"
tferr Dec 4, 2020
cd732e2
Implement CompletionText and modify ClassUtil for upcoming changes ...
tferr Jan 25, 2021
0e90654
Merge remote-tracking branch 'tferr/autocompletion-vars' into autocom…
tferr Jan 25, 2021
04e945c
Merge branch 'master' into autocompletion-vars
tferr Jan 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
<scm>
<connection>scm:git:git://github.com/scijava/script-editor</connection>
<developerConnection>scm:git:git@github.com:scijava/script-editor</developerConnection>
<tag>HEAD</tag>
<tag>script-editor-0.5.9</tag>
<url>https://github.com/scijava/script-editor</url>
</scm>
<issueManagement>
Expand Down
19 changes: 19 additions & 0 deletions release.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#release configuration
#Wed Dec 16 10:56:44 CET 2020
project.scm.org.scijava\:script-editor.developerConnection=scm\:git\:git@github.com\:scijava/script-editor
scm.tagNameFormat=@{project.artifactId}-@{project.version}
scm.tag=script-editor-0.5.9
pushChanges=false
scm.url=scm\:git\:git\://github.com/scijava/script-editor
preparationGoals=clean verify
project.scm.org.scijava\:script-editor.tag=HEAD
project.scm.org.scijava\:script-editor.url=https\://github.com/scijava/script-editor
remoteTagging=true
projectVersionPolicyId=default
scm.commentPrefix=[maven-release-plugin]
project.scm.org.scijava\:script-editor.connection=scm\:git\:git\://github.com/scijava/script-editor
project.dev.org.scijava\:script-editor=0.5.10-SNAPSHOT
exec.snapshotReleasePluginAllowed=false
exec.additionalArguments=-Dgpg.skip\=true -P deploy-to-scijava
completedPhase=end-release
project.rel.org.scijava\:script-editor=0.5.9
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
Expand All @@ -46,9 +49,13 @@
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.fife.ui.autocomplete.BasicCompletion;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;

public class ClassUtil {

static private final String scijava_javadoc_URL = "https://javadoc.scijava.org/"; // with ending slash
static final String scijava_javadoc_URL = "https://javadoc.scijava.org/"; // with ending slash

/** Cache of class names vs list of URLs found in the pom.xml files of their contaning jar files, if any. */
static private final Map<String, JarProperties> class_urls = new HashMap<>();
Expand Down Expand Up @@ -335,4 +342,103 @@ static public final ArrayList<String> findSimpleClassNamesStartingWith(final Str
}
return matches;
}

private static String getJavaDocLink(final Class<?> c) {
final String name = c.getCanonicalName();
final String pkg = getDocPackage(name);
if (pkg == null) return name;
final String url = String.format("%s%s/index.html?%s.html", scijava_javadoc_URL, pkg, name.replace(".", "/"));
return String.format("<a href='%s';>%s</a>", url, name);
}

private static String getDocPackage(final String classCanonicalName) {
//TODO: Do this programatically
if (classCanonicalName.startsWith("ij."))
return "ImageJ1";
else if (classCanonicalName.startsWith("sc.fiji"))
return "Fiji";
else if (classCanonicalName.startsWith("net.imagej"))
return "ImageJ";
else if (classCanonicalName.startsWith("net.imglib2"))
return "ImgLib2";
else if (classCanonicalName.startsWith("org.scijava"))
return "SciJava";
else if (classCanonicalName.startsWith("loci.formats"))
return "Bio-Formats";
if (classCanonicalName.startsWith("java."))
return "Java8";
else if (classCanonicalName.startsWith("sc.iview"))
return "SciView";
else if (classCanonicalName.startsWith("weka."))
return "Weka";
else if (classCanonicalName.startsWith("inra.ijpb"))
return "MorphoLibJ";
return null;
}

/**
* Assembles an HTML-formatted auto-completion summary with functional
* hyperlinks
*
* @param field the field being documented
* @param c the class being documented. Expected to be documented at the
* Scijava API documentation portal.
* @return the completion summary
*/
protected static String getSummaryCompletion(final Field field, final Class<?> c) {
final StringBuffer summary = new StringBuffer();
summary.append("<b>").append(field.getName()).append("</b>");
summary.append(" (").append(field.getType().getName()).append(")");
summary.append("<DL>");
summary.append("<DT><b>Defined in:</b>");
summary.append("<DD>").append(getJavaDocLink(c));
summary.append("</DL>");
return summary.toString();
}

/**
* Assembles an HTML-formatted auto-completion summary with functional
* hyperlinks
*
* @param method the method being documented
* @param c the class being documented. Expected to be documented at the
* Scijava API documentation portal.
* @return the completion summary
*/
protected static String getSummaryCompletion(final Method method, final Class<?> c) {
final StringBuffer summary = new StringBuffer();
final StringBuffer replacementHeader = new StringBuffer(method.getName());
final int bIndex = replacementHeader.length(); // remember '(' position
replacementHeader.append("(");
final Parameter[] params = method.getParameters();
if (params.length > 0) {
for (final Parameter parameter : params) {
replacementHeader.append(parameter.getType().getSimpleName()).append(", ");
}
replacementHeader.setLength(replacementHeader.length() - 2); // remove trailing ', ';
}
replacementHeader.append(")");
replacementHeader.replace(bIndex, bIndex + 1, "</b>("); // In header, highlight only method name for extra
// contrast
summary.append("<b>").append(replacementHeader);
summary.append("<DL>");
summary.append("<DT><b>Returns:</b>");
summary.append("<DD>").append(method.getReturnType().getSimpleName());
summary.append("<DT><b>Defined in:</b>");
summary.append("<DD>").append(getJavaDocLink(c));
summary.append("</DL>");
return summary.toString();
}

static List<Completion> classUnavailableCompletions(final CompletionProvider provider, final String pre) {
// placeholder completions to warn users class was not available (repeated to force pop-up display)
final List<Completion> list = new ArrayList<>();
final String summary = "Class not found or invalid import. See "
+ String.format("<a href='%s';>SciJavaDocs</a>", scijava_javadoc_URL)
+ " or <a href='https://search.imagej.net/';>search</a> for help";
list.add(new BasicCompletion(provider, pre + "?", null, summary));
list.add(new BasicCompletion(provider, pre + "?", null, summary));
return list;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.scijava.ui.swing.script.autocompletion;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.fife.ui.autocomplete.AbstractCompletion;
import org.fife.ui.autocomplete.BasicCompletion;
import org.fife.ui.autocomplete.CompletionProvider;

public class CompletionText {

private String replacementText;
private String description;
private String summary;

public CompletionText(final String replacementText) {
this(replacementText, (String)null, (String)null);
}

public CompletionText(final String replacementText, final String summary, final String description) {
this.replacementText = replacementText;
this.summary = summary;
this.description = description;
}

public CompletionText(final String replacementText, final Class<?> c, final Field f) {
this(replacementText, ClassUtil.getSummaryCompletion(f, c), null);
}

public CompletionText(final String replacementText, final Class<?> c, final Method m) {
this(replacementText, ClassUtil.getSummaryCompletion(m, c), null);
}

public String getReplacementText() {
return replacementText;
}

public String getDescription() {
return description;
}

public String getSummary() {
return summary;
}

public AbstractCompletion getCompletion(final CompletionProvider provider, final String replacementText) {
return new BasicCompletion(provider, replacementText, description, summary);
}

public void setReplacementText(final String replacementText) {
this.replacementText = replacementText;
}

public void setDescription(final String description) {
this.description = description;
}

public void setSummary(final String summary) {
this.summary = summary;
}

@Override
public String toString() {
return replacementText + " | " + description + " | " + summary;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public JythonAutoCompletion(final CompletionProvider provider) {
}

static private final Pattern importPattern = Pattern.compile("^(from[ \\t]+([a-zA-Z_][a-zA-Z0-9._]*)[ \\t]+|)import[ \\t]+([a-zA-Z_][a-zA-Z0-9_]*[ \\ta-zA-Z0-9_,]*)[ \\t]*([\\\\]*|)[ \\t]*(#.*|)$"),
tripleQuotePattern = Pattern.compile("\"\"\"");
tripleQuotePattern = Pattern.compile("\"\"\""),
variableDeclarationPattern = Pattern.compile("([a-zA-Z_][a-zA-Z0-9._]*)[ \\t]*=[ \\t]*([A-Z_][a-zA-Z0-9._]*)(?:\\()"); // E.g., in 'imp=ImagePlus()' group1: imp; group2: ImagePlus

static public class Import {
final public String className,
Expand All @@ -65,7 +66,26 @@ public Import(final String packageName, final String[] parts, final int lineNumb
this(packageName + "." + parts[0], 3 == parts.length ? parts[2] : null, lineNumber);
}
}


static public final String findClassAliasOfVariable(final String variable, String inputText) {
final String[] lines = inputText.split("\n");
for (int i = 0; i < lines.length; ++i) {
final String line = lines[i];
final Matcher matcher = variableDeclarationPattern.matcher(line);
if (matcher.find()) {
// a line containing a variable declaration
// System.out.println("Queried variable: " + variable);
// System.out.println("Hit: line #" + i + ": " + line);
// System.out.println("Matcher g1: " + matcher.group(1));
// System.out.println("Matcher g2: " + matcher.group(2));
if (variable.equals(matcher.group(1))) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach is dangerous in that it doesn't consider the scope of the variable. I would do it differently, by parsing the code like jython's interpreter with a ParserFacade, see e.g. https://github.com/acardona/scripts/blob/master/python/imagej/jython/parse_code.py

@tferr tferr Dec 4, 2020

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Are you able to work on this? To me the priority would be to support he return type of the static method so I'd prefer to work on that instead (unless you say otherwise)

@acardona acardona Dec 4, 2020

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, proper jython code parsing is something I've been working on but haven't committed yet. Will lead to:

  1. autocompletion for variable names within scope, in addition to possible java class imports;
  2. autocompletion and auto-import for python modules included in the jython jar or discoverable from its PYTHONPATH-equivalent;
  3. type resolution for variables within scope, in order to autocomplete their methods. Won't always work (e.g. when an object derives from a call on a function argument), but it will work in many cases.

For the latter case, the AutoCompletionListener interface will allow e.g. @haesleinhuepf's update site to add variable name-based imports (e.g. for imp, ip, fp, etc.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acardona, Great! On a second thought I will hold for now, as you are likely to revamp a bunch of things. But do let me know if there is something you want me too look into. I won't work on this until I hear from you (so feel free to merge), but do let me know if you want me to look into something. I will post below a summary of what this (snowballed) PR now has.

return matcher.group(2);
}
}
}
return null;
}

static public final HashMap<String, Import> findImportedClasses(final String text) {
final HashMap<String, Import> importedClasses = new HashMap<>();
String packageName = "";
Expand Down
Loading