Last active
September 13, 2022 14:41
-
-
Save johnmastro/88cc318f4ce33b626c9d to your computer and use it in GitHub Desktop.
Send text from Emacs to iTerm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; 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) |
Just chiming in with @pkhoueiry, one can't send any text containing a '
character.
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
Thanks for making this script. Its great. 🎉