commit 97966c5154bb842a3e5d04a003feadf7d761259b (HEAD, refs/remotes/origin/master) Author: Po Lu Date: Fri Feb 4 15:39:45 2022 +0800 ; * src/xterm.c (x_alloc_nearest_color_1): Remove extraneous code. diff --git a/src/xterm.c b/src/xterm.c index 874baa20f8..deaa5b5961 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3096,7 +3096,6 @@ x_alloc_nearest_color_1 (Display *dpy, Colormap cmap, XColor *color) cells = x_color_cells (dpy, &no_cells); - XQueryColors (dpy, cmap, cells, no_cells); nearest = 0; /* I'm assuming CSE so I'm not going to condense this. */ nearest_delta = ((((color->red >> 8) - (cells[0].red >> 8)) commit 3da5dc66eae96ce3c62c875a4b0aa0838010bcd4 Author: Po Lu Date: Fri Feb 4 15:38:07 2022 +0800 Fix bit rot in the color allocation code * src/xterm.c (x_alloc_nearest_color_1): Reintroduce an older version of the code that would try to allocate a "compromise delta". diff --git a/src/xterm.c b/src/xterm.c index 2e09c454b2..874baa20f8 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3083,34 +3083,56 @@ x_alloc_nearest_color_1 (Display *dpy, Colormap cmap, XColor *color) if (rc == 0) { /* If we got to this point, the colormap is full, so we're going - to try to get the next closest color. The algorithm used is + to try and get the next closest color. The algorithm used is a least-squares matching, which is what X uses for closest color matching with StaticColor visuals. */ - int nearest, i; - int max_color_delta = 255; - int max_delta = 3 * max_color_delta; - int nearest_delta = max_delta + 1; - int ncells; - const XColor *cells = x_color_cells (dpy, &ncells); - - for (nearest = i = 0; i < ncells; ++i) - { - int dred = (color->red >> 8) - (cells[i].red >> 8); - int dgreen = (color->green >> 8) - (cells[i].green >> 8); - int dblue = (color->blue >> 8) - (cells[i].blue >> 8); - int delta = dred * dred + dgreen * dgreen + dblue * dblue; - if (delta < nearest_delta) + const XColor *cells; + int no_cells; + int nearest; + long nearest_delta, trial_delta; + int x; + Status status; + + cells = x_color_cells (dpy, &no_cells); + + XQueryColors (dpy, cmap, cells, no_cells); + nearest = 0; + /* I'm assuming CSE so I'm not going to condense this. */ + nearest_delta = ((((color->red >> 8) - (cells[0].red >> 8)) + * ((color->red >> 8) - (cells[0].red >> 8))) + + (((color->green >> 8) - (cells[0].green >> 8)) + * ((color->green >> 8) - (cells[0].green >> 8))) + + (((color->blue >> 8) - (cells[0].blue >> 8)) + * ((color->blue >> 8) - (cells[0].blue >> 8)))); + for (x = 1; x < no_cells; x++) + { + trial_delta = ((((color->red >> 8) - (cells[x].red >> 8)) + * ((color->red >> 8) - (cells[x].red >> 8))) + + (((color->green >> 8) - (cells[x].green >> 8)) + * ((color->green >> 8) - (cells[x].green >> 8))) + + (((color->blue >> 8) - (cells[x].blue >> 8)) + * ((color->blue >> 8) - (cells[x].blue >> 8)))); + if (trial_delta < nearest_delta) { - nearest = i; - nearest_delta = delta; + XColor temp; + temp.red = cells[x].red; + temp.green = cells[x].green; + temp.blue = cells[x].blue; + status = XAllocColor (dpy, cmap, &temp); + if (status) + { + nearest = x; + nearest_delta = trial_delta; + } } } - - color->red = cells[nearest].red; + color->red = cells[nearest].red; color->green = cells[nearest].green; - color->blue = cells[nearest].blue; - rc = XAllocColor (dpy, cmap, color) != 0; + color->blue = cells[nearest].blue; + status = XAllocColor (dpy, cmap, color); + + rc = status != 0; } else { commit 8e2d9193ef2700bb93313525cf6e232619860711 Author: Po Lu Date: Fri Feb 4 15:17:40 2022 +0800 * src/xwidget.c (xwidget_button): Always let button events through. Filtering out emulated events is done in handle_one_xevent, so all this accomplishes is to filter out legitimate button events. diff --git a/src/xwidget.c b/src/xwidget.c index 23031f9c93..fc6ba2c633 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -1291,12 +1291,7 @@ xwidget_button (struct xwidget_view *view, if (button < 4 || button > 8) xwidget_button_1 (view, down_p, x, y, button, modifier_state, time); -#ifndef HAVE_XINPUT2 else -#else - else if (!FRAME_DISPLAY_INFO (view->frame)->supports_xi2 - || FRAME_DISPLAY_INFO (view->frame)->xi2_version < 1) -#endif { if (!down_p) { commit e0260f0fe2f10c414afbbf484978149808f42f53 Merge: a42df6542a 0591aa6cd3 Author: Stefan Kangas Date: Fri Feb 4 06:30:30 2022 +0100 Merge from origin/emacs-28 0591aa6cd3 Revert an erroneous change in tramp-cache.el commit a42df6542ac8b66d7a05c2746b2045d1e7df252b Author: Po Lu Date: Fri Feb 4 13:13:06 2022 +0800 Fix toggling `x-gtk-use-native-input'. * lisp/term/x-win.el (x-internal-focus-input-context): (x-gtk-use-native-input-watcher): Update for changes to `x-internal-focus-input-context'. * src/xfns.c (Fx_internal_focus_input_context): New parameter `focus'. diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el index 80a7d81d08..298c23566e 100644 --- a/lisp/term/x-win.el +++ b/lisp/term/x-win.el @@ -1563,16 +1563,15 @@ EVENT is a preedit-text event." (defvaralias 'x-gtk-use-system-tooltips 'use-system-tooltips) -(declare-function x-internal-focus-input-context (frame) "xfns.c") +(declare-function x-internal-focus-input-context (focus frame) "xfns.c") (defun x-gtk-use-native-input-watcher (_symbol newval &rest _ignored) "Variable watcher for `x-gtk-use-native-input'. If NEWVAL is non-nil and the selected frame is displayed through X, focus the GTK input context." (when (and (featurep 'gtk) - newval (eq (framep (selected-frame)) 'x)) - (x-internal-focus-input-context (selected-frame)))) + (x-internal-focus-input-context newval (selected-frame)))) (add-variable-watcher 'x-gtk-use-native-input #'x-gtk-use-native-input-watcher) diff --git a/src/xfns.c b/src/xfns.c index deaf4204a9..4b10f5035a 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -8567,18 +8567,28 @@ DEFUN ("x-gtk-debug", Fx_gtk_debug, Sx_gtk_debug, 1, 1, 0, #endif /* USE_GTK */ DEFUN ("x-internal-focus-input-context", Fx_internal_focus_input_context, - Sx_internal_focus_input_context, 1, 1, 0, + Sx_internal_focus_input_context, 2, 2, 0, doc: /* Focus and set the client window of FRAME's GTK input context. +If FOCUS is nil, focus out and remove the client window instead. This should be called from a variable watcher for `x-gtk-use-native-input'. */) - (Lisp_Object frame) + (Lisp_Object focus, Lisp_Object frame) { #ifdef USE_GTK struct frame *f = decode_window_system_frame (frame); GtkWidget *widget = FRAME_GTK_OUTER_WIDGET (f); - gtk_im_context_focus_in (FRAME_X_OUTPUT (f)->im_context); - gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, - gtk_widget_get_window (widget)); + if (!NILP (focus)) + { + gtk_im_context_focus_in (FRAME_X_OUTPUT (f)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, + gtk_widget_get_window (widget)); + } + else + { + gtk_im_context_focus_out (FRAME_X_OUTPUT (f)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, + NULL); + } #endif return Qnil; commit a654985bca4c48472bd17a2723fc93bcd5e27d6c Author: Po Lu Date: Fri Feb 4 03:28:05 2022 +0000 Make Emacs build with some other XLib implementations * configure.ac (HAVE_XKB): Check for functions that aren't always implemented by various XLib implementations. * src/xfns.c (select_visual): Handle NULL values of vinfo. (XkbRefreshKeyboardMapping): (XkbFreeNames): (XDisplayCells): (XDestroySubwindows): Define replacement functions where they aren't available. * src/xterm.c (x_find_modifier_meanings): Handle NULL values of various fields. diff --git a/configure.ac b/configure.ac index 9f4d5db43f..2ad5f2312c 100644 --- a/configure.ac +++ b/configure.ac @@ -2620,10 +2620,11 @@ if test "${HAVE_X11}" = "yes"; then emacs_cv_xkb=yes, emacs_cv_xkb=no)]) if test $emacs_cv_xkb = yes; then AC_DEFINE(HAVE_XKB, 1, [Define to 1 if you have the Xkb extension.]) + AC_CHECK_FUNCS(XkbRefreshKeyboardMapping XkbFreeNames) fi - AC_CHECK_FUNCS(XrmSetDatabase XScreenResourceString \ -XScreenNumberOfScreen) + AC_CHECK_FUNCS(XrmSetDatabase XScreenResourceString XScreenNumberOfScreen) + AC_CHECK_FUNCS(XDisplayCells XDestroySubwindows) fi if test "${window_system}" = "x11"; then diff --git a/src/xfns.c b/src/xfns.c index 1a0e9f50cd..deaf4204a9 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -6413,7 +6413,7 @@ select_visual (struct x_display_info *dpyinfo) | VisualClassMask), &vinfo_template, &n_visuals); - if (n_visuals > 0) + if (n_visuals > 0 && vinfo) { dpyinfo->n_planes = vinfo->depth; dpyinfo->visual = vinfo->visual; @@ -8644,6 +8644,54 @@ frame_parm_handler x_frame_parm_handlers[] = x_set_alpha_background, }; +/* Some versions of libX11 don't have symbols for a few functions we + need, so define replacements here. */ + +#ifdef HAVE_XKB +#ifndef HAVE_XKBREFRESHKEYBOARDMAPPING +Status +XkbRefreshKeyboardMapping (XkbMapNotifyEvent *event) +{ + return Success; +} +#endif + +#ifndef HAVE_XKBFREENAMES +void +XkbFreeNames (XkbDescPtr xkb, unsigned int which, Bool free_map) +{ + return; +} +#endif +#endif + +#ifndef HAVE_XDISPLAYCELLS +int +XDisplayCells (Display *dpy, int screen_number) +{ + return 1677216; +} +#endif + +#ifndef HAVE_XDESTROYSUBWINDOWS +int +XDestroySubwindows (Display *dpy, Window w) +{ + Window root, parent, *children; + unsigned int nchildren, i; + + if (XQueryTree (dpy, w, &root, &parent, &children, + &nchildren)) + { + for (i = 0; i < nchildren; ++i) + XDestroyWindow (dpy, children[i]); + XFree (children); + } + + return 0; +} +#endif + void syms_of_xfns (void) { diff --git a/src/xterm.c b/src/xterm.c index c24a2ab686..2e09c454b2 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5768,7 +5768,8 @@ x_find_modifier_meanings (struct x_display_info *dpyinfo) dpyinfo->hyper_mod_mask = 0; #ifdef HAVE_XKB - if (dpyinfo->xkb_desc) + if (dpyinfo->xkb_desc + && dpyinfo->xkb_desc->server) { for (i = 0; i < XkbNumVirtualMods; i++) { @@ -5810,6 +5811,14 @@ x_find_modifier_meanings (struct x_display_info *dpyinfo) syms = XGetKeyboardMapping (dpyinfo->display, min_code, max_code - min_code + 1, &syms_per_code); + + if (!syms) + { + dpyinfo->meta_mod_mask = Mod1Mask; + dpyinfo->super_mod_mask = Mod2Mask; + return; + } + mods = XGetModifierMapping (dpyinfo->display); /* Scan the modifier table to see which modifier bits the Meta and commit 52c4275283b342d3792d3eb4d4be44cb567b2bc7 Author: Po Lu Date: Fri Feb 4 10:16:14 2022 +0800 * src/xfns.c (Fx_internal_focus_input_context): Fix typo. diff --git a/src/xfns.c b/src/xfns.c index 7ca791afa1..1a0e9f50cd 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -8580,6 +8580,8 @@ This should be called from a variable watcher for `x-gtk-use-native-input'. */) gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, gtk_widget_get_window (widget)); #endif + + return Qnil; } /*********************************************************************** commit 334b362bf46859bc581643b0fab6674b4c5e1b54 Author: Po Lu Date: Fri Feb 4 10:11:31 2022 +0800 Make `x-gtk-use-native-input' take effect immediately * lisp/term/x-win.el (x-internal-focus-input-context): New declaration. (x-gtk-use-native-input-watcher): New variable watcher. * src/xfns.c (Fx_internal_focus_input_context): New function. (syms_of_xfns): Define new subr. diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el index 019a01e22c..80a7d81d08 100644 --- a/lisp/term/x-win.el +++ b/lisp/term/x-win.el @@ -1563,6 +1563,20 @@ EVENT is a preedit-text event." (defvaralias 'x-gtk-use-system-tooltips 'use-system-tooltips) +(declare-function x-internal-focus-input-context (frame) "xfns.c") + +(defun x-gtk-use-native-input-watcher (_symbol newval &rest _ignored) + "Variable watcher for `x-gtk-use-native-input'. +If NEWVAL is non-nil and the selected frame is displayed through X, +focus the GTK input context." + (when (and (featurep 'gtk) + newval + (eq (framep (selected-frame)) 'x)) + (x-internal-focus-input-context (selected-frame)))) + +(add-variable-watcher 'x-gtk-use-native-input + #'x-gtk-use-native-input-watcher) + (provide 'x-win) (provide 'term/x-win) diff --git a/src/xfns.c b/src/xfns.c index 7007f70b19..7ca791afa1 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -8565,6 +8565,22 @@ DEFUN ("x-gtk-debug", Fx_gtk_debug, Sx_gtk_debug, 1, 1, 0, #endif /* GTK_CHECK_VERSION (3, 14, 0) */ #endif /* HAVE_GTK3 */ #endif /* USE_GTK */ + +DEFUN ("x-internal-focus-input-context", Fx_internal_focus_input_context, + Sx_internal_focus_input_context, 1, 1, 0, + doc: /* Focus and set the client window of FRAME's GTK input context. +This should be called from a variable watcher for `x-gtk-use-native-input'. */) + (Lisp_Object frame) +{ +#ifdef USE_GTK + struct frame *f = decode_window_system_frame (frame); + GtkWidget *widget = FRAME_GTK_OUTER_WIDGET (f); + + gtk_im_context_focus_in (FRAME_X_OUTPUT (f)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, + gtk_widget_get_window (widget)); +#endif +} /*********************************************************************** Initialization @@ -8942,6 +8958,8 @@ eliminated in future versions of Emacs. */); defsubr (&Sx_select_font); #endif + defsubr (&Sx_internal_focus_input_context); + #ifdef USE_CAIRO defsubr (&Sx_export_frames); #ifdef USE_GTK commit 748eb32cc5e3e4264f074e6237dc544496f1c6f1 Author: Po Lu Date: Fri Feb 4 09:04:06 2022 +0800 Try to fix input method flicker on GTK builds * src/xterm.c (x_focus_changed): Only focus GTK context if native input is on. (bug#53698) diff --git a/src/xterm.c b/src/xterm.c index 68488b3ef5..c24a2ab686 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5370,10 +5370,13 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra #ifdef USE_GTK GtkWidget *widget; - gtk_im_context_focus_in (FRAME_X_OUTPUT (frame)->im_context); - widget = FRAME_GTK_OUTER_WIDGET (frame); - gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, - gtk_widget_get_window (widget)); + if (x_gtk_use_native_input) + { + gtk_im_context_focus_in (FRAME_X_OUTPUT (frame)->im_context); + widget = FRAME_GTK_OUTER_WIDGET (frame); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, + gtk_widget_get_window (widget)); + } #endif #endif } @@ -5394,8 +5397,11 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra if (FRAME_XIC (frame)) XUnsetICFocus (FRAME_XIC (frame)); #ifdef USE_GTK - gtk_im_context_focus_out (FRAME_X_OUTPUT (frame)->im_context); - gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, NULL); + if (x_gtk_use_native_input) + { + gtk_im_context_focus_out (FRAME_X_OUTPUT (frame)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (frame)->im_context, NULL); + } #endif #endif if (frame->pointer_invisible) commit 634cedfea17a8caa7d93627b233a2eef19fc43fb Author: Andrew G Cohen Date: Fri Feb 4 08:30:32 2022 +0800 Improve compression of article lists in gnus/nnselect * lisp/gnus/nnselect.el (nnselect-compress-artlist): Allow compressed list. nnselect-uncompress-artlist): Properly loop over rsv values. diff --git a/lisp/gnus/nnselect.el b/lisp/gnus/nnselect.el index 205456a57d..85df0284ef 100644 --- a/lisp/gnus/nnselect.el +++ b/lisp/gnus/nnselect.el @@ -79,30 +79,33 @@ ;;; Helper routines. (defun nnselect-compress-artlist (artlist) "Compress ARTLIST." - (let (selection) - (pcase-dolist (`(,artgroup . ,arts) - (nnselect-categorize artlist #'nnselect-artitem-group)) - (let (list) - (pcase-dolist (`(,rsv . ,articles) - (nnselect-categorize - arts #'nnselect-artitem-rsv #'nnselect-artitem-number)) - (push (cons rsv (gnus-compress-sequence (sort articles #'<))) - list)) - (push (cons artgroup list) selection))) - selection)) + (if (consp artlist) + artlist + (let (selection) + (pcase-dolist (`(,artgroup . ,arts) + (nnselect-categorize artlist #'nnselect-artitem-group)) + (let (list) + (pcase-dolist (`(,rsv . ,articles) + (nnselect-categorize + arts #'nnselect-artitem-rsv #'nnselect-artitem-number)) + (push (cons rsv (gnus-compress-sequence (sort articles #'<))) + list)) + (push (cons artgroup list) selection))) + selection))) (defun nnselect-uncompress-artlist (artlist) "Uncompress ARTLIST." (if (vectorp artlist) artlist (let (selection) - (pcase-dolist (`(,artgroup (,artrsv . ,artseq)) artlist) - (setq selection - (vconcat - (cl-map 'vector - (lambda (art) - (vector artgroup art artrsv)) - (gnus-uncompress-sequence artseq)) selection))) + (pcase-dolist (`(,artgroup . ,list) artlist) + (pcase-dolist (`(,artrsv . ,artseq) list) + (setq selection + (vconcat + (cl-map 'vector + (lambda (art) + (vector artgroup art artrsv)) + (gnus-uncompress-sequence artseq)) selection)))) selection))) (make-obsolete 'nnselect-group-server 'gnus-group-server "28.1") commit d539d00c4263e1d494213a0bcec655036405bd2e Author: Po Lu Date: Fri Feb 4 08:51:24 2022 +0800 Improve image depth handling Emacs defaults to a 32-bit TrueColor visual, but if that happens on a display which defaults to 16-bit TrueColor, yet happens to have 32-bit color, and doesn't have the X Render Extension, an error will occur in x_composite_image as libXpm will load pixmaps of depth 16 instead of depth 32. * src/image.c (x_create_x_image_and_pixmap): Explictly specify display depth. (x_create_xrender_picture): (xpm_load): (gs_load): Use dpyinfo->n_planes instead of DefaultDepthOfScreen. diff --git a/src/image.c b/src/image.c index 32e03ab6f7..7f2bd77781 100644 --- a/src/image.c +++ b/src/image.c @@ -2848,13 +2848,12 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, { Display *display = FRAME_X_DISPLAY (f); Drawable drawable = FRAME_X_DRAWABLE (f); - Screen *screen = FRAME_X_SCREEN (f); eassert (input_blocked_p ()); if (depth <= 0) - depth = DefaultDepthOfScreen (screen); - *ximg = XCreateImage (display, DefaultVisualOfScreen (screen), + depth = FRAME_DISPLAY_INFO (f)->n_planes; + *ximg = XCreateImage (display, FRAME_X_VISUAL (f), depth, ZPixmap, 0, NULL, width, height, depth > 16 ? 32 : depth > 8 ? 16 : 8, 0); if (*ximg == NULL) @@ -2910,7 +2909,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth) if (FRAME_DISPLAY_INFO (f)->xrender_supported_p) { if (depth <= 0) - depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); + depth = FRAME_DISPLAY_INFO (f)->n_planes; if (depth == 32 || depth == 24 || depth == 8 || depth == 4 || depth == 1) { /* FIXME: Do we need to handle all possible bit depths? @@ -4632,8 +4631,10 @@ xpm_load (struct frame *f, struct image *img) #ifndef HAVE_NTGUI attrs.visual = FRAME_X_VISUAL (f); attrs.colormap = FRAME_X_COLORMAP (f); + attrs.depth = FRAME_DISPLAY_INFO (f)->n_planes; attrs.valuemask |= XpmVisual; attrs.valuemask |= XpmColormap; + attrs.valuemask |= XpmDepth; #endif /* HAVE_NTGUI */ #ifdef ALLOC_XPM_COLORS @@ -11024,7 +11025,7 @@ gs_load (struct frame *f, struct image *img) block_input (); img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->width, img->height, - DefaultDepthOfScreen (FRAME_X_SCREEN (f))); + FRAME_DISPLAY_INFO (f)->n_planes); unblock_input (); } commit 12a6d319dcff5f1954f926be979b81170df2806c Author: Lars Ingebrigtsen Date: Thu Feb 3 22:16:14 2022 +0100 Improve the bookmark-default-handler doc string * lisp/bookmark.el (bookmark-default-handler): Document the `buffer' property (bug#20845). diff --git a/lisp/bookmark.el b/lisp/bookmark.el index 0279d5ea83..8753326881 100644 --- a/lisp/bookmark.el +++ b/lisp/bookmark.el @@ -1274,7 +1274,10 @@ then offer interactively to relocate BOOKMARK-NAME-OR-RECORD." (defun bookmark-default-handler (bmk-record) "Default handler to jump to a particular bookmark location. BMK-RECORD is a bookmark record, not a bookmark name (i.e., not a string). -Changes current buffer and point and returns nil, or signals a `file-error'." +Changes current buffer and point and returns nil, or signals a `file-error'. + +If BMK-RECORD has a property called `buffer', it should be a live +buffer object, and this buffer will be selected." (let ((file (bookmark-get-filename bmk-record)) (buf (bookmark-prop-get bmk-record 'buffer)) (forward-str (bookmark-get-front-context-string bmk-record)) commit 322daef4395630303c9b264f7c79839a70ee64bd Author: Lars Ingebrigtsen Date: Thu Feb 3 20:54:17 2022 +0100 Remove tarball instructions from gnus-faq.texi diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi index b576f383ac..630aaa282f 100644 --- a/doc/misc/gnus-faq.texi +++ b/doc/misc/gnus-faq.texi @@ -100,10 +100,9 @@ misprints are the Gnus team's fault, sorry. * FAQ 1-1:: What is the latest version of Gnus? * FAQ 1-2:: What's new in 5.10? * FAQ 1-3:: Where and how to get Gnus? -* FAQ 1-4:: What to do with the tarball now? -* FAQ 1-5:: I sometimes read references to No Gnus and Oort Gnus, +* FAQ 1-4:: I sometimes read references to No Gnus and Oort Gnus, what are those? -* FAQ 1-6:: Which version of Emacs do I need? +* FAQ 1-5:: Which version of Emacs do I need? @end menu @node FAQ 1-1 @@ -165,34 +164,6 @@ Gnus is bundled with Emacs. @node FAQ 1-4 @subsubheading Question 1.4 -What to do with the tarball now? - -@subsubheading Answer - -Untar it via @samp{tar xvzf gnus.tar.gz} and do the common -@samp{./configure; make; make install} circle. -(under MS-Windows either get the Cygwin environment from -@uref{https://www.cygwin.com} -which allows you to do what's described above or unpack the -tarball with some packer (e.g., Winace) -and use the batch-file make.bat included in the tarball to install -Gnus.) If you don't want to (or aren't allowed to) install Gnus -system-wide, you can install it in your home directory and add the -following lines to your ~/.emacs: - -@example -(add-to-list 'load-path "/path/to/gnus/lisp") -(add-to-list 'Info-default-directory-list "/path/to/gnus/texi/") -@end example -@noindent - -Make sure that you don't have any Gnus related stuff -before this line, on MS Windows use something like -"C:/path/to/lisp" (yes, "/"). - -@node FAQ 1-5 -@subsubheading Question 1.5 - I sometimes read references to No Gnus and Oort Gnus, what are those? @@ -205,8 +176,8 @@ once become Gnus 5.12 or Gnus 6. (If you're wondering why not 5.11, the odd version numbers are normally used for the Gnus versions bundled with Emacs) -@node FAQ 1-6 -@subsubheading Question 1.6 +@node FAQ 1-5 +@subsubheading Question 1.5 Which version of Emacs do I need? commit 2ded5938753a2fac94e5aebce2e97fc5b4d91076 Author: Lars Ingebrigtsen Date: Thu Feb 3 20:33:58 2022 +0100 Make sort-numeric-fields resilient towards blank lines * lisp/sort.el (sort-numeric-fields): Don't signal an error on blank lines (bug#31800). diff --git a/lisp/sort.el b/lisp/sort.el index eb8e2787d1..90eee01caf 100644 --- a/lisp/sort.el +++ b/lisp/sort.el @@ -286,25 +286,30 @@ FIELD, BEG and END. BEG and END specify region to sort." (interactive "p\nr") (let ;; To make `end-of-line' and etc. to ignore fields. ((inhibit-field-text-motion t)) - (sort-fields-1 field beg end - (lambda () - (sort-skip-fields field) - (let* ((case-fold-search t) - (base - (if (looking-at "\\(0x\\)[0-9a-f]\\|\\(0\\)[0-7]") - (cond ((match-beginning 1) - (goto-char (match-end 1)) - 16) - ((match-beginning 2) - (goto-char (match-end 2)) - 8) - (t nil))))) - (string-to-number (buffer-substring (point) - (save-excursion - (forward-sexp 1) - (point))) - (or base sort-numeric-base)))) - nil))) + (sort-fields-1 + field beg end + (lambda () + ;; Don't try to parse blank lines (they'll be + ;; sorted at the start). + (if (looking-at "[\t ]*$") + 0 + (sort-skip-fields field) + (let* ((case-fold-search t) + (base + (if (looking-at "\\(0x\\)[0-9a-f]\\|\\(0\\)[0-7]") + (cond ((match-beginning 1) + (goto-char (match-end 1)) + 16) + ((match-beginning 2) + (goto-char (match-end 2)) + 8) + (t nil))))) + (string-to-number (buffer-substring (point) + (save-excursion + (forward-sexp 1) + (point))) + (or base sort-numeric-base))))) + nil))) ;;;;;###autoload ;;(defun sort-float-fields (field beg end) commit e3516ec28f9a3fc81bffcf2d64a0d9e883a0ff26 Author: Jim Porter Date: Wed Feb 2 18:26:50 2022 -0800 Use 'require' to load eshell-tests-helpers * test/lisp/eshell/eshell-tests.el * test/lisp/eshell/em-extpipe-tests.el: Load eshell-tests-helpers with 'require'. * test/lisp/eshell/eshell-tests-helpers.el (eshell-history-file-name): Define this here so individual test files don't have to. diff --git a/test/lisp/eshell/em-extpipe-tests.el b/test/lisp/eshell/em-extpipe-tests.el index 0879ad5b0c..a1d15fe73a 100644 --- a/test/lisp/eshell/em-extpipe-tests.el +++ b/test/lisp/eshell/em-extpipe-tests.el @@ -28,14 +28,10 @@ (require 'ert) (require 'ert-x) (require 'em-extpipe) -(eval-and-compile - (load (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory))))) - -(defvar eshell-history-file-name) -(defvar eshell-test--max-subprocess-time) -(declare-function eshell-command-result-p "eshell-tests-helpers") +(require 'eshell-tests-helpers + (expand-file-name "eshell-tests-helpers" + (file-name-directory (or load-file-name + default-directory)))) (defmacro em-extpipe-tests--deftest (name input &rest body) (declare (indent 2)) diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el index f3fbe90356..33cdd60113 100644 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ b/test/lisp/eshell/eshell-tests-helpers.el @@ -30,6 +30,8 @@ (require 'esh-mode) (require 'eshell) +(defvar eshell-history-file-name nil) + (defvar eshell-test--max-subprocess-time 5 "The maximum amount of time to wait for a subprocess to finish, in seconds. See `eshell-wait-for-subprocess'.") diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index 3b1bbe7188..c5ca0a5485 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -29,16 +29,10 @@ (require 'ert-x) (require 'esh-mode) (require 'eshell) -(eval-and-compile - (load (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory))))) - -(defvar eshell-history-file-name) -(defvar eshell-test--max-subprocess-time) -(declare-function eshell-insert-command "eshell-tests-helpers") -(declare-function eshell-match-result "eshell-tests-helpers") -(declare-function eshell-command-result-p "eshell-tests-helpers") +(require 'eshell-tests-helpers + (expand-file-name "eshell-tests-helpers" + (file-name-directory (or load-file-name + default-directory)))) ;;; Tests: commit 3a388ab4a604f868ab2c1f9b3c63a7f7a3221f7f Author: Jim Porter Date: Sun Jan 30 18:53:53 2022 -0800 When executing an Eshell pipeline, send input to the first process Previously, input was sent to the last process in the pipeline, resulting in unexpected behavior when running commands like 'tr a-z A-Z | rev'. * lisp/eshell/esh-util.el (eshell-process-pair-p) (eshell-make-process-pair): New functions. * lisp/eshell/esh-cmd.el (eshell-last-async-proc): Rename to... (eshell-last-async-procs): ... this, and store a pair of processes. (eshell-interactive-process): Replace with... (eshell-interactive-process-p, eshell-head-process) (eshell-tail-process): ... these. (eshell-cmd-initialize): Set 'eshell-last-async-procs'. (eshell-do-pipelines): Set 'headproc'. (eshell-execute-pipeline): Return 'headproc' and 'tailproc'. (eshell-resume-eval): Use 'eshell-last-async-procs'. (eshell-do-eval): Make sure we work with a pair of processes. * lisp/eshell/esh-proc.el (eshell-send-eof-to-process): Move from here... * lisp/eshell/esh-mode.el (eshell-send-eof-to-process): ... to here, and only send EOF to the head process. * lisp/eshell/em-cmpl.el (eshell-complete-parse-arguments) * lisp/eshell/esh-mode.el (eshell-intercept-commands) (eshell-watch-for-password-prompt): Use 'eshell-interactive-process-p'. * lisp/eshell/em-rebind.el (eshell-delchar-or-maybe-eof) * lisp/eshell/em-term.el (eshell-term-send-raw-string) * lisp/eshell/esh-mode.el (eshell-self-insert-command) (eshell-send-input, eshell-send-invisible): Use 'eshell-head-process'. * lisp/eshell/esh-cmd.el (eshell-as-subcommand): Use 'eshell-tail-process'. * lisp/eshell/eshell.el (eshell-command): * test/lisp/eshell/eshell-tests-helpers.el (eshell-wait-for-subprocess): Use 'eshell-interactive-process-p' and 'eshell-tail-process'. * test/lisp/eshell/eshell-tests.el (eshell-test/pipe-headproc-stdin): New test. diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index c6a51b1793..b79475f6e0 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -314,7 +314,7 @@ to writing a completion function." (defun eshell-complete-parse-arguments () "Parse the command line arguments for `pcomplete-argument'." (when (and eshell-no-completion-during-jobs - (eshell-interactive-process)) + (eshell-interactive-process-p)) (insert-and-inherit "\t") (throw 'pcompleted t)) (let ((end (point-marker)) diff --git a/lisp/eshell/em-rebind.el b/lisp/eshell/em-rebind.el index f24758d4e3..2b56c9e844 100644 --- a/lisp/eshell/em-rebind.el +++ b/lisp/eshell/em-rebind.el @@ -238,7 +238,7 @@ lock it at that." Sends an EOF only if point is at the end of the buffer and there is no input." (interactive "p") - (let ((proc (eshell-interactive-process))) + (let ((proc (eshell-head-process))) (if (eobp) (cond ((/= (point) eshell-last-output-end) diff --git a/lisp/eshell/em-term.el b/lisp/eshell/em-term.el index e34c5ae47c..d150c07b03 100644 --- a/lisp/eshell/em-term.el +++ b/lisp/eshell/em-term.el @@ -224,7 +224,7 @@ the buffer." ; (defun eshell-term-send-raw-string (chars) ; (goto-char eshell-last-output-end) -; (process-send-string (eshell-interactive-process) chars)) +; (process-send-string (eshell-head-process) chars)) ; (defun eshell-term-send-raw () ; "Send the last character typed through the terminal-emulator diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 14139896dd..e702de03a0 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -279,14 +279,33 @@ otherwise t.") (defvar eshell-in-subcommand-p nil) (defvar eshell-last-arguments nil) (defvar eshell-last-command-name nil) -(defvar eshell-last-async-proc nil - "When this foreground process completes, resume command evaluation.") +(defvar eshell-last-async-procs nil + "The currently-running foreground process(es). +When executing a pipeline, this is a cons cell whose CAR is the +first process (usually reading from stdin) and whose CDR is the +last process (usually writing to stdout). Otherwise, the CAR and +CDR are the same process. + +When the process in the CDR completes, resume command evaluation.") ;;; Functions: -(defsubst eshell-interactive-process () - "Return currently running command process, if non-Lisp." - eshell-last-async-proc) +(defsubst eshell-interactive-process-p () + "Return non-nil if there is a currently running command process." + eshell-last-async-procs) + +(defsubst eshell-head-process () + "Return the currently running process at the head of any pipeline. +This only returns external (non-Lisp) processes." + (car-safe eshell-last-async-procs)) + +(defsubst eshell-tail-process () + "Return the currently running process at the tail of any pipeline. +This only returns external (non-Lisp) processes." + (cdr-safe eshell-last-async-procs)) + +(define-obsolete-function-alias 'eshell-interactive-process + 'eshell-tail-process "29.1") (defun eshell-cmd-initialize () ;Called from `eshell-mode' via intern-soft! "Initialize the Eshell command processing module." @@ -295,7 +314,7 @@ otherwise t.") (setq-local eshell-command-arguments nil) (setq-local eshell-last-arguments nil) (setq-local eshell-last-command-name nil) - (setq-local eshell-last-async-proc nil) + (setq-local eshell-last-async-procs nil) (add-hook 'eshell-kill-hook #'eshell-resume-command nil t) @@ -306,7 +325,7 @@ otherwise t.") (add-hook 'eshell-post-command-hook (lambda () (setq eshell-current-command nil - eshell-last-async-proc nil)) + eshell-last-async-procs nil)) nil t) (add-hook 'eshell-parse-argument-hook @@ -781,6 +800,8 @@ This macro calls itself recursively, with NOTFIRST non-nil." ((cdr pipeline) t) (t (quote 'last))))) (let ((proc ,(car pipeline))) + ,(unless notfirst + '(setq headproc proc)) (setq tailproc (or tailproc proc)) proc)))))) @@ -823,7 +844,7 @@ This is used on systems where async subprocesses are not supported." (defmacro eshell-execute-pipeline (pipeline) "Execute the commands in PIPELINE, connecting each to one another." - `(let ((eshell-in-pipeline-p t) tailproc) + `(let ((eshell-in-pipeline-p t) headproc tailproc) (progn ,(if (fboundp 'make-process) `(eshell-do-pipelines ,pipeline) @@ -833,7 +854,7 @@ This is used on systems where async subprocesses are not supported." (car (aref eshell-current-handles ,eshell-error-handle)) nil))) (eshell-do-pipelines-synchronously ,pipeline))) - (eshell-process-identity tailproc)))) + (eshell-process-identity (cons headproc tailproc))))) (defmacro eshell-as-subcommand (command) "Execute COMMAND using a temp buffer. @@ -993,24 +1014,24 @@ produced by `eshell-parse-command'." (unless (or (not (stringp status)) (string= "stopped" status) (string-match eshell-reset-signals status)) - (if (eq proc (eshell-interactive-process)) + (if (eq proc (eshell-tail-process)) (eshell-resume-eval))))) (defun eshell-resume-eval () "Destructively evaluate a form which may need to be deferred." (eshell-condition-case err (progn - (setq eshell-last-async-proc nil) + (setq eshell-last-async-procs nil) (when eshell-current-command (let* (retval - (proc (catch 'eshell-defer + (procs (catch 'eshell-defer (ignore (setq retval (eshell-do-eval eshell-current-command)))))) - (if (eshell-processp proc) - (ignore (setq eshell-last-async-proc proc)) - (cadr retval))))) + (if (eshell-process-pair-p procs) + (ignore (setq eshell-last-async-procs procs)) + (cadr retval))))) (error (error (error-message-string err))))) @@ -1173,17 +1194,16 @@ be finished later after the completion of an asynchronous subprocess." (setcar form (car new-form)) (setcdr form (cdr new-form))) (eshell-do-eval form synchronous-p)) - (if (and (memq (car form) eshell-deferrable-commands) - (not eshell-current-subjob-p) - result - (eshell-processp result)) - (if synchronous-p - (eshell/wait result) + (if-let (((memq (car form) eshell-deferrable-commands)) + ((not eshell-current-subjob-p)) + (procs (eshell-make-process-pair result))) + (if synchronous-p + (eshell/wait (cdr procs)) (eshell-manipulate "inserting ignore form" (setcar form 'ignore) (setcdr form nil)) - (throw 'eshell-defer result)) - (list 'quote result)))))))))))) + (throw 'eshell-defer procs)) + (list 'quote result)))))))))))) ;; command invocation diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 2e0f312f4a..8e6463eac2 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -485,7 +485,7 @@ Returns what was actually sent, or nil if nothing was sent." ((eshell-processp target) (when (eq (process-status target) 'run) (unless (stringp object) - (setq object (eshell-stringify object))) + (setq object (eshell-stringify object))) (process-send-string target object))) ((consp target) diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 8302eefe1e..59c8f8034f 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -423,13 +423,13 @@ and the hook `eshell-exit-hook'." (defun eshell-self-insert-command () (interactive) (process-send-string - (eshell-interactive-process) + (eshell-head-process) (char-to-string (if (symbolp last-command-event) (get last-command-event 'ascii-character) last-command-event)))) (defun eshell-intercept-commands () - (when (and (eshell-interactive-process) + (when (and (eshell-interactive-process-p) (not (and (integerp last-input-event) (memq last-input-event '(?\C-x ?\C-c))))) (let ((possible-events (where-is-internal this-command)) @@ -595,13 +595,13 @@ If NO-NEWLINE is non-nil, the input is sent without an implied final newline." (interactive "P") ;; Note that the input string does not include its terminal newline. - (let ((proc-running-p (and (eshell-interactive-process) + (let ((proc-running-p (and (eshell-head-process) (not queue-p))) (inhibit-point-motion-hooks t) (inhibit-modification-hooks t)) (unless (and proc-running-p (not (eq (process-status - (eshell-interactive-process)) + (eshell-head-process)) 'run))) (if (or proc-running-p (>= (point) eshell-last-output-end)) @@ -627,8 +627,8 @@ newline." (if (or eshell-send-direct-to-subprocesses (= eshell-last-input-start eshell-last-input-end)) (unless no-newline - (process-send-string (eshell-interactive-process) "\n")) - (process-send-region (eshell-interactive-process) + (process-send-string (eshell-head-process) "\n")) + (process-send-region (eshell-head-process) eshell-last-input-start eshell-last-input-end))) (if (= eshell-last-output-end (point)) @@ -665,6 +665,16 @@ newline." (run-hooks 'eshell-post-command-hook) (insert-and-inherit input))))))))) +(defun eshell-send-eof-to-process () + "Send EOF to the currently-running \"head\" process." + (interactive) + (require 'esh-mode) + (declare-function eshell-send-input "esh-mode" + (&optional use-region queue-p no-newline)) + (eshell-send-input nil nil t) + (when (eshell-head-process) + (process-send-eof (eshell-head-process)))) + (defsubst eshell-kill-new () "Add the last input text to the kill ring." (kill-ring-save eshell-last-input-start eshell-last-input-end)) @@ -924,9 +934,9 @@ Then send it to the process running in the current buffer." (interactive) ; Don't pass str as argument, to avoid snooping via C-x ESC ESC (let ((str (read-passwd (format "%s Password: " - (process-name (eshell-interactive-process)))))) + (process-name (eshell-head-process)))))) (if (stringp str) - (process-send-string (eshell-interactive-process) + (process-send-string (eshell-head-process) (concat str "\n")) (message "Warning: text will be echoed")))) @@ -937,7 +947,7 @@ buffer's process if STRING contains a password prompt defined by `eshell-password-prompt-regexp'. This function could be in the list `eshell-output-filter-functions'." - (when (eshell-interactive-process) + (when (eshell-interactive-process-p) (save-excursion (let ((case-fold-search t)) (goto-char eshell-last-output-block-begin) diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el index 5ed692fb5a..bb2136c06c 100644 --- a/lisp/eshell/esh-proc.el +++ b/lisp/eshell/esh-proc.el @@ -101,6 +101,8 @@ information, for example." (defvar eshell-process-list nil "A list of the current status of subprocesses.") +(declare-function eshell-send-eof-to-process "esh-mode") + (defvar-keymap eshell-proc-mode-map "C-c M-i" #'eshell-insert-process "C-c C-c" #'eshell-interrupt-process @@ -542,14 +544,5 @@ See the variable `eshell-kill-processes-on-exit'." ; ;; `eshell-resume-eval'. ; (eshell-kill-process-function nil "continue"))) -(defun eshell-send-eof-to-process () - "Send EOF to process." - (interactive) - (require 'esh-mode) - (declare-function eshell-send-input "esh-mode" - (&optional use-region queue-p no-newline)) - (eshell-send-input nil nil t) - (eshell-process-interact 'process-send-eof)) - (provide 'esh-proc) ;;; esh-proc.el ends here diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el index 0e04dbc7c9..788404fc43 100644 --- a/lisp/eshell/esh-util.el +++ b/lisp/eshell/esh-util.el @@ -609,6 +609,20 @@ gid format. Valid values are `string' and `integer', defaulting to "If the `processp' function does not exist, PROC is not a process." (and (fboundp 'processp) (processp proc))) +(defun eshell-process-pair-p (procs) + "Return non-nil if PROCS is a pair of process objects." + (and (consp procs) + (eshell-processp (car procs)) + (eshell-processp (cdr procs)))) + +(defun eshell-make-process-pair (procs) + "Make a pair of process objects from PROCS if possible. +This represents the head and tail of a pipeline of processes, +where the head and tail may be the same process." + (pcase procs + ((pred eshell-processp) (cons procs procs)) + ((pred eshell-process-pair-p) procs))) + ;; (defun eshell-copy-file ;; (file newname &optional ok-if-already-exists keep-date) ;; "Copy FILE to NEWNAME. See docs for `copy-file'." diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el index 5c356e8928..2c472a2afa 100644 --- a/lisp/eshell/eshell.el +++ b/lisp/eshell/eshell.el @@ -332,9 +332,9 @@ With prefix ARG, insert output into the current buffer at point." ;; make the output as attractive as possible, with no ;; extraneous newlines (when intr - (if (eshell-interactive-process) - (eshell-wait-for-process (eshell-interactive-process))) - (cl-assert (not (eshell-interactive-process))) + (if (eshell-interactive-process-p) + (eshell-wait-for-process (eshell-tail-process))) + (cl-assert (not (eshell-interactive-process-p))) (goto-char (point-max)) (while (and (bolp) (not (bobp))) (delete-char -1))) diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el index 77f5313d57..f3fbe90356 100644 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ b/test/lisp/eshell/eshell-tests-helpers.el @@ -53,7 +53,7 @@ See `eshell-wait-for-subprocess'.") If this takes longer than `eshell-test--max-subprocess-time', raise an error." (let ((start (current-time))) - (while (eshell-interactive-process) + (while (eshell-interactive-process-p) (when (> (float-time (time-since start)) eshell-test--max-subprocess-time) (error "timed out waiting for subprocess")) diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index 7658d5f551..3b1bbe7188 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -136,6 +136,17 @@ e.g. \"{(+ 1 2)} 3\" => 3" (eshell-command-result-p "*echo hi | echo bye" "bye\nhi\n"))) +(ert-deftest eshell-test/pipe-headproc-stdin () + "Check that standard input is sent to the head process in a pipeline" + (skip-unless (and (executable-find "tr") + (executable-find "rev"))) + (with-temp-eshell + (eshell-insert-command "tr a-z A-Z | rev") + (eshell-insert-command "hello") + (eshell-send-eof-to-process) + (eshell-wait-for-subprocess) + (eshell-match-result "OLLEH\n"))) + (ert-deftest eshell-test/window-height () "$LINES should equal (window-height)" (should (eshell-test-command-result "= $LINES (window-height)"))) commit 2e60ef696a3cf16b0eb297948e27ba0cb32a125e Author: Jim Porter Date: Thu Jan 27 23:13:36 2022 -0800 Ensure that tailproc is set for the last process in an Eshell pipeline In particular, this used to fail for pipelines where the last process in the pipeline came from the first element of the pipeline. This could happen when a process was piped to an ordinary Lisp function, like in '*echo hi | echo bye'. * lisp/eshell/esh-cmd.el (eshell-do-pipelines): Set the tailproc even for the first process in the pipeline. * test/lisp/eshell/eshell-tests.el (eshell-test/pipe-tailproc): New test. diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 04d65df4f3..14139896dd 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -764,8 +764,7 @@ This macro calls itself recursively, with NOTFIRST non-nil." (eshell-set-output-handle ,eshell-output-handle 'append nextproc) (eshell-set-output-handle ,eshell-error-handle - 'append nextproc) - (setq tailproc (or tailproc nextproc)))) + 'append nextproc))) ,(let ((head (car pipeline))) (if (memq (car head) '(let progn)) (setq head (car (last head)))) @@ -781,7 +780,9 @@ This macro calls itself recursively, with NOTFIRST non-nil." ,(cond ((not notfirst) (quote 'first)) ((cdr pipeline) t) (t (quote 'last))))) - ,(car pipeline)))))) + (let ((proc ,(car pipeline))) + (setq tailproc (or tailproc proc)) + proc)))))) (defmacro eshell-do-pipelines-synchronously (pipeline) "Execute the commands in PIPELINE in sequence synchronously. diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index 542815df80..7658d5f551 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -129,6 +129,13 @@ e.g. \"{(+ 1 2)} 3\" => 3" (eshell-command-result-p "echo ${echo hi}-${*echo there}" "hi-there\n"))) +(ert-deftest eshell-test/pipe-tailproc () + "Check that piping a process to a non-process command waits for the process" + (skip-unless (executable-find "echo")) + (with-temp-eshell + (eshell-command-result-p "*echo hi | echo bye" + "bye\nhi\n"))) + (ert-deftest eshell-test/window-height () "$LINES should equal (window-height)" (should (eshell-test-command-result "= $LINES (window-height)"))) commit dcc97fec29785051d7d11a66beb5f44fbaae6289 Author: Eli Zaretskii Date: Thu Feb 3 19:36:43 2022 +0200 Allow ensuring that a window's starting point is never invisible * src/xdisp.c (syms_of_xdisp) : New buffer-local variable. (redisplay_window): If 'make-window-start-visible' is non-nil, don't accept window-start position that is in invisible text or is covered by a "replacing" 'display' property. (Bug#14582) diff --git a/src/xdisp.c b/src/xdisp.c index 381df49070..20b0d97b97 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19036,8 +19036,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) force_start: /* Handle case where place to start displaying has been specified, - unless the specified location is outside the accessible range. */ - if (w->force_start) + unless the specified location is outside the accessible range, or + the buffer wants the window-start point to be always visible. */ + if (w->force_start && !make_window_start_visible) { /* We set this later on if we have to adjust point. */ int new_vpos = -1; @@ -19227,6 +19228,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) goto done; } + Lisp_Object iprop, dspec; + struct text_pos ignored; /* Handle case where text has not changed, only point, and it has not moved off the frame, and we are not retrying after hscroll. (current_matrix_up_to_date_p is true when retrying.) */ @@ -19248,10 +19251,28 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) } } /* If current starting point was originally the beginning of a line - but no longer is, find a new starting point. */ + but no longer is, or if the starting point is invisible but the + buffer wants it always visible, find a new starting point. */ else if (w->start_at_line_beg - && !(CHARPOS (startp) <= BEGV - || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n')) + && (!(CHARPOS (startp) <= BEGV + || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n') + || (make_window_start_visible + /* Is window-start in invisible text? */ + && ((CHARPOS (startp) > BEGV + && ((iprop = + Fget_char_property + (make_fixnum (CHARPOS (startp) - 1), Qinvisible, + window)), + TEXT_PROP_MEANS_INVISIBLE (iprop) != 0)) + /* Is window-start covered by a replacing + 'display' property? */ + || (!NILP (dspec = + Fget_char_property + (make_fixnum (CHARPOS (startp)), Qdisplay, + window)) + && handle_display_spec (NULL, dspec, Qnil, Qnil, + &ignored, CHARPOS (startp), + FRAME_WINDOW_P (f)) > 0))))) { #ifdef GLYPH_DEBUG debug_method_add (w, "recenter 1"); @@ -19327,6 +19348,21 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) goto force_start; } + /* Don't use the same window-start if it is covered by a + replacing 'display' property and the buffer requested the + window-start to be always visible. */ + if (make_window_start_visible + && !NILP (dspec = Fget_char_property (make_fixnum (CHARPOS (startp)), + Qdisplay, window)) + && handle_display_spec (NULL, dspec, Qnil, Qnil, &ignored, + CHARPOS (startp), FRAME_WINDOW_P (f)) > 0) + { +#ifdef GLYPH_DEBUG + debug_method_add (w, "recenter 2"); +#endif + goto recenter; + } + #ifdef GLYPH_DEBUG debug_method_add (w, "same window start"); #endif @@ -35973,6 +36009,12 @@ window, nil if it's okay to leave the cursor partially-visible. */); Vmake_cursor_line_fully_visible = Qt; DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible"); + DEFVAR_BOOL ("make-window-start-visible", make_window_start_visible, + doc: /* Whether to ensure `window-start' position is never invisible. */); + make_window_start_visible = false; + DEFSYM (Qmake_window_start_visible, "make-window-start-visible"); + Fmake_variable_buffer_local (Qmake_window_start_visible); + DEFSYM (Qclose_tab, "close-tab"); DEFVAR_LISP ("tab-bar-border", Vtab_bar_border, doc: /* Border below tab-bar in pixels. commit 0591aa6cd3b079028c05b82d3328a0eba1a11f1d (refs/remotes/origin/emacs-28) Author: Michael Albinus Date: Thu Feb 3 15:30:39 2022 +0100 Revert an erroneous change in tramp-cache.el * lisp/net/tramp-cache.el (tramp-get-hash-table): Use `string-match-p' instead of `string-search'. The latter one was introduced by accident. Reported by Kai Tetzlaff . diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el index fc8f04f527..d35f7ffa4e 100644 --- a/lisp/net/tramp-cache.el +++ b/lisp/net/tramp-cache.el @@ -125,7 +125,7 @@ If KEY is `tramp-cache-undefined', don't create anything, and return nil." (puthash key (make-hash-table :test #'equal) tramp-cache-data))) (when (tramp-file-name-p key) (dolist (elt tramp-connection-properties) - (when (tramp-compat-string-search + (when (string-match-p (or (nth 0 elt) "") (tramp-make-tramp-file-name key 'noloc 'nohop)) (tramp-set-connection-property key (nth 1 elt) (nth 2 elt))))) commit 37eef19fd608ca81acb40f974b8d7bbe7fc27127 Author: Thomas Fitzsimmons Date: Thu Feb 3 09:19:03 2022 -0500 soap-client: Add minimum supported Emacs version * lisp/net/soap-client.el: Add Emacs 24.1 to Package-Requires. Bump version to 3.2.1. diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el index 274e3b29e1..5e7bdbe6c6 100644 --- a/lisp/net/soap-client.el +++ b/lisp/net/soap-client.el @@ -5,12 +5,11 @@ ;; Author: Alexandru Harsanyi ;; Author: Thomas Fitzsimmons ;; Created: December, 2009 -;; Version: 3.2.0 +;; Version: 3.2.1 ;; Keywords: soap, web-services, comm, hypermedia ;; Package: soap-client ;; URL: https://github.com/alex-hhh/emacs-soap-client -;; Package-Requires: ((cl-lib "0.6.1")) -;;FIXME: Put in `Package-Requires:' the Emacs version we expect. +;; Package-Requires: ((emacs "24.1") (cl-lib "0.6.1")) ;; This file is part of GNU Emacs. commit 895562a8b24fc637fb422d38ea64e7d445b3480d Author: Michael Albinus Date: Thu Feb 3 14:21:23 2022 +0100 Implement file modification check in Tramp * lisp/net/tramp.el (tramp-handle-lock-file): Check, whether FILE is modified. * test/lisp/net/tramp-tests.el (tramp-test39-detect-external-change): New test. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index c6e55ff688..121ede42c4 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -4008,6 +4008,14 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.") ;; was visited. (catch 'dont-lock (unless (eq (file-locked-p file) t) ;; Locked by me. + (when (and buffer-file-truename + (not (verify-visited-file-modtime)) + (file-exists-p file)) + ;; In filelock.c, `userlock--ask-user-about-supersession-threat' + ;; is called, which also checks file contents. This is unwise + ;; for remote files. + (ask-user-about-supersession-threat file)) + (when-let ((info (tramp-get-lock-file file)) (match (string-match tramp-lock-file-info-regexp info))) (unless (ask-user-about-lock diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 01fe7355f4..b41824a6cf 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -6018,6 +6018,78 @@ Use direct async.") (ignore-errors (delete-file tmp-name1)) (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password))))) +;; The function was introduced in Emacs 28.1. +(ert-deftest tramp-test39-detect-external-change () + "Check that an external file modification is reported." + (skip-unless (tramp--test-enabled)) + (skip-unless (not (tramp--test-ange-ftp-p))) + ;; Since Emacs 28.1. + (skip-unless (fboundp 'file-locked-p)) + + (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil))) + (dolist (create-lockfiles '(nil t)) + (let ((tmp-name (tramp--test-make-temp-name nil quoted)) + (remote-file-name-inhibit-cache t) + (remote-file-name-inhibit-locks nil) + tramp-allow-unsafe-temporary-files + (inhibit-message t) + ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files. + (tramp-fuse-unmount-on-cleanup t) + auto-save-default + (backup-inhibited t) + noninteractive) + (with-temp-buffer + (unwind-protect + (progn + (setq buffer-file-name tmp-name + buffer-file-truename tmp-name) + (insert "foo") + ;; Bug#53207: with `create-lockfiles' nil, saving the + ;; buffer results in a prompt. + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (_) (ert-fail "Test failed unexpectedly")))) + (save-buffer)) + (should-not (file-locked-p tmp-name)) + + ;; For local files, just changing the file + ;; modification on disk doesn't hurt, because file + ;; contents in buffer and on disk are equal. For + ;; remote files, file contents is not compared. We + ;; mock an older modification time in buffer, because + ;; Tramp regards modification times equal if they + ;; differ for less than 2 seconds. + (set-visited-file-modtime (time-add (current-time) -60)) + ;; Some Tramp methods cannot check the file + ;; modification time properly, for them it doesn't + ;; make sense to test. + (when (not (verify-visited-file-modtime)) + (cl-letf (((symbol-function 'read-char-choice) + (lambda (prompt &rest _) (message "%s" prompt) ?y))) + (ert-with-message-capture captured-messages + (insert "bar") + (when create-lockfiles + (should (string-match-p + (format + "^%s changed on disk; really edit the buffer\\?" + (if (tramp--test-crypt-p) + ".+" (file-name-nondirectory tmp-name))) + captured-messages)) + (should (file-locked-p tmp-name))))) + + ;; `save-buffer' removes the file lock. + (cl-letf (((symbol-function 'yes-or-no-p) #'tramp--test-always) + ((symbol-function 'read-char-choice) + (lambda (&rest _) ?y))) + (save-buffer)) + (should-not (file-locked-p tmp-name)))) + + ;; Cleanup. + (set-buffer-modified-p nil) + (ignore-errors (delete-file tmp-name)) + (tramp-cleanup-connection + tramp-test-vec 'keep-debug 'keep-password))))))) + +;; The functions were introduced in Emacs 26.1. (ert-deftest tramp-test40-make-nearby-temp-file () "Check `make-nearby-temp-file' and `temporary-file-directory'." (skip-unless (tramp--test-enabled)) commit 3bd8a8bb91d5bf56216541f359c69c789c942b16 Author: Mattias EngdegÄrd Date: Thu Feb 3 12:25:53 2022 +0100 Don't use `string-replace` in soap-client (bug#53744) * lisp/net/soap-client.el (soap-decode-xs-complex-type): Cease using `string-replace` since it requires Emacs 28. diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el index 718cff829c..274e3b29e1 100644 --- a/lisp/net/soap-client.el +++ b/lisp/net/soap-client.el @@ -659,7 +659,7 @@ representing leap seconds." (if second (if second-fraction (let* ((second-fraction-significand - (string-replace "." "" second-fraction)) + (replace-regexp-in-string "\\." "" second-fraction)) (hertz (expt 10 (length second-fraction-significand))) (ticks (+ (* hertz (string-to-number second)) commit 1b0342628e565ff6fbb3ad1e374a2203d9d4a8ae Author: Mattias EngdegÄrd Date: Thu Feb 3 12:15:06 2022 +0100 Don't use `string-search` in soap-client (bug#53744) * lisp/net/soap-client.el (soap-decode-xs-complex-type): Since soap-client is a core ELPA package and may be used with Emacs versions older than 28, it cannot use `string-search`; revert to `string-match`. diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el index d2092633d8..718cff829c 100644 --- a/lisp/net/soap-client.el +++ b/lisp/net/soap-client.el @@ -1937,7 +1937,7 @@ This is a specialization of `soap-decode-type' for (e-name (soap-xs-element-name element)) ;; Heuristic: guess if we need to decode using local ;; namespaces. - (use-fq-names (string-search ":" (symbol-name (car node)))) + (use-fq-names (string-match ":" (symbol-name (car node)))) (children (if e-name (if use-fq-names ;; Find relevant children commit abd59ad923452cf342ba9d5eadf52bea02a261b0 Author: Eli Zaretskii Date: Thu Feb 3 13:00:47 2022 +0200 Fix "Buffers->Frames" submenu in daemon sessions * lisp/menu-bar.el (menu-bar-update-buffers): Detect the initial frame by its special terminal's name. (Bug#53740) * src/terminal.c (init_initial_terminal): Add comment about using the initial-terminal's name in menu-bar.el. diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 891cdfd5d5..7678b1ece6 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -2346,7 +2346,8 @@ It must accept a buffer as its only required argument.") ;; Ignore the initial frame if present. It can happen if ;; Emacs was started as a daemon. (bug#53740) (dolist (frame (frame-list)) - (unless (eq frame frame-initial-frame) + (unless (equal (terminal-name (frame-terminal frame)) + "initial_terminal") (push frame frames))) ;; Make the menu of buffers proper. (setq buffers-menu diff --git a/src/terminal.c b/src/terminal.c index 3db80f4b1f..80f3aed700 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -622,6 +622,8 @@ init_initial_terminal (void) emacs_abort (); initial_terminal = create_terminal (output_initial, NULL); + /* Note: menu-bar.el:menu-bar-update-buffers knows about this + special name of the initial terminal. */ initial_terminal->name = xstrdup ("initial_terminal"); initial_terminal->kboard = initial_kboard; initial_terminal->delete_terminal_hook = &delete_initial_terminal; commit 9b0cdf461c24e36cedda27c700febebb00b40909 Author: Po Lu Date: Thu Feb 3 10:34:50 2022 +0000 * src/haikufont.c (haikufont_draw): Allocate string buffer on stack. diff --git a/src/haikufont.c b/src/haikufont.c index 67b1113e44..1ef5f54c9a 100644 --- a/src/haikufont.c +++ b/src/haikufont.c @@ -1000,12 +1000,13 @@ haikufont_draw (struct glyph_string *s, int from, int to, else { ptrdiff_t b_len = 0; - char *b = xmalloc (b_len); + char *b = alloca ((to - from + 1) * MAX_MULTIBYTE_LENGTH); for (int idx = from; idx < to; ++idx) { int len = CHAR_STRING (s->char2b[idx], mb); - b = xrealloc (b, b_len = (b_len + len)); + b_len += len; + if (len == 1) b[b_len - len] = mb[0]; else @@ -1013,7 +1014,6 @@ haikufont_draw (struct glyph_string *s, int from, int to, } BView_DrawString (view, b, b_len); - xfree (b); } unblock_input ();