From 1dad71947af87724acf34a9455db9da376369a9a Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Thu, 23 Nov 2023 20:04:23 +0100 Subject: [PATCH] update dwim-shell-command Signed-off-by: Andreas Gerler --- .../dwim-shell-command-autoloads.el | 0 .../dwim-shell-command-pkg.el | 6 +- .../dwim-shell-command.el | 128 +++++--- .../dwim-shell-commands.el | 305 ++++++++++++++++-- 4 files changed, 378 insertions(+), 61 deletions(-) rename elpa/{dwim-shell-command-20221218.1146 => dwim-shell-command-20230709.2129}/dwim-shell-command-autoloads.el (100%) rename elpa/{dwim-shell-command-20221218.1146 => dwim-shell-command-20230709.2129}/dwim-shell-command-pkg.el (56%) rename elpa/{dwim-shell-command-20221218.1146 => dwim-shell-command-20230709.2129}/dwim-shell-command.el (89%) rename elpa/{dwim-shell-command-20221218.1146 => dwim-shell-command-20230709.2129}/dwim-shell-commands.el (74%) diff --git a/elpa/dwim-shell-command-20221218.1146/dwim-shell-command-autoloads.el b/elpa/dwim-shell-command-20230709.2129/dwim-shell-command-autoloads.el similarity index 100% rename from elpa/dwim-shell-command-20221218.1146/dwim-shell-command-autoloads.el rename to elpa/dwim-shell-command-20230709.2129/dwim-shell-command-autoloads.el diff --git a/elpa/dwim-shell-command-20221218.1146/dwim-shell-command-pkg.el b/elpa/dwim-shell-command-20230709.2129/dwim-shell-command-pkg.el similarity index 56% rename from elpa/dwim-shell-command-20221218.1146/dwim-shell-command-pkg.el rename to elpa/dwim-shell-command-20230709.2129/dwim-shell-command-pkg.el index 6f100077..49355d5d 100644 --- a/elpa/dwim-shell-command-20221218.1146/dwim-shell-command-pkg.el +++ b/elpa/dwim-shell-command-20230709.2129/dwim-shell-command-pkg.el @@ -1,6 +1,8 @@ -(define-package "dwim-shell-command" "20221218.1146" "Shell commands with DWIM behaviour" +(define-package "dwim-shell-command" "20230709.2129" "Shell commands with DWIM behaviour" '((emacs "28.1")) - :commit "39ce590191cb63e1f6b77b4419c6a6846c39a67e" :authors + :commit "2503c5a749eaa288d5e7250561893416fd40fbf9" :authors + '(("Alvaro Ramirez")) + :maintainers '(("Alvaro Ramirez")) :maintainer '("Alvaro Ramirez") diff --git a/elpa/dwim-shell-command-20221218.1146/dwim-shell-command.el b/elpa/dwim-shell-command-20230709.2129/dwim-shell-command.el similarity index 89% rename from elpa/dwim-shell-command-20221218.1146/dwim-shell-command.el rename to elpa/dwim-shell-command-20230709.2129/dwim-shell-command.el index 9f76b53e..c9864044 100644 --- a/elpa/dwim-shell-command-20221218.1146/dwim-shell-command.el +++ b/elpa/dwim-shell-command-20230709.2129/dwim-shell-command.el @@ -1,11 +1,11 @@ ;;; dwim-shell-command.el --- Shell commands with DWIM behaviour -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Alvaro Ramirez +;; Copyright (C) 2022 Alvaro Ramirez https://xenodium.com ;; Author: Alvaro Ramirez ;; Package-Requires: ((emacs "28.1")) ;; URL: https://github.com/xenodium/dwim-shell-command -;; Version: 0.41 +;; Version: 0.48 ;; This package is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ (require 'view) (defcustom dwim-shell-command-prompt - "DWIM shell command (<> <> <> <> <<*>> <> <>): " + "DWIM shell command (<> <> <>): " "`dwim-shell-command' prompt. Modify if shorter is preferred." :type 'string :group 'dwim-shell-command) @@ -79,6 +79,11 @@ Set to nil to use `shell-command-switch'." :type '(repeat string) :group 'dwim-shell-command) +(defcustom dwim-shell-command-shell-trace nil + "Attempt to add --xtrace to shell command to debug." + :type 'boolean + :group 'dwim-shell-command) + (defcustom dwim-shell-command-done-buffer-name (lambda (name) (format "✅ %s %s" name (propertize "done" 'face 'success))) @@ -202,7 +207,7 @@ Prefix #'minibuffer-default-add-shell-commands)) (read-from-minibuffer dwim-shell-command-prompt dwim-shell-command-default-command nil nil 'shell-command-history))) -(cl-defun dwim-shell-command-on-marked-files (buffer-name script &key utils extensions shell-util shell-args shell-pipe post-process-template on-completion repeat silent-success no-progress error-autofocus monitor-directory focus-now join-separator) +(cl-defun dwim-shell-command-on-marked-files (buffer-name script &key utils extensions shell-util shell-args shell-trace shell-pipe post-process-template on-completion repeat silent-success no-progress error-autofocus monitor-directory focus-now join-separator) "Create DWIM utilities executing templated SCRIPT on given files. Here's a simple utility invoking SCRIPT to convert image files to jpg. @@ -293,6 +298,7 @@ Quick exit :extensions extensions :shell-util shell-util :shell-args shell-args + :shell-trace shell-trace :shell-pipe shell-pipe :post-process-template post-process-template :on-completion on-completion @@ -304,7 +310,7 @@ Quick exit :focus-now focus-now :join-separator join-separator)) -(cl-defun dwim-shell-command-execute-script (buffer-name script &key files extensions shell-util shell-args shell-pipe utils post-process-template on-completion silent-success gen-temp-dir repeat no-progress error-autofocus monitor-directory focus-now join-separator) +(cl-defun dwim-shell-command-execute-script (buffer-name script &key files extensions shell-util shell-args shell-trace shell-pipe utils post-process-template on-completion silent-success gen-temp-dir repeat no-progress error-autofocus monitor-directory focus-now join-separator) "Execute a script asynchronously, DWIM style with SCRIPT and BUFFER-NAME. :FILES are used to instantiate SCRIPT as a noweb template. @@ -393,14 +399,15 @@ This is implied when <> appears in the script. (when shell-command-switch (list shell-command-switch)) '("-x" "-c"))) + (when (and shell-args (stringp shell-args)) + (setq shell-args (list shell-args))) ;; See if -x can be prepended. (when (and (not (seq-contains-p shell-args "-x")) + (or shell-trace dwim-shell-command-shell-trace) (apply #'dwim-shell-command--program-test (seq-concatenate 'list shell-util '("-x") shell-args (list "echo")))) (setq shell-args (seq-concatenate 'list '("-x") shell-args))) - (when (and shell-args (stringp shell-args)) - (setq shell-args (list shell-args))) (when (stringp utils) (setq utils (list utils))) (when (or gen-temp-dir (string-match-p "\<\\>" script 0)) @@ -444,7 +451,9 @@ This is implied when <> appears in the script. files))) (setq script (string-trim script)) (with-current-buffer proc-buffer - (shell-mode) + (let ((inhibit-message t)) + ;; Silence noise of entering shell-mode. + (shell-mode)) (setq default-directory default-directory) (shell-command-save-pos-or-erase) (view-mode +1) @@ -755,7 +764,18 @@ REPLACEMENTS is a cons list of literals to replace with values." (setq template (replace-regexp-in-string "\\([^ ]\\)\\(\<\\>\\)\\([^ ]\\)" (string-replace unescaped-quote escaped-quote file) template nil nil 2)) - (setq template (replace-regexp-in-string "\\(\<\\>\\)" file template nil nil 1)))) + (setq template (replace-regexp-in-string "\\(\<\\>\\)" file template nil nil 1))) + + ;; "<>" with "/path/file.jpg" -> "/path/file(1).jpg" + (if-let* ((quoting (dwim-shell-command--escaped-quote-around "\<\\>" template)) + (unescaped-quote (nth 0 quoting)) + (escaped-quote (nth 1 quoting))) + (setq template (replace-regexp-in-string "\\([^ ]\\)\\(\<\\>\\)\\([^ ]\\)" + (string-replace unescaped-quote escaped-quote + (dwim-shell-command--unique-new-file-path file)) + template nil nil 2)) + (setq template (replace-regexp-in-string "\\(\<\\>\\)" + (dwim-shell-command--unique-new-file-path file) template nil nil 1)))) ;; "<>" -> some.txt (if unique) ;; -> some(1).txt (if it exist) @@ -809,7 +829,9 @@ Use OVERRIDE to override `default-directory'." (when-let ((default-directory (or override default-directory))) (seq-map (lambda (filename) (file-name-concat default-directory filename)) - (process-lines "ls" "-1")))) + (cl-remove-if + (lambda (e) (member e '("." ".."))) + (directory-files default-directory))))) (defun dwim-shell-command--last-modified-between (before after) "Compare files in BEFORE and AFTER and return oldest file in diff." @@ -822,7 +844,8 @@ Use OVERRIDE to override `default-directory'." CALLING-BUFFER, FILES-BEFORE, PROCESS, PROGRESS-REPORTER, ERROR-AUTOFOCUS, ON-COMPLETION, SILENT-SUCCESS, and MONITOR-DIRECTORY are all needed to finalize processing." - (let ((oldest-new-file)) + (let ((oldest-new-file) + (files-after)) (when progress-reporter (progress-reporter-done progress-reporter)) (if (= (process-exit-status process) 0) @@ -831,43 +854,57 @@ all needed to finalize processing." (with-current-buffer (process-buffer process) (rename-buffer (generate-new-buffer-name (funcall dwim-shell-command-done-buffer-name (process-name process))))) (if on-completion - (funcall on-completion (process-buffer process)) + (funcall on-completion (process-buffer process) process) (with-current-buffer calling-buffer - (when (equal major-mode 'dired-mode) - (when revert-buffer-function - (funcall revert-buffer-function nil t)) - ;; Region is not accurate if new files added. Wipe it. - (when mark-active - (deactivate-mark))) + (if (equal major-mode 'dired-mode) + (progn (when revert-buffer-function + (funcall revert-buffer-function nil t)) + ;; Region is not accurate if new files added. Wipe it. + (when (use-region-p) + (deactivate-mark))) + (if (or buffer-auto-save-file-name + buffer-file-name) + (revert-buffer :ignore-auto :noconfirm))) + (setq files-after (dwim-shell-command--default-directory-files monitor-directory)) (setq oldest-new-file (dwim-shell-command--last-modified-between files-before - (dwim-shell-command--default-directory-files monitor-directory))) - (when oldest-new-file - (dired-jump nil oldest-new-file))) + files-after)) + ;; There's at least one new file. Show that. + (if oldest-new-file + (dired-jump nil oldest-new-file) + ;; Files may have been deleted but harder to track. + ;; Open dired and refresh to show files are gone. + (unless (equal (length files-after) + (length files-before)) + (dired monitor-directory) + (when revert-buffer-function + (funcall revert-buffer-function nil t))))) (unless (equal (process-buffer process) (window-buffer (selected-window))) (if (or oldest-new-file silent-success) (kill-buffer (process-buffer process)) (unless silent-success (switch-to-buffer (process-buffer process))))))) - (if (and (buffer-name (process-buffer process)) - (or error-autofocus - ;; Buffer already selected. Don't ask. - (equal (process-buffer process) - (window-buffer (selected-window))) - (ignore-error quit - (y-or-n-p (format "%s error, see output? " - (buffer-name (process-buffer process))))))) - (progn - (with-current-buffer (process-buffer process) - (rename-buffer (generate-new-buffer-name (funcall dwim-shell-command-error-buffer-name (process-name process))))) - (when (or error-autofocus - (equal (process-buffer process) - (window-buffer (selected-window)))) - (dwim-shell-command--message (funcall dwim-shell-command-error-buffer-name (process-name process)))) - (switch-to-buffer (process-buffer process))) - (kill-buffer (process-buffer process)))) + (if on-completion + (funcall on-completion (process-buffer process) process) + (if (and (buffer-name (process-buffer process)) + (or error-autofocus + ;; Buffer already selected. Don't ask. + (equal (process-buffer process) + (window-buffer (selected-window))) + (ignore-error quit + (y-or-n-p (format "%s error, see output? " + (buffer-name (process-buffer process))))))) + (progn + (with-current-buffer (process-buffer process) + (rename-buffer (generate-new-buffer-name (funcall dwim-shell-command-error-buffer-name (process-name process))))) + (when (or error-autofocus + (equal (process-buffer process) + (window-buffer (selected-window)))) + (dwim-shell-command--message (funcall dwim-shell-command-error-buffer-name (process-name process)))) + (switch-to-buffer (process-buffer process))) + (kill-buffer (process-buffer process))))) (setq dwim-shell-command--commands (map-delete dwim-shell-command--commands (process-name process))))) @@ -906,9 +943,18 @@ all needed to finalize processing." (progress-reporter-update reporter)) (comint-output-filter process output)) +(defun dwim-shell-command--file-extensions () + "Return buffer file extension or marked/region extensions for a `dired' buffer." + (seq-uniq + (seq-map + #'file-name-extension + (seq-filter + #'file-name-extension + (dwim-shell-command--files))))) + (defun dwim-shell-command--files () "Return buffer file (if available) or marked/region files for a `dired' buffer." - (cl-assert (not (and mark-active (let ((files (dired-get-marked-files nil nil nil t))) + (cl-assert (not (and (use-region-p) (let ((files (dired-get-marked-files nil nil nil t))) ;; Based on `dired-number-of-marked-files'. (cond ((null (cdr files)) nil) @@ -916,7 +962,7 @@ all needed to finalize processing." (eq (car files) t)) t) (t - (not (seq-empty-p (length files)))))))) + (not (seq-empty-p files))))))) nil "Region and marked files both active. Choose one only.") (if (buffer-file-name) (list (buffer-file-name)) @@ -927,7 +973,7 @@ all needed to finalize processing." (defun dwim-shell-command--dired-paths-in-region () "If `dired' buffer, return region files. nil otherwise." (when (and (equal major-mode 'dired-mode) - mark-active) + (use-region-p)) (let ((start (region-beginning)) (end (region-end)) (paths)) diff --git a/elpa/dwim-shell-command-20221218.1146/dwim-shell-commands.el b/elpa/dwim-shell-command-20230709.2129/dwim-shell-commands.el similarity index 74% rename from elpa/dwim-shell-command-20221218.1146/dwim-shell-commands.el rename to elpa/dwim-shell-command-20230709.2129/dwim-shell-commands.el index 832c3fe7..4fbaeca2 100644 --- a/elpa/dwim-shell-command-20221218.1146/dwim-shell-commands.el +++ b/elpa/dwim-shell-command-20230709.2129/dwim-shell-commands.el @@ -93,6 +93,15 @@ Optional argument ARGS as per `browse-url-default-browser'" :monitor-directory "~/Downloads" :silent-success t)) +(defun dwim-shell-commands-image-clear-exif-metadata () + "Clear EXIF metadata in image(s)." + (interactive) + (dwim-shell-command-on-marked-files + "View EXIF" + "cp '<>' '<>_cleared.<>' + exiftool -all:all= -overwrite_original '<>_cleared.<>'" + :utils "exiftool")) + (defun dwim-shell-commands-image-exif-metadata () "View EXIF metadata in image(s)." (interactive) @@ -101,6 +110,14 @@ Optional argument ARGS as per `browse-url-default-browser'" "exiftool '<>'" :utils "exiftool")) +(defun dwim-shell-commands-ocr-text-from-image () + "Extract text from image via tesseract." + (interactive) + (dwim-shell-command-on-marked-files + "Extract text from image via tesseract." + "tesseract '<>' -" + :utils "tesseract")) + (defun dwim-shell-commands-image-browse-location () "Open image(s) location in browser." (interactive) @@ -145,7 +162,7 @@ Optional argument ARGS as per `browse-url-default-browser'" :utils '("exiftool" "curl") :silent-success t :error-autofocus t - :on-completion (lambda (buffer) + :on-completion (lambda (buffer _process) (with-current-buffer buffer (goto-char (point-min)) (let ((matches '())) @@ -157,6 +174,22 @@ Optional argument ARGS as per `browse-url-default-browser'" "\n"))) (kill-buffer buffer))))) +(defun dwim-shell-commands-image-horizontal-flip () + "Horizontally flip image(s)." + (interactive) + (dwim-shell-command-on-marked-files + "Image horizontal flip" + "convert -verbose -flop '<>' '<>_h_flipped.<>'" + :utils "convert")) + +(defun dwim-shell-commands-image-vertical-flip () + "Horizontally flip image(s)." + (interactive) + (dwim-shell-command-on-marked-files + "Image vertical flip" + "convert -verbose -flip '<>' '<>_v_flipped.<>'" + :utils "convert")) + (defun dwim-shell-commands-image-to-jpg () "Convert all marked images to jpg(s)." (interactive) @@ -194,13 +227,39 @@ Optional argument ARGS as per `browse-url-default-browser'" (interactive) (dwim-shell-command-on-marked-files "Join as pdf" - (format "convert -verbose '<<*>>' '%s'" + (format "convert -verbose '<<*>>' '<<%s(u)>>'" (dwim-shell-command-read-file-name "Join as pdf named (default \"joined.pdf\"): " :extension "pdf" - :default "<>")) + :default "joined.pdf")) :utils "convert")) +(defun dwim-shell-commands-join-images-horizontally () + "Join all marked images horizontally as a single image." + (interactive) + (let ((filename (format "joined.%s" + (or (seq-first (dwim-shell-command--file-extensions)) "png")))) + (dwim-shell-command-on-marked-files + "Join images horizontally" + (format "convert -verbose '<<*>>' +append '<<%s(u)>>'" + (dwim-shell-command-read-file-name + (format "Join as image named (default \"%s\"): " filename) + :default filename)) + :utils "convert"))) + +(defun dwim-shell-commands-join-images-vertically () + "Join all marked images vertically as a single image." + (interactive) + (let ((filename (format "joined.%s" + (or (seq-first (dwim-shell-command--file-extensions)) "png")))) + (dwim-shell-command-on-marked-files + "Join images vertically" + (format "convert -verbose '<<*>>' -append '<<%s(u)>>'" + (dwim-shell-command-read-file-name + (format "Join as image named (default \"%s\"): " filename) + :default filename)) + :utils "convert"))) + (defun dwim-shell-commands-image-to-grayscale () "Convert all marked images to grayscale." (interactive) @@ -265,6 +324,14 @@ Optional argument ARGS as per `browse-url-default-browser'" "Unzip" "atool --extract --explain '<>'" :utils "atool")) +(defun dwim-shell-commands-optimize-gif () + "Convert all marked videos to optimized gif(s)." + (interactive) + (dwim-shell-command-on-marked-files + "Convert to optimized gif" + "gifsicle -O3 '<>' --lossy=90 -o '<>_optimized.gif'" + :utils '("ffmpeg" "gifsicle"))) + (defun dwim-shell-commands-speed-up-gif () "Speeds up gif(s)." (interactive) @@ -277,6 +344,24 @@ Optional argument ARGS as per `browse-url-default-browser'" :post-process-template (lambda (script file) (string-replace "<>" (dwim-shell-commands--gifsicle-frames-every factor file) script))))) +(defun dwim-shell-commands-clip-round-rect-gif () + "Clip gif(s) with round rectangle." + (interactive) + (dwim-shell-command-on-marked-files + "Clip round rect gif(s)" + "width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 '<>') + height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 '<>') + convert -quiet -size \"${width}x${height}\" xc:none -fill black -draw \"roundRectangle 0,0,${width},${height} <>,<>\" '<>/mask.png' + convert '<>' -coalesce -background black -alpha remove -alpha off '<>/no_alpha.<>' + # https://stackoverflow.com/a/66990135 + convert '<>/no_alpha.<>' -quiet -coalesce -alpha extract null: \\( '<>/mask.png' -alpha extract \\) -compose multiply -layers composite '<>/alpha.gif' + convert '<>/no_alpha.<>' null: '<>/alpha.gif' -quiet -alpha off -compose copy_opacity -layers composite '<>_rounded.<>' + # Turn looping on. + mogrify -loop 0 '<>_rounded.<>' + gifsicle -O3 '<>_rounded.<>' --lossy=80 -o '<>_rounded.<>'" + :extensions "gif" + :utils '("ffprobe" "convert"))) + (defun dwim-shell-commands-resize-gif () "Resize marked gif(s)." (interactive) @@ -339,7 +424,7 @@ Optional argument ARGS as per `browse-url-default-browser'" (map-elt selection 'pid) (map-elt selection 'user) (map-elt selection 'comm)))) - (when (y-or-n-p (format "Kill? %s" prompt-title)) + (when (y-or-n-p (format "Kill %s?" prompt-title)) (dwim-shell-command-on-marked-files (format "Kill %s" prompt-title) (format "kill -9 %d" (map-elt selection 'pid)) @@ -348,7 +433,7 @@ Optional argument ARGS as per `browse-url-default-browser'" :silent-success t)))) (defun dwim-shell-commands-macos-toggle-bluetooth-device-connection () - "" + "Toggle Bluetooth device connection." (interactive) (let* ((devices (seq-filter (lambda (line) @@ -513,7 +598,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< "Generate a QR code from clipboard" (format "qrencode -s10 -o %s %s" temp-file (shell-quote-argument (current-kill 0))) :utils "qrencode" - :on-completion (lambda (buffer) + :on-completion (lambda (buffer _process) (kill-buffer buffer) (switch-to-buffer (find-file-noselect temp-file t)))))) @@ -552,17 +637,17 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< :utils "osascript" :no-progress t :silent-success t - :on-completion (lambda (buffer) + :on-completion (lambda (buffer _process) (kill-buffer buffer) (dired-jump nil (file-name-concat target-dir (file-name-nondirectory (nth 0 files)))))))) -(defun dwim-shell-commands-macos-hardware-overview () - "View macOS hardware overview." +(defun dwim-shell-commands-macos-version-and-hardware-overview-info () + "View macOS version and hardware overview info." (interactive) (dwim-shell-command-on-marked-files "macOS hardware overview" - "system_profiler SPHardwareDataType" - :utils "system_profiler")) + "sw_vers; system_profiler SPHardwareDataType" + :utils '("sw_vers" "system_profiler"))) (defun dwim-shell-commands-macos-reveal-in-finder () "Reveal selected files in macOS Finder." @@ -647,7 +732,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< :utils "swift"))) (defun dwim-shell-commands-macos-toggle-display-rotation () - "View macOS hardware overview." + "Rotate display." (interactive) ;; # Display_ID Resolution ____Display_Bounds____ Rotation ;; 2 0x2b347692 1440x2560 0 0 1440 2560 270 [main] @@ -667,7 +752,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< (seq-mapcat (lambda (paths) (directory-files-recursively paths "\\.app$" t (lambda (path) - (not (string-suffix-p ".app" path))))) + (not (string-suffix-p ".app" path))))) '("/Applications" "~/Applications" "/System/Applications"))))) (defun dwim-shell-commands-macos-set-default-app () @@ -701,6 +786,119 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< :no-progress t :utils "open"))) +(defun dwim-shell-commands-macos-open-with-firefox () + "Open file(s) with specific external app." + (interactive) + (dwim-shell-command-on-marked-files + "Open with Firefox" + "open -a Firefox '<<*>>'" + :silent-success t + :no-progress t + :utils "open")) + +(defun dwim-shell-commands-macos-open-with-safari () + "Open file(s) with specific external app." + (interactive) + (dwim-shell-command-on-marked-files + "Open with Firefox" + "open -a Safari '<<*>>'" + :silent-success t + :no-progress t + :utils "open")) + +(defun dwim-shell-commands-macos-start-recording-window () + "Select and start recording a macOS window." + (interactive) + (let* ((window (dwim-shell-commands--macos-select-window)) + (path (dwim-shell-commands--generate-path "~/Desktop" (car window) ".gif")) + (buffer-file-name path) ;; override so <> picks it up + (inhibit-message t)) + ;; Silence echo to avoid unrelated messages making into animation. + (cl-letf (((symbol-function 'dwim-shell-command--message) + (lambda (fmt &rest args) nil))) + (dwim-shell-command-on-marked-files + "Start recording a macOS window." + (format + "macosrec --record '%s' --gif --output '<>'" + (cdr window)) + :silent-success t + :monitor-directory "~/Desktop" + :no-progress t + :utils '("ffmpeg" "macosrec") + :on-completion + (lambda (buffer process) + (if (= (process-exit-status process) 0) + (progn + "Saved recording" + (dired-jump nil path) + (kill-buffer buffer)) + (with-current-buffer buffer + (goto-char (point-min)) + (if (search-forward "Aborted" nil t) + (progn + (message "Aborted recording") + (kill-buffer buffer)) + (switch-to-buffer buffer))))))))) + +(defun dwim-shell-commands--generate-path (dir name ext) + "Generate a timestamped path with DIR, NAME, and EXT." + (concat (file-name-as-directory (expand-file-name dir)) + (format-time-string "%Y-%m-%d-%H:%M:%S-") + name ext)) + +(defun dwim-shell-commands--macos-select-window () + "Return a list of macOS windows." + (if-let* ((line (completing-read + "Select: " + (process-lines "macosrec" "--list") nil t)) + (window-info (split-string line " ")) + (window-number (string-to-number (nth 0 window-info))) + (window-app (nth 1 window-info)) + (valid (> window-number 0))) + (cons window-app window-number) + (user-error "No window found"))) + +(defun dwim-shell-commands-macos-end-recording-window () + "Stop recording a macOS window." + (interactive) + (let ((inhibit-message t)) + (cl-letf (((symbol-function 'dwim-shell-command--message) + (lambda (fmt &rest args) nil))) + (dwim-shell-command-on-marked-files + "End recording macOS window." + "macosrec --save" + :silent-success t + :no-progress t + :error-autofocus t + :utils "macosrec")))) + +(defun dwim-shell-commands-macos-abort-recording-window () + "Stop recording a macOS window." + (interactive) + (let ((inhibit-message t)) + (cl-letf (((symbol-function 'dwim-shell-command--message) + (lambda (fmt &rest args) nil))) + (dwim-shell-command-on-marked-files + "Abort recording macOS window." + "macosrec --abort" + :silent-success t + :no-progress t + :utils "macosrec")))) + +(defun dwim-shell-commands-macos-screenshot-window () + "Select and screenshot macOS window." + (interactive) + ;; Silence echo to avoid unrelated messages making into screenshot. + (let ((window (dwim-shell-commands--macos-select-window)) + (inhibit-message t)) + (dwim-shell-command-on-marked-files + "Start recording a macOS window." + (format "macosrec --screenshot %s" (cdr window)) + :silent-success t + :monitor-directory "~/Desktop" + :no-progress t + :utils "macosrec"))) + (defun dwim-shell-commands-files-combined-size () "Get files combined file size." (interactive) @@ -708,7 +906,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< "Get files combined file size" "du -csh '<<*>>'" :utils "du" - :on-completion (lambda (buffer) + :on-completion (lambda (buffer _process) (with-current-buffer buffer (message "Total size: %s" (progn @@ -755,7 +953,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< (format "Clone %s" (file-name-base url)) (format "git clone %s" url) :utils "git" - :on-completion (lambda (buffer) + :on-completion (lambda (buffer _process) (kill-buffer buffer) (dired project-dir)))))) @@ -774,14 +972,14 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< "HTTP serve current dir" "python2 -m SimpleHTTPServer" :utils "python2" - :focus-now + :focus-now t :no-progress t)) ((executable-find "python") (dwim-shell-command-on-marked-files "HTTP serve current dir" "python -m SimpleHTTPServer" :utils "python" - :focus-now + :focus-now t :no-progress t)) (t (error "No python found")))) @@ -794,6 +992,38 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< "git clone <>" :utils "git")) +(defun dwim-shell-commands-pass-git-pull () + "Pass git pull." + (interactive) + (dwim-shell-command-on-marked-files + "pass git pull" + "pass git pull" + :utils '("pass" "git") + :silent-success t)) + +(defun dwim-shell-commands-git-list-untracked-files () + "List untracked git files in `default-directory'." + (interactive) + (dwim-shell-command-on-marked-files + "List untracked" + "git ls-files --others ." + :utils "git" + :focus-now t)) + +(defun dwim-shell-commands-git-delete-untracked-files () + "Delete untracked git files in `default-directory'." + (interactive) + (when (y-or-n-p (format "Clean '%s'? \n\n%s\n...\n\n" + default-directory + (string-join + (seq-take (process-lines "git" "ls-files" "--others" ".") 3) + "\n"))) + (dwim-shell-command-on-marked-files + "Clean untracked" + "git clean -f ." + :utils "git" + :silent-success t))) + (defun dwim-shell-commands-external-ip () "Copy external IP to kill ring." (interactive) @@ -801,7 +1031,7 @@ ffmpeg -n -i '<>' -vf \"scale=$width:-2\" '<>_x<>.<< (kill-new ip) (message "Copied %s" ip))) -(defun dwim-shell-commands-install-iphone-device-ipa () +(defun dwim-shell-commands-macos-install-iphone-device-ipa () "Install iPhone device .ipa. Needs ideviceinstaller and libmobiledevice installed." (interactive) @@ -819,6 +1049,14 @@ Needs ideviceinstaller and libmobiledevice installed." (file-name-concat "~/Downloads" (file-name-nondirectory file))) :monitor-directory "~/Downloads")) +(defun dwim-shell-commands-duplicate () + "Duplicate file." + (interactive) + (dwim-shell-command-on-marked-files + "Duplicate file(s)." + "cp -R '<>' '<>'" + :utils "cp")) + (defun dwim-shell-commands-rename-all () "Rename all marked file(s)." (interactive) @@ -878,6 +1116,37 @@ gpg: decryption failed: No pinentry" :utils "gpgconf" :silent-success t)) +;; Based on +;; https://apps.bram85.nl/git/bram/gists/src/commit/31ac3363da925daafa2420b7f96c67612ca28241/gists/dwim-0x0-upload.el +(defun dwim-shell-commands-upload-to-0x0 () + "Upload the marked files to 0x0.st" + (interactive) + (dwim-shell-command-on-marked-files + "0x0 upload" + "curl -Ffile=@<> -Fsecret= https://0x0.st" + :utils "curl" + :post-process-template + ;; Insert the single quotes at the appropriate place according to + ;; 0x0.st example online: + ;; curl -F'file=@yourfile.png' -Fsecret= https://0x0.st + ;; + ;; The placement of these single quotes confuse the escaping + ;; mechanisms of dwim-shell-command, as it considers @ as the + ;; opening 'quote' as it appears right in front of <>. + (lambda (template path) + (string-replace "-Ffile" "-F'file" + (string-replace path (concat path "'") template))) + :on-completion + (lambda (buffer process) + (if (= (process-exit-status process) 0) + (with-current-buffer buffer + (let ((url (car (last (split-string (string-trim (buffer-string)) "\n"))))) + (eww url) + (kill-new url) + (message "Copied: %s" (current-kill 0))) + (kill-buffer buffer)) + (switch-to-buffer buffer))))) + (provide 'dwim-shell-commands) ;;; dwim-shell-commands.el ends here -- 2.47.3