Introducción

Empieza el punto vertebral para una configuración de este tipo. Cuesta creer lo importante que es el resaltado de sintaxis hasta que se trabaja sin ella.

Y, aprovechando que tenemos configurado AST, usaremos un LSP pora cada lenguaje, así además tendremos una revisión temprana del código. NeoVim casi como un IDE

Luasnip

Es un dependencia de blink-cmp que definiremos en su propio archivo:

-- ~/.config/nvim/lua/plugins/luasnip.lua

return {
	"L3MON4D3/LuaSnip",
	-- follow latest release.
	version = "v2.*", -- Replace <CurrentMajor> by the latest released major (first number of latest release)
	-- install jsregexp (optional!).
	build = "make install_jsregexp"
}

Treesitter

Se recomienda instalar el ejecutable de tree-sitter en el sistema, en el caso que vaya a usarse :TSInstallFromGrammar

npm install -g tree-sitter-cli
-- ~/.config/nvim/lua/config/treesitter.lua

return {
    highlight = {
      enable = true,        -- Habilita el resaltado de sintaxis basado en Tree-sitter
      --injections = true,    -- Se supone que activa custom injections
      additional_vim_regex_highlighting = false, -- Deshabilitar el resaltado adicional de expresiones regulares de Vim para prevenir resaltados duplicados y posibles ralentizaciones del rendimiento
    },
    indent = {
      enable = true         -- Habilita la indentación inteligente, puede seguir patrones específicos del language
    },
    fold = {
        enable = true
    },
    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",            -- Habilita plantillas HEEX como sigil ~H, aunque aún no reconoce el sigil en sí
      "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
      "markdown",
      "markdown_inline",    -- Importante para bloques de código
      "regex",              -- No sabía que lo necesitara hasta que me hizo demasiada falta
      "bash",               -- Pues si aún como de eso 
    },

}
-- ~/.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,        -- Este plugin debe iniciar con el sistema 
  config = function()
    require("nvim-treesitter.configs").setup(require("config.treesitter"))
  end,
}

Es posible que el siguiente error aparezca:

[] [12/16] Treesitter parser for rust has been installed
nvim-treesitter[typescript]: Error during compilation
cc1: error fatal: src/scanner.c: No existe el fichero o el directorio
compilación terminada.

No pude hallar más referencias que usar :TSUpdate, que dicho sea de paso, es la forma de actualizar todos sus parser y la posible solución a la mayoría de problemas (Que realmente son pocos) que puede presentar la herramienta

Si la idea de seguir una guía de configuración lo más sencilla posible, debo admitir que este facilmente podría ser el punto final. Al final, sería como usar un vim con un par de superpoderes, pero vim al fin y al cabo.

Pero, es altamente recomendable seguir con al menos el siguiente componente

Se configura antes que LSP porque este le necesita en su configuración

-- ~/.config/nvim/lua/config/blink-cmp.lua

return {
  snippets = {
    preset = "luasnip",             -- Usar LuaSnip como su fuente de snippets
    expand = function(snippet, _)
      return require("luasnip").lsp_expand(snippet)
    end,
  },
  appearance = {
    nerd_font_variant = "mono",
    use_nvim_cmp_as_default = true, -- Mantiene familiaridad visual
  },
  fuzzy = {
    implementation = "prefer_rust"
  },
  completion = {
    accept = {
      auto_brackets = {
        enabled = true              -- Soporte experimental para auto-paréntesis
      },
    },
    menu = {
      enabled = true,  -- Cambiar de función a booleano
      draw = {
        treesitter = { "lsp" },     -- Permite que Treesitter dibuje el menú de autocompletado LSP
        columns = { { "label", "label_description", gap = 1 }, { "kind_icon", "kind" } }
      },
    },
    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 = {
    default = { 'lsp', 'path', 'snippets', 'buffer' },
    providers = {
      lsp = { score_offset = 0 },
      path = { score_offset = 3 },
      buffer = { score_offset = -3 },
      snippets = { score_offset = -1 },
      cmdline = { score_offset = 2 },
    },
  },
  keymap = {
    preset = "default", -- Usa los atajos de teclado predeterminados de blink.cmp
  },
  cmdline = {
    enabled = true,
    sources = { 'cmdline' },  -- Aquí va ahora
    completion = {
      menu = { 
        auto_show = true  -- Simplificado
      }
    },
    keymap = {
      preset = 'inherit'  -- Hereda los mapeos del modo normal
    }
  },
}
-- ~/.config/nvim/lua/plugins/blink-cmp.lua

return {
  {
    "saghen/blink.cmp",
    version = "*",
    dependencies = {
      "L3MON4D3/LuaSnip",
      "rafamadriz/friendly-snippets",
    },
    config = function()
      require("luasnip.loaders.from_vscode").lazy_load()
      require("luasnip.loaders.from_vscode").lazy_load({ paths = { vim.fn.stdpath("config") .. "/snippets" } })
      require("blink.cmp").setup()
    end,
  },
}

LSP

La configuración de LSP es el siguiente mejor salto cualitativo que se puede hacer. Para ello, elegí usar mason y mason-lspconfig, los cuales parecen suficientes para muchos lenguajes (Una lista completa puede encontrarse acá). Para el caso de Elixir, uso ElixirLS por medio de elixir-tools.

-- ~/.config/nvim/lua/config/lsp.lua

return {
  -- Como por ahora solo estoy usando NVim para desarrollo de Elixir, esto es lo que se necesita además de elixir-tools
  ensure_installed = {"html", "rust_analyzer", "tailwindcss", "lua_ls", "bashls"},
  
  servers = {
    rust_analyzer = {
      settings = {
        ["rust-analyzer"] = {
          checkOnSave = { command = "clippy" },
          procMacro = { enable = true },
        },
      },
    },
    html = {
      filetypes = { "html", "heex" },
      init_options = {
        configurationSection = { "html", "css", "javascript" },
        embeddedLanguages = { css = true, javascript = true }
      }
    },
  },
  
}
-- ~/.config/nvim/lua/plugins/lsp.lua

return {
  "neovim/nvim-lspconfig",
  dependencies = {
    "williamboman/mason.nvim",
    "williamboman/mason-lspconfig.nvim",
  },
  config = function()
    local lsp_config = require("config.lsp")
    
    require("mason").setup()
    require("mason-lspconfig").setup({
      ensure_installed = lsp_config.ensure_installed,
      handlers = {
        function(server_name)
          local server_opts = lsp_config.servers[server_name] or {}
          server_opts.capabilities = require("blink.cmp").get_lsp_capabilities()
          
          require("lspconfig")[server_name].setup(server_opts)
        end,
      },
    })
  end,
}

Es podría ser otro final de la guía de configuración para muchos, con el cuidado de agregar los lenguajes que se necesiten para su pila de desarrollo. En mi caso, aún necesito del siguiente para mejorar sus capacidades en Elixir

elixir-tools

-- ~/.config/nvim/lua/config/elixir-tools.lua

return {
  nextls = {
    enable = false -- Nos aseguramos que este desactivo por el momento 
  },
  elixirls = {
    enable = true,
    settings =  {
      autoBuild = true,         -- Se encarga de compilar el proyecto si se guarda desde Neovim 
      suggestSpecs = true,      -- Sugiere anotaciones de tipo @specs
      fetchDeps = true,         -- Obtiene dependencias del proyecto cuando compila 
      dialyzerEnabled = true,   -- Esto podría desactivarse en algunos equipos legacy 
      enableTestLenses = true,  -- Habilita las lentes de código para ejecutar pruebas directamente desde el editor
  },
  projectionist = {
    enable = false
  },
  on_attach = function(_, 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" })
  end,
  },
}
-- ~/.config/nvim/lua/plugins/elixir-tools.lua

return {
  "elixir-tools/elixir-tools.nvim",
  version = "*",
  event = { "BufReadPre", "BufNewFile" },
  config = function(_, opts)
    require("elixir").setup(opts)
  end,
  dependencies = {
    "nvim-lua/plenary.nvim",
  },
}

Y con eso, NeoVim sigue siendo un editor de texto, pero con características de IDE.