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:
| 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:
- pyright (
pyright-langserver) se prueba primero. Es un paquete de npm, así que necesita Node.js:npm i -g pyright. - 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:
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 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:
- 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;
- tu
PATH, para el comando del servidor y cada alias conocido (clangd-20,clangd-19, …); - las ubicaciones de instalación de la definición del servidor para tu sistema operativo;
- 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.jsonen 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.txten la raíz del proyecto; - un archivo de configuración
.clangden 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:
.mte-compile-commands/es uno de los directorios que MTE examina, así que elcompile_commands.jsonresultante 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 (comoQT6_DIRen un proyecto Qt). Sicmakeno 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}"
}
languagees 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/aliasesson los nombres de ejecutable que se prueban en elPATH.extensionses la correspondencia principal de archivo a lenguaje (ver más arriba). Incluye el punto.argslleva 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 endidOpen; su valor predeterminado eslanguage. Úsalo cuando el nombre de la especificación LSP difiera del nuestro (bash→shellscript,objc→objective-c).languageIds(opcional) anulalanguageIdpor 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 dicesin <first marker>; nunca detiene el servidor.searchDirses la lista de ubicaciones de instalación de Windows;searchDirsMacysearchDirsLinuxson 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.argspuede 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 acompile_commands.json.priority(opcional,0de 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
Todos los *.json que haya en ella se cargan después del conjunto incluido,
y:
- un archivo cuyo
languageycommandcoinciden 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).
{
"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,.gity, por último, un.editorconfigque declareroot = 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/searchDirsde arriba; es útil cuando el servidor no está en elPATHo quieres una compilación concreta. - Añadir argumentos adicionales, que se anexan tras los de la propia
definición; por ejemplo
--log=verbosepara 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.jsonclangd 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.