前言

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。

Fair是58技术开源的一个Flutter动态化的框架,能够实现UI和逻辑的动态化。

开发者在使用Fair开发过程中存在一些痛点,比如可能会出现使用语法糖不正确或者存在不支持的语法糖问题,所以我们需要一个配套插件去提示用户使用Fair语法糖。

一、Flutter语法检测机制

在IDE中,Flutter语法检测机制是依赖Dart/Flutter插件实现的,即我们在开发Flutter前需要下载的Dart/Flutter插件。我们需要通过插件去提供Flutter的开发环境,同时插件也能提供语法检测功能。而插件其中的一个核心功能就是Analysis Server。

Analysis Server是什么?

Analysis Server是Dart SDK提供的一个Dart/Flutter语法分析服务,主要功能包括语法静态分析、代码提示、代码补全等。我们常用的Dart/Flutter IDE如intellij、Android Studio和VS Code都是通过安装Dart插件实现Dart开发环境的配置。

Dart/Flutter插件语法检测的工作流程

以Android Studio为例(因为对应插件的语言是Java,比较好阅读和理解),语法检测核心是Analysis Server,每当用户代码有改动的时候,就会通过Socket的方式同步给Analysis Server,当Analysis Server分析结束后也会将分析结果返回来,Dart插件则就是根据Analysis Server返的事件类型去做不同的处理,最终将结果刷新在用户IDE界面上。流程图如下:

Dart插件中Analysis Server的启动流程

当用户配置完Dart SDK路径后,插件会获取到配置路径,在插件启动的时候会启动一个进程去执行Dart SDK里的dart可执行文件,同时也会获取到Dart SDK目录下的"/bin/snapshots/analysis_server.dart.snapshot"文件,并将其路径作为vmArguments。我们可以看一小段代码:


//获取配置的SDK路径
mySdkHome = sdk.getHomePath();//找到dart可执行文件的路径
final String runtimePath = FileUtil.toSystemDependentName(DartSdkUtil.getDartExePath(sdk));//找到analysis_server.dart.snapshot文件路径
String analysisServerPath = FileUtil.toSystemDependentName(mySdkHome + "/bin/snapshots/analysis_server.dart.snapshot");//拼凑vmArguments
String firstArgument = useDartLangServerCall ? "language-server" : analysisServerPath;//创建Socket
myServerSocket =new StdioServerSocket(runtimePath, StringUtil.split(vmArgsRaw, " "), firstArgument, StringUtil.split(serverArgsRaw, " "), debugStream);//创建AnalysisServer实现类
final RemoteAnalysisServerImpl startedServer = new RemoteAnalysisServerImpl(myServerSocket);//这里的具体实现其实就是socket.start(),将socket启动起来
startedServer.start();

Dart插件与Analysis Server交互

Dart插件与Analysis Server交互类型主要分为两种,一种是id,一种是event。每次Dart插件给Analysis Server同步数据时都会生成一个uniqueId并缓存在HashMap,每次Analysis Server返数据的时候也会带上event或id,优先处理event类型数据。

Dart插件同步给Analysis Server数据示例:

request: {"id": String"method": "analysis.setAnalysisRoots""params": {"included": List<FilePath>"excluded": List<FilePath>"packageRoots": optional Map<FilePath, FilePath>}
}

上述说到Dart插件每次给Analysis Server同步数据都会生成一个uniqueId,这里的生成规则就是通过具备原子属性的AtomicInteger的getAndIncrement()方法每同步一次数据+1。Dart插件对应Analysis Server每种事件实现一个Consumer或Processor(如果是event类型数据则是Processor,result事件类型的话就是Consumer)。

语法检测插件的挂载流程

插件是有一个挂载过程的,插件的挂载流程如下图所示:

小结一下:

  1. YamlParaser解析analysis_options.yaml文件中analyzer#plugins节点,获取到一个pluginNameList
  2. 根据pluginNameList遍历通过Plugin Locator获取到每个plugin的analyzer_plugin目录路径,即pluginPath
  3. 将pluginPath目录路径文件copy到系统的.dartServer/plugin_manager/unique Directory目录下,其中unique Directory是通过对pluginPath进行md5操作生成的串
  4. 执行pluginPath下/bin/plugin.dart的main方法

二、Fair语法检测插件的实现原理

当我们掌握了前面的前置知识,再去开发语法插件就简单许多了。

Fair语法检测插件的实现机制

Dart插件是基于Analysis Server实现了Dart语法的检测,Fair语法检测插件则是对Dart插件语法检测功能的补充和扩展,实现对Fair语法糖的检测,Fair语法插件本质页是一个Dart语法检测插件,可以通过配置pubspec.yaml和analysis_options.yaml来使用。

语法插件的开发流程

主要可以分为以下几步:

  1. 首先创建一个类去继承ServerPlugin,并实现抽象属性/方法。
  2. 然后跟lib同级创建tools/analyzer_plugin目录,内容如图所示:
  3. 最后在plugin.dart文件中注册步骤1中实现的ServerPlugin。
//示例
void main(List<String> args, SendPort sendPort) {ServerPluginStarter(FairPlugin(PhysicalResourceProvider.INSTANCE)).start(sendPort);
}

核心实现

Fair语法检测插件的核心就是抽象语法树遍历,通过对抽象语法树的遍历,获取到每个子节点,然后插入自定义语法检测逻辑。

1.创建AnalysisDriver

createAnalysisDriver()是ServerPlugin的一个抽象方法,开发者实现这个方法创建一个AnalysisDriver,然后我们可以通过AnalysisDriver的results数据流去监听AnalysisResult的返回,其中这个AnalysisResult就是Analysis Server对文件的分析结果。示例代码如下所示:

@override
AnalysisDriverGeneric createAnalysisDriver(plugin.ContextRoot contextRoot) {//获取contextRootfinal contextRoots =ContextLocator(resourceProvider: resourceProvider).locateRoots(...);//获取ContextBuilderfinal contextBuilder =ContextBuilderImpl(resourceProvider: resourceProvider);//获取Analysis Contextfinal context = contextBuilder.createContext(contextRoot: contextRoots.first, byteStore: byteStore);//获取Analysis Driverfinal dartDriver = context.driver;//监听分析结果dartDriver.results.listen((analysisResult) {_processResult(...);});return dartDriver;
}

2.感知代码变化

ServerPlugin提供contentChanged方法,我们只需要收到回调的时候将内容有改变的文件路径添加到AnalysisDriver即可

@override
void contentChanged(String path) {super.driverForPath(path)?.addFile(path);
}

3.利用AOP思想对AnalysisResult处理

Dart的抽象语法树遍历是通过访问者模式实现的,我们可以在我们想要处理的节点插入一个自定义的Visitor对其及其子节点实现访问。其中我们可以从AnalysisResult拿到编译单元(CompilationUnit),每个编译单元就是一棵抽象语法树,我们可以通过其accept()方法插入一个Visitor。

4.@FairPatch注解识别实现逻辑

我们了解到Dart抽象语法树是通过访问者模式实现的,就能够很简单地去实现自定义的逻辑了。我们要实现@FairPatch注解的识别,我们只需要在遍历Annotation节点时,判断一下annotation name是我们想要的FairPatch即可。示例代码如下:

class _FairVisitor extends RecursiveAstVisitor<void> {@overridevoid visitAnnotation(Annotation node) {node.visitChildren(this);if (node.name.name == \'FairPatch\') {//...}}
}

5.if语法检测

因为if语法检测是需要前置条件的,首先得是用@FairPatch标注的类,其次还需要是在build()方法下的if才进行检测。

  • 首先是build()方法检测,只需要在method deceleration的时候进行一下方法过滤即可。
@override
void visitMethodDeclaration(MethodDeclaration node) {if (node.name.name == \'build\') {//...}
}
  • 其次是if语法检测,稍微复杂一点,if分为IfStatement和IfElement,IfStatement指代的是外层的if-else语句,IfElement指代的是嵌套在其他语法里的if-else语句,如果想要全覆盖if语法,则需要两者都实现。



上述两张图分别对应IfStatement和IfElement具体含义。

6.Fair的Sugar.if语法糖分类实现

在Fair中常用的Sugar.if相关语法糖有Sugar.ifEqual、Sugar.ifEqualBool和Sugar.ifRange

  • Sugar.ifRange
static K ifRange<T, K>(T actual,List<T> expect, {required K trueValue,required K falseValue,}) =>expect.contains(actual) ? trueValue : falseValue;
  • Sugar.ifEqual
static K ifEqual<T, K>(T actual,T expect, {required K trueValue,required K falseValue,}) =>expect == actual ? trueValue : falseValue;
  • Sugar.ifEqualBool
static K ifEqualBool<T, K>(bool state, {required K trueValue,required K falseValue,}) =>state ? trueValue : falseValue;

使用Fair语法糖能够加快Fair的编译,所以我们都更推荐能使用语法糖就使用语法糖,我们要实现对语法糖分类,其实就是对if的condition进行区分,其中ifRange稍微复杂一点,则我们再讲下如何实现,其他也同理:

//ifRange有个前置条件就是如参expect是一个List,所以再检测的时候只需要判断用户在调用list.contains(xx)即可//先判断用户是在调用contains()方法@overridevoid visitMethodDeclaration(MethodDeclaration node) {if (node.name.name == \'contains\') {//...}}//其次再判断调用contains方法的对象是List@overridevoid visitMethodInvocation(MethodInvocation node) {super.visitMethodInvocation(node);//判断staticType即可_result = node.target?.staticType?.isDartCoreList ?? false;if (_result) {_target = node.target;_actual = node.argumentList.arguments.first;}}

三、Fair Plugin实现效果展示

  • Android Studio实现对build()方法下if语法块的检测效果
  1. build方法下if的代码检测,及提示引导信息
  2. 点击more action 或者 AS代码提示快捷键
  3. 根据提示点击替换

相关推荐

  • Flutter动态化框架Fair文档上线&开源倒计时
  • Flutter动态化框架Fair的设计与思考
  • Fair 2.0 逻辑动态化开源了!
  • Fair逻辑动态化架构设计与实现
  • Fair逻辑动态化通信实现
  • Fair下发产物-布局DSL生成原理
  • Fair 逻辑语法糖设计与实现
  • Fair热更新设计与实现
  • Fair在安居拍房App中的实践
  • Flutter 动态化项目评测
  • Fair 在 58 同城拍客 App 中的实践
  • Flutter + Dart三端一体化动态化平台实践
  • 超级全面的Flutter性能优化实践

支持我们

欢迎大家使用 Fair,也欢迎大家为我们点亮star

Github地址:https://fair.58.com
Fair官网:https://fair.58.com

欢迎贡献

通过Issue提交问题,贡献代码请提交Pull Request,管理员将对代码进行审核。

对Fair感兴趣的小伙伴,可以加入交流群。

微信

微信入群:请先添加58技术小秘书为好友,备注fair,小秘书邀请进群。

Flutter语法检测及原理剖析-FAIR语法检测实践相关推荐

  1. matlab霍夫变换检测直线原理,霍夫变换(霍夫变换检测直线原理)

    OpenCv里面有个概率霍夫变换,但是不知道原理是什么,请各位大侠指教!.. 霍夫变换就是利用参数空间中的(ρ,θ)来表示一条直线,其中ρ是原点到直线的垂直距离,θ是原点到直线的一条垂线段与θ的夹角. ...

  2. webshell检测方式深度剖析 --- 行业内的实践方案

    概述 关于webshell检测我们已经讲了很多理论的东西,最近在网络上搜到了一篇阿里的主机层入侵检测团队在XCON(安全焦点安全信息技术峰会)上的一篇演讲PPT,名字为<云安全环境下恶意脚本检测 ...

  3. java实现html语法检查函数_Html或JS语法检测之JSLint工具

    1.介绍 JSLint是一个JavaScript验证工具(非开源), 可以扫描JavaScript源代码来查找问题.如果JSLint发现一 个问题,JSLint就会显示描述这个问题的消息,并指出错误在 ...

  4. 【编译原理】LR语法分析器的设计与实现

    LR语法分析器的设计与实现 本文为当时编译原理实验作业,要求用设计的思想完成,小题大做,仅供参考 文章目录 LR语法分析器的设计与实现 实验要求 实现功能 输入输出 样例 一.LR语法分析器问题定义 ...

  5. 编译原理实验-LL1语法分析器(自动生成First集、Follow集求法)java实现

    编译原理实验-LL1语法分析器(自动生成First.Follow)java 博主在做实验时,参考众多他人代码,发现bug众多,在@moni_mm代码基础上,与伙伴把能看到的BUG都做出修正,同时增添了 ...

  6. 编译原理之简单语法分析器(c语言)

    语法分析是编译过程的核心部分,其基本任务是根据语言的语法规则进行语法分析,如果不存在语法错误即给出正确的语法结果,并为语义分析和代码生成做准备. 语法分析器的两种方式 语法分析器的任务主要是确定是否可 ...

  7. 门禁系统中人脸检测技术的原理剖析和使用教程

    引言 人脸检测 API 是一种基于深度学习技术的图像处理API,可以快速地检测出一张图片中的人脸,并返回人脸的位置和关键点坐标,在人脸识别系统.人脸情绪识别等多种场景下都有极大的应用. 本文将从人脸检 ...

  8. SLR语法分析器-编译原理

    语法分析器 完整代码及图形化界面演示详见github:编译器前端 JavaFx项目,运行Main可看到图形界面,若要测试请按照文法,举几个栗子: 数据结构定义 单词和单词token表 package ...

  9. react16常见api以及原理剖析

    Vue 与 React 两个框架的粗略区别对比 Vue 的优势包括: 模板和渲染函数的弹性选择 简单的语法及项目创建 更快的渲染速度和更小的体积 React 的优势包括: 更适用于大型应用和更好的可测 ...

最新文章

  1. 2012-12-21
  2. 开发人员学Linux(3):CentOS7中安装JDK8和Tomcat8
  3. 吴恩达《Machine Learning》精炼笔记 11:推荐系统
  4. Linux X Window 与命令行的切换
  5. Openfire使用上的一些技巧
  6. spring4.x aop拦截spring mvc controller
  7. HDFS概述(5)————HDFS HA
  8. 服务器经常崩溃??让我们来看看简单的内存知识:C语言——内存管理
  9. 看门狗性能软件测试,《看门狗:军团》PC版性能测试 不建议光追,优化极差
  10. 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架
  11. 抗锯齿_电竞屏+AI抗锯齿,雷神911 Pro游戏本给你更好游戏体验
  12. VMware vSphere第三方免费工具介绍之一:RVTools
  13. 平面设计从事什么工作
  14. office安装错误“错误25004,您输入的产品密钥无法在此计算机上使用,-----------”
  15. 【科普】准大一新生如何挑选笔记本电脑
  16. 高分系列(GF1-GF7)卫星介绍
  17. Lifeline功能介绍03——课堂信息的查询
  18. airtest获取设备号和获取设备宽度、高度、绝对坐标 相对坐标、滑动屏幕
  19. Java获取Excel中链接的文件
  20. android分享截屏到微信,Android 微信分享长图 ScrollView 生成长截图 View变bitmap

热门文章

  1. 01 微架构教务系统——概览
  2. Persecond for Mac(延时摄影视频制作工具)
  3. DEV ASPXGRIDVIEW控件中的Findcontrol
  4. Java物联网开发(一) —— MQTT协议
  5. matplotlib显示图片与图片批量裁剪
  6. 让北大青鸟网络工程师飞得更高更快!
  7. android Jetpack StateFlow使用
  8. 步进电机使用总结——噪声与振动的抑制
  9. 富士通ERP解决方案举例
  10. Windows统计分析进程流量工具AppNetworkCounter