From 8ee21db4af0f1d3a11b943fa113201dd45e01784 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Sun, 24 Apr 2022 15:14:53 +0200 Subject: [PATCH] Add new function `read-string-from-buffer'. * doc/lispref/minibuf.texi (Text from Minibuffer): Document it. * lisp/textmodes/string-edit.el: New file. --- doc/lispref/minibuf.texi | 7 ++ etc/NEWS | 11 +++ lisp/textmodes/string-edit.el | 122 ++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 lisp/textmodes/string-edit.el diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index f05f087ba70..986da6365f9 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -244,6 +244,13 @@ This function works by calling the value)) @end group @end smallexample + +@findex read-string-from-buffer +If you have a long string (for instance, one that is several lines +long) that you wish to edit, using @code{read-string} may not be +ideal. In that case, popping to a new, normal buffer where the user +can edit the string may be more convenient, and you can use the +@code{read-string-from-buffer} function to do that. @end defun @defun read-regexp prompt &optional defaults history diff --git a/etc/NEWS b/etc/NEWS index 04231ff16fe..81e3003e05a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1469,6 +1469,17 @@ functions. * Lisp Changes in Emacs 29.1 +--- +** New function 'string-edit'. +This is meant to be used when the user has to edit a (potentially) +long string. It pops you to a new buffer where you can edit the +string, and a callback is called when the user types 'C-c C-c'. + ++++ +** New function 'read-string-from-buffer'. +This is a modal version of 'string-edit', and can be used as an +alternative to 'read-string'. + +++ ** The return value of 'clear-message-function' is not ignored anymore. If the function returns 'dont-clear-message', then the message is not diff --git a/lisp/textmodes/string-edit.el b/lisp/textmodes/string-edit.el new file mode 100644 index 00000000000..9f28fe773f0 --- /dev/null +++ b/lisp/textmodes/string-edit.el @@ -0,0 +1,122 @@ +;;; string-edit.el --- editing long strings -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org + +;; 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 . + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defface string-edit-help-text + '((t (:inherit font-lock-comment-face))) + "Face used on `string-edit' help text." + :group 'text + :version "29.1") + +(defvar string-edit--success-callback) +(defvar string-edit--abort-callback) + +(cl-defun string-edit (string success-callback + &key abort-callback help-text) + "Switch to a new buffer to edit STRING. +When the user finishes editing (with `C-c C-c'), SUCCESS-CALLBACK +is called with the resulting string. + +If the user aborts (with `C-c C-d'), ABORT-CALLBACK (if any) is +called with no parameters. + +If present, HELP-TEXT will be inserted at the start of the +buffer, but won't be included in the resulting string." + (pop-to-buffer-same-window (generate-new-buffer "*edit string*")) + (when help-text + (let ((inhibit-read-only t)) + (insert help-text) + (ensure-empty-lines 0) + (add-text-properties (point-min) (point) + (list 'intangible t + 'face 'string-edit-help-text + 'read-only t)) + (insert (propertize (make-separator-line) 'rear-nonsticky t)) + (add-text-properties (point-min) (point) + (list 'string-edit--help-text t)))) + (let ((start (point))) + (insert string) + (goto-char start)) + (set-buffer-modified-p nil) + (setq buffer-undo-list nil) + (string-edit-mode) + (setq-local string-edit--success-callback success-callback) + (when abort-callback + (setq-local string-edit--abort-callback abort-callback)) + (message "%S" (substitute-command-keys + "Type `C-c C-c' when you've finished editing"))) + +(defun read-string-from-buffer (string &optional help-text) + "Switch to a new buffer to edit STRING in a recursive edit. +The user finishes editing with `C-c C-c', or aborts with `C-c C-d'). + +If present, HELP-TEXT will be inserted at the start of the +buffer, but won't be included in the resulting string." + (string-edit + string + (lambda (edited) + (setq string edited) + (exit-recursive-edit)) + :help-text help-text + :abort-callback (lambda () + (exit-recursive-edit) + (error "Aborted edit"))) + (recursive-edit) + string) + +(defvar-keymap string-edit-mode-map + "C-c C-c" #'string-edit-done + "C-c C-d" #'string-edit-abort) + +(define-derived-mode string-edit-mode text-mode "String" + "Mode for editing strings." + :interactive nil) + +(defun string-edit-done () + "Finish editing the string and call the callback function. +This will kill the current buffer." + (interactive) + (goto-char (point-min)) + ;; Skip past the help text. + (when-let ((match (text-property-search-forward + 'string-edit--help-text nil t))) + (goto-char (prop-match-beginning match))) + (let ((string (buffer-substring (point) (point-max))) + (callback string-edit--success-callback)) + (kill-buffer (current-buffer)) + (funcall callback string))) + +(defun string-edit-abort () + "Abort editing the current string." + (interactive) + (let ((callback string-edit--abort-callback)) + (kill-buffer (current-buffer)) + (when callback + (funcall callback)))) + +(provide 'string-edit) + +;;; string-edit.el ends here -- 2.39.2