Giter Club home page Giter Club logo

dart_interactive's Introduction

Flutter Package CI

A 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 highest-voted request). So here it comes!

๐Ÿš€ Features

A full-featured REPL (interactive shell), with:

  • Use any third-party package freely
  • Auto hot-reload code anywhere, with state preserved
  • Supports full grammar in REPL
  • Play with existing code side-by-side

๐Ÿ“š Demo

Demo 1: Demonstrate features

  1. Use 3rd party package
>>> !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. Auto hot-reload
>>> import 'a.dart';
>>> myFunc()
hello, tom
// ... change content of `a.dart` ...
>>> myFunc()
hello, alex
  1. Support full grammar
>>> 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

Demo 2: Sample workflow

Surely, you do not have to use it like this. It is just a workflow that I personally feel comfortable when working with IPython/Juypter.

Suppose we have my_app.dart with some code, probably edited inside an IDE:

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

Play with it a bit:

$ 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!

Then we realize something wrong and want to change it:

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

Continue playing with it (auto hot reloaded, and state preserved):

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

We can also use all dependencies in the package as well, since the REPL code is just like a normal code file in this package.

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

๐ŸŽผ Getting started

Install (just standard procedure of installing global dart packages):

dart pub global activate interactive

Use (just a normal binary):

interactive

And play with it :)

Detailed functionality list

Expressions

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

Statements

>>> print(a)
Hello

(All methods, not only print)

Functions

Define and redefine

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

Use local and global variables

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

Classes

Define and redefine, preserving states

>>> 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

Remark: This follows the Dart hot reload semantics.

Extends and implements

>>> 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

Use local variables, fields, and global variables

>>> 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

Add libraries as dependency

Use !dart pub add package_name, just like what is done in 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

Imports

Built-in package

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

Third party package

Note: If it has not been added to dependency, please follow instructions above and use !dart pub add path to add it.

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

Multiple in one go

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

Multi line if not ended

(The ..., instead of >>>, appears in the two lines, because the package detects it is not finished.)

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

Run commands

Use prefix !.

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

Execute within environment of existing package

interactive --directory path/to/your/package

Implementation

General:

  • Create a blank package and an isolate as execution workspace
  • 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
  • Trigger Dart's hot-reload after the dart file is updated
  • Use analyzer to distinguish expressions/statements/compilation-units and do corresponding transformation
  • The only thing to let Dart VM service to evaluate is generatedMethod(), and do not evaluate anything more
  • Adding dependencies is as simple as running standard shell command

As for "global" variables:

  • Indeed implemented by a field variable
  • Statements: Make it inside extension on dynamic { Object? generatedMethod() { ...the statements... } } to access it seamlessly
  • Functions: Convert functions to extension methods on dynamic to access it seamlessly
  • 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

TODO more implementation discussions if people are interested (above is so brief)

โœจ Contributors

All Contributors

Thanks goes to these wonderful people (emoji key):

fzyzcjy
fzyzcjy

๐Ÿ’ป ๐Ÿ“– ๐Ÿค”
Vyacheslav Egorov
Vyacheslav Egorov

๐Ÿค”
Andreas Kirsch
Andreas Kirsch

๐Ÿค”
Maksim Lin
Maksim Lin

๐Ÿค”
Keithcat1
Keithcat1

๐Ÿ’ป
Sebastian Thomschke
Sebastian Thomschke

๐Ÿ’ป
arcanemachine
arcanemachine

๐Ÿ’ป
David Martos
David Martos

๐Ÿ’ป

More specifically, thanks for all these contributions:

dart_interactive's People

Contributors

allcontributors[bot] avatar arcanemachine avatar davidmartos96 avatar dependabot[bot] avatar fzyzcjy avatar keithcat1 avatar sebthom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dart_interactive's Issues

allow use as a globally installable executable

One issue I ran into with dart_repl was that it's difficult to accommodate usage with dart pub global activate, especially in relation to scratchpad file paths. I suspect this package will run into the same issue when you get to the point of implementing a executable in your pubspec.

FEATURE/DOC: Is this possible to host in integration test ?

Is your feature request related to a problem? Please describe.
The integration test cycle iteration is very long. The first thing I wanted to reach for when we started was a REPL.
At this point we have grok'ed enough of how integration tests work and have most things built out.

Describe the solution you'd like
Explanation of how to host a REPL in dart/flutter integration test so can interactively poke at tree, perform pump and tap.

Impossible to activate: Failed to build

Describe the bug
I tried running the command

dart pub global activate interactive

I got the following result:

 dart pub global activate interactive
Resolving dependencies...
+ _fe_analyzer_shared 51.0.0
+ analyzer 5.3.1
+ args 2.3.1
+ async 2.10.0
+ cli_repl 0.2.3
+ collection 1.17.0
+ convert 3.1.1
+ crypto 3.0.2
+ file 6.1.4
+ glob 2.1.1
+ interactive 1.2.0
+ js 0.6.5
+ logging 1.1.0
+ meta 1.8.0
+ package_config 2.1.0
+ path 1.8.3
+ pub_semver 2.1.3
+ source_span 1.9.1
+ string_scanner 1.2.0
+ term_glyph 1.2.1
+ typed_data 1.3.1
+ vm_service 9.4.0
+ watcher 1.0.2
+ yaml 3.1.1
Downloading interactive 1.2.0...
Downloading cli_repl 0.2.3...
Downloading path 1.8.3...
Downloading analyzer 5.3.1...
Downloading _fe_analyzer_shared 51.0.0...
Downloading string_scanner 1.2.0...
Downloading pub_semver 2.1.3...
Downloading glob 2.1.1...
Downloading convert 3.1.1...
Downloading async 2.10.0...
Building package executables...
Failed to build interactive:interactive:
AppData/Local/Pub/Cache/hosted/pub.dartlang.org/interactive-1.2.0/lib/src/parser.dart:41:66: Error: The argument type 'CompilationUnitMemberImpl' can't be assigned to the parameter type 'ClassDeclaration'.
 - 'CompilationUnitMemberImpl' is from 'package:analyzer/src/dart/ast/ast.dart' ('AppData/Local/Pub/Cache/hosted/pub.dartlang.org/analyzer-5.3.1/lib/src/dart/ast/ast.dart').
 - 'ClassDeclaration' is from 'package:analyzer/dart/ast/ast.dart' ('AppData/Local/Pub/Cache/hosted/pub.dartlang.org/analyzer-5.3.1/lib/dart/ast/ast.dart').
                _PotentialAccessorParser().parseClassDeclaration(declaration),
                                                                 ^

I also tried to activate the 1.1.0 and got the same result.

Looking at the package on pub.dev, it fails the static analysis with the same error I get:
https://pub.dev/packages/interactive/score
image

environment:

dart --version
Dart SDK version: 2.18.5 (stable) (Tue Nov 22 15:47:29 2022 +0000) on "windows_x64"

Cannot run two shells at the same time

Describe the bug
Trying to run two shells at the same time results in Could not start the VM service: localhost:8181 is already in use.

To Reproduce
Run the interactive command in two separate terminal windows.

Expected behavior
Both interactive shells work.

Additional context
As a solution, the shell could either

  • automatically assigns a free port, or
  • a port for --enable-vm-service=<[<port>[/<bind-address>]]> could be provided using a command line option.

`OS Error: The filename, directory name, or volume label syntax is incorrect` on windows, unrelated to package core, but compatibility issues about folder paths

I tried the guidance on readme (Inside packages/interactive, execute dart run --enable-vm-service bin/interactive.dart.), however, it not works for me.

D:\code\dart>git clone [email protected]:fzyzcjy/dart_interactive.git
Cloning into 'dart_interactive'...
remote: Enumerating objects: 820, done.
remote: Counting objects: 100% (368/368), done.
remote: Compressing objects: 100% (205/205), done.
remote: Total 820 (delta 141), reused 344 (delta 120), pack-reused 452
Receiving objects: 100% (820/820), 141.04 KiB | 300.00 KiB/s, done.
Resolving deltas: 100% (318/318), done.

D:\code\dart>cd dart_interactive\

D:\code\dart\dart_interactive>cd packages\interactive\

D:\code\dart\dart_interactive\packages\interactive>
D:\code\dart\dart_interactive\packages\interactive>dart run --enable-vm-service bin\interactive.dart
The Dart VM service is listening on http://127.0.0.1:8181/QttPLrcLj-Y=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/QttPLrcLj-Y=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2FQttPLrcLj-Y%3D%2Fws
Error: Couldn't resolve the package 'interactive' in 'package:interactive/src/main.dart'.
bin/interactive.dart:1:8: Error: Not found: 'package:interactive/src/main.dart'
import 'package:interactive/src/main.dart' as lib_main;
       ^
bin/interactive.dart:3:42: Error: Method not found: 'main'.
void main(List<String> args) => lib_main.main(args);
                                         ^^^^

D:\code\dart\dart_interactive\packages\interactive>dart pub get
Resolving dependencies...
+ _fe_analyzer_shared 49.0.0
+ analyzer 5.1.0
+ args 2.3.1
+ async 2.9.0
+ boolean_selector 2.1.0
+ cli_repl 0.2.3
+ collection 1.17.0
+ convert 3.0.2
+ coverage 1.6.1
+ crypto 3.0.2
+ file 6.1.4
+ frontend_server_client 3.1.0
+ glob 2.1.0
+ http_multi_server 3.2.1
+ http_parser 4.0.2
+ io 1.0.3
+ js 0.6.5
+ lint 1.10.0
+ logging 1.1.0
+ matcher 0.12.12
+ meta 1.8.0
+ mime 1.0.2
+ node_preamble 2.0.1
+ package_config 2.1.0
+ path 1.8.2
+ pool 1.5.1
+ pub_semver 2.1.1
+ shelf 1.4.0
+ shelf_packages_handler 3.0.1
+ shelf_static 1.1.1
+ shelf_web_socket 1.0.2
+ source_map_stack_trace 2.1.0
+ source_maps 0.10.11
+ source_span 1.9.1
+ stack_trace 1.11.0
+ stream_channel 2.1.1
+ string_scanner 1.1.1
+ term_glyph 1.2.1
+ test 1.21.6
+ test_api 0.4.14
+ test_core 0.4.18
+ typed_data 1.3.1
+ vm_service 9.4.0
+ watcher 1.0.2
+ web_socket_channel 2.2.0
+ webkit_inspection_protocol 1.2.0
+ yaml 3.1.1
Downloading test 1.21.6...
Downloading lint 1.10.0...
Downloading logging 1.1.0...
Downloading collection 1.17.0...
Downloading cli_repl 0.2.3...
Downloading test_core 0.4.18...
Downloading test_api 0.4.14...
Downloading analyzer 5.1.0...
Downloading _fe_analyzer_shared 49.0.0...
Downloading stream_channel 2.1.1...
Downloading stack_trace 1.11.0...
Downloading js 0.6.5...
Downloading source_maps 0.10.11...
Downloading watcher 1.0.2...
Downloading http_parser 4.0.2...
Downloading frontend_server_client 3.1.0...
Downloading shelf 1.4.0...
Downloading coverage 1.6.1...
Changed 47 dependencies!

D:\code\dart\dart_interactive\packages\interactive>dart run --enable-vm-service bin/interactive.dart
The Dart VM service is listening on http://127.0.0.1:8181/56H0eTNIIls=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/56H0eTNIIls=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2F56H0eTNIIls%3D%2Fws
Unhandled exception:
FileSystemException: Exists failed, path = 'C:\Users\LIUD~1.H\AppData\Local\Temp/dart_interactive_workspace_2022-10-22T22:38:56.335037' (OS Error: The filename, directory name, or volume label syntax is incorrect.
, errno = 123)
#0      _Directory.existsSync (dart:io/directory_impl.dart:94:7)
#1      _Directory.createSync (dart:io/directory_impl.dart:126:11)
#2      WorkspaceFileTree._prepare (package:interactive/src/workspace_file_tree.dart:31:20)
#3      WorkspaceFileTree.create (package:interactive/src/workspace_file_tree.dart:14:11)
<asynchronous suspension>
#4      run (package:interactive/src/main.dart:30:29)
<asynchronous suspension>

D:\code\dart\dart_interactive\packages\interactive>
D:\code\dart\dart_interactive\packages\interactive>dart --version
Dart SDK version: 2.18.2 (stable) (Tue Sep 27 13:24:11 2022 +0200) on "windows_x64"

D:\code\dart\dart_interactive\packages\interactive>ver

Microsoft Windows [Version 10.0.22621.675]

D:\code\dart\dart_interactive\packages\interactive>

Calling undefined getter on self defined class instance yields global variable

Describe the bug
When calling a non existing getter with the same name as a global variable on another global variable that holds an instance of a self defined class, the value of the first global variable is returned, instead of throwing a NoSuchMethodError.

To Reproduce

  1. Enter the following lines in order:
>>> a=1
1
>>> class Foo {}
>>> f=Foo()
>>> f.a
1

Expected behavior
A NoSuchMethodError is thrown, just like if you were to define f='a string' instead of f=Foo() for example.

Actual Behavior
The value of the global variable a is returned even though an instance of the class Foo clearly does not have a getter called a.

Print all types of variables without needing `print` statement

Hi @fzyzcjy!

Thank you for working on this project.


Is your feature request related to a problem? Please describe.

Given the following sequence:

>>> DateTime.now();
>>> a = DateTime.now();
>>> a
>>> 
>>> print(a)
2022-10-24 16:29:24.020502

I expected the line after >>> a to have the same result as print(a)

repl package name for pub

I'm happy to see you are keen to progress this work on a Dart REPL, you've made excellent progress so far and I currently don't have time to keep working on it, so if you would like, I'd happy to move the repl package name on Pub over to your publisher account so that you can make use of it for this package and I can deprecate and archive my repo.

Provide a convenient way to exit

Is your feature request related to a problem? Please describe.
It would be nice to have a convenient built-in way to exit the repl. Entering import 'dart:io'; and then exit(0) is not really that convenient. (Also it seems to do some weird stuff with the shell, for example i can't see any further input after the program terminates but that is likely a different issue.) The shourtcuts Ctrl+C (send sig TERM) or Ctrl+D (EOF) work but other repls have things like exit() aswell, so I think it would be nice to have too.

Describe the solution you'd like
For example global getter Never get quit => exit(0); could be defined in a file that is imported by default that in turn imports dart:io.

Describe alternatives you've considered
Or it could be a function Never quit() => exit(0).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.