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)))))))