@@ -9,6 +9,7 @@ import "package:analyzer/dart/ast/visitor.dart";
99import "package:analyzer/dart/element/element.dart" ;
1010import "package:analyzer/dart/element/type.dart" ;
1111import "package:analyzer/error/error.dart" ;
12+ import "package:yaml/yaml.dart" ;
1213
1314class SolvroCustomLinterPlugin extends Plugin {
1415 @override
@@ -306,11 +307,15 @@ class AddHapticFeedbackOnUserInteractionRule extends _SolvroRule {
306307 "Encourages HapticFeedback in interaction callbacks." ,
307308 );
308309
310+ static const _defaultHapticWrappers = ["HapticFeedback." ];
311+
309312 @override
310313 void registerNodeProcessors (
311314 RuleVisitorRegistry registry,
312315 RuleContext context,
313316 ) {
317+ final hapticWrappers = _configuredHapticWrappers (context);
318+
314319 registry.addNamedExpression (
315320 this ,
316321 _NamedExpressionVisitor (this , (node) {
@@ -322,12 +327,74 @@ class AddHapticFeedbackOnUserInteractionRule extends _SolvroRule {
322327 return ;
323328 }
324329 final source = node.expression.toString ();
325- if (! source. contains ( "HapticFeedback." )) {
330+ if (! hapticWrappers. any (source.contains )) {
326331 reportAtNode (node);
327332 }
328333 }),
329334 );
330335 }
336+
337+ static List <String > _configuredHapticWrappers (RuleContext context) {
338+ final package = context.package;
339+ if (package == null ) {
340+ return _defaultHapticWrappers;
341+ }
342+
343+ final optionsFile = package.root.getChildAssumingFile (
344+ "analysis_options.yaml" ,
345+ );
346+ if (! optionsFile.exists) {
347+ return _defaultHapticWrappers;
348+ }
349+
350+ try {
351+ final options = loadYamlNode (optionsFile.readAsStringSync ());
352+ if (options is ! YamlMap ) {
353+ return _defaultHapticWrappers;
354+ }
355+
356+ final plugins = options["plugins" ];
357+ if (plugins is ! YamlMap ) {
358+ return _defaultHapticWrappers;
359+ }
360+
361+ for (final pluginName in ["solvro_config" , "solvro_custom_linter" ]) {
362+ final pluginConfig = plugins[pluginName];
363+ if (pluginConfig is ! YamlMap ) {
364+ continue ;
365+ }
366+
367+ final wrappers = < String > [];
368+ _addConfiguredWrappers (wrappers, pluginConfig["haptic_wrapper" ]);
369+ _addConfiguredWrappers (wrappers, pluginConfig["haptic_wrappers" ]);
370+
371+ if (wrappers.isNotEmpty) {
372+ return wrappers;
373+ }
374+ }
375+ } on YamlException {
376+ return _defaultHapticWrappers;
377+ } on Exception {
378+ return _defaultHapticWrappers;
379+ }
380+
381+ return _defaultHapticWrappers;
382+ }
383+
384+ static void _addConfiguredWrappers (List <String > wrappers, Object ? value) {
385+ if (value is String && value.isNotEmpty) {
386+ wrappers.add (value);
387+ return ;
388+ }
389+
390+ if (value is YamlList ) {
391+ for (final item in value) {
392+ if (item is String && item.isNotEmpty) {
393+ wrappers.add (item);
394+ }
395+ }
396+ }
397+ }
331398}
332399
333400class AssetImageRule extends _SolvroRule {
@@ -670,9 +737,7 @@ class PreferToIncludeSliverInNameRule extends _SolvroRule {
670737 _ClassVisitor (this , (node) {
671738 final buildMethod = node.body.members
672739 .whereType <MethodDeclaration >()
673- .where (
674- (method) => method.name.lexeme == "build" ,
675- );
740+ .where ((method) => method.name.lexeme == "build" );
676741 if (buildMethod.isEmpty) {
677742 return ;
678743 }
0 commit comments