Skip to content

Instantly share code, notes, and snippets.

@johnmastro
Last active September 13, 2022 14:41
Show Gist options
  • Save johnmastro/88cc318f4ce33b626c9d to your computer and use it in GitHub Desktop.
Save johnmastro/88cc318f4ce33b626c9d to your computer and use it in GitHub Desktop.
Send text from Emacs to iTerm
;;; iterm.el - Send text to a running iTerm instance
(require 'pcase)
(require 'thingatpt)
;; To match SublimeText's key binding:
;; (global-set-key (kbd "<C-return>") 'iterm-send-text)
(defvar iterm-default-thing 'line
"The \"thing\" to send if no region is active.
Can be any symbol understood by `bounds-of-thing-at-point'.")
(defvar iterm-empty-line-regexp "^[[:space:]]*$"
"Regexp to match empty lines, which will not be sent to iTerm.
Set to nil to disable removing empty lines.")
(defun iterm-escape-string (str)
(let* ((str (replace-regexp-in-string "\\\\" "\\\\" str nil t))
(str (replace-regexp-in-string "\"" "\\\"" str nil t)))
str))
(defun iterm-last-char-p (str char)
(let ((length (length str)))
(and (> length 0)
(char-equal (elt str (- length 1)) char))))
(defun iterm-chop-newline (str)
(let ((length (length str)))
(if (iterm-last-char-p str ?\n)
(substring str 0 (- length 1))
str)))
(defun iterm-maybe-add-newline (str)
(if (iterm-last-char-p str ? )
(concat str "\n")
str))
(defun iterm-handle-newline (str)
(iterm-maybe-add-newline (iterm-chop-newline str)))
(defun iterm-maybe-remove-empty-lines (str)
(if iterm-empty-line-regexp
(let ((regexp iterm-empty-line-regexp)
(lines (split-string str "\n")))
(mapconcat #'identity
(delq nil (mapcar (lambda (line)
(unless (string-match-p regexp line)
line))
lines))
"\n"))
str))
(defun iterm-send-string (str)
"Send STR to a running iTerm instance."
(let* ((str (iterm-maybe-remove-empty-lines str))
(str (iterm-handle-newline str))
(str (iterm-escape-string str)))
(shell-command
(concat "osascript -e 'tell app \"iTerm\"' "
"-e 'set mysession to current session of current terminal' "
(format "-e 'tell mysession to write text \"%s\"' " str)
"-e 'end tell'"))))
(defun iterm-text-bounds ()
(pcase-let ((`(,beg . ,end) (if (use-region-p)
(cons (region-beginning) (region-end))
(bounds-of-thing-at-point
iterm-default-thing))))
(list beg end)))
(defun iterm-send-text (beg end)
"Send buffer text in region from BEG to END to iTerm.
If called interactively without an active region, send text near
point (determined by `iterm-default-thing') instead."
(interactive (iterm-text-bounds))
(let ((str (buffer-substring-no-properties beg end)))
(iterm-send-string str))
(forward-line 1))
(provide 'iterm)
@haberdashPI
Copy link

haberdashPI commented Jun 14, 2016

Thanks for the handy script. This no longer works with iTerm2: changing iterm-send-string to the following fixes the problem.

(defun iterm-send-string (str)   
  "Send STR to a running iTerm instance."
  (let* ((str (iterm-maybe-remove-empty-lines str))
         (str (iterm-handle-newline str))
         (str (iterm-escape-string str)))
    (shell-command (concat "osascript "
                           "-e 'tell app \"iTerm2\"' "
                           "-e 'tell current window' "
                           "-e 'tell current session' "
                           "-e 'write text \"" str "\"' "
                           "-e 'end tell' "
                           "-e 'end tell' "
                           "-e 'end tell' "))))

@pkhoueiry
Copy link

iterm.el is skipping the " ' " character. This needs to be corrected.

@danrasmuson
Copy link

Thanks for making this script. Its great. 🎉

@MasonProtter
Copy link

Just chiming in with @pkhoueiry, one can't send any text containing a ' character.

@bkudria
Copy link

bkudria commented Apr 16, 2019

Here's what worked for me:

(defun iterm-escape-string (str)
  (let* ((str (replace-regexp-in-string "\\\\" "\\\\" str nil t))
         (str (replace-regexp-in-string "\"" "\\\"" str nil t))
         (str (replace-regexp-in-string "'" "\\'" str nil t)))
    str))

and

(defun iterm-send-string (str)
  "Send STR to a running iTerm instance."
  (let* ((str (iterm-maybe-remove-empty-lines str))
         (str (iterm-handle-newline str))
         (str (iterm-escape-string str)))
    (let ((cmd (concat "osascript "
                       "-e 'tell app \"iTerm2\"' "
                       "-e 'tell current window' "
                       "-e 'tell current session' "
                       "-e $'write text \"" str "\"' "
                       "-e 'end tell' "
                       "-e 'end tell' "
                       "-e 'end tell' ")))
      (shell-command cmd)
      )))

Full gist: https://gist.github.com/5486915b95c8cfb684f5cecb877c4cc9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment