Skip to content

Buffers

Windows allow you to view and manage multiple buffers at once.

For example, you may be copying code from file A and pasting it into file B line-by-one with slight modifications. It would be fitting to have two windows open side-by-side for this task.

Difference between Windows and Buffers

  • A buffer is the in-memory text of a file.

    For example you can open 10 files with:

    Terminal window
    # expands to: hx file1 file2 ... file10
    hx file{1..10}

    There will be 10 buffers stored in memory, but only 1 opened window.

  • A window is a viewport on a buffer.

    If you open those 10 files again with --vsplit (probably a bad idea!):

    Terminal window
    hx --vsplit file{1..10}

    It’ll store 10 buffers in memory, as well as open 10 windows.

Opening Multiple Files

Let’s start from the command line, where we used to open files using hx <filename>.

This time, let’s open several files!

Terminal window
hx file1 file2 file3

If you run the above command, you’ll just see an empty screen:

    1                           
    ~                           
                                
 NOR   file1         1 sel  1:1 
Loaded 3 files.                 

But notice the Loaded 3 files. message at the bottom left? We’ve got all of those files open.

Let’s leave a message in our first buffer:

    1  this is buffer #1        
    ~                           
                                
 NOR   file1 [+]    1 sel  1:18 
                                

To go to the next buffer, use the command gn for goto next.

    1                           
    ~                           
                                
 NOR   file2         1 sel  1:1 
                                

Leave another message here, and then let’s do the same on the third buffer:

    1  this is buffer #3        
    ~                           
                                
 NOR   file3 [+]    1 sel  1:18 
                                

You can go to the previous buffers by using gp. So try to cycle between them with the commands we learned now:

  • gn to go to the next buffer
  • gp to go to the previous buffer

Bufferline

So this is useful, we can have multiple files open. But it’s hard to really get a feel for where exactly we are in terms of buffers.

Enable the bufferline by using :set bufferline always. This adds a bar at the top showing all of our three files:

 file1[+]  file2[+]  file3[+]   
    1  this is buffer #3        
    ~                           
 NOR   file3 [+]    1 sel  1:18 
                                

With the bufferline, we can tell right off the bat which buffer is the currently active one.

Opening buffers with a glob

Let’s save all files and quit. We could use :wqa to write quit all but we’ll use :xa which is exactly the same but 1 character shorter!

Go ahead and open those files again, this time we won’t manually type out their names but rather use a glob.

We want a glob which expands to file1 file2 file3, what kind of pattern can we notice with those files? Well, they all begin with the string file.

So we could do something like this:

Terminal window
hx file*

This will match every file in the current directory which begins with the string file and then has whatever at the end.

The “whatever” is exactly what the asterk — * represents. * is a wildcard that matches literally anything, so it will match file1, file2 and file3 (as well as anything else that begins with file).

When we execute that command, helix will open the same 3 buffers. We’ll use :set bufferline always again:

 file1  file2  file3            
    1  this is buffer #1        
    ~                           
 NOR   file1         1 sel  1:7 
                                

Opening buffers with more complex filtering logic

As you’ve just seen, hx command + globs is powerful. But this is only the beginning!

This command will open every single file with a .rs extension in the current directory, recursively. So it will even open super nested files like ./some/directory/some/where/very/nested/main.rs:

Terminal window
hx **/*.rs

Or as another example, we want to open a buffer in Helix for every file which contains the string export default async function, using the GNU utilities find and grep we can do just that:

Terminal window
hx $(find . -type f -exec grep -l \
"export default async function" {} +)

Let’s dissect the command:

  • find .: Search every file and directory in the current directory and any sub-directories.
  • -type f: Match only files, and not directories.
  • -exec grep -l "export default async function" {}: Runs the following command on each file: searches for the string export default async function, outputting the names of files that it matches.
  • + groups the files.
  • The entire command find . -type f -exec grep -l "export default async function" {} + above will output the names of every file matched file as follows:
./file-one.js
./nested/another-file.js
./somewhere/deep/nested/file.ts

Placing it inside of $( ... ) uses shell variable expansion to effectively replace the entire command with the following identical output:

Terminal window
hx ./file-one.js \
./nested/another-file.js \
./somewhere/deep/nested/file.ts

Which then opens each of those files in Helix as a separate buffer.

This functionality comes in handy when you need to complete a certain task on every single file matching a pattern, but the task cannot be automated as each file requires manual inspection.

Opening buffers at specific line and column numbers

Finally, it’s useful to know that Helix can open files at specific line numbers, aswell as column numbers. For example this command opens the main.rs file at line 122:

Terminal window
hx main.rs:122

The following will open on line 122, column 6:

Terminal window
hx main.rs:122:6

We can use the above two facts to make some truly powerful commands. I make a lot of spelling mistakes.

Instead of manually going through 40+ markdown files in order to fix these spelling mistakes, I run the following command which we’ll disect in a moment:

Terminal window
hx $(pnpm dlx cspell **/*.mdx | rg "^\S+")

That command will find every spelling mistake by using the cspell tool.

The cspell which can be used as follows to spellcheck against every .mdx file (these files which contain this site’s content):

Terminal window
pnpm dlx cspell **/*.mdx

It outputs information in the following format:

Terminal window
usage/text-objects.mdx:43:53 - Unknown word (nside)
usage/text-objects.mdx:43:62 - Unknown word (textobject)

Since Helix can work with the first part of the text before the whitespace, we want to filter out everything until the first space.

We can do that by matching “a sequence of non-whitespace characters at the beginning”. In terms of regex, it would be the same as this ^\S+.

  • ^ means “at the beginning”
  • \S means “a non-whitespace character”
  • + means “match at least one occurrence of”

So this regex will extract the following from the above example:

Terminal window
usage/text-objects.mdx:43:53
usage/text-objects.mdx:43:62

To apply the regex to the output of cspell tool, we use ripgrep which is a modern alternative to grep, so the following command:

Terminal window
pnpm dlx cspell **/*.mdx | rg -o "^\S+"

Will produce an output like this:

basics.mdx:6:22 basics.mdx:82:56 basics.mdx:236:51 basics.mdx:297:72 basics.mdx:305:109 basics.mdx:309:104 installation.mdx:6:40

To use that in helix, we simply enclose the entire command in $( ... ) to make use of shell expansion:

Terminal window
hx $(pnpm dlx cspell **/*.mdx | rg -o "^\S+")

And it will open every single spelling error as a buffer, we’ll be able to traverse them with gn and gp. Extremely powerful.

Viewing multiple buffers at once

So far we explored having multiple files open in background, but we’ve only really seen a single buffer at the same time. We had to press commands like gp and gn to switch between them.

It’s possible to open multiple of them at once. For instance, if we have our file1 file2 file3 opened:

 file1  file2  file3            
    1  this is buffer #1        
    ~                           
 NOR   file1         1 sel  1:7 
                                

We can open a “split” by using Ctrl + w + s. Let’s have three opened:

 file1  file2  file3            
    1  this is buffer #1        
    ~                           
                                
                                
       file1         1 sel  1:1 
    1  this is buffer #1        
    ~                           
                                
                                
       file1         1 sel  1:1 
    1  this is buffer #1        
    ~                           
                                
                                
                                
 NOR   file1         1 sel  1:1 
                                

Our cursor is at the bottom split. To navigate to the upper split, we use motions which are similar to h, j, k and l:

  • Ctrl + w + h moves to buffer above
  • Ctrl + w + j moves to buffer below
  • Ctrl + w + k moves to buffer on the right
  • Ctrl + w + l moves to buffer on the left

Each of these buffers has a dedicated statusline. The statusline of the currently active buffer will have its mode shown. For example, we are currently on the second buffer:

 file1  file2  file3            
    1  this is buffer #1        
    ~                           
                                
                                
       file1         1 sel  1:1 
    1  this is buffer #1        
    ~                           
                                
                                
 NOR   file1         1 sel  1:1 
    1  this is buffer #1        
    ~                           
                                
                                
                                
       file1         1 sel  1:5 
                                

Inside of these buffers, we can use them just like normal. So we can switch between the different buffers with gn to go to the next one, and gp to go to the previous one.

Let’s make all of the buffers represent different files:

 file1  file2  file3            
    1  this is buffer #1        
    ~                           
                                
                                
 NOR   file1         1 sel  1:1 
    1  this is buffer #3        
    ~                           
                                
                                
       file3         1 sel  1:1 
    1  this is buffer #2        
    ~                           
                                
                                
                                
       file2         1 sel  1:1 
                                

Close them one-by-one by using Ctrl + w + q.

Opening splits from the command line

The --vsplit and --hsplit flags can be used with Helix to open all of the files as either vertical or horizontal splits.

Let’s quit completely with :xa yet again, and run the following command:

Terminal window
hx --vsplit file1 file2

That command opened two files, but this time in a vertical split instead of horizontal split.

    1  this is buffer #1            1  this is buffer #2       
    ~                               ~                          
                                                               
       file1         1 sel  1:1  NOR   file2        1 sel  1:1 
Loaded 2 files.                                                 

Let’s close the buffer #2 with Ctrl + w + q to explore our next command.

ga lets us goto the last accessed file. That is, consider this chain of actions:

  • Open file main.rs
  • Open file lib.rs, this is our current buffer.
  • Pressing ga will open the buffer corresponding to the main.rs
  • Pressing ga again will open the lib.rs buffer.
  • Pressing ga again will open the main.rs buffer.
  • Pressing ga again will open the lib.rs buffer.
  • Pressing ga again will open the main.rs buffer.

Next steps

Now that you know how to open multiple buffers and work with windows, let’s explore the various useful “pickers” available in Helix.

You may also be interested in the windows mode keymap reference, which includes a list of all the keymappings available to manipulate buffers.