3030
3131import java .io .File ;
3232import java .io .IOException ;
33+ import java .lang .reflect .Parameter ;
3334import java .nio .file .FileVisitOption ;
3435import java .nio .file .Files ;
3536import java .nio .file .Path ;
4445import java .util .regex .Pattern ;
4546import java .util .stream .Collectors ;
4647
48+ import javax .swing .text .JTextComponent ;
49+
4750import org .fife .ui .autocomplete .BasicCompletion ;
4851import org .fife .ui .autocomplete .Completion ;
4952import org .fife .ui .autocomplete .CompletionProvider ;
53+ import org .fife .ui .autocomplete .FunctionCompletion ;
54+ import org .fife .ui .autocomplete .ParameterChoicesProvider ;
55+ import org .fife .ui .autocomplete .ParameterizedCompletion ;
5056import org .python .indexer .types .NModuleType ;
57+ import org .scijava .ui .swing .script .autocompletion .CompletionText ;
5158
5259public class JythonAutoCompletions {
5360
@@ -82,17 +89,15 @@ public List<String> apply(final Path filepath) {
8289 .collect (Collectors .toList ());
8390 return modules ;
8491 } catch (Exception e ) {
85- System .out .println ("Failed to load jython module" );
86- if (JythonAutocompletionProvider .debug >= 2 ) e .printStackTrace ();
92+ JythonDev .print ("Failed to load jython module" , e );
8793 } finally {
8894 if (null != jar ) try { jar .close (); } catch (Exception ee ) {}
8995 }
9096 return Collections .emptyList ();
9197 }
9298 }).findFirst ().orElse (Collections .emptyList ());
9399 } catch (IOException e ) {
94- System .out .println ("Cannot find jython-slim-*.jar file" );
95- if (JythonAutocompletionProvider .debug >= 2 ) e .printStackTrace ();
100+ JythonDev .print ("Cannot find jython-slim-*.jar file" , e );
96101 } finally {
97102 jython_jar_modules = ls ;
98103 }
@@ -101,7 +106,7 @@ public List<String> apply(final Path filepath) {
101106
102107 public JythonAutoCompletions () {}
103108
104- public List <Completion > completionsFor (final CompletionProvider provider , String codeWithoutLastLine , final String lastLine , final String alreadyEnteredText ) {
109+ public List <Completion > completionsFor (final JythonAutocompletionProvider provider , String codeWithoutLastLine , final String lastLine , final String alreadyEnteredText ) {
105110
106111 // Replacing of text will start at crop, given the already entered text that is considered for replacement
107112 final int crop = lastLine .length () - alreadyEnteredText .length ();
@@ -118,14 +123,14 @@ public List<Completion> completionsFor(final CompletionProvider provider, String
118123 // so that the ParserFacade can work
119124 boolean add_pass = false ;
120125 if (codeWithoutLastLine .endsWith ("\n " )) {
121- JythonScriptParser . print ("codeWithoutLastLine ends with line break" );
126+ JythonDev . printTrace ("codeWithoutLastLine ends with line break" );
122127 final int priorLineBreak = codeWithoutLastLine .lastIndexOf ('\n' , codeWithoutLastLine .length () - 2 );
123128 final String endingLine = codeWithoutLastLine .substring (priorLineBreak + 1 );
124129 final Matcher me = endingCode .matcher (endingLine );
125130 if (me .find ()) {
126131 codeWithoutLastLine = codeWithoutLastLine .substring (0 , priorLineBreak + 1 ) + me .group (1 ) + me .group (2 );
127132 add_pass = true ;
128- JythonScriptParser . print ("changed code to: \n " + codeWithoutLastLine + "\n ###" );
133+ JythonDev . printTrace ("changed code to: \n " + codeWithoutLastLine + "\n ###" );
129134 }
130135 }
131136
@@ -137,10 +142,9 @@ public List<Completion> completionsFor(final CompletionProvider provider, String
137142 if (path .exists () && path .isDirectory () && !Scope .indexer .getLoadPath ().stream ().filter (s -> path .equals (new File (s ))).findFirst ().isPresent ())
138143 Scope .indexer .addPath (path .getAbsolutePath ());
139144 }
140- JythonScriptParser . print ("PYTHONPATH:\n " + String .join ("\n " , Scope .indexer .getLoadPath ()));
145+ JythonDev . printTrace ("PYTHONPATH:\n " + String .join ("\n " , Scope .indexer .getLoadPath ()));
141146 } catch (Exception e ) {
142- System .out .println ("Failed to add path from sys.path.append expression." );
143- if (JythonAutocompletionProvider .debug >= 2 ) e .printStackTrace ();
147+ JythonDev .print ("Failed to add path from sys.path.append expression." , e );
144148 }
145149
146150 // Situations to autocomplete:
@@ -172,8 +176,7 @@ public List<Completion> completionsFor(final CompletionProvider provider, String
172176 : s .substring (dir .length (), s .length () -3 )) // remove ending ".py"
173177 .replace ('/' , '.' ));
174178 } catch (IOException e ) {
175- System .out .println ("Failed to read jython module file." );
176- if (JythonAutocompletionProvider .debug >= 2 ) e .printStackTrace ();
179+ JythonDev .print ("Failed to read jython module file." , e );
177180 }
178181 return null ;
179182 }).flatMap (Function .identity ())
@@ -247,27 +250,94 @@ public List<Completion> completionsFor(final CompletionProvider provider, String
247250 String suffix = "" ;
248251 if (add_pass ) {
249252 add_pass = false ; // don't
250- JythonScriptParser . print ("Removed ' pass'" );
253+ JythonDev . printTrace ("Removed ' pass'" );
251254 suffix = ":\n " ;
252255 }
253256 varName = "____GRAB____" ; // an injected var to capture the returned class
254257 code = codeWithoutLastLine + (add_pass ? ": pass" : "" ) + suffix + lastLine .substring (0 , start ) + varName + " = " + lastLine .substring (start , lastLine .length () - 1 - seed .length ());
255- JythonScriptParser . print ("codeWithoutLastLine:\n " + codeWithoutLastLine );
258+ JythonDev . printTrace ("codeWithoutLastLine:\n " + codeWithoutLastLine );
256259 }
257- final DotAutocompletions da = JythonScriptParser .parseAST (code ).getLast ().find (varName , DotAutocompletions .EMPTY );
260+ final Scope scope = JythonScriptParser .parseAST (code );
261+ final DotAutocompletions da = scope .getLast ().find (varName , DotAutocompletions .EMPTY );
258262 final String fullPre = lastLine .substring (crop );
259263 final String pre = fullPre .substring (0 , fullPre .lastIndexOf (seed ));
260264 final String lowerCaseSeed = seed .toLowerCase ();
265+
266+ // Depends on scope
267+ provider .setParameterChoicesProvider (new ParameterChoicesProvider () {
268+ @ Override
269+ public List <Completion > getParameterChoices (JTextComponent tc ,
270+ org .fife .ui .autocomplete .ParameterizedCompletion .Parameter param ) {
271+ final Object typeObj = param .getTypeObject ();
272+ Class <?> clazz = null ;
273+ if (null != typeObj && typeObj instanceof Class ) {
274+ clazz = (Class <?>)typeObj ;
275+ JythonDev .printTrace ("class: " + clazz + ", " + clazz .getCanonicalName ());
276+ }
277+ switch (param .getType ()) {
278+ case "int" : clazz = Integer .class ; break ;
279+ case "long" : clazz = Long .class ; break ;
280+ case "float" : clazz = Float .class ; break ;
281+ case "double" : clazz = Double .class ; break ;
282+ case "boolean" : clazz = Boolean .class ; break ;
283+ case "char" : clazz = Character .class ; break ;
284+ case "short" : clazz = Short .class ; break ;
285+ case "byte" : clazz = Byte .class ; break ;
286+ }
287+ if (null != clazz ) try {
288+ JythonDev .printTrace ("type is: " + param .getType ());
289+ JythonDev .printTrace ("class is: " + clazz .getCanonicalName ());
290+ final List <Completion > bc = scope
291+ .findVarsByType (param .getType (), clazz )
292+ .map (varName -> new BasicCompletion (provider , varName ))
293+ .collect (Collectors .toList ());
294+ for (int i =0 ; i <bc .size (); ++i ) {
295+ ((BasicCompletion )bc .get (i )).setRelevance (bc .size () - i ); // ensure sorted from innermost scope to outer, rather than alphabetical
296+ }
297+ JythonDev .printTrace ("found bc: " + bc .size ());
298+ return bc ;
299+ } catch (Exception e ) {
300+ JythonDev .printError (e );
301+ }
302+ return Collections .emptyList ();
303+ }
304+ });
305+
261306 List <Completion > list = da .get ().stream ()
262307 .filter (s -> s .getReplacementText ().toLowerCase ().contains (lowerCaseSeed ))
263- .map (s -> s .getCompletion (provider , pre + s .getReplacementText (), s .getReplacementText ().startsWith (seed ) ? 1 : 0 ))
308+ //.map(s -> s.getCompletion(provider, pre + s.getReplacementText(), s.getReplacementText().startsWith(seed) ? 1 : 0))
309+ .map (s -> makeDotCompletion (pre , lowerCaseSeed , scope , s , provider ))
264310 .collect (Collectors .toList ());
265311 sortCompletions (list , seed );
266312 return list ;
267313 }
268314
269315 return Collections .emptyList ();
270316 }
317+
318+ private static Completion makeDotCompletion (final String pre , final String seed , final Scope scope , final CompletionText ct , final CompletionProvider provider ) {
319+ List <Parameter > ps = ct .getMethodArgs ();
320+ if (null != ps && !ps .isEmpty ()) {
321+ JythonDev .printTrace ("using FunctionCompletion: ps is " + ps .stream ().map (Parameter ::toString ).collect (Collectors .joining (", " , "[" , "]" )));
322+ final FunctionCompletion fc = new CustomFunctionCompletion (
323+ provider ,
324+ pre + ct .getReplacementText ().substring (0 , ct .getReplacementText ().length () -2 ), // remove trailing parentheses
325+ ct .getReturnType ());
326+ fc .setReturnValueDescription (ct .getReturnType ());
327+ fc .setShortDescription (ct .getSummary ()); // CompletionText.getDescription returns null
328+ //fc.setSummary(ct.getSummary()); // useless: later, FunctionCompletion doesn't use the summary from its superclass
329+ final ArrayList <ParameterizedCompletion .Parameter > params = new ArrayList <>();
330+ for (int i =0 ; i <ps .size (); ++i ) {
331+ params .add (new ParameterizedCompletion .Parameter (ps .get (i ).getType ().getCanonicalName (), ps .get (i ).getName (), false )); // can't mark last one as isEndParam = true, would not allow choosing it for some reason
332+ }
333+ fc .setParams (params );
334+ fc .setRelevance (fc .getReplacementText ().startsWith (seed ) ? 1 : 0 );
335+ return fc ;
336+ }
337+ JythonDev .printTrace ("not FunctionCompletion: replacement text is " + ct .getReplacementText ());
338+ return ct .getCompletion (provider , pre + ct .getReplacementText (), ct .getReplacementText ().startsWith (seed ) ? 1 : 0 );
339+ }
340+
271341
272342 @ SuppressWarnings ("unused" )
273343 private static String removeLastOptionalDot (final String s ) {
0 commit comments