Skip to content

Macros

Now that you’ve learned how to refactor text using multiple cursors, the next step is to learn about macros and how to effectively use them for complex refactorings.

Introduction

Macros are a way to record your keystrokes and then replay them whenever you want to.

The two most important keys here are:

  • Q which begins recording a macro into the register Q.
  • q which replays the macro from register Q.

You can have a lot of macros at your disposal in various registers, ready to use whenever. You use registers with macros just like usual.

For example, to record a macro into a specific register, press to select the register, then a character which will be the register where your macro is stored, and then Q. For instance:

  • + e + Q to record a macro into the e register.

To stop recording, press Q again.

Replay the macro by using register + q. For example:

  • + e + q

Using macros

  1. Place your cursor on the first line, after the dash:

        1  <a class="font-thin px-3" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:16 
                                        
    
  2. Use Q to begin recording a macro, and press e to select the full thin word:

        1  <a class="font-thin px-3" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:19 
                                     [@]
    
  3. Change it to bold using c to delete and insert mode, then type bold and Esc to go back to normal mode:

        1  <a class="font-bold px-3" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:20 
                                     [@]
    
  4. Use f + 3 to find to next 3, and then ; to collapse selection to a single cursor in order to select the 3:

        1  <a class="font-bold px-3" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:24 
                                     [@]
    
  5. Replace it with a 4 using r to enter replace mode which waits for the next character, and type 4:

        1  <a class="font-bold px-4" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:24 
                                     [@]
    
  6. Press Q to stop recording the macro.

        1  <a class="font-bold px-4" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:24 
    Recorded to register [@]            
    
  7. Okay, so the hard part is done!

    Now go back to the - you started from by using F + - which finds the previous -.

    Repeat this motion by using Alt + . and then collapse selection with ;:

        1  <a class="font-bold px-4" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]    1 sel  1:15 
                                        
    
  8. Hit j to go down a line and create 2 more cursors on each - by pressing C 2 more times:

        1  <a class="font-bold px-4" /> 
        2  <a class="font-thin px-3" /> 
        3  <a class="font-thin px-3" /> 
        4  <a class="font-thin px-3" /> 
     NOR   file.html [+]   3 sels  4:15 
                                        
    
  9. And just press q (note the lowercase q) to repeat the motions you’ve recorded earlier.

        1  <a class="font-bold px-4" /> 
        2  <a class="font-bold px-4" /> 
        3  <a class="font-bold px-4" /> 
        4  <a class="font-bold px-4" /> 
     NOR   file.html [+]   3 sels  4:24 
                                        
    

Search and Select

We’ve just explored the very basics of how to use multiple cursors and macros, but in fact they’re a lot more powerful than seems at first glance.

Let’s consider the following file, which includes some repetition:

    1  def calculate_area(length, width):           
    2    result = length * width                    
    3    return result                              
    4                                               
    5  def calculate_perimeter(length, width):      
    6    result = 2 * (length + width)              
    7    return result                              
    8                                               
    9  def calculate_volume(length, width, height): 
   10    result = length * width * height           
   11    return result                              
 NOR   file.py                           1 sel  1:1 
Loaded 1 file.                                      

And we want every variable to be a maximum of 3 characters, and functions to be static methods on a class.

The final outcome will look like this:

    1  class Calculator:                            
    2    @staticmethod                              
    3    def get_area(len, wid):                    
    4      return len * wid                         
    5                                               
    6    @staticmethod                              
    7    def get_perimeter(len, wid):               
    8      return 2 * (len + wid)                   
    9                                               
   10    @staticmethod                              
   11    def get_volume(len, wid, hei):             
   12      return len * wid * hei                   
 NOR   file.py                           1 sel  1:1 
Loaded 1 file.                                      

  1. First, let’s rename each of the functions from calculate_* to get_*.

    The easiest way to do search-and-replace in Helix is to first select the entire file with %.

        1  def calculate_area(length, width):           
        2    result = length * width                    
        3    return result                              
        4                                               
        5  def calculate_perimeter(length, width):      
        6    result = 2 * (length + width)              
        7    return result                              
        8                                               
        9  def calculate_volume(length, width, height): 
       10    result = length * width * height           
       11    return result                              
                                                        
     NOR   file.py [+]                     1 sel  11:15 
                                                        
    
  2. We’re going to use s which allows us to select inside of our selection.

    Basically, it takes an input and will search for that input and create a cursor everywhere it finds it.

        1  def calculate_area(length, width):           
        2    result = length * width                    
        3    return result                              
        4                                               
        5  def calculate_perimeter(length, width):      
        6    result = 2 * (length + width)              
        7    return result                              
        8                                               
        9  def calculate_volume(length, width, height): 
       10    result = length * width * height           
       11    return result                              
                                                        
     NOR   file.py [+]                     1 sel  11:15 
    select:                                             
    
  3. Type the following: calculate and then Enter.

        1  def calculate_area(length, width):           
        2    result = length * width                    
        3    return result                              
        4                                               
        5  def calculate_perimeter(length, width):      
        6    result = 2 * (length + width)              
        7    return result                              
        8                                               
        9  def calculate_volume(length, width, height): 
       10    result = length * width * height           
       11    return result                              
                                                        
     NOR   file.py [+]                     3 sels  1:13 
                                                        
    

    As you see, it created a selection and a cursor on every single instance of the calculate word.

    We can now just press c to delete the contents of each selection, which also places us into insert mode, and type get then Esc to go back to normal more:

        1  def get_area(length, width):                 
        2    result = length * width                    
        3    return result                              
        4                                               
        5  def get_perimeter(length, width):            
        6    result = 2 * (length + width)              
        7    return result                              
        8                                               
        9  def get_volume(length, width, height):       
       10    result = length * width * height           
       11    return result                              
                                                        
     NOR   file.py [+]                      3 sels  1:8 
                                                        
    
  4. Now that we have a cursor on each function definition, lets create an empty line above each cursor by pressing O and then writing @staticmethod, finally going back to normal mode with Esc:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                     3 sels  1:14 
                                                        
    
  5. Our goal is to rename the parameters to be 3 letters.

    To do this, we’re going to first select the function definition as well as the first line of the body of each function, with j and then by using x twice:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                     3 sels  3:26 
                                                        
    
  6. Let’s use s now which will ask us for a prompt.

    This time, we won’t just enter a string but we’ll enter a very small regex: \w+, which selects every word:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                     24 sels  2:3 
                                                        
    
  7. This selected a few extra words than we wanted, but that’s okay! One of the selections is the “primary” selection which we can tell by the 2:3 in the left right corner, which means that our primary selection is on line 2 character 3.

    We can make the next selection our primary selection by using ), which is seen by the 2:12 in the counter, meaning that our primary selection is at the get_area method:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                    24 sels  2:12 
                                                        
    

    We can go back to the previous one with (:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                     24 sels  2:3 
                                                        
    
  8. By pressing Alt + ,, we can remove the primary selection, so that def is not selected:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                    23 sels  2:12 
                                                        
    
  9. Removing that selection automatically made get_area the next selected region. We don’t want this one either, so we’ll remove it again with Alt + ,:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                    22 sels  2:19 
                                                        
    

    Press ) a few times until you reach the next selection you want to remove. If you go over by one, just press ( to make the previous selection primary.

  10. Let’s repeat the previous step until we only have the words length, width and height selected:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                   14 sels  13:17 
                                                        
    
  11. At this point, we can begin our removal process.

    Go to the beginning of each word by pressing b, and then press l a couple of times until you are on the 4th character of each word:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                   14 sels  13:15 
                                                        
    
  12. Let’s press e to go to the end of each word which will highlight it:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                   14 sels  13:17 
                                                        
    
  13. Press d to delete it.

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    result = len * wid                         
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    result = 2 * (len + wid)                   
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    result = len * wid * hei                   
       14    return result                              
     NOR   file.py [+]                   14 sels  13:15 
                                                        
    
  14. Great we’re almost there!

    Let’s actually undo this step so that we can look at an alternative way of accomplishing the same result.

    Press u just once which will undo our replacement:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                   14 sels  13:17 
                                                        
    
  15. An alternative way is to select the entire file again with %:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                     1 sel  14:44 
                                                        
    
  16. With the power of s and Regex, we can select the length, width and height words by chaining them together with the “OR” operator.

    For instance, type the following: length|width|height:

        1  @staticmethod                                
        2  def get_area(length, width):                 
        3    result = length * width                    
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(length, width):            
        8    result = 2 * (length + width)              
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(length, width, height):       
       13    result = length * width * height           
       14    return result                              
     NOR   file.py [+]                    14 sels  2:19 
    select:length|width|height                          
    

    This is certainly simpler but it’s good to know both approaches.

  17. Hit Enter to select and go to the beginning of each word with b

  18. Move 3 characters to the right with lll

  19. Press e to go to the end of each word and delete it:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    result = len * wid                         
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    result = 2 * (len + wid)                   
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    result = len * wid * hei                   
       14    return result                              
     NOR   file.py [+]                    14 sels  2:17 
                                                        
    
  20. Lets to remove the variable declarations of result on each line, and instead directly return the calculation.

    Go ahead and select the entire file again with % then use the s to bring up the select prompt again.

  21. Type result = which will select all instances of result =:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    result = len * wid                         
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    result = 2 * (len + wid)                   
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    result = len * wid * hei                   
       14    return result                              
     NOR   file.py [+]                     3 sels  3:10 
    select:result =                                     
    
  22. Enter to select, and then Press C to duplicate the selection on each line:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    result = len * wid                         
        4    return result                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    result = 2 * (len + wid)                   
        9    return result                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    result = len * wid * hei                   
       14    return result                              
     NOR   file.py [+]                     6 sels  4:10 
                                                        
    
  23. By using Alt + (, we can rotate the contents of the selection backward:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return r len * wid                         
        4    result =esult                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    return r 2 * (len + wid)                   
        9    result =esult                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    return r len * wid * hei                   
       14    result =esult                              
     NOR   file.py [+]                     6 sels  4:10 
                                                        
    

    Try using Alt + ) to rotate the selection forward. You can keep on pressing the key but the difference between Alt + ) and Alt + ( won’t be obvious in this example, since we basically have the same selections repeated.

    Regardless, press Alt + ) at least once to get into the following state:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return r len * wid                         
        4    result =esult                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    return r 2 * (len + wid)                   
        9    result =esult                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    return r len * wid * hei                   
       14    result =esult                              
     NOR   file.py [+]                     6 sels  4:10 
                                                        
    
  24. Collapse each cursor so that there is only a single selection with ;:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return r len * wid                         
        4    result =esult                              
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    return r 2 * (len + wid)                   
        9    result =esult                              
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    return r len * wid * hei                   
       14    result =esult                              
     NOR   file.py [+]                     6 sels  4:10 
                                                        
    
  25. Now, just press d twice to delete two characters on each of the 6 lines:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return len * wid                           
        4    result sult                                
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    return 2 * (len + wid)                     
        9    result sult                                
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    return len * wid * hei                     
       14    result sult                                
     NOR   file.py [+]                     6 sels  4:10 
                                                        
    
  26. Okay so at this point, we are almost there. We just need to delete the result sult line.

    To do this, use the s again and type s in the prompt. This will select the s in each of your selections and place a cursor there:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return len * wid                           
        4    result sult                                
        5                                               
        6  @staticmethod                                
        7  def get_perimeter(len, wid):                 
        8    return 2 * (len + wid)                     
        9    result sult                                
       10                                               
       11  @staticmethod                                
       12  def get_volume(len, wid, hei):               
       13    return len * wid * hei                     
       14    result sult                                
     NOR   file.py [+]                     3 sels  4:10 
    select:s                                            
    
  27. Press Enter to select, and then press x to select the entire line and d to delete it. Voila!

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return len * wid                           
        4                                               
        5  @staticmethod                                
        6  def get_perimeter(len, wid):                 
        7    return 2 * (len + wid)                     
        8                                               
        9  @staticmethod                                
       10  def get_volume(len, wid, hei):               
       11    return len * wid * hei                     
        ~                                               
                                                        
                                                        
     NOR   file.py [+]                      3 sels  4:1 
                                                        
    
  28. We’ve got just a few steps left. Namely, select the entire file again with %:

        1  @staticmethod                                
        2  def get_area(len, wid):                      
        3    return len * wid                           
        4                                               
        5  @staticmethod                                
        6  def get_perimeter(len, wid):                 
        7    return 2 * (len + wid)                     
        8                                               
        9  @staticmethod                                
       10  def get_volume(len, wid, hei):               
       11    return len * wid * hei                     
        ~                                               
                                                        
                                                        
     NOR   file.py [+]                     1 sel  11:25 
                                                        
    
  29. Press > which will indent everything by 1 level. We need this since we’re going to nest it in a class, after all. You can also use < to de-dent the selection by 1 level too.

        1    @staticmethod                              
        2    def get_area(len, wid):                    
        3      return len * wid                         
        4                                               
        5    @staticmethod                              
        6    def get_perimeter(len, wid):               
        7      return 2 * (len + wid)                   
        8                                               
        9    @staticmethod                              
       10    def get_volume(len, wid, hei):             
       11      return len * wid * hei                   
        ~                                               
                                                        
                                                        
     NOR   file.py [+]                     1 sel  11:27 
                                                        
    
  30. Now, press O to create a newline above and enter Insert mode.

  31. Finally, type class Calculator: and Esc to go back to normal mode.

        1  class Calculator:                            
        2    @staticmethod                              
        3    def get_area(len, wid):                    
        4      return len * wid                         
        5                                               
        6    @staticmethod                              
        7    def get_perimeter(len, wid):               
        8      return 2 * (len + wid)                   
        9                                               
       10    @staticmethod                              
       11    def get_volume(len, wid, hei):             
       12      return len * wid * hei                   
        ~                                               
                                                        
     NOR   file.py [+]                      1 sel  1:18 
                                                        
    

We’ve accomplished our goal 🥳!

Next steps

At your disposal is now one of the most powerful methods to manipulate text: Macros and Multiple Cursors. There are still ways to improve to become more powerful.