Subject: End of buffer error for forward-sexp

From: "Aaron S. Hawley" <[email-masked]>
Date: Mon, 18 Mar 2013 17:54:56 -0400

I would like C-M-f (`forward-sexp') to signal an error when reaching
the beginning or end of a buffer.  When running a keyboard macro that
contains this command, it should reach an error condition of "End of
buffer".  If wish to run C-0 C-x e on a file full of S-expressions and
have the macro end when an error is reached.  Currently, my macro puts
Emacs in an infinite loop.

I've attached a patch that adds these error conditions.  I also need
to correct my email address in the ChangeLog.

What I wrote isn't every strict.  I only catch the condition when the
function is started from beginning or end of buffer.  If the user
gives an argument for more S-expressions than there are between the
beginning of the buffer there is no error triggered.  This was a
compromise but also a simpler patch to contribute.  I've added the
unit tests I wrote to implement.   I hope they're helpful.

I'm not sure why this was never the case in the first place, nor do I
know what the consequence of fixing it is.  I'm not sure if the lack
of an error is necessary in the 112 libraries where this function is
used in Emacs (and probably many times more in libraries outside of
Emacs!).  I'm hoping that after the release Emacs 24.3, now is a good
time to install this in trunk and find out.

Thanks,
/a

=== modified file 'lisp/ChangeLog'
--- lisp/ChangeLog	2013-03-18 19:44:15 +0000
+++ lisp/ChangeLog	2013-03-18 20:37:01 +0000
@@ -1,3 +1,5 @@
+2013-03-18  Aaron S. Hawley  <[email-masked]>
+
+        * emacs-lisp/lisp.el (forward-sexp): Signal an error when end of
+        buffer or beginning of buffer reached.
+
@@ -1974,7 +1979,7 @@
 	(js--multi-line-declaration-indentation): New function.
 	(js--proper-indentation): Use it.

-2013-01-11  Aaron S. Hawley  <[email-masked]>
+2013-01-11  Aaron S. Hawley  <[email-masked]>

 	* calc/calc.el (calc-highlight-selections-with-faces)
 	 (calc-dispatch):

=== modified file 'lisp/emacs-lisp/lisp.el'
--- lisp/emacs-lisp/lisp.el	2013-01-01 09:11:05 +0000
+++ lisp/emacs-lisp/lisp.el	2013-03-18 19:38:50 +0000
@@ -58,6 +58,10 @@
   (or arg (setq arg 1))
   (if forward-sexp-function
       (funcall forward-sexp-function arg)
+    (when (and (> arg 0) (eobp))
+      (signal 'end-of-buffer nil))
+    (when (and (< arg 0) (bobp))
+      (signal 'beginning-of-buffer nil))
     (goto-char (or (scan-sexps (point) arg) (buffer-end arg)))
     (if (< arg 0) (backward-prefix-chars))))

=== added file 'test/automated/sexp-tests.el'
--- test/automated/sexp-tests.el	1970-01-01 00:00:00 +0000
+++ test/automated/sexp-tests.el	2013-03-18 21:51:52 +0000
@@ -0,0 +1,98 @@
+;;; sexp-tests.el --- Test S-expression support in Emacs
+
+;; Copyright (C) 2013  Aaron S. Hawley
+
+;; Author: Aaron S. Hawley <[email-masked]>
+;; Keywords: internal
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Testing of `forward-sexp'.
+
+;;; Code:
+
+(require 'ert)
+
+(ert-deftest sexp-forward-1 ()
+  "Test basics of \\[forward-sexp]."
+  (with-temp-buffer
+    (insert "()")
+    (goto-char (point-min))
+    (should (null
+             (forward-sexp 1)))))
+
+(ert-deftest sexp-backward-1 ()
+  "Test basics of \\[backward-sexp]."
+  (with-temp-buffer
+    (insert "()")
+    (should (null
+             (forward-sexp -1)))))
+
+(ert-deftest sexp-forward-1-error-eobp ()
+  "Test error when \\[forward-sexp] at `eobp'."
+  (with-temp-buffer
+    (insert "()")
+    (should-error
+     (forward-sexp 1))))
+
+(ert-deftest sexp-backward-1-error-eobp ()
+  "Test error when \\[backward-sexp] at `bobp'."
+  (with-temp-buffer
+    (insert "()")
+    (goto-char (point-min))
+    (should-error
+     (forward-sexp -1))))
+
+(ert-deftest sexp-forward-2-eobp-no-error ()
+  "Test lack of error when \\[forward-sexp] beyond `eobp'."
+  (with-temp-buffer
+    (insert "()")
+    (goto-char (point-min))
+    (should (null
+     (forward-sexp 2)))
+    (should (eobp))))
+
+(ert-deftest sexp-backward-2-bobp-no-error ()
+  "Test lack of error when \\[backward-sexp] beyond `bobp'."
+  (with-temp-buffer
+    (insert "()")
+    (should (null
+     (forward-sexp -2)))
+    (should (bobp))))
+
+(ert-deftest sexp-forward-2-eobp-subsequent-error ()
+  "Test error when \\[forward-sexp] when started from `eobp'."
+  (with-temp-buffer
+    (insert "()")
+    (goto-char (point-min))
+    (should (null
+     (forward-sexp 2)))
+    (should (eobp))
+    (should-error
+     (forward-sexp 1))))
+
+(ert-deftest sexp-backward-2-bobp-subsequent-error ()
+  "Test error when \\[backward-sexp] when started from `bobp'."
+  (with-temp-buffer
+    (insert "()")
+    (should (null
+     (forward-sexp -2)))
+    (should (bobp))
+    (should-error
+     (forward-sexp -1))))
+
+(provide 'sexp-tests)
+;;; sexp-tests.el ends here

-- 
In general, we reserve the right to have a poor
memory--the computer, however, is supposed to
remember!  Poor computer.  -- Guy Lewis Steele Jr.