Big neovim overhaul (#38)

This commit is contained in:
Patrick Stevens
2024-03-26 00:04:01 +00:00
committed by GitHub
parent 15e603063a
commit 87492c2abe
15 changed files with 1395 additions and 418 deletions

View File

@@ -0,0 +1,4 @@
let g:chadtree_settings = {'xdg': v:true}
autocmd VimEnter * CHADopen --nofocus
autocmd bufenter * if (winnr("$") == 1 && &filetype == 'CHADtree') | q | endif

256
home-manager/nvim/init.lua Normal file
View File

@@ -0,0 +1,256 @@
vim.g.python3_host_prog = "%PYTHONENV%/bin/python"
vim.opt.mouse = ""
vim.opt.history = 500
vim.opt.background = "dark"
vim.opt.wildmenu = true
vim.opt.wildignore = vim.opt.wildignore + { "*/.git/*", "*/.hg/*", "*/.svn/*", "*/.DS_Store" }
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.incsearch = true
vim.opt.magic = true
vim.opt.hlsearch = true
vim.opt.autoindent = true
vim.opt.smartindent = true
vim.opt.wrap = true
vim.opt.linebreak = true
vim.opt.textwidth = 500
vim.opt.switchbuf = "useopen"
vim.opt.laststatus = 2
-- I don't use tabs, but one day I might!
vim.opt.showtabline = 2
vim.opt.langmenu = "en"
vim.opt.ffs = "unix"
vim.opt.encoding = "utf8"
-- Always show current position
vim.opt.ruler = true
vim.opt.number = true
-- A bit of extra margin to the left
vim.opt.foldcolumn = "1"
vim.opt.autoread = true
vim.opt.backup = false
vim.opt.writebackup = true
vim.opt.swapfile = false
vim.opt.cmdheight = 2
-- Use spaces instead of tabs
vim.opt.expandtab = true
vim.opt.smarttab = true
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
vim.opt.lazyredraw = true
-- Show matching brackets when text indicator is on one of them
vim.opt.showmatch = true
vim.opt.mat = 2
-- Turn off sound
vim.opt.errorbells = false
vim.opt.visualbell = false
vim.opt.timeoutlen = 500
vim.opt.scrolloff = 2
-- Return to last edit position when opening files
vim.api.nvim_create_autocmd("BufReadPost", {
pattern = "*",
callback = function()
local line = vim.fn.line
local last_pos = line("'\"")
if last_pos > 1 and last_pos <= line("$") then
vim.cmd("normal! g'\"")
end
end,
})
-- Trim trailing whitespace on save
function CleanExtraSpaces()
local save_cursor = vim.api.nvim_win_get_cursor(0)
local old_query = vim.fn.getreg("/")
vim.cmd("%s/\\s\\+$//e")
vim.api.nvim_win_set_cursor(0, save_cursor)
vim.fn.setreg("/", old_query)
end
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = { "*.fs", "*.fsi", "*.txt", "*.js", "*.py", "*.wiki", "*.sh", "*.coffee" },
callback = CleanExtraSpaces,
})
-- Status line
-- Returns true if paste mode is enabled
function HasPaste()
if vim.opt.paste:get() then
return "PASTE MODE "
end
return ""
end
vim.o.statusline = vim.o.statusline .. "%{v:lua.HasPaste()}%F%m%r%h %w CWD: %r%{getcwd()}%h Line: %l Column: %c"
--------------------------------------------------------------
vim.api.nvim_set_keymap("n", ";", "<Nop>", { noremap = true })
vim.api.nvim_set_var("maplocalleader", ";")
function MarkdownPreview()
local temp_file = vim.fn.tempname() .. ".md"
local file_name = vim.fn.substitute(vim.fn.tolower(vim.fn.expand("%:t")), "\\W", "_", "g")
local temp_html = "/tmp/" .. file_name .. "_tmp.html"
-- Write the current buffer to the temp file
vim.cmd("write! " .. temp_file)
local pandoc_cmd = "pandoc " .. temp_file .. " -o " .. temp_html
-- Execute the pandoc command
vim.fn.system(pandoc_cmd)
-- Use tmux and lynx to preview the HTML file
local lynx_cmd = "tmux split-window -h lynx " .. temp_html
vim.fn.jobstart(vim.split(lynx_cmd, " "), { silent = true })
-- Delete the temp markdown file
vim.fn.delete(temp_file, "rf")
end
function RemoveCarriageReturn()
vim.cmd("mark m")
vim.cmd("normal! Hmt")
vim.cmd("%s/\r//ge")
vim.cmd("normal! 'tzt'm")
end
function FormatJson()
vim.cmd("%!python -m json.tool")
end
function ChangeToCurrentDirectory()
vim.cmd(":cd %:p:h")
vim.cmd(":pwd")
end
local function close_loclist_if_orphaned()
local win = vim.fn.expand("<afile>")
vim.fn.win_execute(win, "lclose")
end
-- Set up an autocmd using the nvim_create_autocmd API
vim.api.nvim_create_autocmd("WinClosed", {
pattern = "*",
callback = close_loclist_if_orphaned,
})
local status, whichkey = pcall(require, "which-key")
if status then
local pickers = require("telescope.pickers")
local action_state = require("telescope.actions.state")
local actions = require("telescope.actions")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
function DisplayAllMappingsWithTelescope()
local mappings = {}
local commands = {} -- Store commands keyed by the display string
local function accumulate(tree)
tree:walk(function(node)
-- Note: we could (if desired) view all groups, because the `node.mapping` table looks like this:
-- { prefix = "g", group = true, keys = {...}}
if node.mapping then
local mapping = node.mapping
if not mapping.group then
local description = mapping.desc or mapping.label or mapping.cmd
-- Some actions are just there for which-key to hook into to display prefixes; they don't have a description.
if description then
local displayString = description .. " | " .. mapping.prefix
commands[displayString] = mapping.prefix
mappings[#mappings + 1] = displayString
else
for k, v in pairs(mapping) do
print("Nothing: " .. k .. " : " .. tostring(v) .. " (type: " .. type(v) .. ")")
end
print("-----")
end
end
-- TODO: If a command is a prefix of an existing command, prepend its description to those commands' descriptions, and append a '...' to the parent's description.
end
end)
end
local cur_buf = vim.api.nvim_win_get_buf(0)
accumulate(require("which-key.keys").get_tree("n").tree)
accumulate(require("which-key.keys").get_tree("n", cur_buf).tree)
pickers
.new({}, {
prompt_title = "Actions",
finder = finders.new_table({
results = mappings,
}),
sorter = conf.generic_sorter({}),
attach_mappings = function(_, map)
map("i", "<CR>", function(bufnr)
local selection = action_state.get_selected_entry()
actions.close(bufnr)
local cmd = commands[selection.value]
if cmd then
vim.api.nvim_command(":normal " .. vim.api.nvim_replace_termcodes(cmd, true, true, true))
else
print("no command found")
end
end)
return true
end,
})
:find()
end
function ToggleSpell()
vim.cmd("setlocal spell!")
end
whichkey.register({
-- TODO: this isn't working for the FSI ones - maybe we've moved to a different buffer by the time we ask for the keymap?
[vim.api.nvim_get_var("maplocalleader")] = {
DisplayAllMappingsWithTelescope,
"View all mappings",
},
m = {
p = { MarkdownPreview, "Preview Markdown in Lynx" },
d = { RemoveCarriageReturn, "Delete carriage returns from file" },
},
["j"] = {
FormatJson,
"Auto-format JSON",
},
["cd"] = {
ChangeToCurrentDirectory,
"Switch CWD to the directory of the open buffer",
},
["ss"] = {
ToggleSpell,
"Toggle spell-checker on or off",
},
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
else
vim.api.nvim_set_keymap("n", "<localleader>mp", ":lua MarkdownPreview()<CR>", { noremap = true, silent = true })
-- Remove the Windows ^M - when the encodings gets messed up
vim.api.nvim_set_keymap("n", "<localleader>md", ":lua RemoveCarriageReturn()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>j", ":lua FormatJson()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>cd", ":lua ChangeToCurrentDirectory()<CR>", { noremap = true })
end

View File

@@ -0,0 +1,263 @@
vim.g["fsharp#fsautocomplete_command"] = { "fsautocomplete" }
vim.g["fsharp#show_signature_on_cursor_move"] = 1
vim.g["fsharp#fsi_keymap"] = "none"
-- MASSIVE HACK - raised https://github.com/ionide/Ionide-vim/pull/78
local function captureLoadedProjects()
vim.fn.execute("redir => g:massive_hack_patrick_capture")
vim.fn.execute("call fsharp#showLoadedProjects()")
vim.fn.execute("redir END")
local output = vim.fn.eval("g:massive_hack_patrick_capture")
local projects = {}
for line in output:gmatch("[^\r\n]+") do
local project = line:gsub("^%s*-%s*", "")
table.insert(projects, project)
end
return projects
end
-- Supply nil to get all loaded F# projects and build them.
local function BuildFSharpProjects(projects)
local function on_output(context, prefix, err, data)
if err or data then
vim.schedule(function()
if err then
-- Append the error message to the buffer
local count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_buf_set_lines(context.buf, count, count, false, { "error " .. prefix .. ": " .. err })
end
if data then
-- Append the data to the buffer
local count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_buf_set_lines(
context.buf,
count,
count,
false,
vim.tbl_map(function(line)
return prefix .. ": " .. line
end, vim.split(data, "\n"))
)
end
if vim.api.nvim_win_is_valid(context.window) then
local cur_win = vim.api.nvim_get_current_win()
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
if cur_buf ~= context.buf then
local new_line_count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 })
end
end
-- Keep the window alive if there were warnings
if string.match(data, "%s[1-9]%d* Warning%(s%)") then
context.warn = context.warn + 1
end
end)
end
end
local function spawn_next(context)
if context.completed == context.expected then
if context.errs == 0 and context.warn == 0 then
local cur_win = vim.api.nvim_get_current_win()
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
if cur_buf ~= context.buf then
vim.api.nvim_win_close(context.window, 1)
end
print("All builds successful")
end
else
local handle
local stdout = vim.loop.new_pipe(false)
local stderr = vim.loop.new_pipe(false)
handle, _ = vim.loop.spawn(
"dotnet",
{
args = { "build", context.projects[context.completed + 1] },
stdio = { nil, stdout, stderr },
},
vim.schedule_wrap(function(code, signal)
stdout:read_stop()
stderr:read_stop()
stdout:close()
stderr:close()
handle:close()
print("Build process exited with code " .. code .. " and signal " .. signal)
if code ~= 0 then
context.errs = context.errs + 1
end
context.completed = context.completed + 1
print(
"Completed: "
.. context.completed
.. " out of "
.. context.expected
.. " (errors: "
.. context.errs
.. ", warnings: "
.. context.warn
.. ")"
)
spawn_next(context)
end)
)
if not handle then
print("Failed to start build process.")
return
end
vim.loop.read_start(stdout, function(err, data)
on_output(context, "OUT", err, data)
end)
vim.loop.read_start(stderr, function(err, data)
on_output(context, "ERR", err, data)
end)
end
end
if not projects then
projects = captureLoadedProjects()
end
if projects then
local total_projects = 0
for _, _ in ipairs(projects) do
total_projects = total_projects + 1
end
-- Create a new buffer for build output
local buf = vim.api.nvim_create_buf(false, true) -- No listed, scratch buffer
-- Calculate window size and position here (example: full width, 10 lines high at the bottom)
local width = vim.api.nvim_get_option("columns")
local height = vim.api.nvim_get_option("lines")
local win_height = math.min(10, math.floor(height * 0.2)) -- 20% of total height or 10 lines
local original_win = vim.api.nvim_get_current_win()
local win_opts = {
relative = "editor",
width = width,
height = win_height,
col = 0,
row = height - win_height,
style = "minimal",
border = "single",
}
local win = vim.api.nvim_open_win(buf, true, win_opts)
-- Switch back to the original window
vim.api.nvim_set_current_win(original_win)
local build_context = {
warn = 0,
errs = 0,
completed = 0,
expected = total_projects,
window = win,
projects = projects,
buf = buf,
}
spawn_next(build_context)
end
end
-- local function fsprojAndDirCompletion(ArgLead, _, _)
-- local results = {}
-- local loc = ArgLead
-- if not loc then
-- loc = "."
-- end
-- local command = string.format(
-- "find "
-- .. vim.fn.shellescape(loc)
-- .. " -maxdepth 2 \\( -type f -name '*.fsproj' -o -type d \\) -print0 2> /dev/null"
-- )
-- local handle = io.popen(command)
-- if handle then
-- local stdout = handle:read("*all")
-- handle:close()
--
-- local allResults = {}
-- for match in string.gmatch(stdout, "([^%z]+)") do
-- table.insert(allResults, match)
-- end
-- table.sort(allResults, function(a, b)
-- local aEndsWithProj = a:match("proj$")
-- local bEndsWithProj = b:match("proj$")
-- if aEndsWithProj and not bEndsWithProj then
-- return true
-- elseif not aEndsWithProj and bEndsWithProj then
-- return false
-- else
-- return a < b -- If both or neither end with 'proj', sort alphabetically
-- end
-- end)
--
-- for _, line in ipairs(allResults) do
-- table.insert(results, line)
-- end
-- end
-- return results
-- end
vim.api.nvim_create_user_command("BuildFSharpProject", function(opts)
if opts.fargs and opts.fargs[1] then
BuildFSharpProjects(opts.fargs)
else
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local action_state = require("telescope.actions.state")
local actions = require("telescope.actions")
pickers
.new({}, {
prompt_title = "Actions",
finder = finders.new_table({
results = captureLoadedProjects(),
}),
sorter = conf.generic_sorter({}),
attach_mappings = function(prompt_buf, _)
actions.select_default:replace(function()
actions.close(prompt_buf)
local selection = action_state.get_selected_entry()
BuildFSharpProjects({ selection.value })
end)
return true
end,
})
:find()
end
end, { nargs = "?", complete = "file" })
vim.api.nvim_create_autocmd("FileType", {
pattern = "fsharp",
callback = function()
local status, whichkey = pcall(require, "which-key")
if status then
whichkey.register({
f = {
t = { ":call fsharp#showTooltip()<CR>", "Show F# Tooltip" },
["si"] = { ":call fsharp#toggleFsi()<CR>", "Toggle FSI (F# Interactive)" },
["sl"] = { ":call fsharp#sendLineToFsi()<cr>", "Send line to FSI (F# Interactive)" },
},
b = {
p = {
a = { BuildFSharpProjects, "Build all projects" },
s = { ":BuildFSharpProject", "Build specified project" },
},
},
}, { prefix = vim.api.nvim_get_var("maplocalleader"), buffer = vim.api.nvim_get_current_buf() })
else
vim.api.nvim_set_keymap("n", "<localleader>ft", ":call fsharp#showTooltip()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>fsi", ":call fsharp#toggleFsi()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>fsl", ":call fsharp#sendLineToFsi()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>bpa", BuildFSharpProjects, { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>bps", ":BuildFSharpProject", { noremap = true })
end
end,
})

View File

@@ -0,0 +1,170 @@
local coq = require("coq")
-- Using rustaceanvim means we shouldn't set up the LSP for Rust manually.
-- Similarly csharp_ls is unnecessary given roslyn.nvim
-- require("lspconfig")["csharp_ls"].setup({})
require("lspconfig")["lua_ls"].setup({
on_init = function(client)
local path = client.workspace_folders[1].name
if vim.loop.fs_stat(path .. "/.luarc.json") or vim.loop.fs_stat(path .. "/.luarc.jsonc") then
return
end
client.config.settings.Lua = vim.tbl_deep_extend("force", client.config.settings.Lua, {
runtime = {
-- Tell the language server which version of Lua you're using
-- (most likely LuaJIT in the case of Neovim)
version = "LuaJIT",
},
-- Make the server aware of Neovim runtime files
workspace = {
checkThirdParty = false,
library = {
vim.env.VIMRUNTIME,
-- Depending on the usage, you might want to add additional paths here.
-- "${3rd}/luv/library"
-- "${3rd}/busted/library",
},
-- or pull in all of 'runtimepath'. NOTE: this is a lot slower
-- library = vim.api.nvim_get_runtime_file("", true)
},
})
end,
settings = {
Lua = {},
},
})
require("lspconfig").pyright.setup(coq.lsp_ensure_capabilities({
handlers = {
["textDocument/publishDiagnostics"] = function(...)
vim.lsp.diagnostic.on_publish_diagnostics(...)
local window = vim.api.nvim_get_current_win()
vim.diagnostic.setloclist({ open_loclist = true })
vim.api.nvim_set_current_win(window)
end,
},
}))
require("lspconfig").nil_ls.setup(coq.lsp_ensure_capabilities({
settings = {
nix = {
flake = {
autoArchive = true,
},
},
},
}))
function ToggleLocList()
local winid = vim.fn.getloclist(0, { winid = 0 }).winid
if winid == 0 then
local window = vim.api.nvim_get_current_win()
vim.cmd.lopen()
vim.api.nvim_set_current_win(window)
else
vim.cmd.lclose()
end
end
do
local whichkey_status, whichkey = pcall(require, "which-key")
if whichkey_status then
whichkey.register({
l = {
name = "loclist-related commands",
p = { vim.diagnostic.goto_prev, "Go to previous entry in loclist" },
n = { vim.diagnostic.goto_next, "Go to next entry in loclist" },
l = { ToggleLocList, "Toggle loclist" },
f = { vim.diagnostic.open_float, "Open current loclist entry in floating window" },
},
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
else
vim.keymap.set("n", "<localleader>lp", vim.diagnostic.goto_prev)
vim.keymap.set("n", "<localleader>ln", vim.diagnostic.goto_next)
vim.keymap.set("n", "<localleader>ll", ToggleLocList)
vim.keymap.set("n", "<localleader>lf", vim.diagnostic.open_float)
end
end
-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
callback = function(ev)
local whichkey_status, whichkey = pcall(require, "which-key")
-- Enable completion triggered by <c-x><c-o>
vim.bo[ev.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
-- Buffer local mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
local opts = { buffer = ev.buf }
if whichkey_status then
whichkey.register({
g = {
name = "Go-to related commands",
D = { vim.lsp.buf.declaration, "Go to declaration" },
d = { vim.lsp.buf.definition, "Go to definition" },
i = { vim.lsp.buf.implementation, "Go to implementation" },
r = {
function()
require("telescope.builtin").lsp_references()
end,
"Find references",
},
},
K = { vim.lsp.buf.hover, "Display information about symbol under cursor" },
})
whichkey.register({
["<C-k>"] = { vim.lsp.buf.signature_help, "Display signature information about symbol under cursor" },
})
whichkey.register({
w = {
a = { vim.lsp.buf.add_workspace_folder, "Add a path to the workspace folders list" },
r = { vim.lsp.buf.add_workspace_folder, "Remove a path from the workspace folders list" },
l = {
function()
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end,
"Show the workspace folders list",
},
},
f = {
function()
vim.lsp.buf.format({ async = true })
end,
"Autoformat",
},
c = {
a = { vim.lsp.buf.code_action, "Select a code action" },
},
r = {
n = { vim.lsp.buf.rename, "Rename variable" },
},
D = { vim.lsp.buf.type_definition, "Go to type definition" },
}, { prefix = "<space>" })
else
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, opts)
vim.keymap.set("n", "<space>wa", vim.lsp.buf.add_workspace_folder, opts)
vim.keymap.set("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, opts)
vim.keymap.set("n", "<space>wl", function()
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end, opts)
vim.keymap.set("n", "<space>D", vim.lsp.buf.type_definition, opts)
vim.keymap.set("n", "<space>rn", vim.lsp.buf.rename, opts)
vim.keymap.set({ "n", "v" }, "<space>ca", vim.lsp.buf.code_action, opts)
vim.keymap.set("n", "gr", function()
require("telescope.builtin").lsp_references()
end, opts)
vim.keymap.set("n", "<space>f", function()
vim.lsp.buf.format({ async = true })
end, opts)
end
end,
})

View File

@@ -0,0 +1 @@
require("dap-python").setup("%PYTHONENV%/bin/python")

View File

@@ -0,0 +1,99 @@
local dap = require("dap")
local dap_ui = require("dap.ui.widgets")
dap.adapters.coreclr = {
type = "executable",
command = "netcoredbg",
args = { "--interpreter=vscode" },
}
dap.configurations.fsharp = {
{
type = "coreclr",
name = "launch - netcoredbg",
request = "launch",
program = function()
return vim.fn.input("Path to dll: ", vim.fn.getcwd() .. "/bin/Debug/", "file")
end,
},
}
dap.configurations.cs = {
{
type = "coreclr",
name = "launch - netcoredbg",
request = "launch",
program = function()
return vim.fn.input("Path to dll: ", vim.fn.getcwd() .. "/bin/Debug/", "file")
end,
},
}
do
local status, whichkey = pcall(require, "which-key")
if status then
whichkey.register({
d = {
name = "Debugger-related commands",
o = { dap.step_over, "Step over" },
i = { dap.step_into, "Step into" },
c = { dap.continue, "Continue" },
C = { dap.run_last, "Run with last debug configuration" },
b = { dap.toggle_breakpoint, "Toggle breakpoint" },
r = { dap.repl.open, "Open debug repl" },
v = {
name = "Commands to view debugger state",
v = {
function()
dap_ui.hover()
end,
"View value of expression under cursor",
},
s = {
function()
dap_ui.sidebar(dap_ui.scopes).open()
end,
"View values of all variables in all scopes",
},
f = {
function()
dap_ui.sidebar(dap_ui.frames).open()
end,
"View stack frames",
},
},
t = { dap.terminate, "Terminate/stop/end debug session" },
},
}, { prefix = vim.api.nvim_get_var("maplocalleader") })
else
vim.api.nvim_set_keymap("n", "<localleader>do", ":lua require('dap').step_over()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>di", ":lua require('dap').step_into()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>dc", ":lua require('dap').continue()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>dC", ":lua require('dap').run_last()<CR>", { noremap = true })
vim.api.nvim_set_keymap(
"n",
"<localleader>db",
":lua require('dap').toggle_breakpoint()<CR>",
{ noremap = true }
)
vim.api.nvim_set_keymap("n", "<localleader>dr", ":lua require('dap').repl.open()<CR>", { noremap = true })
vim.api.nvim_set_keymap(
"n",
"<localleader>dvv",
":lua require('dap.ui.widgets').hover()<CR>",
{ noremap = true }
)
vim.api.nvim_set_keymap(
"n",
"<localleader>dvs",
":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').scopes).open()<CR>",
{ noremap = true }
)
vim.api.nvim_set_keymap(
"n",
"<localleader>dvf",
":lua require('dap.ui.widgets').sidebar(require('dap.ui.widgets').frames).open()<CR>",
{ noremap = true }
)
vim.api.nvim_set_keymap("n", "<localleader>dt", ":lua require('dap').terminate()<CR>", { noremap = true })
end
end

View File

@@ -0,0 +1,4 @@
require("roslyn").setup({
on_attach = function(_, _) end,
capabilities = vim.lsp.protocol.make_client_capabilities(),
})

View File

@@ -0,0 +1,5 @@
require("tokyonight").setup({
style = "night",
})
vim.cmd([[colorscheme tokyonight]])

View File

@@ -0,0 +1,9 @@
require("nvim-treesitter.configs").setup({
-- Automatically install missing parsers when entering buffer
-- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
auto_install = false,
highlight = {
enable = true,
},
})

View File

@@ -0,0 +1,199 @@
local venv_selector = require("venv-selector")
venv_selector.setup({
changed_venv_hooks = { venv_selector.hooks.pyright },
name = { "venv", ".venv" },
})
vim.api.nvim_create_autocmd("VimEnter", {
desc = "Auto select virtualenv Nvim open",
pattern = "*",
callback = function()
-- Mystery: this seems to be being called twice whenever we open nvim
local venv = vim.fn.findfile("pyproject.toml", vim.fn.getcwd() .. ";")
if venv ~= "" then
require("venv-selector").retrieve_from_cache()
end
end,
once = true,
})
function SelectVenv()
local old_path = vim.fn.getenv("PATH")
vim.cmd("VenvSelectCached")
local new_path = vim.fn.getenv("PATH")
if old_path == new_path then
-- Failed to source venv. Get the user to choose one.
vim.cmd("VenvSelect")
end
end
local function find_requirements_txt(start_path)
local path = vim.fn.fnamemodify(start_path, ":p")
while path and #path > 1 do
local req_path = path .. "requirements.txt"
if vim.fn.filereadable(req_path) ~= 0 then
return req_path
end
path = vim.fn.fnamemodify(path, ":h")
end
return nil
end
-- TODO: make this one work
local function load_venv(venv_dir)
require("venv-selector.venv").load()
require("venv-selector.venv").set_venv_and_system_paths(venv_dir)
require("venv-selector.venv").cache_venv(venv_dir)
end
function CreateVenv()
local requirements_path = find_requirements_txt(vim.fn.getcwd())
local venv_dir
if not requirements_path then
print("requirements.txt not found; creating fresh venv in current working directory.")
venv_dir = vim.fn.getcwd() .. "/.venv"
else
venv_dir = vim.fn.fnamemodify(requirements_path, ":h") .. "/.venv"
end
print("Creating virtual environment in " .. venv_dir)
-- Create virtual environment
vim.fn.system("python -m venv " .. vim.fn.shellescape(venv_dir))
-- Install requirements
if requirements_path then
print("Installing requirements from " .. requirements_path)
local handle
local stdout = vim.loop.new_pipe(false)
local stderr = vim.loop.new_pipe(false)
local function on_output(context, prefix, err, data)
if err or data then
vim.schedule(function()
if err then
-- Append the error message to the buffer
local count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_buf_set_lines(
context.buf,
count,
count,
false,
{ "error " .. prefix .. ": " .. err }
)
end
if data then
-- Append the data to the buffer
local count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_buf_set_lines(
context.buf,
count,
count,
false,
vim.tbl_map(function(line)
return prefix .. ": " .. line
end, vim.split(data, "\n"))
)
end
if vim.api.nvim_win_is_valid(context.window) then
local cur_win = vim.api.nvim_get_current_win()
local cur_buf = vim.api.nvim_win_get_buf(cur_win)
if cur_buf ~= context.buf then
local new_line_count = vim.api.nvim_buf_line_count(context.buf)
vim.api.nvim_win_set_cursor(context.window, { new_line_count, 0 })
end
end
end)
end
end
-- TODO: commonise wth what's in ionide-vim
-- Create a new buffer for build output
local buf = vim.api.nvim_create_buf(false, true) -- No listed, scratch buffer
-- Calculate window size and position here (example: full width, 10 lines high at the bottom)
local width = vim.api.nvim_get_option("columns")
local height = vim.api.nvim_get_option("lines")
local win_height = math.min(10, math.floor(height * 0.2)) -- 20% of total height or 10 lines
local original_win = vim.api.nvim_get_current_win()
local win_opts = {
relative = "editor",
width = width,
height = win_height,
col = 0,
row = height - win_height,
style = "minimal",
border = "single",
}
local win = vim.api.nvim_open_win(buf, true, win_opts)
-- Switch back to the original window
vim.api.nvim_set_current_win(original_win)
local context = {
window = win,
buf = buf,
}
handle, _ = vim.loop.spawn(
-- TODO: do we need to escape this? Don't know whether spawn goes via a shell
venv_dir .. "/bin/python",
{
-- TODO: and do we need to escape this?
args = { "-m", "pip", "install", "-r", requirements_path },
stdio = { nil, stdout, stderr },
},
vim.schedule_wrap(function(code, signal)
stdout:read_stop()
stderr:read_stop()
stdout:close()
stderr:close()
handle:close()
print("Venv creation completed, exit code " .. code .. " and signal " .. signal)
load_venv(venv_dir)
end)
)
if not handle then
print("Failed to start venv install process.")
return
end
vim.loop.read_start(stdout, function(err, data)
on_output(context, "OUT", err, data)
end)
vim.loop.read_start(stderr, function(err, data)
on_output(context, "ERR", err, data)
end)
else
load_venv(venv_dir)
end
end
do
local status, whichkey = pcall(require, "which-key")
if status then
whichkey.register({
p = {
name = "Python-related commands",
v = {
name = "Virtual environment-related commands",
c = { CreateVenv, "Create virtual environment" },
l = { SelectVenv, "Load virtual environment" },
o = {
function()
vim.cmd("VenvSelect")
end,
"Choose (override) new virtual environment",
},
},
},
}, { prefix = vim.api.nvim_get_var("maplocalleader"), buffer = vim.api.nvim_get_current_buf() })
else
vim.api.nvim_set_keymap("n", "<localleader>pvc", ":lua CreateVenv()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>pvl", ":lua SelectVenv()<CR>", { noremap = true })
vim.api.nvim_set_keymap("n", "<localleader>pvo", ":VenvSelect<CR>", { noremap = true })
end
end

View File

@@ -0,0 +1,88 @@
require("which-key").setup({
plugins = {
marks = true, -- shows a list of your marks on ' and `
registers = true, -- shows your registers on " in NORMAL or <C-r> in INSERT mode
-- the presets plugin, adds help for a bunch of default keybindings in Neovim
-- No actual key bindings are created
spelling = {
enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions
suggestions = 20, -- how many suggestions should be shown in the list?
},
presets = {
operators = true, -- adds help for operators like d, y, ...
motions = true, -- adds help for motions
text_objects = true, -- help for text objects triggered after entering an operator
windows = true, -- default bindings on <c-w>
nav = true, -- misc bindings to work with windows
z = true, -- bindings for folds, spelling and others prefixed with z
g = true, -- bindings for prefixed with g
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
motions = {
count = true,
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "none", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]. When between 0 and 1, will be treated as a percentage of the screen size.
padding = { 1, 2, 1, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0, -- value between 0-100 0 for fully opaque and 100 for fully transparent
zindex = 1000, -- positive value to position WhichKey above other floating windows.
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = false, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- hide mapping boilerplate
show_help = true, -- show a help message in the command line for using WhichKey
show_keys = true, -- show the currently pressed key and its label as a message in the command line
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specifiy a list manually
-- list of triggers, where WhichKey should not wait for timeoutlen and show immediately
triggers_nowait = {
-- marks
"`",
"'",
"g`",
"g'",
-- registers
'"',
"<c-r>",
-- spelling
"z=",
},
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for keymaps that start with a native binding
i = { "j", "k" },
v = { "j", "k" },
},
-- disable the WhichKey popup for certain buf types and file types.
-- Disabled by default for Telescope
disable = {
buftypes = {},
filetypes = {},
},
})