Skip to content

Commit

Permalink
feat: Add prefer_space_between_elements rule (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
naipaka authored Jul 29, 2024
1 parent 8a22122 commit 1534bff
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/altive_lints/lib/altive_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'src/lints/avoid_hardcoded_color.dart';
import 'src/lints/avoid_hardcoded_japanese.dart';
import 'src/lints/avoid_shrink_wrap_in_list_view.dart';
import 'src/lints/avoid_single_child.dart';
import 'src/lints/prefer_space_between_elements.dart';

PluginBase createPlugin() => _AltivePlugin();

Expand All @@ -16,5 +17,6 @@ class _AltivePlugin extends PluginBase {
const AvoidHardcodedJapanese(),
const AvoidShrinkWrapInListView(),
const AvoidSingleChild(),
const PreferSpaceBetweenElements(),
];
}
111 changes: 111 additions & 0 deletions packages/altive_lints/lib/src/lints/prefer_space_between_elements.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

/// A `prefer_space_between_elements` rule that enforces
/// spacing conventions within class definitions by requiring
/// a blank line between the constructor and fields,
/// and between the constructor and the build method.
///
/// Proper spacing enhances code readability and organization,
/// making it easier to visually distinguish between
/// different sections of a class.
///
/// ### Example
///
/// #### BAD:
///
/// ```dart
/// class MyWidget extends StatelessWidget {
/// final String title;
/// MyWidget(this.title);
/// @override
/// Widget build(BuildContext context) {
/// return Text(title);
/// }
/// }
/// ```
///
/// #### GOOD:
///
/// ```dart
/// class MyWidget extends StatelessWidget {
/// final String title;
///
/// MyWidget(this.title);
///
/// @override
/// Widget build(BuildContext context) {
/// return Text(title);
/// }
/// }
/// ```
class PreferSpaceBetweenElements extends DartLintRule {
const PreferSpaceBetweenElements() : super(code: _code);

static const _code = LintCode(
name: 'prefer_space_between_elements',
problemMessage:
'Ensure there is a blank line between constructor and fields, '
'and between constructor and build method.',
);

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addClassDeclaration((node) {
final lineInfo = resolver.lineInfo;
final members = node.members;
for (var i = 0; i < members.length - 1; i++) {
final currentMember = members[i];
final nextMember = members[i + 1];

// No blank line between constructor and build method.
if (currentMember is ConstructorDeclaration &&
nextMember is MethodDeclaration &&
nextMember.name.lexeme == 'build') {
if (!_hasBlankLineBetween(currentMember, nextMember, lineInfo)) {
reporter.reportErrorForNode(code, nextMember);
}
}

// No blank line between fields and constructor.
if (currentMember is FieldDeclaration &&
nextMember is ConstructorDeclaration) {
if (!_hasBlankLineBetween(currentMember, nextMember, lineInfo)) {
reporter.reportErrorForNode(code, nextMember);
}
}

// No blank line between constructor and fields.
if (currentMember is ConstructorDeclaration &&
nextMember is FieldDeclaration) {
if (!_hasBlankLineBetween(currentMember, nextMember, lineInfo)) {
reporter.reportErrorForNode(code, nextMember);
}
}

// No blank line between constructor and fields.
if (currentMember is FieldDeclaration &&
nextMember is MethodDeclaration &&
nextMember.name.lexeme == 'build') {
if (!_hasBlankLineBetween(currentMember, nextMember, lineInfo)) {
reporter.reportErrorForNode(code, nextMember);
}
}
}
});
}

/// Returns `true` if there is a blank line between [first] and [second].
bool _hasBlankLineBetween(AstNode first, AstNode second, LineInfo lineInfo) {
final firstEndLine = lineInfo.getLocation(first.endToken.end).lineNumber;
final secondStartLine =
lineInfo.getLocation(second.beginToken.offset).lineNumber;
return (secondStartLine - firstEndLine) > 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';

class MyWidget extends StatelessWidget {
const MyWidget({super.key}); // expect_lint: prefer_space_between_elements
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

class MyWidget2 extends StatelessWidget {
const MyWidget2({
super.key,
required this.id,
}); // expect_lint: prefer_space_between_elements
final String id; // expect_lint: prefer_space_between_elements
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

0 comments on commit 1534bff

Please sign in to comment.