Last active
February 17, 2023 00:48
-
-
Save dixler/e0b4a92fee04f9903ad1433bab3bf554 to your computer and use it in GitHub Desktop.
lua/user/init.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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