Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

The plugin to help create custom analyzer plugin quickly and provide some useful lints and get suggestion and auto import for extension member.

License

NotificationsYou must be signed in to change notification settings

fluttercandies/candies_analyzer_plugin

Repository files navigation

pub packageGitHub starsGitHub forksGitHub licenseGitHub issuesFlutterCandies QQ 群

Languages: English |中文简体

Description

The plugin to help create custom lint quickly.

simple use

add into pubspec.yaml

dev_dependencies:# zmtzawqlpcandies_analyzer_plugin:any

add into analysis_options.yaml

analyzer:# zmtzawqlpplugins:candies_analyzer_plugin

default lints are following:

  • prefer_asset_const
  • prefer_named_routes
  • prefer_safe_setState
  • must_call_super_dispose
  • must_call_super_dispose
  • perfer_doc_comments
  • prefer_singleton
  • good_doc_comments
  • prefer_trailing_comma

more info please check [Default lints](#Default lints)

Custom your analyzer plugin

Create Template

  1. activate plugin

    rundart pub global activate candies_analyzer_plugin

  2. cd to your project

    Let us suppose:

    your project isexample

    your lint plugin iscustom_lint

    runcandies_analyzer_plugin --exmaple custom_lint, a simple lint plugin is generated.

  3. addcustom_lint intodev_dependencies of the rootpubspec.yaml

dev_dependencies:# zmtzawqlpcustom_lint:path:custom_lint/
  1. addcustom_lint intoanalyzer plugins of the rootanalysis_options.yaml
analyzer:# zmtzawqlpplugins:custom_lint

after analysis are finished, you will see some custom lint in your ide.

Add your lint

findplugin.dart base on following project tree

├─ example│  ├─ custom_lint│  │  └─ tools│  │     └─ analyzer_plugin│  │        ├─ bin│  │        │  └─ plugin.dart

plugin.dart is the entrance of plugin.

start plugin

we start plugin in this file.

CandiesAnalyzerPluginget plugin=>CustomLintPlugin();// This file must be 'plugin.dart'voidmain(List<String> args,SendPort sendPort) {CandiesAnalyzerPluginStarter.start(    args,    sendPort,    plugin: plugin,  );}classCustomLintPluginextendsCandiesAnalyzerPlugin {@overrideStringget name=>'custom_lint';@overrideList<String>get fileGlobsToAnalyze=>const<String>['**/*.dart','**/*.yaml','**/*.json',      ];@overrideList<DartLint>get dartLints=><DartLint>[// add your dart lint herePerferCandiesClassPrefix(),        ...super.dartLints,      ];@overrideList<YamlLint>get yamlLints=><YamlLint>[RemoveDependency(package:'path')];@overrideList<GenericLint>get genericLints=><GenericLint>[RemoveDuplicateValue()];}

create a lint

you just need to make a custom lint which extends fromDartLint ,YamlLint,GenericLint.

Properties:

PropertyDescriptionDefault
codeThe name, as a string, of the error code associated with this error.required
messageThe message to be displayed for this error. The message should indicate what is wrong with the code and why it is wrong.required
urlThe URL of a page containing documentation associated with this error.
typeThe type of the error.
CHECKED_MODE_COMPILE_TIME_ERROR
COMPILE_TIME_ERROR
HINT
LINT
STATIC_TYPE_WARNING
STATIC_WARNING
SYNTACTIC_ERROR
TODO
The default is LINT.
severityThe severity of the error.
INFO
WARNING
ERROR
The default is INFO.
correctionThe correction message to be displayed for this error. The correction message should indicate how the user can fix the error. The field is omitted if there is no correction message associated with the error code.
contextMessagesAdditional messages associated with this diagnostic that provide context to help the user understand the diagnostic.

Important methodes:

MethodDescriptionOverride
matchLintreturn whether is match lint.must
getDartFixes/getYamlFixes/getGenericFixesreturn fixes if has.getYamlFixes/getGenericFixes doesn't work for now, leave it in case dart team maybe support it someday in the future, seeissue

dart lint

you can ignore lint or ignore file by override [ignoreLint] and [ignoreFile].Here is a demo for a dart lint:

classPerferCandiesClassPrefixextendsDartLint {@overrideStringget code=>'perfer_candies_class_prefix';@overrideString?get url=>'https://github.com/fluttercandies/candies_analyzer_plugin';@overrideSyntacticEntity?matchLint(AstNode node) {if (nodeisClassDeclaration) {finalString name= node.name2.toString();finalint startIndex=_getClassNameStartIndex(name);if (!name.substring(startIndex).startsWith('Candies')) {return node.name2;      }    }returnnull;  }@overrideStringget message=>'Define a class name start with Candies';@overrideFuture<List<SourceChange>>getDartFixes(DartAnalysisError error,CandiesAnalyzerPluginConfig config,  )async {finalResolvedUnitResult resolvedUnitResult= error.result;finalAstNode astNode= error.astNode;// get name nodefinalToken nameNode= (astNodeasClassDeclaration).name2;finalString nameString= nameNode.toString();return<SourceChange>[awaitgetDartFix(        resolvedUnitResult: resolvedUnitResult,        message:'Use Candies as a class prefix.',        buildDartFileEdit: (DartFileEditBuilder dartFileEditBuilder) {finalint startIndex=_getClassNameStartIndex(nameString);finalRegExp regExp=RegExp(nameString);finalString replace='${nameString.substring(0,startIndex)}Candies${nameString.substring(startIndex)}';for (finalMatch matchin regExp.allMatches(resolvedUnitResult.content)) {            dartFileEditBuilder.addSimpleReplacement(SourceRange(match.start, match.end- match.start), replace);          }          dartFileEditBuilder.formatAll(resolvedUnitResult.unit);        },      )    ];  }int_getClassNameStartIndex(String nameString) {int index=0;while (nameString[index]=='_') {      index++;if (index== nameString.length-1) {break;      }    }return index;  }}

yaml lint

Here is a demo for a yaml lint:

classRemoveDependencyextendsYamlLint {RemoveDependency({requiredthis.package});finalString package;@overrideStringget code=>'remove_${package}_dependency';@overrideStringget message=>'don\'t use $package!';@overrideString?get correction=>'Remove $package dependency';@overrideAnalysisErrorSeverityget severity=>AnalysisErrorSeverity.WARNING;@overrideIterable<SourceRange>matchLint(YamlNode root,String content,LineInfo lineInfo,  )sync* {if (rootisYamlMap&& root.containsKey(PubspecField.DEPENDENCIES_FIELD)) {finalYamlNode dependencies=          root.nodes[PubspecField.DEPENDENCIES_FIELD]!;if (dependenciesisYamlMap&& dependencies.containsKey(package)) {finalYamlNodeget= dependencies.nodes[package]!;int start= dependencies.span.start.offset;finalint end=get.span.start.offset;finalint index= content.substring(start, end).indexOf('$package: ');        start+= index;yieldSourceRange(start,get.span.end.offset- start);      }    }  }}

generic lint

Here is a demo for a generic lint:

classRemoveDuplicateValueextendsGenericLint {@overrideStringget code=>'remove_duplicate_value';@overrideIterable<SourceRange>matchLint(String content,String file,LineInfo lineInfo,  )sync* {if (isFileType(file: file, type:'.json')) {finalMap<dynamic,dynamic> map=jsonDecode(content)asMap<dynamic,dynamic>;finalMap<dynamic,dynamic> duplicate=<dynamic,dynamic>{};finalMap<dynamic,dynamic> checkDuplicate=<dynamic,dynamic>{};for (finaldynamic keyin map.keys) {finaldynamic value= map[key];if (checkDuplicate.containsKey(value)) {          duplicate[key]= value;          duplicate[checkDuplicate[value]]= value;        }        checkDuplicate[value]= key;      }if (duplicate.isNotEmpty) {for (finaldynamic keyin duplicate.keys) {finalint start= content.indexOf('"$key"');finaldynamic value= duplicate[key];finalint end= content.indexOf('"$value"',                start,              )+              value.toString().length+1;finalint lineNumber= lineInfo.getLocation(end).lineNumber;bool hasComma=false;int commaIndex= end;int commaLineNumber= lineInfo.getLocation(commaIndex).lineNumber;while (!hasComma&& commaLineNumber== lineNumber) {            commaIndex++;finalString char= content[commaIndex];            hasComma= char==',';            commaLineNumber= lineInfo.getLocation(commaIndex).lineNumber;          }yieldSourceRange(start, (hasComma? commaIndex: end)+1- start);        }      }    }  }@overrideStringget message=>'remove duplicate value';}

Debug

debug lint

finddebug.dart base on following project tree

├─ example│  ├─ custom_lint│  │  └─ tools│  │     └─ analyzer_plugin│  │        ├─ bin│  │        │  └─ debug.dart

change root to which you want to debug, default is example folder.

import'dart:io';import'package:analyzer/dart/analysis/analysis_context.dart';import'package:analyzer/dart/analysis/analysis_context_collection.dart';import'package:analyzer_plugin/protocol/protocol_common.dart';import'package:analyzer_plugin/protocol/protocol_generated.dart';import'package:candies_analyzer_plugin/candies_analyzer_plugin.dart';import'plugin.dart';Future<void>main(List<String> args)async {finalString root=Directory.current.parent.parent.parent.path;finalAnalysisContextCollection collection=AnalysisContextCollection(includedPaths:<String>[root]);finalCandiesAnalyzerPlugin myPlugin= plugin;for (finalAnalysisContext contextin collection.contexts) {for (finalString filein context.contextRoot.analyzedFiles()) {if (!myPlugin.shouldAnalyzeFile(file, context)) {continue;      }finalbool isAnalyzed= context.contextRoot.isAnalyzed(file);if (!isAnalyzed) {continue;      }finalList<AnalysisError> errors=          (await myPlugin.getAnalysisErrorsForDebug(        file,        context,      ))              .toList();for (finalAnalysisError errorin errors) {finalList<AnalysisErrorFixes> fixes=await myPlugin            .getAnalysisErrorFixesForDebug(EditGetFixesParams(file, error.location.offset), context)            .toList();print(fixes.length);      }print(errors.length);    }  }}

update code

├─ example│  ├─ custom_lint│  │  └─ tools│  │     └─ analyzer_plugin

you have two options to update new code into dartServer.

  1. delete .plugin_manager folder

Note,analyzer_plugin folder will be copyed into.plugin_manager and create a folder base on encrypt plugin path.

macos:/Users/user_name/.dartServer/.plugin_manager/

windows:C:\Users\user_name\AppData\Local\.dartServer\.plugin_manager\

if your code is changed, please remove the files under.plugin_manager.

or you can runcandies_analyzer_plugin --clear-cache to remove the files under.plugin_manager.

  1. write new code under custom_lint folder

you can write new code under custom_lint, for exmaple, in custom_lint.dart.

├─ example│  ├─ custom_lint│  │  ├─ lib│  │  │  └─ custom_lint.dart

so you should addcustom_lint dependencies intoanalyzer_plugin\pubspec.yaml

you should useabsolute path due to analyzer_plugin folder will copy to.plugin_manager.

if your don't publishcustom_lint as new package, i don't suggest do as this.

├─ example│  ├─ custom_lint│  │  ├─ lib│  │  │  └─ custom_lint.dart│  │  └─ tools│  │     └─ analyzer_plugin│  │        ├─ analysis_options.yaml
dependencies:custom_lint:# absolute pathpath:xxx/xxx/custom_lintcandies_analyzer_plugin:anypath:anyanalyzer:anyanalyzer_plugin:any

restart server

after update code, you should restart analysis server by following steps in vscode.

  1. findCommand Palette inView

  1. enterRestart Analysis Server

now, you can see the new change.

Log

  1. for performance, default is false, if you want to check log, set it to true. you can open Log.

    CandiesAnalyzerPluginLogger().shouldLog = true;

Under the projectcustom_lint.log will be generated.

  1. you can custom log name

    CandiesAnalyzerPluginLogger().logFileName = 'your name';

  2. log info

CandiesAnalyzerPluginLogger().log('info',// which location custom_lint.log will be generated        root: result.root,      );
  1. log error
CandiesAnalyzerPluginLogger().logError('analyze file failed:',     root: analysisContext.root,     error: e,     stackTrace: stackTrace,   );

Config

disable a lint

As default, all of the custom lints are enable. And you can also write a config in analysis_options.yaml to disable they.

  1. add ignore for a lint.
analyzer:errors:perfer_candies_class_prefix:ignore
  1. exclude files
analyzer:exclude:    -lib/exclude/*.dart
  1. disable a lint
linter:rules:# disable a lintperfer_candies_class_prefix:false

include

we can defineinclude tag undercustom_lint (it's your plugin name).it means that we only analyze the include files.

# your plugin namecustom_lint:# if we define this, we only analyze include filesinclude:     -lib/include/*.dart

custom lint severity

you can change lint severity by following setting.

change the severity ofperfer_candies_class_prefix frominfo towarning.

supportwarning ,info ,error.

analyzer:errors:# override error severityperfer_candies_class_prefix:warning

Default lints

PerferClassPrefix

Define a class name start with prefix

classPerferClassPrefixextendsDartLint {PerferClassPrefix(this.prefix);finalString prefix;@overrideStringget code=>'perfer_${prefix}_class_prefix';}

PreferAssetConst

Prefer to use asset const instead of a string.

classPreferAssetConstextendsDartLint {@overrideStringget code=>'prefer_asset_const';@overrideString?get url=>'https://pub.dev/packages/assets_generator';  }

PreferNamedRoutes

Prefer to use named routes.

classPreferNamedRoutesextendsDartLint {@overrideStringget code=>'prefer_named_routes';@overrideString?get url=>'https://pub.dev/packages/ff_annotation_route';  }

PerferSafeSetState

Prefer to check mounted before setState

classPerferSafeSetStateextendsDartLint {@overrideStringget code=>'prefer_safe_setState';}

MustCallSuperDispose

Implementations of this method should end with a call to the inherited method, as insuper.dispose().

classMustCallSuperDisposeextendsDartLintwithCallSuperDisposeMixin {@overrideStringget code=>'must_call_super_dispose';}

EndCallSuperDispose

Should callsuper.dispose() at the end of this method.

classEndCallSuperDisposeextendsDartLintwithCallSuperDisposeMixin {@overrideStringget code=>'end_call_super_dispose';}

PerferDocComments

https://dart.dev/guides/language/effective-dart/documentationThe same likepublic_member_api_docs, but we can ignore lint or ignore file by override [ignoreLint] and [ignoreFile] and you can override [isPrivate] and [inPrivateMember] to check private member.

classPerferDocCommentsextendsDartLint {@overrideStringget code=>'perfer_doc_comments';}

PreferSingleton

This is not a singleton, and new Object every time.

classPreferSingletonextendsDartLint {@overrideStringget code=>'prefer_singleton';}

GoodDocComments

wrong comments format. (/// xxx) for public api and (// xxx) for other cases.

classGoodDocCommentsextendsDartLint {@overrideStringget code=>'good_doc_comments';}

PreferTrailingComma

Prefer trailing comma for better code style.

classPreferTrailingCommaextendsDartLint {@overrideStringget code=>'prefer_trailing_comma';}

Completion

Make a custom completion

you can define yourCompletionContributor incompletionContributors,ExtensionMemberContributor is default.

/// The completionContributors to finish CompletionRequestList<CompletionContributor>get completionContributors=><CompletionContributor>[ExtensionMemberContributor(),      ];

Suggestions of Extension Member

Although dart team had close the issueAuto import (or quickfix?) for Extensions · Issue #38894 · dart-lang/sdk (github.com) , but still has many problems when developing in different ide.

ExtensionMemberContributor help to handle extension member easily.

Pre-Commit

pre_commit.dart

findpre_commit.dart base on following project tree, it's a demo to check errors before submit code.

├─ example│  ├─ custom_lint│  │  └─ tools│  │     └─ analyzer_plugin│  │        ├─ bin│  │        │  └─ pre_commit.dart

pre-commit script

runcandies_analyzer_plugin --pre-commit, pre-commit script is generated under .git/hooks.

and you can modify this script template by add a pre-commit file under your project.

├─ example│  ├─ pre-commit

{0} and {1} are placeholder, do not modify them.

#!/bin/sh# project pathbase_dir="{0}"dart format "$base_dir"# pre_commit.dart pathpre_commit="{1}" echo "Checking the code before submit..."echo "Analyzing $base_dir..."info=$(dart "$pre_commit" "$base_dir")echo "$info"if [[ -n $info && $info != *"No issues found"* ]];thenexit 1fi

when you are runninggit commit command to submit code, it will run under .git/hooks/pre-commit script first.and the script will callexample/custom_lint/tools/analyzer_plugin/bin/pre_commit.dart, if there are errors, you should exit 1.

you can modifyexample/pre-commit andexample/custom_lint/tools/analyzer_plugin/bin/pre_commit.dart to custom your rule.

cacheErrorsIntoFile

setCandiesAnalyzerPlugin.cacheErrorsIntoFile to true, to reduce the spent time to check error before submit code.

Note

print lag

don't writeprint in the process of analyzing in your plugin, analysis will lag.

pubspec.yaml and analysis_options.yaml

you must do following things to support your project to be analyzed.

  1. addcustom_lint intodev_dependencies inpubspec.yaml , seepubspec.yaml

  2. addcustom_lint intoanalyzerplugins inanalysis_options.yaml seeanalysis_options.yaml

quick fixes are only supported for dart files in vscode.(android studio support any type of file)

issue

completion auto import is not working in vscode

issue

About

The plugin to help create custom analyzer plugin quickly and provide some useful lints and get suggestion and auto import for extension member.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

    Packages

    No packages published

    Contributors2

    •  
    •  

    [8]ページ先頭

    ©2009-2025 Movatter.jp