Nev

Nev documentation

This file contains documentation about features that don’t fit into any of the other docs.

Layout

Nev has a configurable layout system. You can put tabs into splits, splits into tabs, and more.

You can also define multiple layouts and switch between them easily.

The following types of layout exist:

Different types of layouts can be nested in a tree structure.

Slots

Slots are used to specify where to open new views (files, terminals, etc.) or which view to focus.

To specify multiply slots for nested layouts, separate the slots with .. Say you have a tab layout which contains a center layout, then *.center would refer to the center slot in the active tab. Because the empty string acts the same as * you can also write this as .center

Slots for adding views to the layout

center:

vertical, horizontal, alternating and tab:

Example slots used for adding views to the layout:

Slots for identifying a view (used in commands like focus-view) in the center layout:

Slots for identifying a view (used in commands like focus-view) in the vertical, horizontal, alternating and tab layout:

You can also use ** as a slot to refer to the layout containing active view.

Named slots

You can defined named slots per layout in ui.layout.<layout-name>.slots.<slot-name> and then use #slot-name as a slot. This allows you to define e.g. keybindings using named slots so that they can work for different layouts.

Examples

Defining layouts in a settings file:

// settings.json
{
  "ui.layout.default": "vim",             // The name of the layout to use by default

  "ui.layout.vim": {                      // Name of the layout, can be anything. This layout is closest to Vim
    "slots.default": "**.*",              // The slot into which to add new views when you open them (insert new split in current tab)
    "slots.scratch-terminal": "**.*<>",   // Slot used to open scratch terminals
    "slots.build-run-terminal": "**.*<>", // Slot used to open terminals used for build or run tasks
    "slots.new-tab": "+",                 // Slot used to open new tabs
    "kind": "tab",                        // Root layout uses tabs
    "childTemplate": {                    // Layout config for tab contents
      "kind": "horizontal",               // Inside of each tab is an alternating layout
      "max-children": 1,                  // Only allow one view to be opened in this layout. If not specified then
                                          // there is no limit. Splits are created on demand, this is just the base.
    },
  },
  "ui.layout.tabs-in-splits": {
    "slots.default": ".+",                // Insert new tab in current split
    "kind": "alternating",                // Root layout uses alternating splits
    "childTemplate": {
      "kind": "tab",                      // inside of each split are tabs
    },
  },
  "ui.layout.vscode": {                   // This layout tries to imitate the VS Code layout
    "slots.default": "center.*.+",        // By default open views in a new tab of the active split of the center slot of the root layout
    "slots.scratch-terminal": "bottom.+", // Open scratch terminals in a new tab in the bottom slot of the root layout
    "slots.build-run-terminal": "left.+", // Open build/run terminals in a new tab in the left slot of the root layout
    "kind": "center",                     // Root layout
    "center": {                           // Specify which layout to use in the center
      "kind": "alternating",              // In the center we basically have the tabs-in-splits layout
      "childTemplate": {
        "kind": "tab",
      }
    },
    "bottom": {                           // In the bottom slot are just tabs
      "kind": "tab",
    },
    "left": {                             // In the left slot are just tabs
      "kind": "tab",
    },
    "right": {                            // In the right slot are just tabs
      "kind": "tab",
    },
  },
}

Vertical layout:

+---------+
| Child 0 |
+---------+
| Child 1 |
+---------+
| Child 2 |
+---------+

Horizontal layout:

+----+----+----+
| C0 | C1 | C2 |
+----+----+----+

Alternating layout:

+-------------------------+
|                         |
|         Child 0         |
|                         |
+-------------------------+
+------------+------------+
|            |            |
|   Child 0  |  Child 1   |
|            |            |
+------------+------------+
+------------+------------+
|            |  Child 1   |
|   Child 0  +------------+
|            |  Child 2   |
+------------+------------+

Tab layout:

+------------------------+
| [Tab0] [Tab1] [Tab2]   |
+------------------------+
|        Child n         |
+------------------------+

Center layout:

+---------+-----------+---------+
|         |   Top     |         |
|         +-----------+         |
|  Left   |  Center   | Right   |
|         +-----------+         |
|         |  Bottom   |         |
+---------+-----------+---------+

With empty top and right slots:
+---------+-----------+
|         |           |
|         |  Center   |
|  Left   |           |
|         +-----------+
|         |  Bottom   |
+---------+-----------+

Nested layouts:

Layout config:
{
  "kind": "center",
  "center.kind": "alternating",
  "bottom.kind": "tab",
}
+---------------------------------------------------------------+
|             |                         |                       |
|             |                         |                       |
|             |                         |                       |
|             |                         |                       |
|             |        Child 0          |     Child 1           |
|             |        of horizontal    |     of horizontal     |
|             |                         |                       |
|    Left     |                         |                       |
|             |                         |                       |
|             |-------------------------------------------------|
|             | [Tab 0] [Tab 1]                                 |
|             |-------------------------------------------------|
|             |                                                 |
|             |                   Child of tab                  |
|             |                                                 |
+---------------------------------------------------------------+

Arbitrary splits

To create arbitrary splits like in e.g. Vim you can use the wrap-layout command to wrap the current view in either a horizontal or vertical layout, and specify the temporary flag so that the layout is automatically replaced by it’s last remaining child if you close a child view and only one child remains.

Example keybindings:

// keybindings.json
{
  "editor": {
    // Create a vertical split which is closed automatically when closing the second last child
    "<A-v>": ["wrap-layout", {"kind": "vertical", "temporary": true, "max-children": 2}],
    // Create a horizontal split which is closed automatically when closing the second last child
    "<A-h>": ["wrap-layout", {"kind": "horizontal", "temporary": true, "max-children": 2}],
  }
}

If all you want is splits, you can define your base layout like this and use keybindings like above to create splits on demand:

// settings.json
{
  "ui.layout.raw-splits": {
    "slots.default": "**.*", // Open new views in the active view
    "slots.scratch-terminal": "**.*<>", // Open scratch terminals in a neighboring view (if one exists) or the current view
    "slots.build-run-terminal": "**.*<>",
    "kind": "horizontal",
    "max-children": 1,
  },
}

Commands

There are multiple commands used to manipulate the layout (change active view, open/close/hide/move/resize views, etc.):

Command aliases

To create an alias for a command add this to a config file:

// settings.json
{
  "alias.q": "quit",
  "alias.wq": ["write-file", "quit"],

  // alternative syntax (use + to add new aliases without deleting existing ones defined in prior configs):
  "+alias": {
    "q": "quit",
    "wq": ["write-file", "quit"],
  }
}

This defines two aliases w and wq. When you run the w command it will run quit, and if you run wq then it will run write-file and then quit.

Aliases can use other aliases, so the following is possible:

// settings.json
{
  "alias.q": "quit",
  "alias.wq": ["write-file", "q"],
}

Aliases can be bound to keys, so the following will run the wq alias when pressing <SPACE>wq:

// keybindings.json
{
  "editor": {
    "<SPACE>wq": "wq",
  },
}

You can specify parameters and forward parameters in aliases:

// settings.json
{
  "alias.q": "quit 1",
  "alias.wq": ["write-file @0", "quit @1"],
  "alias.echo": [
    // the echo-args command just logs all arguments to the log file
    "echo-args @0 @1",
    "echo-args @",
    "echo-args @@"
  ],
}

@@ refers to all arguments, @n refers to the nth argument and @ refers to the remaining arguments after the previous @n (or all arguments if there is no @n before)

The remaining arguments for @ are tracked across multiple commands, so in this example the second echo-args command doesn’t receive any arguments if you only pass two arguments to the alias, because the first command already consumes both.

You can use the same indices for @n multiple times.

Here are some examples of running these aliases and the commands that will be executed:

Terminal

Nev has a builtin terminal. To create a terminal view there are two commands:

Controls

The terminal by default has two modes, “normal” and “insert”. In insert mode almost every key press is forwarded to the terminal by default. You can still override keys in insert mode (and by default F9 is mapped to exit to normal mode).

In normal mode only some keys are send to the terminal by default (e.g. arrow keys, home/end, enter, i/I/a/A to enter insert mode, etc.)

Open hidden terminals

To open a hidden terminal you can’t use the choose-open command, as that command only shows files you have open. You need to use the select-terminal command instead (<SPACE>to by default, to standing for “terminal open”).

The select-terminal command show all hidden terminals, and allows you to use the terminal directly from within the popup by focusing the preview using <TAB>.

To open the terminal in the main view just press <ENTER> or <C-y>.

Defining shells

To use the run-in-terminal command you need to define the shell command in the settings.

The following shells are already defined by default: bash, sh, zsh, powershell, wsl and default. default is bash on Linux and powershell on windows.

Additional shells can be specified like this:

// settings.json
{
  "editor.shells.bash.command": "/path/to/bash", // Specify a full path
  "editor.shells.zsh.command": "zsh" // Use system $PATH
  "editor.shells.bash-no-profile.command": "bash --noprofile" // Pass command line arguments
}

You can the use a shell like this: run-in-terminal "bash-no-profile" "ls"

Examples

Open a scratch terminal using a key combination

// keybindings.json
{
  "editor": {
    "<SPACE>ts": [
      "run-in-terminal",
      "bash",
      "", // Empty command, so the terminal is just opened and ready for you to enter something.
      {
        "mode": "insert",
        "closeOnTerminate": true,
        "group": "scratch",
      }
    ]
  }
}

With this configuration you can then do the following:

Run a build using a key combination

// keybindings.json
{
  "editor": {
    "<SPACE>zb": [
      "all", // Runs all arguments as commands, allowing you to bind multiple commands in a single key combination.
      [
        "run-in-terminal",
        "bash",
        "clear; nimble build", // Clear the screen, the run `nimble build`
        {
          "mode": "normal", // Set the terminal to normal mode
          "closeOnTerminate": false,
          "group": "build-run", // Group for reusing the terminal when building
        }
      ]
    ]
  }
}

Force open a new terminal running NeoVim

// keybindings.json
{
  "editor": {
    "<SPACE>tn": [
      "create-terminal",
      "bash",
      {
        "autoRunCommand": "nvim ; exit" // Run NeoVim, then exit the shell.
      }
    ]
  }
}