From f0e1f32c87c805bea8b8b2e50694338b0c494845 Mon Sep 17 00:00:00 2001 From: Danny Tuppeny Date: Wed, 17 Jun 2020 19:32:50 +0000 Subject: [PATCH] Add support for folding if/else/elseif blocks Change-Id: I3cbf1f282ee59c577c6ba912e957f838e827fd3e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151520 Reviewed-by: Brian Wilkerson --- pkg/analysis_server/doc/api.html | 4 +- .../lib/protocol/protocol_constants.dart | 2 +- .../lib/src/computer/computer_folding.dart | 40 +++++++++++++++++++ .../support/protocol_matchers.dart | 2 + .../test/lsp/folding_test.dart | 34 ++++++++++++++++ .../generated/java/types/FoldingKind.java | 2 + pkg/analysis_server/tool/spec/spec_input.html | 2 +- .../lib/src/protocol/protocol_constants.dart | 2 +- pkg/analyzer_plugin/doc/api.html | 2 +- .../lib/protocol/protocol_common.dart | 6 +++ .../support/protocol_matchers.dart | 2 + .../tool/spec/common_types_spec.html | 1 + 12 files changed, 93 insertions(+), 6 deletions(-) diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html index 389b7e8327c..56c2aba8e54 100644 --- a/pkg/analysis_server/doc/api.html +++ b/pkg/analysis_server/doc/api.html @@ -109,7 +109,7 @@ a:focus, a:hover {

Analysis Server API Specification

Version - 1.27.4 + 1.28.0

This document contains a specification of the API provided by the @@ -4170,7 +4170,7 @@ a:focus, a:hover { An enumeration of the kinds of folding regions.

-
ANNOTATIONS
CLASS_BODY
DIRECTIVES
DOCUMENTATION_COMMENT
FILE_HEADER
FUNCTION_BODY
INVOCATION
LITERAL
FoldingRegion: object
+
ANNOTATIONS
BLOCK
CLASS_BODY
DIRECTIVES
DOCUMENTATION_COMMENT
FILE_HEADER
FUNCTION_BODY
INVOCATION
LITERAL
FoldingRegion: object

A description of a region that can be folded.

diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart index 6e51b614d9f..2a888ca4f66 100644 --- a/pkg/analysis_server/lib/protocol/protocol_constants.dart +++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart @@ -6,7 +6,7 @@ // To regenerate the file, use the script // "pkg/analysis_server/tool/spec/generate_files". -const String PROTOCOL_VERSION = '1.27.4'; +const String PROTOCOL_VERSION = '1.28.0'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories'; diff --git a/pkg/analysis_server/lib/src/computer/computer_folding.dart b/pkg/analysis_server/lib/src/computer/computer_folding.dart index 7f32a179d69..021a6c47ac2 100644 --- a/pkg/analysis_server/lib/src/computer/computer_folding.dart +++ b/pkg/analysis_server/lib/src/computer/computer_folding.dart @@ -19,6 +19,35 @@ class DartUnitFoldingComputer { DartUnitFoldingComputer(this._lineInfo, this._unit); + void addRegionForConditionalBlock(Block block) { + // For class/function/method blocks, we usually include the whitespace up + // until the `}` in the folding region so that when collapsed they would + // look like: + // + // class Foo { [...] } + // + // For if statements, they may have else/elseIfs which would result in long + // lines like: + // + // if (cond) { [...] } else { [...] } + // + // So these types of blocks should have their folding regions end at the + // end of the preceeding statement. + + final start = block.leftBracket.end; + if (block.endToken.precedingComments != null) { + // If there are comments before the end token, use the last of those. + var lastComment = block.endToken.precedingComments; + while (lastComment.next != null) { + lastComment = lastComment.next; + } + _addRegion(start, lastComment.end, FoldingKind.BLOCK); + } else if (block.statements.isNotEmpty) { + // Otherwise, use the end of the last statement. + _addRegion(start, block.statements.last.end, FoldingKind.BLOCK); + } + } + /// Returns a list of folding regions, not `null`. List compute() { _addFileHeaderRegion(); @@ -181,6 +210,17 @@ class _DartUnitFoldingComputerVisitor extends RecursiveAstVisitor { super.visitFunctionExpressionInvocation(node); } + @override + void visitIfStatement(IfStatement node) { + if (node.thenStatement is Block) { + _computer.addRegionForConditionalBlock(node.thenStatement); + } + if (node.elseStatement is Block) { + _computer.addRegionForConditionalBlock(node.elseStatement); + } + super.visitIfStatement(node); + } + @override void visitImportDirective(ImportDirective node) { _computer._recordDirective(node); diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart index e1f3fcbceef..6764831dd9c 100644 --- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart +++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart @@ -701,6 +701,7 @@ final Matcher isFlutterWidgetPropertyValueEnumItem = LazyMatcher(() => /// /// enum { /// ANNOTATIONS +/// BLOCK /// CLASS_BODY /// DIRECTIVES /// DOCUMENTATION_COMMENT @@ -711,6 +712,7 @@ final Matcher isFlutterWidgetPropertyValueEnumItem = LazyMatcher(() => /// } final Matcher isFoldingKind = MatchesEnum('FoldingKind', [ 'ANNOTATIONS', + 'BLOCK', 'CLASS_BODY', 'DIRECTIVES', 'DOCUMENTATION_COMMENT', diff --git a/pkg/analysis_server/test/lsp/folding_test.dart b/pkg/analysis_server/test/lsp/folding_test.dart index 49dc5ceb3b3..ab3fe89ee52 100644 --- a/pkg/analysis_server/test/lsp/folding_test.dart +++ b/pkg/analysis_server/test/lsp/folding_test.dart @@ -162,6 +162,40 @@ class FoldingTest extends AbstractLspAnalysisServerTest { expect(regions, unorderedEquals(expectedRegions)); } + Future test_ifElseElseIf() async { + final content = ''' + f(int i) { + if (i == 0) {[[ + // only + // comments]] + } else if (i == 1) {[[ + print('statements');]] + } else if (i == 2) { + } else {[[ + // else + // comments]] + } + } + '''; + + final ranges = rangesFromMarkers(content); + final expectedRegions = ranges + .map((range) => FoldingRange( + range.start.line, + range.start.character, + range.end.line, + range.end.character, + null, + )) + .toList(); + + await initialize(); + await openFile(mainFileUri, withoutMarkers(content)); + + final regions = await getFoldingRegions(mainFileUri); + expect(regions, containsAll(expectedRegions)); + } + Future test_nonDartFile() async { await initialize(); await openFile(pubspecFileUri, simplePubspecContent); diff --git a/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java b/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java index 8ae6957cb92..be9f538d149 100644 --- a/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java +++ b/pkg/analysis_server/tool/spec/generated/java/types/FoldingKind.java @@ -17,6 +17,8 @@ public class FoldingKind { public static final String ANNOTATIONS = "ANNOTATIONS"; + public static final String BLOCK = "BLOCK"; + public static final String CLASS_BODY = "CLASS_BODY"; public static final String DIRECTIVES = "DIRECTIVES"; diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html index 097bc1aaa4a..72543e5e357 100644 --- a/pkg/analysis_server/tool/spec/spec_input.html +++ b/pkg/analysis_server/tool/spec/spec_input.html @@ -7,7 +7,7 @@

Analysis Server API Specification

Version - 1.27.4 + 1.28.0

This document contains a specification of the API provided by the diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart index 6e51b614d9f..2a888ca4f66 100644 --- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart +++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart @@ -6,7 +6,7 @@ // To regenerate the file, use the script // "pkg/analysis_server/tool/spec/generate_files". -const String PROTOCOL_VERSION = '1.27.4'; +const String PROTOCOL_VERSION = '1.28.0'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories'; diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html index 2390ce261e8..a95d2c02843 100644 --- a/pkg/analyzer_plugin/doc/api.html +++ b/pkg/analyzer_plugin/doc/api.html @@ -1306,7 +1306,7 @@ a:focus, a:hover { An enumeration of the kinds of folding regions.

-
ANNOTATIONS
CLASS_BODY
DIRECTIVES
DOCUMENTATION_COMMENT
FILE_HEADER
FUNCTION_BODY
INVOCATION
LITERAL
FoldingRegion: object
+
ANNOTATIONS
BLOCK
CLASS_BODY
DIRECTIVES
DOCUMENTATION_COMMENT
FILE_HEADER
FUNCTION_BODY
INVOCATION
LITERAL
FoldingRegion: object

A description of a region that can be folded.

diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart index 732bb9040ad..11791210adf 100644 --- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart +++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart @@ -1894,6 +1894,7 @@ class ElementKind implements Enum { /// /// enum { /// ANNOTATIONS +/// BLOCK /// CLASS_BODY /// DIRECTIVES /// DOCUMENTATION_COMMENT @@ -1907,6 +1908,8 @@ class ElementKind implements Enum { class FoldingKind implements Enum { static const FoldingKind ANNOTATIONS = FoldingKind._('ANNOTATIONS'); + static const FoldingKind BLOCK = FoldingKind._('BLOCK'); + static const FoldingKind CLASS_BODY = FoldingKind._('CLASS_BODY'); static const FoldingKind DIRECTIVES = FoldingKind._('DIRECTIVES'); @@ -1925,6 +1928,7 @@ class FoldingKind implements Enum { /// A list containing all of the enum values that are defined. static const List VALUES = [ ANNOTATIONS, + BLOCK, CLASS_BODY, DIRECTIVES, DOCUMENTATION_COMMENT, @@ -1943,6 +1947,8 @@ class FoldingKind implements Enum { switch (name) { case 'ANNOTATIONS': return ANNOTATIONS; + case 'BLOCK': + return BLOCK; case 'CLASS_BODY': return CLASS_BODY; case 'DIRECTIVES': diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart index 8cebc74a7fd..2ca8d4702cb 100644 --- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart +++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart @@ -301,6 +301,7 @@ final Matcher isFilePath = isString; /// /// enum { /// ANNOTATIONS +/// BLOCK /// CLASS_BODY /// DIRECTIVES /// DOCUMENTATION_COMMENT @@ -311,6 +312,7 @@ final Matcher isFilePath = isString; /// } final Matcher isFoldingKind = MatchesEnum('FoldingKind', [ 'ANNOTATIONS', + 'BLOCK', 'CLASS_BODY', 'DIRECTIVES', 'DOCUMENTATION_COMMENT', diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html index 812c4492390..df9067db2ee 100644 --- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html +++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html @@ -569,6 +569,7 @@

ANNOTATIONS + BLOCK CLASS_BODY DIRECTIVES DOCUMENTATION_COMMENT