637 - Neovim y Copilot. Simplemente brutal

637 - Neovim y Copilot. Simplemente brutal

Utilizar la #ia en #neovim de la mano de #copilot con un complemento no oficial implementado en #lua y que se integra perfectamente con Neovim

1:25
-3:15

Hace unos días, ya adelanté en el grupo de Telegram de atareao con Linux], que había encontrado un complemento de Copilot para Neovim, que funcionaba realmente mejor que el complemento oficial. La cuestión es que llevo al menos un año utilizando diferentes herramientas de inteligencia artificial para ayudarme con el autocompletado de código, pero la realidad, es que con el que más cómodo me siento es con Copilot. En principio, yo lo atribuyo a la integración que tiene con los distintos editores, pero, la verdad es que para lo que lo utilizo funciona perfectamente bien. Y sin embargo, la integración con Neovim, para mi gusto, no terminaba de funcionar como a mi me gusta. Hasta que encontré el complemento que os traigo hoy. Y es que, no solo funciona mejor, sino que además, es más ligero y eficiente. Y lo mejor de todo, es que es Open Source. Un complemento para utilizar Copilot con Neovim que se integra perfectamente con el complemento de completado cmp, del que también te hablaré en este episodio.

Open Source/Software Privativo

Sobre el Open Source y el Software Privativo, puedes encontrar todo tipo de opiniones e ideas, que si uno es mejor que el otro o al revés. Y todo ello perfectamente justificado. Por ejemplo, mi archi enemigo Rfog, del podcast Leña al mono, siempre echa por tierra el Open Source, diciendo que nunca podrá competir con el Software Privativo.
Desde mi punto de vista, se trata, simplemente de dos modelos de negocio. Y creo que son dos modelos de negocio diferentes para el desarrollador, para el creador de ese software. Pienso, que estos modelos de desarrollo, son dos enfoques distintos de gestionar una creación.
Vas a encontrar software que utiliza el modelo de negocio del Software Privativo, que es muy bueno, y vas a encontrar software que utiliza el modelo de negocio del Open Source, que también es muy bueno. Y no creo que el software vaya a ser mejor o peor por utilizar uno u otro. Es mas, estoy seguro que alguno de estos desarrollos tienen éxito con uno de los modelos y con el otro nunca lo hubieran tenido.
Ahora caigo, por ejemplo, en el modelo del crowdfounding para financiar un proyecto, pues es algo muy similar. Aunque evidentemente, el Open Source tiene otras implicaciones, que una simple campaña de crowdfounding, pero es para que te hagas una idea de lo que intento transmitir.

Background

Llegados a este punto, es posible, ¿Porque utilizar Copilot?. Pues lo cierto, es que se trata de una herramienta, como puede ser un editor de texto como Microsoft Word, o un traductor como Google Translator. En el primer caso se trata de un software, y en el segundo caso, de un servicio. Pero ambos son herramientas. En el caso de Copilot, al igual que en el caso de otras aplicaciones de inteligencia artificial, no son mas que herramientas que nos ayudan en nuestro día a día, y en este sentido hay que aprender a utilizarlas y exprimirlas al máximo.
Soy consciente, que se ha hablado muy mal de este tipo de herramientas, pero es como cualquier otra herramienta que llega a perturbar nuestra tranquilidad, cualquier innovación que implica un cambio. Básicamente cualquier cosa que represente un cambio es, inicialmente, una incomodidad. Pero, como todo, hay que aprender a utilizarlo, y a sacarle el máximo partido. Y en este sentido, Copilot es una herramienta que nos puede ayudar a ser más productivos, a ser más eficientes, y a ser mejores programadores, escritores o lo que quieras hacer utilizando esta herramienta.
Y por otro lado, también es posible que te estés preguntado ¿Para que estoy utilizando Copilot?. Pues lo cierto es que lo estoy utilizando como un ayudante, tanto para escribir artículos, guiones de podcast o vídeos, como para programar. Y en este sentido, me ayuda a ser más productivo, a ser más eficiente, y a ser mejor en lo que hago. Y es por eso que lo utilizo.
Desde el punto de vista personal, lo he implantado tanto en Obsidian como en Neovim, para poder exprimirlo al máximo, y es precisamente en Neovim, donde mas lo he utilizado, en su faceta de ayudante a la programación. Básicamente para ayudarme con el completado, porque esto es algo que hace fantásticamente, dado el funcionamiento de este tipo de herramientas.

Hasta ahora

Hasta ahora, he estado utilizando la versión oficial de Copilot para Neovim . Sin embargo, me encontraba con distintos y absurdos problemas referentes a la integración de la versión oficial con otros complementos del ecosistema de Neovim, que vengo utilizando de antes de la llegada de Copilot. Y es que, la versión oficial de Copilot para Neovim, no terminaba de funcionar como a mi me gustaba.
El problema básico es el completado. En general, como cualquier otro editor, utilizo tab para realizar el completado, pero por falta de integración, tuve que utilizar ->, la flecha a derecha.
Además tenía dos sugerencias de completado que funcionaban de forma completamente distinta. Por un lado la de Copilot que me mostraba en gris la sugerencia, y el resto de sugerencias que ofrece cmp, que es el complemento que utilizo para el completado, que utiliza un menú contextual.
Sinceramente, me gusta mucho mas la solución del menú contextual, el resultado es mucho mas interesante.

El complento perfecto

Hace unos días leyendo sobre desarrollo de TypeScript con Neovim, creo recordar que fue, encontré que se hablaba de una herramienta de un desarrollador que daba la funcionalidad de Copilot, pero con algunas claras ventajas sobre la oficial. Se trataba de,

  • zbirenbaum/copilot.lua
    Este complemento, al contrario de lo que sucede con el complemento oficial, no utiliza Nodejs, sino que utiliza lua, y por otro lado, se integra perfectamente con cmp, que es el complemento que utilizo para el completado.
    Me llamó la atención que un desarrollador, ajeno a GitHub, hubiera desarrollado un complemento que funcionaba mejor que el oficial. Y me puse a probarlo. Y efectivamente funciona realmente mucho mejor. Y esta es la explicación del propio desarrollador, para haber creado su propia versión,

Mientras usaba copilot.vim, por primera vez desde que comencé a usar neovim, mi computadora portátil comenzó a sobrecalentarse. Además, encontré inquietantes los grandes fragmentos de texto fantasma que se movían alrededor de mi código e interferían con mi texto fantasma cmp existente. Como lua es mucho más eficiente y facilita la integración con complementos modernos, se creó este repositorio.

La configuración con Lazy

La configuración de este complemento con Lazy es realmente sencilla. Tan solo tienes que utilizar lo siguiente,

{
  "zbirenbaum/copilot.lua",
  cmd = "Copilot",
  event = "InsertEnter",
  config = true
}

Por otro lado, la configuración por defecto es la siguiente, que en cualquier caso, puedes adaptarla a tus necesidades cambiando aquellas opciones que no te gusten,

require('copilot').setup({
  panel = {
    enabled = true,
    auto_refresh = false,
    keymap = {
      jump_prev = "[[",
      jump_next = "]]",
      accept = "<CR>",
      refresh = "gr",
      open = "<M-CR>"
    },
    layout = {
      position = "bottom", -- | top | left | right
      ratio = 0.4
    },
  },
  suggestion = {
    enabled = true,
    auto_trigger = false,
    hide_during_completion = true,
    debounce = 75,
    keymap = {
      accept = "<M-l>",
      accept_word = false,
      accept_line = false,
      next = "<M-]>",
      prev = "<M-[>",
      dismiss = "<C-]>",
    },
  },
  filetypes = {
    yaml = false,
    markdown = false,
    help = false,
    gitcommit = false,
    gitrebase = false,
    hgcommit = false,
    svn = false,
    cvs = false,
    ["."] = false,
  },
  copilot_node_command = 'node', -- Node.js version must be > 18.x
  server_opts_overrides = {},
})

panel

Puedes utilizar un panel para previsualizar las sugerencias en una ventana, para lo que tienes que utilizar :Copilot panel

Tipos de archivos

Es posible especificar los tipos de archivos a los que queremos que responda Copilot. Por ejemplo, para los archivos de variables de entorno lo podemos deshabilitar

Integraciones

Esta versión no oficial, tiene distintas integraciones, y la primera de ellas, la que actualmente esteoy utilizando, es la integración con el complemento de completado cmp.

  • zbirenbaum/copilot-cmp: Integration with nvim-cmp.
  • AndreM222/copilot-lualine: Integration with lualine.nvim.
    La segundo, la correspondiente a la barra de estado lualine no la he utilizado porque no utilizo esta barra, pero, sin embargo, me ha servido de orientación, para saber como personalizar feline que es la que utilizo.

Sobre cmp

Esto de los complementos para el completado, es todo un mundo en Neovim, y puedes encontrar casi todo. Durante bastante tiempo estuve utilizando coc, pero desde que se comenzó a utilizar lua como lenguaje de scripting en Neovim, poco a poco he ido cambiando los distintos complementos, reemplanzado los que utilizan otros lenguajes por los que utilizan lua.
Pero, que ventajas e inconvenientes tiene lua frente a coc

  • Dependencia de terceros: coc depende de nodejs, cmp no depende. Y el tiempo de ejecución de coc utiliza mucha memoria, digamos 500 MB.
  • Listo para usar: coc es una solución todo en uno y lista para usar. mientras que cmp en realidad necesita trabajar con nvim-lspconfig, mason.nvim, null-ls.nvim, mason-lspconfig.nvim, mason-null-ls.nvim, lsp-kind.nvim, neoconf.nvim para ser realmente como coc.
  • Rendimiento: uniforme, pero un caso extremo es que cuando instalo decenas de servidores LSP en Mason, cmp a veces se atasca. No estoy seguro de si coc tiene una mejor estrategia asíncrona para evitar siempre quedarse atascado.
  • Comunidad: cmp win, hoy el soporte de la comunidad neovim es excelente en lua y lsp. Hace un año que no uso coc, no estoy seguro.
    Este repositorio transforma https://github.com/zbirenbaum/copilot.lua en una fuente cmp.
    Las sugerencias de Copilot se cargan automáticamente en su menú cmp como fragmentos y mostrarán su contenido completo cuando se coloque el cursor sobre una sugerencia de copilot.
    Configuración con Lazy,
{
  "zbirenbaum/copilot-cmp",
  config = function ()
    require("copilot_cmp").setup()
  end
}

Resaltado e iconos

Para cmp existe otro complemento mas, conocido como lspkind que permite personalizar como se muestra en el menú contextual las distintas opciones de completado. Por ejemplo, para añadir un icono en las propuestas de Copilot, lo puedes hacer de la siguiente forma,

-- lspkind.lua
local lspkind = require("lspkind")
lspkind.init({
  symbol_map = {
    Copilot = "",
  },
})
vim.api.nvim_set_hl(0, "CmpItemKindCopilot", {fg ="#6CC644"})

O también de esta otra foma,

-- cmp.lua
cmp.setup {
  ...
  formatting = {
    format = lspkind.cmp_format({
      mode = "symbol",
      max_width = 50,
      symbol_map = { Copilot = "" }
    })
  }
  ...
}

Configuración del autocompletado

A diferencia de otras fuentes de completado, este complemento, puede utilizar otras líneas, encima o debajo de la línea en la que estamos trabajando para propornernos el completado. Esto puede causar problemas. Este comportamiento se puede modificar a través de la configuración de cmp.

local has_words_before = function()
  if vim.api.nvim_buf_get_option(0, "buftype") == "prompt" then return false end
  local line, col = unpack(vim.api.nvim_win_get_cursor(0))
  return col ~= 0 and vim.api.nvim_buf_get_text(0, line-1, 0, line-1, col, {})[1]:match("^%s*$") == nil
end
cmp.setup({
  mapping = {
    ["<Tab>"] = vim.schedule_wrap(function(fallback)
      if cmp.visible() and has_words_before() then
        cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
      else
        fallback()
      end
    end),
  },
})

Por otro lado, en cmp, se puede utilizar un comparador personalizado para ordenar las entradas de cmp: priorizar. El comparador de prioridades hace que las entradas del copiloto aparezcan más arriba en el menú cmp. Se recomienda mantener el peso de prioridad en 2 o colocar el comparador exacto encima del copiloto para que las mejores coincidencias de lsp no queden atrapadas debajo de las coincidencias deficientes del copiloto.

cmp.setup {
  ...
  sorting = {
    priority_weight = 2,
    comparators = {
      require("copilot_cmp.comparators").prioritize,

      -- Below is the default comparitor list and order for nvim-cmp
      cmp.config.compare.offset,
      -- cmp.config.compare.scopes, --this is commented in nvim-cmp too
      cmp.config.compare.exact,
      cmp.config.compare.score,
      cmp.config.compare.recently_used,
      cmp.config.compare.locality,
      cmp.config.compare.kind,
      cmp.config.compare.sort_text,
      cmp.config.compare.length,
      cmp.config.compare.order,
    },
  },
  ...
}

feline

Y por último, la barra de estdo Feline, que utilizo, he tenido que adaptarla para que me muestre información de la situación de Copilot. Esto se puede hacer de la siguiente forma. Primero tienes que añadir estas líneas para poder hacer uso,

        local copilot_client = lazy_require("copilot.client")
        local copilot_api = lazy_require("copilot.api")
        local is_current_buffer_attached = function()
            return copilot_client.buf_is_attached(vim.api.nvim_get_current_buf())
        end

Y posteriormente, ya en la definición de la propia barra de estado tendrás que configurar esto otro,

        -- copilot
        components.active[3][2] = {
          provider = function()
                if copilot_client.is_disabled() or not is_current_buffer_attached() then
                    return ''
                end
                local data = copilot_api.status.data.status
                local msg = ""
                if data == "Warning" then
                    msg = "⚠"
                elseif data == "InProgress" then
                    msg = "⌛"
                end
                return "" .. " " .. msg
          end,
          hl = {
            fg = 'cyan',
            bg = colors.bg,
            style = 'bold'
          },
          right_sep = ' '
        }
        -- fileIcon

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *