Introducción

Ningún blog de este tipo estaría completo sin una guía de configuración de NeoVim. Y sí, sé que todas dicen que son “Guías básicas”, pero trataré en gran manera que esta si lo sea, al limitarse al primer acercamiento que yo mismo he hecho a la herramienta

La configuración parte de la idea de darle más capacidades a NeoVim, pero que siga siendo un editor de texto, no un IDE.

Además, actualmente estoy empezando a trabajar con Phoenix LiveView, así que le añadiré soporte para ello. Y para Rust, porque sí.

Sin embargo, voy a usar como referencia a LazyVim, que no es lo mismo que lazy.vim, aunque pareciera que sí.

Pasos previos

  • Instalar NeoVim
  • Instalar NerdFont
  • Instalar Alacritty
  • Configurar Alacritty
  • Asegurarse de tener instalador tanto elixir como erlang.

Creamos el esquema de directorio con

mkdir -p  ~/.config/nvim/lua/{plugins,config}

Configuración

De acá en adelante, todo será copiar y pegar archivos. La idea es copiar un archivo, cerrar nvim, y volverlo a abrir para aplicar cambios, así se trabaja una configuración progresiva

init.lua

Este archivo marca la entrada para nuestra configuración. Establece un par de configuraciones al inicio, pero en un momento, con require(“config.options”), incluye todas nuestras configuraciones desde el archivo lua/config/options.lua Verifica que Lazy este instalado, y después, carga los plugins y su configuración. Ese es uno de tantos enfoques.

cat <<MAFI> ~/.config/nvim/lua/init.lua
-- ~/.config/nvim/init.lua

-- Establece el líder de mapeo de teclas (usualmente la barra espaciadora)
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- Habilita los colores verdaderos en el terminal, esencial para los temas modernos
vim.opt.termguicolors = true

-- Carga las opciones generales del editor desde lua/config/options.lua
-- Esto asegura que las opciones como la indentación se apliquen temprano.
require("config.options")

-- Bootstrap lazy.nvim
-- Este bloque de código asegura que lazy.nvim esté instalado.
-- Si no lo está, lo clona desde GitHub.
local lazypath = vim.fn.stdpath("data").. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", -- última versión estable
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

-- Configura lazy.nvim para cargar tus plugins
-- La forma más robusta de cargar todos los plugins desde el directorio `lua/plugins/`
require("lazy").setup({
  spec = {
    -- Importa todas las especificaciones de plugins desde el directorio `lua/plugins/`
    { import = "plugins" },
  },
  -- Opciones adicionales para lazy.nvim
  checker = {
    enabled = true, -- Habilita la verificación automática de actualizaciones de plugins
    frequency = 86400, -- Frecuencia de verificación en segundos (cada 24 horas)
  },
})

MAFI

lua/config/options.lua

Acá guardamos la configuración más primaria. Algunas cosas, en mi caso relativenumber no las había probado y la verdad es que me sorprendió bastante una vez entendí su utilidad Al abrir este archivo veremos un ligero mensaje de error. Se resolverá en cuanto empecemos a configurar los plugins

cat <<MAFI> ~/.config/nvim/lua/config/options.lua
-- ~/.config/nvim/lua/config/options.lua

-- Opciones generales de Neovim
vim.opt.number = true             -- Muestra números de línea
vim.opt.relativenumber = true     -- Muestra números de línea relativos
vim.opt.mouse = "a"               -- Habilita el soporte del ratón en todos los modos
vim.opt.clipboard = "unnamedplus" -- Permite copiar/pegar desde/hacia el portapapeles del sistema
vim.opt.splitright = true         -- Abre nuevas ventanas divididas a la derecha
vim.opt.splitbelow = true         -- Abre nuevas ventanas divididas abajo
vim.opt.wrap = false              -- Deshabilita el ajuste de línea
vim.opt.scrolloff = 8             -- Mantiene 8 líneas de contexto al desplazarse
vim.opt.sidescrolloff = 8         -- Mantiene 8 columnas de contexto al desplazarse horizontalmente
vim.opt.hlsearch = true           -- Resalta todas las coincidencias de búsqueda
vim.opt.incsearch = true          -- Muestra las coincidencias de búsqueda a medida que se escribe
vim.opt.ignorecase = true         -- Ignora mayúsculas/minúsculas en la búsqueda
vim.opt.smartcase = true          -- No ignora mayúsculas/minúsculas si la búsqueda contiene mayúsculas
vim.opt.updatetime = 300          -- Tiempo en ms para escribir el archivo swap y disparar eventos CursorHold
vim.opt.signcolumn = "yes"        -- Muestra siempre la columna de signos (para diagnósticos, git, etc.)
vim.opt.undofile = true           -- Habilita el historial de deshacer persistente
vim.opt.undodir = vim.fn.stdpath("data").. "/undodir" -- Directorio para archivos de deshacer

-- Configuración de indentación: 4 espacios por defecto
vim.opt.tabstop = 4         -- Un tabulador se muestra como 4 espacios
vim.opt.shiftwidth = 4      -- Las operaciones de indentación usan 4 espacios
vim.opt.expandtab = true    -- Inserta espacios en lugar de tabuladores al presionar Tab
vim.opt.softtabstop = 4     -- Las teclas Tab/Backspace se comportan como si fueran 4 espacios
MAFI

lua/plugins/catppuccin.lua

Este tema me encanta. Es simple, y aún así sus cuatros versiones son bastante buenas, además de estar disponible para más software, como Alacritty

De igual forma, puede cambiarse a conveniencia.

cat <<MAFI> ~/.config/nvim/lua/plugins/catppuccin.lua
-- ~/.config/nvim/lua/plugins/catppuccin.lua

return {
  "catppuccin/nvim",
  name = "catppuccin", -- Nombre del plugin para lazy.nvim
  priority = 1000,     -- Asegura que el esquema de colores se cargue primero
  lazy = false,        -- Esencial: El esquema de colores no debe cargarse de forma perezosa
  opts = {
    flavour = "latte", -- Opciones: "latte", "frappe", "macchiato", "mocha"
    background = {     -- Configuración de fondo para modos claro/oscuro
      light = "latte",
      dark = "mocha",
    },
    transparent_background = false, -- Establecer en `true` si se desea un fondo transparente
    term_colors = true,             -- Habilita la configuración de colores del terminal
    compile = {
      enabled = true, -- Habilita la compilación del tema para un inicio más rápido
      path = vim.fn.stdpath("cache").. "/catppuccin", -- Ruta de caché para el tema compilado
    },
    integrations = { -- Integraciones con otros plugins para una tematización cohesiva
      cmp = true,       -- Integración con blink.cmp
      gitsigns = true,
      nvimtree = true,
      treesitter = true, -- Integración con nvim-treesitter
      bufferline = true, -- Integración con bufferline.nvim
      -- Añadir otras integraciones según los plugins instalados
    },
    -- custom_highlights = function(colors) -- Opcional: Sobrescribir grupos de resaltado específicos
    --   return {
    --     Comment = { fg = colors.flamingo, style = { "italic" } },
    --   }
    -- end,
  },
  config = function(_, opts)
    require("catppuccin").setup(opts)
    vim.cmd.colorscheme("catppuccin") -- Establece el esquema de colores
  end,
}
MAFI

lua/plugins/web-devicons.lua

Proporciona los íconos para bufferline y lualine. Tengo que ser sincero: Estas son casi que las opciones por defecto. En lo visual, esta configuración es lo esencial. Al menos me parece que si le pone íconos bonitos a casi todo.

cat <<MAFI> ~/.config/nvim/lua/plugins/web-devicons.lua
-- ~/.config/nvim/lua/plugins/web-devicons.lua

return {
  'nvim-tree/nvim-web-devicons',
  lazy = true, -- Se carga cuando lo necesiten otros plugins
  config = function()
    require('nvim-web-devicons').setup {
      -- Puedes personalizar los iconos aquí si lo deseas
      -- por ejemplo, para añadir iconos para tipos de archivo específicos
    }
  end,
}
MAFI

lua/plugins/treesitter.lua

Activa el resaltado de sintaxis para los lenguajes que están en ensure_installed. Por el momento, todo lo necesario para desarrollo web con Phoenix LiveView. Y Rust, porque sí.

cat <<MAFI> ~/.config/nvim/lua/plugins/treesitter.lua
-- ~/.config/nvim/lua/plugins/treesitter.lua

return {
  "nvim-treesitter/nvim-treesitter",
  build = ":TSUpdate", -- Instala y actualiza automáticamente los analizadores al instalar/actualizar el plugin
  lazy = false,        -- Esencial: Este plugin no debe cargarse de forma perezosa
  opts = {
    highlight = { enable = true, injections = true }, -- Habilita el resaltado de sintaxis basado en Tree-sitter
    indent = { enable = true },    -- Habilita la indentación inteligente basada en Tree-sitter
    ensure_installed = { -- Una lista de analizadores de lenguaje para asegurar que estén instalados
      "eex",            -- Para plantillas Elixir EEx
      "elixir",         -- Para código Elixir
      "heex",           -- Para plantillas Phoenix HEEx
      "rust",           -- Para código Rust
      "html",           -- Esencial para el desarrollo web
      "surface",        -- Creo que esto arreglo el problema con las plantillas heex embebidas en .ex
      "javascript",     -- Esencial para el desarrollo web
      "embedded_template",
      "typescript",     -- Esencial para el desarrollo web
      "css",            -- Esencial para el desarrollo web
      "json",           -- Para archivos de configuración y formatos de datos
      "lua",            -- Para archivos de configuración de Neovim
      -- Añadir otros lenguajes según sea necesario para su pila de desarrollo específica
    },
    -- Deshabilitar el resaltado adicional de expresiones regulares de Vim para prevenir resaltados duplicados y posibles ralentizaciones del rendimiento
    additional_vim_regex_highlighting = false,
  },
  config = function(_, opts)
    require("nvim-treesitter.configs").setup(opts)
  end,
}
MAFI

lua/plugins/lsp.lua

En este archivo hay dos cosas configuradas: mason y mason-lspconfig. Es acá donde nos encargamos de mantener un rastreo de los LSP que necesitamos. Y sí, aún son configurables

cat <<MAFI> ~/.config/nvim/lua/plugins/lsp.lua 
-- ~/.config/nvim/lua/plugins/lsp.lua

return {
  {
    "neovim/nvim-lspconfig",
    dependencies = {
      "williamboman/mason.nvim",        -- Gestor de paquetes para LSP, DAP, linters, etc.
      "williamboman/mason-lspconfig.nvim", -- Puente entre Mason y nvim-lspconfig
    },
    opts = {
      servers = {
        -- Configuración específica para rust_analyzer
        rust_analyzer = {
          settings = {
            ["rust-analyzer"] = {
              checkOnSave = {
                command = "clippy", -- Configura `rust-analyzer` para ejecutar Clippy para linting al guardar
              },
              procMacro = {
                enable = true, -- Habilita el soporte de macros procedimentales
              },
              -- Añadir otras configuraciones específicas de rust-analyzer aquí según sea necesario,
              -- como `cargo.allFeatures = true` para el conjunto completo de características
            },
          },
          -- Definir una función `on_attach` para atajos de teclado o configuraciones específicas de Rust
          on_attach = function(client, bufnr)
            -- Puedes añadir atajos de teclado específicos de Rust aquí
            -- Por ejemplo:
            -- vim.keymap.set("n", "<leader>ra", function() vim.cmd("RustLsp status") end, { buffer = bufnr, desc = "Rust Analyzer Status" })
            -- Asegúrate de que los atajos de teclado LSP estándar también se configuren
            -- Ejemplo de mapeos LSP comunes (puedes expandir esto)
            local opts = { noremap = true, silent = true, buffer = bufnr }
            vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
            vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
            vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
            vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
            vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
            vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, opts)
            vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, opts)
            vim.keymap.set('n', '<space>wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts)
            vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts)
            vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
            vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, opts)
            vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
            vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, opts)
          end,
        },
        -- elixir_ls no necesita una configuración directa aquí si elixir-tools.nvim lo gestiona
        -- pero se incluye en ensure_installed para que Mason lo instale.
      },
    },
    -- Esencial: Configurar mason-lspconfig para manejar la instalación y configuración de servidores LSP
    config = function(_, opts)
      require("mason").setup() -- Asegura que Mason esté configurado
      require("mason-lspconfig").setup({
        ensure_installed = {"html", "elixirls", "rust_analyzer", "tailwindcss" }, -- Usa "elixirls" para Mason
        handlers = {
          -- Manejador predeterminado para todos los servidores no especificados
          function(server_name)
            require("lspconfig")[server_name].setup(opts.servers[server_name] or {})
          end,
          -- Manejador personalizado para rust_analyzer
          rust_analyzer = function()
            require("lspconfig").rust_analyzer.setup(opts.servers.rust_analyzer)
          end,
          -- elixirls es gestionado por elixir-tools.nvim, por lo que no necesita un setup aquí
          elixirls = function() -- Asegúrate de que el nombre del manejador coincida con el de ensure_installed
            -- No hacer nada, elixir-tools.nvim se encargará de esto
          end,
        },
      })
      require('lspconfig').html.setup({
        filetypes = { "html", "heex", "ex", "elixir" },
        init_options = {
          configurationSection = { "html", "css", "javascript" },
          embeddedLanguages = {
            css = true,
            javascript = true
          }
        }
      })
      --require("elixir").setup({
      --  nextls = { enable = true },
      --  elixirls = { enable = false }, -- Desactiva este
      --})
      -- Configurar el cliente LSP globalmente para que blink.cmp obtenga las capacidades correctas
      vim.lsp.set_log_level("debug") -- Habilita el registro de depuración para LSP

      -- Configura las opciones de diagnóstico usando vim.diagnostic.config()
      vim.diagnostic.config({
        virtual_text = true,
        signs = true,
        underline = true,
        update_in_insert = false,
      })

      vim.lsp.config("*", {
        capabilities = require("blink.cmp").get_lsp_capabilities(),
        on_attach = function(client, bufnr)
          -- Aquí puedes añadir mapeos de teclas comunes para todos los LSP
          -- o llamar a una función de utilidad que los configure.
          -- Los mapeos específicos de Elixir y Rust se manejan en sus respectivos plugins.
          local opts = { noremap = true, silent = true, buffer = bufnr }
          vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
          vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
          vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
          vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
          vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
          vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, opts)
          vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, opts)
          vim.keymap.set('n', '<space>wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts)
          vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts)
          vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
          vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, opts)
          vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
          vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, opts)
        end,
      })
    end,
  },
}

MAFI

lua/plugins/bufferline.lua

Este es ornamental. Pero el crear una pestaña aún cuando solo hay un buffer abierto, para mí es todo lo que necesitaba desde hace tiempo.

cat <<MAFI> ~/.config/nvim/lua/plugins/bufferline.lua
-- ~/.config/nvim/lua/plugins/bufferline.lua

return {
  'akinsho/bufferline.nvim',
  version = "*", -- Usa la última versión estable
  dependencies = 'nvim-tree/nvim-web-devicons', -- Para iconos de tipo de archivo
  config = function()
    require('bufferline').setup({
      options = {
        mode = "buffers", -- Muestra todos los buffers abiertos. Cambiar a "tabs" para páginas de pestañas
        style_preset = "flat_slant", -- Usando la cadena literal del estilo
        diagnostics = "nvim_lsp", -- Muestra diagnósticos LSP (errores, advertencias) en las pestañas
        offsets = {
            filetype = "NvimTree", text = "File Explorer", text_align = "left"
        }, -- Evita que la línea de buffers se dibuje sobre los paneles laterales
        -- Puedes añadir más opciones aquí si son necesarias
      },
      -- Crucial: Integra los resaltados del tema Catppuccin.
      -- `get()` se asegura de que los colores de Catppuccin se apliquen correctamente.
      highlights = require("catppuccin.groups.integrations.bufferline").get()
    })
  end,
  -- Crucial: Asegura que bufferline se cargue *después* de catppuccin para una aplicación de tema correcta
  after = "catppuccin",
}
MAFI

lua/plugins/lualine.lua

Otro ornamental. Y sin embargo, creo que es algo que todos queremos una vez que lo vemos en algún podcast en youtube

cat <<MAFI>~/.config/nvim/lua/plugins/lualine.lua
-- ~/.config/nvim/lua/plugins/lualine.lua

return {
  'nvim-lualine/lualine.nvim',
  dependencies = { 'nvim-tree/nvim-web-devicons' }, -- Para iconos en la barra de estado
  config = function()
    require('lualine').setup({
      options = {
        icons_enabled = true,
        theme = 'catppuccin', -- Aplica el tema Catppuccin a Lualine
        component_separators = { left = '', right = ''}, -- Separadores estéticos para componentes
        section_separators = { left = '', right = ''}, -- Separadores estéticos para secciones
        always_divide_middle = true, -- Asegura que las secciones centrales no ocupen toda la línea de estado
        globalstatus = true, -- Muestra la línea de estado incluso cuando solo hay una ventana abierta
      },
      sections = {
        lualine_a = {'mode'}, -- Muestra el modo Vim actual
        lualine_b = {'branch', 'diff', 'diagnostics'}, -- Muestra la rama de Git, el estado del diff del archivo y los diagnósticos LSP
        lualine_c = {'filename'}, -- Muestra el nombre del archivo actual
        lualine_x = {'filetype', 'encoding', 'fileformat'}, -- Muestra el tipo de archivo, la codificación y el formato del archivo
        lualine_y = {'progress'}, -- Muestra el progreso dentro del archivo
        lualine_z = {'location'}, -- Muestra la línea y columna del cursor
      },
      -- Opcional: Configuración para la línea de pestañas, si prefiere que Lualine la gestione
      -- tabline = {
      --   lualine_a = {'buffers'},
      --   lualine_b = {},
      --   lualine_c = {},
      --   lualine_x = {},
      --   lualine_y = {},
      --   lualine_z = {'tabs'},
      -- }
    })
  end,
}
MAFI

lua/plugins/elixir-tools.lua

No recuerdo porque tuve que ponerlo en su propio archivo, pero funciona y por ahora es lo importante

cat <<MAFI> ~/.config/nvim/lua/plugins/elixir-tools.lua
-- ~/.config/nvim/lua/plugins/elixir-tools.lua

return {
  "elixir-tools/elixir-tools.nvim",
  version = "*", -- Usa la última versión estable
  event = { "BufReadPre", "BufNewFile" }, -- Carga el plugin cuando se lee un buffer de Elixir o se crea uno nuevo
  dependencies = { "nvim-lua/plenary.nvim" }, -- Una dependencia de utilidad común para muchos plugins Lua
  config = function()
    local elixir = require("elixir")
    local elixirls = require("elixir.elixirls")
    elixir.setup {
      nextls = { enable = true }, -- Habilita Next LS, un servidor de lenguaje Elixir más nuevo
      elixirls = {
        enable = true,
        settings = elixirls.settings {
          dialyzerEnabled = false, -- Ejemplo: Deshabilita las verificaciones de Dialyzer para una retroalimentación más rápida
          enableTestLenses = true, -- Habilita las lentes de código para ejecutar pruebas directamente desde el editor
        },
        on_attach = function(client, bufnr)
          -- Atajos de teclado personalizados para operaciones de tubería específicas de Elixir
          vim.keymap.set("n", "<space>fp", ":ElixirFromPipe<cr>", { buffer = bufnr, noremap = true, desc = "Elixir: From Pipe" })
          vim.keymap.set("n", "<space>tp", ":ElixirToPipe<cr>", { buffer = bufnr, noremap = true, desc = "Elixir: To Pipe" })
          vim.keymap.set("v", "<space>em", ":ElixirExpandMacro<cr>", { buffer = bufnr, noremap = true, desc = "Elixir: Expand Macro" })

          -- Puedes añadir más mapeos o configuraciones aquí que dependan del LSP de Elixir
        end,
      },
      projectionist = { enable = true }, -- Habilita la integración con vim-projectionist para el scaffolding de archivos
    }
  end,
}
MAFI

Y este es el plugin que escogí para el autocompletado. Hasta ahora, ha funcionado bien

cat <<MAFI> ~/.config/nvim/lua/plugins/blink-cmp.lua
-- ~/.config/nvim/lua/plugins/blink-cmp.lua
return {
  {
    "saghen/blink.cmp",
    version = "*", -- Se recomienda usar una etiqueta de versión estable, por ejemplo, '1.*'
    dependencies = {
      "L3MON4D3/LuaSnip", -- Motor de snippets
      "rafamadriz/friendly-snippets", -- Colección de snippets para LuaSnip
      -- "saghen/blink.compat", -- Opcional: Capa de compatibilidad para fuentes de nvim-cmp si es necesario
    },
    opts = {
      snippets = {
        preset = "luasnip", -- Configura blink.cmp para usar LuaSnip como su fuente de snippets
        expand = function(snippet, _)
          return require("luasnip").lsp_expand(snippet) -- Función para expandir snippets LSP
        end,
      },
      appearance = {
        -- Configuración de apariencia, por ejemplo, para la alineación de iconos con Nerd Fonts
        nerd_font_variant = "mono", -- Ajusta el espaciado para asegurar que los iconos estén alineados
      },
      completion = {
        accept = {
          auto_brackets = { enabled = true }, -- Soporte experimental para auto-paréntesis
        },
        menu = {
          draw = {
            treesitter = { "lsp" }, -- Permite que Treesitter dibuje el menú de autocompletado LSP
          },
        },
        documentation = {
          auto_show = true, -- Muestra automáticamente la documentación
          auto_show_delay_ms = 200,
        },
        ghost_text = { enabled = true }, -- Muestra la finalización como texto fantasma
      },
      signature = { enabled = true }, -- Habilita el soporte de ayuda de firma (experimental)
      sources = {
        -- Configuración de proveedores de fuentes. LSP, buffer, path son incorporados
        providers = {
          lsp = { score_offset = 0 },      -- Prioridad para las sugerencias LSP
          snippets = { score_offset = -1 }, -- Prioridad para las sugerencias de snippets
          buffer = { score_offset = -3 },   -- Prioridad para las sugerencias del buffer
          path = { score_offset = 3 },      -- Prioridad para las sugerencias de ruta
          }
        },
      },
      keymap = {
        preset = "default", -- Usa los atajos de teclado predeterminados de blink.cmp
        -- Puedes personalizar los atajos de teclado aquí, por ejemplo:
        -- ["<C-y>"] = { "select_and_accept" }, -- Aceptar la sugerencia seleccionada
        -- = { "snippet_forward", "ai_accept", "fallback" }, -- Tab para avanzar en snippets o aceptar
        -- = { "snippet_backward", "fallback" }, -- Shift+Tab para retroceder en snippets
      },
    },
    config = function(_, opts)
      -- Carga LuaSnip y friendly-snippets
      require("luasnip.loaders.from_vscode").lazy_load()
      require("luasnip.loaders.from_vscode").lazy_load({ paths = { vim.fn.stdpath("config").. "/snippets" } })

      -- Configura blink.cmp
      require("blink.cmp").setup(opts)
    end,
  },
}
MAFI

Bonus

  • Es necesario instalar xclip para activar el portapapeles
  • Ante el mensaje de error LSP[elixirls][Warning] Formatter plugin Phoenix.LiveView.HTMLFormatter is not loaded and will be skipped. , es necesario compilar el proyecto por primera vez con mix compile