unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Ricardo Wurmus <rekado@elephly.net>
To: Emacs developers <emacs-devel@gnu.org>
Subject: interactive closure — variables not bound
Date: Wed, 28 Sep 2016 08:21:10 +0200	[thread overview]
Message-ID: <87y42ch7e1.fsf@elephly.net> (raw)

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 assign@gnu.org a week ago with a completed
request form template, but haven’t received any response yet.




             reply	other threads:[~2016-09-28  6:21 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-28  6:21 Ricardo Wurmus [this message]
2016-09-28 12:41 ` interactive closure — variables not bound Stefan Monnier
2016-09-28 21:12   ` Ricardo Wurmus
2016-09-29  0:07     ` Stefan Monnier
2016-09-29  9:09       ` Ricardo Wurmus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87y42ch7e1.fsf@elephly.net \
    --to=rekado@elephly.net \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).