initial
This commit is contained in:
66
elpa/skewer-mode-20180706.1807/cache-table.el
Normal file
66
elpa/skewer-mode-20180706.1807/cache-table.el
Normal file
@@ -0,0 +1,66 @@
|
||||
;;; cache-table.el --- a hash table with expiring entries -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;; Author: Christopher Wellons <mosquitopsu@gmail.com>
|
||||
;; Version: 1.0
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; See the docstring of `cache-table-create'. There is no
|
||||
;; `cache-table-put': use `setf' on `cache-table-get' instead.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(cl-defstruct (cache-table (:constructor cache-table--create))
|
||||
"A cache table with expiring entries."
|
||||
expire-time table)
|
||||
|
||||
(defun cache-table-create (expire-time &rest keyword-args)
|
||||
"Create a new cache-table with entries automatically removed
|
||||
from the table after EXPIRE-TIME seconds. This function accepts
|
||||
the same keyword arguments as `make-hash-table'. Entries are not
|
||||
actually removed from the cache-table until an access is made to
|
||||
the cache-table.
|
||||
|
||||
Use `cache-table-get' to get and put (via setf) entries."
|
||||
(cache-table--create :expire-time expire-time
|
||||
:table (apply #'make-hash-table keyword-args)))
|
||||
|
||||
(defun cache-table-clear-expired (cache-table)
|
||||
"Remove all expired entries from CACHE-TABLE."
|
||||
(cl-loop with expire-time = (cache-table-expire-time cache-table)
|
||||
with table = (cache-table-table cache-table)
|
||||
with dead-time = (- (float-time) expire-time)
|
||||
for key being the hash-keys of table using (hash-value entry)
|
||||
for (time . value) = entry
|
||||
when (< time dead-time) do (remhash key table)))
|
||||
|
||||
(defun cache-table-get (key cache-table &optional default)
|
||||
"Access the value for KEY in CACHE-TABLE if it has not yet
|
||||
expired. Behaves just like `gethash'."
|
||||
(cache-table-clear-expired cache-table)
|
||||
(cdr (gethash key (cache-table-table cache-table) (cons 0 default))))
|
||||
|
||||
(gv-define-setter cache-table-get (value key cache-table)
|
||||
"Put an entry in the hash table, like (setf (gethash key table) value)."
|
||||
`(progn
|
||||
(cache-table-clear-expired ,cache-table)
|
||||
(puthash ,key (cons (float-time) ,value)
|
||||
(cache-table-table ,cache-table))))
|
||||
|
||||
(defun cache-table-map (f cache-table)
|
||||
"Like `maphash', call F for all non-expired entries in CACHE-TABLE."
|
||||
(cache-table-clear-expired cache-table)
|
||||
(maphash (lambda (k v) (funcall f k (cdr v)))
|
||||
(cache-table-table cache-table)))
|
||||
|
||||
(defun cache-table-count (cache-table)
|
||||
"Like `hash-table-count', count the number of non-expired entries."
|
||||
(hash-table-count (cache-table-table cache-table)))
|
||||
|
||||
(provide 'cache-table)
|
||||
|
||||
;;; cache-table.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/cache-table.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/cache-table.elc
Normal file
Binary file not shown.
9
elpa/skewer-mode-20180706.1807/example.html
Normal file
9
elpa/skewer-mode-20180706.1807/example.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Skewer</title>
|
||||
<script src="/skewer"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
217
elpa/skewer-mode-20180706.1807/skewer-bower.el
Normal file
217
elpa/skewer-mode-20180706.1807/skewer-bower.el
Normal file
@@ -0,0 +1,217 @@
|
||||
;;; skewer-bower.el --- dynamic library loading -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package loads libraries into the current page using the bower
|
||||
;; infrastructure. Note: bower is not actually used by this package
|
||||
;; and so does *not* need to be installed. Only git is required (see
|
||||
;; `skewer-bower-git-executable'). It will try to learn how to run git
|
||||
;; from Magit if available.
|
||||
|
||||
;; The interactive command for loading libraries is
|
||||
;; `skewer-bower-load'. It will prompt for a library and a version,
|
||||
;; automatically fetching it from the bower infrastructure if needed.
|
||||
;; For example, I often find it handy to load some version of jQuery
|
||||
;; when poking around at a page that doesn't already have it loaded.
|
||||
|
||||
;; Caveat: unfortunately the bower infrastructure is a mess; many
|
||||
;; packages are in some sort of broken state -- missing dependencies,
|
||||
;; missing metadata, broken metadata, or an invalid repository URL.
|
||||
;; Some of this is due to under-specification of the metadata by the
|
||||
;; bower project. Broken packages are unlikely to be loadable by
|
||||
;; skewer-bower.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'skewer-mode)
|
||||
(require 'simple-httpd)
|
||||
(require 'magit nil t) ; optional
|
||||
|
||||
(defcustom skewer-bower-cache-dir (locate-user-emacs-file "skewer-cache")
|
||||
"Location of library cache (git repositories)."
|
||||
:type 'string
|
||||
:group 'skewer)
|
||||
|
||||
(defcustom skewer-bower-endpoint "https://bower.herokuapp.com"
|
||||
"Endpoint for accessing package information."
|
||||
:type 'string
|
||||
:group 'skewer)
|
||||
|
||||
(defcustom skewer-bower-json '("bower.json" "package.json" "component.json")
|
||||
"Files to search for package metadata."
|
||||
:type 'list
|
||||
:group 'skewer)
|
||||
|
||||
; Try to match Magit's configuration if available
|
||||
(defcustom skewer-bower-git-executable "git"
|
||||
"Name of the git executable."
|
||||
:type 'string
|
||||
:group 'skewer)
|
||||
|
||||
(defvar skewer-bower-packages nil
|
||||
"Alist of all packages known to bower.")
|
||||
|
||||
(defvar skewer-bower-refreshed nil
|
||||
"List of packages that have been refreshed recently. This keeps
|
||||
them from hitting the network frequently.")
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-bower-refresh ()
|
||||
"Update the package listing and packages synchronously."
|
||||
(interactive)
|
||||
(cl-declare (special url-http-end-of-headers))
|
||||
(setf skewer-bower-refreshed nil)
|
||||
(with-current-buffer
|
||||
(url-retrieve-synchronously (concat skewer-bower-endpoint "/packages"))
|
||||
(setf (point) url-http-end-of-headers)
|
||||
(setf skewer-bower-packages
|
||||
(cl-sort
|
||||
(cl-loop for package across (json-read)
|
||||
collect (cons (cdr (assoc 'name package))
|
||||
(cdr (assoc 'url package))))
|
||||
#'string< :key #'car))))
|
||||
|
||||
;; Git functions
|
||||
|
||||
(defun skewer-bower-cache (package)
|
||||
"Return the cache repository directory for PACKAGE."
|
||||
(unless (file-exists-p skewer-bower-cache-dir)
|
||||
(make-directory skewer-bower-cache-dir t))
|
||||
(expand-file-name package skewer-bower-cache-dir))
|
||||
|
||||
(defun skewer-bower-git (package &rest args)
|
||||
"Run git for PACKAGE's repository with ARGS."
|
||||
(with-temp-buffer
|
||||
(when (zerop (apply #'call-process skewer-bower-git-executable nil t nil
|
||||
(format "--git-dir=%s" (skewer-bower-cache package))
|
||||
args))
|
||||
(buffer-string))))
|
||||
|
||||
(defun skewer-bower-git-clone (url package)
|
||||
"Clone or fetch PACKAGE's repository from URL if needed."
|
||||
(if (member package skewer-bower-refreshed)
|
||||
t
|
||||
(let* ((cache (skewer-bower-cache package))
|
||||
(status
|
||||
(if (file-exists-p cache)
|
||||
(when (skewer-bower-git package "fetch")
|
||||
(push package skewer-bower-refreshed))
|
||||
(skewer-bower-git package "clone" "--bare" url cache))))
|
||||
(not (null status)))))
|
||||
|
||||
(defun skewer-bower-git-show (package version file)
|
||||
"Grab FILE from PACKAGE at version VERSION."
|
||||
(when (string-match-p "^\\./" file) ; avoid relative paths
|
||||
(setf file (substring file 2)))
|
||||
(skewer-bower-git package "show" (format "%s:%s" version file)))
|
||||
|
||||
(defun skewer-bower-git-tag (package)
|
||||
"List all the tags in PACKAGE's repository."
|
||||
(split-string (skewer-bower-git package "tag")))
|
||||
|
||||
;; Bower functions
|
||||
|
||||
(defun skewer-bower-package-ensure (package)
|
||||
"Ensure a package is installed in the cache and up to date.
|
||||
Emit an error if the package could not be ensured."
|
||||
(when (null skewer-bower-packages) (skewer-bower-refresh))
|
||||
(let ((url (cdr (assoc package skewer-bower-packages))))
|
||||
(when (null url)
|
||||
(error "Unknown package: %s" package))
|
||||
(when (null (skewer-bower-git-clone url package))
|
||||
(error "Failed to fetch: %s" url))
|
||||
t))
|
||||
|
||||
(defun skewer-bower-package-versions (package)
|
||||
"List the available versions for a package. Always returns at
|
||||
least one version."
|
||||
(skewer-bower-package-ensure package)
|
||||
(or (sort (skewer-bower-git-tag package) #'string<)
|
||||
(list "master")))
|
||||
|
||||
(defun skewer-bower-get-config (package &optional version)
|
||||
"Get the configuration alist for PACKAGE at VERSION. Return nil
|
||||
if no configuration could be found."
|
||||
(skewer-bower-package-ensure package)
|
||||
(unless version (setf version "master"))
|
||||
(json-read-from-string
|
||||
(cl-loop for file in skewer-bower-json
|
||||
for config = (skewer-bower-git-show package version file)
|
||||
when config return it
|
||||
finally (return "null"))))
|
||||
|
||||
;; Serving the library
|
||||
|
||||
(defvar skewer-bower-history ()
|
||||
"Library selection history for `completing-read'.")
|
||||
|
||||
(defun skewer-bowser--path (package version main)
|
||||
"Return the simple-httpd hosted path for PACKAGE."
|
||||
(format "/skewer/bower/%s/%s/%s" package (or version "master") main))
|
||||
|
||||
(defun skewer-bower-prompt-package ()
|
||||
"Prompt for a package and version from the user."
|
||||
(when (null skewer-bower-packages) (skewer-bower-refresh))
|
||||
;; ido-completing-read bug workaround:
|
||||
(when (> (length skewer-bower-history) 32)
|
||||
(setf skewer-bower-history (cl-subseq skewer-bower-history 0 16)))
|
||||
(let* ((packages (mapcar #'car skewer-bower-packages))
|
||||
(selection (nconc skewer-bower-history packages))
|
||||
(package (completing-read "Library: " selection nil t nil
|
||||
'skewer-bower-history))
|
||||
(versions (reverse (skewer-bower-package-versions package)))
|
||||
(version (completing-read "Version: " versions
|
||||
nil t nil nil (car versions))))
|
||||
(list package version)))
|
||||
|
||||
(defun skewer-bower--js-p (filename)
|
||||
"Return non-nil if FILENAME looks like JavaScript."
|
||||
(string-match "\\.js$" filename))
|
||||
|
||||
(defun skewer-bower-guess-main (package version config)
|
||||
"Attempt to determine the main entrypoints from a potentially
|
||||
incomplete or incorrect bower configuration. Returns nil if
|
||||
guessing failed."
|
||||
(let ((check (apply-partially #'skewer-bower-git-show package version))
|
||||
(main (cdr (assoc 'main config))))
|
||||
(cond ((and (vectorp main) (cl-some check main))
|
||||
(cl-coerce (cl-remove-if-not #'skewer-bower--js-p main) 'list))
|
||||
((and (stringp main) (funcall check main))
|
||||
(list main))
|
||||
((funcall check (concat package ".js"))
|
||||
(list (concat package ".js")))
|
||||
((funcall check package)
|
||||
(list package)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-bower-load (package &optional version)
|
||||
"Dynamically load a library from bower into the current page."
|
||||
(interactive (skewer-bower-prompt-package))
|
||||
(let* ((config (skewer-bower-get-config package version))
|
||||
(deps (cdr (assoc 'dependencies config)))
|
||||
(main (skewer-bower-guess-main package version config)))
|
||||
(when (null main)
|
||||
(error "Could not load %s (%s): no \"main\" entrypoint specified"
|
||||
package version))
|
||||
(cl-loop for (dep . version) in deps
|
||||
do (skewer-bower-load (format "%s" dep) version))
|
||||
(cl-loop for entrypoint in main
|
||||
for path = (skewer-bowser--path package version entrypoint)
|
||||
do (skewer-eval path nil :type "script"))))
|
||||
|
||||
(defservlet skewer/bower "application/javascript; charset=utf-8" (path)
|
||||
"Serve a script from the local bower repository cache."
|
||||
(cl-destructuring-bind (_ _skewer _bower package version . parts)
|
||||
(split-string path "/")
|
||||
(let* ((file (mapconcat #'identity parts "/"))
|
||||
(contents (skewer-bower-git-show package version file)))
|
||||
(if contents
|
||||
(insert contents)
|
||||
(httpd-error t 404)))))
|
||||
|
||||
(provide 'skewer-bower)
|
||||
|
||||
;;; skewer-bower.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-bower.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-bower.elc
Normal file
Binary file not shown.
134
elpa/skewer-mode-20180706.1807/skewer-css.el
Normal file
134
elpa/skewer-mode-20180706.1807/skewer-css.el
Normal file
@@ -0,0 +1,134 @@
|
||||
;;; skewer-css.el --- skewer support for live-interaction CSS -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This minor mode provides functionality for CSS like plain Skewer
|
||||
;; does for JavaScript.
|
||||
|
||||
;; * C-x C-e -- `skewer-css-eval-current-declaration'
|
||||
;; * C-M-x -- `skewer-css-eval-current-rule'
|
||||
;; * C-c C-k -- `skewer-css-eval-buffer'
|
||||
|
||||
;; These functions assume there are no comments within a CSS rule,
|
||||
;; *especially* not within a declaration. In the former case, if you
|
||||
;; keep the comment free of CSS syntax it should be able to manage
|
||||
;; reasonably well. This may be fixed someday.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'css-mode)
|
||||
(require 'skewer-mode)
|
||||
|
||||
(defun skewer-css-trim (string)
|
||||
"Trim and compress whitespace in the string."
|
||||
(let ((cleaned (replace-regexp-in-string "[\t\n ]+" " " string)))
|
||||
(replace-regexp-in-string "^[\t\n ]+\\|[\t\n ]+$" "" cleaned)))
|
||||
|
||||
;; Parsing
|
||||
|
||||
(defun skewer-css-beginning-of-rule ()
|
||||
"Move to the beginning of the current rule and return point."
|
||||
(skewer-css-end-of-rule)
|
||||
(re-search-backward "{")
|
||||
(when (re-search-backward "[}/]" nil 'start)
|
||||
(forward-char))
|
||||
(re-search-forward "[^ \t\n]")
|
||||
(backward-char)
|
||||
(point))
|
||||
|
||||
(defun skewer-css-end-of-rule ()
|
||||
"Move to the end of the current rule and return point."
|
||||
(if (eql (char-before) ?})
|
||||
(point)
|
||||
(re-search-forward "}")))
|
||||
|
||||
(defun skewer-css-end-of-declaration ()
|
||||
"Move to the end of the current declaration and return point."
|
||||
(if (eql (char-before) ?\;)
|
||||
(point)
|
||||
(re-search-forward ";")))
|
||||
|
||||
(defun skewer-css-beginning-of-declaration ()
|
||||
"Move to the end of the current declaration and return point."
|
||||
(skewer-css-end-of-declaration)
|
||||
(re-search-backward ":")
|
||||
(backward-sexp 1)
|
||||
(point))
|
||||
|
||||
(defun skewer-css-selectors ()
|
||||
"Return the selectors for the current rule."
|
||||
(save-excursion
|
||||
(let ((start (skewer-css-beginning-of-rule))
|
||||
(end (1- (re-search-forward "{"))))
|
||||
(skewer-css-trim
|
||||
(buffer-substring-no-properties start end)))))
|
||||
|
||||
(defun skewer-css-declaration ()
|
||||
"Return the current declaration as a pair of strings."
|
||||
(save-excursion
|
||||
(let ((start (skewer-css-beginning-of-declaration))
|
||||
(end (skewer-css-end-of-declaration)))
|
||||
(let* ((clip (buffer-substring-no-properties start end))
|
||||
(pair (split-string clip ":")))
|
||||
(mapcar #'skewer-css-trim pair)))))
|
||||
|
||||
;; Evaluation
|
||||
|
||||
(defun skewer-css (rule)
|
||||
"Add RULE as a new stylesheet."
|
||||
(skewer-eval rule nil :type "css"))
|
||||
|
||||
(defun skewer-css-eval-current-declaration ()
|
||||
"Evaluate the declaration at the point."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(let ((selectors (skewer-css-selectors))
|
||||
(rule (skewer-css-declaration))
|
||||
(start (skewer-css-beginning-of-declaration))
|
||||
(end (skewer-css-end-of-declaration)))
|
||||
(skewer-flash-region start end)
|
||||
(skewer-css (apply #'format "%s { %s: %s }" selectors rule)))))
|
||||
|
||||
(defun skewer-css-eval-current-rule ()
|
||||
"Evaluate the rule at the point."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(let* ((start (skewer-css-beginning-of-rule))
|
||||
(end (skewer-css-end-of-rule))
|
||||
(rule (buffer-substring-no-properties start end)))
|
||||
(skewer-flash-region start end)
|
||||
(skewer-css (skewer-css-trim rule)))))
|
||||
|
||||
(defun skewer-css-eval-buffer ()
|
||||
"Send the entire current buffer as a new stylesheet."
|
||||
(interactive)
|
||||
(skewer-css (buffer-substring-no-properties (point-min) (point-max))))
|
||||
|
||||
(defun skewer-css-clear-all ()
|
||||
"Remove *all* Skewer-added styles from the document."
|
||||
(interactive)
|
||||
(skewer-eval nil nil :type "cssClearAll"))
|
||||
|
||||
;; Minor mode definition
|
||||
|
||||
(defvar skewer-css-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(prog1 map
|
||||
(define-key map (kbd "C-x C-e") 'skewer-css-eval-current-declaration)
|
||||
(define-key map (kbd "C-M-x") 'skewer-css-eval-current-rule)
|
||||
(define-key map (kbd "C-c C-k") 'skewer-css-eval-buffer)
|
||||
(define-key map (kbd "C-c C-c") 'skewer-css-clear-all)))
|
||||
"Keymap for skewer-css-mode.")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode skewer-css-mode
|
||||
"Minor mode for interactively loading new CSS rules."
|
||||
:lighter " skewer-css"
|
||||
:keymap skewer-css-mode-map
|
||||
:group 'skewer)
|
||||
|
||||
(provide 'skewer-css)
|
||||
|
||||
;;; skewer-css.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-css.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-css.elc
Normal file
Binary file not shown.
54
elpa/skewer-mode-20180706.1807/skewer-everything.user.js
Normal file
54
elpa/skewer-mode-20180706.1807/skewer-everything.user.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// ==UserScript==
|
||||
// @name Skewer Everything
|
||||
// @description Add a toggle button to run Skewer on the current page
|
||||
// @lastupdated 2015-09-14
|
||||
// @version 1.3
|
||||
// @license Public Domain
|
||||
// @include /^https?:///
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
|
||||
window.skewerNativeXHR = XMLHttpRequest;
|
||||
window.skewerInject = inject;
|
||||
|
||||
var host = 'http://localhost:8080';
|
||||
|
||||
var toggle = document.createElement('div');
|
||||
toggle.onclick = inject;
|
||||
toggle.style.width = '0px';
|
||||
toggle.style.height = '0px';
|
||||
toggle.style.borderStyle = 'solid';
|
||||
toggle.style.borderWidth = '0 12px 12px 0';
|
||||
toggle.style.borderColor = 'transparent #F00 transparent transparent';
|
||||
toggle.style.position = 'absolute';
|
||||
toggle.style.right = 0;
|
||||
toggle.style.top = 0;
|
||||
toggle.style.zIndex = 214748364;
|
||||
|
||||
var injected = false;
|
||||
|
||||
function inject() {
|
||||
if (!injected) {
|
||||
var script = document.createElement('script');
|
||||
script.src = host + '/skewer';
|
||||
document.body.appendChild(script);
|
||||
toggle.style.borderRightColor = '#0F0';
|
||||
} else {
|
||||
/* break skewer to disable it */
|
||||
skewer.fn = null;
|
||||
toggle.style.borderRightColor = '#F00';
|
||||
}
|
||||
injected = !injected;
|
||||
localStorage._autoskewered = JSON.stringify(injected);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
/* Don't use on iframes. */
|
||||
if (window.top === window.self) {
|
||||
document.body.appendChild(toggle);
|
||||
if (JSON.parse(localStorage._autoskewered || 'false')) {
|
||||
inject();
|
||||
}
|
||||
}
|
||||
});
|
||||
165
elpa/skewer-mode-20180706.1807/skewer-html.el
Normal file
165
elpa/skewer-mode-20180706.1807/skewer-html.el
Normal file
@@ -0,0 +1,165 @@
|
||||
;;; skewer-html.el --- skewer support for live-interaction HTML -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This minor mode provides functionality for HTML like plain Skewer
|
||||
;; does for JavaScript. There's no clean way to replace the body and
|
||||
;; head elements of a live document, so "evaluating" these elements is
|
||||
;; not supported.
|
||||
|
||||
;; * C-M-x -- `skewer-html-eval-tag'
|
||||
|
||||
;; See also `skewer-html-fetch-selector-into-buffer' for grabbing the
|
||||
;; page as it current exists.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'sgml-mode)
|
||||
(require 'skewer-mode)
|
||||
|
||||
;; Macros
|
||||
|
||||
(defmacro skewer-html--with-html-mode (&rest body)
|
||||
"Evaluate BODY as if in `html-mode', using a temp buffer if necessary."
|
||||
(declare (indent 0))
|
||||
(let ((orig-buffer (make-symbol "orig-buffer"))
|
||||
(temp-buffer (make-symbol "temp-buffer"))
|
||||
(orig-point (make-symbol "orig-point")))
|
||||
`(let ((,temp-buffer (and (not (eq major-mode 'html-mode))
|
||||
(generate-new-buffer " *skewer-html*")))
|
||||
(,orig-buffer (current-buffer))
|
||||
(,orig-point (point)))
|
||||
(unwind-protect
|
||||
(with-current-buffer (or ,temp-buffer ,orig-buffer)
|
||||
(when ,temp-buffer
|
||||
(insert-buffer-substring ,orig-buffer)
|
||||
(setf (point) ,orig-point)
|
||||
(html-mode))
|
||||
,@body)
|
||||
(when ,temp-buffer
|
||||
(kill-buffer ,temp-buffer))))))
|
||||
|
||||
;; Selector computation
|
||||
|
||||
(defun skewer-html--cleanup (tag)
|
||||
"Cleanup TAG name from sgml-mode."
|
||||
(skewer-html--with-html-mode
|
||||
(replace-regexp-in-string "/$" "" (sgml-tag-name tag))))
|
||||
|
||||
(defun skewer-html--tag-after-point ()
|
||||
"Return the tag struct for the tag immediately following point."
|
||||
(skewer-html--with-html-mode
|
||||
(save-excursion
|
||||
(forward-char 1)
|
||||
(sgml-parse-tag-backward))))
|
||||
|
||||
(defun skewer-html--get-context ()
|
||||
"Like `sgml-get-context' but to the root, skipping close tags."
|
||||
(skewer-html--with-html-mode
|
||||
(save-excursion
|
||||
(cl-loop for context = (sgml-get-context)
|
||||
while context
|
||||
nconc (nreverse context) into tags
|
||||
finally return (cl-delete 'close tags :key #'sgml-tag-type)))))
|
||||
|
||||
(cl-defun skewer-html-compute-tag-nth (&optional (point (point)))
|
||||
"Compute the position of this tag within its parent."
|
||||
(skewer-html--with-html-mode
|
||||
(save-excursion
|
||||
(setf (point) point)
|
||||
(let ((context (skewer-html--get-context)))
|
||||
(when context
|
||||
(let ((tag-name (skewer-html--cleanup (car context)))
|
||||
(target-depth (1- (length context))))
|
||||
(cl-loop with n = 0
|
||||
;; If point doesn't move, we're at the root.
|
||||
for point-start = (point)
|
||||
do (sgml-skip-tag-backward 1)
|
||||
until (= (point) point-start)
|
||||
;; If depth changed, we're done.
|
||||
for current-depth = (length (skewer-html--get-context))
|
||||
until (< current-depth target-depth)
|
||||
;; Examine the sibling tag.
|
||||
for current-name = (save-excursion
|
||||
(forward-char)
|
||||
(sgml-parse-tag-name))
|
||||
when (equal current-name tag-name)
|
||||
do (cl-incf n)
|
||||
finally return n)))))))
|
||||
|
||||
(defun skewer-html-compute-tag-ancestry ()
|
||||
"Compute the ancestry chain at point."
|
||||
(skewer-html--with-html-mode
|
||||
(nreverse
|
||||
(cl-loop for tag in (skewer-html--get-context)
|
||||
for nth = (skewer-html-compute-tag-nth (1+ (sgml-tag-start tag)))
|
||||
for name = (skewer-html--cleanup tag)
|
||||
unless (equal name "html")
|
||||
collect (list name nth)))))
|
||||
|
||||
(defun skewer-html-compute-selector ()
|
||||
"Compute the selector for exactly the tag around point."
|
||||
(let ((ancestry (skewer-html-compute-tag-ancestry)))
|
||||
(mapconcat (lambda (tag)
|
||||
(format "%s:nth-of-type(%d)" (cl-first tag) (cl-second tag)))
|
||||
ancestry " > ")))
|
||||
|
||||
;; Fetching
|
||||
|
||||
(defun skewer-html-fetch-selector (selector)
|
||||
"Fetch the innerHTML of a selector."
|
||||
(let ((result (skewer-eval-synchronously selector :type "fetchselector")))
|
||||
(if (skewer-success-p result)
|
||||
(cdr (assoc 'value result))
|
||||
"")))
|
||||
|
||||
(defun skewer-html-fetch-selector-into-buffer (selector)
|
||||
"Fetch the innerHTML of a selector and insert it into the active buffer."
|
||||
(interactive "sSelector: ")
|
||||
(insert (skewer-html-fetch-selector selector)))
|
||||
|
||||
;; Evaluation
|
||||
|
||||
(defun skewer-html-eval (string ancestry &optional append)
|
||||
"Load HTML into a selector, optionally appending."
|
||||
(let ((ancestry* (cl-coerce ancestry 'vector))) ; for JSON
|
||||
(skewer-eval string nil :type "html" :extra `((ancestry . ,ancestry*)
|
||||
(append . ,append)))))
|
||||
|
||||
(defun skewer-html-eval-tag ()
|
||||
"Load HTML from the immediately surrounding tag."
|
||||
(interactive)
|
||||
(let ((ancestry (skewer-html-compute-tag-ancestry)))
|
||||
(save-excursion
|
||||
;; Move to beginning of opening tag
|
||||
(let* ((beg (skewer-html--with-html-mode
|
||||
(sgml-skip-tag-forward 1) (point)))
|
||||
(end (skewer-html--with-html-mode
|
||||
(sgml-skip-tag-backward 1) (point)))
|
||||
(region (buffer-substring-no-properties beg end)))
|
||||
(skewer-flash-region beg end)
|
||||
(if (= (length ancestry) 1)
|
||||
(error "Error: cannot eval body and head tags.")
|
||||
(skewer-html-eval region ancestry nil))))))
|
||||
|
||||
;; Minor mode definition
|
||||
|
||||
(defvar skewer-html-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(prog1 map
|
||||
(define-key map (kbd "C-M-x") 'skewer-html-eval-tag)))
|
||||
"Keymap for skewer-html-mode")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode skewer-html-mode
|
||||
"Minor mode for interactively loading new HTML."
|
||||
:lighter " skewer-html"
|
||||
:keymap skewer-html-mode-map
|
||||
:group 'skewer)
|
||||
|
||||
(provide 'skewer-html)
|
||||
|
||||
;;; skewer-html.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-html.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-html.elc
Normal file
Binary file not shown.
128
elpa/skewer-mode-20180706.1807/skewer-mode-autoloads.el
Normal file
128
elpa/skewer-mode-20180706.1807/skewer-mode-autoloads.el
Normal file
@@ -0,0 +1,128 @@
|
||||
;;; skewer-mode-autoloads.el --- automatically extracted autoloads
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(add-to-list 'load-path (directory-file-name
|
||||
(or (file-name-directory #$) (car load-path))))
|
||||
|
||||
|
||||
;;;### (autoloads nil "cache-table" "cache-table.el" (0 0 0 0))
|
||||
;;; Generated autoloads from cache-table.el
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "cache-table" '("cache-table-")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-bower" "skewer-bower.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-bower.el
|
||||
|
||||
(autoload 'skewer-bower-refresh "skewer-bower" "\
|
||||
Update the package listing and packages synchronously.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(autoload 'skewer-bower-load "skewer-bower" "\
|
||||
Dynamically load a library from bower into the current page.
|
||||
|
||||
\(fn PACKAGE &optional VERSION)" t nil)
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "skewer-bower" '("skewer")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-css" "skewer-css.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-css.el
|
||||
|
||||
(autoload 'skewer-css-mode "skewer-css" "\
|
||||
Minor mode for interactively loading new CSS rules.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "skewer-css" '("skewer-css")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-html" "skewer-html.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-html.el
|
||||
|
||||
(autoload 'skewer-html-mode "skewer-html" "\
|
||||
Minor mode for interactively loading new HTML.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "skewer-html" '("skewer-html-")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-mode" "skewer-mode.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-mode.el
|
||||
|
||||
(autoload 'list-skewer-clients "skewer-mode" "\
|
||||
List the attached browsers in a buffer.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(autoload 'skewer-mode "skewer-mode" "\
|
||||
Minor mode for interacting with a browser.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(autoload 'run-skewer "skewer-mode" "\
|
||||
Attach a browser to Emacs for a skewer JavaScript REPL. Uses
|
||||
`browse-url' to launch a browser.
|
||||
|
||||
With a prefix arugment (C-u), it will ask the filename of the
|
||||
root document. With two prefix arguments (C-u C-u), it will use
|
||||
the contents of the current buffer as the root document.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(autoload 'skewer-run-phantomjs "skewer-mode" "\
|
||||
Connect an inferior PhantomJS process to Skewer, returning the process.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "skewer-mode" '("skewer" "phantomjs-program-name" "httpd/skewer/")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-repl" "skewer-repl.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-repl.el
|
||||
|
||||
(autoload 'skewer-repl--response-hook "skewer-repl" "\
|
||||
Catches all browser messages logging some to the REPL.
|
||||
|
||||
\(fn RESPONSE)" nil nil)
|
||||
|
||||
(autoload 'skewer-repl "skewer-repl" "\
|
||||
Start a JavaScript REPL to be evaluated in the visiting browser.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(eval-after-load 'skewer-mode '(progn (add-hook 'skewer-response-hook #'skewer-repl--response-hook) (add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook) (define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl)))
|
||||
|
||||
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "skewer-repl" '("skewer-" "company-skewer-repl")))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "skewer-setup" "skewer-setup.el" (0 0 0 0))
|
||||
;;; Generated autoloads from skewer-setup.el
|
||||
|
||||
(autoload 'skewer-setup "skewer-setup" "\
|
||||
Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers.
|
||||
|
||||
\(fn)" nil nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil nil ("skewer-mode-pkg.el") (0 0 0 0))
|
||||
|
||||
;;;***
|
||||
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; skewer-mode-autoloads.el ends here
|
||||
7
elpa/skewer-mode-20180706.1807/skewer-mode-pkg.el
Normal file
7
elpa/skewer-mode-20180706.1807/skewer-mode-pkg.el
Normal file
@@ -0,0 +1,7 @@
|
||||
(define-package "skewer-mode" "20180706.1807" "live browser JavaScript, CSS, and HTML interaction"
|
||||
'((simple-httpd "1.4.0")
|
||||
(js2-mode "20090723")
|
||||
(emacs "24")))
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
||||
620
elpa/skewer-mode-20180706.1807/skewer-mode.el
Normal file
620
elpa/skewer-mode-20180706.1807/skewer-mode.el
Normal file
@@ -0,0 +1,620 @@
|
||||
;;; skewer-mode.el --- live browser JavaScript, CSS, and HTML interaction -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;; Author: Christopher Wellons <wellons@nullprogram.com>
|
||||
;; URL: https://github.com/skeeto/skewer-mode
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Quick start (without package.el):
|
||||
|
||||
;; 1. Put this directory in your `load-path'
|
||||
;; 2. Load skewer-mode.el
|
||||
;; 3. M-x `run-skewer' to attach a browser to Emacs
|
||||
;; 4. From a `js2-mode' buffer with `skewer-mode' minor mode enabled,
|
||||
;; send forms to the browser to evaluate
|
||||
|
||||
;; The function `skewer-setup' can be used to configure all of mode
|
||||
;; hooks (previously this was the default). This can also be done
|
||||
;; manually like so,
|
||||
|
||||
;; (add-hook 'js2-mode-hook 'skewer-mode)
|
||||
;; (add-hook 'css-mode-hook 'skewer-css-mode)
|
||||
;; (add-hook 'html-mode-hook 'skewer-html-mode)
|
||||
|
||||
;; The keybindings for evaluating expressions in the browser are just
|
||||
;; like the Lisp modes. These are provided by the minor mode
|
||||
;; `skewer-mode'.
|
||||
|
||||
;; * C-x C-e -- `skewer-eval-last-expression'
|
||||
;; * C-M-x -- `skewer-eval-defun'
|
||||
;; * C-c C-k -- `skewer-load-buffer'
|
||||
|
||||
;; The result of the expression is echoed in the minibuffer.
|
||||
|
||||
;; Additionally, `css-mode' and `html-mode' get a similar set of
|
||||
;; bindings for modifying the CSS rules and updating HTML on the
|
||||
;; current page.
|
||||
|
||||
;; Note: `run-skewer' uses `browse-url' to launch the browser. This
|
||||
;; may require further setup depending on your operating system and
|
||||
;; personal preferences.
|
||||
|
||||
;; Multiple browsers and browser tabs can be attached to Emacs at
|
||||
;; once. JavaScript forms are sent to all attached clients
|
||||
;; simultaneously, and each will echo back the result
|
||||
;; individually. Use `list-skewer-clients' to see a list of all
|
||||
;; currently attached clients.
|
||||
|
||||
;; Sometimes Skewer's long polls from the browser will timeout after a
|
||||
;; number of hours of inactivity. If you find the browser disconnected
|
||||
;; from Emacs for any reason, use the browser's console to call
|
||||
;; skewer() to reconnect. This avoids a page reload, which would lose
|
||||
;; any fragile browser state you might care about.
|
||||
|
||||
;; To skewer your own document rather than the provided blank page,
|
||||
|
||||
;; 1. Load the dependencies
|
||||
;; 2. Load skewer-mode.el
|
||||
;; 3. Start the HTTP server (`httpd-start')
|
||||
;; 4. Include "http://localhost:8080/skewer" as a script
|
||||
;; (see `example.html' and check your `httpd-port')
|
||||
;; 5. Visit the document from your browser
|
||||
|
||||
;; Skewer fully supports CORS, so the document need not be hosted by
|
||||
;; Emacs itself. A Greasemonkey userscript and a bookmarklet are
|
||||
;; provided for injecting Skewer into any arbitrary page you're
|
||||
;; visiting without needing to modify the page on the host.
|
||||
|
||||
;; With skewer-repl.el loaded, a REPL into the browser can be created
|
||||
;; with M-x `skewer-repl', or C-c C-z. This should work like a console
|
||||
;; within the browser. Messages can be logged to this REPL with
|
||||
;; skewer.log() (just like console.log()).
|
||||
|
||||
;; Extending Skewer:
|
||||
|
||||
;; Skewer is flexible and open to extension. The REPL and the CSS and
|
||||
;; HTML minor modes are a partial examples of this. You can extend
|
||||
;; skewer.js with your own request handlers and talk to them from
|
||||
;; Emacs using `skewer-eval' (or `skewer-eval-synchronously') with
|
||||
;; your own custom :type. The :type string chooses the dispatch
|
||||
;; function under the skewer.fn object. To inject your own JavaScript
|
||||
;; into skewer.js, use `skewer-js-hook'.
|
||||
|
||||
;; You can also catch messages sent from the browser not in response
|
||||
;; to an explicit request. Use `skewer-response-hook' to see all
|
||||
;; incoming objects.
|
||||
|
||||
;;; History:
|
||||
|
||||
;; Version 1.8.0: features
|
||||
;; * Work around XMLHttpRequest tampering in userscript
|
||||
;; * Add Makefile "run" target for testing
|
||||
;; Version 1.7.0: features and fixes
|
||||
;; * Support for other major modes (including web-mode) in skewer-html-mode
|
||||
;; * Opportunistic support for company-mode completions
|
||||
;; * Always serve content as UTF-8
|
||||
;; * Improve skewer-everything.js portability
|
||||
;; Version 1.6.2: fixes
|
||||
;; * skewer.log() takes multiple arguments
|
||||
;; * comint and encoding fixes
|
||||
;; Version 1.6.1: fixes
|
||||
;; * Add `skewer-css-clear-all'
|
||||
;; * Better IE8 compatibility
|
||||
;; * User interface tweaks
|
||||
;; Version 1.6.0: fixes
|
||||
;; * Bring up to speed with Emacs 24.3
|
||||
;; * Switch to cl-lib from cl
|
||||
;; Version 1.5.3: features
|
||||
;; * Add `skewer-run-phantomjs'
|
||||
;; Version 1.5.2: small cleanup
|
||||
;; * Add `skewer-apply' and `skewer-funall'
|
||||
;; * Improved safeStringify
|
||||
;; Version 1.5.1: features
|
||||
;; * No more automatic hook setup (see `skewer-setup')
|
||||
;; * Support for HTML interaction
|
||||
;; * Support for loading Bower packages
|
||||
;; * Drop jQuery dependency
|
||||
;; * Many small improvements
|
||||
;; Version 1.4: features
|
||||
;; * Full CSS interaction
|
||||
;; * Greasemonkey userscript for injection
|
||||
;; * Full, working CORS support
|
||||
;; * Better browser presence detection
|
||||
;; Version 1.3: features and fixes
|
||||
;; * Full offline support
|
||||
;; * No more callback registering
|
||||
;; * Fix 64-bit support
|
||||
;; * Two new hooks for improved extension support
|
||||
;; * More uniform keybindings with other interactive modes
|
||||
;; Version 1.2: features
|
||||
;; * Add a skewer-eval-print-last-expression
|
||||
;; * Display evaluation time when it's long
|
||||
;; * Flash the region on eval
|
||||
;; * Improve JS stringification
|
||||
;; Version 1.1: features and fixes
|
||||
;; * Added `list-skewer-clients'
|
||||
;; * Reduce the number of HTTP requests needed
|
||||
;; * Fix stringification issues
|
||||
;; Version 1.0: initial release
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'json)
|
||||
(require 'url-util)
|
||||
(require 'simple-httpd)
|
||||
(require 'js2-mode)
|
||||
(require 'cache-table)
|
||||
|
||||
(defgroup skewer nil
|
||||
"Live browser JavaScript interaction."
|
||||
:group 'languages)
|
||||
|
||||
(defvar skewer-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(prog1 map
|
||||
(define-key map (kbd "C-x C-e") 'skewer-eval-last-expression)
|
||||
(define-key map (kbd "C-M-x") 'skewer-eval-defun)
|
||||
(define-key map (kbd "C-c C-k") 'skewer-load-buffer)))
|
||||
"Keymap for skewer-mode.")
|
||||
|
||||
(defvar skewer-data-root (file-name-directory load-file-name)
|
||||
"Location of data files needed by impatient-mode.")
|
||||
|
||||
(defvar skewer-js-hook ()
|
||||
"Hook to run when skewer.js is being served to the browser.
|
||||
|
||||
When hook functions are called, the current buffer is the buffer
|
||||
to be served to the client (a defservlet), with skewer.js script
|
||||
already inserted. This is the chance for other packages to insert
|
||||
their own JavaScript to extend skewer in the browser, such as
|
||||
adding a new type handler.")
|
||||
|
||||
(defvar skewer-response-hook ()
|
||||
"Hook to run when a response arrives from the browser. Used for
|
||||
catching messages from the browser with no associated
|
||||
callback. The response object is passed to the hook function.")
|
||||
|
||||
(defvar skewer-timeout 3600
|
||||
"Maximum time to wait on the browser to respond, in seconds.")
|
||||
|
||||
(defvar skewer-clients ()
|
||||
"Browsers awaiting JavaScript snippets.")
|
||||
|
||||
(defvar skewer-callbacks (cache-table-create skewer-timeout :test 'equal)
|
||||
"Maps evaluation IDs to local callbacks.")
|
||||
|
||||
(defvar skewer-queue ()
|
||||
"Queued messages for the browser.")
|
||||
|
||||
(defvar skewer--last-timestamp 0
|
||||
"Timestamp of the last browser response. Use
|
||||
`skewer-last-seen-seconds' to access this.")
|
||||
|
||||
(cl-defstruct skewer-client
|
||||
"A client connection awaiting a response."
|
||||
proc agent)
|
||||
|
||||
(defun skewer-process-queue ()
|
||||
"Send all queued messages to clients."
|
||||
(when (and skewer-queue skewer-clients)
|
||||
(let ((message (pop skewer-queue))
|
||||
(sent nil))
|
||||
(while skewer-clients
|
||||
(ignore-errors
|
||||
(progn
|
||||
(let ((proc (skewer-client-proc (pop skewer-clients))))
|
||||
(with-temp-buffer
|
||||
(insert (json-encode message))
|
||||
(httpd-send-header proc "text/plain" 200
|
||||
:Cache-Control "no-cache"
|
||||
:Access-Control-Allow-Origin "*")))
|
||||
(setq skewer--last-timestamp (float-time))
|
||||
(setq sent t))))
|
||||
(if (not sent) (push message skewer-queue)))
|
||||
(skewer-process-queue)))
|
||||
|
||||
(defun skewer-clients-tabulate ()
|
||||
"Prepare client list for tabulated-list-mode."
|
||||
(cl-loop for client in skewer-clients collect
|
||||
(let ((proc (skewer-client-proc client))
|
||||
(agent (skewer-client-agent client)))
|
||||
(cl-destructuring-bind (host port) (process-contact proc)
|
||||
`(,client [,host ,(format "%d" port) ,agent])))))
|
||||
|
||||
(define-derived-mode skewer-clients-mode tabulated-list-mode "skewer-clients"
|
||||
"Mode for listing browsers attached to Emacs for skewer-mode."
|
||||
(setq tabulated-list-format [("Host" 12 t)
|
||||
("Port" 5 t)
|
||||
("User Agent" 0 t)])
|
||||
(setq tabulated-list-entries #'skewer-clients-tabulate)
|
||||
(tabulated-list-init-header))
|
||||
|
||||
(define-key skewer-clients-mode-map (kbd "g")
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(skewer-ping)
|
||||
(revert-buffer)))
|
||||
|
||||
(defun skewer-update-list-buffer ()
|
||||
"Revert the client list, due to an update."
|
||||
(save-window-excursion
|
||||
(let ((list-buffer (get-buffer "*skewer-clients*")))
|
||||
(when list-buffer
|
||||
(with-current-buffer list-buffer
|
||||
(revert-buffer))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun list-skewer-clients ()
|
||||
"List the attached browsers in a buffer."
|
||||
(interactive)
|
||||
(pop-to-buffer (get-buffer-create "*skewer-clients*"))
|
||||
(skewer-clients-mode)
|
||||
(tabulated-list-print))
|
||||
|
||||
(defun skewer-queue-client (proc req)
|
||||
"Add a client to the queue, given the HTTP header."
|
||||
(let ((agent (cl-second (assoc "User-Agent" req))))
|
||||
(push (make-skewer-client :proc proc :agent agent) skewer-clients))
|
||||
(skewer-update-list-buffer)
|
||||
(skewer-process-queue))
|
||||
|
||||
;; Servlets
|
||||
|
||||
(defservlet skewer "text/javascript; charset=UTF-8" ()
|
||||
(insert-file-contents (expand-file-name "skewer.js" skewer-data-root))
|
||||
(goto-char (point-max))
|
||||
(run-hooks 'skewer-js-hook))
|
||||
|
||||
(defun httpd/skewer/get (proc _path _query req &rest _args)
|
||||
(skewer-queue-client proc req))
|
||||
|
||||
(defun httpd/skewer/post (proc _path _query req &rest _args)
|
||||
(let* ((result (json-read-from-string (cadr (assoc "Content" req))))
|
||||
(id (cdr (assoc 'id result)))
|
||||
(callback (cache-table-get id skewer-callbacks)))
|
||||
(setq skewer--last-timestamp (float-time))
|
||||
(when callback
|
||||
(funcall callback result))
|
||||
(if id
|
||||
(skewer-queue-client proc req)
|
||||
(with-temp-buffer
|
||||
(httpd-send-header proc "text/plain" 200
|
||||
:Access-Control-Allow-Origin "*")))
|
||||
(dolist (hook skewer-response-hook)
|
||||
(funcall hook result))))
|
||||
|
||||
(defvar skewer-demo-source
|
||||
(expand-file-name "example.html" skewer-data-root)
|
||||
"Source file name or buffer for `httpd/skewer/demo' servlet.")
|
||||
|
||||
(defservlet skewer/demo "text/html; charset=UTF-8" ()
|
||||
(cl-etypecase skewer-demo-source
|
||||
(buffer (insert-buffer-substring skewer-demo-source))
|
||||
(string (insert-file-contents skewer-demo-source))))
|
||||
|
||||
;; Minibuffer display
|
||||
|
||||
(defun skewer-success-p (result)
|
||||
"Return T if result was a success."
|
||||
(equal "success" (cdr (assoc 'status result))))
|
||||
|
||||
(define-derived-mode skewer-error-mode special-mode "skewer-error"
|
||||
:group 'skewer
|
||||
"Mode for displaying JavaScript errors returned by skewer-mode."
|
||||
(setq truncate-lines t))
|
||||
|
||||
(defface skewer-error-face
|
||||
'((((class color) (background light))
|
||||
:foreground "red" :underline t)
|
||||
(((class color) (background dark))
|
||||
:foreground "red" :underline t))
|
||||
"Face for JavaScript errors."
|
||||
:group 'skewer)
|
||||
|
||||
(defun skewer--error (string)
|
||||
"Return STRING propertized as an error message."
|
||||
(propertize (or string "<unknown>") 'font-lock-face 'skewer-error-face))
|
||||
|
||||
(defun skewer-post-minibuffer (result)
|
||||
"Report results in the minibuffer or the error buffer."
|
||||
(if (skewer-success-p result)
|
||||
(let ((value (cdr (assoc 'value result)))
|
||||
(time (cdr (assoc 'time result))))
|
||||
(if (and time (> time 1.0))
|
||||
(message "%s (%.3f seconds)" value time)
|
||||
(message "%s" value)))
|
||||
(with-current-buffer (pop-to-buffer (get-buffer-create "*skewer-error*"))
|
||||
(let ((inhibit-read-only t)
|
||||
(error (cdr (assoc 'error result))))
|
||||
(erase-buffer)
|
||||
(skewer-error-mode)
|
||||
(insert (skewer--error (cdr (assoc 'name error))) ": ")
|
||||
(insert (or (cdr (assoc 'message error)) "") "\n\n")
|
||||
(insert (or (cdr (assoc 'stack error)) "") "\n\n")
|
||||
(insert (format "Expression: %s\n\n"
|
||||
(if (cdr (assoc 'strict result)) "(strict)" ""))
|
||||
(cdr (assoc 'eval error)))
|
||||
(goto-char (point-min))))))
|
||||
|
||||
;; Evaluation functions
|
||||
|
||||
(cl-defun skewer-eval (string &optional callback
|
||||
&key verbose strict (type "eval") extra)
|
||||
"Evaluate STRING in the waiting browsers, giving the result to CALLBACK.
|
||||
|
||||
:VERBOSE -- if T, the return will try to be JSON encoded
|
||||
:STRICT -- if T, expression is evaluated with 'use strict'
|
||||
:TYPE -- chooses the JavaScript handler (default: eval)
|
||||
:EXTRA -- additional alist keys to append to the request object"
|
||||
(let* ((id (format "%x" (random most-positive-fixnum)))
|
||||
(request `((type . ,type)
|
||||
(eval . ,string)
|
||||
(id . ,id)
|
||||
(verbose . ,verbose)
|
||||
(strict . ,strict)
|
||||
,@extra)))
|
||||
(prog1 request
|
||||
(setf (cache-table-get id skewer-callbacks) callback)
|
||||
(setq skewer-queue (append skewer-queue (list request)))
|
||||
(skewer-process-queue))))
|
||||
|
||||
(defun skewer-eval-synchronously (string &rest args)
|
||||
"Just like `skewer-eval' but synchronously, so don't provide a
|
||||
callback. Use with caution."
|
||||
(let ((result nil))
|
||||
(apply #'skewer-eval string (lambda (v) (setq result v)) args)
|
||||
(cl-loop until result
|
||||
do (accept-process-output nil 0.01)
|
||||
finally (return result))))
|
||||
|
||||
(defun skewer-apply (function args)
|
||||
"Synchronously apply FUNCTION in the browser with the supplied
|
||||
arguments, returning the result. All ARGS must be printable by
|
||||
`json-encode'. For example,
|
||||
|
||||
(skewer-apply \"Math.atan2\" '(1 -2)) ; => 2.677945044588987
|
||||
|
||||
Uncaught exceptions propagate to Emacs as an error."
|
||||
(let ((specials '(("undefined" . nil)
|
||||
("NaN" . 0.0e+NaN)
|
||||
("Infinity" . 1.0e+INF)
|
||||
("-Infinity" . -1.0e+INF))))
|
||||
(let* ((expr (concat function "(" (mapconcat #'json-encode args ", ") ")"))
|
||||
(result (skewer-eval-synchronously expr :verbose t))
|
||||
(value (cdr (assoc 'value result))))
|
||||
(if (skewer-success-p result)
|
||||
(if (assoc value specials)
|
||||
(cdr (assoc value specials))
|
||||
(condition-case _
|
||||
(json-read-from-string value)
|
||||
(json-readtable-error value)))
|
||||
(signal 'javascript
|
||||
(list (cdr (assoc 'message (cdr (assoc'error result))))))))))
|
||||
|
||||
(defun skewer-funcall (function &rest args)
|
||||
"Synchronously call FUNCTION with the supplied ARGS. All ARGS
|
||||
must be printable by `json-read-from-string. For example,
|
||||
|
||||
(skewer-funcall \"Math.sin\" 0.5) ; => 0.479425538604203
|
||||
|
||||
Uncaught exceptions propagate to Emacs as an error."
|
||||
(skewer-apply function args))
|
||||
|
||||
(defun skewer--save-point (f &rest args)
|
||||
"Return a function that calls F with point at the current point."
|
||||
(let ((saved-point (point)))
|
||||
(lambda (&rest more)
|
||||
(save-excursion
|
||||
(goto-char saved-point)
|
||||
(apply f (append args more))))))
|
||||
|
||||
(defun skewer-ping ()
|
||||
"Ping the browser to test that it's still alive."
|
||||
(unless (null skewer-clients) ; don't queue pings
|
||||
(skewer-eval (prin1-to-string (float-time)) nil :type "ping")))
|
||||
|
||||
(defun skewer-last-seen-seconds ()
|
||||
"Return the number of seconds since the browser was last seen."
|
||||
(skewer-ping) ; make sure it's still alive next request
|
||||
(- (float-time) skewer--last-timestamp))
|
||||
|
||||
(defun skewer-mode-strict-p ()
|
||||
"Return T if buffer contents indicates strict mode."
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(js2-forward-sws)
|
||||
(forward-char 1)
|
||||
(let* ((stricts '("\"use strict\"" "'use strict'"))
|
||||
(node (js2-node-at-point))
|
||||
(code (buffer-substring-no-properties (js2-node-abs-pos node)
|
||||
(js2-node-abs-end node))))
|
||||
(and (member code stricts) t)))))
|
||||
|
||||
(defun skewer-flash-region (start end &optional timeout)
|
||||
"Temporarily highlight region from START to END."
|
||||
(let ((overlay (make-overlay start end)))
|
||||
(overlay-put overlay 'face 'secondary-selection)
|
||||
(run-with-timer (or timeout 0.2) nil 'delete-overlay overlay)))
|
||||
|
||||
(defun skewer-get-last-expression ()
|
||||
"Return the JavaScript expression before the point as a
|
||||
list: (string start end)."
|
||||
(save-excursion
|
||||
(js2-backward-sws)
|
||||
(backward-char)
|
||||
(let ((node (js2-node-at-point nil t)))
|
||||
(when (eq js2-FUNCTION (js2-node-type (js2-node-parent node)))
|
||||
(setq node (js2-node-parent node)))
|
||||
(when (js2-ast-root-p node)
|
||||
(error "no expression found"))
|
||||
(let ((start (js2-node-abs-pos node))
|
||||
(end (js2-node-abs-end node)))
|
||||
(list (buffer-substring-no-properties start end) start end)))))
|
||||
|
||||
(defun skewer-eval-last-expression (&optional prefix)
|
||||
"Evaluate the JavaScript expression before the point in the
|
||||
waiting browser. If invoked with a prefix argument, insert the
|
||||
result into the current buffer."
|
||||
(interactive "P")
|
||||
(if prefix
|
||||
(skewer-eval-print-last-expression)
|
||||
(if js2-mode-buffer-dirty-p
|
||||
(js2-mode-wait-for-parse
|
||||
(skewer--save-point #'skewer-eval-last-expression))
|
||||
(cl-destructuring-bind (string start end) (skewer-get-last-expression)
|
||||
(skewer-flash-region start end)
|
||||
(skewer-eval string #'skewer-post-minibuffer)))))
|
||||
|
||||
(defun skewer-get-defun ()
|
||||
"Return the toplevel JavaScript expression around the point as
|
||||
a list: (string start end)."
|
||||
(save-excursion
|
||||
(js2-backward-sws)
|
||||
(backward-char)
|
||||
(let ((node (js2-node-at-point nil t)))
|
||||
(when (js2-ast-root-p node)
|
||||
(error "no expression found"))
|
||||
(while (and (js2-node-parent node)
|
||||
(not (js2-ast-root-p (js2-node-parent node))))
|
||||
(setf node (js2-node-parent node)))
|
||||
(let ((start (js2-node-abs-pos node))
|
||||
(end (js2-node-abs-end node)))
|
||||
(list (buffer-substring-no-properties start end) start end)))))
|
||||
|
||||
(defun skewer-eval-defun ()
|
||||
"Evaluate the JavaScript expression before the point in the
|
||||
waiting browser."
|
||||
(interactive)
|
||||
(if js2-mode-buffer-dirty-p
|
||||
(js2-mode-wait-for-parse (skewer--save-point #'skewer-eval-defun))
|
||||
(cl-destructuring-bind (string start end) (skewer-get-defun)
|
||||
(skewer-flash-region start end)
|
||||
(skewer-eval string #'skewer-post-minibuffer))))
|
||||
|
||||
;; Print last expression
|
||||
|
||||
(defvar skewer-eval-print-map (cache-table-create skewer-timeout :test 'equal)
|
||||
"A mapping of evaluation IDs to insertion points.")
|
||||
|
||||
(defun skewer-post-print (result)
|
||||
"Insert the result after its source expression."
|
||||
(if (not (skewer-success-p result))
|
||||
(skewer-post-minibuffer result)
|
||||
(let* ((id (cdr (assoc 'id result)))
|
||||
(pos (cache-table-get id skewer-eval-print-map)))
|
||||
(when pos
|
||||
(with-current-buffer (car pos)
|
||||
(goto-char (cdr pos))
|
||||
(insert (cdr (assoc 'value result)) "\n"))))))
|
||||
|
||||
(defun skewer-eval-print-last-expression ()
|
||||
"Evaluate the JavaScript expression before the point in the
|
||||
waiting browser and insert the result in the buffer at point."
|
||||
(interactive)
|
||||
(if js2-mode-buffer-dirty-p
|
||||
(js2-mode-wait-for-parse
|
||||
(skewer--save-point #'skewer-eval-print-last-expression))
|
||||
(cl-destructuring-bind (string start end) (skewer-get-defun)
|
||||
(skewer-flash-region start end)
|
||||
(insert "\n")
|
||||
(let* ((request (skewer-eval string #'skewer-post-print :verbose t))
|
||||
(id (cdr (assoc 'id request)))
|
||||
(pos (cons (current-buffer) (point))))
|
||||
(setf (cache-table-get id skewer-eval-print-map) pos)))))
|
||||
|
||||
;; Script loading
|
||||
|
||||
(defvar skewer-hosted-scripts (cache-table-create skewer-timeout)
|
||||
"Map of hosted scripts to IDs.")
|
||||
|
||||
(defun skewer-host-script (string)
|
||||
"Host script STRING from the script servlet, returning the script ID."
|
||||
(let ((id (random most-positive-fixnum)))
|
||||
(prog1 id
|
||||
(setf (cache-table-get id skewer-hosted-scripts) string))))
|
||||
|
||||
(defun skewer-load-buffer ()
|
||||
"Load the entire current buffer into the browser. A snapshot of
|
||||
the buffer is hosted so that browsers visiting late won't see an
|
||||
inconsistent buffer."
|
||||
(interactive)
|
||||
(let ((id (skewer-host-script (buffer-string)))
|
||||
(buffer-name (buffer-name)))
|
||||
(skewer-eval (format "/skewer/script/%d/%s"
|
||||
id (url-hexify-string buffer-name))
|
||||
(lambda (_) (message "%s loaded" buffer-name))
|
||||
:type "script")))
|
||||
|
||||
(defservlet skewer/script "text/javascript; charset=UTF-8" (path)
|
||||
(let ((id (string-to-number (nth 3 (split-string path "/")))))
|
||||
(insert (cache-table-get id skewer-hosted-scripts ""))))
|
||||
|
||||
;; Define the minor mode
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode skewer-mode
|
||||
"Minor mode for interacting with a browser."
|
||||
:lighter " skewer"
|
||||
:keymap skewer-mode-map
|
||||
:group 'skewer)
|
||||
|
||||
;;;###autoload
|
||||
(defun run-skewer (&optional arg)
|
||||
"Attach a browser to Emacs for a skewer JavaScript REPL. Uses
|
||||
`browse-url' to launch a browser.
|
||||
|
||||
With a prefix arugment (C-u), it will ask the filename of the
|
||||
root document. With two prefix arguments (C-u C-u), it will use
|
||||
the contents of the current buffer as the root document."
|
||||
(interactive "p")
|
||||
(cl-case arg
|
||||
(4 (setf skewer-demo-source (read-file-name "Skewer filename: ")))
|
||||
(16 (setf skewer-demo-source (current-buffer))))
|
||||
(httpd-start)
|
||||
(browse-url (format "http://127.0.0.1:%d/skewer/demo" httpd-port)))
|
||||
|
||||
;; PhantomJS
|
||||
|
||||
(defvar phantomjs-program-name "/usr/bin/phantomjs"
|
||||
"Path to the phantomjs executable.")
|
||||
|
||||
(defvar skewer-phantomjs-processes ()
|
||||
"List of phantomjs processes connected to Skewer.")
|
||||
|
||||
(defun skewer-phantomjs-sentinel (proc event)
|
||||
"Cleanup after phantomjs exits."
|
||||
(when (cl-some (lambda (s) (string-match-p s event))
|
||||
'("finished" "abnormal" "killed"))
|
||||
(delete-file (process-get proc 'tempfile))))
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-run-phantomjs ()
|
||||
"Connect an inferior PhantomJS process to Skewer, returning the process."
|
||||
(interactive)
|
||||
(httpd-start)
|
||||
(let ((script (make-temp-file "phantomjs-"))
|
||||
(url (format "http://0:%d/skewer/demo" httpd-port)))
|
||||
(with-temp-buffer
|
||||
(insert (format "require('webpage').create().open('%s')" url))
|
||||
(write-region nil nil script nil 0)
|
||||
(let ((proc (start-process "phantomjs" nil
|
||||
phantomjs-program-name script)))
|
||||
(prog1 proc
|
||||
(push proc skewer-phantomjs-processes)
|
||||
(process-put proc 'tempfile script)
|
||||
(set-process-sentinel proc 'skewer-phantomjs-sentinel))))))
|
||||
|
||||
(defun skewer-phantomjs-kill ()
|
||||
"Kill all inferior phantomjs processes connected to Skewer."
|
||||
(interactive)
|
||||
(mapc #'delete-process skewer-phantomjs-processes)
|
||||
(setf skewer-phantomjs-processes nil))
|
||||
|
||||
(provide 'skewer-mode)
|
||||
|
||||
;;; skewer-mode.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-mode.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-mode.elc
Normal file
Binary file not shown.
208
elpa/skewer-mode-20180706.1807/skewer-repl.el
Normal file
208
elpa/skewer-mode-20180706.1807/skewer-repl.el
Normal file
@@ -0,0 +1,208 @@
|
||||
;;; skewer-repl.el --- create a REPL in a visiting browser -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This is largely based on of IELM's code. Run `skewer-repl' to
|
||||
;; switch to the REPL buffer and evaluate code. Use
|
||||
;; `skewer-repl-toggle-strict-mode' to turn strict mode on and off.
|
||||
|
||||
;; If `compilation-search-path' is set up properly, along with
|
||||
;; `skewer-path-strip-level', asynchronous errors will provide
|
||||
;; clickable error messages that will take you to the source file of
|
||||
;; the error. This is done using `compilation-shell-minor-mode'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'comint)
|
||||
(require 'compile)
|
||||
(require 'skewer-mode)
|
||||
|
||||
(defcustom skewer-repl-strict-p nil
|
||||
"When non-NIL, all REPL evaluations are done in strict mode."
|
||||
:type 'boolean
|
||||
:group 'skewer)
|
||||
|
||||
(defcustom skewer-repl-prompt "js> "
|
||||
"Prompt string for JavaScript REPL."
|
||||
:type 'string
|
||||
:group 'skewer)
|
||||
|
||||
(defvar skewer-repl-welcome
|
||||
(propertize "*** Welcome to Skewer ***\n"
|
||||
'font-lock-face 'font-lock-comment-face)
|
||||
"Header line to show at the top of the REPL buffer. Hack
|
||||
notice: this allows log messages to appear before anything is
|
||||
evaluated because it provides insertable space at the top of the
|
||||
buffer.")
|
||||
|
||||
(defun skewer-repl-process ()
|
||||
"Return the process for the skewer REPL."
|
||||
(get-buffer-process (current-buffer)))
|
||||
|
||||
(defface skewer-repl-log-face
|
||||
'((((class color) (background light))
|
||||
:foreground "#77F")
|
||||
(((class color) (background dark))
|
||||
:foreground "#77F"))
|
||||
"Face for skewer.log() messages."
|
||||
:group 'skewer)
|
||||
|
||||
(define-derived-mode skewer-repl-mode comint-mode "js-REPL"
|
||||
"Provide a REPL into the visiting browser."
|
||||
:group 'skewer
|
||||
:syntax-table emacs-lisp-mode-syntax-table
|
||||
(setq comint-prompt-regexp (concat "^" (regexp-quote skewer-repl-prompt))
|
||||
comint-input-sender 'skewer-input-sender
|
||||
comint-process-echoes nil)
|
||||
;; Make opportunistic use of company-mode, but don't require it.
|
||||
;; This means company-backends may be undeclared, so don't emit a
|
||||
;; warning about it.
|
||||
(with-no-warnings
|
||||
(setq-local company-backends '(company-skewer-repl)))
|
||||
(unless (comint-check-proc (current-buffer))
|
||||
(insert skewer-repl-welcome)
|
||||
(start-process "skewer-repl" (current-buffer) nil)
|
||||
(set-process-query-on-exit-flag (skewer-repl-process) nil)
|
||||
(goto-char (point-max))
|
||||
(set (make-local-variable 'comint-inhibit-carriage-motion) t)
|
||||
(comint-output-filter (skewer-repl-process) skewer-repl-prompt)
|
||||
(set-process-filter (skewer-repl-process) 'comint-output-filter)))
|
||||
|
||||
(defun skewer-repl-toggle-strict-mode ()
|
||||
"Toggle strict mode for expressions evaluated by the REPL."
|
||||
(interactive)
|
||||
(setq skewer-repl-strict-p (not skewer-repl-strict-p))
|
||||
(message "REPL strict mode %s"
|
||||
(if skewer-repl-strict-p "enabled" "disabled")))
|
||||
|
||||
(defun skewer-input-sender (_ input)
|
||||
"REPL comint handler."
|
||||
(skewer-eval input 'skewer-post-repl
|
||||
:verbose t :strict skewer-repl-strict-p))
|
||||
|
||||
(defun skewer-post-repl (result)
|
||||
"Callback for reporting results in the REPL."
|
||||
(let ((buffer (get-buffer "*skewer-repl*"))
|
||||
(output (cdr (assoc 'value result))))
|
||||
(when buffer
|
||||
(with-current-buffer buffer
|
||||
(comint-output-filter (skewer-repl-process)
|
||||
(concat output "\n" skewer-repl-prompt))))))
|
||||
|
||||
(defvar skewer-repl-types
|
||||
'(("log" . skewer-repl-log-face)
|
||||
("error" . skewer-error-face))
|
||||
"Faces to use for different types of log messages.")
|
||||
|
||||
(defun skewer-log-filename (log)
|
||||
"Create a log string for the source file in LOG if present."
|
||||
(let ((name (cdr (assoc 'filename log)))
|
||||
(line (cdr (assoc 'line log)))
|
||||
(column (cdr (assoc 'column log))))
|
||||
(when name
|
||||
(concat (format "\n at %s:%s" name line)
|
||||
(if column (format ":%s" column))))))
|
||||
|
||||
(defun skewer-post-log (log)
|
||||
"Callback for logging messages to the REPL."
|
||||
(let* ((buffer (get-buffer "*skewer-repl*"))
|
||||
(face (cdr (assoc (cdr (assoc 'type log)) skewer-repl-types)))
|
||||
(value (or (cdr (assoc 'value log)) "<unspecified error>"))
|
||||
(output (propertize value 'font-lock-face face)))
|
||||
(when buffer
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(forward-line 0)
|
||||
(backward-char)
|
||||
(insert (concat "\n" output (skewer-log-filename log))))))))
|
||||
|
||||
(defcustom skewer-path-strip-level 1
|
||||
"Number of folders which will be stripped from url when discovering paths.
|
||||
Use this to limit path matching to files in your filesystem. You
|
||||
may want to add some folders to `compilation-search-path', so
|
||||
matched files can be found."
|
||||
:type 'number
|
||||
:group 'skewer)
|
||||
|
||||
(defun skewer-repl-mode-compilation-shell-hook ()
|
||||
"Setup compilation shell minor mode for highlighting files"
|
||||
(let ((error-re (format "^[ ]*at https?://[^/]+/\\(?:[^/]+/\\)\\{%d\\}\\([^:?#]+\\)\\(?:[?#][^:]*\\)?:\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?$" skewer-path-strip-level)))
|
||||
(setq-local compilation-error-regexp-alist `((,error-re 1 2 3 2))))
|
||||
(compilation-shell-minor-mode 1))
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-repl--response-hook (response)
|
||||
"Catches all browser messages logging some to the REPL."
|
||||
(let ((type (cdr (assoc 'type response))))
|
||||
(when (member type '("log" "error"))
|
||||
(skewer-post-log response))))
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-repl ()
|
||||
"Start a JavaScript REPL to be evaluated in the visiting browser."
|
||||
(interactive)
|
||||
(when (not (get-buffer "*skewer-repl*"))
|
||||
(with-current-buffer (get-buffer-create "*skewer-repl*")
|
||||
(skewer-repl-mode)))
|
||||
(pop-to-buffer (get-buffer "*skewer-repl*")))
|
||||
|
||||
(defun company-skewer-repl (command &optional arg &rest _args)
|
||||
"Skewerl REPL backend for company-mode.
|
||||
See `company-backends' for more info about COMMAND and ARG."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive
|
||||
(with-no-warnings ;; opportunistic use of company-mode
|
||||
(company-begin-backend 'company-skewer-repl)))
|
||||
(prefix (skewer-repl-company-prefix))
|
||||
(ignore-case t)
|
||||
(sorted t)
|
||||
(candidates (cons :async
|
||||
(lambda (callback)
|
||||
(skewer-repl-get-completions arg callback))))))
|
||||
|
||||
(defun skewer-repl-get-completions (arg callback)
|
||||
"Get the completion list matching the prefix ARG.
|
||||
Evaluate CALLBACK with the completion candidates."
|
||||
(let* ((expression (skewer-repl--get-completion-expression arg))
|
||||
(pattern (if expression
|
||||
(substring arg (1+ (length expression)))
|
||||
arg)))
|
||||
(skewer-eval (or expression "window")
|
||||
(lambda (result)
|
||||
(cl-loop with value = (cdr (assoc 'value result))
|
||||
for key being the elements of value
|
||||
when expression
|
||||
collect (concat expression "." key) into results
|
||||
else
|
||||
collect key into results
|
||||
finally (funcall callback results)))
|
||||
:type "completions"
|
||||
:extra `((regexp . ,pattern)))))
|
||||
|
||||
(defun skewer-repl--get-completion-expression (arg)
|
||||
"Get completion expression from ARG."
|
||||
(let ((components (split-string arg "\\.")))
|
||||
(when (> (length components) 1)
|
||||
(mapconcat #'identity (cl-subseq components 0 -1) "."))))
|
||||
|
||||
(defun skewer-repl-company-prefix ()
|
||||
"Prefix for company."
|
||||
(and (eq major-mode 'skewer-repl-mode)
|
||||
(or (with-no-warnings ;; opportunistic use of company-mode
|
||||
(company-grab-symbol-cons "\\." 1))
|
||||
'stop)))
|
||||
|
||||
;;;###autoload
|
||||
(eval-after-load 'skewer-mode
|
||||
'(progn
|
||||
(add-hook 'skewer-response-hook #'skewer-repl--response-hook)
|
||||
(add-hook 'skewer-repl-mode-hook #'skewer-repl-mode-compilation-shell-hook)
|
||||
(define-key skewer-mode-map (kbd "C-c C-z") #'skewer-repl)))
|
||||
|
||||
(provide 'skewer-repl)
|
||||
|
||||
;;; skewer-repl.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-repl.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-repl.elc
Normal file
Binary file not shown.
21
elpa/skewer-mode-20180706.1807/skewer-setup.el
Normal file
21
elpa/skewer-mode-20180706.1807/skewer-setup.el
Normal file
@@ -0,0 +1,21 @@
|
||||
;;; skewer-setup.el --- automatic setup for the Skewer minor modes -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is free and unencumbered software released into the public domain.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This exists as a separate file so that Skewer need not be fully
|
||||
;; loaded just to use this setup function.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;;###autoload
|
||||
(defun skewer-setup ()
|
||||
"Fully integrate Skewer into js2-mode, css-mode, and html-mode buffers."
|
||||
(add-hook 'js2-mode-hook 'skewer-mode)
|
||||
(add-hook 'css-mode-hook 'skewer-css-mode)
|
||||
(add-hook 'html-mode-hook 'skewer-html-mode))
|
||||
|
||||
(provide 'skewer-setup)
|
||||
|
||||
;;; skewer-setup.el ends here
|
||||
BIN
elpa/skewer-mode-20180706.1807/skewer-setup.elc
Normal file
BIN
elpa/skewer-mode-20180706.1807/skewer-setup.elc
Normal file
Binary file not shown.
436
elpa/skewer-mode-20180706.1807/skewer.js
Normal file
436
elpa/skewer-mode-20180706.1807/skewer.js
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* @fileOverview Live browser interaction with Emacs
|
||||
* @version 1.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* Connects to Emacs and waits for a request. After handling the
|
||||
* request it sends back the results and queues itself for another
|
||||
* request.
|
||||
* @namespace Holds all of Skewer's functionality.
|
||||
*/
|
||||
function skewer() {
|
||||
function callback(request) {
|
||||
var result = skewer.fn[request.type](request);
|
||||
if (result) {
|
||||
result = skewer.extend({
|
||||
id: request.id,
|
||||
type: request.type,
|
||||
status: 'success',
|
||||
value: ''
|
||||
}, result);
|
||||
skewer.postJSON(skewer.host + "/skewer/post", result, callback);
|
||||
} else {
|
||||
skewer.getJSON(skewer.host + "/skewer/get", callback);
|
||||
}
|
||||
};
|
||||
skewer.getJSON(skewer.host + "/skewer/get", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON-encoded object from a server.
|
||||
* @param {String} url The location of the remote server
|
||||
* @param {Function} [callback] The callback to receive a response object
|
||||
*/
|
||||
skewer.getJSON = function(url, callback) {
|
||||
var XHR = window.skewerNativeXHR || XMLHttpRequest;
|
||||
var xhr = new XHR();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
callback(JSON.parse(xhr.responseText));
|
||||
}
|
||||
};
|
||||
xhr.open('GET', url, true);
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a JSON-encoded object to a server.
|
||||
* @param {String} url The location of the remote server
|
||||
* @param {Object} object The object to transmit to the server
|
||||
* @param {Function} [callback] The callback to receive a response object
|
||||
*/
|
||||
skewer.postJSON = function(url, object, callback) {
|
||||
var XHR = window.skewerNativeXHR || XMLHttpRequest;
|
||||
var xhr = new XHR();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (callback && xhr.readyState === 4 && xhr.status === 200) {
|
||||
callback(JSON.parse(xhr.responseText));
|
||||
}
|
||||
};
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader("Content-Type", "text/plain"); // CORS
|
||||
xhr.send(JSON.stringify(object));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the properties other objects to a target object (jQuery.extend).
|
||||
* @param {Object} target The object to receive new properties
|
||||
* @param {...Object} objects Source objects for properties
|
||||
* @returns The target object
|
||||
*/
|
||||
skewer.extend = function(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var object = arguments[i];
|
||||
for (var key in object) {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
target[key] = object[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Globally evaluate an expression and return the result. This
|
||||
* <i>only</i> works when the implementation's indirect eval performs
|
||||
* a global eval. If not, there's no alternative, since a return value
|
||||
* is essential.
|
||||
*
|
||||
* @see http://perfectionkills.com/global-eval-what-are-the-options/
|
||||
*
|
||||
* @param expression A string containing an expression to evaluate
|
||||
* @returns The result of the evaluation
|
||||
*/
|
||||
skewer.globalEval = (function() {
|
||||
var eval0 = (function(original, Object) {
|
||||
try {
|
||||
return [eval][0]('Object') === original;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}(Object, false));
|
||||
if (eval0) {
|
||||
return function(expression) {
|
||||
return [eval][0](expression);
|
||||
};
|
||||
} else {
|
||||
return function(expression) { // Safari
|
||||
return eval.call(window, expression);
|
||||
};
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* Same as Date.now(), supplied for pre-ES5 JS (<=IE8).
|
||||
* @returns {number} The epoch time in milliseconds
|
||||
*/
|
||||
skewer.now = function() {
|
||||
return new Date().valueOf();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handlers accept a request object from Emacs and return either a
|
||||
* logical false (no response) or an object to return to Emacs.
|
||||
* @namespace Request handlers.
|
||||
*/
|
||||
skewer.fn = {};
|
||||
|
||||
/**
|
||||
* Handles an code evaluation request from Emacs.
|
||||
* @param request The request object sent by Emacs
|
||||
* @returns The result object to be returned to Emacs
|
||||
*/
|
||||
skewer.fn.eval = function(request) {
|
||||
var result = {
|
||||
strict: request.strict
|
||||
};
|
||||
var start = skewer.now();
|
||||
try {
|
||||
var prefix = request.strict ? '"use strict";\n' : "";
|
||||
var value = skewer.globalEval(prefix + request.eval);
|
||||
result.value = skewer.safeStringify(value, request.verbose);
|
||||
} catch (error) {
|
||||
result = skewer.errorResult(error, result, request);
|
||||
}
|
||||
result.time = (skewer.now() - start) / 1000;
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a hosted script named by the request.
|
||||
* @param request The request object sent by Emacs
|
||||
* @returns The result object to be returned to Emacs
|
||||
*/
|
||||
skewer.fn.script = function(request) {
|
||||
var script = document.createElement('script');
|
||||
script.src = skewer.host + request.eval;
|
||||
document.body.appendChild(script);
|
||||
return {value: JSON.stringify(request.eval)};
|
||||
};
|
||||
|
||||
/**
|
||||
* A keep-alive and connecton testing handler.
|
||||
* @param request The request object sent by Emacs
|
||||
* @returns The result object to be returned to Emacs
|
||||
*/
|
||||
skewer.fn.ping = function(request) {
|
||||
return {
|
||||
type: 'pong',
|
||||
date: skewer.now() / 1000,
|
||||
value: request.eval
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Establish a new stylesheet with the provided value.
|
||||
*/
|
||||
skewer.fn.css = function(request) {
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.className = 'skewer';
|
||||
if (style.styleSheet) { // < IE9
|
||||
style.styleSheet.cssText = request.eval;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(request.eval));
|
||||
}
|
||||
document.body.appendChild(style);
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all of Skewer's style tags from the document.
|
||||
*/
|
||||
skewer.fn.cssClearAll = function(request) {
|
||||
var styles = document.body.querySelectorAll('style.skewer');
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
styles[i].parentNode.removeChild(styles[i]);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* HTML evaluator, appends or replaces a selection with given HTML.
|
||||
*/
|
||||
skewer.fn.html = function(request) {
|
||||
function buildSelector(ancestry) {
|
||||
return ancestry.map(function(tag) {
|
||||
return tag[0] + ':nth-of-type(' + tag[1] + ')';
|
||||
}).join(' > ');
|
||||
}
|
||||
function query(ancestry) {
|
||||
return document.querySelector(buildSelector(ancestry));
|
||||
}
|
||||
function htmlToNode(html) {
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = html;
|
||||
return wrapper.firstChild;
|
||||
}
|
||||
|
||||
var target = query(request.ancestry);
|
||||
|
||||
if (target == null) {
|
||||
/* Determine missing part of the ancestry. */
|
||||
var path = request.ancestry.slice(0); // copy
|
||||
var missing = [];
|
||||
while (query(path) == null) {
|
||||
missing.push(path.pop());
|
||||
}
|
||||
|
||||
/* Build up the missing elements. */
|
||||
target = query(path);
|
||||
while (missing.length > 0) {
|
||||
var tag = missing.pop(),
|
||||
name = tag[0],
|
||||
nth = tag[1];
|
||||
var empty = null;
|
||||
var count = target.querySelectorAll(name).length;
|
||||
for (; count < nth; count++) {
|
||||
empty = document.createElement(tag[0]);
|
||||
target.appendChild(empty);
|
||||
}
|
||||
target = empty;
|
||||
}
|
||||
}
|
||||
|
||||
target.parentNode.replaceChild(htmlToNode(request.eval), target);
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the HTML contents of selector.
|
||||
*/
|
||||
skewer.fn.fetchselector = function(request) {
|
||||
var element = document.querySelector(request.eval);
|
||||
return { value: element.innerHTML };
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a list of completions for an object.
|
||||
*/
|
||||
skewer.fn.completions = function(request) {
|
||||
var object = skewer.globalEval(request.eval);
|
||||
var keys = new Set();
|
||||
var regex = new RegExp(request.regexp);
|
||||
for (var key in object) {
|
||||
if (regex.test(key)) {
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
var props = object != null ? Object.getOwnPropertyNames(object) : [];
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
if (regex.test(props[i])) {
|
||||
keys.add(props[i]);
|
||||
}
|
||||
}
|
||||
return { value: Array.from(keys).sort() };
|
||||
};
|
||||
|
||||
/**
|
||||
* Host of the skewer script (CORS support).
|
||||
* @type string
|
||||
*/
|
||||
(function() {
|
||||
var script = document.querySelector('script[src$="/skewer"]');
|
||||
if (script) {
|
||||
skewer.host = script.src.match(/\w+:\/\/[^/]+/)[0];
|
||||
} else {
|
||||
skewer.host = ''; // default to the current host
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* Stringify a potentially circular object without throwing an exception.
|
||||
* @param object The object to be printed.
|
||||
* @param {boolean} verbose Enable more verbose output.
|
||||
* @returns {string} The printed object.
|
||||
*/
|
||||
skewer.safeStringify = function (object, verbose) {
|
||||
"use strict";
|
||||
var circular = "#<Circular>";
|
||||
var seen = [];
|
||||
|
||||
var stringify = function(obj) {
|
||||
if (obj === true) {
|
||||
return "true";
|
||||
} else if (obj === false) {
|
||||
return "false";
|
||||
} else if (obj === undefined) {
|
||||
return "undefined";
|
||||
} else if (obj === null) {
|
||||
return "null";
|
||||
} else if (typeof obj === "number") {
|
||||
return obj.toString();
|
||||
} else if (obj instanceof Array) {
|
||||
if (seen.indexOf(obj) >= 0) {
|
||||
return circular;
|
||||
} else {
|
||||
seen.push(obj);
|
||||
return "[" + obj.map(function(e) {
|
||||
return stringify(e);
|
||||
}).join(", ") + "]";
|
||||
}
|
||||
} else if (typeof obj === "string") {
|
||||
return JSON.stringify(obj);
|
||||
} else if (window.Node != null && obj instanceof Node) {
|
||||
return obj.toString(); // DOM elements can't stringify
|
||||
} else if (typeof obj === "function") {
|
||||
if (verbose)
|
||||
return obj.toString();
|
||||
else
|
||||
return "Function";
|
||||
} else if (Object.prototype.toString.call(obj) === '[object Date]') {
|
||||
if (verbose)
|
||||
return JSON.stringify(obj);
|
||||
else
|
||||
return obj.toString();
|
||||
} else {
|
||||
if (verbose) {
|
||||
if (seen.indexOf(obj) >= 0)
|
||||
return circular;
|
||||
else
|
||||
seen.push(obj);
|
||||
var pairs = [];
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
var pair = JSON.stringify(key) + ":";
|
||||
pair += stringify(obj[key]);
|
||||
pairs.push(pair);
|
||||
}
|
||||
}
|
||||
return "{" + pairs.join(',') + "}";
|
||||
} else {
|
||||
try {
|
||||
return obj.toString();
|
||||
} catch (error) {
|
||||
return ({}).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return stringify(object);
|
||||
} catch (error) {
|
||||
return skewer.safeStringify(object, false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Log an object to the Skewer REPL in Emacs (console.log).
|
||||
* @param message The object to be logged.
|
||||
*/
|
||||
skewer.log = function() {
|
||||
"use strict";
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var log = {
|
||||
type: "log",
|
||||
value: skewer.safeStringify(arguments[i], true)
|
||||
};
|
||||
skewer.postJSON(skewer.host + "/skewer/post", log);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Report an error event to the REPL.
|
||||
* @param event An error event object.
|
||||
*/
|
||||
skewer.error = function(event) {
|
||||
"use strict";
|
||||
var log = {
|
||||
type: "error",
|
||||
value: event.message,
|
||||
filename: event.filename,
|
||||
line: event.lineno,
|
||||
column: event.column
|
||||
};
|
||||
skewer.postJSON(skewer.host + "/skewer/post", log);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare a result when an error occurs evaluating Javascript code.
|
||||
* @param error The error object given by catch.
|
||||
* @param result The resutl object to return to Emacs.
|
||||
* @param request The request object from Emacs.
|
||||
* @return The result object to send back to Emacs.
|
||||
*/
|
||||
skewer.errorResult = function(error, result, request) {
|
||||
"use strict";
|
||||
return skewer.extend({}, result, {
|
||||
value: error.toString(),
|
||||
status: 'error',
|
||||
error: {
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
type: error.type,
|
||||
message: error.message,
|
||||
eval: request.eval
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('error', skewer.error);
|
||||
if (document.readyState === 'complete') {
|
||||
skewer();
|
||||
} else {
|
||||
window.addEventListener('load', skewer);
|
||||
}
|
||||
} else { // < IE9
|
||||
window.attachEvent('onerror', skewer.error);
|
||||
if (document.readyState === 'complete') {
|
||||
skewer();
|
||||
} else {
|
||||
window.attachEvent('onload', skewer);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user