Subject: Re: bug#16800: 24.3; flyspell works slow on very short words at the end of big file

From: Agustin Martin <[email-masked]>
Date: Fri, 28 Feb 2014 12:45:45 +0100

On Wed, Feb 26, 2014 at 09:32:02PM +0100, Agustin Martin wrote:
> On Mon, Feb 24, 2014 at 08:03:17PM +0400, Aleksey Cherepanov wrote:
> > I played with different (maybe wrong) implementations of
> > flyspell-word-search-backward and measured time against t.txt
> > (produced by the one-liner). All implementations are attached.
> 
> [ ... Tons of extensive and impressive debugging ... ]
> 
> > We could avoid capturing at all. And it works faster as shown by 4
> > last functions.
> 
> Hi, 
> 
> Thanks a lot for the extensive debugging and for all the suggestions. I
> have been playing with something based in your last function, but trying
> to get something more compact, see below current status
[ ... ]
> I did some efficiency test and it seemed similar to those of your efficient
> functions. Need to check further for corner cases, bugs, etc ...

Hi, Aleksey

Please find attached my first candidate for commit. Is similar to what I
sent before, but needed to add an explicit check for word at eob in
`flyspell-word-search-forward'. 

Will try to have more testing before committing. Seems to work well with the
file generated by your one-liner, even with corner cases like new
misspellings added at bob or eob, but the wider the testing the better.

Hope no one will generate files with words containing something in
OTHERCHARS.

Thanks for all your help

-- 
Agustin
--- flyspell.el.orig	2014-02-26 19:05:29.651986038 +0100
+++ flyspell.el	2014-02-28 12:01:03.930010553 +0100
@@ -1048,10 +1048,21 @@
 ;;*---------------------------------------------------------------------*/
 (defun flyspell-word-search-backward (word bound &optional ignore-case)
   (save-excursion
-    (let ((r '())
-	  (inhibit-point-motion-hooks t)
-	  p)
-      (while (and (not r) (setq p (search-backward word bound t)))
+    (let* ((r '())
+	   (inhibit-point-motion-hooks t)
+	   (flyspell-not-casechars (flyspell-get-not-casechars))
+	   (word-re (concat flyspell-not-casechars
+			    (regexp-quote word)
+			    flyspell-not-casechars))
+	   p)
+      (while
+	  (and (not r)
+	       (setq p (if (re-search-backward word-re bound t)
+			   ;; word-re match begins one char before word
+			   (progn (forward-char) (point))
+			 ;; Check above does not match similar word at b-o-b
+			 (goto-char (point-min))
+			 (search-forward word (length word) t))))
 	(let ((lw (flyspell-get-word)))
 	  (if (and (consp lw)
 		   (if ignore-case
@@ -1066,10 +1077,25 @@
 ;;*---------------------------------------------------------------------*/
 (defun flyspell-word-search-forward (word bound)
   (save-excursion
-    (let ((r '())
-	  (inhibit-point-motion-hooks t)
-	  p)
-      (while (and (not r) (setq p (search-forward word bound t)))
+    (let* ((r '())
+	   (inhibit-point-motion-hooks t)
+	   (word-end (nth 2 (flyspell-get-word)))
+	   (flyspell-not-casechars (flyspell-get-not-casechars))
+	   (word-re (concat flyspell-not-casechars
+			    (regexp-quote word)
+			    flyspell-not-casechars))
+	   p)
+      (while
+	  (and (not r)
+	       (setq p (if (= word-end (point-max))
+			   nil ;; Current word is at e-o-b. No forward search
+			 (if (re-search-forward word-re bound t)
+			     ;; word-re match ends one char after word
+			     (progn (backward-char) (point))
+			   ;; Check above does not match similar word at e-o-b
+			   (goto-char (point-max))
+			   (search-backward word (- (point-max)
+						    (length word)) t)))))
 	(let ((lw (flyspell-get-word)))
 	  (if (and (consp lw) (string-equal (car lw) word))
 	      (setq r p)