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