Last active
June 8, 2018 00:05
-
-
Save nicferrier/4700573 to your computer and use it in GitHub Desktop.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this seems to explain how to dip into particular scopes in js: http://stackoverflow.com/questions/4670805/javascript-eval-on-global-scope