private List<LineRange> allRanges(final GroovyPsiElement scope, final boolean stmtLevel, final boolean topLevel) {
    final ArrayList<LineRange> result = new ArrayList<>();
    scope.accept(new PsiRecursiveElementVisitor() {
      int lastStart = -1;

      private void addRange(int endLine) {
        if (lastStart >= 0) {
          result.add(new LineRange(lastStart, endLine));
        }
        lastStart = endLine;
      }

      @Override
      public void visitElement(@NotNull PsiElement element) {
        if (stmtLevel && element instanceof GrCodeBlock) {
          final PsiElement lBrace = ((GrCodeBlock)element).getLBrace();
          if (nlsAfter(lBrace)) {
            addRange(new LineRange(lBrace).endLine);
          }
          addChildRanges(((GrCodeBlock)element).getStatements());
          final PsiElement rBrace = ((GrCodeBlock)element).getRBrace();
          if (nlsAfter(rBrace)) {
            final int endLine = new LineRange(rBrace).endLine;
            if (lastStart >= 0) {
              for (int i = lastStart + 1; i < endLine; i++) {
                addRange(i);
              }
            }
          }
        }
        else if (stmtLevel && element instanceof GrCaseSection) {
          PsiElement delimiter = ((GrCaseSection)element).getColon();
          if (delimiter != null) {
            if (nlsAfter(delimiter)) {
              addRange(new LineRange(element.getFirstChild(), delimiter).endLine);
            }
            addChildRanges(((GrCaseSection)element).getStatements());
          } else {
            var statements = ((GrCaseSection)element).getStatements();
            if (statements.length == 1 && statements[0] instanceof GrBlockStatement) {
              GrOpenBlock block = ((GrBlockStatement)statements[0]).getBlock();
              if (nlsAfter(block.getLBrace())) {
                addRange(new LineRange(element.getFirstChild(), block.getLBrace()).endLine);
              }
              addChildRanges(block.getStatements());
            }
          }
        }
        else if (element instanceof GroovyFileBase) {
          addChildRanges(((GroovyFileBase)element).getTopStatements());
        }
        else if (!stmtLevel && !topLevel && element instanceof GrTypeDefinitionBody) {
          addChildRanges(((GrTypeDefinitionBody)element).getMemberDeclarations());
        }
        else {
          super.visitElement(element);
        }
      }

      private boolean shouldDigInside(GroovyPsiElement statement) {
        if (stmtLevel && (statement instanceof GrMethod || statement instanceof GrTypeDefinition)) {
          return false;
        }
        if (statement instanceof GrVariableDeclaration && !stmtLevel) {
          return false;
        }
        return true;
      }

      private void addChildRanges(GroovyPsiElement[] statements) {
        for (int i = 0; i < statements.length; i++) {
          GroovyPsiElement statement = statements[i];
          if (nlsAfter(statement)) {
            final LineRange range = getLineRange(statement);
            if ((i == 0 || isStatement(statements[i-1])) && isStatement(statement)) {
              for (int j = lastStart; j < range.startLine; j++) {
                addRange(j + 1);
              }
            }
            lastStart = range.startLine;
            if (shouldDigInside(statement)) {
              statement.accept(this);
            }
            addRange(range.endLine);
          }
        }
      }
    });
    return result;
  }
private List<LineRange> allRanges(final GroovyPsiElement scope, final boolean stmtLevel, final boolean topLevel)