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.
 
 
 
 
 

187 lines
6.7 KiB

  1. ;;; indium-inspector.el --- Inspector for JavaScript objects -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2016-2018 Nicolas Petton
  3. ;; Author: Nicolas Petton <nicolas@petton.fr>
  4. ;; Keywords: convenience, tools, javascript
  5. ;; This program is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;;; Code:
  18. (require 'seq)
  19. (require 'map)
  20. (require 'subr-x)
  21. (require 'indium-structs)
  22. (require 'indium-render)
  23. (require 'indium-faces)
  24. (declare-function indium-client-get-properties "indium-client.el")
  25. (defvar indium-inspector-history nil)
  26. (make-variable-buffer-local 'indium-inspector-history)
  27. (defun indium-inspector-inspect (obj)
  28. "Open an inspector on the remote object OBJ."
  29. (if (indium-remote-object-reference-p obj)
  30. (indium-client-get-properties
  31. (indium-remote-object-id obj)
  32. (lambda (properties)
  33. (indium-inspector--inspect-properties properties obj)))
  34. (message "Cannot inspect %S" (indium-remote-object-description obj))))
  35. (defun indium-inspector--inspect-properties (properties obj)
  36. "Insert all PROPERTIES for the remote object OBJ."
  37. (let ((buf (indium-inspector-get-buffer-create))
  38. (inhibit-read-only t))
  39. (with-current-buffer buf
  40. (indium-inspector-push-to-history obj)
  41. (save-excursion
  42. (erase-buffer)
  43. (indium-render-keyword (indium-remote-object-to-string obj t))
  44. (insert "\n\n")
  45. (indium-inspector--insert-sorted-properties properties)))
  46. (pop-to-buffer buf)))
  47. (defun indium-inspector--insert-sorted-properties (properties)
  48. "Insert sorted PROPERTIES."
  49. (let ((sorted-properties (indium-inspector--split-properties properties)))
  50. (indium-render-properties (cadr sorted-properties))
  51. (insert "\n")
  52. (when-let (native (car sorted-properties))
  53. (indium-render-properties native)
  54. (insert "\n"))))
  55. (defun indium-inspector--split-properties (properties)
  56. "Split PROPERTIES into list where the first element is native properties and the second is the rest."
  57. (let ((split (seq-reduce (lambda (result property)
  58. (push property
  59. (if (indium-property-native-p property)
  60. (car result)
  61. (cadr result)))
  62. result)
  63. properties
  64. (list nil nil))))
  65. (seq-map (lambda (list) (nreverse list)) split)))
  66. (defun indium-inspector-pop ()
  67. "Go back in the history to the last object inspected."
  68. (interactive)
  69. (if (cdr indium-inspector-history)
  70. (progn
  71. (pop indium-inspector-history)
  72. (funcall #'indium-inspector-inspect (car indium-inspector-history)))
  73. (message "No previous object to inspect")))
  74. (defun indium-inspector-goto-reference (direction)
  75. "Move point to the next object reference in DIRECTION.
  76. DIRECTION can be either `next' or `previous'."
  77. (let* ((delta (pcase direction
  78. (`next 1)
  79. (`previous -1)))
  80. (limit-check (pcase direction
  81. (`next #'eobp)
  82. (`previous #'bobp)))
  83. (reference (save-excursion
  84. (forward-line delta)
  85. (when (eq direction 'previous)
  86. (end-of-line))
  87. (while (and (not (funcall limit-check))
  88. (not (get-text-property (point) 'indium-reference)))
  89. (forward-char delta))
  90. (when (get-text-property (point) 'indium-reference)
  91. (point)))))
  92. (when reference
  93. (goto-char reference)
  94. ;; go to the first char of the reference
  95. (while (get-text-property (point) 'indium-reference)
  96. (backward-char 1))
  97. (forward-char 1))))
  98. (defun indium-inspector-next-reference ()
  99. "Move the point to the next object reference."
  100. (interactive)
  101. (indium-inspector-goto-reference 'next))
  102. (defun indium-inspector-previous-reference ()
  103. "Move the point to the previous object reference."
  104. (interactive)
  105. (indium-inspector-goto-reference 'previous))
  106. (defun indium-inspector-refresh ()
  107. "Request new data to the backend and update the inspector buffer."
  108. (interactive)
  109. (when indium-inspector-history
  110. (funcall #'indium-inspector-inspect (car indium-inspector-history))))
  111. (defun indium-inspector-push-to-history (reference)
  112. "Add REFERENCE to the inspected objects history."
  113. (let-alist reference
  114. (when (or (seq-empty-p indium-inspector-history)
  115. (not (equal (indium-remote-object-id reference)
  116. (indium-remote-object-id (car indium-inspector-history)))))
  117. (push reference indium-inspector-history))))
  118. (defun indium-inspector-get-buffer ()
  119. "Return the inspector buffer, or nil if no inspector buffer exists."
  120. (get-buffer (indium-inspector-buffer-name)))
  121. (defun indium-inspector-get-buffer-create ()
  122. "Return an inspector buffer for the current connection.
  123. If no buffer exists, create one."
  124. (let ((buf (indium-inspector-get-buffer)))
  125. (unless buf
  126. (setq buf (get-buffer-create (indium-inspector-buffer-name)))
  127. (indium-inspector-setup-buffer buf))
  128. buf))
  129. (defun indium-inspector-setup-buffer (buffer)
  130. "Setup the inspector BUFFER."
  131. (with-current-buffer buffer
  132. (indium-inspector-mode)))
  133. (defun indium-inspector-buffer-name ()
  134. "Return the inspector buffer name for the current connection."
  135. "*JS Inspector*")
  136. (defvar indium-inspector-mode-map
  137. (let ((map (make-sparse-keymap)))
  138. (define-key map [return] #'indium-follow-link)
  139. (define-key map "\C-m" #'indium-follow-link)
  140. (define-key map [mouse-1] #'indium-follow-link)
  141. (define-key map "l" #'indium-inspector-pop)
  142. (define-key map "g" #'indium-inspector-refresh)
  143. (define-key map "n" #'indium-inspector-next-reference)
  144. (define-key map "p" #'indium-inspector-previous-reference)
  145. (define-key map [tab] #'indium-inspector-next-reference)
  146. (define-key map [backtab] #'indium-inspector-previous-reference)
  147. map))
  148. (define-derived-mode indium-inspector-mode special-mode "Inspector"
  149. "Major mode for inspecting JavaScript objects.
  150. \\{indium-inspector-mode-map}"
  151. (setq buffer-read-only t)
  152. (font-lock-ensure)
  153. (setq-local electric-indent-chars nil)
  154. (setq-local truncate-lines t))
  155. (provide 'indium-inspector)
  156. ;;; indium-inspector.el ends here