   "Remove whitespace at the beginning and end of STR."
   (replace-regexp-in-string "^[[:space:]\n]\+\\|[[:space:]\n]\+$" "" str))
 
+(defun merlin--error-position-delta (point err)
+  "Distance between point and error."
+  (setq err (cdr (assoc 'bounds err)))
+  (cond ((< point (car err)) (cons (- (car err) point) 0))
+        ((> point (cdr err)) (cons (- point (cdr err)) 0))
+        (t (cons 0 (min (- (cdr err) point) (- point (car err)))))))
+
+(defun merlin--error-at-position (point errors)
+  "Returns error from ERRORS list most relevant at POINT"
+  (let ((err nil) (d nil))
+    (dolist (err- errors err)
+      (let ((d- (merlin--error-position-delta (point) err-)))
+        (when (or (not err) (< (car d-) (car d))
+                  (and (= (car d-) (car d)) (< (cdr d-) (cdr d))))
+          (setq d d-) (setq err err-))))))
+
 (defun merlin-show-error-on-current-line ()
   "Show the error of the current line in the echo area.
-If there is no error, do nothing."
+  If there is no error, do nothing."
   (when (and merlin-mode (not (current-message)))
-    (lexical-let* ((current-line (line-number-at-pos))
-                   (err (merlin-find-error-for-line
-                         (line-number-at-pos) merlin-pending-errors)))
-      (if err (message (merlin-chomp (cdr (assoc 'message err))))))))
+    (let* ((errors (overlays-in (line-beginning-position) (line-end-position)))
+           (err nil))
+      (setq errors (remove nil (mapcar 'merlin--overlay-pending-error errors)))
+      (setq err (merlin--error-at-position (point) errors))
+      (when err (message (merlin-chomp (cdr (assoc 'message err))))))))
+
+(defun merlin--overlay-next-property-set (point prop &optional limit)
+  "Find next point where PROP is set (like next-single-char-property-change but ensure that prop is not-nil)."
+  (let ((point- (next-single-char-property-change point prop nil limit)))
+    (if (eq point point-)
+        (setq point (next-single-char-property-change (1+ point) prop nil limit))
+      (setq point point-))
+    (unless (find-if (lambda (a) (overlay-get a prop)) (overlays-at point))
+      (setq point (next-single-char-property-change point prop nil limit)))
+    point))
+
+(defun merlin--overlay-previous-property-set (point prop &optional limit)
+  "Find previous point where PROP is set (like previous-single-char-property-change but ensure that prop is not-nil)."
+  (setq point (previous-single-char-property-change point prop nil limit))
+  (unless (find-if (lambda (a) (overlay-get a prop)) (overlays-at point))
+    (setq point (previous-single-char-property-change point prop nil limit)))
+  point)
+
+(defun merlin--error-prev-cycle ()
+  "Returns previous error, cycling when reaching beginning of buffer"
+  (let ((point (point)) (errors nil) (err nil))
+    (setq point (merlin--overlay-previous-property-set point 'merlin-pending-error))
+    (when (eq point (point-min))
+      (setq point (merlin--overlay-previous-property-set (point-max) 'merlin-pending-error (point))))
+    (setq errors (overlays-at point))
+    (setq errors (remove nil (mapcar 'merlin--overlay-pending-error errors)))
+    (setq err (merlin--error-at-position point errors))
+    (if err (cons point err) nil)))
+
+(defun merlin--error-next-cycle ()
+  "Returns next error, cycling when reaching end of buffer"
+  (let ((point (point)) (errors nil) (err nil))
+    (setq point (merlin--overlay-next-property-set point 'merlin-pending-error))
+    (when (eq point (point-max))
+      (setq point (merlin--overlay-next-property-set (point-min) 'merlin-pending-error (point))))
+    (setq errors (overlays-at point))
+    (setq errors (remove nil (mapcar 'merlin--overlay-pending-error errors)))
+    (setq err (merlin--error-at-position point errors))
+    (if err (cons point err) nil)))
+
+(defun merlin-error-prev ()
+  "Jump back to previous error."
+  (interactive)
+  (let (err (merlin--error-prev-cycle))
+    (unless err
+      (merlin--acquire-buffer)
+      (merlin--error-check nil)
+      (setq err (merlin--error-prev-cycle)))
+    (unless err (message "No more errors"))
+    (when err
+      (goto-char (car err))
+      (message (merlin-chomp (cdr (assoc 'message (cdr err)))))
+      (merlin-highlight (cdr (assoc 'bounds (cdr err))) 'next-error))))
 
 (defun merlin-error-next ()
-  "Jump to the next error."
-  (interactive)
-  (if merlin-pending-errors
-      (let ((err (pop merlin-pending-errors)))
-        (goto-char (cadr (assoc 'bounds err)))
-        (if merlin-pending-errors
-            (message "%s (%d more errors, use %s to go to the next)"
-                     (cdr (assoc 'message err))
-                     (length merlin-pending-errors)
-                     (substitute-command-keys "\\[merlin-error-next]"))
-          (message "%s" (cdr (assoc 'message err))))
-        (merlin-highlight (cdr (assoc 'bounds err)) 'next-error))
-    (next-error)))
+  "Jump to next error."
+  (interactive)
+  (let (err (merlin--error-next-cycle))
+    (unless err
+      (merlin--acquire-buffer)
+      (merlin--error-check nil)
+      (setq err (merlin--error-next-cycle)))
+    (unless err (message "No more errors"))
+    (when err
+      (goto-char (car err))
+      (message (merlin-chomp (cdr (assoc 'message (cdr err)))))
+      (merlin-highlight (cdr (assoc 'bounds (cdr err))) 'next-error))))
 
 (defun merlin-error-delete-overlays ()
   "Remove error overlays."

				     end
				     &optional length)
  "Remove an error from the pending error lists if it is edited by the user."
  (when is-after (merlin--clear-error-overlay overlay)))

(defun merlin-transform-display-errors (errors)
  "Populate the error list with ERRORS, transformed into an

(defun merlin--kill-overlapping-errors (start end)
  "Kill any pending errors that overlap the region START..END."
  ;; fixme: factor out common bits with kill-if-edited
  (dolist (overlay (overlays-in start end))
    (when (merlin--overlay overlay) (delete-overlay overlay))))

(defun merlin--completion-bounds ()
  "Returns a pair (start . end) of the content to complete"
