;;; po-mode.el -- for helping GNU gettext lovers to edit PO files.
-;;; Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+;;; Copyright (C) 1995, 96, 97, 98, 99, 00, 01 Free Software Foundation, Inc.
;;; François Pinard <pinard@iro.umontreal.ca>, 1995.
;;; Helped by Greg McGary <gkm@magilla.cichlid.com>.
;;; You may also adjust some variables, below, by defining them in your
;;; `.emacs' file, either directly or through command `M-x customize'.
\f
-;;; Emacs portability matters.
-
-;;; Most portability matters are addressed in this page. All other cases
-;;; involve one of `eval-and-compile' or `fboundp', just search for these.
+;;; Emacs portability matters - part I.
+;;; Here is the minimum for customization to work. See part II.
;; Identify which Emacs variety is being used.
(eval-and-compile
nil)
(defmacro defcustom (var value doc &rest args)
(` (defvar (, var) (, value) (, doc))))))
+\f
+;;; Customisation.
+
+(defgroup po nil
+ "Major mode for editing PO files"
+ :group 'i18n)
-;; Protect string comparisons from text properties.
+(defcustom po-auto-edit-with-msgid nil
+ "*Automatically use msgid when editing untranslated entries."
+ :type 'boolean
+ :group 'po)
+
+(defcustom po-auto-fuzzy-on-edit nil
+ "*Automatically mark entries fuzzy when being edited."
+ :type 'boolean
+ :group 'po)
+
+(defcustom po-auto-select-on-unfuzzy nil
+ "*Automatically select some new entry while making an entry not fuzzy."
+ :type 'boolean
+ :group 'po)
+
+(defcustom po-auto-replace-revision-date 't
+ "*Automatically revise date in headers. Value is nil, t, or ask."
+ :type '(choice (const nil)
+ (const t)
+ (const ask))
+ :group 'po)
+
+(defcustom po-default-file-header "\
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Free Software Foundation, Inc.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid \"\"
+msgstr \"\"
+\"Project-Id-Version: PACKAGE VERSION\\n\"
+\"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\\n\"
+\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"
+\"Language-Team: LANGUAGE <LL@li.org>\\n\"
+\"MIME-Version: 1.0\\n\"
+\"Content-Type: text/plain; charset=CHARSET\\n\"
+\"Content-Transfer-Encoding: 8bit\\n\"
+"
+ "*Default PO file header."
+ :type 'string
+ :group 'po)
+
+(defcustom po-highlighting (or po-EMACS20 po-XEMACS)
+ "*Highlight text whenever appropriate, when non-nil. However, on older
+Emacses, a yet unexplained highlighting bug causes files to get mangled."
+ :type 'boolean
+ :group 'po)
+
+(defcustom po-highlight-face 'highlight
+ "*The face used for PO mode highlighting. For Emacses with overlays.
+Possible values are `highlight', `modeline', `secondary-selection',
+`region', and `underline'.
+This variable can be set by the user to whatever face they desire.
+It's most convenient if the cursor color and highlight color are
+slightly different."
+ :type 'face
+ :group 'po)
+
+(defcustom po-gzip-uuencode-command "gzip -9 | uuencode -m"
+ "*The filter to use for preparing a mail invoice of the PO file.
+Normally \"gzip -9 | uuencode -m\", remove the -9 for lesser compression,
+or remove the -m if you are not using the GNU version of `uuencode'."
+ :type 'string
+ :group 'po)
+\f
+;;; Emacs portability matters - part II.
+
+;;; Many portability matters are addressed in this page. All other cases
+;;; involve one of `eval-and-compile' or `fboundp', just search for these.
+
+;; Protect string comparisons from text properties if possible.
(eval-and-compile
(fset 'po-buffer-substring
(symbol-function (if (fboundp 'buffer-substring-no-properties)
;; Handle missing `with-temp-buffer' function.
(eval-and-compile
- (if nil ; FIXME: just testing... (fboundp 'with-temp-buffer)
-
+ (if (fboundp 'with-temp-buffer)
(fset 'po-with-temp-buffer (symbol-function 'with-temp-buffer))
(defmacro po-with-temp-buffer (&rest forms)
;; Handle missing `kill-new' function.
(eval-and-compile
(if (fboundp 'kill-new)
-
(fset 'po-kill-new (symbol-function 'kill-new))
(defun po-kill-new (string)
;; Handle missing `force-mode-line-update' function.
(eval-and-compile
(if (fboundp 'force-mode-line-update)
-
(fset 'po-force-mode-line-update
(symbol-function 'force-mode-line-update))
(start (marker-position (car overlay)))
(end (marker-position (cdr overlay))))
(and buffer
- (name-buffer buffer)
+ (buffer-name buffer)
(po-highlight overlay start end buffer))))
(defun po-dehighlight (overlay)
(start (marker-position (car overlay)))
(end (marker-position (cdr overlay))))
(and buffer
- (name-buffer buffer)
+ (buffer-name buffer)
(po-highlight overlay start end buffer))))
(defun po-dehighlight (overlay)
)))
\f
-;;; Customisation.
-
-(defgroup po nil
- "Major mode for editing PO files"
- :group 'i18n)
-
-(defcustom po-auto-edit-with-msgid nil
- "*Automatically use msgid when editing untranslated entries."
- :type 'boolean
- :group 'po)
-
-(defcustom po-auto-fuzzy-on-edit nil
- "*Automatically mark entries fuzzy when being edited."
- :type 'boolean
- :group 'po)
+;;; Buffer local variables.
-(defcustom po-auto-select-on-unfuzzy nil
- "*Automatically select some new entry while making an entry not fuzzy."
- :type 'boolean
- :group 'po)
-
-(defcustom po-auto-replace-revision-date t
- "*Automatically revise date in headers. Value is nil, t, or ask."
- :type '(choice (const nil)
- (const t)
- (const ask))
- :group 'po)
-
-(defcustom po-default-file-header "\
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR Free Software Foundation, Inc.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid \"\"
-msgstr \"\"
-\"Project-Id-Version: PACKAGE VERSION\\n\"
-\"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\\n\"
-\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"
-\"Language-Team: LANGUAGE <LL@li.org>\\n\"
-\"MIME-Version: 1.0\\n\"
-\"Content-Type: text/plain; charset=CHARSET\\n\"
-\"Content-Transfer-Encoding: 8bit\\n\"
-"
- "*Default PO file header."
- :type 'string
- :group 'po)
-
-(defcustom po-highlighting (or po-EMACS20 po-XEMACS)
- "*Highlight text whenever appropriate, when non-nil. However, on older
-Emacses, a yet unexplained highlighting bug causes files to get mangled."
- :type 'boolean
- :group 'po)
-
-(defcustom po-highlight-face 'highlight
- "*The face used for PO mode highlighting. For Emacses with overlays.
-Possible values are `highlight', `modeline', `secondary-selection',
-`region', and `underline'.
-This variable can be set by the user to whatever face they desire.
-It's most convenient if the cursor color and highlight color are
-slightly different."
- :type 'face
- :group 'po)
-
-(defcustom po-gzip-uuencode-command "gzip -9 | uuencode -m"
- "*The filter to use for preparing a mail invoice of the PO file.
-Normally \"gzip -9 | uuencode -m\", remove the -9 for lesser compression,
-or remove the -m if you are not using the GNU version of `uuencode'."
- :type 'string
- :group 'po)
-\f
-;;; The following block of declarations has the main purpose of avoiding
-;;; byte compiler warnings. It also introduces some documentation for
-;;; each of these variables, all meant to be local to PO mode buffers.
+;; The following block of declarations has the main purpose of avoiding
+;; byte compiler warnings. It also introduces some documentation for
+;; each of these variables, all meant to be local to PO mode buffers.
;; Flag telling that MODE-LINE-STRING should be displayed. See `Window'
;; page below. Exceptionally, this variable is local to *all* buffers.
-
(defvar po-mode-flag)
;; PO buffers are kept read-only to prevent random modifications. READ-ONLY
;; holds the value of the read-only flag before PO mode was entered.
-
(defvar po-read-only)
;; The current entry extends from START-OF-ENTRY to END-OF-ENTRY, it
;; includes preceding whitespace and excludes following whitespace. The
;; start of keyword lines are START-OF-MSGID and START-OF-MSGSTR.
;; ENTRY-TYPE classifies the entry.
-
(defvar po-start-of-entry)
(defvar po-start-of-msgid)
(defvar po-start-of-msgstr)
(defvar po-entry-type)
;; A few counters are usefully shown in the Emacs mode line.
-
(defvar po-translated-counter)
(defvar po-fuzzy-counter)
(defvar po-untranslated-counter)
;; temporary Emacs buffer used to edit the string. OVERLAY-INFO, when not
;; nil, holds an overlay (or if overlays are not supported, a cons of two
;; markers) for this msgid string which became highlighted for the edit.
-
(defvar po-edited-fields)
;; We maintain a set of movable pointers for returning to entries.
-
(defvar po-marker-stack)
;; SEARCH path contains a list of directories where files may be found,
;; in a format suitable for read completion. Each directory includes
;; its trailing slash. PO mode starts with "./" and "../".
-
(defvar po-search-path)
;; The following variables are meaningful only when REFERENCE-CHECK
;; entry, each list element is (PROMPT FILE LINE), where PROMPT may
;; be used for completing read, FILE is a string and LINE is a number.
;; REFERENCE-CURSOR is a cycling cursor into REFERENCE-ALIST.
-
(defvar po-reference-alist)
(defvar po-reference-cursor)
(defvar po-reference-check)
;; if not `nil', holds the overlay which highlight the last found string;
;; for older Emacses, it holds the cons of two markers around the
;; highlighted region.
-
(defvar po-keywords)
(defvar po-next-file-list)
(defvar po-string-start)
(defun po-mode-version ()
"Show Emacs PO mode version."
(interactive)
- (message (_"Emacs PO mode, version %s") (substring "$Revision: 1.1.1.1 $" 11 -2)))
+ (message (_"Emacs PO mode, version %s")
+ (substring "$Revision: 1.1.1.1 $" 11 -2)))
(defconst po-help-display-string
(_"\
"Additional expressions to highlight in PO mode.")
;; Old activator for `font lock'. Is it still useful? I don't think so.
-;;
;;(if (boundp 'font-lock-keywords)
;; (put 'po-mode 'font-lock-keywords 'po-font-lock-keywords))
Turning on PO mode calls the value of the variable `po-mode-hook',
if that value is non-nil. Behaviour may be adjusted through some variables,
all reachable through `M-x customize', in group `Emacs.Editing.I18n.Po'."
-
(interactive)
(kill-all-local-variables)
(setq major-mode 'po-mode
(use-local-map po-mode-map)
(if (fboundp 'easy-menu-define)
(progn
- (easy-menu-define po-mode-menu po-mode-map "" po-mode-menu-layout)
- (and po-XEMACS (easy-menu-add po-mode-menu))))
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults '(po-font-lock-keywords t))
+ (easy-menu-define po-mode-menu po-mode-map "" po-mode-menu-layout)
+ (and po-XEMACS (easy-menu-add po-mode-menu))))
+ (set (make-local-variable 'font-lock-defaults) '(po-font-lock-keywords t))
- (make-local-variable 'po-read-only)
- (setq po-read-only buffer-read-only
- buffer-read-only t)
+ (set (make-local-variable 'po-read-only) buffer-read-only)
+ (setq buffer-read-only t)
(make-local-variable 'po-start-of-entry)
(make-local-variable 'po-start-of-msgid)
(po-check-file-header)
(po-compute-counters nil)
- (make-local-variable 'po-edited-fields)
- (setq po-edited-fields nil)
-
- (make-local-variable 'po-marker-stack)
- (setq po-marker-stack nil)
+ (set (make-local-variable 'po-edited-fields) nil)
+ (set (make-local-variable 'po-marker-stack) nil)
+ (set (make-local-variable 'po-search-path) '(("./") ("../")))
- (make-local-variable 'po-search-path)
- (setq po-search-path '(("./") ("../")))
+ (set (make-local-variable 'po-reference-alist) nil)
+ (set (make-local-variable 'po-reference-cursor) nil)
+ (set (make-local-variable 'po-reference-check) 0)
- (make-local-variable 'po-reference-alist)
- (make-local-variable 'po-reference-cursor)
- (make-local-variable 'po-reference-check)
- (setq po-reference-alist nil
- po-reference-cursor nil
- po-reference-check 0)
+ (set (make-local-variable 'po-keywords)
+ '(("gettext") ("gettext_noop") ("_") ("N_")))
+ (set (make-local-variable 'po-string-contents) nil)
+ (set (make-local-variable 'po-string-buffer) nil)
+ (set (make-local-variable 'po-string-start) nil)
+ (set (make-local-variable 'po-string-end) nil)
+ (set (make-local-variable 'po-marking-overlay) (po-create-overlay))
- (make-local-variable 'po-keywords)
(make-local-variable 'po-next-file-list)
- (make-local-variable 'po-string-start)
- (make-local-variable 'po-string-end)
- (make-local-variable 'po-marking-overlay)
+ (setq po-next-file-list nil)
+
(add-hook 'write-contents-hooks 'po-replace-revision-date)
- (setq po-keywords '(("gettext") ("gettext_noop") ("_") ("N_"))
- po-next-file-list nil
- po-string-start nil
- po-string-end nil
- po-marking-overlay (po-create-overlay))
(message (_"You may type `h' or `?' for a short PO mode reminder."))
(run-hooks 'po-mode-hook))
(goto-char (point-min))
(if (re-search-forward po-any-msgstr-regexp nil t)
(progn
-
;; There is at least one entry.
(goto-char (match-beginning 0))
(previous-line 1)
(setq end-of-header (match-end 0))
(if (looking-at "msgid \"\"\n")
-
;; There is indeed a PO file header.
(if (re-search-forward "\n\"PO-Revision-Date: "
end-of-header t)
nil
-
;; This is an oldish header. Replace it all.
(goto-char end-of-header)
(while (> (point) (point-min))
(beginning-of-line))
(beginning-of-line)
(setq insert-flag t))
-
;; The first entry is not a PO file header, insert one.
(setq insert-flag t)))
-
;; Not a single entry found.
(setq insert-flag t))
-
(goto-char (point-min))
(and insert-flag (insert po-default-file-header "\n")))))
(let ((here (point)))
(if (re-search-backward po-any-msgstr-regexp nil t)
(progn
-
;; After a backward match, (match-end 0) will not extend
;; beyond point, in case point was *inside* the regexp. We
;; need a dependable (match-end 0), so we redo the match in
(re-search-forward po-any-msgstr-regexp)
(if (<= (match-end 0) here)
(progn
-
;; We most probably found the msgstr of the previous
;; entry. The current entry then starts just after
;; its end, save this information just in case.
(setq po-start-of-entry (match-end 0))
-
;; However, it is also possible that we are located in
;; the crumb after the last entry in the file. If
;; yes, we know the middle and end of last PO entry.
(setq po-start-of-msgstr (match-beginning 0)
po-end-of-entry (match-end 0))
-
(if (re-search-forward po-any-msgstr-regexp nil t)
(progn
-
;; We definitely were not in the crumb.
(setq po-start-of-msgstr (match-beginning 0)
po-end-of-entry (match-end 0)))
-
;; We were in the crumb. The start of the last PO
;; file entry is the end of the previous msgstr if
;; any, or else, the beginning of the file.
(if (re-search-backward po-any-msgstr-regexp nil t)
(match-end 0)
(point-min)))))
-
;; The cursor was inside msgstr of the current entry.
(setq po-start-of-msgstr (match-beginning 0)
po-end-of-entry (match-end 0))
(if (re-search-backward po-any-msgstr-regexp nil t)
(match-end 0)
(point-min)))))
-
;; The cursor was before msgstr in the first entry in the file.
(setq po-start-of-entry (point-min))
(goto-char po-start-of-entry)
(re-search-forward po-any-msgstr-regexp)
(setq po-start-of-msgstr (match-beginning 0)
po-end-of-entry (match-end 0)))
-
;; Find start of msgid.
(goto-char po-start-of-entry)
(re-search-forward po-any-msgid-regexp)
(setq po-start-of-msgid (match-beginning 0))
-
;; Classify the entry.
(setq po-entry-type
(if (eq (following-char) ?#)
(if (looking-at po-untranslated-regexp)
'untranslated
'translated))))
-
;; Put the cursor back where it was.
(goto-char here)))
(if (and (= po-untranslated-counter 0)
(= po-fuzzy-counter 0)
(= po-obsolete-counter 0))
-
;; All entries are plain translated. Next entry will do, or
;; wrap around if there is none.
(if (re-search-forward po-any-msgstr-regexp nil t)
(goto-char (match-beginning 0))
(goto-char (point-min)))
-
;; If over a translated entry, look for an untranslated one first.
;; Else, look for an entry of the same type first.
(let ((goal (if (eq po-entry-type 'translated)
'untranslated
po-entry-type)))
(while goal
-
;; Find an untranslated entry, or wrap up for a fuzzy entry.
(if (eq goal 'untranslated)
(if (and (> po-untranslated-counter 0)
(setq goal nil))
(goto-char (point-min))
(setq goal 'fuzzy)))
-
;; Find a fuzzy entry, or wrap up for an obsolete entry.
(if (eq goal 'fuzzy)
(if (and (> po-fuzzy-counter 0)
(setq goal nil))
(goto-char (point-min))
(setq goal 'obsolete)))
-
;; Find an obsolete entry, or wrap up for an untranslated entry.
(if (eq goal 'obsolete)
(if (and (> po-obsolete-counter 0)
(setq goal nil))
(goto-char (point-min))
(setq goal 'untranslated))))))
-
;; Display this entry nicely.
(po-current-entry))
\f
(save-excursion
(goto-char po-start-of-entry)
(save-excursion ; check for an indexed msgstr
- (when (re-search-forward po-msgstr-idx-keyword-regexp po-end-of-entry t)
+ (when (re-search-forward po-msgstr-idx-keyword-regexp
+ po-end-of-entry t)
(setq msgstr-idx (buffer-substring-no-properties
(match-beginning 0) (match-end 0)))))
(re-search-forward po-any-msgstr-regexp po-end-of-entry)
(setq slot (list marker edit-buffer overlay)
po-edited-fields (cons slot po-edited-fields))
(pop-to-buffer edit-buffer)
- (make-local-variable 'po-subedit-back-pointer)
- (setq po-subedit-back-pointer slot)
+ (set (make-local-variable 'po-subedit-back-pointer) slot)
(setq buffer-file-coding-system edit-coding)
(erase-buffer)
(insert string "<")
(let ((end (1- (match-end 0))))
(goto-char (match-beginning 0))
(re-search-forward "msgstr +" nil t)
- ;; FIXME:
- (po-highlight (po-create-overlay) (point) end))))
+ ;; Just "borrow" the marking overlay.
+ (po-highlight po-marking-overlay (point) end))))
(goto-char po-start-of-msgid))
(goto-char start)
(po-find-span-of-entry)
"Extract all references into a list, with paths resolved, if necessary."
(po-find-span-of-entry)
(if (= po-start-of-entry po-reference-check)
- ()
+ nil
(setq po-reference-alist nil)
(save-excursion
(goto-char po-start-of-entry)
\f
;;; Program sources strings though tags table.
-;;; C mode.
-
-;;; A few long string cases (submitted by Ben Pfaff).
-
-;; #define string "This is a long string " \
-;; "that is continued across several lines " \
-;; "in a macro in order to test \\ quoting\\" \
-;; "\\ with goofy strings.\\"
-
-;; char *x = "This is just an ordinary string "
-;; "continued across several lines without needing "
-;; "to use \\ characters at end-of-line.";
-
-;; char *y = "Here is a string continued across \
-;; several lines in the manner that was sanctioned \
-;; in K&R C compilers and still works today, \
-;; even though the method used above is more esthetic.";
-
-;;; End of long string cases.
-
-(defun po-find-c-string (keywords)
- "Find the next C string, excluding those marked by any of KEYWORDS.
-Returns (START . END) for the found string, or (nil . nil) if none found."
- (let (start end)
- (while (and (not start)
- (re-search-forward "\\([\"']\\|/\\*\\|//\\)" nil t))
- (cond ((= (preceding-char) ?*)
- ;; Disregard comments.
- (search-forward "*/"))
-
- ((= (preceding-char) ?/)
- ;; Disregard C++ comments.
- (end-of-line)
- (forward-char 1))
-
- ((= (preceding-char) ?\')
- ;; Disregard character constants.
- (forward-char (if (= (following-char) ?\\) 3 2)))
-
- ((save-excursion
- (beginning-of-line)
- (looking-at "^# *\\(include\\|line\\)"))
- ;; Disregard lines being #include or #line directives.
- (end-of-line))
-
- ;; Else, find the end of the (possibly concatenated) string.
- (t (setq start (1- (point))
- end nil)
- (while (not end)
- (cond ((= (following-char) ?\")
- (if (looking-at "\"[ \t\n\\\\]*\"")
- (goto-char (match-end 0))
- (forward-char 1)
- (setq end (point))))
- ((= (following-char) ?\\) (forward-char 2))
- (t (skip-chars-forward "^\"\\\\"))))
-
- ;; Check before string for keyword and opening parenthesis.
- (goto-char start)
- (skip-chars-backward " \n\t")
- (if (= (preceding-char) ?\()
- (progn
- (backward-char 1)
- (skip-chars-backward " \n\t")
- (let ((end-keyword (point)))
- (skip-chars-backward "_A-Za-z0-9")
- (if (member (list (po-buffer-substring (point)
- end-keyword))
- keywords)
-
- ;; Disregard already marked strings.
- (progn
- (goto-char end)
- (setq start nil
- end nil)))))))))
-
- ;; Return the found string, if any.
- (cons start end)))
-
-(defun po-mark-c-string (start end keyword)
- "Mark the C string, from START to END, with KEYWORD.
-Return the adjusted value for END."
- (goto-char end)
- (insert ")")
- (goto-char start)
- (insert keyword)
- (if (not (string-equal keyword "_"))
- (progn (insert " ") (setq end (1+ end))))
- (insert "(")
- (+ end 2 (length keyword)))
-
-;;; Emacs LISP mode.
-
-(defun po-find-emacs-lisp-string (keywords)
- "Find the next Emacs LISP string, excluding those marked by any of KEYWORDS.
-Returns (START . END) for the found string, or (nil . nil) if none found."
- (let (start end)
- (while (and (not start)
- (re-search-forward "[;\"?]" nil t))
-
- (cond ((= (preceding-char) ?\;)
- ;; Disregard comments.
- (search-forward "\n"))
-
- ((= (preceding-char) ?\?)
- ;; Disregard character constants.
- (forward-char (if (= (following-char) ?\\) 2 1)))
-
- ;; Else, find the end of the string.
- (t (setq start (1- (point)))
- (while (not (= (following-char) ?\"))
- (skip-chars-forward "^\"\\\\")
- (if (= (following-char) ?\\) (forward-char 2)))
- (forward-char 1)
- (setq end (point))
-
- ;; Check before string for keyword and opening parenthesis.
- (goto-char start)
- (skip-chars-backward " \n\t")
- (let ((end-keyword (point)))
- (skip-chars-backward "-_A-Za-z0-9")
- (if (and (= (preceding-char) ?\()
- (member (list (po-buffer-substring (point)
- end-keyword))
- keywords))
-
- ;; Disregard already marked strings.
- (progn
- (goto-char end)
- (setq start nil
- end nil)))))))
-
- ;; Return the found string, if any.
- (cons start end)))
-
-(defun po-mark-emacs-lisp-string (start end keyword)
- "Mark the Emacs LISP string, from START to END, with KEYWORD.
-Return the adjusted value for END."
- (goto-char end)
- (insert ")")
- (goto-char start)
- (insert "(" keyword)
- (if (not (string-equal keyword "_"))
- (progn (insert " ") (setq end (1+ end))))
- (+ end 2 (length keyword)))
-
;;; Processing generic to all programming modes.
(eval-and-compile
;; Take care of restarting the search if necessary.
(if restart (setq po-next-file-list nil))
-
;; Loop doing things until an interesting string is found.
(let ((keywords po-keywords)
found buffer start
(end po-string-end))
(while (not found)
-
;; Reinitialize the source file list if necessary.
(if (not po-next-file-list)
(progn
(copy-sequence (tags-table-files))))
(or po-next-file-list (error (_"No files to process")))
(setq end nil)))
-
;; Try finding a string after resuming the search position.
(message (_"Scanning %s...") (car po-next-file-list))
(save-excursion
(setq buffer (find-file-noselect (car po-next-file-list)))
(set-buffer buffer)
(goto-char (or end (point-min)))
-
(cond ((member mode-name '("C" "C++"))
(let ((pair (po-find-c-string keywords)))
(setq start (car pair)
(t (message (_"Unknown source mode for PO mode, skipping..."))
(setq start nil
end nil))))
-
;; Advance to next file if no string was found.
(if (not start)
(progn
(setq po-string-end nil)
(and po-highlighting (po-dehighlight po-marking-overlay))
(error (_"All files processed"))))
-
;; Push the string just found string into a work buffer for study.
(po-with-temp-buffer
(insert (po-extract-unquoted buffer start end))
(goto-char (point-min))
-
;; Do not disregard if at least three letters in a row.
(if (re-search-forward "[A-Za-z][A-Za-z][A-Za-z]" nil t)
(setq found t)
-
;; Disregard if two letters, and more punctuations than letters.
(if (re-search-forward "[A-Za-z][A-Za-z]" nil t)
(let ((total (buffer-size)))
(replace-match "" t t))
(if (< (* 2 (buffer-size)) total)
(setq found t))))
-
;; Disregard if single letters or no letters at all.
))))
-
;; Ensure the string is being displayed.
-
(if (one-window-p t) (split-window) (other-window 1))
(switch-to-buffer buffer)
(goto-char start)
(recenter -1))
(other-window 1)
(and po-highlighting (po-highlight po-marking-overlay start end buffer))
-
;; Save the string for later commands.
(message (_"Scanning %s...done") (car po-next-file-list))
(setq po-string-start start
(po-mark-found-string keyword)
(setq po-string-start nil))
(error (_"No such string")))))
+
+;;; C mode.
+
+;;; A few long string cases (submitted by Ben Pfaff).
+
+;; #define string "This is a long string " \
+;; "that is continued across several lines " \
+;; "in a macro in order to test \\ quoting\\" \
+;; "\\ with goofy strings.\\"
+
+;; char *x = "This is just an ordinary string "
+;; "continued across several lines without needing "
+;; "to use \\ characters at end-of-line.";
+
+;; char *y = "Here is a string continued across \
+;; several lines in the manner that was sanctioned \
+;; in K&R C compilers and still works today, \
+;; even though the method used above is more esthetic.";
+
+;;; End of long string cases.
+
+(defun po-find-c-string (keywords)
+ "Find the next C string, excluding those marked by any of KEYWORDS.
+Returns (START . END) for the found string, or (nil . nil) if none found."
+ (let (start end)
+ (while (and (not start)
+ (re-search-forward "\\([\"']\\|/\\*\\|//\\)" nil t))
+ (cond ((= (preceding-char) ?*)
+ ;; Disregard comments.
+ (search-forward "*/"))
+ ((= (preceding-char) ?/)
+ ;; Disregard C++ comments.
+ (end-of-line)
+ (forward-char 1))
+ ((= (preceding-char) ?\')
+ ;; Disregard character constants.
+ (forward-char (if (= (following-char) ?\\) 3 2)))
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at "^# *\\(include\\|line\\)"))
+ ;; Disregard lines being #include or #line directives.
+ (end-of-line))
+ ;; Else, find the end of the (possibly concatenated) string.
+ (t (setq start (1- (point))
+ end nil)
+ (while (not end)
+ (cond ((= (following-char) ?\")
+ (if (looking-at "\"[ \t\n\\\\]*\"")
+ (goto-char (match-end 0))
+ (forward-char 1)
+ (setq end (point))))
+ ((= (following-char) ?\\) (forward-char 2))
+ (t (skip-chars-forward "^\"\\\\"))))
+
+ ;; Check before string for keyword and opening parenthesis.
+ (goto-char start)
+ (skip-chars-backward " \n\t")
+ (if (= (preceding-char) ?\()
+ (progn
+ (backward-char 1)
+ (skip-chars-backward " \n\t")
+ (let ((end-keyword (point)))
+ (skip-chars-backward "_A-Za-z0-9")
+ (if (member (list (po-buffer-substring (point)
+ end-keyword))
+ keywords)
+ ;; Disregard already marked strings.
+ (progn
+ (goto-char end)
+ (setq start nil
+ end nil)))))))))
+ ;; Return the found string, if any.
+ (cons start end)))
+
+(defun po-mark-c-string (start end keyword)
+ "Mark the C string, from START to END, with KEYWORD.
+Return the adjusted value for END."
+ (goto-char end)
+ (insert ")")
+ (goto-char start)
+ (insert keyword)
+ (if (not (string-equal keyword "_"))
+ (progn (insert " ") (setq end (1+ end))))
+ (insert "(")
+ (+ end 2 (length keyword)))
+
+;;; Emacs LISP mode.
+
+(defun po-find-emacs-lisp-string (keywords)
+ "Find the next Emacs LISP string, excluding those marked by any of KEYWORDS.
+Returns (START . END) for the found string, or (nil . nil) if none found."
+ (let (start end)
+ (while (and (not start)
+ (re-search-forward "[;\"?]" nil t))
+ (cond ((= (preceding-char) ?\;)
+ ;; Disregard comments.
+ (search-forward "\n"))
+ ((= (preceding-char) ?\?)
+ ;; Disregard character constants.
+ (forward-char (if (= (following-char) ?\\) 2 1)))
+ ;; Else, find the end of the string.
+ (t (setq start (1- (point)))
+ (while (not (= (following-char) ?\"))
+ (skip-chars-forward "^\"\\\\")
+ (if (= (following-char) ?\\) (forward-char 2)))
+ (forward-char 1)
+ (setq end (point))
+ ;; Check before string for keyword and opening parenthesis.
+ (goto-char start)
+ (skip-chars-backward " \n\t")
+ (let ((end-keyword (point)))
+ (skip-chars-backward "-_A-Za-z0-9")
+ (if (and (= (preceding-char) ?\()
+ (member (list (po-buffer-substring (point)
+ end-keyword))
+ keywords))
+
+ ;; Disregard already marked strings.
+ (progn
+ (goto-char end)
+ (setq start nil
+ end nil)))))))
+ ;; Return the found string, if any.
+ (cons start end)))
+
+(defun po-mark-emacs-lisp-string (start end keyword)
+ "Mark the Emacs LISP string, from START to END, with KEYWORD.
+Return the adjusted value for END."
+ (goto-char end)
+ (insert ")")
+ (goto-char start)
+ (insert "(" keyword)
+ (if (not (string-equal keyword "_"))
+ (progn (insert " ") (setq end (1+ end))))
+ (+ end 2 (length keyword)))
\f
;;; Miscellaneous features.
(interactive)
(if (po-check-all-pending-edits)
(let ((quit t))
-
;; Offer validation of newly modified entries.
(if (and (buffer-modified-p)
(not (y-or-n-p
;; stay within PO mode, even if this implies that another
;; `po-quit' command will be later required to exit for true.
(setq quit nil)))
-
;; Offer to work on untranslated entries.
(if (and quit
(or (> po-untranslated-counter 0)
(progn
(setq quit nil)
(po-auto-select-entry)))
-
;; Clear message area.
(message "")
-
;; Or else, kill buffers and quit for true.
(if quit
(progn