commit 431f8ff1e38ca4367637c6b9fbc25d13d6f760a7 (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Mon May 13 09:59:07 2024 +0300 * lisp/imenu.el: Support more values for imenu-flatten (bug#70846) (imenu-flatten): Change type boolean to choice of more values for prefix/suffix section names. (imenu--completion-buffer): Add :annotation-function if 'imenu-flatten' is 'annotation'. (imenu--flatten-index-alist): Put property 'imenu-section' if 'imenu-flatten' is 'annotation'. diff --git a/lisp/imenu.el b/lisp/imenu.el index 9c0c1ae144e..93a544ff550 100644 --- a/lisp/imenu.el +++ b/lisp/imenu.el @@ -147,10 +147,16 @@ Used for flattening nested indexes with name concatenation." (defcustom imenu-flatten nil "Whether to flatten the list of sections in an imenu or show it nested. -If non-nil, popup the completion buffer with a flattened menu. +If nil, use nested indexes. +If t, popup the completion buffer with a flattened menu. +If `annotation', use completion annotation as a suffix +to append section names after the index names. + The string from `imenu-level-separator' is used to separate names of nested levels while flattening nested indexes with name concatenation." - :type 'boolean + :type '(choice (const :tag "Nested" nil) + (const :tag "By prefix" t) + (const :tag "By suffix" annotation)) :version "30.1") (defcustom imenu-generic-skip-comments-and-strings t @@ -743,7 +749,12 @@ Return one of the entries in index-alist or nil." ;; Display the completion buffer. (minibuffer-with-setup-hook (lambda () - (setq-local completion-extra-properties '(:category imenu)) + (setq-local completion-extra-properties + `( :category imenu + ,@(when (eq imenu-flatten 'annotation) + `(:annotation-function + ,(lambda (s) (get-text-property + 0 'imenu-section s)))))) (unless imenu-eager-completion-buffer (minibuffer-completion-help))) (setq name (completing-read prompt @@ -787,7 +798,11 @@ Returns t for rescan and otherwise an element or subelement of INDEX-ALIST." name)))) (cond ((not (imenu--subalist-p item)) - (list (cons new-prefix pos))) + (list (cons (if (and (eq imenu-flatten 'annotation) prefix) + (propertize name 'imenu-section + (format " (%s)" prefix)) + new-prefix) + pos))) (t (imenu--flatten-index-alist pos concat-names new-prefix))))) index-alist)) commit 9443f8145e1db86664a4af318b3bd1448094040e Author: Po Lu Date: Mon May 13 14:40:15 2024 +0800 Communicate frame titles to the window manager on Android * java/org/gnu/emacs/EmacsActivity.java (detachWindow) (attachWindow): Call updateWmName. (updateWmName): New function; transfer wm name from the window attached to the task's description. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) : New field. (setWmName): New function. * src/android.c (android_init_emacs_window): Link to new function. (android_set_wm_name): New function. * src/android.h (struct android_emacs_service): Delete unused entries. * src/androidfns.c (android_set_name_internal, android_set_name) (android_implicitly_set_name, android_explicitly_set_name) (android_set_title): Port from X. * src/androidterm.c (android_term_init): Compute default frame title. * src/androidterm.h (struct android_display_info) : New field. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 118c3375ad5..7d02e4f4834 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit; import android.app.Activity; +import android.app.ActivityManager.TaskDescription; import android.content.ContentResolver; import android.content.Context; @@ -166,6 +167,10 @@ children and RESETWHENCHILDLESS is set (implying it is a layout.removeView (window.view); window = null; + /* Reset the WM name. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + updateWmName (); + invalidateFocus (0); } } @@ -205,6 +210,11 @@ children and RESETWHENCHILDLESS is set (implying it is a invalidateFocus (1); } }); + + /* Synchronize the window's window manager name with this activity's + task in the recents list. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + updateWmName (); } @Override @@ -522,6 +532,29 @@ children and RESETWHENCHILDLESS is set (implying it is a } } + /* Update the name of this activity's task description from the + current window, or reset the same if no window is attached. */ + + @SuppressWarnings ("deprecation") + public final void + updateWmName () + { + String wmName; + TaskDescription description; + + if (window == null || window.wmName == null) + wmName = "Emacs"; + else + wmName = window.wmName; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) + description = new TaskDescription (wmName); + else + description = (new TaskDescription.Builder () + .setLabel (wmName).build ()); + setTaskDescription (description); + } + @Override public final void onAttachedToWindow () diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index f8dd8518af0..483ba0dbced 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -169,6 +169,11 @@ private static class Coordinate and whether this window has previously been attached to a task. */ public boolean preserve, previouslyAttached; + /* The window manager name of this window, which supplies the name of + activities in which it is displayed as a toplevel window, or + NULL. */ + public String wmName; + public EmacsWindow (final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -1562,6 +1567,36 @@ else if (EmacsWindow.this.isMapped) return dontFocusOnMap; } + public void + setWmName (final String wmName) + { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return; + + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + EmacsActivity activity; + Object tem; + + EmacsWindow.this.wmName = wmName; + + /* If an activity is already attached, replace its task + description. */ + + tem = getAttachedConsumer (); + + if (tem != null && tem instanceof EmacsActivity) + { + activity = (EmacsActivity) tem; + activity.updateWmName (); + } + } + }); + } + public int[] translateCoordinates (int x, int y) { @@ -1631,7 +1666,7 @@ else if (EmacsWindow.this.isMapped) fullscreen = isFullscreen; tem = getAttachedConsumer (); - if (tem != null) + if (tem != null && tem instanceof EmacsActivity) { activity = (EmacsActivity) tem; activity.syncFullscreenWith (EmacsWindow.this); diff --git a/src/android.c b/src/android.c index 72ef9e689ef..17b5d6d4115 100644 --- a/src/android.c +++ b/src/android.c @@ -115,6 +115,7 @@ struct android_emacs_window jmethodID recreate_activity; jmethodID clear_window; jmethodID clear_area; + jmethodID set_wm_name; }; struct android_emacs_cursor @@ -1842,6 +1843,7 @@ android_init_emacs_window (void) FIND_METHOD (recreate_activity, "recreateActivity", "()V"); FIND_METHOD (clear_window, "clearWindow", "()V"); FIND_METHOD (clear_area, "clearArea", "(IIII)V"); + FIND_METHOD (set_wm_name, "setWmName", "(Ljava/lang/String;)V"); #undef FIND_METHOD } @@ -5603,6 +5605,27 @@ android_set_dont_accept_focus (android_window handle, android_exception_check (); } +/* Set the WM name of HANDLE to STRING, a Java string. This name + provides the task description of activities that receive HANDLE. */ + +void +android_set_wm_name (android_window handle, jstring name) +{ + jmethodID method; + jobject window; + + window = android_resolve_handle (handle); + method = window_class.set_wm_name; + + if (android_get_current_api_level () < 21) + return; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, + window_class.class, method, + name); + android_exception_check (); +} + void android_get_keysym_name (int keysym, char *name_return, size_t size) { diff --git a/src/android.h b/src/android.h index a582a9b7dff..78482d64de4 100644 --- a/src/android.h +++ b/src/android.h @@ -118,6 +118,7 @@ extern bool android_detect_keyboard (void); extern void android_set_dont_focus_on_map (android_window, bool); extern void android_set_dont_accept_focus (android_window, bool); +extern void android_set_wm_name (android_window, jstring); extern int android_verify_jni_string (const char *); extern jstring android_build_string (Lisp_Object, ...); @@ -275,8 +276,6 @@ struct android_emacs_service jmethodID draw_rectangle; jmethodID draw_line; jmethodID draw_point; - jmethodID clear_window; - jmethodID clear_area; jmethodID ring_bell; jmethodID query_tree; jmethodID get_screen_width; diff --git a/src/androidfns.c b/src/androidfns.c index df425e5779e..1c2690394a6 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -211,18 +211,90 @@ android_set_parent_frame (struct frame *f, Lisp_Object new_value, FRAME_TERMINAL (f)->fullscreen_hook (f); } +/* Set the WM name to NAME for frame F. Also set the icon name. + If the frame already has an icon name, use that, otherwise set the + icon name to NAME. */ + +static void +android_set_name_internal (struct frame *f, Lisp_Object name) +{ + jstring java_name; + + if (FRAME_ANDROID_WINDOW (f)) + { + java_name = android_build_string (name, NULL); + android_set_wm_name (FRAME_ANDROID_WINDOW (f), java_name); + ANDROID_DELETE_LOCAL_REF (java_name); + } +} + +/* Change the name of frame F to NAME. If NAME is nil, set F's name to + x_id_name. + + If EXPLICIT is true, that indicates that lisp code is setting the + name; if NAME is a string, set F's name to NAME and set + F->explicit_name; if NAME is Qnil, then clear F->explicit_name. + + If EXPLICIT is false, that indicates that Emacs redisplay code is + suggesting a new name, which lisp code should override; if + F->explicit_name is set, ignore the new name; otherwise, set it. */ + +static void +android_set_name (struct frame *f, Lisp_Object name, bool explicit) +{ + /* Make sure that requests from lisp code override requests from + Emacs redisplay code. */ + if (explicit) + { + /* If we're switching from explicit to implicit, we had better + update the mode lines and thereby update the title. */ + if (f->explicit_name && NILP (name)) + update_mode_lines = 37; + + f->explicit_name = ! NILP (name); + } + else if (f->explicit_name) + return; + + /* If NAME is nil, set the name to the x_id_name. */ + if (NILP (name)) + { + /* Check for no change needed in this very common case + before we do any consing. */ + if (!strcmp (FRAME_DISPLAY_INFO (f)->x_id_name, + SSDATA (f->name))) + return; + name = build_string (FRAME_DISPLAY_INFO (f)->x_id_name); + } + else + CHECK_STRING (name); + + /* Don't change the name if it's already NAME. */ + if (! NILP (Fstring_equal (name, f->name))) + return; + + fset_name (f, name); + + /* For setting the frame title, the title parameter should override + the name parameter. */ + if (! NILP (f->title)) + name = f->title; + + android_set_name_internal (f, name); +} + void android_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - + android_set_name (f, arg, false); } void android_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - + android_set_name (f, arg, true); } /* Set the number of lines used for the tool bar of frame F to VALUE. @@ -2988,6 +3060,8 @@ android_set_title (struct frame *f, Lisp_Object name, name = f->name; else CHECK_STRING (name); + + android_set_name_internal (f, name); } static void diff --git a/src/androidterm.c b/src/androidterm.c index 67c20ec5245..94a115a66a6 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -6649,6 +6649,26 @@ android_term_init (void) terminal->name = xstrdup ("android"); + { + Lisp_Object system_name = Fsystem_name (); + static char const title[] = "GNU Emacs"; + if (STRINGP (system_name)) + { + static char const at[] = " at "; + ptrdiff_t nbytes = sizeof (title) + sizeof (at); + if (ckd_add (&nbytes, nbytes, SBYTES (system_name))) + memory_full (SIZE_MAX); + dpyinfo->x_id_name = xmalloc (nbytes); + sprintf (dpyinfo->x_id_name, "%s%s%s", title, at, + SDATA (system_name)); + } + else + { + dpyinfo->x_id_name = xmalloc (sizeof (title)); + strcpy (dpyinfo->x_id_name, title); + } + } + /* The display "connection" is now set up, and it must never go away. */ terminal->reference_count = 30000; diff --git a/src/androidterm.h b/src/androidterm.h index f4459c45dc9..c8f1ab655a9 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -90,6 +90,9 @@ struct android_display_info /* Minimum font height over all fonts in font_table. */ int smallest_font_height; + /* Default name for all frames on this display. */ + char *x_id_name; + /* The number of fonts opened for this display. */ int n_fonts; commit f560e759338e4cd43113fef39bb6e35c9e8a5893 Author: Dmitry Gutov Date: Mon May 13 05:36:28 2024 +0300 ruby-rubocop-use-bundler: New user option * lisp/progmodes/ruby-mode.el (ruby-rubocop-use-bundler): New user option. (ruby-flymake-rubocop--use-bundler-p): Use it. * etc/NEWS: Mention it. diff --git a/etc/NEWS b/etc/NEWS index 846bf759995..8a2c8950fd8 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1706,6 +1706,11 @@ options of GNU 'ls'. If non-nil, moving point forward or backward between widgets by typing 'TAB' or 'S-TAB' skips over inactive widgets. The default value is nil. +** Ruby mode +New user option 'ruby-rubocop-use-bundler'. By default it retains the +previous behavior: read the contens of Gemfile and act accordingly. But +you can also set it to t or nil to skip the check. + ** Miscellaneous --- diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 999fbebfb08..f6ef175e11e 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -2553,6 +2553,16 @@ If there is no Rubocop config file, Rubocop will be passed a flag :type 'string :safe 'stringp) +(defcustom ruby-rubocop-use-bundler 'check + "Non-nil with allow `ruby-flymake-rubocop' to use `bundle exec'. +When the value is `check', it will first see whether Gemfile exists in +the same directory as the configuration file, and whether it mentions +the gem \"rubocop\". When t, it's used unconditionally. " + :type '(choice (const :tag "Always" t) + (const :tag "No" nil) + (const :tag "If rubocop is in Gemfile" check)) + :safe 'booleanp) + (defun ruby-flymake-rubocop (report-fn &rest _args) "RuboCop backend for Flymake." (unless (executable-find "rubocop") @@ -2614,11 +2624,17 @@ If there is no Rubocop config file, Rubocop will be passed a flag finally (funcall report-fn diags))))))) (defun ruby-flymake-rubocop--use-bundler-p (dir) - (let ((file (expand-file-name "Gemfile" dir))) - (and (file-exists-p file) - (with-temp-buffer - (insert-file-contents file) - (re-search-forward "^ *gem ['\"]rubocop['\"]" nil t))))) + (cond + ((eq t ruby-rubocop-use-bundler) + t) + ((null ruby-rubocop-use-bundler) + nil) + (t + (let ((file (expand-file-name "Gemfile" dir))) + (and (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (re-search-forward "^ *gem ['\"]rubocop['\"]" nil t))))))) (defun ruby-flymake-auto (report-fn &rest args) (apply commit ad588f81317db2faa2770a700f0e29a3b74ae682 Author: Po Lu Date: Mon May 13 08:26:04 2024 +0800 ; * src/epaths.in: Declare and use android_game_path. diff --git a/src/epaths.in b/src/epaths.in index 8415ce51586..a928830dba2 100644 --- a/src/epaths.in +++ b/src/epaths.in @@ -94,10 +94,11 @@ along with GNU Emacs. If not, see . */ # define PATH_DATA "/assets/etc/" # define PATH_DOC "/assets/etc/" # define PATH_INFO "/assets/info/" - # define PATH_GAME "" + # define PATH_GAME (android_game_path) # define PATH_BITMAPS "/assets/bitmaps/" extern char *android_site_load_path; extern char *android_lib_dir; +extern char *android_game_path; #endif commit b86359d4abfb47820b5644adb0306bd628de6dd8 Author: Po Lu Date: Mon May 13 08:25:39 2024 +0800 Complete pacification of build warnings on Android * build-aux/makecounter.sh: Introduce extern declaration of emacs_shortlisp_counter. * src/android.c (android_class_path): Declare static. * src/androidfont.c (font_driver_class, font_spec_class) (integer_class, font_metrics_class, font_object_class): Declare static. * src/androidterm.c (android_frame_parm_handlers): Move declaration... * src/androidterm.h (android_frame_parm_handlers): ... here. diff --git a/build-aux/makecounter.sh b/build-aux/makecounter.sh index a63fcbb7c61..4d572d5ab80 100755 --- a/build-aux/makecounter.sh +++ b/build-aux/makecounter.sh @@ -36,6 +36,7 @@ cat > $1 < Date: Sun May 12 20:42:08 2024 +0300 Fix 'C-u C-x p g' globally and 'A' in dired-mode * lisp/progmodes/project.el (project-find-regexp): Ensure the DEFAULT-DIRECTORY is set correctly for the 'C-u' case (bug#70888). * lisp/progmodes/project.el (project--files-in-directory): Ensure that the DIR argument ends with a slash -- 'dired-do-find-regexp' passes it differently, for example. * lisp/progmodes/xref.el (xref--group-name-for-display): Ensure the project-relative and absolute display modes work well for groups with "relative" file names. (xref--analyze, xref--add-log-current-defun): Change accordingly. * test/lisp/progmodes/project-tests.el (project-find-regexp-with-prefix): New test. diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 52fe4df9080..a95d1267dd2 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -347,7 +347,8 @@ to find the list of ignores for each directory." (defun project--files-in-directory (dir ignores &optional files) (require 'find-dired) (require 'xref) - (let* ((default-directory dir) + (let* ((dir (file-name-as-directory dir)) + (default-directory dir) ;; Make sure ~/ etc. in local directory name is ;; expanded and not left for the shell command ;; to interpret. @@ -989,8 +990,9 @@ requires quoting, e.g. `\\[quoted-insert]'." (files (if (not current-prefix-arg) (project-files pr) - (let ((dir (read-directory-name "Base directory: " - caller-dir nil t))) + (let* ((dir (read-directory-name "Base directory: " + caller-dir nil t))) + (setq default-directory dir) (project--files-in-directory dir nil (grep-read-files regexp)))))) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 90ed8eb20e9..f9faec1b474 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1048,11 +1048,15 @@ beginning of the line." (defun xref--add-log-current-defun () "Return the string used to group a set of locations. This function is used as a value for `add-log-current-defun-function'." - (xref--group-name-for-display - (if-let (item (xref--item-at-point)) - (xref-location-group (xref-match-item-location item)) - (xref--imenu-extract-index-name)) - (xref--project-root (project-current)))) + (let ((project-root (xref--project-root (project-current)))) + (xref--group-name-for-display + (if-let (item (xref--item-at-point)) + (xref-location-group (xref-match-item-location item)) + (xref--imenu-extract-index-name)) + project-root + (and + (string-prefix-p project-root default-directory) + (substring default-directory (length project-root)))))) (defun xref--next-error-function (n reset?) (when reset? @@ -1184,12 +1188,15 @@ GROUP is a string for decoration purposes and XREF is an (xref--apply-truncation))) (run-hooks 'xref-after-update-hook)) -(defun xref--group-name-for-display (group project-root) +(defun xref--group-name-for-display (group project-root dd-suffix) "Return GROUP formatted in the preferred style. The style is determined by the value of `xref-file-name-display'. If GROUP looks like a file name, its value is formatted according -to that style. Otherwise it is returned unchanged." +to that style. Otherwise it is returned unchanged. + +PROJECT-ROOT is the root of the current project, if any. DD-SUFFIX is +the relative name of `default-directory' relative to the project root." ;; XXX: The way we verify that it's indeed a file name and not some ;; other kind of string, e.g. Java package name or TITLE from ;; `tags-apropos-additional-actions', is pretty lax. But we don't @@ -1199,16 +1206,19 @@ to that style. Otherwise it is returned unchanged." ;; values themselves (e.g. by piping through some public function), ;; or adding a new accessor to locations, like GROUP-TYPE. (cl-ecase xref-file-name-display - (abs group) + (abs (if (file-name-absolute-p group) group (expand-file-name group))) (nondirectory - (if (file-name-absolute-p group) - (file-name-nondirectory group) - group)) + (file-name-nondirectory group)) (project-relative - (if (and project-root - (string-prefix-p project-root group)) - (substring group (length project-root)) - group)))) + (cond + ((not (file-name-absolute-p group)) + (concat dd-suffix group)) + ((and project-root + (string-prefix-p project-root group)) + (substring group (length project-root))) + ;; Default to absolute when there's not project around. + (t + (expand-file-name group)))))) (defun xref--analyze (xrefs) "Find common groups in XREFS and format group names. @@ -1221,10 +1231,13 @@ Return an alist of the form ((GROUP . (XREF ...)) ...)." (eq xref-file-name-display 'project-relative) (project-current))) (project-root (and project - (expand-file-name (xref--project-root project))))) + (expand-file-name (xref--project-root project)))) + (dd-suffix (and project-root + (string-prefix-p project-root default-directory) + (substring default-directory (length project-root))))) (mapcar (lambda (pair) - (cons (xref--group-name-for-display (car pair) project-root) + (cons (xref--group-name-for-display (car pair) project-root dd-suffix) (cdr pair))) alist))) diff --git a/test/lisp/progmodes/project-tests.el b/test/lisp/progmodes/project-tests.el index 21703cbdad6..93943cef43b 100644 --- a/test/lisp/progmodes/project-tests.el +++ b/test/lisp/progmodes/project-tests.el @@ -188,4 +188,33 @@ When `project-ignores' includes a name matching project dir." (should (equal (sort (mapcar #'xref-item-summary matches) #'string<) '("((nil . ((project-vc-ignores . (\"etc\")))))" "etc")))))) +(ert-deftest project-find-regexp-with-prefix () + "Check the happy path." + (skip-unless (executable-find find-program)) + (skip-unless (executable-find "xargs")) + (skip-unless (executable-find "grep")) + (let* ((directory (ert-resource-directory)) + (project-find-functions nil) + (project-list-file (expand-file-name "emacs-projects" temporary-file-directory)) + (project (cons 'transient (expand-file-name "../elisp-mode-resources/" directory)))) + (add-hook 'project-find-functions (lambda (_dir) project)) + (should (eq (project-current) project)) + (let* ((matches nil) + (xref-search-program 'grep) + (xref-show-xrefs-function + (lambda (fetcher _display) + (setq matches (funcall fetcher)))) + (current-prefix-arg t)) + (cl-letf (((symbol-function 'read-directory-name) + (lambda (_prompt _default _dirname _mm) directory)) + ((symbol-function 'grep-read-files) (lambda (_re) "*"))) + (project-find-regexp "etc")) + (should (equal (mapcar (lambda (item) + (file-name-base + (xref-location-group (xref-item-location item)))) + matches) + '(".dir-locals" "etc"))) + (should (equal (sort (mapcar #'xref-item-summary matches) #'string<) + '("((nil . ((project-vc-ignores . (\"etc\")))))" "etc")))))) + ;;; project-tests.el ends here commit 2c759b9ce620311df97f43b791ba27b1302171c4 Author: Dmitry Gutov Date: Sun May 12 05:57:10 2024 +0300 completion--replace: Fix completion cycling * lisp/minibuffer.el (completion--replace): Don't "insert before markers" after all because in some cases (https://lists.gnu.org/archive/html/emacs-devel/2024-05/msg00701.html) - like cycling - BEG can be denoted by a marker as well. (minibuffer-completion-help): Make FIELD-END simply a fixnum. And adjust its value manually inside the local completion-list-insert-choice-function (bug#48356). diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 61395577035..fbd49b569a8 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1363,7 +1363,7 @@ Moves point to the end of the new text." (setq newtext (substring newtext 0 (- suffix-len)))) (goto-char beg) (let ((length (- end beg))) ;Read `end' before we insert the text. - (insert-before-markers-and-inherit newtext) + (insert-and-inherit newtext) (delete-region (point) (+ (point) length))) (forward-char suffix-len))) @@ -2594,7 +2594,7 @@ The candidate will still be chosen by `choose-completion' unless ctable cpred (buffer-substring (point) end)))) - (point-marker))) + (point))) (field-char (and (< field-end end) (char-after field-end))) (all-md (completion--metadata (buffer-substring-no-properties start (point)) @@ -2702,6 +2702,7 @@ The candidate will still be chosen by `choose-completion' unless (= (aref choice (1- (length choice))) field-char)) (setq end (1+ end))) + (cl-decf field-end (- end start (length choice))) ;; FIXME: Use `md' to do quoting&terminator here. (completion--replace start end choice) (let* ((minibuffer-completion-table ctable) commit 6e8bee27a5cb9915c5db0f8756c8a903e4be1cec Author: Po Lu Date: Sun May 12 21:42:52 2024 +0800 Fix offset of reported conversion regions * src/androidterm.c (request_class, text_class): Pacify new warning. (android_update_selection): Don't redundantly subtract 1 from compose region start. diff --git a/src/androidterm.c b/src/androidterm.c index f4c071f4519..e1cd96c9176 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5707,10 +5707,10 @@ struct android_extracted_text_class /* Fields and methods associated with the `ExtractedTextRequest' class. */ -struct android_extracted_text_request_class request_class; +static struct android_extracted_text_request_class request_class; /* Fields and methods associated with the `ExtractedText' class. */ -struct android_extracted_text_class text_class; +static struct android_extracted_text_class text_class; /* Return an ExtractedText object corresponding to the extracted text TEXT. START is a character position describing the offset of the @@ -6273,8 +6273,8 @@ android_update_selection (struct frame *f, struct window *w) end = marker_position (f->conversion.compose_region_end); /* Offset and detect underflow. */ - start = max (start, field_start) - field_start - 1; - end = min (end, field_end) - field_start - 1; + start = max (start, field_start) - field_start; + end = min (end, field_end) - field_start; if (end < 0 || start < 0) end = start = -1; } commit 9d7b22fcb408b3d864d4d74d14ab428a2706b45d Author: Mattias Engdegård Date: Sun May 12 15:18:19 2024 +0200 Revert "Avoid resizing mutation in subst-char-in-string" This reverts commit 184d6e8c02345583264b053bb59ae031bb1c5a00. It wasn't quite correct: the path through `string-replace` would not preserve text properties in the input string, which broke Gnus. diff --git a/lisp/subr.el b/lisp/subr.el index 444afc0e486..0ac71560c59 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -5690,19 +5690,13 @@ The SEPARATOR regexp defaults to \"\\s-+\"." (defun subst-char-in-string (fromchar tochar string &optional inplace) "Replace FROMCHAR with TOCHAR in STRING each time it occurs. Unless optional argument INPLACE is non-nil, return a new string." - (if (and (not inplace) - (if (multibyte-string-p string) - (> (max fromchar tochar) 127) - (> tochar 255))) - ;; Avoid quadratic behaviour from resizing replacement. - (string-replace (string fromchar) (string tochar) string) - (let ((i (length string)) - (newstr (if inplace string (copy-sequence string)))) - (while (> i 0) - (setq i (1- i)) - (if (eq (aref newstr i) fromchar) - (aset newstr i tochar))) - newstr))) + (let ((i (length string)) + (newstr (if inplace string (copy-sequence string)))) + (while (> i 0) + (setq i (1- i)) + (if (eq (aref newstr i) fromchar) + (aset newstr i tochar))) + newstr)) (defun string-replace (from-string to-string in-string) "Replace FROM-STRING with TO-STRING in IN-STRING each time it occurs." commit 9365feadccf1e28438e41c4ce8bf60ab8e4e1def Author: Mattias Engdegård Date: Sun May 12 14:55:55 2024 +0200 Silence some macOS build warnings * src/image.c (native_image_type): Declare static. * src/keyboard.c (lispy_function_keys): Declare static on NS. * src/nsmenu.m (context_menu_value): * src/nsterm.m (ns_frame_parm_handlers): Move extern declarations... * src/nsterm.h: ...here. * test/src/emacs-module-resources/mod-test.c: Add extern declaration. diff --git a/src/image.c b/src/image.c index e93fc3183af..3138ef25a63 100644 --- a/src/image.c +++ b/src/image.c @@ -12744,7 +12744,7 @@ static struct image_type const image_types[] = }; #if HAVE_NATIVE_IMAGE_API -struct image_type native_image_type = +static struct image_type native_image_type = { SYMBOL_INDEX (Qnative_image), native_image_p, native_image_load, image_clear_image }; #endif diff --git a/src/keyboard.c b/src/keyboard.c index dff86bbca13..d5892115e4b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -5396,6 +5396,10 @@ static const char *const lispy_kana_keys[] = /* You'll notice that this table is arranged to be conveniently indexed by X Windows keysym values. */ +#ifdef HAVE_NS +/* FIXME: Why are we using X11 keysym values for NS? */ +static +#endif const char *const lispy_function_keys[] = { /* X Keysym value */ diff --git a/src/nsmenu.m b/src/nsmenu.m index 0d21f7d03d3..46ee9e1c3fc 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -47,7 +47,6 @@ #endif -extern long context_menu_value; EmacsMenu *svcsMenu; /* Nonzero means a menu is currently active. */ static int popup_activated_flag; diff --git a/src/nsterm.h b/src/nsterm.h index ae940ec5b4f..3a713f8e8c9 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -921,6 +921,8 @@ struct ns_display_info /* This is a chain of structures for all the NS displays currently in use. */ extern struct ns_display_info *x_display_list; +extern long context_menu_value; + struct ns_output { #ifdef __OBJC__ @@ -1269,6 +1271,8 @@ extern void ns_finish_events (void); extern double ns_frame_scale_factor (struct frame *); +extern frame_parm_handler ns_frame_parm_handlers[]; + #ifdef NS_IMPL_GNUSTEP extern char gnustep_base_version[]; /* version tracking */ #endif diff --git a/src/nsterm.m b/src/nsterm.m index f26cdb17903..794630de1c1 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -5438,7 +5438,6 @@ static Lisp_Object ns_string_to_lispmod (const char *s) redisplay interface. In addition, many of the ns_ methods have code that is shared with all terms, indicating need for further refactoring. */ -extern frame_parm_handler ns_frame_parm_handlers[]; static struct redisplay_interface ns_redisplay_interface = { ns_frame_parm_handlers, diff --git a/test/src/emacs-module-resources/mod-test.c b/test/src/emacs-module-resources/mod-test.c index 3aafae1b896..3abe2a4122b 100644 --- a/test/src/emacs-module-resources/mod-test.c +++ b/test/src/emacs-module-resources/mod-test.c @@ -46,6 +46,7 @@ uintptr_t _beginthread (void (__cdecl *) (void *), unsigned, void *); #include #include +extern int plugin_is_GPL_compatible; int plugin_is_GPL_compatible; #if INTPTR_MAX <= 0 commit 38091e43bee991920db81263fe56da444a123fae Author: Mattias Engdegård Date: Sun May 12 11:22:23 2024 +0200 Safer and faster string sorting * lisp/emacs-lisp/cl-seq.el (cl-sort): Don't use string mutation when sorting the characters in a string. This avoids O(n^2) run time and makes it future-safe. diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el index 42f54603899..60d72d8657d 100644 --- a/lisp/emacs-lisp/cl-seq.el +++ b/lisp/emacs-lisp/cl-seq.el @@ -668,7 +668,10 @@ This is a destructive function; it reuses the storage of SEQ if possible. \nKeywords supported: :key \n(fn SEQ PREDICATE [KEYWORD VALUE]...)" (if (nlistp cl-seq) - (cl-replace cl-seq (apply 'cl-sort (append cl-seq nil) cl-pred cl-keys)) + (if (stringp cl-seq) + (concat (apply #'cl-sort (vconcat cl-seq) cl-pred cl-keys)) + (cl-replace cl-seq + (apply #'cl-sort (append cl-seq nil) cl-pred cl-keys))) (cl--parsing-keywords (:key) () (if (memq cl-key '(nil identity)) (sort cl-seq cl-pred) commit 86d196c71d763ae81229e7e1eb2c6e37d0456f06 Author: Po Lu Date: Sun May 12 20:24:19 2024 +0800 Properly deinitialize directory cursors on closedir * src/androidvfs.c (android_saf_tree_closedir): Call cursor.close, for deleting the local reference to enable its garbage collection is not sufficient to release all related resources. diff --git a/src/androidvfs.c b/src/androidvfs.c index 004abd62518..c0bd86e54b8 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5562,6 +5562,10 @@ android_saf_tree_closedir (struct android_vdir *vdir) free (dir->name); /* Yes, DIR->cursor is a local reference. */ + (*android_java_env)->CallVoidMethod (android_java_env, + dir->cursor, + cursor_class.close); + (*android_java_env)->ExceptionClear (android_java_env); ANDROID_DELETE_LOCAL_REF (dir->cursor); /* If the ``directory file descriptor'' has been opened, close commit ea98a6af2f30eccfed3e152149c6a039bd16ff33 Author: Po Lu Date: Sun May 12 15:46:38 2024 +0800 Additional changes to processing of Num Lock on Android * java/org/gnu/emacs/EmacsView.java (onKeyDown, onKeyMultiple) (onKeyDown): Disregard Num and Scroll Lock keys, and return value of window functions to the system. * java/org/gnu/emacs/EmacsWindow.java (eventModifiers): Return normalized meta state, not only those bits the system considers modifiers. (onKeyDown, onKeyUp): Ignore numpad keys to which no base characters are assigned, so that the system may generate the proper action keys instead. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 977ad90310d..074e7242540 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -505,42 +505,45 @@ else if (child.getVisibility () != GONE) public boolean onKeyDown (int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) - && !EmacsNative.shouldForwardMultimediaButtons ()) - return false; + if (((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + || keyCode == KeyEvent.KEYCODE_SCROLL_LOCK + || keyCode == KeyEvent.KEYCODE_NUM_LOCK) + return super.onKeyDown (keyCode, event); - window.onKeyDown (keyCode, event); - return true; + return window.onKeyDown (keyCode, event); } @Override public boolean onKeyMultiple (int keyCode, int repeatCount, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) - && !EmacsNative.shouldForwardMultimediaButtons ()) - return false; + if (((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + || keyCode == KeyEvent.KEYCODE_SCROLL_LOCK + || keyCode == KeyEvent.KEYCODE_NUM_LOCK) + return super.onKeyMultiple (keyCode, repeatCount, event); - window.onKeyDown (keyCode, event); - return true; + return window.onKeyDown (keyCode, event); } @Override public boolean onKeyUp (int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) - && !EmacsNative.shouldForwardMultimediaButtons ()) - return false; + if (((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + || keyCode == KeyEvent.KEYCODE_SCROLL_LOCK + || keyCode == KeyEvent.KEYCODE_NUM_LOCK) + return super.onKeyUp (keyCode, event); - window.onKeyUp (keyCode, event); - return true; + return window.onKeyUp (keyCode, event); } @Override diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 6442f373736..f8dd8518af0 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -633,8 +633,8 @@ private static class Coordinate /* Return the modifier mask associated with the specified keyboard - input EVENT. Replace bits corresponding to Left or Right keys - with their corresponding general modifier bits. */ + input EVENT. Replace bits representing Left or Right keys with + their corresponding general modifier bits. */ public static int eventModifiers (KeyEvent event) @@ -642,7 +642,7 @@ private static class Coordinate int state; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) - state = event.getModifiers (); + state = KeyEvent.normalizeMetaState (event.getMetaState ()); else { /* Replace this with getMetaState and manual @@ -667,10 +667,10 @@ private static class Coordinate /* event.getCharacters is used because older input methods still require it. */ @SuppressWarnings ("deprecation") - public void + public boolean onKeyDown (int keyCode, KeyEvent event) { - int state, state_1, extra_ignored; + int state, state_1, extra_ignored, unicode_char; long serial; String characters; @@ -686,7 +686,7 @@ private static class Coordinate Deliver onKeyDown events in onKeyUp instead, so as not to navigate backwards during gesture navigation. */ - return; + return true; } state = eventModifiers (event); @@ -720,23 +720,36 @@ private static class Coordinate state &= ~KeyEvent.META_ALT_MASK; } + unicode_char = getEventUnicodeChar (event, state_1); + + /* If a NUMPAD_ key is detected for which no character is returned, + return false without sending the key event, as this will prompt + the system to send an event with the corresponding action + key. */ + + if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 + && keyCode <= KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN + && unicode_char == 0) + return false; + synchronized (eventStrings) { serial = EmacsNative.sendKeyPress (this.handle, event.getEventTime (), state, keyCode, - getEventUnicodeChar (event, - state_1)); + unicode_char); characters = event.getCharacters (); if (characters != null && characters.length () > 1) saveUnicodeString ((int) serial, characters); } + + return true; } - public void + public boolean onKeyUp (int keyCode, KeyEvent event) { int state, state_1, unicode_char, extra_ignored; @@ -781,12 +794,20 @@ private static class Coordinate /* If the key press's been canceled, return immediately. */ if ((event.getFlags () & KeyEvent.FLAG_CANCELED) != 0) - return; + return true; /* Dispatch the key press event that was deferred till now. */ EmacsNative.sendKeyPress (this.handle, event.getEventTime (), state, keyCode, unicode_char); } + /* If a NUMPAD_ key is detected for which no character is returned, + return false without sending the key event, as this will prompt + the system to send an event with the corresponding action + key. */ + else if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 + && keyCode <= KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN + && unicode_char == 0) + return false; EmacsNative.sendKeyRelease (this.handle, event.getEventTime (), state, keyCode, unicode_char); @@ -804,6 +825,8 @@ private static class Coordinate lastQuitKeyRelease = time; } + + return true; } public void commit af6df8e0454f3f51d3855683216dfd12843e6959 Author: Po Lu Date: Sun May 12 14:13:27 2024 +0800 Correct earlier changes to processing of Num Lock on Android * java/org/gnu/emacs/EmacsWindow.java (onKeyDown, onKeyUp): Cease stripping META_NUM_LOCK_ON and META_SCROLL_LOCK_ON from meta masks reported to getUnicodeChar. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 9acdc9502cf..6442f373736 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -691,13 +691,10 @@ private static class Coordinate state = eventModifiers (event); - /* Num Lock, Scroll Lock and Meta aren't supported by systems older - than Android 3.0. */ + /* Meta isn't supported by systems older than Android 3.0. */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) - extra_ignored = (KeyEvent.META_NUM_LOCK_ON - | KeyEvent.META_SCROLL_LOCK_ON - | KeyEvent.META_META_MASK); + extra_ignored = KeyEvent.META_META_MASK; else extra_ignored = 0; @@ -748,13 +745,10 @@ private static class Coordinate /* Compute the event's modifier mask. */ state = eventModifiers (event); - /* Num Lock, Scroll Lock and Meta aren't supported by systems older - than Android 3.0. */ + /* Meta isn't supported by systems older than Android 3.0. */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) - extra_ignored = (KeyEvent.META_NUM_LOCK_ON - | KeyEvent.META_SCROLL_LOCK_ON - | KeyEvent.META_META_MASK); + extra_ignored = KeyEvent.META_META_MASK; else extra_ignored = 0;