dart_interactive

Flutter Package CI

许多同类语言都有REPL,并且在日常使用中非常有用,而Dart却没有(尽管它是投票数第8高的请求)。所以它来了!

? 特点

一个功能齐全的REPL(交互式shell),具有:

  • 自由使用任何第三方包
  • 随时自动热重载代码,并保留状态
  • 在REPL中支持完整语法
  • 旁观现有代码

? 示例

演示1:演示功能

  1. 使用第三方包

>>> !dart pub add path // normal shell command
>>> import 'package:path/path.dart'; // normal import
>>> join('directory', 'file.txt') // use it (`join` is a function in 3rd party package `path`)
directory/file.txt
  1. 自动热重载

>>> import 'a.dart';
>>> myFunc()
hello, tom
// ... change content of `a.dart` ...
>>> myFunc()
hello, alex
  1. 支持完整语法

>>> a = 10;
// support rich grammar
>>> int g() => a++; class A {} class B {}
... class C extends A implements B {
...   int b = 20;
...   int f() { int c = 30; a++; b++; c++; return a+b+c+g(); }
... }
>>> c = C()
>>> c.f()
74
// support redefine class/method/...
>>> class C extends A implements B { int b = 20; int f() => b; }
>>> c.f()
21

演示2:示例工作流程

当然,您不必*一定要*这样使用它。这只是我个人在使用IPython/Juypter时觉得舒服的工作流程。

假设我们有一个包含代码的my_app.dart文件,可能是在IDE中编辑的

class Counter {
  int count = 0;
  String greet() => 'Hi Tom, you have count $count!';
}

稍微玩一下

$ interactive --directory path/to/my/package
>>> import 'my_app.dart';
>>> counter = Counter();
>>> counter.count = 10;
>>> counter.greet()
Hi Tom, you have count 10!
>>> counter.count = 20;
>>> counter.greet()
Hi Tom, you have count 20!

然后我们发现有些不对,想修改它

(change "Tom" to "Alex" inside `my_app.dart`)

继续玩(自动热重载,并保留状态)

>>> counter.greet()
Hi Alex, you have count 20!

我们还可以使用该包中的所有依赖项,因为REPL代码就像该包中的普通代码文件一样。

>>> import 'package:whatever_package';
>>> functionInWhateverPackage();

? 入门

安装(只是安装全局Dart包的标准流程)

dart pub global activate interactive

使用(只是一个普通的二进制文件)

interactive

然后玩玩?

详细功能列表

表达式

>>> a = 'Hello'; b = ' world!'; 
>>> '$a, $b'                   
Hello,  world!

声明

>>> print(a)
Hello

(所有方法,不只是print

函数

定义和重新定义

>>> String f() => 'old';
>>> f()
old
>>> String f() => 'new';
>>> f()
new

使用局部和全局变量

>>> a = 10;
>>> int f() { int b = 20; a++; b++; return a+b; }
>>> f() 
32
>>> f()
33

定义和重新定义,保留状态

>>> class C { int a = 10; int f() => a * 2; }
>>> c = C(); print(c.f());
20
>>> class C { int a = 1000; int f() => a * 3; }
>>> c.f()
30

注意:这遵循Dart的热重载语义。

扩展和实现

>>> class A { int f() => 10; } class B extends A { int f() => 20; }
>>> A().f() + B().f()
30
>>> class B implements A { int f() => 30; }
>>> A().f() + B().f()
40

使用局部变量、字段和全局变量

>>> a = 10;
>>> class C { int b = 20; int f() { int c = 30; a++; b++; c++; return a+b+c; } }
>>> c = C(); print(c.f()); print(c.f());
63
65

添加库作为依赖项

使用!dart pub add package_name,就像在Python(Jupyter/IPython)中所做的一样。

>>> join('directory', 'file.txt')
(...error, since have not added that dependency...)
>>> !dart pub add path
Resolving dependencies...

+ path 1.8.2

Changed 1 dependency!

>>> join('directory', 'file.txt')
(...error, since have imported it...)
>>> import 'package:path/path.dart';
>>> join('directory', 'file.txt')   
directory/file.txt

导入

内置包

>>> Random().nextInt(100)
(some error outputs here, because it is not imported)
>>> import "dart:math";
>>> Random().nextInt(100)
9

第三方包

注意:如果尚未添加到依赖项,请按照上面的说明操作,并使用!dart pub add path添加。

>>> join('directory', 'file.txt')
(...error, since have imported it...)
>>> import 'package:path/path.dart';
>>> join('directory', 'file.txt')   
directory/file.txt

一次处理多个

>>> int g() => 42; class C { int a = 10; int f() => a * 2; }
>>> C().f() + g()
62

多行,如果未结束

(因为包检测到它未完成,所以会在两行中出现...,而不是>>>。)

>>> class C {
...   int a = 10;
... }
>>> 

运行命令

使用前缀!

>>> !whoami
tom
>>> !date
2022-10-22 ...outputs...

在现有包的环境中执行

interactive --directory path/to/your/package

实施

通用

  • 创建一个空白包和一个isolate作为执行工作区
  • 使用分析器提取导入/类/函数/等,当名称相同时进行替换,并合成一个dart文件——从而支持丰富的Dart功能
  • 更新dart文件后触发Dart的热重载
  • 使用分析器区分表达式/语句/编译单元,并进行相应的转换
  • 唯一需要让Dart VM服务评估的是generatedMethod(),不要评估更多内容
  • 添加依赖项就像运行标准shell命令一样简单

至于“全局”变量

  • 实际上是通过字段变量实现的
  • 语句:将其放入extension on dynamic { Object? generatedMethod() { ...statements... } }中,以便无缝访问。
  • 函数:将函数转换为动态的扩展方法,以便无缝访问。
  • 类:在类中合成getter/setter,并在可能访问全局变量时委托给字段变量,以便无缝访问。

TODO:如果人们感兴趣,可以进一步讨论实现(以上内容非常简略)

✨ 贡献者

All Contributors

感谢这些出色的人(表情符号键

fzyzcjyfzyzcjy? ? ? Vyacheslav EgorovVyacheslav Egorov? Andreas KirschAndreas Kirsch? Maksim LinMaksim Lin?

更具体地说,感谢所有这些贡献

GitHub

查看 Github