Browse Source

Add support for breakpoints in files with sourcemaps

workspaces
Nicolas Petton 3 years ago
parent
commit
74dfcfbcbb
Signed by: nico GPG Key ID: 233587A47C207910
6 changed files with 108 additions and 38 deletions
  1. +11
    -10
      indium-breakpoint.el
  2. +4
    -1
      indium-interaction.el
  3. +60
    -19
      indium-script.el
  4. +1
    -0
      indium-webkit.el
  5. +3
    -2
      test/unit/indium-breakpoint-test.el
  6. +29
    -6
      test/unit/indium-script-test.el

+ 11
- 10
indium-breakpoint.el View File

@ -30,21 +30,20 @@
(require 'indium-backend)
(require 'indium-faces)
(require 'indium-script)
(defun indium-breakpoint-add (&optional condition)
"Add a breakpoint at point.
(defun indium-breakpoint-add (location &optional condition)
"Add a breakpoint at LOCATION.
When CONDITION is non-nil, the breakpoint will be hit when
CONDITION is true."
(let ((ov (indium-breakpoint--put-icon condition))
(location (make-indium-location :file buffer-file-name
:line (1- (line-number-at-pos)))))
(let ((ov (indium-breakpoint--put-icon condition)))
(when indium-connection
(indium-backend-add-breakpoint (indium-backend)
location
(lambda (id)
(indium-breakpoint-added id ov))
condition))))
(lambda (id)
(indium-breakpoint-added id ov))
condition))))
(defun indium-breakpoint-edit-condition ()
"Edit condition of breakpoint at point."
@ -58,7 +57,8 @@ CONDITION is true."
new-condition)))
(map-put breakpoint 'condition new-condition)
(indium-breakpoint-remove)
(indium-breakpoint-add new-condition)))
(indium-breakpoint-add (indium-script-generated-location-at-point)
new-condition)))
(defun indium-breakpoint-remove ()
"Remove the breakpoint from the current line."
@ -112,7 +112,8 @@ This function is used when reconnecting to a new connection."
(let ((condition (overlay-get ov 'indium-breakpoint-condition))
(start (overlay-start ov)))
(goto-char start)
(indium-breakpoint-add condition)))))))))
(indium-breakpoint-add (indium-script-generated-location-at-point)
condition)))))))))
(defun indium-breakpoint--put-icon (&optional condition)
"Add a breakpoint icon on the current line.


+ 4
- 1
indium-interaction.el View File

@ -37,6 +37,7 @@
(require 'indium-breakpoint)
(require 'indium-repl)
(require 'indium-render)
(require 'indium-script)
(declare-function indium-backend-activate-breakpoints "indium-backend.el")
(declare-function indium-backend-deactivate-breakpoints "indium-backend.el")
@ -123,7 +124,9 @@ If PRINT is non-nil, print the output into the current buffer."
(defun indium-add-breakpoint ()
"Add a breakpoint at point."
(interactive)
(indium-breakpoint-add))
(if-let ((location (indium-script-generated-location-at-point)))
(indium-breakpoint-add location)
(user-error "Cannot place a breakpoint here")))
(defun indium-add-conditional-breakpoint ()
"Add a conditional breakpoint at point."


+ 60
- 19
indium-script.el View File

@ -85,6 +85,20 @@ Return nil if no script can be found."
(when-let ((sourcemap-url (indium-script-sourcemap-url script)))
(not (seq-empty-p sourcemap-url))))
(defun indium-script-all-scripts-with-sourcemap ()
"Return all parsed scripts that contain a sourcemap."
(seq-filter #'indium-script-has-sourcemap-p
(map-values (map-elt indium-connection 'scripts))))
(defun indium-script-sourcemap-file (script)
"Return the local sourcemap file associated with SCRIPT.
If no sourcemap file can be found, return nil."
(when (indium-script-has-sourcemap-p script)
(when-let ((script-file (indium-script-get-file script)))
(indium-workspace-lookup-file-safe
(expand-file-name (indium-script-sourcemap-url script)
(file-name-directory script-file))))))
(defun indium-script-get-frame-original-location (frame)
"Return the location stack FRAME, possibly using sourcemaps."
(let* ((script (indium-frame-script frame))
@ -97,32 +111,60 @@ Return nil if no script can be found."
(defun indium-script-original-location (script location)
"Use the sourcemap of SCRIPT to lookup its original LOCATION.
If SCRIPT has no sourcemap, return LOCATION."
(if (and indium-script-enable-sourcemaps
(indium-script-has-sourcemap-p script))
(if-let ((script-file (indium-script-get-file script))
(sourcemap-file (indium-workspace-lookup-file-safe
(expand-file-name (indium-script-sourcemap-url script)
(file-name-directory script-file)))))
(let* ((sourcemap (sourcemap-from-file sourcemap-file))
(original-location (indium-script--sourcemap-original-position-for
(if indium-script-enable-sourcemaps
(if-let ((sourcemap-file (indium-script-sourcemap-file script))
(sourcemap (sourcemap-from-file sourcemap-file))
(original-location (indium-script--sourcemap-original-position-for
sourcemap
:line (1+ (indium-location-line location))
:column (1+ (indium-location-column location))
:nearest t)))
(if original-location
(let ((file (expand-file-name (plist-get original-location :source)
(file-name-directory script-file))))
(make-indium-location :file file
:line (max 0 (1- (plist-get original-location :line)))
:column (max 0 (1- (plist-get original-location :column)))))
(progn
(message "Could not locate original position from sourcemap!")
location)))
:nearest t))
(file (expand-file-name (plist-get original-location :source)
(file-name-directory (indium-script-get-file script)))))
(make-indium-location :file file
:line (max 0 (1- (plist-get original-location :line)))
:column (plist-get original-location :column))
(progn
(message "The sourcemap file does not exist!")
location))
location))
(defun indium-script-generated-location (location)
"Return a generated location from the original LOCATION.
If there is a parsed script for LOCATION's file, return LOCATION.
Otherwise, if a sourcemap exists, generate a location using that
sourcemap."
(let ((file (indium-location-file location)))
(if (indium-script-find-from-file file)
location
(if indium-script-enable-sourcemaps
(or (seq-some (lambda (script)
(if-let ((sourcemap-file (indium-script-sourcemap-file script))
(script-file (indium-script-get-file script))
(sourcemap (sourcemap-from-file sourcemap-file))
(generated-location (sourcemap-generated-position-for
sourcemap
:source (file-relative-name
(indium-location-file location)
(file-name-directory script-file))
:line (1+ (indium-location-line location))
:column 0
:nearest t)))
(make-indium-location :file script-file
:line (max 0 (1- (plist-get generated-location :line)))
:column (plist-get generated-location :column))))
(indium-script-all-scripts-with-sourcemap))
location)
location))))
(defun indium-script-generated-location-at-point ()
"Return a location for the position of POINT.
If no location can be found, return nil."
(indium-script-generated-location
(make-indium-location :file buffer-file-name
:line (1- (line-number-at-pos)))))
;; TODO: wait for https://github.com/syohex/emacs-sourcemap/pull/6 to be merged
(defun indium-script--sourcemap-original-position-for (sourcemap &rest props)
"Lookup a position in SOURCEMAP based on PROPS.
@ -136,7 +178,6 @@ PROPS should be a plist with a `:line' and `:column' key."
:line (sourcemap-entry-original-line ret)
:column (sourcemap-entry-original-column ret))))))
(memoize 'indium-script-original-location "2 minutes")
(provide 'indium-script)


+ 1
- 0
indium-webkit.el View File

@ -108,6 +108,7 @@ non-nil, evaluate it with the breakpoint's location and id."
`((method . "Debugger.setBreakpointByUrl")
(params . ((url . ,url)
(lineNumber . ,(indium-location-line location))
(columnNumber . ,(indium-location-column location))
(condition . ,condition))))
(lambda (response)
(let* ((breakpoint (map-elt response 'result))


+ 3
- 2
test/unit/indium-breakpoint-test.el View File

@ -45,11 +45,12 @@
(spy-on #'indium-breakpoint-add)
(with-js2-buffer "let a = 1;"
(goto-char (point-min))
(indium-breakpoint-add "old condition")
(indium-breakpoint-add 'position "old condition")
(indium-breakpoint-edit-condition)
(expect #'read-from-minibuffer :to-have-been-called)
(expect #'indium-breakpoint-remove :to-have-been-called)
(expect #'indium-breakpoint-add :to-have-been-called-with "new condition"))))
;; called with nil because no valid position could be generated
(expect #'indium-breakpoint-add :to-have-been-called-with nil "new condition"))))
(describe "Breakpoint duplication handling"
(it "can add a breakpoint multiple times on the same line without duplicating it"


+ 29
- 6
test/unit/indium-script-test.el View File

@ -23,23 +23,46 @@
;;; Code:
(require 'buttercup)
(require 'assess)
(require 'indium-script)
(defvar indium-script--test-fs
'(".indium"
("js" ("foo.js" "foo.js.map" "bar.js")))
"Fake filesystem used in script tests.")
(describe "Looking up scripts"
(it "should be able to retrieve parsed scripts url"
(with-fake-indium-connection
(indium-script-add-script-parsed "1" "foo")
(expect (indium-script-url (indium-script-find-by-id "1")) :to-equal "foo")))
(indium-script-add-script-parsed "1" "foo")
(expect (indium-script-url (indium-script-find-by-id "1")) :to-equal "foo")))
(it "should be able to retrieve parsed scripts sourcemap url"
(with-fake-indium-connection
(indium-script-add-script-parsed "1" "foo" "foo-map")
(expect (indium-script-sourcemap-url (indium-script-find-by-id "1")) :to-equal "foo-map")))
(indium-script-add-script-parsed "1" "foo" "foo-map")
(expect (indium-script-sourcemap-url (indium-script-find-by-id "1")) :to-equal "foo-map")))
(it "should be able to retrieve parsed scripts ids"
(with-fake-indium-connection
(indium-script-add-script-parsed "1" "foo")
(expect (indium-script-id (indium-script-find-from-url "foo")) :to-equal "1"))))
(indium-script-add-script-parsed "1" "foo")
(expect (indium-script-id (indium-script-find-from-url "foo")) :to-equal "1")))
(it "should be able to return all scripts with a sourcemap"
(with-fake-indium-connection
(indium-script-add-script-parsed "1" "foo")
(indium-script-add-script-parsed "2" "bar" "bar.map")
(expect (seq-map #'indium-script-id
(indium-script-all-scripts-with-sourcemap))
:to-equal '("2"))))
(it "should be able to find the sourcemap file for a script"
(assess-with-filesystem indium-script--test-fs
(with-fake-indium-connection
(indium-script-add-script-parsed "1" "http://localhost/js/foo.js" "foo.js.map")
(spy-on 'indium-repl-get-buffer :and-return-value (find-file-noselect (expand-file-name ".")))
(let ((script (indium-script-find-by-id "1")))
(expect (indium-script-sourcemap-file script)
:to-equal (expand-file-name "js/foo.js.map")))))))
(provide 'indium-script-test)
;;; indium-script-test.el ends here

Loading…
Cancel
Save