return {
	-- competion
	"hrsh7th/nvim-cmp",
	event = { "InsertEnter", "LspAttach" },
	dependencies = {
		{ "hrsh7th/cmp-buffer" }, -- source for text in buffer
		{ "hrsh7th/cmp-path" }, -- source for file system paths
		{ "L3MON4D3/LuaSnip" }, -- snippet engine
		{ "saadparwaiz1/cmp_luasnip" }, -- for autocompletion
		{ "rafamadriz/friendly-snippets" }, -- useful snippets
		{ "onsails/lspkind.nvim" }, -- icons for cmp
	},
	config = function()
		local cmp = require("cmp")
		local lspkind = require("lspkind")
		local luasnip = require("luasnip")

		-- loads vscode style snippets from installed plugins (e.g. friendly-snippets)
		require("luasnip.loaders.from_vscode").lazy_load()
		require("luasnip.loaders.from_vscode").load({ paths = { "~/.config/nvim/snippets" } })
		luasnip.setup({})

		-- luasnip.filetype_extend("htmldjango", { "html" })

		cmp.setup({
			completion = {
				completeopt = "menu,menuone,preview,noselect",
			},

			window = {
				documentation = cmp.config.window.bordered({
					border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
				}),
				completion = cmp.config.window.bordered({
					border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
					winhighlight = "Normal:CmpPmenu,CursorLine:PmenuSel,Search:None",
				}),
			},

			snippet = { -- configure how nvim-cmp interacts with snippet engine
				expand = function(args)
					luasnip.lsp_expand(args.body)
				end,
			},

			mapping = cmp.mapping.preset.insert({
				["<C-j>"] = cmp.mapping.select_next_item({ behavior = cmp.ConfirmBehavior.Select }), -- next suggestion
				["<C-k>"] = cmp.mapping.select_prev_item({ behavior = cmp.ConfirmBehavior.Select }), -- prev suggestion
				["<C-S-J>"] = cmp.mapping.scroll_docs(4, { behavior = cmp.ConfirmBehavior.Select }), -- docs forward
				["<C-S-K>"] = cmp.mapping.scroll_docs(-4, { behavior = cmp.ConfirmBehavior.Select }), -- docs back
				["<C-Space>"] = cmp.mapping.complete(), -- show completion suggestions
				["<C-q>"] = cmp.mapping.abort(), -- close completion window
				["<CR>"] = cmp.mapping.confirm({ select = false }), -- autocomplete if selected
				-- Mapping for Tab key
				["<Tab>"] = cmp.mapping(function(fallback)
					if cmp.visible() then
						cmp.confirm({ behavior = cmp.ConfirmBehavior.Insert, select = true })
					else
						-- Otherwise, fallback (insert a tab character)
						require("neotab").tabout()
						-- fallback()
					end
				end, { "i", "s" }),

				-- Mapping for jumping in snippets
				["<C-p>"] = cmp.mapping(function(fallback)
					if require("luasnip").jumpable() then
						-- Jump backwards if inside a snippet
						require("luasnip").jump()
					else
						-- Otherwise, fallback (insert a tab character)
						fallback()
					end
				end, { "i", "s" }),

				-- Mapping for jumping backwards in snippets
				["<C-S-p>"] = cmp.mapping(function(fallback)
					if require("luasnip").jumpable() then
						-- Jump backwards if inside a snippet
						require("luasnip").jump()
					else
						-- Otherwise, fallback (insert a tab character)
						fallback()
					end
				end, { "i", "s" }),
			}),

			-- sources for autocompletion
			sources = cmp.config.sources({
				{ name = "nvim_lsp" }, -- lsp
				{ name = "luasnip" }, -- snippets
				{ name = "buffer" }, -- text within current buffer
				{ name = "path" }, -- file system paths
			}),

			formatting = {
				fields = { "kind", "abbr", "menu" },
				format = lspkind.cmp_format({
					mode = "symbol", -- show only symbol annotations
					maxwidth = 50, -- prevent the popup from showing more than provided characters (e.g 50 will not show more than 50 characters)
					-- can also be a function to dynamically calculate max width such as
					-- maxwidth = function() return math.floor(0.45 * vim.o.columns) end,
					ellipsis_char = "…", -- when popup menu exceed maxwidth, the truncated part would show ellipsis_char instead (must define maxwidth first)
					show_labelDetails = true, -- show labelDetails in menu. Disabled by default

					-- The function below will be called before any actual modifications from lspkind
					-- so that you can provide more controls on popup customization. (See [#30](https://github.com/onsails/lspkind-nvim/pull/30))
					before = function(entry, vim_item)
						return vim_item
					end,
				}),
			},
		})

		local opts = { silent = true }
		opts.desc = "next snippet placeholder"
		vim.keymap.set({ "i", "s" }, "<C-,>", function()
			luasnip.jump(1)
		end, opts)

		opts.desc = "previous snippet placeholder"
		vim.keymap.set({ "i", "s" }, "<C-.>", function()
			luasnip.jump(-1)
		end, opts)

		opts.desc = "LuaSnip unlink"
		vim.keymap.set({ "i", "n" }, "<C-s>", function()
			luasnip.unlink_current()
		end, opts)
	end,
}
