branch: master
commit 22eeb1ef0e8b9c2221f5570984841913e3c21cbf
Author: Noam Postavsky <npost...@users.sourceforge.net>
Commit: Noam Postavsky <npost...@users.sourceforge.net>

    Fix interaction with c auto-fill
    
    Save and restore all snippet marker and overlays in the current
    paragraph around auto-fill calls.  Extend the marker/overlay snapshot
    and restore mechanisms to work in multiline regions.
    
    * yasnippet.el (yas-minor-mode): Install our auto-fill function around
    the default one.
    (yas--original-auto-fill-function): New variable.
    (yas--auto-fill): New function, saves snippet markers & overlays,
    calls original auto-fill-function, then restores markers and overlays.
    (yas--snapshot-marker-location): Take new optional parameters BEG END,
    allow for newlines in whitespace sequences (even if newline doesn't
    have whitespace syntax).
    (yas--snapshot-overlay-location): New function, takes BEG END,
    produced non-line based location snapshots.
    (yas--snapshot-overlay-line-location): Renamed from old
    `yas--snapshot-overlay-location' function.
    (yas--restore-overlay-line-location): Renamed from old
    `yas--restore-overlay-location' function, narrow to line before calling
    `yas--goto-saved-location'.
    (yas--goto-saved-location): Use whole narrowed buffer instead of
    assuming current line.
    (yas--restore-overlay-location): New function, assume whole narrowed
    buffer may be used.
    (yas--prepare-snippets-for-move, yas--finish-moving-snippets):
    (yas--indent-region): Adjust callers.
---
 yasnippet.el | 186 +++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 129 insertions(+), 57 deletions(-)

diff --git a/yasnippet.el b/yasnippet.el
index 6b55bc0..7f541e8 100644
--- a/yasnippet.el
+++ b/yasnippet.el
@@ -558,6 +558,10 @@ conditions.
 (defvar yas--snippet-id-seed 0
   "Contains the next id for a snippet.")
 
+(defvar yas--original-auto-fill-function nil
+  "The original value of `auto-fill-function'.")
+(make-variable-buffer-local 'yas--original-auto-fill-function)
+
 (defun yas--snippet-next-id ()
   (let ((id yas--snippet-id-seed))
     (cl-incf yas--snippet-id-seed)
@@ -796,12 +800,18 @@ Key bindings:
              (set-default name nil)
              (set (make-local-variable name) t)))
          ;; Perform JIT loads
-         ;;
-         (yas--load-pending-jits))
+         (yas--load-pending-jits)
+         ;; Install auto-fill handler.
+         (when (and auto-fill-function
+                    (not (eq auto-fill-function #'yas--auto-fill)))
+           (setq yas--original-auto-fill-function auto-fill-function)
+           (setq auto-fill-function #'yas--auto-fill)))
         (t
-         ;; Uninstall the direct keymaps and the post-command hook
-         ;;
+         ;; Uninstall the direct keymaps, post-command hook, and
+         ;; auto-fill handler.
          (remove-hook 'post-command-hook #'yas--post-command-handler t)
+         (when (local-variable-p 'yas--original-auto-fill-function)
+           (setq auto-fill-function yas--original-auto-fill-function))
          (setq emulation-mode-map-alists
                (remove 'yas--direct-keymaps emulation-mode-map-alists)))))
 
@@ -3307,7 +3317,7 @@ This renders the snippet as ordinary text."
                         (yas--snapshot-marker-location m))
              (set-marker m nil)))
          snippet)
-        (let ((ctrl-ov (yas--snapshot-overlay-location
+        (let ((ctrl-ov (yas--snapshot-overlay-line-location
                         (yas--snippet-control-overlay snippet))))
           (push (list ctrl-ov dst-base-line snippet) to-move)
           (delete-overlay (car ctrl-ov))))
@@ -3341,7 +3351,10 @@ This renders the snippet as ordinary text."
                (lambda (l-m-r-w)
                  (goto-char base-pos)
                  (forward-line (nth 0 l-m-r-w))
-                 (yas--restore-marker-location (cdr l-m-r-w))
+                 (save-restriction
+                   (narrow-to-region (line-beginning-position)
+                                     (line-end-position))
+                   (yas--restore-marker-location (cdr l-m-r-w)))
                  (nth 1 l-m-r-w))
                snippet)
            (goto-char base-pos)
@@ -3560,6 +3573,33 @@ field start.  This hook does nothing if an undo is in 
progress."
               (yas--update-mirrors snippet)))
         (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
         (delete-overlay overlay)))))
+
+(defun yas--auto-fill ()
+  (let* ((orig-point (point))
+         (end (progn (forward-paragraph) (point)))
+         (beg (progn (backward-paragraph) (point)))
+         (snippets (yas-active-snippets beg end))
+         (remarkers nil)
+         (reoverlays nil))
+    (dolist (snippet snippets)
+      (dolist (m (yas--collect-snippet-markers snippet))
+        (push (yas--snapshot-marker-location m beg end) remarkers))
+      (push (yas--snapshot-overlay-location
+             (yas--snippet-control-overlay snippet) beg end)
+            reoverlays))
+    (goto-char orig-point)
+    (let ((yas--inhibit-overlay-hooks t))
+      (funcall yas--original-auto-fill-function))
+    (save-excursion
+      (setq end (progn (forward-paragraph) (point)))
+      (setq beg (progn (backward-paragraph) (point))))
+    (save-excursion
+      (save-restriction
+        (narrow-to-region beg end)
+        (mapc #'yas--restore-marker-location remarkers)
+        (mapc #'yas--restore-overlay-location reoverlays))
+      (mapc #'yas--update-mirrors snippets))))
+
 
 ;;; Apropos protection overlays:
 ;;
@@ -4036,34 +4076,48 @@ Meant to be called in a narrowed buffer, does various 
passes"
 ;; indentation generally affects whitespace at the beginning, not the
 ;; end.
 ;;
+;; Two other cases where we apply a similar strategy:
+;;
+;; 1. Handling `auto-fill-mode', in this case we need to use the
+;; current paragraph instead of line.
+;;
+;; 2. Moving snippets from an `org-src' temp buffer into the main org
+;; buffer, in this case we need to count the line offsets (because org
+;; may add indentation on each line making character positions
+;; unreliable).
+;;
 ;; This is all best-effort heuristic stuff, but it should cover 99% of
 ;; use-cases.
 
-(defun yas--snapshot-marker-location (marker)
+(defun yas--snapshot-marker-location (marker &optional beg end)
   "Returns info for restoring MARKER's location after indent.
-The returned value is a list of the form (MARKER REGEXP WS-COUNT).
-If MARKER is not on current line, then return nil."
-  (when (and (<= (line-beginning-position) marker)
-             (<= marker (line-end-position)))
-    (let ((before
-           (split-string (buffer-substring-no-properties
-                          (line-beginning-position) marker) "[[:space:]]+" t))
-          (after
-           (split-string (buffer-substring-no-properties
-                          marker (line-end-position)) "[[:space:]]+" t)))
-      (list marker
-            (concat "[[:space:]]*"
-                    (mapconcat (lambda (s)
-                                 (if (eq s marker) "\\(\\)"
-                                   (regexp-quote s)))
-                               (nconc before (list marker) after)
-                               "[[:space:]]*"))
-            (progn (goto-char marker)
-                   (skip-syntax-forward " " (line-end-position))
-                   (- (point) marker))))))
-
-(defun yas--snapshot-overlay-location (overlay)
-  "Like `yas--snapshot-marker-location', but for overlays.
+The returned value is a list of the form (MARKER REGEXP WS-COUNT)."
+  (unless beg (setq beg (line-beginning-position)))
+  (unless end (setq end (line-end-position)))
+  (let ((before (split-string (buffer-substring-no-properties beg marker)
+                              "[[:space:]\n]+" t))
+        (after (split-string (buffer-substring-no-properties marker end)
+                             "[[:space:]\n]+" t)))
+    (list marker
+          (concat "[[:space:]\n]*"
+                  (mapconcat (lambda (s)
+                               (if (eq s marker) "\\(\\)"
+                                 (regexp-quote s)))
+                             (nconc before (list marker) after)
+                             "[[:space:]\n]*"))
+          (progn (goto-char marker)
+                 (skip-syntax-forward " " end)
+                 (- (point) marker)))))
+
+(defun yas--snapshot-overlay-location (overlay beg end)
+  "Like `yas--snapshot-marker-location' for overlays.
+The returned format is (OVERLAY (RE WS) (RE WS))."
+  (list overlay
+        (cdr (yas--snapshot-marker-location (overlay-start overlay) beg end))
+        (cdr (yas--snapshot-marker-location (overlay-end overlay) beg end))))
+
+(defun yas--snapshot-overlay-line-location (overlay)
+  "Return info for restoring OVERLAY's line based location.
 The returned format is (OVERLAY (LINE RE WS) (LINE RE WS))."
   (let ((loc-beg (progn (goto-char (overlay-start overlay))
                         (yas--snapshot-marker-location (point))))
@@ -4076,35 +4130,47 @@ The returned format is (OVERLAY (LINE RE WS) (LINE RE 
WS))."
     (list overlay loc-beg loc-end)))
 
 (defun yas--goto-saved-location (regexp ws-count)
-  "Move point to location saved by `yas--snapshot-marker-location'."
-  (beginning-of-line)
-  (save-restriction
-    ;; Narrowing is the only way to limit `looking-at'.
-    (narrow-to-region (point) (line-end-position))
-    (if (not (looking-at regexp))
-        (lwarn '(yasnippet re-marker) :warning
-               "Couldn't find: %S" regexp)
-      (goto-char (match-beginning 1))
-      (skip-syntax-forward " ")
-      (skip-syntax-backward " " (- (point) ws-count)))))
+  "Move point to location saved by `yas--snapshot-marker-location'.
+Buffer must be narrowed to BEG..END used to create the snapshot info."
+  (goto-char (point-min))
+  (if (not (looking-at regexp))
+      (lwarn '(yasnippet re-marker) :warning
+             "Couldn't find: %S" regexp)
+    (goto-char (match-beginning 1))
+    (skip-syntax-forward " ")
+    (skip-syntax-backward " " (- (point) ws-count))))
 
 (defun yas--restore-marker-location (re-marker)
   "Restores marker based on info from `yas--snapshot-marker-location'.
-Assumes point is currently on the 'same' line as before."
+Buffer must be narrowed to BEG..END used to create the snapshot info."
   (apply #'yas--goto-saved-location (cdr re-marker))
   (set-marker (car re-marker) (point)))
 
 (defun yas--restore-overlay-location (ov-locations)
-  "Restores overlay based on info from `yas--snapshot-overlay-location'."
-  (move-overlay (car ov-locations)
-                (save-excursion
-                  (forward-line (car (nth 1 ov-locations)))
-                  (apply #'yas--goto-saved-location (cdr (nth 1 ov-locations)))
-                  (point))
-                (save-excursion
-                  (forward-line (car (nth 2 ov-locations)))
-                  (apply #'yas--goto-saved-location (cdr (nth 2 ov-locations)))
-                  (point))))
+  "Restores marker based on info from `yas--snapshot-marker-location'.
+Buffer must be narrowed to BEG..END used to create the snapshot info."
+  (cl-destructuring-bind (overlay loc-beg loc-end) ov-locations
+    (move-overlay overlay
+                  (progn (apply #'yas--goto-saved-location loc-beg)
+                         (point))
+                  (progn (apply #'yas--goto-saved-location loc-end)
+                         (point)))))
+
+
+(defun yas--restore-overlay-line-location (ov-locations)
+  "Restores overlay based on info from `yas--snapshot-overlay-line-location'."
+  (save-restriction
+    (move-overlay (car ov-locations)
+                  (save-excursion
+                    (forward-line (car (nth 1 ov-locations)))
+                    (narrow-to-region (line-beginning-position) 
(line-end-position))
+                    (apply #'yas--goto-saved-location (cdr (nth 1 
ov-locations)))
+                    (point))
+                  (save-excursion
+                    (forward-line (car (nth 2 ov-locations)))
+                    (narrow-to-region (line-beginning-position) 
(line-end-position))
+                    (apply #'yas--goto-saved-location (cdr (nth 2 
ov-locations)))
+                    (point)))))
 
 (defun yas--indent-region (from to snippet)
   "Indent the lines between FROM and TO with `indent-according-to-mode'.
@@ -4115,15 +4181,21 @@ The SNIPPET's markers are preserved."
       (goto-char from)
       (save-restriction
         (widen)
-        (cl-loop if (/= (line-beginning-position) (line-end-position)) do
+        (cl-loop for bol = (line-beginning-position)
+                 for eol = (line-end-position)
+                 if (/= bol eol) do
                  ;; Indent each non-empty line.
-                 (let ((remarkers
-                        (delq nil (mapcar #'yas--snapshot-marker-location
-                                          snippet-markers))))
+                 (let ((remarkers nil))
+                   (dolist (m snippet-markers)
+                     (when (and (<= bol m) (<= m eol))
+                       (push (yas--snapshot-marker-location m bol eol)
+                             remarkers)))
                    (unwind-protect
                        (progn (back-to-indentation)
                               (indent-according-to-mode))
-                     (mapc #'yas--restore-marker-location remarkers)))
+                     (save-restriction
+                       (narrow-to-region bol (line-end-position))
+                       (mapc #'yas--restore-marker-location remarkers))))
                  while (and (zerop (forward-line 1))
                             (< (point) to)))))))
 

Reply via email to