{"id":13549726,"url":"https://github.com/fzyzcjy/dart_interactive","last_synced_at":"2026-01-11T04:49:08.147Z","repository":{"id":61846440,"uuid":"555668458","full_name":"fzyzcjy/dart_interactive","owner":"fzyzcjy","description":"REPL (interactive shell) for Dart, supporting 3rd party packages, hot reload, and full grammar","archived":false,"fork":false,"pushed_at":"2024-06-29T14:11:45.000Z","size":240,"stargazers_count":220,"open_issues_count":3,"forks_count":13,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-30T01:38:24.650Z","etag":null,"topics":["dart","flutter","interactive","repl","shell"],"latest_commit_sha":null,"homepage":"https://github.com/fzyzcjy/dart_interactive","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/fzyzcjy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-22T03:35:08.000Z","updated_at":"2024-10-22T22:06:46.000Z","dependencies_parsed_at":"2024-08-01T12:17:23.043Z","dependency_job_id":"52fd5b50-36cf-491f-bf28-128b985f469b","html_url":"https://github.com/fzyzcjy/dart_interactive","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzyzcjy%2Fdart_interactive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzyzcjy%2Fdart_interactive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzyzcjy%2Fdart_interactive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fzyzcjy%2Fdart_interactive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fzyzcjy","download_url":"https://codeload.github.com/fzyzcjy/dart_interactive/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767236,"owners_count":20992548,"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":["dart","flutter","interactive","repl","shell"],"created_at":"2024-08-01T12:01:24.826Z","updated_at":"2026-01-11T04:49:08.141Z","avatar_url":"https://github.com/fzyzcjy.png","language":"Dart","funding_links":[],"categories":["Dart"],"sub_categories":[],"readme":"# [dart_interactive](https://github.com/fzyzcjy/dart_interactive)\n\n[![Flutter Package](https://img.shields.io/pub/v/interactive.svg)](https://pub.dev/packages/interactive)\n[![CI](https://github.com/fzyzcjy/dart_interactive/actions/workflows/ci.yaml/badge.svg)](https://github.com/fzyzcjy/dart_interactive/actions/workflows/ci.yaml)\n\n![](https://raw.githubusercontent.com/fzyzcjy/dart_interactive/master/doc/logo.svg)\n\nA lot of sibling languages have a REPL, and is quite helpful in everyday usage, while Dart did not have it (even though it was the [7th](https://github.com/dart-lang/sdk/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) highest-voted request). So here it comes!\n\n## 🚀 Features\n\nA full-featured REPL (interactive shell), with:\n\n* Use any third-party package freely\n* Auto hot-reload code anywhere, with state preserved\n* Supports full grammar in REPL\n* Play with existing code side-by-side\n\n## 📚 Demo\n\n### Demo 1: Demonstrate features\n\n1. Use 3rd party package\n\n```dart\n\u003e\u003e\u003e !dart pub add path // normal shell command\n\u003e\u003e\u003e import 'package:path/path.dart'; // normal import\n\u003e\u003e\u003e join('directory', 'file.txt') // use it (`join` is a function in 3rd party package `path`)\ndirectory/file.txt\n```\n\n2. Auto hot-reload\n\n```dart\n\u003e\u003e\u003e import 'a.dart';\n\u003e\u003e\u003e myFunc()\nhello, tom\n// ... change content of `a.dart` ...\n\u003e\u003e\u003e myFunc()\nhello, alex\n```\n\n3. Support full grammar\n\n```dart\n\u003e\u003e\u003e a = 10;\n// support rich grammar\n\u003e\u003e\u003e int g() =\u003e a++; class A {} class B {}\n... class C extends A implements B {\n...   int b = 20;\n...   int f() { int c = 30; a++; b++; c++; return a+b+c+g(); }\n... }\n\u003e\u003e\u003e c = C()\n\u003e\u003e\u003e c.f()\n74\n// support redefine class/method/...\n\u003e\u003e\u003e class C extends A implements B { int b = 20; int f() =\u003e b; }\n\u003e\u003e\u003e c.f()\n21\n```\n\n### Demo 2: Sample workflow\n\nSurely, you do not *have to* use it like this. It is just a workflow that I personally feel comfortable when working with IPython/Juypter.\n\nSuppose we have `my_app.dart` with some code, probably edited inside an IDE:\n\n```dart\nclass Counter {\n  int count = 0;\n  String greet() =\u003e 'Hi Tom, you have count $count!';\n}\n```\n\nPlay with it a bit:\n\n```dart\n$ interactive --directory path/to/my/package\n\u003e\u003e\u003e import 'my_app.dart';\n\u003e\u003e\u003e counter = Counter();\n\u003e\u003e\u003e counter.count = 10;\n\u003e\u003e\u003e counter.greet()\nHi Tom, you have count 10!\n\u003e\u003e\u003e counter.count = 20;\n\u003e\u003e\u003e counter.greet()\nHi Tom, you have count 20!\n```\n\nThen we realize something wrong and want to change it:\n\n```dart\n(change \"Tom\" to \"Alex\" inside `my_app.dart`)\n```\n\nContinue playing with it (auto hot reloaded, and state preserved):\n\n```dart\n\u003e\u003e\u003e counter.greet()\nHi Alex, you have count 20!\n```\n\nWe can also use all dependencies in the package as well, since the REPL code is just like a normal code file in this package.\n\n```dart\n\u003e\u003e\u003e import 'package:whatever_package';\n\u003e\u003e\u003e functionInWhateverPackage();\n```\n\n## 🎼 Getting started\n\nInstall (just standard procedure of installing global dart packages):\n\n```shell\ndart pub global activate interactive\n```\n\nUse (just a normal binary):\n\n```shell\ninteractive\n```\n\nAnd play with it :)\n\n## Detailed functionality list\n\n### Expressions\n\n```dart\n\u003e\u003e\u003e a = 'Hello'; b = ' world!'; \n\u003e\u003e\u003e '$a, $b'                   \nHello,  world!\n```\n\n### Statements\n\n```dart\n\u003e\u003e\u003e print(a)\nHello\n```\n\n\u003csmall\u003e(All methods, not only `print`)\u003c/small\u003e\n\n### Functions\n\n#### Define and redefine\n\n```dart\n\u003e\u003e\u003e String f() =\u003e 'old';\n\u003e\u003e\u003e f()\nold\n\u003e\u003e\u003e String f() =\u003e 'new';\n\u003e\u003e\u003e f()\nnew\n```\n\n#### Use local and global variables\n\n```dart\n\u003e\u003e\u003e a = 10;\n\u003e\u003e\u003e int f() { int b = 20; a++; b++; return a+b; }\n\u003e\u003e\u003e f() \n32\n\u003e\u003e\u003e f()\n33\n```\n\n### Classes\n\n#### Define and redefine, preserving states\n\n```dart\n\u003e\u003e\u003e class C { int a = 10; int f() =\u003e a * 2; }\n\u003e\u003e\u003e c = C(); print(c.f());\n20\n\u003e\u003e\u003e class C { int a = 1000; int f() =\u003e a * 3; }\n\u003e\u003e\u003e c.f()\n30\n```\n\n\u003csmall\u003eRemark: This follows the Dart hot reload semantics.\u003c/small\u003e\n\n#### Extends and implements\n\n```dart\n\u003e\u003e\u003e class A { int f() =\u003e 10; } class B extends A { int f() =\u003e 20; }\n\u003e\u003e\u003e A().f() + B().f()\n30\n\u003e\u003e\u003e class B implements A { int f() =\u003e 30; }\n\u003e\u003e\u003e A().f() + B().f()\n40\n```\n\n#### Use local variables, fields, and global variables\n\n```dart\n\u003e\u003e\u003e a = 10;\n\u003e\u003e\u003e class C { int b = 20; int f() { int c = 30; a++; b++; c++; return a+b+c; } }\n\u003e\u003e\u003e c = C(); print(c.f()); print(c.f());\n63\n65\n```\n\n### Add libraries as dependency\n\nUse `!dart pub add package_name`, just like what is done in Python (Jupyter/IPython).\n\n```dart\n\u003e\u003e\u003e join('directory', 'file.txt')\n(...error, since have not added that dependency...)\n\u003e\u003e\u003e !dart pub add path\nResolving dependencies...\n\n+ path 1.8.2\n\nChanged 1 dependency!\n\n\u003e\u003e\u003e join('directory', 'file.txt')\n(...error, since have imported it...)\n\u003e\u003e\u003e import 'package:path/path.dart';\n\u003e\u003e\u003e join('directory', 'file.txt')   \ndirectory/file.txt\n```\n\n### Imports\n\n#### Built-in package\n\n```dart\n\u003e\u003e\u003e Random().nextInt(100)\n(some error outputs here, because it is not imported)\n\u003e\u003e\u003e import \"dart:math\";\n\u003e\u003e\u003e Random().nextInt(100)\n9\n```\n\n#### Third party package\n\nNote: If it has not been added to dependency, please follow instructions above and use `!dart pub add path` to add it.\n\n```dart\n\u003e\u003e\u003e join('directory', 'file.txt')\n(...error, since have imported it...)\n\u003e\u003e\u003e import 'package:path/path.dart';\n\u003e\u003e\u003e join('directory', 'file.txt')   \ndirectory/file.txt\n```\n\n### Multiple in one go\n\n```dart\n\u003e\u003e\u003e int g() =\u003e 42; class C { int a = 10; int f() =\u003e a * 2; }\n\u003e\u003e\u003e C().f() + g()\n62\n```\n\n### Multi line if not ended\n\n(The `...`, instead of `\u003e\u003e\u003e`, appears in the two lines, because the package detects it is not finished.)\n\n```dart\n\u003e\u003e\u003e class C {\n...   int a = 10;\n... }\n\u003e\u003e\u003e \n```\n\n### Run commands\n\nUse prefix `!`.\n\n```dart\n\u003e\u003e\u003e !whoami\ntom\n\u003e\u003e\u003e !date\n2022-10-22 ...outputs...\n```\n\n### Execute within environment of existing package\n\n```dart\ninteractive --directory path/to/your/package\n```\n\n## Implementation\n\nGeneral:\n\n* Create a blank package and an isolate as execution workspace\n* Extract imports/classes/functions/etc using analyzer, with replacing when it has the same name, and synthesize a dart file - thus supports rich Dart feature\n* Trigger Dart's hot-reload after the dart file is updated\n* Use analyzer to distinguish expressions/statements/compilation-units and do corresponding transformation\n* The only thing to let Dart VM service to evaluate is `generatedMethod()`, and do not evaluate anything more\n* Adding dependencies is as simple as running standard shell command\n\nAs for \"global\" variables:\n\n* Indeed implemented by a field variable\n* Statements: Make it inside `extension on dynamic { Object? generatedMethod() { ...the statements... } }` to access it seamlessly\n* Functions: Convert functions to extension methods on dynamic to access it seamlessly\n* Classes: Synthesize getters/setters in classes, and delegate to the field variables, whenever there is a potential access to global variable to access it seamlessly\n\nTODO more implementation discussions if people are interested (above is so brief)\n\n## Known Issues\n\n#### Windows non-WSL terminal\n\nBecause of Dart's bug (https://github.com/dart-lang/sdk/issues/48329), the upstream `cli_repl` package does not work well on Windows. The issues vary from terminal to terminal, but generally speaking, backspace doesn't work, we cannot move on the command line with arrows nor Ctrl+B/F, and no command history with arrows or ^P/^N either.\n\nSince `dart_interactive` depends on it, it is suggested to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) or *nix.\n\n#### Command history\n\nCommand history is not yet saved between sessions. However, this is implementable, and feel free to create an issue or PR.\n\n#### Some errors will lead to `Hot reload failed`\n\nCurrently, some user mistakes will produce `Hot reload failed` error instead of the actual error. It will break the next command or the whole REPL. If you can't evaluate something simple as \"1\" after two tries, you can restart quickly with Ctrl/Cmd+D, Up Arrow and Enter in most terminals.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```\n\u003e\u003e\u003e 1\n1\n\u003e\u003e\u003e print() // \u003c----- oops, argument is not optional\n[WARNING 2024-03-13 01:50:18.419137] Error: Hot reload failed, maybe because code has syntax error?\n\u003e\u003e\u003e 1\n[WARNING 2024-03-13 01:50:20.464239] Error: Hot reload failed, maybe because code has syntax error?\n\u003e\u003e\u003e 1\n[WARNING 2024-03-13 01:50:20.464239] Error: Hot reload failed, maybe because code has syntax error?\n```\n\n\u003c/details\u003e\n\n## ✨ Contributors\n\n\u003c!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --\u003e\n[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-)\n\u003c!-- ALL-CONTRIBUTORS-BADGE:END --\u003e\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/fzyzcjy\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/5236035?v=4?s=100\" width=\"100px;\" alt=\"fzyzcjy\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003efzyzcjy\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=fzyzcjy\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=fzyzcjy\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#ideas-fzyzcjy\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://mrale.ph\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/131846?v=4?s=100\" width=\"100px;\" alt=\"Vyacheslav Egorov\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVyacheslav Egorov\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-mraleph\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://blackhc.net\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/729312?v=4?s=100\" width=\"100px;\" alt=\"Andreas Kirsch\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAndreas Kirsch\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-BlackHC\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://manichord.com/blog\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/71999?v=4?s=100\" width=\"100px;\" alt=\"Maksim Lin\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMaksim Lin\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-maks\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Keithcat1\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/47483928?v=4?s=100\" width=\"100px;\" alt=\"Keithcat1\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKeithcat1\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=Keithcat1\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://vegardit.com\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/426959?v=4?s=100\" width=\"100px;\" alt=\"Sebastian Thomschke\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSebastian Thomschke\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=sebthom\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/arcanemachine\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/3351767?v=4?s=100\" width=\"100px;\" alt=\"arcanemachine\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003earcanemachine\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=arcanemachine\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/davidmartos96\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/22084723?v=4?s=100\" width=\"100px;\" alt=\"David Martos\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDavid Martos\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=davidmartos96\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/Chematronix\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/6773039?v=4?s=100\" width=\"100px;\" alt=\"Chematronix\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChematronix\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=Chematronix\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/jheld\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/828813?v=4?s=100\" width=\"100px;\" alt=\"Jason Held\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJason Held\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/fzyzcjy/dart_interactive/commits?author=jheld\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nMore specifically, thanks for all these contributions:\n\n* [@mraleph](https://github.com/mraleph) (Dart team): [Pointing](https://github.com/dart-lang/sdk/issues/39965#issuecomment-854854283) out Dart exposes hot reload and expression evaluation.\n* [@BlackHC](https://github.com/BlackHC): Prior [proof of concept](https://github.com/BlackHC/dart_repl) and [article](https://medium.com/dartlang/evolving-dart-repl-poc-233440a35e1f) on the problem of creating a REPL.\n* [@maks](https://github.com/maks): Prior [prototype](https://github.com/maks/dart_repl) as [an update-to-Dart-2](https://github.com/dart-lang/sdk/issues/39965#issuecomment-1287953021) of @BlackHC's prototype.\n* [@davidmartos96](https://github.com/davidmartos96): Fix package import bug. Fix windows hot reload bug. Update analyze.\n* [@Keithcat1](https://github.com/Keithcat1): Partially fix printing object.\n* [@sebthom](https://github.com/sebthom): Use unused TCP port.\n* [@arcanemachine](https://github.com/arcanemachine): Pin dependency.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffzyzcjy%2Fdart_interactive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffzyzcjy%2Fdart_interactive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffzyzcjy%2Fdart_interactive/lists"}