local cmd = vim.cmd -- to execute Vim commands e.g. cmd('pwd')
local g = vim.g -- a table to access global variables
-- Leader namespaces - for related actions
-- b: Buffers
-- e: Edit file (init.lua, etc)
-- g: Go (code completion)
-- h: Git Hunks
-- r: Refactor
-- s: Spell checker, Source file (init.lua, etc), Sideways plugin
-- t: Tabs, Tests
g['mapleader'] = ','
g['mapleader'] = ','
local utils = require('utils')
-- Keep list of augroups
local augroup_defs = {}
-- Plugins
-- TODO make paq install itself here
vim.cmd 'packadd paq-nvim' -- Load Paq
local paq = require'paq-nvim'.paq
paq{'savq/paq-nvim', opt=true} -- Let Paq manage itself
-- General
paq 'scrooloose/nerdtree' -- View of the file system
paq 'nvim-lua/popup.nvim' -- Helper for other plugins
paq 'nvim-lua/plenary.nvim' -- Helper for other plugins
paq 'nvim-telescope/telescope.nvim'
paq 'morhetz/gruvbox' -- Color scheme
paq 'vim-airline/vim-airline' -- Status line at the bottom
paq 'mileszs/ack.vim' -- ack/ag in vim
paq 'embear/vim-localvimrc' -- Local vimrc files for project-specific settings
-- General programming
paq 'vim-syntastic/syntastic' -- Syntax checker
paq 'tpope/vim-surround' -- Act on surrounding things (e.g. quotes)
paq 'tpope/vim-repeat' -- Repeat certain commands such as surround
paq 'tpope/vim-commentary' -- Comment/uncomment code
paq 'janko/vim-test' -- Run tests
paq 'tpope/vim-fugitive' -- git integration
paq 'lewis6991/gitsigns.nvim' -- Shows git diff in the 'gutter' (sign column)
paq 'neovim/nvim-lspconfig' -- Code navigation
paq 'hrsh7th/nvim-compe' -- Code completion
paq 'knsh14/vim-github-link' -- Generate shareable file permalinks
paq{'nvim-treesitter/nvim-treesitter', run=':TSUpdate'} -- Parser generator (highlighting etc)
-- Python
paq 'numirias/semshi' -- Syntax highlighting
paq 'nvie/vim-flake8' -- Python style checker
-- PlantUML
paq 'aklt/plantuml-syntax' -- Syntax highlighting
-- Load plugin-specific configs
require 'plugin_configs/nvim-lspconfig'
require 'plugin_configs/nvim-compe_config' -- TODO figure out why just 'nvim-compe' doesn't work
require('gitsigns').setup()
-- Plugin configs
-- NERDTree
-- Shortcut to open NERDTree
utils.map('n', '<leader>ne', ':NERDTreeToggle<Enter>')
-- Ignore some files
cmd [[ let NERDTreeIgnore=['\.pyc$', '\~$', '\.o', '\.lo'] ]]
-- Automatically close NERDTree when you open a file
g['NERDTreeQuitOnOpen'] = 1
-- Automatically close a tab if the only remaining window is NERDTree
vim.cmd('autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif')
-- Automatically delete the buffer of the file you just deleted with NerdTree:
g['NERDTreeAutoDeleteBuffer'] = 1
-- Making it prettier
g['NERDTreeMinimalUI'] = 1
g['NERDTreeDirArrows'] = 1
-- Syntastic
g['syntastic_always_populate_loc_list'] = 1
g['syntastic_auto_loc_list'] = 1
g['syntastic_check_on_open'] = 1
g['syntastic_check_on_wq'] = 0
cmd "let g:syntastic_python_checkers = ['flake8', 'pylint']"
-- Show all levels of pylint messages
cmd 'let g:syntastic_quiet_messages = { "level": [] }'
cmd 'let g:syntastic_python_pylint_quiet_messages = { "level" : [] }'
-- TODO probably need something better, but might just disappear if I find a way
-- to run pylint and flake8 asynchronously.
utils.map('n', '<leader>l', ':SyntasticCheck<CR>', {silent = true})
-- Disable Syntastic for c files - reply on LSP instead
cmd 'let g:syntastic_ignore_files = [".c"]'
-- Telescope
-- Shortcut for 'go to buffer'
utils.map('n', 'gb', ':Telescope buffers<CR>')
utils.map('n', '<leader>ff', ':Telescope find_files<CR>')
-- Treesitter
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
},
indent = {
enable = false,
},
ensure_installed = {
"c",
"cpp",
},
}
-- vim-test
-- Run tests in a split window
cmd "let g:test#strategy = 'neovim'"
-- vim-surround
-- Don't select extra space when working around quotes
-- https://github.com/tpope/vim-surround/issues/276
utils.map('n', [[ysa']], [[ys2i']])
utils.map('n', [[ysa"]], [[ys2i"]])
utils.map('n', [[ysa`]], [[ys2i`]])
--
-- General
--
-- Sets how many lines of history VIM has to remember
vim.o.history = 500
-- Access the system clipboard
cmd 'set clipboard+=unnamedplus'
-- vim.o.clipboard = vim.o.clipboard + { 'unnamedplus' }
-- Quickly edit/reload init.lua
utils.map('n', '<leader>ei', ':e ~/.config/nvim/init.lua<CR>', {silent = true})
utils.map('n', '<leader>si', ':luafile ~/.config/nvim/init.lua<CR>', {silent = true})
utils.map('n', '<leader>sc', ':luafile %<CR>', {silent = true})
--
-- User interface
--
-- Show the number of the current line and the relative number of the others
vim.o.number = true
vim.o.relativenumber = true
-- Show absolute numbers when in insert mode or losing focus
augroup_defs['number_toggle'] = {
'BufEnter,FocusGained,InsertLeave * set relativenumber',
'BufLeave,FocusLost,InsertEnter * set norelativenumber'
}
-- Set 7 lines/columns to the cursor
vim.o.scrolloff = 7
vim.o.sidescrolloff = 7
-- First tab completes the longest common string; next tab completes the first alternative
vim.o.wildmode = 'longest:full,full'
-- Ignore compiled files and version control folders
local wildignore = '*.o,*~,*.pyc'
-- TODO not sure that's working
if vim.fn.has('win16') or vim.fn.has('win32') then
wildignore = wildignore .. '.git\\*,.hg\\*,.svn\\*'
else
wildignore = wildignore .. '*/.git/*,*/.hg/*,*/.svn/*,*/.DS_Store'
end
vim.o.wildignore = wildignore
-- Height of the command bar
vim.o.cmdheight = 2
-- A buffer becomes hidden when it is abandoned
vim.o.hidden = true
-- Configure backspace so it acts as it should act
vim.o.backspace = 'eol,start,indent'
cmd 'set whichwrap+=<,>,h,l'
-- Search
vim.o.ignorecase = true -- Ignore case when searching
vim.o.smartcase = true -- When searching try to be smart about cases
-- Stop highlighting last search pattern
utils.map('n', '<leader><cr>', ':noh<cr>', {silent = true})
-- Don't redraw while executing macros
vim.o.lazyredraw = true
-- Show matching brackets when text indicator is over them
vim.o.showmatch = true
-- How many tenths of a second to blink when matching brackets
vim.o.matchtime = 2
-- Time in milliseconds to wait for a mapped sequence to complete.
vim.o.timeoutlen = 500
-- Update frequency for the sign column
vim.o.updatetime = 250
-- Add a bit extra margin to the left
vim.o.foldcolumn = '1'
-- Colors and Fonts
vim.o.cursorline = true
cmd 'colorscheme gruvbox'
vim.o.termguicolors = true
-- Use Unix as the standard file type
vim.o.fileformats = 'unix,dos,mac'
--
-- Text, tab and indent
--
-- Use spaces instead of tabs
vim.o.expandtab = true
-- 1 tab == 2 spaces
local indent = 2
vim.o.shiftwidth = indent
vim.o.tabstop = indent -- Number of visual spaces per TAB
vim.o.softtabstop = indent -- Number of spaces per TAB when editing
vim.o.shiftround = true -- Indenting rounds to a multiple of 2 (instead of just changing by 2)
-- Limit lines to 80 characters and still wrap longer lines
vim.o.linebreak = true
vim.o.textwidth = 80
vim.o.colorcolumn = '80'
vim.o.smartindent = true
--
-- Visual mode
--
-- Visual mode pressing * or # searches for the current selection
utils.map('v', '*', ':<C-u>call VisualSelection("", "")<CR>/<C-R>=@/<CR><CR>', {silent = true})
utils.map('v', '#', ':<C-u>call VisualSelection("", "")<CR>?<C-R>=@/<CR><CR>', {silent = true})
--
-- Terminal mode
--
-- Better command to switch from insert to normal mode
utils.map('t', '<C-o>', [[<C-\><C-n>]])
--
-- Moving around, tabs, windows and buffers
--
-- Map j to gj so vim behaves as expected with wrapped lines
-- But keep normal behaviour if doing something like 4j
cmd "nnoremap <expr> j v:count ? 'j' : 'gj'"
cmd "nnoremap <expr> k v:count ? 'k' : 'gk'"
-- Specify where splits should occur
vim.o.splitbelow = true
vim.o.splitright = true
-- Smart way to move between windows
utils.map('n', '<C-j>', '<C-W>j')
utils.map('n', '<C-k>', '<C-W>k')
utils.map('n', '<C-h>', '<C-W>h')
utils.map('n', '<C-l>', '<C-W>l')
-- Close the current buffer
utils.map('n', '<leader>bd', ':Bclose<cr>')
-- Useful mappings for managing tabs
utils.map('n', '<leader>tn', ':tabnew<cr>')
utils.map('n', '<leader>to', ':tabonly<cr>')
utils.map('n', '<leader>tc', ':tabclose<cr>')
utils.map('n', '<leader>tm', ':tabmove ')
utils.map('n', '<leader>t<leader>', ':tabnext ')
-- Opens a new tab with the current buffer's path
utils.map('n', '<leader>te', ':tabedit <c-r>=expand("%:p:h")<cr>/')
-- Switch CWD to the directory of the open buffer
utils.map('n', '<leader>cd', ':cd %:p:h<cr>:pwd<cr>')
-- Specify the behavior when switching between buffers
vim.o.switchbuf = 'useopen,usetab,newtab'
vim.o.showtabline = 1
-- Return to last edit position when opening files
vim.cmd([[autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif]])
--
-- Editing mappings
--
-- Map Y to act like D and C, i.e. to yank until EOL, rather than act as yy,
-- which is the default
utils.map('n', 'Y', 'y$')
-- Don't write to yank buffer when deleting a single character
utils.map('n', 'x', '"_x')
utils.map('n', 'X', '"_X')
-- Delete trailing white space on save
function clean_extra_spaces()
local save_cursor = vim.fn.getpos(".")
local old_query = vim.fn.getreg("/")
vim.cmd([[silent! %s/\s\+$//e]])
vim.fn.setpos('.', save_cursor)
vim.fn.setreg('/', old_query)
end
vim.cmd('autocmd BufWritePre *.txt,*.js,*.py,*.sh,*.lua :lua clean_extra_spaces()')
--
-- Code
--
-- Enable code folding
vim.o.foldmethod = 'indent'
vim.o.foldlevel = 99
-- Enable folding with the spacebar
utils.map('n', '<space>', 'za')
-- Show list of tags if there are multiples matches
-- https://stackoverflow.com/a/42078499/
utils.map('n', '<C-]>', 'g<C-]>')
-- Ruby autocomplete
vim.cmd('autocmd FileType ruby,eruby let g:rubycomplete_buffer_loading = 1')
vim.cmd('autocmd FileType ruby,eruby let g:rubycomplete_classes_in_global = 1')
vim.cmd('autocmd FileType ruby,eruby let g:rubycomplete_rails = 1')
-- Run tests
-- Using ,tj because ,tn is used by tabs
utils.map('n', '<leader>tj', ':TestNearest<CR>')
utils.map('n', '<leader>tf', ':TestFile<CR>')
utils.map('n', '<leader>ts', ':TestSuite<CR>')
utils.map('n', '<leader>tl', ':TestLast<CR>')
utils.map('n', '<leader>tg', ':TestVisit<CR>')
-- Toggle between source file and header file in C and C++
utils.map('n', '<leader>gt', ':lua header_file_toggle()<CR>', {silent=true})
function header_file_toggle()
-- For "/rnd/hello.c", file dir is "/rnd", file name is "hello" and ext is "c"
local current_file_dir = vim.api.nvim_exec([[echo expand('%:h')]], true)
local current_file_name = vim.api.nvim_exec([[echo expand('%:t:r')]], true)
local current_file_ext = vim.api.nvim_exec([[echo expand('%:e')]], true)
-- TODO don't open a blank buffer if the file doesn't exist
if current_file_ext == 'c' or current_file_ext == 'cc' then
vim.cmd('edit ' .. current_file_dir .. '/' .. current_file_name .. '.h')
elseif current_file_ext == 'h' then
-- TODO If .cc file doesn't exist, try .cpp and .c
vim.cmd('edit ' .. current_file_dir .. '/' .. current_file_name .. '.cc')
end
end
-- Spell checking
-- Toggle and untoggle spell checking
utils.map('n', '<leader>ss', ':setlocal spell!<cr>')
-- Next misspelled word
utils.map('n', '<leader>sn', ']s')
-- Previous misspelled word
utils.map('n', '<leader>sp', '[s')
-- Add to dictionary
utils.map('n', '<leader>sa', 'zg')
-- Suggest corrections
utils.map('n', '<leader>s?', 'z=')
-- Replace with first correction
utils.map('n', '<leader>s1', 'z=1<CR><CR>')
utils.map('n', '<leader>se', ':setlocal spelllang=en_en<CR>')
utils.map('n', '<leader>sf', ':setlocal spelllang=fr<CR>')
--
-- Misc
--
-- Remove the Windows ^M - when the encodings gets messed up
utils.map('n', '<leader>m', "mmHmt:%s/<C-V><cr>//ge<cr>'tzt'm")
-- Quickly open a scratchpad for scribble
utils.map('n', '<leader>q', ':e ~/scratchpad<cr>')
-- Toggle paste mode on and off
utils.map('n', '<leader>pp', ':setlocal paste!<cr>')
-- Helper functions
-- Don't close window, when deleting a buffer
-- TODO find out how to write in lua (can't use <SID> inside nvim_exec)
-- command! Bclose call <SID>BufcloseCloseIt()
-- function! <SID>BufcloseCloseIt()
-- let l:currentBufNum = bufnr("%")
-- let l:alternateBufNum = bufnr("#")
-- if buflisted(l:alternateBufNum)
-- buffer #
-- else
-- bnext
-- endif
-- if bufnr("%") == l:currentBufNum
-- new
-- endif
-- if buflisted(l:currentBufNum)
-- execute("bdelete! ".l:currentBufNum)
-- endif
-- endfunction
vim.api.nvim_exec([[
function! CmdLine(str)
exe "menu Foo.Bar :" . a:str
emenu Foo.Bar
unmenu Foo
endfunction
function! VisualSelection(direction, extra_filter) range
let l:saved_reg = @"
execute "normal! vgvy"
let l:pattern = escape(@", "\\/.*'$^~[]")
let l:pattern = substitute(l:pattern, "\n$", "", "")
if a:direction == 'gv'
call CmdLine("Ack '" . l:pattern . "' " )
elseif a:direction == 'replace'
call CmdLine("%s" . '/'. l:pattern . '/')
endif
let @/ = l:pattern
let @" = l:saved_reg
endfunction
]], false)
-- Local configuration
-- TODO make it optional to have that file
require 'local_init'
-- assert(io.open('lua/local_init.lua', 'r'))
_G.tab_complete = function()
if vim.fn.pumvisible() == 1 then
return utils.t "<C-n>"
elseif utils.check_back_space() then
return utils.t "<Tab>"
else
return vim.fn['compe#complete']()
end
end
_G.s_tab_complete = function()
if vim.fn.pumvisible() == 1 then
return utils.t "<C-p>"
else
return utils.t "<S-Tab>"
end
end
utils.map("i", "<Tab>", "v:lua.tab_complete()", {expr = true})
utils.map("s", "<Tab>", "v:lua.tab_complete()", {expr = true})
utils.map("i", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
utils.map("s", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
-- Create augroups
utils.nvim_create_augroups(augroup_defs)
-- TODO
-- Rewrite usages of cmd and nvim_exec in Lua
-- Tidy code with plugin-specific config to its own files, like https://github.com/ibhagwan/nvim-lua