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 <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2020-06-17 19:32:50 +00:00 committed by commit-bot@chromium.org
parent 90613154e5
commit f0e1f32c87
12 changed files with 93 additions and 6 deletions

View file

@ -109,7 +109,7 @@ a:focus, a:hover {
<body> <body>
<h1>Analysis Server API Specification</h1> <h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version <h1 style="color:#999999">Version
1.27.4 1.28.0
</h1> </h1>
<p> <p>
This document contains a specification of the API provided by the 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. An enumeration of the kinds of folding regions.
</p> </p>
<dl><dt class="value">ANNOTATIONS</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd> <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
<p> <p>
A description of a region that can be folded. A description of a region that can be folded.
</p> </p>

View file

@ -6,7 +6,7 @@
// To regenerate the file, use the script // To regenerate the file, use the script
// "pkg/analysis_server/tool/spec/generate_files". // "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 = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';

View file

@ -19,6 +19,35 @@ class DartUnitFoldingComputer {
DartUnitFoldingComputer(this._lineInfo, this._unit); 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`. /// Returns a list of folding regions, not `null`.
List<FoldingRegion> compute() { List<FoldingRegion> compute() {
_addFileHeaderRegion(); _addFileHeaderRegion();
@ -181,6 +210,17 @@ class _DartUnitFoldingComputerVisitor extends RecursiveAstVisitor<void> {
super.visitFunctionExpressionInvocation(node); 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 @override
void visitImportDirective(ImportDirective node) { void visitImportDirective(ImportDirective node) {
_computer._recordDirective(node); _computer._recordDirective(node);

View file

@ -701,6 +701,7 @@ final Matcher isFlutterWidgetPropertyValueEnumItem = LazyMatcher(() =>
/// ///
/// enum { /// enum {
/// ANNOTATIONS /// ANNOTATIONS
/// BLOCK
/// CLASS_BODY /// CLASS_BODY
/// DIRECTIVES /// DIRECTIVES
/// DOCUMENTATION_COMMENT /// DOCUMENTATION_COMMENT
@ -711,6 +712,7 @@ final Matcher isFlutterWidgetPropertyValueEnumItem = LazyMatcher(() =>
/// } /// }
final Matcher isFoldingKind = MatchesEnum('FoldingKind', [ final Matcher isFoldingKind = MatchesEnum('FoldingKind', [
'ANNOTATIONS', 'ANNOTATIONS',
'BLOCK',
'CLASS_BODY', 'CLASS_BODY',
'DIRECTIVES', 'DIRECTIVES',
'DOCUMENTATION_COMMENT', 'DOCUMENTATION_COMMENT',

View file

@ -162,6 +162,40 @@ class FoldingTest extends AbstractLspAnalysisServerTest {
expect(regions, unorderedEquals(expectedRegions)); expect(regions, unorderedEquals(expectedRegions));
} }
Future<void> 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<void> test_nonDartFile() async { Future<void> test_nonDartFile() async {
await initialize(); await initialize();
await openFile(pubspecFileUri, simplePubspecContent); await openFile(pubspecFileUri, simplePubspecContent);

View file

@ -17,6 +17,8 @@ public class FoldingKind {
public static final String ANNOTATIONS = "ANNOTATIONS"; public static final String ANNOTATIONS = "ANNOTATIONS";
public static final String BLOCK = "BLOCK";
public static final String CLASS_BODY = "CLASS_BODY"; public static final String CLASS_BODY = "CLASS_BODY";
public static final String DIRECTIVES = "DIRECTIVES"; public static final String DIRECTIVES = "DIRECTIVES";

View file

@ -7,7 +7,7 @@
<body> <body>
<h1>Analysis Server API Specification</h1> <h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version <h1 style="color:#999999">Version
<version>1.27.4</version> <version>1.28.0</version>
</h1> </h1>
<p> <p>
This document contains a specification of the API provided by the This document contains a specification of the API provided by the

View file

@ -6,7 +6,7 @@
// To regenerate the file, use the script // To regenerate the file, use the script
// "pkg/analysis_server/tool/spec/generate_files". // "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 = 'analysis.analyzedFiles';
const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories'; const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';

View file

@ -1306,7 +1306,7 @@ a:focus, a:hover {
An enumeration of the kinds of folding regions. An enumeration of the kinds of folding regions.
</p> </p>
<dl><dt class="value">ANNOTATIONS</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd> <dl><dt class="value">ANNOTATIONS</dt><dt class="value">BLOCK</dt><dt class="value">CLASS_BODY</dt><dt class="value">DIRECTIVES</dt><dt class="value">DOCUMENTATION_COMMENT</dt><dt class="value">FILE_HEADER</dt><dt class="value">FUNCTION_BODY</dt><dt class="value">INVOCATION</dt><dt class="value">LITERAL</dt></dl></dd><dt class="typeDefinition"><a name="type_FoldingRegion">FoldingRegion: object</a></dt><dd>
<p> <p>
A description of a region that can be folded. A description of a region that can be folded.
</p> </p>

View file

@ -1894,6 +1894,7 @@ class ElementKind implements Enum {
/// ///
/// enum { /// enum {
/// ANNOTATIONS /// ANNOTATIONS
/// BLOCK
/// CLASS_BODY /// CLASS_BODY
/// DIRECTIVES /// DIRECTIVES
/// DOCUMENTATION_COMMENT /// DOCUMENTATION_COMMENT
@ -1907,6 +1908,8 @@ class ElementKind implements Enum {
class FoldingKind implements Enum { class FoldingKind implements Enum {
static const FoldingKind ANNOTATIONS = FoldingKind._('ANNOTATIONS'); static const FoldingKind ANNOTATIONS = FoldingKind._('ANNOTATIONS');
static const FoldingKind BLOCK = FoldingKind._('BLOCK');
static const FoldingKind CLASS_BODY = FoldingKind._('CLASS_BODY'); static const FoldingKind CLASS_BODY = FoldingKind._('CLASS_BODY');
static const FoldingKind DIRECTIVES = FoldingKind._('DIRECTIVES'); 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. /// A list containing all of the enum values that are defined.
static const List<FoldingKind> VALUES = <FoldingKind>[ static const List<FoldingKind> VALUES = <FoldingKind>[
ANNOTATIONS, ANNOTATIONS,
BLOCK,
CLASS_BODY, CLASS_BODY,
DIRECTIVES, DIRECTIVES,
DOCUMENTATION_COMMENT, DOCUMENTATION_COMMENT,
@ -1943,6 +1947,8 @@ class FoldingKind implements Enum {
switch (name) { switch (name) {
case 'ANNOTATIONS': case 'ANNOTATIONS':
return ANNOTATIONS; return ANNOTATIONS;
case 'BLOCK':
return BLOCK;
case 'CLASS_BODY': case 'CLASS_BODY':
return CLASS_BODY; return CLASS_BODY;
case 'DIRECTIVES': case 'DIRECTIVES':

View file

@ -301,6 +301,7 @@ final Matcher isFilePath = isString;
/// ///
/// enum { /// enum {
/// ANNOTATIONS /// ANNOTATIONS
/// BLOCK
/// CLASS_BODY /// CLASS_BODY
/// DIRECTIVES /// DIRECTIVES
/// DOCUMENTATION_COMMENT /// DOCUMENTATION_COMMENT
@ -311,6 +312,7 @@ final Matcher isFilePath = isString;
/// } /// }
final Matcher isFoldingKind = MatchesEnum('FoldingKind', [ final Matcher isFoldingKind = MatchesEnum('FoldingKind', [
'ANNOTATIONS', 'ANNOTATIONS',
'BLOCK',
'CLASS_BODY', 'CLASS_BODY',
'DIRECTIVES', 'DIRECTIVES',
'DOCUMENTATION_COMMENT', 'DOCUMENTATION_COMMENT',

View file

@ -569,6 +569,7 @@
</p> </p>
<enum> <enum>
<value><code>ANNOTATIONS</code></value> <value><code>ANNOTATIONS</code></value>
<value><code>BLOCK</code></value>
<value><code>CLASS_BODY</code></value> <value><code>CLASS_BODY</code></value>
<value><code>DIRECTIVES</code></value> <value><code>DIRECTIVES</code></value>
<value><code>DOCUMENTATION_COMMENT</code></value> <value><code>DOCUMENTATION_COMMENT</code></value>