branch: externals/shell-command+ commit 339931da79b02a83effd37363dde2ebb65141d9a Author: Philip K <phil...@posteo.net> Commit: Philip K <phil...@posteo.net>
rename bang to shell-command+ --- README.md | 24 +++++---- bang.el | 128 --------------------------------------------- shell-command+.el | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 7036bc5..1a51bf8 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,26 @@ `bang.el` ========= -`bang` is a `shell-command` substitute that makes it easier to run -commands on regions or whole buffers. This is done by potentially -interpreting the first character differently, as the `bang` docstring -explains. +`shell-command+` is a `shell-command` substitute that makes it easier +to run commands on regions or whole buffers, among other things. -Bang has been based on a function of the same name by [Leah +`shell-command+` has been based on a function named `bang` by [Leah Neukirchen][leah]. +Features +-------- + + + How to use ---------- -Using [MELPA] and `use-package`, a minimal setup might look something like -this: +`shell-command+` is available from [ELPA]. It can be installed by +invoking + + M-x package-install RET shell-command+ RET - (use-package bang - :bind ("M-!" . bang)) +Bind the command `shell-command+` to any key, for example `M-!`. Bug reports and patches should be sent to my [public inbox]. @@ -27,6 +31,6 @@ Copying Domain Dedication][cc0] license. [leah]: http://leahneukirchen.org/dotfiles/.emacs -[MELPA]: https://melpa.org/#/bang +[ELPA]: http://elpa.gnu.org/packages/shell-command+.html [public inbox]: https://lists.sr.ht/~zge/public-inbox [cc0]: https://creativecommons.org/publicdomain/zero/1.0/deed diff --git a/bang.el b/bang.el deleted file mode 100644 index 50ec7b1..0000000 --- a/bang.el +++ /dev/null @@ -1,128 +0,0 @@ -;;; bang.el --- A sam-like shell-command -*- lexical-binding: t -*- - -;; Author: Philip K. <phi...@warpmail.net> -;; Version: 1.0.3 -;; Keywords: unix, processes, convenience -;; Package-Requires: ((emacs "24.1")) -;; URL: https://git.sr.ht/~zge/bang - -;; This file is NOT part of Emacs. -;; -;; This file is in the public domain, to the extent possible under law, -;; published under the CC0 1.0 Universal license. -;; -;; For a full copy of the CC0 license see -;; https://creativecommons.org/publicdomain/zero/1.0/legalcode - -;;; Commentary: -;; -;; Bang is a interactive `shell-command' substitute, that extends the -;; regular Emacs function by considering the first character as special. -;; Read `bang's docstring for more details. -;; -;; This version of Bang has been based on Leah Neukirchen's version, -;; though it has been internally rewritten. -;; -;; The original version can be found here: -;; https://leahneukirchen.org/dotfiles/.emacs - -(eval-when-compile (require 'rx)) - -;;; Code: - -(defgroup bang nil - "An extended `shell-command'" - :group 'external - :prefix "bang-") - -(defcustom bang-use-eshell t - "Check if there is an eshell-handler for each command." - :type 'boolean) - -(defconst bang--command-regexp - (rx bos (* space) - (? (group (or (: ?. (not (any "/"))) ?/ ?~) - (* (not space))) - (+ space)) - (or (group ?<) (group ?>) (group ?|) ?! "") - (* space) - (group (: (group (* (any alnum ?_ ?-))) - (? space)) - (+ not-newline)) - eos)) - -(defun bang-expand-path (path) - "Expand any PATH into absolute path with additional tricks. - -Furthermore, replace each sequence with three or more `.'s with a -proper upwards directory pointers. This means that '....' becomes -'../../../..', and so on." - (expand-file-name - (replace-regexp-in-string - (rx (>= 2 ".")) - (lambda (sub) - (mapconcat #'identity (make-list (1- (length sub)) "..") "/")) - path))) - -;;;###autoload -(defun bang (command beg end) - "Intelligently execute string COMMAND in inferior shell. - -When COMMAND starts with - < the output of COMMAND replaces the current selection - > COMMAND is run with the current selection as input - | the current selection is filtered through COMMAND - ! COMMAND is simply executed (same as without any prefix) - -Without any argument, `bang' will behave like `shell-command'. - -Before these characters, one may also place a relative or -absolute path, which will be the current working directory in -which the command will be executed. See `bang-expand-path' for -more details on this expansion. - -Inside COMMAND, % is replaced with the current file name. To -insert a literal % quote it using a backslash. - -In case a region is active, bang will only work with the region -between BEG and END. Otherwise the whole buffer is processed." - (interactive (list (read-shell-command "Shell command: ") - (if (use-region-p) (region-beginning) (point-min)) - (if (use-region-p) (region-end) (point-max)))) - (save-match-data - (unless (string-match bang--command-regexp command) - (error "Invalid command")) - (let* ((path (match-string-no-properties 1 command)) - (has-< (match-string-no-properties 2 command)) - (has-> (match-string-no-properties 3 command)) - (has-| (match-string-no-properties 4 command)) - (cmd (match-string-no-properties 6 command)) - (can-eshell (intern-soft (concat "eshell/" cmd))) - (rest (condition-case nil - (replace-regexp-in-string - (rx (* ?\\ ?\\) (or ?\\ (group "%"))) - buffer-file-name - (match-string-no-properties 5 command) - nil nil 1) - (error (match-string-no-properties 5 command))))) - (let ((default-directory (bang-expand-path (or path ".")))) - (cond (has-< (delete-region beg end) - (shell-command rest t shell-command-default-error-buffer) - (exchange-point-and-mark)) - (has-> (shell-command-on-region - beg end rest nil nil - shell-command-default-error-buffer t)) - (has-| (shell-command-on-region - beg end rest t t - shell-command-default-error-buffer t)) - ((and bang-use-eshell can-eshell) - (eshell-command rest (and current-prefix-arg t))) - (t (shell-command rest (and current-prefix-arg t) - shell-command-default-error-buffer)))) - (when has-> - (with-current-buffer "*Shell Command Output*" - (delete-region (point-min) (point-max))))))) - -(provide 'bang) - -;;; bang.el ends here diff --git a/shell-command+.el b/shell-command+.el new file mode 100644 index 0000000..32b4fa2 --- /dev/null +++ b/shell-command+.el @@ -0,0 +1,153 @@ +;;; shell-command+.el --- An extended shell-command -*- lexical-binding: t -*- + +;; Author: Philip K. <phil...@posteo.net> +;; Version: 1.0.3 +;; Keywords: unix, processes, convenience +;; Package-Requires: ((emacs "24.1")) +;; URL: http://elpa.gnu.org/packages/shell-command+.html + +;; This file is NOT part of Emacs. +;; +;; This file is in the public domain, to the extent possible under law, +;; published under the CC0 1.0 Universal license. +;; +;; For a full copy of the CC0 license see +;; https://creativecommons.org/publicdomain/zero/1.0/legalcode + +;;; Commentary: +;; +;; `shell-command+' is a `shell-command' substitute, that extends the +;; regular Emacs command with several features. +;; +;; A few examples of what `shell-command+' can do: +;; +;; +;; > wc -l +;; +;; Count all lines in a buffer, and display the result in the +;; minibuffer. +;; +;; +;; .. < ls -l +;; +;; Replace the current region (or buffer in no region is selected) +;; with a directory listing of the parent directory. +;; +;; +;; | tr -d a-z +;; +;; Delete all instances of the charachters a, b, c, ..., z, in the +;; selected region (or buffer, if no region was selected). +;; +;; +;; ... make +;; +;; Run Eshell's make (i.e. `compile') in the parent's parent +;; directory. + +(eval-when-compile (require 'rx)) +(require 'eshell) + +;;; Code: + +(defgroup shell-command+ nil + "An extended `shell-command'" + :group 'external + :prefix "shell-command+-") + +(defcustom shell-command+-use-eshell t + "Check if there is an eshell-handler for each command." + :type 'boolean) + +(defconst shell-command+--command-regexp + (rx bos + ;; ignore all preceding whitespace + (* space) + ;; check for working directory string + (? (group (or (: ?. (not (any "/"))) ?/ ?~) + (* (not space))) + (+ space)) + ;; check for redirection indicator + (? (or (group ?<) (group ?>) (group ?|) ?!)) + ;; allow whitespace after indicator + (* space) + ;; actual command (and command name) + (group (: (group (*? not-newline)) + (? space)) + (+ not-newline)) + eos) + "Regular expression to parse `shell-command+' input.") + +(defun shell-command+-expand-path (path) + "Expand any PATH into absolute path with additional tricks. + +Furthermore, replace each sequence with three or more `.'s with a +proper upwards directory pointers. This means that '....' becomes +'../../../..', and so on." + (expand-file-name + (replace-regexp-in-string + (rx (>= 2 ".")) + (lambda (sub) + (mapconcat #'identity (make-list (1- (length sub)) "..") "/")) + path))) + +;;;###autoload +(defun shell-command+ (command beg end) + "Intelligently execute string COMMAND in inferior shell. + +If COMMAND is prefixed with an absolute or relative path, the +created process will the executed in the specified path. + +When COMMAND starts with... + < the output of COMMAND replaces the current selection + > COMMAND is run with the current selection as input + | the current selection is filtered through COMMAND + ! COMMAND is simply executed (same as without any prefix) + +If `shell-command+-use-eshell' is non-nil, and the the first +argument of COMMAND has a defined `eshell'-function, use that. + +Inside COMMAND, % is replaced with the current file name. To +insert a literal % quote it using a backslash. + +These extentions can all be combined with one-another. + +In case a region is active, `shell-command+' will only work with the region +between BEG and END. Otherwise the whole buffer is processed." + (interactive (list (read-shell-command "Shell command: ") + (if (use-region-p) (region-beginning) (point-min)) + (if (use-region-p) (region-end) (point-max)))) + (save-match-data + (unless (string-match shell-command+--command-regexp command) + (error "Invalid command")) + (let ((path (match-string-no-properties 1 command)) + (cmd (match-string-no-properties 6 command)) + (rest (condition-case nil + (replace-regexp-in-string + (rx (* ?\\ ?\\) (or ?\\ (group "%"))) + buffer-file-name + (match-string-no-properties 5 command) + nil nil 1) + (error (match-string-no-properties 5 command))))) + (let ((default-directory (shell-command+-expand-path (or path ".")))) + (cond ((match-string-no-properties 2 command) ;< + (delete-region beg end) + (shell-command rest t shell-command-default-error-buffer) + (exchange-point-and-mark)) + ((match-string-no-properties 3 command) ;> + (shell-command-on-region + beg end rest nil nil + shell-command-default-error-buffer t)) + ((match-string-no-properties 4 command) ;| + (shell-command-on-region + beg end rest t t + shell-command-default-error-buffer t)) + ((and shell-command+-use-eshell + (intern-soft (concat "eshell/" cmd))) + (eshell-command rest (and current-prefix-arg t))) + (t (shell-command rest (and current-prefix-arg t) + shell-command-default-error-buffer))))))) + +(provide 'shell-command+) + +;;; shell-command+.el ends here