Does tmux have anything like vim's marks?

Let's say I'm tailing some logs from a running app and I expect some long output to come soon that I would like to scroll to the top of once it's done. Does tmux have anything that could help me here? I'm thinking something like vim's marks, where I could press ma to make a mark called a, and then press 'a to jump to that line.

Ideal workflow:

  1. tail -f some stuff
  2. <prefix> m a to set a mark called a
  3. Do something which causes a lot of output to be logged
  4. <prefix> ' a to jump to where a was defined

While Tmux does have a "mark" function, I don't think it will work for your use-case.

In copy-mode, pressing Shiftx (a.k.a. "X") sets a mark, and Metax will then toggle between the current location and that mark. The problem is that when you exit copy-mode, the mark is lost. So you wouldn't be able to run a subsequent tail -f and get back to that point.

My personal recommendation would be to use a pager for the output that supports (a) following a growing file, and (b) mark/jump functionality. That can found pretty easily in the less command. Your workflow would be pretty close to what you describe:

  1. less +F some stuff
  2. Ctrl+c to temporarily exit follow-mode
  3. ma to set a mark called "a"
  4. F to restart follow-mode
  5. Do something which causes a lot of output to be logged
  6. Ctrl+c to temporarily exit follow-mode (you wouldn't want it scrolling while analyzing your output anyway).
  7. 'a to jump to your mark
  8. F to restart follow-mode when/if desired

Edit/Update

If you really want a "mark" and "goto-mark" function, it's possible, but very hacky, IMHO. I'm really only posting this because (a) I finally got it to (mostly) work, and (b) I want to get it out of my config, but don't want to lose work.

First, create two separate additional files:

.tmux-set_mark.tmux:

# Must use "run-shell" since it can run the
# command immediately.  The #() syntax always 
# uses the *previous* results.
# The capture-pane is hackery to get the current 
# line number we are on (starting from the 
# beginning of the history.
run "tmux set-environment TMUX_CUR_LINE $(tmux capture-pane -S - -E - -p | tac | awk 'NF {p=1} p' | wc -l)"

# Store it in a user-option on the current 
# pane.  This allows marks in multiple panes 
# to work.
set-option -F -p @TMUX_MARK "#{TMUX_CUR_LINE}"

display-message "Mark set at line #{@TMUX_MARK}"

.tmux-goto_mark.tmux:

# Must use "run-shell" since it can run 
# the command immediately.  The #() 
# syntax always uses the *previous* 
# results.
run-shell "tmux set-environment TMUX_CUR_LINE $(tmux capture-pane -S - -E - -p | tac | awk 'NF {p=1} p' | wc -l)"

# Set a user option on the pane with 
# the line number we need to goto.
# This is calculated, of course, by 
# subtracting the marked-line-number
# from the current-line-number
set-option -F -p @TMUX_GOTO "#{e|-:#{TMUX_CUR_LINE},#{@TMUX_MARK}}"

copy-mode

# Goto the line.  Again `run-shell` is needed
# here since send-keys can't expand the FORMAT 
# string, but run-shell can.

run-shell 'tmux display-message #{@TMUX_GOTO}'
run-shell 'tmux send-keys -X goto-line #{@TMUX_GOTO}'

# The next two lines are just additional
# hackery to use the built-in TMux marking
# ability so that the marked line is highlighted
# and at the top of the screen
send-keys -X set-mark
send-keys -X jump-to-mark

Then, in your .tmux.conf, set key-bindings to source these files:

unbind x
bind x source-file "$HOME/.tmux-set_mark.tmux"
unbind X
bind X source-file "$HOME/.tmux-goto_mark.tmux"

This is horrible, IMHO. Easier possible methods fail:

  • It would be easier if there was a better way to get the current line number you are on in the buffer, but there doesn't seem to be a variable for this. The closest I could find was history_size, but the history doesn't start until you begin scrolling your pane.
  • It would be easier if goto-line took an absolute line number, instead it is the relative line number from the bottom of the buffer in copy-mode, not the top.

There's one additional caveat/bug to this code. If you do this on the first "page" of a Tmux pane (before you start scrolling), the line that it hightlights will not be the one that you marked. The alternative is to not use the:

send-keys -X set-mark
send-keys -X jump-to-mark

But then the goto-line will always place the desired mark at the bottom of the pane, meaning you will always need to scroll down a pane.


Given the comment by @NotTheDr01ds to my comment below the OP, you could append a unique line to the file you're tailing (if you have write permission and appending if such a line doesn't interfere with any future use of that log file) before you start the process in step 3 by doing:

echo '# mymark' >> file.log && tail -f file.log

Or if that's not preferable, and if the file you're tailing isn't growing rapidly before you start the process in step 3, you could type directly into the terminal were the output of tail -f is shown

$ tail -f file.log
 > some line from the log
 > another line from the log
 # mymark!
 > line generated by step3
 > and there will be many more lines generated by step3...
 > .           .           .           .           .           
 > .           .           .           .           .           
 > .           .           .           .           .           

Then in both cases you could search backwards for the # mymark string in tmux's copy mode (with ?^# mymark).