Permalink
Browse files

Add support for source maps

  • Loading branch information...
1 parent 929b909 commit a6161d13bf64a6136e8272e25df3261e13e4c644 @jscheid committed Sep 8, 2012
Showing with 530 additions and 21 deletions.
  1. +109 −19 kite-debug.el
  2. +108 −0 kite-sourcemap-tests.el
  3. +307 −0 kite-sourcemap.el
  4. +6 −2 kite.el
View
@@ -155,12 +155,11 @@
(kite-session kite-session))
(kite-visit-script
script-info
+ line-number
+ column-number
(lambda ()
(kite-debugging-mode)
- (set (make-local-variable 'kite-session) kite-session)
- (goto-line line-number)
- (beginning-of-line)
- (forward-char column-number))))
+ (set (make-local-variable 'kite-session) kite-session))))
(message "Debugger paused"))))
(defun kite--Debugger-scriptParsed (websocket-url packet)
@@ -171,7 +170,8 @@
:start-line (plist-get packet :startLine)
:start-column (plist-get packet :startColumn)
:end-line (plist-get packet :endLine)
- :end-column (plist-get packet :endColumn))
+ :end-column (plist-get packet :endColumn)
+ :source-map-url (plist-get packet :sourceMapURL))
(kite-session-script-infos kite-session)))
(add-hook 'kite-Debugger-paused-hooks 'kite--Debugger-paused)
@@ -229,18 +229,109 @@
(funcall after-load-function))))
new-buffer))
-(defun kite-visit-script (script-info after-load-function)
+(defun kite-script-info--source-map (script-info)
+ "Return the parsed source map for the given SCRIPT-INFO as a
+`kite-source-map' struct, or nil if there is no source map for
+the SCRIPT-INFO. Raise an error if the source map can't be
+loaded or parsed."
+ (when (kite-script-info-source-map-url script-info)
+ (with-current-buffer
+ (url-retrieve-synchronously
+ (url-expand-file-name
+ (kite-script-info-source-map-url script-info)
+ (kite-script-info-url script-info)))
+ (goto-char 0)
+ (if (and (or (looking-at "HTTP/1\\.. 200")
+ (not (looking-at "HTTP/")))
+ (re-search-forward "\n\n" nil t))
+ (kite--source-map-decode
+ (let ((json-array-type 'list)
+ (json-object-type 'plist))
+ (json-read)))
+ (error "Could not retrieve source map: %s"
+ (buffer-substring-no-properties
+ (point-min) (point-max)))))))
+
+(defun kite-script-info--source-map-cached (script-info)
+ "Return the parsed source map for the given SCRIPT-INFO as a
+`kite-source-map' struct, or nil if there is no source map for
+the SCRIPT-INFO. Raise an error if the source map can't be
+loaded or parsed. Uses a cache in the session so that each
+source map is loaded and parsed only once."
+ (when (kite-script-info-source-map-url script-info)
+ (let ((cached-entry
+ (gethash (kite-script-info-source-map-url script-info)
+ (kite-session-source-map-cache kite-session))))
+ (cond
+ ((kite-source-map-p cached-entry)
+ cached-entry)
+ ((consp cached-entry)
+ (signal (car err) (cdr err)))
+ (t
+ (condition-case err
+ (puthash (kite-script-info-source-map-url script-info)
+ (kite-script-info--source-map script-info)
+ (kite-session-source-map-cache kite-session))
+ (error
+ (puthash (kite-script-info-source-map-url script-info)
+ err
+ (kite-session-source-map-cache kite-session))
+ (signal (car err) (cdr err)))))))))
+
+(defun kite-script-info--original-source (script-info line column)
+ "Return original URL, line, and column corresponding to the
+given SCRIPT-INFO, LINE, and COLUMN. The original location is
+returned as a plist with keys `:url', `:line' and `:column'."
+ (let ((source-map
+ (condition-case err
+ (kite-script-info--source-map-cached script-info)
+ (error
+ ;; In case of error, display error and fall back to
+ ;; generated source
+ (message (cdr err))
+ nil))))
+ (if source-map
+ (let ((original-pos
+ (kite-source-map-original-position-for
+ source-map
+ line
+ column)))
+ (list :url
+ (url-expand-file-name
+ (plist-get original-pos :source)
+ (kite-script-info-url script-info))
+ :line (plist-get original-pos :line)
+ :column (plist-get original-pos :column)))
+ (list :url (kite-script-info-url script-info)
+ :line line
+ :column column))))
+
+(defun kite-visit-script (script-info line column after-load-function)
+ "Visit the script described by the given SCRIPT-INFO and, once
+loaded, move point to LINE and COLUMN and execute
+AFTER-LOAD-FUNCTION with the new buffer current. If a source map
+is available, go to the original location instead."
(interactive)
- (let* ((url (kite-script-info-url script-info))
+ (let* ((original-source (kite-script-info--original-source
+ script-info
+ line
+ column))
+ (url (plist-get original-source :url))
(url-parts (url-generic-parse-url url)))
- (cond
- ((string= (url-type url-parts) "file")
- (find-file (url-filename url-parts))
- (funcall after-load-function))
- (t
- (switch-to-buffer (or (get-buffer url)
- (kite--create-remote-script-buffer
- script-info after-load-function)))))))
+ (flet ((after-load ()
+ (goto-line (plist-get original-source :line))
+ (beginning-of-line)
+ (forward-char
+ (plist-get original-source :column))
+ (funcall after-load-function)))
+ (cond
+ ((string= (url-type url-parts) "file")
+ (find-file (url-filename url-parts))
+ (after-load))
+ (t
+ (switch-to-buffer (or (get-buffer url)
+ (kite--create-remote-script-buffer
+ script-info (function after-load)))))))))
(defun kite--debug-stats-mode-line-indicator ()
"Returns a string to be displayed in the mode line"
@@ -270,11 +361,10 @@ to visit the source file."
(if script-info
(kite-visit-script
script-info
+ line-number
+ (- column-number 1)
(lambda ()
- (set (make-local-variable 'kite-session) kite-session)
- (goto-line line-number)
- (beginning-of-line)
- (forward-char (- column-number 1))))
+ (set (make-local-variable 'kite-session) kite-session)))
(error "Source is unavailable"))))
(provide 'kite-debug)
@@ -0,0 +1,108 @@
+;;; kite-sourcemap-tests.el --- Kite test suite for source map decoding
+
+;; Copyright (C) 2012 Julian Scheid
+
+;; Author: Julian Scheid <julians37@gmail.com>
+;; Keywords: tools
+;; Package: kite
+;; Compatibility: GNU Emacs 24
+
+;; This file is not part of GNU Emacs.
+
+;; Kite 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.
+
+;; Kite 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 Kite. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Kite test suite for source map decoding.
+;;
+;; It is part of Kite, a WebKit inspector front-end.
+
+
+;;; Code:
+
+(ert-deftest kite-test-base64-decoding ()
+ "Base64 decoding works"
+
+ (should (eq (kite--base64-decode ?A) 0))
+ (should (eq (kite--base64-decode ?Z) 25))
+ (should (eq (kite--base64-decode ?a) 26))
+ (should (eq (kite--base64-decode ?z) 51))
+ (should (eq (kite--base64-decode ?0) 52))
+ (should (eq (kite--base64-decode ?9) 61))
+ (should (eq (kite--base64-decode ?+) 62))
+ (should (eq (kite--base64-decode ?/) 63))
+ (should-error (kite--base64-decode ?%)))
+
+(ert-deftest kite-test-from-vlq-signed ()
+ "VLQ sign conversion works"
+ (should (eq (kite--from-vlq-signed 2) 1))
+ (should (eq (kite--from-vlq-signed 3) -1))
+ (should (eq (kite--from-vlq-signed 4) 2))
+ (should (eq (kite--from-vlq-signed 5) -2)))
+
+(ert-deftest kite-test-vlq-decoding ()
+ "VLQ decoding works"
+
+ (should (equal (kite--base64-vlq-decode (string-to-list "A"))
+ `(0 ,(string-to-list ""))))
+
+ (should (equal (kite--base64-vlq-decode (string-to-list "zA"))
+ `(-9 ,(string-to-list ""))))
+
+ (should (equal (kite--base64-vlq-decode (string-to-list "zza"))
+ `(-13625 ,(string-to-list ""))))
+
+ (should (equal (kite--base64-vlq-decode (string-to-list "gzX"))
+ `(12080 ,(string-to-list "")))))
+
+(defconst kite-test-source-map-json
+ (list :version 3
+ :file "test.coffee"
+ :sources '["test.coffee"]
+ :names []
+ :mappings "\
+AAAC;;;EAAA,eAAK,IAAL,GAAa,SAAA,CAAA,QAAA,CAAA;;;MACV,2BAAmB,YAAnB,aAAA,\
+CAAA,KAAA,CAAA;QAAI,OAAe;QAAT;oBACR,QAAQ,KAAR,CAAc,IAAd,EAAoB,KAApB;;;;;\
+EAED,GAAA,GAAM,CAAA;AAAA,IAAC,CAAD;AAAA,IAAG,CAAH;AAAA,IAAK,CAAL;AAAA,\
+EAAA;EACV,MAAA,GAAS,GAAG,IAAH,CAAQ,SAAA,CAAA,IAAA,CAAA;WAAU,IAAA,CAAA,\
+CAAA,CAAO;GAAzB;EAET,OAAO,IAAP,CAAY,MAAZ"))
+
+(ert-deftest kite-test-source-map ()
+ "Source map decoding works"
+ (let ((source-map (kite--source-map-decode
+ kite-test-source-map-json)))
+
+ (should (equal (kite-source-map-sources source-map) '["test.coffee"]))
+ (should (equal (kite-source-map-names source-map) '[]))
+ (should (eq (length (kite-source-map-generated-mappings source-map)) 62))
+ (let ((mapping (elt (kite-source-map-generated-mappings source-map) 20)))
+ (should (kite-source-mapping-p mapping))
+ (should (eq (kite-source-mapping-generated-column mapping) 28))
+ (should (eq (kite-source-mapping-generated-line mapping) 10))
+ (should (string= (kite-source-mapping-source mapping) "test.coffee"))
+ (should (eq (kite-source-mapping-original-column mapping) 14))
+ (should (eq (kite-source-mapping-original-line mapping) 3))
+ (should (null (kite-source-mapping-name mapping))))))
+
+(ert-deftest kite-test-source-map-lookup ()
+ "Source map lookup works"
+ (let ((source-map (kite--source-map-decode
+ kite-test-source-map-json)))
+
+ (should (equal (kite-source-map-original-position-for source-map 23 10)
+ (list :source "test.coffee"
+ :line 8
+ :column 8
+ :name nil)))))
+
Oops, something went wrong. Retry.

0 comments on commit a6161d1

Please sign in to comment.