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>
<h1>Analysis Server API Specification</h1>
<h1 style="color:#999999">Version
1.27.4
1.28.0
</h1>
<p>
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.
</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>
A description of a region that can be folded.
</p>

View file

@ -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';

View file

@ -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<FoldingRegion> compute() {
_addFileHeaderRegion();
@ -181,6 +210,17 @@ class _DartUnitFoldingComputerVisitor extends RecursiveAstVisitor<void> {
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);

View file

@ -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',

View file

@ -162,6 +162,40 @@ class FoldingTest extends AbstractLspAnalysisServerTest {
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 {
await initialize();
await openFile(pubspecFileUri, simplePubspecContent);

View file

@ -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";

View file

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

View file

@ -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';

View file

@ -1306,7 +1306,7 @@ a:focus, a:hover {
An enumeration of the kinds of folding regions.
</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>
A description of a region that can be folded.
</p>

View file

@ -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<FoldingKind> VALUES = <FoldingKind>[
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':

View file

@ -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',

View file

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