[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 56cf4d5: Added shelisp package
From: |
Michael Mauger |
Subject: |
[elpa] master 56cf4d5: Added shelisp package |
Date: |
Sat, 6 Apr 2019 21:15:04 -0400 (EDT) |
branch: master
commit 56cf4d589d3190daff2df0a2fb6d7334045272c0
Author: Michael R. Mauger <address@hidden>
Commit: Michael R. Mauger <address@hidden>
Added shelisp package
---
packages/shelisp/shelisp.el | 214 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 214 insertions(+)
diff --git a/packages/shelisp/shelisp.el b/packages/shelisp/shelisp.el
new file mode 100644
index 0000000..7d3cd98
--- /dev/null
+++ b/packages/shelisp/shelisp.el
@@ -0,0 +1,214 @@
+;;; shelisp.el --- execute elisp in shell -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2018, 2019 Michael R. Mauger
+
+;; Author: Michael R. Mauger <address@hidden>
+;; Version: 0.9.0
+;; Package-Type: simple
+;; Keywords: terminals, lisp, processes
+;; URL: https://gitlab.com/mmauger/shelisp
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Comint process (likely shell-mode) can write out Emacs Lisp
+;; expressions and have them executed.
+
+;; When the shell process writes out a string of the form:
+;; \e_#EMACS# elisp-expr \a
+;;
+;; Where, "elisp-expr" is a valid elisp expression. The elisp
+;; expression is executed as if you had invoked the function
+;; within Emacs itself. The elisp expression may include a call to
+;; the function `f' which will expand the filename parameter into an
+;; appropriate filename for Emacs using the appropriate Tramp prefix
+;; if necessary.
+
+;; This script also defines an Alist variable that creates shell
+;; commands and the `printf'-style format to generate the full elisp
+;; expression with command parameters substituted into the command. A
+;; function is placed in the `shell-mode-hook' to actually create the
+;; shell functions and aliases to format the elisp expressions and
+;; embed them in an escape sequence so that they are detected and
+;; executed.
+
+;; In most usage this mode merely allows you to type "e filename"
+;; rather than "C-x C-f filename" which isn't much of a savings.
+;; However, with this mode enabled, you can write shell scripts to
+;; invoke Emacs Lisp functions. But beware, the shell script will not
+;; wait for completion of the elisp expression, nor return anything
+;; back (see ToDo's below).
+
+;; INSTALLATION
+
+;; After installing this package from ELPA, you must add the following
+;; to your Emacs initialization script:
+
+;; (add-hook 'shell-mode-hook #'shelisp-mode)
+
+;; TO DOs:
+
+;; * Support `term-mode' like `shell-mode'
+
+;; * Provide support for creation of shell commands for command shells
+;; other than bash -- csh, tcsh, zsh, ksh, ash, dash, fish, mosh, sh.
+;;
+;; Support for non-Linux shells is left as an exercise for a
+;; masochistic hacker.
+
+;; * Implement a wait for completion facility similar to `emacsclient'
+;; or the work done in `with-editor' with the "sleeping editor."
+;; That is, pause the shell activity with a long sleep, until C-c
+;; C-c or C-c C-k is typed in Emacs and the caller is awoken with a
+;; signal.
+
+;; KNOWN BUGS
+
+;; The simplistic implementation of the shell functions will not
+;; properly handle filenames containing double quote characters (\")
+;; nor backslashes (\\). While this is an error, it does not
+;; represent a significant limitation in the implementation. The
+;; caller can properly add backslashes to the filename string before
+;; passing it to printf to generate the elisp expression. In the end,
+;; the purpose is to create a valid elisp expression string.
+
+;;; Code:
+(require 'cl-macs)
+(require 'pp)
+
+;;;###autoload
+(define-minor-mode shelisp-mode
+ "Enable elisp expressions embedded in ANSI APC (Application
+Program Control) escape sequences to be located and executed
+while in a shell mode buffer."
+ nil " ShElisp" nil
+
+ (if (not shelisp-mode)
+ (remove-hook 'comint-preoutput-filter-functions
+ #'shelisp-exec-lisp)
+ ;; Parse elisp escape sequences
+ (add-hook 'comint-preoutput-filter-functions
+ #'shelisp-exec-lisp 'append)
+ (shelisp-add-commands)))
+
+;;;###autoload
+(defvar shelisp-debug nil
+ "When non-nil, display messages showing the elisp expression.")
+
+(defun shelisp--file-name (file)
+ "Apply remote host in `default-directory' to FILE."
+ (if (and (file-name-absolute-p file)
+ (not (file-remote-p file)))
+ (concat (file-remote-p default-directory) file)
+ file))
+
+(defun shelisp--result-as-string (result)
+ "Return RESULT as a string.
+If it already is a string, then just return it. Otherwise,
+convert it to a string."
+ (cond ((null result) "")
+ ((stringp result) result)
+ (:else (pp-to-string result))))
+
+(defun shelisp-exec-lisp (&optional str)
+ "Detect escape sequence in STR to execute Emacs Lisp."
+ (interactive)
+
+ (when (and shelisp-mode str)
+ (let* ((APC "\\(?:\e_\\|\x9f\\)")
+ (tag "#EMACS#")
+ (ST "\\(?:[\a\x9c]\\|[\e][\\\\]\\)")
+ (cmd-re "\\(?:[^\a\x9c\e]\\|\e[^\\\\]\\)")
+ (apc-re (concat APC tag "\\(" cmd-re "*\\)" ST))
+ (case-fold-search nil)
+ cmd rep)
+
+ ;; Look for APC escape sequences
+ (while (string-match apc-re str)
+ (setq cmd (match-string 1 str)
+ rep "")
+ ;; Trace, if requested
+ (when shelisp-debug
+ (message "shelisp> `%s'" cmd))
+
+ ;; Replace the elisp expresssion with it's value
+ ;; if the value is nil, treat it as an empty string
+ (setq rep (save-match-data
+ (save-excursion
+ (condition-case err
+ (shelisp--result-as-string
+ (eval `(cl-flet ((f (file) (shelisp--file-name
file)))
+ ,(read cmd))))
+ ;; When an error occurs, replace with the error message
+ (error
+ (format "shelisp: `%s': %S" cmd err)))))
+ str (replace-match
+ (concat rep (unless (string-equal "" rep) "\n"))
+ t t str)))))
+ str)
+
+
+;;;###autoload
+(defvar shelisp-commands (let ((cmds '(("e" . "(find-file-other-window (f
\"%s\"))")
+ ("v" . "(view-file-other-window (f
\"%s\"))")
+ ("dired" . "(dired \"%s\")")
+ ("ediff" . "(ediff (f \"%s\") (f
\"%s\"))"))))
+ (when (locate-library "magit")
+ (push '("magit" . "(magit-status)") cmds))
+ (when (or (bound-and-true-p viper-mode)
+ (bound-and-true-p evil-mode))
+ (push '("vim" . "(find-file-other-window (f
\"%s\"))") cmds)
+ (push '("vi" . "(find-file-other-window (f
\"%s\"))") cmds))
+ cmds)
+
+ "Alist of shell commands and corresponding Lisp expressions.
+Each entry in the alist consists of the shell alias to be set as the
+command, and the `printf' style string to generate the elisp
+expression to be executed.
+
+If a parameter to the elisp expression is a filename, then we
+need to be sure that proper filename parsing in context occurs.
+We do this by passing filename parameters through the elisp
+function `f'[1]. This function makes sure that filename has
+proper Tramp prefixes if the shell session is remote. So, rather
+than just embedding the filename in the elisp expression, using
+printf, with \"\\\"%s\\\"\", you use \\=`(f \\\"%s\\\")\\='.
+
+[1] The `f' function is `cl-flet' bound for the shelisp
+expression and cannot be used elsewhere.")
+
+(defun shelisp-add-commands ()
+ "Add Emacs Lisp to shell aliases (assumes GNU bash syntax)."
+
+ (when (and shelisp-mode shelisp-commands)
+ (let ((proc (get-buffer-process (current-buffer))))
+ (dolist (c shelisp-commands)
+ (let ((cmd (car c))
+ (expr (cdr c)))
+ (process-send-string
+ proc
+ (apply #'format
+ (mapconcat #'identity
+ '("unset -f shelisp_%s"
+ "function shelisp_%s { printf '\\e_#EMACS# %s
\\a' \"address@hidden"; }"
+ "alias %s=shelisp_%s" "")
+ " ; ")
+ (list cmd cmd
+ (replace-regexp-in-string "\"" "\\\\\"" expr)
+ cmd cmd)))))
+ (process-send-string proc "\n"))))
+
+(provide 'shelisp)
+;;; shelisp.el ends here
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] master 56cf4d5: Added shelisp package,
Michael Mauger <=