Basic IDE mode interface
This commit is contained in:
parent
c34c38fc03
commit
72393480e3
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,4 +4,5 @@
|
|||
tmp/
|
||||
.tmp/
|
||||
.direnv/
|
||||
**/build/
|
||||
**/.precomp/
|
||||
|
|
17
bin/iutils
17
bin/iutils
|
@ -1,3 +1,20 @@
|
|||
#!/usr/bin/env raku
|
||||
use v6.d;
|
||||
use IUtils::IDEMode;
|
||||
|
||||
my $ide = IUtils::IDEMode::IDEMode.new();
|
||||
# my @res = $ide.browse-namespace: 'Data.List';
|
||||
# say @res;
|
||||
|
||||
my @res = $ide.version;
|
||||
say @res[0][1][1][1][0];
|
||||
|
||||
@res = $ide.load-file: '.tmp/test.idr';
|
||||
say @res.raku;
|
||||
|
||||
@res = $ide.interpret: ':exec works >>= print';
|
||||
say @res.raku;
|
||||
say $ide.read-sexp();
|
||||
|
||||
@res = $ide.interpret: ':exec fails';
|
||||
say @res.raku;
|
||||
|
|
|
@ -1 +1,146 @@
|
|||
unit module IUtils::IDEMode;
|
||||
|
||||
grammar SExp {
|
||||
rule TOP { <sexp> }
|
||||
|
||||
proto rule sexp {*}
|
||||
rule sexp:sym<list> { '(' <sexp>* ')' }
|
||||
rule sexp:sym<nil> { 'nil' }
|
||||
rule sexp:sym<num> { \d+ }
|
||||
rule sexp:sym<symbol> { ':' <[\w\-]>+ }
|
||||
rule sexp:sym<string> { '"' <str-content>* '"' }
|
||||
|
||||
token str-content {
|
||||
| <-[\"\\]>+ # Any char except " or \
|
||||
| '\\"' # Escaped quote
|
||||
| '\\' # Escaped backslash
|
||||
}
|
||||
}
|
||||
|
||||
class SExp::Actions {
|
||||
method TOP($/) { make $<sexp>.made }
|
||||
|
||||
method sexp:sym<list>($/) {
|
||||
make $<sexp>».made.List
|
||||
}
|
||||
method sexp:sym<nil>($/) { make List }
|
||||
method sexp:sym<num>($/) { make $/.Int }
|
||||
method sexp:sym<symbol>($/) { make $/.Str.trim }
|
||||
method sexp:sym<string>($/) {
|
||||
make $<str-content>».made.join
|
||||
}
|
||||
|
||||
method str-content($/) {
|
||||
make $/.Str.subst(/\\(.)/, {$0}, :g)
|
||||
}
|
||||
}
|
||||
|
||||
class IDEMode {
|
||||
has $!process;
|
||||
has $!port;
|
||||
has $!socket;
|
||||
has $!request-id = 0;
|
||||
|
||||
submethod TWEAK {
|
||||
# Start idris2 in IDE mode
|
||||
my $ret = Promise.new;
|
||||
$!process = Proc::Async.new('idris2', '--ide-mode-socket');
|
||||
start {
|
||||
react {
|
||||
whenever $!process.stdout.lines {
|
||||
$!port = $_.Int;
|
||||
$!socket = IO::Socket::INET.new(:host<localhost>, :port($!port));
|
||||
$ret.keep;
|
||||
}
|
||||
whenever $!process.start {
|
||||
say 'Idris 2 exited, exitcode=', .exitcode, ' signal=', .signal;
|
||||
done;
|
||||
}
|
||||
whenever $!process.ready {
|
||||
say 'Idris 2 online, PID=', $_;
|
||||
}
|
||||
}
|
||||
}
|
||||
await $ret;
|
||||
my ($major, $minor) = self.process-protocol-version;
|
||||
my @ret = self.version;
|
||||
my @version = @ret[0][1][1][0];
|
||||
my $commit = @ret[0][1][1][1][0];
|
||||
say "Idris 2 Version: ", @version[0], ".", @version[1], ".", @version[2],
|
||||
" (", $commit, ")";
|
||||
say "IDE Protocol Version: $major.$minor";
|
||||
}
|
||||
|
||||
method process-protocol-version() {
|
||||
my @resp = self.read-sexp();
|
||||
if @resp[0] eq ':protocol-version' {
|
||||
return (@resp[1], @resp[2]);
|
||||
}
|
||||
die "Expected protocol version, got: ", @resp;
|
||||
}
|
||||
|
||||
method read-sexp() {
|
||||
my $len = $!socket.read(6).decode('utf8');
|
||||
my $msg = $!socket.read(:bin, $len.parse-base(16)).decode('utf8');
|
||||
# say "Got: ", $len, $msg;
|
||||
SExp.parse($msg, actions => SExp::Actions).made
|
||||
}
|
||||
|
||||
method send-command(*@cmd) {
|
||||
my $id = ++$!request-id;
|
||||
my $cmd-str;
|
||||
if @cmd.elems > 1 {
|
||||
$cmd-str = "((" ~ (":" ~ @cmd[0]) ~ " " ~ @cmd[1..*].join(" ") ~ ") $id)";
|
||||
} else {
|
||||
$cmd-str = "(" ~ (":" ~ @cmd[0]) ~ " $id)";
|
||||
}
|
||||
my $len = sprintf("%06x", $cmd-str.chars);
|
||||
# say "Sending: ", $len, $cmd-str;
|
||||
$!socket.print($len ~ $cmd-str);
|
||||
|
||||
my @responses;
|
||||
loop {
|
||||
my $resp = self.read-sexp();
|
||||
@responses.push($resp);
|
||||
|
||||
if $resp[0] eq ':return' && $resp[2] == $id {
|
||||
return @responses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method load-file($filename, $line-number?){
|
||||
if $line-number {
|
||||
self.send-command('load-file', "\"$filename\"", $line-number.Str)
|
||||
} else {
|
||||
self.send-command('load-file', "\"$filename\"")
|
||||
}
|
||||
}
|
||||
|
||||
method cd($filepath) {
|
||||
self.send-command('cd', "\"$filepath\"")
|
||||
}
|
||||
|
||||
method interpret($cmd) {
|
||||
self.send-command('interpret', "\"$cmd\"")
|
||||
}
|
||||
|
||||
method type-of($item) {
|
||||
self.send-command('type-of', "\"$item\"")
|
||||
}
|
||||
|
||||
method docs-for($item) {
|
||||
self.send-command('docs-for', "\"$item\"")
|
||||
}
|
||||
|
||||
method browse-namespace($namespace) {
|
||||
# Import the namespace first to make sure this works properly
|
||||
self.interpret: ":import $namespace";
|
||||
self.send-command('browse-namespace', "\"$namespace\"")
|
||||
}
|
||||
|
||||
method version() {
|
||||
self.send-command('version')
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue