Send queries unobtrusively to the terminal

As per another thread, I’m trying to write an extension for Visual Studio Code which makes use of the Integrated Terminal feature of the IDE to provide the user with a running prolog instance.

I thought about using the wasm library initially for ease of use and interoperation, and for the ability to issue queries unknowingly to the user, but I faced two main issues:

  • I would have had to reproduce most of the ordinary behavior of the top level from scratch;
  • the swipl version provided by the wasm library is still not on par with the normal one (as reminded me by Jan).

So I got back to using the ordinary swipl terminal.
For user interaction this is perfect, as everything is as the developer expects it to be, but now I cannot issue “hidden queries” to the prolog instance anymore (these queries are used by the extension to get up-to-date information about loaded prolog code).

Regarding communication of such queries (i.e. retrieving their result), I’m currently writing the results’ bindings (either through message_hook/3 or expand_answer/2) to a temporary json file, which is subsequently read by the extension.
I didn’t really like the idea of communicating through a file initially, but in the end it seemed to me the cleanest and most platform-independent way to do it. I knew about the MQI library, but I discarded that because of overhead complexity and because I would like to keep the swipl instance as pristine as possible regarding the developer’s loaded/run code (i.e. no threads created apart from those specifically created by the developer etc).

There remained the issue of sending these queries unknowingly to the user, and this basically translates to:

  1. not showing the query in the terminal or - better said - removing it as soon as it’s read (possibly staying on the same line, not generating another prompt);
  2. not saving the query as a history entry (preventing it from showing up with the up-key feature), and
  3. not updating the history counter (the number shown before the prompt).

By studying the source code (namely toplevel.pl and history.pl) and by testing with some system predicate redefinitions I could achieve some of the objectives above, but obviously this is not ideal as the source code is not fixed in time and I would have to warn the user that some lines of code were added or altered for core modules of the swipl instance loaded in the VSC terminal.

While reading the code, I found kind of a minor feature that seems to (partially) suit my needs.
This is the '$silent'(Goal) interception in history.pl (made for the GNU Emacs, as said in the predicate’s comment), which on finding such a goal executes Goal once and then proceeds. This possibly was to cope with a similar need as mine (issuing utility queries for an IDE).
'$silent'/1 seems to solve automatically requirements 2) and 3), but I’m still faced with 1), i.e. I don’t want the query to show up in the terminal, or better said I wish that once one of such queries is issued (and read) its line gets removed (up to the prompt) as if the query was never written.
I don’t even know if that’s possible, as the final predicate used for reading queries seems to be read_term_from_atom/3 and the logic behind “erasing” the read input is or should be there I guess.

So, I don’t know whether this could even be made into a “feature request”, i.e. having certain queries read by swipl being “swallowed up”, not updating the terminal in any way (history, prompt and such).
I don’t think this requirement would be that unique (maybe the very existence of '$silent'/1 is proof of that), and I’m sure its implementation would not interfere with anything of importance.

Maybe something like silent_query_hook/2, so that one might say:

silent_query_hook(foo(Goal), [bindings(Bindings)]) :-
  call(Goal),
  write_bindings_to_json(Bindings). % This is from my use case

“Silent queries” would not update query history and would be removed from the current line once read.

As it is now, with '$silent'/1:

1 ?- '$silent'(A=2).
1 ?-                   % Still getting a new line; up key showing the query

As I would like to have it:

1 ?- foo(A=2). % Before
---
1 ?- % After (same line, up-key not showing the query); meanwhile, stuff defined by the user executed

Silently adding something to the input mostly depends on what exactly this terminal is. AFAIK, in Emacs we have process interaction (shell) mode. That means that things you type there are sent to the process, but the lisp code can also bypass this as send some string directly to the process.

Another common way to solve this is by firing a Prolog thread and make a back channel to talk to Prolog. The main issue is setting up communication. One option is using sockets. That is typically fairly easy, but comes with some security issues. On POSIX systems you can use Unix domain sockets. Alternatively you can probably create a pipe with some effort.

Using a thread has an advantage and disadvantage. The main advantage is that while some user query is running in the terminal you can still communicate with Prolog. The disadvantage is that some actions are thread-specific and may (thus) not have the desired effect. To some extend you can use call_in_thread/2 from the controller thread to the main thread, running the goal as an interrupt.

There is also an extension pdt_console. I forgot the details. It deals with the Prolog console in the PDT Eclipse IDE plugin. See source :slight_smile:

Plenty of options … Some might be platform specific though.

Thanks Jan.

Well, as I said perhaps I would have avoided threads altogether both because I would like to keep the state of the prolog instance as “neutral” as possible regarding the developer (what code was loaded and which actions were taken), and because (but actually I might say “and especially because” :wink:) I’m not yet competent enough to even start trying some of those options (e.g. those involving intimate Unix knowledge, which I don’t have). Plus what you said about thread-specific actions (although I don’t know if that would impact the queries I would issue from the extension).

With the pattern outlined above I was also close to getting to the desired results with good performance and easy code, and that doesn’t help I guess.

PDT is something I want to check about more deeply, yes, as basically the PDT plugin is the inspiration for this extension (“historically” I’ve always done my prolog stuff within Eclipse), or if not an inspiration, at least I’m planning to get the VSC extension to behave as the PDT plugin does regarding some core aspects (e.g. the automatic reloading of files on save, F9 for manual reconsulting etc).

I think PDT also uses threads (could be wrong). So does the new Emacs sweep mode (which uses a socket for the communication). Same holds for the built-in tools that run the GUI tools in a separate thread so you can edit, debug, inspect, … the code while it is running. … Even modify :slight_smile:

Threads are everywhere in SWI-Prolog :slight_smile:

…and so let there be threads. :slight_smile:

(Yes, earlier I checked the PDT repo and it’s using threads.)

I’ll take this as a chance to do some programming I’ve not done before (for one reason or another).

Thanks again, I hope to get where I want to get as I’m envisioning a good and useful VSC extension for swipl.

1 Like

Another common way to solve this is by firing a Prolog thread and make a back channel to talk to Prolog. The main issue is setting up communication. One option is using sockets. That is typically fairly easy, but comes with some security issues. On POSIX systems you can use Unix domain sockets.

Hi Jan, I came across this (Unix sockets apparently available on modern versions of Windows).

Could it be relevant to make Unix-sockets predicates and options more cross-platform in future releases (and update the documentation)?

Good news. All seems a bit cutting edge though. We will get there automatically if the changes come to the MinGW compiler suite and cmake finds support for AF_UNIX. We just have to wait …

1 Like