Simply put, I’d like to be able to begin typing the name of a component from somewhere else in a project and have it autocomplete from within TSX/JSX (specifically the embedded html part). Does anyone have a working configuration in which they’ve achieved this?
All I can get working are the emmet completions inside of the html:
Sharing the config is kind of weird on this one because I use Nix to bundle together the lua and plugins that I use in my neovim config.
And this is the full config as it would be built (and slightly cleaned up by hand for posting here):
require("catppuccin").setup({
flavour = "mocha",
})
local status, typescripttools = pcall(require, "typescript-tools")
if (not status) then return end
local status, whichKey = pcall(require, 'which-key')
if (not status) then return end
whichKey.setup {}
local options = {
encoding = "utf-8",
mouse = "a",
tabstop = 2,
shiftwidth = 2,
softtabstop = 2,
expandtab = true,
cmdheight = 1,
updatetime = 300,
tm = 500,
hidden = true,
splitbelow = true,
splitright = true,
signcolumn = "yes",
autoindent = true,
swapfile = false,
backup = false,
writebackup = false,
visualbell = true,
errorbells = true,
relativenumber = true,
number = true,
wrap = false,
termguicolors = true,
hlsearch = true,
incsearch = false,
}
for k, v in pairs(options) do
vim.opt[k] = v
end
vim.opt.clipboard:append("unnamedplus")
vim.cmd("syntax on")
vim.cmd("au BufNewFile,BufRead *.md set spell")
vim.g.mapleader = " "
vim.g.maplocalleader = " "
local status, formatter = pcall(require, "formatter")
if (not status) then return end
local status, cmp = pcall(require, "cmp")
if (not status) then return end
local status, lspkind = pcall(require, "lspkind")
if (not status) then return end
local tsBuiltin = require('telescope.builtin')
local status, autopairs = pcall(require, "nvim-autopairs")
if (not status) then return end
local status, APRule = pcall(require, "nvim-autopairs.rule")
if (not status) then return end
local status, APcond = pcall(require, "nvim-autopairs.conds")
if (not status) then return end
local status, colorizer = pcall(require, "colorizer")
if (not status) then return end
local status, lspsaga = pcall(require, 'lspsaga')
if (not status) then return end
lspsaga.setup({
lightbulb = {
enable = false
},
diagnostic = {
show_code_action = false
}
})
local status, startup = pcall(require, "startup")
if (not status) then return end
local status, luasnip = pcall(require, "luasnip")
if (not status) then return end
local status, reactSnippets = pcall(require, "vim-react-snippets")
if (not status) then return end
local status, lualine = pcall(require, "lualine")
if (not status) then return end
-- Who's gonna carry the boats?
local status, noice = pcall(require, 'noice')
if (not status) then return end
local status, notify = pcall(require, 'notify')
if (not status) then return end
local status, glow = pcall(require, "glow")
if (not status) then return end
local attach_keymaps = function(client, bufnr)
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lca", "<cmd>lua vim.lsp.buf.code_action()<CR>", {
noremap = true,
desc = "Code action",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgD", "<cmd>lua vim.lsp.buf.declaration()<CR>", {
noremap = true,
desc = "Goto declaration",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgd", "<cmd>lua vim.lsp.buf.definition()<CR>", {
noremap = true,
desc = "Goto definition",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {
noremap = true,
desc = "Goto implementation",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {
noremap = true,
desc = "Goto next diagnostic",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {
noremap = true,
desc = "Goto previous diagnostic",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgr", "<cmd>lua vim.lsp.buf.references()<CR>", {
noremap = true,
desc = "Goto references",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lgt", "<cmd>lua vim.lsp.buf.type_definition()<CR>", {
noremap = true,
desc = "Goto type definition",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lh", "<cmd>lua vim.lsp.buf.hover()<CR>", {
noremap = true,
desc = "Hover",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>ln", "<cmd>lua vim.lsp.buf.rename()<CR>", {
noremap = true,
desc = "Rename",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lsh", "<cmd>lua vim.lsp.buf.signature_help()<CR>", {
noremap = true,
desc = "Signature help",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lwa", "<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>", {
noremap = true,
desc = "Add workspace folder",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lwl", "<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>", {
noremap = true,
desc = "List workspace folders",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>lwr", "<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>", {
noremap = true,
desc = "Remove workspace folder",
silent = true
})
vim.api.nvim_buf_set_keymap(bufnr, "n", "F", "<cmd>lua vim.lsp.buf.format { async = true }<CR>", {
noremap = true,
desc = "Format",
silent = true
})
end
-- Enable lspconfig
local status, lspconfig = pcall(require, "lspconfig")
if (not status) then return end
local status, util = pcall(require, "lspconfig.util")
if (not status) then return end
local testForLSPBinaryOnPath = function(name, alternate)
local path = vim.fn.exepath(name)
if path == "" then
return alternate
end
return path
end
vim.g.formatsave = true
-- Enable formatting
--[[ format_callback = function(client, bufnr)
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
if vim.g.formatsave then
local params = require'vim.lsp.util'.make_formatting_params({})
client.request('textDocument/formatting', params, nil, bufnr)
end
end
})
end ]]--
default_on_attach = function(client, bufnr)
attach_keymaps(client, bufnr)
-- format_callback(client, bufnr)
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').default_capabilities(capabilities);
typescripttools.setup {
on_attach = default_on_attach,
}
-- Python config
lspconfig.pylsp.setup {
cmd = { testForLSPBinaryOnPath("pylsp", "/nix/store/cadm42vrp8g7r6l1wsw2d9g4rrxrdvxa-python3.11-python-lsp-server-1.12.0/bin/pylsp") }
}
lspconfig.rust_analyzer.setup {
cmd = { testForLSPBinaryOnPath("rust-analyzer", "/nix/store/0fdrbfqza9p1vwzccy1hb1pdbx55hags-rust-analyzer-2024-09-02/bin/rust-analyzer") },
settings = {
['rust-analyzer'] = {
diagnostics = {
enable = false;
}
}
}
}
-- HTML config
lspconfig.emmet_language_server.setup {
cmd = { testForLSPBinaryOnPath("emmet-language-server", "/nix/store/mfqqqz3fp87aaf1dhjjv9zr7hfqiy0p8-emmet-language-server-2.2.0/bin/emmet-language-server"), "--stdio" }
}
-- Zig config
lspconfig.zls.setup {
cmd = { testForLSPBinaryOnPath("zls", "/nix/store/93shjdfpz3pzvw9q5wbwsz0wp4v43gir-zls-0.13.0/bin/zls") },
}
-- Nix config
lspconfig.nixd.setup{
capabilities = capabilities;
on_attach = function(client, bufnr)
attach_keymaps(client, bufnr)
end,
cmd = { testForLSPBinaryOnPath("nixd", "/nix/store/wwn50pvri8909w78wnzr95spa0zgcj52-nixd-2.3.2/bin/nixd") },
filetypes = { 'nix' },
single_file_support = true,
root_dir = function(fname)
return util.root_pattern(unpack { '.nixd.json', 'flake.nix' })(fname) or util.find_git_ancestor(fname)
end,
}
lspconfig.lua_ls.setup {
capabilities = capabilities,
on_attach = default_on_attach,
cmd = { testForLSPBinaryOnPath("lua-language-server", "/nix/store/3fp5s0cydnczsh74ylsqni2c3kiapx4s-lua-language-server-3.10.6/bin/lua-language-server") },
on_init = function(client)
local path = client.workspace_folders[1].name
if not vim.loop.fs_stat(path .. '/.luarc.json') and not vim.loop.fs_stat(path .. '/.luarc.jsonc') then
client.config.settings = vim.tbl_deep_extend('force', client.config.settings, {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using
-- (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT'
},
-- Make the server aware of Neovim runtime files
workspace = {
checkThirdParty = false,
library = {
vim.env.VIMRUNTIME,
[vim.fn.expand('$VIMRUNTIME/lua')] = true,
[vim.fn.expand('$VIMRUNTIME/lua/vim/lsp')] = true
}
}
}
})
client.notify("workspace/didChangeConfiguration", { settings = client.config.settings })
end
return true
end
}
-- Go config
lspconfig.gopls.setup {
cmd = { testForLSPBinaryOnPath("gopls", "/nix/store/6nysag23irns7ldbsdrjwc1ckap1hqm6-gopls-0.16.2/bin/gopls") },
settings = {
gopls = {
analyses = {
unusedparams = true;
};
staticcheck = true;
};
};
}
function readAll(file)
local f = assert(io.open(file, "rb"))
local content = f:read("*all")
f:close()
return content
end
vim.cmd.colorscheme("catppuccin")
-- Treesitter config
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
},
indent = {
enable = true,
},
incremental_selection = {
enable = true,
},
}
-- Utilities for creating configurations
local fmt_util = require "formatter.util"
-- Provides the Format, FormatWrite, FormatLock, and FormatWriteLock commands
formatter.setup {
-- Enable or disable logging
logging = true,
-- Set the log level
log_level = vim.log.levels.WARN,
filetype = {
typescriptreact = {
require("formatter.filetypes.typescript").prettier,
},
typescript = {
require("formatter.filetypes.typescript").prettier,
},
javascriptreact = {
require("formatter.filetypes.javascript").prettier,
},
javascript = {
require("formatter.filetypes.javascript").prettier,
},
}
}
local has_words_before = function()
unpack = unpack or table.unpack
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
require("luasnip.loaders.from_snipmate").lazy_load({ paths = { "/nix/store/96hw8jhyrh5mb4iqndhjkv1sjln142br-snippets" } })
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
formatting = {
format = lspkind.cmp_format({with_text = true, maxwidth = 50})
},
completion = {
completeopt = 'menu,menuone,noinsert',
},
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping({
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
}),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
-- You could replace the expand_or_jumpable() calls with expand_or_locally_jumpable()
-- that way you will only jump inside the snippet region
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "path" },
}, {
{ name = "buffer" },
})
})
cmp.setup.cmdline(':', {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
})
})
-- blanko
vim.keymap.set('n', '<leader>ff', tsBuiltin.find_files, { desc = "Find Files" })
vim.keymap.set('n', '<leader>fg', tsBuiltin.live_grep, { desc = "Live Grep" })
vim.keymap.set('n', '<leader>fb', tsBuiltin.buffers, { desc = "Buffers" })
vim.keymap.set('n', '<leader>fh', tsBuiltin.help_tags, { desc = "Help Tags" })
autopairs.setup({
disable_filetype = { "TelescopePrompt" , "vim" },
})
--[[
autopairs.add_rules({
APRule("=", "")
:with_pair(APcond.not_inside_quote())
:with_pair(function(opts)
local last_char = opts.line:sub(opts.col - 1, opts.col - 1)
if last_char:match("[%w%=%s]") then
return true
end
return false
end)
:replace_endpair(function(opts)
local prev_2char = opts.line:sub(opts.col - 2, opts.col - 1)
local next_char = opts.line:sub(opts.col, opts.col)
next_char = next_char == " " and "" or " "
if prev_2char:match("%w$") then
return "<bs> =" .. next_char
end
if prev_2char:match("%=$") then
return next_char
end
if prev_2char:match("=") then
return "<bs><bs>=" .. next_char
end
return ""
end)
:set_end_pair_length(0)
:with_move(APcond.none())
:with_del(APcond.none())
})
]]
colorizer.setup()
require("true-zen").setup({
integrations = {
twilight = true
}
})
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
vim.g.loaded_netrwSettings = 1
vim.g.loaded_netrwFileHandlers = 1
require('neo-tree').setup({
hijack_netrw_behavior = 'open_current',
})
vim.keymap.set({'n', 't'}, '<A-d>', '<cmd>Lspsaga term_toggle<CR>', { desc = "Toggle the FLoating Terminal"})
vim.keymap.set("n", "<C-j>", "<Cmd>Lspsaga diagnostic_jump_next<CR>", {
noremap = false,
desc = "Jump to next diagnostic",
})
vim.keymap.set("n", "K", "<Cmd>Lspsaga hover_doc<CR>", {
noremap = false,
desc = "Show hover documentation",
})
vim.keymap.set("n", "cA", "<cmd>Lspsaga code_action<CR>", {
noremap = false,
desc = "Code action",
})
vim.keymap.set("n", "gd", "<Cmd>Lspsaga finder<CR>", {
noremap = false,
desc = "Find references",
})
vim.keymap.set("n", "gp", "<Cmd>Lspsaga preview_definition<CR>", {
noremap = false,
desc = "Preview definition",
})
vim.keymap.set("n", "gr", "<Cmd>Lspsaga rename<CR>", {
noremap = false,
desc = "Rename",
})
vim.keymap.set("i", "<C-k>", "<Cmd>Lspsaga signature_help<CR>", {
noremap = false,
})
startup.setup({theme = "dashboard"})
reactSnippets.lazy_load()
require("luasnip.loaders.from_snipmate").lazy_load({ paths = { "/nix/store/96hw8jhyrh5mb4iqndhjkv1sjln142br-snippets" } })
vim.keymap.set({"i"}, "<C-K>", function() luasnip.expand() end, {silent = true})
vim.keymap.set({"i", "s"}, "<C-L>", function() luasnip.jump( 1) end, {silent = true})
vim.keymap.set({"i", "s"}, "<C-J>", function() luasnip.jump(-1) end, {silent = true})
vim.keymap.set({"i", "s"}, "<C-E>", function()
if luasnip.choice_active() then
luasnip.change_choice(1)
end
end, {silent = true})
lualine.setup({
options = {
globalstatus = true,
theme = "catppuccin",
},
sections = {
lualine_x = {
{
require("noice").api.status.message.get_hl,
cond = require("noice").api.status.message.has,
},
{
require("noice").api.status.command.get,
cond = require("noice").api.status.command.has,
color = { fg = "#ff9e64" },
},
{
require("noice").api.status.mode.get,
cond = require("noice").api.status.mode.has,
color = { fg = "#ff9e64" },
},
{
require("noice").api.status.search.get,
cond = require("noice").api.status.search.has,
color = { fg = "#ff9e64" },
},
},
},
})
noice.setup({
lsp = {
-- override markdown rendering so that **cmp** and other plugins use **Treesitter**
override = {
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
["vim.lsp.util.stylize_markdown"] = true,
["cmp.entry.get_documentation"] = true,
},
},
-- you can enable a preset for easier configuration
presets = {
bottom_search = true, -- use a classic bottom cmdline for search
command_palette = true, -- position the cmdline and popupmenu together
long_message_to_split = true, -- long messages will be sent to a split
inc_rename = false, -- enables an input dialog for inc-rename.nvim
lsp_doc_border = true, -- add a border to hover docs and signature help
},
})
notify.setup({
background_colour = "#000000"
})
glow.setup({})
vim.api.nvim_create_augroup("Markdown", {
clear = true,
})
vim.api.nvim_create_autocmd({"FileType"}, {
pattern = "markdown",
callback = function ()
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>mdp', ':Glow<CR>', { desc = "Glow preview" })
end
})
vim.keymap.set("n", "<C-Down>", "<cmd>resize -2<cr>", {
noremap = false,
desc = "Decrease window height",
})
vim.keymap.set("n", "<C-Left>", "<cmd>vertical resize -2<cr>", {
noremap = false,
desc = "Decrease window width",
})
vim.keymap.set("n", "<C-Right>", "<cmd>vertical resize +2<cr>", {
noremap = false,
desc = "Increase window width",
})
vim.keymap.set("n", "<C-Up>", "<cmd>resize +2<cr>", {
noremap = false,
desc = "Increase window height",
})
vim.keymap.set("n", "<leader>F", ":Neotree filesystem reveal float<CR>", {
noremap = false,
desc = "Open filesystem",
})
vim.keymap.set("n", "<leader>K", "<Cmd>WhichKey<CR>", {
noremap = false,
desc = "Which Keys!",
})
vim.keymap.set("n", "<leader>bF", "<Cmd>Format<CR>", {
noremap = false,
desc = "Format current buffer!",
})
vim.keymap.set("n", "<leader>bf", "<cmd>bfirst<CR>", {
noremap = false,
desc = "First Buffer",
})
vim.keymap.set("n", "<leader>bk", "<cmd>bd<CR>", {
noremap = false,
desc = "Close buffer",
})
vim.keymap.set("n", "<leader>bl", "<cmd>blast<CR>", {
noremap = false,
desc = "Last Buffer",
})
vim.keymap.set("n", "<leader>bn", "<cmd>bnext<CR>", {
noremap = false,
desc = "Next Buffer",
})
vim.keymap.set("n", "<leader>bp", "<cmd>bprevious<CR>", {
noremap = false,
desc = "Previous Buffer",
})
vim.keymap.set("n", "<leader>bw", "<cmd>w<CR>", {
noremap = false,
desc = "Write buffer",
})
vim.keymap.set("n", "<leader>fmL", "<cmd>CellularAutomaton game_of_life<CR>", {
noremap = false,
desc = "Game of life",
})
vim.keymap.set("n", "<leader>fml", "<cmd>CellularAutomaton make_it_rain<CR>", {
noremap = false,
desc = "Make it rain",
})
vim.keymap.set("n", "<leader>gs", ":Neotree git_status reveal float<CR>", {
noremap = false,
desc = "Git Status",
})
vim.keymap.set("n", "<leader>hc", "<cmd>noh<CR>", {
noremap = false,
desc = "Clear hlsearch",
silent = true,
})
vim.keymap.set("n", "<leader>lb", ":Neotree buffers reveal float<CR>", {
noremap = false,
desc = "Open buffers",
})
vim.keymap.set("n", "<leader>qq", "<cmd>q<CR>", {
noremap = false,
desc = "Close nvim",
})
vim.keymap.set("n", "<leader>w=", "<C-W>=", {
noremap = false,
desc = "Balance windows",
})
vim.keymap.set("n", "<leader>wd", "<C-W>c", {
noremap = false,
desc = "Delete window",
})
vim.keymap.set("n", "<leader>wh", "<C-W>h", {
noremap = false,
desc = "Move to the left window",
})
vim.keymap.set("n", "<leader>wj", "<C-W>j", {
noremap = false,
desc = "Move to the bottom window",
})
vim.keymap.set("n", "<leader>wk", "<C-W>k", {
noremap = false,
desc = "Move to the top window",
})
vim.keymap.set("n", "<leader>wl", "<C-W>l", {
noremap = false,
desc = "Move to the right window",
})
vim.keymap.set("n", "<leader>wq", "<C-W>q", {
noremap = false,
desc = "Quit window",
})
vim.keymap.set("n", "<leader>wr", "<C-W>r", {
noremap = false,
desc = "Rotate windows",
})
vim.keymap.set("n", "<leader>ws", "<C-W>s", {
noremap = false,
desc = "Split window horizontally",
})
vim.keymap.set("n", "<leader>wv", "<C-W>v", {
noremap = false,
desc = "Split window vertically",
})
vim.keymap.set("n", "<leader>ww", "<C-W>w", {
noremap = false,
desc = "Toggle between open windows",
})
vim.keymap.set("n", "<leader>za", ":TZAtaraxis<CR>", {
noremap = false,
desc = "Ataraxis zen",
})
vim.keymap.set("n", "<leader>zf", ":TZFocus<CR>", {
noremap = false,
desc = "Focus zen",
})
vim.keymap.set("n", "<leader>zm", ":TZMinimalist<CR>", {
noremap = false,
desc = "Minimalist zen",
})
vim.keymap.set("n", "<leader>zn", ":TZNarrow<CR>", {
noremap = false,
desc = "Narrow zen",
})
vim.keymap.set("v", "<leader>zn", ":'<,'>TZNarrow<CR>", {
noremap = false,
})