Refactoring con Vim

Hace ya unos cuantos años que uso Vim como mi editor para mis cosas (katas, Advent of Code, etc.) pero desafortunadamente no podía decir lo mismo en lo que a trabajo se refiere.

Desde hace unas semanas he estado probando un nuevo plugin llamado RopeVim y me gustaría compartir mi experiencia con vosotros.

Introducción

Vim es una de las herramientas que más utilizo en mi día a día, con el que puedo programar o lanzar los tests de forma sencilla pero siempre he echado en falta la posibilidad de aplicar técnicas más complejas como el Refactoring.

Hoy quiero explicar cómo he conseguido configurarlo de tal forma que la experiencia de refactoring sea lo suficientemente buena como para ser una alternativa real y empezar a usarlo en el trabajo.

¿Qué es un Refactor?

Antes de entrar en materia me gustaría aclarar que es un Refactor, según Martin Fowler el refactoring es:

A controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations, each of which “too small to be worth doing”.

A change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

To restructure software by applying a series of refactorings without changing its observable behavior.

Es decir una técnica que nos permite mejorar la calidad de nuestro código sin cambiar su comportamiento.

Este último detalle es super importante y por ello se suele delegar completamente en los IDEs para aplicar el refactor ya que de esta forma las probabilidades de meter un cambio no deseado son muy bajas. Por otro lado es muy recomendado tener una buena red de seguridad basada en tests que nos permita explorar esta fase en nuestro código con total confianza y sin miedo a romper nada.

Existen multitud de técnicas de refactors, todas disponibles aquí por si le queréis echar un ojo tranquilmente.

Pre-requisitos

Para poder ser conseguir la experiencia de uso que os voy a mostrar después es necesario tener instalado los siguientes componentes:

  • Python: Todos los ejemplos están hechos con 3.10.
  • Neovim: Versión vitaminada de Vim.
  • Plug: Gestor de plugins.
  • RopeVim: Plugin para realizar los refactors.

Configuración de Vim

Una vez que tengamos configurado todo e instalado RopeVim hay un par de ajustes del mismo que me parecen muy interesantes comentar:

  • Desde hace mucho tiempo yo uso como tecla principal de vim la , ya que es mucho más cómoda de usar que los :. Así mismo RopeVim usa Ctrl-C como su tecla principal y es posible cambiarla:
    • let g:ropevim_local_prefix="<leader>"
  • A su vez RopeVim necesita crear una carpeta para poder trabajar y reconocer el proyecto. Esto puede ser un poco tedioso por lo que con este ajuste solo nos preguntará la primera vez:
    • let g:openvim_guess_project=1

Principales Técnicas de Refactor

¡Vayamos al grano!

RopeVim permite realizar una serie de técnicas de refactor, la lista completa se pueden consultar aquí, ya que algunas de ellas no las veo relevantes ni usables en el día a día por lo que me centraré en las que creo que son las más habituales:

Rename

  • <leader>r

Comencemos por la más sencilla y posiblemente de las más conocidas. Imaginemos que tenemos una variable en un método y queremos cambiarle el nombre. Una opción sería buscar todas las apariciones y hacer un replace pero es aún más sencillo si aplicamos esta técnica.

Basta con ponerse encima de la variable e introducir el shortcut, a continuación nos pedirá el nuevo nombre, podemos hacer preview para finalmente que confirmemos la acción.

Inline

  • <leader>i

Un refactor muy habitual es querer borrar una variable y usar su valor donde antes se usaba dicha variable. Para ello es tan sencilla como poner el curso encima de la variable y aplicar el shortcut, solo queda aceptar el cambio con la opción perform.

class MyClass:
 def __init__(self):
   my_variable = "my_string_class"
   print(my_variable)
class MyClass:
 def __init__(self):
   print("my_string_class")

Extract variable

  • <leader>v

Si queremos hacer el contrario al caso anterior, es decir extraer a una variable algún valor necesitaremos hacer uso del modo visual de Vim ya que será necesario previamente seleccionar el texto que queremos extraer para a continuación introducir el shortcut. Después de este paso nos pedirá el nombre de la nueva variable, podemos hacer preview y finalmente aplicar el cambio.

class MyClass:
 def __init__(self):
   print("my_string_class")
class MyClass:
 def __init__(self):
   my_variable = "my_string_class"
   print(my_variable)

Extract method

  • <leader>m

El refactor para extraer un fragmento de código a un nuevo método es muy similar al anterior, es decir necesitamos seleccionar en modo visual el código a extraer para a continuación aplicar el shortcut.

class MyClass:
 def __init__(self):
   x = 1
   y = 1

   z = x + y

Este sería el resultado final:

class MyClass:
 def __init__(self):
   x = 1
   y = 1

   z = self.sum(x, y)
  def sum(self, x, y):
   return x + y

Es importante comentar que este refactor nos permite crear estos métodos como estáticos ($method_name) o de clase (@method_name).

Change signature

  • <leader>s

Para mí uno de los refactors más importantes de todos los que vamos a ver. ¿Cuántas veces hemos necesitado cambiar la firma de un método que se usa en muchos ficheros? Pues con este refactor podremos cambiar una sola vez y que este cambio se aplicó en todo los ficheros donde se esté utilizando.

Basta con poner el cursor encima del nombre del método y aplicar el shortcut, a continuación nos pedirá la nueva forma que podremos previsualizar antes de aplicar.

Introduce Factory

  • <leader>f

Si tenemos la necesidad de crear una factoría para una de nuestras clases existe un refactor que nos va a facilitar muchísimo esta creación. Bastaría con poner el cursor sobre el nombre la clase en cuestión y hacer uso del shortcut. Este refactor nos preguntará por el nombre de la factoría para a continuación dejarnos previsualizar los cambios antes de aplicarlos (la factoría se creará en el mismo fichero donde está la clase).

class MyClass():
 pass
class MyClass():
 pass


def create_myclass(*args, **kwds):
 return MyClass(*args, **kwds)

Use function

  • <leader>u

Por último un refactor muy útil cuando queremos cambiar partes de nuestro código por llamadas a funciones que realizan esa misma operación, pongamos un ejemplo:

def square(p):
   return p ** 2

my_var = 3 ** 2

Si ponemos el cursor sobre square y aplicamos el shortcut RopeVim buscar operaciones como la que realiza el método seleccionado y las reemplaza por llamadas a dicho método.

def square(p):
   return p ** 2

my_var = square(3)

Conclusiones

Espero que como introducción os haya gustado esta entrada, como podéis ver es posible aplicar técnicas de refactor con Vim (aunque sea una experiencia incompleta).

Por mi parte estaré pendiente de cómo evoluciona RopeVim ya que pese a no ser la misma experiencia a cuánto usas un IDE como puede ser PyCharm creo que este plugin es una muy buena opción para los que preferimos Vim.

Un saludo!