commit 6f3243db55e61847784178ea812f28ddf003544a (HEAD, refs/remotes/origin/master) Author: Paul Pogonyshev Date: Sat Mar 26 11:19:43 2016 +0300 Implement 'func-arity' * src/eval.c (Ffunc_arity, lambda_arity): New functions. * src/bytecode.c (get_byte_code_arity): New function. * src/lisp.h (get_byte_code_arity): Add prototype. * doc/lispref/functions.texi (What Is a Function): Document 'func-arity'. * etc/NEWS: Mention 'func-arity'. * test/src/fns-tests.el (fns-tests-func-arity): New test set. diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index a2e94c3..ff21abb 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -143,6 +143,37 @@ function, i.e., can be passed to @code{funcall}. Note that and returns @code{nil} for special forms. @end defun + It is also possible to find out how many arguments an arbitrary +function expects: + +@defun func-arity function +This function provides information about the argument list of the +specified @var{function}. The returned value is a cons cell of the +form @w{@code{(@var{min} . @var{max})}}, where @var{min} is the +minimum number of arguments, and @var{max} is either the maximum +number of arguments, or the symbol @code{many} for functions with +@code{&rest} arguments, or the symbol @code{unevalled} if +@var{function} is a special form. + +Note that this function might return inaccurate results in some +situations, such as the following: + +@itemize @minus +@item +Functions defined using @code{apply-partially} (@pxref{Calling +Functions, apply-partially}). + +@item +Functions that are advised using @code{advice-add} (@pxref{Advising +Named Functions}). + +@item +Functions that determine the argument list dynamically, as part of +their code. +@end itemize + +@end defun + @noindent Unlike @code{functionp}, the next three functions do @emph{not} treat a symbol as its function definition. @@ -176,12 +207,9 @@ function. For example: @end defun @defun subr-arity subr -This function provides information about the argument list of a -primitive, @var{subr}. The returned value is a pair -@code{(@var{min} . @var{max})}. @var{min} is the minimum number of -args. @var{max} is the maximum number or the symbol @code{many}, for a -function with @code{&rest} arguments, or the symbol @code{unevalled} if -@var{subr} is a special form. +This works like @code{func-arity}, but only for built-in functions and +without symbol indirection. It signals an error for non-built-in +functions. We recommend to use @code{func-arity} instead. @end defun @node Lambda Expressions diff --git a/etc/NEWS b/etc/NEWS index 0bc6130..ce21532 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -182,6 +182,13 @@ a new window when opening man pages when there's already one, use (mode . Man-mode)))) +++ +** New function 'func-arity' returns information about the argument list +of an arbitrary function. +This is a generalization of 'subr-arity' for functions that are not +built-in primitives. We recommend using this new function instead of +'subr-arity'. + ++++ ** 'parse-partial-sexp' state has a new element. Element 10 is non-nil when the last character scanned might be the first character of a two character construct, i.e. a comment delimiter or escaped diff --git a/src/bytecode.c b/src/bytecode.c index 9ae2e82..4ff15d2 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -1987,6 +1987,24 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, return result; } +/* `args_template' has the same meaning as in exec_byte_code() above. */ +Lisp_Object +get_byte_code_arity (Lisp_Object args_template) +{ + if (INTEGERP (args_template)) + { + ptrdiff_t at = XINT (args_template); + bool rest = (at & 128) != 0; + int mandatory = at & 127; + ptrdiff_t nonrest = at >> 8; + + return Fcons (make_number (mandatory), + rest ? Qmany : make_number (nonrest)); + } + else + error ("Unknown args template!"); +} + void syms_of_bytecode (void) { diff --git a/src/eval.c b/src/eval.c index 74b30e6..64a6655 100644 --- a/src/eval.c +++ b/src/eval.c @@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE; static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *); static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t); +static Lisp_Object lambda_arity (Lisp_Object); static Lisp_Object specpdl_symbol (union specbinding *pdl) @@ -2934,6 +2935,115 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs, return unbind_to (count, val); } +DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0, + doc: /* Return minimum and maximum number of args allowed for FUNCTION. +FUNCTION must be a function of some kind. +The returned value is a cons cell (MIN . MAX). MIN is the minimum number +of args. MAX is the maximum number, or the symbol `many', for a +function with `&rest' args, or `unevalled' for a special form. */) + (Lisp_Object function) +{ + Lisp_Object original; + Lisp_Object funcar; + Lisp_Object result; + short minargs, maxargs; + + original = function; + + retry: + + /* Optimize for no indirection. */ + function = original; + if (SYMBOLP (function) && !NILP (function) + && (function = XSYMBOL (function)->function, SYMBOLP (function))) + function = indirect_function (function); + + if (SUBRP (function)) + result = Fsubr_arity (function); + else if (COMPILEDP (function)) + result = lambda_arity (function); + else + { + if (NILP (function)) + xsignal1 (Qvoid_function, original); + if (!CONSP (function)) + xsignal1 (Qinvalid_function, original); + funcar = XCAR (function); + if (!SYMBOLP (funcar)) + xsignal1 (Qinvalid_function, original); + if (EQ (funcar, Qlambda) + || EQ (funcar, Qclosure)) + result = lambda_arity (function); + else if (EQ (funcar, Qautoload)) + { + Fautoload_do_load (function, original, Qnil); + goto retry; + } + else + xsignal1 (Qinvalid_function, original); + } + return result; +} + +/* FUN must be either a lambda-expression or a compiled-code object. */ +static Lisp_Object +lambda_arity (Lisp_Object fun) +{ + Lisp_Object val, syms_left, next; + ptrdiff_t minargs, maxargs; + bool optional; + + if (CONSP (fun)) + { + if (EQ (XCAR (fun), Qclosure)) + { + fun = XCDR (fun); /* Drop `closure'. */ + CHECK_LIST_CONS (fun, fun); + } + syms_left = XCDR (fun); + if (CONSP (syms_left)) + syms_left = XCAR (syms_left); + else + xsignal1 (Qinvalid_function, fun); + } + else if (COMPILEDP (fun)) + { + ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK; + if (size <= COMPILED_STACK_DEPTH) + xsignal1 (Qinvalid_function, fun); + syms_left = AREF (fun, COMPILED_ARGLIST); + if (INTEGERP (syms_left)) + return get_byte_code_arity (syms_left); + } + else + emacs_abort (); + + minargs = maxargs = optional = 0; + for (; CONSP (syms_left); syms_left = XCDR (syms_left)) + { + next = XCAR (syms_left); + if (!SYMBOLP (next)) + xsignal1 (Qinvalid_function, fun); + + if (EQ (next, Qand_rest)) + return Fcons (make_number (minargs), Qmany); + else if (EQ (next, Qand_optional)) + optional = 1; + else + { + if (!optional) + minargs++; + maxargs++; + } + } + + if (!NILP (syms_left)) + xsignal1 (Qinvalid_function, fun); + + return Fcons (make_number (minargs), make_number (maxargs)); +} + + DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode, 1, 1, 0, doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now. */) @@ -3808,6 +3918,7 @@ alist of active lexical bindings. */); defsubr (&Seval); defsubr (&Sapply); defsubr (&Sfuncall); + defsubr (&Sfunc_arity); defsubr (&Srun_hooks); defsubr (&Srun_hook_with_args); defsubr (&Srun_hook_with_args_until_success); diff --git a/src/lisp.h b/src/lisp.h index e606ffa..7c8b452 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4215,6 +4215,7 @@ extern struct byte_stack *byte_stack_list; extern void relocate_byte_stack (void); extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, ptrdiff_t, Lisp_Object *); +extern Lisp_Object get_byte_code_arity (Lisp_Object); /* Defined in macros.c. */ extern void init_macros (void); diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index 8617369..688ff1f 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el @@ -208,3 +208,14 @@ (should (string-version-lessp "foo1.25.5.png" "foo1.125.5")) (should (string-version-lessp "2" "1245")) (should (not (string-version-lessp "1245" "2")))) + +(ert-deftest fns-tests-func-arity () + (should (equal (func-arity 'car) '(1 . 1))) + (should (equal (func-arity 'caar) '(1 . 1))) + (should (equal (func-arity 'format) '(1 . many))) + (require 'info) + (should (equal (func-arity 'Info-goto-node) '(1 . 3))) + (should (equal (func-arity (lambda (&rest x))) '(0 . many))) + (should (equal (func-arity (eval (lambda (x &optional y)) nil)) '(1 . 2))) + (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2))) + (should (equal (func-arity 'let) '(1 . unevalled)))) commit 368b9bb45f125061506d43af4bd4791ab2cfd7b9 Author: Alan Mackenzie Date: Fri Mar 25 18:25:08 2016 +0000 Fix absence of c-noise-macro-name-re, etc., in languages which don't use it * lisp/progmodes/cc-engine.el (c-forward-keyword-prefixed-id, c-forward-type) (c-forward-declarator, c-forward-decl-or-cast-1, c-backward-over-enum-header) (c-guess-basic-syntax): Check c-opt-cpp-prefix before `looking-at' c-noise-macro-with-parens-name-re. * lisp/progmodes/cc-fonts.el (c-complex-decl-matchers): The same as for cc-engine.el. * lisp/progmodes/cc-mode.el (c-basic-common-init): Add call to `c-make-noise-macro-regexps'. (c-mode, c++-mode, objc-mode): Remove calls to `c-make-noise-macro-regexps'. * lisp/progmodes/cc-vars.el (c-noise-macro-with-parens-name-re) (c-noise-macro-with-re): Initialize to "\\<\\>" rather than nil. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index f9eae21..e04929a 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -5838,7 +5838,8 @@ comment at the start of cc-engine.el for more info." nil (cond ((looking-at c-keywords-regexp) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause))))) (when (memq res '(t known found prefix)) ,(when (eq type 'ref) @@ -6506,7 +6507,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-decl-hangon-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (setq pos (point)) @@ -6911,7 +6913,8 @@ comment at the start of cc-engine.el for more info." (cond ((looking-at c-decl-hangon-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)) ((and (looking-at c-type-decl-prefix-key) (if (and (c-major-mode-is 'c++-mode) @@ -6966,7 +6969,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-decl-hangon-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (<= (point) limit)) @@ -7161,7 +7165,8 @@ comment at the start of cc-engine.el for more info." (save-excursion (c-forward-keyword-clause 1) (setq kwd-clause-end (point)))) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (setq noise-start (point)) (c-forward-noise-clause) (setq kwd-clause-end (point)))) @@ -7267,7 +7272,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-decl-hangon-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (setq id-start (point))) @@ -9038,7 +9044,8 @@ comment at the start of cc-engine.el for more info." ((eq (char-after) ?\() (and (eq (c-backward-token-2) 0) (or (looking-at c-decl-hangon-key) - (looking-at c-noise-macro-with-parens-name-re)))) + (and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re))))) ((and c-recognize-<>-arglists (eq (char-after) ?<) @@ -10303,7 +10310,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-specifier-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (setq placeholder (c-point 'boi)) (or (consp special-brace-list) @@ -10359,7 +10367,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-specifier-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (c-add-syntax 'defun-open (c-point 'boi)) ;; Bogus to use bol here, but it's the legacy. (Resolved, @@ -10994,7 +11003,8 @@ comment at the start of cc-engine.el for more info." (while (cond ((looking-at c-specifier-key) (c-forward-keyword-clause 1)) - ((looking-at c-noise-macro-with-parens-name-re) + ((and c-opt-cpp-prefix + (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)))) (c-add-syntax 'brace-list-open (c-point 'boi)))) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index a7097b9..67e88a3 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1705,7 +1705,9 @@ on level 2 only and so aren't combined with `c-complex-decl-matchers'." (looking-at "@[A-Za-z0-9]+"))) (c-forward-keyword-clause 1) t) - (when (looking-at c-noise-macro-with-parens-name-re) + (when (and c-opt-cpp-prefix + (looking-at + c-noise-macro-with-parens-name-re)) (c-forward-noise-clause) t))) ,(if (c-major-mode-is 'c++-mode) diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 9ebe6f7..a53c86c 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -596,6 +596,7 @@ that requires a literal mode spec at compile time." (not (string-equal c-indentation-style style))))))) (c-setup-paragraph-variables) + (c-make-noise-macro-regexps) ;; we have to do something special for c-offsets-alist so that the ;; buffer local value has its own alist structure. @@ -1493,7 +1494,6 @@ Key bindings: abbrev-mode t) (use-local-map c-mode-map) (c-init-language-vars-for 'c-mode) - (c-make-noise-macro-regexps) (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ; (c-common-init 'c-mode) (easy-menu-add c-c-menu) @@ -1549,7 +1549,6 @@ Key bindings: abbrev-mode t) (use-local-map c++-mode-map) (c-init-language-vars-for 'c++-mode) - (c-make-noise-macro-regexps) (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ; (c-common-init 'c++-mode) (easy-menu-add c-c++-menu) @@ -1603,7 +1602,6 @@ Key bindings: abbrev-mode t) (use-local-map objc-mode-map) (c-init-language-vars-for 'objc-mode) - (c-make-noise-macro-regexps) (c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ; (c-common-init 'objc-mode) (easy-menu-add c-objc-menu) diff --git a/lisp/progmodes/cc-vars.el b/lisp/progmodes/cc-vars.el index a695718..33ea152 100644 --- a/lisp/progmodes/cc-vars.el +++ b/lisp/progmodes/cc-vars.el @@ -1619,8 +1619,8 @@ names).")) :type 'c-extra-types-widget :group 'c) -(defvar c-noise-macro-with-parens-name-re nil) -(defvar c-noise-macro-name-re nil) +(defvar c-noise-macro-with-parens-name-re "\\<\\>") +(defvar c-noise-macro-name-re "\\<\\>") (defcustom c-noise-macro-names nil "A list of names of macros which expand to nothing, or compiler extensions commit 7570b35740915626e94c6038e3203374ce4267b1 Author: Lars Magne Ingebrigtsen Date: Fri Mar 25 16:57:35 2016 +0100 (shr-insert): Respect non-breaking space in non-
 text
    
    * lisp/net/shr.el (shr-insert): Respect non-breaking space in
    non-
 text.

diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index 6079415..3adc573 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -554,6 +554,16 @@ size, and full-buffer size."
       (insert string)
       (shr-pixel-column))))
 
+(defsubst shr--translate-insertion-chars ()
+  ;; Remove soft hyphens.
+  (goto-char (point-min))
+  (while (search-forward "­" nil t)
+    (replace-match "" t t))
+  ;; Translate non-breaking spaces into real spaces.
+  (goto-char (point-min))
+  (while (search-forward " " nil t)
+    (replace-match " " t t)))
+
 (defun shr-insert (text)
   (when (and (not (bolp))
 	     (get-text-property (1- (point)) 'image-url))
@@ -564,14 +574,11 @@ size, and full-buffer size."
       (insert text)
       (save-restriction
 	(narrow-to-region start (point))
-	;; Remove soft hyphens.
-	(goto-char (point-min))
-	(while (search-forward "­" nil t)
-	  (replace-match "" t t))
+        (shr--translate-insertion-chars)
 	(goto-char (point-max)))))
    (t
     (let ((font-start (point)))
-      (when (and (string-match "\\`[ \t\n\r ]" text)
+      (when (and (string-match "\\`[ \t\n\r]" text)
 		 (not (bolp))
 		 (not (eq (char-after (1- (point))) ? )))
 	(insert " "))
@@ -581,14 +588,11 @@ size, and full-buffer size."
 	(save-restriction
 	  (narrow-to-region start (point))
 	  (goto-char start)
-	  (when (looking-at "[ \t\n\r ]+")
+	  (when (looking-at "[ \t\n\r]+")
 	    (replace-match "" t t))
-	  (while (re-search-forward "[ \t\n\r ]+" nil t)
+	  (while (re-search-forward "[ \t\n\r]+" nil t)
 	    (replace-match " " t t))
-	  ;; Remove soft hyphens.
-	  (goto-char (point-min))
-	  (while (search-forward "­" nil t)
-	    (replace-match "" t t))
+          (shr--translate-insertion-chars)
 	  (goto-char (point-max)))
 	;; We may have removed everything we inserted if if was just
 	;; spaces.
diff --git a/test/data/shr/nonbr.html b/test/data/shr/nonbr.html
new file mode 100644
index 0000000..56282cf
--- /dev/null
+++ b/test/data/shr/nonbr.html
@@ -0,0 +1 @@
+
(progn
  (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))

  (defun turn-on-cursor-intangible-mode ()
    "Turns on cursor-intangible-mode."
    (interactive)
    (cursor-intangible-mode 1))
  (define-globalized-minor-mode global-cursor-intangible-mode cursor-intangible-mode turn-on-cursor-intangible-mode)

  (global-cursor-intangible-mode 1))

diff --git a/test/data/shr/nonbr.txt b/test/data/shr/nonbr.txt new file mode 100644 index 0000000..0c3cffa --- /dev/null +++ b/test/data/shr/nonbr.txt @@ -0,0 +1,12 @@ +(progn + (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face +minibuffer-prompt)) + + (defun turn-on-cursor-intangible-mode () + "Turns on cursor-intangible-mode." + (interactive) + (cursor-intangible-mode 1)) + (define-globalized-minor-mode global-cursor-intangible-mode +cursor-intangible-mode turn-on-cursor-intangible-mode) + + (global-cursor-intangible-mode 1)) commit b2e629c0d043720fe9f043e59d583eb7068dfb11 Author: Lars Magne Ingebrigtsen Date: Fri Mar 25 16:32:39 2016 +0100 parse-times-string doc string clarification * lisp/calendar/parse-time.el (parse-time-string): Note that we accept RFC2822 strings. diff --git a/lisp/calendar/parse-time.el b/lisp/calendar/parse-time.el index c17d04a..b62f9fa 100644 --- a/lisp/calendar/parse-time.el +++ b/lisp/calendar/parse-time.el @@ -146,8 +146,12 @@ letters, digits, plus or minus signs or colons." ;;;###autoload (defun parse-time-string (string) "Parse the time-string STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ). -The values are identical to those of `decode-time', but any values that are -unknown are returned as nil." +STRING should be on something resembling an RFC2822 string, a la +\"Fri, 25 Mar 2016 16:24:56 +0100\", but this function is +somewhat liberal in what format it accepts, and will attempt to +return a \"likely\" value even for somewhat malformed strings. +The values returned are identical to those of `decode-time', but +any values that are unknown are returned as nil." (let ((time (list nil nil nil nil nil nil nil nil nil)) (temp (parse-time-tokenize (downcase string)))) (while temp commit 2812099bcc1449b126d3435b05c2ad3d19b6c1a1 Author: Marcin Borkowski Date: Fri Mar 25 16:26:08 2016 +0100 Doc string fix * lisp/calendar/parse-time.el (parse-time-tokenize): Clarify doc string and clean up code. diff --git a/lisp/calendar/parse-time.el b/lisp/calendar/parse-time.el index 6ba26a4..c17d04a 100644 --- a/lisp/calendar/parse-time.el +++ b/lisp/calendar/parse-time.el @@ -48,7 +48,9 @@ ((eq char ?:) ?d))) (defun parse-time-tokenize (string) - "Tokenize STRING into substrings." + "Tokenize STRING into substrings. +Each substring is a run of \"valid\" characters, i.e., lowercase +letters, digits, plus or minus signs or colons." (let ((start nil) (end (length string)) (all-digits nil) @@ -59,7 +61,8 @@ (while (and (< index end) ;Skip invalid characters. (not (setq c (parse-time-string-chars (aref string index))))) (cl-incf index)) - (setq start index all-digits (eq c ?0)) + (setq start index + all-digits (eq c ?0)) (while (and (< (cl-incf index) end) ;Scan valid characters. (setq c (parse-time-string-chars (aref string index)))) (setq all-digits (and all-digits (eq c ?0)))) commit c62371c23bde6c5cd4b0c711ff1754a8809a63d3 Author: Michael Albinus Date: Fri Mar 25 14:36:14 2016 +0100 Cleanup file notification code in Tramp * lisp/net/tramp-sh.el (tramp-sh-handle-file-notify-add-watch): Make `events' a list of symbols for "inotifywait". (tramp-sh-gvfs-monitor-dir-process-filter): Make event a list. Call `file-notify-handle-event' for better traces. (tramp-sh-inotifywait-process-filter): Check for expected events. Call `file-notify-handle-event' for better traces. diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 4ff21c1..402e1cc 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -3674,7 +3674,12 @@ Fall back to normal file name handler if no Tramp handler exists." (concat "create,modify,move,moved_from,moved_to,move_self," "delete,delete_self,ignored")) ((memq 'attribute-change flags) "attrib,ignored")) - sequence `(,command "-mq" "-e" ,events ,localname))) + sequence `(,command "-mq" "-e" ,events ,localname) + ;; Make events a list of symbols. + events + (mapcar + (lambda (x) (intern-soft (replace-regexp-in-string "_" "-" x))) + (split-string events "," 'omit)))) ;; None. (t (tramp-error v 'file-notify-error @@ -3695,7 +3700,7 @@ Fall back to normal file name handler if no Tramp handler exists." (mapconcat 'identity sequence " ")) (tramp-message v 6 "Run `%s', %S" (mapconcat 'identity sequence " ") p) (tramp-set-connection-property p "vector" v) - ;; Needed for `tramp-sh-gvfs-monitor-dir-process-filter'. + ;; Needed for process filter. (process-put p 'events events) (process-put p 'watch-name localname) (set-process-query-on-exit-flag p nil) @@ -3711,7 +3716,8 @@ Fall back to normal file name handler if no Tramp handler exists." (defun tramp-sh-gvfs-monitor-dir-process-filter (proc string) "Read output from \"gvfs-monitor-dir\" and add corresponding \ file-notify events." - (let ((remote-prefix + (let ((events (process-get proc 'events)) + (remote-prefix (with-current-buffer (process-buffer proc) (file-remote-p default-directory))) (rest-string (process-get proc 'rest-string))) @@ -3737,23 +3743,26 @@ file-notify events." (object (list proc - (intern-soft - (replace-regexp-in-string - "_" "-" (downcase (match-string 4 string)))) + (list + (intern-soft + (replace-regexp-in-string + "_" "-" (downcase (match-string 4 string))))) ;; File names are returned as absolute paths. We must ;; add the remote prefix. (concat remote-prefix file) (when file1 (concat remote-prefix file1))))) (setq string (replace-match "" nil nil string)) ;; Remove watch when file or directory to be watched is deleted. - (when (and (member (cadr object) '(moved deleted)) + (when (and (member (caadr object) '(moved deleted)) (string-equal file (process-get proc 'watch-name))) (delete-process proc)) ;; Usually, we would add an Emacs event now. Unfortunately, ;; `unread-command-events' does not accept several events at - ;; once. Therefore, we apply the callback directly. - (when (member (cadr object) (process-get proc 'events)) - (tramp-compat-funcall 'file-notify-callback object)))) + ;; once. Therefore, we apply the handler directly. + (when (member (caadr object) events) + (tramp-compat-funcall + 'file-notify-handle-event + `(file-notify ,object file-notify-callback))))) ;; Save rest of the string. (when (zerop (length string)) (setq string nil)) @@ -3762,33 +3771,37 @@ file-notify events." (defun tramp-sh-inotifywait-process-filter (proc string) "Read output from \"inotifywait\" and add corresponding file-notify events." - (tramp-message proc 6 "%S\n%s" proc string) - (dolist (line (split-string string "[\n\r]+" 'omit)) - ;; Check, whether there is a problem. - (unless - (string-match - (concat "^[^[:blank:]]+" - "[[:blank:]]+\\([^[:blank:]]+\\)+" - "\\([[:blank:]]+\\([^\n\r]+\\)\\)?") - line) - (tramp-error proc 'file-notify-error "%s" line)) - - (let ((object - (list - proc - (mapcar - (lambda (x) - (intern-soft - (replace-regexp-in-string "_" "-" (downcase x)))) - (split-string (match-string 1 line) "," 'omit)) - (match-string 3 line)))) - ;; Remove watch when file or directory to be watched is deleted. - (when (equal (cadr object) 'ignored) - (delete-process proc)) - ;; Usually, we would add an Emacs event now. Unfortunately, - ;; `unread-command-events' does not accept several events at - ;; once. Therefore, we apply the callback directly. - (tramp-compat-funcall 'file-notify-callback object)))) + (let ((events (process-get proc 'events))) + (tramp-message proc 6 "%S\n%s" proc string) + (dolist (line (split-string string "[\n\r]+" 'omit)) + ;; Check, whether there is a problem. + (unless + (string-match + (concat "^[^[:blank:]]+" + "[[:blank:]]+\\([^[:blank:]]+\\)+" + "\\([[:blank:]]+\\([^\n\r]+\\)\\)?") + line) + (tramp-error proc 'file-notify-error "%s" line)) + + (let ((object + (list + proc + (mapcar + (lambda (x) + (intern-soft + (replace-regexp-in-string "_" "-" (downcase x)))) + (split-string (match-string 1 line) "," 'omit)) + (match-string 3 line)))) + ;; Remove watch when file or directory to be watched is deleted. + (when (member (caadr object) '(move-self delete-self ignored)) + (delete-process proc)) + ;; Usually, we would add an Emacs event now. Unfortunately, + ;; `unread-command-events' does not accept several events at + ;; once. Therefore, we apply the handler directly. + (when (member (caadr object) events) + (tramp-compat-funcall + 'file-notify-handle-event + `(file-notify ,object file-notify-callback))))))) ;;; Internal Functions: