Skip to content

Commit

Permalink
Implement FILE_HEADER folding
Browse files Browse the repository at this point in the history
Bug: #33033
Change-Id: I66833cc73b4adecf1743c623cfc2c7fbd9af45bc
Reviewed-on: https://dart-review.googlesource.com/55284
Commit-Queue: Danny Tuppeny <dantup@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
  • Loading branch information
DanTup authored and commit-bot@chromium.org committed May 16, 2018
1 parent 05deb5b commit 39fa25c
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 39 deletions.
115 changes: 78 additions & 37 deletions pkg/analysis_server/lib/src/computer/computer_folding.dart
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/generated/source.dart';
Expand All @@ -24,6 +25,7 @@ class DartUnitFoldingComputer {
* Returns a list of folding regions, not `null`.
*/
List<FoldingRegion> compute() {
_addFileHeaderRegion();
_unit.accept(new _DartUnitFoldingComputerVisitor(this));

if (_firstDirective != null &&
Expand All @@ -37,6 +39,67 @@ class DartUnitFoldingComputer {

return _foldingRegions;
}

void _addFileHeaderRegion() {
Token firstToken = _unit.beginToken;
while (firstToken?.type == TokenType.SCRIPT_TAG) {
firstToken = firstToken.next;
}

final Token firstComment = firstToken?.precedingComments;
if (firstComment == null ||
firstComment.type != TokenType.SINGLE_LINE_COMMENT) {
return;
}

// Walk through the comments looking for a blank line to signal the end of
// the file header.
Token lastComment = firstComment;
while (lastComment.next != null) {
lastComment = lastComment.next;

// If we ran out of tokens, use the original token as starting position.
final hasBlankLine =
_hasBlankLineBetween(lastComment, lastComment.next ?? firstToken);

// Also considerd non-single-line-comments as the end
final nextCommentIsDifferentType = lastComment.next != null &&
lastComment.next.type != TokenType.SINGLE_LINE_COMMENT;

if (hasBlankLine || nextCommentIsDifferentType) {
_addRegion(firstComment.end, lastComment.end, FoldingKind.FILE_HEADER);
break;
}
}
}

_addRegion(int startOffset, int endOffset, FoldingKind kind) {
final CharacterLocation start = _lineInfo.getLocation(startOffset);
final CharacterLocation end = _lineInfo.getLocation(endOffset);

if (start.lineNumber != end.lineNumber) {
_foldingRegions
.add(new FoldingRegion(kind, startOffset, endOffset - startOffset));
}
}

_addRegionForAnnotations(List<Annotation> annotations) {
if (annotations.isNotEmpty) {
_addRegion(annotations.first.name.end, annotations.last.end,
FoldingKind.ANNOTATIONS);
}
}

bool _hasBlankLineBetween(Token first, Token second) {
final CharacterLocation firstLoc = _lineInfo.getLocation(first.end);
final CharacterLocation secondLoc = _lineInfo.getLocation(second.offset);
return secondLoc.lineNumber - firstLoc.lineNumber > 1;
}

_recordDirective(Directive node) {
_firstDirective ??= node;
_lastDirective = node;
}
}

/**
Expand All @@ -48,101 +111,79 @@ class _DartUnitFoldingComputerVisitor extends RecursiveAstVisitor<Object> {

@override
Object visitBlockFunctionBody(BlockFunctionBody node) {
_addRegion(node.block.leftBracket.end, node.block.rightBracket.offset,
FoldingKind.FUNCTION_BODY);
_computer._addRegion(node.block.leftBracket.end,
node.block.rightBracket.offset, FoldingKind.FUNCTION_BODY);
return super.visitBlockFunctionBody(node);
}

@override
Object visitClassDeclaration(ClassDeclaration node) {
_addRegionForAnnotations(node.metadata);
_addRegion(
_computer._addRegionForAnnotations(node.metadata);
_computer._addRegion(
node.leftBracket.end, node.rightBracket.offset, FoldingKind.CLASS_BODY);
return super.visitClassDeclaration(node);
}

@override
Object visitComment(Comment node) {
if (node.isDocumentation) {
_addRegion(node.offset, node.end, FoldingKind.DOCUMENTATION_COMMENT);
_computer._addRegion(
node.offset, node.end, FoldingKind.DOCUMENTATION_COMMENT);
}
return super.visitComment(node);
}

@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
_addRegionForAnnotations(node.metadata);
_computer._addRegionForAnnotations(node.metadata);
return super.visitConstructorDeclaration(node);
}

@override
Object visitExportDirective(ExportDirective node) {
_recordDirective(node);
_computer._recordDirective(node);
return super.visitExportDirective(node);
}

@override
Object visitFieldDeclaration(FieldDeclaration node) {
_addRegionForAnnotations(node.metadata);
_computer._addRegionForAnnotations(node.metadata);
return super.visitFieldDeclaration(node);
}

@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
_addRegionForAnnotations(node.metadata);
_computer._addRegionForAnnotations(node.metadata);
return super.visitFunctionDeclaration(node);
}

@override
visitImportDirective(ImportDirective node) {
_recordDirective(node);
_computer._recordDirective(node);
return super.visitImportDirective(node);
}

@override
Object visitLibraryDirective(LibraryDirective node) {
_recordDirective(node);
_computer._recordDirective(node);
return super.visitLibraryDirective(node);
}

@override
Object visitMethodDeclaration(MethodDeclaration node) {
_addRegionForAnnotations(node.metadata);
_computer._addRegionForAnnotations(node.metadata);
return super.visitMethodDeclaration(node);
}

@override
Object visitPartDirective(PartDirective node) {
_recordDirective(node);
_computer._recordDirective(node);
return super.visitPartDirective(node);
}

@override
Object visitPartOfDirective(PartOfDirective node) {
_recordDirective(node);
_computer._recordDirective(node);
return super.visitPartOfDirective(node);
}

_addRegion(int startOffset, int endOffset, FoldingKind kind) {
final CharacterLocation start =
_computer._lineInfo.getLocation(startOffset);
final CharacterLocation end = _computer._lineInfo.getLocation(endOffset);

if (start.lineNumber != end.lineNumber) {
_computer._foldingRegions
.add(new FoldingRegion(kind, startOffset, endOffset - startOffset));
}
}

_addRegionForAnnotations(List<Annotation> annotations) {
if (annotations.isNotEmpty) {
_addRegion(annotations.first.name.end, annotations.last.end,
FoldingKind.ANNOTATIONS);
}
}

_recordDirective(Directive node) {
_computer._firstDirective ??= node;
_computer._lastDirective = node;
}
}
42 changes: 40 additions & 2 deletions pkg/analysis_server/test/src/computer/folding_computer_test.dart
Expand Up @@ -196,7 +196,6 @@ class MyClass2 {/*4:INC*/
_compareRegions(regions, content);
}

@failingTest
test_file_header() async {
String content = """
// Copyright some year by some people/*1:EXC*/
Expand All @@ -211,7 +210,46 @@ main() {}
_compareRegions(regions, content);
}

@failingTest
test_file_header_with_no_function_comment() async {
String content = """
// Copyright some year by some people/*1:EXC*/
// See LICENCE etc./*1:INC:FILE_HEADER*/
main() {}
""";

final regions = await _computeRegions(content);
_compareRegions(regions, content);
}

test_file_header_with_non_end_of_line_comment() async {
String content = """
// Copyright some year by some people/*1:EXC*/
// See LICENCE etc./*1:INC:FILE_HEADER*/
/* This shouldn't be part of the file header */
main() {}
""";

final regions = await _computeRegions(content);
_compareRegions(regions, content);
}

test_file_header_does_not_include_block_comments() async {
String content = """
/*
* Copyright some year by some people
* See LICENCE etc.
*/
/* This shouldn't be part of the file header */
main() {}
""";

final regions = await _computeRegions(content);
expect(regions, hasLength(0));
}

test_file_header_with_script_prefix() async {
String content = """
#! /usr/bin/dart
Expand Down

0 comments on commit 39fa25c

Please sign in to comment.