summaryrefslogtreecommitdiff
path: root/elpa/skewer-mode-20200304.1142/skewer-html.el
diff options
context:
space:
mode:
Diffstat (limited to 'elpa/skewer-mode-20200304.1142/skewer-html.el')
-rw-r--r--elpa/skewer-mode-20200304.1142/skewer-html.el165
1 files changed, 165 insertions, 0 deletions
diff --git a/elpa/skewer-mode-20200304.1142/skewer-html.el b/elpa/skewer-mode-20200304.1142/skewer-html.el
new file mode 100644
index 0000000..b21de16
--- /dev/null
+++ b/elpa/skewer-mode-20200304.1142/skewer-html.el
@@ -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