Skip to content

Latest commit

 

History

History
413 lines (311 loc) · 15 KB

iterm2.org

File metadata and controls

413 lines (311 loc) · 15 KB

iTerm2 shell integration for Elvish

This library implements iTerm2’s Shell Integration for the Elvish shell.

Table of Contents

Usage

Install the elvish-modules package using epm:

use epm
epm:install github.com/zzamboni/elvish-modules

In your rc.elv, load this module and call iterm2:init to enable the integration:

use github.com/zzamboni/elvish-modules/iterm2

If the iTerm2 utilities are installed in their default location of ~/.iterm2, that directory will be added automatically to the path. Note that some of the functionality provided by the utilities is also provided by functions in this module.

iTerm2 shell integration

After loading the module and after configuring your prompt, call the following command:

iterm2:init

After this all Shell Integration features will be available.

Notes:

  • At the moment Elvish does not have a mechanism for capturing the exit code of the last command executed, therefore the prompt marker is always colored as if the command was successful.
  • The Elvish prompt updates dynamically. If the length of the prompt changes as you are typing (e.g. if something in the background changes the git status of a repo and the git indicators in the prompt change), the context menu you get by right-clicking the prompt mark might show the wrong captured command.
  • The iterm2:init function needs to hook into the prompt function to emit the necessary Escape codes. For this reason it is important that you call it only after you have configured your prompt function.
  • If you use the Elvish readline-bindings module, you should re-bind the Ctrl-L key to the iterm2:clear-screen function, which clears the screen but emits the necessary prompt marker before redrawing it. For example
    edit:insert:binding[Ctrl-L] = $iterm2:clear-screen~
        

iTerm2 escape sequence commands

Most of the iTerm2 custom escape codes are implemented by this module, either through custom functions or by constructing them directly using the iterm2:escape-cmd, iterm2:cmd or iterm2:set functions. See the Implementation section below for the full list of available functions.

The FTCS codes used for shell integration cal also be emitted manually if needed, using the iterm2:ftcs-prompt, iterm2:ftcs-command-start, iterm2:ftcs-command-executed and iterm2:ftcs-command-finished functions. You should for the most part not need to use these functions directly, as they are inserted in the correct places by the iterm2:init function.

Implementation

Note: This file is written in literate programming style, to make it easy to explain. See iterm2.elv for the generated file.

The escape sequences used by iTerm2 for shell integration are described in detail in the Proprietary Escape Codes documentation.

Load some modules

use str
use path

Base escape sequences

All escape sequences have the same basic sequence: start with an ESC character (\e) followed by ], semi-colon separated components, and ending with a BEL character (\a). The iterm2:mk-escape-str builds such a string, using its arguments for the components.

fn mk-escape-str {|xs| put "\e]"(str:join ';' $xs)"\a" }

The iterm2:escape-cmd calls iterm2:mk-escape-str and prints the result.

fn escape-cmd {|xs|
  print (mk-escape-str $xs)
}

Two types of escape sequences are used: custom iTerm2 commands start with 1337, and the “FinalTerm” commands used for the basic shell integration start with 133. These two sequences are produced by iterm2:mk-iterm2-cmd and iterm2:mk-ftcs-cmd, respectively.

fn mk-iterm2-cmd {|@x| mk-escape-str [1337 $@x] }
fn mk-ftcs-cmd {|@x| mk-escape-str [133 $@x] }

iTerm2 commands come in two types: plain commands and “set” commands, which assign a value. We have two functions to emit these as needed, and a function to emit FTCS command sequences.

fn cmd {|@x| print (mk-iterm2-cmd $@x) }
fn set-var {|@x| print (mk-iterm2-cmd (str:join '=' $x)) }
fn ftcs-cmd {|@x| print (mk-ftcs-cmd $@x) }

iTerm2 commands

Colors and background

Set window title and tab background color. Arguments are the red, green and blue values. iterm2:reset-title-color resets to the default.

fn set-title-color {|r g b|
  escape-cmd [6 1 bg red brightness $r]
  escape-cmd [6 1 bg green brightness $g]
  escape-cmd [6 1 bg blue brightness $b]
}

fn reset-title-color {
  escape-cmd [6 1 bg '*' default]
}

Change color palette. See the documentation for the possible key values.

fn setcolor {|key r g b|
  set-var SetColors $key (printf %02x%02x%02x $r $g $b)
}

Report foreground and background colors.

fn report-background-color {
  print (mk-escape-str [4 -2 '?'])
}

fn report-foreground-color {
  print (mk-escape-str [4 -1 '?'])
}

Set background image. Without an argument, the background image is removed.

fn setbackground {|@file|
  var encoded-file = ""
  if (not-eq $file []) {
    set encoded-file = (print $file[0] | /usr/bin/base64)
  }
  set-var SetBackgroundImageFile $encoded-file
}

Hyperlinks

Produce a hyperlink in the terminal. params if given, should be a map containing key/value pairs (the only supported param at the moment is id). This function does not print the string, you need to do that with print or echo, e.g.:

echo "This is" (iterm2:hyperlink http://zzamboni.org "my website")
fn hyperlink {|url text &params=[&]|
  var params-str = ""
  if (not-eq $params [&]) {
    set params-str = (str:join ":" (each {|k| print $k"="$params[$k] } [(keys $params)]))
  }
  put (mk-escape-str [ '8' $params-str $url ])$text(mk-escape-str [ '8' '' ''])
}

Marks

Setting a mark at the current position.

fn mark { cmd SetMark }

Bring iTerm2 to the foreground.

fn focus { cmd StealFocus }

Set current directory

Inform iTerm2 of the current directory. This is a wrapper around iterm2:set CurrentDir, but we have a dedicated function because it’s commonly used.

fn setdir {|d|
  set-var CurrentDir $d
}

Notifications

Post a notification through iTerm2.

fn notify {|msg|
  print (mk-escape-str [9 $msg])
}

Copy to clipboard

Everything displayed in the terminal between calling iterm2:startcopy and iterm2:endcopy is copied to the clipboard. The general clipboard is used by default, but an option &name can be passed to iterm2:startcopy with values rule, find or font (not sure what each does, but they are mentioned in the iTerm2 documentation).

fn startcopy {|&name=""|
  set-var CopyToClipboard $name
}

fn endcopy {
  cmd EndCopy
}

If you want to put a stored string in the clipboard, you can use iterm2:copystr.

fn copystr {|s|
  var encoded-str = (print $s | /usr/bin/base64)
  set-var Copy :$encoded-str
}

Annotations

Set an annotation at the current position. By default, the annotation covers the whole line where the cursor is, and its label is shown immediately. The following options are supported:

&hidden
the label is not shown immediately;
&length=n
length of the annotation;
&xy=[x y]
position of the annotation. If &xy is specified, &length must ber specified as well.
fn annotate {|ann &hidden=$false &length=$nil &xy=$nil|
  var parts = [ $ann ]
  if (and $length $xy) {
    set parts = [ $ann $length $@xy ]
  } elif (and $length (not $xy)) {
    set parts = [ $length $ann ]
  }
  var cmd = AddAnnotation
  if $hidden { set cmd = AddHiddenAnnotation }
  cmd $cmd=(str:join "|" $parts)
}

Change profile

fn profile {|p| set-var SetProfile $p }

Set and report iTerm2 user variables

fn setuservar {|var val|
  set-var SetUserVar $var (print $val | /usr/bin/base64)
}
fn reportvar {|var|
  set-var ReportVariable (print $var | /usr/bin/base64)
}

Set the badge on the terminal

fn setbadge {|@badge|
  set-var SetBadgeFormat (print $@badge | /usr/bin/base64)
}

Set current hostname, user, directory

These functions can be used to set these values, which are used for allowing file downloads.

fn set-remotehost {|user host|
  set-var RemoteHost $user"@"$host
}

fn set-currentdir {|dir|
  set-var CurrentDir $dir
}

Set window title

This is not really an iTerm2-specific escape sequence, but it’s here to maintain compatibility with the original code from which this module came :)

fn windowtitle {|t| print "\e]0;"$t"\a" }

Other commands

Some of the less-common commands can be invoked through the iterm2:cmd or iterm2:set commands:

Set cursor shape
iterm2:set CursorShape $shape, where $shape can indicate a block (0), vertical bar (1) or underline (2).
Clear scrollback history
iterm2:cmd ClearScrollback.
Enable/disable cursor guide
iterm2:set HighlightCursorLine yes/no.
Request attention
iterm2:set RequestAttention value. Possible values are yes, once, no and fireworks.
Report cell size
iterm2:cmd ReportCellSize.
Set function key labels
iterm2:set SetKeyLabel $key $label.

FinalTerm commands

The FTCS commands are used for the base shell integration, with some iTerm2 extensions.

Prompt/command markers

The following commands are used to mark the different parts of the prompt, command line and command output, as per the following definition, which matches the documentation (see section “Shell Integration/FinalTerm”):

[ftcs-prompt]prompt% [ftcs-command-start] ls -l
[ftcs-command-executed]
-rw-r--r-- 1 user group 127 May 1 2016 filename
[ftcs-command-finished]
fn ftcs-prompt { ftcs-cmd A }
fn ftcs-command-start { ftcs-cmd B }
fn ftcs-command-executed {|cmd| ftcs-cmd C }
fn ftcs-command-finished {|&status=0| ftcs-cmd D $status }

Shell integration

The iterm2:init function inserts the corresponding FTCS commands in the appropriate places for the prompt and the command line.

At the moment, proper capture of the command means modifying the prompt function, which means iterm2:init needs to be called after setting up your prompt. The original prompt function is saved in $iterm2:original-prompt-fn, and used within the modified prompt function to display it. We also modify the $edit:before-readline and $edit:after-readline hooks to emit the corresponding escape sequences in the correct places.

Note: at the moment Elvish does not have a mechanism for capturing the exit code of the last command executed, therefore the FTCS “command finished” marker is always emitted with an exit code of zero, so the marker is always colored as if the command was successful.

use platform

var original-prompt-fn = $nil

fn init {
  # Save the original prompt
  set original-prompt-fn = $edit:prompt
  # Define a new prompt function which calls the original one and
  # additionally emits the necessary escape codes at the end.
  set edit:prompt = {
    $original-prompt-fn
    set-currentdir $pwd >/dev/tty
    ftcs-command-start >/dev/tty
  }
  # Emit end-of-command and start-of-prompt markers before displaying
  # each new prompt line, and set current host/user/dir.
  set edit:before-readline = [
    {
      ftcs-command-finished
      set-remotehost $E:USER (platform:hostname)
      ftcs-prompt
    }
    $@edit:before-readline
  ]
  # Emit start-of-command-output marker after the user presses Enter
  # on the command line.
  set edit:after-readline = [
    $ftcs-command-executed~
    $@edit:after-readline
  ]
}

The iterm2:clear-screen function clears the screen and issues the corresponding markers before redrawing the prompt. If you are using the readline bindings through use readline-bindings, you should bind this function to the Ctrl-L key to keep the correct markers when clearing the screen.

fn clear-screen {
  edit:clear
  ftcs-prompt > /dev/tty
}

iTerm2 utilities

If the iTerm2 utilities are installed in their default location of ~/.iterm2, we detect them and add them automatically to the path. Note that some of the functionality provided by the utilities is also provided by functions in this module.

if (path:is-dir ~/.iterm2) {
  set paths = [ $@paths ~/.iterm2 ]
}