emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

interactive closure — variables not bound


From: Ricardo Wurmus
Subject: interactive closure — variables not bound
Date: Wed, 28 Sep 2016 08:21:10 +0200
User-agent: mu4e 0.9.16; emacs 25.1.1

Hi emacs-devel,

I’m currently attempting to rewrite some of the procedures and commands
in xwidget.el to do without the “title hack”.  Currently, the return
value of any JavaScript snippet that is run in WebKit is stored in the
title and then read out by running JavaScript to retrieve the title.

With the WebKit2 API all JavaScript is run asynchronously.  I’ve changed
“xwidget-webkit-execute-script” such that it takes an optional third
argument holding a Lisp callback, which is invoked with the return value
from JavaScript using “call1”.  This works fine for the most part,
although using callbacks is a little less convenient than doing things
synchronously (which is not supported by WebKit2).

Now there is at least one instance where things aren’t as smooth.  Take
“xwidget-webkit-insert-string” for example.  It is a command using
either “read-string” or “read-passwd” dependent on the input field
type.  The input field type is retrieved using JavaScript.  Using the
title hack this can be implemented in a faux synchronous fashion:

 (defun xwidget-webkit-insert-string (xw str)
   …
  (interactive
   (let* ((xww …)
          (field-value …)
          (field-type (xwidget-webkit-execute-script-rv
                       xww
                       "findactiveelement(document).type;")))
     (list xww
           (cond ((equal "text" field-type)
                  (read-string "Text: " field-value))
                 ((equal "password" field-type)
                  (read-passwd "Password: " nil field-value))
                 ((equal "textarea" field-type)
                  …)))))
   … ; do things with “xw” and “str”
   )

Using callbacks instead we have a problem, because the return value of
the JavaScript is only available in the callback procedure, not in the
“interactive” form.  To go around this problem I’m now trying to split
up “xwidget-webkit-insert-string” in two parts:

* “xwidget-webkit-insert-string”, which remains the interactive command
  exposed to the user.  It runs the JavaScript expression and in the
  callback passes the return value to …

* … a second interactive command, which takes care of prompting the user
  for a string to input.

This looks something like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(defun xwidget-webkit-insert-string ()
  (interactive)
  (let ((xww (xwidget-webkit-current-session)))
    (xwidget-webkit-execute-script
     xww
     (concat xwidget-webkit-activeelement-js
             "var res = findactiveelement(document); [res.value, res.type];")

     ;; This is called with “call1” from within “xwidget.c” when the
     ;; JavaScript return value is available
     (lambda (field)
       (let* ((field-value (car field))
              (field-type  (cadr field)))
         (call-interactively
          (lambda (str)
            (interactive
             (list (cond ((equal "text" field-type)
                          (read-string "Text: " field-value))
                         ((equal "password" field-type)
                          (read-passwd "Password: " nil field-value))
                         ((equal "textarea" field-type)
                          (xwidget-webkit-begin-edit-textarea xww 
field-value)))))
            (xwidget-webkit-execute-script
             xww
             (format "findactiveelement(document).value='%s'" str)))))))))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The problem with this definition is that it doesn’t work (the other
problem is that I’m replacing one ugly hack with another).  At runtime
Emacs says that “field-type” is undefined.  At compile time Emacs says
that in the callback “xww”, “field-value”, and “field-type” are
references to free variables.

But why?  Isn’t this a closure?  “field-value” and “field-type” are
let-bound inside of the callback, so they should be available in the
scope of the interactive lambda’s “cond”.

I’ve also tried to do without “interactive” and just use “read-string”
directly, but while this gives me a prompt at runtime it also freezes
Emacs and eats up all my memory (which might be normal when using
“read-string” in a lambda that’s called from C with “call1”, dunno).

~~ Ricardo


PS: I’ve sent an email to address@hidden a week ago with a completed
request form template, but haven’t received any response yet.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]