Subject: bug#37189: *** GMX Spamverdacht *** Re: bug#37189: Acknowledgement (25.4.1: vc-hg-ignore implementation is missing)

From: Wolfgang Scherer <[email-masked]>
Date: Wed, 28 Aug 2019 03:46:53 +0200

Am 27.08.19 um 09:45 schrieb Eli Zaretskii:
>> From: Wolfgang Scherer <[email-masked]>
>> Date: Tue, 27 Aug 2019 01:25:59 +0200
>>
>> +(defun vc-hg-ignore (file &optional directory remove)
>> +  "Ignore FILE of DIRECTORY (default is `default-directory').
>> +
>> +FILE is a file wildcard, relative to the root directory of DIRECTORY.
> I think instead of "root directory of DIRECTORY" this should say "the
> top-level directory of DIRECTORY's repository".
I disagree. This comment is a modified version of the comment for
`vc-default-ignore'. And the exact same phrase also pops up for
`vc-svn-ignore':

vc.el:1420:FILE is a file wildcard, relative to the root directory of DIRECTORY.
vc-svn.el:356:FILE is a file wildcard, relative to the root directory of DIRECTORY.

Also `top level' only appears twice in the `vc-' files:

vc-bzr.el:1070: A merge has been performed.\nA commit from the top-level directory
vc-cvs.el:904:;; at the top level for CVS.

while the phrases `root', `root directory', `repository root'
appear 23 times in strings and comments alone.

The sentence is actually utterly false for `vc-default-ignore',
since FILE is usually an absolute file path when called from a
*vc-dir* buffer. With relative paths the code is still wrong and
ends up as plain basename for the pattern.

Since the implementation of `vc-hg-ignore' corresponds to the
actual situation, I have changed the wording to reflect that using
the term `project root'.

>
>> +If FILE matches the regular expression
>> +`vc-hg-ignore-detect-wildcard', it is appended to .hgignore as
>> +is. Otherwise, FILE is escaped/expanded according to the active
>> +syntax in .hgignore. If the syntax is `regexp', FILE is quoted as
>> +anchored literal Python regexp and if FILE is a directory, the
>> +trailing `$' is omitted.  Otherwise, if the syntax is `glob',
>> +FILE is used unquoted and if FILE is a directory, a `*' is
>> +appended.
> Our convention is to leave 2 spaces between sentences in comments and
> doc strings.
Done.
>> +When called from Lisp code, if DIRECTORY is non-nil, the
> "When called from Lisp" implies that this function can be called in
> some other way, which is generally correct only with commands.  But
> this function is not a command, so I'm unsure what this means here.
I agree, and I fixed the remove option, so it actually does what
it says ;-)
> Thanks.

You're welcome.

Oops, I had forgotten to include the variable
`vc-hg-ignore-detect-wildcard'. So here is an overhauled patch.

From ec04366f6f9ba813b66d62396b1cfa7f2a865a25 Mon Sep 17 00:00:00 2001
From: Wolfgang Scherer <[email-masked]>
Date: Wed, 28 Aug 2019 03:42:22 +0200
Subject: [PATCH] Provides vc-hg-ignore to make vc-ignore work correctly

* lisp/vc/vc-hg.el: (vc-hg-ignore) Ignore file of directory.  Add
filepath relative to directory of Mercurial .hgignore file.  The
filepath is quoted according to the active ignore syntax (Bug#37189).
(vc-hg--py-regexp-quote) Quote string as regexp to match exactly
string.
---
 lisp/vc/vc-hg.el | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index f287adf..aad0bd3 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -1153,6 +1153,48 @@ REV is ignored."
   (expand-file-name ".hgignore"
 		    (vc-hg-root file)))
 
+(defvar vc-hg-ignore-detect-wildcard "[*^$]"
+  "Regular expresssion to detect wildcards in an ignored file
+  specification.")
+
+(defun vc-hg-ignore (file &optional directory remove)
+  "Ignore FILE of DIRECTORY (default is `default-directory').
+If FILE matches the regular expression
+`vc-hg-ignore-detect-wildcard', it is appended to .hgignore
+unmodified.
+Otherwise, FILE is assumed to be relative to DIRECTORY and is
+converted to a path relative to the project root of DIRECTORY.
+It is then further escaped/expanded according to the active
+syntax in .hgignore.  If the syntax is `regexp', FILE is quoted
+as anchored literal Python regexp and if FILE is a directory, the
+trailing `$' is omitted.  Otherwise, if the syntax is `glob',
+FILE is used unquoted and if FILE is a directory, a `*' is
+appended.
+If REMOVE is non-nil, remove the pattern derived from FILE from
+ignored files."
+  (let ((ignore (vc-hg-find-ignore-file (or directory default-directory)))
+        (pattern file)
+        root-dir file-path syntax)
+    (unless (string-match vc-hg-ignore-detect-wildcard pattern)
+      (setq root-dir (file-name-directory ignore))
+      (setq file-path (expand-file-name file directory))
+      (setq pattern (substring file-path (length root-dir)))
+      (save-match-data
+        (with-current-buffer (find-file-noselect ignore)
+          (goto-char (point-max))
+          (setq syntax
+                (if (re-search-backward "^ *syntax: *\\(regexp\\|glob\\)$" nil t)
+                    (match-string 1)
+                  "regexp")))
+        (setq pattern
+              (if (string= syntax "regexp")
+                  (concat "^" (vc-hg--py-regexp-quote pattern)
+                          (and (not (file-directory-p file-path)) "$"))
+                (concat pattern (and (file-directory-p file-path) "*"))))))
+    (if remove
+        (vc--remove-regexp (concat (regexp-quote pattern ) "\n?") ignore)
+      (vc--add-line pattern ignore))))
+
 ;; Modeled after the similar function in vc-bzr.el
 (defun vc-hg-checkout (file &optional rev)
   "Retrieve a revision of FILE.
@@ -1451,6 +1493,24 @@ This function differs from vc-do-command in that it invokes
 (defun vc-hg-root (file)
   (vc-find-root file ".hg"))
 
+(defvar vc-hg--py-regexp-special-chars
+  (mapcar
+   (function
+    (lambda (_c)
+      (cons _c (concat "\\" (char-to-string _c)))))
+   (append "()[]{}?*+-|^$\\.&~# \t\n\r\v\f" nil))
+  "Characters that have special meaning in Python regular expressions.")
+
+(defun vc-hg--py-regexp-quote (string)
+  "Return a Python regexp string which matches exactly STRING and nothing else.
+Ported from Python v3.7"
+  (mapconcat
+   (function
+    (lambda (_c)
+      (or (cdr (assq _c vc-hg--py-regexp-special-chars))
+          (char-to-string _c))))
+   string ""))
+
 (provide 'vc-hg)
 
 ;;; vc-hg.el ends here
-- 
2.7.4