Capitulo 17: LSP y Formatters

Por: Artiko
opencodeailspformatterscode-intelligence

Capitulo 17: LSP y Formatters

< Volver al Indice del Tutorial

OpenCode puede conectarse a Language Servers y ejecutar formatters automáticamente. Esto permite que el agente reciba diagnósticos del código en tiempo real y que todo el código generado siga las reglas de formato del proyecto. En este capítulo exploraremos en profundidad cómo configurar, personalizar y aprovechar al máximo ambas integraciones para obtener un flujo de trabajo donde el agente AI produce código que compila correctamente y cumple con los estándares de estilo desde la primera iteración.

LSP Integration (Experimental)

La integración con Language Server Protocol le da a OpenCode la misma inteligencia de código que tu IDE: errores de tipo, warnings, autocompletado y más. Los diagnósticos del LSP se pasan directamente al LLM como contexto adicional, lo que significa que el modelo no trabaja a ciegas sino con información real del compilador o intérprete del lenguaje.

Cómo Funciona

El flujo de comunicación entre OpenCode y el Language Server sigue un patrón bien definido:

  1. OpenCode detecta los Language Servers configurados en opencode.json
  2. Inicia el proceso del Language Server correspondiente al lenguaje del archivo
  3. Al editar o crear archivos, consulta al LSP por diagnósticos
  4. Los errores y warnings se incluyen automáticamente en el contexto del agente
  5. El LLM puede corregir problemas basándose en los diagnósticos reales del compilador
  6. El ciclo se repite hasta que no hay diagnósticos pendientes
sequenceDiagram
    participant Agent as OpenCode Agent
    participant LSP as Language Server
    participant File as Archivo

    Agent->>File: Escribe código
    Agent->>LSP: textDocument/didChange
    LSP->>LSP: Analiza código
    LSP-->>Agent: Diagnósticos (errores, warnings)
    Agent->>Agent: Incluye diagnósticos en contexto LLM
    Agent->>File: Corrige basándose en diagnósticos
    Agent->>LSP: textDocument/didChange
    LSP-->>Agent: Sin diagnósticos
    Agent->>Agent: Código correcto, continúa

Esta retroalimentación continua es lo que diferencia a OpenCode de otros asistentes que simplemente generan código y esperan que funcione. Con LSP activo, el agente tiene un ciclo de verificación automático que reduce drásticamente los errores en el código generado.

Lenguajes Soportados

La integración funciona con cualquier Language Server que implemente el protocolo LSP estándar. Esto significa que prácticamente todos los lenguajes modernos son compatibles:

LenguajeLanguage ServerPaquete
TypeScript/JavaScripttypescript-language-servertypescript-language-server
Pythonpylsppython-lsp-server
Gogoplsgolang.org/x/tools/gopls
Rustrust-analyzerrust-analyzer
JavajdtlsEclipse JDT Language Server
JuliaLanguageServer.jlLanguageServer
C/C++clangdclangd
Kotlinkotlin-language-serverkotlin-language-server
Rubysolargraphsolargraph
PHPintelephenseintelephense

No estás limitado a esta lista. Cualquier ejecutable que implemente el protocolo LSP sobre stdio puede integrarse con OpenCode. Si tu lenguaje favorito tiene un Language Server, es compatible.

Configuración

Agrega los Language Servers en tu archivo de configuración opencode.json. Cada entrada necesita al menos el comando que inicia el servidor:

{
  "lsp": {
    "typescript": {
      "command": "typescript-language-server",
      "args": ["--stdio"]
    },
    "python": {
      "command": "pylsp"
    },
    "go": {
      "command": "gopls",
      "args": ["serve"]
    },
    "rust": {
      "command": "rust-analyzer"
    }
  }
}

Cada clave bajo "lsp" es un identificador libre que tú eliges. Lo importante es que el command apunte al ejecutable correcto del Language Server y que los args incluyan los flags necesarios para que el servidor funcione en modo stdio.

Asegúrate de que el Language Server esté instalado y disponible en tu PATH antes de configurarlo. OpenCode intentará iniciar el proceso cuando detecte archivos del lenguaje correspondiente y reportará un error si no puede encontrar el ejecutable.

Herramienta LSP

El agente tiene acceso a una herramienta lsp que le permite interactuar directamente con el Language Server. Esta herramienta es experimental pero ya ofrece capacidades poderosas. El agente puede:

Esto significa que el agente no solo recibe errores pasivamente, sino que puede consultar activamente al Language Server para tomar mejores decisiones. Cuando el agente necesita refactorizar una función, primero busca todas las referencias, entiende el impacto del cambio y luego procede con la modificación.

Diagnósticos en el Contexto

Cuando el LSP detecta problemas, estos se inyectan automáticamente en el prompt del LLM. El formato es claro y estructurado para que el modelo pueda interpretarlo correctamente:

[LSP Diagnostics for src/utils/math.ts]
Error (line 15, col 3): Type 'string' is not assignable to type 'number'.
Warning (line 22, col 1): Variable 'result' is declared but never used.

El modelo recibe esta información junto con el código fuente, lo que le permite generar correcciones precisas en lugar de hacer suposiciones sobre qué podría estar mal. La combinación de código + diagnósticos reales produce correcciones mucho más acertadas que pedirle al modelo que “busque errores” en el código.

Formatters

OpenCode puede ejecutar formatters automáticamente después de editar archivos. Esto garantiza que el código generado por el agente respete las mismas reglas de formato que el resto del proyecto, eliminando la necesidad de correcciones manuales de estilo.

Nueva Configuración (v1.3.x)

A partir de las versiones recientes, la configuración de formatters cambió para soportar formatters nombrados con mayor flexibilidad. Ahora puedes definir formatters personalizados, desactivar los built-in y especificar extensiones de archivo por formatter:

{
  "formatter": {
    "prettier": {
      "disabled": true
    },
    "custom-prettier": {
      "command": ["npx", "prettier", "--write", "$FILE"],
      "extensions": [".js", ".ts", ".jsx", ".tsx"]
    },
    "gofmt": {
      "command": ["gofmt", "-w", "$FILE"],
      "extensions": [".go"]
    },
    "black": {
      "command": ["black", "$FILE"],
      "extensions": [".py"]
    }
  }
}

Cada formatter tiene un nombre identificador como clave. Las propiedades disponibles son:

Esta estructura permite tener múltiples formatters para diferentes lenguajes y desactivar selectivamente los que no necesitas. Por ejemplo, puedes desactivar el prettier built-in y reemplazarlo con tu propia configuración que usa npx para garantizar la versión correcta.

Formatters Populares

FormatterLenguajesComando
PrettierJS, TS, CSS, HTML, JSON, MD["npx", "prettier", "--write", "$FILE"]
BiomeJS, TS, JSON["npx", "biome", "format", "--write", "$FILE"]
BlackPython["black", "$FILE"]
gofmtGo["gofmt", "-w", "$FILE"]
rustfmtRust["rustfmt", "$FILE"]
dfmtD["dfmt", "$FILE"]
clang-formatC, C++["clang-format", "-i", "$FILE"]
ruffPython["ruff", "format", "$FILE"]
styluaLua["stylua", "$FILE"]

Nota: el soporte para dfmt fue agregado en la versión v1.2.6.

Flujo de Trabajo Automático

Cuando el agente edita un archivo, el formateo ocurre de forma transparente:

  1. El agente escribe los cambios en el archivo
  2. OpenCode detecta la extensión del archivo modificado
  3. Busca un formatter configurado para esa extensión
  4. Ejecuta el formatter sobre el archivo con $FILE reemplazado por la ruta real
  5. El resultado final es código correcto y bien formateado
flowchart LR
    A[Agente escribe código] --> B[Detecta extensión]
    B --> C{Formatter configurado?}
    C -->|Sí| D[Ejecuta formatter]
    C -->|No| E[Archivo sin cambios de formato]
    D --> F[Código formateado]
    F --> G[LSP analiza resultado]
    E --> G

El agente no necesita saber qué formatter se usa ni cómo configurarlo. OpenCode se encarga de todo el proceso automáticamente. Esto significa que puedes cambiar de Prettier a Biome o de Black a Ruff sin modificar nada en tu flujo de trabajo con el agente.

Múltiples Formatters por Extensión

Si configuras más de un formatter para la misma extensión, OpenCode ejecutará todos en el orden en que aparecen en la configuración. Esto es útil cuando quieres ejecutar un linter con fix automático antes del formatter:

{
  "formatter": {
    "eslint-fix": {
      "command": ["npx", "eslint", "--fix", "$FILE"],
      "extensions": [".ts", ".tsx"]
    },
    "prettier-ts": {
      "command": ["npx", "prettier", "--write", "$FILE"],
      "extensions": [".ts", ".tsx"]
    }
  }
}

En este ejemplo, primero se ejecuta ESLint para corregir problemas de linting y luego Prettier para el formateo final. El orden de ejecución sigue el orden de definición en el JSON.

Combinando LSP y Formatters

La combinación de ambas features crea un ciclo de retroalimentación poderoso que replica el flujo de trabajo de un desarrollador experimentado usando un IDE:

flowchart TD
    A[Agente genera código] --> B[Formatter formatea automáticamente]
    B --> C[LSP analiza código formateado]
    C --> D{Hay diagnósticos?}
    D -->|Sí| E[Diagnósticos se envían al LLM]
    E --> F[Agente corrige basándose en diagnósticos]
    F --> B
    D -->|No| G[Código correcto y formateado]

Este ciclo se ejecuta automáticamente sin intervención del usuario. El resultado es código que:

  1. Compila correctamente gracias a los diagnósticos del LSP
  2. Sigue las convenciones de estilo gracias al formatter
  3. No tiene errores de tipo porque el LSP reporta incompatibilidades
  4. Mantiene imports organizados si el formatter o linter lo soporta

Ejemplo Práctico: Proyecto TypeScript

Para un proyecto TypeScript típico, la configuración completa sería:

{
  "lsp": {
    "typescript": {
      "command": "typescript-language-server",
      "args": ["--stdio"]
    }
  },
  "formatter": {
    "prettier": {
      "command": ["npx", "prettier", "--write", "$FILE"],
      "extensions": [".ts", ".tsx", ".js", ".jsx", ".css", ".json"]
    }
  }
}

Con esta configuración, cuando el agente crea un componente React:

  1. Escribe el archivo .tsx
  2. Prettier formatea el código automáticamente
  3. El typescript-language-server analiza el código
  4. Si hay errores de tipo, el agente los recibe y corrige
  5. El ciclo continúa hasta que el código está limpio

Ejemplo Práctico: Proyecto Go

Para Go, la configuración es aún más simple porque gofmt es el estándar universal:

{
  "lsp": {
    "go": {
      "command": "gopls",
      "args": ["serve"]
    }
  },
  "formatter": {
    "gofmt": {
      "command": ["gofmt", "-w", "$FILE"],
      "extensions": [".go"]
    }
  }
}

gopls es extremadamente preciso con los diagnósticos y gofmt no tiene configuración, así que el resultado es siempre código Go idiomático.

Ejemplo Práctico: Proyecto Python

Python se beneficia enormemente de la combinación LSP + formatter porque los errores de tipo son frecuentes en código dinámico:

{
  "lsp": {
    "python": {
      "command": "pylsp"
    }
  },
  "formatter": {
    "black": {
      "command": ["black", "$FILE"],
      "extensions": [".py"]
    },
    "isort": {
      "command": ["isort", "$FILE"],
      "extensions": [".py"]
    }
  }
}

Aquí usamos dos formatters para Python: Black para el estilo de código e isort para organizar los imports. Ambos se ejecutan en secuencia después de cada edición.

Troubleshooting

El LSP no detecta errores

Verifica que el Language Server está instalado y accesible:

which typescript-language-server  # Debe devolver una ruta
which gopls                       # Debe devolver una ruta

Si el comando no se encuentra, instala el Language Server primero. OpenCode no puede iniciar un proceso que no existe en el PATH.

El formatter no se ejecuta

Revisa que la extensión del archivo coincide exactamente con las extensiones configuradas. La comparación incluye el punto: .ts, no ts. También verifica que el comando del formatter está disponible en el PATH o usa npx para ejecutar binarios de npm locales.

Diagnósticos desactualizados

Si los diagnósticos parecen no actualizarse, puede ser que el Language Server necesita tiempo para re-analizar el proyecto después de cambios grandes. Los Language Servers procesan cambios de forma asíncrona y proyectos grandes pueden tardar varios segundos en producir diagnósticos actualizados.

Beneficios Clave


Siguiente: Capitulo 18: IDE y ACP —>