List<LineRange> (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; }