Autocomplete popup performance for JavaScript - Java Beans

General Discussion on RSyntaxTextArea.

Moderator: robert

Autocomplete popup performance for JavaScript - Java Beans

Postby lubomir.benes » Thu Jun 27, 2013 9:39 am

I am using autocomplete in my JavaScript editor. I also want to have autocomplete for Java Beans. Therefore I have turned on the
Code: Select all
SourceCompletionProvider scp = (SourceCompletionProvider) javaScriptCompletionProvider.getDefaultCompletionProvider();
scp.getJavaScriptTypesFactory().setUseBeanProperties(true);
to get it. It works fine but for beans with large amount of methods and fields it was terribly slow. After looking for the problem I have hopefuly found it.

When completions are sorted string case insensitive comparison is used on string. Strings for comperison are get from toString() method of completions. In classes:
Code: Select all
org.fife.rsta.ac.js.completion.JSFunctionCompletion
org.fife.rsta.ac.js.completion.JavaScriptMethodCompletion

the toString() method calls everytime getNameAndParameters() method what is probably the cause of the slowness. I have changed it to cache the resulting string from this method with great success.
Here is the source code:

org.fife.rsta.ac.js.completion.JavaScriptMethodCompletion:
Code: Select all
package org.fife.rsta.ac.js.completion;

import java.util.ArrayList;
import java.util.List;

import javax.swing.Icon;

import org.fife.rsta.ac.java.rjc.ast.FormalParameter;
import org.fife.rsta.ac.java.rjc.ast.Method;
import org.fife.rsta.ac.js.IconFactory;
import org.fife.rsta.ac.js.ast.type.TypeDeclarationFactory;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.autocomplete.FunctionCompletion;


public class JavaScriptMethodCompletion extends FunctionCompletion implements
      JSCompletionUI, JSCompletion {

   private Method method;

   private String compareString;
   private boolean systemFunction;
        private String nameAndParameters;

   public JavaScriptMethodCompletion(CompletionProvider provider, Method method) {
      super(provider, method.getName(), null);
      this.method = method;
      int count = method.getParameterCount();
      List params = new ArrayList(count);
      for (int i = 0; i < count; i++) {
         FormalParameter param = method.getParameter(i);
         String name = param.getName();
         params.add(new FunctionCompletion.Parameter(null, name));
      }
      setParams(params);
                nameAndParameters = createNameAndParameters();
   }


   public Icon getIcon() {
      return IconFactory.getIcon(systemFunction ? IconFactory.FUNCTION_ICON
            : IconFactory.DEFAULT_FUNCTION_ICON);
   }


   public int getRelevance() {
      return systemFunction ? GLOBAL_FUNCTION_RELEVANCE : DEFAULT_FUNCTION_RELEVANCE;
   }


   public void setSystemFunction(boolean systemFunction) {
      this.systemFunction = systemFunction;
   }


   public boolean isSystemFunction() {
      return systemFunction;
   }


   public String getSummary() {
      String summary = getMethodSummary(); // Could be just the method name

      // If it's the Javadoc for the method...
      if (summary != null && summary.startsWith("/**")) {
         summary = org.fife.rsta.ac.java.Util.docCommentToHtml(summary);
      }

      return summary;
   }


   public String getSignature() {
      return nameAndParameters;
   }


   private String createNameAndParameters() {
      StringBuffer sb = new StringBuffer(getName());
      sb.append('(');
      int count = method.getParameterCount();
      for (int i = 0; i < count; i++) {
         FormalParameter fp = method.getParameter(i);
         sb.append(fp.getName());
         if (i < count - 1) {
            sb.append(", ");
         }
      }
      sb.append(')');
      return sb.toString();
   }


   /**
    * Overridden since <code>equals()</code> is overridden.
    */
   public int hashCode() {
      return getCompareString().hashCode();
   }


   /**
    * {@inheritDoc}
    */
   public String toString() {
      return getSignature();
   }


   private String getMethodSummary() {
      String docComment = method.getDocComment();
      return docComment != null ? docComment : method.toString();
   }


   /**
    * {@inheritDoc}
    */
   public int compareTo(Object o) {
      int rc = -1;
      if (o==this) {
         rc = 0;
      }
      else if (o instanceof JSCompletion) {
         JSCompletion c2 = (JSCompletion)o;
         rc= getLookupName().compareTo(c2.getLookupName());
      }
      else if (o instanceof Completion) {
         Completion c2 = (Completion) o;
         rc = toString().compareTo(c2.toString());
         if (rc == 0) { // Same text value
            String clazz1 = getClass().getName();
            clazz1 = clazz1.substring(clazz1.lastIndexOf('.'));
            String clazz2 = c2.getClass().getName();
            clazz2 = clazz2.substring(clazz2.lastIndexOf('.'));
            rc = clazz1.compareTo(clazz2);
         }
      }

      return rc;
   }


   public boolean equals(Object obj) {
      if(obj == this) {
         return true;
      }
      if(obj instanceof JSCompletion)
      {
         JSCompletion jsComp = (JSCompletion) obj;
         return getLookupName().equals(jsComp.getLookupName());
      }
      return super.equals(obj);
   }


   private String getCompareString() {

      /*
       * This string compares the following parts of methods in this order, to
       * optimize sort order in completion lists.
       *
       * 1. First, by name 2. Next, by number of parameters. 3. Finally, by
       * parameter type.
       */

      if (compareString == null) {
         StringBuffer sb = new StringBuffer(getName());
         // NOTE: This will fail if a method has > 99 parameters (!)
         int paramCount = getParamCount();
         if (paramCount < 10) {
            sb.append('0');
         }
         sb.append(paramCount);
         for (int i = 0; i < paramCount; i++) {
            String type = getParam(i).getType();
            sb.append(type);
            if (i < paramCount - 1) {
               sb.append(',');
            }
         }
         compareString = sb.toString();
      }

      return compareString;

   }


   public String getDefinitionString() {
      return getSignature();
   }
   
   public String getType(boolean qualified) {
      return TypeDeclarationFactory.convertJavaScriptType("void", qualified);
   }
   
   public String getEnclosingClassName(boolean fullyQualified) {
      return null;
   }


   public String getLookupName() {
      StringBuffer sb = new StringBuffer(getName());
      sb.append('(');
      int count = getParamCount();
      for (int i = 0; i < count; i++) {
         sb.append("p");
         if (i < count - 1) {
            sb.append(",");
         }
      }
      sb.append(')');
      return sb.toString();
   }

   

}

org.fife.rsta.ac.js.completion.JSFunctionCompletion:
Code: Select all
/*
 * 02/25/2012
 *
 * Copyright (C) 2012 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.js.completion;

import java.util.ArrayList;
import java.util.List;

import javax.swing.Icon;
import javax.swing.text.JTextComponent;

import org.fife.rsta.ac.java.classreader.MethodInfo;
import org.fife.rsta.ac.java.rjc.ast.Method;
import org.fife.rsta.ac.js.IconFactory;
import org.fife.rsta.ac.js.JavaScriptHelper;
import org.fife.rsta.ac.js.SourceCompletionProvider;
import org.fife.rsta.ac.js.ast.type.TypeDeclarationFactory;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.autocomplete.FunctionCompletion;
import org.fife.ui.autocomplete.ParameterizedCompletion;


public class JSFunctionCompletion extends FunctionCompletion implements
      JSCompletion {

   private JSMethodData methodData;
   private String compareString;
        private String nameAndParameters;

   public JSFunctionCompletion(CompletionProvider provider, MethodInfo method) {
      this(provider, method, false);
   }


   public JSFunctionCompletion(CompletionProvider provider,
         MethodInfo methodInfo, boolean showParameterType) {
      super(provider, getMethodName(methodInfo), null);
      this.methodData = new JSMethodData(methodInfo,
            ((SourceCompletionProvider) provider).getJarManager());
      List params = populateParams(methodData, showParameterType);
      setParams(params);
                nameAndParameters = createNameAndParameters();
   }
   
   private static String getMethodName(MethodInfo info)
   {
      if(info.isConstructor()){
         return TypeDeclarationFactory.convertJavaScriptType(info.getClassFile().getClassName(true), false);
      } else {
         return info.getName();
      }
   }

   private List populateParams(JSMethodData methodData,
         boolean showParameterType) {
      MethodInfo methodInfo = methodData.getMethodInfo();
      int count = methodInfo.getParameterCount();
      String[] paramTypes = methodInfo.getParameterTypes();
      List params = new ArrayList(count);
      for (int i = 0; i < count; i++) {
         String name = methodData.getParameterName(i);
         String type = methodData.getParameterType(paramTypes, i);
         params.add(new JSFunctionParam(type, name, showParameterType));
      }

      return params;
   }


   /**
    * {@inheritDoc}
    */
   public int compareTo(Object o) {
      int rc = -1;
      if (o==this) {
         rc = 0;
      }
      else if (o instanceof JSCompletion) {
         JSCompletion c2 = (JSCompletion)o;
         rc = getLookupName().compareTo(c2.getLookupName());
      }
      else if (o instanceof Completion) {
         Completion c2 = (Completion) o;
         rc = toString().compareTo(c2.toString());
         if (rc == 0) { // Same text value
            String clazz1 = getClass().getName();
            clazz1 = clazz1.substring(clazz1.lastIndexOf('.'));
            String clazz2 = c2.getClass().getName();
            clazz2 = clazz2.substring(clazz2.lastIndexOf('.'));
            rc = clazz1.compareTo(clazz2);
         }
      }

      return rc;
   }

   public boolean equals(Object obj) {
      return (obj instanceof JSCompletion)
            && ((JSCompletion) obj).getLookupName().equals(
                  getLookupName());
   }


   public String getAlreadyEntered(JTextComponent comp) {
      String temp = getProvider().getAlreadyEnteredText(comp);
      int lastDot = JavaScriptHelper
            .findLastIndexOfJavaScriptIdentifier(temp);
      if (lastDot > -1) {
         temp = temp.substring(lastDot + 1);
      }
      return temp;
   }


   private String getCompareString() {

      /*
       * This string compares the following parts of methods in this order, to
       * optimize sort order in completion lists.
       *
       * 1. First, by name 2. Next, by number of parameters. 3. Finally, by
       * parameter type.
       */

      if (compareString == null) {

         compareString = getLookupName();
      }

      return compareString;

   }


   public String getLookupName() {
      SourceCompletionProvider provider = (SourceCompletionProvider) getProvider();
      return provider.getJavaScriptEngine().getJavaScriptResolver(provider).getLookupText(methodData, getName());
   }


   public String getDefinitionString() {
      return getSignature();
   }


   private String getMethodSummary() {

      // String summary = methodData.getSummary(); // Could be just the method
      // name

      Method method = methodData.getMethod();
      String summary = method != null ? method.getDocComment() : null;
      // If it's the Javadoc for the method...
      if (summary != null && summary.startsWith("/**")) {
         summary = org.fife.rsta.ac.java.Util.docCommentToHtml(summary);
      }

      return summary != null ? summary : nameAndParameters;
   }


   private String createNameAndParameters() {
      return formatMethodAtString(getName(), methodData);
   }


   private static String formatMethodAtString(String name, JSMethodData method) {
      StringBuffer sb = new StringBuffer(name);
      sb.append('(');
      int count = method.getParameterCount();
      for (int i = 0; i < count; i++) {
         sb.append(method.getParameterName(i));
         if (i < count - 1) {
            sb.append(", ");
         }
      }
      sb.append(')');
      return sb.toString();
   }


   public String getSignature() {
      return nameAndParameters;
   }


   public String getSummary() {
      String summary = getMethodSummary(); // Could be just the method name

      // If it's the Javadoc for the method...
      if (summary != null && summary.startsWith("/**")) {
         summary = org.fife.rsta.ac.java.Util.docCommentToHtml(summary);
      }

      return summary;
   }


   public int hashCode() {
      return getCompareString().hashCode();
   }


   /**
    * {@inheritDoc}
    */
   public String toString() {
      return getSignature();
   }


   public String getType() {
      String value = getType(true);
      return TypeDeclarationFactory.convertJavaScriptType(value, false);
   }


   public String getType(boolean qualified) {
      return TypeDeclarationFactory.convertJavaScriptType(methodData
            .getType(qualified), qualified);
   }


   public Icon getIcon() {
      return methodData.isStatic() ? IconFactory
            .getIcon(IconFactory.PUBLIC_STATIC_FUNCTION_ICON) : IconFactory
            .getIcon(IconFactory.DEFAULT_FUNCTION_ICON);
   }


   public int getRelevance() {
      return DEFAULT_FUNCTION_RELEVANCE;
   }


   public String getEnclosingClassName(boolean fullyQualified) {
      return methodData.getEnclosingClassName(fullyQualified);
   }
   
   public JSMethodData getMethodData()
   {
      return methodData;
   }

   /**
    * Override the FunctionCompletion.Parameter to lookup the Javascript name
    * for the completion type
    */
   public static class JSFunctionParam extends
         ParameterizedCompletion.Parameter {

      private boolean showParameterType;


      public JSFunctionParam(Object type, String name,
            boolean showParameterType) {
         super(type, name);
         this.showParameterType = showParameterType;
      }


      public String getType() {
         return showParameterType ? TypeDeclarationFactory
               .convertJavaScriptType(super.getType(), false) : null;
      }

   }

}
lubomir.benes
 
Posts: 6
Joined: Thu Jun 27, 2013 9:23 am

Re: Autocomplete popup performance for JavaScript - Java Bea

Postby robert » Fri Jun 28, 2013 3:52 am

Thanks for finding this; I've committed this as revision 806 in SVN. It should be in the next release, although it may be a little bit before that's actually released.
User avatar
robert
 
Posts: 786
Joined: Sat May 10, 2008 5:16 pm


Return to Open Discussion

Who is online

Users browsing this forum: No registered users and 6 guests

cron