kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal

Home Page:https://sw.kovidgoyal.net/kitty/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature Request: Ability to select text with the keyboard (vim-like)

pjrt opened this issue · comments

urxvt has the ability to select text using just the keyboard. By hitting a key you enter into something akin to Vim's normal mode (where you can move the cursor with your keyboard and select text using v). I found this to be a great productivity boost and would be great if kitty supported it as well.

You can open the contents of the kitty screen/scrollback buffer in any arbitrary program
you like, including vimpager. https://sw.kovidgoyal.net/kitty/conf.html#shortcut-kitty.Browse scrollback buffer in less

vimpager doesn't really allow for vim-like selection (or at the very least I can't figure out if it does).

But I was able to do so with neovim and its pager mode. For future reference:

scrollback_pager nvim -c 'set ft=man' -

It isn't as fast as urxvt (since it needs to launch a program), nor are pretty (urxvt just "uncages" the cursor), but it will will do for now.

Still think it would be a great feature to support natively.

To me the ability to use dedicated programs is far superior to using some kind of builtin mode. In your own example, you can take advantage of all the power of vim, which no dedicated mode is ever going to match.

I haven't used urxvt but I highly doubt it supports the level of sophistication in searching/cursor movement/etc. that vim does.

It does not, of course. But that wouldn't be the point, the point would be speed. Opening anything (except for many less) is gonna take several hundreds of millis.

really? vim opens on my system in 27 milliseconds

time vim -u NONE -c exit 
0.02s user 0.01s system 96% cpu 0.027 total

yeah I just figured out that I was missing -u NONE. Which makes sense, my plugins are gonna slow things down. Ok, this should be enough. Thanks!

Unfortunately with -u NONE you get ANSI color codes breaking everything up. I can't find a way to get rid of those without loading packages and slowing down startup. Any suggestions on how to proceed?

This has my vote for a feature request: AFAIK there aren't any good options when it comes to pagers that allow native selection and are fast enough on startup.

I seriously doubt that loading the package to parse ansi color codes could materially affect startup times. But even if it did, use the pipe function in kitty to pipe plain text without ansi codes instead. Then you can use -u NONE

I've been using scrollback_pager vim -u NONE so far and it works fine except for the ANSI codes mentioned earlier, but I don't think you can enable raw text with this option. So I tried using map f1 pipe @ansi overlay less +G -R as in the docs but it does nothing without any errors. I've tried adding @screen and @text as well with no luck.

Additionally, using this method the cursor always starts on the beginning of scroll-back instead of at the last prompt, you have to type three keys to get out :q!, and using text editors as pagers generally doesn't work very well.

that stack exchange post is just a bunch of hand waving about how vim wont work well in some terminals as a pager. It works fine in kitty. And the example command using pipe works fine for me. You are probably confused by the fact that on running it the screen does not change, since it basically shows you the screne contents but inside less.

and it is trivial to pass commands to vim on startup if you need the cursor to go to the bottom. One of the many advantages of using something as powerful as vim as apager.

I can understand your position on not including a copy mode implementation in kitty. Different people would want different things and it would either evolve into creeping featurism or leave some people dissatisfied.

However, in order to say that it’s trivial to implement using an external program in a scrollback_pager or pipe command, kitty should pass to that program at least the following:

  • Scrollback buffer text (optionally with formatting) — via stdin
  • Scroll position — INPUT_LINE_NUMBER in scrollback_pager
  • Cursor position, so that the external program would be able to start out with the screen laid out exactly the same way as it was

The external program can then implement its own ideas of cursor motion, selection, and confirmation or cancellation. It will also have to solve the problem of copying the selection to the clipboard and having it persist beyond its own termination — possibly by spawning nohup xclip.

I think it would be pretty easy to extend Window.show_scrollback to pass cursor position analogously to INPUT_LINE_NUMBER. I must ask, however, why is that implemented as text substitution? I’d probably choose carefully named environment variables.

Also, scrollback_pager looks very similar to pipe @ansi overlay; would it be a good idea to extend pipe so that it would be able to replace scrollback_pager completely?

Cursor position can be queried via escape codes

Hm, is that right? I assumed the commands run by scrollback_pager and pipe start out in a new window, with its own cursor position (initially at 0, 0).

The external program […] will also have to solve the problem of copying the selection to the clipboard and having it persist beyond its own termination — possibly by spawning nohup xclip.

There is no such problem, it can use the OSC 52 escape code to have kitty do the copying for it.

Assuming the clipboard_control setting allows it. And there are reasons to block both reading and writing the clipboard. While allowing reading exposes the user to information disclosure, allowing writing sets up denial of service: a malicious program can repeatedly set the clipboard to a random value, thereby preventing its normal use.

Or are these windows exempt from clipboard_control protection?

why is that implemented as text substitution? I’d probably choose carefully named environment variables.

Because scrollback_pager is meant to work with external programs not designed for it, like less. less needs it as a command line argument, not an env var.

Environment variables would work if scrollback_pager and pipe started their external programs via shell, or by using shell explicitly:

scrollback_pager $SHELL -c "less -RS +$KITTY_TOP_LINE"

Oh and incidentally, pipe is not a replacement for show_scrollback. That is because currently show_scrollback takes lines with hard line-breaks at screen boundaries as input, while pipe takes lines with hard linebreaks only at actual line boundaries at input. In other words pipe takes geometry independent data while show_scrollback is mostly designed for use only in overlays which have the same geometry as the underlying window. This also means that it is not really possible to pass in scroll position/cursor position to pipe since they dont correspond to the input.

And I just committed some changes to pipe to make all we have discussed possible.

There's also vim -u NORC instead of NONE which provides some more features while still having a fast startup.

-u {vimrc}      [...]
                When {vimrc} is equal to "NORC" (all uppercase), this has the
                same effect as "NONE", but plugins and syntax highlighting are
                not skipped.
--noplugin      Skip loading plugins.  Resets the 'loadplugins' option.
                Note that the -u argument may also disable loading plugins:
                        argument        load vimrc files        load plugins
                        (nothing)               yes                 yes
                        -u NONE                 no                  no
                        -u NORC                 no                  yes
                        --noplugin              yes                 no

I have implemented a keyboard-driven screen grabber for Kitty as a kitten.

@pjrt @RosalesJ @yurikhan @lithammer

Sorry to ping you directly like this but I'm stuck and could use your help if you have been successful here.

I'm trying to use neovim (or vim if I have to) as a scrollback pager in kitty. I tried a few things with various amounts of luck:

# Works but ANSI escape sequences clutter the actual text
scrollback_pager vim -

# Does not work; screen just flashes
scrollback_pager nvim

# Does not work; screen just flashes
scrollback_pager nvim -c 'set ft=man'

# Works but loses text coloring
map kitty_mod+h launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay vim -c "normal G" -c "set nonumber nolist" -

If you can answer:

  • How does your scrollback pager setup look?
  • Have you been able to use neovim as a scrollback pager? I would prefer to use it over vim
  • Have you been able to retain the text coloring? May be via the vim AnsiEsc plugin? Is this plugin reliable?

Thanks!

Sorry @Asheq this was a while ago so idk if what I said said still applies.

I just ended up going back to urxvt.

Unfortunately I'm using the default pager (i.e. less). But searching for "scrollback_pager nvim" here on GitHub reveals quite a few elaborate setups. Maybe try one of those? Otherwise it might be a good idea to raise an issue over at Neovim.

I do not use a scrollback pager. If I need to read something a few pages back, I just scroll (which I have bound to Shift+PgUp/PgDn). If I need to copy something (which is the topic of this issue), I use the kitten I’ve linked to in my previous comment.

Before I wrote that kitten, I tried adapting vim for the purpose. I was similarly discontent with the loss of color, so I looked at AnsiEsc and Improved AnsiEsc. When I found out that Improved means it handles 16 colors, I said “FFFFUUUUUUUUUU” and dropped the whole idea. I don’t even like vim; why would I use it for scrollback pager if it does not make 24-bit color support ridiculously easy?

And, lastly, yes, my kitten retains full color, by working directly with Kitty’s representation of colored text and not trying to shoehorn that into a mechanism designed primarily for programming language syntax highlighting.

Sorry @Asheq this was a while ago so idk if what I said said still applies.

I just ended up going back to urxvt.

Thanks @pjrt. I'm curious: do you use vim as a scrollback pager in urxvt? Or does urxvt have some sort of built-in pseudo-vim mode?

Unfortunately I'm using the default pager (i.e. less). But searching for "scrollback_pager nvim" here on GitHub reveals quite a few elaborate setups. Maybe try one of those? Otherwise it might be a good idea to raise an issue over at Neovim.

Thanks @lithammer. I tried a couple of those examples and am still getting a flash. I'll try more thoroughly later when I get a chance and raise an issue in neovim if need be.

I noticed an example that executes the vim command ":set ft=terminal", which made me realize that it would make a lot of sense to call into neovim's built-in terminal for a task like this since it would understand ANSI escape codes (without using a "hack" like the AnsiEsc vim plugin).

@Asheq yeah. there is a plugin in urxvt called keyboard-select which allows u to unhatch the cursor from the command line and move it up and down like VIM.

With this u can highlight and scroll up and down. And even search in the terminal.

I do not use a scrollback pager. If I need to read something a few pages back, I just scroll (which I have bound to Shift+PgUp/PgDn). If I need to copy something (which is the topic of this issue), I use the kitten I’ve linked to in my previous comment.

Before I wrote that kitten, I tried adapting vim for the purpose. I was similarly discontent with the loss of color, so I looked at AnsiEsc and Improved AnsiEsc. When I found out that Improved means it handles 16 colors, I said “FFFFUUUUUUUUUU” and dropped the whole idea. I don’t even like vim; why would I use it for scrollback pager if it does not make 24-bit color support ridiculously easy?

And, lastly, yes, my kitten retains full color, by working directly with Kitty’s representation of colored text and not trying to shoehorn that into a mechanism designed primarily for programming language syntax highlighting.

@yurikhan Thanks! I'll try the kitten that you wrote. However, I have a highly-customized vim that I would really like to use because it would be impossible to replicate that experience down to every key-stroke in something else.

I haven't tried it, but the AnsiEsc plugin sounds a bit like a hack. I think the ideal solution should somehow make use of the built-in vim/neovim terminal that already understands ANSI escape codes. Not sure how to do it yet; I might have to open an issue in the vim/neovim repos.

I don’t even like vim

I'm curious: what editor do you use instead?

I'm curious: what editor do you use instead?

Why, Emacs, of course.

@pjrt @yurikhan @lithammer

If you are curious, I got it working using neovim with text formatting/coloring intact. I have a long, complicated command and am still working on perfecting it, but here it is so far:

scrollback_pager /usr/local/bin/nvim  -c "set nonumber nolist showtabline=0 foldcolumn=0" -c "autocmd TermOpen * normal G" -c "silent write /tmp/kitty_scrollback_buffer | te cat /tmp/kitty_scrollback_buffer - "

I needed to provide the full path to the nvim binary. The built-in neovim terminal handles the ANSI escape codes.

example

This worked for me with vim 8.2 (I think you need at least 8.1):

scrollback_pager vim - -c 'w! /tmp/kitty_scrollback' -c 'term ++curwin cat /tmp/kitty_scrollback'

@Asheq, that's awesome! Just wondering how could we automatically delete the existing /tmp/kitty_scrollback_buffer file before opening?

Error detected while processing command line:                                                                                                                                                                                
E13: File exists (add ! to override)                                                                                                                                                                                         
Press ENTER or type command to continue

on second open attempt. What I'm doing now is a manual rm /tmp/kitty_scrollback_buffer. Before I open the scrollback pager.

@suessflorian: You can do it with one of these:

scrollback_pager vim - -c 'set nonumber' -c 'w! /tmp/kitty_scrollback' -c 'term ++curwin sh -c "head -c-1 /tmp/kitty_scrollback; rm /tmp/kitty_scrollback"'
scrollback_pager nvim -c "set nonumber nolist showtabline=0 foldcolumn=0 laststatus=0" -c "autocmd TermOpen * normal G" -c "silent write! /tmp/kitty_scrollback_buffer | te head -c-1 /tmp/kitty_scrollback_buffer; rm /tmp/kitty_scrollback_buffer; cat"

It's the commands from @Asheq and @i-tub modified to delete the file, and to suppress the blank line which was at the end. To fix the error you got, it would have been enough to just add ! after write, but these commands additionally deletes the file right after it has been read, so your backlog doesn't remain lying around in /tmp.

If anyone is wondering how to copy from this vim pager and keep the clipboard contents after exiting vim, you can use https://github.com/chromium/hterm/blob/master/etc/osc52.vim. Just run the source and vmap commands from the top of that file.

@Asheq Thanks for this scrollback_pager command. How do you switch to the pager mode?


Update: I found it. Add the following setting to kitty.conf

map f6 show_scrollback

I would also like this feature, but I don't use vim, is there any way to integrate those scripts into something like micro or atom?
Having the output of the terminal show up in my text editor would be neat.

I tried a few of the nvim suggestions on this page but most of them didn't work for one reason or another. Here's my tweaks to Asheq's config:

scrollback_pager /usr/local/bin/nvim -u NONE -c "set nonumber nolist showtabline=0 foldcolumn=0" -c "autocmd TermOpen * normal G" -c "map q :qa!<CR>" -c "set clipboard+=unnamedplus" -c "silent write! /tmp/kitty_scrollback_buffer | te cat /tmp/kitty_scrollback_buffer - "

command explained:

scrollback_pager /usr/local/bin/nvim

this is the original invocation of nvim, using the full path because Asheq mentioned it explicitly

-u NONE

Added this -u NONE to stop nvim from loading any of my plugins/config, one of them was throwing an error halting the entire process from rendering my scrollback

-c "set nonumber nolist showtabline=0 foldcolumn=0"

This is from the original, it strips out various vim things so your scrollback buffer looks as close as possible to what your terminal looked like a moment ago before invoking the pager

-c "autocmd TermOpen * normal G"

I don't actually know what this does! haha, the embedded terminal features of nvim are still outside of my understanding.

-c "map q :qa!<CR>"

Added this mapping so I can hit q and have it quit the pager, just like in less. I definitely do not need vim's macro-mapping when i'm scrollin up through terminal output.

-c "set clipboard+=unnamedplus"

nvim, as installed by homebrew on osx supports the osx clipboard by default. it's kept in the + register, but if you set this config then all yanks/pastes will automatically sync to your osx clipboard.

-c "silent write! /tmp/kitty_scrollback_buffer | te cat /tmp/kitty_scrollback_buffer - "

I don't really know what this does, but i had to change write to write! so that it was possible to enter the scrollback buffer more than once a day, lol. I don't know what the second half does at all.

Now I can enter nvim to navigate the scrollback, see all the colors and stuff, yank as normal, and hit q to simply pop out just like in other pagers!

Thank you @cpdean !

-c "autocmd TermOpen * normal G"

I don't actually know what this does! haha, the embedded terminal features of nvim are still outside of my understanding.

This will run G command (go to last line) when the terminal jobs starts. The te in te cat /tmp/... command is a shorter way of running terminal cat /tmp/... which starts a terminal job.

My version which has laststatus=0 and te echo -n \"$(cat /tmp/kitty_scrollback_buffer)\" && sleep 1000 to switch seamlessly(no extra newlines):

scrollback_pager /usr/local/bin/nvim -u NONE -c "set nonumber nolist showtabline=0 foldcolumn=0 laststatus=0" -c "autocmd TermOpen * normal G" -c "map q :qa!<CR>" -c "set clipboard+=unnamedplus" -c "silent write! /tmp/kitty_scrollback_buffer | te echo -n \"$(cat /tmp/kitty_scrollback_buffer)\" && sleep 1000 "

Explain:

laststatus=0

Hide the status bar.

te echo -n "$(cat /tmp/kitty_scrollback_buffer)" && sleep 1000

echo -n removes the newline from cat command. sleep to avoid Process exited 0 from nvim terminal.

commented

I'm trying to replace tmux capture-pane with kitty scrollback.
Does anyone successful to get unwrapped lines?
tmux capture-panes can control line wrap.

My solution for this is to use term_start(). While researching I stumbled also upon vim's innate support for less mode ($VIMRUNTIME/macros/less.sh and $VIMRUNTIME/macros/less.vim). However it was not what I was looking for, as it is a stripped down vim to be used as a pager, whereas I was looking for something that behaves like a pager, but has most of the vim functionality.

So I ended cooking my own recipe:

scrollback_pager vimx --cmd 'let no_plugin_maps = 1' -c 'let g:lz = &lz | set lz | au VimEnter * noremap q :q<CR> | set nomodified | call term_start("cat -", {"curwin": 1, "norestore": 1, "in_io": "buffer", "in_buf": 1, "eof_chars": ""}) | redraw | let &lz = g:lz' -

The main mechanism is as follows:

  • vim - loads the contents from stdin raw in a new buffer 1 and we set it to unmodified so we can continue
  • term_start() creates a new terminal in the current window and executes cat -
  • the stdin from the new terminal is set to read from the already existing buffer 1 having the raw text with escapes

On top of this there is the icing on the cake:

  • we disable plugin maps as in less.sh
  • we use lazy redraw to increase the speed (and restore it at the end)
  • we can exit quickly by pressing q only
  • we do not store the terminal in any session
  • we do not send any additional characters (normally it sends a ^D) to prevent getting an additional line at the end

Note: vimx is just Fedora's packaged version of vim with +X11 and +xterm_clipboard compiled in.

@aca i think it is not currently possible to remove the linewrap that is flushed to the scrollback_pager you set. newlines are added to make things work for less #3141

I would also like this feature, but I don't use vim, is there any way to integrate those scripts into something like micro or atom?
Having the output of the terminal show up in my text editor would be neat.

Here's my solution to open this is micro:

# open scrollback buffer in micro in the last possible line (HISTSIZE=2500 in zsh)
map kitty_mod+b launch --type overlay --stdin-source=@screen_scrollback micro +2500:1

However sometimes I just want to easily copy the command I just wrote in the terminal, is it possible to add the shift key as a "selector" and allow it to select as you move around the text, for instance pressing something like shift+ctrl+left would select the word to the left or shift+home select from the starting point to the beginning of the line?
This would probably be the behavior most people expect from using a standard text editor and its rather convenient, considering I'm having trouble implementing such in zsh.

I would also like this feature, but I don't use vim, is there any way to integrate those scripts into something like micro or atom?
Having the output of the terminal show up in my text editor would be neat.

Here's my solution to open this is micro:

# open scrollback buffer in micro in the last possible line (HISTSIZE=2500 in zsh)
map kitty_mod+b launch --type overlay --stdin-source=@screen_scrollback micro +2500:1

However sometimes I just want to easily copy the command I just wrote in the terminal, is it possible to add the shift key as a "selector" and allow it to select as you move around the text, for instance pressing something like shift+ctrl+left would select the word to the left or shift+home select from the starting point to the beginning of the line?
This would probably be the behavior most people expect from using a standard text editor and its rather convenient, considering I'm having trouble implementing such in zsh.

I second this, it is the most intuitive way of selecting text and using it easily.

My take for neovim (mostly a compilation from the excellent previous answers, but with a twist):

scrollback_pager nvim -u NONE -c "set laststatus=0 clipboard=unnamedplus" -c "autocmd TermOpen * normal G" -c "map q :qa!<CR>" -c "silent write! /tmp/kitty_scrollback_buffer | te echo -n \"$(sed -e 's/\\^[]8;;file:[^\\\\]*\\\\//g' /tmp/kitty_scrollback_buffer)\"; rm -f /tmp/kitty_scrollback_buffer; sleep 1000"
  • Note, there is a special character here. Copy this to kitty.conf in vim/neovim, delete '^[' and instead press 'Ctrl+V', release and press 'Escape'

I have the following alias to use hyperlinking and colors in ls:

ls='ls --color=auto --hyperlink=auto'

The hyperlinking results in additional characters added to the temporary file, sometimes causing characters that follow them to be recognized as part of an escape sequence and to be swallowed. For ex., the real content:
image
but in scrollback, using cat command:
image
I prevented this with sed

Here's my take on using vim as a pager but without using any temporary files :)

# Scrollback buffer
# https://sw.kovidgoyal.net/kitty/overview/#the-scrollback-buffer
# `bash -c '...'` Run everything in a shell taking the scrollback content on stdin
# `-u NORC` Load plugins but not initialization files
# `-c "map q :qa!<CR>"` Close with `q` key
# `-c "autocmd TermOpen * normal G"` On opening of the embedded terminal go to last line
# `-c "terminal cat /proc/$$/fd/0 -"` Open the embedded terminal and read stdin of the shell
# `-c "set clipboard+=unnamedplus"` Always use clipboard to yank/put instead of having to specify +
scrollback_pager bash -c 'nvim </dev/null -u NORC -c "map q :qa!<CR>" -c "autocmd TermOpen * normal G" -c "terminal cat /proc/$$/fd/0 -" -c "set clipboard+=unnamedplus" -c "call cursor(CURSOR_LINE, CURSOR_COLUMN)"'

@grawp’s suggestion above was pretty good. But when I moved to a Mac, I realized I hadn't noticed that it’s Linux-specific because it relies on ProcFS.

So I have the following incremental improvement that I believe does everything that’s been proposed in this discussion, but is also portable between both Linux and Mac:

scrollback_pager bash -c 'nvim -u NORC -c "map q :qa!<CR>" -c "terminal cat "<(cat)" - " -c "map i <Nop>" -c "set clipboard+=unnamedplus" -c "call cursor(CURSOR_LINE, CURSOR_COLUMN)" '

Here’s some explanation of the parts:

  • scrollback_pager bash -c ‘: running within Bash to take advantage of its process substitution
  • nvim: Neovim seems to have good support for both Linux and Mac clipboards without needing a plugin
  • -u NORC: don’t want Neovim to load up the user’s configuration
  • -c “map q :qa!<CR>“: makes quitting as easy as hitting q
  • -c “terminal cat “<(cat)" - ": uses Bash process substitution to get a file descriptor populated by cat getting the scrollback from stdin; this file descriptor is then cat’d to make a terminal, which gives us good processing of things like ANSI color codes, and we also have cat pull from stdin, which just keeps the cat process alive until we quit
  • -c “map i <Nop>“: we’re only using the terminal for rendering, and don’t want to actually insert into it, so preventing that by unbinding i
  • -c “set clipboard+=unnamedplus”: makes it so we don’t need to mess with selection registers to get to the system's clipboard
  • -c “call cursor(CURSOR_LINE, CURSOR_COLUMN)” ’: puts the cursor in a good place to start.

My version which has laststatus=0 and te echo -n \"$(cat /tmp/kitty_scrollback_buffer)\" && sleep 1000 to switch seamlessly(no extra newlines):

scrollback_pager /usr/local/bin/nvim -u NONE -c "set nonumber nolist showtabline=0 foldcolumn=0 laststatus=0" -c "autocmd TermOpen * normal G" -c "map q :qa!<CR>" -c "set clipboard+=unnamedplus" -c "silent write! /tmp/kitty_scrollback_buffer | te echo -n \"$(cat /tmp/kitty_scrollback_buffer)\" && sleep 1000 "

Explain:

laststatus=0

Hide the status bar.

te echo -n "$(cat /tmp/kitty_scrollback_buffer)" && sleep 1000

echo -n removes the newline from cat command. sleep to avoid Process exited 0 from nvim terminal.

this works very well for me.

If someone just copy-pasted the final nvim solution, like me, and it doesn't work, that's because in the code snippet "nice", typography style quotes and double quotes are used, just replace them with "normal", programming compatible ones and it'll work, well, at least once, then it'll start throwing this warning before opening scrollback and messing up the ascii codes :(
(I'm on Linux, latest OpenSuse Tumbleweed)

Error detected while processing command line:
E37: No write since last change (add ! to override)
Press ENTER or type command to continue

screenshot_20211025_104705

Thanks to all of you.

Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

  • Works on Linux and macOS.
  • No temporary files are written, which prevents sensitive information from being leaked to the local disk.
  • Only the Neovim process remains running after startup.
  • The cursor is correctly positioned after scrollback is fully loaded from stdin.
  • Prevents accidental switching to terminal insert mode.
  • Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed.
This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete.
Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

  • Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.
  • In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use autocmd TermClose * normal G instead to jump directly to the last line.
  • For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

If someone just copy-pasted the final nvim solution, like me, and it doesn't work, that's because in the code snippet "nice", typography style quotes and double quotes are used, just replace them with "normal", programming compatible ones and it'll work, well, at least once, then it'll start throwing this warning before opening scrollback and messing up the ascii codes :(
(I'm on Linux, latest OpenSuse Tumbleweed)

I edited my submission with friendlier quoting. But also good to see further contributions on top of mine.

A small commentary to circle back to the beginning of this issue's discussion. This issue was closed earlier on because it was claimed that we'd get better functionality delegating to Vim than having it builtin to Kitty. At this point, I don't think this argument is quite as clear-cut. It seems much more to me a design choice. Alacritty gives a built-in Vi-mode. This mode isn't really a full-featured editor, but it's got the basics, and it pretty much works smoothly as advertised. But as of this thread, we seem to be bending over backwards to get Neovim to get us pretty much the same we get for free with Alacritty. The major benefits to me seem to be:

  • less code footprint for the maintainer(s) to maintain
  • the off-chance you use a Vim/Neovim feature for managing history that isn't included in Alacritty's Vi-mode.

Personally, I don't have too strong a preference for Alacritty over Kitty, or vice versa. Just being honest that figuring out how to get Vim/Neovim set up correctly has been more complicated than people may have thought coming into this.

@page-down thank you very much for the script! on my mac i'm getting bad file descriptor with it though. and pager is not running. could you please give me a hand with getting it working?

on my mac i'm getting bad file descriptor with it though. and pager is not running. could you please give me a hand with getting it working?

This indicates a problem related to the stdin redirect. Have you tried installing the latest version of bash? The version that comes with macOS is very old and may have problems.

Or try shajra's script to see if it works.

fwiw -- producing a vimulation that makes everyone happy is hard, having a concise integration method so people can use what they want is a lot more solid. I haven't been interpreting the length of this github issue as indicative of how much kitty needs a vim-like set of keyboard bindings for selecting and copying text. Instead it shows how large of a space in customization this one integration point offers, and I've been lurking in this thread for over a year enjoying learning about everyone else's approaches. It's taught me a lot of tricks about using vim as a tool that can be integrated with other systems instead of only using it stand-alone, and only using the terminal stand-alone. Demanding every stand-alone tool should have all the config options to make everyone happy is a losing battle, empowering people to build what they want from reusable parts is so much more sustainable.

For what it's worth: I'm still looking for a robust solution, for nvim v0.4.4 on Linux Mint 19.3.
I tested all the above. They either don't show anything, only work once per session, don't show colors or complain that "No write since last change (add ! to override)". :-/

You do know that nvim is on version 0.5.1 for a month or so and you may just download binary release of it or nightly from its GitHub page?

@igorepst Thanks. Yes, I know. But this commit neovim/neovim@6995fad broke my precious undo files, so I'm waiting for 0.6, which hopefully will be able to manage both undo files.

Thanks to all of you.

Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

* Works on Linux and macOS.

* No temporary files are written, which prevents sensitive information from being leaked to the local disk.

* Only the Neovim process remains running after startup.

* The cursor is correctly positioned after scrollback is fully loaded from stdin.

* Prevents accidental switching to terminal insert mode.

* Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed. This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete. Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

* Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.

* In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use `autocmd TermClose * normal G` instead to jump directly to the last line.

* For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

Awesome solution. I've been using it and it's flawless so far.

For my specific use case, I'd like to have some visual cue that the window is in scrollback buffer mode, is it possible to draw a different border or something?

I'd like to have some visual cue that the window is in scrollback buffer mode, is it possible to draw a different border or something?

I don't think kitty has a mechanism to change the window border color now.
Maybe you can draw Neovim's Status Line yourself, or change the background color.

@page-down I found this ticket where you posted a solution to make scrollback_pager work with neovim...
unfortunately, I tried this config in my macOS kitty but I am not able to launch scrollback_pager

I used this single liner config but tried with another script one also but both not working don't know why

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

... not able to launch scrollback_pager ... but both not working ...

Sorry I can't guide you from zero and send a lot of messages to the other 25 people in this issue. This reply is final.

How To Ask Questions The Smart Way
http://www.catb.org/~esr/faqs/smart-questions.html
See "Be precise and informative about your problem"

How to Report Bugs Effectively
https://www.chiark.greenend.org.uk/~sgtatham/bugs.html

... not able to launch scrollback_pager ... but both not working ...

Sorry I can't guide you from zero and send a lot of messages to the other 25 people in this issue. This reply is final.

How To Ask Questions The Smart Way http://www.catb.org/~esr/faqs/smart-questions.html See "Be precise and informative about your problem"

How to Report Bugs Effectively https://www.chiark.greenend.org.uk/~sgtatham/bugs.html

That's very much okay sir. Thank you

Thanks to all of you.

Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

  • Works on Linux and macOS.
  • No temporary files are written, which prevents sensitive information from being leaked to the local disk.
  • Only the Neovim process remains running after startup.
  • The cursor is correctly positioned after scrollback is fully loaded from stdin.
  • Prevents accidental switching to terminal insert mode.
  • Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed. This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete. Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

  • Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.
  • In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use autocmd TermClose * normal G instead to jump directly to the last line.
  • For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

I got super strange behaviour on my macbook.
After configuring with Single line version,
if I open kitty from terminal with open /Applications/kitty.app, then this scrollback works.
however if I open kitty by clicking the icon in Applications folder, this scrollback doesn't work, nothing happened.

Hi! I used the pager script describe above by @page-down it's kind of nice, but when I exit vim it output "Press Enter or Esc to exit" I guess it's probably because of kitty. How do I disable this message that require me to type extra keys. So that when I quit vim (the pager) it directly goes back to the shell without this intermediate sequence?

when I exit vim it output "Press Enter or Esc to exit" I guess it's probably because of kitty

It's actually because of how the vim pager workes. Neovim shows this when you exit the terminal mode. I haven't been able to figure out a way to get around it.

Thanks to all of you.
Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

  • Works on Linux and macOS.
  • No temporary files are written, which prevents sensitive information from being leaked to the local disk.
  • Only the Neovim process remains running after startup.
  • The cursor is correctly positioned after scrollback is fully loaded from stdin.
  • Prevents accidental switching to terminal insert mode.
  • Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed. This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete. Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

  • Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.
  • In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use autocmd TermClose * normal G instead to jump directly to the last line.
  • For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

I got super strange behaviour on my macbook. After configuring with Single line version, if I open kitty from terminal with open /Applications/kitty.app, then this scrollback works. however if I open kitty by clicking the icon in Applications folder, this scrollback doesn't work, nothing happened.

@xiangpeng2008
I got the same issue on my Macbook pro m1, super weird. I got it to work by implementing a solution based on this post.

In kitty.conf:
scrollback_pager nvim --noplugin -u ~/.config/kitty/scrollback-pager/nvim/init.vim -c "silent write! /tmp/kitty_scrollback_buffer | te cat /tmp/kitty_scrollback_buffer - "

And then in ~/.config/kitty/scrollback-pager/nvim/init.vim:

set relativenumber
set number
set mouse=a
set clipboard+=unnamedplus
set virtualedit=all
set scrollback=100000 
set termguicolors 
set laststatus=0
set background=dark
set ignorecase
set scrolloff=8

map <silent> q :qa!<CR>

" Short highlight on yanked text
augroup highlight_yank
    autocmd!
    autocmd TextYankPost * silent! lua require'vim.highlight'.on_yank({timeout = 40})
augroup END

augroup start_at_bottom
    autocmd!
    autocmd VimEnter * normal G
augroup END

augroup prevent_insert
    autocmd!
    autocmd TermEnter * stopinsert
augroup END

You can customize the init.vim however you'd like, hope this helps!

I frequently find myself immediately searching for text after opening the scrollback pager in nvim, which means I start typing '/' after opening the pager. Is there a way to automatically do this?

I'm assuming nvim needs to be run with some form of the -c command

I tried the below which doesn't work

nvim -c "autocmd VimEnter normal! /"
commented

#719 (comment)
This worked well for me for a while, but now it suddenly broke - when I press F1 kitty hangs and I have to force-close it:
image

Any idea what's going on?

EDIT: Suddenly working again, very weird... It was also not completely hanging, just neovim not responding to key input while it was waiting for data.

commented

What I am still wondering is why the latter was suggested up there - the former seems to be the inbuilt standard and actually scrolls to the right position to me.

map f1 show_scrollback
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/scrollback_pager

... it suddenly broke ...
... Suddenly working again ... neovim not responding to key input ...

Have you tried the latest neovim nightly? There are some recent fixes related to the kitty keyboard protocol.
If you exclude other configuration and runtime environment effects and the problem persists, then you can try to go through the temporary file approach.

The following is a neovim pager that uses temporary files:
https://github.com/lucc/nvimpager

What I am still wondering is why the latter was suggested up there ...
...
map f1 launch ...

The first way configures only scrollback_pager (single line), which is used with the action show_scrollback (default shortcut ctrl+shift+h).

The second way, on the other hand, will call the script saved as a file, so you need to use launch. And instead of changing the default scrollback pager, a dedicated shortcut key is set independently so that the default use of less as pager is not affected.

I have given two ways and you can choose whichever one suits you best.

commented

I've been having an issue recently, which also wasn't fixed by 0.26.4. When I copy things tot he unnamedplus register with Neovim and want to paste afterwards, I have to press Ctrl+v twice until it works. I think this is a Neovim issue. Does anyone else have this issue?

@page-down Is it possible to achieve the same behavior with standard Vim? I usually just use Vim and only use Neovim for this feature

commented

For those having your clipboard cleared when you close the window to the pager-down solution (thanks!) I've added the next flag:

-c 'autocmd VimLeave * call system(\"xclip -o -selection c | xclip -selection c\")'

You may need to play with xclip -o selection <register> to see which one is the good one for your (primary, secondary or clipboard)

Thanks to all of you.

Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

* Works on Linux and macOS.

* No temporary files are written, which prevents sensitive information from being leaked to the local disk.

* Only the Neovim process remains running after startup.

* The cursor is correctly positioned after scrollback is fully loaded from stdin.

* Prevents accidental switching to terminal insert mode.

* Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed. This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete. Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

* Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.

* In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use `autocmd TermClose * normal G` instead to jump directly to the last line.

* For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

For those having your clipboard cleared when you close the window to the pager-down solution (thanks!) I've added the next flag:
-c 'autocmd VimLeave * call system("xclip -o -selection c | xclip -selection c")'
You may need to play with xclip -o selection to see which one is the good one for your (primary, secondary or clipboard)

My problem was i had to wait like 3 seconds after pasting for the text to appear. This fixed it thanks!.

Hi! I used the pager script describe above by @page-down it's kind of nice, but when I exit vim it output "Press Enter or Esc to exit" I guess it's probably because of kitty. How do I disable this message that require me to type extra keys. So that when I quit vim (the pager) it directly goes back to the shell without this intermediate sequence?

Somebody has a solution for this?

when I exit vim it output "Press Enter or Esc to exit" I guess it's probably because of kitty
It's actually because of how the vim pager workes. Neovim shows this when you exit the terminal mode. I haven't been able to figure out a way to get around it.

I don't think so. I'm using moar as a pager and it still gives me that prompt.

Maybe moar has similar behavior, but that message definitely comes from neovim itself, not from kitty.

You can get the same message if you run some command in terminal mode once the command finishes.

Maybe moar has similar behavior, but that message definitely comes from neovim itself, not from kitty.

You can get the same message if you run some command in terminal mode once the command finishes.

Isn't it in line 26 of ./tools/tui/hold.go in kitty source code branch?

oh, I think you are right. I think I confused it with the (also annoying) "[Process exited 0]" at the end of output in :terminal.

oh, I think you are right. I think I confused it with the (also annoying) "[Process exited 0]" at the end of output in :terminal.

See #5945.

I agree that offloading this to a separate program is probably the right move from a design perspective. The problem is that at least for what I want, that separate program doesn't quite exist. Most pagers, like less, most, moar, vimpager, etc. don't have any way to select and copy text. But an editor like vim is difficult to use as a pager, see below.

The best solution I've found is the one in #719 (comment). But it is in the middle of a very long thread (and in fact Github hides the original post when you initial open this issue). However, it is far from trivial. At the very least I think it is worth mentioning this solution in the documentation, so it is more visible (maybe along with some other options for configuration scrollback pagers) and maybe including a similar script as part of the kitty distribution?

It still isn't perfect though. In particular I still have a couple of issues with it:

  1. It exploits undocumented and probably unintentional behavior of neovim, specifically that file descriptor 63 isn't closed. See neovim/neovim#16764. Relatedly using /dev/fd/63 only works on linux.
  2. After the terminal finishes, it adds a "[Process exited 0]" message to the end

If you don't like using neovim, write one, it should be a project of a few days for a motivated
individual. Or use one someone has written already like https://github.com/yurikhan/kitty_grab

Or wait until I get around to writing my less replacement.

... but when I exit vim it output "Press Enter or Esc to exit" ...

I forgot to mention in my original reply that the pager script should be given executable permission.
This message will not appear.

chmod +x ~/.config/kitty/pager.sh

... worth mentioning this solution in the documentation ...

I had considered posting it independently in GitHub Discussion, but decided it wasn't of sufficient quality to warrant a separate topic. This script is a workaround at best.

@tmccombs

... I still have a couple of issues with it:

  1. ... exploits undocumented and probably unintentional behavior of neovim ...

As long as it works, I don't care much. Until there is a "documented behavior".

... Relatedly using /dev/fd/63 only works on linux.

I actually use it under macOS. At least it works in my environment.

  1. ... it adds a "[Process exited 0]" message ...

This has been explicitly solved by an uncompleted escape sequence, which is of course an imperfect solution.
The neovim terminal is ended immediately, so there is no harm.

I agree that offloading this to a separate program is probably the right move from a design perspective. The problem is that at least for what I want, that separate program doesn't quite exist. Most pagers, like less, most, moar, vimpager, etc. don't have any way to select and copy text. But an editor like vim is difficult to use as a pager, see below.

nvimpager allows you to select text and copy to clipboard, provided you always use the pager mode with -p

You don't even need to use a nvimpager. You can use nvim directly. It can read from stdin and has scrolling bindings. Maybe with -u NONE if you don't need the plugins.

If you don't like using neovim

I do like using neovim. The problem is that using it as a pager for this is, perhaps surprisingly, rather difficult to get right. Unless you don't care about colors or other formatting :).

it should be a project of a few days for a motivated individual

To get something basic maybe. To get something that is featureful and works, well, I think it would probably be more than that. I did look at kitty_grab, my main complaint is it doesn't support search. Maybe I could make a PR to add that, although honestly I'm more interested in getting a better solution for nvim.

Just so we are on the same page, I'm not advocating for adding native support for keyboard navigation. The biggest thing I'd like is for better (i.e. more organized and findable) documentation on solutions for this. And maybe a little bit of griping that there isn't quite something that does everything I want.

This script is a workaround at best.

Yeah... unfortunately I think there probably needs to be a change in nvim for solution that is less of a workaround.

This has been explicitly solved by an uncompleted escape sequence, which is of course an imperfect solution.

Oh, I wondered what that was for, and didn't put it in my version of the script. But that seems like another hacky workaround.

You don't even need to use a nvimpager. You can use nvim directly. It can read from stdin and has scrolling bindings. Maybe with -u NONE if you don't need the plugins.

But that doesn't preserve ANSI colors.

commented

@page-down
I'm having a weird problem with your script: only a portion of the scrollback gets outputted, ending with a [Process exited 0] midway. But I can't reproduce this consistently, sometimes it happens, sometimes it doesn't. The location of the cutoff is also inconsistent when it happens.

Any clue what's happening?

EDIT: It seems that this is caused by removing the sleep 0.01 command, I guess that's essential.

I guess the biggest caveat to this approach is that there will be a flicker and cursor jump when you first initiate the pager, which is a bit disorienting. I don't think there's a way around this other than patching nvim itself.

I'm having success using github.com/lunixbochs/vtclean to strip control characters from stdin. It works better than all the substitution commands I've seen.

This is my set-up:

scrollback_pager sh -c 'vtclean | nvim -c "normal G"'

I tried some alternatives to preserve colours using nvim's terminal, but none of them worked properly, so I gave up on that.

scrollback_pager sh -c 'vtclean | nvim -c "normal G"'

Nice, that works, I just had to replace vtclean with its full path.

EDIT: after some time with this solution it started feeling like too much of a downgrade to lose the colors. Some comments here show solutions using Vim's terminal and some mention that there are plugins that allow keeping the colors. The latter seemed like a more "natural" way to go, so I tried the Baleia plugin and it's been working really well so far. It can be installed in Neovim with Lazy like this:

{
  'm00qek/baleia.nvim',
  config = function()
    require('baleia').setup({})
  end
},

and then in Kitty you use it like this:

scrollback_pager $SHELL -c 'nvim -c "lua local baleia = require(\"baleia\").setup({}); baleia.once(0)" -c "map <silent> q :qa!<CR>"'
commented

Here is my solution for original Vim. It works for X and Wayland, but involves a lot more settings, plugins and workarounds. I have tested this for some time and it works great. When the buffer is very long you will see some scrolling, since Vim's terminal buffer does not support pasting without moving the cursor.

.config/kitty/kitty.conf

scrollback_pager sh -c 'vim --not-a-term -u ~/.vim/kitty_scrollback.vim +"terminal ++curwin tail -n 10000 "<(tail -n 10000 | sed -e "s/$\x1b'']8;;file:[^\]*[\]//g" ) +"PreserveCursorPosition INPUT_LINE_NUMBER CURSOR_LINE CURSOR_COLUMN"'

.vim/kitty_scrollback.vim

set encoding=utf-8
set nocompatible

call plug#begin('~/.vim/plugged')
Plug 'pineapplegiant/spaceduck'
Plug 'jasonccox/vim-wayland-clipboard'
call plug#end()

colorscheme spaceduck

set nonu
set nornu
set termguicolors
set background=dark
set noshowmode
set noruler
set laststatus=0
set noshowcmd
set termwinscroll=10000

nnoremap q <Cmd>quitall!<CR>
nnoremap i <Cmd>quitall!<CR>
vnoremap y "+y
nnoremap y "+y

command -bar -nargs=+ PreserveCursorPosition call s:PreserveCursorPosition(<f-args>)

function s:PreserveCursorPosition(topline, curline, curcol)
	let input_line_nr = str2nr(a:topline)
	let cursor_line = str2nr(a:curline)
	let cursor_column = str2nr(a:curcol)
	let last_line = line('$')
	sleep 100m
	call cursor(max([0, input_line_nr - 1]) + cursor_line, cursor_column)
endfunction

If you use another plugin manager than vim-plug, you should change those lines. Same goes for the colortheme.

~/.config/kitty/kitty.conf:

# ...
scrollback_pager nvim -u ~/.config/nvim/kitty+page.lua -R -M -
# ...

~/.config/nvim/kitty+page.lua:

local setup = function()
  local nlines = vim.fn.getenv('INPUT_LINE_NUMBER')
  local cur_line = vim.fn.getenv('CURSOR_LINE')
  local cur_column = vim.fn.getenv('CURSOR_COLUMN')
  vim.opt.encoding='utf-8'
  vim.opt.clipboard = 'unnamed'
  vim.opt.compatible = false
  vim.opt.number = false
  vim.opt.relativenumber = false
  vim.opt.termguicolors = true
  vim.opt.showmode = false
  vim.opt.ruler = false
  vim.opt.laststatus = 0
  vim.opt.showcmd = false
  vim.opt.scrollback = 1000
  local term_buf = vim.api.nvim_create_buf(true, false);
  local term_io = vim.api.nvim_open_term(term_buf, {})
  vim.api.nvim_buf_set_keymap(term_buf, 'n', 'q', '<Cmd>q<CR>', { })
  local group = vim.api.nvim_create_augroup('kitty+page', {})

  vim.api.nvim_create_autocmd('VimEnter', {
    group = group,
    pattern = '*',
    once = true,
    callback = function(ev)
        local current_win = vim.fn.win_getid()
        for _, line in ipairs(vim.api.nvim_buf_get_lines(ev.buf, 0, -1, false)) do
          vim.api.nvim_chan_send(term_io, line)
          vim.api.nvim_chan_send(term_io, '\r\n')
        end
        term_io = false
        vim.api.nvim_create_autocmd('ModeChanged', {
          group = group,
          pattern = "([nN]:[^vV])|([vV]:[^nN])",
          command = 'stopinsert'
        })
        vim.api.nvim_win_set_buf(current_win, term_buf)
        if nlines ~= vim.NIL and cur_line ~= vim.NIL and cur_column ~= vim.NIL then
          vim.api.nvim_win_set_cursor(current_win, {vim.fn.max({1, nlines}) + cur_line, cur_column - 1})
        else
          vim.api.nvim_input([[<C-\><C-n>G]])
        end
        vim.api.nvim_buf_delete(ev.buf, { force = true } )
    end
  })
end
setup()

After exploring multitudes of solutions, spending my time with it patiently, I can't help but think that an external pager is just an awkward, dead-end solution riddled with unresolvable issues and compromises.

Here's an argument by @xJonathanLEI from a Zellij issue centering around the same dillema that I find myself completely agreeing with:

Now that this old thread is dug out... Tbh this is still one of main things that deter me from daily driving with Zellij. I understand that it's the maintainer's decision to not implement something like tmux's copy mode, and I respect that. However, apart from the extra keystroke, the actual main downside for me is the context switch that comes with this approach.
With something like tmux's copy mode, you act on what's on your screen, with all the text positioning and colors in place. Once the buffer is sent to the editor, the positioning can change (due to bufferlines and stuff), and the colors are most definitely lost. There's some mental effort to refocus on where you wanted to work on before entering this mode. And it's another context switch after you finish copying and exit the editor.

(I understand that the color issue does not apply to kitty)

To add another point, there will be an inevitable nasty flicker when you transition between the terminal and the pager process, which I also find jarring and disorienting. There's no way to actually resolve this issue due to the very nature of the pipeline, which is why I think it's a dead-end solution.

I understand the appeal of delegating the editor/pager functionality to a separate process, because this relieves the maintainer from the burden of having to re-implement one.

But basically everything I've tried so far feels second-class compared to a built-in vi-mode solution, which is really unfortunate.

commented

I just released v1.0.0 of kitty-scrollback.nvim. If you have any feedback, issues, or suggestions feel free to open an issue. I'm interested to get feedback if the setup easy or if there are any unclear/confusing steps. This requires Neovim nightly (v0.10) and Kitty v0.29.

kitty-scrollback.nvim

Features

  • 😻 Navigate Kitty's scrollback buffer with Neovim
  • 🐱 Copy contents from Neovim to system clipboard
  • 😺 Send contents from Neovim to Kitty shell
  • 🙀 Execute shell command from Neovim to Kitty shell

Thanks @galaxia4Eva -- works great for me!

@ibash here's an updated version that takes kitty input into account:

https://gist.github.com/galaxia4Eva/9e91c4f275554b4bd844b6feece16b3d

files:
~/.config/kitty/kitty.conf
~/.config/nvim/lua/kitty+page.lua

Thanks to all of you.

Here's how I use Neovim as the scrollback pager, my "Good Enough" version.

Neovim scrollback pager

Configuration

Single line

scrollback_pager bash -c "exec nvim 63<&0 0</dev/null -u NONE -c 'map <silent> q :qa!<CR>' -c 'set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus' -c 'autocmd TermEnter * stopinsert' -c 'autocmd TermClose * call cursor(max([0,INPUT_LINE_NUMBER-1])+CURSOR_LINE, CURSOR_COLUMN)' -c 'terminal sed </dev/fd/63 -e \"s/'$'\x1b'']8;;file:[^\]*[\]//g\" && sleep 0.01 && printf \"'$'\x1b'']2;\"'"

Standalone scrollback pager script

#!/usr/bin/env bash
set -eu

if [ "$#" -eq 3 ]; then
    INPUT_LINE_NUMBER=${1:-0}
    CURSOR_LINE=${2:-1}
    CURSOR_COLUMN=${3:-1}
    AUTOCMD_TERMCLOSE_CMD="call cursor(max([0,${INPUT_LINE_NUMBER}-1])+${CURSOR_LINE}, ${CURSOR_COLUMN})"
else
    AUTOCMD_TERMCLOSE_CMD="normal G"
fi

exec nvim 63<&0 0</dev/null \
    -u NONE \
    -c "map <silent> q :qa!<CR>" \
    -c "set shell=bash scrollback=100000 termguicolors laststatus=0 clipboard+=unnamedplus" \
    -c "autocmd TermEnter * stopinsert" \
    -c "autocmd TermClose * ${AUTOCMD_TERMCLOSE_CMD}" \
    -c 'terminal sed </dev/fd/63 -e "s/'$'\x1b'']8;;file:[^\]*[\]//g" && sleep 0.01 && printf "'$'\x1b'']2;"'
# default scrollback pager
scrollback_pager ~/.config/kitty/pager.sh 'INPUT_LINE_NUMBER' 'CURSOR_LINE' 'CURSOR_COLUMN'

# launch action (optional `--stdin-add-formatting`)
map f1 launch --type overlay --stdin-source=@screen_scrollback ~/.config/kitty/pager.sh

Features

* Works on Linux and macOS.

* No temporary files are written, which prevents sensitive information from being leaked to the local disk.

* Only the Neovim process remains running after startup.

* The cursor is correctly positioned after scrollback is fully loaded from stdin.

* Prevents accidental switching to terminal insert mode.

* Replace escape sequences that may not be compatible.

Explanation

Use bash to redirect stdin into Neovim's Terminal Mode. Also prevents Neovim from reading stdin directly.

bash -c "exec 63<&0 0</dev/null; exec nvim -c 'terminal sed </dev/fd/63'"

Stops when entering terminal insert mode.

autocmd TermEnter * stopinsert

Hides Neovim's [Process exited] message, only when the command is successfully executed. This is done by sending out the set title escape code, so the message is not displayed, and the title will not be set because the sequence is incomplete. Wait 10ms for sed to output everything, otherwise it will be intercepted earlier by the output of later commands in Neovim‘s Terminal.

terminal sed </dev/fd/63 && sleep 0.01 && printf "\x1b]2;"

Set the cursor position after terminal is fully closed.

autocmd TermClose * call cursor(1, 1)`

Use the q key to quit, and the executed command will not be displayed.

map <silent> q :qa!<CR>

Enable RGB color in terminal.

set termguicolors

Set Neovim scrollback to the maximum value of 100,000.

set scrollback=100000

Disable the status line.

set laststatus=0

Use the system clipboard by default.

set clipboard+=unnamedplus

Speed up startup by disabling the loading of rc profiles and plugins, given that the restricted terminal mode is used. Can be turned on as needed.

-u NONE

Known Issues

* Due to the way Neovim's Terminal treats escape codes, there may be cases where the style is incorrect.

* In case of special unicode characters, the number of lines may not be consistent in Neovim, resulting in incorrect cursor positioning, use `autocmd TermClose * normal G` instead to jump directly to the last line.

* For scrollbacks that do not fill a screen, the empty lines are represented in Neovim as well.

This works perfectly for the most part. However, it seems to affect the background of selected text in Neovim. The color is neither the one set in Kitty nor the one from my Neovim configuration. It makes the selected text unreadable. How can I change this?