From: "João Távora" <joaotavora@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Christopher Wellons <wellons@nullprogram.com>,
Dmitry Gutov <dgutov@yandex.ru>,
andreyk.mad@gmail.com, 41531@debbugs.gnu.org
Subject: bug#41531: 27.0.91; Better handle asynchronous eldoc backends
Date: Tue, 26 May 2020 19:49:04 +0100 [thread overview]
Message-ID: <871rn6r0pr.fsf@gmail.com> (raw)
In-Reply-To: <jwv1rn64n38.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Tue, 26 May 2020 13:39:34 -0400")
[-- Attachment #1: Type: text/plain, Size: 2565 bytes --]
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> It is, yes. The code is clear, not transcendental at all. But writing
>> docstrings is hard. In fact, it's possibly the hardest part of the
>> game. Tell you what: you write the docstring for
>> eldoc-documentation-functions, I'll do the implementation.
>>
>> Deal?
>
> Deal, at the condition that the code comes before I write the docstrings ;-)
Okay. 2 patches attached (my version + your futures). Just search for
'WORLD CLASS DOCSTRING' and fill in your part.
For my part, really feels like I've reimplemented funcall.
Anyway both approaches lightly tested on this foo.el file:
;;; foo.el --- foobarbaz -*- lexical-binding:t; -*-
(setq-local eldoc-documentation-function #'eldoc-documentation-compose)
;; Callback approach
;;
(setq-local eldoc-documentation-functions nil)
(defun eldoc-cback-0 (cback &rest _)
(run-with-timer 0.22 nil
(lambda () (funcall cback (symbol-name (gensym "cback-0-"))))))
(defun eldoc-cback-1 (&rest _) (symbol-name (gensym "cback-1-")))
(defun eldoc-cback-2 (cback &rest _)
(run-with-timer 2.22 nil
(lambda () (funcall cback (symbol-name (gensym "cback-2-"))))))
(add-hook 'eldoc-documentation-functions #'eldoc-cback-0 00 t)
(add-hook 'eldoc-documentation-functions #'eldoc-cback-1 10 t)
(add-hook 'eldoc-documentation-functions #'eldoc-cback-2 20 t)
;; Very futuristic approach
;;
(setq-local eldoc-documentation-functions nil)
(defun eldoc-future-0 ()
(let ((f (eldoc-future-make)))
(run-with-timer 0.22 nil (lambda () (eldoc-future-set
f (symbol-name (gensym "future-0-")))))
f))
(defun eldoc-future-1 () (symbol-name (gensym "future-1-")))
(defun eldoc-future-2 ()
(let ((f (eldoc-future-make)))
(run-with-timer 2.22 nil (lambda () (eldoc-future-set
f (symbol-name (gensym "future-2-")))))
f))
(add-hook 'eldoc-documentation-functions #'eldoc-future-0 00 t)
(add-hook 'eldoc-documentation-functions #'eldoc-future-1 10 t)
(add-hook 'eldoc-documentation-functions #'eldoc-future-2 20 t)
> [ I like this deal: it's always better for someone else to write the
> docstrings, so at least 2 persons need to agree on what they think the
> code does (assuming the author of the code checks the resulting docstrings). ]
Yes, it's called cascade dev, and it gets a bad rep nowadays. Tho the
person who writes the docstrings usually goes first ;-)
João
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Better-handle-asynchronously-produced-eldoc-docstrin.patch --]
[-- Type: text/x-diff, Size: 11774 bytes --]
From b97c0cfb0cbfea20cbf75ad7ea8abf9f4c51ec54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= <joaotavora@gmail.com>
Date: Mon, 25 May 2020 16:39:40 +0100
Subject: [PATCH 1/2] Better handle asynchronously produced eldoc docstrings
No longer do clients of eldoc need to call eldoc-message (an internal
function) directly. They may return any non-nil, non-string value and
call a callback afterwards. This enables eldoc.el to exert control
over how (and crucially also when) to display the docstrings to the
user.
* lisp/emacs-lisp/eldoc.el (eldoc-documentation-functions):
Overhaul docstring.
(eldoc-documentation-compose, eldoc-documentation-default): Handle
non-nil, non-string values of elements of
eldoc-documentation-functions.
(eldoc-print-current-symbol-info): Redesign.
(eldoc--handle-multiline): New helper.
(eldoc--callback): New internal special var.
(Version): Bump to 1.1.0.
* lisp/hexl.el (hexl-print-current-point-info): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/cfengine.el (cfengine3-documentation-function):
Adjust to new eldoc-documentation-functions protocol.
* lisp/progmodes/elisp-mode.el
(elisp-eldoc-documentation-function): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/octave.el (octave-eldoc-function): Adjust to new
eldoc-documentation-functions protocol.
* lisp/progmodes/python.el (python-eldoc-function): Adjust to new
eldoc-documentation-functions protocol.
---
lisp/emacs-lisp/eldoc.el | 101 ++++++++++++++++++++++++++---------
lisp/hexl.el | 2 +-
lisp/progmodes/cfengine.el | 2 +-
lisp/progmodes/elisp-mode.el | 6 ++-
lisp/progmodes/octave.el | 4 +-
lisp/progmodes/python.el | 2 +-
6 files changed, 84 insertions(+), 33 deletions(-)
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index ef5dbf8103..fa36987014 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -5,7 +5,7 @@
;; Author: Noah Friedman <friedman@splode.com>
;; Keywords: extensions
;; Created: 1995-10-06
-;; Version: 1.0.0
+;; Version: 1.1.0
;; Package-Requires: ((emacs "26.3"))
;; This is a GNU ELPA :core package. Avoid functionality that is not
@@ -338,12 +338,24 @@ eldoc-display-message-no-interference-p
\f
(defvar eldoc-documentation-functions nil
- "Hook for functions to call to return doc string.
-Each function should accept no arguments and return a one-line
-string for displaying doc about a function etc. appropriate to
-the context around point. It should return nil if there's no doc
-appropriate for the context. Typically doc is returned if point
-is on a function-like name or in its arg list.
+ "Hook of functions that produce doc strings.
+Each hook function should accept at least one argument CALLBACK
+and decide whether to display a doc short string about the
+context around point. If the decision and the doc string can be
+produced quickly, the hook function can ignore CALLBACK and
+immediately return the doc string, or nil if there's no doc
+appropriate for the context. Otherwise, if its computation is
+expensive or can't be performed directly, the hook function
+should arrange for CALLBACK to be asynchronously called at a
+later time, passing it either nil or the desired doc string. The
+hook function should then return a non-nil, non-string value.
+
+Note that this hook is only in effect if the value of
+`eldoc-documentation-function' (notice the singular) is bound to
+one of its pre-set values.
+
+Typically doc is returned if point is on a function-like name or
+in its arg list.
Major modes should modify this hook locally, for example:
(add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t)
@@ -351,30 +363,30 @@ eldoc-documentation-functions
taken into account if the major mode specific function does not
return any documentation.")
+(defun eldoc--handle-multiline (res)
+ "Helper for handling a bit of `eldoc-echo-area-use-multiline-p'."
+ (if eldoc-echo-area-use-multiline-p res
+ (truncate-string-to-width
+ res (1- (window-width (minibuffer-window))))))
+
(defun eldoc-documentation-default ()
"Show first doc string for item at point.
Default value for `eldoc-documentation-function'."
- (let ((res (run-hook-with-args-until-success 'eldoc-documentation-functions)))
- (when res
- (if eldoc-echo-area-use-multiline-p res
- (truncate-string-to-width
- res (1- (window-width (minibuffer-window))))))))
+ (run-hook-with-args-until-success 'eldoc-documentation-functions
+ eldoc--callback))
(defun eldoc-documentation-compose ()
"Show multiple doc string results at once.
Meant as a value for `eldoc-documentation-function'."
- (let (res)
- (run-hook-wrapped
- 'eldoc-documentation-functions
- (lambda (f)
- (let ((str (funcall f)))
- (when str (push str res))
- nil)))
- (when res
- (setq res (mapconcat #'identity (nreverse res) ", "))
- (if eldoc-echo-area-use-multiline-p res
- (truncate-string-to-width
- res (1- (window-width (minibuffer-window))))))))
+ (let ((res 0))
+ (run-hook-wrapped 'eldoc-documentation-functions
+ (lambda (f)
+ (let ((str (funcall f eldoc--callback)))
+ (if (stringp str) (funcall eldoc--callback str)
+ (when str (setq res (1+ res))))
+ nil)))
+ ;; play ball with `eldoc-print-current-symbol-info'
+ (if (plusp res) (1- res) "")))
(defcustom eldoc-documentation-function #'eldoc-documentation-default
"Function to call to return doc string.
@@ -408,6 +420,11 @@ eldoc--supported-p
;; there's some eldoc support in the current buffer.
(local-variable-p 'eldoc-documentation-function))))
+;; this variable should be unbound, but that confuses
+;; `describe-symbol' for some reason.
+(defvar eldoc--callback nil
+ "Dynamically bound. Passed to `eldoc-documentation-functions'.")
+
(defun eldoc-print-current-symbol-info ()
"Print the text produced by `eldoc-documentation-function'."
;; This is run from post-command-hook or some idle timer thing,
@@ -417,11 +434,43 @@ eldoc-print-current-symbol-info
;; Erase the last message if we won't display a new one.
(when eldoc-last-message
(eldoc-message nil))
- (let ((non-essential t))
+ (let ((non-essential t)
+ (buffer (current-buffer)))
;; Only keep looking for the info as long as the user hasn't
;; requested our attention. This also locally disables inhibit-quit.
(while-no-input
- (eldoc-message (funcall eldoc-documentation-function)))))))
+ (let* (;; `wanted' and `received' keep track of how many
+ ;; docstrings we expect from the clients. negative
+ ;; `wanted' means store docstring for later but don't
+ ;; message yet; likewise for a positive value, but we
+ ;; decrease it by one. Any other value (including 0)
+ ;; means the next time the callback is called we're
+ ;; composing and outputting whatever we got.
+ (wanted -1) (received '())
+ (eldoc--callback
+ (lambda (string)
+ (with-current-buffer buffer
+ (cond ((and (numberp wanted) (not (zerop wanted)))
+ (if (plusp wanted)
+ (setq wanted (1- wanted))) ; decf where art thou?
+ (push string received))
+ (wanted
+ (unless (string= string "") (push string received))
+ (setq wanted nil)
+ (eldoc-message
+ (eldoc--handle-multiline
+ (mapconcat #'identity (nreverse received) ", "))))
+ (t
+ ;; For now, silently swallow anything the
+ ;; client unexpectedly gives us
+ )))))
+ (res (funcall eldoc-documentation-function)))
+ (cond (;; we got a string, we should output immediately
+ (stringp res) (setq wanted t) (funcall eldoc--callback res))
+ (;; got something else, trust eldoc--callback will be called
+ res (setq wanted res))
+ (;; got nil, clear the echo area
+ t (eldoc-message nil)))))))))
;; If the entire line cannot fit in the echo area, the symbol name may be
;; truncated or eliminated entirely from the output to make room for the
diff --git a/lisp/hexl.el b/lisp/hexl.el
index cf7118f208..38eca77e26 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -515,7 +515,7 @@ hexl-current-address
(message "Current address is %d/0x%08x" hexl-address hexl-address))
hexl-address))
-(defun hexl-print-current-point-info ()
+(defun hexl-print-current-point-info (&rest _ignored)
"Return current hexl-address in string.
This function is intended to be used as eldoc callback."
(let ((addr (hexl-current-address)))
diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el
index f25b3cb9e2..9a6d81ce06 100644
--- a/lisp/progmodes/cfengine.el
+++ b/lisp/progmodes/cfengine.el
@@ -1294,7 +1294,7 @@ cfengine3-make-syntax-cache
'symbols))
syntax)))
-(defun cfengine3-documentation-function ()
+(defun cfengine3-documentation-function (&rest _ignored)
"Document CFengine 3 functions around point.
Intended as the value of `eldoc-documentation-function', which see.
Use it by enabling `eldoc-mode'."
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index d37eb8c152..d7865a7319 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1402,8 +1402,10 @@ elisp--eldoc-last-data
or argument string for functions.
2 - `function' if function args, `variable' if variable documentation.")
-(defun elisp-eldoc-documentation-function ()
- "`eldoc-documentation-function' (which see) for Emacs Lisp."
+(defun elisp-eldoc-documentation-function (_ignored &rest _also-ignored)
+ "Contextual documentation function for Emacs Lisp.
+Intended to be placed in `eldoc-documentation-functions' (which
+see)."
(let ((current-symbol (elisp--current-symbol))
(current-fnsym (elisp--fnsym-in-current-sexp)))
(cond ((null current-fnsym)
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 352c1810d1..2cf305c404 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -1639,8 +1639,8 @@ octave-eldoc-function-signatures
(nreverse result)))))
(cdr octave-eldoc-cache))
-(defun octave-eldoc-function ()
- "A function for `eldoc-documentation-function' (which see)."
+(defun octave-eldoc-function (&rest _ignored)
+ "A function for `eldoc-documentation-functions' (which see)."
(when (inferior-octave-process-live-p)
(let* ((ppss (syntax-ppss))
(paren-pos (cadr ppss))
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 1ca9f01963..404a67ba9f 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -4571,7 +4571,7 @@ python-eldoc-function-timeout-permanent
:type 'boolean
:version "25.1")
-(defun python-eldoc-function ()
+(defun python-eldoc-function (&rest _ignored)
"`eldoc-documentation-function' for Python.
For this to work as best as possible you should call
`python-shell-send-buffer' from time to time so context in
--
2.20.1
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Reimplement-funcall-but-in-the-future.patch --]
[-- Type: text/x-diff, Size: 3905 bytes --]
From c039a4356fa363ef10b8320c3856eb3466eb32f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= <joaotavora@gmail.com>
Date: Tue, 26 May 2020 19:38:08 +0100
Subject: [PATCH 2/2] Reimplement funcall, but in the future
* lisp/emacs-lisp/eldoc.el (eldoc-future, eldoc-future-set)
(eldoc-future-set-call): New functions.
(eldoc-documentation-functions): Prepare for Stefan's genius
docstring.
(eldoc-documentation-default, eldoc-documentation-compose): Use
futuristic stuff.
---
lisp/emacs-lisp/eldoc.el | 54 ++++++++++++++++++++++++++--------------
1 file changed, 36 insertions(+), 18 deletions(-)
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index fa36987014..e015076c4d 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -337,18 +337,32 @@ eldoc-display-message-no-interference-p
(not (or executing-kbd-macro (bound-and-true-p edebug-active))))
\f
+;;;; Futuristic interlude
+(cl-defstruct (eldoc-future
+ (:conc-name eldoc-future--)
+ (:constructor eldoc-future-make ())) ; become Yoda we?
+ "<WORLD CLASS DOCSTRING>"
+ (value 'eldoc-future--unset)
+ callback)
+
+(defun eldoc-future-set (f v)
+ "<WORLD CLASS DOCSTRING>"
+ (cl-assert (eq (eldoc-future--value f) 'eldoc-future--unset))
+ (setf (eldoc-future--value f) v)
+ (when (eldoc-future--callback f)
+ (funcall (eldoc-future--callback f) v)))
+
+(defun eldoc-future-set-callback (f c)
+ "<WORLD CLASS DOCSTRING>"
+ (cl-assert (null (eldoc-future--callback f)))
+ (setf (eldoc-future--callback f) c)
+ (unless (eq (eldoc-future--value f) 'eldoc-future--unset)
+ (funcall c (eldoc-future--value f))))
+
+\f
(defvar eldoc-documentation-functions nil
"Hook of functions that produce doc strings.
-Each hook function should accept at least one argument CALLBACK
-and decide whether to display a doc short string about the
-context around point. If the decision and the doc string can be
-produced quickly, the hook function can ignore CALLBACK and
-immediately return the doc string, or nil if there's no doc
-appropriate for the context. Otherwise, if its computation is
-expensive or can't be performed directly, the hook function
-should arrange for CALLBACK to be asynchronously called at a
-later time, passing it either nil or the desired doc string. The
-hook function should then return a non-nil, non-string value.
+<WORLD CLASS DOCSTRING GOES HERE>
Note that this hook is only in effect if the value of
`eldoc-documentation-function' (notice the singular) is bound to
@@ -372,19 +386,23 @@ eldoc--handle-multiline
(defun eldoc-documentation-default ()
"Show first doc string for item at point.
Default value for `eldoc-documentation-function'."
- (run-hook-with-args-until-success 'eldoc-documentation-functions
- eldoc--callback))
+ (let ((x (run-hook-with-args-until-success 'eldoc-documentation-functions)))
+ (if (eldoc-future-p x) (eldoc-future-set-callback x eldoc--callback)
+ x)))
(defun eldoc-documentation-compose ()
"Show multiple doc string results at once.
Meant as a value for `eldoc-documentation-function'."
(let ((res 0))
- (run-hook-wrapped 'eldoc-documentation-functions
- (lambda (f)
- (let ((str (funcall f eldoc--callback)))
- (if (stringp str) (funcall eldoc--callback str)
- (when str (setq res (1+ res))))
- nil)))
+ (run-hook-wrapped
+ 'eldoc-documentation-functions
+ (lambda (f)
+ (let ((x (funcall f)))
+ (cond ((stringp x) (funcall eldoc--callback x))
+ ((eldoc-future-p x)
+ (eldoc-future-set-callback x eldoc--callback)
+ (setq res (1+ res))))
+ nil)))
;; play ball with `eldoc-print-current-symbol-info'
(if (plusp res) (1- res) "")))
--
2.20.1
next prev parent reply other threads:[~2020-05-26 18:49 UTC|newest]
Thread overview: 84+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-25 17:04 bug#41531: 27.0.91; Better handle asynchronous eldoc backends João Távora
2020-05-25 23:52 ` Dmitry Gutov
2020-05-26 1:21 ` João Távora
2020-05-26 13:57 ` Dmitry Gutov
2020-05-26 16:03 ` João Távora
2020-05-26 19:14 ` Dmitry Gutov
2020-05-26 20:00 ` João Távora
2020-05-27 21:14 ` Dmitry Gutov
2020-05-27 22:13 ` João Távora
2020-05-27 23:35 ` Dmitry Gutov
2020-05-27 23:57 ` João Távora
2020-05-26 2:38 ` Stefan Monnier
2020-05-26 11:22 ` João Távora
2020-05-26 14:53 ` Stefan Monnier
2020-05-26 15:19 ` João Távora
2020-05-26 15:56 ` Stefan Monnier
2020-05-26 16:26 ` João Távora
2020-05-26 17:39 ` Stefan Monnier
2020-05-26 18:49 ` João Távora [this message]
2020-06-03 2:45 ` Stefan Monnier
2020-06-03 18:07 ` João Távora
2020-06-03 20:22 ` Stefan Monnier
2020-06-03 20:36 ` João Távora
2020-06-03 21:21 ` Stefan Monnier
2020-06-05 11:26 ` João Távora
2020-06-03 21:28 ` Dmitry Gutov
2020-06-06 1:57 ` Dmitry Gutov
2020-05-26 13:32 ` Dmitry Gutov
2020-05-26 16:56 ` João Távora
2020-06-03 18:56 ` bug#41531: 28.0.50; proper Eldoc async support João Távora
2020-06-04 16:20 ` Andrii Kolomoiets
2020-06-04 18:22 ` Dmitry Gutov
2020-06-04 19:00 ` Andrii Kolomoiets
2020-06-05 22:53 ` João Távora
2020-06-05 11:00 ` João Távora
2020-06-05 17:50 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2020-06-05 23:25 ` João Távora
2020-06-05 23:28 ` João Távora
2020-06-11 11:11 ` Andrii Kolomoiets
2020-06-30 11:31 ` bug#41531: 27.0.91; Better handle asynchronous eldoc backends João Távora
2020-07-04 7:45 ` Eli Zaretskii
2020-07-04 9:21 ` João Távora
2020-07-04 9:31 ` Eli Zaretskii
2020-07-04 9:37 ` João Távora
2020-07-04 9:44 ` Eli Zaretskii
2020-07-04 11:00 ` João Távora
2020-07-04 21:06 ` Dmitry Gutov
2020-07-04 23:12 ` João Távora
2020-07-07 0:43 ` Dmitry Gutov
2020-07-07 10:58 ` João Távora
2020-07-07 14:18 ` Dmitry Gutov
2020-07-07 14:34 ` João Távora
2020-07-05 12:03 ` João Távora
2020-07-05 15:09 ` Eli Zaretskii
2020-07-05 15:13 ` Stefan Monnier
2020-07-04 10:04 ` Dmitry Gutov
2020-07-04 11:48 ` João Távora
2020-07-04 21:27 ` Dmitry Gutov
2020-07-04 21:30 ` Dmitry Gutov
2020-07-04 23:07 ` João Távora
2020-07-07 3:01 ` Dmitry Gutov
2020-07-07 10:56 ` João Távora
2020-07-07 12:23 ` João Távora
2020-07-07 13:38 ` Stefan Monnier
2020-07-07 14:24 ` Dmitry Gutov
2020-07-07 16:07 ` Stefan Monnier
2020-07-07 23:11 ` Dmitry Gutov
2020-07-08 3:58 ` Stefan Monnier
2020-07-08 11:20 ` Dmitry Gutov
2020-07-08 13:25 ` Stefan Monnier
2020-07-08 13:41 ` João Távora
2020-07-08 14:21 ` Dmitry Gutov
2020-07-08 15:12 ` João Távora
2020-07-08 18:32 ` Dmitry Gutov
2020-07-08 19:12 ` Eli Zaretskii
2020-07-07 14:45 ` João Távora
2020-07-07 14:40 ` Dmitry Gutov
2020-07-07 22:24 ` Dmitry Gutov
2020-07-07 22:49 ` João Távora
2020-07-07 23:00 ` Dmitry Gutov
2020-07-07 23:24 ` João Távora
2020-07-07 23:42 ` Dmitry Gutov
2020-07-07 23:46 ` João Távora
2020-07-08 0:10 ` Dmitry Gutov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=871rn6r0pr.fsf@gmail.com \
--to=joaotavora@gmail.com \
--cc=41531@debbugs.gnu.org \
--cc=andreyk.mad@gmail.com \
--cc=dgutov@yandex.ru \
--cc=monnier@iro.umontreal.ca \
--cc=wellons@nullprogram.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).