Saltar a contenido

Plugin LSP (servidor de lenguaje)

Inteligencia de código impulsada por el Language Server Protocol —líneas onduladas de error, navegación, información al pasar el puntero, autocompletado, ayuda de firma, formateo, buscar referencias, renombrar e ir al símbolo— para cualquier lenguaje que tenga un servidor de lenguaje. El editor habla con el servidor por LSP; el plugin es el cliente. Qué servidores existen viene dado por datos (un pequeño JSON por lenguaje), así que C++/clangd, más abajo, es solo una de varias definiciones incluidas.

El editor no incluye ni descarga servidores

Un servidor de lenguaje es un programa aparte que instalas una vez. MTE detecta un servidor instalado y lo apunta a tu proyecto; nunca incluye ninguno. Consulta Conseguir un servidor de lenguaje.

Funciones actuales

Función Cómo
Diagnósticos Los errores y las advertencias del servidor aparecen como subrayados ondulados (rojo = error, ámbar = advertencia, azul = información, gris = sugerencia), actualizados mientras escribes.
Ir a la definición Pon el cursor sobre un símbolo y pulsa F12, o usa Buscar ▸ Navegación por el código ▸ Ir a la definición. Salta a la definición, abriendo otro archivo si hace falta.
Buscar referencias Cursor sobre un símbolo → Shift+F12 (o Buscar ▸ Navegación por el código ▸ Buscar referencias). Los resultados se abren en un panel inferior de Referencias, agrupados por archivo con una vista previa de la línea de origen; haz doble clic para saltar.
Información al pasar el puntero Deja el puntero sobre un símbolo; una ventana emergente muestra su tipo o firma y su documentación (dibujada a partir del Markdown del servidor).
Autocompletado Ctrl+Space pide terminaciones al servidor y las funde en la ventana emergente de autocompletado del editor (por delante de las sugerencias integradas de palabras clave y del búfer), filtradas mientras escribes.
Ayuda de firma Escribir el ( o una , de una llamada muestra un aviso con la firma de la función y el parámetro activo resaltado; ) lo descarta.
Formatear el documento Alt+Shift+F (⌃⌥F en macOS; o Editar ▸ Formatear el documento) vuelve a formatear todo el documento a través del servidor, en un único paso de deshacer. El estilo lo gobierna la configuración del propio servidor (para clangd, un archivo .clang-format).
Buscar referencias (listado arriba) —
Cambiar el nombre del símbolo Cursor sobre un símbolo → Shift+F6 (o Editar ▸ Cambiar el nombre del símbolo). Introduce un nombre nuevo; se actualizan todos los usos. Cuando el renombrado abarca varios archivos se te pide confirmación primero: cada archivo se abre, se cambia y se deja modificado para que lo revises y lo guardes.
Ir al símbolo (en el archivo) Ctrl+Shift+O abre la paleta de comandos con todos los símbolos del archivo actual (funciones, clases, métodos…); escribe para filtrar, Intro salta. Cada fila muestra el tipo de símbolo y su contenedor.
Ir al símbolo en el espacio de trabajo Ctrl+T busca símbolos por todo el proyecto mientras escribes (es el servidor el que hace coincidir la consulta); Intro abre el archivo en el símbolo.

Los selectores de ir al símbolo son los modos de símbolo de la paleta de comandos; consulta Paleta de comandos.

Los comandos aparecen atenuados hasta que hay un servidor en marcha

Ir a la definición, Buscar referencias, Formatear el documento y Cambiar el nombre del símbolo solo están activos cuando un servidor de lenguaje para el lenguaje del archivo actual está Listo, y cuando ese servidor informó en su saludo inicial de que admite esa petición. En un archivo sin servidor configurado, o mientras uno está arrancando, reiniciándose o se ha dado por vencido, las entradas de menú aparecen atenuadas en lugar de no hacer nada al pulsarlas. El indicador de estado de abajo te dice en cuál de esos casos estás. Reiniciar los servidores de lenguaje nunca se atenúa: es la salida cuando un servidor se ha dado por vencido.

Las referencias entre archivos y el renombrado necesitan el índice del servidor

Buscar referencias y Renombrar solo llegan a otros archivos una vez que el servidor ha indexado el proyecto. Para C/C++ eso significa un compile_commands.json (ver más abajo) y el índice en segundo plano de clangd; sin él operan únicamente sobre el archivo actual.

De momento, el renombrado deshace archivo por archivo

Un renombrado en varios archivos se aplica de uno en uno, así que cada archivo es su propio paso de deshacer (el diálogo de confirmación lo dice). Revisa las pestañas modificadas antes de guardar.

En el menú contextual del editor

Haz clic derecho sobre un símbolo del texto y el menú ofrece, debajo de los comandos de edición, Ir a la definición, Buscar referencias, Cambiar el nombre del símbolo y Formatear el documento. Cada uno aparece solo cuando el mismo comando del menú principal funcionaría en ese momento: hay un servidor de lenguaje listo para el archivo que lo admite y, para cambiar el nombre y formatear, el documento no es de solo lectura. En un archivo de texto sin formato, en el margen a la izquierda del texto o mientras el servidor se está iniciando, no aparece ninguno.

Las entradas actúan sobre el símbolo en el que hiciste clic derecho, no sobre el que está bajo el cursor; el cursor y la selección se quedan donde estaban. Cambiar el nombre propone la selección como nombre antiguo si hiciste clic derecho dentro de ella, y si no, la palabra bajo el puntero. Formatear el documento siempre formatea el documento entero. Un menú abierto desde el teclado actúa en la posición del cursor.

Donde la plataforma muestra los atajos de teclado en los menús contextuales (Windows), cada entrada muestra su atajo: el que está en vigor, incluidos tus propios cambios en la Asignación de atajos. En macOS, los menús contextuales no muestran atajos.

Cómo llegan tus ediciones al servidor

Escribir no envía un mensaje por pulsación. Las ediciones se agrupan durante una fracción de segundo y luego se envían como un único cambio, y como un único intervalo editado en lugar del archivo entero, siempre que el servidor diga que puede aceptarlo (la mayoría puede). Así, un archivo grande cuesta unas pocas decenas de bytes por ráfaga de escritura en vez de una copia de sí mismo.

Todo lo que pregunta al servidor sobre el texto —información al pasar el puntero, ir a la definición, buscar referencias, renombrar, formatear, autocompletar, ayuda de firma— envía primero la edición pendiente, de modo que una respuesta nunca se refiera a una versión que ya has dejado atrás. Guardar un archivo hace lo mismo.

Arrancar un servidor tampoco bloquea el editor: el proceso se lanza en segundo plano y el segmento de estado muestra … hasta que ha terminado su saludo inicial. Un binario de servidor que se cuelga al arrancar no te cuesta más que ese lenguaje.

Un techo para cuántos problemas se dibujan

Se dibujan como máximo 2000 líneas onduladas por documento. Más allá de eso el archivo es ilegible de todas formas, y cada marcador adicional le cuesta tiempo real al editor; así que, cuando un archivo produce más, se muestran las 2000 primeras y la barra de estado dice Mostrando los primeros 2000 de N problemas de este archivo, en vez de dejar que el recuento parezca en silencio menor de lo que es. Suele ser señal de que el servidor está mirando mal el archivo: una base de datos de compilación que falta, o un archivo generado que no está pensado para compilar por su cuenta.

El indicador de estado del servidor

La barra de estado muestra un segmento compacto LSP: (a la izquierda de los demás segmentos de plugins) que resume todos los servidores de lenguaje relevantes para tus archivos abiertos:

LSP: cpp ✓ · no compile database  python ↻2  rust ✗
Glifo Significado
… Arrancando — el servidor se ha lanzado y está haciendo el saludo inicial.
✓ Listo — atendiendo peticiones.
↻n Reiniciando — el servidor se detuvo de forma inesperada; hay programado un intento automático de reinicio número n.
✗ No hay servidor instalado para este lenguaje. Instala uno (ver más abajo) y luego usa Reiniciar los servidores de lenguaje.
⚠ Se ha dado por vencido — el servidor no dejaba de fallar y los reinicios automáticos se detuvieron. Solo un reinicio manual lo revive.

Un lenguaje puede llevar un breve calificador tras el glifo: el servidor está en marcha y algo de tu proyecto sigue limitando lo que puede responder. Solo se muestra uno a la vez, el más grave primero:

Calificador Significado
· server not responding Varias peticiones seguidas se quedaron sin respuesta. El servidor está vivo pero atascado; usa Mostrar el registro del servidor de lenguaje y después Reiniciar los servidores de lenguaje. El calificador desaparece en cuanto el servidor responde a algo.
· no compile database El servidor de C/C++ está en marcha, pero solo puede ver el archivo que tienes abierto; consulta Los proyectos de C/C++ necesitan compile_commands.json.
· no Cargo.toml, · no go.mod, · no tsconfig.json, … El proyecto no tiene ninguno de los archivos que este servidor necesita para entenderlo como proyecto. Sigue funcionando sobre el archivo abierto; las respuestas entre archivos serán incompletas hasta que ese archivo exista. El nombre exacto es el que más quiere el servidor; consulta Qué necesita cada servidor de tu proyecto.

El segmento aparece cuando se abre el primer documento con un lenguaje configurado, sigue solo los lenguajes de los que realmente tienes documentos abiertos —cerrar el último archivo de un lenguaje elimina su entrada (y abrir uno de nuevo la devuelve al instante)— y se oculta por completo cuando no queda ningún documento así.

Buscar ▸ Navegación por el código ▸ Mostrar el registro del servidor de lenguaje muestra la salida más reciente de todos los servidores en marcha. Ahí es donde un servidor se explica: una cadena de herramientas que falta, un archivo de configuración ilegible, un proyecto que se ha negado a cargar. La ventana es una ventana normal y redimensionable: cada lenguaje es un encabezado que nombra el servidor que está corriendo para él, el texto se puede seleccionar, Copiar pone todo el registro en el portapapeles y el tamaño que le des a la ventana se recuerda.

Si un servidor falla

Un servidor de lenguaje que termina de forma inesperada se reinicia automáticamente con un retardo creciente (1 s, 2 s, 4 s… con un tope de 30 s). Mientras eso ocurre, el indicador muestra ↻n y una notificación en la barra de estado informa del primer fallo. Tras un reinicio con éxito, tus documentos abiertos se vuelven a anunciar al servidor y los diagnósticos regresan solos; las líneas onduladas obsoletas del servidor muerto se borran de inmediato. La edición nunca se bloquea.

Tras 5 intentos fallidos seguidos, el plugin se da por vencido: el indicador muestra ✗, una notificación te remite a los ajustes y el lenguaje se queda a oscuras hasta que lo reinicies a mano. Un servidor que funcionó bien al menos durante un minuto recibe un lote nuevo de intentos cuando falla más adelante.

Reinicio manual — cualquiera de estos:

  • Buscar ▸ Navegación por el código ▸ Reiniciar los servidores de lenguaje (sin atajo predeterminado; disponible también en la paleta de comandos), o
  • el botón Volver a explorar o reiniciar los servidores de Preferencias ▸ Servidor de lenguaje.

Para ver por qué un servidor está descontento antes de reiniciarlo, usa Buscar ▸ Navegación por el código ▸ Mostrar el registro del servidor de lenguaje.

Ambos desmontan todos los servidores, vuelven a ejecutar la detección con los ajustes actuales y reinician el presupuesto de fallos; úsalos tras instalar un servidor que faltaba o tras arreglar uno roto.

Conseguir un servidor de lenguaje

Instala el servidor de tu lenguaje y asegúrate de que está en tu PATH (o define una ruta explícita en los ajustes). Si no se encuentra ninguno, la función simplemente no hace nada para ese lenguaje: el indicador de estado muestra ✗ y el registro anota una pista una única vez, pero nada te interrumpe.

MTE incluye definiciones de servidores (no los servidores), así que la mayoría solo necesita que el servidor esté instalado, sin editar ningún JSON. Las definiciones incluidas cubren C/C++, Objective-C, Python, Rust, Go, TypeScript, JavaScript, C#, Java, Kotlin, Swift, HTML, CSS/SCSS/Less, JSON, YAML y scripts de shell.

Lenguaje Servidor Dónde conseguirlo
C/C++ clangd winget install LLVM.LLVM (Windows), el componente «C++ Clang tools» del instalador de Visual Studio, brew install llvm (macOS) o el paquete clang-tools-extra de tu distribución.
Python pyright o pylsp npm i -g pyright (necesita Node.js), o pip install python-lsp-server (necesita solo Python).
TypeScript/JS typescript-language-server npm i -g typescript-language-server typescript.
Rust rust-analyzer rustup component add rust-analyzer.
Go gopls go install golang.org/x/tools/gopls@latest.
C# csharp-ls o OmniSharp dotnet tool install -g csharp-ls (una única herramienta de dotnet, que se prueba primero), o descarga una versión de OmniSharp.
Java jdtls (Eclipse JDT Language Server) brew install jdtls (macOS), o descomprime una versión y pon su bin en tu PATH.

Compruébalo en un terminal, p. ej. clangd --version o where clangd / which clangd. Para un lenguaje del que MTE no incluye definición, añade una como se describe en Añadir un lenguaje.

Python: pyright o pylsp

Python tiene dos definiciones incluidas, y OctoEdit ejecuta la que realmente esté instalada:

  1. pyright (pyright-langserver) se prueba primero. Es un paquete de npm, así que necesita Node.js: npm i -g pyright.
  2. pylsp (python-lsp-server) se usa cuando no se encuentra pyright. Es un paquete de pip y necesita solo Python: pip install python-lsp-server.

Si ambos están instalados, gana pyright. Hay una única fila de ajustes para Python (un indicador de activación, una anulación de ruta) y se aplica al servidor que se acabe usando: definir una ruta explícita a un ejecutable pylsp ejecuta pylsp con sus argumentos de arranque, no con los de pyright. Mostrar el registro del servidor de lenguaje nombra el servidor que está realmente en marcha.

Varios servidores para un lenguaje

Esto es un mecanismo general, no un caso especial de Python: un lenguaje puede tener cualquier número de definiciones incluidas, ordenadas por un campo priority, y la primera cuyo ejecutable se encuentre en tu máquina es la que arranca. C# funciona igual —csharp-ls primero, OmniSharp después— porque ambos toman argumentos de arranque distintos y solo uno de ellos puede ser el correcto.

Java: el espacio de trabajo propio del servidor

jdtls se niega a arrancar sin un directorio de espacio de trabajo propio en el que pueda escribir (-data). OctoEdit le proporciona uno automáticamente, por proyecto, bajo su carpeta de datos de plugin:

<user data>/plugins/org.mte.lsp/jdtls-workspaces/<project id>/

Nunca tienes que crearla ni darle nombre. Borrar la carpeta es seguro: jdtls la reconstruye, a costa de una reindexación.

Configuración en Windows

Una máquina Windows recién instalada no suele tener ninguno de clangd, cmake, ninja, node o python en su PATH. Esta sección dice de dónde sale cada uno y cómo comprobarlo. Normalmente no tienes que editar el PATH tú mismo: OctoEdit busca además en los directorios de ecosistema que se listan más abajo.

Lo que quieres Instalar Dónde acaba
clangd (C/C++) winget install LLVM.LLVM C:\Program Files\LLVM\bin\clangd.exe
cmake y ninja El componente «C++ CMake tools for Windows» del instalador de Visual Studio …\Microsoft Visual Studio\2022\<edition>\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin y …\CMake\Ninja — no están en tu PATH, pero se alcanzan a través del entorno de desarrollo de Visual Studio (ver más abajo). Un winget install Kitware.CMake / winget install Ninja-build.Ninja independiente sí los pone en el PATH.
pyright (Python) winget install OpenJS.NodeJS.LTS y después npm i -g pyright %APPDATA%\npm\pyright-langserver.cmd
pylsp (Python, sin necesidad de Node) Python de la Microsoft Store o de python.org, y después pip install python-lsp-server %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.3.*_*\LocalCache\local-packages\Python3*\Scripts\pylsp.exe (Store) o %APPDATA%\Python\Python3*\Scripts\pylsp.exe

Comprobar qué es visible. En un símbolo del sistema o en PowerShell normales:

where clangd
where cmake
where ninja
where pyright-langserver
where pylsp

where busca únicamente en tu PATH. Una herramienta que no encuentre puede seguir siendo hallada por OctoEdit, porque la búsqueda cubre también los directorios de ecosistema anteriores; los directorios de scripts de Python, en particular, nunca están en el PATH. Si un lenguaje sigue en ✗ tras instalar su servidor, usa Buscar ▸ Navegación por el código ▸ Reiniciar los servidores de lenguaje, que vuelve a ejecutar toda la búsqueda.

La generación de la base de datos de compilación se ejecuta dentro del entorno de desarrollo de Visual Studio. Cuando OctoEdit ejecuta CMake por ti (más abajo), primero localiza Visual Studio —mediante VSINSTALLDIR y, si no, con vswhere.exe— y envuelve la llamada en el VsDevCmd.bat -arch=x64 de esa instalación. Eso es lo que pone cl, el SDK de Windows y el propio cmake/ninja de Visual Studio en la ruta para ese único comando, de modo que el editor no los necesita en tu PATH en absoluto. Sin Visual Studio (una máquina solo con clang o con MinGW) la llamada se hace tal cual, en el propio entorno del editor, y el registro lo dice; entonces necesitas cmake y ninja accesibles por tu cuenta, y un compilador que CMake pueda identificar.

Regenerar bajo demanda. Buscar ▸ Navegación por el código ▸ Generar la base de datos de compilación… ejecuta la misma generación en cualquier momento, incluso cuando ya existe una base de datos (se reconfigura en el sitio) y cuando antes respondiste No preguntar para este proyecto. La entrada está siempre disponible: cuando no puede generar, dice por qué en la barra de estado (no hay documento de C/C++, no hay carpeta de proyecto, no hay CMakeLists.txt en la raíz del proyecto, no hay cmake, o no hay aquí ningún generador capaz de exportarla) en vez de aparecer atenuada por un motivo que no puedes ver.

Dónde se guarda la respuesta de «no preguntar». No preguntar para este proyecto se guarda por raíz de proyecto en %APPDATA%\7-bits\OctoEdit.ini, bajo plugins/org.mte.lsp/noCompileDbPrompt/<hash> (la ruta del proyecto se convierte en hash porque una ruta no es una clave de ajustes válida). Ejecutar Generar la base de datos de compilación… la borra para ese proyecto.

Dónde busca OctoEdit un servidor

No deberías tener que configurar nada. Tras instalar un servidor, basta con abrir una carpeta y un archivo fuente. En orden, el editor prueba:

  1. una ruta explícita que hayas definido en los ajustes —que manda: si no se resuelve, el lenguaje se queda a oscuras en lugar de recurrir en silencio a otra cosa;
  2. tu PATH, para el comando del servidor y cada alias conocido (clangd-20, clangd-19, …);
  3. las ubicaciones de instalación de la definición del servidor para tu sistema operativo;
  4. una lista integrada de directorios de ecosistema.
Directorios de ecosistema en los que se busca
Windows el prefijo global de npm y %APPDATA%\npm, %LOCALAPPDATA%\Microsoft\WinGet\Links, %USERPROFILE%\.cargo\bin, %USERPROFILE%\go\bin y %LOCALAPPDATA%\go\bin, %USERPROFILE%\.dotnet\tools, %ProgramFiles%\LLVM\bin, %ProgramFiles%\nodejs, y los directorios de scripts de Python por usuario: %APPDATA%\Python\Python3*\Scripts, %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.3.*_*\LocalCache\local-packages\Python3*\Scripts (Python de la Microsoft Store) y %LOCALAPPDATA%\Programs\Python\Python3*\Scripts
macOS /opt/homebrew/bin y /opt/homebrew/opt/llvm/bin (el clangd de Homebrew vive en el segundo: nunca se enlaza al primero), /usr/local/bin, /usr/local/opt/llvm/bin, ~/.cargo/bin, ~/go/bin, ~/.local/bin, ~/.npm-global/bin, ~/.dotnet/tools, ~/Library/Python/3.*/bin
Linux /usr/local/bin, /usr/lib/llvm*/bin, Linuxbrew, y los mismos ~/.cargo/bin, ~/go/bin, ~/.local/bin, ~/.npm-global/bin, ~/.dotnet/tools

Las entradas con * son comodines reales: el directorio de versión que crea Python (Python313, 3.13 o el …Python.3.13_qbz5n2kfra8p0 de la Store) se compara con lo que realmente hay en el disco, de más nuevo a más antiguo. Ahí es donde pip install python-lsp-server coloca pylsp, y por eso instalarlo no requiere tocar el PATH.

macOS: lanzar desde el Finder frente a hacerlo desde un terminal

Una aplicación iniciada desde el Finder o el Dock no hereda el PATH del perfil de tu intérprete de comandos: macOS le da uno mínimo (/usr/bin:/bin:/usr/sbin:/sbin). Homebrew, Cargo, Go y npm serían todos invisibles. Por eso OctoEdit pregunta a tu intérprete de inicio de sesión cuál es su PATH y busca también ahí, y entrega el entorno resultante al servidor que arranca, de modo que un servidor instalado por cualquiera de esas herramientas se encuentra sea cual sea la forma en que lances el editor.

El servidor se arranca una vez por lenguaje y se apaga con el editor.

Los proyectos de C/C++ necesitan compile_commands.json

clangd solo entiende tu código con precisión cuando conoce las opciones de compilación, a partir de una base de datos de compilación (compile_commands.json). Sin ella, clangd recurre a adivinar y las funciones entre archivos (buscar referencias, renombrar) se limitan al archivo abierto. clangd se arranca igualmente en ambos casos.

Un proyecto de C/C++ cuenta como configurado cuando tiene alguno de estos:

  • un compile_commands.json en la raíz del proyecto o en uno de los directorios de compilación que OctoEdit examina (build/, Build/, out/, out/build/, cmake-build-debug/, cmake-build-release/, .mte-compile-commands/);
  • un compile_flags.txt en la raíz del proyecto;
  • un archivo de configuración .clangd en la raíz del proyecto.

clangd lee los dos últimos por su cuenta, así que de ellos no se informa nada.

Cuando no hay ninguno —para todos los proyectos de C/C++, sea cual sea su sistema de compilación— el segmento de estado muestra · no compile database y un mensaje de la barra de estado lo dice una vez. Nada se bloquea; ningún diálogo interrumpe lo que estás escribiendo.

Dejar que OctoEdit la genere (proyectos CMake)

Si la raíz del proyecto tiene un CMakeLists.txt y un CMake que realmente produciría una base de datos, se te ofrece esto una vez por proyecto:

  • Generar — ejecuta CMake en segundo plano, solo la fase de configuración: no se compila nada y tus propios directorios de compilación quedan intactos. El resultado va a <project>/.mte-compile-commands/. Si tiene éxito, clangd se reinicia contra la base de datos nueva; si falla, se muestran las últimas líneas del registro de CMake junto con el comando exacto que se intentó, para que puedas repetirlo tú mismo.
  • Cómo hacerlo a mano — muestra ese mismo comando sin ejecutarlo.
  • No preguntar para este proyecto — se recuerda por raíz de proyecto.

Tanto la ventana de «cómo hacerlo a mano» como la de fallo muestran la línea de comandos exacta en un cuadro de código seleccionable, encima de un único botón Copiar cuya descripción emergente dice qué se lleva: el comando solo, o el comando junto con el tramo final del registro de CMake cuando lo hay. La ventana de fallo es redimensionable y recuerda el tamaño que le das: cada una de estas ventanas recuerda su propio tamaño, de modo que agrandar el registro del servidor no hace que una ventana de instrucciones de dos líneas se abra igual de grande.

No estás limitado a esa única oferta: Buscar ▸ Navegación por el código ▸ Generar la base de datos de compilación… inicia la misma generación cuando quieras —tras añadir archivos, tras cambiar opciones de compilación o después de haber respondido No preguntar para este proyecto (que así se borra)—. Una base de datos existente se reconfigura en el sitio. La entrada está siempre disponible; y cuando no puede generar, dice por qué en la barra de estado.

Tus preajustes van primero. Si el proyecto tiene un CMakePresets.json (o un CMakeUserPresets.json), OctoEdit configura con uno de tus preajustes en lugar de inventarse una línea de comandos: cmake --preset <name> -B <project>/.mte-compile-commands -DCMAKE_EXPORT_COMPILE_COMMANDS=ON. Así, el generador, el conjunto de herramientas y el bloque environment del preajuste (un QT6_DIR, una ruta de SDK) son los que el proyecto ya utiliza, y es CMake quien los aplica. Se omiten los preajustes ocultos, los que una condition excluye en tu sistema operativo y los que configuran con un generador incapaz de exportar; se prefiere un nombre que contenga debug. Tu propio directorio de compilación nunca se toca: el editor siempre redirige la salida con -B.

En Windows, CMake se ejecuta dentro de un entorno de desarrollo de Visual Studio. Un editor iniciado desde el Explorador o el menú Inicio no tiene cl en su PATH, y entonces CMake se conformaría en silencio con cualquier otro compilador que encontrara, incluido el clang.exe que viene dentro de una instalación de Visual Studio, que falla enseguida con ajustes específicos de MSVC. Por eso OctoEdit localiza Visual Studio con vswhere y envuelve la llamada en VsDevCmd.bat -arch=x64. Si no hay ningún Visual Studio instalado (una máquina con MinGW o solo con clang), la llamada se hace tal cual y el registro lo dice.

Un intento fallido anterior se limpia antes de reintentar: su CMakeCache.txt fijaría, si no, el compilador que falló. Un directorio que ya contiene un compile_commands.json que funciona se reconfigura en el sitio, nunca se borra.

Por qué a veces no aparece la oferta

CMAKE_EXPORT_COMPILE_COMMANDS solo lo respetan los generadores Ninja y Makefile; el generador de Visual Studio lo ignora y no escribe ninguna base de datos. Así que en Windows, para un proyecto sin ningún preajuste utilizable, la oferta aparece solo cuando ninja está en tu PATH. Cuando los únicos preajustes aplicables de un proyecto son de Visual Studio y tampoco hay Ninja, OctoEdit dice qué preajuste se interpuso en lugar de ofrecerse a ejecutar algo que no podría funcionar.

Generarla tú mismo (CMake)

CMAKE_EXPORT_COMPILE_COMMANDS lo respetan solo los generadores Ninja y Makefile: el generador de Visual Studio lo ignora (no produce ningún compile_commands.json ni siquiera con la opción activada). Así que genera la base de datos con Ninja, en un directorio dedicado, separado de aquel en el que compilas normalmente: CMake se niega a reconfigurar un directorio de compilación existente con otro generador, de modo que un proyecto que sueles compilar con Visual Studio necesita su propio directorio de Ninja.

Este es un paso solo de configuración (no hace falta una compilación completa). Desde la raíz del proyecto:

cmake -S . -B .mte-compile-commands -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
  • .mte-compile-commands/ es uno de los directorios que MTE examina, así que el compile_commands.json resultante se encuentra automáticamente. Cualquier directorio examinado sirve también (build/, out/, cmake-build-* o la raíz del proyecto); solo mantenlo separado de un directorio configurado con otro generador.
  • Ejecútalo desde un entorno en el que el proyecto se configure realmente: el compilador correcto en el PATH (p. ej. un x64 Native Tools Command Prompt en Windows) y cualquier variable que tu proyecto necesite (como QT6_DIR en un proyecto Qt). Si cmake no encuentra tu compilador o tu SDK, la base de datos acaba vacía o sin rutas de inclusión y clangd no indexará bien.
  • Vuelve a abrir la carpeta después. El registro ya no debería decir «no compile_commands.json»; dale a clangd un momento para construir su índice en segundo plano antes de que las referencias y el renombrado abarquen varios archivos.

Otros sistemas de compilación: Bear (bear -- make), compiledb, Meson, Bazel y otros pueden producir un compile_commands.json que el plugin encontrará.

Qué servidor atiende qué archivo

Lo decide el nombre del archivo. Cada definición de servidor lista las extensiones que atiende, y la primera definición que reclama la extensión de un archivo es la que arranca. La comparación ignora las mayúsculas y solo cuenta el último punto: Deploy.Prod.SH es un script de shell.

Esto es deliberadamente independiente del coloreado de sintaxis. Desactivar el coloreado, o abrir un archivo mayor que el límite de tamaño del coloreado, ya no te cuesta también el servidor de lenguaje.

El id del colorizador sigue siendo un recurso de reserva

Si ninguna definición reclama la extensión, OctoEdit recurre al id de lenguaje que le asignó el motor de coloreado. Eso mantiene funcionando una definición escrita a mano que nombra un lenguaje pero no lista extensiones.

El nombre que se le dice al servidor es una segunda cosa, distinta: LSP tiene su propio vocabulario de identificadores de lenguaje, y varios no coinciden con los de OctoEdit. Un archivo .sh se anuncia como shellscript, un .scss como scss, un .jsx como javascriptreact, un .mm como objective-cpp. Los servidores que se comportan de forma distinta según el lenguaje —el servidor de CSS, el de TypeScript— necesitan que esto sea correcto.

Extensión Atendido por Anunciado como
.cpp .cxx .cc .h .hpp .hh .hxx .ipp .inl clangd cpp
.c clangd c
.m / .mm clangd objective-c / objective-cpp
.sh .bash .zsh .ksh bash-language-server shellscript
.css / .scss / .less vscode-css-language-server css / scss / less
.ts .mts .cts / .tsx typescript-language-server typescript / typescriptreact
.js .mjs .cjs / .jsx typescript-language-server javascript / javascriptreact
.json / .jsonc .json5 vscode-json-language-server json / jsonc

.h lo reclama la definición de C/C++ y no la de Objective-C: ambas ejecutan clangd, que decide por sí mismo qué contiene una cabecera.

Qué necesita cada servidor de tu proyecto

Varios servidores solo funcionan bien cuando un archivo concreto está en la raíz del proyecto. Cuando falta, el indicador de estado lo dice en vez de fingir que todo va bien:

Lenguaje Quiere Sin ello
Rust Cargo.toml rust-analyzer no cargará el crate en absoluto.
Go go.mod gopls no puede resolver el módulo.
TypeScript tsconfig.json (o package.json) las respuestas de ámbito de proyecto no están disponibles.
JavaScript jsconfig.json (o package.json) igual que arriba.
Python (pyright) pyproject.toml / pyrightconfig.json / setup.py pyright sigue funcionando; solo usa valores predeterminados para las raíces de importación y el rigor.
Java pom.xml / build.gradle jdtls no puede construir un classpath.
C/C++ compile_commands.json consulta la sección anterior.

Añadir un lenguaje

Los servidores se describen mediante archivos JSON que el plugin carga al arrancar desde <editor>/lsp-servers/*.json, más tu propia carpeta. Cada entrada asocia un lenguaje a su comando, junto con pistas de detección:

{
    "language":    "cpp",
    "languageId":  "cpp",
    "languageIds": { ".c": "c" },
    "command":     "clangd",
    "aliases":     ["clangd-20", "clangd-19"],
    "args":        ["--background-index", "--log=verbose"],
    "extensions":  [".cpp", ".h", ".hpp", ".c"],
    "projectMarkers": [],
    "searchDirs":      ["${LLVM}/bin", "${ProgramFiles}/LLVM/bin"],
    "searchDirsMac":   ["/opt/homebrew/opt/llvm/bin"],
    "searchDirsLinux": ["/usr/lib/llvm-20/bin"],
    "docsAnchor":  "cpp-clangd",
    "compileCommandsArg": "--compile-commands-dir=${dir}"
}
  • language es el identificador propio que OctoEdit da al lenguaje: la clave por la que se agrupan la página de ajustes, el segmento de estado y la conexión. command/aliases son los nombres de ejecutable que se prueban en el PATH.
  • extensions es la correspondencia principal de archivo a lenguaje (ver más arriba). Incluye el punto.
  • args lleva un elemento de la matriz por argumento. ["--background-index", "--log=verbose"] pasa dos argumentos; ["--background-index --log=verbose"] pasa un argumento que casualmente contiene un espacio, algo que todos los servidores rechazan; y como entonces terminan de inmediato, el resultado parece un bucle de fallos en vez de una errata. OctoEdit avisa de esa forma en el registro de diagnóstico cuando carga tu archivo.
  • languageId (opcional) es el identificador que se envía al servidor en didOpen; su valor predeterminado es language. Úsalo cuando el nombre de la especificación LSP difiera del nuestro (bash → shellscript, objc → objective-c).
  • languageIds (opcional) anula languageId por extensión, para un mismo servidor que atiende varios lenguajes de la especificación.
  • projectMarkers (opcional) son los nombres de archivo que el servidor quiere en la raíz del proyecto. Cuando no hay ninguno de ellos, el segmento de estado dice sin <first marker>; nunca detiene el servidor.
  • searchDirs es la lista de ubicaciones de instalación de Windows; searchDirsMac y searchDirsLinux son sus equivalentes por plataforma. Las tres expanden ${VAR}, y las listas POSIX expanden además un ~ inicial. Una entrada cuyas variables no están definidas se omite en vez de convertirse en una ruta sin sentido.
  • args puede usar dos marcadores de posición: ${dataDir}, la carpeta de datos del plugin en la que se puede escribir, y ${rootHash}, un identificador corto y estable del proyecto actual. Así es como jdtls recibe su -data <dir>. Un marcador que no se puede resolver se deja literalmente en el argumento en lugar de expandirse a nada.
  • compileCommandsArg (opcional) se añade al final, con ${dir} sustituido por el directorio de la base de datos localizada: así es como se apunta clangd a compile_commands.json.
  • priority (opcional, 0 de forma predeterminada) ordena varias definiciones del mismo lenguaje: el número más bajo se prueba primero, y la primera definición cuyo ejecutable se encuentra es la que arranca. Así es como Python obtiene pyright (priority: 0) con pylsp (priority: 10) como reserva.

Tus propias definiciones de servidor

Las definiciones incluidas viven dentro del directorio de instalación, que no es tuyo para editarlo (Program Files en Windows, un paquete de aplicación firmado en macOS). Pon las tuyas al lado: Preferencias ▸ Servidor de lenguaje ▸ Abrir la carpeta de definiciones de servidor del usuario crea y abre

<user data>/plugins/org.mte.lsp/lsp-servers/

Todos los *.json que haya en ella se cargan después del conjunto incluido, y:

  • un archivo cuyo language y command coinciden con una definición incluida la sustituye: así es como cambias los argumentos de clangd o añades un directorio de búsqueda;
  • cualquier otra cosa se añade: un segundo servidor para un lenguaje que incluimos, o un lenguaje para el que OctoEdit no incluye nada en absoluto.

Un archivo que no es JSON válido, o al que le falta language o command, se omite y se informa en el registro de diagnóstico en lugar de costarte un lenguaje en silencio.

Las definiciones se leen una sola vez, al arrancar: reinicia el editor después de editarlas (el botón Volver a explorar o reiniciar los servidores repite la búsqueda, no la carga).

Una definición de usuario mínima: <user data>/…/lsp-servers/zig.json
{
    "language":   "zig",
    "command":    "zls",
    "extensions": [".zig", ".zon"],
    "projectMarkers": ["build.zig"],
    "searchDirs":      ["${LOCALAPPDATA}/zls"],
    "searchDirsMac":   ["/opt/homebrew/bin", "~/.local/bin"],
    "searchDirsLinux": ["~/.local/bin"]
}

Qué carpeta se le entrega al servidor

Un servidor de lenguaje necesita saber dónde está el proyecto: rust-analyzer no cargará un crate sin ello, gopls necesita la raíz del módulo, pyright lee de ahí pyrightconfig.json y clangd busca ahí su base de datos.

  • Con una carpeta abierta (Archivo ▸ Abrir carpeta), los archivos de dentro pertenecen a esa carpeta.
  • Para un archivo fuera de la carpeta abierta, o sin ninguna carpeta abierta, OctoEdit sube desde el archivo buscando compile_commands.json, compile_flags.txt, .clangd, CMakeLists.txt, Cargo.toml, go.mod, pyrightconfig.json, pyproject.toml, setup.py, tsconfig.json, package.json, pom.xml, build.gradle, un .sln, .git y, por último, un .editorconfig que declare root = true. El propio directorio del archivo es el recurso de reserva, así que un servidor nunca se queda sin proyecto.
  • Abrir la carpeta después del archivo funciona. A los servidores que entienden los cambios de carpeta de espacio de trabajo se les informa de la nueva carpeta; los demás se reinician, y tus documentos abiertos se les vuelven a anunciar automáticamente. No hace falta ningún reinicio manual.

Solución de problemas

Síntoma Qué comprobar
El segmento LSP: no aparece nunca Ninguna definición de servidor reclama la extensión de este archivo. Consulta qué servidor atiende qué archivo, o añade una definición.
· server not responding El servidor ha dejado de responder. Usa Mostrar el registro del servidor de lenguaje y después Reiniciar los servidores de lenguaje.
✗ en un lenguaje para el que has instalado un servidor Confirma que el ejecutable está realmente ahí: where clangd (Windows) / which clangd (macOS, Linux). Después, Buscar ▸ Navegación por el código ▸ Reiniciar los servidores de lenguaje, que vuelve a ejecutar toda la búsqueda.
⚠ El servidor arrancó y luego no dejó de fallar. Mostrar el registro del servidor de lenguaje suele nombrar el motivo; corrígelo y usa Reiniciar los servidores de lenguaje.
· no compile database en un proyecto de C/C++ Consulta más arriba. Buscar referencias y Renombrar solo verán el archivo abierto hasta que se arregle.
El autocompletado no devuelve nada en un archivo de C++ Probablemente ese archivo falta en compile_commands.json (una cabecera que ninguna unidad de traducción incluye, o un archivo añadido después de generar la base de datos). Regenérala.
Todo funciona lanzándolo desde un terminal, pero no desde el Finder Repórtalo: la consulta del PATH del intérprete de inicio de sesión existe precisamente para que ambas formas sean idénticas. Mostrar el registro del servidor de lenguaje y el registro de diagnóstico anotan qué se buscó.
Un servidor necesita un archivo de proyecto que no tienes typescript-language-server necesita el paquete typescript, gopls un go.mod, rust-analyzer un Cargo.toml, y pyright un pyrightconfig.json/pyproject.toml para dar respuestas de ámbito de proyecto.

Ajustes del servidor de lenguaje

Preferencias ▸ Servidor de lenguaje lista todos los lenguajes para los que existe una definición de servidor. Por lenguaje puedes:

  • Activar / desactivar el servidor (un lenguaje desactivado nunca arranca uno).
  • Definir una ruta de ejecutable explícita (con Examinar…). Esta anulación gana sobre la búsqueda por PATH/searchDirs de arriba; es útil cuando el servidor no está en el PATH o quieres una compilación concreta.
  • Añadir argumentos adicionales, que se anexan tras los de la propia definición; por ejemplo --log=verbose para clangd. La línea se divide como se divide una línea de comandos, así que pon comillas alrededor de un valor que contenga espacios.

El campo de ruta es una ruta, no una línea de comandos

Escribir C:\tools\clangd.exe --log=verbose en Ruta del ejecutable le pide a OctoEdit que encuentre un archivo cuyo nombre termine en .exe --log=verbose, que no existe; el lenguaje informa entonces de que no hay servidor en absoluto. Pon el ejecutable en el campo de ruta y sus argumentos en Argumentos adicionales. Si ocurre de todos modos, el registro de diagnóstico lo dice con todas las letras.

Los cambios surten efecto cuando pulsas Aceptar / Aplicar: si algún indicador de activación o alguna ruta han cambiado realmente, los servidores se reinician automáticamente con los ajustes nuevos (una página que no se ha tocado nunca molesta a los servidores en marcha). El botón Volver a explorar o reiniciar los servidores fuerza además un desmontaje y una nueva detección con los ajustes actuales, útil tras instalar un servidor que el editor no encontraba antes; y también reinicia el presupuesto de reinicios por fallo.

Abrir la carpeta de definiciones de servidor del usuario crea y abre la carpeta descrita en Tus propias definiciones de servidor.

Limitaciones

  • Dentro de las ventanas emergentes de información al pasar el puntero no se aplica el coloreado de código (el texto se formatea —negrita, encabezados, código monoespaciado—, pero no se colorea la sintaxis).
  • La precisión depende del servidor: para C/C++, sin compile_commands.json clangd recurre a heurísticas y los resultados pueden ser incompletos.
  • OctoEdit nunca instala ni descarga un servidor. Encuentra el que tú hayas instalado y lo arranca; cuando no hay ninguno, la función se queda a oscuras y el segmento de estado lo dice.
  • Un proceso de servidor por lenguaje, compartido por todos los proyectos abiertos de ese lenguaje. Un segundo proyecto se añade como carpeta de espacio de trabajo cuando el servidor lo admite; en caso contrario se atiende bajo la raíz del primer proyecto.
  • Kotlin, Swift y Objective-C tienen inteligencia de código pero no coloreado de sintaxis: el editor todavía no tiene colorizador para ellos. Ahora ambas cosas son independientes, así que el servidor funciona igualmente.
  • Las definiciones de servidor se leen una sola vez al arrancar; editarlas (las tuyas o las incluidas) requiere reiniciar el editor.