diff options
| author | rocky <rocky@gnu.org> | 2018-03-08 21:15:50 -0500 |
|---|---|---|
| committer | rocky <rocky@gnu.org> | 2018-03-09 00:04:44 -0500 |
| commit | 1bf7fc50dea38b89e60234d2f03cc2dc3292d381 (patch) | |
| tree | 273e375515d1407783bcef32a04abf6482ef3f74 | |
"node inspect" with V8 inspector support.
Not working yet. For node before version 6, an older protocol is used.
In realgud it is called nodejs.
| -rw-r--r-- | .gitignore | 21 | ||||
| -rw-r--r-- | INSTALL.md | 5 | ||||
| -rw-r--r-- | Makefile.am | 49 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rwxr-xr-x | autogen.sh | 12 | ||||
| -rw-r--r-- | common.mk | 24 | ||||
| -rw-r--r-- | common.mk.in | 24 | ||||
| -rwxr-xr-x | compute-lispdir.sh | 46 | ||||
| -rw-r--r-- | configure.ac | 55 | ||||
| -rw-r--r-- | node-inspect/Makefile.am | 5 | ||||
| -rw-r--r-- | node-inspect/core.el | 169 | ||||
| -rw-r--r-- | node-inspect/init.el | 177 | ||||
| -rw-r--r-- | node-inspect/main.el | 85 | ||||
| -rw-r--r-- | node-inspect/track-mode.el | 84 | ||||
| -rw-r--r-- | realgud-node-inspect.el | 48 | ||||
| -rw-r--r-- | test/Makefile.am | 70 | ||||
| -rw-r--r-- | test/gcd.js | 45 | ||||
| -rw-r--r-- | test/make-check-filter.rb | 24 | ||||
| -rw-r--r-- | test/regexp-helper.el | 50 | ||||
| -rw-r--r-- | test/test-loc-regexp-node-inspect.el | 118 | ||||
| -rw-r--r-- | test/test-node-inspect.el | 37 |
21 files changed, 1154 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e2b8ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +*.elc +*~ +/*-pkg.el +/*.tar.gz +/README +/aclocal.m4 +/autom4te.cache +/config.log +/config.status +/configure +/configure.lineno +/elpa +/install-sh +/missing +/test/npm-debug.log +/tmp +Makefile +Makefile.in +elc-stamp +elc-temp +script diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..4d46fae --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,5 @@ +* Have `realgud` installed. +* From inside emacs, evaluate: +```lisp + (compile (format "EMACSLOADPATH=:%s:%s ./autogen.sh" (file-name-directory (locate-library "test-simple.elc")) (file-name-directory (locate-library "realgud.elc")))) +``` diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..66d3f8b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,49 @@ +# Note: This makefile include remake-style target comments. +# These comments before the targets start with #: +# remake --tasks to shows the targets and the comments + +SUBDIRS = node-inspect test + +GIT2CL ?= git2cl +RUBY ?= ruby + +lispdir = @lispdir@ + +lisp_files = $(wildcard *.el) +lisp_LISP = $(lisp_files) +include common.mk + +PHONY=check clean dist distclean test check-short check-terse install-short + +EXTRA_DIST=common.mk.in README.md THANKS $(lisp_files) test/gcd.rb + +if MAINTAINER_MODE + +#: Remove change log: ChangeLog +rmChangeLog: + rm ChangeLog || true + +#: Create a ChangeLog file from git logs +ChangeLog: rmChangeLog + git log --pretty --numstat --summary | $(GIT2CL) > $@ + +ACLOCAL_AMFLAGS=-I . + +endif + +#: Run all tests +test: check + +#: Run all tests without bloated output +check-short: + $(MAKE) check 2>&1 | $(RUBY) test/make-check-filter.rb + +#: Run all tests without and show just the failure lines +check-terse: + $(MAKE) check 2>&1 | $(RUBY) tes/make-check-filter.rb | grep failure + +#: Run "make install" +install-short: + $(MAKE) install 2>&1 | $(RUBY) test/make-check-filter.rb + +.PHONY: test check check-short rmChangeLog diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c71ef3 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +Module to add [node inspect](https://nodejs.org/api/debugger.html) +debugger support to emacs +[realgud](http://github.com/realgud/realgud). + +For node before version 6, an older protocol is used. In realgud, +that debugger for the older version is called nodejs. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..7beb4b9c --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# An autoconf setup script. +# From inside emacs, make sure test-simple is installed and then +# Press C-x C-e at the end of the next line run this script +# (test-simple-run "EMACSLOADPATH=%s ./autogen.sh" (mapconcat 'identity load-path ":")) +ln -fs README.md README +touch common.mk +autoreconf -vfi && \ +autoconf && { + echo "Running configure with --enable-maintainer-mode $@" + ./configure --enable-maintainer-mode $@ +} diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..ac10f31 --- /dev/null +++ b/common.mk @@ -0,0 +1,24 @@ +MOSTLYCLEANFILES = *.elc +EMACSLOADPATH=:/home/rocky/.emacs.d/elpa/test-simple-20170527.832/:/home/rocky/.emacs.d/elpa/realgud-20180207.1330/ + +short: + $(MAKE) 2>&1 >/dev/null | ruby $(top_srcdir)/make-check-filter.rb + +%.short: + $(MAKE) $(@:.short=) 2>&1 >/dev/null + +# This is the default rule, but we need to include an EMACLOADPATH +.el.elc: + if test "$(EMACS)" != "no"; then \ + am__dir=. am__subdir_includes=''; \ + case $@ in */*) \ + am__dir=`echo '$@' | sed 's,/[^/]*$$,,'`; \ + am__subdir_includes="-L $$am__dir -L $(srcdir)/$$am__dir"; \ + esac; \ + test -d "$$am__dir" || $(MKDIR_P) "$$am__dir" || exit 1; \ + EMACSLOADPATH=$(EMACSLOADPATH) $(EMACS) --batch \ + $(AM_ELCFLAGS) $(ELCFLAGS) \ + $$am__subdir_includes -L $(builddir) -L $(srcdir) \ + --eval "(defun byte-compile-dest-file (f) \"$@\")" \ + --eval "(unless (byte-compile-file \"$<\") (kill-emacs 1))"; \ + else :; fi diff --git a/common.mk.in b/common.mk.in new file mode 100644 index 0000000..c28f493 --- /dev/null +++ b/common.mk.in @@ -0,0 +1,24 @@ +MOSTLYCLEANFILES = *.elc +EMACSLOADPATH=@EMACSLOADPATH@ + +short: + $(MAKE) 2>&1 >/dev/null | ruby $(top_srcdir)/make-check-filter.rb + +%.short: + $(MAKE) $(@:.short=) 2>&1 >/dev/null + +# This is the default rule, but we need to include an EMACLOADPATH +.el.elc: + if test "$(EMACS)" != "no"; then \ + am__dir=. am__subdir_includes=''; \ + case $@ in */*) \ + am__dir=`echo '$@' | sed 's,/[^/]*$$,,'`; \ + am__subdir_includes="-L $$am__dir -L $(srcdir)/$$am__dir"; \ + esac; \ + test -d "$$am__dir" || $(MKDIR_P) "$$am__dir" || exit 1; \ + EMACSLOADPATH=$(EMACSLOADPATH) $(EMACS) --batch \ + $(AM_ELCFLAGS) $(ELCFLAGS) \ + $$am__subdir_includes -L $(builddir) -L $(srcdir) \ + --eval "(defun byte-compile-dest-file (f) \"$@\")" \ + --eval "(unless (byte-compile-file \"$<\") (kill-emacs 1))"; \ + else :; fi diff --git a/compute-lispdir.sh b/compute-lispdir.sh new file mode 100755 index 0000000..220befd --- /dev/null +++ b/compute-lispdir.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Figures out a reasonable --prefix +typeset -i rc=0 +typeset -i DEBUG=${DEBUG:-0} +EMACS_PROG=${EMACS_PROG:-emacs} +list=$($EMACS_PROG --batch --no-splash --no-site-file --eval '(message (substring (format "%s" load-path) 1 -1))' 2>&1) +rc=$? +if (( rc != 0 )) ; then + echo >&2 "Something went wrong running $EMACS_PROG" + exit $rc +$cmd +fi +for dir in $list ; do + if [[ -d $dir ]] ; then + case $dir in + */emacs/site-lisp) + ((DEBUG)) && echo "site lisp: $dir" + echo "$dir" + exit 0 + ;; + esac + fi +done +for dir in $list ; do + if [[ -d $dir ]] ; then + case $dir in + */emacs/2[34]\.[0-9]/site-lisp) + ((DEBUG)) && echo "versioned site lisp: $dir" + echo "$dir" + exit 0 + ;; + esac + fi +done +for dir in $list ; do + if [[ -d $dir ]] ; then + case $dir in + */emacs/2[34]\.[0-9]/site-lisp) + ((DEBUG)) && echo "versioned site lisp: $dir" + echo "$dir" + exit 0 + ;; + esac + fi +done +exit 0 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..6d5d73d --- /dev/null +++ b/configure.ac @@ -0,0 +1,55 @@ +dnl FIXME: pick up from realgud.el +AC_INIT(realgud-inspect, 1.0,) +AC_CONFIG_SRCDIR(node-inspect/main.el) +AM_INIT_AUTOMAKE([foreign]) +AM_MAINTAINER_MODE + +AC_PATH_PROG([EMACS], [emacs], [emacs]) +AC_ARG_WITH(emacs, AC_HELP_STRING([--with-emacs], + [location of emacs program]), EMACS=$withval) + +AC_MSG_NOTICE("Checking emacs version") +$EMACS -batch -q --no-site-file -eval \ + '(if (<= emacs-major-version 23) + (progn + (error "You need GNU Emacs 24 or better.") + (kill-emacs 1) + ) + )' +if test $? -ne 0 ; then + AC_MSG_ERROR([Can't continue until above error is corrected.]) +fi + +################################################################## +# See if --with-lispdir was set. If not, set it to a reasonable default +# based on where bash thinks bashdb is supposed to be installed. +################################################################## + +AM_MISSING_PROG(GIT2CL, git2cl, $missing_dir) + +# Check whether --with-lispdir was given. +if test "${with_lispdir+set}" = set; then : +else + my_lispdir=$(EMACS_PROG=$EMACS $SH_PROG $(dirname $0)/compute-lispdir.sh) + if test "${my_lispdir+set}" = set; then : + with_lispdir=$my_lispdir + echo "'compute-lispdir.sh' lispdir install directory override: '$with_lispdir'" + fi +fi + +## +## Find out where to install the debugger emacs lisp files +## +AM_PATH_LISPDIR +lispdir_realgud=$lispdir/realgud +AC_SUBST([lispdir]) +AC_SUBST([lispdir_realgud]) + +AM_CONDITIONAL(INSTALL_EMACS_LISP, test "x$lispdir_realgud" != "x") + +AC_CONFIG_FILES([Makefile \ + common.mk \ + node-inspect/Makefile \ + test/Makefile \ + ]) +AC_OUTPUT diff --git a/node-inspect/Makefile.am b/node-inspect/Makefile.am new file mode 100644 index 0000000..aa624b8 --- /dev/null +++ b/node-inspect/Makefile.am @@ -0,0 +1,5 @@ +lisp_files = $(wildcard *.el) +EXTRA_DIST=$(lisp_files) +lisp_LISP = $(lisp_files) +include ../common.mk +lispdir = @lispdir@/node-inspect diff --git a/node-inspect/core.el b/node-inspect/core.el new file mode 100644 index 0000000..b3a13f1 --- /dev/null +++ b/node-inspect/core.el @@ -0,0 +1,169 @@ +;;; Copyright (C) 2015-2018 Rocky Bernstein <rocky@gnu.org> +;; 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/>. + +(eval-when-compile (require 'cl-lib)) + +(require 'realgud) + +(declare-function realgud:expand-file-name-if-exists 'realgud-core) +(declare-function realgud-lang-mode? 'realgud-lang) +(declare-function realgud-parse-command-arg 'realgud-core) +(declare-function realgud-query-cmdline 'realgud-core) + +;; FIXME: I think the following could be generalized and moved to +;; realgud-... probably via a macro. +(declare-function realgud:expand-file-name-if-exists 'realgud-core) +(declare-function realgud-parse-command-arg 'realgud-core) +(declare-function realgud-query-cmdline 'realgud-core) +(declare-function realgud-suggest-invocation 'realgud-core) + +;; FIXME: I think the following could be generalized and moved to +;; realgud-... probably via a macro. +(defvar realgud:node-inspect-minibuffer-history nil + "minibuffer history list for the command `node-inspect'.") + +(easy-mmode-defmap realgud:node-inspect-minibuffer-local-map + '(("\C-i" . comint-dynamic-complete-filename)) + "Keymap for minibuffer prompting of node-inspect startup command." + :inherit minibuffer-local-map) + +;; FIXME: I think this code and the keymaps and history +;; variable chould be generalized, perhaps via a macro. +(defun node-inspect-query-cmdline (&optional opt-debugger) + (realgud-query-cmdline + 'realgud:node-inspect-suggest-invocation + realgud:node-inspect-minibuffer-local-map + 'realgud:node-inspect-minibuffer-history + opt-debugger)) + +;;; FIXME: DRY this with other *-parse-cmd-args routines +(defun node-inspect-parse-cmd-args (orig-args) + "Parse command line ORIG-ARGS for the name of script to debug. + +ORIG-ARGS should contain a tokenized list of the command line to run. + +We return the a list containing +* the name of the debugger given (e.g. node-inspect) and its arguments - a list of strings +* the script name and its arguments - list of strings + +For example for the following input: + (map 'list 'symbol-name + '(node --interactive --debugger-port 5858 /tmp node-inspect ./gcd.js a b)) + +we might return: + ((\"node\" \"--interactive\" \"--debugger-port\" \"5858\") nil (\"/tmp/gcd.js\" \"a\" \"b\")) + +Note that path elements have been expanded via `expand-file-name'. +" + + ;; Parse the following kind of pattern: + ;; node node-inspect-options script-name script-options + (let ( + (args orig-args) + (pair) ;; temp return from + (node-two-args '("-debugger_port" "C" "D" "i" "l" "m" "-module" "x")) + ;; node doesn't have any optional two-arg options + (node-opt-two-args '()) + + ;; One dash is added automatically to the below, so + ;; h is really -h and -debugger_port is really --debugger_port. + (node-inspect-two-args '("-debugger_port")) + (node-inspect-opt-two-args '()) + + ;; Things returned + (script-name nil) + (debugger-name nil) + (interpreter-args '()) + (script-args '()) + ) + (if (not (and args)) + ;; Got nothing: return '(nil, nil, nil) + (list interpreter-args nil script-args) + ;; else + (progn + ;; Remove "node-inspect" (or "nodemon" or "node") from invocation like: + ;; node-inspect --node-inspect-options script --script-options + (setq debugger-name (file-name-sans-extension + (file-name-nondirectory (car args)))) + (unless (string-match "^node\\(?:js\\|mon\\)?$" debugger-name) + (message + "Expecting debugger name `%s' to be `node', `nodemon', or `node-inspect'" + debugger-name)) + (setq interpreter-args (list (pop args))) + + ;; Skip to the first non-option argument. + (while (and args (not script-name)) + (let ((arg (car args))) + (cond + ((equal "debug" arg) + (nconc interpreter-args (list arg)) + (setq args (cdr args)) + ) + + ;; Options with arguments. + ((string-match "^-" arg) + (setq pair (realgud-parse-command-arg + args node-inspect-two-args node-inspect-opt-two-args)) + (nconc interpreter-args (car pair)) + (setq args (cadr pair))) + ;; Anything else must be the script to debug. + (t (setq script-name (realgud:expand-file-name-if-exists arg)) + (setq script-args (cons script-name (cdr args)))) + ))) + (list interpreter-args nil script-args))) + )) + +;; To silence Warning: reference to free variable +(defvar realgud:node-inspect-command-name) + +(defun realgud:node-inspect-suggest-invocation (debugger-name) + "Suggest a node-inspect command invocation via `realgud-suggest-invocaton'" + (realgud-suggest-invocation realgud:node-inspect-command-name + realgud:node-inspect-minibuffer-history + "js" "\\.js$")) + +(defun realgud:node-inspect-remove-ansi-shmutz() + "Remove ASCII escape sequences that node.js 'decorates' in +prompts and interactive output with" + (add-to-list + 'comint-preoutput-filter-functions + (lambda (output) + (replace-regexp-in-string "\033\\[[0-9]+[GKJ]" "" output))) + ) + +(defun realgud:node-inspect-reset () + "Node-Inspect cleanup - remove debugger's internal buffers (frame, +breakpoints, etc.)." + (interactive) + ;; (node-inspect-breakpoint-remove-all-icons) + (dolist (buffer (buffer-list)) + (when (string-match "\\*node-inspect-[a-z]+\\*" (buffer-name buffer)) + (let ((w (get-buffer-window buffer))) + (when w + (delete-window w))) + (kill-buffer buffer)))) + +;; (defun node-inspect-reset-keymaps() +;; "This unbinds the special debugger keys of the source buffers." +;; (interactive) +;; (setcdr (assq 'node-inspect-debugger-support-minor-mode minor-mode-map-alist) +;; node-inspect-debugger-support-minor-mode-map-when-deactive)) + + +(defun realgud:node-inspect-customize () + "Use `customize' to edit the settings of the `node-inspect' debugger." + (interactive) + (customize-group 'realgud:node-inspect)) + +(provide-me "realgud:node-inspect-") diff --git a/node-inspect/init.el b/node-inspect/init.el new file mode 100644 index 0000000..27f8ccd --- /dev/null +++ b/node-inspect/init.el @@ -0,0 +1,177 @@ +;; Copyright (C) 2015-2016, 2018 Rocky Bernstein <rocky@gnu.org> + +;; 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/>. + +;;; node inspect debugger + +(eval-when-compile (require 'cl-lib)) + +(require 'realgud) +(require 'realgud-lang-js) +(require 'ansi-color) + +(defvar realgud:node-inspect-pat-hash) +(declare-function make-realgud-loc-pat (realgud-loc)) + +(defvar realgud:node-inspect-pat-hash (make-hash-table :test 'equal) + "Hash key is the what kind of pattern we want to match: +backtrace, prompt, etc. The values of a hash entry is a +realgud-loc-pat struct") + +;; before a command prompt. +;; For example: +;; break in /home/indutny/Code/git/indutny/myscript.js:1 +(setf (gethash "loc" realgud:node-inspect-pat-hash) + (make-realgud-loc-pat + :regexp (format + "\\(?:%s\\)*\\(?:break\\|exception\\) in %s:%s" + realgud:js-term-escape "\\([^:]+\\)" + realgud:regexp-captured-num) + :file-group 1 + :line-group 2)) + +;; Regular expression that describes a node-inspect command prompt +;; For example: +;; debug> +(setf (gethash "prompt" realgud:node-inspect-pat-hash) + (make-realgud-loc-pat + :regexp (format "^\\(?:%s\\)*debug> " realgud:js-term-escape) + )) + +;; Regular expression that describes a "breakpoint set" line +;; +;; (setf (gethash "brkpt-set" realgud:node-inspect-pat-hash) +;; (make-realgud-loc-pat +;; :regexp "^[*] \\([0-9]+\\) " +;; :line-group 1)) + +;; Regular expression that describes a V8 backtrace line. +;; For example: +;; at repl:1:7 +;; at Interface.controlEval (/src/external-vcs/github/trepanjs/lib/interface.js:352:18) +;; at REPLServer.b [as eval] (domain.js:183:18) +(setf (gethash "lang-backtrace" realgud:node-inspect-pat-hash) + realgud:js-backtrace-loc-pat) + +;; Regular expression that describes a debugger "delete" (breakpoint) +;; response. +;; For example: +;; Removed 1 breakpoint(s). +(setf (gethash "brkpt-del" realgud:node-inspect-pat-hash) + (make-realgud-loc-pat + :regexp (format "^Removed %s breakpoint(s).\n" + realgud:regexp-captured-num) + :num 1)) + + +(defconst realgud:node-inspect-frame-start-regexp "\\(?:^\\|\n\\)\\(?:#\\)") +(defconst realgud:node-inspect-frame-num-regexp realgud:regexp-captured-num) +(defconst realgud:node-inspect-frame-module-regexp "[^ \t\n]+") +(defconst realgud:node-inspect-frame-file-regexp "[^ \t\n]+") + +;; Regular expression that describes a node-inspect location generally shown +;; Regular expression that describes a debugger "backtrace" command line. +;; For example: +;; #0 module.js:380:17 +;; #1 dbgtest.js:3:9 +;; #2 Module._compile module.js:456:26 +;; #3 Module._extensions..js module.js:474:10 +;; #4 Module.load module.js:356:32 +;; #5 Module._load module.js:312:12 +;; #6 Module.runMain module.js:497:10 +; ;#7 timers.js:110:15 +(setf (gethash "debugger-backtrace" realgud:node-inspect-pat-hash) + (make-realgud-loc-pat + :regexp (concat realgud:node-inspect-frame-start-regexp + realgud:node-inspect-frame-num-regexp " " + "\\(?:" realgud:node-inspect-frame-module-regexp " \\)?" + "\\(" realgud:node-inspect-frame-file-regexp "\\)" + ":" + realgud:regexp-captured-num + ":" + realgud:regexp-captured-num + ) + :num 1 + :file-group 2 + :line-group 3 + :char-offset-group 4)) + +(defconst realgud:node-inspect-debugger-name "node-inspect" "Name of debugger") + +;; ;; Regular expression that for a termination message. +;; (setf (gethash "termination" realgud:node-inspect-pat-hash) +;; "^node-inspect: That's all, folks...\n") + +(setf (gethash "font-lock-keywords" realgud:node-inspect-pat-hash) + '( + ;; The frame number and first type name, if present. + ;; E.g. ->0 in file `/etc/init.d/apparmor' at line 35 + ;; --^- + ("^\\(->\\|##\\)\\([0-9]+\\) " + (2 realgud-backtrace-number-face)) + + ;; File name. + ;; E.g. ->0 in file `/etc/init.d/apparmor' at line 35 + ;; ---------^^^^^^^^^^^^^^^^^^^^- + ("[ \t]+\\(in\\|from\\) file `\\(.+\\)'" + (2 realgud-file-name-face)) + + ;; File name. + ;; E.g. ->0 in file `/etc/init.d/apparmor' at line 35 + ;; --------^^ + ;; Line number. + ("[ \t]+at line \\([0-9]+\\)$" + (1 realgud-line-number-face)) + )) + +(setf (gethash "node-inspect" realgud-pat-hash) + realgud:node-inspect-pat-hash) + +;; Prefix used in variable names (e.g. short-key-mode-map) for +;; this debugger + +(setf (gethash "node-inspect" realgud:variable-basename-hash) + "realgud:node-inspect") + +(defvar realgud:node-inspect-command-hash (make-hash-table :test 'equal) + "Hash key is command name like 'finish' and the value is + the node-inspect command to use, like 'out'") + +(setf (gethash realgud:node-inspect-debugger-name + realgud-command-hash) + realgud:node-inspect-command-hash) + +(setf (gethash "backtrace" realgud:node-inspect-command-hash) "backtrace") +(setf (gethash "break" realgud:node-inspect-command-hash) + "setBreakpoint('%X',%l)") +(setf (gethash "continue" realgud:node-inspect-command-hash) "cont") +(setf (gethash "kill" realgud:node-inspect-command-hash) "kill") +(setf (gethash "quit" realgud:node-inspect-command-hash) "") +(setf (gethash "finish" realgud:node-inspect-command-hash) "out") +(setf (gethash "shell" realgud:node-inspect-command-hash) "repl") +(setf (gethash "eval" realgud:node-inspect-command-hash) "exec('%s')") +(setf (gethash "up" realgud:node-inspect-command-hash) "*not-implemented*") +(setf (gethash "down" realgud:node-inspect-command-hash) "*not-implemented*") +(setf (gethash "frame" realgud:node-inspect-command-hash) "*not-implemented*") + +;; We need aliases for step and next because the default would +;; do step 1 and node-inspect doesn't handle this. And if it did, +;; it would probably look like step(1). +(setf (gethash "step" realgud:node-inspect-command-hash) "step") +(setf (gethash "next" realgud:node-inspect-command-hash) "next") + +;; Unsupported features: +(setf (gethash "jump" realgud:node-inspect-command-hash) "*not-implemented*") + +(provide-me "realgud:node-inspect-") diff --git a/node-inspect/main.el b/node-inspect/main.el new file mode 100644 index 0000000..7cd9ce8 --- /dev/null +++ b/node-inspect/main.el @@ -0,0 +1,85 @@ +;; Copyright (C) 2016, 2018 Rocky Bernstein + +;; 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/>. + +;; `realgud:node-inspect' Main interface to "node inspect" debugger via Emacs + +(require 'cl-lib) +(require 'load-relative) +(require 'realgud) +(require-relative-list '("core" "track-mode") "realgud:node-inspect-") + +;; This is needed, or at least the docstring part of it is needed to +;; get the customization menu to work in Emacs 24. +(defgroup realgud:node-inspect nil + "The realgud interface to the node-inspect debugger" + :group 'realgud + :version "24.3") + +;; ------------------------------------------------------------------- +;; User-definable variables +;; + +(defcustom realgud:node-inspect-command-name + "node inspect" + "File name for executing the Javascript debugger and command options. +This should be an executable on your path, or an absolute file name." + :type 'string + :group 'realgud:node-inspect) + +;; ------------------------------------------------------------------- +;; The end. +;; + +(declare-function node-inspect-track-mode 'realgud-node-inspect-track-mode) +(declare-function node-inspect-query-cmdline 'realgud:node-inspect-core) +(declare-function node-inspect-parse-cmd-args 'realgud:node-inspect-core) + +;;;###autoload +(defun realgud:node-inspect (&optional opt-cmd-line no-reset) + "Invoke the node-inspect shell debugger and start the Emacs user interface. + +String OPT-CMD-LINE specifies how to run node-inspect. + +OPT-CMD-LINE is treated like a shell string; arguments are +tokenized by `split-string-and-unquote'. The tokenized string is +parsed by `node-inspect-parse-cmd-args' and path elements found by that +are expanded using `realgud:expand-file-name-if-exists'. + +Normally, command buffers are reused when the same debugger is +reinvoked inside a command buffer with a similar command. If we +discover that the buffer has prior command-buffer information and +NO-RESET is nil, then that information which may point into other +buffers and source buffers which may contain marks and fringe or +marginal icons is reset. See `loc-changes-clear-buffer' to clear +fringe and marginal icons. +" + (interactive) + (let ((cmd-buf + (realgud:run-debugger "node inspect" + 'node-inspect-query-cmdline 'node-inspect-parse-cmd-args + 'realgud:node-inspect-minibuffer-history + opt-cmd-line no-reset))) + ;; (if cmd-buf + ;; (with-current-buffer cmd-buf + ;; ;; FIXME should allow customization whether to do or not + ;; ;; and also only do if hook is not already there. + ;; (realgud:remove-ansi-schmutz) + ;; ) + ;; ) + )) + +(defalias 'node-inspect 'realgud:node-inspect) + +(provide-me "realgud-") diff --git a/node-inspect/track-mode.el b/node-inspect/track-mode.el new file mode 100644 index 0000000..818924d --- /dev/null +++ b/node-inspect/track-mode.el @@ -0,0 +1,84 @@ +;;; track-mode.el --- +;;; Copyright (C) 2016, 2018 Rocky Bernstein <rocky@gnu.org> + +;; 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/>. + +;; "node inspect" tracking a comint or eshell buffer. + +(declare-function realgud:track-set-debugger 'realgud-track-mode) +(declare-function realgud-track-mode-setup 'realgud-track-mode) +(declare-function realgud:remove-ansi-schmutz 'realgud:utils) + +(require 'realgud) + +(require-relative-list '("core" "init") "realgud:node-inspect-") + +(defun realgud:node-inspect-track-mode-hook() + (if realgud:node-inspect-track-mode + (progn + (use-local-map realgud:node-inspect-track-mode-map) + (realgud:remove-ansi-schmutz) + (message "using node-inspect mode map") + ) + (message "node-inspect track-mode-hook disable called")) +) + +;; FIXME: this shouldn't be needed +(defvar realgud:node-inspect-track-mode-map (make-keymap)) + +(define-minor-mode realgud:node-inspect-track-mode + "Minor mode for tracking node-inspect source locations inside a node-inspect shell via realgud. + +If called interactively with no prefix argument, the mode is +toggled. A prefix argument, captured as ARG, enables the mode if +the argument is positive, and disables it otherwise. + +\\{realgud:node-inspect-track-mode-map}" + :init-value nil + ;; :lighter " node-inspect" ;; mode-line indicator from realgud-track is sufficient. + ;; The minor mode bindings. + :global nil + :group 'realgud:node-inspect + :keymap realgud:node-inspect-track-mode-map + + (realgud:track-set-debugger "node-inspect") + (realgud:node-inspect-track-mode-internal) +) + +(defun realgud:node-inspect-track-mode-internal (&optional arg) + (realgud:track-set-debugger "node-inspect") + (if realgud:node-inspect-track-mode + (progn + (realgud-track-mode-setup 't) + (realgud:node-inspect-track-mode-hook)) + (progn + (setq realgud-track-mode nil) + )) + ) + +;; ;; Debugger commands that node-inspect doesn't have +;; (define-key node-inspect-track-mode-map +;; [remap realgud:cmd-newer-frame] 'undefined) +;; (define-key node-inspect-track-mode-map +;; [remap realgud:cmd-older-frame] 'undefined) +(defvar realgud:node-inspect-short-key-mode-map (make-keymap)) + +(define-key realgud:node-inspect-short-key-mode-map + [remap realgud:cmd-step] 'realgud:cmd-step-no-arg) +(define-key realgud:node-inspect-short-key-mode-map + [remap realgud:cmd-step] 'realgud:cmd-step-no-arg) +(define-key realgud:node-inspect-short-key-mode-map + [remap realgud:cmd-next] 'realgud:cmd-next-no-arg) + +(provide-me "realgud:node-inspect-") diff --git a/realgud-node-inspect.el b/realgud-node-inspect.el new file mode 100644 index 0000000..bd7f39f --- /dev/null +++ b/realgud-node-inspect.el @@ -0,0 +1,48 @@ +;;; realgud-node-inspect.el --- realgud front-end to newer "node inspect" + +;; Author: Rocky Bernstein +;; Version: 1.0 +;; Package-Type: multi +;; Package-Requires: ((realgud "1.4.3") (cl-lib "0.5") (emacs "24")) +;; URL: http://github.com/realgud/realgud-node-inspect +;; Compatibility: GNU Emacs 24.x + +;; Copyright (C) 2015, 2016, 2018 Rocky Bernstein <rocky@gnu.org> + +;; 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: + +;; realgud support for the "node inspect" with V8 inspector support. +;; See https://nodejs.org/api/debugger.html +;; +;;; Code: + +;; Press C-x C-e at the end of the next line configure the program in +;; for building via "make" to get set up. +;; (compile (format "EMACSLOADPATH=:%s:%s:%s:%s ./autogen.sh" (file-name-directory (locate-library "test-simple.elc")) (file-name-directory (locate-library "realgud.elc")) (file-name-directory (locate-library "load-relative.elc")) (file-name-directory (locate-library "loc-changes.elc")))) + +(require 'load-relative) + +(defgroup realgud-node-inspector nil + "Realgud interface to the 'node inspect' debugger" + :group 'realgud + :version "24.3") + +(require-relative-list '( "./node-inspect/main" ) "realgud-") +(load-relative "./node-inspect/main.el") + +(provide-me) + +;;; realgud-node-inspect.el ends here diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..fbfb2c6 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,70 @@ +include $(top_srcdir)/common.mk + +PHONY=check test all check-elget test-elget help + +#: overall help on running the make targets +help: + @echo "The main function of this Makefile is to facilitate running tests." + @echo + @echo "To run all of the tests, use targets \"test\", \"check\" or \"check-short\"." + @echo "For example:" + @echo + @echo " make check" + @echo "or:" + @echo " make check-short" + @echo + @echo "The -short target uses a filter written in Ruby to remove extreanous output." + @echo + @echo "To run a specific test like test-srcbuf.el, change \".el\" to" + @echo "\".run\". For example:" + @echo + @echo " make test-srcbuf.run" + @echo + @echo "Tests can also be run via the Emacs el-get package and this loads dependent emacs " + @echo "package, like load-relative. To do this, use targets, \"check-elget\"," + @echo "\"test-elget\", or \"check-short-elget\"." + @echo + @echo "To run a specific test like test-srcbuf.el via el-get change \".el\"" + @echo "to \".elrun\" For example:" + @echo + @echo " make test-srcbuf.elrun" + + +#: same thing as "check" +test: check + +#: same thing as "check-elget" +test-elget: check-elget + +test_files := $(wildcard test-*.el) + +CHECK_FILES = $(notdir $(test_files:.el=.run)) +EL_GET_CHECK_FILES = $(notdir $(test_files:.el=.elrun)) + +#: Run all tests +check: $(CHECK_FILES) + +#: Run all tests via el-get +check-elget: $(EL_GET_CHECK_FILES) + +#: Run all tests with minimum verbosity +check-short: + $(MAKE) check 2>&1 | ruby ../make-check-filter.rb + +#: Run all tests with minimum verbosity via el-get +check-short-elget: + $(MAKE) check-elget 2>&1 | ruby ../make-check-filter.rb + +test-%.run: + (cd $(top_srcdir)/test && EMACSLOADPATH=$(EMACSLOADPATH) $(EMACS) --batch --no-site-file --no-splash --load $(@:.run=.el)) + +#: Run tests using el-get to specify external Lisp dependencies +test-%.elrun: + (cd $(top_srcdir)/test && EMACSLOADPATH=$(EMACSLOADPATH) $(EMACS) --batch --no-site-file --no-splash --load ../el-get-install.el --load $(@:.elrun=.el)) + +install-lispLISP: $(lisp_LISP) $(ELCFILES) + +# Whatever it is you want to do, it should be forwarded to the +# to top-level directories +# %: +# $(MAKE) -C .. $@ diff --git a/test/gcd.js b/test/gcd.js new file mode 100644 index 0000000..ee56555 --- /dev/null +++ b/test/gcd.js @@ -0,0 +1,45 @@ +//!/usr/bin/env node +var util = require("util"); +require("console"); + +function ask(question, format, callback) { + var stdin = process.stdin, stdout = process.stdout; + + stdin.resume(); + stdout.write(question + ": "); + + stdin.once('data', function(data) { + data = data.toString().trim(); + + if (format.test(data)) { + callback(data); + } else { + stdout.write("It should match: "+ format +"\n"); + ask(question, format, callback); + } + }); +} + +// GCD. We assume positive numbers +function gcd(a, b) { + // Make: a <= b + if (a > b) { + var temp = a; + a = b; + b = temp; + } + + if (a <= 0) { return null }; + + if (a == 1 || b-a == 0) { + return a; + } + return gcd(b-a, a); +} + +var a = parseInt(process.argv[0]) || 24, + b = parseInt(process.argv[0]) || 5; + +console.log(util.format("The GCD of %d and %d is %d", a, b, + gcd(a, b))); +process.exit(); diff --git a/test/make-check-filter.rb b/test/make-check-filter.rb new file mode 100644 index 0000000..508c8f6 --- /dev/null +++ b/test/make-check-filter.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +# Use this to cut out the crud from make check. +# Use like this: +# make check 2>&1 | ruby ../make-check-filter.rb +# See Makefile.am +pats = ["^(?:Loading", + '(re)?make\[', + "Making check in", + '\(cd \.\.', + "make -C", + "Test-Unit", + "Fontifying", + "`flet'", + '\s*$', + '##[<>]+$' + ].join('|') + ')' +# puts pats +skip_re = /#{pats}/ + +while gets() + next if $_.encode!('UTF-8', 'binary', + invalid: :replace, undef: :replace, replace: '') =~ skip_re + puts $_ +end diff --git a/test/regexp-helper.el b/test/regexp-helper.el new file mode 100644 index 0000000..2660077 --- /dev/null +++ b/test/regexp-helper.el @@ -0,0 +1,50 @@ +(require 'test-simple) +(require 'realgud) + +(eval-when-compile + (defvar helper-bps) + (defvar helper-loc) + (defvar helper-tb) + (defvar prompt-pat) +) + +(declare-function realgud-loc-pat-regexp 'realgud-backtrace-mode) +(declare-function realgud-cmdbuf-info-loc-regexp 'realgud-buffer-command) +(declare-function test-simple-start 'test-simple) + + +(defun setup-regexp-vars(pat-hash) + (setq helper-bps (gethash "brkpt-set" pat-hash)) + (setq helper-loc (gethash "loc" pat-hash)) + (setq helper-tb (gethash "lang-backtrace" pat-hash)) +) + +(defun loc-match(text var) + "Match TEXT against regexp field VAR" + (string-match (realgud-loc-pat-regexp var) text) +) + +(defun bp-loc-match(text) + (string-match (realgud-loc-pat-regexp helper-bps) text) +) + +(defun tb-loc-match(text) + (string-match (realgud-loc-pat-regexp helper-tb) text) +) + +(defun cmdbuf-loc-match(text dbgr) + "Match TEXT against cmdbuf-info-loc field VAR" + (string-match (realgud-cmdbuf-info-loc-regexp dbgr) text) + ) + +(defun prompt-match(prompt-str &optional num-str fmt-str) + (unless fmt-str (setq fmt-str "debugger prompt %s")) + (assert-equal 0 (string-match (realgud-loc-pat-regexp prompt-pat) + prompt-str) + (format fmt-str prompt-str)) + (cond (num-str + (assert-equal num-str (substring prompt-str + (match-beginning 1) (match-end 1)))) + ('t 't)) + ) +(provide 'realgud-regexp-helper) diff --git a/test/test-loc-regexp-node-inspect.el b/test/test-loc-regexp-node-inspect.el new file mode 100644 index 0000000..58be563 --- /dev/null +++ b/test/test-loc-regexp-node-inspect.el @@ -0,0 +1,118 @@ +;; Press C-x C-e at the end of the next line to run this file test non-interactively +;; (test-simple-run "emacs -batch -L %s -l %s" (file-name-directory (locate-library "test-simple.elc")) buffer-file-name) + +(require 'test-simple) +(require 'load-relative) +(require 'realgud) + +(load-file "./regexp-helper.el") +(load-file "../node-inspect/init.el") + +(declare-function realgud-cmdbuf-info-loc-regexp 'realgud-buffer-command) +(declare-function cmdbuf-loc-match 'realgud-regexp-helper) +(declare-function realgud-loc-pat-regexp 'realgud-regexp) +(declare-function realgud-loc-pat-file-group 'realgud-regexp) +(declare-function realgud-loc-pat-line-group 'realgud-regexp) +(declare-function realgud-cmdbuf-info-file-group 'realgud-regexp) +(declare-function realgud-cmdbuf-info-line-group 'realgud-regexp) +(declare-function realgud-cmdbuf-info 'realgud-regexp) +(declare-function make-realgud-cmdbuf-info 'realgud-regexp) +(declare-function realgud-loc-pat-num 'realgud-regexp) +(declare-function test-simple-start 'test-simple) +(declare-function assert-t 'test-simple) +(declare-function assert-equal 'test-simple) +(declare-function note 'test-simple) +(declare-function end-tests 'test-simple) +(declare-function realgud-loc-pat-char-offset-group 'realgud:nodejs-init) + +(test-simple-start) + +(eval-when-compile + (defvar file-group) + (defvar frame-re) + (defvar line-group) + (defvar num-group) + (defvar col-group) + (defvar test-pos) + (defvar bt-re) + (defvar dbg-name) + (defvar realgud-pat-hash) + (defvar loc-pat) + (defvar test-dbgr) + (defvar test-s1) + (defvar realgud-pat-bt) + (defvar realgud:nodejs-pat-hash) +) + +; Some setup usually done in setting up the buffer. +; We customize this for the debugger trepan. Others may follow. +; FIXME: encapsulate this. +(setq dbg-name "nodejs") +(setq loc-pat (gethash "loc" (gethash dbg-name realgud-pat-hash))) + +(setq test-dbgr (make-realgud-cmdbuf-info + :debugger-name dbg-name + :loc-regexp (realgud-loc-pat-regexp loc-pat) + :file-group (realgud-loc-pat-file-group loc-pat) + :line-group (realgud-loc-pat-line-group loc-pat))) + + +(note "debugger-backtrace") +(setq realgud-pat-bt (gethash "debugger-backtrace" + realgud:nodejs-pat-hash)) +(setq test-s1 + "#0 module.js:380:17 +#1 Module._compile module2.js:456:26 +#2 Module._extensions..js module.js:474:10 +#3 Module.load module.js:356:32 +") + +(setq bt-re (realgud-loc-pat-regexp realgud-pat-bt)) +(setq num-group (realgud-loc-pat-num realgud-pat-bt)) +(setq file-group (realgud-loc-pat-file-group realgud-pat-bt)) +(setq line-group (realgud-loc-pat-line-group realgud-pat-bt)) +(setq col-group (realgud-loc-pat-char-offset-group realgud-pat-bt)) +(assert-equal 0 (string-match bt-re test-s1)) +(assert-equal "0" (substring test-s1 + (match-beginning num-group) + (match-end num-group))) +(assert-equal "module.js" + (substring test-s1 + (match-beginning file-group) + (match-end file-group))) +(assert-equal "380" + (substring test-s1 + (match-beginning line-group) + (match-end line-group))) +(assert-equal "17" (substring test-s1 + (match-beginning col-group) + (match-end col-group))) + +(setq test-pos (match-end 0)) +(assert-equal 19 (string-match bt-re test-s1 test-pos)) + +(setq test-s1 + "#1 Module._compile module2.js:456:26 +#2 Module._extensions..js module.js:474:10 +#3 Module.load module.js:356:32 +") +(assert-equal 0 (string-match bt-re test-s1)) + +(assert-equal "1" (substring test-s1 + (match-beginning num-group) + (match-end num-group))) +(assert-equal "module2.js" + (substring test-s1 + (match-beginning file-group) + (match-end file-group))) +(assert-equal "456" + (substring test-s1 + (match-beginning line-group) + (match-end line-group))) +(assert-equal "26" (substring test-s1 + (match-beginning col-group) + (match-end col-group))) +(setq test-pos (match-end 0)) +(assert-equal 36 test-pos) + +(end-tests) diff --git a/test/test-node-inspect.el b/test/test-node-inspect.el new file mode 100644 index 0000000..659b9ad --- /dev/null +++ b/test/test-node-inspect.el @@ -0,0 +1,37 @@ +;; Press C-x C-e at the end of the next line to run this file test non-interactively +;; (test-simple-run "emacs -batch -L %s -l %s" (file-name-directory (locate-library "test-simple.elc")) buffer-file-name) + +(require 'test-simple) +(load-file "../node-inspect/main.el") + +(eval-when-compile (defvar test:run-process-save)) + +(declare-function nodejs-parse-cmd-args 'realgud:nodejs) +(declare-function nodejs 'realgud:nodejs) +(declare-function __FILE__ 'load-relative) + +(test-simple-start) + +;; Save value realgud:run-process and change it to something we want +(setq test:run-process-save (symbol-function 'realgud:run-process)) +(defun realgud:run-process(debugger-name script-filename cmd-args + minibuf-history &optional no-reset) + "Fake realgud:run-process used in testing" + (note + (format "%s %s %s" debugger-name script-filename cmd-args)) + (assert-equal "node" debugger-name "debugger name gets passed") + (let ((expanded-name (expand-file-name "./gcd.js"))) + (assert-equal expanded-name script-filename "file name check") + )) + +(note "nodejs-parse-cmd-args") +(assert-equal (list '("node" "inspect") nil '("foo")) + (nodejs-parse-cmd-args '("node" "inspect" "foo"))) + +;; FIXME: need to mock remove-ansi-schmutz in realgud:nodejs +;; (realgud:nodejs "node inspect ./gcd.js 3 5") + +;; Restore the old value of realgud:run-process +(fset 'realgud:run-process test:run-process-save) + +(end-tests) |
