Commit 30f3432e authored by Artur Malabarba's avatar Artur Malabarba

* lisp/character-fold.el: Remove special case-folding support

(character-fold-to-regexp): Remove special code for
case-folding.  Char-fold search still respects the
`case-fold-search' variable (i.e., f matches F).  This only
removes the code that was added to ensure that f also matched
all chars that F matched.  For instance, after this commit, f
no longer matches 𝔽.

This was necessary because the logic created a regexp with
2^(length of the string) redundant paths.  So, when a very
long string "almost" matched, Emacs took a very long time to
figure out that it didn't.  This became particularly relevant
because isearch's lazy-highlight does a search bounded by (1-
match-end) (which, in most circumstances, is a search that
almost matches).  A recipe for this can be found in bug#22090.
parent 3a9df758
......@@ -157,8 +157,6 @@ FROM is for internal use. It specifies an index in the STRING
from which to start."
(let* ((spaces 0)
(multi-char-table (char-table-extra-slot character-fold-table 0))
(lower-case-table (current-case-table))
(upper-case-table (char-table-extra-slot lower-case-table 0))
(i (or from 0))
(end (length string))
(out nil))
......@@ -178,21 +176,9 @@ from which to start."
(setq spaces 0))
(let ((regexp (or (aref character-fold-table c)
(regexp-quote (string c))))
(alist nil))
;; Long string. The regexp would probably be too long.
(unless (> end 50)
(setq alist (aref multi-char-table c))
(when case-fold-search
(let ((other-c (aref lower-case-table c)))
(when (or (not other-c)
(eq other-c c))
(setq other-c (aref upper-case-table c)))
(when other-c
(setq alist (append alist (aref multi-char-table other-c)))
(setq regexp (concat "\\(?:" regexp "\\|"
(or (aref character-fold-table other-c)
(regexp-quote (string other-c)))
"\\)"))))))
;; Long string. The regexp would probably be too long.
(alist (unless (> end 50)
(aref multi-char-table c))))
(push (let ((matched-entries nil)
(max-length 0))
(dolist (entry alist)
......@@ -229,7 +215,7 @@ from which to start."
(push (character-fold--make-space-string spaces) out))
(let ((regexp (apply #'concat (nreverse out))))
;; Limited by `MAX_BUF_SIZE' in `regex.c'.
(if (> (length regexp) 10000)
(if (> (length regexp) 5000)
(regexp-quote string)
regexp))))
......
......@@ -98,6 +98,27 @@
;; (character-fold--test-match-exactly "a12" "xxyy")
))
(ert-deftest character-fold--speed-test ()
(dolist (string (append '("tty-set-up-initial-frame-face"
"tty-set-up-initial-frame-face-frame-faceframe-faceframe-faceframe-face")
(mapcar #'character-fold--random-word '(10 50 100
50 100))))
(message "Testing %s" string)
;; Make sure we didn't just fallback on the trivial search.
(should-not (string= (regexp-quote string)
(character-fold-to-regexp string)))
(with-temp-buffer
(save-excursion (insert string))
(let ((time (time-to-seconds (current-time))))
;; Our initial implementation of case-folding in char-folding
;; created a lot of redundant paths in the regexp. Because of
;; that, if a really long string "almost" matches, the regexp
;; engine took a long time to realise that it doesn't match.
(should-not (character-fold-search-forward (concat string "c") nil 'noerror))
;; Ensure it took less than a second.
(should (< (- (time-to-seconds (current-time))
time)
1))))))
(provide 'character-fold-tests)
;;; character-fold-tests.el ends here
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment