Vim Fennel
October 14, 2021

Horizontal Split Separators in Vim

Making default Vim look better

I’ve been playing around with Vim, it as an IDE for a while. So far I’m very happy with it. Sure it has a few problems. And sure, it’s not as stable as something like Intellij Idea. However, it makes up for it by offering extreme customizability and comfort.

I find that Vim’s simplicity shines in comparison with its competitors, the big, complex IDEs. Those often feel overwhelming and crowded, whereas Vim is minimal but similarly powerful (well, except for Kotlin development). Don’t get me wrong, having your editor directly run in your terminal has its benefits, but it also means that users are quite limited when tweaking its appearance.

In this post, I will explain how I configured my editor to display clean lines, instead of status bars all over the place.

Neovim’s default look

Vim’s default split separators look like this by default:

Default Nvim

As we can see, each window has its own status line bellow, which seems quite unnecessary. It is worth noting, that Vim provides the option fillchars to specify different characters used for the UI. The following piece of code changes the look of vertical separators. It makes everything look so much cleaner:

(vim.opt.fillchars:append "vert:│")

Vertical Separators

Horizontal separators

Great! so… Where is the problem you might ask:

Vim doesn’t have a concept of horizontal separators, it’s the status bar that marks the end of a window. This means that most users have to live with a bunch of bars all over the place, displaying information that is often irrelevant.

This is something I personally don’t like, having a status bar per split, only clutters the interface. Thus I had to figure out a way of clearing my screen of all those annoying things.

The first thought that went through my head was that I could replace the status bar with a horizontal straight line. Luckily for me, I was already using Feline. Feline, is a very flexible status bar plugin for Neovim. It is particularly interesting how it is configured, that is, by defining a set of providers that display isolated bits of information.

We can simply define a provider that fills the status bar with the character . We should take the width of our current split window so that the characters don’t overflow:

(defn inactive-separator-provider []
    (string.rep "─" (vim.api.nvim_win_get_width 0)))

We probably still want to display the status bar for the window which is focused. Feline has a specific setting to configure its appearance for inactive windows. Its called components.inactive and we can use it to establish which bars should be filled with dashes.

(tset components.inactive 1
     [{:provider inactive-separator-provider 
       :hl {:bg "NONE" :fg horiz-separator-color}}])

Finally, we must also override the background and foreground colors so that they look the same way the vertical separators do:

(feline.setup 
  {:default_hl  
    {:inactive 
      {:fg horiz-separator-color 
       :bg "NONE"}}
   :components components})

Horizontal Separators

Polishing the interface further

There’s still one thing that bothered me though, windows at the bottom of the screen don’t really need a separator below them. I actually tried two different approaches to solve this. The first one was checking the position of the window relative to the rest, by using its coordinates. It turned out to be a not-so-good idea, as it causes problems when we resize our terminal. The way I ended up going with was to use the winnr function to determine which windows where located at the bottom.

The function works as it follows:

winnr([{arg}])  The result is a Number, which is the number of the current
                window.  The top window has number 1.

                The optional argument {arg} supports the following values:
                        ...
                        {N}j    the number of the Nth window below the
                                current window (where CTRL-W_j goes to).
			...

If there isn’t any window next, the function will return the id of the current one, this way, we can add a check in our inactive-separator-provider:

(defn inactive-separator-provider []
  (if (not= (vim.fn.winnr) (vim.fn.winnr :j))
    (string.rep "─" (vim.api.nvim_win_get_width 0))
    ""))

The resulting UI something like this:

Final Result

Other options I’d like to try

There are still a few things that I haven’t gotten around to try:

Anyway, hope this helped, have a good day!