 (save-excursion
    (goto-char po-start-of-entry)
    (if (re-search-forward rx po-start-of-msgctxt t)
        (list (cons (copy-marker (match-beginning 0))
                    (copy-marker (match-end 0))))
      nil)))

(defun po-previous-untranslated-regions ()
  "Return the list of previous untranslated regions in the current entry."
  (append (po-previous-untranslated-region-for po-any-previous-msgctxt-regexp)
          (po-previous-untranslated-region-for po-any-previous-msgid-regexp)
          (po-previous-untranslated-region-for po-any-previous-msgid_plural-regexp)))

(defun po-delete-previous-untranslated ()
  "Delete the previous msgctxt, msgid, msgid_plural fields (marked as #|
comments) from the current entry."
  (interactive)
  (po-find-span-of-entry)
  (let ((buffer-read-only po-read-only))
    (dolist (region (po-previous-untranslated-regions))
      (delete-region (car region) (cdr region))))
  (po-redisplay))

(defun po-maybe-delete-previous-untranslated ()
  "Delete the previous msgctxt, msgid, msgid_plural fields (marked as #|
comments) from the current entry, if the user gives the permission."
  (po-find-span-of-entry)
  (let ((previous-regions (po-previous-untranslated-regions)))
    (if previous-regions
        (if (or (eq po-auto-delete-previous-msgid t)
                (and (eq po-auto-delete-previous-msgid 'ask)
                     (let ((overlays nil))
                       (unwind-protect
                           (progn
                             (setq overlays
                                   (mapcar (function
                                             (lambda (region)
                                               (let ((overlay (po-create-overlay)))
                                                 (po-highlight overlay (car region) (cdr region))
                                                 overlay)))
                                           previous-regions))
                             ;; Scroll, to show the previous-regions.
                             (goto-char (car (car previous-regions)))
                             (prog1 (y-or-n-p (_"Delete previous msgid comments? "))
                                    (message "")))
                         (mapc 'po-dehighlight overlays)))))
            (let ((buffer-read-only po-read-only))
              (dolist (region previous-regions)
                (delete-region (car region) (cdr region))))))))

;;; Editing management and submode.

;; In a string edit buffer, BACK-POINTER points to one of the slots of the
;; list EDITED-FIELDS kept in the PO buffer.  See its description elsewhere.
;; Reminder: slots have the form (ENTRY-MARKER EDIT-BUFFER OVERLAY-INFO).

(defvar po-subedit-back-pointer)

(defun po-clean-out-killed-edits ()
  "From EDITED-FIELDS, clean out any edit having a killed edit buffer."
  (let ((cursor po-edited-fields))
    (while cursor
      (let ((slot (car cursor)))
        (setq cursor (cdr cursor))
        (if (buffer-name (nth 1 slot))
            nil
          (let ((overlay (nth 2 slot)))
            (and overlay (po-dehighlight overlay)))
          (setq po-edited-fields (delete slot po-edited-fields)))))))

(defun po-check-all-pending-edits ()
  "Resume any pending edit.  Return nil if some remains."
  (po-clean-out-killed-edits)
  (or (null po-edited-fields)
      (let ((slot (car po-edited-fields)))
        (goto-char (nth 0 slot))
        (pop-to-buffer (nth 1 slot))
        (message po-subedit-message)
        nil)))

(defun po-check-for-pending-edit (position)
  "Resume any pending edit at POSITION.  Return nil if such edit exists."
  (po-clean-out-killed-edits)
  (let ((marker (make-marker)))
    (set-marker marker position)
    (let ((slot (assoc marker po-edited-fields)))
      (if slot
          (progn
            (goto-char marker)
            (pop-to-buffer (nth 1 slot))
            (message po-subedit-message)))
      (not slot))))

(defun po-edit-out-full ()
  "Get out of PO mode, leaving PO file buffer in fundamental mode."
  (interactive)
  (if (po-check-all-pending-edits)
      ;; Don't ask the user for confirmation, since he has explicitly asked
      ;; for it.
      (progn
        (setq buffer-read-only po-read-only)
        (fundamental-mode)
        (message (_"Type 'M-x po-mode RET' once done")))))

(defun po-ediff-quit ()
  "Quit ediff and exit `recursive-edit'."
  (interactive)
  (ediff-quit t)
  (exit-recursive-edit))

(add-hook 'ediff-keymap-setup-hook
          '(lambda ()
             (define-key ediff-mode-map "Q" 'po-ediff-quit)))

;; Avoid byte compiler warnings.
(defvar entry-buffer)

(defun po-ediff-buffers-exit-recursive (b1 b2 oldbuf end)
  "Ediff buffer B1 and B2, pop back to OLDBUF and replace the old variants.
This function will delete the first two variants in OLDBUF, call
`ediff-buffers' to compare both strings and replace the two variants in
OLDBUF with the contents of B2.
Once done kill B1 and B2.

For more info cf. `po-subedit-ediff'."
  (ediff-buffers b1 b2)
  (recursive-edit)
  (pop-to-buffer oldbuf)
  (delete-region (point-min) end)
  (insert-buffer-substring b2)
  (mapc 'kill-buffer `(,b1 ,b2))
  (display-buffer entry-buffer t))

(defun po-subedit-ediff ()
  "Edit the subedit buffer using `ediff'.
`po-subedit-ediff' calls `po-ediff-buffers-exit-recursive' to edit translation
variants side by side if they are actually different; if variants are equal just
delete the first one.

`msgcat' is able to produce those variants; every variant is marked with:

#-#-#-#-#  file name reference  #-#-#-#-#

Put changes in second buffer.

When done with the `ediff' session press \\[exit-recursive-edit] exit to
`recursive-edit', or call \\[po-ediff-quit] (`Q') in the ediff control panel."
  (interactive)
  (let* ((marker-regex "^#-#-#-#-#  \\(.*\\)  #-#-#-#-#\n")
         (buf1 " *po-msgstr-1") ; default if first marker is missing
         buf2 start-1 end-1 start-2 end-2
         (back-pointer po-subedit-back-pointer)
         (entry-marker (nth 0 back-pointer))
         (entry-buffer (marker-buffer entry-marker)))
    (goto-char (point-min))
    (if (looking-at marker-regex)
        (and (setq buf1 (match-string-no-properties 1))
             (forward-line 1)))
    (setq start-1 (point))
    (if (not (re-search-forward marker-regex (point-max) t))
        (error "Only 1 msgstr found")
      (setq buf2 (match-string-no-properties 1)
            end-1 (match-beginning 0))
      (let ((oldbuf (current-buffer)))
        (save-current-buffer
          (set-buffer (get-buffer-create
                       (generate-new-buffer-name buf1)))
          (setq buffer-read-only nil)
          (erase-buffer)
          (insert-buffer-substring oldbuf start-1 end-1)
          (setq buffer-read-only t))

        (setq start-2 (point))
        (save-excursion
          ;; check for a third variant; if found ignore it
          (if (re-search-forward marker-regex (point-max) t)
              (setq end-2 (match-beginning 0))
            (setq end-2 (goto-char (1- (point-max))))))
        (save-current-buffer
          (set-buffer (get-buffer-create
                       (generate-new-buffer-name buf2)))
          (erase-buffer)
          (insert-buffer-substring oldbuf start-2 end-2))

        (if (not (string-equal (buffer-substring-no-properties start-1 end-1)
                               (buffer-substring-no-properties start-2 end-2)))
            (po-ediff-buffers-exit-recursive buf1 buf2 oldbuf end-2)
          (message "Variants are equal; delete %s" buf1)
          (forward-line -1)
          (delete-region (point-min) (point)))))))

(defun po-subedit-abort ()
  "Exit the subedit buffer, merely discarding its contents."
  (interactive)
  (let* ((edit-buffer (current-buffer))
         (back-pointer po-subedit-back-pointer)
         (entry-marker (nth 0 back-pointer))
         (overlay-info (nth 2 back-pointer))
         (entry-buffer (marker-buffer entry-marker)))
    (if (null entry-buffer)
        (error (_"Corresponding PO buffer does not exist anymore"))
      (or (one-window-p) (delete-window))
      (switch-to-buffer entry-buffer)
      (goto-char entry-marker)
      (and overlay-info (po-dehighlight overlay-info))
      (kill-buffer edit-buffer)
      (setq po-edited-fields (delete back-pointer po-edited-fields)))))

(defun po-subedit-exit ()
  "Exit the subedit buffer, replacing the string in the PO buffer."
  (interactive)
  (goto-char (point-max))
  (skip-chars-backward " \t\n")
  (if (eq (preceding-char) ?<)
      (delete-region (1- (point)) (point-max)))
  (run-hooks 'po-subedit-exit-hook)
  (let ((string (buffer-string)))
    (po-subedit-abort)
    (po-find-span-of-entry)
    (cond ((= (point) po-start-of-msgid)
           (po-set-comment string)
           (po-redisplay))
          ((= (point) po-start-of-msgstr-form)
           (if (po-set-msgstr-form string)
               (progn
                 (po-maybe-delete-previous-untranslated)
                 (if (and po-auto-fuzzy-on-edit
                          (eq po-entry-type 'translated))
                     (progn
                       (po-decrease-type-counter)
                       (po-add-attribute "fuzzy")
                       (po-current-entry)
                       (po-increase-type-counter))))))
          (t (debug)))))

(defun po-edit-string (string type expand-tabs)
  "Prepare a pop up buffer for editing STRING, which is of a given TYPE.
TYPE may be 'comment or 'msgstr.  If EXPAND-TABS, expand tabs to spaces.
Run functions on po-subedit-mode-hook."
  (let ((marker (make-marker)))
    (set-marker marker (cond ((eq type 'comment) po-start-of-msgid)
                             ((eq type 'msgstr) po-start-of-msgstr-form)))
    (if (po-check-for-pending-edit marker)
        (let ((edit-buffer (generate-new-buffer
                            (concat "*" (buffer-name) "*")))
              (edit-coding buffer-file-coding-system)
              (buffer (current-buffer))
              overlay slot)
          (if (and (eq type 'msgstr) po-highlighting)
              ;; ;; Try showing all of msgid in the upper window while editing.
              ;; (goto-char (1- po-start-of-msgstr-block))
              ;; (recenter -1)
              (save-excursion
                (goto-char po-start-of-entry)
                (re-search-forward po-any-msgid-regexp nil t)
                (let ((end (1- (match-end 0))))
                  (goto-char (match-beginning 0))
                  (re-search-forward "msgid +" nil t)
                  (setq overlay (po-create-overlay))
                  (po-highlight overlay (point) end buffer))))
          (setq slot (list marker edit-buffer overlay)
                po-edited-fields (cons slot po-edited-fields))
          (pop-to-buffer edit-buffer)
          (text-mode)
          (set (make-local-variable 'po-subedit-back-pointer) slot)
          (set (make-local-variable 'indent-line-function)
               'indent-relative)
          (setq buffer-file-coding-system edit-coding)
          (setq local-abbrev-table po-mode-abbrev-table)
          (erase-buffer)
          (insert string "<")
          (goto-char (point-min))
          (and expand-tabs (setq indent-tabs-mode nil))
          (use-local-map po-subedit-mode-map)
          (easy-menu-define po-subedit-mode-menu po-subedit-mode-map ""
            po-subedit-mode-menu-layout)
          (set-syntax-table po-subedit-mode-syntax-table)
          (run-hooks 'po-subedit-mode-hook)
          (message po-subedit-message)))))

(defun po-edit-comment ()
  "Use another window to edit the current translator comment."
  (interactive)
  (po-find-span-of-entry)
  (po-edit-string (po-get-comment nil) 'comment nil))

(defun po-edit-comment-and-ediff ()
  "Use `ediff' to edit the current translator comment.
This function calls `po-edit-msgstr' and `po-subedit-ediff'; for more info
read `po-subedit-ediff' documentation."
  (interactive)
  (po-edit-comment)
  (po-subedit-ediff))

(defun po-edit-msgstr ()
  "Use another window to edit the current msgstr."
  (interactive)
  (po-find-span-of-entry)
  (po-edit-string (if (and po-auto-edit-with-msgid
                           (eq po-entry-type 'untranslated))
                      (po-get-msgid)
                    (po-get-msgstr-form))
                  'msgstr
                  t))

(defun po-edit-msgstr-and-ediff ()
  "Use `ediff' to edit the current msgstr.
This function calls `po-edit-msgstr' and `po-subedit-ediff'; for more info
read `po-subedit-ediff' documentation."
  (interactive)
  (po-edit-msgstr)
  (po-subedit-ediff))
