• Stars
    star
    476
  • Rank 92,280 (Top 2 %)
  • Language
    Lua
  • License
    MIT License
  • Created over 1 year ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Open files and command output from wezterm, kitty, and neovim terminals in your current neovim instance

flatten.nvim

Flatten allows you to open files from a neovim terminal buffer in your current neovim instance instead of a nested one.

Features

  • Open files from terminal buffers without creating a nested session
  • Allow blocking for git commits
  • Configuration
    • Callbacks/hooks for user-specific workflows
    • Open in vsplit, split, tab, current window, or alternate window
  • Pipe from terminal into a new Neovim buffer (demo)
  • Setting to force blocking from the commandline, regardless of filetype
  • Command passthrough from guest to host

Plans and Ideas

Ideas:

  • Multi-screen support
    • Move buffers between Neovim instances in separate windows
    • Single cursor between Neovim instances in separate windows

If you have an idea or feature request, open an issue with the enhancement tag!

Demo

Flatten.demo.mp4

Config for demo here (autodelete gitcommit on write and toggling terminal are not defaults)

Installation1

With lazy.nvim:

{
    'willothy/flatten.nvim',
    config = true,
    -- or pass configuration with
    -- opts = {  }
    -- Ensure that it runs first to minimize delay when opening file from terminal
    lazy = false, priority = 1001,
}

To avoid loading plugins in guest sessions you can use the following in your config:

-- If opening from inside neovim terminal then do not load all the other plugins
if os.getenv("NVIM") ~= nil then
    require('lazy').setup {
        {'willothy/flatten.nvim', config = true },
    }
    return
end

-- Otherwise proceed as normal
require('lazy').setup( --[[ your normal config ]] )

Usage

# open files normally
nvim file1 file2

# force blocking for a file
nvim --cmd 'let g:flatten_wait=1' file1

# enable blocking for $VISUAL
# allows edit-exec
# in your .bashrc, .zshrc, etc.
export VISUAL="nvim --cmd 'let g:flatten_wait=1'"

# enable manpage formatting
export MANPAGER="nvim +Man!"

# execute a command in the **host**, *before* opening files
nvim --cmd <cmd>

# execute a command on the **host**, *after* opening files
nvim +<cmd>

Configuration

Defaults

Flatten comes with the following defaults:

{
    callbacks = {
        ---@param argv table a list of all the arguments in the nested session
        should_block = function(argv)
          return false
        end,
        -- Called when a request to edit file(s) is received
        pre_open = function() end,
        -- Called after a file is opened
        -- Passed the buf id, win id, and filetype of the new window
        post_open = function(bufnr, winnr, filetype) end,
        -- Called when a file is open in blocking mode, after it's done blocking
        -- (after bufdelete, bufunload, or quitpre for the blocking buffer)
        block_end = function() end,
    },
    -- <String, Bool> dictionary of filetypes that should be blocking
    block_for = {
        gitcommit = true
    },
    -- Command passthrough
    allow_cmd_passthrough = true,
    -- Allow a nested session to open if Neovim is opened without arguments
    nest_if_no_args = false,
    -- Window options
    window = {
        -- Options:
        -- current        -> open in current window (default)
        -- alternate      -> open in alternate window (recommended)
        -- tab            -> open in new tab
        -- split          -> open in split
        -- vsplit         -> open in vsplit
        -- func(new_file_names, argv, stdin_buf_id) -> only open the files, allowing you to handle window opening yourself.
        -- The first argument is an array of file names representing the newly opened files.
        -- The third argument is only provided when a buffer is created from stdin.
        -- IMPORTANT: For `block_for` to work, you need to return a buffer number.
        --            The `filetype` of this buffer will determine whether block should happen or not.
        open = "current",
        -- Affects which file gets focused when opening multiple at once
        -- Options:
        -- "first"        -> open first file of new files (default)
        -- "last"         -> open last file of new files
        focus = "first"
    }
}

Advanced configuration

If you use a toggleable terminal and don't want the new buffer(s) to be opened in your current window, you can use the alternate mode instead of current to open in your last window. With this method, the terminal doesn't need to be closed and re-opened as it did with the old example config.

The only reason 'alternate' isn't the default is to avoid breaking people's configs. It may become the default at some point if that's something that people ask for (e.g., open an issue if you want that, or comment on one if it exists).

Note that when opening a file in blocking mode, such as a git commit, the terminal will be inaccessible. You can get the filetype from the bufnr or filetype arguments of the post_open callback to only close the terminal for blocking files, and the block_end callback to reopen it afterwards.

Here's my setup for toggleterm, including an autocmd to automatically close a git commit buffer on write:

{
    'willothy/flatten.nvim',
    opts = {
        window = {
            open = "alternate"
        },
        callbacks = {
            should_block = function(argv)
                -- Note that argv contains all the parts of the CLI command, including
                -- Neovim's path, commands, options and files.
                -- See: :help v:argv

                -- In this case, we would block if we find the `-b` flag
                -- This allows you to use `nvim -b file1` instead of `nvim --cmd 'let g:flatten_wait=1' file1`
                return vim.tbl_contains(argv, "-b")

                -- Alternatively, we can block if we find the diff-mode option
                -- return vim.tbl_contains(argv, "-d")
            end,
            post_open = function(bufnr, winnr, ft, is_blocking)
                if is_blocking then
                    -- Hide the terminal while it's blocking
                    require("toggleterm").toggle(0)
                else
                    -- If it's a normal file, just switch to its window
                    vim.api.nvim_set_current_win(winnr)
                end

                -- If the file is a git commit, create one-shot autocmd to delete its buffer on write
                -- If you just want the toggleable terminal integration, ignore this bit
                if ft == "gitcommit" then
                    vim.api.nvim_create_autocmd(
                        "BufWritePost",
                        {
                            buffer = bufnr,
                            once = true,
                            callback = function()
                                -- This is a bit of a hack, but if you run bufdelete immediately
                                -- the shell can occasionally freeze
                                vim.defer_fn(
                                    function()
                                        vim.api.nvim_buf_delete(bufnr, {})
                                    end,
                                    50
                                )
                            end
                        }
                    )
                end
            end,
            block_end = function()
                -- After blocking ends (for a git commit, etc), reopen the terminal
                require("toggleterm").toggle(0)
            end
        }
    }
}

About

The name is inspired by the flatten function in Rust (and maybe other languages?), which flattens nested types (Option<Option<T>> -> Option<T>, etc).

The plugin itself is inspired by nvim-unception, which accomplishes the same goal but functions a bit differently and doesn't allow as much configuration.

Footnotes

  1. Lazy loading this plugin is not recommended - flatten should always be loaded as early as possible. Starting the host is essentially overhead-free other than the setup() function as it leverages the RPC server started on init by Neovim, and loading plugins before this in a guest session will only result in poor performance. ↩

More Repositories

1

nvim-cokeline

👃 A Neovim bufferline for people with addictive personalities
Lua
540
star
2

wezterm.nvim

Utilities for interacting with Wezterm from within Neovim
Lua
178
star
3

veil.nvim

A blazingly fast, animated, and infinitely customizeable startup / dashboard plugin (currently unmaintained, but with plans for a ground-up rewrite)
Lua
140
star
4

moveline.nvim

Neovim plugin for moving lines up and down
Rust
131
star
5

nvim-utils

Utilities and bindings for writing Neovim plugins in Rust
Rust
78
star
6

nvim-config

My personal Neovim config
Lua
59
star
7

strat-hero.nvim

Helldivers 2's "Strategem Hero" minigame for Neovim. Become a better Helldiver from the comfort of your favorite editor!
Lua
24
star
8

savior.nvim

Customizable, event-based auto saving for Neovim
Lua
22
star
9

luahint

LSP inline hints for Lua, intended for use with Neovim.
Rust
16
star
10

glass.nvim

A stateful windowing and animation plugin / library for Neovim (WIP)
Lua
8
star
11

dotfiles

Shell
7
star
12

sesh

Simple terminal session management (tmux but super mid), written in Rust btw
Rust
7
star
13

blam.nvim

A simple plugin for viewing git blame info, inspired by GitLens
Rust
6
star
14

Letter

Letter is a compiler project built in TypeScript using LLVM node bindings.
TypeScript
5
star
15

sharp

LLVM-Based procedural-ish programming language written in Rust, with Rust-like syntax and modules
Rust
4
star
16

sanguine

A library for creating dynamic TUI experiences in Rust
Rust
3
star
17

twitch.nvim

Highly experimental Twitch chat integration for Neovim
Rust
2
star
18

minimus

Personal color scheme
Lua
2
star
19

wrangler.nvim

Harpoon-like mark manager for Neovim, backed by SQLite (WIP)
Lua
2
star
20

micro-async.nvim

Ultra-simple async library for Neovim, with cancellation support
Lua
2
star
21

hollywood.nvim

📸 Action! A flexible, elegant and featureful code action menu (WIP)
Lua
2
star
22

worth

Compiler for the Porth programming language, targeting x86_64 assembly.
Rust
1
star
23

notify-ls.nvim

Experimental progress and notification reporting for Neovim using an in-process language server. Not at all ready for use.
Lua
1
star
24

lazyline.nvim

Lazy-loading statusline for Neovim
Lua
1
star
25

lua-lexer-deez

Simple transpiler in Lua (Luajit). Transpiles toy language into Lua. Incomplete, may revisit at some point.
Lua
1
star
26

neosuggest

WIP cross-shell autosuggestion engine
Rust
1
star
27

PeachOS

C
1
star
28

crates_cli

A command line interface for finding and managing Rust crates.
Rust
1
star
29

judge.lua

Lua
1
star
30

awesome-config

Lua
1
star
31

leptos.nvim

Experimental Lua/Nvim bindings for the leptos-reactive rust crate
Rust
1
star
32

winborder.nvim

Floating border surrounding the active split
Lua
1
star
33

crane-lang

WIP compiler targeting x86_64
Rust
1
star
34

.nix

Nix
1
star
35

goose

Good ol' OS experiment. Probably bad. I have no idea what I'm doing, but the goal is to have some idea when I'm done.
Rust
1
star
36

libsql-lua

Mlua-based Luajit bindings to tursodatabase/libsql. Synchronous only, for now. Entirely untested and not ready for use.
Rust
1
star