Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto add imports #3864

Merged
merged 4 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/riverpod_lint/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased build

- All lints/assists now automatically add the relevant imports when
updating code.
- Updated `provider_dependencies` to support `@Dependencies`
- added `riverpod_syntax_error`, for reporting errors when the generator would throw.
- added `avoid_keep_alive_dependency_inside_auto_dispose`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dar
import 'package:collection/collection.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

import '../imports.dart';
import '../riverpod_custom_lint.dart';
import 'convert_to_widget_utils.dart';

Expand Down Expand Up @@ -72,15 +73,15 @@ class ConvertToStatefulBaseWidget extends RiverpodAssist {
ExtendsClause node,
) {
final changeBuilder = reporter.createChangeBuilder(
message: 'Convert to ${targetWidget.widgetName}',
message: 'Convert to ${targetWidget.widgetAssistName}',
priority: targetWidget.priority,
);

changeBuilder.addDartFileEdit((builder) {
// Change the extended base class
builder.addSimpleReplacement(
node.superclass.sourceRange,
targetWidget.widgetName,
targetWidget.widgetName(builder),
);

final widgetClass = node.thisOrAncestorOfType<ClassDeclaration>();
Expand Down Expand Up @@ -143,10 +144,10 @@ class ConvertToStatefulBaseWidget extends RiverpodAssist {
switch (targetWidget) {
case StatefulBaseWidgetType.consumerStatefulWidget:
case StatefulBaseWidgetType.statefulHookConsumerWidget:
baseStateName = 'ConsumerState';
baseStateName = builder.importConsumerState();
case StatefulBaseWidgetType.statefulHookWidget:
case StatefulBaseWidgetType.statefulWidget:
baseStateName = 'State';
baseStateName = builder.importState();
}

// Split the class into two classes right before the build method
Expand Down Expand Up @@ -178,15 +179,15 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> {
required int priorityAdjustment,
}) {
final changeBuilder = reporter.createChangeBuilder(
message: 'Convert to ${targetWidget.widgetName}',
message: 'Convert to ${targetWidget.widgetAssistName}',
priority: targetWidget.priority + priorityAdjustment,
);

changeBuilder.addDartFileEdit((builder) {
// Change the extended base class
builder.addSimpleReplacement(
node.superclass.sourceRange,
targetWidget.widgetName,
targetWidget.widgetName(builder),
);

final widgetClass = node.thisOrAncestorOfType<ClassDeclaration>();
Expand All @@ -199,10 +200,10 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> {
switch (targetWidget) {
case StatefulBaseWidgetType.consumerStatefulWidget:
case StatefulBaseWidgetType.statefulHookConsumerWidget:
baseStateName = 'ConsumerState';
baseStateName = builder.importConsumerState();
case StatefulBaseWidgetType.statefulHookWidget:
case StatefulBaseWidgetType.statefulWidget:
baseStateName = 'State';
baseStateName = builder.importState();
}

final createStateMethod = widgetClass.members
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:analyzer/source/source_range.dart';
import 'package:collection/collection.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

import '../imports.dart';
import '../riverpod_custom_lint.dart';
import 'convert_to_widget_utils.dart';

Expand Down Expand Up @@ -74,15 +75,15 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist {
ExtendsClause node,
) {
final changeBuilder = reporter.createChangeBuilder(
message: 'Convert to ${targetWidget.widgetName}',
message: 'Convert to ${targetWidget.assistName}',
priority: targetWidget.priority,
);

changeBuilder.addDartFileEdit((builder) {
// Change the extended base class
builder.addSimpleReplacement(
node.superclass.sourceRange,
targetWidget.widgetName,
targetWidget.widgetName(builder),
);

final buildMethod = node
Expand All @@ -99,11 +100,13 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist {
switch (targetWidget) {
case StatelessBaseWidgetType.consumerWidget:
case StatelessBaseWidgetType.hookConsumerWidget:
final widgetRef = builder.importWidgetRef();

// If the build method has not a ref, add it
if (buildParams.parameters.length == 1) {
builder.addSimpleInsertion(
buildParams.parameters.last.end,
', WidgetRef ref',
', $widgetRef ref',
);
}
case StatelessBaseWidgetType.hookWidget:
Expand All @@ -128,15 +131,15 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist {
required int priorityAdjustment,
}) {
final changeBuilder = reporter.createChangeBuilder(
message: 'Convert to ${targetWidget.widgetName}',
message: 'Convert to ${targetWidget.assistName}',
priority: targetWidget.priority + priorityAdjustment,
);

changeBuilder.addDartFileEdit((builder) {
// Change the extended base class
builder.addSimpleReplacement(
node.superclass.sourceRange,
targetWidget.widgetName,
targetWidget.widgetName(builder),
);

final widgetClass = node.thisOrAncestorOfType<ClassDeclaration>();
Expand Down Expand Up @@ -252,9 +255,10 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist {
switch (targetWidget) {
case StatelessBaseWidgetType.consumerWidget:
case StatelessBaseWidgetType.hookConsumerWidget:
final widgetRef = builder.importWidgetRef();
builder.addSimpleReplacement(
parameterRange,
'BuildContext context, WidgetRef ref',
'BuildContext context, $widgetRef ref',
);
case StatelessBaseWidgetType.hookWidget:
case StatelessBaseWidgetType.statelessWidget:
Expand All @@ -275,7 +279,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist {
}
}

// Original implemenation in
// Original implementation in
// package:analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart
class _FieldFinder extends RecursiveAstVisitor<void> {
final fieldsAssignedInConstructors = <FieldElement>{};
Expand Down
54 changes: 42 additions & 12 deletions packages/riverpod_lint/lib/src/assists/convert_to_widget_utils.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:collection/collection.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

import '../imports.dart';
import '../object_utils.dart';

enum StatelessBaseWidgetType {
hookConsumerWidget(
widgetName: 'HookConsumerWidget',
priority: 37,
typeChecker: TypeChecker.fromName(
'HookConsumerWidget',
Expand All @@ -16,7 +17,6 @@ enum StatelessBaseWidgetType {
requiredPackage: 'hooks_riverpod',
),
hookWidget(
widgetName: 'HookWidget',
priority: 36,
typeChecker: TypeChecker.fromName(
'HookWidget',
Expand All @@ -25,15 +25,13 @@ enum StatelessBaseWidgetType {
requiredPackage: 'flutter_hooks',
),
consumerWidget(
widgetName: 'ConsumerWidget',
priority: 35,
typeChecker: TypeChecker.fromName(
'ConsumerWidget',
packageName: 'flutter_riverpod',
),
),
statelessWidget(
widgetName: 'StatelessWidget',
priority: 34,
typeChecker: TypeChecker.fromName(
'StatelessWidget',
Expand All @@ -43,20 +41,39 @@ enum StatelessBaseWidgetType {
;

const StatelessBaseWidgetType({
required this.widgetName,
required this.priority,
required this.typeChecker,
this.requiredPackage,
});
final String widgetName;

final int priority;
final TypeChecker typeChecker;
final String? requiredPackage;

String widgetName(DartFileEditBuilder builder) {
return switch (this) {
StatelessBaseWidgetType.hookConsumerWidget =>
builder.importHookConsumerWidget(),
StatelessBaseWidgetType.hookWidget => builder.importHookWidget(),
StatelessBaseWidgetType.consumerWidget => builder.importConsumerWidget(),
StatelessBaseWidgetType.statelessWidget =>
builder.importStatelessWidget(),
};
}

String get assistName {
return switch (this) {
StatelessBaseWidgetType.hookConsumerWidget => 'HookConsumerWidget',
StatelessBaseWidgetType.hookWidget => 'HookWidget',
StatelessBaseWidgetType.consumerWidget => 'ConsumerWidget',
StatelessBaseWidgetType.statelessWidget => 'StatelessWidget',
};
}
}

enum StatefulBaseWidgetType {
statefulHookConsumerWidget(
widgetName: 'StatefulHookConsumerWidget',
widgetAssistName: 'StatefulHookConsumerWidget',
priority: 33,
typeChecker: TypeChecker.fromName(
'StatefulHookConsumerWidget',
Expand All @@ -65,7 +82,7 @@ enum StatefulBaseWidgetType {
requiredPackage: 'hooks_riverpod',
),
statefulHookWidget(
widgetName: 'StatefulHookWidget',
widgetAssistName: 'StatefulHookWidget',
priority: 32,
typeChecker: TypeChecker.fromName(
'StatefulHookWidget',
Expand All @@ -74,15 +91,15 @@ enum StatefulBaseWidgetType {
requiredPackage: 'flutter_hooks',
),
consumerStatefulWidget(
widgetName: 'ConsumerStatefulWidget',
widgetAssistName: 'ConsumerStatefulWidget',
priority: 31,
typeChecker: TypeChecker.fromName(
'ConsumerStatefulWidget',
packageName: 'flutter_riverpod',
),
),
statefulWidget(
widgetName: 'StatefulWidget',
widgetAssistName: 'StatefulWidget',
priority: 30,
typeChecker: TypeChecker.fromName(
'StatefulWidget',
Expand All @@ -92,15 +109,28 @@ enum StatefulBaseWidgetType {
;

const StatefulBaseWidgetType({
required this.widgetName,
required this.widgetAssistName,
required this.priority,
required this.typeChecker,
this.requiredPackage,
});
final String widgetName;

final String widgetAssistName;
final int priority;
final TypeChecker typeChecker;
final String? requiredPackage;

String widgetName(DartFileEditBuilder builder) {
return switch (this) {
StatefulBaseWidgetType.statefulHookConsumerWidget =>
builder.importStatefulHookConsumerWidget(),
StatefulBaseWidgetType.statefulHookWidget =>
builder.importStatefulHookWidget(),
StatefulBaseWidgetType.consumerStatefulWidget =>
builder.importConsumerStatefulWidget(),
StatefulBaseWidgetType.statefulWidget => builder.importStatefulWidget(),
};
}
}

TypeChecker getStatelessBaseType({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:analyzer/source/source_range.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart';

import '../imports.dart';
import '../riverpod_custom_lint.dart';

/// Right above "wrap in builder"
Expand Down Expand Up @@ -33,9 +34,11 @@ class WrapWithConsumer extends RiverpodAssist {
);

changeBuilder.addDartFileEdit((builder) {
final consumer = builder.importConsumer();

builder.addSimpleInsertion(
node.offset,
'Consumer(builder: (context, ref, child) { return ',
'$consumer(builder: (context, ref, child) { return ',
);
builder.addSimpleInsertion(node.end, '; },)');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:analyzer/source/source_range.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart';

import '../imports.dart';
import '../riverpod_custom_lint.dart';
import 'wrap_with_consumer.dart';

Expand Down Expand Up @@ -31,10 +32,8 @@ class WrapWithProviderScope extends RiverpodAssist {
);

changeBuilder.addDartFileEdit((builder) {
builder.addSimpleInsertion(
node.offset,
'ProviderScope(child: ',
);
final providerScope = builder.importProviderScope();
builder.addSimpleInsertion(node.offset, '$providerScope(child: ');
builder.addSimpleInsertion(node.end, ',)');
});
});
Expand Down
Loading
Loading