|
| 1 | +/* |
| 2 | + * This file is a part of BSL Language Server. |
| 3 | + * |
| 4 | + * Copyright © 2018-2019 |
| 5 | + * Alexey Sosnoviy <[email protected]>, Nikita Gryzlov <[email protected]> and contributors |
| 6 | + * |
| 7 | + * SPDX-License-Identifier: LGPL-3.0-or-later |
| 8 | + * |
| 9 | + * BSL Language Server is free software; you can redistribute it and/or |
| 10 | + * modify it under the terms of the GNU Lesser General Public |
| 11 | + * License as published by the Free Software Foundation; either |
| 12 | + * version 3.0 of the License, or (at your option) any later version. |
| 13 | + * |
| 14 | + * BSL Language Server is distributed in the hope that it will be useful, |
| 15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 17 | + * Lesser General Public License for more details. |
| 18 | + * |
| 19 | + * You should have received a copy of the GNU Lesser General Public |
| 20 | + * License along with BSL Language Server. |
| 21 | + */ |
| 22 | +package org.github._1c_syntax.bsl.languageserver.providers; |
| 23 | + |
| 24 | +import org.antlr.v4.runtime.Token; |
| 25 | +import org.antlr.v4.runtime.tree.ParseTree; |
| 26 | +import org.antlr.v4.runtime.tree.TerminalNode; |
| 27 | +import org.eclipse.lsp4j.FoldingRange; |
| 28 | +import org.github._1c_syntax.bsl.languageserver.context.DocumentContext; |
| 29 | +import org.github._1c_syntax.bsl.parser.BSLParser; |
| 30 | +import org.github._1c_syntax.bsl.parser.BSLParserBaseVisitor; |
| 31 | + |
| 32 | +import java.util.ArrayDeque; |
| 33 | +import java.util.ArrayList; |
| 34 | +import java.util.Deque; |
| 35 | +import java.util.List; |
| 36 | + |
| 37 | +public final class FoldingRangeProvider { |
| 38 | + |
| 39 | + private static final String REGION_KIND = "region"; |
| 40 | + private static final String COMMENT_KIND = "comment"; |
| 41 | + |
| 42 | + private FoldingRangeProvider() { |
| 43 | + // only statics |
| 44 | + } |
| 45 | + |
| 46 | + public static List<FoldingRange> getFoldingRange(DocumentContext documentContext) { |
| 47 | + |
| 48 | + List<FoldingRange> foldingRanges = getCommentRanges(documentContext); |
| 49 | + |
| 50 | + CodeBlockRangeFinder codeBlockRangeFinder = new CodeBlockRangeFinder(); |
| 51 | + codeBlockRangeFinder.visitFile(documentContext.getAst()); |
| 52 | + List<FoldingRange> codeBlockRegionRanges = codeBlockRangeFinder.getRegionRanges(); |
| 53 | + |
| 54 | + RegionRangeFinder regionRangeFinder = new RegionRangeFinder(); |
| 55 | + regionRangeFinder.visitFile(documentContext.getAst()); |
| 56 | + List<FoldingRange> regionRanges = regionRangeFinder.getRegionRanges(); |
| 57 | + |
| 58 | + PreprocIfRegionRangeFinder preprocIfRegionRangeFinder = new PreprocIfRegionRangeFinder(); |
| 59 | + preprocIfRegionRangeFinder.visitFile(documentContext.getAst()); |
| 60 | + List<FoldingRange> preprocRegionRanges = preprocIfRegionRangeFinder.getRegionRanges(); |
| 61 | + |
| 62 | + foldingRanges.addAll(codeBlockRegionRanges); |
| 63 | + foldingRanges.addAll(regionRanges); |
| 64 | + foldingRanges.addAll(preprocRegionRanges); |
| 65 | + |
| 66 | + return foldingRanges; |
| 67 | + } |
| 68 | + |
| 69 | + private static List<FoldingRange> getCommentRanges(DocumentContext documentContext) { |
| 70 | + List<FoldingRange> foldingRanges = new ArrayList<>(); |
| 71 | + |
| 72 | + int lastRangeStart = -1; |
| 73 | + int previousLine = -1; |
| 74 | + List<Token> comments = documentContext.getComments(); |
| 75 | + for (Token token : comments) { |
| 76 | + int tokenLine = token.getLine(); |
| 77 | + |
| 78 | + if (tokenLine != previousLine + 1) { |
| 79 | + if (lastRangeStart != previousLine) { |
| 80 | + FoldingRange foldingRange = new FoldingRange(lastRangeStart - 1, previousLine - 1); |
| 81 | + foldingRange.setKind(COMMENT_KIND); |
| 82 | + |
| 83 | + foldingRanges.add(foldingRange); |
| 84 | + } |
| 85 | + // new range |
| 86 | + lastRangeStart = tokenLine; |
| 87 | + } |
| 88 | + |
| 89 | + previousLine = tokenLine; |
| 90 | + } |
| 91 | + |
| 92 | + // add last range |
| 93 | + if (lastRangeStart != previousLine) { |
| 94 | + FoldingRange foldingRange = new FoldingRange(lastRangeStart - 1, previousLine - 1); |
| 95 | + foldingRange.setKind(COMMENT_KIND); |
| 96 | + |
| 97 | + foldingRanges.add(foldingRange); |
| 98 | + } |
| 99 | + return foldingRanges; |
| 100 | + } |
| 101 | + |
| 102 | + private static class CodeBlockRangeFinder extends BSLParserBaseVisitor<ParseTree> { |
| 103 | + |
| 104 | + private List<FoldingRange> regionRanges = new ArrayList<>(); |
| 105 | + |
| 106 | + public List<FoldingRange> getRegionRanges() { |
| 107 | + return new ArrayList<>(regionRanges); |
| 108 | + } |
| 109 | + |
| 110 | + @Override |
| 111 | + public ParseTree visitProcedure(BSLParser.ProcedureContext ctx) { |
| 112 | + addRegionRange(ctx.procDeclaration().PROCEDURE_KEYWORD(), ctx.ENDPROCEDURE_KEYWORD()); |
| 113 | + return super.visitProcedure(ctx); |
| 114 | + } |
| 115 | + |
| 116 | + @Override |
| 117 | + public ParseTree visitFunction(BSLParser.FunctionContext ctx) { |
| 118 | + addRegionRange(ctx.funcDeclaration().FUNCTION_KEYWORD(), ctx.ENDFUNCTION_KEYWORD()); |
| 119 | + return super.visitFunction(ctx); |
| 120 | + } |
| 121 | + |
| 122 | + @Override |
| 123 | + public ParseTree visitIfStatement(BSLParser.IfStatementContext ctx) { |
| 124 | + addRegionRange(ctx.IF_KEYWORD(), ctx.ENDIF_KEYWORD()); |
| 125 | + return super.visitIfStatement(ctx); |
| 126 | + } |
| 127 | + |
| 128 | + @Override |
| 129 | + public ParseTree visitWhileStatement(BSLParser.WhileStatementContext ctx) { |
| 130 | + addRegionRange(ctx.WHILE_KEYWORD(), ctx.ENDDO_KEYWORD()); |
| 131 | + return super.visitWhileStatement(ctx); |
| 132 | + } |
| 133 | + |
| 134 | + @Override |
| 135 | + public ParseTree visitForStatement(BSLParser.ForStatementContext ctx) { |
| 136 | + addRegionRange(ctx.FOR_KEYWORD(), ctx.ENDDO_KEYWORD()); |
| 137 | + return super.visitForStatement(ctx); |
| 138 | + } |
| 139 | + |
| 140 | + @Override |
| 141 | + public ParseTree visitForEachStatement(BSLParser.ForEachStatementContext ctx) { |
| 142 | + addRegionRange(ctx.FOR_KEYWORD(), ctx.ENDDO_KEYWORD()); |
| 143 | + return super.visitForEachStatement(ctx); |
| 144 | + } |
| 145 | + |
| 146 | + @Override |
| 147 | + public ParseTree visitTryStatement(BSLParser.TryStatementContext ctx) { |
| 148 | + addRegionRange(ctx.TRY_KEYWORD(), ctx.ENDTRY_KEYWORD()); |
| 149 | + return super.visitTryStatement(ctx); |
| 150 | + } |
| 151 | + |
| 152 | + private void addRegionRange(TerminalNode start, TerminalNode stop) { |
| 153 | + int startLine = start.getSymbol().getLine(); |
| 154 | + int stopLine = stop.getSymbol().getLine(); |
| 155 | + |
| 156 | + if (stopLine > startLine) { |
| 157 | + FoldingRange foldingRange = new FoldingRange(startLine - 1, stopLine - 1); |
| 158 | + foldingRange.setKind(REGION_KIND); |
| 159 | + |
| 160 | + regionRanges.add(foldingRange); |
| 161 | + } |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + private static class RegionRangeFinder extends BSLParserBaseVisitor<ParseTree> { |
| 166 | + |
| 167 | + private Deque<BSLParser.RegionStartContext> regionStack = new ArrayDeque<>(); |
| 168 | + private List<FoldingRange> regionRanges = new ArrayList<>(); |
| 169 | + |
| 170 | + public List<FoldingRange> getRegionRanges() { |
| 171 | + return new ArrayList<>(regionRanges); |
| 172 | + } |
| 173 | + |
| 174 | + @Override |
| 175 | + public ParseTree visitRegionStart(BSLParser.RegionStartContext ctx) { |
| 176 | + regionStack.push(ctx); |
| 177 | + return super.visitRegionStart(ctx); |
| 178 | + } |
| 179 | + |
| 180 | + @Override |
| 181 | + public ParseTree visitRegionEnd(BSLParser.RegionEndContext ctx) { |
| 182 | + |
| 183 | + BSLParser.RegionStartContext regionStart = regionStack.pop(); |
| 184 | + |
| 185 | + int start = regionStart.getStart().getLine(); |
| 186 | + int stop = ctx.getStop().getLine(); |
| 187 | + |
| 188 | + FoldingRange foldingRange = new FoldingRange(start - 1, stop - 1); |
| 189 | + foldingRange.setKind(REGION_KIND); |
| 190 | + |
| 191 | + regionRanges.add(foldingRange); |
| 192 | + |
| 193 | + return super.visitRegionEnd(ctx); |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + private static class PreprocIfRegionRangeFinder extends BSLParserBaseVisitor<ParseTree> { |
| 198 | + |
| 199 | + private Deque<BSLParser.Preproc_ifContext> preprocIfRegionStack = new ArrayDeque<>(); |
| 200 | + private List<FoldingRange> regionRanges = new ArrayList<>(); |
| 201 | + |
| 202 | + public List<FoldingRange> getRegionRanges() { |
| 203 | + return new ArrayList<>(regionRanges); |
| 204 | + } |
| 205 | + |
| 206 | + @Override |
| 207 | + public ParseTree visitPreproc_if(BSLParser.Preproc_ifContext ctx) { |
| 208 | + preprocIfRegionStack.push(ctx); |
| 209 | + return super.visitPreproc_if(ctx); |
| 210 | + } |
| 211 | + |
| 212 | + @Override |
| 213 | + public ParseTree visitPreproc_endif(BSLParser.Preproc_endifContext ctx) { |
| 214 | + BSLParser.Preproc_ifContext regionStart = preprocIfRegionStack.pop(); |
| 215 | + |
| 216 | + int start = regionStart.getStart().getLine(); |
| 217 | + int stop = ctx.getStop().getLine(); |
| 218 | + |
| 219 | + FoldingRange foldingRange = new FoldingRange(start - 1, stop - 1); |
| 220 | + foldingRange.setKind(REGION_KIND); |
| 221 | + |
| 222 | + regionRanges.add(foldingRange); |
| 223 | + |
| 224 | + return super.visitPreproc_endif(ctx); |
| 225 | + } |
| 226 | + |
| 227 | + } |
| 228 | +} |
0 commit comments