1

Refresh generated neovim config

This commit is contained in:
2024-08-15 13:01:03 +02:00
parent 64b51cf53a
commit f5af8e2b28
1836 changed files with 38979 additions and 31094 deletions

View File

@ -1,49 +0,0 @@
---@diagnostic disable: deprecated, duplicate-doc-field, duplicate-doc-alias
---@mod haskell-tools.compat Functions for backward compatibility with older Neovim versions
---@brief [[
---WARNING: This is not part of the public API.
---Breaking changes to this module will not be reflected in the semantic versioning of this plugin.
---@brief ]]
local compat = {}
compat.joinpath = vim.fs.joinpath or function(...)
return (table.concat({ ... }, '/'):gsub('//+', '/'))
end
compat.get_clients = vim.lsp.get_clients or vim.lsp.get_active_clients
compat.uv = vim.uv or vim.loop
--- @class vim.SystemCompleted
--- @field code integer
--- @field signal integer
--- @field stdout? string
--- @field stderr? string
--- @alias lsp.Client vim.lsp.Client
compat.system = vim.system
-- wrapper around vim.fn.system to give it a similar API to vim.system
or function(cmd, _, on_exit)
local output = vim.fn.system(cmd)
local ok = vim.v.shell_error
---@type vim.SystemCompleted
local systemObj = {
signal = 0,
stdout = ok and (output or '') or nil,
stderr = not ok and (output or '') or nil,
code = vim.v.shell_error,
}
on_exit(systemObj)
return systemObj
end
---@type fun(tbl:table):table
compat.tbl_flatten = vim.iter and function(tbl)
return vim.iter(tbl):flatten(math.huge):totable()
end or vim.tbl_flatten
return compat

View File

@ -7,8 +7,8 @@
---@brief ]]
---@class HtConfigCheck
local HtConfigCheck = {}
---@class haskell-tools.config.Check
local Check = {}
---@param path string
---@param msg string|nil
@ -29,10 +29,10 @@ local function validate(path, tbl)
end
---Validates the config.
---@param cfg HTConfig
---@param cfg haskell-tools.Config
---@return boolean is_valid
---@return string|nil error_message
function HtConfigCheck.validate(cfg)
function Check.validate(cfg)
local ok, err
local hls = cfg.hls
ok, err = validate('hls', {
@ -151,7 +151,7 @@ function HtConfigCheck.validate(cfg)
end
local auto_discover = dap.auto_discover
if type(auto_discover) == 'table' then
---@cast auto_discover AddDapConfigOpts
---@cast auto_discover haskell-tools.dap.AddConfigOpts
ok, err = validate('dap.auto_discover', {
autodetect = { auto_discover.autodetect, 'boolean' },
settings_file_pattern = { auto_discover.settings_file_pattern, 'string' },
@ -169,7 +169,7 @@ end
---@param default_tbl table
---@param ignored_keys string[]
---@return string[]
function HtConfigCheck.get_unrecognized_keys(tbl, default_tbl, ignored_keys)
function Check.get_unrecognized_keys(tbl, default_tbl, ignored_keys)
local unrecognized_keys = {}
for k, _ in pairs(tbl) do
unrecognized_keys[k] = true
@ -183,7 +183,7 @@ function HtConfigCheck.get_unrecognized_keys(tbl, default_tbl, ignored_keys)
ret[k] = k
end
if type(default_tbl[k]) == 'table' and tbl[k] then
for _, subk in pairs(HtConfigCheck.get_unrecognized_keys(tbl[k], default_tbl[k], {})) do
for _, subk in pairs(Check.get_unrecognized_keys(tbl[k], default_tbl[k], {})) do
local key = k .. '.' .. subk
ret[key] = key
end
@ -199,4 +199,4 @@ function HtConfigCheck.get_unrecognized_keys(tbl, default_tbl, ignored_keys)
return vim.tbl_keys(ret)
end
return HtConfigCheck
return Check

View File

@ -2,122 +2,223 @@
---
---@brief [[
---To configure haskell-tools.nvim, set the variable `vim.g.haskell_tools`,
---which is a `HTOpts` table, in your neovim configuration.
---which is a `haskell-tools.Opts` table, in your neovim configuration.
---
---Example:
--->
------@type HTOpts
------@type haskell-tools.Opts
---vim.g.haskell_tools = {
--- ---@type ToolsOpts
--- ---@type haskell-tools.tools.Opts
--- tools = {
--- -- ...
--- },
--- ---@type HaskellLspClientOpts
--- ---@type haskell-tools.lsp.ClientOpts
--- hls = {
--- on_attach = function(client, bufnr)
--- -- Set keybindings, etc. here.
--- end,
--- -- ...
--- },
--- ---@type HTDapOpts
--- ---@type haskell-tools.dap.Opts
--- dap = {
--- -- ...
--- },
--- }
---<
---
---Note: `vim.g.haskell_tools` can also be a function that returns a 'HTOpts' table.
---Note: `vim.g.haskell_tools` can also be a function that returns a 'haskell-tools.Opts' table.
---
---@brief ]]
local config = {}
---@type (fun():HTOpts) | HTOpts | nil
---@type (fun():haskell-tools.Opts) | haskell-tools.Opts | nil
vim.g.haskell_tools = vim.g.haskell_tools
---@class HTOpts
---@field tools? ToolsOpts haskell-tools module options.
---@field hls? HaskellLspClientOpts haskell-language-server client options.
---@field dap? HTDapOpts debug adapter config for nvim-dap.
---@class ToolsOpts
---@field codeLens? CodeLensOpts LSP codeLens options.
---@field hoogle? HoogleOpts Hoogle type signature search options.
---@field hover? HoverOpts LSP hover options.
---@field definition? DefinitionOpts LSP go-to-definition options.
---@field repl? ReplOpts GHCi repl options.
---@field tags? FastTagsOpts fast-tags module options.
---@field log? HTLogOpts haskell-tools logger options.
---@class haskell-tools.Opts
---
---haskell-tools module options.
---@field tools? haskell-tools.tools.Opts
---
---haskell-language-server client options.
---@field hls? haskell-tools.lsp.ClientOpts
---
---debug adapter config for nvim-dap.
---@field dap? haskell-tools.dap.Opts
---@class CodeLensOpts
---@field autoRefresh? (fun():boolean) | boolean (default: `true`) Whether to auto-refresh code-lenses.
---@class haskell-tools.tools.Opts
---
---LSP codeLens options.
---@field codeLens? haskell-tools.codeLens.Opts
---
---Hoogle type signature search options.
---@field hoogle? haskell-tools.hoogle.Opts
---
---LSP hover options.
---@field hover? haskell-tools.hover.Opts
---LSP go-to-definition options.
---@field definition? haskell-tools.definition.Opts
---
---GHCi repl options.
---@field repl? haskell-tools.repl.Opts
---
---fast-tags module options.
---@field tags? haskell-tools.fast-tags.Opts
---
---haskell-tools logger options.
---@field log? haskell-tools.log.Opts
---@class HoogleOpts
---@field mode? HoogleMode Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search?
---@class haskell-tools.codeLens.Opts
---
---(default: `true`) Whether to auto-refresh code-lenses.
---@field autoRefresh? (fun():boolean) | boolean
---@alias HoogleMode 'auto' | 'telescope-local' | 'telescope-web' | 'browser'
---@class haskell-tools.hoogle.Opts
---
---Use a telescope with a local hoogle installation or a web backend,
---or use the browser for hoogle signature search?
---@field mode? haskell-tools.hoogle.Mode
---@class HoverOpts
---@field enable? (fun():boolean) | boolean (default: `true`) Whether to enable haskell-tools hover.
---@field border? string[][] The hover window's border. Set to `nil` to disable.
---@field stylize_markdown? boolean (default: `false`) The builtin LSP client's default behaviour is to stylize markdown. Setting this option to false sets the file type to markdown and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed.
---@field auto_focus? boolean (default: `false`) Whether to automatically switch to the hover window.
---@alias haskell-tools.hoogle.Mode 'auto' | 'telescope-local' | 'telescope-web' | 'browser'
---@class DefinitionOpts
---@field hoogle_signature_fallback? (fun():boolean) | boolean (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`).
---@class haskell-tools.hover.Opts
---
---(default: `true`) Whether to enable haskell-tools hover.
---@field enable? (fun():boolean) | boolean
---
---The hover window's border. Set to `nil` to disable.
---@field border? string[][]
---
---(default: `false`) The builtin LSP client's default behaviour is to stylize markdown.
---Setting this option to false sets the file type to markdown
---and enables treesitter syntax highligting for Haskell snippets if nvim-treesitter is installed.
---@field stylize_markdown? boolean
---
---(default: `false`) Whether to automatically switch to the hover window.
---@field auto_focus? boolean
---@class ReplOpts
---@field handler? (fun():ReplHandler) | ReplHandler `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim.
---@field prefer? (fun():repl_backend) | repl_backend Prefer cabal or stack when both stack and cabal project files are present?
---@field builtin? BuiltinReplOpts Configuration for the builtin repl.
---@field auto_focus? boolean Whether to auto-focus the repl on toggle or send. If unset, the handler decides.
---@class haskell-tools.definition.Opts
---
---(default: `false`) Configure |vim.lsp.definition| to fall back to hoogle search
---(does not affect |vim.lsp.tagfunc|).
---@field hoogle_signature_fallback? (fun():boolean) | boolean
---@alias ReplHandler 'builtin' | 'toggleterm'
---@alias repl_backend 'cabal' | 'stack'
---@class haskell-tools.repl.Opts
---
---`'builtin'`: Use the simple builtin repl.
---`'toggleterm'`: Use akinsho/toggleterm.nvim.
---@field handler? (fun():haskell-tools.repl.Handler) | haskell-tools.repl.Handler
---
---Prefer cabal or stack when both stack and cabal project files are present?
---@field prefer? (fun():haskell-tools.repl.Backend) | haskell-tools.repl.Backend
---
---Configuration for the builtin repl.
---@field builtin? haskell-tools.repl.builtin.Opts
---
---Whether to auto-focus the repl on toggle or send. If unset, the handler decides.
---@field auto_focus? boolean
---@class BuiltinReplOpts
---@field create_repl_window? (fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun)) How to create the repl window. Should return a function that calls one of the `ReplView`'s functions.
---@alias haskell-tools.repl.Handler 'builtin' | 'toggleterm'
---@alias haskell-tools.repl.Backend 'cabal' | 'stack'
---@class ReplView
---@field create_repl_split? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a horizontally split window.
---@field create_repl_vsplit? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a vertically split window.
---@field create_repl_tabnew? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in a new tab.
---@field create_repl_cur_win? fun(opts:ReplViewOpts):mk_repl_cmd_fun Create the REPL in the current window.
---@class haskell-tools.repl.builtin.Opts
---
---How to create the repl window.
---Should return a function that calls one of the |haskell-tools.repl.View|'s functions.
---@field create_repl_window? (fun(view:haskell-tools.repl.View):fun(mk_repl_cmd:mk_ht_repl_cmd_fun))
---@class ReplViewOpts
---@field delete_buffer_on_exit? boolean Whether to delete the buffer when the Repl quits.
---@field size? (fun():number) | number The size of the window or a function that determines it.
---@class haskell-tools.repl.View
---
---Create the REPL in a horizontally split window.
---@field create_repl_split? fun(opts:haskell-tools.repl.view.Opts):mk_ht_repl_cmd_fun
---
---Create the REPL in a vertically split window.
---@field create_repl_vsplit? fun(opts:haskell-tools.repl.view.Opts):mk_ht_repl_cmd_fun
---
---Create the REPL in a new tab.
---@field create_repl_tabnew? fun(opts:haskell-tools.repl.view.Opts):mk_ht_repl_cmd_fun
---
---Create the REPL in the current window.
---@field create_repl_cur_win? fun(opts:haskell-tools.repl.view.Opts):mk_ht_repl_cmd_fun
---@alias mk_repl_cmd_fun fun():(string[]|nil)
---@class haskell-tools.repl.view.Opts
---
---Whether to delete the buffer when the Repl quits.
---@field delete_buffer_on_exit? boolean
---
---The size of the window or a function that determines it.
---@field size? (fun():number) | number
---@class FastTagsOpts
---@field enable? boolean | (fun():boolean) Enabled by default if the `fast-tags` executable is found.
---@field package_events? string[] `autocmd` Events to trigger package tag generation.
---@alias mk_ht_repl_cmd_fun fun():(string[]|nil)
---@class HTLogOpts
---@field level? number | string The log level.
---@class haskell-tools.fast-tags.Opts
---
---Enabled by default if the `fast-tags` executable is found.
---@field enable? boolean | (fun():boolean)
---
---The |autocmd| events to trigger package tag generation.
---@field package_events? string[]
---@class haskell-tools.log.Opts
---
---The log level.
---@field level? number | string
---@see vim.log.levels
---@class HaskellLspClientOpts
---@field auto_attach? (fun():boolean) | boolean Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found.
---@field debug? boolean Whether to enable haskell-language-server debug logging.
---@field on_attach? fun(client:number,bufnr:number,ht:HaskellTools) Callback that is invoked when the client attaches to a buffer.
---@field cmd? (fun():string[]) | string[] The command to start haskell-language-server with.
---@field capabilities? lsp.ClientCapabilities LSP client capabilities.
---@field settings? (fun(project_root:string|nil):table) | table The haskell-language-server settings or a function that creates them. To view the default settings, run `haskell-language-server generate-default-config`.
---@field default_settings? table The default haskell-language-server settings that will be used if no settings are specified or detected.
---@field logfile? string The path to the haskell-language-server log file.
---@class haskell-tools.lsp.ClientOpts
---
---Whether to automatically attach the LSP client.
---Defaults to `true` if the haskell-language-server executable is found.
---@field auto_attach? (fun():boolean) | boolean
---
---Whether to enable haskell-language-server debug logging.
---@field debug? boolean
---
---Callback that is invoked when the client attaches to a buffer.
---@field on_attach? fun(client:number,bufnr:number,ht:HaskellTools)
---
---The command to start haskell-language-server with.
---@field cmd? (fun():string[]) | string[]
---
---LSP client capabilities.
---@field capabilities? lsp.ClientCapabilities
---
---The haskell-language-server settings or a function that creates them.
--- To view the default settings, run `haskell-language-server generate-default-config`.
---@field settings? (fun(project_root:string|nil):table) | table
---
---The default haskell-language-server settings that will be used if no settings are specified or detected.
---@field default_settings? table
---
---The path to the haskell-language-server log file.
---@field logfile? string
---@brief [[
--- To print all options that are available for your haskell-language-server version, run `haskell-language-server-wrapper generate-default-config`
---See: https://haskell-language-server.readthedocs.io/en/latest/configuration.html.
---@brief ]]
---@class HTDapOpts
---@field cmd? string[] The command to start the debug adapter server with.
---@field logFile? string Log file path for detected configurations.
---@field logLevel? HaskellDebugAdapterLogLevel 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`.
---@class haskell-tools.dap.Opts
---
---The command to start the debug adapter server with.
---@field cmd? string[]
---
---Log file path for detected configurations.
---@field logFile? string
---
---The log level for detected configurations.
---@field logLevel? haskell-tools.debugAdapter.LogLevel
---@field auto_discover? boolean | haskell-tools.dap.AddConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`.
---@alias HaskellDebugAdapterLogLevel 'Debug' | 'Info' | 'Warning' | 'Error'
---@alias haskell-tools.debugAdapter.LogLevel 'Debug' | 'Info' | 'Warning' | 'Error'
---@class haskell-tools.dap.AddConfigOpts
---
---Whether to automatically detect launch configurations for the project.
---@field autodetect boolean
---
---File name or pattern to search for.
---Defaults to 'launch.json'.
---@field settings_file_pattern string
return config

View File

@ -10,9 +10,8 @@
---@brief ]]
local deps = require('haskell-tools.deps')
local compat = require('haskell-tools.compat')
---@type HTConfig
---@type haskell-tools.Config
local HTConfig = {}
local ht_capabilities = vim.lsp.protocol.make_client_capabilities()
@ -40,22 +39,22 @@ local capabilities = vim.tbl_deep_extend(
folding_range_capabilities
)
---@class HTConfig haskell-tools.nvim plugin configuration.
---@class haskell-tools.Config haskell-tools.nvim plugin configuration.
local HTDefaultConfig = {
---@class ToolsConfig haskell-tools module config.
---@class haskell-tools.tools.Config haskell-tools module config.
tools = {
---@class CodeLensConfig LSP codeLens options.
---@class haskell-tools.codeLens.Config LSP codeLens options.
codeLens = {
---@type boolean | (fun():boolean) (default: `true`) Whether to auto-refresh code-lenses.
autoRefresh = true,
},
---@class HoogleConfig hoogle type signature search config.
---@class haskell-tools.hoogle.Config hoogle type signature search config.
hoogle = {
---@type HoogleMode Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search?
---@type haskell-tools.hoogle.Mode Use a telescope with a local hoogle installation or a web backend, or use the browser for hoogle signature search?
mode = 'auto',
},
---@class HoverConfig Enhanced LSP hover options.
---@class haskell-tools.hover.Config Enhanced LSP hover options.
hover = {
---@type boolean | (fun():boolean) (default: `true`) Whether to enable haskell-tools hover.
enable = true,
@ -75,22 +74,22 @@ local HTDefaultConfig = {
---@type boolean (default: `false`) Whether to automatically switch to the hover window.
auto_focus = false,
},
---@class DefinitionConfig Enhanced LSP go-to-definition options.
---@class haskell-tools.definition.Config Enhanced LSP go-to-definition options.
definition = {
---@type boolean | (fun():boolean) (default: `false`) Configure `vim.lsp.definition` to fall back to hoogle search (does not affect `vim.lsp.tagfunc`).
hoogle_signature_fallback = false,
},
---@class ReplConfig GHCi repl options.
---@class haskell-tools.repl.Config GHCi repl options.
repl = {
---@type ReplHandler | (fun():ReplHandler) `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim.
---@type haskell-tools.repl.Handler | (fun():haskell-tools.repl.Handler) `'builtin'`: Use the simple builtin repl. `'toggleterm'`: Use akinsho/toggleterm.nvim.
handler = 'builtin',
---@type repl_backend | (fun():repl_backend) Prefer cabal or stack when both stack and cabal project files are present?
---@type haskell-tools.repl.Backend | (fun():haskell-tools.repl.Backend) Prefer cabal or stack when both stack and cabal project files are present?
prefer = function()
return vim.fn.executable('stack') == 1 and 'stack' or 'cabal'
end,
---@class BuiltinReplConfig Configuration for the builtin repl
---@class haskell-tools.repl.builtin.Config Configuration for the builtin repl
builtin = {
---@type fun(view:ReplView):fun(mk_repl_cmd:mk_repl_cmd_fun) How to create the repl window. Should return a function that calls one of the `ReplView`'s functions.
---@type fun(view:haskell-tools.repl.View):fun(mk_repl_cmd:mk_ht_repl_cmd_fun) How to create the repl window. Should return a function that calls one of the `ReplView`'s functions.
create_repl_window = function(view)
return view.create_repl_split { size = vim.o.lines / 3 }
end,
@ -98,7 +97,7 @@ local HTDefaultConfig = {
---@type boolean | nil Whether to auto-focus the repl on toggle or send. If unset, the handler decides.
auto_focus = nil,
},
---@class FastTagsConfig fast-tags module options.
---@class haskell-tools.fast-tags.Config fast-tags module options.
tags = {
---@type boolean | (fun():boolean) Enabled by default if the `fast-tags` executable is found.
enable = function()
@ -107,16 +106,16 @@ local HTDefaultConfig = {
---@type string[] `autocmd` Events to trigger package tag generation.
package_events = { 'BufWritePost' },
},
---@class HTLogConfig haskell-tools logger options.
---@class haskell-tools.log.Config haskell-tools logger options.
log = {
---@diagnostic disable-next-line: param-type-mismatch
logfile = compat.joinpath(vim.fn.stdpath('log'), 'haskell-tools.log'),
logfile = vim.fs.joinpath(vim.fn.stdpath('log'), 'haskell-tools.log'),
---@type number | string The log level.
---@see vim.log.levels
level = vim.log.levels.WARN,
},
},
---@class HaskellLspClientConfig haskell-language-server client options.
---@class haskell-tools.lsp.ClientConfig haskell-language-server client options.
hls = {
---@type boolean | (fun():boolean) Whether to automatically attach the LSP client. Defaults to `true` if the haskell-language-server executable is found.
auto_attach = function()
@ -250,15 +249,15 @@ local HTDefaultConfig = {
---@type string The path to the haskell-language-server log file.
logfile = vim.fn.tempname() .. '-haskell-language-server.log',
},
---@class HTDapConfig debug adapter config for nvim-dap.
---@class haskell-tools.dap.Config debug adapter config for nvim-dap.
dap = {
---@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.
---@type haskell-tools.debugAdapter.LogLevel 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`.
---@type boolean | haskell-tools.dap.AddConfigOpts Set to `false` to disable auto-discovery of launch configurations. `true` uses the default configurations options`.
auto_discover = true,
},
debug_info = {
@ -268,10 +267,10 @@ local HTDefaultConfig = {
}
local haskell_tools = vim.g.haskell_tools or {}
---@type HTOpts
---@type haskell-tools.Opts
local opts = type(haskell_tools) == 'function' and haskell_tools() or haskell_tools
---@type HTConfig
---@type haskell-tools.Config
HTConfig = vim.tbl_deep_extend('force', {}, HTDefaultConfig, opts)
local check = require('haskell-tools.config.check')
local ok, err = check.validate(HTConfig)

View File

@ -2,7 +2,6 @@
local deps = require('haskell-tools.deps')
local Types = require('haskell-tools.types.internal')
local compat = require('haskell-tools.compat')
---@param root_dir string
local function get_ghci_dap_cmd(root_dir)
@ -15,13 +14,13 @@ local function get_ghci_dap_cmd(root_dir)
end
---@param root_dir string
---@param opts AddDapConfigOpts
---@return HsDapLaunchConfiguration[]
---@param opts haskell-tools.dap.AddConfigOpts
---@return haskell-tools.dap.LaunchConfiguration[]
local function find_json_configurations(root_dir, opts)
---@type HsDapLaunchConfiguration[]
---@type haskell-tools.dap.LaunchConfiguration[]
local configurations = {}
local log = require('haskell-tools.log.internal')
local results = vim.fn.glob(compat.joinpath(root_dir, opts.settings_file_pattern), true, true)
local results = vim.fn.glob(vim.fs.joinpath(root_dir, opts.settings_file_pattern), true, true)
if #results == 0 then
log.info(opts.settings_file_pattern .. ' not found in project root ' .. root_dir)
else
@ -41,21 +40,21 @@ local function find_json_configurations(root_dir, opts)
end
---@param root_dir string
---@return HsDapLaunchConfiguration[]
---@return haskell-tools.dap.LaunchConfiguration[]
local function detect_launch_configurations(root_dir)
local launch_configurations = {}
local HTConfig = require('haskell-tools.config.internal')
local dap_opts = HTConfig.dap
---@param entry_point HsEntryPoint
---@return HsDapLaunchConfiguration
---@param entry_point haskell-tools.EntryPoint
---@return haskell-tools.dap.LaunchConfiguration
local function mk_launch_configuration(entry_point)
---@class HsDapLaunchConfiguration
local HsDapLaunchConfiguration = {
---@class haskell-tools.dap.LaunchConfiguration
local LaunchConfiguration = {
type = 'ghc',
request = 'launch',
name = entry_point.package_name .. ':' .. entry_point.exe_name,
workspace = '${workspaceFolder}',
startup = compat.joinpath(entry_point.package_dir, entry_point.source_dir, entry_point.main),
startup = vim.fs.joinpath(entry_point.package_dir, entry_point.source_dir, entry_point.main),
startupFunc = '', -- defaults to 'main' if not set
startupArgs = '',
stopOnEntry = false,
@ -68,7 +67,7 @@ local function detect_launch_configurations(root_dir)
ghciCmd = get_ghci_dap_cmd(root_dir),
forceInspect = false,
}
return HsDapLaunchConfiguration
return LaunchConfiguration
end
local HtProjectHelpers = require('haskell-tools.project.helpers')
for _, entry_point in pairs(HtProjectHelpers.parse_project_entrypoints(root_dir)) do
@ -81,7 +80,7 @@ end
local _configuration_cache = {}
if not deps.has('dap') then
---@type HsDapTools
---@type haskell-tools.Dap
local NullHsDapTools = {
discover_configurations = function(_) end,
}
@ -90,22 +89,20 @@ end
local dap = require('dap')
---@class HsDapTools
local HsDapTools = {}
---@class haskell-tools.Dap
local Dap = {}
---@class AddDapConfigOpts
---@type haskell-tools.dap.AddConfigOpts
local DefaultAutoDapConfigOpts = {
---@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'.
settings_file_pattern = 'launch.json',
}
---Discover nvim-dap launch configurations for haskell-debug-adapter.
---@param bufnr number|nil The buffer number
---@param opts AddDapConfigOpts|nil
---@param opts haskell-tools.dap.AddConfigOpts|nil
---@return nil
HsDapTools.discover_configurations = function(bufnr, opts)
Dap.discover_configurations = function(bufnr, opts)
local HTConfig = require('haskell-tools.config.internal')
local HTDapConfig = HTConfig.dap
local log = require('haskell-tools.log.internal')
@ -140,7 +137,7 @@ HsDapTools.discover_configurations = function(bufnr, opts)
vim.list_extend(discovered_configurations, detected_configurations)
end
_configuration_cache[project_root] = discovered_configurations
---@type HsDapLaunchConfiguration[]
---@type haskell-tools.dap.LaunchConfiguration[]
local dap_configurations = dap.configurations.haskell or {}
for _, cfg in ipairs(discovered_configurations) do
for i, existing_config in pairs(dap_configurations) do
@ -153,4 +150,4 @@ HsDapTools.discover_configurations = function(bufnr, opts)
dap.configurations.haskell = dap_configurations
end
return HsDapTools
return Dap

View File

@ -1,6 +1,4 @@
local compat = require('haskell-tools.compat')
---@class DapInternal
---@class haskell-tools.internal.Dap
local Dap = {}
---@param package_name string
@ -8,13 +6,13 @@ local Dap = {}
---@param package_dir string
---@param mains string[]
---@param source_dirs string[]
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
Dap.mk_entry_points = function(package_name, exe_name, package_dir, mains, source_dirs)
---@type HsEntryPoint[]
---@type haskell-tools.EntryPoint[]
local entry_points = {}
for _, source_dir in pairs(source_dirs) do
for _, main in pairs(mains) do
local filename = compat.joinpath(package_dir, source_dir, main)
local filename = vim.fs.joinpath(package_dir, source_dir, main)
if vim.fn.filereadable(filename) == 1 then
local entry_point = {
package_name = package_name,

View File

@ -7,7 +7,7 @@
---@brief ]]
---@class Deps
---@class haskell-tools.Deps
local Deps = {}
---@param modname string The name of the module

View File

@ -22,13 +22,13 @@ local error = h.error or h.report_error
---@diagnostic disable-next-line: deprecated
local warn = h.warn or h.report_warn
---@class LuaDependency
---@class haskell-tools.LuaDependency
---@field module string The name of a module
---@field optional fun():boolean Function that returns whether the dependency is optional
---@field url string URL (markdown)
---@field info string Additional information
---@type LuaDependency[]
---@type haskell-tools.LuaDependency[]
local lua_dependencies = {
{
module = 'telescope',
@ -44,7 +44,7 @@ local lua_dependencies = {
},
}
---@class ExternalDependency
---@class haskell-tools.ExternalDependency
---@field name string Name of the dependency
---@field get_binaries fun():string[]Function that returns the binaries to check for
---@field optional fun():boolean Function that returns whether the dependency is optional
@ -52,7 +52,7 @@ local lua_dependencies = {
---@field info string Additional information
---@field extra_checks function|nil Optional extra checks to perform if the dependency is installed
---@type ExternalDependency[]
---@type haskell-tools.ExternalDependency[]
local external_dependencies = {
{
name = 'haskell-language-server',
@ -153,7 +153,7 @@ local external_dependencies = {
},
}
---@param dep LuaDependency
---@param dep haskell-tools.LuaDependency
local function check_lua_dependency(dep)
if deps.has(dep.module) then
ok(dep.url .. ' installed.')
@ -166,7 +166,7 @@ local function check_lua_dependency(dep)
end
end
---@param dep ExternalDependency
---@param dep haskell-tools.ExternalDependency
---@return boolean is_installed
---@return string|nil version
local check_installed = function(dep)
@ -188,7 +188,7 @@ local check_installed = function(dep)
return false
end
---@param dep ExternalDependency
---@param dep haskell-tools.ExternalDependency
local function check_external_dependency(dep)
local installed, mb_version = check_installed(dep)
if installed then

View File

@ -17,13 +17,13 @@ local actions = deps.require_telescope('telescope.actions')
local actions_state = deps.require_telescope('telescope.actions.state')
local entry_display = deps.require_telescope('telescope.pickers.entry_display')
---@class HoogleHelpers
local HoogleHelpers = {}
---@class haskell-tools.hoogle.Helpers
local Helpers = {}
---@param buf number the telescope buffebuffer numberr
---@param map fun(mode:string,keys:string,action:function) callback for creating telescope keymaps
---@return boolean
function HoogleHelpers.hoogle_attach_mappings(buf, map)
function Helpers.hoogle_attach_mappings(buf, map)
actions.select_default:replace(function()
-- Copy type signature to clipboard
local entry = actions_state.get_selected_entry()
@ -57,19 +57,19 @@ local function format_html(html)
return html and html:gsub('&lt;', '<'):gsub('&gt;', '>'):gsub('&amp', '&') or ''
end
---@class TelescopeHoogleEntry
---@class haskell-tools.hoogle.telescope.Entry
---@field value string
---@field valid boolean
---@field type_sig string The entry's type signature
---@field module_name string The name of the module that contains the entry
---@field url string|nil The entry's Hackage URL
---@field docs string|nil The Hoogle entry's documentation
---@field display fun(TelescopeHoogleEntry):TelescopeDisplay
---@field display fun(TelescopeHoogleEntry):haskell-tools.telescope.Display
---@field ordinal string
---@field preview_command fun(TelescopeHoogleEntry, number):nil
---Show a preview in the Telescope previewer
---@param entry TelescopeHoogleEntry
---@param entry haskell-tools.hoogle.telescope.Entry
---@param buf number the Telescope preview buffer
local function show_preview(entry, buf)
local docs = format_html(entry.docs)
@ -85,10 +85,10 @@ local function show_preview(entry, buf)
end)
end
---@class TelescopeDisplay
---@class haskell-tools.telescope.Display
---@param entry TelescopeHoogleEntry
---@return TelescopeDisplay
---@param entry haskell-tools.hoogle.telescope.Entry
---@return haskell-tools.telescope.Display
local function make_display(entry)
local module = entry.module_name
@ -114,18 +114,18 @@ local function get_type_sig(hoogle_item)
return hoogle_item
end
---@class HoogleData
---@field module HoogleModule|nil
---@class haskell-tools.hoogle.Data
---@field module haskell-tools.hoogle.Module|nil
---@field item string|nil
---@field url string|nil
---@field docs string|nil
---@class HoogleModule
---@class haskell-tools.hoogle.Module
---@field name string
---@param data HoogleData
---@return TelescopeHoogleEntry|nil
function HoogleHelpers.mk_hoogle_entry(data)
---@param data haskell-tools.hoogle.Data
---@return haskell-tools.hoogle.telescope.Entry|nil
function Helpers.mk_hoogle_entry(data)
local module_name = (data.module or {}).name
local type_sig = data.item and get_type_sig(data.item) or ''
if not module_name or not type_sig then
@ -144,4 +144,4 @@ function HoogleHelpers.mk_hoogle_entry(data)
}
end
return HoogleHelpers
return Helpers

View File

@ -87,12 +87,12 @@ elseif opts.mode == 'auto' then
end
end
---@class HoogleTools
local HoogleTools = {}
---@class haskell-tools.Hoogle
local Hoogle = {}
---@param options table<string,any>|nil Includes the `search_term` and options to pass to the telescope picker (if available)
---@return nil
HoogleTools.hoogle_signature = function(options)
Hoogle.hoogle_signature = function(options)
options = options or {}
log.debug { 'Hoogle signature search options', options }
if options.search_term then
@ -111,4 +111,4 @@ HoogleTools.hoogle_signature = function(options)
end
end
return HoogleTools
return Hoogle

View File

@ -9,35 +9,34 @@
local log = require('haskell-tools.log.internal')
local deps = require('haskell-tools.deps')
local compat = require('haskell-tools.compat')
---@class LocalHoogleHandler
local HoogleLocal = {}
---@class haskell-tools.hoogle.handler.Local
local Local = {}
---@return boolean has_hoogle `true` if the `hoogle` executable exists
function HoogleLocal.has_hoogle()
function Local.has_hoogle()
return vim.fn.executable('hoogle') == 1
end
if not HoogleLocal.has_hoogle() then
return HoogleLocal
if not Local.has_hoogle() then
return Local
end
if not deps.has_telescope() then
return HoogleLocal
return Local
end
---@class LocalHoogleOpts
---@class haskell-tools.hoogle.handler.Local.Opts
---@field entry_maker function|nil telescope entry maker
---@field count number|nil number of results to display
---Construct the hoogle cli arguments
---@param search_term string The Hoogle search term
---@param opts LocalHoogleOpts
---@param opts haskell-tools.hoogle.handler.Local.Opts
---@return string[] hoogle_args
local function mk_hoogle_args(search_term, opts)
local count = opts.count or 50
local args = compat.tbl_flatten { '--json', '--count=' .. count, search_term }
local args = vim.iter({ '--json', '--count=' .. count, search_term }):flatten():totable()
log.debug { 'Hoogle local args', args }
return args
end
@ -48,9 +47,9 @@ local previewers = deps.require_telescope('telescope.previewers')
local HoogleHelpers = require('haskell-tools.hoogle.helpers')
---@param search_term string The Hoogle search term
---@param opts LocalHoogleOpts|nil
---@param opts haskell-tools.hoogle.handler.Local.Opts|nil
---@return nil
function HoogleLocal.telescope_search(search_term, opts)
function Local.telescope_search(search_term, opts)
opts = opts or {}
opts.entry_maker = opts.entry_maker or HoogleHelpers.mk_hoogle_entry
local config = deps.require_telescope('telescope.config').values
@ -61,7 +60,7 @@ function HoogleLocal.telescope_search(search_term, opts)
return
end
local cmd = vim.list_extend({ 'hoogle' }, mk_hoogle_args(search_term, opts))
compat.system(
vim.system(
cmd,
nil,
vim.schedule_wrap(function(result)
@ -99,4 +98,4 @@ function HoogleLocal.telescope_search(search_term, opts)
)
end
return HoogleLocal
return Local

View File

@ -10,9 +10,8 @@
local log = require('haskell-tools.log.internal')
local deps = require('haskell-tools.deps')
local OS = require('haskell-tools.os')
local compat = require('haskell-tools.compat')
---@class WebHoogleHandler
---@class haskell-tools.hoogle.handler.Web
local WebHoogleHandler = {}
---@param c string A single character
@ -31,16 +30,16 @@ local function urlencode(url)
return url
end
---@class TelescopeHoogleWebOpts
---@field hoogle HoogleWebSearchOpts|nil
---@class haskell-tools.hoogle.telescope.web.Opts
---@field hoogle haskell-tools.hoogle.web-search.Opts|nil
---@class HoogleWebSearchOpts
---@class haskell-tools.hoogle.web-search.Opts
---@field scope string|nil The scope of the search
---@field json boolean|nil Whather to request JSON enocded results
---Build a Hoogle request URL
---@param search_term string
---@param opts TelescopeHoogleWebOpts
---@param opts haskell-tools.hoogle.telescope.web.Opts
local function mk_hoogle_request(search_term, opts)
local hoogle_opts = opts.hoogle or {}
local scope_param = hoogle_opts.scope and '&scope=' .. hoogle_opts.scope or ''
@ -59,7 +58,7 @@ if deps.has_telescope() then
local HoogleHelpers = require('haskell-tools.hoogle.helpers')
---@param search_term string
---@param opts TelescopeHoogleWebOpts|nil
---@param opts haskell-tools.hoogle.telescope.web.Opts|nil
---@return nil
function WebHoogleHandler.telescope_search(search_term, opts)
local config = deps.require_telescope('telescope.config').values
@ -81,7 +80,7 @@ if deps.has_telescope() then
local url = mk_hoogle_request(search_term, opts)
local curl_command = { 'curl', '--silent', url, '-H', 'Accept: application/json' }
log.debug(curl_command)
compat.system(curl_command, nil, function(result)
vim.system(curl_command, nil, function(result)
---@cast result vim.SystemCompleted
log.debug { 'Hoogle web response', result }
local response = result.stdout
@ -117,7 +116,7 @@ if deps.has_telescope() then
end
---@param search_term string
---@param opts TelescopeHoogleWebOpts|nil
---@param opts haskell-tools.hoogle.telescope.web.Opts|nil
---@return nil
function WebHoogleHandler.browser_search(search_term, opts)
opts = vim.tbl_deep_extend('keep', opts or {}, {

View File

@ -39,19 +39,19 @@ end
---@class HaskellTools
local HaskellTools = {
---@type HlsTools
---@type haskell-tools.Hls
lsp = lazy_require('haskell-tools.lsp'),
---@type HoogleTools
---@type haskell-tools.Hoogle
hoogle = lazy_require('haskell-tools.hoogle'),
---@type HsReplTools
---@type haskell-tools.Repl
repl = lazy_require('haskell-tools.repl'),
---@type HsProjectTools
---@type haskell-tools.Project
project = lazy_require('haskell-tools.project'),
---@type FastTagsTools
---@type haskell-tools.FastTags
tags = lazy_require('haskell-tools.tags'),
---@type HsDapTools
---@type haskell-tools.Dap
dap = lazy_require('haskell-tools.dap'),
---@type HaskellToolsLog
---@type haskell-tools.Log
log = lazy_require('haskell-tools.log'),
}

View File

@ -10,8 +10,8 @@
local HTConfig = require('haskell-tools.config.internal')
---@class InternalApi
local InternalApi = {}
---@class haskell-tools.internal.Api
local Api = {}
---@return boolean tf Is LSP supported for the current buffer?
local function buf_is_lsp_supported()
@ -65,15 +65,24 @@ local function dap_discover()
elseif type(auto_discover) == 'boolean' then
return require('haskell-tools').dap.discover_configurations()
end
---@cast auto_discover AddDapConfigOpts
---@cast auto_discover haskell-tools.dap.AddConfigOpts
local bufnr = vim.api.nvim_get_current_buf()
require('haskell-tools').dap.discover_configurations(bufnr, auto_discover)
end
local function init()
if vim.g.haskell_tools_loaded then
return
end
vim.g.haskell_tools_loaded = true
require('haskell-tools.commands').init()
end
---ftplugin implementation
function InternalApi.ftplugin()
function Api.ftplugin()
init()
start_or_attach()
dap_discover()
end
return InternalApi
return Api

View File

@ -3,35 +3,35 @@
---@brief [[
--- The following commands are available:
---
--- * `:HtLog` - Open the haskell-tools.nvim log file.
--- * `:HlsLog` - Open the haskell-language-server log file.
--- * `:HtSetLogLevel` - Set the haskell-tools.nvim and LSP client log level.
--- * `:Haskell log setLevel` - Set the haskell-tools.nvim and LSP client log level.
--- * `:Haskell log openLog` - Open the haskell-tools.nvim log file.
--- * `:Haskell log openHlsLog` - Open the haskell-language-server log file.
---@brief ]]
---@class HaskellToolsLog
local HaskellToolsLog = {}
---@class haskell-tools.Log
local Log = {}
---Get the haskell-language-server log file
---@return string filepath
function HaskellToolsLog.get_hls_logfile()
function Log.get_hls_logfile()
return require('haskell-tools.log.internal').get_hls_logfile()
end
---Get the haskell-tools.nvim log file path.
---@return string filepath
function HaskellToolsLog.get_logfile()
function Log.get_logfile()
return require('haskell-tools.log.internal').get_logfile()
end
---Open the haskell-language-server log file
---@return nil
function HaskellToolsLog.nvim_open_hls_logfile()
function Log.nvim_open_hls_logfile()
return require('haskell-tools.log.internal').nvim_open_hls_logfile()
end
---Open the haskell-tools.nvim log file.
---@return nil
function HaskellToolsLog.nvim_open_logfile()
function Log.nvim_open_logfile()
return require('haskell-tools.log.internal').nvim_open_logfile()
end
@ -39,38 +39,8 @@ end
---@param level (string|integer) The log level
---@return nil
---@see vim.log.levels
function HaskellToolsLog.set_level(level)
function Log.set_level(level)
return require('haskell-tools.log.internal').set_level(level)
end
local commands = {
{
'HtLog',
function()
HaskellToolsLog.nvim_open_logfile()
end,
{},
},
{
'HlsLog',
function()
HaskellToolsLog.nvim_open_hls_logfile()
end,
{ nargs = 1 },
},
{
'HtSetLogLevel',
function(tbl)
local level = vim.fn.expand(tbl.args)
---@cast level string
HaskellToolsLog.set_level(tonumber(level) or level)
end,
{ nargs = 1 },
},
}
for _, command in ipairs(commands) do
vim.api.nvim_create_user_command(unpack(command))
end
return HaskellToolsLog
return Log

View File

@ -8,10 +8,8 @@
--- The internal API for use by this plugin's ftplugins
---@brief ]]
local compat = require('haskell-tools.compat')
---@class HaskellToolsLogInternal
local HaskellToolsLogInternal = {
---@class haskell-tools.internal.Log
local Log = {
-- NOTE: These functions are initialised as empty for type checking purposes
-- and implemented later.
trace = function(_) end,
@ -35,13 +33,13 @@ local logfilename = HTConfig.tools.log.logfile
---Get the haskell-tools.nvim log file path.
---@return string filepath
function HaskellToolsLogInternal.get_logfile()
function Log.get_logfile()
return logfilename
end
---Open the haskell-tools.nvim log file.
function HaskellToolsLogInternal.nvim_open_logfile()
vim.cmd('e ' .. HaskellToolsLogInternal.get_logfile())
function Log.nvim_open_logfile()
vim.cmd('e ' .. Log.get_logfile())
end
local logfile, openerr
@ -64,7 +62,7 @@ local function open_logfile()
return false
end
local log_info = compat.uv.fs_stat(logfilename)
local log_info = vim.uv.fs_stat(logfilename)
if log_info and log_info.size > LARGE then
local warn_msg =
string.format('haskell-tools.nvim log is large (%d MB): %s', log_info.size / (1000 * 1000), logfilename)
@ -81,13 +79,13 @@ local opts = HTConfig.tools.log
local hls_log = HTConfig.hls.logfile
--- Get the haskell-language-server log file
function HaskellToolsLogInternal.get_hls_logfile()
function Log.get_hls_logfile()
return hls_log
end
-- Open the haskell-language-server log file
function HaskellToolsLogInternal.nvim_open_hls_logfile()
vim.cmd('e ' .. HaskellToolsLogInternal.get_hls_logfile())
function Log.nvim_open_hls_logfile()
vim.cmd('e ' .. Log.get_hls_logfile())
end
local log_levels = vim.deepcopy(vim.log.levels)
@ -98,26 +96,25 @@ end
--- Set the log level
--- @param level (string|integer) The log level
--- @see vim.log.levels
function HaskellToolsLogInternal.set_level(level)
function Log.set_level(level)
if type(level) == 'string' then
HaskellToolsLogInternal.level =
assert(log_levels[string.upper(level)], string.format('haskell-tools: Invalid log level: %q', level))
Log.level = assert(log_levels[string.upper(level)], string.format('haskell-tools: Invalid log level: %q', level))
else
assert(log_levels[level], string.format('haskell-tools: Invalid log level: %d', level))
HaskellToolsLogInternal.level = level
Log.level = level
end
vim.lsp.set_log_level(HaskellToolsLogInternal.level)
vim.lsp.set_log_level(Log.level)
end
HaskellToolsLogInternal.set_level(opts.level)
Log.set_level(opts.level)
for level, levelnr in pairs(vim.log.levels) do
HaskellToolsLogInternal[level:lower()] = function(...)
if HaskellToolsLogInternal.level == vim.log.levels.OFF or not open_logfile() then
Log[level:lower()] = function(...)
if Log.level == vim.log.levels.OFF or not open_logfile() then
return false
end
local argc = select('#', ...)
if levelnr < HaskellToolsLogInternal.level then
if levelnr < Log.level then
return false
end
if argc == 0 then
@ -143,6 +140,6 @@ for level, levelnr in pairs(vim.log.levels) do
end
end
HaskellToolsLogInternal.debug { 'Config', HTConfig }
Log.debug { 'Config', HTConfig }
return HaskellToolsLogInternal
return Log

View File

@ -10,31 +10,30 @@
local Types = require('haskell-tools.types.internal')
---@class LspHelpers
---@class haskell-tools.lsp.Helpers
local LspHelpers = {}
local compat = require('haskell-tools.compat')
LspHelpers.get_clients = compat.get_clients
LspHelpers.get_clients = vim.lsp.get_clients
LspHelpers.haskell_client_name = 'haskell-tools.nvim'
LspHelpers.cabal_client_name = 'haskell-tools.nvim (cabal)'
---@param bufnr number the buffer to get clients for
---@return lsp.Client[] haskell_clients
---@return vim.lsp.Client[] haskell_clients
---@see util.get_clients
function LspHelpers.get_active_haskell_clients(bufnr)
return LspHelpers.get_clients { bufnr = bufnr, name = LspHelpers.haskell_client_name }
end
---@param bufnr number the buffer to get clients for
---@return lsp.Client[] cabal_clinets
---@return vim.lsp.Client[] cabal_clinets
---@see util.get_clients
function LspHelpers.get_active_cabal_clients(bufnr)
return LspHelpers.get_clients { bufnr = bufnr, name = LspHelpers.cabal_client_name }
end
---@param bufnr number the buffer to get clients for
---@return lsp.Client[] ht_clients The haskell + cabal clients
---@return vim.lsp.Client[] ht_clients The haskell + cabal clients
---@see util.get_clients
---@see util.get_active_haskell_clients
---@see util.get_active_cabal_clients

View File

@ -14,11 +14,11 @@ local HtProjectHelpers = require('haskell-tools.project.helpers')
local hover = {}
---@class HtHoverState
---@class haskell-tools.hover.State
---@field winnr number|nil The hover window number
---@field commands (fun():nil)[] List of hover actions
---@type HtHoverState
---@type haskell-tools.hover.State
local _state = {
winnr = nil,
commands = {},

View File

@ -3,19 +3,18 @@
local HTConfig = require('haskell-tools.config.internal')
local log = require('haskell-tools.log.internal')
local Types = require('haskell-tools.types.internal')
local compat = require('haskell-tools.compat')
---@brief [[
--- The following commands are available:
---
--- * `:HlsStart` - Start the LSP client.
--- * `:HlsStop` - Stop the LSP client.
--- * `:HlsRestart` - Restart the LSP client.
--- * `:HlsEvalAll` - Evaluate all code snippets in comments.
--- * `:Hls start` - Start the LSP client.
--- * `:Hls stop` - Stop the LSP client.
--- * `:Hls restart` - Restart the LSP client.
--- * `:Hls evalAll` - Evaluate all code snippets in comments.
---@brief ]]
---To minimise the risk of this occurring, we attempt to shut down hls cleanly before exiting neovim.
---@param client lsp.Client The LSP client
---@param client vim.lsp.Client The LSP client
---@param bufnr number The buffer number
---@return nil
local function ensure_clean_exit_on_quit(client, bufnr)
@ -33,7 +32,7 @@ end
---Some plugins that add LSP client capabilities which are not built-in to neovim
---(like nvim-ufo and nvim-lsp-selection-range) cause error messages, because
---haskell-language-server falsly advertises those server_capabilities for cabal files.
---@param client lsp.Client
---@param client vim.lsp.Client
---@return nil
local function fix_cabal_client(client)
local LspHelpers = require('haskell-tools.lsp.helpers')
@ -47,7 +46,7 @@ local function fix_cabal_client(client)
end
end
---@class LoadHlsSettingsOpts
---@class haskell-tools.load_hls_settings.Opts
---@field settings_file_pattern string|nil File name or pattern to search for. Defaults to 'hls.json'
log.debug('Setting up the LSP client...')
@ -68,22 +67,22 @@ if Types.evaluate(hover_opts.enable) then
handlers['textDocument/hover'] = hover.on_hover
end
---@class HlsTools
local HlsTools = {}
---@class haskell-tools.Hls
local Hls = {}
---Search the project root for a haskell-language-server settings JSON file and load it to a Lua table.
---Falls back to the `hls.default_settings` if no file is found or file cannot be read or decoded.
---@param project_root string|nil The project root
---@param opts LoadHlsSettingsOpts|nil
---@param opts haskell-tools.load_hls_settings.Opts|nil
---@return table hls_settings
---@see https://haskell-language-server.readthedocs.io/en/latest/configuration.html
HlsTools.load_hls_settings = function(project_root, opts)
Hls.load_hls_settings = function(project_root, opts)
local default_settings = HTConfig.hls.default_settings
if not project_root then
return default_settings
end
local default_opts = { settings_file_pattern = 'hls.json' }
opts = vim.tbl_deep_extend('force', {}, default_opts, opts or {})
local results = vim.fn.glob(compat.joinpath(project_root, opts.settings_file_pattern), true, true)
local results = vim.fn.glob(vim.fs.joinpath(project_root, opts.settings_file_pattern), true, true)
if #results == 0 then
log.info(opts.settings_file_pattern .. ' not found in project root ' .. project_root)
return default_settings
@ -106,7 +105,7 @@ end
---Fails silently if the buffer's filetype is not one of the filetypes specified in the config.
---@param bufnr number|nil The buffer number (optional), defaults to the current buffer
---@return number|nil client_id The LSP client ID
HlsTools.start = function(bufnr)
Hls.start = function(bufnr)
local ht = require('haskell-tools')
bufnr = bufnr or vim.api.nvim_get_current_buf()
local file = vim.api.nvim_buf_get_name(bufnr)
@ -175,7 +174,7 @@ end
---Stop the LSP client.
---@param bufnr number|nil The buffer number (optional), defaults to the current buffer
---@return table[] clients A list of clients that will be stopped
HlsTools.stop = function(bufnr)
Hls.stop = function(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
local LspHelpers = require('haskell-tools.lsp.helpers')
local clients = LspHelpers.get_active_ht_clients(bufnr)
@ -187,11 +186,11 @@ end
---Fails silently if the buffer's filetype is not one of the filetypes specified in the config.
---@param bufnr number|nil The buffer number (optional), defaults to the current buffer
---@return number|nil client_id The LSP client ID after restart
HlsTools.restart = function(bufnr)
Hls.restart = function(bufnr)
local lsp = require('haskell-tools').lsp
bufnr = bufnr or vim.api.nvim_get_current_buf()
local clients = lsp.stop(bufnr)
local timer, err_name, err_msg = compat.uv.new_timer()
local timer, err_name, err_msg = vim.uv.new_timer()
if not timer then
log.error { 'Could not create timer', err_name, err_msg }
return
@ -222,44 +221,47 @@ end
---Evaluate all code snippets in comments.
---@param bufnr number|nil Defaults to the current buffer.
---@return nil
HlsTools.buf_eval_all = function(bufnr)
Hls.buf_eval_all = function(bufnr)
local eval = require('haskell-tools.lsp.eval')
return eval.all(bufnr)
end
local commands = {
{
'HlsStart',
function()
HlsTools.start()
end,
{},
},
{
'HlsStop',
function()
HlsTools.stop()
end,
{},
},
{
'HlsRestart',
function()
HlsTools.restart()
end,
{},
},
{
'HlsEvalAll',
function()
HlsTools.buf_eval_all()
end,
{},
},
---@enum haskell-tools.HlsCmd
local HlsCmd = {
start = 'start',
stop = 'stop',
restart = 'restart',
evalAll = 'evalAll',
}
for _, command in ipairs(commands) do
vim.api.nvim_create_user_command(unpack(command))
local function hls_command(opts)
local fargs = opts.fargs
local cmd = fargs[1] ---@as haskell-tools.HlsCmd
if cmd == HlsCmd.start then
Hls.start()
elseif cmd == HlsCmd.stop then
Hls.stop()
elseif cmd == HlsCmd.restart then
Hls.restart()
elseif cmd == HlsCmd.evalAll then
Hls.buf_eval_all()
end
end
return HlsTools
vim.api.nvim_create_user_command('Hls', hls_command, {
nargs = '+',
desc = 'Commands for interacting with the haskell-language-server LSP client',
complete = function(arg_lead, cmdline, _)
local clients = require('haskell-tools.lsp.helpers').get_active_haskell_clients(0)
---@type haskell-tools.HlsCmd[]
local available_commands = #clients == 0 and { 'start' } or { 'stop', 'restart', 'evalAll' }
---@type haskell-tools.HlsCmd[]
if cmdline:match('^Hls%s+%w*$') then
return vim.tbl_filter(function(command)
return command:find(arg_lead) ~= nil
end, available_commands)
end
end,
})
return Hls

View File

@ -8,11 +8,9 @@
--- Functions for interacting with the operating system
---@brief ]]
local compat = require('haskell-tools.compat')
local log = require('haskell-tools.log.internal')
local uv = compat.uv
---@class OS
---@class haskell-tools.OS
local OS = {}
---@param url string
@ -32,7 +30,7 @@ OS.open_browser = function(url)
if browser_cmd and vim.fn.executable(browser_cmd) == 1 then
local cmd = { browser_cmd, url }
log.debug { 'Opening browser', cmd }
compat.system(cmd, nil, function(sc)
vim.system(cmd, nil, function(sc)
---@cast sc vim.SystemCompleted
if sc.code ~= 0 then
log.error { 'Error opening browser', sc.code, sc.stderr }
@ -63,16 +61,16 @@ end
---@return string|nil content
---@async
OS.read_file_async = function(filename)
local file_fd = uv.fs_open(filename, 'r', 438)
local file_fd = vim.uv.fs_open(filename, 'r', 438)
if not file_fd then
return nil
end
local stat = uv.fs_fstat(file_fd)
local stat = vim.uv.fs_fstat(file_fd)
if not stat then
return nil
end
local data = uv.fs_read(file_fd, stat.size, 0)
uv.fs_close(file_fd)
local data = vim.uv.fs_read(file_fd, stat.size, 0)
vim.uv.fs_close(file_fd)
---@cast data string?
return data
end

View File

@ -8,7 +8,7 @@
--- Parsing functions
---@brief ]]
---@class HtParser
---@class haskell-tools.Parser
local HtParser = {}
--- Pretty-print a type signature

View File

@ -12,27 +12,26 @@ local Strings = require('haskell-tools.strings')
local HtParser = require('haskell-tools.parser')
local Dap = require('haskell-tools.dap.internal')
local OS = require('haskell-tools.os')
local compat = require('haskell-tools.compat')
---@class CabalProjectHelper
local CabalProjectHelper = {}
---@class haskell-tools.project.cabal.Helper
local Helper = {}
---@class CabalEntryPointParserData
---@class haskell-tools.project.cabal.EntryPointParserData
---@field idx integer
---@field lines string[]
---@field line string
---@field package_dir string
---@class CabalEntryPointParserState
---@class haskell-tools.project.cabal.EntryPointParserState
---@field package_name string
---@field entry_points HsEntryPoint[]
---@field entry_points haskell-tools.EntryPoint[]
---@field mains string[]
---@field source_dirs string[]
---@field src_dir_indent_pattern string
---@field exe_name string | nil
---@param data CabalEntryPointParserData
---@param state CabalEntryPointParserState
---@param data haskell-tools.project.cabal.EntryPointParserData
---@param state haskell-tools.project.cabal.EntryPointParserState
local function get_entrypoint_from_line(data, state)
local package_dir = data.package_dir
local idx = data.idx
@ -78,7 +77,7 @@ end
---Parse the DAP entry points from a *.cabal file
---@param package_file string Path to the *.cabal file
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
---@async
local function parse_package_entrypoints(package_file)
local state = {
@ -96,7 +95,7 @@ local function parse_package_entrypoints(package_file)
for idx, line in ipairs(lines) do
local is_comment = vim.startswith(Strings.trim(line), '--')
if not is_comment then
---@type CabalEntryPointParserData
---@type haskell-tools.project.cabal.EntryPointParserData
local data = {
package_dir = package_dir,
line = line,
@ -111,14 +110,14 @@ end
---Parse the DAP entry points from a *.cabal file
---@param package_path string Path to a package directory
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
---@async
function CabalProjectHelper.parse_package_entrypoints(package_path)
function Helper.parse_package_entrypoints(package_path)
local entry_points = {}
for _, package_file in pairs(vim.fn.glob(compat.joinpath(package_path, '*.cabal'), true, true)) do
for _, package_file in pairs(vim.fn.glob(vim.fs.joinpath(package_path, '*.cabal'), true, true)) do
vim.list_extend(entry_points, parse_package_entrypoints(package_file))
end
return entry_points
end
return CabalProjectHelper
return Helper

View File

@ -13,9 +13,8 @@ local Strings = require('haskell-tools.strings')
local OS = require('haskell-tools.os')
local cabal = require('haskell-tools.project.cabal')
local stack = require('haskell-tools.project.stack')
local compat = require('haskell-tools.compat')
---@class HtProjectHelpers
---@class haskell-tools.project.Helpers
local HtProjectHelpers = {}
---@param path string
@ -32,8 +31,8 @@ end
---@param ... string Search patterns (can be globs)
---@return string|nil The first file that matches the globs
local function find_file(path, ...)
for _, search_term in ipairs(compat.tbl_flatten { ... }) do
local results = vim.fn.glob(compat.joinpath(path, search_term), true, true)
for _, search_term in ipairs(vim.iter({ ... }):flatten():totable()) do
local results = vim.fn.glob(vim.fs.joinpath(path, search_term), true, true)
if #results > 0 then
return results[1]
end
@ -55,7 +54,7 @@ local function iterate_parents(startpath)
if not next or vim.fn.isdirectory(next) == 0 or next == path or next == '/nix/store' then
return
end
if compat.uv.fs_realpath(next) then
if vim.uv.fs_realpath(next) then
return next, startpath
end
end
@ -87,7 +86,7 @@ end
---@param ... string Globs to match in the root directory
---@return fun(path:string):(string|nil)
local function root_pattern(...)
local args = compat.tbl_flatten { ... }
local args = vim.iter({ ... }):flatten():totable()
local function matcher(path)
return find_file(path, unpack(args))
end
@ -147,7 +146,7 @@ function HtProjectHelpers.get_package_cabal(path)
return nil
end
dir = escape_glob_wildcards(dir)
for _, pattern in ipairs(vim.fn.glob(compat.joinpath(dir, '*.cabal'), true, true)) do
for _, pattern in ipairs(vim.fn.glob(vim.fs.joinpath(dir, '*.cabal'), true, true)) do
if pattern then
return pattern
end
@ -209,7 +208,7 @@ function HtProjectHelpers.parse_package_paths(project_file)
if packages_start then
local trimmed = Strings.trim(line)
local pkg_rel_path = trimmed:match('/(.+)')
local pkg_path = compat.joinpath(project_dir, pkg_rel_path)
local pkg_path = vim.fs.joinpath(project_dir, pkg_rel_path)
if vim.fn.isdirectory(pkg_path) == 1 then
package_paths[#package_paths + 1] = pkg_path
end
@ -223,7 +222,7 @@ end
---Parse the DAP entry points from a *.cabal file
---@param package_path string Path to a package directory
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
---@async
function HtProjectHelpers.parse_package_entrypoints(package_path)
if HtProjectHelpers.is_cabal_project(package_path) then
@ -233,18 +232,18 @@ function HtProjectHelpers.parse_package_entrypoints(package_path)
end
---@param project_root string Project root directory
---@return HsEntryPoint[]
---@return haskell-tools.EntryPoint[]
---@async
function HtProjectHelpers.parse_project_entrypoints(project_root)
local entry_points = {}
local project_file = compat.joinpath(project_root, 'cabal.project')
local project_file = vim.fs.joinpath(project_root, 'cabal.project')
if vim.fn.filereadable(project_file) == 1 then
for _, package_path in pairs(HtProjectHelpers.parse_package_paths(project_file)) do
vim.list_extend(entry_points, cabal.parse_package_entrypoints(package_path))
end
return entry_points
end
project_file = compat.joinpath(project_root, 'stack.yaml')
project_file = vim.fs.joinpath(project_root, 'stack.yaml')
if vim.fn.filereadable(project_file) == 1 then
for _, package_path in pairs(HtProjectHelpers.parse_package_paths(project_file)) do
vim.list_extend(entry_points, stack.parse_package_entrypoints(package_path))

View File

@ -6,9 +6,9 @@ local deps = require('haskell-tools.deps')
---@brief [[
--- The following commands are available:
---
--- * `:HsProjectFile` - Open the project file for the current buffer (cabal.project or stack.yaml).
--- * `:HsPackageYaml` - Open the package.yaml file for the current buffer.
--- * `:HsPackageCabal` - Open the *.cabal file for the current buffer.
--- * `:Haskell projectFile` - Open the project file for the current buffer (cabal.project or stack.yaml).
--- * `:Haskell packageYaml` - Open the package.yaml file for the current buffer.
--- * `:Haskell packageCabal` - Open the *.cabal file for the current buffer.
---@brief ]]
---@param callback fun(opts:table<string,any>):nil
@ -56,13 +56,13 @@ local function telescope_package_files(opts)
telescope_package_search(t.find_files, opts)
end
---@class HsProjectTools
local HsProjectTools = {}
---@class haskell-tools.Project
local Project = {}
---Get the project's root directory
---@param project_file string The path to a project file
---@return string|nil
HsProjectTools.root_dir = function(project_file)
Project.root_dir = function(project_file)
local HtProjectHelpers = require('haskell-tools.project.helpers')
return HtProjectHelpers.match_cabal_project_root(project_file)
or HtProjectHelpers.match_stack_project_root(project_file)
@ -72,7 +72,7 @@ end
---Open the package.yaml of the package containing the current buffer.
---@return nil
HsProjectTools.open_package_yaml = function()
Project.open_package_yaml = function()
local HtProjectHelpers = require('haskell-tools.project.helpers')
vim.schedule(function()
local file = vim.api.nvim_buf_get_name(0)
@ -93,7 +93,7 @@ end
---Open the *.cabal file of the package containing the current buffer.
---@return nil
HsProjectTools.open_package_cabal = function()
Project.open_package_cabal = function()
vim.schedule(function()
local HtProjectHelpers = require('haskell-tools.project.helpers')
local file = vim.api.nvim_buf_get_name(0)
@ -114,7 +114,7 @@ end
---Open the current buffer's project file (cabal.project or stack.yaml).
---@return nil
HsProjectTools.open_project_file = function()
Project.open_project_file = function()
vim.schedule(function()
local HtProjectHelpers = require('haskell-tools.project.helpers')
local file = vim.api.nvim_buf_get_name(0)
@ -138,37 +138,8 @@ HsProjectTools.open_project_file = function()
end)
end
HsProjectTools.telescope_package_grep = deps.has('telescope.builtin') and telescope_package_grep or nil
Project.telescope_package_grep = deps.has('telescope.builtin') and telescope_package_grep or nil
HsProjectTools.telescope_package_files = deps.has('telescope.builtin') and telescope_package_files or nil
Project.telescope_package_files = deps.has('telescope.builtin') and telescope_package_files or nil
local commands = {
{
'HsPackageYaml',
function()
HsProjectTools.open_package_yaml()
end,
{},
},
{
'HsPackageCabal',
function()
HsProjectTools.open_package_cabal()
end,
{},
},
{
'HsProjectFile',
function()
HsProjectTools.open_project_file()
end,
{},
},
}
--- Available if nvim-telescope/telescope.nvim is installed.
for _, command in ipairs(commands) do
vim.api.nvim_create_user_command(unpack(command))
end
return HsProjectTools
return Project

View File

@ -12,10 +12,9 @@ local Strings = require('haskell-tools.strings')
local HtParser = require('haskell-tools.parser')
local Dap = require('haskell-tools.dap.internal')
local OS = require('haskell-tools.os')
local compat = require('haskell-tools.compat')
---@class StackProjectHelper
local StackProjectHelper = {}
---@class haskell-tools.project.stack.Helper
local Helper = {}
---@param str string
---@return boolean is_yaml_comment
@ -23,16 +22,16 @@ local function is_yaml_comment(str)
return vim.startswith(Strings.trim(str), '#')
end
---@class StackEntryPointParserData
---@class haskell-tools.project.stack.EntryPointParserData
---@field idx integer
---@field lines string[]
---@field line string
---@field package_dir string
---@field next_line string|nil
---@class StackEntryPointParserState
---@class haskell-tools.project.stack.EntryPointParserState
---@field package_name string
---@field entry_points HsEntryPoint[]
---@field entry_points haskell-tools.EntryPoint[]
---@field mains string[]
---@field source_dirs string[]
---@field parsing_exe_list boolean
@ -41,8 +40,8 @@ end
---@field exe_indent integer | nil
---@field exe_name string | nil
---@param data StackEntryPointParserData
---@param state StackEntryPointParserState
---@param data haskell-tools.project.stack.EntryPointParserData
---@param state haskell-tools.project.stack.EntryPointParserState
local function parse_exe_list_line(data, state)
local package_dir = data.package_dir
local idx = data.idx
@ -92,8 +91,8 @@ local function parse_exe_list_line(data, state)
end
end
---@param data StackEntryPointParserData
---@param state StackEntryPointParserState
---@param data haskell-tools.project.stack.EntryPointParserData
---@param state haskell-tools.project.stack.EntryPointParserState
local function get_entrypoint_from_line(data, state)
local line = data.line
state.package_name = state.package_name or line:match('^name:%s*(.+)')
@ -109,7 +108,7 @@ end
---Parse the DAP entry points from a *.cabal file
---@param package_file string Path to the *.cabal file
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
---@async
local function parse_package_entrypoints(package_file)
local state = {
@ -128,7 +127,7 @@ local function parse_package_entrypoints(package_file)
local lines = vim.split(content, '\n') or {}
for idx, line in ipairs(lines) do
if not is_yaml_comment(line) then
---@type StackEntryPointParserData
---@type haskell-tools.project.stack.EntryPointParserData
local data = {
package_dir = package_dir,
line = line,
@ -146,14 +145,14 @@ end
---Parse the DAP entry points from a package.yaml file
---@param package_path string Path to a package directory
---@return HsEntryPoint[] entry_points
---@return haskell-tools.EntryPoint[] entry_points
---@async
function StackProjectHelper.parse_package_entrypoints(package_path)
function Helper.parse_package_entrypoints(package_path)
local entry_points = {}
for _, package_file in pairs(vim.fn.glob(compat.joinpath(package_path, 'package.yaml'), true, true)) do
for _, package_file in pairs(vim.fn.glob(vim.fs.joinpath(package_path, 'package.yaml'), true, true)) do
vim.list_extend(entry_points, parse_package_entrypoints(package_file))
end
return entry_points
end
return StackProjectHelper
return Helper

View File

@ -10,24 +10,24 @@
local log = require('haskell-tools.log.internal')
---@class BuiltinRepl
---@class haskell-tools.repl.builtin
---@field bufnr number
---@field job_id number
---@field cmd string[]
---@type BuiltinRepl | nil
---@type haskell-tools.repl.builtin | nil
local BuiltinRepl = nil
local function is_repl_loaded()
return BuiltinRepl ~= nil and vim.api.nvim_buf_is_loaded(BuiltinRepl.bufnr)
end
---@param callback fun(repl:BuiltinRepl)
---@param callback fun(repl:haskell-tools.repl.builtin)
---@return nil
local function when_repl_loaded(callback)
if is_repl_loaded() then
local repl = BuiltinRepl
---@cast repl BuiltinRepl
---@cast repl haskell-tools.repl.builtin
callback(repl)
end
end
@ -40,7 +40,7 @@ end
---Creates a repl on buffer with id `bufnr`.
---@param bufnr number Buffer to be used.
---@param cmd string[] command to start the repl
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return nil
local function buf_create_repl(bufnr, cmd, opts)
vim.api.nvim_win_set_buf(0, bufnr)
@ -114,16 +114,16 @@ local function create_tab(_)
end
---@param mk_repl_cmd fun(string):(string[]?)
---@param options ReplConfig
---@return ReplHandlerImpl handler
---@param options haskell-tools.repl.Config
---@return haskell-tools.repl.impl.Handler handler
return function(mk_repl_cmd, options)
---@class ReplHandlerImpl
local ReplHandlerImpl = {}
---@class haskell-tools.repl.impl.Handler
local Handler = {}
---Create a new repl (or toggle its visibility)
---@param create_win function|number Function for creating the window or an existing window number
---@param mk_cmd fun():string[] Function for creating the repl command
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return nil
local function create_or_toggle(create_win, mk_cmd, opts)
local cmd = mk_cmd()
@ -135,11 +135,11 @@ return function(mk_repl_cmd, options)
end
if is_new_cmd(cmd) then
log.debug { 'repl.builtin: New command', cmd }
ReplHandlerImpl.quit()
Handler.quit()
end
if is_repl_loaded() then
local repl = BuiltinRepl
---@cast repl BuiltinRepl
---@cast repl haskell-tools.repl.builtin
log.debug('repl.builtin: is loaded')
local winid = vim.fn.bufwinid(repl.bufnr)
if winid ~= -1 then
@ -169,10 +169,10 @@ return function(mk_repl_cmd, options)
buf_create_repl(bufnr, cmd, opts)
end
---@type ReplView
---@type haskell-tools.repl.View
local ReplView = {
---Create a new repl in a horizontal split
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return fun(mk_cmd_fun) create_repl
create_repl_split = function(opts)
return function(mk_cmd)
@ -181,7 +181,7 @@ return function(mk_repl_cmd, options)
end,
---Create a new repl in a vertical split
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return fun(function) create_repl
create_repl_vsplit = function(opts)
return function(mk_cmd)
@ -190,7 +190,7 @@ return function(mk_repl_cmd, options)
end,
---Create a new repl in a new tab
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return fun(function) create_repl
create_repl_tabnew = function(opts)
return function(mk_cmd)
@ -199,7 +199,7 @@ return function(mk_repl_cmd, options)
end,
---Create a new repl in the current window
---@param opts ReplViewOpts?
---@param opts haskell-tools.repl.view.Opts?
---@return fun(function) create_repl
create_repl_cur_win = function(opts)
return function(mk_cmd)
@ -210,11 +210,11 @@ return function(mk_repl_cmd, options)
log.debug { 'repl.builtin setup', options }
---@private
ReplHandlerImpl.go_back = options.auto_focus ~= true
Handler.go_back = options.auto_focus ~= true
---@param filepath string path of the file to load into the repl
---@param _ table?
function ReplHandlerImpl.toggle(filepath, _)
function Handler.toggle(filepath, _)
local cur_win = vim.api.nvim_get_current_win()
if filepath and not vim.endswith(filepath, '.hs') then
local err_msg = 'haskell-tools.repl.builtin: Not a Haskell file: ' .. filepath
@ -230,19 +230,19 @@ return function(mk_repl_cmd, options)
local create_or_toggle_callback = options.builtin.create_repl_window(ReplView)
create_or_toggle_callback(mk_repl_cmd_wrapped)
if cur_win ~= -1 and ReplHandlerImpl.go_back then
if cur_win ~= -1 and Handler.go_back then
vim.api.nvim_set_current_win(cur_win)
else
vim.cmd('startinsert')
vim.cmd.startinsert()
end
end
---Quit the repl
---@return nil
function ReplHandlerImpl.quit()
function Handler.quit()
when_repl_loaded(function(repl)
log.debug('repl.builtin: sending quit to repl.')
local success, result = pcall(ReplHandlerImpl.send_cmd, ':q')
local success, result = pcall(Handler.send_cmd, ':q')
if not success then
log.warn { 'repl.builtin: Could not send quit command', result }
end
@ -257,7 +257,7 @@ return function(mk_repl_cmd, options)
---Send a command to the repl, followed by <cr>
---@param txt string The text to send
---@return nil
function ReplHandlerImpl.send_cmd(txt)
function Handler.send_cmd(txt)
when_repl_loaded(function(repl)
local cr = '\13'
local repl_winid = vim.fn.bufwinid(repl.bufnr)
@ -269,11 +269,11 @@ return function(mk_repl_cmd, options)
repl_set_cursor()
vim.api.nvim_chan_send(repl.job_id, txt .. cr)
repl_set_cursor()
if not ReplHandlerImpl.go_back and repl_winid ~= nil then
if not Handler.go_back and repl_winid ~= nil then
vim.api.nvim_set_current_win(repl_winid)
vim.cmd('startinsert')
end
end)
end
return ReplHandlerImpl
return Handler
end

View File

@ -7,15 +7,18 @@
---@brief [[
--- The following commands are available:
---
--- * `:HtReplToggle` - Toggle a GHCi repl.
--- * `:HtReplQuit` - Quit the current repl.
--- * `:HtReplLoad` - Load a Haskell file into the repl.
--- * `:HtReplReload` - Reload the current repl.
--- * `:Haskell repl toggle {file?}` - Toggle a GHCi repl.
--- * `:Haskell repl quit` - Quit the current repl.
--- * `:Haskell repl load {file?}` - Load a Haskell file into the repl.
--- * `:Haskell repl reload` - Reload the current repl.
--- * `:Haskell repl paste_type {register?}` - Query the repl for the type of |registers| {register}
--- * `:Haskell repl cword_type` - Query the repl for the type of |cword|
--- * `:Haskell repl paste_info {register?}` - Query the repl for the info on |registers| {register}
--- * `:Haskell repl cword_info` - Query the repl for info on |cword|
---@brief ]]
local log = require('haskell-tools.log.internal')
local Types = require('haskell-tools.types.internal')
local compat = require('haskell-tools.compat')
---Extend a repl command for `file`.
---If `file` is `nil`, create a repl the nearest package.
@ -99,7 +102,7 @@ local function mk_repl_cmd(file)
return mk_stack_repl_cmd(file)
end
if vim.fn.executable('ghci') == 1 then
local cmd = compat.tbl_flatten { 'ghci', file and { file } or {} }
local cmd = vim.iter({ 'ghci', file and { file } or {} }):flatten():totable()
log.debug { 'mk_repl_cmd', cmd }
return cmd
end
@ -111,11 +114,11 @@ end
local HTConfig = require('haskell-tools.config.internal')
local opts = HTConfig.tools.repl
---@type ReplHandlerImpl
---@type haskell-tools.repl.impl.Handler
local handler
local handler_type = Types.evaluate(opts.handler)
---@cast handler_type ReplHandler
---@cast handler_type haskell-tools.repl.Handler
if handler_type == 'toggleterm' then
log.info('handler = toggleterm')
handler = require('haskell-tools.repl.toggleterm')(mk_repl_cmd, opts)
@ -155,30 +158,30 @@ local function repl_send_lines(lines)
end
end
---@class HsReplTools
local HsReplTools = {}
---@class haskell-tools.Repl
local Repl = {}
HsReplTools.mk_repl_cmd = mk_repl_cmd
Repl.mk_repl_cmd = mk_repl_cmd
---Create the command to create a repl for the current buffer.
---@return table|nil command
HsReplTools.buf_mk_repl_cmd = function()
Repl.buf_mk_repl_cmd = function()
local file = vim.api.nvim_buf_get_name(0)
return mk_repl_cmd(file)
end
---Toggle a GHCi REPL
HsReplTools.toggle = handler.toggle
Repl.toggle = handler.toggle
---Quit the REPL
HsReplTools.quit = handler.quit
Repl.quit = handler.quit
---Can be used to send text objects to the repl.
---@usage [[
---vim.keymap.set('n', 'ghc', ht.repl.operator, {noremap = true})
---@usage ]]
---@see operatorfunc
HsReplTools.operator = function()
Repl.operator = function()
local old_operator_func = vim.go.operatorfunc
_G.op_func_send_to_repl = function()
local start = vim.api.nvim_buf_get_mark(0, '[')
@ -194,7 +197,7 @@ end
---Paste from register `reg` to the REPL
---@param reg string|nil register (defaults to '"')
HsReplTools.paste = function(reg)
Repl.paste = function(reg)
local data = vim.fn.getreg(reg or '"')
---@cast data string
if vim.endswith(data, '\n') then
@ -209,29 +212,29 @@ end
---Query the REPL for the type of register `reg`
---@param reg string|nil register (defaults to '"')
HsReplTools.paste_type = function(reg)
Repl.paste_type = function(reg)
handle_reg(':t', reg)
end
---Query the REPL for the type of word under the cursor
HsReplTools.cword_type = function()
Repl.cword_type = function()
handle_cword(':t')
end
---Query the REPL for info on register `reg`
---@param reg string|nil register (defaults to '"')
HsReplTools.paste_info = function(reg)
Repl.paste_info = function(reg)
handle_reg(':i', reg)
end
---Query the REPL for the type of word under the cursor
HsReplTools.cword_info = function()
Repl.cword_info = function()
handle_cword(':i')
end
---Load a file into the REPL
---@param filepath string The absolute file path
HsReplTools.load_file = function(filepath)
Repl.load_file = function(filepath)
if vim.fn.filereadable(filepath) == 0 then
local err_msg = 'File: ' .. filepath .. ' does not exist or is not readable.'
log.error(err_msg)
@ -241,51 +244,10 @@ HsReplTools.load_file = function(filepath)
end
---Reload the repl
HsReplTools.reload = function()
Repl.reload = function()
handler.send_cmd(':r')
end
vim.keymap.set('n', 'ghc', HsReplTools.operator, { noremap = true })
vim.keymap.set('n', 'ghc', Repl.operator, { noremap = true })
local commands = {
{
'HtReplToggle',
---@param tbl table
function(tbl)
local filepath = tbl.args ~= '' and vim.fn.expand(tbl.args)
---@cast filepath string
HsReplTools.toggle(filepath)
end,
{ nargs = '?' },
},
{
'HtReplLoad',
---@param tbl table
function(tbl)
local filepath = vim.fn.expand(tbl.args)
---@cast filepath string
HsReplTools.load_file(filepath)
end,
{ nargs = 1 },
},
{
'HtReplQuit',
function()
HsReplTools.quit()
end,
{},
},
{
'HtReplReload',
function()
HsReplTools.reload()
end,
{},
},
}
for _, command in ipairs(commands) do
vim.api.nvim_create_user_command(unpack(command))
end
return HsReplTools
return Repl

View File

@ -27,8 +27,8 @@ local function quote(str)
end
---@param mk_repl_cmd fun(string?):string[]? Function for building the repl that takes an optional file path
---@param opts ReplConfig
---@return ReplHandlerImpl
---@param opts haskell-tools.repl.Config
---@return haskell-tools.repl.impl.Handler
return function(mk_repl_cmd, opts)
local ReplHandlerImpl = {
---@private

View File

@ -8,7 +8,7 @@
--- Helper functions for working with strings
---@brief ]]
---@class StringsUtil
---@class haskell-tools.Strings
local Strings = {}
---Trim leading and trailing whitespace.

View File

@ -3,7 +3,6 @@
local HTConfig = require('haskell-tools.config.internal')
local Types = require('haskell-tools.types.internal')
local log = require('haskell-tools.log.internal')
local compat = require('haskell-tools.compat')
local _state = {
fast_tags_generating = false,
@ -13,17 +12,17 @@ local _state = {
log.debug('Setting up fast-tags tools')
local config = HTConfig.tools.tags
---@class GenerateProjectTagsOpts
---@class haskell-tools.tags.generate_project_tags.Opts
---@field refresh boolean Whether to refresh the tags if they have already been generated
--- for the project (default: true)
---@class FastTagsTools
local FastTagsTools = {}
---@class haskell-tools.FastTags
local FastTags = {}
---Generates tags for the current project
---@param path string|nil File path
---@param opts GenerateProjectTagsOpts|nil Options
FastTagsTools.generate_project_tags = function(path, opts)
---@param opts haskell-tools.tags.generate_project_tags.Opts|nil Options
FastTags.generate_project_tags = function(path, opts)
path = path or vim.api.nvim_buf_get_name(0)
opts = vim.tbl_extend('force', { refresh = true }, opts or {})
local HtProjectHelpers = require('haskell-tools.project.helpers')
@ -40,7 +39,7 @@ FastTagsTools.generate_project_tags = function(path, opts)
_state.fast_tags_generating = true
if project_root then
log.debug('Generating project tags for' .. project_root)
compat.system({ 'fast-tags', '-R', project_root }, nil, function(sc)
vim.system({ 'fast-tags', '-R', project_root }, nil, function(sc)
if sc.code ~= 0 then
log.error { 'Error running fast-tags on project root', sc.code, sc.stderr }
end
@ -52,7 +51,7 @@ end
---Generate tags for the package containing `path`
---@param path string|nil File path
FastTagsTools.generate_package_tags = function(path)
FastTags.generate_package_tags = function(path)
path = path or vim.api.nvim_buf_get_name(0)
_state.fast_tags_generating = true
local HtProjectHelpers = require('haskell-tools.project.helpers')
@ -71,7 +70,7 @@ FastTagsTools.generate_package_tags = function(path)
log.warn('generate_package_tags: No project root found.')
return
end
compat.system({ 'fast-tags', '-R', package_root, project_root }, nil, function(sc)
vim.system({ 'fast-tags', '-R', package_root, project_root }, nil, function(sc)
---@cast sc vim.SystemCompleted
if sc.code ~= 0 then
log.error { 'Error running fast-tags on package', sc.code, sc.stderr }
@ -99,9 +98,9 @@ if #package_events > 0 then
if _state.fast_tags_generating then
return
end
FastTagsTools.generate_package_tags(meta.file)
FastTags.generate_package_tags(meta.file)
end,
})
end
return FastTagsTools
return FastTags

View File

@ -8,7 +8,7 @@
--- Type definitions
---@brief ]]
---@class HsEntryPoint
---@class haskell-tools.EntryPoint
---@field package_dir string
---@field package_name string
---@field exe_name string