/**
   * @return the function name for this breakpoint.
   *     <p>A return of "None" signals that we couldn't discover the function name (so, we should
   *     try to match things in the whole file, and not only in the given context, as we don't know
   *     which context it is)
   */
  public String getFunctionName() {
    String fileStr = getFile();
    File file = fileStr != null ? new File(fileStr) : null;
    if (file == null || !file.exists()) {
      return "None";
    }

    if (file.lastModified() == lastModifiedTimeCached) {
      return functionName;
    }

    try {
      IPythonNature nature = getPythonNature();
      if (nature == null) {
        lastModifiedTimeCached = 0;
        return "None";
      }
      ICodeCompletionASTManager astManager = nature.getAstManager();
      if (astManager == null) {
        lastModifiedTimeCached = 0;
        return "None";
      }
      // Only mark it as found if we were able to get the python nature (otherwise, this could
      // change later
      // if requesting during a setup)
      if (nature.startRequests()) { // start requests, as we'll ask for resolve and get module.
        SourceModule sourceModule = null;
        try {
          String modName = nature.resolveModule(fileStr);
          if (modName != null) {
            // when all is set up, this is the most likely path we're going to use
            // so, we shouldn't have delays when the module is changed, as it's already
            // ok for use.
            IModule module = astManager.getModule(modName, nature, true);
            if (module instanceof SourceModule) {
              sourceModule = (SourceModule) module;
            }
          }
        } finally {
          nature.endRequests();
        }
        lastModifiedTimeCached = file.lastModified();

        if (sourceModule == null) {
          // the text for the breakpoint requires the function name, and it may be requested before
          // the ast manager is actually restored (so, modName is None, and we have little
          // alternative
          // but making a parse to get the function name)
          IDocument doc = getDocument();
          sourceModule = AbstractModule.createModuleFromDoc("", null, doc, nature, true);
        }

        int lineToUse = getLineNumber() - 1;

        if (sourceModule == null || sourceModule.getAst() == null || lineToUse < 0) {
          functionName = "None";
          return functionName;
        }

        SimpleNode ast = sourceModule.getAst();

        functionName = NodeUtils.getContextName(lineToUse, ast);
        if (functionName == null) {
          functionName = ""; // global context
        }
        return functionName;
      }
      // If it was found, it would've already returned. So, match anything as we couldn't determine
      // it.
      functionName = "None";

    } catch (Exception e) {
      // Some error happened determining it. Match anything.
      Log.log(
          "Error determining breakpoint context. Breakpoint at: "
              + file
              + " will match any context.",
          e);
      functionName = "None";
    }
    return functionName;
  }
  /**
   * @param name determines the name of the method to visit (added removed or changed)
   * @param resource the resource to visit
   * @param document the document from the resource
   * @param monitor
   */
  private void visitWith(
      int visitType,
      final IResource resource,
      ICallback0<IDocument> document,
      IProgressMonitor monitor) {
    if (monitor.isCanceled()) {
      return; // it's already cancelled
    }
    IPythonNature nature = PythonNature.getPythonNature(resource);
    if (nature == null) {
      return;
    }
    if (!nature.startRequests()) {
      return;
    }

    try {

      try {
        // we visit external because we must index them
        if (!isResourceInPythonpathProjectSources(resource, nature, true)) {
          return; // we only analyze resources that are in the pythonpath
        }
      } catch (Exception e1) {
        Log.log(e1);
        return; // we only analyze resources that are in the pythonpath
      }

      VisitorMemo copyMemo = new VisitorMemo(this.memo);
      FastStringBuffer bufferToCommunicateProgress = new FastStringBuffer();

      for (PyDevBuilderVisitor visitor : visitors) {
        // some visitors cannot visit too many elements because they do a lot of processing
        if (visitor.maxResourcesToVisit() == PyDevBuilderVisitor.MAX_TO_VISIT_INFINITE
            || visitor.maxResourcesToVisit() >= totalResources) {
          visitor.memo = copyMemo; // setting the memo must be the first thing.
          try {
            // communicate progress for each visitor
            PyDevBuilder.communicateProgress(
                monitor,
                totalResources,
                currentResourcesVisited,
                resource,
                visitor,
                bufferToCommunicateProgress);
            switch (visitType) {
              case VISIT_ADD:
                visitor.visitAddedResource(resource, document, monitor);
                break;

              case VISIT_CHANGE:
                visitor.visitChangedResource(resource, document, monitor);
                break;

              case VISIT_REMOVE:
                visitor.visitRemovedResource(resource, document, monitor);
                break;

              default:
                throw new RuntimeException("Error: visit type not properly given!"); // $NON-NLS-1$
            }
          } catch (Exception e) {
            Log.log(e);
          }
        }
      }

    } finally {
      nature.endRequests();
    }
  }