package org.das2.jythoncompletion; import java.awt.Color; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import javax.swing.text.Utilities; import org.das2.jythoncompletion.support.CompletionResultSet; import org.das2.jythoncompletion.support.CompletionTask; import org.das2.util.LoggerManager; import org.python.core.PyClass; import org.python.core.PyClassPeeker; import org.python.core.PyException; import org.python.core.PyFunction; import org.python.core.PyInteger; import org.python.core.PyJavaClass; import org.python.core.PyJavaClassPeeker; import org.python.core.PyJavaInstance; import org.python.core.PyJavaInstancePeeker; import org.python.core.PyJavaPackage; import org.python.core.PyList; import org.python.core.PyMethod; import org.python.core.PyMethodPeeker; import org.python.core.PyNone; import org.python.core.PyObject; import org.python.core.PyReflectedFunction; import org.python.core.PyReflectedFunctionPeeker; import org.python.core.PyString; import org.python.core.PyStringMap; import org.python.core.PyTableCode; import org.python.util.PythonInterpreter; import org.autoplot.jythonsupport.JythonOps; import org.autoplot.jythonsupport.JythonRefactory; import org.autoplot.jythonsupport.JythonToJavaConverter; import org.autoplot.jythonsupport.SimplifyScriptSupport; import org.das2.graph.GraphUtil; import org.python.core.PyArray; import org.python.core.PyFloat; import org.python.core.PyReflectedField; /** * Completions for Jython code. The completion task is created with the * editor configured for completions (code and caret position within code), * and "query" is called which will fill a CompletionResultSet. * @author jbf * @see org.das2.jythoncompletion.JythonCompletionProvider */ public class JythonCompletionTask implements CompletionTask { private static final Logger logger= LoggerManager.getLogger("jython.editor.completion"); private static final ImageIcon LOCALVARICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/localVariable.png") ); private static final ImageIcon JAVA_CLASS_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaClass.png") ); private static final ImageIcon JYTHONCOMMANDICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/jythonCommand.png") ); private static final ImageIcon JAVA_JYTHON_METHOD_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaJythonMethod.png") ); private static final ImageIcon JAVA_FIELD_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaStaticField.png") ); private static final ImageIcon JAVA_METHOD_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaMethod.png") ); private static final ImageIcon JAVA_STATIC_METHOD_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaStaticMethod.png") ); private static final ImageIcon JAVA_CONSTRUCTOR_ICON= new ImageIcon( JythonCompletionTask.class.getResource("ui/javaConstructor.png") ); private static final int JYTHONCOMMAND_SORT = 2; private static final int JAVAMETHOD_SORT = 1; private static final int JAVACLASS_SORT = 1; private static final int PYREFLECTEDFIELD_SORT = 3; private static final int PYCLASS_SORT = 3; private static final int LOCALVAR_SORT = -10; private static final int AUTOVAR_SORT = -3; private static final int AUTOCOMMAND_SORT = -2; private static final int AUTOVARHIDE_SORT = 9; private static final int JAVASTATICFIELD_SORT=1; public static final String CLIENT_PROPERTY_INTERPRETER_PROVIDER = "JYTHON_INTERPRETER_PROVIDER"; JTextComponent editor; private final JythonInterpreterProvider jythonInterpreterProvider; /** * create the completion task on the text component, using its content and caret position. * @param t the text component */ public JythonCompletionTask(JTextComponent t) { this.editor = t; jythonInterpreterProvider = (JythonInterpreterProvider) t.getClientProperty(CLIENT_PROPERTY_INTERPRETER_PROVIDER); } private Method getReadMethod(PyObject context, PyObject po, Class dc, String propName) { try { String methodName = "get" + propName.substring(0,1).toUpperCase() + propName.substring(1); Method m = dc.getMethod(methodName); return m; } catch (NoSuchMethodException ex) { if ( po instanceof PyInteger ) { String methodName = "is" + propName.substring(0,1).toUpperCase() + propName.substring(1); try { Method m = dc.getMethod(methodName); return m; } catch ( NoSuchMethodException | SecurityException ex2 ) { return null; } } return null; } catch (SecurityException ex) { return null; } } @Override public void query(CompletionResultSet arg0) throws PyException { try { JythonCompletionProvider.getInstance().setMessage("busy: getting completions"); CompletionContext cc = CompletionSupport.getCompletionContext(editor); if (cc == null) { logger.fine("no completion context"); } else { doQuery( cc, arg0); // TODO: how to make it so the plotx reference documentation waits? I guess we add multiple completions. //( arg0.addItem(new MessageCompletionItem("please wait")) ); } } catch ( BadLocationException ex ) { logger.log( Level.WARNING, null, ex ); arg0.addItem( new MessageCompletionItem( ex.getMessage() ) ); } finally { JythonCompletionProvider.getInstance().setMessage("done getting completions"); arg0.finish(); } } /** * perform the completions query. This is the heart of Jython completions. * @param cc * @param resultSet * @return the count */ public int doQuery( CompletionContext cc, CompletionResultSet resultSet ) { int c=0; try { switch (cc.contextType) { case CompletionContext.MODULE_NAME: c= queryModules(cc, resultSet); break; case CompletionContext.PACKAGE_NAME: c= queryPackages(cc, resultSet); break; case CompletionContext.DEFAULT_NAME: c= queryNames(cc, resultSet); break; case CompletionContext.METHOD_NAME: c= queryMethods(cc, resultSet); break; case CompletionContext.STRING_LITERAL_ARGUMENT: c= queryStringLiteralArgument(cc, resultSet); break; case CompletionContext.COMMAND_ARGUMENT: c= queryCommandArgument(cc, resultSet); c+= queryNames(cc, resultSet); break; case CompletionContext.CLASS_METHOD_NAME: c= queryClassMethods( cc, resultSet ); break; default: break; } } catch ( BadLocationException ex ) { logger.log( Level.WARNING, null, ex ); if ( resultSet!=null ) resultSet.addItem( new MessageCompletionItem( ex.getMessage() ) ); } finally { } return c; } private Method getJavaMethod(PyMethod m, int i) { PyMethodPeeker mpeek = new PyMethodPeeker(m); //PyJavaInstancePeeker peek = new PyJavaInstancePeeker((PyJavaInstance) context); return new PyReflectedFunctionPeeker(mpeek.getReflectedFunction()).getMethod(i); } private int getMethodCount( PyMethod m ) { PyMethodPeeker mpeek = new PyMethodPeeker(m); return new PyReflectedFunctionPeeker(mpeek.getReflectedFunction()).getArgsCount(); } private int queryClassMethods(CompletionContext cc, CompletionResultSet rs) { int count= 0; Class c= cc.getContextObjectClass(); while ( c!=null && c!=Object.class ) { Method[] mm= c.getDeclaredMethods(); for ( Method m: mm ){ if ( m.getName().startsWith(cc.completable) ) { String signature = methodSignature(m); String args = methodArgs(m); String ss= m.getName(); String label= ss + args; String link = getLinkForJavaSignature(signature); rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link)); count++; } } c= c.getSuperclass(); } return count; } /** * remove getProp and setProp and replace with just "prop" * @param po2 * @return */ private List<String> reduceGetterSetters( PyObject lcontext, PyList po2, boolean cullGetterSetters ) { Map<String,String> mm= new LinkedHashMap<>(); for (int i = 0; i < po2.__len__(); i++) { PyString s = (PyString) po2.__getitem__(i); mm.put( s.toString(), s.toString() ); } if ( cullGetterSetters ) { List<String> ss= new ArrayList<>( mm.keySet() ); for ( String s: ss ) { if ( s.startsWith("set") ) { String prop= s.substring(3); if ( mm.get("get"+prop )!=null ) { String propName= Character.toLowerCase( prop.charAt(0) ) + prop.substring(1); if ( mm.containsKey(propName) ) { mm.remove("get"+prop); mm.remove("set"+prop); } } else if ( mm.get("is"+prop )!=null ) { String propName= Character.toLowerCase( prop.charAt(0) ) + prop.substring(1); if ( mm.containsKey(propName) ) { mm.remove("is"+prop); mm.remove("set"+prop); } } } } } return new ArrayList<>( mm.keySet() ); } private int queryMethods(CompletionContext cc, CompletionResultSet rs) throws BadLocationException { logger.fine("queryMethods"); PythonInterpreter interp; interp = getInterpreter(); String eval; if ( JythonCompletionProvider.getInstance().settings().isSafeCompletions() ) { eval = editor.getText(0, Utilities.getRowStart(editor, editor.getCaretPosition())); String eval1 = SimplifyScriptSupport.removeSideEffects( eval ); //String eval2 = JythonUtil.removeSideEffects( eval ); eval = eval1; } else { eval= editor.getText(0, Utilities.getRowStart(editor, editor.getCaretPosition())); } //kludge to handle increase in indent level if (eval.endsWith(":\n")) { eval = eval + " pass\n"; } try { interp.exec(JythonRefactory.fixImports(eval)); } catch ( PyException ex ) { // something bad has happened, remove side effects (we might have done this already) and try again. eval = editor.getText(0, Utilities.getRowStart(editor, editor.getCaretPosition())); String eval1 = SimplifyScriptSupport.removeSideEffects( eval ); //String eval2 = JythonUtil.removeSideEffects( eval ); eval= eval1; if (eval.endsWith(":\n")) { eval = eval + " pass\n"; } try { eval= sanitizeLeaveImports(eval); interp.exec(eval); } catch (PyException ex2 ) { rs.addItem(new MessageCompletionItem("Eval error in code before current position", ex2.toString())); return 0; } } catch (IOException ex) { rs.addItem(new MessageCompletionItem("Exception occurred: " + ex.toString())); return 0; } PyObject lcontext=null; PyJavaClass lcontextClass=null; boolean fromArray= false; try { lcontext = interp.eval(cc.contextString); } catch (PyException ex) { try { if ( cc.contextString.endsWith("]") ) { int k= cc.contextString.lastIndexOf("["); if ( k>-1 ) { PyObject occ= interp.eval(cc.contextString.substring(0,k)); if ( occ instanceof PyArray ) { PyArray pa= (PyArray)occ; Object o= pa.getArray(); Class oc= o.getClass(); if ( oc.isArray() ) { lcontextClass= PyJavaClass.lookup( oc.getComponentType() ); try { lcontext = new PyJavaInstance( oc.getComponentType().getDeclaredConstructors()[0].newInstance() ); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex1) { Logger.getLogger(JythonCompletionTask.class.getName()).log(Level.SEVERE, null, ex1); } fromArray= true; } } } } // check to see if we have identified the class of the symbol. if ( lcontextClass==null ) { PyObject occ= interp.eval(cc.contextString+__CLASSTYPE); if ( occ!=null && occ instanceof PyJavaClass ) { lcontextClass= (PyJavaClass)occ; } else { rs.addItem(new MessageCompletionItem("EVAL error: " + cc.contextString, ex.toString())); return 0; } } } catch ( PyException ex2 ) { rs.addItem(new MessageCompletionItem("Eval error: " + cc.contextString, ex.toString())); return 0; } } if ( lcontext==null ) { logger.log(Level.FINE, "completions have the class but not the instance to work with: {0}", lcontextClass.__name__); lcontext= lcontextClass; } PyList po2; try { po2= (PyList) lcontext.__dir__(); } catch ( PyException e ) { logger.log( Level.SEVERE, e.getMessage(), e ); return 0; } List<String> po3= reduceGetterSetters( lcontext, po2, fromArray || ( lcontext!=lcontextClass ) ); int count=0; for (int i = 0; i < po3.size(); i++) { String ss = po3.get(i); logger.log(Level.FINEST, "does {0} start {1}", new Object[] { cc.completable, ss } ); if (ss.startsWith(cc.completable)) { boolean notAlreadyAdded= true; PyObject po; try { po = lcontext.__getattr__(ss); } catch (PyException e) { logger.log(Level.FINE, "PyException from \"{0}\":", ss); logger.log( Level.SEVERE, e.getMessage(), e ); continue; } catch ( IllegalArgumentException e ) { logger.log( Level.SEVERE, e.getMessage(), e ); continue; } String label = ss; String signature = null; String args = ""; ImageIcon icon= null; if (lcontext instanceof PyJavaClass) { if (po.getClass().toString().equals( "class org.python.core.PyReflectedConstructor" ) ) { args= "()"; signature= ""; } else if (po instanceof PyReflectedFunction) { Method m = new PyReflectedFunctionPeeker((PyReflectedFunction) po).getMethod(0); if ( Modifier.isStatic( m.getModifiers() ) ) { signature = methodSignature(m); icon= getIconFor(m); args = methodArgs(m); } else { if ( lcontext==lcontextClass ) { // whoops, we have an instance of a class here signature = methodSignature(m); icon= getIconFor(m); args = methodArgs(m); } else { continue; } } } else if ( po instanceof PyString || po instanceof PyInteger || po instanceof PyJavaInstance) { Class c= new PyClassPeeker((PyJavaClass) lcontext).getJavaClass(); try { Field f = c.getField(ss); signature= fieldSignature(f); icon= getIconFor(f); } catch ( NoSuchFieldException ex ) { } } } else if ( lcontext instanceof PyJavaPackage ) { if (po instanceof PyJavaClass) { Class dc = getJavaClass((PyJavaClass)po); if ( dc.getConstructors().length>0 ) { Constructor constructor= dc.getConstructors()[0]; signature= constructorSignature( constructor ); args= argsList( constructor.getParameterTypes() ); signature= signature+args; } else { signature= dc.getCanonicalName().replaceAll("\\.","/")+".html"; } //Method m = new PyJavaClassPeeker((PyJavaClass)po).getMethod(0); //signature = methodSignature(m); //args = methodArgs(m); } else if ( po instanceof PyJavaPackage ) { //Method m = new PyJavaClassPeeker((PyJavaClass)po).getMethod(0); //signature = methodSignature(m); //args = methodArgs(m); } } else if (lcontext instanceof PyClass) { PyClassPeeker peek = new PyClassPeeker((PyClass) lcontext); Class dc = peek.getJavaClass(); Field f = null; try { f = dc.getField(label); } catch (NoSuchFieldException | SecurityException ex) { } if (f == null) { continue; } signature = fieldSignature(f); } else if (lcontext instanceof PyJavaInstance) { if (po instanceof PyMethod) { PyMethod m = (PyMethod) po; Method jm; try { for ( int im=0; im<getMethodCount(m); im++ ) { jm = getJavaMethod(m, im); signature = methodSignature(jm); args = methodArgs(jm); label= ss + args; icon= getIconFor(jm); String link = getLinkForJavaSignature(signature); rs.addItem( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, JAVAMETHOD_SORT, icon ) ); count++; notAlreadyAdded= false; } } catch ( RuntimeException ex ) { logger.fine(ex.toString()); continue; } } else { PyJavaInstancePeeker peek = new PyJavaInstancePeeker((PyJavaInstance) lcontext); Class dc = peek.getInstanceClass(); Method propReadMethod = getReadMethod(lcontext, po, dc, label); if (propReadMethod != null) { signature = methodSignature(propReadMethod); args = ""; String type= propReadMethod.getReturnType().getCanonicalName(); label = ss + " <i>("+type+")</i>"; } else { Field f = null; try { f = dc.getField(label); } catch (NoSuchFieldException ex) { logger.log(Level.FINEST, "NoSuchFieldException for item {0}", ss); } catch (SecurityException ex) { logger.log(Level.FINEST, "SecurityException for item {0}", ss); } if (f == null) continue; icon= getIconFor(f); //TODO: don't include static fields in list. signature = fieldSignature(f); boolean showValues=false; if ( showValues ) { if ( po instanceof PyInteger ) { label= ss + " = " + po.toString(); } else if ( po instanceof PyFloat ) { label= ss + " = " + po.toString(); } else if ( po instanceof PyString ) { label= ss + " = " + po.toString(); } else { label = ss; } } else { label = ss; } } } } else { //PyObject o= context.__dir__(); label= ss; signature= null; if ( po instanceof PyMethod ) { PyMethod pm= (PyMethod)po; PyObject pm2= pm.im_func; if ( pm2 instanceof PyFunction ) { Object doc= ((PyFunction)pm2).__doc__; if ( doc!=null ) { signature= doc instanceof PyNone ? "(No documentation)" : doc.toString(); String[] ss2= signature.split("\n"); if ( ss2.length>1 ) { for ( int jj= 0; jj< ss2.length; jj++ ){ ss2[jj]= escapeHtml(ss2[jj]); } String sig= getPyFunctionSignature( (PyFunction)pm2 ); if ( !signature.startsWith("<html>" ) ) { signature= "<html><b>"+sig+ "</b><br><br>"+join( ss2, "<br>" )+"</html>"; } else { signature= "<html><b>"+sig+ "</b><br><br>" + signature.substring(6)+"</html>"; } } signature= "inline:"+signature; } } } //String link = "http://docs.python.org/library/"; //TODO: this could probably be done } if ( notAlreadyAdded ) { if ( signature!=null && signature.startsWith("inline:") ) { rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, signature)); } else { String link = getLinkForJavaSignature(signature); if ( icon==null ) { rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, JAVAMETHOD_SORT, null ) ); } else { rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, JAVAMETHOD_SORT, icon ) ); } } count++; } } } return count; } public static final String __CLASSTYPE = "__CLASSTYPE"; /** * * @param cc * @param rs * @return the count */ private int queryModules(CompletionContext cc, CompletionResultSet rs) { logger.fine("queryModules"); PythonInterpreter interp = getInterpreter(); String eval = "targetComponents = '" + cc.contextString + "'.split('.')\n" + "base = targetComponents[0]\n" + "baseModule = __import__(base, globals(), locals())\n" + "module = baseModule \n" + "for component in targetComponents[1:]:\n" + " module = getattr(module, component)\n" + "list = dir(module)\n" + "if ( list.count('__name__')>0 ):\n" + " list.remove('__name__')\n" + "list.append('*')\n" + "list"; try { interp.exec(eval); } catch ( PyException ex ) { if ( rs!=null ) rs.addItem(new MessageCompletionItem("Eval error in code before current position", ex.toString())); return 0; } int count=0; PyList po2 = (PyList) interp.eval("list"); for (int i = 0; i < po2.__len__(); i++) { PyString s = (PyString) po2.__getitem__(i); String ss = s.toString(); if (ss.startsWith(cc.completable)) { String javaClass= cc.contextString + "." + ss; String signature; String link; if ( ss.length()>0 && Character.isUpperCase(ss.charAt(0)) ) { signature= join( javaClass.split("\\."), "/") + ".html"; link= JavadocLookup.getInstance().getLinkForJavaSignature(signature); } else { signature= join( javaClass.split("\\."), "/") + "/package-summary.html"; link= JavadocLookup.getInstance().getLinkForJavaSignature(signature); } if ( link!=null ) link+= "#skip.navbar.top"; if ( rs!=null ) rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss, ss, link)); count++; } } return count; } /** * look for package names. * @param cc * @param rs * @return the count */ private int queryPackages(CompletionContext cc, CompletionResultSet rs) { logger.fine("queryPackages"); PythonInterpreter interp = getInterpreter(); HashSet<String> results= new HashSet(); int count=0; if ( cc.completable.equals("import") ) { if ( rs!=null ) rs.addItem(new DefaultCompletionItem( " ", 0, " ", "space", null )); return 1; } if ( !cc.contextString.equals( cc.completable ) ) { // something to work with String eval = "import " + cc.contextString + "\n" + "targetComponents = '" + cc.contextString + "'.split('.')\n" + "base = targetComponents[0]\n" + "baseModule = __import__(base, globals(), locals(), [], -1 )\n" + "module = baseModule \n" + "name= base\n" + "for component in targetComponents[1:]:\n" + " name= name + '.' + component\n" + " baseModule = __import__( name, None, None )\n" + " module = getattr(module, component)\n" + "list = dir(module)\n" + "if ( '__name__' in list ): list.remove('__name__')\n" + "list\n"; PyList po2; try { interp.exec(eval); } catch ( PyException ex ) { if ( rs!=null ) rs.addItem(new MessageCompletionItem("Eval error in code before current position", ex.toString())); return 0; } po2 = (PyList) interp.eval("list"); for (int i = 0; i < po2.__len__(); i++) { PyString s = (PyString) po2.__getitem__(i); String ss = s.toString(); if (ss.startsWith(cc.completable)) { String javaClass= cc.contextString + "." + ss; String signature; String link; if ( ss.length()>0 && Character.isUpperCase(ss.charAt(0)) ) { signature= join( javaClass.split("\\."), "/") + ".html"; link= JavadocLookup.getInstance().getLinkForJavaSignature(signature); } else { signature= join( javaClass.split("\\."), "/") + "/package-summary.html"; link= JavadocLookup.getInstance().getLinkForJavaSignature(signature); } if ( link!=null ) link+= "#skip.navbar.top"; if ( rs!=null ) rs.addItem(new DefaultCompletionItem(ss, cc.completable.length(), ss, ss, link )); count++; results.add(ss); } } } BufferedReader reader= null; try { reader= new BufferedReader( new InputStreamReader( JythonCompletionTask.class.getResourceAsStream("packagelist.txt") ) ); String ss= reader.readLine(); String search= cc.contextString + "." + cc.completable; int plen= cc.contextString.length()+1; if ( cc.contextString.equals(cc.completable) ) { search= cc.contextString; plen= search.length(); } while ( ss!=null ) { if ( !ss.startsWith("#") && ss.length()>0 ) { if ( ss.startsWith(search) && !results.contains(ss.substring(plen)) ) { String link= "http://www-pw.physics.uiowa.edu/~jbf/autoplot/javadoc2018/" + ss.replaceAll("\\.","/") + "/package-summary.html"; if ( rs!=null ) rs.addItem(new DefaultCompletionItem(ss, search.length(), ss, ss, link )); count++; } } ss= reader.readLine(); } } catch ( IOException ex ) { logger.log( Level.WARNING, null, ex ); } finally { if ( reader!=null ) try { reader.close(); } catch (IOException ex) { logger.log( Level.WARNING, null, ex ); } } return count; } private static String join(String[] list, String delim) { return join(Arrays.asList(list), delim); } private static String join(List<String> list, String delim) { if (list.isEmpty()) { return ""; } else { StringBuilder result = new StringBuilder(list.get(0)); for (int i = 1; i < list.size(); i++) { result.append(delim).append(list.get(i)); } return result.toString(); } } /** * quick-n-dirty one line parser is better than nothing. * @param s * @return */ private static String popOffComments( String s ) { char inString= 0; for ( int i=0; i<s.length(); i++ ) { if( inString==0 && s.charAt(i)=='#' ) { return s.substring(0,i); } else if ( s.charAt(i)=='\'' || s.charAt(i)=='"' ){ if ( s.charAt(i)==inString ) { inString= 0; } else { inString= s.charAt(i); } } } return s; } /** * get the documentation, looking for terminator. TODO: this should all be redone with Jython AST. * @param line * @param read continue to read from here. * @return the source containing the documentation. */ private static String popDoc( String line, BufferedReader read ) throws IOException { String lin= line.trim(); if ( lin.startsWith("\"") && lin.endsWith("\"") ) { return line; } else if ( lin.startsWith("'") && lin.endsWith("'") ) { return line; } else if ( lin.startsWith("\"\"\"" ) || lin.startsWith("'''") ) { String term= lin.substring(0,3); if ( lin.endsWith(term) ) return line; StringBuilder build= new StringBuilder(line); build.append("\n"); line= read.readLine(); while ( line!=null ) { build.append(line).append("\n"); lin= line.trim(); if ( lin.endsWith(term) ) { break; } line= read.readLine(); } if ( line==null ) { throw new IllegalArgumentException("unterminated string"); } else { return build.toString(); } } else { return null; } } /** * return the imports for the python script, also the def's are * returned with a trivial definition, and assignments are converted to * be a trivial assignment. * * @param src jython source * @return subset sufficient to provide completions */ private static String sanitizeLeaveImports( String src ) { return SimplifyScriptSupport.simplifyScriptToCompletions( src ); } /** * put in function that is trivial to evaluate so we can still do completions on datasets. * @param interp */ private void putInGetDataSetStub( PythonInterpreter interp ) { String ss2= "def getDataSet( st, tr=None, mon=None ):\n return findgen(100)\n\n"; logger.finer(ss2); interp.exec( ss2 ); } public static String getLastLine( String script ) { int i = script.lastIndexOf("\n"); if ( i==-1 ) return script; // just one line String l= script.substring(i+1); // +1 is for the new line String s= l.trim(); if ( s.length()==0 ) return ""; // on an empty line LinkedList<String> lastLine= new LinkedList<>(); int i1= script.indexOf(s,i); String indent= script.substring(i+1,i1); lastLine.add( 0, l ); int i2= script.lastIndexOf("\n",i-1); String l2= script.substring(i2+1,i); lastLine.add( 0, l2 ); while ( l2.startsWith(indent) ) { i= i2; i2= script.lastIndexOf("\n",i-1); l2= script.substring(i2+1,i); lastLine.add( 0, l2 ); } return String.join( "\n",lastLine ); } /** * introduced to see if we can pop a little code from the end, in case we * are within a triple-quoted string. * @param script the script * @return the script, possibly with a few fewer lines. * @see SimplifyScriptSupport#alligatorParse(java.lang.String) */ public static String trimLinesToMakeValid( String script ) { return SimplifyScriptSupport.alligatorParse(script); } private int queryNames(CompletionContext cc, CompletionResultSet rs) throws BadLocationException { logger.fine("queryNames"); int count=0; String[] keywords = new String[]{ "def", "elif", "except", "from", "for", "finally", "import", "while", "print", "raise"}; //TODO: not complete for (String kw : keywords) { if (kw.startsWith(cc.completable)) { if ( rs!=null ) rs.addItem(new DefaultCompletionItem(kw, cc.completable.length(), kw, kw, null, JYTHONCOMMAND_SORT, JYTHONCOMMANDICON)); count++; } } PythonInterpreter interp = getInterpreter(); String eval; int eolnCarot= Utilities.getRowStart(editor, editor.getCaretPosition()); eval= editor.getText(0, eolnCarot); eval= trimLinesToMakeValid( eval ); if ( eolnCarot>0 ) { int startLastLine= Utilities.getRowStart(editor, eolnCarot-1 ); String lastLine= editor.getText( startLastLine, eolnCarot-startLastLine ); Matcher m= Pattern.compile("(\\s*)(\\S+).*(\\s)*").matcher(lastLine); if ( m.matches() ) { int i= m.group(1).length(); String indent= lastLine.substring(0,i); if ( !eval.endsWith("\n") ) { eval= eval + "\n" + indent + "__dummy__=1\n"; } else { eval= eval + indent + "__dummy__=1\n"; } } } if ( JythonCompletionProvider.getInstance().settings().isSafeCompletions() ) { try { eval= sanitizeLeaveImports( eval ); } catch ( Exception ex ) { // adding __dummy__ didn't work, so start removing lines at the end. eval= editor.getText(0, eolnCarot); eval= trimLinesToMakeValid( eval ); eval= sanitizeLeaveImports( eval ); } } try { interp.exec( JythonRefactory.fixImports(eval) ); } catch ( PyException ex ) { String message= "<html><p>Code completions couldn't run on a simplified version of the script. This may" + " due to a bug in the simplification process, or there may be a bug in the script. " + "The error is shown below, and the simplified script can be reveiwed using " + "Actions→Developer→\"Show Simplified Script used for Completions.\"</p><br><hr><code>"+ex.toString()+"</code>"; if ( rs!=null ) rs.addItem( new MessageCompletionItem("Eval error in code before current position", message)); int nlocal= getLocalsCompletions( interp, cc, rs); int nimportable; if ( cc.completable.length()>0 ) { nimportable= getImportableCompletions( eval, cc, rs ); } else { nimportable= 0; } return count + nlocal + nimportable + 1; } catch (IOException ex) { if ( rs!=null ) rs.addItem(new MessageCompletionItem("Error with completions",ex.toString())); return 0; } int nlocal= getLocalsCompletions( interp, cc, rs); int nimportable; if ( cc.completable.length()>0 ) { nimportable= getImportableCompletions( eval, cc, rs ); } else { nimportable= 0; } return count + nlocal + nimportable; } private static String argsList( Class[] classes ) { //String LPAREN="%28"; String LPAREN = "("; //String RPAREN="%29"; String RPAREN = ")"; String SPACE = " "; // "%20"; StringBuilder sig = new StringBuilder(); sig.append(LPAREN); List<String> sargs = new ArrayList<>(); for (Class arg : classes ) { sargs.add(arg.getSimpleName()); } sig.append(join(sargs, "," )); sig.append(RPAREN); return sig.toString(); } private static String methodArgs(Method javaMethod) { return argsList( javaMethod.getParameterTypes()); } /** * Javadocs don't have the path on the constructor internal links. * @param c * @return */ private static String constructorSignatureNew( Constructor c ) { String n= c.getName(); //String[] ss= n.split("\\."); String javadocPath = join( n.split("\\."), "/") + ".html"; StringBuilder sig = new StringBuilder(javadocPath); //String LPAREN="%28"; String LPAREN = "("; //String RPAREN="%29"; String RPAREN = ")"; String name= c.getName(); int i= name.lastIndexOf("."); if ( i>-1 ) name= name.substring(i+1); sig.append("#").append( name ).append(LPAREN); List<String> sargs = new ArrayList<>(); for (Class arg : c.getParameterTypes()) { sargs.add(arg.getCanonicalName()); } sig.append(join(sargs, "," )); sig.append(RPAREN); return sig.toString(); } private static String methodSignature(Method javaMethod) { String n= javaMethod.getDeclaringClass().getCanonicalName(); if ( n==null ) { // anonymous methods or inner class. return "<inner>"; } String javadocPath = join( n.split("\\."), "/") + ".html"; StringBuilder sig = new StringBuilder(javadocPath); //String LPAREN="%28"; String LPAREN = "("; //String RPAREN="%29"; String RPAREN = ")"; String SPACE = " "; // "%20"; sig.append("#").append(javaMethod.getName()).append(LPAREN); List<String> sargs = new ArrayList<>(); for (Class arg : javaMethod.getParameterTypes()) { sargs.add(arg.getCanonicalName()); } sig.append(join(sargs, "," )); sig.append(RPAREN); return sig.toString(); } private String fieldSignature(Field f) { String javadocPath = join(f.getDeclaringClass().getCanonicalName().split("\\."), "/") + ".html"; StringBuilder sig = new StringBuilder(javadocPath); sig.append("#").append(f.getName()); return sig.toString(); } private String constructorSignature( Constructor f ) { String javadocPath = join( f.getDeclaringClass().getCanonicalName().split("\\."), "/") + ".html"; StringBuilder sig = new StringBuilder(javadocPath); int i= f.getName().lastIndexOf("."); sig.append("#").append(f.getName().substring(i + 1)); return sig.toString(); } /** * Do dataset URL completions on strings that start with / and getDataSet calls. * There's a bug here where "formatDataSet" is not detected as the context in * formatDataSet(ds,'/home/jbf/foo.cdf') * @param cc * @param arg0 * @return */ private int queryStringLiteralArgument(CompletionContext cc, CompletionResultSet arg0) { String method = cc.contextString; int [] pos= new int[2]; Map<String,Object> r= DataSetUrlCompletionTask.popString(editor,pos); String s= (String)r.get("string"); if (method.equals("getDataSet") || method.equals("plot") || method.equals("plotx") || method.equals("getCompletions") ) { DataSetUrlCompletionTask task = new DataSetUrlCompletionTask(editor); task.query(arg0); } else if ( method.equals("File") && s.startsWith("/") ) { DataSetUrlCompletionTask task = new DataSetUrlCompletionTask(editor); task.query(arg0); } else if ( method.equals("'resourceURI'") ) { DataSetUrlCompletionTask task = new DataSetUrlCompletionTask(editor); task.query(arg0); } else if ( s.startsWith("/") ) { DataSetUrlCompletionTask task = new DataSetUrlCompletionTask(editor); task.query(arg0); } return 0; } /** * show the documentation found for the command. * @param cc * @param result * @return * @throws BadLocationException */ private int queryCommandArgument(CompletionContext cc, CompletionResultSet result ) throws BadLocationException { logger.fine("queryCommandArgument"); String method = cc.contextString; PythonInterpreter interp = getInterpreter(); String eval; eval= editor.getText(0, Utilities.getRowStart(editor, editor.getCaretPosition())); if ( JythonCompletionProvider.getInstance().settings().isSafeCompletions() ) { eval= sanitizeLeaveImports( eval ); } try { interp.exec(eval); } catch ( PyException ex ) { result.addItem(new MessageCompletionItem("Eval error in code before current position", ex.toString())); return 0; } try { PyObject po= interp.eval(method); PyObject doc= interp.eval(method+".__doc__"); if ( po instanceof PyFunction ) { method= getPyFunctionSignature((PyFunction)po); String signature= makeInlineSignature( po, doc ); result.addItem( new MessageCompletionItem( method, signature ) ); } else if ( po instanceof PyReflectedFunction ) { PyReflectedFunction prf = (PyReflectedFunction) po; List<String> labels= new ArrayList(); List<String> signatures= new ArrayList(); List<String> argss= new ArrayList(); doPyReflectedFunction(eval, prf, labels, signatures, argss ); for ( int jj=0; jj<labels.size(); jj++ ) { String signature= signatures.get(jj); if ( signature==null ) continue; // I don't this this happens, but findbugs pointed out inconsistent code. String link = getLinkForJavaSignature(signature); DefaultCompletionItem item = new DefaultCompletionItem( method, 0, signature, labels.get(jj), link ); item.setReferenceOnly(true); result.addItem( item ); //result.addItem( new MessageCompletionItem( method + labels.get(jj), signatures.get(jj) ) ); } } else { String signature= makeInlineSignature( po, doc ); result.addItem( new MessageCompletionItem( method, signature ) ); } } catch ( RuntimeException ex ) { return 0; } //logger.fine( "DefaultCompletionItem("+ss+","+cc.completable.length()+",\n" + ss + argss.get(jj)+",\n"+label+",\n"+link+")"); return 1; } /** * return an interpreter to match the one the user's code lives in. * @return */ private PythonInterpreter getInterpreter() { PythonInterpreter interp; try { if (jythonInterpreterProvider != null) { interp = jythonInterpreterProvider.createInterpreter(); } else { interp = new PythonInterpreter(); } if ( org.autoplot.jythonsupport.Util.isLegacyImports() ) { URL imports = JythonOps.class.getResource("/imports2017.py"); try (InputStream in = imports.openStream()) { interp.execfile(in,"imports2017.py"); } } interp.set("PWD","file:/tmp/"); return interp; } catch (IOException ex) { throw new RuntimeException(ex); } } @Override public void refresh(CompletionResultSet arg0) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void cancel() { //throw new UnsupportedOperationException("Not supported yet."); } public static int getLocalsCompletions(PythonInterpreter interp, CompletionContext cc, CompletionResultSet rs) { int count= 0; List<DefaultCompletionItem> rr= getLocalsCompletions( interp, cc ); for ( DefaultCompletionItem item: rr ) { if ( rs!=null ) rs.addItem( item ); count++; } return count; } /** * get completions by looking at importLookup.jy, which is a list of commonly imported codes. * @param source the script source. * @param cc * @param result * @return */ public static int getImportableCompletions( String source, CompletionContext cc, CompletionResultSet result ) { int count= 0; List<String> completions= JythonToJavaConverter.guessCompletions(cc.completable); for ( String ss: completions ) { String pkg= JythonToJavaConverter.guessPackage(ss); if ( !JythonToJavaConverter.hasImport( source, pkg, ss ) ) { String javaClass= pkg+"."+ss; String signature= join( javaClass.split("\\."), "/") + ".html"; String link= JavadocLookup.getInstance().getLinkForJavaSignature(signature); ClassImportCompletionItem ci= new ClassImportCompletionItem( cc.completable, cc.completable.length(), ss, ss + " and import from " + pkg, link, 0, JAVA_CLASS_ICON, pkg, ss ); result.addItem( ci ); } count++; } return count; } /** * replace java names like org.das2.qds.QDataSet with less-ominous names like "QDataSet" * @param label * @return the simplified name. */ private static String hideJavaPaths( String label ) { StringBuffer build= new StringBuffer(); Pattern p= Pattern.compile("(org.das2.qds.QDataSet|java.lang.String|java.lang.Object|org.das2.util.monitor.ProgressMonitor|org.das2.datum.DatumRange|org.das2.datum.Datum)"); Matcher m= p.matcher(label); while ( m.find() ) { String s= m.group(1); switch (s) { case "org.das2.qds.QDataSet": m.appendReplacement(build,"QDataSet"); break; case "java.lang.String": m.appendReplacement(build,"String"); break; case "java.lang.Object": m.appendReplacement(build,"Object"); break; case "org.das2.util.monitor.ProgressMonitor": m.appendReplacement(build,"Monitor"); break; case "org.das2.datum.DatumRange": m.appendReplacement(build,"DatumRange"); break; case "org.das2.datum.Datum": m.appendReplacement(build,"Datum"); break; default: break; } } m.appendTail(build); return build.toString(); } public static String escapeHtml( String s ) { StringBuffer out= new StringBuffer(); Pattern p= Pattern.compile("([\\<\\>])"); Matcher m= p.matcher(s); while ( m.find() ) { m.appendReplacement( out, "" ); String ss= m.group(1); if ( ss.equals("<") ) { out.append( "<"); } else if ( ss.equals(">") ) { out.append( ">"); } } m.appendTail(out); return out.toString(); } /** * get the python signature for the function. I can't figure out how to get defaults for the named keyword parameters. * @param pf the function * @return String like "docDemo8( arg )" */ private static String getPyJavaClassSignature( PyJavaClass pf ) { Class javaClass= getJavaClass(pf); return javaClass.getCanonicalName().replaceAll("\\.","/"); } /** * get the python signature for the function. I can't figure out how to get defaults for the named keyword parameters. * @param pf the function * @return String like "docDemo8( arg )" */ private static String getPyFunctionSignature( PyFunction pf ) { Object[] defaults= pf.func_defaults; String[] vars= ((PyTableCode)pf.func_code).co_varnames; int count= ((PyTableCode)pf.func_code).co_argcount; StringBuilder sig= new StringBuilder( pf.__name__+"(" ); if ( count>0 ) { if ( defaults.length==vars.length ) { sig.append(vars[0]).append("=").append(defaults[0]); } else { sig.append(vars[0]); } } int nreq= vars.length-defaults.length; for ( int i=1; i<count; i++ ) { if ( i>=nreq) { sig.append(",").append(vars[i]).append("=").append(defaults[i-nreq]); } else { sig.append(",").append(vars[i]); } } if ( count + defaults.length == vars.length-2 ) { // quick kludge for var args see /home/jbf/ct/autoplot/script/demos/operators/synchronizeDemo.jy sig.append(",..."); } sig.append(")"); return sig.toString(); } /** * get __doc__ from the function. * @param po PyFunction, typically. * @param doc the documentation for the function. * @return "inline:..." */ private static String makeInlineSignature( PyObject po, PyObject doc ) { String sig= ( po instanceof PyFunction ) ? getPyFunctionSignature((PyFunction)po) : ""; String signature= doc instanceof PyNone ? "(No documentation)" : doc.toString(); if ( sig.length()>0 ) { sig= "<b>"+sig+ "</b><br><br>"; } String[] ss2= signature.split("\n"); if ( ss2.length>1 ) { for ( int jj= 0; jj< ss2.length; jj++ ){ ss2[jj]= escapeHtml(ss2[jj]); } if ( !signature.startsWith("<html>" ) ) { signature= "<html>"+sig+join( ss2, "<br>" )+"</html>"; } else { signature= "<html>"+sig+ signature.substring(6)+"</html>"; } } else { signature= "<html>"+sig+ signature+"</html>"; } signature= "inline:" + signature; return signature; } private static void doPyReflectedFunction( String ss, PyReflectedFunction prf, List<String> labels, List<String> signatures, List<String> argss ) { PyReflectedFunctionPeeker peek = new PyReflectedFunctionPeeker(prf); for ( int jj=0; jj<peek.getArgsCount(); jj++ ) { Method method1= peek.getMethod(jj); String signature = methodSignature(method1); String args = methodArgs(method1); int j= signature.indexOf("#"); String label= ss + "() JAVA"; if ( j>-1 ) { label= signature.substring(j+1); label= hideJavaPaths( label ); Class ret= method1.getReturnType(); label= label + "->" + hideJavaPaths( ret.getCanonicalName() ); } signatures.add(signature); labels.add(label); argss.add(args); } } private static void doConstructors( Constructor[] constructors, List<String> labels, List<String> signatures, String ss, List<String> argss ) { for (Constructor constructor : constructors) { String signature = constructorSignatureNew(constructor); if ( signature.contains("$") ) { signature= signature.replaceAll("\\$","."); } int j= signature.indexOf("#"); String label= ss + "() JAVA"; if (j>-1) { label= signature.substring(j+1); label= hideJavaPaths( label ); Class ret = constructor.getDeclaringClass(); label= label + "->" + hideJavaPaths( ret.getCanonicalName() ); } signatures.add(signature); labels.add(label); argss.add(argsList(constructor.getParameterTypes())); } } /** * sorts all the lists by the first list. * See http://stackoverflow.com/questions/15400514/syncronized-sorting-between-two-arraylists/24688828#24688828 * Note the key list must be repeated for it to be sorted as well! * @param <T> the list type * @param key the list used to sort * @param lists the lists to be sorted, often containing the key as well. */ public static <T extends Comparable<T>> void keySort( final List<T> key, List<?>... lists){ // Create a List of indices List<Integer> indices = new ArrayList<>(); for(int i = 0; i < key.size(); i++) { indices.add(i); } // Sort the indices list based on the key Collections.sort(indices, new Comparator<Integer>(){ @Override public int compare(Integer i, Integer j) { return key.get(i).compareTo(key.get(j)); } }); // Create a mapping that allows sorting of the List by N swaps. // Only swaps can be used since we do not know the type of the lists Map<Integer,Integer> swapMap = new HashMap<>(indices.size()); List<Integer> swapFrom = new ArrayList<>(indices.size()), swapTo = new ArrayList<>(indices.size()); for(int i = 0; i < key.size(); i++){ int k = indices.get(i); while(i != k && swapMap.containsKey(k)) { k = swapMap.get(k); } swapFrom.add(i); swapTo.add(k); swapMap.put(i, k); } // use the swap order to sort each list by swapping elements for(List<?> list : lists) { for(int i = 0; i < list.size(); i++) { Collections.swap(list, swapFrom.get(i), swapTo.get(i)); } } } /** * At some point we decided all the methods would take Object as well as QDataSet, and then convert * to these. This should be discouraged, and hide these in the popups. * @param m1 * @param m2 * @return */ private static boolean methodIsSuperset( String m1, String m2 ) { Pattern p0= Pattern.compile("([a-zA-Z0-9/]*\\.html)#([a-zA-Z0-9]*)\\((([a-zA-Z0-9\\.\\[\\]]+)?(,([a-zA-Z0-9\\.\\[\\]]+))*)\\)" ); Matcher m8= p0.matcher(m1); Matcher m9= p0.matcher(m2); if ( m8.matches() && m9.matches() ) { String s1= m8.group(3); String s2= m9.group(3); String[] s8= s1.split(",",-2); String[] s9= s2.split(",",-2); if ( s8.length==s9.length ) { boolean superSet= true; for ( int i=0; i<s8.length; i++ ) { if ( !s8[i].equals("java.lang.Object") && !s8[i].equals(s9[i]) ) { superSet=false; } } return superSet; } } return false; } public static void reduceObject( List<String> signatures, List<String> labels, List<String> argss ) { if ( signatures.size()>1 ) { for ( int i=1; i<signatures.size(); i++ ) { if ( methodIsSuperset( signatures.get(0), signatures.get(i) ) ) { signatures.remove(0); labels.remove(0); argss.remove(0); break; } } } } public static List<DefaultCompletionItem> getLocalsCompletions(PythonInterpreter interp, CompletionContext cc) { List<DefaultCompletionItem> result= new ArrayList(); PyStringMap locals = (PyStringMap) interp.getLocals(); PyList po2 = locals.keys(); for (int i = 0; i < po2.__len__(); i++) { ImageIcon icon= null; PyString s = (PyString) po2.__getitem__(i); String ss = s.toString(); String signature = null; // java signature List<String> signatures= new ArrayList(); List<String> argss= new ArrayList(); if (ss.startsWith(cc.completable)) { if ( ss.endsWith(__CLASSTYPE) ) { ss= ss.substring(0,ss.length()-__CLASSTYPE.length()); if ( !ss.startsWith(cc.completable) ){ continue; } else { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss, ss, null, LOCALVAR_SORT, LOCALVARICON ) ); continue; } } logger.log(Level.FINER, "found completion item: {0}", ss); boolean allStatic= false; // true if the completion is a utility class. PyObject po = locals.get(s); String label = ss; List<String> labels= new ArrayList(); String args = ""; if (po instanceof PyReflectedFunction) { PyReflectedFunction prf = (PyReflectedFunction) po; doPyReflectedFunction( ss, prf, labels, signatures, argss ); } else if (po.isCallable()) { label = ss + "() "; if ( po instanceof PyFunction ) { label= getPyFunctionSignature((PyFunction)po); args= label.substring(ss.length()); } PyObject doc= interp.eval(ss+".__doc__"); signature= makeInlineSignature( po, doc ); } else if (po.isNumberType()) { switch (po.getType().getFullName()) { case "javaclass": case "javainnerclass": label = ss; Class jclass= getJavaClass((PyJavaClass)po); String n= jclass.getCanonicalName(); allStatic= true; logger.log(Level.FINER, "check for non-static methods: {0}", n); Method[] mm= jclass.getMethods(); for ( Method m: mm ) { if ( !m.getDeclaringClass().equals(Object.class) ) { if ( !Modifier.isStatic(m.getModifiers()) ) { logger.log(Level.FINEST, "not static: {0}", m.getName()); allStatic= false; } } } logger.log(Level.FINER, " class is all static methods: {0}", allStatic ); if ( allStatic ) { doConstructors(jclass.getConstructors(),labels,signatures,n,argss); for ( int i1=0; i1<argss.size(); i1++ ) { argss.set(i1,""); } } else { doConstructors(jclass.getConstructors(),labels,signatures,n,argss); } icon= JAVA_CONSTRUCTOR_ICON; //signature= join( n.split("\\."), "/") + ".html#"+ jclass.getSimpleName() + "()"; break; case "javapackage": label = ss; break; default: //TODO: check for PyFloat, etc. String sss= po.toString(); if ( po instanceof PyJavaInstance ) { Object jo= po.__tojava__(Object.class); if ( jo instanceof org.das2.qds.QDataSet ) { //TODO: mark it so we know it is a placeholder. sss= "dataset"; } else { sss= jo.toString(); } } if ( sss.contains("<") ) { // it's not what I think it is, a number label = ss; } else { label = ss + " = " + sss; } break; } } else if ( po instanceof PyJavaClass ) { } else { logger.log(Level.FINE, "skipping {0}", ss); } keySort( signatures, signatures, labels, argss ); if ( !signatures.isEmpty() ) { String objectRemoved= ""; int n= signatures.size(); reduceObject( signatures, labels, argss ); if ( signatures.size()!=n ) { objectRemoved= "*"; } for ( int jj= 0; jj<signatures.size(); jj++ ) { signature= signatures.get(jj); label= labels.get(jj)+objectRemoved; String link = null; if (signature != null) { link= getLinkForJavaSignature(signature); // TODO: inner class like Rectangle.Double is only Double } if ( ss.equals("dom") ) { link= "http://autoplot.org/developer.scripting#DOM"; } logger.log(Level.FINER, "DefaultCompletionItem({0},{1},\n{2}{3},\n{4},\n{5})", new Object[]{ss, cc.completable.length(), ss, argss.get(jj), label, link}); result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + argss.get(jj), label, link, JAVAMETHOD_SORT, icon ) ); } } else { String link = null; if ( signature!=null && signature.startsWith("inline:") ) { link= signature; } else if ( ss.equals("dom") ) { link= "http://autoplot.org/developer.scripting#DOM"; } else if (signature != null) { link= getLinkForJavaSignature(signature); } else if ( po instanceof PyJavaClass ) { link= getLinkForJavaSignature( getPyJavaClassSignature( (PyJavaClass)po ) ); } if ( po instanceof PyString ) { if ( ss.equals("PWD") ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, LOCALVAR_SORT, LOCALVARICON ) ); } else if ( !ss.equals("__name__") ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label+" -> "+po+"", link, LOCALVAR_SORT, LOCALVARICON ) ); } else { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label+" -> "+po+"", link, JAVASTATICFIELD_SORT, null ) ); } } else { if ( allStatic ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args + ".", label, link, JAVACLASS_SORT, JAVA_CLASS_ICON) ); } else { if ( po instanceof PyJavaClass ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, JAVACLASS_SORT, JAVA_CONSTRUCTOR_ICON ) ); } else if ( po.getType().toString().contains("Command") ) { // TODO: FIX THIS if ( ss.equals("plotx") ) continue; result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, AUTOCOMMAND_SORT, JAVA_JYTHON_METHOD_ICON ) ); } else if ( po instanceof PyFunction ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, AUTOCOMMAND_SORT, JAVA_JYTHON_METHOD_ICON ) ); } else if ( po instanceof PyClass ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, PYCLASS_SORT, JAVA_JYTHON_METHOD_ICON ) ); } else if ( po instanceof PyReflectedField ) { result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, PYREFLECTEDFIELD_SORT, JAVA_JYTHON_METHOD_ICON ) ); } else { switch (ss) { case "monitor": case "dom": case "PI": case "TAU": case "E": result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, AUTOVAR_SORT, LOCALVARICON ) ); break; case "params": case "outputParams": case "__doc__": //things I don't want developers to see result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, AUTOVARHIDE_SORT, LOCALVARICON ) ); break; default: result.add( new DefaultCompletionItem(ss, cc.completable.length(), ss + args, label, link, LOCALVAR_SORT, LOCALVARICON ) ); break; } } } } } } } logger.log( Level.FINE, "getLocalsCompletions found {0} completions", new Object[]{ result.size() } ); return result; } /** * return the Java class for the PyJavaClass. The implementation may change * with Jython2.7. * @param po the PyJavaClass wrapper. * @return the Java class. */ private static Class getJavaClass(PyJavaClass po) { PyJavaClassPeeker peek= new PyJavaClassPeeker(po); Class jclass= peek.getProxyClass(); return jclass; } /** * return a link to the documentation for a java signature. For standard library * things, this goes to Oracle's website. For other things, this goes * to the Autoplot/Das2 javadocs. * @param signature signature like javax.swing.JCheckBox#paramString() * @return the link, like http://docs.oracle.com/javase/7/docs/api/javax/swing/JCheckBox#paramString() */ private static String getLinkForJavaSignature(String signature) { return JavadocLookup.getInstance().getLinkForJavaSignature(signature); } /** * return an identifying icon for the object, or null. * @param jm java.lang.reflect.Method, or PyInteger, etc. * @return the icon or null. */ public static ImageIcon getIconFor(Object jm) { ImageIcon icon=null; if ( jm instanceof java.lang.reflect.Method ) { Method m= (Method)jm; if ( Modifier.isStatic(m.getModifiers()) ) { icon= JAVA_STATIC_METHOD_ICON; } else { icon= JAVA_METHOD_ICON; } } else if ( jm instanceof java.lang.reflect.Field ) { Field m= (Field)jm; if ( Modifier.isStatic(m.getModifiers()) ) { try { Object o= m.get(java.awt.Color.class); if ( o instanceof Color ) { Color testColor= (Color) o; return GraphUtil.colorImageIcon( testColor, 16, 16 ); } else { return JAVA_FIELD_ICON; } } catch (IllegalArgumentException | IllegalAccessException ex) { logger.log( Level.FINE, null, ex ); return JAVA_FIELD_ICON; } } else { icon= JAVA_FIELD_ICON; } } return icon; } }