【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
项目地址:
https://gitcode.com/gh_mirrors/sdk1/sdk
点击查看 免费下载
导读
Dart SDK 分析服务器插件(analyzer plugin) 是扩展 dart analyze、编辑器诊断、代码补全与重构能力的关键机制:工具(tool)通过运行插件来接收外部贡献的分析结果。本文以官方教程 Package Structure 为核心骨架,结合仓库内 pkg/analyzer_plugin 的真实源码实现,系统拆解插件的四包模型(target / host / bootstrap / plugin)、发现与加载流程、bootstrap 包的强制结构以及插件的隔离执行机制。读完本文,你将能正确设计一个分析服务器插件的目录布局,并理解插件是如何被工具自动发现、启动与通信的。
一、插件机制与“工具”的抽象
插件被用于允许外部贡献参与到工具产生的分析结果中。目前,分析服务器(analysis server)是唯一支持插件的工具,但 Dart 团队计划将插件支持扩展到命令行分析器(command-line analyzer)乃至其他工具。因此,官方文档统一将运行插件的工具泛称为 the tool。
插件用 Dart 编写,与分析服务器运行在同一个 VM 中,但每个插件运行在独立的 isolate 中(参见 introduction.md)。工具与插件之间通过一组基于请求/响应/通知的线协议通信,这部分协议在 pkg/analyzer_plugin/doc/api.html 中定义。
二、四包模型:一次说清四个角色的职责
为了描述工具如何使用插件,官方文档引入了四个相互区分的包。理解这四个概念是设计插件架构的第一步。
| target package | 目标包 | 工具正在为其产生分析结果的包;若工具是分析服务器,即用户在客户端打开并正在开发的包 |
| host package | 宿主包 | 包含定位与运行插件所需信息的包,其中内嵌 bootstrap 包;target 包需依赖它(普通依赖或 dev 依赖均可) |
| bootstrap package | 引导包 | 内嵌于宿主包中的小型包,用于加载 plugin 包 |
| plugin package | 插件包 | 包含插件实际实现的包 |
若对 Dart 包(package)概念不熟悉,建议先了解 Dart 的包管理器 pub 的工作方式。
值得强调的是依赖方向:target 包必须声明对 host 包的依赖(普通依赖或 dev 依赖皆可),工具才能据此发现 host 包、进而定位并运行其中内嵌的 bootstrap 包。
三、官方推荐的结构:为什么 bootstrap 与 plugin 分开
从技术上来说,你可以把 bootstrap 包与 plugin 包合并成一个包。但官方文档明确推荐保持两者分离,理由非常实际:
对于没有选择启用该托管插件的用户而言,分离结构能最小化需要额外下载的文件数量。
也就是说,用户可能只是普通依赖了 host 包(例如某个框架包),而并不想启用它的插件功能。若 bootstrap 与 plugin 合并,那么插件实现的大量文件也会被一并拉取;分开存放则可以让“不启用插件”的用户只下载 bootstrap 这个轻量壳。
典型案例:angular 包
文档给出的真实案例是 angular 包。angular 包自身内嵌了一个 bootstrap 包;当一个实现 Web 应用的 target 包依赖 angular 包时,只需在分析选项中将 angular 列为 approved host package(受批准的宿主包),angular 插件就会被运行。
四、插件发现:analysis_options.yaml 中的 approved host 列表
工具只有在被明确告知的情况下才会运行插件来分析 target 包。发现机制分两步:
analyzer:
plugins:
– host_package_1
– host_package_2
因此,从配置到生效的完整链路是:analysis_options.yaml 白名单 → .packages 解析宿主包 → 宿主包内 tools/analyzer_plugin 目录 → bootstrap 包启动 → plugin 包加载。在源码层面,这一“插件被运行”的落点是 pkg/analyzer_plugin/lib/src/driver.dart 中的 Driver 类,它代表一个正在运行的插件实例,负责与服务器的通信并将请求转发给插件。
五、Bootstrap 包的强制结构
前面三个包(target、host、plugin)都可以是任意合法的 Dart 包结构,唯独 bootstrap 包必须包含两个特定文件:
1. tools/analyzer_plugin/pubspec.yaml
该文件供 pub 命令生成 .packages 文件,用于解析 bootstrap 包内出现的 package: URI。典型情况下,它只需声明对 plugin 包的一个依赖即可,例如:
name: my_plugin_bootstrap
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
my_plugin:
path: ../my_plugin
注意:虽然从技术上讲依赖可以任意,但官方强调“通常唯一需要的依赖就是对 plugin 包的依赖”,这保证了 bootstrap 包的轻量性。
2. tools/analyzer_plugin/bin/plugin.dart
这是插件的入口点。由于每个插件都运行在独立 isolate 中,入口函数必须具备如下签名:
void main(List<String> args, SendPort sendPort) {
// Invoke the real main method in the plugin package.
}
main 的函数体通常只有一行:调用 plugin 包中负责创建并启动插件的函数。例如,借助 analyzer_plugin 包提供的 ServerPluginStarter(见 starter.dart):
import 'dart:isolate';
import 'package:analyzer_plugin/starter.dart';
import 'package:my_plugin/my_plugin.dart';
void main(List<String> args, SendPort sendPort) {
ServerPluginStarter(MyPlugin()).start(sendPort);
}
ServerPluginStarter 的工厂构造会创建 Driver 实例;Driver.start(SendPort) 内部通过 PluginIsolateChannel(sendPort) 建立与服务器的通信通道,并调用插件实例的 start(channel) 开始监听请求(见 channel.dart 与 plugin.dart 中的 ServerPlugin.start)。入口点收到 SendPort、建立通道并启动插件,这正是 main 签名中 SendPort 参数的用途。
六、插件执行:临时目录、pub 解析与独立 isolate
当一个 bootstrap 包被决定运行时,工具的加载流程如下:
采用“复制到临时目录再运行 pub”的方式,是为了保证 bootstrap 包的分析与依赖解析独立、干净,不污染宿主包或 target 包的环境;而“独立 isolate 运行”保证了插件之间、插件与服务器之间互不阻塞。服务器通过 plugin.versionCheck 请求验证插件与服务器的 API 版本兼容性,通过 plugin.shutdown 请求在关闭时通知插件释放资源(详见 introduction.md)。
七、落地实践:从包结构到最小插件
推荐的仓库布局
综合四包模型与 bootstrap 强制结构,一个典型的插件项目目录如下:
my_plugin/ # plugin package:插件实现
lib/
my_plugin.dart # 定义 ServerPlugin 子类
pubspec.yaml
my_host/ # host package:宿主包,内嵌 bootstrap
lib/
my_host.dart
tools/
analyzer_plugin/ # bootstrap package
bin/
plugin.dart # 入口点:void main(List<String>, SendPort)
pubspec.yaml # 声明对 my_plugin 的依赖
pubspec.yaml
consumer_app/ # target package:被分析的包
analysis_options.yaml # analyzer.plugins 白名单
.packages # 解析 host 包依赖
pubspec.yaml # 依赖 my_host
最小插件的核心类
plugin 包中的插件实现类继承 ServerPlugin(源码见 plugin.dart)。一个最小实现需要提供构造器、三个 getter(name、version、fileGlobsToAnalyze)以及两个方法(createAnalysisDriver、sendNotificationsForSubscriptions),参见教程 Getting Started 中的示例骨架:
class MyPlugin extends ServerPlugin {
MyPlugin(ResourceProvider provider) : super(provider);
@override
List<String> get fileGlobsToAnalyze => <String>['**/*.dart'];
@override
String get name => 'My fantastic plugin';
@override
String get version => '1.0.0';
@override
AnalysisDriverGeneric createAnalysisDriver(ContextRoot contextRoot) {
// TODO: implement createAnalysisDriver
return null;
}
@override
void sendNotificationsForSubscriptions(
Map<String, List<AnalysisService>> subscriptions) {
// TODO: implement sendNotificationsForSubscriptions
}
}
其中 name 与 version 会在遇到问题时出现在错误信息中,fileGlobsToAnalyze 声明插件关心的文件 glob 模式。从源码可以看出(plugin.dart),ServerPlugin 内部通过 OverlayResourceProvider 访问文件系统、用 SubscriptionManager 管理订阅、并统一在 _getResponse 中分发 analysis.*、completion.*、edit.* 与 plugin.* 等请求——这些正是插件与服务器通信协议的服务端实现。
八、继续深入
本主题在官方教程中属于系列文章的一环。若想继续实现具体功能,可依次阅读同目录下的其余章节(均位于 pkg/analyzer_plugin/doc/tutorial/):
- Creating Edits:组装 assists、fixes 与 refactorings 所需的编辑操作
- Providing Quick Assists 与 Providing Quick Fixes:上下文相关的代码操作
- Providing Code Completions:代码补全建议
- Providing Navigation Information、Providing Occurrences Information、Providing Outline Information、Providing Folding Information:编辑器导航、标记、大纲与折叠支持
- Debugging Plugins:通过状态页、instrumentation log 与 DevTools 排查插件问题
插件的完整通信协议定义与分析能力封装,可在 pkg/analyzer_plugin/lib 的 plugin/、protocol/、channel/ 与 utilities/ 子目录中进一步研读;pkg/analyzer_plugin/test/plugin/ 下各 mixin 测试(如 assist_mixin_test.dart、completion_mixin_test.dart)则提供了各功能接口的调用与验证示例,可作为实现参考。
赞
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
项目地址:
https://gitcode.com/gh_mirrors/sdk1/sdk
点击查看 免费下载
相关推荐
Pitaya RPC系统揭秘:Sys RPC与User RPC的区别与应用场景
EBAZ4205网络功能实战:10/100M以太网PHY配置与性能测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网硕互联帮助中心





评论前必须登录!
注册