diff --git a/haskell-mode.el b/haskell-mode.el index 7658da4f9..2534db000 100644 --- a/haskell-mode.el +++ b/haskell-mode.el @@ -849,16 +849,28 @@ it that many times. Negative arg -N means move backward across N balanced expressions. This command assumes point is not in a string or comment. -Note that negative arguments do not work so well." +If unable to move over a sexp, signal `scan-error' with three +arguments: a message, the start of the obstacle (a parenthesis or +list marker of some kind), and end of the obstacle." (interactive "^p") (or arg (setq arg 1)) (if (< arg 0) - ;; Fall back to native Emacs method for negative arguments. - ;; Haskell has maximum munch rule that does not work well - ;; backwards. - (progn - (goto-char (or (scan-sexps (point) arg) (buffer-end arg))) - (backward-prefix-chars)) + (while (< arg 0) + (skip-syntax-backward "->") + ;; Navigate backwards using plain `backward-sexp', assume that it + ;; skipped over at least one Haskell expression, and jump forward until + ;; last possible point before the starting position. If applicable, + ;; `scan-error' is signalled by `backward-sexp'. + (let ((end (point)) + (forward-sexp-function nil)) + (backward-sexp) + (let ((cur (point))) + (while (< (point) end) + (setf cur (point)) + (haskell-forward-sexp) + (skip-syntax-forward "->")) + (goto-char cur))) + (setf arg (1+ arg))) (save-match-data (while (> arg 0) (when (haskell-lexeme-looking-at-token) diff --git a/tests/haskell-mode-tests.el b/tests/haskell-mode-tests.el index e3b6d7ef8..238d5029f 100644 --- a/tests/haskell-mode-tests.el +++ b/tests/haskell-mode-tests.el @@ -464,4 +464,25 @@ of sexp." (haskell-forward-sexp 1) (eq (point) 6)))) +(ert-deftest backward-sexp () + "Check if `forward-sexp-function' behaves properly on +beginning of sexp." + (should (with-temp-buffer + (haskell-mode) + (insert "(foo) bar") + (goto-char 2) + (condition-case err + (progn (backward-sexp) + nil) + (scan-error (equal (cddr err) (list 1 1))))))) + +(ert-deftest haskell-backward-sexp () + "Check if `haskell-forward-sexp' with negatives arg properly +moves over sexps." + (should (with-temp-buffer + (insert "a (b c) = d . e") + (goto-char 15) + (haskell-forward-sexp -4) + (eq (point) 3)))) + (provide 'haskell-mode-tests)