diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d2abdd..3e70ee68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Added +- Automatically discover debug adapter launch configurations if `nvim-dap` and `haskell-debug-adapter` + are detected. + This can be disabled by setting the `vim.g.haskell_tools.dap.auto_discover` option to `false`. + ## [2.0.2] - 2023-09-02 ### Fixed - Hover: Decode url-encoded (type-)definition paths in hover actions ([#238](https://github.com/mrcjkb/haskell-tools.nvim/issues/238)). diff --git a/README.md b/README.md index 42c141f9..df207429 100644 --- a/README.md +++ b/README.md @@ -137,10 +137,6 @@ vim.keymap.set('n', 'rf', function() ht.repl.toggle(vim.api.nvim_buf_get_name(0)) end, def_opts) vim.keymap.set('n', 'rq', ht.repl.quit, opts) - --- Detect nvim-dap launch configurations --- (requires nvim-dap and haskell-debug-adapter) -ht.dap.discover_configurations(bufnr) ``` >**Note** @@ -249,10 +245,10 @@ this plugin will set up `autocmd`s to automatically generate tags: This feature can be tweaked or disabled in the [advanced configuration](#advanced-configuration). -- [x] **Auto-detect `haskell-debug-adapter` configurations** +- [x] **Auto-discover `haskell-debug-adapter` configurations** If the [`nvim-dap`](https://github.com/mfussenegger/nvim-dap) plugin is installed, -you can use `haskell-tools.nvim` to auto-detect [`haskell-debug-adapter`](https://hackage.haskell.org/package/haskell-debug-adapter) +`haskell-tools.nvim` will automatically discover [`haskell-debug-adapter`](https://hackage.haskell.org/package/haskell-debug-adapter) configurations. ![dap](https://user-images.githubusercontent.com/12857160/232348888-4fea5393-d624-417e-b994-6eb44113a3d9.gif) @@ -543,6 +539,12 @@ local ht = require('haskell-tools') ht.dap.discover_configurations(bufnr, opts) ``` +> **Note** +> +> `haskell-tools.nvim` will discover DAP launch configurations automatically, +> if `nivm-dap` is installed and the debug adapter server is executable. +> There is typically no need to call this function manually. + ### Telescope extension If [`telescope.nvim`](https://github.com/nvim-telescope/telescope.nvim) is installed, diff --git a/doc/haskell-tools.txt b/doc/haskell-tools.txt index 6886fab9..5c10e781 100644 --- a/doc/haskell-tools.txt +++ b/doc/haskell-tools.txt @@ -196,9 +196,10 @@ HaskellLspClientOpts *HaskellLspClientOpts* HTDapOpts *HTDapOpts* Fields: ~ - {cmd} (string[]|nil) The command to start haskell-debug-adapter with. - {logFile} (string|nil) Log file path for detected configurations. - {logLevel} (HaskellDebugAdapterLogLevel|nil) The log level for detected configurations. + {cmd} (string[]|nil) The command to start the debug adapter server with. + {logFile} (string|nil) Log file path for detected configurations. + {logLevel} (HaskellDebugAdapterLogLevel|nil) The log level for detected configurations. + {auto_discover} (boolean|AddDapConfigOpts) Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. HaskellDebugAdapterLogLevel *HaskellDebugAdapterLogLevel* diff --git a/ftplugin/cabal.lua b/ftplugin/cabal.lua index e7b22005..67ded7f6 100644 --- a/ftplugin/cabal.lua +++ b/ftplugin/cabal.lua @@ -1,2 +1,3 @@ local ht = require('haskell-tools.internal') ht.start_or_attach() +ht.dap_discover() diff --git a/ftplugin/cabalproject.lua b/ftplugin/cabalproject.lua index e7b22005..67ded7f6 100644 --- a/ftplugin/cabalproject.lua +++ b/ftplugin/cabalproject.lua @@ -1,2 +1,3 @@ local ht = require('haskell-tools.internal') ht.start_or_attach() +ht.dap_discover() diff --git a/ftplugin/haskell.lua b/ftplugin/haskell.lua index e7b22005..67ded7f6 100644 --- a/ftplugin/haskell.lua +++ b/ftplugin/haskell.lua @@ -1,2 +1,3 @@ local ht = require('haskell-tools.internal') ht.start_or_attach() +ht.dap_discover() diff --git a/ftplugin/lhaskell.lua b/ftplugin/lhaskell.lua index e7b22005..67ded7f6 100644 --- a/ftplugin/lhaskell.lua +++ b/ftplugin/lhaskell.lua @@ -1,2 +1,3 @@ local ht = require('haskell-tools.internal') ht.start_or_attach() +ht.dap_discover() diff --git a/lua/haskell-tools/config.lua b/lua/haskell-tools/config.lua index ae8fb35b..4f647676 100644 --- a/lua/haskell-tools/config.lua +++ b/lua/haskell-tools/config.lua @@ -108,9 +108,10 @@ vim.g.haskell_tools = vim.g.haskell_tools ---@see https://haskell-language-server.readthedocs.io/en/latest/configuration.html. ---@class HTDapOpts ----@field cmd string[] | nil The command to start haskell-debug-adapter with. +---@field cmd string[] | nil The command to start the debug adapter server with. ---@field logFile string | nil Log file path for detected configurations. ---@field logLevel HaskellDebugAdapterLogLevel | nil The log level for detected configurations. +---@field auto_discover boolean | AddDapConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. ---@alias HaskellDebugAdapterLogLevel 'Debug' | 'Info' | 'Warning' | 'Error' diff --git a/lua/haskell-tools/config/check.lua b/lua/haskell-tools/config/check.lua index a099d2c9..9e273a43 100644 --- a/lua/haskell-tools/config/check.lua +++ b/lua/haskell-tools/config/check.lua @@ -132,6 +132,34 @@ function HtConfigCheck.validate(cfg) if not ok then return false, err end + local dap = cfg.dap + local valid_dap_log_levels = { 'Debug', 'Info', 'Warning', 'Error' } + ok, err = validate('dap', { + auto_discover = { dap.auto_discover, { 'boolean', 'table' } }, + cmd = { dap.cmd, { 'function', 'table' } }, + logFile = { dap.logFile, 'string' }, + logLevel = { + dap.logLevel, + function(level) + return type(level) == 'string' and vim.tbl_contains(valid_dap_log_levels, level) + end, + 'one of ' .. vim.inspect(valid_backends), + }, + }) + if not ok then + return false, err + end + local auto_discover = dap.auto_discover + if type(auto_discover) == 'table' then + ---@cast auto_discover AddDapConfigOpts + ok, err = validate('dap.auto_discover', { + autodetect = { auto_discover.autodetect, 'boolean' }, + settings_file_pattern = { auto_discover.settings_file_pattern, 'string' }, + }) + if not ok then + return false, err + end + end return true end diff --git a/lua/haskell-tools/config/internal.lua b/lua/haskell-tools/config/internal.lua index d071ba58..26c4cc59 100644 --- a/lua/haskell-tools/config/internal.lua +++ b/lua/haskell-tools/config/internal.lua @@ -236,12 +236,14 @@ local HTDefaultConfig = { }, ---@class HTDapConfig debug adapter config for nvim-dap. dap = { - ---@type string[] The command to start haskell-debug-adapter with. + ---@type string[] | (fun():string[]) The command to start the debug adapter server with. cmd = { 'haskell-debug-adapter' }, ---@type string Log file path for detected configurations. logFile = vim.fn.stdpath('data') .. '/haskell-dap.log', ---@type HaskellDebugAdapterLogLevel The log level for detected configurations. logLevel = 'Warning', + ---@type boolean | AddDapConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`. + auto_discover = true, }, } diff --git a/lua/haskell-tools/dap.lua b/lua/haskell-tools/dap.lua index b34a8e25..e75d4946 100644 --- a/lua/haskell-tools/dap.lua +++ b/lua/haskell-tools/dap.lua @@ -1,6 +1,7 @@ ---@mod haskell-tools.dap haskell-tools nvim-dap setup local deps = require('haskell-tools.deps') +local Types = require('haskell-tools.types.internal') ---@param root_dir string local function get_ghci_dap_cmd(root_dir) @@ -40,7 +41,7 @@ local function find_json_configurations(root_dir, opts) end ---@param root_dir string ----@return table +---@return HsDapLaunchConfiguration[] local function detect_launch_configurations(root_dir) local launch_configurations = {} local Path = deps.require_plenary('plenary.path') @@ -83,9 +84,7 @@ local _configuration_cache = {} if not deps.has('dap') then ---@type HsDapTools local NullHsDapTools = { - discover_configurations = function(_) - vim.notify_once('haskell-tools.dap: Failed to initialise. Is nvim-dap installed?', vim.log.levels.ERROR) - end, + discover_configurations = function(_) end, } return NullHsDapTools end @@ -95,18 +94,11 @@ local dap = require('dap') ---@class HsDapTools local HsDapTools = {} -local HTConfig = require('haskell-tools.config.internal') -local dap_opts = HTConfig.dap -dap.adapters.ghc = { - type = 'executable', - command = table.concat(dap_opts.cmd, ' '), -} - ---@class AddDapConfigOpts local DefaultAutoDapConfigOpts = { - ---@type boolean Whether to automatically detect launch configurations for the project + ---@type boolean Whether to automatically detect launch configurations for the project. autodetect = true, - ---@type string File name or pattern to search for. Defaults to 'launch.json' + ---@type string File name or pattern to search for. Defaults to 'launch.json'. settings_file_pattern = 'launch.json', } @@ -115,8 +107,20 @@ local DefaultAutoDapConfigOpts = { ---@param opts AddDapConfigOpts|nil ---@return nil HsDapTools.discover_configurations = function(bufnr, opts) - local async = deps.require_plenary('plenary.async') + local HTConfig = require('haskell-tools.config.internal') + local HTDapConfig = HTConfig.dap local log = require('haskell-tools.log.internal') + local dap_cmd = Types.evaluate(HTDapConfig.cmd) or {} + if #dap_cmd == 0 or vim.fn.executable(dap_cmd[1]) ~= 1 then + log.debug { 'DAP server executable not found.', dap_cmd } + return + end + ---@cast dap_cmd string[] + dap.adapters.ghc = { + type = 'executable', + command = table.concat(dap_cmd, ' '), + } + local async = deps.require_plenary('plenary.async') async.run(function() bufnr = bufnr or 0 -- Default to current buffer opts = vim.tbl_deep_extend('force', {}, DefaultAutoDapConfigOpts, opts or {}) @@ -124,10 +128,11 @@ HsDapTools.discover_configurations = function(bufnr, opts) local HtProjectHelpers = require('haskell-tools.project.helpers') local project_root = HtProjectHelpers.match_project_root(filename) if not project_root then - log.warn('haskell-tools.dap: Unable to detect project root for file ' .. filename) + log.warn('dap: Unable to detect project root for file ' .. filename) return end if _configuration_cache[project_root] then + log.debug('dap: Found cached configuration. Skipping.') return end local discovered_configurations = {} diff --git a/lua/haskell-tools/internal.lua b/lua/haskell-tools/internal.lua index ba54b23a..74b37c3b 100644 --- a/lua/haskell-tools/internal.lua +++ b/lua/haskell-tools/internal.lua @@ -57,4 +57,17 @@ function InternalApi.start_or_attach() end end +---Auto-discover nvim-dap launch configurations (if auto-discovery is enabled) +function InternalApi.dap_discover() + local auto_discover = HTConfig.dap.auto_discover + if not auto_discover then + return + elseif type(auto_discover) == 'boolean' then + return require('haskell-tools').dap.discover_configurations() + end + ---@cast auto_discover AddDapConfigOpts + local bufnr = vim.api.nvim_get_current_buf() + require('haskell-tools').dap.discover_configurations(bufnr, auto_discover) +end + return InternalApi