public
Last active

A complete javascript shell with elnode. It's pretty simple obviously and there's a lot of return value hacking to do. It's quite interesting that this stuff is so easy though. I think it might be a useful addition to any elnode project to map in a handler that let you control the javascript from the elnode emacs instance.

  • Download Gist
elnode-ijs.el
Emacs Lisp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
;;; interactive javascript shell with elnode -*- lexical-binding: t -*-
 
(elnode-app elnode-ijs-dir comint uuid)
 
(defvar elnode-ijs/input nil
"Input for the js shell.
 
Each element is a cons of a uuid and the javascript to send.")
 
(defvar elnode-ijs/input-hash (make-hash-table :test 'equal)
"Record of things sent to the js engine.
 
This stores the UUID and the js comint process for all input.
The js engine uses the UUID to communicate results back to us,
when it does the relevant element is deleted.")
 
(defun elnode-ijs-handler (httpcon)
(elnode-defer-until (car-safe elnode-ijs/input)
(unwind-protect
(elnode-send-json httpcon elnode-ijs/input)
(setq elnode-ijs/input nil))))
 
(defun elnode-ijs-result-handler (httpcon)
(let* ((id (elnode-http-param httpcon "uuid"))
(result (elnode-http-param httpcon "result"))
(buffer (gethash id elnode-ijs/input-hash)))
(remhash id elnode-ijs/input-hash)
(if buffer
(with-current-buffer buffer
;; Delete the hash
(let ((buffer-read-only nil))
(save-excursion
(re-search-backward
(concat "#" id)
nil t)
(delete-region (point) (line-end-position))
(insert (format "%S" result)))))
;; Else
(message "%s is not in the hash" id))))
 
(defun elnode-ijs/bootstrap (httpcon)
(elnode-http-start httpcon 200 '("Content-type" . "text/html"))
(elnode-http-return
httpcon
"<html>
<head>
<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js\">
</script>
<script src=\"/ijs.js\" language=\"Javascript\">
</script>
</head>
<body>
</body>
</html>"))
 
(defun elnode-ijs-router (httpcon)
"Main router for ijs"
(elnode-dispatcher
httpcon
`(("^/boot/" . elnode-ijs/bootstrap)
("^/ijs\\.js" . ,(elnode-make-send-file
(concat elnode-ijs-dir "elnode-ijs.js")))
("^/ijsresult/" . elnode-ijs-result-handler)
("^/ijs/" . elnode-ijs-handler))))
 
(defconst elnode-ijs-prompt "IJS> ")
 
(defun elnode-ijs/input-sender (proc input)
(let ((id (uuid-string))
(buffer-read-only nil)
(lb (- (line-beginning-position) 5)))
(comint-output-filter proc (format "#%s\n" id))
(push (cons id input) elnode-ijs/input)
(puthash id (process-buffer proc) elnode-ijs/input-hash))
(comint-output-filter proc elnode-ijs-prompt))
 
(define-derived-mode
elnode-ijs/mode comint-mode "IJSM"
"Run a Javascript shell."
:syntax-table js2-mode-syntax-table
(setq comint-prompt-regexp (concat "^" (regexp-quote elnode-ijs-prompt)))
(setq comint-input-sender 'elnode-ijs/input-sender)
 
(unless (comint-check-proc (current-buffer))
;; Was cat, but on non-Unix platforms that might not exist, so
;; use hexl instead, which is part of the Emacs distribution.
(let ((fake-proc
(condition-case nil
(start-process "ijsm" (current-buffer) "hexl")
(file-error (start-process "ijsm" (current-buffer) "cat")))))
(set-process-query-on-exit-flag fake-proc nil)
;; Add a silly header
(insert "Interactive Javascript Mode\n")
(set-marker
(process-mark fake-proc) (point))
(comint-output-filter fake-proc elnode-ijs-prompt))))
 
;; This needs to take a url
(defun elnode-ijs (port)
(interactive "nEnter a port: ")
(elnode-start 'elnode-ijs-router :port port)
(let ((url (format "http://localhost:%d" port)))
(browse-url (format "%s/boot/" url))
(let ((buf
;; This needs to be tied to the url
(get-buffer-create (format "*ijs-%s*" url))))
(with-current-buffer buf
(elnode-ijs/mode)
(switch-to-buffer buf)))))
 
;;; elnode-ijs.el ends here
elnode-ijs.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
// elnode-ijs.js - js for doing js shell
var elnode_ijs =
(function() {
var comet = function () {
$.ajax(
{ url: "/ijs/",
dataType: "json",
success: function (data, status) {
if (data) {
for (var key in data) {
var val = data[key];
var result;
try {
with (window) {
result = JSON.stringify(eval(val));
}
}
catch (err) {
console.log("error: ", err);
}
$.post(
"/ijsresult/", {
uuid: key,
result: result
});
}
}
else {
console.log("no data!");
}
},
complete: function (jqXHR, status) {
elnode_ijs.comet();
}
}
);
};
return {
comet: comet,
thing: 10
};
})();
 
elnode_ijs.comet();
 
// elnode_ijs.js ends here

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.