Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

607 lines
22 KiB

  1. ;;; skewer-mode.el --- live browser JavaScript, CSS, and HTML interaction -*- lexical-binding: t; -*-
  2. ;; This is free and unencumbered software released into the public domain.
  3. ;; Author: Christopher Wellons <wellons@nullprogram.com>
  4. ;; URL: https://github.com/skeeto/skewer-mode
  5. ;;; Commentary:
  6. ;; Quick start (without package.el):
  7. ;; 1. Put this directory in your `load-path'
  8. ;; 2. Load skewer-mode.el
  9. ;; 3. M-x `run-skewer' to attach a browser to Emacs
  10. ;; 4. From a `js2-mode' buffer with `skewer-mode' minor mode enabled,
  11. ;; send forms to the browser to evaluate
  12. ;; The function `skewer-setup' can be used to configure all of mode
  13. ;; hooks (previously this was the default). This can also be done
  14. ;; manually like so,
  15. ;; (add-hook 'js2-mode-hook 'skewer-mode)
  16. ;; (add-hook 'css-mode-hook 'skewer-css-mode)
  17. ;; (add-hook 'html-mode-hook 'skewer-html-mode)
  18. ;; The keybindings for evaluating expressions in the browser are just
  19. ;; like the Lisp modes. These are provided by the minor mode
  20. ;; `skewer-mode'.
  21. ;; * C-x C-e -- `skewer-eval-last-expression'
  22. ;; * C-M-x -- `skewer-eval-defun'
  23. ;; * C-c C-k -- `skewer-load-buffer'
  24. ;; The result of the expression is echoed in the minibuffer.
  25. ;; Additionally, `css-mode' and `html-mode' get a similar set of
  26. ;; bindings for modifying the CSS rules and updating HTML on the
  27. ;; current page.
  28. ;; Note: `run-skewer' uses `browse-url' to launch the browser. This
  29. ;; may require further setup depending on your operating system and
  30. ;; personal preferences.
  31. ;; Multiple browsers and browser tabs can be attached to Emacs at
  32. ;; once. JavaScript forms are sent to all attached clients
  33. ;; simultaneously, and each will echo back the result
  34. ;; individually. Use `list-skewer-clients' to see a list of all
  35. ;; currently attached clients.
  36. ;; Sometimes Skewer's long polls from the browser will timeout after a
  37. ;; number of hours of inactivity. If you find the browser disconnected
  38. ;; from Emacs for any reason, use the browser's console to call
  39. ;; skewer() to reconnect. This avoids a page reload, which would lose
  40. ;; any fragile browser state you might care about.
  41. ;; To skewer your own document rather than the provided blank page,
  42. ;; 1. Load the dependencies
  43. ;; 2. Load skewer-mode.el
  44. ;; 3. Start the HTTP server (`httpd-start')
  45. ;; 4. Include "http://localhost:8080/skewer" as a script
  46. ;; (see `example.html' and check your `httpd-port')
  47. ;; 5. Visit the document from your browser
  48. ;; Skewer fully supports CORS, so the document need not be hosted by
  49. ;; Emacs itself. A Greasemonkey userscript and a bookmarklet are
  50. ;; provided for injecting Skewer into any arbitrary page you're
  51. ;; visiting without needing to modify the page on the host.
  52. ;; With skewer-repl.el loaded, a REPL into the browser can be created
  53. ;; with M-x `skewer-repl', or C-c C-z. This should work like a console
  54. ;; within the browser. Messages can be logged to this REPL with
  55. ;; skewer.log() (just like console.log()).
  56. ;; Extending Skewer:
  57. ;; Skewer is flexible and open to extension. The REPL and the CSS and
  58. ;; HTML minor modes are a partial examples of this. You can extend
  59. ;; skewer.js with your own request handlers and talk to them from
  60. ;; Emacs using `skewer-eval' (or `skewer-eval-synchronously') with
  61. ;; your own custom :type. The :type string chooses the dispatch
  62. ;; function under the skewer.fn object. To inject your own JavaScript
  63. ;; into skewer.js, use `skewer-js-hook'.
  64. ;; You can also catch messages sent from the browser not in response
  65. ;; to an explicit request. Use `skewer-response-hook' to see all
  66. ;; incoming objects.
  67. ;;; History:
  68. ;; Version 1.8.0: features
  69. ;; * Work around XMLHttpRequest tampering in userscript
  70. ;; * Add Makefile "run" target for testing
  71. ;; Version 1.7.0: features and fixes
  72. ;; * Support for other major modes (including web-mode) in skewer-html-mode
  73. ;; * Opportunistic support for company-mode completions
  74. ;; * Always serve content as UTF-8
  75. ;; * Improve skewer-everything.js portability
  76. ;; Version 1.6.2: fixes
  77. ;; * skewer.log() takes multiple arguments
  78. ;; * comint and encoding fixes
  79. ;; Version 1.6.1: fixes
  80. ;; * Add `skewer-css-clear-all'
  81. ;; * Better IE8 compatibility
  82. ;; * User interface tweaks
  83. ;; Version 1.6.0: fixes
  84. ;; * Bring up to speed with Emacs 24.3
  85. ;; * Switch to cl-lib from cl
  86. ;; Version 1.5.3: features
  87. ;; * Add `skewer-run-phantomjs'
  88. ;; Version 1.5.2: small cleanup
  89. ;; * Add `skewer-apply' and `skewer-funall'
  90. ;; * Improved safeStringify
  91. ;; Version 1.5.1: features
  92. ;; * No more automatic hook setup (see `skewer-setup')
  93. ;; * Support for HTML interaction
  94. ;; * Support for loading Bower packages
  95. ;; * Drop jQuery dependency
  96. ;; * Many small improvements
  97. ;; Version 1.4: features
  98. ;; * Full CSS interaction
  99. ;; * Greasemonkey userscript for injection
  100. ;; * Full, working CORS support
  101. ;; * Better browser presence detection
  102. ;; Version 1.3: features and fixes
  103. ;; * Full offline support
  104. ;; * No more callback registering
  105. ;; * Fix 64-bit support
  106. ;; * Two new hooks for improved extension support
  107. ;; * More uniform keybindings with other interactive modes
  108. ;; Version 1.2: features
  109. ;; * Add a skewer-eval-print-last-expression
  110. ;; * Display evaluation time when it's long
  111. ;; * Flash the region on eval
  112. ;; * Improve JS stringification
  113. ;; Version 1.1: features and fixes
  114. ;; * Added `list-skewer-clients'
  115. ;; * Reduce the number of HTTP requests needed
  116. ;; * Fix stringification issues
  117. ;; Version 1.0: initial release
  118. ;;; Code:
  119. (require 'cl-lib)
  120. (require 'json)
  121. (require 'url-util)
  122. (require 'simple-httpd)
  123. (require 'js2-mode)
  124. (require 'cache-table)
  125. (defgroup skewer nil
  126. "Live browser JavaScript interaction."
  127. :group 'languages)
  128. (defvar skewer-mode-map
  129. (let ((map (make-sparse-keymap)))
  130. (prog1 map
  131. (define-key map (kbd "C-x C-e") 'skewer-eval-last-expression)
  132. (define-key map (kbd "C-M-x") 'skewer-eval-defun)
  133. (define-key map (kbd "C-c C-k") 'skewer-load-buffer)))
  134. "Keymap for skewer-mode.")
  135. (defvar skewer-data-root (file-name-directory load-file-name)
  136. "Location of data files needed by impatient-mode.")
  137. (defvar skewer-js-hook ()
  138. "Hook to run when skewer.js is being served to the browser.
  139. When hook functions are called, the current buffer is the buffer
  140. to be served to the client (a defservlet), with skewer.js script
  141. already inserted. This is the chance for other packages to insert
  142. their own JavaScript to extend skewer in the browser, such as
  143. adding a new type handler.")
  144. (defvar skewer-response-hook ()
  145. "Hook to run when a response arrives from the browser. Used for
  146. catching messages from the browser with no associated
  147. callback. The response object is passed to the hook function.")
  148. (defvar skewer-timeout 3600
  149. "Maximum time to wait on the browser to respond, in seconds.")
  150. (defvar skewer-clients ()
  151. "Browsers awaiting JavaScript snippets.")
  152. (defvar skewer-callbacks (cache-table-create skewer-timeout :test 'equal)
  153. "Maps evaluation IDs to local callbacks.")
  154. (defvar skewer-queue ()
  155. "Queued messages for the browser.")
  156. (defvar skewer--last-timestamp 0
  157. "Timestamp of the last browser response. Use
  158. `skewer-last-seen-seconds' to access this.")
  159. (cl-defstruct skewer-client
  160. "A client connection awaiting a response."
  161. proc agent)
  162. (defun skewer-process-queue ()
  163. "Send all queued messages to clients."
  164. (when (and skewer-queue skewer-clients)
  165. (let ((message (pop skewer-queue))
  166. (sent nil))
  167. (while skewer-clients
  168. (ignore-errors
  169. (progn
  170. (let ((proc (skewer-client-proc (pop skewer-clients))))
  171. (with-temp-buffer
  172. (insert (json-encode message))
  173. (httpd-send-header proc "text/plain" 200
  174. :Cache-Control "no-cache"
  175. :Access-Control-Allow-Origin "*")))
  176. (setq skewer--last-timestamp (float-time))
  177. (setq sent t))))
  178. (if (not sent) (push message skewer-queue)))
  179. (skewer-process-queue)))
  180. (defun skewer-clients-tabulate ()
  181. "Prepare client list for tabulated-list-mode."
  182. (cl-loop for client in skewer-clients collect
  183. (let ((proc (skewer-client-proc client))
  184. (agent (skewer-client-agent client)))
  185. (cl-destructuring-bind (host port) (process-contact proc)
  186. `(,client [,host ,(format "%d" port) ,agent])))))
  187. (define-derived-mode skewer-clients-mode tabulated-list-mode "skewer-clients"
  188. "Mode for listing browsers attached to Emacs for skewer-mode."
  189. (setq tabulated-list-format [("Host" 12 t)
  190. ("Port" 5 t)
  191. ("User Agent" 0 t)])
  192. (setq tabulated-list-entries #'skewer-clients-tabulate)
  193. (tabulated-list-init-header))
  194. (define-key skewer-clients-mode-map (kbd "g")
  195. (lambda ()
  196. (interactive)
  197. (skewer-ping)
  198. (revert-buffer)))
  199. (defun skewer-update-list-buffer ()
  200. "Revert the client list, due to an update."
  201. (save-window-excursion
  202. (let ((list-buffer (get-buffer "*skewer-clients*")))
  203. (when list-buffer
  204. (with-current-buffer list-buffer
  205. (revert-buffer))))))
  206. ;;;###autoload
  207. (defun list-skewer-clients ()
  208. "List the attached browsers in a buffer."
  209. (interactive)
  210. (pop-to-buffer (get-buffer-create "*skewer-clients*"))
  211. (skewer-clients-mode)
  212. (tabulated-list-print))
  213. (defun skewer-queue-client (proc req)
  214. "Add a client to the queue, given the HTTP header."
  215. (let ((agent (cl-second (assoc "User-Agent" req))))
  216. (push (make-skewer-client :proc proc :agent agent) skewer-clients))
  217. (skewer-update-list-buffer)
  218. (skewer-process-queue))
  219. ;; Servlets
  220. (defservlet skewer "text/javascript; charset=UTF-8" ()
  221. (insert-file-contents (expand-file-name "skewer.js" skewer-data-root))
  222. (goto-char (point-max))
  223. (run-hooks 'skewer-js-hook))
  224. (defun httpd/skewer/get (proc _path _query req &rest _args)
  225. (skewer-queue-client proc req))
  226. (defun httpd/skewer/post (proc _path _query req &rest _args)
  227. (let* ((result (json-read-from-string (cadr (assoc "Content" req))))
  228. (id (cdr (assoc 'id result)))
  229. (callback (cache-table-get id skewer-callbacks)))
  230. (setq skewer--last-timestamp (float-time))
  231. (when callback
  232. (funcall callback result))
  233. (if id
  234. (skewer-queue-client proc req)
  235. (with-temp-buffer
  236. (httpd-send-header proc "text/plain" 200
  237. :Access-Control-Allow-Origin "*")))
  238. (dolist (hook skewer-response-hook)
  239. (funcall hook result))))
  240. (defservlet skewer/demo "text/html; charset=UTF-8" ()
  241. (insert-file-contents (expand-file-name "example.html" skewer-data-root)))
  242. ;; Minibuffer display
  243. (defun skewer-success-p (result)
  244. "Return T if result was a success."
  245. (equal "success" (cdr (assoc 'status result))))
  246. (define-derived-mode skewer-error-mode special-mode "skewer-error"
  247. :group 'skewer
  248. "Mode for displaying JavaScript errors returned by skewer-mode."
  249. (setq truncate-lines t))
  250. (defface skewer-error-face
  251. '((((class color) (background light))
  252. :foreground "red" :underline t)
  253. (((class color) (background dark))
  254. :foreground "red" :underline t))
  255. "Face for JavaScript errors."
  256. :group 'skewer)
  257. (defun skewer--error (string)
  258. "Return STRING propertized as an error message."
  259. (propertize (or string "<unknown>") 'font-lock-face 'skewer-error-face))
  260. (defun skewer-post-minibuffer (result)
  261. "Report results in the minibuffer or the error buffer."
  262. (if (skewer-success-p result)
  263. (let ((value (cdr (assoc 'value result)))
  264. (time (cdr (assoc 'time result))))
  265. (if (and time (> time 1.0))
  266. (message "%s (%.3f seconds)" value time)
  267. (message "%s" value)))
  268. (with-current-buffer (pop-to-buffer (get-buffer-create "*skewer-error*"))
  269. (let ((inhibit-read-only t)
  270. (error (cdr (assoc 'error result))))
  271. (erase-buffer)
  272. (skewer-error-mode)
  273. (insert (skewer--error (cdr (assoc 'name error))) ": ")
  274. (insert (or (cdr (assoc 'message error)) "") "\n\n")
  275. (insert (or (cdr (assoc 'stack error)) "") "\n\n")
  276. (insert (format "Expression: %s\n\n"
  277. (if (cdr (assoc 'strict result)) "(strict)" ""))
  278. (cdr (assoc 'eval error)))
  279. (goto-char (point-min))))))
  280. ;; Evaluation functions
  281. (cl-defun skewer-eval (string &optional callback
  282. &key verbose strict (type "eval") extra)
  283. "Evaluate STRING in the waiting browsers, giving the result to CALLBACK.
  284. :VERBOSE -- if T, the return will try to be JSON encoded
  285. :STRICT -- if T, expression is evaluated with 'use strict'
  286. :TYPE -- chooses the JavaScript handler (default: eval)
  287. :EXTRA -- additional alist keys to append to the request object"
  288. (let* ((id (format "%x" (random most-positive-fixnum)))
  289. (request `((type . ,type)
  290. (eval . ,string)
  291. (id . ,id)
  292. (verbose . ,verbose)
  293. (strict . ,strict)
  294. ,@extra)))
  295. (prog1 request
  296. (setf (cache-table-get id skewer-callbacks) callback)
  297. (setq skewer-queue (append skewer-queue (list request)))
  298. (skewer-process-queue))))
  299. (defun skewer-eval-synchronously (string &rest args)
  300. "Just like `skewer-eval' but synchronously, so don't provide a
  301. callback. Use with caution."
  302. (let ((result nil))
  303. (apply #'skewer-eval string (lambda (v) (setq result v)) args)
  304. (cl-loop until result
  305. do (accept-process-output nil 0.01)
  306. finally (return result))))
  307. (defun skewer-apply (function args)
  308. "Synchronously apply FUNCTION in the browser with the supplied
  309. arguments, returning the result. All ARGS must be printable by
  310. `json-encode'. For example,
  311. (skewer-apply \"Math.atan2\" '(1 -2)) ; => 2.677945044588987
  312. Uncaught exceptions propagate to Emacs as an error."
  313. (let ((specials '(("undefined" . nil)
  314. ("NaN" . 0.0e+NaN)
  315. ("Infinity" . 1.0e+INF)
  316. ("-Infinity" . -1.0e+INF))))
  317. (let* ((expr (concat function "(" (mapconcat #'json-encode args ", ") ")"))
  318. (result (skewer-eval-synchronously expr :verbose t))
  319. (value (cdr (assoc 'value result))))
  320. (if (skewer-success-p result)
  321. (if (assoc value specials)
  322. (cdr (assoc value specials))
  323. (condition-case _
  324. (json-read-from-string value)
  325. (json-readtable-error value)))
  326. (signal 'javascript
  327. (list (cdr (assoc 'message (cdr (assoc'error result))))))))))
  328. (defun skewer-funcall (function &rest args)
  329. "Synchronously call FUNCTION with the supplied ARGS. All ARGS
  330. must be printable by `json-read-from-string. For example,
  331. (skewer-funcall \"Math.sin\" 0.5) ; => 0.479425538604203
  332. Uncaught exceptions propagate to Emacs as an error."
  333. (skewer-apply function args))
  334. (defun skewer--save-point (f &rest args)
  335. "Return a function that calls F with point at the current point."
  336. (let ((saved-point (point)))
  337. (lambda (&rest more)
  338. (save-excursion
  339. (goto-char saved-point)
  340. (apply f (append args more))))))
  341. (defun skewer-ping ()
  342. "Ping the browser to test that it's still alive."
  343. (unless (null skewer-clients) ; don't queue pings
  344. (skewer-eval (prin1-to-string (float-time)) nil :type "ping")))
  345. (defun skewer-last-seen-seconds ()
  346. "Return the number of seconds since the browser was last seen."
  347. (skewer-ping) ; make sure it's still alive next request
  348. (- (float-time) skewer--last-timestamp))
  349. (defun skewer-mode-strict-p ()
  350. "Return T if buffer contents indicates strict mode."
  351. (save-excursion
  352. (save-restriction
  353. (widen)
  354. (goto-char (point-min))
  355. (js2-forward-sws)
  356. (forward-char 1)
  357. (let* ((stricts '("\"use strict\"" "'use strict'"))
  358. (node (js2-node-at-point))
  359. (code (buffer-substring-no-properties (js2-node-abs-pos node)
  360. (js2-node-abs-end node))))
  361. (and (member code stricts) t)))))
  362. (defun skewer-flash-region (start end &optional timeout)
  363. "Temporarily highlight region from START to END."
  364. (let ((overlay (make-overlay start end)))
  365. (overlay-put overlay 'face 'secondary-selection)
  366. (run-with-timer (or timeout 0.2) nil 'delete-overlay overlay)))
  367. (defun skewer-get-last-expression ()
  368. "Return the JavaScript expression before the point as a
  369. list: (string start end)."
  370. (save-excursion
  371. (js2-backward-sws)
  372. (backward-char)
  373. (let ((node (js2-node-at-point nil t)))
  374. (when (eq js2-FUNCTION (js2-node-type (js2-node-parent node)))
  375. (setq node (js2-node-parent node)))
  376. (when (js2-ast-root-p node)
  377. (error "no expression found"))
  378. (let ((start (js2-node-abs-pos node))
  379. (end (js2-node-abs-end node)))
  380. (list (buffer-substring-no-properties start end) start end)))))
  381. (defun skewer-eval-last-expression (&optional prefix)
  382. "Evaluate the JavaScript expression before the point in the
  383. waiting browser. If invoked with a prefix argument, insert the
  384. result into the current buffer."
  385. (interactive "P")
  386. (if prefix
  387. (skewer-eval-print-last-expression)
  388. (if js2-mode-buffer-dirty-p
  389. (js2-mode-wait-for-parse
  390. (skewer--save-point #'skewer-eval-last-expression))
  391. (cl-destructuring-bind (string start end) (skewer-get-last-expression)
  392. (skewer-flash-region start end)
  393. (skewer-eval string #'skewer-post-minibuffer)))))
  394. (defun skewer-get-defun ()
  395. "Return the toplevel JavaScript expression around the point as
  396. a list: (string start end)."
  397. (save-excursion
  398. (js2-backward-sws)
  399. (backward-char)
  400. (let ((node (js2-node-at-point nil t)))
  401. (when (js2-ast-root-p node)
  402. (error "no expression found"))
  403. (while (and (js2-node-parent node)
  404. (not (js2-ast-root-p (js2-node-parent node))))
  405. (setf node (js2-node-parent node)))
  406. (let ((start (js2-node-abs-pos node))
  407. (end (js2-node-abs-end node)))
  408. (list (buffer-substring-no-properties start end) start end)))))
  409. (defun skewer-eval-defun ()
  410. "Evaluate the JavaScript expression before the point in the
  411. waiting browser."
  412. (interactive)
  413. (if js2-mode-buffer-dirty-p
  414. (js2-mode-wait-for-parse (skewer--save-point #'skewer-eval-defun))
  415. (cl-destructuring-bind (string start end) (skewer-get-defun)
  416. (skewer-flash-region start end)
  417. (skewer-eval string #'skewer-post-minibuffer))))
  418. ;; Print last expression
  419. (defvar skewer-eval-print-map (cache-table-create skewer-timeout :test 'equal)
  420. "A mapping of evaluation IDs to insertion points.")
  421. (defun skewer-post-print (result)
  422. "Insert the result after its source expression."
  423. (if (not (skewer-success-p result))
  424. (skewer-post-minibuffer result)
  425. (let* ((id (cdr (assoc 'id result)))
  426. (pos (cache-table-get id skewer-eval-print-map)))
  427. (when pos
  428. (with-current-buffer (car pos)
  429. (goto-char (cdr pos))
  430. (insert (cdr (assoc 'value result)) "\n"))))))
  431. (defun skewer-eval-print-last-expression ()
  432. "Evaluate the JavaScript expression before the point in the
  433. waiting browser and insert the result in the buffer at point."
  434. (interactive)
  435. (if js2-mode-buffer-dirty-p
  436. (js2-mode-wait-for-parse
  437. (skewer--save-point #'skewer-eval-print-last-expression))
  438. (cl-destructuring-bind (string start end) (skewer-get-defun)
  439. (skewer-flash-region start end)
  440. (insert "\n")
  441. (let* ((request (skewer-eval string #'skewer-post-print :verbose t))
  442. (id (cdr (assoc 'id request)))
  443. (pos (cons (current-buffer) (point))))
  444. (setf (cache-table-get id skewer-eval-print-map) pos)))))
  445. ;; Script loading
  446. (defvar skewer-hosted-scripts (cache-table-create skewer-timeout)
  447. "Map of hosted scripts to IDs.")
  448. (defun skewer-host-script (string)
  449. "Host script STRING from the script servlet, returning the script ID."
  450. (let ((id (random most-positive-fixnum)))
  451. (prog1 id
  452. (setf (cache-table-get id skewer-hosted-scripts) string))))
  453. (defun skewer-load-buffer ()
  454. "Load the entire current buffer into the browser. A snapshot of
  455. the buffer is hosted so that browsers visiting late won't see an
  456. inconsistent buffer."
  457. (interactive)
  458. (let ((id (skewer-host-script (buffer-string)))
  459. (buffer-name (buffer-name)))
  460. (skewer-eval (format "/skewer/script/%d/%s"
  461. id (url-hexify-string buffer-name))
  462. (lambda (_) (message "%s loaded" buffer-name))
  463. :type "script")))
  464. (defservlet skewer/script "text/javascript; charset=UTF-8" (path)
  465. (let ((id (string-to-number (nth 3 (split-string path "/")))))
  466. (insert (cache-table-get id skewer-hosted-scripts ""))))
  467. ;; Define the minor mode
  468. ;;;###autoload
  469. (define-minor-mode skewer-mode
  470. "Minor mode for interacting with a browser."
  471. :lighter " skewer"
  472. :keymap skewer-mode-map
  473. :group 'skewer)
  474. ;;;###autoload
  475. (defun run-skewer ()
  476. "Attach a browser to Emacs for a skewer JavaScript REPL. Uses
  477. `browse-url' to launch a browser."
  478. (interactive)
  479. (httpd-start)
  480. (browse-url (format "http://127.0.0.1:%d/skewer/demo" httpd-port)))
  481. ;; PhantomJS
  482. (defvar phantomjs-program-name "/usr/bin/phantomjs"
  483. "Path to the phantomjs executable.")
  484. (defvar skewer-phantomjs-processes ()
  485. "List of phantomjs processes connected to Skewer.")
  486. (defun skewer-phantomjs-sentinel (proc event)
  487. "Cleanup after phantomjs exits."
  488. (when (cl-some (lambda (s) (string-match-p s event))
  489. '("finished" "abnormal" "killed"))
  490. (delete-file (process-get proc 'tempfile))))
  491. ;;;###autoload
  492. (defun skewer-run-phantomjs ()
  493. "Connect an inferior PhantomJS process to Skewer, returning the process."
  494. (interactive)
  495. (httpd-start)
  496. (let ((script (make-temp-file "phantomjs-"))
  497. (url (format "http://0:%d/skewer/demo" httpd-port)))
  498. (with-temp-buffer
  499. (insert (format "require('webpage').create().open('%s')" url))
  500. (write-region nil nil script nil 0)
  501. (let ((proc (start-process "phantomjs" nil
  502. phantomjs-program-name script)))
  503. (prog1 proc
  504. (push proc skewer-phantomjs-processes)
  505. (process-put proc 'tempfile script)
  506. (set-process-sentinel proc 'skewer-phantomjs-sentinel))))))
  507. (defun skewer-phantomjs-kill ()
  508. "Kill all inferior phantomjs processes connected to Skewer."
  509. (interactive)
  510. (mapc #'delete-process skewer-phantomjs-processes)
  511. (setf skewer-phantomjs-processes nil))
  512. (provide 'skewer-mode)
  513. ;;; skewer-mode.el ends here