Compare commits

...

2 commits

Author SHA1 Message Date
525b9c68cd Handle idris errors 2024-12-30 14:14:19 +00:00
2784a7d80a Docs 2024-12-30 14:10:05 +00:00
3 changed files with 30 additions and 9 deletions

View file

@ -9,7 +9,7 @@ my $ide = IUtils::IDEMode.new();
my @res = $ide.version; my @res = $ide.version;
say @res[0][1][1][1][0]; say @res[0][1][1][1][0];
@res = $ide.load-file: '.tmp/test.idr'; @res = $ide.load-file: '.tmp/test.nope';
say @res.raku; say @res.raku;
@res = $ide.interpret: ':exec works >>= print'; @res = $ide.interpret: ':exec works >>= print';

View file

@ -1,3 +1,4 @@
#| Utilities for interacting with idris and associated tooling
unit module IUtils; unit module IUtils;
need IUtils::IDEMode; need IUtils::IDEMode;

View file

@ -1,10 +1,16 @@
#| Interaction wtih Idris 2's IDE Mode
unit class IUtils::IDEMode; unit class IUtils::IDEMode;
#| The underlying idris process
has $!process; has $!process;
#| The port number we are connected via
has $!port; has $!port;
#| The socket we are connected via
has $!socket; has $!socket;
#| The next request id
has $!request-id = 0; has $!request-id = 0;
#| Grammar for S-Expressions
grammar SExp { grammar SExp {
rule TOP { <sexp> } rule TOP { <sexp> }
@ -22,6 +28,7 @@ grammar SExp {
} }
} }
#| Convert a parsed S-Expression into a list of lists
class SExp::Actions { class SExp::Actions {
method TOP($/) { make $<sexp>.made } method TOP($/) { make $<sexp>.made }
@ -36,11 +43,7 @@ class SExp::Actions {
} }
method str-content($/) { method str-content($/) {
if $/.Str && $0 {
make $/.Str.subst(/\\(.)/, {$0}, :g) make $/.Str.subst(/\\(.)/, {$0}, :g)
} else {
make ''
}
} }
} }
@ -74,6 +77,7 @@ submethod TWEAK {
say "IDE Protocol Version: $major.$minor"; say "IDE Protocol Version: $major.$minor";
} }
#| Capture and parse the initial protocol version message
method process-protocol-version() { method process-protocol-version() {
my @resp = self.read-sexp(); my @resp = self.read-sexp();
if @resp[0] eq ':protocol-version' { if @resp[0] eq ':protocol-version' {
@ -82,13 +86,18 @@ method process-protocol-version() {
die "Expected protocol version, got: ", @resp; die "Expected protocol version, got: ", @resp;
} }
#| Read one sexp from the IDE server
method read-sexp() { method read-sexp() {
my $len = $!socket.read(6).decode('utf8'); my $len = $!socket.read(6).decode('utf8');
my $msg = $!socket.read(:bin, $len.parse-base(16)).decode('utf8'); my $msg = $!socket.read(:bin, $len.parse-base(16)).decode('utf8');
# say "Got: ", $len, $msg;
SExp.parse($msg, actions => SExp::Actions).made SExp.parse($msg, actions => SExp::Actions).made
} }
#| Send a command to the IDE server, and collect all the responses to that
#| command
#|
#| For convinence, automatically symbolizes the first argument, and will wrap
#| the command in parens if there is more than one argument
method send-command(*@cmd) { method send-command(*@cmd) {
my $id = ++$!request-id; my $id = ++$!request-id;
my $cmd-str; my $cmd-str;
@ -98,7 +107,6 @@ method send-command(*@cmd) {
$cmd-str = "(" ~ (":" ~ @cmd[0]) ~ " $id)"; $cmd-str = "(" ~ (":" ~ @cmd[0]) ~ " $id)";
} }
my $len = sprintf("%06x", $cmd-str.chars); my $len = sprintf("%06x", $cmd-str.chars);
# say "Sending: ", $len, $cmd-str;
$!socket.print($len ~ $cmd-str); $!socket.print($len ~ $cmd-str);
my @responses; my @responses;
@ -106,12 +114,17 @@ method send-command(*@cmd) {
my $resp = self.read-sexp(); my $resp = self.read-sexp();
@responses.push($resp); @responses.push($resp);
if $resp[1][0] eq ':error' && $resp[2] == $id {
die "Idris error: ", $resp[1][1];
}
if $resp[0] eq ':return' && $resp[2] == $id { if $resp[0] eq ':return' && $resp[2] == $id {
return @responses; return @responses;
} }
} }
} }
#| Wrapper for :load-file command
method load-file($filename, $line-number?){ method load-file($filename, $line-number?){
if $line-number { if $line-number {
self.send-command('load-file', "\"$filename\"", $line-number.Str) self.send-command('load-file', "\"$filename\"", $line-number.Str)
@ -120,28 +133,35 @@ method load-file($filename, $line-number?){
} }
} }
#| Wrapper for :cd command
method cd($filepath) { method cd($filepath) {
self.send-command('cd', "\"$filepath\"") self.send-command('cd', "\"$filepath\"")
} }
#| Wrapper for :interpret command
method interpret($cmd) { method interpret($cmd) {
self.send-command('interpret', "\"$cmd\"") self.send-command('interpret', "\"$cmd\"")
} }
#| Wrapper for :type-of command
method type-of($item) { method type-of($item) {
self.send-command('type-of', "\"$item\"") self.send-command('type-of', "\"$item\"")
} }
#| Wrapper for :docs-for command
method docs-for($item) { method docs-for($item) {
self.send-command('docs-for', "\"$item\"") self.send-command('docs-for', "\"$item\"")
} }
#| Wrapper for :browse-namespace command
#|
#| Will import $namespace before browsing it to ensure we get results
method browse-namespace($namespace) { method browse-namespace($namespace) {
# Import the namespace first to make sure this works properly
self.interpret: ":import $namespace"; self.interpret: ":import $namespace";
self.send-command('browse-namespace', "\"$namespace\"") self.send-command('browse-namespace', "\"$namespace\"")
} }
#| Wrapper for :version command
method version() { method version() {
self.send-command('version') self.send-command('version')
} }