Skip to content

Instantly share code, notes, and snippets.

@dixler
Last active February 17, 2023 00:48
Show Gist options
  • Save dixler/e0b4a92fee04f9903ad1433bab3bf554 to your computer and use it in GitHub Desktop.
Save dixler/e0b4a92fee04f9903ad1433bab3bf554 to your computer and use it in GitHub Desktop.
lua/user/init.lua
--[[
Copyright 2023 Kyle Dixler
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
--]]
local ensure_packer = function()
local fn = vim.fn
local install_path = fn.stdpath('data')..'/site/pack/packer/start/packer.nvim'
if fn.empty(fn.glob(install_path)) > 0 then
fn.system({'git', 'clone', '--depth', '1', 'https://github.com/wbthomason/packer.nvim', install_path})
vim.cmd [[packadd packer.nvim]]
return true
end
return false
end
local packer_bootstrap = ensure_packer()
require('packer').startup(function(use)
use 'wbthomason/packer.nvim'
-- My plugins here
-- use 'foo1/bar1.nvim'
-- use 'foo2/bar2.nvim'
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v1.x',
requires = {
-- LSP Support
{'neovim/nvim-lspconfig'}, -- Required
{'williamboman/mason.nvim'}, -- Optional
{'williamboman/mason-lspconfig.nvim'}, -- Optional
-- Autocompletion
{'hrsh7th/nvim-cmp'}, -- Required
{'hrsh7th/cmp-nvim-lsp'}, -- Required
-- Snippets
{'L3MON4D3/LuaSnip'}, -- Required
},
}
use "ray-x/lsp_signature.nvim"
use {
"folke/trouble.nvim",
requires = "nvim-tree/nvim-web-devicons",
config = function()
require("trouble").setup {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
end
}
use 'tpope/vim-fugitive'
use 'nvim-treesitter/nvim-treesitter'
use { 'lewis6991/gitsigns.nvim',
requires = { 'nvim-lua/plenary.nvim' },
}
use { 'romgrk/barbar.nvim',
requires = "nvim-tree/nvim-web-devicons",
}
use {
"nvim-neo-tree/neo-tree.nvim",
branch = "v2.x",
requires = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons", -- not strictly required, but recommended
"MunifTanjim/nui.nvim",
}
}
use {
'nvim-telescope/telescope.nvim', tag = '0.1.1',
requires = { {'nvim-lua/plenary.nvim'} }
}
use "folke/neodev.nvim"
use "lukas-reineke/indent-blankline.nvim"
-- Or with configuration
use {
'projekt0n/github-nvim-theme', tag = 'v0.0.7',
}
use {
'nvim-lualine/lualine.nvim',
requires = { 'nvim-tree/nvim-web-devicons', opt = true },
}
-- Automatically set up your configuration after cloning packer.nvim
-- Put this at the end after all plugins
if packer_bootstrap then
require('packer').sync()
end
end)
require "lsp_signature".setup {
fix_pos = true, -- set to true, the floating window will not auto-close until finish all parameters
floating_window_above_cur_line = false,
}
vim.opt.number = true
--vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.autoindent = true
vim.opt.mouse = ""
vim.opt.timeout = false
vim.opt.ttimeout = true
vim.opt.showcmd = true
vim.api.nvim_exec([[
let &scrolloff=999-&scrolloff+1
" set cursorcolumn
" set cursorline
]], false)
local cmp = require('cmp')
cmp.setup {
snippet = {
-- REQUIRED - you must specify a snippet engine
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = {
["<C-p>"] = cmp.mapping.select_prev_item {},
["<C-n>"] = cmp.mapping.select_next_item {},
["<C-j>"] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
["<C-k>"] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
["<C-Enter>"] = cmp.mapping.confirm { select = false },
},
}
-- IMPORTANT: make sure to setup neodev BEFORE lspconfig
require("neodev").setup()
-- then setup your lsp server as usual
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup()
require('gitsigns').setup()
require('lualine').setup {
options = { theme = 'onedark' }
}
require("neo-tree").setup {
close_if_last_window = true,
default_component_configs = {
container = {
enable_character_fade = true
},
indent = {
padding = 0, -- extra padding on left hand side
},
},
window = {
width = 30,
}
}
local actions = require("telescope.actions")
require('telescope').setup {
defaults = {
prompt_prefix = string.format("%s ", ">"),
selection_caret = string.format("%s ", ">"),
path_display = { "truncate" },
sorting_strategy = "ascending",
layout_config = {
horizontal = {
prompt_position = "top",
preview_width = 0.55,
results_width = 0.8,
},
vertical = {
mirror = false,
},
width = 0.99,
height = 0.99,
preview_cutoff = 120,
},
mappings = {
i = {
["<C-n>"] = actions.cycle_history_next,
["<C-p>"] = actions.cycle_history_prev,
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
},
n = { ["q"] = actions.close },
},
},
}
vim.lsp.handlers["textDocument/publishDiagnostics"] =
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = true,
})
vim.opt.termguicolors = true
vim.cmd [[highlight IndentBlanklineIndent1 guifg=#E06C75 gui=nocombine]]
vim.cmd [[highlight IndentBlanklineIndent2 guifg=#E5C07B gui=nocombine]]
vim.cmd [[highlight IndentBlanklineIndent3 guifg=#98C379 gui=nocombine]]
vim.cmd [[highlight IndentBlanklineIndent4 guifg=#56B6C2 gui=nocombine]]
vim.cmd [[highlight IndentBlanklineIndent5 guifg=#61AFEF gui=nocombine]]
vim.cmd [[highlight IndentBlanklineIndent6 guifg=#C678DD gui=nocombine]]
require("indent_blankline").setup {
show_current_context = true,
show_current_context_start = true,
space_char_blankline = " ",
char_highlight_list = {
"IndentBlanklineIndent1",
"IndentBlanklineIndent2",
"IndentBlanklineIndent3",
"IndentBlanklineIndent4",
"IndentBlanklineIndent5",
"IndentBlanklineIndent6",
},
}
require('github-theme').setup {
theme_style = "dimmed",
colors = {hint = "orange", error = "#ff0000"},
}
-- keybindings
vim.g.mapleader = " "
vim.keymap.set('n', '<leader>o', '<cmd>NeoTreeFocus<CR>', {noremap = true})
vim.keymap.set('n', '<leader>e', '<cmd>NeoTreeClose<CR>', {noremap = true})
vim.keymap.set('n', "<C-h>", "<C-w>h", {noremap = true})
vim.keymap.set('n', "<C-j>", "<C-w>j", {noremap = true})
vim.keymap.set('n', "<C-k>", "<C-w>k", {noremap = true})
vim.keymap.set('n', "<C-l>", "<C-w>l", {noremap = true})
vim.keymap.set('n', "<C-Up>", "<cmd>resize -2<CR>", {noremap = true})
vim.keymap.set('n', "<C-Down>", "<cmd>resize +2<CR>", {noremap = true})
vim.keymap.set('n', "<C-Left>", "<cmd>vertical resize -2<CR>", {noremap = true})
vim.keymap.set('n', "<C-Right>", "<cmd>vertical resize +2<CR>", {noremap = true})
vim.keymap.set('n', "<S-h>", "<cmd>BufferPrevious<CR>", {noremap = true})
vim.keymap.set('n', "<S-l>", "<cmd>BufferNext<CR>", {noremap = true})
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, {})
vim.keymap.set('n', '<leader>fg', builtin.live_grep, {})
vim.keymap.set('n', '<leader>fb', builtin.buffers, {})
vim.keymap.set('n', '<leader>fh', builtin.help_tags, {})
-- git-appraise.nvim
--- Gets the range of the current visual selection.
-- @return start line, start column, end line, end column where lines and columns are both
-- inclusive, lines are 1-indexed and columns are 0-indexed.
local function visual_selection_range()
local _, csrow, cscol, _ = unpack(vim.fn.getpos("v"))
local _, cerow, cecol, _ = unpack(vim.fn.getpos("."))
if csrow < cerow or (csrow == cerow and cscol <= cecol) then
return csrow, cscol, cerow, cecol
else
return cerow, cecol, csrow, cscol
end
end
local function shellSanitize(str)
return string.gsub(str, "'", [['"'"']])
end
vim.cmd [[highlight CommentTextHeader cterm=bold ctermfg=236 ctermbg=114 gui=bold guifg=#282c34 guibg=#98c379]]
vim.cmd [[highlight CommentTextHeaderInvert cterm=bold ctermfg=114 ctermbg=236 gui=bold guifg=#98c379 guibg=#282c34]]
vim.cmd [[highlight CommentText ctermfg=121 guifg=#539bf5]]
local function format_comment(comment, opts)
local max_width = 80
local function wrap(str)
local lines = {}
table.insert(lines, {
{comment.author, 'CommentTextHeader'},
{" L" .. opts.line, 'CommentTextHeaderInvert'},
})
local line = ""
for word in string.gmatch(str, "%S+") do
if #line + #word > max_width then
table.insert(lines, {
{line, 'CommentText'},
})
line = ""
end
if #line > 0 then
line = line .. " " .. word
else
line = word
end
end
if #line > 0 then
table.insert(lines, {
{line, 'CommentText'},
})
end
return lines
end
return wrap(comment.description)
end
local comments_ns = vim.api.nvim_create_namespace('comments')
local function blame_map(filename, opts)
local since = ""
if opts ~= nil and opts.since ~= nil then
since = " '--since=".. opts.since .."'"
end
local blame_cmd = "git blame"
.. since
.. " --show-name" -- show the name to discard it.
-- if you use a space in your filenames, it's your fault.
.. " -s"
.. " -n"
.. " --contents -"
.. " '"..filename.."'"
local file_contents = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local output = vim.fn.system(blame_cmd, file_contents)
print(output)
local buffer_comment_map = {}
local comment_buffer_map = {}
for line in string.gmatch(output, "[^\n]+") do
local blame_data = nil
for prefix in string.gmatch(line, "[^)]+") do
blame_data = prefix
goto done
end
::done::
if blame_data == nil then
error("blame line is nil")
end
local elems = {}
for elem in string.gmatch(blame_data, "%S+") do
table.insert(elems, elem)
end
local line_blame = {
commit = elems[1],
original_line = tonumber(elems[3]),
current_line = tonumber(elems[4]),
}
buffer_comment_map[line_blame.current_line] = line_blame
comment_buffer_map[line_blame.original_line] = line_blame
::continue::
end
local map = {
get_blame_from_cur_line = function(line)
return buffer_comment_map[line]
end,
get_blame_from_comment_line = function(line)
return comment_buffer_map[line]
end,
}
return map
end
local function clear_comments()
vim.api.nvim_buf_clear_namespace(0, comments_ns, 0, -1)
end
local function display_comments()
clear_comments()
local baseDirRes = vim.fn.system("git rev-parse --show-toplevel")
local baseDir = string.gsub(baseDirRes, '\n', '')
if baseDir == "" then
-- not a git repository
return
end
local absFilePath = vim.fn.expand('%:p')
local filePath = string.sub(absFilePath, 1 + string.len(baseDir .. "/"))
if filePath == "" then
return
end
local bmap = blame_map(filePath, {since = "HEAD"})
local comment_show_cmd = "git appraise show"
.. " -d"
.. " -json"
.. " " .. filePath
local commentJSON = vim.fn.system(comment_show_cmd)
local comments = vim.json.decode(commentJSON)
if type(comments) ~= "table" then
print("no comments found for file " .. filePath)
return
end
for _, entry in ipairs(comments) do
local comment = entry.comment
local comment_location = comment.location
local blame = bmap.get_blame_from_comment_line(comment_location.range.startLine)
if blame == nil then
goto continue
end
if blame.commit == "00000000" then
-- shouldn't happen
goto continue
end
local actual_line = blame.current_line
local comment_virt_lines = format_comment(comment, {
line = actual_line,
})
vim.api.nvim_buf_set_extmark(0, comments_ns, actual_line-1, 0, {
virt_lines_above = true,
virt_lines = comment_virt_lines,
strict = false,
})
vim.fn.setqflist({{filename = "file.txt", lnum = 10, col = 5, text = "error message"}}, "a")
::continue::
end
end
local cur_comment_ns = vim.api.nvim_create_namespace('cur_comment_ns')
local function post_comment()
local absFilePath = vim.fn.expand('%:p')
-- get range of comment
local csrow, cscol, cerow, cecol = visual_selection_range()
local MAX_LINE = 2147483647
local orig_buf = vim.api.nvim_get_current_buf()
-- Open a new buffer
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_create_autocmd("BufDelete", {
buffer = buf,
callback = function ()
vim.api.nvim_buf_clear_namespace(0, cur_comment_ns, 0, -1)
end,
})
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'wipe')
vim.api.nvim_buf_set_option(buf, 'buftype', 'prompt')
vim.api.nvim_command('startinsert')
vim.fn.prompt_setprompt(buf, 'Comment: ')
vim.fn.prompt_setinterrupt(buf, function()
vim.api.nvim_buf_clear_namespace(orig_buf, cur_comment_ns, 0, -1)
vim.api.nvim_buf_delete(buf, {force = true})
print("cancelled posting comment")
end)
vim.fn.prompt_setcallback(buf, function(comment)
vim.api.nvim_buf_clear_namespace(orig_buf, cur_comment_ns, 0, -1)
-- "git appraise comment -m <comment> -f <cur-file> -l <csrow[+cscol]:cerow[+cecol]>"
vim.api.nvim_buf_delete(buf, {force=true})
local baseDirRes = vim.fn.system("git rev-parse --show-toplevel")
local baseDir = string.gsub(baseDirRes, '\n', '')
local filePath = string.sub(absFilePath, 1 + string.len(baseDir .. "/"))
local bmap = blame_map(filePath, {since = "HEAD"})
local start_blame = bmap.get_blame_from_cur_line(csrow)
if start_blame == nil then
return
end
if start_blame.commit == "00000000" then
print("cannot write comment on uncommitted line (start)")
return
end
local start = start_blame.original_line
if cscol ~= 1 and cscol ~= MAX_LINE then
start = start_blame.original_line .. "+" .. cscol
end
local end_blame = bmap.get_blame_from_cur_line(cerow)
if end_blame == nil then
return
end
if end_blame.commit == "00000000" then
print("cannot write comment on uncommitted line (end)")
return
end
local finish = end_blame.original_line
if cecol ~= 1 and cecol ~= MAX_LINE then
finish = end_blame.original_line .. "+" .. cecol
end
local range = start .. ":" .. finish
-- "git appraise comment -m <comment> -f <cur-file> -l <csrow[+cscol]:cerow[+cecol]>"
local cmd = "git appraise comment"
.. " -d"
.. " -nmw"
.. " -m '" .. shellSanitize(comment) .. "'"
.. " -f '" .. shellSanitize(filePath) .. "'"
.. " -l '" .. shellSanitize(range) .. "'"
vim.fn.system(cmd)
display_comments()
print("successfully posted comment!")
end)
vim.highlight.range(
0,
cur_comment_ns, ---@diagnostic disable-line: param-type-mismatch
"Cursor", ---@diagnostic disable-line: param-type-mismatch
{csrow-1,cscol-1},
{cerow,cecol-1},
{}
)
-- Open the buffer in a new window
local height = 3
vim.api.nvim_open_win(buf, true, {
relative='win', anchor="SW", width=80, height=height, bufpos={math.max(csrow-1, 0),0}})
end
vim.keymap.set('x', '<leader>cc', post_comment, {noremap = true})
vim.keymap.set('n', '<leader>cs', display_comments, {noremap = true})
vim.keymap.set('n', '<leader>ch', clear_comments, {noremap = true})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment