commit 4e97019a77731d634e6b059954c8fef792c11fa8 (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Sun Aug 23 10:10:47 2020 +0300 Minor fixes for last change * lisp/international/kinsoku.el (kinsoku): Provide 'kinsoku'. * lisp/cus-start.el (standard): Use 'require' instead of 'load. * etc/NEWS: * doc/emacs/display.texi (Visual Line Mode): Improve wording and markup of last change. diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index d56a135b64..e7b8745a04 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1813,21 +1813,23 @@ variable @code{visual-line-fringe-indicators}. @findex char-category-set @findex category-set-mnemonics By default, Emacs only breaks lines after whitespace characters. -That strategy produces bad results when CJK and Latin text are mixed +That produces incorrect results when CJK and Latin text are mixed together (because CJK characters don't use whitespace to separate -words). You can customize @code{word-wrap-by-category} to allow Emacs -to break lines after any character with ``|'' category +words). You can customize the option @code{word-wrap-by-category} to +allow Emacs to break lines after any character with ``|'' category (@pxref{Categories,,, elisp, the Emacs Lisp Reference Manual}), which -includes CJK characters. Also, if this variable is set using -Customize, Emacs automatically loads kinsoku.el. When kinsoku.el is -loaded, Emacs respects kinsoku rules when breaking lines. That means -characters with the ``>'' category don't appear at the beginning of a -line (e.g., FULLWIDTH COMMA), and characters with the ``<'' category -don't appear at the end of a line (e.g., LEFT DOUBLE ANGLE BRACKET). -You can view the categories of a character by @code{char-category-set} -and @code{category-set-mnemonics}, or type @kbd{C-u C-x =} with point -on the character and look at the ``category'' section in the report. -You can add categories to a character by @code{modify-category-entry}. +provides better support for CJK characters. Also, if this variable is +set using Customize, Emacs automatically loads @file{kinsoku.el}. +When @file{kinsoku.el} is loaded, Emacs respects kinsoku rules when +breaking lines. That means characters with the ``>'' category don't +appear at the beginning of a line (e.g., U+FF0C FULLWIDTH COMMA), and +characters with the ``<'' category don't appear at the end of a line +(e.g., U+300A LEFT DOUBLE ANGLE BRACKET). You can view the category +set of a character using the commands @code{char-category-set} and +@code{category-set-mnemonics}, or by typing @kbd{C-u C-x =} with point +on the character and looking at the ``category'' section in the +report. You can add categories to a character using the command +@code{modify-category-entry}. @node Display Custom @section Customization of Display diff --git a/etc/NEWS b/etc/NEWS index 4271364258..d79cbe7611 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -93,11 +93,12 @@ specify 'cursor-type' to be '(box . SIZE)', the cursor becomes a hollow box if the point is on an image larger than 'SIZE' pixels in any dimension. ++++ ** New custom option 'word-wrap-by-category'. - -When word-wrap is enabled, this option allows Emacs to break lines -after more characters (instead of just whitespace characters), that -means word-wrapping for CJK text mixed with Latin text is improved. +When word-wrap is enabled, and this option is non-nil, that allows +Emacs to break lines after more characters than just whitespace +characters. In particular, this significantly improves word-wrapping +for CJK text mixed with Latin text. --- *** Improved language transliteration in Malayalam input methods. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 7ecb7b51be..f5b70e082a 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -102,7 +102,7 @@ display boolean "28.1" :set (lambda (symbol value) (set-default symbol value) - (when value (load "kinsoku.el")))) + (when value (require 'kinsoku)))) (selective-display-ellipses display boolean) (indicate-empty-lines fringe boolean) (indicate-buffer-boundaries diff --git a/lisp/international/kinsoku.el b/lisp/international/kinsoku.el index 54bf0e9531..4e9b6b015a 100644 --- a/lisp/international/kinsoku.el +++ b/lisp/international/kinsoku.el @@ -182,4 +182,6 @@ the context of text formatting." (aref (char-category-set (preceding-char)) ?<)) (kinsoku-shorter linebeg)))) +(provide 'kinsoku) + ;;; kinsoku.el ends here commit 0d1ca2ac3805443690f3bcb6877251d9b74902c9 Author: Yuan Fu Date: Tue May 26 22:47:27 2020 -0400 Improve word wrapping for CJK characters Note about the change around line 9257 and 23372: Before, the test for whitespace checks for can_wrap_before and can_wrap_after simutaniously. Now we separate these two checks, and the logic needs to change a little bit. However, when we don't enable the new wrapping feature, 'can_wrap_after' is equivalent to 'IT_DISPLAYING_WHITESPACE' and 'can_wrap_before' is equivalent to '!IT_DISPLAYING_WHITESPACE'. And the new logic is equivalent with the old one in that case. Old logic: if (whitespace) /* Which means can wrap after && can't wrap before. */ may_wrap = true; else if (may_wrap) /* aka (!whitespace && may_wrap) (set wrap point) * aka (can't wrap after && can wrap before may_wrap = false * && may_wrap) */ New logic: if (can_wrap_after) next_may_wrap = true else next_may_wrap = false; if (may_wrap && can_wrap_before) (set wrap point) /* Update may_wrap. */ may_wrap = next_may_wrap; * src/xdisp.c (it_char_has_category, char_can_wrap_before) (char_can_wrap_after): New functions. (move_it_in_display_line_to, display_line): Replace calls to 'IT_DISPLAYING_WHITESPACE' with either 'char_can_wrap_before' or 'char_can_wrap_after'. (word-wrap-by-category): New variable. * lisp/cus-start.el (minibuffer-prompt-properties--setter): Add 'word-wrap-by-category' as a customizable variable. * doc/emacs/display.texi (Visual Line Mode): Add a paragraph about the new 'word-wrap-by-category' feature. * etc/NEWS: Announce the change. diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 75ef520d62..d56a135b64 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1808,6 +1808,27 @@ logical lines, so having a fringe indicator for each wrapped line would be visually distracting. You can change this by customizing the variable @code{visual-line-fringe-indicators}. +@vindex word-wrap-by-category +@findex modify-category-entry +@findex char-category-set +@findex category-set-mnemonics + By default, Emacs only breaks lines after whitespace characters. +That strategy produces bad results when CJK and Latin text are mixed +together (because CJK characters don't use whitespace to separate +words). You can customize @code{word-wrap-by-category} to allow Emacs +to break lines after any character with ``|'' category +(@pxref{Categories,,, elisp, the Emacs Lisp Reference Manual}), which +includes CJK characters. Also, if this variable is set using +Customize, Emacs automatically loads kinsoku.el. When kinsoku.el is +loaded, Emacs respects kinsoku rules when breaking lines. That means +characters with the ``>'' category don't appear at the beginning of a +line (e.g., FULLWIDTH COMMA), and characters with the ``<'' category +don't appear at the end of a line (e.g., LEFT DOUBLE ANGLE BRACKET). +You can view the categories of a character by @code{char-category-set} +and @code{category-set-mnemonics}, or type @kbd{C-u C-x =} with point +on the character and look at the ``category'' section in the report. +You can add categories to a character by @code{modify-category-entry}. + @node Display Custom @section Customization of Display diff --git a/etc/NEWS b/etc/NEWS index 736892db8e..4271364258 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -93,6 +93,12 @@ specify 'cursor-type' to be '(box . SIZE)', the cursor becomes a hollow box if the point is on an image larger than 'SIZE' pixels in any dimension. +** New custom option 'word-wrap-by-category'. + +When word-wrap is enabled, this option allows Emacs to break lines +after more characters (instead of just whitespace characters), that +means word-wrapping for CJK text mixed with Latin text is improved. + --- *** Improved language transliteration in Malayalam input methods. Added a new Mozhi scheme. The inapplicable ITRANS scheme is now diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 6632687da4..7ecb7b51be 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -98,6 +98,11 @@ (ctl-arrow display boolean) (truncate-lines display boolean) (word-wrap display boolean) + (word-wrap-by-category + display boolean "28.1" + :set (lambda (symbol value) + (set-default symbol value) + (when value (load "kinsoku.el")))) (selective-display-ellipses display boolean) (indicate-empty-lines fringe boolean) (indicate-buffer-boundaries diff --git a/src/xdisp.c b/src/xdisp.c index ad03ac4605..a1f7706ead 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -447,6 +447,7 @@ along with GNU Emacs. If not, see . */ #include "termchar.h" #include "dispextern.h" #include "character.h" +#include "category.h" #include "buffer.h" #include "charset.h" #include "indent.h" @@ -508,6 +509,80 @@ static Lisp_Object list_of_error; && (*BYTE_POS_ADDR (IT_BYTEPOS (*it)) == ' ' \ || *BYTE_POS_ADDR (IT_BYTEPOS (*it)) == '\t')))) +/* These are the category sets we use. They are defined by + kinsoku.el and chracters.el. */ +#define NOT_AT_EOL '<' +#define NOT_AT_BOL '>' +#define LINE_BREAKABLE '|' + +static bool +it_char_has_category(struct it *it, int cat) +{ + int ch = 0; + if (it->what == IT_CHARACTER) + ch = it->c; + else if (STRINGP (it->string)) + ch = SREF (it->string, IT_STRING_BYTEPOS (*it)); + else if (it->s) + ch = it->s[IT_BYTEPOS (*it)]; + else if (IT_BYTEPOS (*it) < ZV_BYTE) + ch = *BYTE_POS_ADDR (IT_BYTEPOS (*it)); + + if (ch == 0) + return false; + else + return CHAR_HAS_CATEGORY (ch, cat); +} + +/* Return true if the current character allows wrapping before it. */ +static bool +char_can_wrap_before (struct it *it) +{ + if (!Vword_wrap_by_category) + return !IT_DISPLAYING_WHITESPACE (it); + + /* For CJK (LTR) text in RTL paragraph, EOL and BOL are flipped. + Because in RTL paragraph, each glyph is prepended to the last + one, effectively drawing right to left. */ + int not_at_bol; + if (it->glyph_row && it->glyph_row->reversed_p) + not_at_bol = NOT_AT_EOL; + else + not_at_bol = NOT_AT_BOL; + /* You cannot wrap before a space or tab because that way you'll + have space and tab at the beginning of next line. */ + return (!IT_DISPLAYING_WHITESPACE (it) + /* Can be at BOL. */ + && !it_char_has_category (it, not_at_bol)); +} + +/* Return true if the current character allows wrapping after it. */ +static bool +char_can_wrap_after (struct it *it) +{ + if (!Vword_wrap_by_category) + return IT_DISPLAYING_WHITESPACE (it); + + /* For CJK (LTR) text in RTL paragraph, EOL and BOL are flipped. + Because in RTL paragraph, each glyph is prepended to the last + one, effectively drawing right to left. */ + int not_at_eol; + if (it->glyph_row && it->glyph_row->reversed_p) + not_at_eol = NOT_AT_BOL; + else + not_at_eol = NOT_AT_EOL; + + return (IT_DISPLAYING_WHITESPACE (it) + /* Can break after && can be at EOL. */ + || (it_char_has_category (it, LINE_BREAKABLE) + && !it_char_has_category (it, not_at_eol))); +} + +#undef IT_DISPLAYING_WHITESPACE +#undef NOT_AT_EOL +#undef NOT_AT_BOL +#undef LINE_BREAKABLE + /* If all the conditions needed to print the fill column indicator are met, return the (nonnegative) column number, else return a negative value. */ @@ -9193,13 +9268,20 @@ move_it_in_display_line_to (struct it *it, { if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA) { - if (IT_DISPLAYING_WHITESPACE (it)) - may_wrap = true; - else if (may_wrap) + bool next_may_wrap = may_wrap; + /* Can we wrap after this character? */ + if (char_can_wrap_after (it)) + next_may_wrap = true; + else + next_may_wrap = false; + /* Can we wrap here? */ + if (may_wrap && char_can_wrap_before (it)) { /* We have reached a glyph that follows one or more - whitespace characters. If the position is - already found, we are done. */ + whitespace characters or a character that allows + wrapping after it. If this character allows + wrapping before it, save this position as a + wrapping point. */ if (atpos_it.sp >= 0) { RESTORE_IT (it, &atpos_it, atpos_data); @@ -9214,8 +9296,10 @@ move_it_in_display_line_to (struct it *it, } /* Otherwise, we can wrap here. */ SAVE_IT (wrap_it, *it, wrap_data); - may_wrap = false; + next_may_wrap = false; } + /* Update may_wrap for the next iteration. */ + may_wrap = next_may_wrap; } } @@ -9343,10 +9427,10 @@ move_it_in_display_line_to (struct it *it, { bool can_wrap = true; - /* If we are at a whitespace character - that barely fits on this screen line, - but the next character is also - whitespace, we cannot wrap here. */ + /* If the previous character says we can + wrap after it, but the current + character says we can't wrap before + it, then we can't wrap here. */ if (it->line_wrap == WORD_WRAP && wrap_it.sp >= 0 && may_wrap @@ -9358,7 +9442,7 @@ move_it_in_display_line_to (struct it *it, SAVE_IT (tem_it, *it, tem_data); set_iterator_to_next (it, true); if (get_next_display_element (it) - && IT_DISPLAYING_WHITESPACE (it)) + && !char_can_wrap_before (it)) can_wrap = false; RESTORE_IT (it, &tem_it, tem_data); } @@ -9437,19 +9521,18 @@ move_it_in_display_line_to (struct it *it, else IT_RESET_X_ASCENT_DESCENT (it); - /* If the screen line ends with whitespace, and we - are under word-wrap, don't use wrap_it: it is no - longer relevant, but we won't have an opportunity - to update it, since we are done with this screen - line. */ + /* If the screen line ends with whitespace (or + wrap-able character), and we are under word-wrap, + don't use wrap_it: it is no longer relevant, but + we won't have an opportunity to update it, since + we are done with this screen line. */ if (may_wrap && IT_OVERFLOW_NEWLINE_INTO_FRINGE (it) /* If the character after the one which set the - may_wrap flag is also whitespace, we can't - wrap here, since the screen line cannot be - wrapped in the middle of whitespace. - Therefore, wrap_it _is_ relevant in that - case. */ - && !(moved_forward && IT_DISPLAYING_WHITESPACE (it))) + may_wrap flag says we can't wrap before it, + we can't wrap here. Therefore, wrap_it + (previously found wrap-point) _is_ relevant + in that case. */ + && !(moved_forward && char_can_wrap_before (it))) { /* If we've found TO_X, go back there, as we now know the last word fits on this screen line. */ @@ -23322,9 +23405,14 @@ display_line (struct it *it, int cursor_vpos) if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA) { - if (IT_DISPLAYING_WHITESPACE (it)) - may_wrap = true; - else if (may_wrap) + bool next_may_wrap = may_wrap; + /* Can we wrap after this character? */ + if (char_can_wrap_after (it)) + next_may_wrap = true; + else + next_may_wrap = false; + /* Can we wrap here? */ + if (may_wrap && char_can_wrap_before (it)) { SAVE_IT (wrap_it, *it, wrap_data); wrap_x = x; @@ -23338,8 +23426,9 @@ display_line (struct it *it, int cursor_vpos) wrap_row_min_bpos = min_bpos; wrap_row_max_pos = max_pos; wrap_row_max_bpos = max_bpos; - may_wrap = false; } + /* Update may_wrap for the next iteration. */ + may_wrap = next_may_wrap; } } @@ -23463,14 +23552,18 @@ display_line (struct it *it, int cursor_vpos) /* If line-wrap is on, check if a previous wrap point was found. */ if (!IT_OVERFLOW_NEWLINE_INTO_FRINGE (it) - && wrap_row_used > 0 + && wrap_row_used > 0 /* Found. */ /* Even if there is a previous wrap point, continue the line here as usual, if (i) the previous character - was a space or tab AND (ii) the - current character is not. */ - && (!may_wrap - || IT_DISPLAYING_WHITESPACE (it))) + allows wrapping after it, AND (ii) + the current character allows wrapping + before it. Because this is a valid + break point, we can just continue to + the next line at here, there is no + need to wrap early at the previous + wrap point. */ + && (!may_wrap || !char_can_wrap_before (it))) goto back_to_wrap; /* Record the maximum and minimum buffer @@ -23498,13 +23591,16 @@ display_line (struct it *it, int cursor_vpos) /* If line-wrap is on, check if a previous wrap point was found. */ else if (wrap_row_used > 0 - /* Even if there is a previous wrap - point, continue the line here as - usual, if (i) the previous character - was a space or tab AND (ii) the - current character is not. */ - && (!may_wrap - || IT_DISPLAYING_WHITESPACE (it))) + /* Even if there is a previous + wrap point, continue the + line here as usual, if (i) + the previous character was a + space or tab AND (ii) the + current character is not, + AND (iii) the current + character allows wrapping + before it. */ + && (!may_wrap || !char_can_wrap_before (it))) goto back_to_wrap; } @@ -34662,6 +34758,23 @@ A value of nil means to respect the value of `truncate-lines'. If `word-wrap' is enabled, you might want to reduce this. */); Vtruncate_partial_width_windows = make_fixnum (50); + DEFVAR_BOOL("word-wrap-by-category", Vword_wrap_by_category, doc: /* + Non-nil means also wrap after characters of a certain category. +Normally when `word-wrap' is on, Emacs only breaks lines after +whitespace characters. When this option is turned on, Emacs also +breaks lines after characters that have the "|" category (defined in +characters.el). This is useful for allowing breaking after CJK +characters and improves the word-wrapping for CJK text mixed with +Latin text. + +If this variable is set using Customize, Emacs automatically loads +kinsoku.el. When kinsoku.el is loaded, Emacs respects kinsoku rules +when breaking lines. That means characters with the ">" category +don't appear at the beginning of a line (e.g., FULLWIDTH COMMA), and +characters with the "<" category don't appear at the end of a line +(e.g., LEFT DOUBLE ANGLE BRACKET). */); + Vword_wrap_by_category = false; + DEFVAR_LISP ("line-number-display-limit", Vline_number_display_limit, doc: /* Maximum buffer size for which line number should be displayed. If the buffer is bigger than this, the line number does not appear commit 065ab1ba44e891e18c6468c94e7e734e20a7903b Author: Lars Ingebrigtsen Date: Sat Aug 22 14:34:54 2020 -0700 Bind the time zone so that the tests work everywhere diff --git a/test/lisp/gnus/gnus-icalendar-tests.el b/test/lisp/gnus/gnus-icalendar-tests.el index 04a94e6897..48a9996e7a 100644 --- a/test/lisp/gnus/gnus-icalendar-tests.el +++ b/test/lisp/gnus/gnus-icalendar-tests.el @@ -48,7 +48,8 @@ (ert-deftest gnus-icalendar-parse () "test" - (let ((event (gnus-icalendar-tests--get-ical-event " + (let ((tz (getenv "TZ")) + (event (gnus-icalendar-tests--get-ical-event " BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 @@ -94,22 +95,29 @@ END:VEVENT END:VCALENDAR "))) - (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) - (should (not (gnus-icalendar-event:recurring-p event))) - (should (string= (gnus-icalendar-event:start event) "2020-12-08 15:00")) - (with-slots (organizer summary description location end-time uid rsvp participation-type) event - (should (string= organizer "liveintent.com_3bm6fh805bme9uoeliqcle1sag@group.calendar.google.com")) - (should (string= summary "Townhall | All Company Meeting")) - (should (string= description "In this meeting\, we will cover topics from product and engineering presentations and demos to new hire announcements to watching the late")) - (should (string= location "New York-22-Town Hall Space (250) [Chrome Box]")) - (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-12-08 16:00")) - (should (string= uid "iipdt88slddpeu7hheuu09sfmd@google.com")) - (should (not rsvp)) -(should (eq participation-type 'non-participant))))) + (unwind-protect + (progn + ;; Use this form so as not to rely on system tz database. + ;; Eg hydra.nixos.org. + (setenv "TZ" "CET-1CEST,M3.5.0/2,M10.5.0/3") + (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) + (should (not (gnus-icalendar-event:recurring-p event))) + (should (string= (gnus-icalendar-event:start event) "2020-12-08 15:00")) + (with-slots (organizer summary description location end-time uid rsvp participation-type) event + (should (string= organizer "liveintent.com_3bm6fh805bme9uoeliqcle1sag@group.calendar.google.com")) + (should (string= summary "Townhall | All Company Meeting")) + (should (string= description "In this meeting\, we will cover topics from product and engineering presentations and demos to new hire announcements to watching the late")) + (should (string= location "New York-22-Town Hall Space (250) [Chrome Box]")) + (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-12-08 16:00")) + (should (string= uid "iipdt88slddpeu7hheuu09sfmd@google.com")) + (should (not rsvp)) + (should (eq participation-type 'non-participant)))) + (setenv "TZ" tz)))) (ert-deftest gnus-icalendary-byday () "" - (let ((event (gnus-icalendar-tests--get-ical-event " + (let ((tz (getenv "TZ")) + (event (gnus-icalendar-tests--get-ical-event " BEGIN:VCALENDAR PRODID:Zimbra-Calendar-Provider VERSION:2.0 @@ -156,6 +164,11 @@ END:VALARM END:VEVENT END:VCALENDAR" (list "Mark Hershberger")))) + (unwind-protect + (progn + ;; Use this form so as not to rely on system tz database. + ;; Eg hydra.nixos.org. + (setenv "TZ" "CET-1CEST,M3.5.0/2,M10.5.0/3") (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) (should (gnus-icalendar-event:recurring-p event)) (should (string= (gnus-icalendar-event:recurring-interval event) "1")) @@ -174,8 +187,8 @@ END:VCALENDAR" (list "Mark Hershberger")))) <2020-07-27 15:00-15:30 +1w> <2020-07-28 15:00-15:30 +1w> <2020-07-29 15:00-15:30 +1w> -<2020-07-30 15:00-15:30 +1w>")) - )) +<2020-07-30 15:00-15:30 +1w>"))) + (setenv "TZ" tz)))) ;; (VCALENDAR nil commit 04b62489ca0823c04dfdc0a64c92d32ad14551cd Author: Lars Ingebrigtsen Date: Sat Aug 22 22:47:31 2020 +0200 Use lexical-binding and remove compat code diff --git a/lisp/progmodes/vera-mode.el b/lisp/progmodes/vera-mode.el index 3c9ced0291..8bde89e774 100644 --- a/lisp/progmodes/vera-mode.el +++ b/lisp/progmodes/vera-mode.el @@ -1,4 +1,4 @@ -;;; vera-mode.el --- major mode for editing Vera files +;;; vera-mode.el --- major mode for editing Vera files -*- lexical-binding: t; -*- ;; Copyright (C) 1997-2020 Free Software Foundation, Inc. @@ -33,9 +33,7 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Commentary: -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; This package provides a simple Emacs major mode for editing Vera code. ;; It includes the following features: @@ -44,38 +42,11 @@ ;; - Indentation ;; - Word/keyword completion ;; - Block commenting -;; - Works under GNU Emacs and XEmacs -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Documentation ;; See comment string of function `vera-mode' or type `C-h m' in Emacs. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Installation - -;; Prerequisites: GNU Emacs 20.X/21.X, XEmacs 20.X/21.X - -;; Put `vera-mode.el' into the `site-lisp' directory of your Emacs installation -;; or into an arbitrary directory that is added to the load path by the -;; following line in your Emacs start-up file (`.emacs'): - -;; (setq load-path (cons (expand-file-name "") load-path)) - -;; If you already have the compiled `vera-mode.elc' file, put it in the same -;; directory. Otherwise, byte-compile the source file: -;; Emacs: M-x byte-compile-file -> vera-mode.el -;; Unix: emacs -batch -q -no-site-file -f batch-byte-compile vera-mode.el - -;; Add the following lines to the `site-start.el' file in the `site-lisp' -;; directory of your Emacs installation or to your Emacs start-up file -;; (`.emacs'): - -;; (autoload 'vera-mode "vera-mode" "Vera Mode" t) -;; (setq auto-mode-alist (cons '("\\.vr[hi]?\\'" . vera-mode) auto-mode-alist)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ;;; Code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -90,16 +61,14 @@ (defcustom vera-basic-offset 2 "Amount of basic offset used for indentation." - :type 'integer - :group 'vera) + :type 'integer) (defcustom vera-underscore-is-part-of-word nil "Non-nil means consider the underscore character `_' as part of word. An identifier containing underscores is then treated as a single word in select and move operations. All parts of an identifier separated by underscore are treated as single words otherwise." - :type 'boolean - :group 'vera) + :type 'boolean) (make-obsolete-variable 'vera-underscore-is-part-of-word 'superword-mode "24.4") @@ -110,8 +79,7 @@ else if not at beginning of line then insert tab, else if last command was a `TAB' or `RET' then dedent one step, else indent current line. If nil, TAB always indents current line." - :type 'boolean - :group 'vera) + :type 'boolean) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -125,9 +93,6 @@ If nil, TAB always indents current line." (let ((map (make-sparse-keymap))) ;; Backspace/delete key bindings. (define-key map [backspace] 'backward-delete-char-untabify) - (unless (boundp 'delete-key-deletes-forward) ; XEmacs variable - (define-key map [delete] 'delete-char) - (define-key map [(meta delete)] 'kill-word)) ;; Standard key bindings. (define-key map "\M-e" 'vera-forward-statement) (define-key map "\M-a" 'vera-backward-statement) @@ -227,9 +192,7 @@ If nil, TAB always indents current line." (modify-syntax-entry ?\{ "(}" syntax-table) (modify-syntax-entry ?\} "){" syntax-table) ;; comment - (if (featurep 'xemacs) - (modify-syntax-entry ?\/ ". 1456" syntax-table) ; XEmacs - (modify-syntax-entry ?\/ ". 124b" syntax-table)) ; Emacs + (modify-syntax-entry ?\/ ". 124b" syntax-table) (modify-syntax-entry ?\* ". 23" syntax-table) ;; newline and CR (modify-syntax-entry ?\n "> b" syntax-table) @@ -314,8 +277,6 @@ Key bindings: ;; initialize font locking (set (make-local-variable 'font-lock-defaults) '(vera-font-lock-keywords nil nil ((?\_ . "w")))) - ;; add menu (XEmacs) - (easy-menu-add vera-mode-menu) ;; miscellaneous (message "Vera Mode %s. Type C-c C-h for documentation." vera-version)) @@ -542,12 +503,6 @@ Key bindings: ) "List of Vera-RVM predefined constants.") -;; `regexp-opt' undefined (`xemacs-devel' not installed) -(unless (fboundp 'regexp-opt) - (defun regexp-opt (strings &optional paren) - (let ((open (if paren "\\(" "")) (close (if paren "\\)" ""))) - (concat open (mapconcat 'regexp-quote strings "\\|") close)))) - (defconst vera-keywords-regexp (concat "\\<\\(" (regexp-opt vera-keywords) "\\)\\>") "Regexp for Vera keywords.") @@ -796,10 +751,7 @@ This function does not modify point or mark." (defun vera-skip-forward-literal () "Skip forward literal and return t if within one." - (let ((state (save-excursion - (if (fboundp 'syntax-ppss) - (syntax-ppss) - (parse-partial-sexp (point-min) (point)))))) + (let ((state (save-excursion (syntax-ppss)))) (when (nth 8 state) ;; Inside a string or comment. (goto-char (nth 8 state)) @@ -814,10 +766,7 @@ This function does not modify point or mark." (defun vera-skip-backward-literal () "Skip backward literal and return t if within one." - (let ((state (save-excursion - (if (fboundp 'syntax-ppss) - (syntax-ppss) - (parse-partial-sexp (point-min) (point)))))) + (let ((state (save-excursion (syntax-ppss)))) (when (nth 8 state) ;; Inside a string or comment. (goto-char (nth 8 state)) @@ -1232,6 +1181,8 @@ Calls `indent-region' for whole buffer." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; electrifications +(defvar hippie-expand-only-buffers) + (defun vera-electric-tab (&optional prefix) "Do what I mean (indent, expand, tab, change indent, etc..). If preceding character is part of a word or a paren then `hippie-expand', @@ -1243,7 +1194,7 @@ If `vera-intelligent-tab' is nil, always indent line." (interactive "*P") (if vera-intelligent-tab (progn - (cond ((and (not (featurep 'xemacs)) (use-region-p)) + (cond ((use-region-p) (vera-indent-region (region-beginning) (region-end) nil)) ((memq (char-syntax (preceding-char)) '(?w ?_)) (let ((case-fold-search t) commit 1002852f1aecb5e2568c91e2ca293f0ebfa1c9c9 Author: Eli Zaretskii Date: Sat Aug 22 19:14:35 2020 +0300 Fix documentation of a recent change in Dired * etc/NEWS: * doc/emacs/dired.texi (Dired Enter): Fix the text describing 'dired-maybe-use-globstar'. * lisp/dired.el (dired-maybe-use-globstar): Add :version. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 39caab086c..19aaca962d 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -79,23 +79,28 @@ The former lists all the files with extension @samp{.el} in directory @samp{foo}. The latter lists the files with extension @samp{.el} in all the subdirectories of @samp{foo}. -When the system shell supports globstar and it's enabled, then you -can use recursive globbing: +@cindex globstar, in Dired +On Posix systems, when the system shell supports @dfn{globstar}, a +recursive globbing feature, and that support is enabled, you can use +recursive globbing in Dired: @example C-x d ~/foo/**/*.el @key{RET} @end example -This command lists all the files with extension @samp{.el} descending -recursively in all the subdirectories of @samp{foo}. Note that there -are small differences in the implementation of globstar between shells. -Check your shell manual to know the expected behavior. +This command produces a directory listing with all the files with +extension @samp{.el}, descending recursively in all the subdirectories +of @samp{foo}. Note that there are small differences in the +implementation of globstar between different shells. Check your shell +manual to know the expected behavior. @vindex dired-maybe-use-globstar @vindex dired-enable-globstar-in-shell -If the shell supports globstar and disables it by default, you -can still enable this feature with @code{dired-maybe-use-globstar} if -the shell is included in @code{dired-enable-globstar-in-shell}. +If the shell supports globstar, but that support is disabled by +default, you can still let Dired use this feature by customizing +@code{dired-maybe-use-globstar} to a non-@code{nil} value; then Dired +will enable globstar for those shells for which it knows how (see +@code{dired-enable-globstar-in-shell} for the list of those shells). The usual history and completion commands can be used in the minibuffer; in particular, @kbd{M-n} puts the name of the visited file (if any) in diff --git a/etc/NEWS b/etc/NEWS index a47b6e10b1..736892db8e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -230,9 +230,12 @@ time zones will use a form like "+0100" instead of "CET". +++ *** New user option 'dired-maybe-use-globstar'. -If set, enables globstar in shells that support this feature. The new -variable 'dired-enable-globstar-in-shell' lists which shells can have -globstar enabled. +If set, enables globstar (recursive globbing) in shells that support +this feature, but turn it off by default. This allows producing +directory listings with files matching a wildcard in all the +subdirectories of a given directory. The new variable +'dired-enable-globstar-in-shell' lists which shells can have globstar +enabled, and how to enable it. +++ *** New user option 'dired-copy-dereference'. diff --git a/lisp/dired.el b/lisp/dired.el index d22d52ce62..94d3befda8 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -88,7 +88,8 @@ Note that the implementations of globstar have small differences between shells. You must check your shell documentation to see what to expect." :type 'boolean - :group 'dired) + :group 'dired + :version "28.1") (defconst dired-enable-globstar-in-shell '(("ksh" . "set -G") commit 2e5198781c42385d9c1723e137f3f059b6df363f Author: Glenn Morris Date: Sat Aug 22 16:58:27 2020 +0100 ; Copyright fix Author has assignment diff --git a/test/lisp/gnus/gnus-icalendar-tests.el b/test/lisp/gnus/gnus-icalendar-tests.el index 66b8c8099a..04a94e6897 100644 --- a/test/lisp/gnus/gnus-icalendar-tests.el +++ b/test/lisp/gnus/gnus-icalendar-tests.el @@ -1,22 +1,24 @@ ;;; gnus-icalendar-tests.el --- tests -*- lexical-binding: t; -*- -;; Copyright (C) 2020 Jan Tatarik +;; Copyright (C) 2020 Free Software Foundation, Inc. ;; Author: Jan Tatarik ;; Keywords: -;; This program is free software; you can redistribute it and/or modify +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This program is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: commit 83ade9e4e2d8f9bd5b7cfc35faa1d23d2487974f Author: Eli Zaretskii Date: Sat Aug 22 18:57:57 2020 +0300 ; * lisp/progmodes/grep.el (grep-find-command): Fix typos. diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index 287f43d358..c71a90344f 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el @@ -170,9 +170,9 @@ In interactive usage, the actual value of this variable is set up by `grep-compute-defaults'; to change the default value, use \\[customize] or call the function `grep-apply-setting'. -This variable can either be a string, or a cons on the +This variable can either be a string, or a cons of the form (COMMAND . POSITION). In the latter case, COMMAND will be -used as the default command, and point will be placed on POSITION +used as the default command, and point will be placed at POSITION for easier editing." :type '(choice string (cons string integer) commit 4102fe1e91e46fb2fde9ac2f8d279a942d0a6e6f Author: Lars Ingebrigtsen Date: Sat Aug 22 17:57:35 2020 +0200 Clarify inline-letevals in the manual * doc/lispref/functions.texi (Inline Functions): Try to clarify what inline-letevals really does, and how it differs from `let' (bug#31052). diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 2898cb4d2b..26b212d05e 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -2163,15 +2163,24 @@ the backquote (@pxref{Backquote}), but quotes code and accepts only @end defmac @defmac inline-letevals (bindings@dots{}) body@dots{} -This is similar to @code{let} (@pxref{Local Variables}): it sets up -local variables as specified by @var{bindings}, and then evaluates -@var{body} with those bindings in effect. Each element of -@var{bindings} should be either a symbol or a list of the form -@w{@code{(@var{var} @var{expr})}}; the result is to evaluate -@var{expr} and bind @var{var} to the result. The tail of -@var{bindings} can be either @code{nil} or a symbol which should hold -a list of arguments, in which case each argument is evaluated, and the -symbol is bound to the resulting list. +This provides a convenient way to ensure that the arguments to an +inlined function are evaluated exactly once, as well as to create +local variables. + +It's similar to @code{let} (@pxref{Local Variables}): It sets up local +variables as specified by @var{bindings}, and then evaluates +@var{body} with those bindings in effect. + +Each element of @var{bindings} should be either a symbol or a list of +the form @w{@code{(@var{var} @var{expr})}}; the result is to evaluate +@var{expr} and bind @var{var} to the result. However, when an element +of @var{bindings} is just a symbol @var{var}, the result of evaluating +@var{var} is re-bound to @var{var} (which is quite different from the +way @code{let} works). + +The tail of @var{bindings} can be either @code{nil} or a symbol which +should hold a list of arguments, in which case each argument is +evaluated, and the symbol is bound to the resulting list. @end defmac @defmac inline-const-p expression commit 29bb72f0432c7b89d2f7dec5022c582f8e10ada9 Author: Tino Calancha Date: Sat Aug 22 17:39:16 2020 +0200 Handle globstar in dired Allow user to enable globstar when the shell support it and disable it by default (e.g. bash). * lisp/dired.el (dired-maybe-use-globstar): New user option. (dired-enable-globstar-in-shell): New variable. (dired-insert-directory): if `dired-maybe-use-globstar' is non-nil and the shell supports globstar, then enable it. * doc/emacs/dired.texi: Document feature. ; * etc/NEWS: Add entry. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index de449e31c3..39caab086c 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -79,6 +79,24 @@ The former lists all the files with extension @samp{.el} in directory @samp{foo}. The latter lists the files with extension @samp{.el} in all the subdirectories of @samp{foo}. +When the system shell supports globstar and it's enabled, then you +can use recursive globbing: + +@example +C-x d ~/foo/**/*.el @key{RET} +@end example + +This command lists all the files with extension @samp{.el} descending +recursively in all the subdirectories of @samp{foo}. Note that there +are small differences in the implementation of globstar between shells. +Check your shell manual to know the expected behavior. + +@vindex dired-maybe-use-globstar +@vindex dired-enable-globstar-in-shell +If the shell supports globstar and disables it by default, you +can still enable this feature with @code{dired-maybe-use-globstar} if +the shell is included in @code{dired-enable-globstar-in-shell}. + The usual history and completion commands can be used in the minibuffer; in particular, @kbd{M-n} puts the name of the visited file (if any) in the minibuffer (@pxref{Minibuffer History}). diff --git a/etc/NEWS b/etc/NEWS index b4833473ec..a47b6e10b1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -228,6 +228,12 @@ time zones will use a form like "+0100" instead of "CET". ** Dired ++++ +*** New user option 'dired-maybe-use-globstar'. +If set, enables globstar in shells that support this feature. The new +variable 'dired-enable-globstar-in-shell' lists which shells can have +globstar enabled. + +++ *** New user option 'dired-copy-dereference'. If set to non-nil, Dired will dereference symbolic links when copying. diff --git a/lisp/dired.el b/lisp/dired.el index 77bb6cfa9c..d22d52ce62 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -77,6 +77,26 @@ If nil, `dired-listing-switches' is used." :type '(choice (const :tag "Use dired-listing-switches" nil) (string :tag "Switches"))) +(defcustom dired-maybe-use-globstar nil + "If non-nil, enable globstar if the shell supports it. +Some shells enable this feature by default (e.g. zsh or fish). + +See `dired-enable-globstar-in-shell' for a list of shells +that support globstar and disable it by default. + +Note that the implementations of globstar have small differences +between shells. You must check your shell documentation to see +what to expect." + :type 'boolean + :group 'dired) + +(defconst dired-enable-globstar-in-shell + '(("ksh" . "set -G") + ("bash" . "shopt -s globstar")) + "Alist of (SHELL . COMMAND), where COMMAND enables globstar in SHELL. +If `dired-maybe-use-globstar' is non-nil, then `dired-insert-directory' +checks this alist to enable globstar in the shell subprocess.") + (defcustom dired-chown-program (purecopy (cond ((executable-find "chown") "chown") ((file-executable-p "/usr/sbin/chown") "/usr/sbin/chown") @@ -1470,6 +1490,13 @@ see `dired-use-ls-dired' for more details.") (executable-find explicit-shell-file-name)) (executable-find "sh"))) (switch (if remotep "-c" shell-command-switch))) + ;; Enable globstar + (when-let ((globstar dired-maybe-use-globstar) + (enable-it + (assoc-default + (file-truename sh) dired-enable-globstar-in-shell + (lambda (reg shell) (string-match reg shell))))) + (setq script (format "%s; %s" enable-it script))) (unless (zerop (process-file sh nil (current-buffer) nil switch script)) commit e63a0c466c10d0b9de6a28e2ddd412226087bd27 Author: Alan Third Date: Sat Aug 22 16:31:02 2020 +0100 Fix NS build failure * src/thread.c: xgselect isn't used with NS, even when HAVE_GLIB is defined. diff --git a/src/thread.c b/src/thread.c index b4d8a53cf6..7ab1e6de1f 100644 --- a/src/thread.c +++ b/src/thread.c @@ -28,7 +28,7 @@ along with GNU Emacs. If not, see . */ #include "pdumper.h" #include "keyboard.h" -#ifdef HAVE_GLIB +#if defined HAVE_GLIB && ! defined (HAVE_NS) #include #else #define release_select_lock() do { } while (0) commit 96e5d8ce0d92eacbd7e237e50f0b572bb56e6d66 Author: Alan Third Date: Thu Aug 20 23:45:38 2020 +0100 Extend NSString further and use the new methods * src/nsfns.m (ns_set_icon_name): (ns_set_name): (ns_set_represented_filename): (ns_implicitly_set_icon_type): (ns_set_icon_type): (ns_appkit_version_str): (Fx_create_frame): (Fns_read_file_name): (Fns_get_resource): (Fns_set_resource): (Fns_list_colors): (Fns_perform_service): (ns_do_applescript): Use the new NSString methods. ([NSString stringWithLispString:]): Fix the surrogate algorithm. ([NSString lispString]): New method. * src/nsterm.h (NSString): Add new method. * src/nsterm.m ([EmacsApp openFile:]): ([EmacsApp requestService:userData:error:]): ([EmacsApp fulfillService:withArg:]): ([EmacsView changeFont:]): ([EmacsView setMarkedText:selectedRange:]): ([EmacsView initFrameFromEmacs:]): ([EmacsView performDragOperation:]): ([EmacsView performDragOperation:]): ([EmacsView performDragOperation:]): ([EmacsWindow accessibilityAttributeValue:]): Use the new NSString methods. diff --git a/src/nsfns.m b/src/nsfns.m index 5fca15588d..c7956497c4 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -390,12 +390,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. /* Don't change the name if it's already NAME. */ if ([[view window] miniwindowTitle] && ([[[view window] miniwindowTitle] - isEqualToString: [NSString stringWithUTF8String: - SSDATA (arg)]])) + isEqualToString: [NSString stringWithLispString:arg]])) return; [[view window] setMiniwindowTitle: - [NSString stringWithUTF8String: SSDATA (arg)]]; + [NSString stringWithLispString:arg]]; } static void @@ -437,7 +436,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. return; if (NILP (name)) - name = build_string ([ns_app_name UTF8String]); + name = [ns_app_name lispString]; else CHECK_STRING (name); @@ -476,7 +475,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. { encoded_filename = ENCODE_UTF_8 (filename); - fstr = [NSString stringWithUTF8String: SSDATA (encoded_filename)]; + fstr = [NSString stringWithLispString:encoded_filename]; if (fstr == nil) fstr = @""; } else @@ -723,7 +722,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. block_input (); pool = [[NSAutoreleasePool alloc] init]; if (f->output_data.ns->miniimage - && [[NSString stringWithUTF8String: SSDATA (f->name)] + && [[NSString stringWithLispString:f->name] isEqualToString: [(NSImage *)f->output_data.ns->miniimage name]]) { [pool release]; @@ -748,7 +747,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. if (SYMBOLP (elt) && EQ (elt, Qt) && SSDATA (f->name)[0] == '/') { NSString *str - = [NSString stringWithUTF8String: SSDATA (f->name)]; + = [NSString stringWithLispString:f->name]; if ([[NSFileManager defaultManager] fileExistsAtPath: str]) image = [[[NSWorkspace sharedWorkspace] iconForFile: str] retain]; } @@ -760,8 +759,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. image = [EmacsImage allocInitFromFile: XCDR (elt)]; if (image == nil) image = [[NSImage imageNamed: - [NSString stringWithUTF8String: - SSDATA (XCDR (elt))]] retain]; + [NSString stringWithLispString:XCDR (elt)]] retain]; } } @@ -805,8 +803,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. image = [EmacsImage allocInitFromFile: arg]; if (image == nil) - image =[NSImage imageNamed: [NSString stringWithUTF8String: - SSDATA (arg)]]; + image =[NSImage imageNamed: [NSString stringWithLispString:arg]]; if (image == nil) { @@ -840,20 +837,18 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. static Lisp_Object ns_appkit_version_str (void) { - char tmp[256]; + NSString *tmp; #ifdef NS_IMPL_GNUSTEP - sprintf(tmp, "gnustep-gui-%s", Xstr(GNUSTEP_GUI_VERSION)); + tmp = [NSString stringWithFormat:@"gnustep-gui-%s", Xstr(GNUSTEP_GUI_VERSION)]; #elif defined (NS_IMPL_COCOA) - NSString *osversion - = [[NSProcessInfo processInfo] operatingSystemVersionString]; - sprintf(tmp, "appkit-%.2f %s", - NSAppKitVersionNumber, - [osversion UTF8String]); + tmp = [NSString stringWithFormat:@"appkit-%.2f %@", + NSAppKitVersionNumber, + [[NSProcessInfo processInfo] operatingSystemVersionString]]; #else - tmp = "ns-unknown"; + tmp = [NSString initWithUTF8String:@"ns-unknown"]; #endif - return build_string (tmp); + return [tmp lispString]; } @@ -1157,7 +1152,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. be set. */ if (EQ (name, Qunbound) || NILP (name) || ! STRINGP (name)) { - fset_name (f, build_string ([ns_app_name UTF8String])); + fset_name (f, [ns_app_name lispString]); f->explicit_name = 0; } else @@ -1598,12 +1593,12 @@ Frames are listed from topmost (first) to bottommost (last). */) Lisp_Object fname = Qnil; NSString *promptS = NILP (prompt) || !STRINGP (prompt) ? nil : - [NSString stringWithUTF8String: SSDATA (prompt)]; + [NSString stringWithLispString:prompt]; NSString *dirS = NILP (dir) || !STRINGP (dir) ? - [NSString stringWithUTF8String: SSDATA (BVAR (current_buffer, directory))] : - [NSString stringWithUTF8String: SSDATA (dir)]; + [NSString stringWithLispString:BVAR (current_buffer, directory)] : + [NSString stringWithLispString:dir]; NSString *initS = NILP (init) || !STRINGP (init) ? nil : - [NSString stringWithUTF8String: SSDATA (init)]; + [NSString stringWithLispString:init]; NSEvent *nxev; check_window_system (NULL); @@ -1679,7 +1674,7 @@ Frames are listed from topmost (first) to bottommost (last). */) { NSString *str = ns_filename_from_panel (panel); if (! str) str = ns_directory_from_panel (panel); - if (str) fname = build_string ([str UTF8String]); + if (str) fname = [str lispString]; } [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow]; @@ -1709,7 +1704,7 @@ Frames are listed from topmost (first) to bottommost (last). */) check_window_system (NULL); if (NILP (owner)) - owner = build_string([ns_app_name UTF8String]); + owner = [ns_app_name lispString]; CHECK_STRING (name); value = ns_get_defaults_value (SSDATA (name)); @@ -1728,20 +1723,19 @@ Frames are listed from topmost (first) to bottommost (last). */) { check_window_system (NULL); if (NILP (owner)) - owner = build_string ([ns_app_name UTF8String]); + owner = [ns_app_name lispString]; CHECK_STRING (name); if (NILP (value)) { [[NSUserDefaults standardUserDefaults] removeObjectForKey: - [NSString stringWithUTF8String: SSDATA (name)]]; + [NSString stringWithLispString:name]]; } else { CHECK_STRING (value); [[NSUserDefaults standardUserDefaults] setObject: - [NSString stringWithUTF8String: SSDATA (value)] - forKey: [NSString stringWithUTF8String: - SSDATA (name)]]; + [NSString stringWithLispString:value] + forKey: [NSString stringWithLispString:name]]; } return Qnil; @@ -2033,7 +2027,7 @@ Frames are listed from topmost (first) to bottommost (last). */) NSEnumerator *cnames = [[clist allKeys] reverseObjectEnumerator]; NSString *cname; while ((cname = [cnames nextObject])) - list = Fcons (build_string ([cname UTF8String]), list); + list = Fcons ([cname lispString], list); /* for (i = [[clist allKeys] count] - 1; i >= 0; i--) list = Fcons (build_string ([[[clist allKeys] objectAtIndex: i] UTF8String]), list); */ @@ -2081,13 +2075,11 @@ Frames are listed from topmost (first) to bottommost (last). */) { id pb; NSString *svcName; - char *utfStr; CHECK_STRING (service); check_window_system (NULL); - utfStr = SSDATA (service); - svcName = [NSString stringWithUTF8String: utfStr]; + svcName = [NSString stringWithLispString:service]; pb =[NSPasteboard pasteboardWithUniqueName]; ns_string_to_pasteboard (pb, send); @@ -2117,7 +2109,7 @@ Frames are listed from topmost (first) to bottommost (last). */) NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource: - [NSString stringWithUTF8String: SSDATA (script)]]; + [NSString stringWithLispString:script]]; returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; [scriptObject release]; @@ -2140,7 +2132,7 @@ Frames are listed from topmost (first) to bottommost (last). */) { desc = [returnDescriptor coerceToDescriptorType: typeUTF8Text]; if (desc) - *result = build_string([[desc stringValue] UTF8String]); + *result = [[desc stringValue] lispString]; } else { @@ -3055,8 +3047,8 @@ handled fairly well by the NS libraries (displayed with distinct *d++ = c; else if (c <= 0x10ffff) { - *d++ = 0xd800 + (c & 0x3ff); - *d++ = 0xdc00 + ((c - 0x10000) >> 10); + *d++ = 0xd800 + ((c - 0x10000) >> 10); + *d++ = 0xdc00 + (c & 0x3ff); } else *d++ = 0xfffd; /* Not valid for UTF-16. */ @@ -3066,6 +3058,12 @@ handled fairly well by the NS libraries (displayed with distinct xfree (chars); return str; } + +/* Make a Lisp string from an NSString. */ +- (Lisp_Object)lispString +{ + return build_string ([self UTF8String]); +} @end /* ========================================================================== diff --git a/src/nsterm.h b/src/nsterm.h index ab868ed344..b56bcad4dc 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -364,6 +364,7 @@ typedef id instancetype; @interface NSString (EmacsString) + (NSString *)stringWithLispString:(Lisp_Object)string; +- (Lisp_Object)lispString; @end /* ========================================================================== diff --git a/src/nsterm.m b/src/nsterm.m index 98c5b69d68..26059ab67c 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -5909,7 +5909,7 @@ - (BOOL) openFile: (NSString *)fileName emacs_event->kind = NS_NONKEY_EVENT; emacs_event->code = KEY_NS_OPEN_FILE_LINE; - ns_input_file = append2 (ns_input_file, build_string ([fileName UTF8String])); + ns_input_file = append2 (ns_input_file, [fileName lispString]); ns_input_line = Qnil; /* can be start or cons start,end */ emacs_event->modifiers =0; EV_TRAILER (theEvent); @@ -6273,8 +6273,7 @@ - (void)requestService: (NSPasteboard *)pboard error: (NSString **)error { [ns_pending_service_names addObject: userData]; - [ns_pending_service_args addObject: [NSString stringWithUTF8String: - SSDATA (ns_string_from_pasteboard (pboard))]]; + [ns_pending_service_args addObject: [NSString stringWithLispString:ns_string_from_pasteboard (pboard)]]; } @@ -6291,8 +6290,8 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg emacs_event->kind = NS_NONKEY_EVENT; emacs_event->code = KEY_NS_SPI_SERVICE_CALL; - ns_input_spi_name = build_string ([name UTF8String]); - ns_input_spi_arg = build_string ([arg UTF8String]); + ns_input_spi_name = [name lispString]; + ns_input_spi_arg = [arg lispString]; emacs_event->modifiers = EV_MODIFIERS (theEvent); EV_TRAILER (theEvent); @@ -6374,7 +6373,7 @@ - (void)changeFont: (id)sender size = [newFont pointSize]; ns_input_fontsize = make_fixnum (lrint (size)); - ns_input_font = build_string ([[newFont familyName] UTF8String]); + ns_input_font = [[newFont familyName] lispString]; EV_TRAILER (e); } } @@ -6685,7 +6684,7 @@ - (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange processingCompose = YES; [workingText release]; workingText = [str copy]; - ns_working_text = build_string ([workingText UTF8String]); + ns_working_text = [workingText lispString]; emacs_event->kind = NS_TEXT_EVENT; emacs_event->code = KEY_NS_PUT_WORKING_TEXT; @@ -7605,7 +7604,7 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f tem = f->icon_name; if (!NILP (tem)) [win setMiniwindowTitle: - [NSString stringWithUTF8String: SSDATA (tem)]]; + [NSString stringWithLispString:tem]]; if (FRAME_PARENT_FRAME (f) != NULL) { @@ -8609,7 +8608,7 @@ -(BOOL)performDragOperation: (id ) sender fenum = [files objectEnumerator]; while ( (file = [fenum nextObject]) ) - strings = Fcons (build_string ([file UTF8String]), strings); + strings = Fcons ([file lispString], strings); } else if ([type isEqualToString: NSURLPboardType]) { @@ -8618,7 +8617,7 @@ -(BOOL)performDragOperation: (id ) sender type_sym = Qurl; - strings = list1 (build_string ([[url absoluteString] UTF8String])); + strings = list1 ([[url absoluteString] lispString]); } else if ([type isEqualToString: NSStringPboardType] || [type isEqualToString: NSTabularTextPboardType]) @@ -8630,7 +8629,7 @@ -(BOOL)performDragOperation: (id ) sender type_sym = Qnil; - strings = list1 (build_string ([data UTF8String])); + strings = list1 ([data lispString]); } else { @@ -8802,9 +8801,7 @@ - (id)accessibilityAttributeValue:(NSString *)attribute } if (STRINGP (str)) { - const char *utfStr = SSDATA (str); - NSString *nsStr = [NSString stringWithUTF8String: utfStr]; - return nsStr; + return [NSString stringWithLispString:str]; } } commit 811f5890f3ad9b35464c5ccd818091abcaf6803d Author: Lars Ingebrigtsen Date: Sat Aug 22 16:27:17 2020 +0200 Doc string (and defcustom type) fix for grep-find-command * lisp/progmodes/grep.el (grep-find-command): Add the cons type to the defcustom, and document it (bug#36113). (It has always been a valid value for the variable.) diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index 7731be5965..287f43d358 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el @@ -168,8 +168,14 @@ This is done to disambiguate file names in `grep's output." "The default find command for \\[grep-find]. In interactive usage, the actual value of this variable is set up by `grep-compute-defaults'; to change the default value, use -\\[customize] or call the function `grep-apply-setting'." +\\[customize] or call the function `grep-apply-setting'. + +This variable can either be a string, or a cons on the +form (COMMAND . POSITION). In the latter case, COMMAND will be +used as the default command, and point will be placed on POSITION +for easier editing." :type '(choice string + (cons string integer) (const :tag "Not Set" nil)) :set #'grep-apply-setting) commit 3183259481992af4e5dc2b31dee1ad844a054e5a Author: Lars Ingebrigtsen Date: Sat Aug 22 15:42:17 2020 +0200 nndoc minor clean-up * lisp/gnus/nndoc.el (nndoc-possibly-change-buffer): Erase the buffer before changing multibyteness. diff --git a/lisp/gnus/nndoc.el b/lisp/gnus/nndoc.el index 8960b3d7aa..81431270d7 100644 --- a/lisp/gnus/nndoc.el +++ b/lisp/gnus/nndoc.el @@ -352,8 +352,8 @@ from the document.") nndoc-group-alist) (setq nndoc-dissection-alist nil) (with-current-buffer nndoc-current-buffer - (set-buffer-multibyte nil) (erase-buffer) + (set-buffer-multibyte nil) (condition-case error (if (and (stringp nndoc-address) (string-match nndoc-binary-file-names nndoc-address)) commit 5c715113efe91f4feb03e37b8b1287cd5afe9e94 Author: Jan Tatarik Date: Sat Aug 22 15:39:17 2020 +0200 gnus-icalendar does not understand multiple repeating days * lisp/gnus/gnus-icalendar.el (gnus-icalendar-event:recurring-days): New function (bug#39782). (gnus-icalendar-event:org-timestamp): New function. (gnus-icalendar--find-day): Use them. (gnus-icalendar-event--org-timestamp): Ditto. diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el index ab121f1f9e..f13d4dec01 100644 --- a/lisp/gnus/gnus-icalendar.el +++ b/lisp/gnus/gnus-icalendar.el @@ -138,6 +138,22 @@ (or (match-string 1 rrule) default-interval))) +(cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event)) + "Return, when available, the week day numbers on which the EVENT recurs." + (let ((rrule (gnus-icalendar-event:recur event)) + (weekday-map '(("SU" . 0) + ("MO" . 1) + ("TU" . 2) + ("WE" . 3) + ("TH" . 4) + ("FR" . 5) + ("SA" . 6)))) + (when (string-match "BYDAY=\\([^;]+\\)" rrule) + (let ((bydays (split-string (match-string 1 rrule) ","))) + (seq-map + (lambda (x) (cdr (assoc x weekday-map))) + (seq-filter (lambda (x) (string-match "^[A-Z]\\{2\\}$" x)) bydays)))))) + (cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event)) (format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event))) @@ -401,21 +417,26 @@ Return nil for non-recurring EVENT." (when org-freq (format "+%s%s" (gnus-icalendar-event:recurring-interval event) org-freq))))) -(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event)) - "Build `org-mode' timestamp from EVENT start/end dates and recurrence info." - (let* ((start (gnus-icalendar-event:start-time event)) - (end (gnus-icalendar-event:end-time event)) - (start-date (format-time-string "%Y-%m-%d" start)) +(defun gnus-icalendar--find-day (start-date end-date day) + (let ((time-1-day 86400)) + (if (= (decoded-time-weekday (decode-time start-date)) + day) + (list start-date end-date) + (gnus-icalendar--find-day (time-add start-date time-1-day) + (time-add end-date time-1-day) + day)))) + +(defun gnus-icalendar-event--org-timestamp (start end org-repeat) + (let* ((start-date (format-time-string "%Y-%m-%d" start)) (start-time (format-time-string "%H:%M" start)) (start-at-midnight (string= start-time "00:00")) (end-date (format-time-string "%Y-%m-%d" end)) (end-time (format-time-string "%H:%M" end)) (end-at-midnight (string= end-time "00:00")) (start-end-date-diff - (time-to-number-of-days (time-subtract - (org-time-string-to-time end-date) - (org-time-string-to-time start-date)))) - (org-repeat (gnus-icalendar-event:org-repeat event)) + (time-to-number-of-days + (time-subtract (org-time-string-to-time end-date) + (org-time-string-to-time start-date)))) (repeat (if org-repeat (concat " " org-repeat) "")) (time-1-day 86400)) @@ -446,7 +467,31 @@ Return nil for non-recurring EVENT." ;; A .:. - A .:. -> A .:.-.:. ;; A .:. - B .:. ((zerop start-end-date-diff) (format "<%s %s-%s%s>" start-date start-time end-time repeat)) - (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time))))) + (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time)))) + ) + +(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event)) + "Build `org-mode' timestamp from EVENT start/end dates and recurrence info." + ;; if org-repeat +1d or +1w and byday: generate one timestamp per + ;; byday, starting at start-date. Change +1d to +7d. + (let ((start (gnus-icalendar-event:start-time event)) + (end (gnus-icalendar-event:end-time event)) + (org-repeat (gnus-icalendar-event:org-repeat event)) + (recurring-days (gnus-icalendar-event:recurring-days event))) + (if (and (or (string= org-repeat "+1d") + (string= org-repeat "+1w")) + recurring-days) + (let ((repeat "+1w") + (dates (seq-sort-by + 'car + 'time-less-p + (seq-map (lambda (x) + (gnus-icalendar--find-day start end x)) + recurring-days)))) + (mapconcat (lambda (x) + (gnus-icalendar-event--org-timestamp (car x) (cadr x) + repeat)) dates "\n")) + (gnus-icalendar-event--org-timestamp start end org-repeat)))) (defun gnus-icalendar--format-summary-line (summary &optional location) (if location diff --git a/test/lisp/gnus/gnus-icalendar-tests.el b/test/lisp/gnus/gnus-icalendar-tests.el new file mode 100644 index 0000000000..66b8c8099a --- /dev/null +++ b/test/lisp/gnus/gnus-icalendar-tests.el @@ -0,0 +1,229 @@ +;;; gnus-icalendar-tests.el --- tests -*- lexical-binding: t; -*- + +;; Copyright (C) 2020 Jan Tatarik + +;; Author: Jan Tatarik +;; Keywords: + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(require 'ert) +(require 'gnus-icalendar) + + +(defun gnus-icalendar-tests--get-ical-event (ical-string &optional participant) + "Return gnus-icalendar event for ICAL-STRING." + (let (event) + (with-temp-buffer + (insert ical-string) + (setq event (gnus-icalendar-event-from-buffer (buffer-name) participant))) + event)) + +(defun icalendar-tests--get-ical-event (ical-string) + "Return iCalendar event for ICAL-STRING." + (save-excursion + (with-temp-buffer + (insert ical-string) + (goto-char (point-min)) + (car (icalendar--read-element nil nil))))) + +(ert-deftest gnus-icalendar-parse () + "test" + (let ((event (gnus-icalendar-tests--get-ical-event " +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=America/New_York:20201208T090000 +DTEND;TZID=America/New_York:20201208T100000 +DTSTAMP:20200728T182853Z +ORGANIZER;CN=Company Events:mailto:liveintent.com_3bm6fh805bme9uoeliqcle1sa + g@group.calendar.google.com +UID:iipdt88slddpeu7hheuu09sfmd@google.com +X-MICROSOFT-CDO-OWNERAPPTID:-362490173 +RECURRENCE-ID;TZID=America/New_York:20201208T091500 +CREATED:20200309T134939Z +DESCRIPTION:In this meeting\, we will cover topics from product and enginee + ring presentations and demos to new hire announcements to watching the late +LAST-MODIFIED:20200728T182852Z +LOCATION:New York-22-Town Hall Space (250) [Chrome Box] +SEQUENCE:4 +STATUS:CONFIRMED +SUMMARY:Townhall | All Company Meeting +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR +"))) + + (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) + (should (not (gnus-icalendar-event:recurring-p event))) + (should (string= (gnus-icalendar-event:start event) "2020-12-08 15:00")) + (with-slots (organizer summary description location end-time uid rsvp participation-type) event + (should (string= organizer "liveintent.com_3bm6fh805bme9uoeliqcle1sag@group.calendar.google.com")) + (should (string= summary "Townhall | All Company Meeting")) + (should (string= description "In this meeting\, we will cover topics from product and engineering presentations and demos to new hire announcements to watching the late")) + (should (string= location "New York-22-Town Hall Space (250) [Chrome Box]")) + (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-12-08 16:00")) + (should (string= uid "iipdt88slddpeu7hheuu09sfmd@google.com")) + (should (not rsvp)) +(should (eq participation-type 'non-participant))))) + +(ert-deftest gnus-icalendary-byday () + "" + (let ((event (gnus-icalendar-tests--get-ical-event " +BEGIN:VCALENDAR +PRODID:Zimbra-Calendar-Provider +VERSION:2.0 +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:America/New_York +BEGIN:STANDARD +DTSTART:16010101T020000 +TZOFFSETTO:-0500 +TZOFFSETFROM:-0400 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T020000 +TZOFFSETTO:-0400 +TZOFFSETFROM:-0500 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:903a5415-9067-4f63-b499-1b6205f49c88 +RRULE:FREQ=DAILY;UNTIL=20200825T035959Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR +SUMMARY:appointment every weekday\, start jul 24\, 2020\, end aug 24\, 2020 +ATTENDEE;CN=Mark Hershberger;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP + =TRUE:mailto:hexmode gmail.com +ORGANIZER;CN=Mark A. Hershberger:mailto:mah nichework.com +DTSTART;TZID=\"America/New_York\":20200724T090000 +DTEND;TZID=\"America/New_York\":20200724T093000 +STATUS:CONFIRMED +CLASS:PUBLIC +X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY +TRANSP:OPAQUE +LAST-MODIFIED:20200719T150815Z +DTSTAMP:20200719T150815Z +SEQUENCE:0 +DESCRIPTION:The following is a new meeting request: +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;RELATED=START:-PT5M +DESCRIPTION:Reminder +END:VALARM +END:VEVENT +END:VCALENDAR" (list "Mark Hershberger")))) + + (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) + (should (gnus-icalendar-event:recurring-p event)) + (should (string= (gnus-icalendar-event:recurring-interval event) "1")) + (should (string= (gnus-icalendar-event:start event) "2020-07-24 15:00")) + (with-slots (organizer summary description location end-time uid rsvp participation-type) event + (should (string= organizer "mah nichework.com")) + (should (string= summary "appointment every weekday\, start jul 24\, 2020\, end aug 24\, 2020")) + (should (string= description "The following is a new meeting request:")) + (should (null location)) + (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-07-24 15:30")) + (should (string= uid "903a5415-9067-4f63-b499-1b6205f49c88")) + (should rsvp) + (should (eq participation-type 'required))) + (should (equal (gnus-icalendar-event:recurring-days event) '(1 2 3 4 5))) + (should (string= (gnus-icalendar-event:org-timestamp event) "<2020-07-24 15:00-15:30 +1w> +<2020-07-27 15:00-15:30 +1w> +<2020-07-28 15:00-15:30 +1w> +<2020-07-29 15:00-15:30 +1w> +<2020-07-30 15:00-15:30 +1w>")) + )) + + +;; (VCALENDAR nil +;; ((PRODID nil "Zimbra-Calendar-Provider") +;; (VERSION nil "2.0") +;; (METHOD nil "REQUEST")) +;; ((VTIMEZONE nil +;; ((TZID nil "America/New_York")) +;; ((STANDARD nil +;; ((DTSTART nil "16010101T020000") +;; (TZOFFSETTO nil "-0500") +;; (TZOFFSETFROM nil "-0400") +;; (RRULE nil "FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU") +;; (TZNAME nil "EST")) +;; nil) +;; (DAYLIGHT nil +;; ((DTSTART nil "16010101T020000") +;; (TZOFFSETTO nil "-0400") +;; (TZOFFSETFROM nil "-0500") +;; (RRULE nil "FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU") +;; (TZNAME nil "EDT")) +;; nil))) +;; (VEVENT nil +;; ((UID nil "903a5415-9067-4f63-b499-1b6205f49c88") +;; (RRULE nil "FREQ=DAILY;UNTIL=20200825T035959Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR") +;; (SUMMARY nil "appointment every weekday, start jul 24, 2020, end aug 24, 2020") +;; (ATTENDEE +;; (CN "Mark Hershberger" ROLE "REQ-PARTICIPANT" PARTSTAT "NEEDS-ACTION" CN "Mark A. Hershberger") +;; "mailto:mah nichework.com") +;; (DTSTART +;; (TZID "America/New_York") +;; "20200724T090000") +;; (DTEND +;; (TZID "America/New_York") +;; "20200724T093000") +;; (STATUS nil "CONFIRMED") +;; (CLASS nil "PUBLIC") +;; (X-MICROSOFT-CDO-INTENDEDSTATUS nil "BUSY") +;; (TRANSP nil "OPAQUE") +;; (LAST-MODIFIED nil "20200719T150815Z") +;; (DTSTAMP nil "20200719T150815Z") +;; (SEQUENCE nil "0") +;; (DESCRIPTION nil "The following is a new meeting request:")) +;; ((VALARM nil +;; ((ACTION nil "DISPLAY") +;; (TRIGGER +;; (RELATED "START") +;; "-PT5M") +;; (DESCRIPTION nil "Reminder")) +;; nil))))) + +(provide 'gnus-icalendar-tests) +;;; gnus-icalendar-tests.el ends here commit 2725254ab5463124388b1278e1cf0fdfafa0ba96 Author: Dmitry Gutov Date: Sat Aug 22 16:21:50 2020 +0300 Make ruby-parse-partial more stable * lisp/progmodes/ruby-mode.el (ruby-parse-partial): Don't call ruby-deep-indent-paren-p (bug#42841). diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index e16225c7fa..831acf87bf 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -1060,22 +1060,12 @@ delimiter." (goto-char (point)) ) ((looking-at "[\\[{(]") - (let ((deep (ruby-deep-indent-paren-p (char-after)))) - (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg))) - (progn - (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]") - (setq pnt (1- (match-end 0)))) - (setq nest (cons (cons (char-after (point)) pnt) nest)) - (setq pcol (cons (cons pnt depth) pcol)) - (setq depth 0)) - (setq nest (cons (cons (char-after (point)) pnt) nest)) - (setq depth (1+ depth)))) + (setq nest (cons (cons (char-after (point)) pnt) nest)) + (setq depth (1+ depth)) (goto-char pnt) ) ((looking-at "[])}]") - (if (ruby-deep-indent-paren-p (matching-paren (char-after))) - (setq depth (cdr (car pcol)) pcol (cdr pcol)) - (setq depth (1- depth))) + (setq depth (1- depth)) (setq nest (cdr nest)) (goto-char pnt)) ((looking-at ruby-block-end-re) commit 0aa4647f9cc53f3ded2c1bdea3d9f44962d318c0 Author: Lars Ingebrigtsen Date: Sat Aug 22 15:18:15 2020 +0200 Restrict the range of image formats to be converted * lisp/image/image-converter.el (image-converter--filter-formats): New function. (image-converter): Mention this in the doc string. diff --git a/etc/NEWS b/etc/NEWS index 79b3aa3732..b4833473ec 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -664,6 +664,14 @@ sorted there. The commands have also been extended to work when the "parent" buffer is an archive mode (i.e., zip file or the like) or tar mode buffer. +--- +*** 'image-converter' is now restricted to formats in 'auto-mode-alist'. +When using external image converters, the external program is queried +for what formats it supports. This list may contain formats that are +problematic in some contexts (like PDFs), so this list is now filtered +based on 'auto-mode-alist'. Only file names that map to 'image-mode' +are now supported. + ** EWW +++ diff --git a/lisp/image/image-converter.el b/lisp/image/image-converter.el index ee1dc845fb..c31a3b8d3c 100644 --- a/lisp/image/image-converter.el +++ b/lisp/image/image-converter.el @@ -33,8 +33,15 @@ "Type of the external image converter to use. The value should a symbol, either `imagemagick', `graphicsmagick', or `ffmpeg'. + If nil, Emacs will try to find one of the supported converters -installed on the system." +installed on the system. + +The actual range of image formats that will be converted depends +on what image formats the chosen converter reports being able to +handle. `auto-mode-alist' is then used to further filter what +formats that are to be supported: Only the suffixes that map to +`image-mode' will be handled." :group 'image :type 'symbol :version "27.1") @@ -186,12 +193,25 @@ data is returned as a string." "Find an installed image converter." (catch 'done (dolist (elem image-converter--converters) - (when-let ((formats (image-converter--probe (car elem)))) + (when-let ((formats (image-converter--filter-formats + (image-converter--probe (car elem))))) (setq image-converter (car elem) image-converter-regexp (concat "\\." (regexp-opt formats) "\\'") image-converter-file-name-extensions formats) (throw 'done image-converter))))) +(defun image-converter--filter-formats (suffixes) + "Filter SUFFIXES based on `auto-mode-alist'. +Only suffixes that map to `image-mode' are returned." + (cl-loop with case-fold-search = (if (not auto-mode-case-fold) + nil + t) + for suffix in suffixes + when (eq (cdr (assoc (concat "foo." suffix) auto-mode-alist + #'string-match)) + 'image-mode) + collect suffix)) + (cl-defmethod image-converter--convert ((type (eql graphicsmagick)) source image-format) "Convert using GraphicsMagick." commit 61fc4bf286da9081b822e9280e351ce7045868dc Author: Stefan Kangas Date: Sat Aug 22 13:21:13 2020 +0200 Bind mwheel-scroll on more parts of frame's display * lisp/mwheel.el (mouse-wheel-mode): Bind unmodified 'mwheel-scroll' on scroll bars, fringes, margins, header and mode line. (Bug#5557) (mouse-wheel--create-scroll-keys): New helper function for 'mouse-wheel-mode'. * test/lisp/mwheel-tests.el: New file. diff --git a/etc/NEWS b/etc/NEWS index 0a6a7dec5c..79b3aa3732 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,6 +102,13 @@ deprecated. Errors in the Inscript method were corrected. ** Rudimentary support for the 'st' terminal emulator. Emacs now supports 256 color display on the 'st' terminal emulator. +--- +** Mouse wheel scrolling now works on more parts of frame's display. +When using 'mwheel-mode', the mouse wheel will now scroll also when +the mouse cursor is on the scroll bars, fringes, margins, header line, +and mode line. ('mwheel-mode' is enabled by default on most graphical +displays.) + * Editing Changes in Emacs 28.1 diff --git a/lisp/mwheel.el b/lisp/mwheel.el index 8e2039ba9d..c385fdfc26 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -355,6 +355,18 @@ This is a helper function for `mouse-wheel-mode'." (when (memq (lookup-key (current-global-map) key) funs) (global-unset-key key)))) +(defun mouse-wheel--create-scroll-keys (binding event) + "Return list of key vectors for BINDING and EVENT. +BINDING is an element in `mouse-wheel-scroll-amount'. EVENT is +an event used for scrolling, such as `mouse-wheel-down-event'." + (let ((prefixes (list 'left-margin 'right-margin + 'left-fringe 'right-fringe + 'vertical-scroll-bar 'horizontal-scroll-bar + 'mode-line 'header-line))) + (cons (vector event) ; default case: no prefix. + (when (not (consp binding)) + (mapcar (lambda (prefix) (vector prefix event)) prefixes))))) + (define-minor-mode mouse-wheel-mode "Toggle mouse wheel support (Mouse Wheel mode)." :init-value t @@ -379,14 +391,16 @@ This is a helper function for `mouse-wheel-mode'." ;; Bindings for changing font size. ((and (consp binding) (eq (cdr binding) 'text-scale)) (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event)) + ;; Add binding. (let ((key `[,(list (caar binding) event)])) (global-set-key key 'mouse-wheel-text-scale) (push key mwheel-installed-text-scale-bindings)))) ;; Bindings for scrolling. (t (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event - mouse-wheel-right-event mouse-wheel-left-event)) - (let ((key `[(,@(if (consp binding) (car binding)) ,event)])) + mouse-wheel-left-event mouse-wheel-right-event)) + (dolist (key (mouse-wheel--create-scroll-keys binding event)) + ;; Add binding. (global-set-key key 'mwheel-scroll) (push key mwheel-installed-bindings)))))))) diff --git a/test/lisp/mwheel-tests.el b/test/lisp/mwheel-tests.el new file mode 100644 index 0000000000..f2989d608b --- /dev/null +++ b/test/lisp/mwheel-tests.el @@ -0,0 +1,38 @@ +;;; mwheel-tests.el --- tests for mwheel.el -*- lexical-binding:t -*- + +;; Copyright (C) 2020 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) +(require 'mwheel) + +(ert-deftest mwheel-test--create-scroll-keys () + (should (equal (mouse-wheel--create-scroll-keys 10 'mouse-1) + '([mouse-1] + [left-margin mouse-1] [right-margin mouse-1] + [left-fringe mouse-1] [right-fringe mouse-1] + [vertical-scroll-bar mouse-1] [horizontal-scroll-bar mouse-1] + [mode-line mouse-1] [header-line mouse-1]))) + ;; Don't bind modifiers outside of buffer area (e.g. for fringes). + (should (equal (mouse-wheel--create-scroll-keys '((shift) . 1) 'mouse-1) + '([mouse-1]))) + (should (equal (mouse-wheel--create-scroll-keys '((control) . 9) 'mouse-7) + '([mouse-7])))) + +;;; mwheel-tests.el ends here