Rolv-Apneseth/tfm.nvim

github github
file-explorer
stars 62
issues 1
subscribers 2
forks 2
CREATED

2023-09-10

UPDATED

4 days ago


tfm.nvim

Neovim plugin for Terminal File Manager integration.

Demo video

Introduction

When I discovered ranger.nvim, it ended up replacing nvim-tree for me, which was great. My only issue was that I wasn't such a huge fan of ranger itself. In trying to find an alternative, I was having to switch plugins entirely for each new one I wanted to try out, which was not ideal and inspired me to create this.

With this plugin, you can simply change which TFM you wish to use (from the supported ones) in your configuration and you're good to go. It should also allow you to replace netrw.

I am using this plugin full time but I don't use all the different file managers and modes available so if you find any issues please let me know and I'll do my best to address it.

Configuration

The setup function is completely optional. However, please note that by default this plugin does not set any keymaps or create any commands, so it is recommended to do so in your configuration.

The below example configurations are given for lazy.nvim but feel free to use your desired plugin manager.

Minimal

{
    "rolv-apneseth/tfm.nvim",
    config = function()
        -- Set keymap so you can open the default terminal file manager (yazi)
        vim.api.nvim_set_keymap("n", "<leader>e", "", {
            noremap = true,
            callback = require("tfm").open,
        })
    end,
}

Full

{
    "rolv-apneseth/tfm.nvim",
    lazy = false,
    opts = {
        -- TFM to use
        -- Possible choices: "ranger" | "nnn" | "lf" | "vifm" | "yazi" (default)
        file_manager = "yazi",
        -- Replace netrw entirely
        -- Default: false
        replace_netrw = true,
        -- Enable creation of commands
        -- Default: false
        -- Commands:
        --   Tfm: selected file(s) will be opened in the current window
        --   TfmSplit: selected file(s) will be opened in a horizontal split
        --   TfmVsplit: selected file(s) will be opened in a vertical split
        --   TfmTabedit: selected file(s) will be opened in a new tab page
        enable_cmds = false, 
        -- Custom keybindings only applied within the TFM buffer
        -- Default: {}
        keybindings = {
            ["<ESC>"] = "q",
            -- Override the open mode (i.e. vertical/horizontal split, new tab)
            -- Tip: you can add an extra `<CR>` to the end of these to immediately open the selected file(s) (assuming the TFM uses `enter` to finalise selection)
            ["<C-v>"] = "<C-\\><C-O>:lua require('tfm').set_next_open_mode(require('tfm').OPEN_MODE.vsplit)<CR>",
            ["<C-x>"] = "<C-\\><C-O>:lua require('tfm').set_next_open_mode(require('tfm').OPEN_MODE.split)<CR>",
            ["<C-t>"] = "<C-\\><C-O>:lua require('tfm').set_next_open_mode(require('tfm').OPEN_MODE.tabedit)<CR>",
        },
        -- Customise UI. The below options are the default
        ui = {
            border = "rounded",
            height = 1,
            width = 1,
            x = 0.5,
            y = 0.5,
        },
    },
    keys = {
        -- Make sure to change these keybindings to your preference,
        -- and remove the ones you won't use
        {
            "<leader>e",
            ":Tfm<CR>",
            desc = "TFM",
        },
        {
            "<leader>mh",
            ":TfmSplit<CR>",
            desc = "TFM - horizontal split",
        },
        {
            "<leader>mv",
            ":TfmVsplit<CR>",
            desc = "TFM - vertical split",
        },
        {
            "<leader>mt",
            ":TfmTabedit<CR>",
            desc = "TFM - new tab",
        },
    },
}

Configuration - UI

Key Type Default Value
border string "none" See :h nvim_open_win.
height number 1 From 0 to 1 (0 = 0% of screen and 1 = 100% of screen).
width number 1 From 0 to 1 (0 = 0% of screen and 1 = 100% of screen).
x number 0.5 From 0 to 1 (0 = left most of screen and 1 = right most of screen).
y number 0.5 From 0 to 1 (0 = top most of screen and 1 = bottom most of screen).

API

open()

Opens the TFM, focusing the file from the current buffer, and falling back to the CWD if that is not possible.

open(path_to_open, open_mode)

Opens the TFM at the given destination. If the path is a file, focuses that file. Selected file(s) will be opened with the given mode.

  • Setting path_to_open to nil is equivalent to calling open()
  • open_mode should be an option from the enum defined below. Defaults to opening file(s) in the current window if an invalid option is received

select_file_manager(file_manager)

Changes the selected file manager. This is not persistent so the change will be lost when restarting Neovim.

  • file_manager should be one of the supported TFMs listed at the top of the page

set_next_open_mode(open_mode)

Changes the next mode with which to open/edit selected files. Can be run while the terminal window is open.

  • open_mode should be an option from the enum defined below.

enum OPEN_MODE

Enum to configure modes with which to open/edit selected files.

Variant Action
vsplit Open files in vertical split
split Open files in horizontal split
tabedit Open files in tab

Extras

Image previews

Image previews depend entirely on the terminal emulator + TFM combo you are using, as well as Neovim's support for the protocol being used. Any issues related to image previews are probably not related to this plugin as all this is doing is opening a terminal window in Neovim and running the TFM.

In the demo video, the combo being used is Wezterm + Yazi, using ueberzugpp.

Pure Lua keybindings

If you don't want to enable the commands, you can just use pure Lua keybindings:

    keys = {
        {
            "<leader>e",
            function()
                require("tfm").open()
            end,
            desc = "TFM",
        },
        {
            "<leader>mh",
            function()
                local tfm = require("tfm")
                tfm.open(nil, tfm.OPEN_MODE.split)
            end,
            desc = "TFM - horizontal split",
        },
        {
            "<leader>mv",
            function()
                local tfm = require("tfm")
                tfm.open(nil, tfm.OPEN_MODE.vsplit)
            end,
            desc = "TFM - vertical split",
        },
        {
            "<leader>mt",
            function()
                local tfm = require("tfm")
                tfm.open(nil, tfm.OPEN_MODE.tabedit)
            end,
            desc = "TFM - new tab",
        },
    },

Keybind to switch file manager

{
    "<leader>mc",
    function()
        require("tfm").select_file_manager(vim.fn.input("Change file manager: "))
    end,
    desc = "TFM - change selected file manager",
},

Contributing

Feel free let me know how I can improve this plugin by opening an issue. PRs are also welcome.

Credit

Other similar plugins

  • yazi.nvim
  • nnn.nvim
  • lf.nvim
  • rnvimr - for ranger as well, but actually uses RPC to communicate with it so it seems a good deal more complex than other similar plugins
  • fm-nvim - Very similar to this plugin, probably wish I had found this sooner lol. Unfortunately it does not seem to be actively maintained, and does not do some of the things I really wanted this to do, namely close buffers for files that were deleted and completely replace netrw. Also yazi is my favourite so far and that is unsupported.