A JavaScript development environment for Emacs https://indium.readthedocs.io
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.
 
 
 
 

162 lines
6.0 KiB

;;; jade-backend.el --- Backend for jade.el -*- lexical-binding: t; -*-
;; Copyright (C) 2016 Nicolas Petton
;; Author: Nicolas Petton <nicolas@petton.fr>
;; Keywords: internal
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Generic backend implementation.
;; Backends should define a new backend symbol using `jade-register-backend',
;;; Code:
(require 'map)
(require 'jade-repl)
(defvar jade-connections (list) "List of connections.")
(defvar jade-connection nil
"Current connection to the browser tab.
A connection should be an alist with the following required keys:
`backend' and `url'. Other backend-specific keys might be used
by backends.")
(make-variable-buffer-local 'jade-connection)
(defvar jade-backends nil "List of registered backends.")
(defmacro jade-with-connection (connection &rest body)
"Set the value of `jade-connection' to CONNECTION and evaluate BODY."
(declare (debug t) (indent 1))
`(let ((jade-connection ,connection))
,@body))
(defun jade-backend ()
"Return the backend for the current connection."
(map-elt jade-connection 'backend))
(defun jade-register-backend (backend)
"Register a new BACKEND.
BACKEND should be a symbol."
(add-to-list 'jade-backends backend))
(defun jade-quit ()
"Close the current connection and kill its REPL buffer if any.
When called interactively, prompt for a confirmation first."
(interactive)
(unless jade-connection
(user-error "No active connection to close"))
(when (or (not (called-interactively-p 'interactive))
(y-or-n-p (format "Do you really want to close the connection to %s ? "
(map-elt jade-connection 'url))))
(jade-backend-close-connection (jade-backend) jade-connection)
(setq jade-connections (remq jade-connection jade-connections))
(jade-backend-kill-all-buffers jade-connection)))
(defun jade-reconnect ()
"Try to re-establish a connection.
The new connection is based on the current (usually closed) one."
(interactive)
(unless jade-connection
(user-error "No connection associated to the current buffer"))
(jade-backend-reconnect (jade-backend)))
(defun jade-backend-kill-all-buffers (connection)
"Kill all buffers that have the `jade-connection' CONNECTION."
(seq-map #'kill-buffer
(seq-filter (lambda (buf)
(with-current-buffer buf
(eq jade-connection connection)))
(buffer-list))))
(defun jade-reload ()
"Reload the page."
(interactive)
(jade-backend-evaluate (jade-backend) "window.location.reload()"))
;;; jade-connection methods
(cl-defgeneric jade-backend-close-connection (backend connection)
"Close CONNECTION.")
(cl-defgeneric jade-backend-reconnect (backend)
"Try to re-establish a connection.
The new connection is created based on the current
`jade-connection'.")
(cl-defgeneric jade-backend-evaluate (backend string &optional callback)
"Evaluate STRING then call CALLBACK.
CALLBACK is called with two arguments, the value returned by the
evaluation and non-nil if the evaluation threw an error.
The value should be an alist with a the following required keys:
`type', `value' and `description'. If the value represents a
remote object that can be inspected, it should also have an
`objectid' key.")
(cl-defgeneric jade-backend-evaluate-on-frame (backend string frame &optional callback)
"Evaluate STRING on the call frame FRAME then call CALLBACK.
CALLBACK is called with two arguments, the value returned by the
evaluation and non-nil if the evaluation threw an error.
The value should be an alist with a the following required keys:
`type', `value' and `description'. If the value represents a
remote object that can be inspected, it should also have an
`objectid' key.")
(cl-defgeneric jade-backend-get-completions (backend expression prefix callback)
"Get the completion list using CONNECTION for EXPRESSION that match PREFIX.
Evaluate CALLBACK on the filtered candidates.
EXPRESSION should be a valid JavaScript expression string.")
(cl-defgeneric jade-backend-get-properties (backend reference &optional callback all-properties)
"Request the properties of the remote object represented by REFERENCE.
REFERENCE must be the id of a remote object.
CALLBACK is called with the fetched list of properties.
If ALL-PROPERTIES is non-nil, get all the properties from the
prototype chain of the remote object.")
(cl-defgeneric jade-backend-get-script-source (backend frame callback)
"Get the source of the script for FRAME.
Evaluate CALLBACK with the result.")
(cl-defgeneric jade-backend-resume (backend &optional callback)
"Resume the debugger and evaluate CALLBACK if non-nil.")
(cl-defgeneric jade-backend-step-into (backend &optional callback)
"Step into the current stack frame and evaluate CALLBACK if non-nil.")
(cl-defgeneric jade-backend-step-out (backend &optional callback)
"Step out the current stack frame and evaluate CALLBACK if non-nil.")
(cl-defgeneric jade-backend-step-over (backend &optional callback)
"Step over the current stack frame and evaluate CALLBACK if non-nil.")
(cl-defgeneric jade-backend-continue-to-location (backend location &optional callback)
"Continue to LOCATION and evaluate CALLBACK if non-nil.
Location should be an alist with a `column' and `row' key.")
(defun jade-backend-object-reference-p (value)
"Return non-nil if VALUE is a reference to a remote object."
(map-elt value 'objectid))
(provide 'jade-backend)
;;; jade-backend.el ends here