public void reformat() throws BadLocationException {
    final Document document = context.document();
    final BaseDocument bd = (BaseDocument) document;
    bd.runAtomic(
        new Runnable() {

          public void run() {
            try {
              Element rootElement = document.getDefaultRootElement();
              List<Context.Region> regions = context.indentRegions();

              for (Context.Region region : regions) {
                int startOffset = region.getStartOffset();
                int length = region.getEndOffset() - region.getStartOffset();
                int elementIndex = rootElement.getElementIndex(startOffset);
                final int prevIndex = elementIndex > 0 ? elementIndex - 1 : elementIndex;
                int indent = getIndent(document, prevIndex);

                String text = document.getText(startOffset, length);
                if (text.startsWith(TEMPLATE_START)) {
                  // remove template tags
                  int st = startOffset + text.indexOf(TEMPLATE_START);
                  document.remove(st, TEMPLATE_START.length());
                  int en = startOffset + text.indexOf(TEMPLATE_END) - TEMPLATE_START.length();
                  document.remove(en, TEMPLATE_END.length());

                  // modify indent
                  int lastElement = rootElement.getElementIndex(en);
                  for (int i = elementIndex; i <= lastElement; i++) {
                    Element element = rootElement.getElement(i);
                    int so = element.getStartOffset();
                    int currentIndent = context.lineIndent(so);

                    // first line should not be indented if there is something already except
                    // whitespaces
                    boolean doIndent = true;
                    if (i == elementIndex) {
                      String textBefore = document.getText(so, startOffset - so);
                      boolean isTextWSOnly = textBefore.matches("\\s+"); // NOI18N
                      doIndent = textBefore.length() == 0 || isTextWSOnly;
                      if (!doIndent) {
                        indent = getIndent(document, i);
                      }
                    } else {
                      doIndent = true;
                    }

                    if (doIndent) {
                      context.modifyIndent(so, currentIndent + indent);
                    }
                  }
                }
              }
            } catch (BadLocationException e) {
              logger.log(Level.FINE, e.getLocalizedMessage());
            }
          }
        });
  }
  private void addFolds(
      BaseDocument doc,
      List<? extends ASTElement> elements,
      Map<String, List<OffsetRange>> folds,
      List<OffsetRange> codeblocks)
      throws BadLocationException {
    for (ASTElement element : elements) {
      ElementKind kind = element.getKind();
      switch (kind) {
        case FIELD:
        case METHOD:
        case CONSTRUCTOR:
        case CLASS:
        case MODULE:
          ASTNode node = element.getNode();
          OffsetRange range = ASTUtils.getRangeFull(node, doc);

          // beware of synthetic elements
          if ((kind == ElementKind.METHOD && !((MethodNode) node).isSynthetic())
              || (kind == ElementKind.CONSTRUCTOR && !((ConstructorNode) node).isSynthetic())
              || (kind == ElementKind.FIELD
                  && ((FieldNode) node).getInitialExpression() instanceof ClosureExpression)
              // Only make nested classes/modules foldable, similar to what the java editor is doing
              || (range.getStart() > Utilities.getRowStart(doc, range.getStart()))
                  && kind != ElementKind.FIELD) {

            int start = range.getStart();
            // Start the fold at the END of the line behind last non-whitespace, remove curly brace,
            // if any
            start = Utilities.getRowLastNonWhite(doc, start);
            if (start >= 0 && doc.getChars(start, 1)[0] != '{') {
              start++;
            }
            int end = range.getEnd();
            if (start != (-1) && end != (-1) && start < end && end <= doc.getLength()) {
              range = new OffsetRange(start, end);
              codeblocks.add(range);
            }
          }
          break;
      }

      List<? extends ASTElement> children = element.getChildren();

      if (children != null && children.size() > 0) {
        addFolds(doc, children, folds, codeblocks);
      }
    }
  }
Esempio n. 3
0
    public EditList getEditList() {
      try {
        BaseDocument doc = context.doc;

        Node bodyNode = ifNode.getThenBody();
        boolean isIf = bodyNode != null;
        if (bodyNode == null) {
          bodyNode = ifNode.getElseBody();
        }
        ParserResult info = context.parserResult;
        OffsetRange bodyRange = AstUtilities.getRange(bodyNode);
        bodyRange = LexUtilities.getLexerOffsets(info, bodyRange);
        if (bodyRange == OffsetRange.NONE) {
          return null;
        }

        String body = doc.getText(bodyRange.getStart(), bodyRange.getLength()).trim();
        if (body.endsWith(";")) {
          body = body.substring(0, body.length() - 1);
        }
        StringBuilder sb = new StringBuilder();
        sb.append(body);
        sb.append(" ");
        sb.append(isIf ? "if" : "unless"); // NOI18N
        sb.append(" ");
        OffsetRange range = AstUtilities.getRange(ifNode.getCondition());
        range = LexUtilities.getLexerOffsets(info, range);
        if (range == OffsetRange.NONE) {
          return null;
        }
        sb.append(doc.getText(range.getStart(), range.getLength()));

        OffsetRange ifRange = AstUtilities.getRange(ifNode);
        ifRange = LexUtilities.getLexerOffsets(info, ifRange);
        if (ifRange == OffsetRange.NONE) {
          return null;
        }

        return new EditList(doc)
            .replace(ifRange.getStart(), ifRange.getLength(), sb.toString(), false, 0);
      } catch (Exception ex) {
        Exceptions.printStackTrace(ex);
        return null;
      }
    }
Esempio n. 4
0
 public static void reformat(final BaseDocument doc, final int startOffset, final int endOffset) {
   final XMLLexerFormatter formatter = new XMLLexerFormatter(null);
   doc.runAtomic(
       new Runnable() {
         public void run() {
           formatter.doReformat(doc, startOffset, endOffset);
         }
       });
 }
Esempio n. 5
0
  /**
   * Makes the bar visible and sensitive to LogOutoutListener events that should deliver actual
   * content to be displayed.
   */
  public void annotate() {
    annotated = true;
    elementAnnotations = Collections.<Element, AnnotateLine>emptyMap();

    doc.addDocumentListener(this);
    textComponent.addComponentListener(this);
    editorUI.addPropertyChangeListener(this);

    revalidate(); // resize the component
  }
Esempio n. 6
0
 /**
  * Gets a the file related to the document
  *
  * @return the file related to the document, <code>null</code> if none exists.
  */
 File getCurrentFile() {
   File result = referencedFile;
   if (result == null) {
     DataObject dobj = (DataObject) doc.getProperty(Document.StreamDescriptionProperty);
     if (dobj != null) {
       FileObject fo = dobj.getPrimaryFile();
       result = FileUtil.toFile(fo);
     }
   }
   return result;
 }
Esempio n. 7
0
 FileObject getCurrentFileObject() {
   FileObject result = referencedFileObject;
   if (result == null) {
     Object sdp = doc.getProperty(Document.StreamDescriptionProperty);
     if (sdp instanceof FileObject) {
       result = (FileObject) sdp;
     } else if (sdp instanceof DataObject) {
       result = ((DataObject) sdp).getPrimaryFile();
     }
   }
   return result;
 }
  /**
   * Reformat with NetBeans format settings
   *
   * @param doc
   */
  private void reformat(Document doc) {
    final BaseDocument baseDoc = (BaseDocument) doc;
    final Reformat reformat = Reformat.get(baseDoc);
    reformat.lock();
    try {
      baseDoc.runAtomic(
          new Runnable() {
            @Override
            public void run() {
              try {
                reformat.reformat(0, baseDoc.getLength());

                // Remove tab from Document Block
                removeIndentDocBlock();
              } catch (BadLocationException ex) {
                LOGGER.log(Level.WARNING, null, ex);
              }
            }
          });
    } finally {
      reformat.unlock();
    }
  }
Esempio n. 9
0
  /** Pair method to {@link #annotate}. It releases all resources. */
  private void release() {
    editorUI.removePropertyChangeListener(this);
    textComponent.removeComponentListener(this);
    doc.removeDocumentListener(this);
    caret.removeChangeListener(this);
    if (caretTimer != null) {
      caretTimer.removeActionListener(this);
    }
    elementAnnotations = Collections.<Element, AnnotateLine>emptyMap();
    previousRevisions = null;
    originalFiles = null;
    // cancel running annotation task if active
    if (latestAnnotationTask != null) {
      latestAnnotationTask.cancel();
    }
    AnnotationMarkProvider amp = AnnotationMarkInstaller.getMarkProvider(textComponent);
    if (amp != null) {
      amp.setMarks(Collections.<AnnotationMark>emptyList());
    }

    clearRecentFeedback();
  }
Esempio n. 10
0
  /**
   * Locates AnnotateLine associated with given line. The line is translated to Element that is used
   * as map lookup key. The map is initially filled up with Elements sampled on annotate() method.
   *
   * <p>Key trick is that Element's identity is maintained until line removal (and is restored on
   * undo).
   *
   * @param line
   * @return found AnnotateLine or <code>null</code>
   */
  private AnnotateLine getAnnotateLine(int line) {
    StyledDocument sd = (StyledDocument) doc;
    int lineOffset = NbDocument.findLineOffset(sd, line);
    Element element = sd.getParagraphElement(lineOffset);
    AnnotateLine al = elementAnnotations.get(element);

    if (al != null) {
      int startOffset = element.getStartOffset();
      int endOffset = element.getEndOffset();
      try {
        int len = endOffset - startOffset;
        String text = doc.getText(startOffset, len - 1);
        String content = al.getContent();
        if (text.equals(content)) {
          return al;
        }
      } catch (BadLocationException e) {
        Mercurial.LOG.log(Level.INFO, "HG.AB: can not locate line annotation."); // NOI18N
      }
    }

    return null;
  }
  @Override
  public DeclarationLocation findDeclaration(ParserResult info, int lexOffset) {

    final Document document = info.getSnapshot().getSource().getDocument(false);
    if (document == null) {
      return DeclarationLocation.NONE;
    }

    final BaseDocument doc = (BaseDocument) document;

    PythonParserResult parseResult = PythonAstUtils.getParseResult(info);
    doc.readLock(); // Read-lock due to Token hierarchy use
    try {
      PythonTree root = PythonAstUtils.getRoot(parseResult);
      final int astOffset = PythonAstUtils.getAstOffset(info, lexOffset);
      if (astOffset == -1) {
        return DeclarationLocation.NONE;
      }

      AstPath path = null;
      PythonTree node = null;
      if (root != null) {
        path = AstPath.get(root, astOffset);
        node = path.leaf();
      }

      // See if it's an import
      DeclarationLocation imp = findImport(parseResult, lexOffset, doc);
      if (imp != DeclarationLocation.NONE) {
        return imp;
      }

      DeclarationLocation url = findUrl(parseResult, doc, lexOffset);
      if (url != DeclarationLocation.NONE) {
        return url;
      }

      final TokenHierarchy<Document> th = TokenHierarchy.get(document);
      org.netbeans.modules.python.editor.lexer.Call call =
          org.netbeans.modules.python.editor.lexer.Call.getCallType(doc, th, lexOffset);

      FileObject fileObject = info.getSnapshot().getSource().getFileObject();

      // Search for local variables
      if (root != null && call.getLhs() == null) {
        if ((path.leaf() instanceof Name)) {
          PythonTree scope = PythonAstUtils.getLocalScope(path);
          SymbolTable symbolTable = parseResult.getSymbolTable();

          String name = ((Name) path.leaf()).getInternalId();

          SymInfo sym = symbolTable.findDeclaration(scope, name, true);
          if (sym != null) {
            if (sym.isFree()) {
              PythonIndex index = PythonIndex.get(fileObject);

              List<Import> imports = symbolTable.getImports();
              List<ImportFrom> importsFrom = symbolTable.getImportsFrom();
              Set<IndexedElement> elements =
                  index.getImportedElements(
                      name, QuerySupport.Kind.EXACT, parseResult, imports, importsFrom);
              if (elements != null && elements.size() > 0) {
                return getDeclaration(
                    parseResult, null /*name*/, elements, path, node, index, astOffset, lexOffset);
              }
              // Must be defined by one of the imported symbols
            }
            if (sym.node != null) {
              PythonTree declNode = sym.node;
              if (sym
                  .isImported()) { // Rather than showing the import symbol go to the definition in
                                   // the library!
                // Determine if it's an "as" name (import foo as bar) and if so just show the "as",
                // if not,
                // follow through to the library
                if (declNode instanceof Import) {
                  Import impNode = (Import) declNode;
                  List<alias> names = impNode.getInternalNames();
                  if (names != null) {
                    for (alias at : names) {
                      if (at.getInternalAsname() != null && name.equals(at.getInternalAsname())) {
                        break;
                      } else if (at.getInternalName().equals(name)) {
                        // We found our library - just show it
                        return findImport(parseResult, name, null);
                      }
                    }
                  }
                } else {
                  assert declNode instanceof ImportFrom : declNode;
                  ImportFrom impNode = (ImportFrom) declNode;
                  List<alias> names = impNode.getInternalNames();
                  if (names != null) {
                    for (alias at : names) {
                      if (at.getInternalAsname() != null && name.equals(at.getInternalAsname())) {
                        break;
                      } else if (at.getInternalName().equals(name)) {
                        // We found our library - just show it
                        return findImport(parseResult, impNode.getInternalModule(), name);
                      }
                    }
                  }
                }
              }

              if (sym.isUnresolved()) {
                PythonIndex index = PythonIndex.get(fileObject);

                List<Import> imports = symbolTable.getImports();
                List<ImportFrom> importsFrom = symbolTable.getImportsFrom();
                Set<IndexedElement> elements =
                    index.getImportedElements(
                        name, QuerySupport.Kind.EXACT, parseResult, imports, importsFrom);
                if (elements != null && elements.size() > 0) {
                  return getDeclaration(
                      parseResult,
                      null /*name*/,
                      elements,
                      path,
                      node,
                      index,
                      astOffset,
                      lexOffset);
                }
              } else {
                OffsetRange astRange = PythonAstUtils.getNameRange(null, declNode);
                int lexerOffset = PythonLexerUtils.getLexerOffset(parseResult, astRange.getStart());
                if (lexerOffset == -1) {
                  lexerOffset = 0;
                }
                return new DeclarationLocation(fileObject, lexerOffset);
              }
            }
          }
          //
          //                    List<Name> localVars = PythonAstUtils.getLocalVarNodes(info, scope,
          // name);
          //                    if (localVars.size() > 0) {
          //                        return new DeclarationLocation(info.getFileObject(),
          // PythonAstUtils.getRange(localVars.get(0)).getStart());
          //                    }
        }
      }

      // I'm not doing any data flow analysis at this point, so
      // I can't do anything with a LHS like "foo.". Only actual types.
      String type = call.getType();
      if (type != null && "self".equals(type)) { // NOI18N
        type = PythonAstUtils.getFqnName(path);
      }
      if (type != null && type.length() > 0) {
        String name = null;
        PythonTree leaf = path.leaf();
        if (leaf instanceof Name) {
          name = ((Name) path.leaf()).getInternalId();
        } else if (leaf instanceof Attribute) {
          name = ((Attribute) leaf).getInternalAttr();
        }

        if (name != null) {
          PythonIndex index = PythonIndex.get(fileObject);
          // Add methods in the class (without an FQN)
          Set<IndexedElement> elements =
              index.getInheritedElements(type, name, QuerySupport.Kind.EXACT);
          if (elements != null && elements.size() > 0) {
            return getDeclaration(
                parseResult, null /*name*/, elements, path, node, index, astOffset, lexOffset);
          }
        }
      }

      // Fallback: Index search on all names
      String prefix = new PythonCodeCompleter().getPrefix(info, lexOffset, false);
      if (prefix == null) {
        try {
          prefix = Utilities.getIdentifier(doc, lexOffset);
        } catch (BadLocationException ex) {
          Exceptions.printStackTrace(ex);
        }
      }
      if (prefix != null) {
        PythonIndex index = PythonIndex.get(fileObject);

        Set<? extends IndexedElement> elements = null;
        if (prefix.length() > 0 && Character.isUpperCase(prefix.charAt(0))) {
          elements = index.getClasses(prefix, QuerySupport.Kind.EXACT, parseResult, true);
        }

        if (elements == null || elements.size() == 0) {
          elements = index.getAllElements(prefix, QuerySupport.Kind.EXACT, parseResult, true);
        }

        if (elements == null || elements.size() == 0) {
          elements = index.getAllMembers(prefix, QuerySupport.Kind.EXACT, parseResult, true);
        }

        if (elements != null && elements.size() > 0) {
          return getDeclaration(
              parseResult, null /*name*/, elements, path, node, index, astOffset, lexOffset);
        }

        // TODO - classes
        // WORKING HERE
        //                if (elements == null || elements.size() == 0) {
        //                    elements = index.getClasses(prefix, QuerySupport.Kind.EXACT,
        // PythonIndex.ALL_SCOPE, parseResult, true);
        //                }
        //                if (elements != null && elements.size() > 0) {
        //                    String name = null; // unused!
        //
        //                    return getMethodDeclaration(info, name, elements,
        //                         path, null, index, astOffset, lexOffset);
        //                }
      }
    } finally {
      doc.readUnlock();
    }
    return DeclarationLocation.NONE;
  }
Esempio n. 12
0
 @Override
 protected void initDocument(BaseDocument doc) {
   super.initDocument(doc);
   doc.putProperty(SyntaxUpdateTokens.class, new SUT());
 }
Esempio n. 13
0
  /** Result computed show it... Takes AnnotateLines and shows them. */
  public void annotationLines(File file, List<AnnotateLine> annotateLines) {
    // set repository root for popup menu, now should be the right time
    repositoryRoot = Mercurial.getInstance().getRepositoryRoot(getCurrentFile());
    final List<AnnotateLine> lines = new LinkedList<AnnotateLine>(annotateLines);
    int lineCount = lines.size();
    /** 0 based line numbers => 1 based line numbers */
    final int ann2editorPermutation[] = new int[lineCount];
    for (int i = 0; i < lineCount; i++) {
      ann2editorPermutation[i] = i + 1;
    }

    DiffProvider diff = (DiffProvider) Lookup.getDefault().lookup(DiffProvider.class);
    if (diff != null) {
      Reader r = new LinesReader(lines);
      Reader docReader = Utils.getDocumentReader(doc);
      try {

        Difference[] differences = diff.computeDiff(r, docReader);

        // customize annotation line numbers to match different reality
        // compule line permutation

        for (Difference d : differences) {
          int offset, editorStart;
          if (d.getType() == Difference.ADD) {
            offset = d.getSecondEnd() - d.getSecondStart() + 1;
            editorStart = d.getFirstStart();
          } else if (d.getType() == Difference.DELETE) {
            offset = d.getFirstEnd() - d.getFirstStart() + 1;
            editorStart = d.getFirstEnd();
            for (int c = editorStart - offset; c < editorStart; c++) {
              ann2editorPermutation[c] = -1;
            }
            offset = -offset;
          } else {
            // change
            int firstLen = d.getFirstEnd() - d.getFirstStart();
            int secondLen = d.getSecondEnd() - d.getSecondStart();
            offset = secondLen - firstLen;
            if (offset == 0) continue;
            editorStart = d.getFirstEnd();
            for (int c = d.getFirstStart(); c < editorStart; c++) {
              ann2editorPermutation[c] += -1;
            }
          }
          for (int c = editorStart; c < lineCount; c++) {
            ann2editorPermutation[c] += offset;
          }
        }

      } catch (IOException e) {
        Mercurial.LOG.log(
            Level.INFO,
            "Cannot compute local diff required for annotations, ignoring..."); // NOI18N
      }
    }

    doc.render(
        new Runnable() {
          @Override
          public void run() {
            StyledDocument sd = (StyledDocument) doc;
            Iterator<AnnotateLine> it = lines.iterator();
            previousRevisions = Collections.synchronizedMap(new HashMap<String, HgRevision>());
            originalFiles = Collections.synchronizedMap(new HashMap<String, File>());
            elementAnnotations =
                Collections.synchronizedMap(new HashMap<Element, AnnotateLine>(lines.size()));
            while (it.hasNext()) {
              AnnotateLine line = it.next();
              int lineNum = ann2editorPermutation[line.getLineNum() - 1];
              if (lineNum == -1) {
                continue;
              }
              try {
                int lineOffset = NbDocument.findLineOffset(sd, lineNum - 1);
                Element element = sd.getParagraphElement(lineOffset);
                elementAnnotations.put(element, line);
              } catch (IndexOutOfBoundsException ex) {
                // TODO how could I get line behind document end?
                // furtunately user does not spot it
                Mercurial.LOG.log(Level.INFO, null, ex);
              }
            }
          }
        });

    final String url = HgUtils.getRemoteRepository(repositoryRoot);
    final boolean isKenaiRepository = url != null && HgKenaiAccessor.getInstance().isKenai(url);
    if (isKenaiRepository) {
      kenaiUsersMap = new HashMap<String, KenaiUser>();
      Iterator<AnnotateLine> it = lines.iterator();
      while (it.hasNext()) {
        AnnotateLine line = it.next();
        String author = line.getAuthor();
        if (author != null && !author.equals("") && !kenaiUsersMap.keySet().contains(author)) {
          KenaiUser ku = HgKenaiAccessor.getInstance().forName(author, url);
          if (ku != null) {
            kenaiUsersMap.put(author, ku);
          }
        }
      }
    }

    // lazy listener registration
    caret.addChangeListener(this);
    this.caretTimer = new Timer(500, this);
    caretTimer.setRepeats(false);

    elementAnnotationsSubstitute = "";
    onCurrentLine();
    revalidate();
    repaint();
  }
Esempio n. 14
0
  public void run(RubyRuleContext context, List<Hint> result) {
    Node node = context.node;
    ParserResult info = context.parserResult;

    IfNode ifNode = (IfNode) node;
    if (ifNode.getCondition() == null) {
      // Can happen for this code:
      //   if ()
      //   end
      // (typically while editing)
      return;
    }
    Node body = ifNode.getThenBody();
    Node elseNode = ifNode.getElseBody();

    if (body != null && elseNode != null) {
      // Can't convert if-then-else conditionals
      return;
    }

    if (body == null && elseNode == null) {
      // Can't convert empty conditions
      return;
    }

    // Can't convert if !x/elseif blocks
    if (ifNode.getElseBody() != null && ifNode.getElseBody().getNodeType() == NodeType.IFNODE) {
      return;
    }

    int start = ifNode.getPosition().getStartOffset();
    if (!RubyHints.isNullOrInvisible(body)
        && (
        // Can't convert blocks with multiple statements
        body.getNodeType() == NodeType.BLOCKNODE
            ||
            // Already a statement modifier?
            body.getPosition().getStartOffset() <= start)) {
      return;
    } else if (!RubyHints.isNullOrInvisible(elseNode)
        && (elseNode.getNodeType() == NodeType.BLOCKNODE
            || elseNode.getPosition().getStartOffset() <= start)) {
      return;
    }

    BaseDocument doc = context.doc;
    try {
      int keywordOffset = ConvertIfToUnless.findKeywordOffset(context, ifNode);
      if (keywordOffset == -1 || keywordOffset > doc.getLength() - 1) {
        return;
      }

      char k = doc.getText(keywordOffset, 1).charAt(0);
      if (!(k == 'i' || k == 'u')) {
        return; // Probably ternary operator, ?:
      }
    } catch (BadLocationException ble) {
      Exceptions.printStackTrace(ble);
    }

    // If statement that is not already a statement modifier
    OffsetRange range = AstUtilities.getRange(node);

    if (RubyUtils.isRhtmlDocument(doc) || RubyUtils.isYamlDocument(doc)) {
      // Make sure that we're in a single contiguous Ruby section; if not, this won't work
      range = LexUtilities.getLexerOffsets(info, range);
      if (range == OffsetRange.NONE) {
        return;
      }

      try {
        doc.readLock();
        TokenHierarchy th = TokenHierarchy.get(doc);
        TokenSequence ts = th.tokenSequence();
        ts.move(range.getStart());
        if (!ts.moveNext() && !ts.movePrevious()) {
          return;
        }

        if (ts.offset() + ts.token().length() < range.getEnd()) {
          return;
        }
      } finally {
        doc.readUnlock();
      }
    }

    ConvertToModifier fix = new ConvertToModifier(context, ifNode);

    if (fix.getEditList() == null) {
      return;
    }

    List<HintFix> fixes = Collections.<HintFix>singletonList(fix);

    String displayName = NbBundle.getMessage(ConvertConditionals.class, "ConvertConditionals");
    Hint desc = new Hint(this, displayName, RubyUtils.getFileObject(info), range, fixes, 500);
    result.add(desc);
  }
  @Override
  public Map<String, List<OffsetRange>> folds(ParserResult info) {
    ASTNode root = ASTUtils.getRoot(info);

    if (root == null) {
      return Collections.emptyMap();
    }

    GroovyParserResult rpr = ASTUtils.getParseResult(info);
    AnalysisResult analysisResult = rpr.getStructure();

    Map<String, List<OffsetRange>> folds = new HashMap<String, List<OffsetRange>>();
    List<OffsetRange> codefolds = new ArrayList<OffsetRange>();
    folds.put("codeblocks", codefolds); // NOI18N

    final BaseDocument doc = LexUtilities.getDocument(rpr, false);
    if (doc == null) {
      return Collections.emptyMap();
    }

    final OffsetRange[] importsRange = new OffsetRange[1];
    final List<OffsetRange> commentsRanges = new ArrayList<OffsetRange>();

    doc.render(
        new Runnable() {
          @Override
          public void run() {
            TokenSequence<?> ts = LexUtilities.getGroovyTokenSequence(doc, 1);

            int importStart = 0;
            int importEnd = 0;

            boolean startSet = false;

            while (ts != null && ts.isValid() && ts.moveNext()) {
              Token t = ts.token();
              if (t.id() == GroovyTokenId.LITERAL_import) {
                int offset = ts.offset();
                if (!startSet) {
                  importStart = offset;
                  startSet = true;
                }
                importEnd = offset;
              } else if (t.id() == GroovyTokenId.BLOCK_COMMENT) {
                // does this Block comment (GSF_BLOCK_COMMENT) span
                // multiple lines? E.g. includes \n ?
                StringBuffer sb = new StringBuffer(t.text());

                if (sb.indexOf("\n") != -1) {
                  int offset = ts.offset();
                  commentsRanges.add(new OffsetRange(offset, offset + t.length()));
                }
              }
            }
            try {
              importEnd = Utilities.getRowEnd(doc, importEnd);
              importsRange[0] = new OffsetRange(importStart, importEnd);
            } catch (BadLocationException ble) {
              Exceptions.printStackTrace(ble);
            }
          }
        });

    if (!commentsRanges.isEmpty()) {
      folds.put("comments", commentsRanges); // NOI18N
    }

    try {
      if (importsRange[0] != null
          && Utilities.getRowCount(doc, importsRange[0].getStart(), importsRange[0].getEnd()) > 1) {
        folds.put("imports", Collections.singletonList(importsRange[0])); // NOI18N
      }
      addFolds(doc, analysisResult.getElements(), folds, codefolds);
    } catch (BadLocationException ex) {
      Exceptions.printStackTrace(ex);
    }

    return folds;
  }