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