local M = {}

-- lsp_signature >>>
M.signature = function()
   require "lsp_signature".setup()
   cfg = {
      Gse_lspsaga = true
   }
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
            -- How to read these next keybinds
            -- first character:
            --   [ means previous
            --   ] means next
            -- second character:
            --   m means function beginning
            --   M means function end
            -- OR
            --   both the same means start in that direction
            --   ex: [[ means previous start
            --   both in opposite directions means end in that way
            --   ex: [] means previous end
            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 vim.fn.pumvisible() == 1 then
               vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-n>', true, true, true), 'n')
            else
               fallback()
            end
         end,
         ['<S-Tab>'] = function(fallback)
            if vim.fn.pumvisible() == 1 then
               vim.fn.feedkeys(vim.api.nvim_replace_termcodes('<C-p>', true, true, true), 'n')
            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')

   -- 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

   -- 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',
      sumneko_lua    = 'lua-language-server',
      bashls         = 'bash-language-server',
   }
   for lsp, exe in pairs(lsp_servers) do
      if (vim.fn.executable(exe) == 1) then
         nvim_lsp[lsp].setup {
            on_attach = on_attach,
            flags = {
               debounce_text_changes = 150,
            }
         }
      end
   end
end -- <<<

return M

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