/**
  * Checks an element that must be defined according to the extension point schema. Throws an
  * <code>InvalidRegistryObjectException</code> if <code>obj</code> is <code>null</code>.
  */
 private void checkNotNull(Object obj, String attribute) throws InvalidRegistryObjectException {
   if (obj == null) {
     Object[] args = {getId(), fElement.getContributor().getName(), attribute};
     String message =
         Messages.format(
             RubyTextMessages.CompletionProposalComputerDescriptor_illegal_attribute_message,
             args);
     IStatus status =
         new Status(IStatus.WARNING, RubyPlugin.getPluginId(), IStatus.OK, message, null);
     RubyPlugin.log(status);
     throw new InvalidRegistryObjectException();
   }
 }
 private IStatus createExceptionStatus(InvalidRegistryObjectException x) {
   // extension has become invalid - log & disable
   String blame = createBlameMessage();
   String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_invalid;
   return new Status(
       IStatus.INFO, RubyPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); // $NON-NLS-1$
 }
Exemple #3
0
  /**
   * Inspects the context of the compilation unit around <code>completionPosition</code> and feeds
   * the collector with proposals.
   *
   * @param viewer the text viewer
   * @param completionPosition the context position in the document of the text viewer
   * @param compilationUnit the compilation unit (may be <code>null</code>)
   */
  public void complete(ITextViewer viewer, int completionPosition, IRubyScript compilationUnit) {
    IDocument document = viewer.getDocument();

    if (!(fContextType instanceof RubyScriptContextType)) return;

    Point selection = viewer.getSelectedRange();

    // remember selected text
    String selectedText = null;
    if (selection.y != 0) {
      try {
        selectedText = document.get(selection.x, selection.y);
      } catch (BadLocationException e) {
      }
    }

    RubyScriptContext context =
        ((RubyScriptContextType) fContextType)
            .createContext(document, completionPosition, selection.y, compilationUnit);
    context.setVariable("selection", selectedText); // $NON-NLS-1$
    int start = context.getStart();
    int end = context.getEnd();
    IRegion region = new Region(start, end - start);

    Template[] templates = RubyPlugin.getDefault().getTemplateStore().getTemplates();

    if (selection.y == 0) {
      for (int i = 0; i != templates.length; i++)
        if (context.canEvaluate(templates[i]))
          fProposals.add(
              new TemplateProposal(
                  templates[i],
                  context,
                  region,
                  RubyPluginImages.get(RubyPluginImages.IMG_OBJS_TEMPLATE)));

    } else {

      if (context.getKey().length() == 0) context.setForceEvaluation(true);

      boolean multipleLinesSelected = areMultipleLinesSelected(viewer);

      for (int i = 0; i != templates.length; i++) {
        Template template = templates[i];
        if (context.canEvaluate(template)
            && template.getContextTypeId().equals(context.getContextType().getId())
            && (!multipleLinesSelected && template.getPattern().indexOf($_WORD_SELECTION) != -1
                || (multipleLinesSelected
                    && template.getPattern().indexOf($_LINE_SELECTION) != -1))) {
          fProposals.add(
              new TemplateProposal(
                  templates[i],
                  context,
                  region,
                  RubyPluginImages.get(RubyPluginImages.IMG_OBJS_TEMPLATE)));
        }
      }
    }
  }
 /* (non-Rubydoc)
  * Method declared on IElementChangedListener.
  */
 public void elementChanged(final ElementChangedEvent event) {
   try {
     // 58952 delete project does not update Package Explorer [package explorer]
     // if the input to the viewer is deleted then refresh to avoid the display of stale elements
     if (inputDeleted()) return;
     processDelta(event.getDelta());
   } catch (RubyModelException e) {
     RubyPlugin.log(e);
   }
 }
 private IStatus createExceptionStatus(RuntimeException x) {
   // misbehaving extension - log & disable
   String blame = createBlameMessage();
   String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_runtime_ex;
   return new Status(
       IStatus.WARNING,
       RubyPlugin.getPluginId(),
       IStatus.OK,
       blame + " " + reason,
       x); //$NON-NLS-1$
 }
 private IStatus createExceptionStatus(CoreException x) {
   // unable to instantiate the extension - log & disable
   String blame = createBlameMessage();
   String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_instantiation;
   return new Status(
       IStatus.ERROR,
       RubyPlugin.getPluginId(),
       IStatus.OK,
       blame + " " + reason,
       x); //$NON-NLS-1$
 }
 private IStatus createAPIViolationStatus(String operation) {
   String blame = createBlameMessage();
   Object[] args = {operation};
   String reason =
       Messages.format(RubyTextMessages.CompletionProposalComputerDescriptor_reason_API, args);
   return new Status(
       IStatus.WARNING,
       RubyPlugin.getPluginId(),
       IStatus.OK,
       blame + " " + reason,
       null); //$NON-NLS-1$
 }
  protected Object[] fetchChildren(MethodWrapper methodWrapper) {
    IRunnableContext context = RubyPlugin.getActiveWorkbenchWindow();
    MethodWrapperRunnable runnable = new MethodWrapperRunnable(methodWrapper);
    try {
      context.run(true, true, runnable);
    } catch (InvocationTargetException e) {
      ExceptionHandler.handle(
          e,
          CallHierarchyMessages.CallHierarchyContentProvider_searchError_title,
          CallHierarchyMessages.CallHierarchyContentProvider_searchError_message);
      return EMPTY_ARRAY;
    } catch (InterruptedException e) {
      return new Object[] {TreeTermination.SEARCH_CANCELED};
    }

    return runnable.getCalls();
  }
  public void run() {
    if (!(selection instanceof TextSelection)) {
      return;
    }
    String replacementValue = ((TextSelection) selection).getText();
    if (replacementValue == null || replacementValue.length() == 0) {
      replacementValue = "self";
    }
    final String evaluationText = expression.substitute(replacementValue);
    ITextSelection textSelection =
        new ITextSelection() {

          public int getOffset() {
            return 0;
          }

          public int getLength() {
            return 0;
          }

          public int getStartLine() {
            return 0;
          }

          public int getEndLine() {
            return 0;
          }

          public String getText() {
            return evaluationText;
          }

          public boolean isEmpty() {
            return false;
          }
        };

    InspectAction inspectAction = new InspectAction();
    inspectAction.selectionChanged(null, textSelection);
    inspectAction.setActiveEditor(this, (IEditorPart) RubyPlugin.getActivePage().getActivePart());
    inspectAction.run(this);

    super.run();
  }
  /*
   * Assume that the hierarchy is intact (no refresh needed)
   */
  private void processDelta(IRubyElementDelta delta, ArrayList changedTypes) {
    IRubyElement element = delta.getElement();
    switch (element.getElementType()) {
      case IRubyElement.TYPE:
        processTypeDelta((IType) element, changedTypes);
        processChildrenDelta(delta, changedTypes); // (inner types)
        break;
      case IRubyElement.RUBY_MODEL:
      case IRubyElement.RUBY_PROJECT:
      case IRubyElement.SOURCE_FOLDER_ROOT:
      case IRubyElement.SOURCE_FOLDER:
        processChildrenDelta(delta, changedTypes);
        break;
      case IRubyElement.SCRIPT:
        IRubyScript cu = (IRubyScript) element;
        if (!RubyModelUtil.isPrimary(cu)) {
          return;
        }

        if (delta.getKind() == IRubyElementDelta.CHANGED
            && isPossibleStructuralChange(delta.getFlags())) {
          try {
            if (cu.exists()) {
              IType[] types = cu.getAllTypes();
              for (int i = 0; i < types.length; i++) {
                processTypeDelta(types[i], changedTypes);
              }
            }
          } catch (RubyModelException e) {
            RubyPlugin.log(e);
          }
        } else {
          processChildrenDelta(delta, changedTypes);
        }
        break;
    }
  }
/**
 * The description of an extension to the <code>org.rubypeople.rdt.ui.rubyCompletionProposalComputer
 * </code> extension point. Instances are immutable. Instances can be obtained from a {@link
 * CompletionProposalComputerRegistry}.
 *
 * @see CompletionProposalComputerRegistry
 * @since 1.0.0
 */
final class CompletionProposalComputerDescriptor {
  /** The default category id. */
  private static final String DEFAULT_CATEGORY_ID =
      "org.rubypeople.rdt.ui.defaultProposalCategory"; //$NON-NLS-1$
  /** The extension schema name of the category id attribute. */
  private static final String CATEGORY_ID = "categoryId"; // $NON-NLS-1$
  /** The extension schema name of the partition type attribute. */
  private static final String TYPE = "type"; // $NON-NLS-1$
  /** The extension schema name of the class attribute. */
  private static final String CLASS = "class"; // $NON-NLS-1$
  /** The extension schema name of the activate attribute. */
  private static final String ACTIVATE = "activate"; // $NON-NLS-1$
  /** The extension schema name of the partition child elements. */
  private static final String PARTITION = "partition"; // $NON-NLS-1$
  /** Set of Ruby partition types. */
  private static final Set<String> PARTITION_SET;
  /** The name of the performance event used to trace extensions. */
  private static final String PERFORMANCE_EVENT =
      RubyPlugin.getPluginId() + "/perf/content_assist/extensions"; // $NON-NLS-1$
  /**
   * If <code>true</code>, execution time of extensions is measured and the data forwarded to core's
   * {@link PerformanceStats} service.
   */
  private static final boolean MEASURE_PERFORMANCE = PerformanceStats.isEnabled(PERFORMANCE_EVENT);
  /**
   * Independently of the {@link PerformanceStats} service, any operation that takes longer than
   * {@value} milliseconds will be flagged as an violation. This timeout does not apply to the first
   * invocation, as it may take longer due to plug-in initialization etc. See also {@link
   * #fIsReportingDelay}.
   */
  private static final long MAX_DELAY = 5000;

  /* log constants */
  private static final String COMPUTE_COMPLETION_PROPOSALS =
      "computeCompletionProposals()"; //$NON-NLS-1$
  private static final String COMPUTE_CONTEXT_INFORMATION =
      "computeContextInformation()"; //$NON-NLS-1$
  private static final String SESSION_STARTED = "sessionStarted()"; // $NON-NLS-1$
  private static final String SESSION_ENDED = "sessionEnded()"; // $NON-NLS-1$

  static {
    Set<String> partitions = new HashSet<String>();
    partitions.add(IDocument.DEFAULT_CONTENT_TYPE);
    partitions.add(IRubyPartitions.RUBY_MULTI_LINE_COMMENT);
    partitions.add(IRubyPartitions.RUBY_SINGLE_LINE_COMMENT);
    partitions.add(IRubyPartitions.RUBY_REGULAR_EXPRESSION);
    partitions.add(IRubyPartitions.RUBY_STRING);
    partitions.add(IRubyPartitions.RUBY_COMMAND);

    PARTITION_SET = Collections.unmodifiableSet(partitions);
  }

  /** The identifier of the extension. */
  private final String fId;
  /** The name of the extension. */
  private final String fName;
  /** The class name of the provided <code>IRubyCompletionProposalComputer</code>. */
  private final String fClass;
  /** The activate attribute value. */
  private final boolean fActivate;
  /** The partition of the extension (element type: {@link String}). */
  private final Set<String> fPartitions;
  /** The configuration element of this extension. */
  private final IConfigurationElement fElement;
  /** The registry we are registered with. */
  private final CompletionProposalComputerRegistry fRegistry;
  /** The computer, if instantiated, <code>null</code> otherwise. */
  private IRubyCompletionProposalComputer fComputer;
  /** The ui category. */
  private final CompletionProposalCategory fCategory;
  /** The first error message in the most recent operation, or <code>null</code>. */
  private String fLastError;
  /**
   * Tells whether to inform the user when <code>MAX_DELAY</code> has been exceeded. We start timing
   * execution after the first session because the first may take longer due to plug-in activation
   * and initialization.
   */
  private boolean fIsReportingDelay = false;
  /** The start of the last operation. */
  private long fStart;

  /**
   * Creates a new descriptor.
   *
   * @param element the configuration element to read
   * @param registry the computer registry creating this descriptor
   */
  CompletionProposalComputerDescriptor(
      IConfigurationElement element,
      CompletionProposalComputerRegistry registry,
      List<CompletionProposalCategory> categories)
      throws InvalidRegistryObjectException {
    Assert.isLegal(registry != null);
    Assert.isLegal(element != null);

    fRegistry = registry;
    fElement = element;
    IExtension extension = element.getDeclaringExtension();
    fId = extension.getUniqueIdentifier();
    checkNotNull(fId, "id"); // $NON-NLS-1$

    String name = extension.getLabel();
    if (name.length() == 0) fName = fId;
    else fName = name;

    Set<String> partitions = new HashSet<String>();
    IConfigurationElement[] children = element.getChildren(PARTITION);
    if (children.length == 0) {
      fPartitions = PARTITION_SET; // add to all partition types if no partition is configured
    } else {
      for (int i = 0; i < children.length; i++) {
        String type = children[i].getAttribute(TYPE);
        checkNotNull(type, TYPE);
        partitions.add(type);
      }
      fPartitions = Collections.unmodifiableSet(partitions);
    }

    String activateAttribute = element.getAttribute(ACTIVATE);
    fActivate = Boolean.valueOf(activateAttribute).booleanValue();

    fClass = element.getAttribute(CLASS);
    checkNotNull(fClass, CLASS);

    String categoryId = element.getAttribute(CATEGORY_ID);
    if (categoryId == null) categoryId = DEFAULT_CATEGORY_ID;
    CompletionProposalCategory category = null;
    for (CompletionProposalCategory cat : categories) {
      if (cat.getId().equals(categoryId)) {
        category = cat;
        break;
      }
    }
    if (category == null) {
      // create a category if it does not exist
      fCategory = new CompletionProposalCategory(categoryId, fName, registry);
      categories.add(fCategory);
    } else {
      fCategory = category;
    }
  }

  /**
   * Checks an element that must be defined according to the extension point schema. Throws an
   * <code>InvalidRegistryObjectException</code> if <code>obj</code> is <code>null</code>.
   */
  private void checkNotNull(Object obj, String attribute) throws InvalidRegistryObjectException {
    if (obj == null) {
      Object[] args = {getId(), fElement.getContributor().getName(), attribute};
      String message =
          Messages.format(
              RubyTextMessages.CompletionProposalComputerDescriptor_illegal_attribute_message,
              args);
      IStatus status =
          new Status(IStatus.WARNING, RubyPlugin.getPluginId(), IStatus.OK, message, null);
      RubyPlugin.log(status);
      throw new InvalidRegistryObjectException();
    }
  }

  /**
   * Returns the identifier of the described extension.
   *
   * @return Returns the id
   */
  public String getId() {
    return fId;
  }

  /**
   * Returns the name of the described extension.
   *
   * @return Returns the name
   */
  public String getName() {
    return fName;
  }

  /**
   * Returns the partition types of the described extension.
   *
   * @return the set of partition types (element type: {@link String})
   */
  public Set<String> getPartitions() {
    return fPartitions;
  }

  /**
   * Returns a cached instance of the computer as described in the extension's xml. The computer is
   * {@link #createComputer() created} the first time that this method is called and then cached.
   *
   * @return a new instance of the completion proposal computer as described by this descriptor
   * @throws CoreException if the creation fails
   * @throws InvalidRegistryObjectException if the extension is not valid any longer (e.g. due to
   *     plug-in unloading)
   */
  private synchronized IRubyCompletionProposalComputer getComputer()
      throws CoreException, InvalidRegistryObjectException {
    if (fComputer == null && (fActivate || isPluginLoaded())) fComputer = createComputer();
    return fComputer;
  }

  private boolean isPluginLoaded() {
    Bundle bundle = getBundle();
    return bundle != null && bundle.getState() == Bundle.ACTIVE;
  }

  private Bundle getBundle() {
    String namespace = fElement.getDeclaringExtension().getContributor().getName();
    Bundle bundle = Platform.getBundle(namespace);
    return bundle;
  }

  /**
   * Returns a new instance of the computer as described in the extension's xml. Note that the
   * safest way to access the computer is by using the {@linkplain
   * #computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor)
   * computeCompletionProposals} and {@linkplain
   * #computeContextInformation(ContentAssistInvocationContext, IProgressMonitor)
   * computeContextInformation} methods. These delegate the functionality to the contributed
   * computer, but handle instance creation and any exceptions thrown.
   *
   * @return a new instance of the completion proposal computer as described by this descriptor
   * @throws CoreException if the creation fails
   * @throws InvalidRegistryObjectException if the extension is not valid any longer (e.g. due to
   *     plug-in unloading)
   */
  public IRubyCompletionProposalComputer createComputer()
      throws CoreException, InvalidRegistryObjectException {
    return (IRubyCompletionProposalComputer) fElement.createExecutableExtension(CLASS);
  }

  /**
   * Safely computes completion proposals through the described extension. If the extension is
   * disabled, throws an exception or otherwise does not adhere to the contract described in {@link
   * IRubyCompletionProposalComputer}, an empty list is returned.
   *
   * @param context the invocation context passed on to the extension
   * @param monitor the progress monitor passed on to the extension
   * @return the list of computed completion proposals (element type: {@link
   *     org.eclipse.jface.text.contentassist.ICompletionProposal})
   */
  public List computeCompletionProposals(
      ContentAssistInvocationContext context, IProgressMonitor monitor) {
    if (!isEnabled()) return Collections.EMPTY_LIST;

    IStatus status;
    try {
      IRubyCompletionProposalComputer computer = getComputer();
      if (computer == null) // not active yet
      return Collections.EMPTY_LIST;

      try {
        PerformanceStats stats = startMeter(context, computer);
        List proposals = computer.computeCompletionProposals(context, monitor);
        stopMeter(stats, COMPUTE_COMPLETION_PROPOSALS);

        if (proposals != null) {
          fLastError = computer.getErrorMessage();
          return proposals;
        }
      } finally {
        fIsReportingDelay = true;
      }
      status = createAPIViolationStatus(COMPUTE_COMPLETION_PROPOSALS);
    } catch (InvalidRegistryObjectException x) {
      status = createExceptionStatus(x);
    } catch (CoreException x) {
      status = createExceptionStatus(x);
    } catch (RuntimeException x) {
      status = createExceptionStatus(x);
    } finally {
      monitor.done();
    }

    fRegistry.informUser(this, status);

    return Collections.EMPTY_LIST;
  }

  /**
   * Safely computes context information objects through the described extension. If the extension
   * is disabled, throws an exception or otherwise does not adhere to the contract described in
   * {@link IRubyCompletionProposalComputer}, an empty list is returned.
   *
   * @param context the invocation context passed on to the extension
   * @param monitor the progress monitor passed on to the extension
   * @return the list of computed context information objects (element type: {@link
   *     org.eclipse.jface.text.contentassist.IContextInformation})
   */
  public List computeContextInformation(
      ContentAssistInvocationContext context, IProgressMonitor monitor) {
    if (!isEnabled()) return Collections.EMPTY_LIST;

    IStatus status;
    try {
      IRubyCompletionProposalComputer computer = getComputer();
      if (computer == null) // not active yet
      return Collections.EMPTY_LIST;

      PerformanceStats stats = startMeter(context, computer);
      List proposals = computer.computeContextInformation(context, monitor);
      stopMeter(stats, COMPUTE_CONTEXT_INFORMATION);

      if (proposals != null) {
        fLastError = computer.getErrorMessage();
        return proposals;
      }

      status = createAPIViolationStatus(COMPUTE_CONTEXT_INFORMATION);
    } catch (InvalidRegistryObjectException x) {
      status = createExceptionStatus(x);
    } catch (CoreException x) {
      status = createExceptionStatus(x);
    } catch (RuntimeException x) {
      status = createExceptionStatus(x);
    } finally {
      monitor.done();
    }

    fRegistry.informUser(this, status);

    return Collections.EMPTY_LIST;
  }

  /**
   * Notifies the described extension of a proposal computation session start.
   *
   * <p><em> Note: This method is called every time code assist is invoked and is
   * <strong>not</strong> filtered by partition type. </em>
   */
  public void sessionStarted() {
    if (!isEnabled()) return;

    IStatus status;
    try {
      IRubyCompletionProposalComputer computer = getComputer();
      if (computer == null) // not active yet
      return;

      PerformanceStats stats = startMeter(SESSION_STARTED, computer);
      computer.sessionStarted();
      stopMeter(stats, SESSION_ENDED);

      return;
    } catch (InvalidRegistryObjectException x) {
      status = createExceptionStatus(x);
    } catch (CoreException x) {
      status = createExceptionStatus(x);
    } catch (RuntimeException x) {
      status = createExceptionStatus(x);
    }

    fRegistry.informUser(this, status);
  }

  /**
   * Notifies the described extension of a proposal computation session end.
   *
   * <p><em> Note: This method is called every time code assist is invoked and is
   * <strong>not</strong> filtered by partition type. </em>
   */
  public void sessionEnded() {
    if (!isEnabled()) return;

    IStatus status;
    try {
      IRubyCompletionProposalComputer computer = getComputer();
      if (computer == null) // not active yet
      return;

      PerformanceStats stats = startMeter(SESSION_ENDED, computer);
      computer.sessionEnded();
      stopMeter(stats, SESSION_ENDED);

      return;
    } catch (InvalidRegistryObjectException x) {
      status = createExceptionStatus(x);
    } catch (CoreException x) {
      status = createExceptionStatus(x);
    } catch (RuntimeException x) {
      status = createExceptionStatus(x);
    }

    fRegistry.informUser(this, status);
  }

  private PerformanceStats startMeter(Object context, IRubyCompletionProposalComputer computer) {
    final PerformanceStats stats;
    if (MEASURE_PERFORMANCE) {
      stats = PerformanceStats.getStats(PERFORMANCE_EVENT, computer);
      stats.startRun(context.toString());
    } else {
      stats = null;
    }

    if (fIsReportingDelay) {
      fStart = System.currentTimeMillis();
    }

    return stats;
  }

  private void stopMeter(final PerformanceStats stats, String operation) {
    if (MEASURE_PERFORMANCE) {
      stats.endRun();
      if (stats.isFailure()) {
        IStatus status = createPerformanceStatus(operation);
        fRegistry.informUser(this, status);
        return;
      }
    }

    if (fIsReportingDelay) {
      long current = System.currentTimeMillis();
      if (current - fStart > MAX_DELAY) {
        IStatus status = createPerformanceStatus(operation);
        fRegistry.informUser(this, status);
      }
    }
  }

  private IStatus createExceptionStatus(InvalidRegistryObjectException x) {
    // extension has become invalid - log & disable
    String blame = createBlameMessage();
    String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_invalid;
    return new Status(
        IStatus.INFO, RubyPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); // $NON-NLS-1$
  }

  private IStatus createExceptionStatus(CoreException x) {
    // unable to instantiate the extension - log & disable
    String blame = createBlameMessage();
    String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_instantiation;
    return new Status(
        IStatus.ERROR,
        RubyPlugin.getPluginId(),
        IStatus.OK,
        blame + " " + reason,
        x); //$NON-NLS-1$
  }

  private IStatus createExceptionStatus(RuntimeException x) {
    // misbehaving extension - log & disable
    String blame = createBlameMessage();
    String reason = RubyTextMessages.CompletionProposalComputerDescriptor_reason_runtime_ex;
    return new Status(
        IStatus.WARNING,
        RubyPlugin.getPluginId(),
        IStatus.OK,
        blame + " " + reason,
        x); //$NON-NLS-1$
  }

  private IStatus createAPIViolationStatus(String operation) {
    String blame = createBlameMessage();
    Object[] args = {operation};
    String reason =
        Messages.format(RubyTextMessages.CompletionProposalComputerDescriptor_reason_API, args);
    return new Status(
        IStatus.WARNING,
        RubyPlugin.getPluginId(),
        IStatus.OK,
        blame + " " + reason,
        null); //$NON-NLS-1$
  }

  private IStatus createPerformanceStatus(String operation) {
    String blame = createBlameMessage();
    Object[] args = {operation};
    String reason =
        Messages.format(
            RubyTextMessages.CompletionProposalComputerDescriptor_reason_performance, args);
    return new Status(
        IStatus.WARNING,
        RubyPlugin.getPluginId(),
        IStatus.OK,
        blame + " " + reason,
        null); //$NON-NLS-1$
  }

  private String createBlameMessage() {
    Object[] args = {getName(), fElement.getDeclaringExtension().getContributor().getName()};
    String disable =
        Messages.format(RubyTextMessages.CompletionProposalComputerDescriptor_blame_message, args);
    return disable;
  }

  /**
   * Returns the enablement state of the described extension.
   *
   * @return the enablement state of the described extension
   */
  private boolean isEnabled() {
    return fCategory.isEnabled();
  }

  CompletionProposalCategory getCategory() {
    return fCategory;
  }

  /**
   * Returns the error message from the described extension.
   *
   * @return the error message from the described extension
   */
  public String getErrorMessage() {
    return fLastError;
  }

  /**
   * Returns the contributor of the described extension.
   *
   * @return the contributor of the described extension
   */
  IContributor getContributor() {
    try {
      return fElement.getContributor();
    } catch (InvalidRegistryObjectException e) {
      return null;
    }
  }
}