From c40ea5d078804c9592c0a6ac2fd5ad90f524760a Mon Sep 17 00:00:00 2001 From: jeff-phil Date: Wed, 3 May 2023 02:12:14 -0500 Subject: [PATCH 1/3] MS `vscode-js-debug' Installation and Integration Add latest vscode-js-debug adaptor, update installation routines so vscode adaptors on Github can be easily installed. In order to make this work, updates for the latest DAP Spec for reverse requests. --- CHANGELOG.org | 2 + README.org | 1 + dap-js-debug.el | 275 ++++++++++++++++++++++++++++++++++++++++++++++++ dap-mode.el | 32 ++++-- dap-utils.el | 49 ++++++--- 5 files changed, 338 insertions(+), 21 deletions(-) create mode 100644 dap-js-debug.el diff --git a/CHANGELOG.org b/CHANGELOG.org index d95c92ba..b6c976d4 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -5,6 +5,8 @@ ** 0.8 - [Breaking Change] Change debug provider names to match VS Code's naming: ~lldb~ to ~lldb-mi~ and ~codelldb~ to ~lldb~ - Added ~dap-gdscript~ + - Added ~vscode-js-debug~ latest Node.js, Chrome, Edge debuggers, along with latest DAP Spec reverse requests. + - Updates for extension installations ** 0.7 - [Breaking change] For ~dap-lldb.el~, change ~type~ to ~lldb-vscode~. ** 0.5 diff --git a/README.org b/README.org index 74be3aa4..ee1fb63b 100644 --- a/README.org +++ b/README.org @@ -93,3 +93,4 @@ support, (with some groundwork by yyoncho) runInTerminal support, various bug fixes. - [[https://github.com/factyy][Andrei Mochalov]] - Docker (debugging in containers) integration. + - [[https://github.com/jeff-phil][Jeffrey Phillips]] - [[https://github.com/microsoft/vscode-js-debug][vscode-js-debug]] installation and integration. diff --git a/dap-js-debug.el b/dap-js-debug.el new file mode 100644 index 00000000..3d4032b9 --- /dev/null +++ b/dap-js-debug.el @@ -0,0 +1,275 @@ +;;; dap-js-debug.el --- Debug Adapter Protocol mode for vscode-js-debug -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Jeffrey Phillips + +;; 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 . + +;; Author: Jeffrey Phillips +;; Keywords: languages, debug, javascript +;; Version: 0.1 +;; URL: https://github.com/emacs-lsp/dap-mode + +;;; Commentary: +;; Adapter for microsoft/vscode-js-debug, see: https://github.com/microsoft/vscode-js-debug +;; Package-Requires: ((dap-mode "0.8")) +;; Also requires vscode-js-debug v1.77.2+ which can be installed here + +;;; Code: + +(require 'cl-lib) +(require 'dash) +(require 'ht) +(require 'json) + +(require 'dap-mode) +(require 'dap-utils) + +(defcustom dap-js-debug-path (expand-file-name "vscode/ms-vscode.js-debug" + dap-utils-extension-path) + "The path to ms-vscode js-debugger extension." + :group 'dap-js-debug + :type 'string) + +(defcustom dap-js-debug-program `("node" + ,(f-join dap-js-debug-path "extension/dist/src/dapDebugServer.js")) + "The path and program for the ms-vscode js-debugger." + :group 'dap-js-debug + :type 'string) + +(defcustom dap-js-debug-output-telemetry t + "Output telemetry data from js-debug server if non-nil." + :group 'dap-js-debug + :type 'boolean) + +(defcustom dap-js-debug-extension-version "latest" + "The version of the github release found at https://github.com/microsoft/vscode-js-debug/releases" + :group 'dap-js-debug + :type 'string) + +(dap-utils-github-extension-setup-function "dap-js-debug" "microsoft" "vscode-js-debug" + dap-js-debug-extension-version dap-js-debug-path + #'dap-js-debug-extension-build) + +(defun dap-js-debug-extension-build () + "Callback from setup function in order to install extension node_module deps and compile." + (message "Building ms-vscode.js-debug in %s directory." dap-js-debug-path) + (let ((buf (get-buffer-create "*dap-js-debug extension build*")) + (default-directory (concat dap-js-debug-path "/extension"))) + ;;(async-shell-command "npm install --sav-dev --force; npm run compile -- vsDebugServerBundle" buf buf))) + (async-shell-command "npm install --sav-dev --force; npm run compile -- dapDebugServer" buf buf))) + +(cl-defun dap-js-debug-extension-update (&optional (ask-upgrade t)) + "Check for update, and then if `ask-upgrade' arg is non-nil will prompt user to upgrade." + (interactive) + (let* ((url (format dap-utils-github-extension-releases-info-url "microsoft" + "vscode-js-debug" "latest")) + (cur-version + (let ((file (f-join dap-js-debug-path "extension/package.json"))) + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (cdr (assoc 'version (json-read))))))) + (latest-version + (let ((inhibit-message dap-inhibit-io)) + (with-current-buffer + (if-let ((buf (url-retrieve-synchronously url t t 10))) + buf ;returned + (progn + ;; Probably timeout + (message "Problem getting latest version from: %s" url) + (generate-new-buffer "*dap-js-debug-temp*"))) + (if (/= (point-max) 1) + (progn + (goto-char (point-min)) + (re-search-forward "^$") + (substring (cdr (assoc 'tag_name (json-read))) 1)) + (progn + (kill-buffer) + cur-version)))))) + (if (string= cur-version latest-version) + (when ask-upgrade + (message "ms-vscode.js-debug extension is up to date at version: %s" latest-version)) + (let ((msg (format "Newer version (%s) of vscode/ms-vscode.js-debug exists than currently \ +installed version (%s)." latest-version cur-version))) + (if ask-upgrade + (when (y-or-n-p (concat msg " Do you want to upgrade now?")) + (dap-js-debug-setup t)) + (message "%s Upgrade with `M-x dap-js-debug-extension-update'" msg)))))) + +;; Check extension version when loading, and give a message about upgrading. +(dap-js-debug-extension-update nil) + +(defun dap-js-debug--populate-start-file-args (conf) + "Load up the start config for the debug adapter from launch.json, and required ones if missing. + See full options: `https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md'" + (dap--put-if-absent conf :type "pwa-chrome") + (dap--put-if-absent conf :cwd (lsp-workspace-root)) + (dap--put-if-absent conf :request "launch") + (dap--put-if-absent conf :console "internalConsole") + (dap--put-if-absent conf :name (concat (plist-get conf :type) "-js-debug")) + (let ((host "localhost") + (debug-port (dap--find-available-port))) + (dap--put-if-absent conf :host "localhost") + (dap--put-if-absent conf :debugServer debug-port) + (dap--put-if-absent conf :debugPort debug-port) + (dap--put-if-absent conf :program-to-start + (if (not (file-exists-p dap-js-debug-path)) + (error "DAP program path: %s does not exist! \ +Install it with M-x dap-js-debug-setup." dap-js-debug-path) + (format "%s %s %s" + (mapconcat 'identity dap-js-debug-program " ") + (plist-get conf :debugPort) + (plist-get conf :host))))) + (if (plist-member conf :url) + (progn + ;;(plist-put conf :mode "url") + (dap--put-if-absent conf :url (read-string + "Browse url: " + "http://localhost:3000" t)) + (dap--put-if-absent conf :webRoot (lsp-workspace-root)))) + (if (plist-member conf :file) + (if (plist-get conf :url) + (error "Both \"file\" and \"url\" properties are set in launch.json. Choose one.") + (progn + (plist-put conf :mode "file") + (dap--put-if-absent conf :file + (read-file-name "Select the file to open in the browser:" + nil (buffer-file-name) t))))) + (if (plist-member conf :program) + (dap--put-if-absent conf :program (read-file-name + "Select the Node.js program to run: " + nil (buffer-file-name) t))) + (when (string= "node-terminal" (plist-get conf :type)) + (error "In launch.json \"node-terminal\" debug type is currently not supported.")) + (when (string= "integratedTerminal" (plist-get conf :console)) + (error "In launch.json \"console\":\"integratedTerminal\" not supported at this time, \ +use \"console\":\"internalConsole\" instead")) + (dap--put-if-absent conf :output-filter-function #'dap-js-debug--output-filter-function) + (unless dap-inhibit-io + (message "dap-js-debug--populate-start-file-args: %s" conf)) + conf) + +;; Note, vscode-js-debug prefers now not using `pwa-' prefix, but still takes. Need to deprecate +;; and replace: dap-chrome.el, dap-edge.el, dap-node.el before can remove `pwa-' here. +(dap-register-debug-provider "pwa-node" #'dap-js-debug--populate-start-file-args) +(dap-register-debug-provider "pwa-chrome" #'dap-js-debug--populate-start-file-args) +(dap-register-debug-provider "pwa-msedge" #'dap-js-debug--populate-start-file-args) +(dap-register-debug-provider "node-terminal" #'dap-js-debug--populate-start-file-args) +;;If writing a vscode extension, probably wouldn't come to emacs. Don't register this. +;;(dap-register-debug-provider "pwa-extensionHost" #'dap-js-debug--populate-start-file-args) + +(dap-register-debug-template "Node.js Launch Program" + (list :type "pwa-node" + :cwd nil + :request "launch" + :program nil + :name "Node.js Launch Program")) + +(dap-register-debug-template "Chrome Launch File" + (list :type "pwa-chrome" + :cwd nil + :request "launch" + :file nil + :name "Chrome Launch File")) + +(dap-register-debug-template "Chrome Launch URL" + (list :type "pwa-chrome" + :cwd nil + :request "launch" + :webRoot nil + :url nil + :name "Chrome Launch URL")) + +(add-hook 'dap-session-created-hook #'dap-js-debug--session-created) +(defun dap-js-debug--session-created (debug-session) + "Set up so that processes won't ask about closing." + (when-let (proc (dap--debug-session-program-proc debug-session)) + (set-process-query-on-exit-flag proc nil))) + +(defun dap-js-debug--output-filter-function (debug-session event) + "Output event data, including for vscode-js-debug, some useful telemetry data. + Future can do something more with the telemetry data than just printing." + (-let [(&hash "seq" "event" event-type "body") event] + (if (hash-table-p body) + (progn + (if (and (bound-and-true-p dap-js-debug-output-telemetry) + (string= (gethash "category" body) "telemetry")) + (dap--print-to-output-buffer debug-session (concat (dap--json-encode body) "\n")) + (dap--print-to-output-buffer debug-session (concat (dap--output-buffer-format body) "\n"))))))) + +(add-hook 'dap-terminated-hook #'dap-js-debug--term-parent) +(defun dap-js-debug--term-parent (debug-session) + "Kill off parent process when child is disconnected." + (if (eq debug-session (if (boundp 'parent-debug-session) parent-debug-session nil)) + (progn + (when-let (proc (dap--debug-session-program-proc debug-session)) + (when (process-live-p proc) + (makunbound 'parent-debug-session) + (set-process-query-on-exit-flag proc nil) + (with-current-buffer (process-buffer proc) + ;; Switching mode, prevents triggering to open error file after killing proc + (shell-script-mode) + (kill-buffer)) + (dap-delete-session debug-session))))) + (kill-buffer (dap--debug-session-output-buffer debug-session))) + +(add-hook 'dap-executed-hook #'dap-js-debug--reverse-request-handler) +(defun dap-js-debug--reverse-request-handler (debug-session command) + "Callback hook to get messages from dap-mode reverse requests. This is set with `add-hook' above." + (unless dap-inhibit-io + (message "dap-js-debug--reverse-request-handler -> command: %s" command)) + (pcase command + ((guard (string= command "startDebugging")) + ;; Assume current session becomes parent requesting start debugging in child session + (setq parent-debug-session debug-session) + (-let [(&hash "seq" "command" "arguments" + (&hash "request" "configuration" + (&hash? "type" "__pendingTargetId"))) + (dap--debug-session-metadata debug-session)] + (-let (((&plist :mode :url :file :webroot :program :outputCapture :skipFiles :timeout :host :name :debugPort) + (dap--debug-session-launch-args debug-session)) + (conf `(:request ,request))) + ;;(plist-put conf :type type) + (plist-put conf :name (concat type "-" command)) + (plist-put conf :__pendingTargetId __pendingTargetId) + (plist-put conf :outputCapture outputCapture) + (plist-put conf :skipFiles skipFiles) + (plist-put conf :timeout timeout) + (plist-put conf :host host) + (plist-put conf :debugServer debugPort) + (plist-put conf :debugPort debugPort) + (if (or (string= "pwa-node" type) (string= "node" type)) + (plist-put conf :program program) + (progn + (if (string= mode "file") + (plist-put conf :file file) + (progn + (plist-put conf :url url) + (plist-put conf :webroot webroot))))) + (unless dap-inhibit-io + (message "dap-js-debug startDebugging conf: %s" conf)) + (dap-start-debugging-noexpand conf) + ;; success + (dap--send-message (dap--make-success-response seq command) + (dap--resp-handler) debug-session)))) + ;; This is really just confirmation response, but good place to ensure session selected + ("launch" (dap--switch-to-session debug-session)) + (_ + (unless dap-inhibit-io + (message "command: %s wasn't handled by dap-js-debug." command))))) + +(provide 'dap-js-debug) + +;;; dap-js-debug.el ends here diff --git a/dap-mode.el b/dap-mode.el index ac6348aa..be50ac9c 100644 --- a/dap-mode.el +++ b/dap-mode.el @@ -117,7 +117,7 @@ also `dap--make-terminal-buffer'." (const :tag "asnyc-shell" :value dap-internal-terminal-shell) (function :tag "Custom function"))) -(defcustom dap-output-buffer-filter '("stdout" "stderr") +(defcustom dap-output-buffer-filter '("stdout" "stderr" "console") "If non-nil, a list of output types to display in the debug output buffer." :group 'dap-mode :type 'list) @@ -937,7 +937,11 @@ PARAMS are the event params.") (formatted-output (if-let ((output-filter-fn (-> debug-session (dap--debug-session-launch-args) (plist-get :output-filter-function)))) - (funcall output-filter-fn formatted-output) + (progn + ;; Test # of params. Consider deprecating 1 param function. + (if (= 1 (cdr (func-arity output-filter-fn))) + (funcall output-filter-fn formatted-output) + (funcall output-filter-fn debug-session event))) formatted-output))) (when (or (not dap-output-buffer-filter) (member (gethash "category" body) dap-output-buffer-filter)) @@ -1090,7 +1094,17 @@ terminal configured (probably xterm)." debug-session (gethash "command" parsed-msg))) (message "Unable to find handler for %s." (pp parsed-msg)))) - ("request" (dap--start-process debug-session parsed-msg))) + ("request" + ;; These are "Reverse Requests", or requests from DAP server to client + (pcase (gethash "command" parsed-msg) + ("runInTerminal" + (dap--start-process debug-session parsed-msg)) + (_ + (setf (dap--debug-session-metadata debug-session) parsed-msg) + ;; Consider moving this hook out to also include runInTerminal reverse requests + (run-hook-with-args 'dap-executed-hook + debug-session + (gethash "command" parsed-msg)))))) (quit)))) (dap--parser-read parser msg))))) @@ -1134,8 +1148,8 @@ etc...." "Create initialize message. ADAPTER-ID the id of the adapter." (list :command "initialize" - :arguments (list :clientID "vscode" - :clientName "Visual Studio Code" + :arguments (list :clientID "emacs" + :clientName "emacs DAP client" :adapterID adapter-id :pathFormat "path" :linesStartAt1 t @@ -1143,6 +1157,8 @@ ADAPTER-ID the id of the adapter." :supportsVariableType t :supportsVariablePaging t :supportsRunInTerminalRequest t + :supportsStartDebuggingRequest t + :supportTerminateDebuggee t :locale "en-us") :type "request")) @@ -1195,9 +1211,9 @@ ADAPTER-ID the id of the adapter." (message "Failed to connect to %s:%s with error message %s" host port - (error-message-string err)) - (sit-for dap-connect-retry-interval) - (setq retries (1+ retries)))))) + (error-message-string err))) + (sleep-for dap-connect-retry-interval) + (setq retries (1+ retries))))) (or result (error "Failed to connect to port %s" port)))) (defun dap--create-session (launch-args) diff --git a/dap-utils.el b/dap-utils.el index fe5c23c1..d71b2505 100644 --- a/dap-utils.el +++ b/dap-utils.el @@ -32,7 +32,6 @@ (require 'dom) (require 'json) - (defconst dap-utils--ext-unzip-script "bash -c 'mkdir -p %2$s && unzip -qq %1$s -d %2$s'" "Unzip script to unzip vscode extension package file.") @@ -55,7 +54,7 @@ (shell-command (format dap-utils-unzip-script temp-file dest)))) (defcustom dap-utils-vscode-ext-url - "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/%s/vsextensions/%s/%s/vspackage" + "https://marketplace.gallery.vsassets.io/_apis/public/gallery/publisher/%s/extension/%s/%s/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage" "Vscode extension template url." :group 'dap-utils :type 'string) @@ -72,6 +71,12 @@ :group 'dap-utils :type 'string) +(defcustom dap-utils-github-extension-releases-info-url + "https://api.github.com/repos/%s/%s/releases/%s" + "Github extension's latest version information template url." + :group 'dap-utils + :type 'string) + (defcustom dap-utils-extension-path (expand-file-name ".extension" user-emacs-directory) "Directory to store vscode extension." :group 'dap-utils @@ -113,8 +118,6 @@ PATH is the download destination path." (defun dap-utils-vscode-get-installed-extension-version (path) "Check the version of the vscode extension installed in PATH. Returns nil if the extension is not installed." - (require 'xml) - (require 'dom) (let* ((extension-manifest (f-join path "extension.vsixmanifest"))) (when (f-exists? extension-manifest) (let ((pkg-identity (dom-by-tag (xml-parse-file extension-manifest) 'Identity))) @@ -170,11 +173,11 @@ With prefix, FORCED to redownload the extension." extension-name))) (message "%s: %s debug extension are not set. You can download it with M-x %s-setup" ,dapfile ,extension-name ,dapfile))))) -(defmacro dap-utils-github-extension-setup-function (dapfile owner repo version &optional path callback) +(defmacro dap-utils-github-extension-setup-function (dapfile owner repo &optional version path callback) "Helper to create DAPFILE setup function for debug extension from github. OWNER is the github owner. REPO is the github repository. -VERSION is the github extension version. +VERSION is the github extension version, if not set or set to `latest' then grab latest version. PATH is the download destination dir. CALLBACK is the fn to be called after the download." (let* ((extension-name (concat owner "." repo)) @@ -186,13 +189,33 @@ With prefix, FORCED to redownload the extension." extension-name))) (defun ,(intern (format "%s-setup" dapfile)) (&optional forced) ,help-string (interactive "P") - (unless (and (not forced) (file-exists-p ,dest)) - (dap-utils-get-github-extension ,owner ,repo ,version ,dest) - (rename-file (concat ,dest "/" (concat ,repo "-" ,version)) - (concat ,dest "/extension")) - (message "%s: Downloading done!" ,dapfile) - (when ,callback - (funcall ,callback)))) + (if (or (not ,version) + (string= "latest" ,version)) + (progn ; Get the latest actual version + (let* ((url (format dap-utils-github-extension-releases-info-url ,owner ,repo ,version))) + (with-current-buffer (url-retrieve-synchronously url) + (goto-char (point-min)) + (re-search-forward "^$") + (set ',version (substring (cdr (assoc 'tag_name (json-read))) 1))))) + (progn ; Check that version requested exists. + (let* ((url (format dap-utils-github-extension-releases-info-url + ,owner ,repo (concat "tags/v" ,version))) + (status (url-http-symbol-value-in-buffer 'url-http-response-status + (url-retrieve-synchronously url)))) + (unless (eql 200 status) + (error "Error! Extension: %s.%s version: %s returned status: %s for: %s" + ,owner ,repo ,version status url))))) + (if (or forced (not (file-exists-p ,dest))) + (progn + (message "Installing %s.%s version: %s to %s" ,owner ,repo ,version ,dest) + (dap-utils-get-github-extension ,owner ,repo ,version ,dest) + (rename-file (concat ,dest "/" (concat ,repo "-" ,version)) + (concat ,dest "/extension")) + (message "%s: Downloading done!" ,dapfile) + (when ,callback + (funcall ,callback))) + (message "Extension %s.%s exists already in %s. Remove extension, or pass the `forced' \ +argument." ,owner ,repo ,dest))) (unless (file-exists-p ,dest) (message "%s: %s debug extension are not set. You can download it with M-x %s-setup" ,dapfile ,extension-name ,dapfile))))) From aab923f78ee4b3f1eeee51feed4e6739752860ba Mon Sep 17 00:00:00 2001 From: jeff-phil Date: Sat, 5 Aug 2023 01:48:00 -0500 Subject: [PATCH 2/3] Update PR #736 fix for `dap-debug-last' command + Remove child debug session from dap--debug-configuration, if added. This will allow `dap-debug-last' command to work using parent. + Other general long line reformatting to make more readable. --- dap-js-debug.el | 79 +++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/dap-js-debug.el b/dap-js-debug.el index 3d4032b9..255ea8dd 100644 --- a/dap-js-debug.el +++ b/dap-js-debug.el @@ -21,7 +21,8 @@ ;; URL: https://github.com/emacs-lsp/dap-mode ;;; Commentary: -;; Adapter for microsoft/vscode-js-debug, see: https://github.com/microsoft/vscode-js-debug +;; Adapter for microsoft/vscode-js-debug, +;; see: https://github.com/microsoft/vscode-js-debug ;; Package-Requires: ((dap-mode "0.8")) ;; Also requires vscode-js-debug v1.77.2+ which can be installed here @@ -42,7 +43,8 @@ :type 'string) (defcustom dap-js-debug-program `("node" - ,(f-join dap-js-debug-path "extension/dist/src/dapDebugServer.js")) + ,(f-join dap-js-debug-path + "extension/dist/src/dapDebugServer.js")) "The path and program for the ms-vscode js-debugger." :group 'dap-js-debug :type 'string) @@ -53,24 +55,26 @@ :type 'boolean) (defcustom dap-js-debug-extension-version "latest" - "The version of the github release found at https://github.com/microsoft/vscode-js-debug/releases" + "The version of the github release found at +https://github.com/microsoft/vscode-js-debug/releases" :group 'dap-js-debug :type 'string) (dap-utils-github-extension-setup-function "dap-js-debug" "microsoft" "vscode-js-debug" - dap-js-debug-extension-version dap-js-debug-path + dap-js-debug-extension-version + dap-js-debug-path #'dap-js-debug-extension-build) (defun dap-js-debug-extension-build () - "Callback from setup function in order to install extension node_module deps and compile." + "Callback from setup function in order to install extension node deps and compile." (message "Building ms-vscode.js-debug in %s directory." dap-js-debug-path) (let ((buf (get-buffer-create "*dap-js-debug extension build*")) (default-directory (concat dap-js-debug-path "/extension"))) - ;;(async-shell-command "npm install --sav-dev --force; npm run compile -- vsDebugServerBundle" buf buf))) - (async-shell-command "npm install --sav-dev --force; npm run compile -- dapDebugServer" buf buf))) + (async-shell-command + "npm install --sav-dev --force; npm run compile -- dapDebugServer" buf buf))) (cl-defun dap-js-debug-extension-update (&optional (ask-upgrade t)) - "Check for update, and then if `ask-upgrade' arg is non-nil will prompt user to upgrade." + "Check for update, and if `ask-upgrade' arg is non-nil will prompt user to upgrade." (interactive) (let* ((url (format dap-utils-github-extension-releases-info-url "microsoft" "vscode-js-debug" "latest")) @@ -100,9 +104,10 @@ cur-version)))))) (if (string= cur-version latest-version) (when ask-upgrade - (message "ms-vscode.js-debug extension is up to date at version: %s" latest-version)) - (let ((msg (format "Newer version (%s) of vscode/ms-vscode.js-debug exists than currently \ -installed version (%s)." latest-version cur-version))) + (message "ms-vscode.js-debug extension is up to date at version: %s" + latest-version)) + (let ((msg (format "Newer version (%s) of vscode/ms-vscode.js-debug exists than \ +currently installed version (%s)." latest-version cur-version))) (if ask-upgrade (when (y-or-n-p (concat msg " Do you want to upgrade now?")) (dap-js-debug-setup t)) @@ -112,8 +117,9 @@ installed version (%s)." latest-version cur-version))) (dap-js-debug-extension-update nil) (defun dap-js-debug--populate-start-file-args (conf) - "Load up the start config for the debug adapter from launch.json, and required ones if missing. - See full options: `https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md'" + "Load up the start config CONF for the debug adapter from launch.json, and default + required attributes if missing. See full options: + `https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md'" (dap--put-if-absent conf :type "pwa-chrome") (dap--put-if-absent conf :cwd (lsp-workspace-root)) (dap--put-if-absent conf :request "launch") @@ -134,14 +140,15 @@ Install it with M-x dap-js-debug-setup." dap-js-debug-path) (plist-get conf :host))))) (if (plist-member conf :url) (progn - ;;(plist-put conf :mode "url") + ;;(plist-put conf :mode "url") (dap--put-if-absent conf :url (read-string "Browse url: " "http://localhost:3000" t)) - (dap--put-if-absent conf :webRoot (lsp-workspace-root)))) + (dap--put-if-absent conf :webRoot (lsp-workspace-root)))) (if (plist-member conf :file) (if (plist-get conf :url) - (error "Both \"file\" and \"url\" properties are set in launch.json. Choose one.") + (error "Both \"file\" and \"url\" properties are set in launch.json. \ +Choose one.") (progn (plist-put conf :mode "file") (dap--put-if-absent conf :file @@ -154,15 +161,17 @@ Install it with M-x dap-js-debug-setup." dap-js-debug-path) (when (string= "node-terminal" (plist-get conf :type)) (error "In launch.json \"node-terminal\" debug type is currently not supported.")) (when (string= "integratedTerminal" (plist-get conf :console)) - (error "In launch.json \"console\":\"integratedTerminal\" not supported at this time, \ -use \"console\":\"internalConsole\" instead")) - (dap--put-if-absent conf :output-filter-function #'dap-js-debug--output-filter-function) + (error "In launch.json \"console\":\"integratedTerminal\" not supported at this \ +time, use \"console\":\"internalConsole\" instead")) + (dap--put-if-absent conf + :output-filter-function #'dap-js-debug--output-filter-function) (unless dap-inhibit-io (message "dap-js-debug--populate-start-file-args: %s" conf)) conf) -;; Note, vscode-js-debug prefers now not using `pwa-' prefix, but still takes. Need to deprecate -;; and replace: dap-chrome.el, dap-edge.el, dap-node.el before can remove `pwa-' here. +;; Note, vscode-js-debug prefers now not using `pwa-' prefix, but still takes. +;; Need to deprecate and replace: dap-chrome.el, dap-edge.el, dap-node.el before can +;; remove `pwa-' here. (dap-register-debug-provider "pwa-node" #'dap-js-debug--populate-start-file-args) (dap-register-debug-provider "pwa-chrome" #'dap-js-debug--populate-start-file-args) (dap-register-debug-provider "pwa-msedge" #'dap-js-debug--populate-start-file-args) @@ -206,8 +215,10 @@ use \"console\":\"internalConsole\" instead")) (progn (if (and (bound-and-true-p dap-js-debug-output-telemetry) (string= (gethash "category" body) "telemetry")) - (dap--print-to-output-buffer debug-session (concat (dap--json-encode body) "\n")) - (dap--print-to-output-buffer debug-session (concat (dap--output-buffer-format body) "\n"))))))) + (dap--print-to-output-buffer + debug-session (concat (dap--json-encode body) "\n")) + (dap--print-to-output-buffer + debug-session (concat (dap--output-buffer-format body) "\n"))))))) (add-hook 'dap-terminated-hook #'dap-js-debug--term-parent) (defun dap-js-debug--term-parent (debug-session) @@ -219,7 +230,7 @@ use \"console\":\"internalConsole\" instead")) (makunbound 'parent-debug-session) (set-process-query-on-exit-flag proc nil) (with-current-buffer (process-buffer proc) - ;; Switching mode, prevents triggering to open error file after killing proc + ;; Switching mode, prevents triggering to open err file after killing proc (shell-script-mode) (kill-buffer)) (dap-delete-session debug-session))))) @@ -227,22 +238,25 @@ use \"console\":\"internalConsole\" instead")) (add-hook 'dap-executed-hook #'dap-js-debug--reverse-request-handler) (defun dap-js-debug--reverse-request-handler (debug-session command) - "Callback hook to get messages from dap-mode reverse requests. This is set with `add-hook' above." + "Callback hook to get messages from dap-mode reverse requests." + ;;This is set with `add-hook' above. (unless dap-inhibit-io (message "dap-js-debug--reverse-request-handler -> command: %s" command)) (pcase command ((guard (string= command "startDebugging")) - ;; Assume current session becomes parent requesting start debugging in child session + ;; Assume current session now parent requesting start debugging in child session (setq parent-debug-session debug-session) (-let [(&hash "seq" "command" "arguments" (&hash "request" "configuration" (&hash? "type" "__pendingTargetId"))) (dap--debug-session-metadata debug-session)] - (-let (((&plist :mode :url :file :webroot :program :outputCapture :skipFiles :timeout :host :name :debugPort) + (-let (((&plist :mode :url :file :webroot :program :outputCapture + :skipFiles :timeout :host :name :debugPort) (dap--debug-session-launch-args debug-session)) (conf `(:request ,request))) + ;; DAP Spec says not to include client variables to start child, including type ;;(plist-put conf :type type) - (plist-put conf :name (concat type "-" command)) + (plist-put conf :name (concat type "-" command)) (plist-put conf :__pendingTargetId __pendingTargetId) (plist-put conf :outputCapture outputCapture) (plist-put conf :skipFiles skipFiles) @@ -261,10 +275,17 @@ use \"console\":\"internalConsole\" instead")) (unless dap-inhibit-io (message "dap-js-debug startDebugging conf: %s" conf)) (dap-start-debugging-noexpand conf) + ;; Remove child session if stored in list of recent/last configurations to + ;; allow `dap-debug-last' to work by getting parent not child. + (when-let ((last-conf (cdr (cl-first dap--debug-configuration))) + (_ptid-equal (string= __pendingTargetId + (plist-get last-conf :__pendingTargetId)))) + (pop dap--debug-configuration)) ;; success (dap--send-message (dap--make-success-response seq command) (dap--resp-handler) debug-session)))) - ;; This is really just confirmation response, but good place to ensure session selected + ;; This is really just confirmation response, but good place to ensure session + ;; selected ("launch" (dap--switch-to-session debug-session)) (_ (unless dap-inhibit-io From e529e9b2b626eb48ea66e61f5289bee566717121 Mon Sep 17 00:00:00 2001 From: jeff-phil Date: Tue, 19 Sep 2023 13:34:25 -0500 Subject: [PATCH 3/3] Resync with latest emacs-lsp:master changes --- .github/workflows/test.yml | 1 - CHANGELOG.org | 7 ++-- Eask | 2 +- dap-mode.el | 17 ++++++---- dap-ocaml.el | 54 +++++++++++++++++++++++++++++++ docs/page/configuration.md | 66 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 dap-ocaml.el diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b171eca3..bd3e61e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,6 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] emacs-version: - - 26.3 - 27.2 - 28.2 - 29.1 diff --git a/CHANGELOG.org b/CHANGELOG.org index b6c976d4..b99379da 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -2,11 +2,14 @@ #+STARTUP: content * Changelog +** 0.9 + - Added support for DAP Specification ~Reverse Requests~. + - Added ~vscode-js-debug~ latest Node.js, Chrome, Edge debuggers + - Updates for extension installations ** 0.8 - [Breaking Change] Change debug provider names to match VS Code's naming: ~lldb~ to ~lldb-mi~ and ~codelldb~ to ~lldb~ - Added ~dap-gdscript~ - - Added ~vscode-js-debug~ latest Node.js, Chrome, Edge debuggers, along with latest DAP Spec reverse requests. - - Updates for extension installations + - Drop support for emacs 26.x ** 0.7 - [Breaking change] For ~dap-lldb.el~, change ~type~ to ~lldb-vscode~. ** 0.5 diff --git a/Eask b/Eask index c18b6af5..37287282 100644 --- a/Eask +++ b/Eask @@ -14,7 +14,7 @@ (source "gnu") (source "melpa") -(depends-on "emacs" "26.1") +(depends-on "emacs" "27.1") (depends-on "lsp-mode") (depends-on "lsp-treemacs") (depends-on "lsp-docker") diff --git a/dap-mode.el b/dap-mode.el index 23b02a0d..c01e10e0 100644 --- a/dap-mode.el +++ b/dap-mode.el @@ -18,7 +18,7 @@ ;; Author: Ivan Yonchovski ;; Keywords: languages, debug ;; URL: https://github.com/emacs-lsp/dap-mode -;; Package-Requires: ((emacs "26.1") (dash "2.18.0") (lsp-mode "6.0") (bui "1.1.0") (f "0.20.0") (s "1.12.0") (lsp-treemacs "0.1") (posframe "0.7.0") (ht "2.3") (lsp-docker "1.0.0")) +;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (lsp-mode "6.0") (bui "1.1.0") (f "0.20.0") (s "1.12.0") (lsp-treemacs "0.1") (posframe "0.7.0") (ht "2.3") (lsp-docker "1.0.0")) ;; Version: 0.7 ;;; Commentary: @@ -858,9 +858,14 @@ will be reversed." "Get all file backed buffers." (-filter 'buffer-file-name (buffer-list))) +(defun dap--buffers-w-breakpoints () + "Get only the buffers featuring at least one breakpoint" + ;; get the list from the keys of the breakpoint hash-table + (ht-keys (dap--get-breakpoints))) + (defun dap--refresh-breakpoints () "Refresh breakpoints for DEBUG-SESSION." - (--each (dap--buffer-list) + (--each (dap--buffers-w-breakpoints) (when (buffer-live-p it) (with-current-buffer it (dap--set-breakpoints-in-file @@ -1583,10 +1588,10 @@ When ALL? is non-nil select from threads in all debug sessions." (when new-session (let ((breakpoints (dap--get-breakpoints))) - (--each (dap--buffer-list) (with-current-buffer it - (->> breakpoints - (gethash buffer-file-name) - (dap--set-breakpoints-in-file buffer-file-name)))))) + (--each (dap--buffers-w-breakpoints) (with-current-buffer it + (->> breakpoints + (gethash buffer-file-name) + (dap--set-breakpoints-in-file buffer-file-name)))))) (run-hook-with-args 'dap-session-changed-hook lsp--cur-workspace) diff --git a/dap-ocaml.el b/dap-ocaml.el new file mode 100644 index 00000000..4ae19337 --- /dev/null +++ b/dap-ocaml.el @@ -0,0 +1,54 @@ +;;; dap-mode/dap-ocaml.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2018 Austin Theriault + +;; Author: Austin Theriault +;; Keywords: languages + +;; 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 . + +;; URL: https://github.com/yyoncho/dap-mode +;; Package-Requires: ((emacs "25.1") (dash "2.14.1") (lsp-mode "4.0")) +;; Version: 0.2 + +;;; Commentary: +;; Adapter for OCaml Earlybird (https://github.com/hackwaly/ocamlearlybird) + +;;; Code: + +(require 'dap-mode) + +(defcustom dap-ocaml-executable "ocamlearlybird" + "Path to the OCaml Earlybird executable." + :group 'dap-ocaml + :risky t + :type 'file) + +(defun dap-ocaml--populate-start-file-args (conf) + "Populate CONF with the start file argument." + (-> conf + (dap--put-if-absent :console "internalConsole") ;; integratedTerminal doesn't work + (dap--put-if-absent :dap-server-path (list dap-ocaml-executable "debug")))) + +(dap-register-debug-provider "ocaml.earlybird" 'dap-ocaml--populate-start-file-args) +(dap-register-debug-template "OCaml Debug Template" + (list :type "ocaml.earlybird" + :request "launch" + :name "OCaml::Run" + :program nil + :arguments nil)) + + +(provide 'dap-ocaml) +;;; dap-ocaml.el ends here diff --git a/docs/page/configuration.md b/docs/page/configuration.md index d72d608e..11629577 100644 --- a/docs/page/configuration.md +++ b/docs/page/configuration.md @@ -73,6 +73,52 @@ settings. :env '(("DEV" . "1")))) ``` +## Kotlin + +1. Installation + To use dap-mode with Kotlin, you need to download the [kotlin-debug-adapter](https://github.com/fwcd/kotlin-debug-adapter). + The releases are a bit infrequent, so it is recommended to build it from source yourself. + You will also need to have installed `lsp-mode`, as `dap-kotlin` shares some configuration with it. + After building it, point the variable `lsp-kotlin-debug-adapter-path ` to the path of the kotlin-debug-adapter executable. + You will find this in the path `adapter/build/install/adapter/bin` (from the kotlin-debug-adapter root). + You should also make sure that `lsp-kotlin-debug-adapter-enabled` is set to true. + +2. Usage + **First of all, each time you you want to debug, make sure you BUILD YOUR PROJECT FIRST!** + Simply running your regular build with Maven or Gradle should be enough. + + You can set up debug templates using Kotlin. `dap-kotlin`provides some sensible defaults, + but there are one parameters you MUST give yourself: + - `:mainClass`: The class name, including package for the main class you want to run. If the class takes argument, you can give them as well. + If project root path needs to be different, you can give it using the parameter `:projectRoot`. + Other parameters include: + - `:type`: `launch` or `attach` + - `:hostName`: If type is `attach`, you can specify a hostname to connect to. Defaults to `localhost`. + - `:port`: If type is `attach`, you can specify a port to connect to. Defaults to `5005`. + - `:noDebug`: Whether or not to use a debug session + - `:enableJsonLogging`: Enable logging of adapter communication logs. + - `:jsonLogFile`: File to log to. + + + Thanks to interop with `lsp-kotlin`, you can have it set up code lenses with run/debug-options for main classes. + For this to work, you need kotlin-langauge-server running, be in a file with a main method, and have activated `lsp-kotlin-lens-mode` + + Sadly, there is no test-running functionality like in Java `dap-mode`. This can be combated by setting up a debug template with + the Junit5 `ConsoleLauncher`. Remember that this class needs to be part of your classpath. Sometimes this is included in bigger frameworks + testing utilities, but can also be included explicitly by adding the `junit-platform-console` dependency. + + ```elisp + (dap-register-debug-template "Kotlin tests with launcher" + (list :type "kotlin" + :request "launch" + :mainClass "org.junit.platform.console.ConsoleLauncher --scan-class-path" + :enableJsonLogging nil + :noDebug nil)) + ``` + This will run all tests in the projects in debug mode, with no json logging. You can experiment with the + arguments to `ConsoleLauncher`. Arguments are documented on [the official JUnit website](https://junit.org/junit5/docs/current/user-guide/#running-tests-console-launcher-options). + + ## Python 1. Installation @@ -610,3 +656,23 @@ Core Attach (Console)" from `dap-debug` menu. 2. Usage Call `dap-debug` and select the "Unity Editor" template + +## OCaml + +[earlybird](https://github.com/hackwaly/ocamlearlybird) is an OCaml debugger with support for DAP. + +1. Installation + + - Install earlybird through `opam install earlybird` + + - Put in your configuration file: + + ``` elisp + (require 'dap-ocaml) + ``` + +2. Usage + + Call `dap-debug` and edit the "OCaml Debug Template". Make sure to set the program field as the path of your *byte-code* compiled program. + Note that this debugger _only_ works with bytecode compiled OCaml programs. +