Ponte sobre un método y mira sus usos sin moverte: el "Find Usages" con preview que llevaba años instalado
La escena que echaba de menos
Estás leyendo una clase. Te paras sobre un método —pongamos un ~getEmail(): string~— y el IDE te susurra: "7 usages". Picas, y se abre un panelito con los siete sitios donde se llama, y a un lado el código real de cada uno. Te mueves por la lista, ves el contexto sin saltar, encuentras el que buscabas, Enter, y ya estás allí. Si no era ninguno, cierras y sigues donde estabas.
Lo usas cien veces al día sin pensar. Y al pasarme a Doom lo busqué, no lo encontré a la primera, y pensé: "esto sí que lo voy a perder". Spoiler: no lo perdí. Estaba ahí. Apagado.
Tres sabores de "¿dónde se usa esto?"
Doom, con su módulo de LSP, trae varias formas de ver los usos de un símbolo. Yo no conocía ninguna:
Lista en ventana aparte —
gD(g mayúscula). Todos los usos listados en una ventana. Útil cuando hay muchos y quieres la lista completa para ir tachando.Peek con preview — el clavado a IntelliJ. Un popup pegado al cursor: a un lado la lista, al otro la vista previa del código de cada uso. Te mueves con
j/k, el preview cambia en vivo, saltas solo si quieres. Eslsp-ui-peek-find-references.Árbol lateral — una ventana de
treemacsa un lado, redimensionable, con las rutas completas y plegable. Eslsp-treemacs-references. Ideal cuando hay treinta referencias, o cuando estás en zen-mode (que estrecha la ventana y ahoga al peek: el árbol, al ser ventana aparte, ni se entera).
Los tres venían ya instalados con el módulo de LSP de Doom. Lo único que les faltaba era una tecla.
Por qué no tenían tecla, y dónde se la puse
El atajo natural para "references" sería gr ("go
references"), pero en Doom gr ya evalúa la región de código
(+eval:region). En un fichero Lisp tiene todo el sentido
—marcas un trozo y lo ejecutas—. En PHP, no: ahí gr no hace
nada útil. Así que lo reutilizamos solo cuando hay
LSP:
;; gr -> peek (rapido) ; gR -> arbol treemacs (listas grandes / zen)
;; En buffers sin LSP, gr/gR siguen siendo el eval de siempre.
(defun my/gr-references-dwim ()
(interactive)
(if (bound-and-true-p lsp-mode)
(lsp-ui-peek-find-references)
(call-interactively #'+eval:region)))Un detalle que me costó una tarde: Doom bindea gr en un
keymap de alta prioridad (general
"override"), así que un binding en lsp-mode-map quedaba
tapado aunque estuviera bien puesto. Y para más coña,
la función con la que yo lo verificaba (key-binding) me
mentía desde fuera de Emacs. La verdad
solo apareció listando el orden real de keymaps activos con
current-active-maps. Lección: si tu herramienta de
diagnóstico te da una respuesta rara dos veces, sospecha de la
herramienta.
El GAP de verdad: los métodos mágicos
Aquí viene lo gordo, y donde por primera vez en toda la serie tuve que admitir que PhpStorm gana.
PHP tiene métodos mágicos. El rey hoy es
__invoke: hace que un objeto se pueda llamar
como si fuera una función. Se usa a
punta pala —command handlers, single-action controllers, invokables de
todo tipo:
final class SendWelcomeEmail
{
public function __invoke(User $user): void { /* ... */ }
}
// y en otro sitio, se dispara invocando el objeto:
($sendWelcomeEmail)($user); // <- esto ejecuta __invokePon el cursor sobre ese __invoke, pide "find
references"… y salen cero. ¿Por qué?
Porque nadie escribe
->__invoke(). Se invoca implícitamente
(($obj)($arg)), y el servidor de lenguaje no ata esa
llamada al método. No es que Doom esté roto: es que Intelephense —como
casi todos los servidores LSP— no hace ese análisis.
Investigué a fondo buscando salida:
- Intelephense: no lo resuelve. Limitación conocida.
- Phpactor (el otro server PHP, libre): implementó
**justo este mecanismo**… pero solo para
__construct(te encuentra losnew Foo()). Para__invoke, no. - ¿Los dos a la vez? No se puede:
lsp-modeadmite un solo cliente por lenguaje. Sería cambiar Intelephense por phpactor, peor en todo lo demás.
Veredicto honesto: en este punto, PhpStorm gana de calle. Tiene un motor de análisis PHP propio (no LSP) que entiende la invocación implícita. El ecosistema LSP libre, hoy, no lo iguala. No hay botón mágico.
Pero Doom tiene algo que PhpStorm no: es moldeable
Si el servidor no es lo bastante listo, puedo enseñar al editor a hacer lo que yo haría a mano. ¿Qué hago yo cuando quiero saber quién usa un handler invokable? Busco los usos de la CLASE (de ahí salen las invocaciones). Pues que lo haga el editor solo:
(defvar my/php-magic-methods
'("__invoke" "__construct" "__get" "__set" "__call" "__callStatic"
"__toString" "__clone" "__destruct" "__isset" "__unset")
"Se invocan implicitamente: pedir sus referencias por nombre da 0.")
(defun my/php--class-name-position ()
"Posicion del NOMBRE de la clase/interface/trait/enum del buffer."
(save-excursion
(goto-char (point-min))
(when (re-search-forward
"^[[:space:]]*\\(?:\\(?:final\\|abstract\\|readonly\\)[[:space:]]+\\)*\\(?:class\\|interface\\|trait\\|enum\\)[[:space:]]+\\([A-Za-z_][A-Za-z0-9_]*\\)"
nil t)
(match-beginning 1))))
(defun my/lsp-smart-references ()
"Peek de referencias; si el simbolo es un metodo magico, busca los
usos de la CLASE contenedora en su lugar."
(interactive)
(let ((sym (thing-at-point 'symbol t)))
(if (and (derived-mode-p 'php-mode 'php-ts-mode)
sym (member sym my/php-magic-methods)
(my/php--class-name-position))
(save-excursion
(goto-char (my/php--class-name-position))
(message "[smart-refs] %s es magico -> usos de la clase" sym)
(lsp-ui-peek-find-references))
(lsp-ui-peek-find-references))))Ahora pones gr sobre un __invoke y, en vez
de cero resultados, ves dónde se usa el handler. No es magia ni un
análisis nuevo: es el editor haciendo el paso manual por ti, en
transparente. No iguala el motor de PhpStorm —si el handler se inyecta
por una interfaz, hay que mirar también ahí— pero cubre el caso real del
95% de las veces, y es tuyo, escrito
en veinte líneas que entiendes entero.
Coda: la trampa del "numerito flotante"
Un cabo suelto, por si tropiezas con él. El número permanente que IntelliJ pinta encima del método ("7 usages" fijo, sin pedirlo) se llama CodeLens en el mundo LSP. Doom lo trae preparado, pero por más que lo activaba no salía nada. La razón: ese contador no es una pieza estándar que cualquier servidor rellene; muchos sencillamente no lo producen. Así que puedes tener el CodeLens "encendido" y ver cero números, no porque algo falle, sino porque nadie manda ese dato. Perseguí ese fantasma un rato hasta darme cuenta de que la función que de verdad uso —"enséñame los usos"— la tengo igual de bien bajo demanda. A veces la mejor optimización es saber qué no necesitas.
El patrón, una vez más
Lo que crees que te falta casi nunca falta. Está apagado, sin una tecla asignada, o se llama de una forma que no imaginabas. Y cuando de verdad falta, el editor es lo bastante tuyo como para añadírtelo.
El peek, el árbol y la lista llevaban ahí desde que instalé Doom. El parche de los métodos mágicos no estaba —pero el editor me dejó escribirlo. Esa es la diferencia que al final inclina la balanza: no tener mejor motor, sino poder abrir el capó y meter mano.
— doomguru
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario