{"id":19273073,"url":"https://github.com/fluttercandies/candies_analyzer_plugin","last_synced_at":"2025-07-06T14:03:49.657Z","repository":{"id":62030927,"uuid":"555847554","full_name":"fluttercandies/candies_analyzer_plugin","owner":"fluttercandies","description":"The plugin to help create custom analyzer plugin quickly and provide some useful lints and get suggestion and auto import for extension member.","archived":false,"fork":false,"pushed_at":"2025-04-20T12:27:26.000Z","size":251,"stargazers_count":15,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-06T14:03:35.405Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fluttercandies.png","metadata":{"files":{"readme":"README-ZH.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"custom":"http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"}},"created_at":"2022-10-22T13:25:46.000Z","updated_at":"2025-04-20T12:27:29.000Z","dependencies_parsed_at":"2024-06-24T10:42:25.319Z","dependency_job_id":"f92400fc-e2a2-4957-bf0e-1c6521a19bc7","html_url":"https://github.com/fluttercandies/candies_analyzer_plugin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fluttercandies/candies_analyzer_plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fcandies_analyzer_plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fcandies_analyzer_plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fcandies_analyzer_plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fcandies_analyzer_plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluttercandies","download_url":"https://codeload.github.com/fluttercandies/candies_analyzer_plugin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fcandies_analyzer_plugin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263914030,"owners_count":23529074,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T20:40:43.403Z","updated_at":"2025-07-06T14:03:49.651Z","avatar_url":"https://github.com/fluttercandies.png","language":"Dart","funding_links":["http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"],"categories":[],"sub_categories":[],"readme":"# candies_analyzer_plugin\n\n[![pub package](https://img.shields.io/pub/v/candies_analyzer_plugin.svg)](https://pub.dartlang.org/packages/candies_analyzer_plugin) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/candies_analyzer_plugin)](https://github.com/fluttercandies/candies_analyzer_plugin/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/candies_analyzer_plugin)](https://github.com/fluttercandies/candies_analyzer_plugin/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/candies_analyzer_plugin)](https://github.com/fluttercandies/candies_analyzer_plugin/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/candies_analyzer_plugin)](https://github.com/fluttercandies/candies_analyzer_plugin/issues) \u003ca href=\"https://qm.qq.com/q/ZyJbSVjfSU\"\u003e![FlutterCandies QQ 群](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml\u0026query=%24.qq_group_number\u0026label=QQ%E7%BE%A4\u0026logo=qq\u0026color=1DACE8)\n\n语言: [English](README.md) | 中文简体\n\n## 描述\n\n帮助快速创建自定义 lint 的插件.\n\n- [candies\\_analyzer\\_plugin](#candies_analyzer_plugin)\n  - [描述](#描述)\n- [简单使用](#简单使用)\n  - [添加引用到 pubspec.yaml](#添加引用到-pubspecyaml)\n  - [增加插件到 analysis\\_options.yaml](#增加插件到-analysis_optionsyaml)\n- [自定义你自己的分析插件](#自定义你自己的分析插件)\n  - [模版创建](#模版创建)\n  - [增添你的 lint](#增添你的-lint)\n    - [启动插件](#启动插件)\n    - [创建一个 lint](#创建一个-lint)\n      - [dart lint](#dart-lint)\n      - [yaml lint](#yaml-lint)\n      - [generic lint](#generic-lint)\n  - [调试](#调试)\n    - [调试错误](#调试错误)\n    - [更新代码](#更新代码)\n    - [重启 dart analysis 服务](#重启-dart-analysis-服务)\n  - [Log](#log)\n  - [配置](#配置)\n    - [禁止一个 lint](#禁止一个-lint)\n    - [包含文件](#包含文件)\n    - [自定义 lint 严肃性](#自定义-lint-严肃性)\n  - [Default lints](#default-lints)\n    - [PerferClassPrefix](#perferclassprefix)\n    - [PreferAssetConst](#preferassetconst)\n    - [PreferNamedRoutes](#prefernamedroutes)\n    - [PerferSafeSetState](#perfersafesetstate)\n    - [MustCallSuperDispose](#mustcallsuperdispose)\n    - [EndCallSuperDispose](#endcallsuperdispose)\n    - [PerferDocComments](#perferdoccomments)\n    - [PreferSingleton](#prefersingleton)\n    - [GoodDocComments](#gooddoccomments)\n    - [PreferTrailingComma](#prefertrailingcomma)\n  - [Completion](#completion)\n    - [Make a custom completion](#make-a-custom-completion)\n    - [扩展方法提示](#扩展方法提示)\n  - [Pre-Commit](#pre-commit)\n    - [pre\\_commit.dart](#pre_commitdart)\n    - [pre-commit script](#pre-commit-script)\n    - [cacheErrorsIntoFile](#cacheerrorsintofile)\n  - [注意事项](#注意事项)\n    - [print lag](#print-lag)\n    - [pubspec.yaml and analysis\\_options.yaml](#pubspecyaml-and-analysis_optionsyaml)\n    - [在 vscode 中快速修复只支持 dart 文件.(android studio支持任何文件)](#在-vscode-中快速修复只支持-dart-文件android-studio支持任何文件)\n    - [提示自动导入在 vscode 无法完成](#提示自动导入在-vscode-无法完成)\n\n* [example](https://github.com/fluttercandies/candies_analyzer_plugin/example)\n\n* [analyzer_plugin 文档](https://github.com/dart-lang/sdk/blob/master/pkg/analyzer_plugin/doc/tutorial/tutorial.md)\n\n# 简单使用\n\n## 添加引用到 pubspec.yaml\n\n``` yaml\ndev_dependencies:\n  # zmtzawqlp  \n  candies_analyzer_plugin: any\n``` \n\n## 增加插件到 analysis_options.yaml\n\n``` yaml\nanalyzer:\n  # zmtzawqlp  \n  plugins:\n    candies_analyzer_plugin\n``` \n\n默认的 lints 有如下这些:\n\n* prefer_asset_const\n* prefer_named_routes\n* prefer_safe_setState\n* must_call_super_dispose\n* must_call_super_dispose\n* perfer_doc_comments\n* prefer_singleton\n* good_doc_comments\n* prefer_trailing_comma\n\n更多的信息可以查看 [Default lints](#Default lints)\n\n\n# 自定义你自己的分析插件\n  \n## 模版创建\n\n1. 激活插件\n\n   执行命令 `dart pub global activate candies_analyzer_plugin`\n\n\n2. 到你的项目的根目录\n\n   假设:\n   \n   你的项目叫做 `example`\n   \n   你想创建的插件叫做 `custom_lint`\n   \n   执行命令 `candies_analyzer_plugin --example custom_lint`, 一个简单插件模板创建成功.\n\n3. 将 `custom_lint` 增加到 根目录 `pubspec.yaml` 的 `dev_dependencies` 中\n\n```yaml\ndev_dependencies:\n  # zmtzawqlp  \n  custom_lint:\n    path: custom_lint/\n```\n\n4. 将 `custom_lint` 增加到根目录 `analysis_options.yaml` 的 `analyzer plugins` tag 下面\n\n```yaml\nanalyzer:\n  # zmtzawqlp  \n  plugins:\n    custom_lint\n```\n\n当分析结束的时候，在你的 ide 中可以看到一些自定义的 lint 。\n\n## 增添你的 lint\n\n在下面的项目结构下面找到  `plugin.dart`\n\n```\n├─ example\n│  ├─ custom_lint\n│  │  └─ tools\n│  │     └─ analyzer_plugin\n│  │        ├─ bin\n│  │        │  └─ plugin.dart\n```\n\n`plugin.dart` 是整个插件的入口。\n\n### 启动插件\n\n我们将在 main 方法中启动我们的插件.\n\n``` dart\nCandiesAnalyzerPlugin get plugin =\u003e CustomLintPlugin();\n\n// This file must be 'plugin.dart'\nvoid main(List\u003cString\u003e args, SendPort sendPort) {\n  CandiesAnalyzerPluginStarter.start(\n    args,\n    sendPort,\n    plugin: plugin,\n  );\n}\n\nclass CustomLintPlugin extends CandiesAnalyzerPlugin {\n  @override\n  String get name =\u003e 'custom_lint';\n\n  @override\n  List\u003cString\u003e get fileGlobsToAnalyze =\u003e const \u003cString\u003e[\n        '**/*.dart',\n        '**/*.yaml',\n        '**/*.json',\n      ];\n\n  @override\n  List\u003cDartLint\u003e get dartLints =\u003e \u003cDartLint\u003e[\n        // add your dart lint here\n        PerferCandiesClassPrefix(),\n        ...super.dartLints,\n      ];\n\n  @override\n  List\u003cYamlLint\u003e get yamlLints =\u003e \u003cYamlLint\u003e[RemoveDependency(package: 'path')];\n\n  @override\n  List\u003cGenericLint\u003e get genericLints =\u003e \u003cGenericLint\u003e[RemoveDuplicateValue()];\n}\n```\n\n### 创建一个 lint\n\n你只需要创一个新的类来继承 `DartLint` ,`YamlLint`, `GenericLint` 即可。\n\n\n属性: \n\n| 属性            | 描述                                                                                                                                                              | 默认         |\n| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |\n| code            | 这个错误的名字，唯一.                                                                                                                                             | 必填         |\n| message         | 描述这个错误的信息                                                                                                                                                | 必填         |\n| url             | 这个错误文档的链接.                                                                                                                                               |              |\n| type            | 错误的类型. \u003cbr/\u003eCHECKED_MODE_COMPILE_TIME_ERROR\u003cbr/\u003eCOMPILE_TIME_ERROR\u003cbr/\u003eHINT\u003cbr/\u003eLINT\u003cbr/\u003eSTATIC_TYPE_WARNING\u003cbr/\u003eSTATIC_WARNING\u003cbr/\u003eSYNTACTIC_ERROR\u003cbr/\u003eTODO | 默认为 LINT. |\n| severity        | 这个错误的严肃性(一般我们修改的是这个).\u003cbr/\u003eINFO\u003cbr/\u003eWARNING\u003cbr/\u003eERROR                                                                                            | 默认为 INFO. |\n| correction      | 修复这个错误的一些描述.                                                                                                                                           |              |\n| contextMessages | 额外的信息帮助修复这个错误。                                                                                                                                      |              |\n\n\n重要的方法:\n\n| 方法                                      | 描述                   | 重载                                                                                                                                  |\n| ----------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |\n| matchLint                                 | 判断是否是你定义的lint | 必须                                                                                                                                  |\n| getDartFixes/getYamlFixes/getGenericFixes | 返回快速修复           | getYamlFixes/getGenericFixes 没有效果，保留它以备 dart team 未来某天支持, 查看 [issue](https://github.com/dart-lang/sdk/issues/50306) |\n\n#### dart lint\n\n你可以通过重载 [ignoreLint] and [ignoreFile] 方法来忽略某个规则或者某个文件。\n\n下面是一个 dart lint 的例子:\n\n``` dart\nclass PerferCandiesClassPrefix extends DartLint {\n  @override\n  String get code =\u003e 'perfer_candies_class_prefix';\n\n  @override\n  String? get url =\u003e\n      'https://github.com/fluttercandies/candies_analyzer_plugin';\n\n  @override\n  SyntacticEntity? matchLint(AstNode node) {\n    if (node is ClassDeclaration) {\n      final String name = node.name2.toString();\n      final int startIndex = _getClassNameStartIndex(name);\n      if (!name.substring(startIndex).startsWith('Candies')) {\n        return node.name2;\n      }\n    }\n    return null;\n  }\n\n  @override\n  String get message =\u003e 'Define a class name start with Candies';\n\n  @override\n  Future\u003cList\u003cSourceChange\u003e\u003e getDartFixes(\n    DartAnalysisError error,\n    CandiesAnalyzerPluginConfig config,\n  ) async {\n    final ResolvedUnitResult resolvedUnitResult = error.result;\n\n    final Iterable\u003cDartAnalysisError\u003e cacheErrors = config\n        .getCacheErrors(resolvedUnitResult.path, code: code)\n        .whereType\u003cDartAnalysisError\u003e();\n\n    final Map\u003cDartAnalysisError, Set\u003cSyntacticEntity\u003e\u003e references =\n        _findClassReferences(cacheErrors, resolvedUnitResult);\n\n    return \u003cSourceChange\u003e[\n      await getDartFix(\n        resolvedUnitResult: resolvedUnitResult,\n        message: 'Use Candies as a class prefix.',\n        buildDartFileEdit: (DartFileEditBuilder dartFileEditBuilder) {\n          _fix(\n            error,\n            resolvedUnitResult,\n            dartFileEditBuilder,\n            references[error]!,\n          );\n          dartFileEditBuilder.formatAll(resolvedUnitResult.unit);\n        },\n      ),\n      if (cacheErrors.length \u003e 1)\n        await getDartFix(\n          resolvedUnitResult: resolvedUnitResult,\n          message: 'Use Candies as a class prefix where possible in file.',\n          buildDartFileEdit: (DartFileEditBuilder dartFileEditBuilder) {\n            for (final DartAnalysisError error in cacheErrors) {\n              _fix(\n                error,\n                resolvedUnitResult,\n                dartFileEditBuilder,\n                references[error]!,\n              );\n            }\n            dartFileEditBuilder.formatAll(resolvedUnitResult.unit);\n          },\n        ),\n    ];\n  }\n\n  void _fix(\n    DartAnalysisError error,\n    ResolvedUnitResult resolvedUnitResult,\n    DartFileEditBuilder dartFileEditBuilder,\n    Set\u003cSyntacticEntity\u003e references,\n  ) {\n    final AstNode astNode = error.astNode;\n    // get name node\n    final Token nameNode = (astNode as ClassDeclaration).name2;\n    final String nameString = nameNode.lexeme;\n\n    final int startIndex = _getClassNameStartIndex(nameString);\n    final String replace =\n        '${nameString.substring(0, startIndex)}Candies${nameString.substring(startIndex)}';\n\n    for (final SyntacticEntity match in references) {\n      dartFileEditBuilder.addSimpleReplacement(\n          SourceRange(match.offset, match.length), replace);\n    }\n  }\n\n  Map\u003cDartAnalysisError, Set\u003cSyntacticEntity\u003e\u003e _findClassReferences(\n    Iterable\u003cDartAnalysisError\u003e errors,\n    ResolvedUnitResult resolvedUnitResult,\n  ) {\n    final Map\u003cDartAnalysisError, Set\u003cSyntacticEntity\u003e\u003e references =\n        \u003cDartAnalysisError, Set\u003cSyntacticEntity\u003e\u003e{};\n    final Map\u003cString, DartAnalysisError\u003e classNames =\n        \u003cString, DartAnalysisError\u003e{};\n\n    for (final DartAnalysisError error in errors) {\n      classNames[(error.astNode as ClassDeclaration).name2.lexeme] = error;\n      references[error] = \u003cSyntacticEntity\u003e{};\n    }\n\n    resolvedUnitResult.unit\n        .accept(_FindClassReferenceVisitor(references, classNames));\n\n    return references;\n  }\n\n  int _getClassNameStartIndex(String nameString) {\n    int index = 0;\n    while (nameString[index] == '_') {\n      index++;\n      if (index == nameString.length - 1) {\n        break;\n      }\n    }\n    return index;\n  }\n}\n\nclass _FindClassReferenceVisitor extends GeneralizingAstVisitor\u003cvoid\u003e {\n  _FindClassReferenceVisitor(this.references, this.classNames);\n  final Map\u003cDartAnalysisError, Set\u003cSyntacticEntity\u003e\u003e references;\n  final Map\u003cString, DartAnalysisError\u003e classNames;\n\n  @override\n  void visitNode(AstNode node) {\n    if (node.childEntities.length == 1) {\n      final String source = node.toSource();\n      if (classNames.keys.contains(source)) {\n        references[classNames[source]]!.add(node);\n        return;\n      }\n    }\n    super.visitNode(node);\n  }\n}\n```\n#### yaml lint\n\n下面是一个 yaml lint 的例子:\n\n``` dart\nclass RemoveDependency extends YamlLint {\n  RemoveDependency({required this.package});\n  final String package;\n  @override\n  String get code =\u003e 'remove_${package}_dependency';\n\n  @override\n  String get message =\u003e 'don\\'t use $package!';\n\n  @override\n  String? get correction =\u003e 'Remove $package dependency';\n\n  @override\n  AnalysisErrorSeverity get severity =\u003e AnalysisErrorSeverity.WARNING;\n\n  @override\n  Iterable\u003cSourceRange\u003e matchLint(\n    YamlNode root,\n    String content,\n    LineInfo lineInfo,\n  ) sync* {\n    if (root is YamlMap \u0026\u0026 root.containsKey(PubspecField.DEPENDENCIES_FIELD)) {\n      final YamlNode dependencies =\n          root.nodes[PubspecField.DEPENDENCIES_FIELD]!;\n      if (dependencies is YamlMap \u0026\u0026 dependencies.containsKey(package)) {\n        final YamlNode get = dependencies.nodes[package]!;\n        int start = dependencies.span.start.offset;\n        final int end = get.span.start.offset;\n        final int index = content.substring(start, end).indexOf('$package: ');\n        start += index;\n        yield SourceRange(start, get.span.end.offset - start);\n      }\n    }\n  }\n}\n```\n\n#### generic lint\n\n下面是一个 generic lint 的例子:\n\n``` dart\nclass RemoveDuplicateValue extends GenericLint {\n  @override\n  String get code =\u003e 'remove_duplicate_value';\n\n  @override\n  Iterable\u003cSourceRange\u003e matchLint(\n    String content,\n    String file,\n    LineInfo lineInfo,\n  ) sync* {\n    if (isFileType(file: file, type: '.json')) {\n      final Map\u003cdynamic, dynamic\u003e map =\n          jsonDecode(content) as Map\u003cdynamic, dynamic\u003e;\n\n      final Map\u003cdynamic, dynamic\u003e duplicate = \u003cdynamic, dynamic\u003e{};\n      final Map\u003cdynamic, dynamic\u003e checkDuplicate = \u003cdynamic, dynamic\u003e{};\n      for (final dynamic key in map.keys) {\n        final dynamic value = map[key];\n        if (checkDuplicate.containsKey(value)) {\n          duplicate[key] = value;\n          duplicate[checkDuplicate[value]] = value;\n        }\n        checkDuplicate[value] = key;\n      }\n\n      if (duplicate.isNotEmpty) {\n        for (final dynamic key in duplicate.keys) {\n          final int start = content.indexOf('\"$key\"');\n          final dynamic value = duplicate[key];\n          final int end = content.indexOf(\n                '\"$value\"',\n                start,\n              ) +\n              value.toString().length +\n              1;\n\n          final int lineNumber = lineInfo.getLocation(end).lineNumber;\n\n          bool hasComma = false;\n          int commaIndex = end;\n          int commaLineNumber = lineInfo.getLocation(commaIndex).lineNumber;\n\n          while (!hasComma \u0026\u0026 commaLineNumber == lineNumber) {\n            commaIndex++;\n            final String char = content[commaIndex];\n            hasComma = char == ',';\n            commaLineNumber = lineInfo.getLocation(commaIndex).lineNumber;\n          }\n\n          yield SourceRange(start, (hasComma ? commaIndex : end) + 1 - start);\n        }\n      }\n    }\n  }\n\n  @override\n  String get message =\u003e 'remove duplicate value';\n}\n```\n\n## 调试\n\n### 调试错误\n\n在下面的项目结构下面找到  `debug.dart`，已经自动为你创建了 debug 的例子。你可以通过调试来编写符合你条件的 lint\n\n```\n├─ example\n│  ├─ custom_lint\n│  │  └─ tools\n│  │     └─ analyzer_plugin\n│  │        ├─ bin\n│  │        │  └─ debug.dart\n```\n\n把 root 修改为你想要调试的项目路径, 默认为 example 的根目录\n\n``` dart\nimport 'dart:io';\nimport 'package:analyzer/dart/analysis/analysis_context.dart';\nimport 'package:analyzer/dart/analysis/analysis_context_collection.dart';\nimport 'package:analyzer_plugin/protocol/protocol_common.dart';\nimport 'package:analyzer_plugin/protocol/protocol_generated.dart';\nimport 'package:candies_analyzer_plugin/candies_analyzer_plugin.dart';\nimport 'plugin.dart';\n\nFuture\u003cvoid\u003e main(List\u003cString\u003e args) async {\n  final String root = Directory.current.parent.parent.parent.path;\n  final AnalysisContextCollection collection =\n      AnalysisContextCollection(includedPaths: \u003cString\u003e[root]);\n\n  final CandiesAnalyzerPlugin myPlugin = plugin;\n  for (final AnalysisContext context in collection.contexts) {\n    for (final String file in context.contextRoot.analyzedFiles()) {\n      if (!myPlugin.shouldAnalyzeFile(file, context)) {\n        continue;\n      }\n\n      final bool isAnalyzed = context.contextRoot.isAnalyzed(file);\n      if (!isAnalyzed) {\n        continue;\n      }\n\n      final List\u003cAnalysisError\u003e errors =\n          (await myPlugin.getAnalysisErrorsForDebug(\n        file,\n        context,\n      ))\n              .toList();\n      for (final AnalysisError error in errors) {\n        final List\u003cAnalysisErrorFixes\u003e fixes = await myPlugin\n            .getAnalysisErrorFixesForDebug(\n                EditGetFixesParams(file, error.location.offset), context)\n            .toList();\n        print(fixes.length);\n      }\n\n      print(errors.length);\n    }\n  }\n}\n```\n\n### 更新代码\n\n```\n├─ example\n│  ├─ custom_lint\n│  │  └─ tools\n│  │     └─ analyzer_plugin\n```\n\n你有2种方式更新代码到 dartServer。\n\n\n1. 删除 .plugin_manager 文件夹\n\n注意, `analyzer_plugin` 文件夹下面的东西会复制到 `.plugin_manager` 下面，根据插件的路径加密生成对应的文件夹。\n\nmacos:  `/Users/user_name/.dartServer/.plugin_manager/`\n\nwindows: `C:\\Users\\user_name\\AppData\\Local\\.dartServer\\.plugin_manager\\`\n\n如果你的代码改变了, 请删除掉 `.plugin_manager` 下面的文件\n\n或者通过执行 `candies_analyzer_plugin --clear-cache` 来删除 `.plugin_manager` 下面的文件. \n\n\n1. 把新的代码写到 custom_lint 下面\n\n你可以把新代码写到 custom_lint 下面, 比如在 custom_lint.dart. \n\n```\n├─ example\n│  ├─ custom_lint\n│  │  ├─ lib\n│  │  │  └─ custom_lint.dart\n```\n\n如果这样的话，你必须增加 `custom_lint` 引用到 `analyzer_plugin\\pubspec.yaml` 当中\n\n你必须使用 `绝对路径`，因为 analyzer_plugin 文件夹是会被复制到 `.plugin_manager` 下面的.\n\n如果你不是要发布一个新的 package 的话，我不建议你使用第2种方式。\n\n\n```\n├─ example\n│  ├─ custom_lint\n│  │  ├─ lib\n│  │  │  └─ custom_lint.dart\n│  │  └─ tools\n│  │     └─ analyzer_plugin\n│  │        ├─ analysis_options.yaml\n```\n\n``` yaml\ndependencies:\n  custom_lint: \n    # absolute path  \n    path: xxx/xxx/custom_lint\n  candies_analyzer_plugin: any\n  path: any\n  analyzer: any\n  analyzer_plugin: any\n```\n\n### 重启 dart analysis 服务\n\n更新完毕代码之后，你可以通过在 vscode 中，通过下面的方式重启服务。\n\n1. 在 `View` 下面找到 `Command Palette`\n\n![](command_palette.png)\n\n2. 输入 `Restart Analysis Server`\n\n![](analysis_command.png)\n\n\n分析结束之后，你可以看到最新的结果.\n\n## Log\n\n在被分析的项目根目录会生成  `custom_lint.log`，用于查看分析过程的信息。\n\n1. 为了性能，默认是关闭的，你可以打开. \n\n   `CandiesAnalyzerPluginLogger().shouldLog = true;`\n\n2. 你可以更改日志的名字 \n\n   `CandiesAnalyzerPluginLogger().logFileName = 'your name';`\n\n3. 记录信息\n   \n``` dart\n   CandiesAnalyzerPluginLogger().log(\n        'info',\n        // which location custom_lint.log will be generated\n        root: result.root,\n      );\n```\n\n4. 记录错误\n\n``` dart\n   CandiesAnalyzerPluginLogger().logError(\n     'analyze file failed:',\n     root: analysisContext.root,\n     error: e,\n     stackTrace: stackTrace,\n   );\n```\n\n## 配置\n\n### 禁止一个 lint\n\n编写的自定义 lints 默认是全部开启的。当然你可以通过在 analysis_options.yaml 增加配置来禁用它。\n\n1. 使用 ignore tag 来禁用.\n\n``` yaml\nanalyzer:\n  errors:\n    perfer_candies_class_prefix: ignore\n```\n\n2. 使用 exclude 来过滤掉不想分析的文件\n  \n``` yaml\nanalyzer:\n  exclude:\n    - lib/exclude/*.dart\n```\n\n3. 通过将某个lint 设置为 false\n\n``` yaml\nlinter:\n  rules:\n    # disable a lint\n    perfer_candies_class_prefix: false \n```\n\n### 包含文件\n\n我们可以通过在 `custom_lint`(你定义的插件名字) 下面的 `include` 标记下面增加包含的文件。\n如果我们做了这个设置，那么我们就只会分析这些文件。\n\n``` yaml\n\n# your plugin name\ncustom_lint:\n  # if we define this, we only analyze include files\n  include: \n    - lib/include/*.dart\n```\n\n### 自定义 lint 严肃性\n\n你可以设置某个 lint 的严肃性。\n\n比如 `perfer_candies_class_prefix` 把它的严肃性从 `info` 改为 `warning`.\n\n支持 `warning` , `info` , `error`.\n\n``` yaml\nanalyzer:\n  errors:\n    # override error severity\n    perfer_candies_class_prefix: warning\n```\n\n## Default lints\n\n### PerferClassPrefix\n\n全部的类已某个前缀开始\n\n``` dart\nclass PerferClassPrefix extends DartLint {\n  PerferClassPrefix(this.prefix);\n\n  final String prefix;\n\n  @override\n  String get code =\u003e 'perfer_${prefix}_class_prefix';\n}\n```\n\n### PreferAssetConst\n\nasset 资源使用不要直接写字符串，而应该使用定义好的 const\n\n``` dart\nclass PreferAssetConst extends DartLint {\n  @override\n  String get code =\u003e 'prefer_asset_const';\n  @override\n  String? get url =\u003e 'https://pub.flutter-io.cn/packages/assets_generator';    \n}\n```\n### PreferNamedRoutes\n\n推荐使用命名路由\n\n``` dart\nclass PreferNamedRoutes extends DartLint {\n  @override\n  String get code =\u003e 'prefer_named_routes';\n  @override\n  String? get url =\u003e 'https://pub.flutter-io.cn/packages/ff_annotation_route';    \n}\n```\n\n### PerferSafeSetState\n\n在使用 `setState` 之前请先检查 `mounted`\n\n``` dart\nclass PerferSafeSetState extends DartLint {\n  @override\n  String get code =\u003e 'prefer_safe_setState';\n}\n```\n\n### MustCallSuperDispose\n\n此方法的实现应以调用继承的方法结束\n\n``` dart\nclass MustCallSuperDispose extends DartLint with CallSuperDisposeMixin {\n  @override\n  String get code =\u003e 'must_call_super_dispose';\n}\n```\n\n### EndCallSuperDispose\n\n应该在方法的最后才调用 `super.dispose()` \n\n``` dart\nclass EndCallSuperDispose extends DartLint with CallSuperDisposeMixin {\n  @override\n  String get code =\u003e 'end_call_super_dispose';\n}\n```\n\n### PerferDocComments\n\nhttps://dart.dev/guides/language/effective-dart/documentation\n跟 `public_member_api_docs` 一致，但是我们提供了 [ignoreLint] and [ignoreFile] 来过滤结果，你也可以重载\n[isPrivate] and [inPrivateMember] 来检查私有成员。\n\n\n``` dart\nclass PerferDocComments extends DartLint {\n  @override\n  String get code =\u003e 'perfer_doc_comments';\n}\n```\n\n### PreferSingleton\n\n这不是一个单例，但是每次都被创建使用。\n \n``` dart\nclass PreferSingleton extends DartLint {\n  @override\n  String get code =\u003e 'prefer_singleton';\n}\n```\n\n### GoodDocComments\n\n错误的注释格式。公共 api 使用 (/// xxx)， 其他情况使用 (// xxx)。\n \n``` dart\nclass GoodDocComments extends DartLint {\n  @override\n  String get code =\u003e 'good_doc_comments';\n}\n```\n\n### PreferTrailingComma\n\n为了更好的代码格式，在结尾增加逗号。\n \n``` dart\nclass PreferTrailingComma extends DartLint {\n  @override\n  String get code =\u003e 'prefer_trailing_comma';\n}\n```\n\n## Completion\n\n### Make a custom completion\n\n你可以定义自己的 `CompletionContributor` , `ExtensionMemberContributor` 是默认自带的.\n\n``` dart\n  /// The completionContributors to finish CompletionRequest\n  List\u003cCompletionContributor\u003e get completionContributors =\u003e\n      \u003cCompletionContributor\u003e[\n        ExtensionMemberContributor(),\n      ];\n``` \n\n### 扩展方法提示\n\n虽然官方已经关闭了这个问题 [Auto import (or quickfix?) for Extensions · Issue #38894 · dart-lang/sdk (github.com)](https://github.com/dart-lang/sdk/issues/38894) , 但是在不同的编辑器中依然有很多问题。\n\n`ExtensionMemberContributor` 帮助更好的处理扩展.\n\n## Pre-Commit\n\n### pre_commit.dart\n\n在下面的项目结构下面找到  `pre_commit.dart`  , 它是一个在提交代码前检查错误的例子\n\n```\n├─ example\n│  ├─ custom_lint\n│  │  └─ tools\n│  │     └─ analyzer_plugin\n│  │        ├─ bin\n│  │        │  └─ pre_commit.dart\n```\n### pre-commit script\n\n执行 `candies_analyzer_plugin --pre-commit`, pre-commit 脚本将被生成到 .git/hooks 中\n\n你可以通过增加以下文件，来自定这个脚本内容。\n\n```\n├─ example\n│  ├─ pre-commit\n```\n\n其中 {0} and {1} 是占位符，请勿修改.\n\n```\n#!/bin/sh\n\n# project path\nbase_dir=\"{0}\"\n\ndart format \"$base_dir\"\n\n# pre_commit.dart path\npre_commit=\"{1}\"\n \necho \"Checking the code before submit...\"\necho \"Analyzing $base_dir...\"\n\ninfo=$(dart \"$pre_commit\" \"$base_dir\")\n\necho \"$info\"\n\nif [[ -n $info \u0026\u0026 $info != *\"No issues found\"* ]];then\nexit 1\nfi\n```\n\n当前通过 `git commit` 命令提交代码的时候, 会先执行 .git/hooks/pre-commit 脚本.\n而这个脚本会去调用 `example/custom_lint/tools/analyzer_plugin/bin/pre_commit.dart`, 如果有错误的话，请调用 exit 1。\n\n所以你可以通过修改 `example/pre-commit` 和 `example/custom_lint/tools/analyzer_plugin/bin/pre_commit.dart`  来定义你自己的检查规则。\n\n### cacheErrorsIntoFile\n\n将 `CandiesAnalyzerPlugin.cacheErrorsIntoFile` 为 true, 来减少提交代码之前检查错误的时间。\n\n## 注意事项 \n### print lag\n\n不要在插件的分析代码中使用 `print` ，这会导致 analysis 卡顿\n\n### pubspec.yaml and analysis_options.yaml\n\n只有当你在 `pubspec.yaml` 和 `analysis_options.yaml` 中添加了 `custom_lint`，分析才会进行\n   \n1. 将 `custom_lint` 添加到  `pubspec.yaml` 中的 `dev_dependencies`  , 查看 [pubspec.yaml](https://github.com/fluttercandies/candies_analyzer_plugin/example/pubspec.yaml)\n   \n2. 将 `custom_lint` 添加到 `analysis_options.yaml` 中的 `analyzer` `plugins` ，查看 [analysis_options.yaml](https://github.com/fluttercandies/candies_analyzer_plugin/example/analysis_options.yaml)\n\n\n### 在 vscode 中快速修复只支持 dart 文件.(android studio支持任何文件)\n\n[issue](https://github.com/dart-lang/sdk/issues/50306)\n\n### 提示自动导入在 vscode 无法完成\n\n[issue](https://github.com/dart-lang/sdk/issues/50449)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fcandies_analyzer_plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluttercandies%2Fcandies_analyzer_plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fcandies_analyzer_plugin/lists"}