local M = {}

-- lsp_signature >>>
M.signature = function()
   cfg = {
      toggle_key = '<M-x>'
   }
   require "lsp_signature".setup(cfg)
end -- <<<

-- lspinstall >>>
M.lspinstall = function() --
   local function setup_servers()
      require'lspinstall'.setup()
      local servers = require'lspinstall'.installed_servers()
      for _, server in pairs(servers) do
         require'lspconfig'[server].setup{}
      end
   end
   setup_servers()
   -- Automatically reload after `:LspInstall <server>` so we don't have to restart neovim
   require'lspinstall'.post_install_hook = function ()
      setup_servers() -- reload installed servers
      vim.cmd("bufdo e") -- this triggers the FileType autocmd that starts the server
   end
end -- <<<

-- treesitter >>>
M.treesitter = function()
   require'nvim-treesitter.configs'.setup {
      -- install the necessary parsers
      ensure_installed = {
         "bash",
         "c",
         "cmake",
         "comment",
         "cpp",
         "css",
         "dockerfile",
         "html",
         "javascript",
         "json",
         "lua",
         "python",
         "r",
         "regex",
         "rst",
         "rust",
         "toml",
         "vim",
         "yaml",
      },
      --ignore_install = { "javascript", "java" }, -- List of parsers to ignore installing
      highlight = {
         enable = true, -- false will disable the whole extension
      },
      indent = {
         enable = true
      },
      incremental_selection = {
         enable = true,
         keymaps = {
            init_selection = 'gnn',
            node_incremental = 'grn',
            scope_incremental = 'grc',
            node_decremental = 'grm',
         },
      },
      textobjects = {
         select = {
            enable = true,
            lookahead = true, -- Automatically jump forward to textobj, similar to targets.vim
            keymaps = {
               -- You can use the capture groups defined in textobjects.scm
               ['af'] = '@function.outer',
               ['if'] = '@function.inner',
               ['ac'] = '@class.outer',
               ['ic'] = '@class.inner',
            },
         },
         move = {
            enable = true,
            set_jumps = true, -- whether to set jumps in the jumplist
            goto_next_start = {
               [']m'] = '@function.outer',
               [']]'] = '@class.outer',
            },
            goto_next_end = {
               [']M'] = '@function.outer',
               [']['] = '@class.outer',
            },
            goto_previous_start = {
               ['[m'] = '@function.outer',
               ['[['] = '@class.outer',
            },
            goto_previous_end = {
               ['[M'] = '@function.outer',
               ['[]'] = '@class.outer',
            },
         },
      },
   }
end -- <<<

-- cmp >>>
M.cmp = function()
   -- nvim-cmp supports additional completion capabilities
   local capabilities = vim.lsp.protocol.make_client_capabilities()
   capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
   -- nvim-cmp setup
   local cmp = require 'cmp'
   cmp.setup {
      mapping = {
         ['<C-p>'] = cmp.mapping.select_prev_item(),
         ['<C-n>'] = cmp.mapping.select_next_item(),
         ['<C-d>'] = cmp.mapping.scroll_docs(-4),
         ['<C-f>'] = cmp.mapping.scroll_docs(4),
         ['<C-Space>'] = cmp.mapping.complete(),
         ['<C-e>'] = cmp.mapping.close(),
         ['<CR>'] = cmp.mapping.confirm {
            behavior = cmp.ConfirmBehavior.Replace,
            select = false,
         },
         ['<Tab>'] = function(fallback)
            if cmp.visible() then
               cmp.select_next_item()
            else
               fallback()
            end
         end,
         ['<S-Tab>'] = function(fallback)
            if cmp.visible() then
               cmp.select_prev_item()
            else
               fallback()
            end
         end,
      },
      sources = {
         { name = 'path' },
         { name = 'buffer' },
         { name = 'calc' },
         { name = 'nvim_lsp' },
         { name = 'nvim_lua' },
         { name = 'spell' },
         { name = 'treesitter' },
         { name = 'emoji' },
      },
      formatting = {
         format = function(entry, vim_item)
            -- fancy icons and a name of kind
            vim_item.kind = require("lspkind").presets.default[vim_item.kind] .. " " .. vim_item.kind
            -- set a name for each source
            vim_item.menu = ({
               path           = "[Path]",
               buffer         = "[Buffer]",
               calc           = "[Calc]",
               nvim_lsp       = "[LSP]",
               nvim_lua       = "[Lua]",
               spell          = "[Spell]",
               treesitter     = "[TS]",
            })[entry.source.name]
            return vim_item
         end,
      },
   }
end -- <<<

-- lspconfig >>>
M.lspconfig = function()
   local nvim_lsp = require('lspconfig')
   -- Keybinds for servers >>>
   -- Use an on_attach function to only map the following keys
   -- after the language server attaches to the current buffer
   local on_attach = function(client, bufnr)
      local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
      local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end

      -- Enable completion triggered by <c-x><c-o>
      buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')

      -- Mappings.
      local opts = { noremap=true, silent=true }

      -- See `:help vim.lsp.*` for documentation on any of the below functions
      buf_set_keymap('n', 'gD',         '<cmd>lua vim.lsp.buf.declaration()<CR>',                                 opts)
      buf_set_keymap('n', 'gd',         '<cmd>lua vim.lsp.buf.definition()<CR>',                                  opts)
      buf_set_keymap('n', 'K',          '<cmd>lua vim.lsp.buf.hover()<CR>',                                       opts)
      buf_set_keymap('n', 'gi',         '<cmd>lua vim.lsp.buf.implementation()<CR>',                              opts)
      buf_set_keymap('n', '<C-k>',      '<cmd>lua vim.lsp.buf.signature_help()<CR>',                              opts)
      buf_set_keymap('n', '<leader>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>',                        opts)
      buf_set_keymap('n', '<leader>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>',                     opts)
      buf_set_keymap('n', '<leader>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>',  opts)
      buf_set_keymap('n', '<leader>D',  '<cmd>lua vim.lsp.buf.type_definition()<CR>',                             opts)
      buf_set_keymap('n', '<leader>rn', '<cmd>lua vim.lsp.buf.rename()<CR>',                                      opts)
      buf_set_keymap('n', '<leader>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>',                                 opts)
      buf_set_keymap('n', 'gr',         '<cmd>lua vim.lsp.buf.references()<CR>',                                  opts)
      buf_set_keymap('n', '<leader>e',  '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>',                opts)
      buf_set_keymap('n', '[d',         '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>',                            opts)
      buf_set_keymap('n', ']d',         '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>',                            opts)
      buf_set_keymap('n', '<leader>q',  '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>',                          opts)
      buf_set_keymap('n', '<leader>f',  '<cmd>lua vim.lsp.buf.formatting()<CR>',                                  opts)
   end -- <<<
   -- Load servers >>>
   -- Use a loop to conveniently call 'setup' on multiple servers and
   -- map buffer local keybindings when the language server attaches
   local lsp_servers = {
      clangd         = 'clangd',
      html           = 'vscode-html-language-server',
      bashls         = 'bash-language-server',
   }
   for lsp, exe in pairs(lsp_servers) do
      if (vim.fn.executable(exe)) then
         nvim_lsp[lsp].setup {
            on_attach = on_attach,
            flags = {
               debounce_text_changes = 150,
            }
         }
      end
   end
   -- Custom servers
   -- lua >>>
   local sumneko_root_path
   local sumneko_binary
   if (vim.fn.exepath('lua-language-server') ~= '') then
      -- If you installed this with your package manager
      sumneko_root_path = '/usr/lib/lua-language-server/bin/Linux'
      sumneko_binary = vim.fn.exepath('lua-language-server')
   else
      -- If you installed this with lspinstall
      sumneko_root_path = vim.fn.getenv 'HOME' .. '/.local/share/nvim/lspinstall/lua/sumneko-lua/extension/server'
      sumneko_binary = sumneko_root_path .. '/lua-language-server'
   end

   -- From kickstart.nvim:
   if (vim.fn.executable(sumneko_binary) == true) then
      -- Make runtime files discoverable to the server
      local runtime_path = vim.split(package.path, ';')
      table.insert(runtime_path, 'lua/?.lua')
      table.insert(runtime_path, 'lua/?/init.lua')
   
      require('lspconfig').sumneko_lua.setup {
         cmd = { sumneko_binary, '-E', sumneko_root_path .. '/main.lua' },
         on_attach = on_attach,
         capabilities = capabilities,
         settings = {
            Lua = {
               runtime = {
                  -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
                  version = 'LuaJIT',
                  -- Setup your lua path
                  path = runtime_path,
               },
               diagnostics = {
                  -- Get the language server to recognize the `vim` global
                  globals = { 'vim' },
               },
               workspace = {
                  -- Make the server aware of Neovim runtime files
                  library = vim.api.nvim_get_runtime_file('', true),
               },
               -- Do not send telemetry data containing a randomized but unique identifier
               telemetry = {
                  enable = false,
               },
            },
         },
      }
   end
   -- lua <<<
   -- Load servers <<<
end -- <<<

return M

-- vim:fdm=marker:fmr=>>>,<<<:expandtab:tabstop=3:sw=3