commit e2be1987a2e1206b77d2f11c78bb6e770a661452 (HEAD, refs/remotes/origin/master) Author: Po Lu Date: Mon Jan 8 15:40:45 2024 +0800 ; Fix a crash in sfnt_read_fvar_table * src/sfnt.c (sfnt_read_fvar_table): Derive padding from correct type. diff --git a/src/sfnt.c b/src/sfnt.c index b300eb4ba89..0666bb17cf0 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -14269,7 +14269,7 @@ sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) goto bail; - pad = alignof (struct sfnt_variation_axis); + pad = alignof (struct sfnt_instance); pad -= min_bytes & (pad - 1); if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes)) commit 2656d756851d97434da7846a5a30202baafb2241 Author: Po Lu Date: Mon Jan 8 15:32:07 2024 +0800 Properly instruct Italic Arial or BS Mono at small PPEM sizes * src/sfnt.c (sfnt_read_simple_glyph): Correct alignment errors. (PUSH2_UNCHECKED): Don't shift negative signed value to the left. (SLOOP): Permit LOOP to be set to 0, which inhibits the execution of instructions it affects. (sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Permit X and Y to be NULL. (sfnt_dot_fix_14): Guarantee that the final value is rounded to negative infinity, not zero. (sfnt_project_zp1_zp0_org): New function. (sfnt_interpret_mdrp): Avoid rounding issues by computing original distance from unscaled coordinates, if at all possible. (sfnt_interpret_simple_glyph, sfnt_interpret_compound_glyph_2): Set zone->simple. (all_tests) : Update test. (sfnt_identify_instruction, main): Adjust tests. * src/sfnt.h (struct sfnt_interpreter_zone): New field simple. diff --git a/src/sfnt.c b/src/sfnt.c index 36240f4cdff..b300eb4ba89 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -1937,8 +1937,11 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, simple->instructions comes one word past number_of_contours, because end_pts_of_contours also contains the instruction length. */ - simple->instructions = (uint8_t *) (simple->end_pts_of_contours - + glyph->number_of_contours + 1); + + simple->x_coordinates = (int16_t *) (simple->end_pts_of_contours + + glyph->number_of_contours + 1); + simple->y_coordinates = simple->x_coordinates + number_of_points; + simple->instructions = (uint8_t *) (simple->y_coordinates + number_of_points); simple->flags = simple->instructions + simple->instruction_length; /* Read instructions into the glyph. */ @@ -2022,7 +2025,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, /* Now that the flags have been decoded, start decoding the vectors. */ - simple->x_coordinates = (int16_t *) (simple->flags + number_of_points); vec_start = flags_start; i = 0; x = 0; @@ -2080,7 +2082,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, pointer to the flags for the current vector. */ flags_start = simple->flags; y = 0; - simple->y_coordinates = simple->x_coordinates + i; i = 0; while (i < number_of_points) @@ -6944,7 +6945,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, { \ int16_t word; \ \ - word = (((int8_t) high) << 8 | low); \ + word = (((uint8_t) high) << 8 | low); \ PUSH_UNCHECKED (word); \ } \ @@ -7024,14 +7025,18 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, #define SLOOP() \ { \ - uint32_t loop; \ + int32_t loop; \ \ loop = POP (); \ \ - if (!loop) \ - TRAP ("loop set to 0"); \ + if (loop < 0) \ + TRAP ("loop set to invalid value"); \ \ - interpreter->state.loop = loop; \ + /* N.B. loop might be greater than 65535, \ + but no reasonable font should define \ + such values. */ \ + interpreter->state.loop \ + = MIN (65535, loop); \ } #define SMD() \ @@ -8570,8 +8575,11 @@ sfnt_address_zp2 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP2 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -8618,8 +8626,11 @@ sfnt_address_zp1 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP1 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -8666,8 +8677,11 @@ sfnt_address_zp0 (struct sfnt_interpreter *interpreter, if (number >= interpreter->glyph_zone->num_points) TRAP ("address to ZP0 (glyph zone) out of bounds"); - *x = interpreter->glyph_zone->x_current[number]; - *y = interpreter->glyph_zone->y_current[number]; + if (x && y) + { + *x = interpreter->glyph_zone->x_current[number]; + *y = interpreter->glyph_zone->y_current[number]; + } if (x_org && y_org) { @@ -10570,6 +10584,7 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) return (int32_t) (((uint32_t) hi << 18) | (l >> 14)); #else int64_t xx, yy; + int64_t temp; xx = (int64_t) ax * bx; yy = (int64_t) ay * by; @@ -10578,7 +10593,12 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) yy = xx >> 63; xx += 0x2000 + yy; - return (int32_t) (xx / (1 << 14)); + /* TrueType fonts rely on "division" here truncating towards + negative infinity, so compute the arithmetic right shift in place + of division. */ + temp = -(xx < 0); + temp = (temp ^ xx) >> 14 ^ temp; + return (int32_t) (temp); #endif } @@ -11412,6 +11432,63 @@ sfnt_interpret_mirp (struct sfnt_interpreter *interpreter, interpreter->state.rp0 = p; } +/* Return the projection of the two points P1 and P2's original values + along the dual projection vector, with P1 inside ZP0 and P2 inside + ZP1. If this zone is the glyph zone and the outline positions of + those points are directly accessible, project their original + positions and scale the result with rounding, so as to prevent + rounding-introduced inaccuracies. + + The scenario where such inaccuracies are significant is generally + where an Italic glyph is being instructed at small PPEM sizes, + during which a point moved by MDAP[rN] is within 1/64th of a + pixel's distance from a point on the grid, yet the measurements + taken between such a point and the reference point against which + the distance to move is computed is such that the position of the + point after applying their rounded values differs by one grid + coordinate from the font designer's intentions, either exaggerating + or neutralizing the slant of the stem to which it belongs. + + This behavior applies only to MDRP, which see. */ + +static sfnt_f26dot6 +sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter, + uint32_t p1, uint32_t p2) +{ + sfnt_fword x1, y1, x2, y2, projection; + struct sfnt_simple_glyph *simple; + sfnt_f26dot6 org_x1, org_y1, org_x2, org_y2; + + /* Addressing the twilight zone, perhaps only partially. */ + if (!interpreter->state.zp0 + || !interpreter->state.zp1 + /* Not interpreting a glyph. */ + || !interpreter->glyph_zone + /* Not interpreting a simple glyph. */ + || !interpreter->glyph_zone->simple + /* P1 or P2 are phantom points. */ + || p1 >= interpreter->glyph_zone->simple->number_of_points + || p2 >= interpreter->glyph_zone->simple->number_of_points) + goto project_normally; + + simple = interpreter->glyph_zone->simple; + x1 = simple->x_coordinates[p1]; + y1 = simple->y_coordinates[p1]; + x2 = simple->x_coordinates[p2]; + y2 = simple->y_coordinates[p2]; + + /* Compute the projection. */ + projection = DUAL_PROJECT (x1 - x2, y1 - y2); + + /* Return the projection, scaled with rounding. */ + return sfnt_mul_fixed_round (projection, interpreter->scale); + + project_normally: + sfnt_address_zp1 (interpreter, p1, NULL, NULL, &org_x1, &org_y1); + sfnt_address_zp0 (interpreter, p2, NULL, NULL, &org_x2, &org_y2); + return DUAL_PROJECT (org_x1 - org_x2, org_y1 - org_y2); +} + /* Interpret an MDRP instruction with the specified OPCODE in INTERPRETER. Pop a point in ZP1, and move the point until its distance from RP0 in ZP0 is the same as in the original outline. @@ -11428,20 +11505,19 @@ sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter, uint32_t p; sfnt_f26dot6 distance, applied; sfnt_f26dot6 current_projection; - sfnt_f26dot6 x, y, org_x, org_y; - sfnt_f26dot6 rx, ry, org_rx, org_ry; + sfnt_f26dot6 x, y, rx, ry; /* Point number. */ p = POP (); /* Load the points. */ - sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y); + sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL); sfnt_address_zp0 (interpreter, interpreter->state.rp0, - &rx, &ry, &org_rx, &org_ry); + &rx, &ry, NULL, NULL); /* Calculate the distance between P and rp0 prior to hinting. */ - distance = DUAL_PROJECT (org_x - org_rx, - org_y - org_ry); + distance = sfnt_project_zp1_zp0_org (interpreter, p, + interpreter->state.rp0); /* Calculate the distance between P and rp0 as of now in the hinting process. */ @@ -12478,6 +12554,7 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, zone->y_current = zone->y_points + zone->num_points; zone->flags = (unsigned char *) (zone->y_current + zone->num_points); + zone->simple = glyph->simple; /* Load x_points and x_current. */ for (i = 0; i < glyph->simple->number_of_points; ++i) @@ -12776,6 +12853,7 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, zone->y_current = zone->y_points + zone->num_points; zone->flags = (unsigned char *) (zone->y_current + zone->num_points); + zone->simple = NULL; /* Copy and renumber all contour end points to start from base_index. */ @@ -18459,13 +18537,13 @@ static struct sfnt_interpreter_test all_tests[] = "SLOOP", /* PUSHB[0] 2 SLOOP[] - PUSHB[0] 0 + PUSHW[0] 255 255 (-1) SLOOP[] */ (unsigned char []) { 0xb0, 2, 0x17, - 0xb0, 0, + 0xb8, 255, 255, 0x17, }, - 6, + 7, NULL, sfnt_check_sloop, }, @@ -20258,7 +20336,8 @@ sfnt_identify_instruction (struct sfnt_interpreter *interpreter) return buffer; } - if (exec_fpgm->instructions + if (exec_fpgm + && exec_fpgm->instructions && where >= exec_fpgm->instructions && where < (exec_fpgm->instructions + exec_fpgm->num_instructions)) @@ -20529,6 +20608,13 @@ main (int argc, char **argv) if (!interpreter) abort (); + if (getenv ("SFNT_VERBOSE")) + { + interpreter->run_hook = sfnt_verbose; + interpreter->push_hook = sfnt_push_hook; + interpreter->pop_hook = sfnt_pop_hook; + } + for (i = 0; i < ARRAYELTS (all_tests); ++i) sfnt_run_interpreter_test (&all_tests[i], interpreter); @@ -20631,8 +20717,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 14 -#define EASY_PPEM 14 +#define FANCY_PPEM 16 +#define EASY_PPEM 16 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -21023,6 +21109,16 @@ main (int argc, char **argv) interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar, FANCY_PPEM, FANCY_PPEM); + + if (getenv ("SFNT_DEBUG")) + interpreter->run_hook = sfnt_run_hook; + else if (getenv ("SFNT_VERBOSE")) + { + interpreter->run_hook = sfnt_verbose; + interpreter->push_hook = sfnt_push_hook; + interpreter->pop_hook = sfnt_pop_hook; + } + state = interpreter->state; if (instance && gvar) @@ -21236,15 +21332,6 @@ main (int argc, char **argv) if (interpreter) { - if (getenv ("SFNT_DEBUG")) - interpreter->run_hook = sfnt_run_hook; - else if (getenv ("SFNT_VERBOSE")) - { - interpreter->run_hook = sfnt_verbose; - interpreter->push_hook = sfnt_push_hook; - interpreter->pop_hook = sfnt_pop_hook; - } - if (!sfnt_lookup_glyph_metrics (code, &metrics, hmtx, hhea, maxp)) { diff --git a/src/sfnt.h b/src/sfnt.h index 2b92f9f540a..5b01270e8ce 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1759,6 +1759,10 @@ struct sfnt_interpreter_zone /* Pointer to the flags associated with this data. */ unsigned char *flags; + + /* If this structure was produced from a simple glyph, pointer to + the simple glyph itself. NULL otherwise. */ + struct sfnt_simple_glyph *simple; }; enum commit c946efe7b3778302cd64442b451f806f4be7e78e Author: Po Lu Date: Mon Jan 8 15:26:50 2024 +0800 ; * etc/PROBLEMS: Remove several resolved problems. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 4d3b236ab03..7a5f029af65 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -3406,26 +3406,21 @@ this and many other problems do not exist on the regular X builds. ** Text displayed in the default monospace font looks horrible. -Droid Sans Mono (the default Monospace font which comes with Android) -incorporates instruction code designed for Microsoft's proprietary -TrueType font scaler. When this code is executed by Emacs to instruct -a glyph containing more than one component, it tries to address -"reference points" which are set to the values of two extra "phantom -points" in the glyph, that are a proprietary extension of the MS font -scaler. - -Emacs does not support these extensions, and as a result characters -such as - - ĥ - -display incorrectly, with the right most edge of the `h' component -stretched very far out to the right, on some low density displays. - -The solution is to replace the MS-specific hinting code in Droid Sans -Mono with automatically generated code from the FreeType project's -"ttfautohint" program. First, extract -'/system/fonts/DroidSansMono.ttf' from your device: +TrueType fonts incorporate instruction code executed by the font +scaler (the component responsible for transforming outlines into +bitmap images capable of being displayed onscreen), in order that +features of each glyph might be aligned to pixel boundaries +intelligently, preventing faintness while maintaining the shape of its +features. The substandard instruction code provided by the monospace +font distributed with Android misplaces features of such glyphs as "E" +and "F" between point sizes of 16 and 24, resulting in noticeable +whitespace inconsistencies with other glyphs. Furthermore, the +vertical stem in the glyph "T" is positioned too far to the left. + +The remedy for this is to replace the instruction code with +automatically generated code from the FreeType project's "ttfautohint" +program. First, extract '/system/fonts/DroidSansMono.ttf' from your +device: $ adb pull /system/fonts/DroidSansMono.ttf /system/fonts/DroidSansMono.ttf: 1 file pulled, 0 skipped. @@ -3448,85 +3443,18 @@ allowed by free versions of Android, such as Replicant): or to the user fonts directory described in the "Android Fonts" node of the Emacs manual. You may want to perform this procedure even if -you are not seeing problems with character display, as the -automatically generated instructions result in superior display -results that are easier to read. - -We have been told that the default Sans font under Android 2.3.7, -named "Droid Sans", also exhibits this problem. The procedure for -repairing the font is identical to the procedure outlined above, -albeit with "DroidSansMono" replaced by simply "DroidSans". - -** The "Anonymous Pro" font displays incorrectly. - -Glyph instruction code within the Anonymous Pro font relies on -undocumented features of the Microsoft TrueType font scaler, namely -that the scaler always resets the "projection" and "freedom" vector -interpreter control registers after the execution of the font -pre-program, which sets them to a value that is perpendicular to the -horizontal plane of movement. - -Since Emacs does not provide this "feature", various points inside -glyphs are moved vertically rather than horizontally when a glyph -program later executes an instruction such as "MIRP" (Move Indirect -Relative Point) that moves and measures points along the axis -specified by those registers. - -This can be remedied in two ways; the first (and the easiest) is to -replace its instruction code with that supplied by "ttfautohint", as -depicted above. The second is to patch the instruction code inside -the font itself, using the "ttx" utility: - - https://fonttools.readthedocs.io/en/latest/ttx.html - -First, convert the font to its XML representation: - - $ ttx Anonymous_Pro.ttf - -then, find the end of the section labeled 'prep': - - - - [...] - ROUND[01] /* Round */ - RTG[ ] /* RoundToGrid */ - WCVTP[ ] /* WriteCVTInPixels */ - - - -and insert the following instruction immediately before the closing -'/assembly' tag, so as to reset the interpreter control registers back -to their default values prior to the completion of the pre-program: - - SVTCA[1] /* Set Vector registers to Control Axis X */ - -Then, reassemble the font from the modified XML: - - $ ttx Anonymous_Pro.ttx - -which should produce a modified font by the name of -Anonymous_Pro#1.ttf. - -** The "IBM Plex Mono" font displays incorrectly. - -This problem is precipitated by an attempt to exploit the undocumented -feature of the MS font scaler explicated within the previous heading. - -Its remedy is also unsurprisingly alike the fix described there: both -patching the preprogram to reset the point movement vectors and -replacing the instruction code with code generated by "ttfautohint" -will adequately resolve the problem. +you are not experiencing problems with character display, as the +automatically generated instructions result in more legible text. ** Glyphs are missing within the "Arial" font or it does not load. -On account of its origins at Microsoft, instruction code included -within this font is awash with references to behavior specific to the -MS scaler. It is incorrigibly broken, to a degree that even -"ttfautohint" cannot repair; your only recourse is to select some -other font. - -This issue may extend beyond Arial to encompass a larger selection of -fonts designed by Microsoft. +Old versions of this font included instruction code that assumed a +degree of latitude from the Microsoft font scaler, which grants fonts +leave to address nonexistent points without aborting the scaling +process, among other invalid TrueType operations. This issue may +extend beyond Arial to encompass a larger selection of old fonts +designed by Microsoft or Monotype; most of the time, installing newer +versions of such fonts will suffice. ** Some TrueType test fonts don't work. @@ -3554,9 +3482,9 @@ Executing instruction code is not a strict requirement for producing correct display results from most current fonts. If a font's instruction code produces results that are merely unpleasing, but not incorrect, then the font was presumably not designed for Emacs's -scaler. If its uninstructed glyphs are satisfactory (such as if your -screen resolution is high to the extent that scaling artifacts prove -invisible), disable instruction code execution by appending its family +scaler. If its uninstructed glyphs are satisfactory (such as when +your screen resolution is high enough to ameliorate scaling +artifacts), disable instruction code execution by appending its family name to the variable 'sfnt-uninstructable-font-regexp', then restarting Emacs. commit 267c9b54b16e50f76e5ce88ff153d1a24d093563 Author: Stefan Kangas Date: Sun Jan 7 22:19:06 2024 +0100 Remove redundant conversion from bool to bool * src/xdisp.c (maybe_produce_line_number): Remove redundant conversion of the value of an expression from bool to bool. diff --git a/src/xdisp.c b/src/xdisp.c index f8670c6ecb5..14cf030ca4e 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -24648,7 +24648,7 @@ maybe_produce_line_number (struct it *it) /* Produce the glyphs for the line number. */ struct it tem_it; char lnum_buf[INT_STRLEN_BOUND (ptrdiff_t) + 1]; - bool beyond_zv = IT_BYTEPOS (*it) >= ZV_BYTE ? true : false; + bool beyond_zv = IT_BYTEPOS (*it) >= ZV_BYTE; ptrdiff_t lnum_offset = -1; /* to produce 1-based line numbers */ int lnum_face_id = merge_faces (it->w, Qline_number, 0, DEFAULT_FACE_ID); int current_lnum_face_id commit c21995ff008d37e768a33412cad5fc9f5c3c2dbb Author: Stefan Kangas Date: Sun Jan 7 22:07:45 2024 +0100 Support string literals in build_string.cocci * admin/coccinelle/build_string.cocci: Support string literals. diff --git a/admin/coccinelle/build_string.cocci b/admin/coccinelle/build_string.cocci index d47727018dd..9421a140658 100644 --- a/admin/coccinelle/build_string.cocci +++ b/admin/coccinelle/build_string.cocci @@ -4,3 +4,9 @@ identifier I; @@ - make_string (I, strlen (I)) + build_string (I) + +@@ +constant C; +@@ +- make_string (C, strlen (C)) ++ build_string (C) commit 6fdf035f62ed3cdd55a5cafe823a2d749637ce25 Author: Stefan Kangas Date: Sun Jan 7 21:43:56 2024 +0100 ; Delete a superfluous bitwise 'or' * src/xterm.c (x_term_init): Avoid bitwise 'or' using the same variable as both operands (X | X => X). diff --git a/src/xterm.c b/src/xterm.c index 1f398b2e39a..0cbf32ae1ea 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -31503,7 +31503,6 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) dpyinfo->selection_tracking_window, selection_name, (XFixesSetSelectionOwnerNotifyMask - | XFixesSetSelectionOwnerNotifyMask | XFixesSelectionClientCloseNotifyMask)); } commit 50f430ebcd87b77207013f97e6e5d1b8fe93f990 Author: F. Jason Park Date: Fri Jan 5 07:20:34 2024 -0800 Clarify purpose of module aliases in ERC * doc/misc/erc.texi: Mention that aliases should not be defined for new modules. * lisp/erc/erc-common.el (define-erc-module): Refactor slightly for readability. (erc-with-all-buffers-of-server): Redo doc string. * lisp/erc/erc-pcomplete.el: Declare `completion' module's feature and group as being `erc-pcomplete'. * test/lisp/erc/erc-tests.el (erc--find-group--real): Assert group lookup works for "normalized" module name `completion' of `erc-pcomplete-mode'. diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 52c7477c9dd..f877fb681fe 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi @@ -678,6 +678,14 @@ signals an error. Users defining personal modules in an init file should @code{(provide 'erc-my-module)} somewhere to placate ERC. Dynamically generating modules on the fly is not supported. +Some older built-in modules have a second name along with a second +minor-mode toggle, which is just a function alias for its primary +counterpart. For practical reasons, ERC does not define a +corresponding variable alias because contending with indirect +variables complicates bookkeeping tasks, such as persisting module +state across IRC sessions. New modules should definitely avoid +defining aliases without a good reason. + Some packages have been known to autoload a module's definition instead of its minor-mode command, which severs the link between the library and the module. This means that enabling the mode by invoking diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index 2581e40f850..28ab6aad466 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -333,6 +333,7 @@ instead of a `set' state, which precludes any actual saving." (read (current-buffer)))) (defmacro erc--find-feature (name alias) + ;; Don't use this outside of the file that defines NAME. `(pcase (erc--find-group ',name ,(and alias (list 'quote alias))) ('erc (and-let* ((file (or (macroexp-file-name) buffer-file-name))) (intern (file-name-base file)))) @@ -350,8 +351,12 @@ See Info node `(elisp) Defining Minor Modes' for more.") (defmacro define-erc-module (name alias doc enable-body disable-body &optional local-p) "Define a new minor mode using ERC conventions. -Symbol NAME is the name of the module. -Symbol ALIAS is the alias to use, or nil. +Expect NAME to be the module's name and ALIAS, when non-nil, to +be a retired name used only for compatibility purposes. In new +code, assume NAME is the same symbol users should specify when +customizing `erc-modules' (see info node `(erc) Module Loading' +for more on naming). + DOC is the documentation string to use for the minor mode. ENABLE-BODY is a list of expressions used to enable the mode. DISABLE-BODY is a list of expressions used to disable the mode. @@ -382,7 +387,10 @@ Example: (let* ((sn (symbol-name name)) (mode (intern (format "erc-%s-mode" (downcase sn)))) (enable (intern (format "erc-%s-enable" (downcase sn)))) - (disable (intern (format "erc-%s-disable" (downcase sn))))) + (disable (intern (format "erc-%s-disable" (downcase sn)))) + (nmodule (erc--normalize-module-symbol name)) + (amod (and alias (intern (format "erc-%s-mode" + (downcase (symbol-name alias))))))) `(progn (define-minor-mode ,mode @@ -399,13 +407,9 @@ if ARG is omitted or nil. (if ,mode (,enable) (,disable)))) ,(erc--assemble-toggle local-p name enable mode t enable-body) ,(erc--assemble-toggle local-p name disable mode nil disable-body) - ,@(and-let* ((alias) - ((not (eq name alias))) - (aname (intern (format "erc-%s-mode" - (downcase (symbol-name alias)))))) - `((defalias ',aname #',mode) - (put ',aname 'erc-module ',(erc--normalize-module-symbol name)))) - (put ',mode 'erc-module ',(erc--normalize-module-symbol name)) + ,@(and amod `((defalias ',amod #',mode) + (put ',amod 'erc-module ',nmodule))) + (put ',mode 'erc-module ',nmodule) ;; For find-function and find-variable. (put ',mode 'definition-name ',name) (put ',enable 'definition-name ',name) @@ -462,10 +466,9 @@ If no server buffer exists, return nil." ,@body))))) (defmacro erc-with-all-buffers-of-server (process pred &rest forms) - "Execute FORMS in all buffers which have same process as this server. -FORMS will be evaluated in all buffers having the process PROCESS and -where PRED matches or in all buffers of the server process if PRED is -nil." + "Evaluate FORMS in all buffers of PROCESS in which PRED returns non-nil. +When PROCESS is nil, do so in all ERC buffers. When PRED is nil, +run FORMS unconditionally." (declare (indent 2) (debug (form form body))) (macroexp-let2 nil pred pred `(erc-buffer-filter (lambda () diff --git a/lisp/erc/erc-pcomplete.el b/lisp/erc/erc-pcomplete.el index 52ebdc83e5e..05cbaf3872f 100644 --- a/lisp/erc/erc-pcomplete.el +++ b/lisp/erc/erc-pcomplete.el @@ -58,7 +58,9 @@ add this string to nicks completed." ;;;###autoload(put 'Completion 'erc--module 'completion) ;;;###autoload(put 'pcomplete 'erc--module 'completion) +;;;###autoload(put 'completion 'erc--feature 'erc-pcomplete) ;;;###autoload(autoload 'erc-completion-mode "erc-pcomplete" nil t) +(put 'completion 'erc-group 'erc-pcomplete) (define-erc-module pcomplete Completion "In ERC Completion mode, the TAB key does completion whenever possible." ((add-hook 'erc-mode-hook #'pcomplete-erc-setup) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index a71cc806f6a..2318fed28f2 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -3186,6 +3186,7 @@ (should (eq (erc--find-group 'autojoin) 'erc-autojoin)) (should (eq (erc--find-group 'pcomplete 'Completion) 'erc-pcomplete)) (should (eq (erc--find-group 'capab-identify) 'erc-capab)) + (should (eq (erc--find-group 'completion) 'erc-pcomplete)) ;; No group specified. (should (eq (erc--find-group 'smiley nil) 'erc)) (should (eq (erc--find-group 'unmorse nil) 'erc))) commit 37e87bc3eeb8e62e2900d73cf4dd9fc9e942d66d Author: F. Jason Park Date: Wed Jan 3 23:10:55 2024 -0800 Make ERC's format catalogs more extensible * lisp/erc/erc-common.el (erc--define-catalog): Accept a `:parent' keyword to allow for extending an existing catalog by overriding some subset of defined entries. (erc-define-message-format-catalog): Add edebug spec. * lisp/erc/erc.el (erc-retrieve-catalog-entry): Check parent for definition before looking to `default-toplevel-value'. * test/lisp/erc/erc-tests.el (erc-retrieve-catalog-entry): Add test case for inheritance. * test/lisp/erc/resources/erc-tests-common.el (erc-tests-common-pp-propertized-parts): Fix bug in convenience command. (Bug#67677) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index b8ba0673355..2581e40f850 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -554,9 +554,21 @@ See `erc-define-message-format-catalog' for the meaning of ENTRIES, an alist, and `erc-tests-common-pp-propertized-parts' in tests/lisp/erc/erc-tests.el for a convenience command to convert a literal string into a sequence of `propertize' forms, which are -much easier to review and edit." +much easier to review and edit. When ENTRIES begins with a +sequence of keyword-value pairs remove them and consider their +evaluated values before processing the alist proper. + +Currently, the only recognized keyword is `:parent', which tells +ERC to search recursively for a given template key using the +keyword's associated value, another catalog symbol, if not found +in catalog NAME." (declare (indent 1)) (let (out) + (while (keywordp (car entries)) + (push (pcase-exhaustive (pop entries) + (:parent `(put ',name 'erc--base-format-catalog + ,(pop entries)))) + out)) (dolist (e entries (cons 'progn (nreverse out))) (push `(defvar ,(intern (format "erc-message-%s-%s" name (car e))) ,(cdr e) @@ -575,7 +587,8 @@ symbol, and FORMAT evaluates to a format string compatible with `format-spec'. Expect modules that only define a handful of entries to do so manually, instead of using this macro, so that the resulting variables will end up with more useful doc strings." - (declare (indent 1)) + (declare (indent 1) + (debug (symbolp [&rest [keywordp form]] &rest (symbolp . form)))) `(erc--define-catalog ,language ,entries)) (defmacro erc--doarray (spec &rest body) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index d0c43134f9d..478683a77f5 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -9320,6 +9320,12 @@ if yet untried." (unless catalog (setq catalog erc-current-message-catalog)) (symbol-value (or (erc--make-message-variable-name catalog key 'softp) + (let ((parent catalog) + last) + (while (and (setq parent (get parent 'erc--base-format-catalog)) + (not (setq last (erc--make-message-variable-name + parent key 'softp))))) + last) (let ((default (default-toplevel-value 'erc-current-message-catalog))) (or (and (not (eq default catalog)) (erc--make-message-variable-name default key 'softp)) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index a9aa255718d..a71cc806f6a 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -3533,6 +3533,20 @@ connection." (should (equal (erc-retrieve-catalog-entry 's221) "test-top 221 val")) (makunbound (intern "erc-message-test-top-s221")) - (unintern "erc-message-test-top-s221" obarray)) + (unintern "erc-message-test-top-s221" obarray) + + ;; Inheritance. + (let ((obarray (obarray-make))) + (set (intern "erc-message-test1-abc") "val test1 abc") + (set (intern "erc-message-test2-abc") "val test2 abc") + (set (intern "erc-message-test2-def") "val test2 def") + (put (intern "test0") 'erc--base-format-catalog (intern "test1")) + (put (intern "test1") 'erc--base-format-catalog (intern "test2")) + (should (equal (erc-retrieve-catalog-entry 'abc (intern "test0")) + "val test1 abc")) + (should (equal (erc-retrieve-catalog-entry 'def (intern "test0")) + "val test2 def")) + ;; Terminates. + (should-not (erc-retrieve-catalog-entry 'ghi (intern "test0"))))) ;;; erc-tests.el ends here diff --git a/test/lisp/erc/resources/erc-tests-common.el b/test/lisp/erc/resources/erc-tests-common.el index fc5649798b5..906aa891352 100644 --- a/test/lisp/erc/resources/erc-tests-common.el +++ b/test/lisp/erc/resources/erc-tests-common.el @@ -150,7 +150,7 @@ between literal strings." For simplicity, assume string evaluates to itself." (interactive "P") (let ((sexp (erc-tests-common-string-to-propertized-parts (pp-last-sexp)))) - (if arg (insert (pp-to-string sexp)) (pp-eval-expression sexp)))) + (if arg (insert (pp-to-string sexp)) (pp-macroexpand-expression sexp)))) ;; The following utilities are meant to help prepare tests for ;; `erc--get-inserted-msg-bounds' and friends. commit d6f9379d1c708dddc0543bf7242ba1ec6aee9746 Author: F. Jason Park Date: Wed Jan 3 02:00:45 2024 -0800 Allow setting `erc-split-line-length' to zero * etc/ERC-NEWS: Mention that `erc-flood-protect' no longer affects line splitting. * lisp/erc/erc-backend.el (erc-split-line-length): Mention ways for modules to suppress line splitting entirely. (erc--split-line): Exit loop instead of asserting progress has been made. * lisp/erc/erc.el (erc--split-lines): Don't split input when option `erc-split-line-length' is zero. * test/lisp/erc/erc-tests.el (erc--split-line): Assert behavior when `erc-split-line-length' is 0. (Bug#62947) diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index c51b6f05458..6cfa704d995 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -560,6 +560,11 @@ third-party code, the key takeaway is that more 'font-lock-face' properties encountered in the wild may be combinations of faces rather than lone ones. +*** 'erc-flood-protect' no longer influences input splitting. +This variable's role has been narrowed to rate limiting only. ERC +used to suppress protocol line-splitting when its value was nil, but +that's now handled by setting 'erc-split-line-length' to zero. + *** 'erc-pre-send-functions' visits prompt input post-split. ERC now adjusts input lines to fall within allowed length limits before showing hook members the result. For compatibility, diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 4162df00595..95207e56fd1 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -433,7 +433,11 @@ and optionally alter the attempts tally." (defcustom erc-split-line-length 440 "The maximum length of a single message. -If a message exceeds this size, it is broken into multiple ones. +ERC normally splits chat input submitted at its prompt into +multiple messages when the initial size exceeds this value in +bytes. Modules can tell ERC to forgo splitting entirely by +setting this to zero locally or, preferably, by binding it around +a remapped `erc-send-current-line' command. IRC allows for lines up to 512 bytes. Two of them are CR LF. And a typical message looks like this: @@ -596,7 +600,8 @@ escape hatch for inhibiting their transmission.") (if (= (car cmp) (point-min)) (goto-char (nth 1 cmp)) (goto-char (car cmp))))) - (cl-assert (/= (point-min) (point))) + (when (= (point-min) (point)) + (goto-char (point-max))) (push (buffer-substring-no-properties (point-min) (point)) out) (delete-region (point-min) (point))) (or (nreverse out) (list ""))) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index b73e80cedde..d0c43134f9d 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7821,7 +7821,7 @@ When all lines are empty, remove all but the first." "Partition non-command input into lines of protocol-compliant length." ;; Prior to ERC 5.6, line splitting used to be predicated on ;; `erc-flood-protect' being non-nil. - (unless (erc--input-split-cmdp state) + (unless (or (zerop erc-split-line-length) (erc--input-split-cmdp state)) (setf (erc--input-split-lines state) (mapcan #'erc--split-line (erc--input-split-lines state))))) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 2cd47ec3f89..a9aa255718d 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1298,6 +1298,14 @@ (should-not erc-debug-irc-protocol))) (ert-deftest erc--split-line () + (let ((erc-split-line-length 0)) + (should (equal (erc--split-line "") '(""))) + (should (equal (erc--split-line " ") '(" "))) + (should (equal (erc--split-line "1") '("1"))) + (should (equal (erc--split-line " 1") '(" 1"))) + (should (equal (erc--split-line "1 ") '("1 "))) + (should (equal (erc--split-line "abc") '("abc")))) + (let ((erc-default-recipients '("#chan")) (erc-split-line-length 10)) (should (equal (erc--split-line "") '(""))) commit fad2d1e2acc12cf8b1770d821738d924105acd8a Author: F. Jason Park Date: Mon Jan 1 23:18:54 2024 -0800 Use global window hook for erc-keep-place-indicator * lisp/erc/erc-goodies.el (erc--keep-place-indicator-on-window-buffer-change): Expect a frame instead of a window argument for the only parameter, which is now ignored, and exit early when entering a minibuffer. (erc--keep-place-indicator-setup): Remove function because local modules don't need a separate setup function. (erc-keep-place-indicator-mode): Add autoload cookie even though this is a local module, since this particular one is intended for more granular, interactive activation. This is mostly a formality because it only matters in the unlikely event `erc-modules' is missing all other modules defined in `erc-goodies'. (erc-keep-place-indicator-mode, erc-keep-place-indicator-enable, erc-keep-place-indicator-disable): Move functionality from `erc--keep-place-indicator-setup' into enable body. Use global instead of local members for `erc-keep-place-mode-hook' and `window-buffer-change-functions'. (erc--keep-place-indicator-on-global-module): Perform necessary action in all ERC buffers, not just the current one, where the user has ostensibly disabled `erc-keep-place-mode'. * test/lisp/erc/erc-goodies-tests.el (erc-goodies-tests--assert-kp-indicator-on, erc-goodies-tests--assert-kp-indicator-off): Change expected hook membership for dependencies from global to local. (erc-goodies-tests--keep-place-indicator): Use new helpers from the `erc-tests-common' library. (Bug#59943) diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index c5ab25bea98..23589657b2d 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -331,14 +331,15 @@ buffer than the window's start." (defvar-local erc--keep-place-indicator-overlay nil "Overlay for `erc-keep-place-indicator-mode'.") -(defun erc--keep-place-indicator-on-window-buffer-change (window) +(defun erc--keep-place-indicator-on-window-buffer-change (_) "Maybe sync `erc--keep-place-indicator-overlay'. Do so only when switching to a new buffer in the same window if the replaced buffer is no longer visible in another window and its `window-start' at the time of switching is strictly greater than the indicator's position." (when-let ((erc-keep-place-indicator-follow) - ((eq window (selected-window))) + (window (selected-window)) + ((not (eq window (active-minibuffer-window)))) (old-buffer (window-old-buffer window)) ((buffer-live-p old-buffer)) ((not (eq old-buffer (current-buffer)))) @@ -352,67 +353,70 @@ than the indicator's position." (with-current-buffer old-buffer (erc-keep-place-move old-start)))) -(defun erc--keep-place-indicator-setup () - "Initialize buffer for maintaining `erc--keep-place-indicator-overlay'." - (require 'fringe) - (erc--restore-initialize-priors erc-keep-place-indicator-mode - erc--keep-place-indicator-overlay (make-overlay 0 0)) - (add-hook 'erc-keep-place-mode-hook - #'erc--keep-place-indicator-on-global-module nil t) - (add-hook 'window-buffer-change-functions - #'erc--keep-place-indicator-on-window-buffer-change 40 t) - (when-let* (((memq erc-keep-place-indicator-style '(t arrow))) - (ov-property (if (zerop (fringe-columns 'left)) - 'after-string - 'before-string)) - (display (if (zerop (fringe-columns 'left)) - `((margin left-margin) ,overlay-arrow-string) - '(left-fringe right-triangle - erc-keep-place-indicator-arrow))) - (bef (propertize " " 'display display))) - (overlay-put erc--keep-place-indicator-overlay ov-property bef)) - (when (memq erc-keep-place-indicator-style '(t face)) - (overlay-put erc--keep-place-indicator-overlay 'face - 'erc-keep-place-indicator-line))) - ;;;###autoload(put 'keep-place-indicator 'erc--feature 'erc-goodies) +;;;###autoload(autoload 'erc-keep-place-indicator-mode "erc-goodies" nil t) (define-erc-module keep-place-indicator nil "Buffer-local `keep-place' with fringe arrow and/or highlighted face. Play nice with global module `keep-place' but don't depend on it. Expect that users may want different combinations of `keep-place' -and `keep-place-indicator' in different buffers. Unlike global -`keep-place', when `switch-to-buffer-preserve-window-point' is -enabled, don't forcibly sync point in all windows where buffer -has previously been shown because that defeats the purpose of -having a placeholder." +and `keep-place-indicator' in different buffers." ((cond (erc-keep-place-mode) ((memq 'keep-place erc-modules) (erc-keep-place-mode +1)) ;; Enable a local version of `keep-place-mode'. (t (add-hook 'erc-insert-pre-hook #'erc-keep-place 65 t))) + (require 'fringe) + (add-hook 'window-buffer-change-functions + #'erc--keep-place-indicator-on-window-buffer-change 40) + (add-hook 'erc-keep-place-mode-hook + #'erc--keep-place-indicator-on-global-module 40) (if (pcase erc-keep-place-indicator-buffer-type ('target erc--target) ('server (not erc--target)) ('t t)) - (erc--keep-place-indicator-setup) + (progn + (erc--restore-initialize-priors erc-keep-place-indicator-mode + erc--keep-place-indicator-overlay (make-overlay 0 0)) + (when-let (((memq erc-keep-place-indicator-style '(t arrow))) + (ov-property (if (zerop (fringe-columns 'left)) + 'after-string + 'before-string)) + (display (if (zerop (fringe-columns 'left)) + `((margin left-margin) ,overlay-arrow-string) + '(left-fringe right-triangle + erc-keep-place-indicator-arrow))) + (bef (propertize " " 'display display))) + (overlay-put erc--keep-place-indicator-overlay ov-property bef)) + (when (memq erc-keep-place-indicator-style '(t face)) + (overlay-put erc--keep-place-indicator-overlay 'face + 'erc-keep-place-indicator-line))) (erc-keep-place-indicator-mode -1))) ((when erc--keep-place-indicator-overlay (delete-overlay erc--keep-place-indicator-overlay)) - (remove-hook 'window-buffer-change-functions - #'erc--keep-place-indicator-on-window-buffer-change t) + (let ((buffer (current-buffer))) + ;; Remove global hooks unless others exist with mode enabled. + (unless (erc-buffer-filter (lambda () + (and (not (eq buffer (current-buffer))) + erc-keep-place-indicator-mode))) + (remove-hook 'erc-keep-place-mode-hook + #'erc--keep-place-indicator-on-global-module) + (remove-hook 'window-buffer-change-functions + #'erc--keep-place-indicator-on-window-buffer-change))) + (when (local-variable-p 'erc-insert-pre-hook) + (remove-hook 'erc-insert-pre-hook #'erc-keep-place t)) (remove-hook 'erc-keep-place-mode-hook #'erc--keep-place-indicator-on-global-module t) - (remove-hook 'erc-insert-pre-hook #'erc-keep-place t) (kill-local-variable 'erc--keep-place-indicator-overlay)) 'local) (defun erc--keep-place-indicator-on-global-module () - "Ensure `keep-place-indicator' can cope with `erc-keep-place-mode'. -That is, ensure the local module can survive a user toggling the -global one." - (if erc-keep-place-mode - (remove-hook 'erc-insert-pre-hook #'erc-keep-place t) - (add-hook 'erc-insert-pre-hook #'erc-keep-place 65 t))) + "Ensure `keep-place-indicator' survives toggling `erc-keep-place-mode'. +Do this by simulating `keep-place' in all buffers where +`keep-place-indicator' is enabled." + (erc-with-all-buffers-of-server nil (lambda () erc-keep-place-indicator-mode) + (if erc-keep-place-mode + (remove-hook 'erc-insert-pre-hook #'erc-keep-place t) + (add-hook 'erc-insert-pre-hook #'erc-keep-place 65 t)))) (defun erc-keep-place-move (pos) "Move keep-place indicator to current line or POS. diff --git a/test/lisp/erc/erc-goodies-tests.el b/test/lisp/erc/erc-goodies-tests.el index b8e00c57ef5..170e28bda96 100644 --- a/test/lisp/erc/erc-goodies-tests.el +++ b/test/lisp/erc/erc-goodies-tests.el @@ -251,15 +251,16 @@ (defun erc-goodies-tests--assert-kp-indicator-on () (should erc--keep-place-indicator-overlay) - (should (local-variable-p 'window-buffer-change-functions)) - (should window-configuration-change-hook) + (should (memq 'erc--keep-place-indicator-on-window-buffer-change + window-buffer-change-functions)) (should (memq 'erc-keep-place erc-insert-pre-hook)) (should (eq erc-keep-place-mode (not (local-variable-p 'erc-insert-pre-hook))))) (defun erc-goodies-tests--assert-kp-indicator-off () (should-not (local-variable-p 'erc-insert-pre-hook)) - (should-not (local-variable-p 'window-buffer-change-functions)) + (should-not (memq 'erc--keep-place-indicator-on-window-buffer-change + window-buffer-change-functions)) (should-not erc--keep-place-indicator-overlay)) (defun erc-goodies-tests--kp-indicator-populate () @@ -272,12 +273,9 @@ (goto-char erc-input-marker)) (defun erc-goodies-tests--keep-place-indicator (test) - (with-current-buffer (get-buffer-create "*erc-keep-place-indicator-mode*") - (erc-mode) - (erc--initialize-markers (point) nil) - (setq erc-server-process - (start-process "sleep" (current-buffer) "sleep" "1")) - (set-process-query-on-exit-flag erc-server-process nil) + (erc-keep-place-mode -1) + (with-current-buffer (erc-tests-common-make-server-buf + "*erc-keep-place-indicator-mode*") (let (erc-connect-pre-hook erc-modules) @@ -294,7 +292,7 @@ (should-not (member 'erc-keep-place (default-value 'erc-insert-pre-hook))) (should-not (local-variable-p 'erc-insert-pre-hook)) - (kill-buffer)))) + (erc-tests-common-kill-buffers)))) (ert-deftest erc-keep-place-indicator-mode--no-global () (erc-goodies-tests--keep-place-indicator commit 74f022b2797567ab04405af37b877d94cc4fdca2 Author: F. Jason Park Date: Mon Jan 1 00:34:53 2024 -0800 ; Make erc--send-input-lines a normal function again * lisp/erc/erc.el (erc--send-input-lines): Revert portion of 174b3dd9bd78c662ce9fff78404dcfa02259d21b "Make nested input handling more robust in ERC" that converted this from a function to a method. Instead, defer change until it's needed, likely for bug#49860. Also, don't inadvertently allow overloading of `insertp' because user code can legitimately set that to a function, which we then blindly call. Instead, hard-code it to the only expected alternate display function. diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index e639a6278fc..b73e80cedde 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -7878,12 +7878,13 @@ queue. Expect LINES-OBJ to be an `erc--input-split' object." (user-error "Multiline command detected" )) lines-obj) -(cl-defmethod erc--send-input-lines (lines-obj) +(defun erc--send-input-lines (lines-obj) "Send lines in `erc--input-split-lines' object LINES-OBJ." (when (erc--input-split-sendp lines-obj) (dolist (line (erc--input-split-lines lines-obj)) (when (erc--input-split-insertp lines-obj) - (if (functionp (erc--input-split-insertp lines-obj)) + (if (eq (erc--input-split-insertp lines-obj) + 'erc--command-indicator-display) (funcall (erc--input-split-insertp lines-obj) line) (erc-display-msg line))) (erc-process-input-line (concat line "\n") commit 94f760163e221587fbba08a31e81c19527f037fe Author: F. Jason Park Date: Sat Jan 6 13:32:42 2024 -0800 ; doc/misc/erc.texi: Improve SASL intro. diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 7fbe6f9766e..52c7477c9dd 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi @@ -1048,13 +1048,19 @@ acceptable. @section Authenticating via SASL @cindex SASL -Regardless of the mechanism or the network, you'll likely have to be -registered before first use. Please refer to the network's own +If you've used @acronym{SASL} elsewhere, you can probably skip to the +examples below. Otherwise, if you haven't already registered with +your network, please do so now, referring to the network's own instructions for details. If you're new to IRC and using a bouncer, -know that you probably won't be needing SASL for the client-to-bouncer -connection. To get started, just add @code{sasl} to -@code{erc-modules} like any other module. But before that, please -explore all custom options pertaining to your chosen mechanism. +know that you probably won't be needing this for the client-to-bouncer +connection. + +When you're ready to get started, add @code{sasl} to +@code{erc-modules}, like you would any other module. If unsure which +@dfn{mechanism} to choose, stick with the default of @samp{PLAIN}. +Then try @kbd{C-u M-x erc-tls @key{RET}}, and give your account name +for the @samp{user} parameter and your account password for the +@samp{server password}. @defopt erc-sasl-mechanism The name of an SASL subprotocol type as a @emph{lowercase} symbol. commit 18de131222ee24c4088ac45be1babad26284af5b Author: Juri Linkov Date: Sun Jan 7 20:04:06 2024 +0200 Support more metadata properties in completion-category-overrides (bug#68214) * doc/lispref/minibuf.texi (Completion Variables): Add to the table of completion-category-overrides new items: `cycle-sort-function', `group-function', `annotation-function', `affixation-function'. * lisp/minibuffer.el (completion-metadata-get): Try also to get the property from completion-category-overrides by category. Suggested by Daniel Mendler . (completion-category-defaults): Add new properties to docstring. (completion-category-overrides): Add customization for new properties: `cycle-sort-function', `group-function', `annotation-function', `affixation-function'. (completion-metadata-override-get): Remove function. (minibuffer-completion-help): Replace 'completion-metadata-override-get' with 'completion-metadata-get' for 'display-sort-function'. diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index 8aed1515764..8d25a53161e 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -1879,17 +1879,30 @@ The value should be a value for @code{completion-cycle-threshold} (@pxref{Completion Options,,, emacs, The GNU Emacs Manual}) for this category. +@item cycle-sort-function +The function to sort entries when cycling. + @item display-sort-function +The function to sort entries in the @file{*Completions*} buffer. The possible values are: @code{nil}, which means to use either the sorting function from metadata or if that is @code{nil}, fall back to @code{completions-sort}; @code{identity}, which means not to sort at all, leaving the original order; or any other value out of those used in @code{completions-sort} (@pxref{Completion Options,,, emacs, The GNU Emacs Manual}). + +@item group-function +The function to group completions. + +@item annotation-function +The function to add annotations to completions. + +@item affixation-function +The function to add prefixes and suffixes to completions. @end table @noindent -Additional alist entries may be defined in the future. +See @ref{Programmed Completion}, for a complete list of metadata entries. @end defopt @defvar completion-extra-properties diff --git a/etc/NEWS b/etc/NEWS index 3a1168f62b3..c3d777b971f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -745,10 +745,12 @@ will be first sorted alphabetically, and then re-sorted by their order in the minibuffer history, with more recent candidates appearing first. +++ -*** 'completion-category-overrides' supports 'display-sort-function'. -You can now customize the sorting order for any category in -'completion-category-overrides' that will override the sorting order -defined in the metadata or in 'completions-sort'. +*** 'completion-category-overrides' supports more metadata. +The new supported completion properties are 'cycle-sort-function', +'display-sort-function', 'annotation-function', 'affixation-function', +'group-function'. You can now customize them for any category in +'completion-category-overrides' that will override the properties +defined in completion metadata. ** Pcomplete diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index b7aebae63a8..04b36f03d11 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -151,7 +151,15 @@ The metadata of a completion table should be constant between two boundaries." minibuffer-completion-predicate)) (defun completion-metadata-get (metadata prop) - (cdr (assq prop metadata))) + "Get PROP from completion METADATA. +If the metadata specifies a completion category, the variables +`completion-category-overrides' and +`completion-category-defaults' take precedence." + (if-let (((not (eq prop 'category))) + (cat (alist-get 'category metadata)) + (over (completion--category-override cat prop))) + (cdr over) + (alist-get prop metadata))) (defun complete-with-action (action collection string predicate) "Perform completion according to ACTION. @@ -1138,27 +1146,38 @@ styles for specific categories, such as files, buffers, etc." (symbol-help (styles . (basic shorthand substring))) (calendar-month (display-sort-function . identity))) "Default settings for specific completion categories. + Each entry has the shape (CATEGORY . ALIST) where ALIST is an association list that can specify properties such as: - `styles': the list of `completion-styles' to use for that category. - `cycle': the `completion-cycle-threshold' to use for that category. -- `display-sort-function': the sorting function. +- `cycle-sort-function': function to sort entries when cycling. +- `display-sort-function': function to sort entries in *Completions*. +- `group-function': function for grouping the completion candidates. +- `annotation-function': function to add annotations in *Completions*. +- `affixation-function': function to prepend/append a prefix/suffix. + Categories are symbols such as `buffer' and `file', used when completing buffer and file names, respectively. Also see `completion-category-overrides'.") (defcustom completion-category-overrides nil - "List of category-specific user overrides for completion styles. + "List of category-specific user overrides for completion metadata. Each override has the shape (CATEGORY . ALIST) where ALIST is an association list that can specify properties such as: - `styles': the list of `completion-styles' to use for that category. - `cycle': the `completion-cycle-threshold' to use for that category. +- `cycle-sort-function': function to sort entries when cycling. - `display-sort-function': nil means to use either the sorting function from metadata, or if that is nil, fall back to `completions-sort'; `identity' disables sorting and keeps the original order; and other possible values are the same as in `completions-sort'. +- `group-function': function for grouping the completion candidates. +- `annotation-function': function to add annotations in *Completions*. +- `affixation-function': function to prepend/append a prefix/suffix. +See more description of metadata in `completion-metadata'. Categories are symbols such as `buffer' and `file', used when completing buffer and file names, respectively. @@ -1180,6 +1199,10 @@ overrides the default specified in `completion-category-defaults'." (cons :tag "Completion Cycling" (const :tag "Select one value from the menu." cycle) ,completion--cycling-threshold-type) + (cons :tag "Cycle Sorting" + (const :tag "Select one value from the menu." + cycle-sort-function) + (choice (function :tag "Custom function"))) (cons :tag "Completion Sorting" (const :tag "Select one value from the menu." display-sort-function) @@ -1189,18 +1212,24 @@ overrides the default specified in `completion-category-defaults'." minibuffer-sort-alphabetically) (const :tag "Historical sorting" minibuffer-sort-by-history) - (function :tag "Custom function")))))) + (function :tag "Custom function"))) + (cons :tag "Completion Groups" + (const :tag "Select one value from the menu." + group-function) + (choice (function :tag "Custom function"))) + (cons :tag "Completion Annotation" + (const :tag "Select one value from the menu." + annotation-function) + (choice (function :tag "Custom function"))) + (cons :tag "Completion Affixation" + (const :tag "Select one value from the menu." + affixation-function) + (choice (function :tag "Custom function")))))) (defun completion--category-override (category tag) (or (assq tag (cdr (assq category completion-category-overrides))) (assq tag (cdr (assq category completion-category-defaults))))) -(defun completion-metadata-override-get (metadata prop) - (if-let ((cat (completion-metadata-get metadata 'category)) - (over (completion--category-override cat prop))) - (cdr over) - (completion-metadata-get metadata prop))) - (defun completion--styles (metadata) (let* ((cat (completion-metadata-get metadata 'category)) (over (completion--category-override cat 'styles))) @@ -2546,7 +2575,7 @@ The candidate will still be chosen by `choose-completion' unless (aff-fun (or (completion-metadata-get all-md 'affixation-function) (plist-get completion-extra-properties :affixation-function))) - (sort-fun (completion-metadata-override-get all-md 'display-sort-function)) + (sort-fun (completion-metadata-get all-md 'display-sort-function)) (group-fun (completion-metadata-get all-md 'group-function)) (mainbuf (current-buffer)) ;; If the *Completions* buffer is shown in a new commit aadcb906095e8588ed6302920bf835df20ab320f Author: Michael Albinus Date: Sun Jan 7 12:39:47 2024 +0100 Handle local default directory in connection-local-value * lisp/files-x.el (connection-local-p, connection-local-value): Handle local `default-directory'. * test/lisp/files-x-tests.el (files-x-test-connection-local-value): Extend test. diff --git a/lisp/files-x.el b/lisp/files-x.el index fccb2fa4a9f..f70be5f7ff3 100644 --- a/lisp/files-x.el +++ b/lisp/files-x.el @@ -929,19 +929,23 @@ earlier in the `setq-connection-local'. The return value of the ;;;###autoload (defmacro connection-local-p (variable &optional application) "Non-nil if VARIABLE has a connection-local binding in `default-directory'. +`default-directory' must be a remote file name. If APPLICATION is nil, the value of `connection-local-default-application' is used." (declare (debug (symbolp &optional form))) (unless (symbolp variable) (signal 'wrong-type-argument (list 'symbolp variable))) - `(let (connection-local-variables-alist file-local-variables-alist) - (hack-connection-local-variables - (connection-local-criteria-for-default-directory ,application)) - (and (assq ',variable connection-local-variables-alist) t))) + `(let ((criteria + (connection-local-criteria-for-default-directory ,application)) + connection-local-variables-alist file-local-variables-alist) + (when criteria + (hack-connection-local-variables criteria) + (and (assq ',variable connection-local-variables-alist) t)))) ;;;###autoload (defmacro connection-local-value (variable &optional application) "Return connection-local VARIABLE for APPLICATION in `default-directory'. +`default-directory' must be a remote file name. If APPLICATION is nil, the value of `connection-local-default-application' is used. If VARIABLE does not have a connection-local binding, the return @@ -949,12 +953,15 @@ value is the default binding of the variable." (declare (debug (symbolp &optional form))) (unless (symbolp variable) (signal 'wrong-type-argument (list 'symbolp variable))) - `(let (connection-local-variables-alist file-local-variables-alist) - (hack-connection-local-variables - (connection-local-criteria-for-default-directory ,application)) - (if-let ((result (assq ',variable connection-local-variables-alist))) - (cdr result) - ,variable))) + `(let ((criteria + (connection-local-criteria-for-default-directory ,application)) + connection-local-variables-alist file-local-variables-alist) + (if (not criteria) + ,variable + (hack-connection-local-variables criteria) + (if-let ((result (assq ',variable connection-local-variables-alist))) + (cdr result) + ,variable)))) ;;;###autoload (defun path-separator () diff --git a/test/lisp/files-x-tests.el b/test/lisp/files-x-tests.el index a2f16d5ae35..528467a5641 100644 --- a/test/lisp/files-x-tests.el +++ b/test/lisp/files-x-tests.el @@ -553,6 +553,49 @@ If it's not initialized yet, initialize it." (should-not (boundp 'remote-shell-file-name)) (should (string-equal (symbol-value 'remote-null-device) "null")))) + ;; `connection-local-value' and `connection-local-p' care about a + ;; local default directory. + (with-temp-buffer + (let ((enable-connection-local-variables t) + (default-directory temporary-file-directory) + (remote-null-device "null")) + (should-not connection-local-variables-alist) + (should-not (local-variable-p 'remote-shell-file-name)) + (should-not (local-variable-p 'remote-null-device)) + (should-not (boundp 'remote-shell-file-name)) + (should (string-equal (symbol-value 'remote-null-device) "null")) + + ;; The recent variable values are used. + (should-not (connection-local-p remote-shell-file-name)) + ;; `remote-shell-file-name' is not defined, so we get an error. + (should-error + (connection-local-value remote-shell-file-name) :type 'void-variable) + (should-not (connection-local-p remote-null-device)) + (should + (string-equal + (connection-local-value remote-null-device) remote-null-device)) + (should-not (connection-local-p remote-lazy-var)) + + ;; Run with a different application. + (should-not + (connection-local-p + remote-shell-file-name (cadr files-x-test--application))) + ;; `remote-shell-file-name' is not defined, so we get an error. + (should-error + (connection-local-value + remote-shell-file-name (cadr files-x-test--application)) + :type 'void-variable) + (should-not + (connection-local-p + remote-null-device (cadr files-x-test--application))) + (should + (string-equal + (connection-local-value + remote-null-device (cadr files-x-test--application)) + remote-null-device)) + (should-not + (connection-local-p remote-lazy-var (cadr files-x-test--application))))) + ;; Cleanup. (custom-set-variables `(connection-local-profile-alist ',clpa now) commit f866c85ac4e32df8061b285b6b44b15346994f3d Author: Stefan Monnier Date: Sun Jan 7 00:02:08 2024 -0500 (jsonrpc--log-event): Try and fix bug#68072 * lisp/jsonrpc.el (jsonrpc--log-event): Force the use of `lisp-indent-function` in `pp-to-string`. diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el index 3f33443f321..f0f5842a0ee 100644 --- a/lisp/jsonrpc.el +++ b/lisp/jsonrpc.el @@ -1003,16 +1003,17 @@ of the API instead.") (or method "") (if id (format "[%s]" id) ""))))) (msg - (cond ((eq format 'full) - (format "%s%s\n" preamble (or json log-text))) - ((eq format 'short) - (format "%s%s\n" preamble (or log-text ""))) - (t - (format "%s%s" preamble - (or (and foreign-message - (concat "\n" (pp-to-string - foreign-message))) - (concat log-text "\n"))))))) + (pcase format + ('full (format "%s%s\n" preamble (or json log-text))) + ('short (format "%s%s\n" preamble (or log-text ""))) + (_ + (format "%s%s" preamble + (or (and foreign-message + (let ((lisp-indent-function ;bug#68072 + #'lisp-indent-function)) + (concat "\n" (pp-to-string + foreign-message)))) + (concat log-text "\n"))))))) (goto-char (point-max)) ;; XXX: could use `run-at-time' to delay server logs ;; slightly to play nice with verbose servers' stderr. commit b5de9ae8010684a5ed0c6f2703077a61d325ccad Author: João Távora Date: Sat Jan 6 17:56:33 2024 -0600 Eglot: careful when invoking code actions on no symbol at all Invoking code actions without a marked region or over a symbol will trip certain servers up since BEG and END in eglot-code-actions will be nil, causing 'eglot--pos-to-lsp-position' to assume point (which is OK) but the 'flymake-diagnostics' call to return all diagnostics. This causes an absolutely undecipherable JavaScript backtrace to be sent back to Eglot from typescript-language-server. Github-reference: https://github.com/joaotavora/eglot/issues/847 * lisp/progmodes/eglot.el (eglot--code-action-bounds): Avoid returning (list nil nil) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index d330e6e23cb..ba2cc72a6b4 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3605,16 +3605,17 @@ edit proposed by the server." (defun eglot--code-action-bounds () "Calculate appropriate bounds depending on region and point." - (let (diags) + (let (diags boftap) (cond ((use-region-p) `(,(region-beginning) ,(region-end))) ((setq diags (flymake-diagnostics (point))) (cl-loop for d in diags minimizing (flymake-diagnostic-beg d) into beg maximizing (flymake-diagnostic-end d) into end finally (cl-return (list beg end)))) + ((setq boftap (bounds-of-thing-at-point 'sexp)) + (list (car boftap) (cdr boftap))) (t - (let ((boftap (bounds-of-thing-at-point 'sexp))) - (list (car boftap) (cdr boftap))))))) + (list (point) (point)))))) (defun eglot-code-actions (beg &optional end action-kind interactive) "Find LSP code actions of type ACTION-KIND between BEG and END. commit 73cb931e5bab1b956f0569cd542468cfa7f4c9a7 Author: Stefan Monnier Date: Sat Jan 6 18:50:25 2024 -0500 (describe-package-1): Fix bug#68288 Fix support for multiple maintainers in `describe-package` and in `package-report-bug`. * lisp/emacs-lisp/package.el (describe-package-1): There's no `:maintainers:`, instead `:maintainer` can hold a list of maintainers. (package-maintainers): Adapt to the possibility of having multiple maintainers. (package-report-bug): Don't burp if the package is not installed. diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 8df2088ce43..b21e0f8fc51 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -2804,8 +2804,7 @@ Helper function for `describe-package'." (status (if desc (package-desc-status desc) "orphan")) (incompatible-reason (package--incompatible-p desc)) (signed (if desc (package-desc-signed desc))) - (maintainers (or (cdr (assoc :maintainers extras)) - (list (cdr (assoc :maintainer extras))))) + (maintainers (cdr (assoc :maintainer extras))) (authors (cdr (assoc :authors extras))) (news (and-let* (pkg-dir ((not built-in)) @@ -4699,18 +4698,23 @@ will be signaled in that case." (let* ((name (package-desc-name pkg-desc)) (extras (package-desc-extras pkg-desc)) (maint (alist-get :maintainer extras))) + (unless (listp (cdr maint)) + (setq maint (list maint))) (cond ((and (null maint) (null no-error)) (user-error "Package `%s' has no explicit maintainer" name)) ((and (not (progn (require 'ietf-drums) - (ietf-drums-parse-address (cdr maint)))) + (ietf-drums-parse-address (cdar maint)))) (null no-error)) (user-error "Package `%s' has no maintainer address" name)) - ((not (null maint)) + (t (with-temp-buffer - (package--print-email-button maint) - (string-trim (substring-no-properties (buffer-string)))))))) + (mapc #'package--print-email-button maint) + (replace-regexp-in-string + "\n" ", " (string-trim + (buffer-substring-no-properties + (point-min) (point-max))))))))) ;;;###autoload (defun package-report-bug (desc) @@ -4720,17 +4724,19 @@ DESC must be a `package-desc' object." package-menu-mode) (let ((maint (package-maintainers desc)) (name (symbol-name (package-desc-name desc))) + (pkgdir (package-desc-dir desc)) vars) - (dolist-with-progress-reporter (group custom-current-group-alist) - "Scanning for modified user options..." - (when (and (car group) - (file-in-directory-p (car group) (package-desc-dir desc))) - (dolist (ent (get (cdr group) 'custom-group)) - (when (and (custom-variable-p (car ent)) - (boundp (car ent)) - (not (eq (custom--standard-value (car ent)) - (default-toplevel-value (car ent))))) - (push (car ent) vars))))) + (when pkgdir + (dolist-with-progress-reporter (group custom-current-group-alist) + "Scanning for modified user options..." + (when (and (car group) + (file-in-directory-p (car group) pkgdir)) + (dolist (ent (get (cdr group) 'custom-group)) + (when (and (custom-variable-p (car ent)) + (boundp (car ent)) + (not (eq (custom--standard-value (car ent)) + (default-toplevel-value (car ent))))) + (push (car ent) vars)))))) (dlet ((reporter-prompt-for-summary-p t)) (reporter-submit-bug-report maint name vars)))) commit 4411d98c47576d5d47ea17269617b7c5a0f04f3c Author: Paul Eggert Date: Sat Jan 6 13:39:57 2024 -0800 Pacify Ubuntu GCC 13.2 in x_get_local_selection * src/xselect.c: Ignore -Wanalyzer-null-dereference, to work around GCC bug 102671. diff --git a/src/xselect.c b/src/xselect.c index bb82798bb62..fd0f06eeed9 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -19,6 +19,12 @@ along with GNU Emacs. If not, see . */ /* Rewritten by jwz */ #include + +/* Work around GCC bug 102671. */ +#if 10 <= __GNUC__ +# pragma GCC diagnostic ignored "-Wanalyzer-null-dereference" +#endif + #include #ifdef HAVE_SYS_TYPES_H commit 0b312e310db2b06113f2b09d90951f82e8edf02f Author: Paul Eggert Date: Sat Jan 6 13:38:13 2024 -0800 Pacify Ubuntu GCC 13.2 in set_marker_internal * src/marker.c (set_marker_internal): Ignore -Wanalyzer-deref-before-check, to work around GCC bug 113253. diff --git a/src/marker.c b/src/marker.c index 377f6fbe8db..0101e144b4d 100644 --- a/src/marker.c +++ b/src/marker.c @@ -20,6 +20,11 @@ along with GNU Emacs. If not, see . */ #include +/* Work around GCC bug 113253. */ +#if 13 <= __GNUC__ +# pragma GCC diagnostic ignored "-Wanalyzer-deref-before-check" +#endif + #include "lisp.h" #include "character.h" #include "buffer.h" commit 166b10e9f80dc78147601a87b6425f59860bcfe4 Author: Michael Albinus Date: Sat Jan 6 18:15:23 2024 +0100 Complete change of ert-remote-temporary-file-directory * lisp/emacs-lisp/ert-x.el: Adapt comment. * test/lisp/net/tramp-tests.el (ert-remote-temporary-file-directory): Make it a defvar. diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index a6d2fe4a1da..cd60f9f457f 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el @@ -540,7 +540,7 @@ The same keyword arguments are supported as in (when (and (featurep 'tramp) (getenv "EMACS_HYDRA_CI")) (add-to-list 'tramp-remote-path 'tramp-own-remote-path)) -;; If this defconst is used in a test file, `tramp' shall be loaded +;; If this defvar is used in a test file, `tramp' shall be loaded ;; prior `ert-x'. There is no default value on w32 systems, which ;; could work out of the box. (defvar ert-remote-temporary-file-directory diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 3216a8be1b0..91b0542c759 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -134,7 +134,7 @@ A resource file is in the resource directory as per (eval-and-compile ;; There is no default value on w32 systems, which could work out ;; of the box. - (defconst ert-remote-temporary-file-directory + (defvar ert-remote-temporary-file-directory (cond ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY")) ((eq system-type 'windows-nt) null-device) commit 3071f6981d5b93b77abbd5cf4a36e15b0b410f3d Author: Michael Albinus Date: Sat Jan 6 18:14:15 2024 +0100 Minor change in tramp.texi * doc/misc/tramp.texi (Obtaining @value{tramp}): Mention the ELPA Tramp manual. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 7e938d0f97f..56945d3071c 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -321,7 +321,7 @@ behind the scenes when you open a file with @value{tramp}. @cindex GNU ELPA @vindex tramp-version -@value{tramp} is included as part of Emacs (since @w{Emacs 22.1}). +@value{tramp} is included as part of Emacs. @value{tramp} is also freely packaged for download on the Internet at @uref{https://ftp.gnu.org/gnu/tramp/}. The version number of @@ -343,10 +343,12 @@ versions packaged with Emacs can be retrieved by @end lisp @value{tramp} is also available as @uref{https://elpa.gnu.org, GNU -ELPA} package. Besides the standalone releases, further minor versions -of @value{tramp} will appear on GNU ELPA, until the next @value{tramp} -release appears. These minor versions have a four-number string, like -``2.4.5.1''. +ELPA} package. Besides the standalone releases, further minor +versions of @value{tramp} will appear on GNU ELPA, until the next +@value{tramp} release appears. These minor versions have a +four-number string, like ``2.4.5.1''. The manual of the latest +@value{tramp} ELPA package is located at +@uref{https://elpa.gnu.org/packages/doc/tramp.html}. @value{tramp} development versions are available on Git servers. Development versions contain new and incomplete features. The commit e48a396d4ba1694e083f900dda1f41cc41d00ead Author: Michael Albinus Date: Sat Jan 6 18:12:47 2024 +0100 Adapt Tramp version * doc/misc/trampver.texi: * lisp/net/trampver.el (tramp-version): Adapt Tramp versions. diff --git a/doc/misc/trampver.texi b/doc/misc/trampver.texi index a239c091889..8cb0e3d574a 100644 --- a/doc/misc/trampver.texi +++ b/doc/misc/trampver.texi @@ -7,7 +7,7 @@ @c In the Tramp GIT, the version number and the bug report address @c are auto-frobbed from configure.ac. -@set trampver 2.7.0-pre +@set trampver 2.7.0 @set trampurl https://www.gnu.org/software/tramp/ @set tramp-bug-report-address tramp-devel@@gnu.org @set emacsver 27.1 diff --git a/lisp/net/trampver.el b/lisp/net/trampver.el index bfabbbeaf34..4b8868561d4 100644 --- a/lisp/net/trampver.el +++ b/lisp/net/trampver.el @@ -7,7 +7,7 @@ ;; Maintainer: Michael Albinus ;; Keywords: comm, processes ;; Package: tramp -;; Version: 2.7.0-pre +;; Version: 2.7.0 ;; Package-Requires: ((emacs "27.1")) ;; Package-Type: multi ;; URL: https://www.gnu.org/software/tramp/ @@ -40,7 +40,7 @@ ;; ./configure" to change them. ;;;###tramp-autoload -(defconst tramp-version "2.7.0-pre" +(defconst tramp-version "2.7.0" "This version of Tramp.") ;;;###tramp-autoload @@ -78,7 +78,7 @@ ;; Check for Emacs version. (let ((x (if (not (string-version-lessp emacs-version "27.1")) "ok" - (format "Tramp 2.7.0-pre is not fit for %s" + (format "Tramp 2.7.0 is not fit for %s" (replace-regexp-in-string "\n" "" (emacs-version)))))) (unless (string-equal "ok" x) (error "%s" x))) commit 16162e0645d959d824d97e3f9908e46d401e8028 Author: Steven Allen Date: Fri Dec 29 09:53:05 2023 -0800 Make 'advice-remove' interactive `ad-advice-remove' is already interactive, but it doesn't work with new-style advice. * lisp/emacs-lisp/nadvice.el (advice-remove): Make it interactive (Bug#67926). * doc/lispref/functions.texi (Advising Named Functions): Document that 'advice-remove' is now an interactive command. diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 2b2c9287d91..29e9f04a076 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -2077,10 +2077,12 @@ Add the advice @var{function} to the named function @var{symbol}. (@pxref{Core Advising Primitives}). @end defun -@defun advice-remove symbol function +@deffn Command advice-remove symbol function Remove the advice @var{function} from the named function @var{symbol}. -@var{function} can also be the @code{name} of a piece of advice. -@end defun +@var{function} can also be the @code{name} of a piece of advice. When +called interactively, prompt for both an advised @var{function} and +the advice to remove. +@end deffn @defun advice-member-p function symbol Return non-@code{nil} if the advice @var{function} is already in the named diff --git a/etc/NEWS b/etc/NEWS index 7bbfbf9512d..3a1168f62b3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -104,6 +104,10 @@ to your init: * Changes in Emacs 30.1 +** 'advice-remove' is now an interactive command. +When called interactively, 'advice-remove' now prompts for an advised +function to the advice to remove. + ** Emacs now supports Unicode Standard version 15.1. ** Network Security Manager diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el index 0d45b4b95fa..de287e43b21 100644 --- a/lisp/emacs-lisp/nadvice.el +++ b/lisp/emacs-lisp/nadvice.el @@ -539,6 +539,32 @@ Contrary to `remove-function', this also works when SYMBOL is a macro or an autoload and it preserves `fboundp'. Instead of the actual function to remove, FUNCTION can also be the `name' of the piece of advice." + (interactive + (let* ((pred (lambda (sym) (advice--p (advice--symbol-function sym)))) + (default (when-let* ((f (function-called-at-point)) + ((funcall pred f))) + (symbol-name f))) + (prompt (format-prompt "Remove advice from function" default)) + (symbol (intern (completing-read prompt obarray pred t nil nil default))) + advices) + (advice-mapc (lambda (f p) + (let ((k (or (alist-get 'name p) f))) + (push (cons + ;; "name" (string) and 'name (symbol) are + ;; considered different names so we use + ;; `prin1-to-string' even if the name is + ;; a string to distinguish between these + ;; two cases. + (prin1-to-string k) + ;; We use `k' here instead of `f' because + ;; the same advice can have multiple + ;; names. + k) + advices))) + symbol) + (list symbol (cdr (assoc-string + (completing-read "Advice to remove: " advices nil t) + advices))))) (let ((f (symbol-function symbol))) (remove-function (cond ;This is `advice--symbol-function' but as a "place". ((get symbol 'advice--pending) commit 6184e120c0e1b52d9bbf359131dd8da35654cea1 Author: Eli Zaretskii Date: Sat Jan 6 15:25:29 2024 +0200 ; * doc/lispref/buffers.texi (Buffer List): Fix merge snafu. diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi index 4994d8c2252..77f5f09c7bd 100644 --- a/doc/lispref/buffers.texi +++ b/doc/lispref/buffers.texi @@ -959,8 +959,8 @@ infinite recursion. @defun buffer-match-p condition buffer-or-name &rest args This function checks if a buffer designated by @code{buffer-or-name} -satisfies the specified @var{condition}. Optional third argument -@var{arg} is passed to the predicate function in @var{condition}. A +satisfies the specified @var{condition}. Optional arguments +@var{args} are passed to the predicate function in @var{condition}. A valid @var{condition} can be one of the following: @itemize @bullet{} @item commit 5256b8dd4ebf163aa67ed50eb72168d965583caf Merge: d9dabcacefa bf7034048c1 Author: Eli Zaretskii Date: Sat Jan 6 08:20:19 2024 -0500 Merge from origin/emacs-29 bf7034048c1 ; * doc/emacs/custom.texi (Changing a Variable): Update e... 466d1c98a9e Fix icons.el when icon does not exist as a file 2a861124e89 ; Improve documentation of 'buffer-match-p' dc9d02f8a01 * lisp/isearch.el (isearch-search-and-update): Let-bind '... 9308d9a74ab * src/comp.c (Fcomp__compile_ctxt_to_file): Fix hash tabl... a2a6619b282 Provide decent documentation for 'help-quick' ab66b749a27 ; * src/window.c (Fset_window_margins): Doc fix. 1a677d1429d treesit--pre-syntax-ppss: Fix args-out-of-range in intern... commit bf7034048c16a95263e3f7c121dafbf1824ff28f Author: Eli Zaretskii Date: Sat Jan 6 13:45:33 2024 +0200 ; * doc/emacs/custom.texi (Changing a Variable): Update example (bug#68279). diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index b45e0ef953d..4bd78f3ce83 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -188,14 +188,15 @@ find with @kbd{M-x customize-browse}. the customization buffer: @smallexample -[Hide] Kill Ring Max: 60 +[Hide] Kill Ring Max: Integer (positive or zero): 120 [State]: STANDARD. Maximum length of kill ring before oldest elements are thrown away. @end smallexample The first line shows that the variable is named @code{kill-ring-max}, formatted as @samp{Kill Ring Max} for easier -viewing. Its value is @samp{120}. The button labeled @samp{[Hide]}, +viewing, and also shows its expected type: a positive integer or zero. +The default value is @samp{120}. The button labeled @samp{[Hide]}, if activated, hides the variable's value and state; this is useful to avoid cluttering up the customization buffer with very long values (for this reason, variables that have very long values may start out commit 466d1c98a9ef7490332469165f63a38c2b07a05d Author: Eli Zaretskii Date: Sat Jan 6 13:26:29 2024 +0200 Fix icons.el when icon does not exist as a file * lisp/emacs-lisp/icons.el (icons--create): Handle the case when ICON is a file that doesn't exists or is unreadable. Suggested by David Ponce . (Bug#66846) diff --git a/lisp/emacs-lisp/icons.el b/lisp/emacs-lisp/icons.el index a35a00ec1f3..1fc0e39f9fe 100644 --- a/lisp/emacs-lisp/icons.el +++ b/lisp/emacs-lisp/icons.el @@ -187,11 +187,13 @@ present if the icon is represented by an image." merged) (cl-defmethod icons--create ((_type (eql 'image)) icon keywords) - (let ((file (if (file-name-absolute-p icon) - icon - (and (fboundp 'image-search-load-path) - (image-search-load-path icon))))) - (and (display-images-p) + (let* ((file (if (file-name-absolute-p icon) + icon + (and (fboundp 'image-search-load-path) + (image-search-load-path icon)))) + (file-exists (and (stringp file) (file-readable-p file)))) + (and file-exists + (display-images-p) (fboundp 'image-supported-file-p) (image-supported-file-p file) (propertize commit d9dabcacefad084cccaa32e4f5fffcb78728fa00 Author: Eli Zaretskii Date: Sat Jan 6 12:44:26 2024 +0200 ; Minor copyedits of doc of 'handler-bind' * doc/lispref/control.texi (Handling Errors): Fix wording and punctuation. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 3c9f26262c1..0c6895332a0 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -2325,16 +2325,17 @@ error description. Contrary to what happens with @code{condition-case}, @var{handler} is called in the dynamic context where the error happened. This means it -is executed unbinding any variable bindings or running any cleanups of -@code{unwind-protect}, so that all those dynamic bindings are still in -effect. There is one exception: while running the @var{handler} -function, all the error handlers between the code that signaled the -error and the @code{handler-bind} are temporarily suspended, meaning -that when an error is signaled, Emacs will only search the active -@code{condition-case} and @code{handler-bind} forms that are inside -the @var{handler} function or outside of the current -@code{handler-bind}. Note also that lexical variables are not -affected, since they do not have dynamic extent. +is executed without unbinding any variable bindings or running any +cleanups of @code{unwind-protect}, so that all those dynamic bindings +are still in effect. There is one exception: while running the +@var{handler} function, all the error handlers between the code that +signaled the error and the @code{handler-bind} are temporarily +suspended, meaning that when an error is signaled, Emacs will only +search the active @code{condition-case} and @code{handler-bind} forms +that are inside the @var{handler} function or outside of the current +@code{handler-bind}. Note also that lexically-bound variables +(@pxref{Lexical Binding}) are not affected, since they do not have +dynamic extent. Like any normal function, @var{handler} can exit non-locally, typically via @code{throw}, or it can return normally. @@ -2391,10 +2392,10 @@ We can get almost the same result with @code{condition-case}: @noindent but with the difference that when we (re)signal the new error in -@code{handler-bind} the dynamic environment from the original error is -still active, which means for example that if we enter the -debugger at this point, it will show us a complete backtrace including -the point where we signaled the original error: +@code{handler-bind}, the dynamic environment from the original error +is still active, which means for example that if we enter the debugger +at this point, it will show us a complete backtrace including the +point where we signaled the original error: @example @group commit 409985288dc83b20b4af2ce4072177fdc06b6ad7 Author: Eli Zaretskii Date: Sat Jan 6 12:33:44 2024 +0200 Fix last change (bug#67930) * lisp/progmodes/compile.el (compilation--expand-fn): Renamed from 'safe-expand-file-name'; all callers changed. Doc fix. diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 3002cd1b86c..e7d4e9966cf 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -3123,10 +3123,10 @@ and overlay is highlighted between MK and END-MK." (remove-hook 'pre-command-hook #'compilation-goto-locus-delete-o)) -(defun safe-expand-file-name (directory filename) - "Expand the specified filename using expand-file-name. If this fails, -retry with file-truename (see bug #8035) -Unlike expand-file-name, file-truename follows symlinks which we try to avoid if possible." +(defun compilation--expand-fn (directory filename) + "Expand FILENAME or resolve its true name. +Unlike `expand-file-name', `file-truename' follows symlinks, which +we try to avoid if possible." (let* ((expandedname (expand-file-name filename directory))) (if (file-exists-p expandedname) expandedname @@ -3152,7 +3152,8 @@ Unlike expand-file-name, file-truename follows symlinks which we try to avoid if fmts formats) ;; For each directory, try each format string. (while (and fmts (null buffer)) - (setq name (safe-expand-file-name thisdir (format (car fmts) filename)) + (setq name (compilation--expand-fn thisdir + (format (car fmts) filename)) buffer (and (file-exists-p name) (find-file-noselect name)) fmts (cdr fmts))) @@ -3174,7 +3175,8 @@ Unlike expand-file-name, file-truename follows symlinks which we try to avoid if (setq thisdir (car dirs) fmts formats) (while (and fmts (null buffer)) - (setq name (safe-expand-file-name thisdir (format (car fmts) filename)) + (setq name (compilation--expand-fn thisdir + (format (car fmts) filename)) buffer (and (file-exists-p name) (find-file-noselect name)) fmts (cdr fmts))) @@ -3234,7 +3236,7 @@ attempts to find a file whose name is produced by (format FMT FILENAME)." (ding) (sit-for 2)) ((and (file-directory-p name) (not (file-exists-p - (setq name (safe-expand-file-name name filename))))) + (setq name (compilation--expand-fn name filename))))) (message "No `%s' in directory %s" filename origname) (ding) (sit-for 2)) (t commit 471cc26002d3f6028252c77998272fccf73722ec Author: Jurgen De Backer Date: Thu Jan 4 11:10:56 2024 +0000 Fix file-name resolution in *compilation* and *grep* buffers Resolving symlinks in file names could lead to non-existent files if some leading directory is a symlink to its parent. In emacs 28 'expand-file-name' was replaced by 'file-truename' to solve bug #8035. * lisp/progmodes/compile.el (safe-expand-file-name): New function. (compilation-find-file-1): Call 'safe-expand-file-name'. (Bug#67930) diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 4af6a96900a..3002cd1b86c 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -3122,7 +3122,16 @@ and overlay is highlighted between MK and END-MK." (cancel-timer next-error-highlight-timer)) (remove-hook 'pre-command-hook #'compilation-goto-locus-delete-o)) - + +(defun safe-expand-file-name (directory filename) + "Expand the specified filename using expand-file-name. If this fails, +retry with file-truename (see bug #8035) +Unlike expand-file-name, file-truename follows symlinks which we try to avoid if possible." + (let* ((expandedname (expand-file-name filename directory))) + (if (file-exists-p expandedname) + expandedname + (file-truename (file-name-concat directory filename))))) + (defun compilation-find-file-1 (marker filename directory &optional formats) (or formats (setq formats '("%s"))) (let ((dirs compilation-search-path) @@ -3143,8 +3152,7 @@ and overlay is highlighted between MK and END-MK." fmts formats) ;; For each directory, try each format string. (while (and fmts (null buffer)) - (setq name (file-truename - (file-name-concat thisdir (format (car fmts) filename))) + (setq name (safe-expand-file-name thisdir (format (car fmts) filename)) buffer (and (file-exists-p name) (find-file-noselect name)) fmts (cdr fmts))) @@ -3166,8 +3174,7 @@ and overlay is highlighted between MK and END-MK." (setq thisdir (car dirs) fmts formats) (while (and fmts (null buffer)) - (setq name (file-truename - (file-name-concat thisdir (format (car fmts) filename))) + (setq name (safe-expand-file-name thisdir (format (car fmts) filename)) buffer (and (file-exists-p name) (find-file-noselect name)) fmts (cdr fmts))) @@ -3227,8 +3234,7 @@ attempts to find a file whose name is produced by (format FMT FILENAME)." (ding) (sit-for 2)) ((and (file-directory-p name) (not (file-exists-p - (setq name (file-truename - (file-name-concat name filename)))))) + (setq name (safe-expand-file-name name filename))))) (message "No `%s' in directory %s" filename origname) (ding) (sit-for 2)) (t commit d3a4fe5694f7bd1a09546d67d2cddc0f444d41ca Author: Martin Rudalics Date: Wed Jan 3 11:35:25 2024 +0100 Fix use of 'display-buffer-alist' for Info buffers * lisp/info.el (info-pop-to-buffer): New function. (info-other-window, info, Info-find-node, Info-revert-find-node) (Info-next, Info-prev, Info-up, info-display-manual): Call 'info-pop-to-buffer'. (Bug#68081) diff --git a/lisp/info.el b/lisp/info.el index 39ca88c358c..f4384934155 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -732,8 +732,53 @@ in `Info-file-supports-index-cookies-list'." (read-file-name "Info file name: " nil nil t)) (if (numberp current-prefix-arg) (format "*info*<%s>" current-prefix-arg)))) - (info-setup file-or-node - (switch-to-buffer-other-window (or buffer "*info*")))) + (info-pop-to-buffer file-or-node buffer t)) + +(defun info-pop-to-buffer (&optional file-or-node buffer-or-name other-window) + "Put Info node FILE-OR-NODE in specified buffer and display it. +Optional argument FILE-OR-NODE is as for `info'. + +If the optional argument BUFFER-OR-NAME is a buffer, use that +buffer. If it is a string, use that string as the name of the +buffer, creating it if it does not exist. Otherwise, use a +buffer with the name `*info*', creating it if it does not exist. + +Optional argument OTHER-WINDOW nil means to prefer the selected +window. OTHER-WINDOW non-nil means to prefer another window. +Select the window used, if it has been made." + (let ((buffer (cond + ((bufferp buffer-or-name) + buffer-or-name) + ((stringp buffer-or-name) + (get-buffer-create buffer-or-name)) + (t + (get-buffer-create "*info*"))))) + (with-current-buffer buffer + (unless (derived-mode-p 'Info-mode) + (Info-mode))) + + (let* ((window + (display-buffer buffer + (if other-window + '(nil (inhibit-same-window . t)) + '(display-buffer-same-window))))) + (with-current-buffer buffer + (if file-or-node + ;; If argument already contains parentheses, don't add another set + ;; since the argument will then be parsed improperly. This also + ;; has the added benefit of allowing node names to be included + ;; following the parenthesized filename. + (Info-goto-node + (if (and (stringp file-or-node) (string-match "(.*)" file-or-node)) + file-or-node + (concat "(" file-or-node ")"))) + (if (and (zerop (buffer-size)) + (null Info-history)) + ;; If we just created the Info buffer, go to the directory. + (Info-directory)))) + + (when window + (select-window window))))) ;;;###autoload (put 'info 'info-file (purecopy "emacs")) ;;;###autoload @@ -768,8 +813,8 @@ See a list of available Info commands in `Info-mode'." ;; of names that might have been wrapped (in emails, etc.). (setq file-or-node (string-replace "\n" " " file-or-node))) - (info-setup file-or-node - (pop-to-buffer-same-window (or buffer "*info*")))) + + (info-pop-to-buffer file-or-node buffer)) (defun info-setup (file-or-node buffer) "Display Info node FILE-OR-NODE in BUFFER." @@ -789,6 +834,8 @@ See a list of available Info commands in `Info-mode'." ;; If we just created the Info buffer, go to the directory. (Info-directory)))) +(make-obsolete 'info-setup "use `info-pop-to-buffer' instead" "30.1") + ;;;###autoload (defun info-emacs-manual () "Display the Emacs manual in Info mode." @@ -927,7 +974,7 @@ If NOERROR, inhibit error messages when we can't find the node." (setq nodename (info--node-canonicalize-whitespace nodename)) (setq filename (Info-find-file filename noerror)) ;; Go into Info buffer. - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (info-pop-to-buffer filename)) ;; Record the node we are leaving, if we were in one. (and (not no-going-back) Info-current-file @@ -957,7 +1004,7 @@ otherwise, that defaults to `Top'." "Go to an Info node FILENAME and NODENAME, re-reading disk contents. When *info* is already displaying FILENAME and NODENAME, the window position is preserved, if possible." - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (info-pop-to-buffer filename)) (let ((old-filename Info-current-file) (old-nodename Info-current-node) (window-selected (eq (selected-window) (get-buffer-window))) @@ -2290,7 +2337,7 @@ This command doesn't descend into sub-nodes, like \\\\[Info-forwa (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (info-pop-to-buffer)) (Info-goto-node (Info-extract-pointer "next")))) (defun Info-prev () @@ -2299,7 +2346,7 @@ This command doesn't go up to the parent node, like \\\\[Info-bac (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (info-pop-to-buffer)) (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous")))) (defun Info-up (&optional same-file) @@ -2308,7 +2355,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (interactive nil Info-mode) ;; In case another window is currently selected (save-window-excursion - (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) + (or (derived-mode-p 'Info-mode) (info-pop-to-buffer)) (let ((old-node Info-current-node) (old-file Info-current-file) (node (Info-extract-pointer "up")) p) @@ -5485,7 +5532,7 @@ completion alternatives to currently visited manuals." (raise-frame (window-frame window)) (select-frame-set-input-focus (window-frame window)) (select-window window)) - (switch-to-buffer found))) + (info-pop-to-buffer nil found))) ;; The buffer doesn't exist; create it. (info-initialize) (info (Info-find-file manual) commit 2a861124e89d7a29b19bb9a6f22e962c37444212 Author: Eli Zaretskii Date: Sat Jan 6 11:15:31 2024 +0200 ; Improve documentation of 'buffer-match-p' * doc/lispref/buffers.texi (Buffer List): * doc/lispref/windows.texi (Choosing Window): Add caveats for calling 'buffer-match-p' too early, when CONDITION is 'derived-mode' or 'major-mode'. (Bug#68081) diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi index b7047a68856..f67a954edc5 100644 --- a/doc/lispref/buffers.texi +++ b/doc/lispref/buffers.texi @@ -959,7 +959,7 @@ infinite recursion. @defun buffer-match-p condition buffer-or-name &optional arg This function checks if a buffer designated by @code{buffer-or-name} -satisfies the specified @code{condition}. Optional third argument +satisfies the specified @var{condition}. Optional third argument @var{arg} is passed to the predicate function in @var{condition}. A valid @var{condition} can be one of the following: @itemize @bullet{} @@ -987,10 +987,15 @@ Satisfied if @emph{any} condition in @var{conds} satisfies Satisfied if @emph{all} the conditions in @var{conds} satisfy @code{buffer-match-p}, with the same buffer and @code{arg}. @item derived-mode -Satisfied if the buffer's major mode derives from @var{expr}. +Satisfied if the buffer's major mode derives from @var{expr}. Note +that this condition might fail to report a match if +@code{buffer-match-p} is invoked before the major mode of the buffer +has been established. @item major-mode Satisfied if the buffer's major mode is equal to @var{expr}. Prefer -using @code{derived-mode} instead, when both can work. +using @code{derived-mode} instead, when both can work. Note that this +condition might fail to report a match if @code{buffer-match-p} is +invoked before the major mode of the buffer has been established. @end table @item t Satisfied by any buffer. A convenient alternative to @code{""} (empty @@ -1000,7 +1005,7 @@ string) or @code{(and)} (empty conjunction). @defun match-buffers condition &optional buffer-list arg This function returns a list of all buffers that satisfy the -@code{condition}. If no buffers match, the function returns +@var{condition}. If no buffers match, the function returns @code{nil}. The argument @var{condition} is as defined in @code{buffer-match-p} above. By default, all the buffers are considered, but this can be restricted via the optional argument diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 39e6d1386c6..d72da704f13 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2629,11 +2629,15 @@ default value is an empty display action, i.e., @w{@code{(nil . nil)}}. @defopt display-buffer-alist The value of this option is an alist mapping conditions to display -actions. Each condition is passed to @code{buffer-match-p}, along -with the buffer name and the @var{action} argument passed to -@code{display-buffer}. If it returns a non-@code{nil} value, then -@code{display-buffer} uses the corresponding display action to display -the buffer. +actions. Each condition is passed to @code{buffer-match-p} +(@pxref{Buffer List}), along with the buffer name and the @var{action} +argument passed to @code{display-buffer}. If it returns a +non-@code{nil} value, then @code{display-buffer} uses the +corresponding display action to display the buffer. Caveat: if you +use @code{derived-mode} or @code{major-mode} as condition, +@code{buffer-match-p} could fail to report a match if +@code{display-buffer} is called before the major mode of the buffer is +set. @end defopt @defopt display-buffer-base-action commit 657275529e31226bbc6c92eb7f7af887474a0bb8 Author: Po Lu Date: Sat Jan 6 15:28:14 2024 +0800 Revert "Add new `swap` macro and use it" typeof is an extension which does not exist in Standard C, so macros using it are unsuitable for inclusion in Emacs. This reverts commit 37889523278fe65733938fb11c3701898309961c. diff --git a/lwlib/xlwmenu.c b/lwlib/xlwmenu.c index a3d9474bed0..0f8f94b803c 100644 --- a/lwlib/xlwmenu.c +++ b/lwlib/xlwmenu.c @@ -671,7 +671,12 @@ draw_shadow_rectangle (XlwMenuWidget mw, Window window, int x, int y, } if (!erase_p && down_p) - swap (top_gc, bottom_gc); + { + GC temp; + temp = top_gc; + top_gc = bottom_gc; + bottom_gc = temp; + } /* Do draw (or erase) shadows */ points [0].x = x; @@ -752,7 +757,12 @@ draw_shadow_rhombus (XlwMenuWidget mw, Window window, int x, int y, } if (!erase_p && down_p) - swap (top_gc, bottom_gc); + { + GC temp; + temp = top_gc; + top_gc = bottom_gc; + bottom_gc = temp; + } points [0].x = x; points [0].y = y + height / 2; diff --git a/src/androidterm.c b/src/androidterm.c index 34734e63c37..2e4ee64f390 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5849,6 +5849,7 @@ android_get_surrounding_text (void *data) { struct android_get_surrounding_text_context *request; struct frame *f; + ptrdiff_t temp; request = data; @@ -5869,7 +5870,11 @@ android_get_surrounding_text (void *data) bad input methods. */ if (request->end < request->start) - swap (request->start, request->end); + { + temp = request->start; + request->start = request->end; + request->end = temp; + } /* Retrieve the conversion region. */ diff --git a/src/buffer.c b/src/buffer.c index 14c67224551..352aca8ddfd 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3591,7 +3591,10 @@ for the rear of the overlay advance when text is inserted there CHECK_FIXNUM_COERCE_MARKER (end); if (XFIXNUM (beg) > XFIXNUM (end)) - swap (beg, end); + { + Lisp_Object temp; + temp = beg; beg = end; end = temp; + } ptrdiff_t obeg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), BUF_Z (b)); @@ -3611,7 +3614,11 @@ static void modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) { if (start > end) - swap (start, end); + { + ptrdiff_t temp = start; + start = end; + end = temp; + } BUF_COMPUTE_UNCHANGED (buf, start, end); @@ -3651,7 +3658,10 @@ buffer. */) CHECK_FIXNUM_COERCE_MARKER (end); if (XFIXNUM (beg) > XFIXNUM (end)) - swap (beg, end); + { + Lisp_Object temp; + temp = beg; beg = end; end = temp; + } specbind (Qinhibit_quit, Qt); /* FIXME: Why? */ diff --git a/src/dispnew.c b/src/dispnew.c index 78ec3537a35..d0f259eef6c 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -649,7 +649,14 @@ reverse_rows (struct glyph_matrix *matrix, int start, int end) int i, j; for (i = start, j = end - 1; i < j; ++i, --j) - swap (matrix->rows[i], matrix->rows[j]); + { + /* Non-ISO HP/UX compiler doesn't like auto struct + initialization. */ + struct glyph_row temp; + temp = matrix->rows[i]; + matrix->rows[i] = matrix->rows[j]; + matrix->rows[j] = temp; + } } @@ -959,7 +966,9 @@ increment_row_positions (struct glyph_row *row, static void swap_glyphs_in_rows (struct glyph_row *a, struct glyph_row *b) { - for (int area = 0; area < LAST_AREA; ++area) + int area; + + for (area = 0; area < LAST_AREA; ++area) { /* Number of glyphs to swap. */ int max_used = max (a->used[area], b->used[area]); @@ -975,7 +984,12 @@ swap_glyphs_in_rows (struct glyph_row *a, struct glyph_row *b) while (glyph_a < glyph_a_end) { - swap (*glyph_a, *glyph_b); + /* Non-ISO HP/UX compiler doesn't like auto struct + initialization. */ + struct glyph temp; + temp = *glyph_a; + *glyph_a = *glyph_b; + *glyph_b = temp; ++glyph_a; ++glyph_b; } diff --git a/src/editfns.c b/src/editfns.c index 2e455a2efed..f3b3cfb7243 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1739,7 +1739,7 @@ versa, strings are converted from unibyte to multibyte or vice versa using `string-make-multibyte' or `string-make-unibyte', which see. */) (Lisp_Object buffer, Lisp_Object start, Lisp_Object end) { - register EMACS_INT b, e; + register EMACS_INT b, e, temp; register struct buffer *bp, *obuf; Lisp_Object buf; @@ -1753,7 +1753,7 @@ using `string-make-multibyte' or `string-make-unibyte', which see. */) b = !NILP (start) ? fix_position (start) : BUF_BEGV (bp); e = !NILP (end) ? fix_position (end) : BUF_ZV (bp); if (b > e) - swap (b, e); + temp = b, b = e, e = temp; if (!(BUF_BEGV (bp) <= b && e <= BUF_ZV (bp))) args_out_of_range (start, end); @@ -1782,7 +1782,7 @@ The value of `case-fold-search' in the current buffer determines whether case is significant or ignored. */) (Lisp_Object buffer1, Lisp_Object start1, Lisp_Object end1, Lisp_Object buffer2, Lisp_Object start2, Lisp_Object end2) { - register EMACS_INT begp1, endp1, begp2, endp2; + register EMACS_INT begp1, endp1, begp2, endp2, temp; register struct buffer *bp1, *bp2; register Lisp_Object trt = (!NILP (Vcase_fold_search) @@ -1808,7 +1808,7 @@ determines whether case is significant or ignored. */) begp1 = !NILP (start1) ? fix_position (start1) : BUF_BEGV (bp1); endp1 = !NILP (end1) ? fix_position (end1) : BUF_ZV (bp1); if (begp1 > endp1) - swap (begp1, endp1); + temp = begp1, begp1 = endp1, endp1 = temp; if (!(BUF_BEGV (bp1) <= begp1 && begp1 <= endp1 @@ -1833,7 +1833,7 @@ determines whether case is significant or ignored. */) begp2 = !NILP (start2) ? fix_position (start2) : BUF_BEGV (bp2); endp2 = !NILP (end2) ? fix_position (end2) : BUF_ZV (bp2); if (begp2 > endp2) - swap (begp2, endp2); + temp = begp2, begp2 = endp2, endp2 = temp; if (!(BUF_BEGV (bp2) <= begp2 && begp2 <= endp2 diff --git a/src/eval.c b/src/eval.c index 6a866d6cc32..94f6d8e31f8 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2794,9 +2794,12 @@ usage: (run-hook-with-args-until-failure HOOK &rest ARGS) */) static Lisp_Object run_hook_wrapped_funcall (ptrdiff_t nargs, Lisp_Object *args) { - swap (args[0], args[1]); - Lisp_Object ret = Ffuncall (nargs, args); - swap (args[1], args[0]); + Lisp_Object tmp = args[0], ret; + args[0] = args[1]; + args[1] = tmp; + ret = Ffuncall (nargs, args); + args[1] = args[0]; + args[0] = tmp; return ret; } diff --git a/src/fns.c b/src/fns.c index c8adc5cb891..c03aea02397 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5674,7 +5674,11 @@ extract_data_from_object (Lisp_Object spec, b = !NILP (start) ? fix_position (start) : BEGV; e = !NILP (end) ? fix_position (end) : ZV; if (b > e) - swap (b, e); + { + EMACS_INT temp = b; + b = e; + e = temp; + } if (!(BEGV <= b && e <= ZV)) args_out_of_range (start, end); diff --git a/src/lisp.h b/src/lisp.h index f96932ab0c1..44f69892c6f 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -67,10 +67,6 @@ INLINE_HEADER_BEGIN #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) -/* Swap values of a and b. */ -#define swap(a, b) \ - do { typeof (a) __tmp; __tmp = (a); (a) = (b); (b) = __tmp; } while (0); - /* Number of elements in an array. */ #define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0]) diff --git a/src/regex-emacs.c b/src/regex-emacs.c index fdc2cc63445..6aa6f4f9b34 100644 --- a/src/regex-emacs.c +++ b/src/regex-emacs.c @@ -2839,7 +2839,7 @@ forall_firstchar_1 (re_char *p, re_char *pend, while (true) { - re_char *newp1, *newp2; + re_char *newp1, *newp2, *tmp; re_char *p_orig = p; int offset; @@ -2930,7 +2930,7 @@ forall_firstchar_1 (re_char *p, re_char *pend, /* We have to check that both destinations are safe. Arrange for `newp1` to be the smaller of the two. */ if (newp1 > newp2) - swap (newp1, newp2); + (tmp = newp1, newp1 = newp2, newp2 = tmp); if (newp2 <= p_orig) /* Both destinations go backward! */ { diff --git a/src/textconv.c b/src/textconv.c index e0707522d7e..2a7b0ed330d 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -176,7 +176,7 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, { specpdl_ref count; ptrdiff_t pos, pos_byte, end, end_byte, start; - ptrdiff_t mark; + ptrdiff_t temp, temp1, mark; char *buffer; struct window *w; @@ -383,8 +383,12 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, if (end < pos) { eassert (end_byte < pos_byte); - swap (pos_byte, end_byte); - swap (pos, end); + temp = pos_byte; + temp1 = pos; + pos_byte = end_byte; + pos = end; + end = temp1; + end_byte = temp; } /* Return the string first. */ @@ -1901,9 +1905,15 @@ get_extracted_text (struct frame *f, ptrdiff_t n, start = marker_position (BVAR (current_buffer, mark)); end = PT; - /* Sort start and end. */ + /* Sort start and end. start_byte is used to hold a + temporary value. */ + if (start > end) - swap (start, end); + { + start_byte = end; + end = start; + start = start_byte; + } } else goto finish; @@ -1969,7 +1979,7 @@ get_surrounding_text (struct frame *f, ptrdiff_t left, ptrdiff_t *end_return) { specpdl_ref count; - ptrdiff_t start, end, start_byte, end_byte, mark; + ptrdiff_t start, end, start_byte, end_byte, mark, temp; char *buffer; if (!WINDOW_LIVE_P (f->old_selected_window)) @@ -2002,7 +2012,11 @@ get_surrounding_text (struct frame *f, ptrdiff_t left, /* Now sort start and end. */ if (end < start) - swap (start, end) + { + temp = start; + start = end; + end = temp; + } /* And subtract left and right. */ diff --git a/src/textprop.c b/src/textprop.c index ec9435219ea..7d9aae0d2c5 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -142,7 +142,12 @@ validate_interval_range (Lisp_Object object, Lisp_Object *begin, return NULL; if (XFIXNUM (*begin) > XFIXNUM (*end)) - swap (*begin, *end); + { + Lisp_Object n; + n = *begin; + *begin = *end; + *end = n; + } if (BUFFERP (object)) { @@ -2196,7 +2201,11 @@ verify_interval_modification (struct buffer *buf, return; if (start > end) - swap (start, end); + { + ptrdiff_t temp = start; + start = end; + end = temp; + } /* For an insert operation, check the two chars around the position. */ if (start == end) diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c index c417159cf9e..a73c0de06f9 100644 --- a/src/w32uniscribe.c +++ b/src/w32uniscribe.c @@ -765,10 +765,10 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font, #define OTF_INT16_VAL(TABLE, OFFSET, PTR) \ do { \ - BYTE data[2]; \ + BYTE temp, data[2]; \ if (GetFontData (context, TABLE, OFFSET, data, 2) != 2) \ goto font_table_error; \ - swap (data[0], data[1]); \ + temp = data[0], data[0] = data[1], data[1] = temp; \ memcpy (PTR, data, 2); \ } while (0) diff --git a/src/xfaces.c b/src/xfaces.c index f79eb022e15..e30c2fac70c 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -1357,7 +1357,12 @@ load_face_colors (struct frame *f, struct face *face, /* Swap colors if face is inverse-video. */ if (EQ (attrs[LFACE_INVERSE_INDEX], Qt)) - swap (fg, bg); + { + Lisp_Object tmp; + tmp = fg; + fg = bg; + bg = tmp; + } /* Check for support for foreground, not for background because face_color_supported_p is smart enough to know that grays are diff --git a/src/xterm.c b/src/xterm.c index 0b83b0554b3..1f398b2e39a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1569,19 +1569,19 @@ typedef enum xm_byte_order #define SWAPCARD32(l) \ { \ struct { unsigned t : 32; } bit32; \ - char *tp = (char *) &bit32; \ + char n, *tp = (char *) &bit32; \ bit32.t = l; \ - swap (tp[0], tp[3]); \ - swap (tp[1], tp[2]); \ + n = tp[0]; tp[0] = tp[3]; tp[3] = n; \ + n = tp[1]; tp[1] = tp[2]; tp[2] = n; \ l = bit32.t; \ } #define SWAPCARD16(s) \ { \ struct { unsigned t : 16; } bit16; \ - char *tp = (char *) &bit16; \ + char n, *tp = (char *) &bit16; \ bit16.t = s; \ - swap (tp[0], tp[1]); \ + n = tp[0]; tp[0] = tp[1]; tp[1] = n; \ s = bit16.t; \ } commit 2f59052602e71fb818dd5d671be119793864e712 Author: Po Lu Date: Sat Jan 6 15:24:58 2024 +0800 Properly parse TTC tables with digital signatures * src/sfnt.c (sfnt_read_ttc_header): Don't inadvertently overwrite first two offsets while reading the digital signature. diff --git a/src/sfnt.c b/src/sfnt.c index aa8b49a9ecd..36240f4cdff 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -6195,7 +6195,7 @@ sfnt_read_ttc_header (int fd) size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset, uint32_t) - offsetof (struct sfnt_ttc_header, ul_dsig_tag)); - rc = read (fd, &ttc->ul_dsig_offset, size); + rc = read (fd, &ttc->ul_dsig_tag, size); if (rc == -1 || rc < size) { xfree (ttc); @@ -20631,8 +20631,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 44 -#define EASY_PPEM 44 +#define FANCY_PPEM 14 +#define EASY_PPEM 14 interpreter = NULL; head = sfnt_read_head_table (fd, font); commit 37889523278fe65733938fb11c3701898309961c Author: Stefan Kangas Date: Sat Jan 6 08:22:08 2024 +0100 Add new `swap` macro and use it A `swap` macro prevents programming errors and is more concise. It is a natural addition to our existing `min` and `max` macros. * src/lisp.h (swap): New macro. * lwlib/xlwmenu.c (draw_shadow_rectangle, draw_shadow_rhombus): * src/androidterm.c (android_get_surrounding_text): * src/buffer.c (Fmake_overlay, modify_overlay, Fmove_overlay): * src/dispnew.c (swap_glyphs_in_rows, reverse_rows): * src/editfns.c (Finsert_buffer_substring) (Fcompare_buffer_substrings): * src/eval.c (run_hook_wrapped_funcall): * src/fns.c (extract_data_from_object): * src/regex-emacs.c (forall_firstchar_1): * src/textconv.c (textconv_query, get_extracted_text) (get_surrounding_text): * src/textprop.c (validate_interval_range) (verify_interval_modification): * src/w32uniscribe.c (OTF_INT16_VAL): * src/xfaces.c (load_face_colors): * src/xterm.c (SWAPCARD32): Prefer using above macro to open-coding. diff --git a/lwlib/xlwmenu.c b/lwlib/xlwmenu.c index 0f8f94b803c..a3d9474bed0 100644 --- a/lwlib/xlwmenu.c +++ b/lwlib/xlwmenu.c @@ -671,12 +671,7 @@ draw_shadow_rectangle (XlwMenuWidget mw, Window window, int x, int y, } if (!erase_p && down_p) - { - GC temp; - temp = top_gc; - top_gc = bottom_gc; - bottom_gc = temp; - } + swap (top_gc, bottom_gc); /* Do draw (or erase) shadows */ points [0].x = x; @@ -757,12 +752,7 @@ draw_shadow_rhombus (XlwMenuWidget mw, Window window, int x, int y, } if (!erase_p && down_p) - { - GC temp; - temp = top_gc; - top_gc = bottom_gc; - bottom_gc = temp; - } + swap (top_gc, bottom_gc); points [0].x = x; points [0].y = y + height / 2; diff --git a/src/androidterm.c b/src/androidterm.c index 2e4ee64f390..34734e63c37 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5849,7 +5849,6 @@ android_get_surrounding_text (void *data) { struct android_get_surrounding_text_context *request; struct frame *f; - ptrdiff_t temp; request = data; @@ -5870,11 +5869,7 @@ android_get_surrounding_text (void *data) bad input methods. */ if (request->end < request->start) - { - temp = request->start; - request->start = request->end; - request->end = temp; - } + swap (request->start, request->end); /* Retrieve the conversion region. */ diff --git a/src/buffer.c b/src/buffer.c index 352aca8ddfd..14c67224551 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3591,10 +3591,7 @@ for the rear of the overlay advance when text is inserted there CHECK_FIXNUM_COERCE_MARKER (end); if (XFIXNUM (beg) > XFIXNUM (end)) - { - Lisp_Object temp; - temp = beg; beg = end; end = temp; - } + swap (beg, end); ptrdiff_t obeg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), BUF_Z (b)); @@ -3614,11 +3611,7 @@ static void modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) { if (start > end) - { - ptrdiff_t temp = start; - start = end; - end = temp; - } + swap (start, end); BUF_COMPUTE_UNCHANGED (buf, start, end); @@ -3658,10 +3651,7 @@ buffer. */) CHECK_FIXNUM_COERCE_MARKER (end); if (XFIXNUM (beg) > XFIXNUM (end)) - { - Lisp_Object temp; - temp = beg; beg = end; end = temp; - } + swap (beg, end); specbind (Qinhibit_quit, Qt); /* FIXME: Why? */ diff --git a/src/dispnew.c b/src/dispnew.c index d0f259eef6c..78ec3537a35 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -649,14 +649,7 @@ reverse_rows (struct glyph_matrix *matrix, int start, int end) int i, j; for (i = start, j = end - 1; i < j; ++i, --j) - { - /* Non-ISO HP/UX compiler doesn't like auto struct - initialization. */ - struct glyph_row temp; - temp = matrix->rows[i]; - matrix->rows[i] = matrix->rows[j]; - matrix->rows[j] = temp; - } + swap (matrix->rows[i], matrix->rows[j]); } @@ -966,9 +959,7 @@ increment_row_positions (struct glyph_row *row, static void swap_glyphs_in_rows (struct glyph_row *a, struct glyph_row *b) { - int area; - - for (area = 0; area < LAST_AREA; ++area) + for (int area = 0; area < LAST_AREA; ++area) { /* Number of glyphs to swap. */ int max_used = max (a->used[area], b->used[area]); @@ -984,12 +975,7 @@ swap_glyphs_in_rows (struct glyph_row *a, struct glyph_row *b) while (glyph_a < glyph_a_end) { - /* Non-ISO HP/UX compiler doesn't like auto struct - initialization. */ - struct glyph temp; - temp = *glyph_a; - *glyph_a = *glyph_b; - *glyph_b = temp; + swap (*glyph_a, *glyph_b); ++glyph_a; ++glyph_b; } diff --git a/src/editfns.c b/src/editfns.c index f3b3cfb7243..2e455a2efed 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1739,7 +1739,7 @@ versa, strings are converted from unibyte to multibyte or vice versa using `string-make-multibyte' or `string-make-unibyte', which see. */) (Lisp_Object buffer, Lisp_Object start, Lisp_Object end) { - register EMACS_INT b, e, temp; + register EMACS_INT b, e; register struct buffer *bp, *obuf; Lisp_Object buf; @@ -1753,7 +1753,7 @@ using `string-make-multibyte' or `string-make-unibyte', which see. */) b = !NILP (start) ? fix_position (start) : BUF_BEGV (bp); e = !NILP (end) ? fix_position (end) : BUF_ZV (bp); if (b > e) - temp = b, b = e, e = temp; + swap (b, e); if (!(BUF_BEGV (bp) <= b && e <= BUF_ZV (bp))) args_out_of_range (start, end); @@ -1782,7 +1782,7 @@ The value of `case-fold-search' in the current buffer determines whether case is significant or ignored. */) (Lisp_Object buffer1, Lisp_Object start1, Lisp_Object end1, Lisp_Object buffer2, Lisp_Object start2, Lisp_Object end2) { - register EMACS_INT begp1, endp1, begp2, endp2, temp; + register EMACS_INT begp1, endp1, begp2, endp2; register struct buffer *bp1, *bp2; register Lisp_Object trt = (!NILP (Vcase_fold_search) @@ -1808,7 +1808,7 @@ determines whether case is significant or ignored. */) begp1 = !NILP (start1) ? fix_position (start1) : BUF_BEGV (bp1); endp1 = !NILP (end1) ? fix_position (end1) : BUF_ZV (bp1); if (begp1 > endp1) - temp = begp1, begp1 = endp1, endp1 = temp; + swap (begp1, endp1); if (!(BUF_BEGV (bp1) <= begp1 && begp1 <= endp1 @@ -1833,7 +1833,7 @@ determines whether case is significant or ignored. */) begp2 = !NILP (start2) ? fix_position (start2) : BUF_BEGV (bp2); endp2 = !NILP (end2) ? fix_position (end2) : BUF_ZV (bp2); if (begp2 > endp2) - temp = begp2, begp2 = endp2, endp2 = temp; + swap (begp2, endp2); if (!(BUF_BEGV (bp2) <= begp2 && begp2 <= endp2 diff --git a/src/eval.c b/src/eval.c index 94f6d8e31f8..6a866d6cc32 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2794,12 +2794,9 @@ usage: (run-hook-with-args-until-failure HOOK &rest ARGS) */) static Lisp_Object run_hook_wrapped_funcall (ptrdiff_t nargs, Lisp_Object *args) { - Lisp_Object tmp = args[0], ret; - args[0] = args[1]; - args[1] = tmp; - ret = Ffuncall (nargs, args); - args[1] = args[0]; - args[0] = tmp; + swap (args[0], args[1]); + Lisp_Object ret = Ffuncall (nargs, args); + swap (args[1], args[0]); return ret; } diff --git a/src/fns.c b/src/fns.c index c03aea02397..c8adc5cb891 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5674,11 +5674,7 @@ extract_data_from_object (Lisp_Object spec, b = !NILP (start) ? fix_position (start) : BEGV; e = !NILP (end) ? fix_position (end) : ZV; if (b > e) - { - EMACS_INT temp = b; - b = e; - e = temp; - } + swap (b, e); if (!(BEGV <= b && e <= ZV)) args_out_of_range (start, end); diff --git a/src/lisp.h b/src/lisp.h index 44f69892c6f..f96932ab0c1 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -67,6 +67,10 @@ INLINE_HEADER_BEGIN #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) +/* Swap values of a and b. */ +#define swap(a, b) \ + do { typeof (a) __tmp; __tmp = (a); (a) = (b); (b) = __tmp; } while (0); + /* Number of elements in an array. */ #define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0]) diff --git a/src/regex-emacs.c b/src/regex-emacs.c index 6aa6f4f9b34..fdc2cc63445 100644 --- a/src/regex-emacs.c +++ b/src/regex-emacs.c @@ -2839,7 +2839,7 @@ forall_firstchar_1 (re_char *p, re_char *pend, while (true) { - re_char *newp1, *newp2, *tmp; + re_char *newp1, *newp2; re_char *p_orig = p; int offset; @@ -2930,7 +2930,7 @@ forall_firstchar_1 (re_char *p, re_char *pend, /* We have to check that both destinations are safe. Arrange for `newp1` to be the smaller of the two. */ if (newp1 > newp2) - (tmp = newp1, newp1 = newp2, newp2 = tmp); + swap (newp1, newp2); if (newp2 <= p_orig) /* Both destinations go backward! */ { diff --git a/src/textconv.c b/src/textconv.c index 2a7b0ed330d..e0707522d7e 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -176,7 +176,7 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, { specpdl_ref count; ptrdiff_t pos, pos_byte, end, end_byte, start; - ptrdiff_t temp, temp1, mark; + ptrdiff_t mark; char *buffer; struct window *w; @@ -383,12 +383,8 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, if (end < pos) { eassert (end_byte < pos_byte); - temp = pos_byte; - temp1 = pos; - pos_byte = end_byte; - pos = end; - end = temp1; - end_byte = temp; + swap (pos_byte, end_byte); + swap (pos, end); } /* Return the string first. */ @@ -1905,15 +1901,9 @@ get_extracted_text (struct frame *f, ptrdiff_t n, start = marker_position (BVAR (current_buffer, mark)); end = PT; - /* Sort start and end. start_byte is used to hold a - temporary value. */ - + /* Sort start and end. */ if (start > end) - { - start_byte = end; - end = start; - start = start_byte; - } + swap (start, end); } else goto finish; @@ -1979,7 +1969,7 @@ get_surrounding_text (struct frame *f, ptrdiff_t left, ptrdiff_t *end_return) { specpdl_ref count; - ptrdiff_t start, end, start_byte, end_byte, mark, temp; + ptrdiff_t start, end, start_byte, end_byte, mark; char *buffer; if (!WINDOW_LIVE_P (f->old_selected_window)) @@ -2012,11 +2002,7 @@ get_surrounding_text (struct frame *f, ptrdiff_t left, /* Now sort start and end. */ if (end < start) - { - temp = start; - start = end; - end = temp; - } + swap (start, end) /* And subtract left and right. */ diff --git a/src/textprop.c b/src/textprop.c index 7d9aae0d2c5..ec9435219ea 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -142,12 +142,7 @@ validate_interval_range (Lisp_Object object, Lisp_Object *begin, return NULL; if (XFIXNUM (*begin) > XFIXNUM (*end)) - { - Lisp_Object n; - n = *begin; - *begin = *end; - *end = n; - } + swap (*begin, *end); if (BUFFERP (object)) { @@ -2201,11 +2196,7 @@ verify_interval_modification (struct buffer *buf, return; if (start > end) - { - ptrdiff_t temp = start; - start = end; - end = temp; - } + swap (start, end); /* For an insert operation, check the two chars around the position. */ if (start == end) diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c index a73c0de06f9..c417159cf9e 100644 --- a/src/w32uniscribe.c +++ b/src/w32uniscribe.c @@ -765,10 +765,10 @@ add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font, #define OTF_INT16_VAL(TABLE, OFFSET, PTR) \ do { \ - BYTE temp, data[2]; \ + BYTE data[2]; \ if (GetFontData (context, TABLE, OFFSET, data, 2) != 2) \ goto font_table_error; \ - temp = data[0], data[0] = data[1], data[1] = temp; \ + swap (data[0], data[1]); \ memcpy (PTR, data, 2); \ } while (0) diff --git a/src/xfaces.c b/src/xfaces.c index e30c2fac70c..f79eb022e15 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -1357,12 +1357,7 @@ load_face_colors (struct frame *f, struct face *face, /* Swap colors if face is inverse-video. */ if (EQ (attrs[LFACE_INVERSE_INDEX], Qt)) - { - Lisp_Object tmp; - tmp = fg; - fg = bg; - bg = tmp; - } + swap (fg, bg); /* Check for support for foreground, not for background because face_color_supported_p is smart enough to know that grays are diff --git a/src/xterm.c b/src/xterm.c index 1f398b2e39a..0b83b0554b3 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1569,19 +1569,19 @@ typedef enum xm_byte_order #define SWAPCARD32(l) \ { \ struct { unsigned t : 32; } bit32; \ - char n, *tp = (char *) &bit32; \ + char *tp = (char *) &bit32; \ bit32.t = l; \ - n = tp[0]; tp[0] = tp[3]; tp[3] = n; \ - n = tp[1]; tp[1] = tp[2]; tp[2] = n; \ + swap (tp[0], tp[3]); \ + swap (tp[1], tp[2]); \ l = bit32.t; \ } #define SWAPCARD16(s) \ { \ struct { unsigned t : 16; } bit16; \ - char n, *tp = (char *) &bit16; \ + char *tp = (char *) &bit16; \ bit16.t = s; \ - n = tp[0]; tp[0] = tp[1]; tp[1] = n; \ + swap (tp[0], tp[1]); \ s = bit16.t; \ } commit 2740a3cbfde65a899f2fcefceee9c4bc06eebc2d Author: Po Lu Date: Fri Jan 5 17:51:40 2024 +0800 ; Update Android port splash screen message * lisp/term/android-win.el (android-after-splash-screen): Insert missing newline. diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index 51163e5b9b2..876b24683bc 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -417,7 +417,7 @@ been denied. Click " :link '("here" android-display-storage-permission-popup) " to grant them.") (insert - "Permissions necessary to access external storage directories have been + "\nPermissions necessary to access external storage directories have been denied. ") (insert-button "Click here to grant them." 'action #'android-display-storage-permission-popup commit 790b5982175b8dcd45fe444379e8039b6cc05e97 Author: Nicholas Vollmer Date: Fri Jan 5 12:22:10 2024 -0500 Use special-mode in checkdoc status buffer * lisp/emacs-lisp/checkdoc.el (checkdoc-display-status-buffer): Use `special-mode'. (Bug#68268) diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 80eaf93c3b7..82c6c03a592 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -556,7 +556,8 @@ the users will view as each check is completed." "Display and update the status buffer for the current checkdoc mode. CHECK is a list of four strings stating the current status of each test; the nth string describes the status of the nth test." - (let (temp-buffer-setup-hook) + (let (temp-buffer-setup-hook + (temp-buffer-show-hook #'special-mode)) (with-output-to-temp-buffer "*Checkdoc Status*" (mapc #'princ (list "Buffer comments and tags: " (nth 0 check) commit dc9d02f8a01d86ac8ff3fb004bb2f22cf211dcef Author: Juri Linkov Date: Fri Jan 5 09:39:04 2024 +0200 * lisp/isearch.el (isearch-search-and-update): Let-bind 'isearch-cmds'. When 'isearch-wrap-pause' is 'no' or 'no-ding', let-bind 'isearch-cmds' to avoid changing it by 'isearch-push-state' in 'isearch-repeat', so that a later DEL (isearch-delete-char) doesn't stop at the intermediate failing state (bug#68158). diff --git a/lisp/isearch.el b/lisp/isearch.el index ee5660309df..f753a5377ca 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -2844,7 +2844,8 @@ The command accepts Unicode names like \"smiling face\" or (isearch-search) (when (and (memq isearch-wrap-pause '(no no-ding)) (not isearch-success)) - (isearch-repeat (if isearch-forward 'forward 'backward))))) + (let ((isearch-cmds isearch-cmds)) + (isearch-repeat (if isearch-forward 'forward 'backward)))))) (isearch-push-state) (if isearch-op-fun (funcall isearch-op-fun)) (isearch-update)) commit 9308d9a74ab586e9793b2561da23116f2b4fe205 Author: Andrea Corallo Date: Thu Jan 4 11:06:41 2024 +0100 * src/comp.c (Fcomp__compile_ctxt_to_file): Fix hash table Qunbound use. diff --git a/src/comp.c b/src/comp.c index 882b42cdbd5..8428cf9020e 100644 --- a/src/comp.c +++ b/src/comp.c @@ -4964,12 +4964,12 @@ DEFUN ("comp--compile-ctxt-to-file", Fcomp__compile_ctxt_to_file, struct Lisp_Hash_Table *func_h = XHASH_TABLE (CALL1I (comp-ctxt-funcs-h, Vcomp_ctxt)); for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (func_h); i++) - if (!BASE_EQ (HASH_VALUE (func_h, i), Qunbound)) + if (!BASE_EQ (HASH_KEY (func_h, i), Qunbound)) declare_function (HASH_VALUE (func_h, i)); /* Compile all functions. Can't be done before because the relocation structs has to be already defined. */ for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (func_h); i++) - if (!BASE_EQ (HASH_VALUE (func_h, i), Qunbound)) + if (!BASE_EQ (HASH_KEY (func_h, i), Qunbound)) compile_function (HASH_VALUE (func_h, i)); /* Work around bug#46495 (GCC PR99126). */ commit a2a6619b2825c3c3d159610f0cd6fd89b791bd3f Author: Eli Zaretskii Date: Thu Jan 4 10:17:30 2024 +0200 Provide decent documentation for 'help-quick' * lisp/help.el (help-quick, help-quick-toggle): Doc fix. * doc/emacs/help.texi (Help Summary, Misc Help): Document 'help-quick-toggle'. diff --git a/doc/emacs/help.texi b/doc/emacs/help.texi index 68299c057d7..1330717b758 100644 --- a/doc/emacs/help.texi +++ b/doc/emacs/help.texi @@ -115,6 +115,9 @@ Display a list of commands whose names match @var{topics} Display all active key bindings; minor mode bindings first, then those of the major mode, then global bindings (@code{describe-bindings}). @xref{Misc Help}. +@item C-h C-q +Toggle display of a window showing popular commands and their key +bindings. @xref{Misc Help}. @item C-h c @var{key} Show the name of the command that the key sequence @var{key} is bound to (@code{describe-key-briefly}). Here @kbd{c} stands for @@ -700,6 +703,18 @@ displays the contents of the syntax table, with explanations of each character's syntax (@pxref{Syntax Tables,, Syntax Tables, elisp, The Emacs Lisp Reference Manual}). +@kindex C-h C-q +@findex help-quick-toggle +@findex help-quick +@cindex cheat sheet of popular Emacs commands + @kbd{C-h C-q} (@code{help-quick-toggle}) toggles on and off the +display of a buffer showing the most popular Emacs commands and their +respective key bindings (a.k.a.@: ``cheat sheet''). The contents of +that buffer are created by the command @code{help-quick}. Each key +binding shown in this buffer is a button: click on it with +@kbd{mouse-1} or @kbd{mouse-2} to show the documentation of the +command bound to that key sequence. + @findex describe-prefix-bindings You can get a list of subcommands for a particular prefix key by typing @kbd{C-h}, @kbd{?}, or @key{F1} diff --git a/lisp/help.el b/lisp/help.el index fe80dd3a72d..accd01e56f5 100644 --- a/lisp/help.el +++ b/lisp/help.el @@ -171,7 +171,10 @@ buffer.") ;; Inspired by a mg fork (https://github.com/troglobit/mg) (defun help-quick () - "Display a quick-help buffer." + "Display a quick-help buffer showing popular commands and their bindings. +The window showing quick-help can be toggled using \\[help-quick-toggle]. +You can click on a key binding shown in the quick-help buffer to display +the documentation of the command bound to that key sequence." (interactive) (with-current-buffer (get-buffer-create "*Quick Help*") (let ((inhibit-read-only t) (padding 2) blocks) @@ -244,10 +247,14 @@ buffer.") ;; ... and shrink it immediately. (fit-window-to-buffer)) (message - (substitute-command-keys "Toggle the quick help buffer using \\[help-quick-toggle].")))) + (substitute-command-keys "Toggle display of quick-help buffer using \\[help-quick-toggle].")))) (defun help-quick-toggle () - "Toggle the quick-help window." + "Toggle display of a window showing popular commands and their bindings. +This toggles on and off the display of the quick-help buffer, which shows +popular commands and their bindings as produced by `help-quick'. +You can click on a key binding shown in the quick-help buffer to display +the documentation of the command bound to that key sequence." (interactive) (if (and-let* ((window (get-buffer-window "*Quick Help*"))) (quit-window t window)) commit ab66b749a276c9fdc70ad2ee114314f0cde862fc Author: Eli Zaretskii Date: Wed Jan 3 15:14:41 2024 +0200 ; * src/window.c (Fset_window_margins): Doc fix. diff --git a/src/window.c b/src/window.c index 894d9c4fc19..8d4bde8d6db 100644 --- a/src/window.c +++ b/src/window.c @@ -7793,7 +7793,11 @@ means no margin. Leave margins unchanged if WINDOW is not large enough to accommodate margins of the desired width. Return t if any margin was actually -changed and nil otherwise. */) +changed and nil otherwise. + +The margins specified by calling this function may be later overridden +by invoking `set-window-buffer' for the same WINDOW, with its +KEEP-MARGINS argument nil or omitted. */) (Lisp_Object window, Lisp_Object left_width, Lisp_Object right_width) { struct window *w = set_window_margins (decode_live_window (window), commit 1a677d1429d1f9fea2d6b2bc9dd5644a5564cc27 Author: Dmitry Gutov Date: Tue Jan 2 15:32:03 2024 +0200 treesit--pre-syntax-ppss: Fix args-out-of-range in internal--syntax-propertize * lisp/treesit.el (treesit--pre-syntax-ppss): Make sure the lower bound is still within the current restriction (bug#67977). diff --git a/lisp/treesit.el b/lisp/treesit.el index b656040958d..c63bf510a24 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1150,7 +1150,7 @@ START and END mark the current to-be-propertized region." (if (and new-start (< new-start start)) (progn (setq treesit--syntax-propertize-start nil) - (cons new-start end)) + (cons (max new-start (point-min)) end)) nil))) ;;; Indent