Nick Sieger writes: > > From: Nascif Abousalh-Neto [mailto:[EMAIL PROTECTED]] > > Sent: Monday, December 09, 2002 11:46 AM > > To: Nick Sieger; Galen Boyer; [EMAIL PROTECTED] > > Cc: Graham Bennett > > Subject: Java -> elisp communication (was RE: BanInfo wizard anyone?) > >
[snip] > > 3) Both my implementation and Graham's use gnuclient to > > implement the Java -> elisp side of the equation (elisp -> > > Java using the BeanShell support in JDE). The reason being > > that Java tools will need (and that is true for Transmogrify) > > to make asynchronous calls to the elisp side. gnuclient is not necessary to do this. My interface between the MATLAB debugger and Emacs, implemented in Java (one of MATLAB's extension languages), uses threads and sockets to handle asynchronous communications (i.e., debugger events) between the debugger and Emacs. At startup, the MATLAB-Emacs interface creates an Emacs-Matlab communictions socket and starts a "transciever" thread to listen for input on the socket. The interface then starts Emacs, using a command-line switch that starts up the Emacs side of the MATLAB-Emacs interface. The Emacs side of the interface creates a process that listens for input from the socket created by the MATLAB side. The input is always in the form of a Lisp form that the Emacs "transceiver" evaluates. The MATLAB side of the interface listens for debugger events (e.g., a breakpoint hit) and reformats them as Lisp function evaluations (e.g., (matlab-eei-breakpoint-hit "matlab-function.m" 22)) that are evaluated by the Emacs side. In other words, the Emacs side of the interface has a whole bunch of functions that correspond to debugger events. The Java side of the interface invokes them to do things like move a debug cursor through code being displayed in an Emacs buffer. In general, I highly recommend that anyone trying to implement an interface between Emacs and an external application do so via an asynchronous interface, i.e., each side interacts with the other side purely via commands and event handlers, e.g., if Emacs needs a response to a command sent to the other side, it should register an event handler for the response, issue the external command, and return to the internal (Emacs user) command loop. It should not attempt to suspend Emacs until the response comes back. If Emacs needs to issue a sequence of commands to the other side, with each command completing before the next is issued, it can create a queue of commands and have the event handler for the previous command issue the next command in the sequence. This approach is far more robust in my experience than having each command suspend Emacs until the other side responds to the command. - Paul > > I'm curious on how the JUCI would solve that problem, as I > > assume the BeanShell call that starts the communication with > > the Java portion would block until a response is received, so > > how could the Java portion (in the same thread of execution) > > submit a lisp form back for evaluation? Would you use > > multiple BeanShell instances or multiple threads inside > > Beanshell, one to submit the Java request and one to "listen" > > to stdout for asynchronous evaluation requests? > > The latter approach is what I'm using. Java invocations made through JUCI come >through a proxy which queues the invocation for execution on a separate thread. When >the java code needs to invoke elisp, it prints an elisp form and blocks while waiting >for results to be returned (through the beanshell's foreground thread). > > > An "real world" example from my Transmogrify Hook > > implementation should make it clear. Let's say you use the > > elisp interface to call the ExtractMethod refactoring on > > Transmogrify. The steps would be: > > 1) User marks a region in the current buffer and calls > > "jde-transmogrify-extract-method"; > > 2) This defun calls jde-eval, using the BeanShell to activate > > the Transmogrify engine; > > 3) Transmogrify gathers the necessary information by making > > calls to the Hook interface: > > Here I'm guessing a little, I don't remember exactly the > > number and order of calls to the Hook interface. > > For simplicity let's assume all the source code info can be > > obtained in 3.2 call. It could actually require three to four > > calls to obtain line number, caret position, etc. > > 3.1) getUserInput (to obtain the new method name) > > 3.2) getSelecteText (to obtain the body of the extracted method) > > 3.3) displayMessage or displayException (for the result) > > Each one of the 3.* calls will create an elisp form that will > > be sent (via gnuclient) back to Emacs for evaluation. > > The processing keeps going back and forth between the > > Transmogrify engine and Emacs (through the hook > > implementation and gnuclient) until Transmogrify has all the > > info it needs, at which point: > > 4) Transmogrify applies the refactoring to the source code; > > 5) Transmogrify returns the control to the original jde-eval call. > > 6) jde-transmogrify-extract-method completes > > (this is really asking for an UML sequence diagram :-) > > I agree -- I'll try to put one together with some amount of detail that would show >the above scenario in JUCI. > > > So even though the refactoring is "put in motion" by the > > jde-eval call, the control of execution is pretty much in the > > hands of Transmogrify. Emacs must be ready to act as an > > "evaluation server", receiving forms on demand. This is the > > behavior implemented by gnuserv/gnuclient. It would be great > > if you can find a way to implement this behavior using > > BeanShell only. You mentioned that the Java code would print > > "lisp forms back to stdout and are evaluated by > > `bsh-eval-r'". Doesn't that imply that you are limited to > > control flows where Emacs is driving? How can the Java proxy > > "trigger" the call to bsh-eval-r, get the result of the > > evaluation and recover the control of the execution flow - > > for example, to move from 3.1 to 3.2 in the scenario described above? > > I can't say that I've tested such a scenario like the above, but I'd expect to be >able to get it working otherwise JUCI wouldn't be viable. The only aspect which may >be tricky is the fact that multiple lisp forms must be sent back and evalled >(probably one each for getUserInput, getSelectedText and displayMessage). But each >lisp form evaluated would produce a result (even nil) which would have to be sent >back to the beanshell. As long as there is an alternating balance between beanshell >script sent to the beanshell and elisp forms sent back to Emacs, I don't think this >will be an issue. > > [snip] > > Thanks for the helpful feedback. > > /Nick
