- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
analyzer_plugin是 Dart SDK 中用于构建 analysis server 插件的官方框架,其 CHANGELOG.md 记录了从 0.0.1 到 0.14.18-dev 的完整演进轨迹。本文以该变更日志为核心骨架,结合pkg/analyzer_plugin/lib下的源码实现,系统梳理插件框架的架构、关键 API 的破坏性变更、版本兼容策略,帮助插件开发者理解 API 的来龙去脉,并据此规划插件升级路径。
一、框架定位:为 analysis server 构建插件的支持代码
analyzer_plugin是一套用于构建 analysis server 插件的框架与支持代码。按 README.md 的说明,插件用 Dart 编写,运行在与 analysis server 相同的 VM 中,每个插件运行在独立 isolate 中,通过插件 API(与 analysis server 和客户端通信的 API 类似)与服务器通信,并被服务器自动发现和运行。
从 pubspec.yaml 可以看到,该包除依赖analyzer外,还依赖collection、dart_style、pub_semver、yaml和path,其版本策略声明:"We use 'any' version constraints here as we get our package versions from the dart-lang/sdk repo's DEPS file"。这意味着在 SDK 仓库内联开发时,依赖版本由 DEPS 统一管理。
需要特别说明的是,README 已明确标注该包为legacy support,新插件开发推荐使用analysis_server_plugin包。但对维护既有插件、理解 analysis server 插件协议演进历史的开发者而言,本变更日志仍是权威资料。
二、版本兼容策略:与analyzer包严格对齐
变更日志中超过一半的条目是同一句话的变体——"Require version X of theanalyzerpackage"。这构成了该包最重要的工程策略:每个 analyzer_plugin 版本都严格绑定一个 analyzer 版本范围,二者必须同步升级。
| analyzer_plugin 版本 | 要求的 analyzer 版本 | 备注 |
|---|---|---|
| 0.14.18-dev | 14.5.0-dev | 当前开发版本 |
| 0.14.17 | 14.4.0 | |
| 0.14.16 | 14.3.0 | |
| 0.14.15 | 14.2.0 | |
| 0.14.14 | 14.1.0 | |
| 0.14.13 | 14.0.0 | |
| 0.14.12 | 13.3.0 | |
| 0.14.11 | 13.2.0 | |
| 0.14.10 | 13.1.0 | |
| 0.14.9 | 13.0.0 | |
| 0.14.8 | 12.1.0 | |
| 0.14.7 | 12.0.0 | |
| 0.14.6-dev | 11.1.0-dev | |
| 0.14.5 | ^11.0.0-0 | |
| 0.14.4-dev | 10.3.0-dev | |
| 0.14.4 | 10.2.0 | |
| 0.14.3 | 10.1.0 | |
| 0.14.2 | 10.0.2 | |
| 0.14.1 | 10.0.1 | |
| 0.14.0 | 10.0.0 | 本版本含大量破坏性变更(见下文) |
| 0.13.11 | 9.0.0 | |
| 0.13.8 | 8.2.0 | 同时要求 Dart SDK^3.9.0 |
| 0.13.5 | ^8.0.0 | |
| 0.13.4 | ^7.5.1 | |
| 0.13.2 | ^7.4.6 | 同时弃用RangeFactory.error |
| 0.12.0 | 7.x | 协议枚举转为真枚举 |
| 0.11.3 | 6.x | |
| 0.11.2 | 5.x | |
| 0.10.0 | 4.x | |
| 0.9.0 | 3.x | |
| 0.7.0 | 2.x | |
| 0.5.0 | ^1.3.0 | 稳定版空安全 |
| 0.4.0 | >=0.41.0 <0.42.0 | |
| 0.2.5 | ^0.39.12 | |
| 0.2.2 | ^0.39.0 | |
| 0.2.1 | <0.39.0 | 同时修复 issue #37916、#38326 |
可见早期版本约束写法经历了从^0.39.0、<0.39.0到^1.3.0,再到^8.0.0、7.x和精确版本14.4.0的演化。当前仓库中 pubspec.yaml 实际锁定analyzer: 14.5.0-dev,与 0.14.18-dev 条目完全对应。
对于插件作者,这一策略意味着:升级 analyzer_plugin 时必须同步升级 analyzer,二者版本一一对应,不存在跨大版本混用的空间。这也是该包频繁发布小版本、每条记录都只有一行版本声明的原因。
三、插件生命周期与核心基类 ServerPlugin
在 lib/plugin/plugin.dart 中,ServerPlugin是所有插件必须继承的抽象基类。它定义了插件的核心形态:
- 基本属性:
name(用户可见插件名)、version(插件协议版本号)、fileGlobsToAnalyze(插件关注的文件 glob 模式)、contactInfo(作者联系信息,可空)。 - 通信通道:
start(PluginCommunicationChannel channel)启动插件并监听通道;channelgetter 返回当前通信通道。 - 版本握手:
handlePluginVersionCheck处理plugin.versionCheck请求,记录 SDK 路径并返回isCompatibleWith(serverVersion)、插件名、版本与 glob 模式;isCompatibleWith的判定逻辑是serverVersion <= Version.parse(version)。 - 上下文管理:
handleAnalysisSetContextRoots根据客户端下发的根路径创建AnalysisContextCollectionImpl,并在创建后回调afterNewContextCollection执行初始分析;销毁前回调beforeContextCollectionDispose。 - 文件内容同步:
handleAnalysisUpdateContent支持AddContentOverlay、ChangeContentOverlay、RemoveContentOverlay三种 overlay 变更,并将变更路径交给contentChanged,最终触发handleAffectedFiles对受影响文件重分析。 - 订阅机制:
handleAnalysisSetSubscriptions把客户端订阅的服务集合写入subscriptionManager,并立即对新订阅文件发送通知;sendNotificationsForFile按订阅的AnalysisService(FOLDING、HIGHLIGHTS、NAVIGATION、OCCURRENCES、OUTLINE)分发通知。 - 各功能请求的默认实现:
handleAnalysisGetNavigation、handleCompletionGetSuggestions、handleEditGetAssists、handleEditGetFixes、handleEditGetAvailableRefactorings、handleEditGetRefactoring等均返回空结果,子类按需覆写。
启动入口在 lib/starter.dart:ServerPluginStarter工厂创建Driver(plugin),start(SendPort sendPort)建立与服务器通信的通道并启动插件。插件由此运行在独立 isolate 中,与 analysis server 通过 SendPort 交换消息。
ServerPlugin内部还维护了priorityPaths(优先分析文件集合)与ByteStore(跨 AnalysisContext 复用的字节缓存,默认实现为 256MB 内存缓存)。flushAnalysisState可清理元素模型状态以降低堆占用,代价是下次分析会从缓存恢复、速度变慢。
四、分析上下文模型的演进(0.11.0)
0.11.0 的条目写明:
Using
AnalysisContextCollectionandAnalysisContextfor analysis.
这是插件分析模型的一次架构升级。在 0.11.0 之前,插件内部直接基于AnalysisDriver进行文件分析;之后统一改为AnalysisContextCollection(上下文集合)+AnalysisContext(单个上下文)模型。这一模型与 analyzer 包自身的分析驱动一致,使得插件可以按 context root 组织分析单元。
从 plugin.dart 的实现看,handleAnalysisSetContextRoots会基于请求中的includedPaths、resourceProvider、_byteStore、_sdkPath等构造新的AnalysisContextCollectionImpl,并开启withFineDependencies: true以支持细粒度依赖跟踪。contentChanged则对每个上下文调用analysisContext.changeFile(path)与applyPendingFileChanges(),得到受影响文件后再交给handleAffectedFiles。
0.11.1 进一步修正了行为细节:handleAffectedFiles默认仅对该分析上下文中确实被分析的文件调用analyzeFiles。对应实现是 plugin.dart 中的paths.where(analysisContext.contextRoot.isAnalyzed)过滤,避免把不属于当前上下文(如未纳入分析根目录)的文件也纳入分析,减少无效工作。
getResolvedUnitResult(path)是对外暴露的关键方法,通过当前上下文会话调用analysisSession.getResolvedUnit(path)获取ResolvedUnitResult,失败时抛出RequestFailure并返回pluginError。
五、变更构建器(ChangeBuilder)体系的演进
变更构建器是插件为 IDE/编辑器生成代码修改(assists、fixes)的核心设施,也是历次破坏性变更最密集的区域。
5.1 从 DartChangeBuilder 到 ChangeBuilder(0.4.0 → 0.5.0)
0.4.0 引入三个重要变化:
- 弃用
DartChangeBuilder类,增强ChangeBuilder作为替代; - 弃用
ChangeBuilder.addFileEdit,新增addDartFileEdit与addGenericFileEdit; - analyzer 支持范围改为
>=0.41.0 <0.42.0。
0.5.0 在稳定空安全(null safety)发布时正式移除DartChangeBuilder、DartChangeBuilderImpl与addFileEdit(),并弃用Plugin.fileContentOverlay——资源提供者统一改为OverlayResourceProvider,由analysis.updateContent更新。
当前 change_builder_core.dart 中,ChangeBuilder的工厂签名要求传入AnalysisSession或ChangeWorkspace(二选一,不能同时),并提供defaultEol参数(默认Platform.lineTerminator,已有 EOL 标记的文件保持原 EOL)。三个文件编辑入口分工明确:
addDartFileEdit(path, buildFileEdit):针对 Dart 源文件,builder 具备 Dart 专用能力;createEditsForImports参数(默认true)控制是否自动生成缺失 import 的编辑;addGenericFileEdit(path, buildFileEdit):通用文件,无特殊支持;addYamlFileEdit(path, buildFileEdit):针对 YAML 源文件,builder 具备 YAML 专用能力。
0.14.0 又移除了ChangeBuilder.new已弃用的eol参数与addDartFileEdit已弃用的importPrefixGenerator参数,并删除ChangeBuilder.copy方法。
5.2 DartEditBuilder 的新增 API(0.12.0)
0.12.0 在DartEditBuilder上新增了writeFormalParameter与writeFormalParameters,在DartFileEditBuilder上新增了:
getIndent:按层级返回缩进;insertCaseClauseAtEnd:在 switch 语句/表达式末尾插入 case 子句;insertConstructor、insertField、insertGetter、insertMethod:按 lint 规则与代码风格把成员插入到合适位置;writeIndent:写出缩进(每个层级两个空格)。
在 change_builder_dart.dart 中可以确认这些 API 的完整签名与语义。例如insertConstructor会考虑sort_constructors_first、sort_unnamed_constructors_first两条 lint 规则来决定插入点;insertField会参考CodeStyleOptions;writeIndent([int level = 1])默认写两级空格缩进。
0.12.0 还加入了一套实验性 API:writeOverride2、writeReference2、writeType2、writeTypeParameter2、writeTypeParameters2,作为下一代成员写入接口的试验性前身。
5.3 DartFileEditBuilder 的破坏性变更(0.12.0 → 0.13.0)
- 0.12.0:
convertFunctionFromSyncToAsync与replaceTypeWithFuture发生破坏性变更。在源码中,这两个方法目前都需要FunctionBody/TypeAnnotation加TypeSystem、TypeProvider三个参数,用于在同步/异步函数间转换时同步调整返回类型(Future<T>)。另一个方向convertFunctionFromAsyncToSync与replaceTypeWithFutureArgument同样存在。二者对生成器函数(generator)体有限制,不满足条件时抛出ArgumentError。 - 0.13.0:
DartFileEditBuilder与DartEditBuilder再次发生破坏性变更(日志未列出细节,但可推断与写入 API 的重新设计相关)。 - 0.14.0:移除各方法上已弃用的
methodBeingCopied参数。
5.4 链接编辑(Linked Edit)与导入管理
EditBuilder.addLinkedEdit(groupName, builder)与addSimpleLinkedEdit支持把编辑中的文本区域归入链接编辑组,并附带LinkedEditSuggestionKind类型的建议值,供 IDE 实现"重命名一处、联动多处"的交互。FileEditBuilder.addLinkedPosition(range, groupName)则把既有代码区域纳入链接组。
导入管理集中在DartFileEditBuilder.importLibrary与importLibraryElement:
importLibrary(uri, {prefix, showName, useShow}):安排为给定 URI 的库添加 import;可指定前缀、show 子句;返回实际写入指令的 URI 文本(可能因 lint 规则从绝对 URI 转为相对 URI);importLibraryElement:确保库已导入——若已存在且未带前缀,则修改现有 import 以展示showName;若已带前缀则新增一个无前缀 import;importsLibrary(uri):查询某库是否已被现有或计划中的编辑导入;fileHeadersetter:设置在生成的 import 之前的文件头(自动补一个空行);requiredImports:列出编辑中引用的新类型所必须导入的 URI。
ImportPrefixGenerator类型在 change_builder_dart.dart 中已被标注@Deprecated('This type is no longer used or necessary'),呼应 0.14.0 移除importPrefixGenerator参数的变更。
六、RangeFactory 的两次改名(0.13.0 → 0.13.2 → 0.14.0)
RangeFactory是生成SourceRange的工厂类,range_factory.dart 提供了从 AST 节点、token、语法实体、offset 组合派生范围的大量方法,例如:
node(AstNode)、token(Token)、entity(SyntacticEntity):覆盖单个语法实体;startEnd(left, right)、endEnd(left, right)、startStart、endStart:按两个实体的起止位置组合范围;startLength、endLength、offsetBy:按偏移与长度构造;argumentRange(argumentList, lower, upper, forDeletion):覆盖参数列表中的一段;forDeletion为true时包含邻近逗号以支持安全删除(对(a, b, c, d)取下标 1~2 时,范围分别是'b, c'或', b, c');deletionRange(node, {overrideEnd}):考虑节点前后的空白与注释,计算可安全删除的范围;nodeInList(list, item):返回列表中单个元素含前导/尾随逗号的范围。
该类的演进体现了 API 清理的连贯性:
- 0.13.0:移除
elementName(),改用fragmentName()。fragmentName(Fragment fragment)基于 fragment 的nameOffset/name返回名称范围,对合成(synthetic)fragment 返回null。 - 0.13.2:弃用
RangeFactory.error,由RangeFactory.diagnostic替代。diagnostic(Diagnostic)返回与诊断对象范围一致的SourceRange。 - 0.14.0:删除已弃用的
RangeFactory.error方法。
这一系列变更显示:插件 API 越来越强调"诊断对象"与"元素片段(fragment)"这些新概念,逐步淘汰旧有的"错误(error)"与"元素名(elementName)"表述。
七、协议层演进:真枚举、AnalysisStatus 与 SourceEdit 变更描述
7.1 协议枚举转为真枚举(0.12.0)
0.12.0 是协议层最大的一次破坏性变更:lib/protocol/protocol_common.dart与lib/protocol/protocol_generated.dart中所有实现Enum的类全部转为真正的 Dart enum。影响如下:
- 每个枚举不再有静态
VALUES字段; - 无公开构造函数;
- 枚举值不再有实例 getter
name(但dart:core的EnumName扩展提供了name); - 枚举实例被视为穷尽(exhaustive),既有 switch 语句/表达式可能触发新的诊断。
这解释了 AnalyzerConverter 中大量使用.values.byName(...)的写法——例如convertErrorSeverity用plugin.AnalysisErrorSeverity.values.byName(severity.name)完成 analyzer 诊断严重级别到插件协议枚举的映射。
7.2 AnalysisStatus 通知(0.13.0)
0.13.0 支持插件发送AnalysisStatus通知,带有一个isAnalyzing布尔字段。这一字段让 IDE 能够获知插件当前是否处于分析中,用于展示加载状态。搜索仓库可知该字段贯穿 protocol_generated.dart 与集成测试的协议匹配器(protocol_matchers.dart)。
7.3 SourceEdit 变更描述(0.12.0)
0.12.0 支持在SourceEdit上附加变更描述(change descriptions),使编辑结果可携带人类可读的说明信息,IDE 可在应用编辑时展示。
八、混入(Mixin)与贡献者体系的演进
0.14.0 将AssistContributorMixin从普通类改为 mixin。当前实现位于 assist_contributor_mixin.dart:mixin AssistContributorMixin on AssistContributor,提供addAssist(AssistKind kind, ChangeBuilder builder, {List<Object>? args})工具方法——通过 kind 获取消息与优先级,通过 builder 获取编辑集;当编辑集为空时不产生 assist;否则设置change.id、change.message(用formatList填充消息参数)并交给collector以PrioritizedSourceChange形式收集。
插件的各类功能请求都通过**贡献者(contributor)**模式组装。例如 assist_mixin.dart 中的AssistsMixin提供handleEditGetAssists的通用实现:先由getAssistContributors(path)取得贡献者列表,再构造AssistRequest,交给AssistGenerator生成响应。配套的DartAssistsMixin则基于getResolvedUnitResult构造DartAssistRequestImpl。类似地,lib/plugin 目录下还有fix_mixin.dart、completion_mixin.dart、folding_mixin.dart、highlights_mixin.dart、navigation_mixin.dart、occurrences_mixin.dart、outline_mixin.dart等,对应导航、折叠、高亮、出现次数、大纲、补全、修复等能力,均在相应版本迭代中随协议演进。
九、早期版本里程碑:从 0.0.1 到 0.5.0
- 0.0.1:初始版本。
- 0.0.1-alpha.7:移除
CompletionSuggestion.elementUri,改用AvailableSuggestionSet;从CompletionSuggestion移除importUri;补全建议中纳入类型参数。 - 0.2.0:
DartEditBuilder.writeOverride()改为接受ExecutableElement而非FunctionType(当前 writeOverride 的签名即ExecutableElement element)。 - 0.2.3:新增
Relevance类;FixKind.name改为FixKind.id(官方认定为良性破坏性变更,因原name仅用于调试);新增computeDartNavigation函数;注意此版本从未发布(存在对package:analysis_server的问题导入)。 - 0.2.4:公开
AnalyzerConverter.locationFromElement(原为私有)。 - 0.3.0:移除已弃用的
Plugin.getResolveResult,改用getResolvedUnitResult。 - 0.5.0:稳定空安全发布,依赖更新到空安全版本;要求 SDK 2.14 以使用
Object.hash();要求yaml 3.1.0以使用recover。此时 analyzer 支持范围改为^1.3.0。
0.8.0 的"Require SDK2.14to useObject.hash()"与yaml 3.1.0的recover依赖,表明该版本是空安全依赖链全面落地的收尾。
十、版本时间线总览
| 版本 | 核心变更摘要 |
|---|---|
| 0.0.1 ~ 0.0.1-alpha.8 | 初始发布,随 pkg:analyzer 演进 |
| 0.2.x | writeOverride 签名变更、Relevance 与 FixKind.id、AnalyzerConverter.locationFromElement 公开 |
| 0.3.0 | 移除 getResolveResult,改用 getResolvedUnitResult |
| 0.4.0 | 引入 ChangeBuilder 替代 DartChangeBuilder |
| 0.5.0 | 稳定空安全;移除 DartChangeBuilder;overlay 机制重构 |
| 0.6.0 | 协议 bug 修复 |
| 0.7.0 ~ 0.11.3 | analyzer 2.x~6.x 支持;0.11.0 起采用 AnalysisContextCollection/AnalysisContext 模型 |
| 0.12.0 | 协议枚举转真枚举;DartEditBuilder/DartFileEditBuilder 新 API;SourceEdit 变更描述 |
| 0.13.0 | 移除 elementName();DartFileEditBuilder/DartEditBuilder 破坏性变更;AnalyzerConverter 破坏性变更;AnalysisStatus 通知 |
| 0.13.2 | 弃用 RangeFactory.error → diagnostic |
| 0.14.0 | AssistContributorMixin 转 mixin;移除若干弃用 API 与参数 |
| 0.14.1 ~ 0.14.17 | analyzer 10.0.x~14.4.0 逐版本对齐 |
| 0.14.18-dev | 要求 analyzer 14.5.0-dev |
十一、给插件开发者的升级指南
结合变更日志与源码,升级插件时可遵循以下要点:
- 版本配对升级:任何 analyzer_plugin 升级都必须同步升级 analyzer 到对应版本(见第二节对照表),二者不可错位。
- 优先处理破坏性变更版本:0.12.0(协议枚举)、0.13.0(RangeFactory 与编辑构建器)、0.14.0(移除弃用 API)是三个跳板版本,建议逐个跨越、分别编译验证。
- 统一使用新 API:
- 构建变更一律使用
ChangeBuilder的addDartFileEdit/addGenericFileEdit/addYamlFileEdit,不再使用旧式addFileEdit; - 获取范围统一使用
RangeFactory.diagnostic与fragmentName,避免error与elementName; - 范围/位置转换使用
AnalyzerConverter(analyzer_converter.dart)。
- 构建变更一律使用
- 适配真枚举:协议枚举已无
VALUES字段,switch 需保证穷尽性,name请通过EnumName扩展获取。 - 模型对齐:分析能力基于
AnalysisContextCollection构建,覆盖handleAffectedFiles时记得先过滤contextRoot.isAnalyzed,避免分析范围外文件。 - 新项目改用 analysis_server_plugin:本包已标记 legacy,仅当维护既有插件或研究协议历史时使用本框架。
十二、可深入研读的仓库入口
- 变更日志原文:pkg/analyzer_plugin/CHANGELOG.md
- 包说明与使用指引:pkg/analyzer_plugin/README.md
- 依赖与版本声明:pkg/analyzer_plugin/pubspec.yaml
- 插件基类与请求分发:pkg/analyzer_plugin/lib/plugin/plugin.dart
- 变更构建器核心:pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
- Dart 编辑构建器:pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
- 范围工厂:pkg/analyzer_plugin/lib/utilities/range_factory.dart
- 协议转换器:pkg/analyzer_plugin/lib/utilities/analyzer_converter.dart
- 各类功能 mixin:pkg/analyzer_plugin/lib/plugin
- 插件启动器:pkg/analyzer_plugin/lib/starter.dart
- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
相关推荐
Finagle服务版本控制:API兼容性与演进策略
Finagle服务版本控制:API兼容性与演进策略 在微服务架构中,服务版本控制是确保系统平滑升级和维护兼容性的关键环节。Finagle作为一个容错的、协议无关
后端RPC框架终极指南:Containerd插件版本兼容性策略与API演进最佳实践
终极指南:Containerd插件版本兼容性策略与API演进最佳实践 在容器化技术快速发展的今天,作为核心容器运行时的Containerd面临着API演进与向后
云原生容器运行时Cerebro跨版本兼容性:API变更与插件适配策略
Cerebro跨版本兼容性:API变更与插件适配策略 随着Cerebro版本迭代,API变更可能导致插件功能异常或失效。本文系统梳理API演进规律,提供插件开发
桌面应用开发者工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考