Package: emacs;
Reported by: Álvaro Ramírez <alvaro <at> xenodium.com>
Date: Fri, 7 Feb 2025 15:00:02 UTC
Severity: wishlist
Tags: patch
Message #307 received at 76120 <at> debbugs.gnu.org (full text, mbox):
From: Visuwesh <visuweshm <at> gmail.com> To: Alvaro Ramirez <alvaro <at> xenodium.com> Cc: 76120 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>, stefankangas <at> gmail.com, shipmints <at> gmail.com, rms <at> gnu.org Subject: Re: bug#76120: This feature is not about "sharing", or a "native" anything. Date: Thu, 20 Feb 2025 17:54:02 +0530
[வியாழன் பிப்ரவரி 20, 2025] Alvaro Ramirez wrote: In interest of adapting my personal KDE Connect code as a plug-in for the new library, I have some comments below. > Eli Zaretskii <eliz <at> gnu.org> writes: > >>> From: Alvaro Ramirez <alvaro <at> xenodium.com> >>> Cc: 76120 <at> debbugs.gnu.org >>> Date: Wed, 19 Feb 2025 12:02:39 +0000 >>> >>> Richard Stallman <rms <at> gnu.org> writes: >>> >>> > Let's replace "share" with "manipulate" now, with the idea > >>> that >>> > we can >>> > change the word a few weeks from now -- after discussing > about >>> > the >>> > best word to use. >>> > >>> > "Manipulate" fits what the feature does, and it is clearly >>> > better than >>> > "share". If we find a better word, we can change to that. >>> >>> How about "send-to" as package prefix instead of "share"? The >>> context menu would also be "Send to...". >>> >>> "Send to" feels like a great candidate, as it would apply >>> generally across different platforms. >> >> Yes, I think it's better (just sand the same proposal, before even >> reading this). > > Sounds good. Thank you. > > Attaching the latest iteration of the patch (renamed > 0004-Add-Send-to-context-menu-item-to-mouse-el.patch).. > > New since 0003-Add-context-menu-share-to-mouse.el.patch: > > - Replacing the word "share" throughout patch and using "send"/"send > to" instead. > > From 7e1f671568fde6e5f3f3381a634f6ba6077105bd Mon Sep 17 00:00:00 2001 > From: xenodium <8107219+xenodium <at> users.noreply.github.com> > Date: Thu, 13 Feb 2025 17:30:01 +0000 > Subject: [PATCH] Add "Send to..." context menu item to mouse.el > > * lisp/send-to.el: New package implements sending to apps or services. > > * lisp/mouse.el (context-menu-send-to): Add "Send to..." context menu. > > * lisp/term/ns-win.el (ns-send-items): Expose native macOS send API. > > * src/nsfns.m (ns-send-items): Implement native macOS sending. > > * etc/NEWS: Announce the new feature. > --- > etc/NEWS | 7 ++ > lisp/mouse.el | 17 +++- > lisp/send-to.el | 216 ++++++++++++++++++++++++++++++++++++++++++++ > lisp/term/ns-win.el | 1 + > src/nsfns.m | 62 +++++++++++++ > 5 files changed, 302 insertions(+), 1 deletion(-) > create mode 100644 lisp/send-to.el > > diff --git a/etc/NEWS b/etc/NEWS > index dea24adb3c9..a1798322ad6 100644 > --- a/etc/NEWS > +++ b/etc/NEWS > @@ -280,6 +280,13 @@ customize help text for tabs displayed on the tab-bar. Help text is > normally shown in the echo area or via tooltips. See the variable's > docstring for arguments passed to a help-text function. > > +** Mouse > + > +--- > +*** context-menu-mode now includes a "Send to..." menu item. > +The menu item enables sending current file(s) or region text to external > +(non-Emacs) apps or services. See send-to.el for customisations. > + > ** Project > > --- > diff --git a/lisp/mouse.el b/lisp/mouse.el > index 1f0ca6a51b6..4076097c90c 100644 > --- a/lisp/mouse.el > +++ b/lisp/mouse.el > @@ -30,6 +30,7 @@ > ;;; Code: > > (eval-when-compile (require 'rect)) > +(eval-when-compile (require 'send-to)) > > ;; Indent track-mouse like progn. > (put 'track-mouse 'lisp-indent-function 0) > @@ -393,7 +394,8 @@ context-menu-functions > context-menu-region > context-menu-middle-separator > context-menu-local > - context-menu-minor) > + context-menu-minor > + context-menu-send-to) > "List of functions that produce the contents of the context menu. > Each function receives the menu and the mouse click event as its arguments > and should return the same menu with changes such as added new menu items." > @@ -536,6 +538,19 @@ context-menu-minor > (cdr mode)))) > menu) > > +(defun context-menu-send-to (menu _click) > + "Add a \"Send to...\" context MENU entry on supported platforms." > + (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) > + (when (send-to-supported-p) > + (define-key-after menu [separator-send] menu-bar-separator) > + (define-key-after menu [send] > + '(menu-item "Send to..." (lambda () > + (interactive) > + (send-to)) > + :help > + "Send item (region, buffer file, or dired files) to app or service"))) > + menu) > + > (defun context-menu-buffers (menu _click) > "Populate MENU with the buffer submenus to buffer switching." > (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) > diff --git a/lisp/send-to.el b/lisp/send-to.el > new file mode 100644 > index 00000000000..78fd1b03ec3 > --- /dev/null > +++ b/lisp/send-to.el > @@ -0,0 +1,216 @@ > +;;; send-to.el --- send files to apps or services -*- lexical-binding: t -*- > + > +;; Copyright (C) 1993-2025 Free Software Foundation, Inc. > + > +;; Maintainer: emacs-devel <at> gnu.org > +;; Keywords: send, apps > +;; Package: emacs > + > +;; This file is part of GNU Emacs. > + > +;; GNU Emacs 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. > + > +;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. > + > +;;; Commentary: > + > +;; This package provides commands to facilitate sending files to > +;; external apps or services. > +;; > +;; Enable `context-menu-mode' to get a "Send to..." context menu item. > +;; > +;; `send-to' uses `send-to-context-items-function' to > +;; pick which file(s) to send and `send-to-handler-function' to > +;; route handling to external (non-Emacs) app or service. > + > +;;; Code: > + > +(require 'seq) This isn't required since seq is preloaded nowadays IIRC. > +(declare-function dired-between-files "dired") > +(declare-function dired-get-filename "dired") > +(declare-function dired-get-marked-files "dired") > +(declare-function dired-move-to-filename "dired") > + > +(defgroup send-to nil > + "Send files or text to external applications or services." > + :group 'external > + :version "31.1") > + > +(defcustom send-to-support-checker-function #'send-to--default-support-checker-p > + "A function returning non-nil when sending is supported by current platform." > + :type '(function :tag "Function") > + :group 'send-to > + :version "31.1") Does this need to be a defcustom? This seems like an internal feature, and I wonder if a defvar/defcustom is necessary even. > +(defcustom send-to-handler-function #'send-to--default-handler > + "A function handling `send-to' external routing. > + > +The function receives a list of items to send. > + > +Each item can be a either a filename or plain text." I see that the way to distinguish between a filename and "plain text" is `file-exists-p'. Why not make filenames be a file: URL? What about other URLs such as https:? IMO, these should be accounted for since it is quite common to send URLs (to other devices in my case). > + :type '(function :tag "Function") > + :group 'send-to > + :version "31.1") > + > +(defcustom send-to-context-items-function #'send-to--default-context-items > + "A function to collect the items to be sent by `send-to'. > + > +Defaults to `send-to--default-context-items'. > + > +The function returns a list of items, where each item can be a either > +a filename or plain text." > + :type '(function :tag "Function") > + :group 'send-to > + :version "31.1") We should document how a filename is distinguished from plain text item here again. Also should this be buffer-local so different major-mode can provide different providers? The current approach is a bit difficult for major-mode authors to provide support for this feature. > +;;;###autoload > +(defun send-to-supported-p () > + "Return non-nil for platforms where `send-to' is supported." > + (unless send-to-support-checker-function > + (error "`send-to-support-checker-function' must be set")) > + (funcall send-to-support-checker-function)) > + > +;;;###autoload > +(defun send-to (&optional items) > + "Send file(s) or region text to external (non-Emacs) apps or services. > + > +Sending implementation is handled by `send-to-handler-function'. > + > +ITEMS list is automatically populated based on context. > +See `send-to-context-items-function' for details. > + > +ITEMS can be overridden to send a specific list of items." > + (interactive) > + (unless send-to-handler-function > + (error "`send-to-handler-function' must be set")) > + (unless send-to-context-items-function > + (error "`send-to-context-items-function' must be set")) > + (dolist (item items) > + (unless (stringp item) > + (error "Item must be a string: %s" item))) > + (funcall send-to-handler-function > + (or items > + (funcall send-to-context-items-function) > + (user-error "Nothing to send")))) > + > +(defun send-to--format-items (items) > + "Format ITEMS into a user-presentable message string." > + (truncate-string-to-width > + (string-join > + (seq-map (lambda (item) > + (if (and (stringp item) > + (file-exists-p item)) > + (format "\"%s\"" (file-name-nondirectory item)) > + (format "\"%s\"" (truncate-string-to-width item 35 nil nil "…")))) > + items) " ") 70 nil nil "…")) > + > +(defun send-to--dired-filenames-in-region () > + "If in `dired' buffer, return region files. nil otherwise." > + (when (and (derived-mode-p 'dired-mode) > + (use-region-p)) > + (let* ((start (region-beginning)) > + (end (region-end)) > + (marked-files (dired-get-marked-files nil nil nil t)) > + (active-marks-p (if (= (length marked-files) 1) > + nil ;; File found at point (not marked) > + (not (seq-empty-p marked-files)))) > + (filenames)) > + (when active-marks-p > + (user-error "Either mark `dired' files or select a region, but not both")) > + (save-excursion > + (save-restriction > + (goto-char start) > + (while (< (point) end) > + ;; Skip non-file lines. > + (while (and (< (point) end) (dired-between-files)) > + (forward-line 1)) > + (when (and (dired-get-filename nil t) > + ;; Filename must be in region. > + (< (save-excursion > + (forward-line 0) > + (dired-move-to-filename)) > + end)) > + (setq filenames (append filenames (list (dired-get-filename nil t))))) > + (forward-line 1)))) > + filenames))) > + > +(defun send-to--default-support-checker-p () > + "Return non-nil for platforms supporting send capability." > + (or (and (featurep 'ns) (fboundp 'ns-send-items)) > + (executable-find "xdg-open"))) > + > +(defun send-to--default-handler (items) > + "Send ITEMS to external (non-Emacs) apps or services. > + > +ITEMS can be filenames or any text to send. > + > +Text is written to a temporary file before sending." > + (unless items > + (error "Nothing to send")) > + (cond ((and (featurep 'ns) (fboundp 'ns-send-items)) > + (ns-send-items > + (seq-map #'send-to--convert-item-to-filename items))) > + ((executable-find "xdg-open") > + (when (y-or-n-p (format "Open externally: %s ?" > + (send-to--format-items items))) > + (dolist (item items) > + (with-temp-buffer > + (unless (zerop (call-process > + "xdg-open" nil (current-buffer) t > + (send-to--convert-item-to-filename > + item))) > + (error "%s" (string-trim (buffer-string)))))))) > + (t > + (error "Don't know how to sende %s (adjust `send-to-handler-function')" ^^^^^ Typo here! > + (send-to--format-items items))))) > + > +(defun send-to--convert-item-to-filename (item) > + "Convert ITEM to a filename. > + > +Unless ITEM is a verifiable filename, save its content to a file and > +return its new timestamped filename." > + (if (file-exists-p item) > + item > + (let ((filename (concat temporary-file-directory > + (format-time-string "%F_%H.%M.%S") ".txt"))) > + (with-temp-file filename > + (insert item)) > + filename))) > + > +(defun send-to--default-context-items () > + "Build a list of items to send based on default context. > + > +From a `dired' buffer, chosen items are based on either of these being active: > + > + - Marked files > + - Files in region. > + - File at point. > + > +From any other buffer, either of these two, in order of preference: > + > + - Active region text. > + - Buffer file." > + (cond ((derived-mode-p 'dired-mode) > + (or > + (send-to--dired-filenames-in-region) > + (dired-get-marked-files))) > + ((use-region-p) > + (list (buffer-substring-no-properties > + (region-beginning) > + (region-end)))) > + ((buffer-file-name) > + (list (buffer-file-name))))) Why not add (thing-at-point 'existing-filename)? Addition of this will also provide rudimentary support for other major-modes via thing-at-point-provider-alist.
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.