Volviendo a configurar Nvim desde cero
Llevo usando Vim como 10 años (Nvim desde hace 3 o 4 años) y mi config ha ido evolucionando con el paso del tiempo, he decidido volver a crearla desde cero, **hacer un remake** , como en los videojuegos.
Hacerlo desde cero me va a permitir hacer limpieza de muchas cosas que he ido arrastrando con el paso del tiempo.
Voy a abrir Nvim y según vaya notando que me faltan cosas las añado, así no meto cosas de más.
Por cierto, mi config antigua la puedes ver en el artículo de Configuración básica de Vim. También te recomiendo que eches un ojo a Plugins interesantes para Vim y Neovim por si quieres pillar algún plugin que no mencione aquí.
## Ajustes básicos
Bien empecemos con los ajustes básicos. Lo bueno de Nvim es que no hay que incluir las típicas **configuraciones antiguas de mierda** que a día de hoy no tienen mucho sentido. Que ya estamos en el 2026 y no en los 90, welcome to the future.
Aquí puedo decidir crear un fichero `init.vim` o `init.lua`. EL fichero `.vim` es el que se usa en vim y el otro es propio de Nvim. Yo voy a tirar por el de **lua** , ya que voy a tener toda mi config en ese lenguaje.
Me configuro **la tecla leader en la coma** , ya que es a lo que estoy acostumbrado. La tecla leader la uso luego para crear otros mapeos.
vim.g.mapleader = ","
Nvim por defecto **no muestra los números de cada línea** , los voy a añadir porque a veces los uso. Nvim te dice abajo a la derecha en la línea en la que estás, pero bueno, prefiero tenerlo activado sobre todo por si comparto pantalla o trabajo en pair programming con alguien para que me pueda señalar la línea. Es cierto que podría crear un mapeo para activarlo y quitarlo, pero paso, lo dejo activado por defecto y mantengo la config limpia, con menos cosas que recordar
vim.opt.number = true
Seguimos con **la configuración para las búsquedas**. Aquí voy a hacer que **las búsquedas no tengan en cuenta las mayúsculas y las minúsculas** , así es más cómodo. Va a ser raro el caso en el que necesite diferenciar entre mayúsculas y minúsculas.
vim.opt.ignorecase = true
Otro tema interesante de las búsquedas es que Nvim **por defecto hace highlight de las búsquedas, pero lo deja activo aunque te mueves por el fichero o hagas otra cosa**. Antaño en mi config de vim tenía puesto `set nohlsearch`. Ahora voy a probar a dejarlo activado, ya que en Nvim han metido la combinación de teclas de `Control` + `l` para borrar el highlight de los resultados (por debajo ejecuta `nohlsearch`.
Al parecer Nvim sigue usando los **swapfiles** para hacer recover. Nunca me han gustado, prefiero usar git. No me gusta que se creen esos ficheros por lo que lo desactivo.
vim.opt.noswapfile = true
vim.opt.nobackup = true
También voy a aumentar el número de líneas que deja siempre por debajo (o por arriba) cuando llegas al borde del scroll. Es decir, que cuando queden 4 líneas en la pantalla por debajo, o 4 por arriba empiece a hacer scroll dejando siempre ese margen de 4 líneas. Eso me gusta cuando hago scroll para ver un poco más del fichero y no tener el cursor siempre en la última o primera línea.
vim.opt.scrolloff = 4
Y la verdad es que de momento ya estaría. Pensaba que sería más config la verdad, pero la he reducido bastante.
## Plugins
Vamos con el meollo de los plugins. Aquí depende de en qué lenguajes programes y cuáles sean tus gustos.
Lo primero es usar un manager de plugins, para gestionar su instalación, actualización, etc. Yo antiguamente usaba Plug, luego me pasé a Packer pero lo dejaron de mantener y me pasé a Lazy pero no me gustaba tanto boilerplate y compilación y acabé en Paq, que es mucho más simple. Pero es un plugin pequeño y no tiene mucho mantenimiento.
Quiero algo simple, poner el nombre de plugins, que se instalen y poco más. Paso de configurar en qué punto el plugin arranca y ese tipo de cosas. Como mucho poder poner la rama a descargar del plugin o commit, por si hubiera algún bug, o comando a ejecutar tras instalación (algunos plugins lo requieren).
Por lo que veo ya existe una solución nativa de Nvim para gestionar plugins, por lo que la voy a probar. Voy a crear un fichero `plugins.lua` y a añadir los que yo uso.
vim.pack.add({
{ src = "https://github.com/nvim-treesitter/nvim-treesitter", version = 'main' },
"https://github.com/nvim-lualine/lualine.nvim",
"https://github.com/karb94/neoscroll.nvim",
"https://github.com/nvim-tree/nvim-tree.lua",
"https://codeberg.org/andyg/leap.nvim",
"https://github.com/junegunn/fzf",
"https://github.com/junegunn/fzf.vim",
"https://github.com/kylechui/nvim-surround",
"https://github.com/hrsh7th/nvim-cmp",
"https://github.com/hrsh7th/cmp-nvim-lsp",
"https://github.com/sbdchd/neoformat",
"https://github.com/folke/tokyonight.nvim"
})
require "configs.treesitter"
require "configs.lsp"
require "configs.lualine"
require "configs.nvim-tree"
require "configs.neoscroll"
require "configs.leap"
require "configs.fzf"
require "configs.neoformat"
require "configs.tokyonight"
Ya ves lo simple que es. Simplemente se ponen las URLs de los repos que quieras instalar y listo. Abajo hago `require` al nombre del fichero en el que tengo la configuración para cada plugin.
Vamos con la explicación de cada plugin y la configuración que tengo metida.
Diego López 3 sept 2024, 17:56 Permalink
Confieso que pocas veces he usado el debbuger en Javascript. Las veces que he podido hacer algo con el debugger en la consola he acabado dentro de código complicado o en sitios raros.
Se supone que lo configuro para que eso no pase, y además uso el botón de step out, pero no hay manera.
Tengo pendiente probar nvim-dap, pero la última vez que probé a configurarlo era un horror.
### LSP (autocompletado)
El **LSP** (Lenguaje Server Protocol) es de los plugins más importantes porque es lo que permite el **autocompletado** de código. Además tiene otras cosas esenciales como **renombrado** (de variables y funciones), **extracción de código** (por ejemplo refactor de una parte del código para extraer a una función nueva), **code actions** (auto-importado, refactors automáticos, etc), **navegación por el código** (ir a referencias, ir a definición, etc) y más cosas.
Lo que mola del LSP es que es un sistema estándar. Es decir, cada editor de código puede hacer uso del mismo LSP. De hecho muchos de los providers son los mismos que los que usa el vscode por lo que el autocompletado es el mismo.
Mi config actual es esta:
configs/lsp.lua
local cmp = require('cmp')
cmp.setup({
mapping = {
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.confirm({ select = true }) -- Seleccionar primera opción
else
fallback()
end
end, { 'i', 's' }),
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-p>'] = cmp.mapping.select_prev_item(),
['<Down>'] = cmp.mapping.select_next_item(),
['<Up>'] = cmp.mapping.select_prev_item(),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'buffer' },
{ name = 'path' },
},
formatting = {
format = function(entry, vim_item)
vim_item.menu = ({
nvim_lsp = "[LSP]",
buffer = "[Buffer]",
path = "[Path]",
})[entry.source.name]
return vim_item
end,
},
experimental = { ghost_text = false },
window = {
documentation = { border = "rounded" },
},
})
local lsp_servers = {
'bash',
'css',
'html',
'json',
'lua',
'pyright',
'svelte',
'typescript',
'yaml',
}
local lsp_capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities())
local function on_attach(client, bufnr)
local bufopts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
vim.keymap.set('n', '<leader>gd', vim.lsp.buf.definition, bufopts)
vim.keymap.set('n', '<leader>gr', vim.lsp.buf.references, bufopts)
vim.keymap.set('n', '<leader>gi', vim.lsp.buf.implementation, bufopts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, bufopts)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, bufopts)
vim.keymap.set('n', '<leader>e', function()
vim.diagnostic.open_float(nil, { focusable = true })
end, bufopts)
end
vim.lsp.config('lua', {
cmd = { 'lua-language-server' },
settings = {
Lua = {
diagnostics = { globals = { 'vim' } },
workspace = { library = vim.api.nvim_get_runtime_file("", true), checkThirdParty = false },
telemetry = { enable = false },
},
},
})
vim.lsp.config('typescript', {
cmd = { 'typescript-language-server', '--stdio' },
filetypes = { 'javascript', 'javascriptreact', 'typescript', 'typescriptreact' },
init_options = { preferences = { importModuleSpecifierPreference = 'relative' } },
})
vim.lsp.config('svelte', {
cmd = { 'svelte-language-server', '--stdio' },
filetypes = { 'svelte' },
settings = { svelte = { plugin = { typescript = { diagnostics = { enable = true } } } } },
})
vim.lsp.config('bash', {
cmd = { 'bash-language-server', 'start' },
})
vim.lsp.config('css', {
cmd = { 'vscode-css-language-server', '--stdio' },
filetypes = { 'css', 'scss', 'less' },
})
vim.lsp.config('html', {
cmd = { 'vscode-html-language-server', '--stdio' },
filetypes = { 'html' },
})
vim.lsp.config('json', {
cmd = { 'vscode-json-language-server', '--stdio' },
filetypes = { 'json' },
})
vim.lsp.config('pyright', {
cmd = { 'pyright-langserver', '--stdio' },
settings = {
python = { analysis = { autoSearchPaths = true, useLibraryCodeForTypes = true, diagnosticMode = 'openFilesOnly' } },
},
})
vim.lsp.config('yaml', {
cmd = { 'yaml-language-server', '--stdio' },
filetypes = { 'yaml' },
})
vim.lsp.enable(lsp_servers)
vim.o.signcolumn = 'yes'
vim.diagnostic.config({ virtual_text = false, underline = true })
vim.api.nvim_create_autocmd('CursorHold', {
callback = function()
vim.diagnostic.open_float(nil, { focusable = false })
end,
})
vim.cmd [[
augroup lsp_document_highlight
autocmd!
autocmd CursorHold,CursorHoldI * lua vim.lsp.buf.document_highlight()
autocmd CursorMoved * lua vim.lsp.buf.clear_references()
augroup END
]]
Verás que lo primero que hago es hacer un `require` de **cmp**. Y es que cmp es otro plugin que uso junto a LSP. Básicamente se trata de un plugin para crear ventanas flotantes de las que salen para autocompletar. Prefiero usar **cmp** y no la que viene con **Nvim** porque permite más personalización. Yo tengo una config que hace que se comporte igual que la del **vscode**.
Lo siguiente de la config, la parte de `mapping` es para crear los mismos mapeos que tiene el vscode, de tal forma que pueda usar las flechas del teclado para moverme entre las opciones del autocompletado y `Tab` para aceptar la selección.
Luego está la parte de `sources` en la que indico que quiero que el autocompletado se rellene con el LSP, con opciones del buffer (cualquier cosa abierta en el fichero, por ejemplo autocompletado de palabras o cosas de string a los que el LSP no llega) y **path** para que autocomplete rutas de ficheros o carpetas.
Luego sigue la parte de configuración del LSP como tal. Defino qué lenguajes quiero usar, y para cada tipo de fichero creo una config con el comando del LSP que se va a ejecutar. En el `on_attach` es donde defino el mapeo de teclas para cosas del LSP. Yo tengo los siguientes mapeos:
* `K` mayúscula y me abre una ventana flotante con el tipo de la variable en la que esté o descripción de la función sobre la que tenga el cursor.
* `,` `g` `d` para ir a la definición de la función o variable
* `,` `g` `r` para ir a las referencias, es decir, donde se usa la variable o función en la que esté
* `,` `g` `i` para ir a la implementación
* `,` `r` `n` para renombrar la función o variable sobre la que esté
* `,` `c` `a` code actions, es decir, acciones como extraer a función, refactors, importar, etc
* `,` `e` si la línea en la que estoy tiene un error me aparece en una ventana flotante para verlo mejor
Por último hay otras configs para que el **cmp** se abra correctamente en cada pulsación de tecla.
Por cierto, para encontrar **LSPs** para el lenguaje de programación que uses te recomiendo esta página. Es para lsp-config, que no lo uso, pero de todas formas me sirve para encontrar la mayoría de LSPs que existen.
### Treesitter
Treesitter Es el plugin que parsea la sintaxis. Antaño para colorear sintácticamente con colores los lenguajes de programación los editores usaban regex. Ahora se crean árboles de símbolos haciendo que sea mucho más preciso y que falle menos. Vamos, que mejora el coloreado de la sintaxis, la indentación, selección incremental (seleccionar toda la función, clase, etc).
Mi config es muy sencilla, simplemente lo arranco para los lenguajes que quiero:
local ts_parsers = {
"angular",
"css",
"html",
"javascript",
"lua",
"python",
"svelte",
"typescript",
"vue"
}
local nts = require("nvim-treesitter")
nts.install(ts_parsers)
### Lualine
Lualine Es la típica **barra de abajo** de Vim que te marca el modo (Insert, Normal, Visual), el fichero, etc. Este plugin pesa muy poco y funciona muy bien, no tengo queja.
require("lualine").setup {
options = {
theme = "auto"
},
sections = {
lualine_a = { "mode" },
lualine_b = {{'filename', path = 4}},
lualine_x = { "branch" },
lualine_y = { "encoding" },
lualine_z = { "location" }
}
}
En `sections` indicas qué cosas quieres en la barra inferior. Yo tengo puesto el mood actual, nombre de fichero (con path = 4 para que ponga también el directorio padre), rama, encoding y localización (línea actual del fichero y posición dentro de ella).
### Nvim-tree
Nvim-tree es un plugin para tener el árbol de carpetas y ficheros del proyecto en el que estés. Hace mucho tiempo usaba NERDTree pero me cambié aquí. Además es muy cómodo porque tiene implementado un sistema de atajos para crear fichero, renombrar, copiar, etc. Los atajos aparecen cuando pulsas las teclas `g` `?`
require("nvim-tree").setup({
view = {
adaptive_size = true,
},
renderer = {
icons = {
show = {
file = false,
folder = false,
folder_arrow = true,
git = false,
},
glyphs = {
folder = {
arrow_closed = "›",
arrow_open = "⌃",
}
}
}
}
})
Con `adaptive_size` permites que el espacio en el que aparece el árbol se adapte para que siempre quepan los nombres de los ficheros, ya que sin eso se corta y no sabes el nombre del fichero sobre el que estás.
Además tengo quitado los iconos ya que no tengo fuente de iconos.
### Neoscroll
Neoscroll no lo tenía, lo estoy probando. Permite hacer scroll suave al hacer los típicos atajos de avanzar verticalmente por el documento. En mi caso me viene muy bien porque cuando hago la típica combinación de `Control` `f` me pierdo, no sé dónde estoy. En cambio con la animación veo lo que me he desplazado.
require('neoscroll').setup({ mappings = {'<C-u>', '<C-d>', '<C-b>', '<C-f>'} })
Lo tengo para los atajos básicos, pero se puede personalizar mucho más.
### Leap
Leap sirve para dar saltos por el fichero con un atajo muy cómodo. Es rollo `vim-sneak`, `vim-lightspeed` y ese tipo de plugins. Pulsas la tecla `s` y empiezas a escribir los dos primeros caracteres del sitio en pantalla al que quieras ir.
local leap = require('leap')
leap.opts.labels = 'sfnjklhodweimbuyvrgtaqpcxz'
leap.opts.safe_labels = 'sfnut'
Yo lo tengo configurado para usar solo letras minúsculas (cuando lo usas y pones dos caracteres con varias coincidencias, te aparece una letra a pulsar para ir a cada una de ellas, ahí prefiero que sea minúscula).
Por cierto, para que funcione tienes que añadir los keybindings, yo lo tengo puesto en la tecla `s`
vim.keymap.set({'n', 'x', 'o'}, 's', '<Plug>(leap)')
vim.keymap.set('n', 'S', '<Plug>(leap-from-window)')
### FZF
Es el típico plugin buscador de ficheros a lo `Control` + `P` de otros editores. Aquí hay quien usa **Telescope** , pero yo prefiero FZF. Por cierto, puedes usar FZF en la terminal de Linux, fuera de Nvim, para crearte buscadores de ficheros en la terminal.
vim.g.fzf_layout = { down = '40%' }
vim.g.fzf_preview_window = { 'right,40%', 'ctrl-/' }
-- Fzf to search files mapped to Ctrl+P (ignores .gitignore files)
vim.keymap.set('n', '<C-P>', ':GFiles --cached --others --exclude-standard<CR>', { silent = true })
vim.keymap.set('n', '<C-U>', ':Buffers<CR>', { silent = true })
-- Search text inside project files using Ctrl+T
vim.keymap.set('n', '<C-T>', ':Rg<CR>', { silent = true })
vim.cmd([[
command! -bang -nargs=* Rg
\ call fzf#vim#grep(
\ 'rg --column --line-number --no-heading --color=always --smart-case -- '.shellescape(<q-args>), 1,
\ fzf#vim#with_preview({'options': '--delimiter : --nth 4..'}), <bang>0)
]])
Lo tengo configurado para que con `Control` `P` se abra en la parte inferior de la pantalla, ignorando los ficheros definidos en el `.gitignore` y con una previsualización del fichero seleccionado antes de abrirlo.
Con `Control` + `U` se abre un buscador igual pero solo de los ficheros que tengo abiertos (los buffers abiertos).
Además tengo puesto la combinación `Control` + `T` para crear un buscador para el contenido de todos los ficheros del código usando Ripgrep (lo tienes que tener instalado de antes).
### Neoformat
Neoformat sirve para tener un formatter de código en Nvim. En mi caso lo tengo configurado con Prettier para que formatee y arregle el fichero automáticamente al guardar:
-- Use formatters from node_modules/.bin (project-local prettier)
vim.g.neoformat_try_node_exe = 1
vim.g.neoformat_enabled_javascript = { 'prettier' }
vim.g.neoformat_enabled_typescript = { 'prettier' }
vim.g.neoformat_enabled_javascriptreact = { 'prettier' }
vim.g.neoformat_enabled_typescriptreact = { 'prettier' }
vim.g.neoformat_enabled_json = { 'prettier' }
vim.g.neoformat_enabled_css = { 'prettier' }
vim.g.neoformat_enabled_scss = { 'prettier' }
vim.g.neoformat_enabled_html = { 'prettier' }
vim.g.neoformat_enabled_markdown = { 'prettier' }
vim.g.neoformat_enabled_yaml = { 'prettier' }
-- Format on save
vim.api.nvim_create_augroup('fmt', { clear = true })
vim.api.nvim_create_autocmd('BufWritePre', {
group = 'fmt',
pattern = '*',
command = 'undojoin | Neoformat'
})
### Tokionight
Voy cambiando de tema cada pocos meses, pero Tokionight es de los que más me gustan.
require('tokyonight').setup({
style = 'night',
})
vim.cmd('colorscheme tokyonight')
Diego López 19 sept 2022, 19:17 Permalink
hace tiempo estuve unos cuantos meses con el tema claro en el nvim (solarized), pero me volví a pasar a tema oscuro (tokionight), ¿habrá llegado la hora de volver a tema claro hasta que me vuelva a cansar?
yo ni light ni dark, el que sea hasta que me aburra
## Atajos y otras configs propias
-- Open nerdtree with ,nt and ,nf
vim.api.nvim_set_keymap('n', '<Leader>nt', ':NvimTreeToggle<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<Leader>nf', ':NvimTreeFindFile<CR>', { silent = true })
-- Capital Y to copy to the end of the line like C or D
vim.keymap.set('n', 'Y', 'y$')
-- Make window navigation less painful
vim.keymap.set('', '<C-h>', '<C-w>h')
vim.keymap.set('', '<C-j>', '<C-w>j')
vim.keymap.set('', '<C-k>', '<C-w>k')
vim.keymap.set('', '<C-l>', '<C-w>l')
-- To avoid undo points when using arrow keys
vim.keymap.set('i', '<Left>', '<C-g>U<Left>')
vim.keymap.set('i', '<Right>', '<C-g>U<Right>')
Con `,` `n` `t` abro **NvimTree** (esto lo arrastro desde que usaba NERDTree). Además con `,` `n` `f` abro **NvimTree** pero justo en el fichero en el que estoy para no tener que buscarlo en el árbol.
Luego tengo una config que vi en un vídeo de Youtube para que la `Y` mayúscula para copiar funcione igual que `C` o `D`.
También tengo una config para moverme por los splits y ventanas abiertas usando `Control` + `hjkl`. Esto lo uso por ejemplo si tengo **NvimTree** abierto para cambiar el focus entre el código y el árbol de ficheros usando `Control` + `h` y `Control` + `l`
Por último tengo un truquito para que al usar las teclas de dirección (confieso que las uso bastante) no se me creen puntos de undo points.