gnus-spec.el 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. ;;; gnus-spec.el --- format spec functions for Gnus
  2. ;; Copyright (C) 1996-2015 Free Software Foundation, Inc.
  3. ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
  4. ;; Keywords: news
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;; Code:
  18. (eval-when-compile (require 'cl))
  19. (defvar gnus-newsrc-file-version)
  20. (require 'gnus)
  21. (defcustom gnus-use-correct-string-widths (featurep 'xemacs)
  22. "*If non-nil, use correct functions for dealing with wide characters."
  23. :version "22.1"
  24. :group 'gnus-format
  25. :type 'boolean)
  26. (defcustom gnus-make-format-preserve-properties (featurep 'xemacs)
  27. "*If non-nil, use a replacement `format' function which preserves
  28. text properties. This is only needed on XEmacs, as Emacs does this anyway."
  29. :version "22.1"
  30. :group 'gnus-format
  31. :type 'boolean)
  32. ;;; Internal variables.
  33. (defvar gnus-summary-mark-positions nil)
  34. (defvar gnus-group-mark-positions nil)
  35. (defvar gnus-group-indentation "")
  36. ;; Format specs. The chunks below are the machine-generated forms
  37. ;; that are to be evalled as the result of the default format strings.
  38. ;; We write them in here to get them byte-compiled. That way the
  39. ;; default actions will be quite fast, while still retaining the full
  40. ;; flexibility of the user-defined format specs.
  41. ;; First we have lots of dummy defvars to let the compiler know these
  42. ;; are really dynamic variables.
  43. (defvar gnus-tmp-unread)
  44. (defvar gnus-tmp-replied)
  45. (defvar gnus-tmp-score-char)
  46. (defvar gnus-tmp-indentation)
  47. (defvar gnus-tmp-opening-bracket)
  48. (defvar gnus-tmp-lines)
  49. (defvar gnus-tmp-name)
  50. (defvar gnus-tmp-closing-bracket)
  51. (defvar gnus-tmp-subject-or-nil)
  52. (defvar gnus-tmp-subject)
  53. (defvar gnus-tmp-marked)
  54. (defvar gnus-tmp-marked-mark)
  55. (defvar gnus-tmp-subscribed)
  56. (defvar gnus-tmp-process-marked)
  57. (defvar gnus-tmp-number-of-unread)
  58. (defvar gnus-tmp-group-name)
  59. (defvar gnus-tmp-group)
  60. (defvar gnus-tmp-article-number)
  61. (defvar gnus-tmp-unread-and-unselected)
  62. (defvar gnus-tmp-news-method)
  63. (defvar gnus-tmp-news-server)
  64. (defvar gnus-mouse-face)
  65. (defvar gnus-mouse-face-prop)
  66. (defvar gnus-tmp-header)
  67. (defvar gnus-tmp-from)
  68. (declare-function gnus-summary-from-or-to-or-newsgroups "gnus-sum"
  69. (header gnus-tmp-from))
  70. (defmacro gnus-lrm-string-p (string)
  71. (if (fboundp 'bidi-string-mark-left-to-right)
  72. ;; LRM, RLM, PDF characters as integers to avoid breaking Emacs
  73. ;; 23.
  74. `(memq (aref ,string (1- (length ,string))) '(8206 8207 8236))
  75. nil))
  76. (defvar gnus-lrm-string (if (ignore-errors (string 8206))
  77. (propertize (string 8206) 'invisible t)
  78. ""))
  79. (defvar gnus-summary-line-format-spec nil)
  80. (defvar gnus-summary-dummy-line-format-spec nil)
  81. (defvar gnus-group-line-format-spec nil)
  82. (defvar gnus-format-specs
  83. `((version . ,emacs-version)
  84. (gnus-version . ,(gnus-continuum-version)))
  85. "Alist of format specs.")
  86. (defvar gnus-default-format-specs gnus-format-specs)
  87. (defvar gnus-article-mode-line-format-spec nil)
  88. (defvar gnus-summary-mode-line-format-spec nil)
  89. (defvar gnus-group-mode-line-format-spec nil)
  90. ;;; Phew. All that gruft is over with, fortunately.
  91. ;;;###autoload
  92. (defun gnus-update-format (var)
  93. "Update the format specification near point."
  94. (interactive
  95. (list
  96. (save-excursion
  97. (eval-defun nil)
  98. ;; Find the end of the current word.
  99. (re-search-forward "[ \t\n]" nil t)
  100. ;; Search backward.
  101. (when (re-search-backward "\\(gnus-[-a-z]+-line-format\\)" nil t)
  102. (match-string 1)))))
  103. (let* ((type (intern (progn (string-match "gnus-\\([-a-z]+\\)-line" var)
  104. (match-string 1 var))))
  105. (entry (assq type gnus-format-specs))
  106. value spec)
  107. (when entry
  108. (setq gnus-format-specs (delq entry gnus-format-specs)))
  109. (set
  110. (intern (format "%s-spec" var))
  111. (gnus-parse-format (setq value (symbol-value (intern var)))
  112. (symbol-value (intern (format "%s-alist" var)))
  113. (not (string-match "mode" var))))
  114. (setq spec (symbol-value (intern (format "%s-spec" var))))
  115. (push (list type value spec) gnus-format-specs)
  116. (pop-to-buffer "*Gnus Format*")
  117. (erase-buffer)
  118. (lisp-interaction-mode)
  119. (insert (gnus-pp-to-string spec))))
  120. (defun gnus-update-format-specifications (&optional force &rest types)
  121. "Update all (necessary) format specifications.
  122. Return a list of updated types."
  123. ;; Make the indentation array.
  124. ;; See whether all the stored info needs to be flushed.
  125. (when (or force
  126. (not gnus-newsrc-file-version)
  127. (not (equal (gnus-continuum-version)
  128. (gnus-continuum-version gnus-newsrc-file-version)))
  129. (not (equal emacs-version
  130. (cdr (assq 'version gnus-format-specs)))))
  131. (setq gnus-format-specs nil))
  132. ;; Go through all the formats and see whether they need updating.
  133. (let (new-format entry type val updated)
  134. (while (setq type (pop types))
  135. ;; Jump to the proper buffer to find out the value of the
  136. ;; variable, if possible. (It may be buffer-local.)
  137. (save-excursion
  138. (let ((buffer (intern (format "gnus-%s-buffer" type))))
  139. (when (and (boundp buffer)
  140. (setq val (symbol-value buffer))
  141. (gnus-buffer-exists-p val))
  142. (set-buffer val))
  143. (setq new-format (symbol-value
  144. (intern (format "gnus-%s-line-format" type)))))
  145. (setq entry (cdr (assq type gnus-format-specs)))
  146. (if (and (car entry)
  147. (equal (car entry) new-format))
  148. ;; Use the old format.
  149. (set (intern (format "gnus-%s-line-format-spec" type))
  150. (cadr entry))
  151. ;; This is a new format.
  152. (setq val
  153. (if (not (stringp new-format))
  154. ;; This is a function call or something.
  155. new-format
  156. ;; This is a "real" format.
  157. (gnus-parse-format
  158. new-format
  159. (symbol-value
  160. (intern (format "gnus-%s-line-format-alist" type)))
  161. (not (string-match "mode$" (symbol-name type))))))
  162. ;; Enter the new format spec into the list.
  163. (if entry
  164. (progn
  165. (setcar (cdr entry) val)
  166. (setcar entry new-format))
  167. (push (list type new-format val) gnus-format-specs))
  168. (set (intern (format "gnus-%s-line-format-spec" type)) val)
  169. (push type updated))))
  170. (unless (assq 'version gnus-format-specs)
  171. (push (cons 'version emacs-version) gnus-format-specs))
  172. updated))
  173. (defcustom gnus-mouse-face-0 'highlight
  174. "The \"%(hello%)\" face."
  175. :group 'gnus-format
  176. :type 'face)
  177. (defcustom gnus-mouse-face-1 'highlight
  178. "The \"%1(hello%)\" face."
  179. :group 'gnus-format
  180. :type 'face)
  181. (defcustom gnus-mouse-face-2 'highlight
  182. "The \"%2(hello%)\" face."
  183. :group 'gnus-format
  184. :type 'face)
  185. (defcustom gnus-mouse-face-3 'highlight
  186. "The \"%3(hello%)\" face."
  187. :group 'gnus-format
  188. :type 'face)
  189. (defcustom gnus-mouse-face-4 'highlight
  190. "The \"%4(hello%)\" face."
  191. :group 'gnus-format
  192. :type 'face)
  193. (defun gnus-mouse-face-function (form type)
  194. `(gnus-put-text-property
  195. (point) (progn ,@form (point))
  196. gnus-mouse-face-prop
  197. ,(if (equal type 0)
  198. 'gnus-mouse-face
  199. `(quote ,(symbol-value (intern (format "gnus-mouse-face-%d" type)))))))
  200. (defcustom gnus-face-0 'bold
  201. "The \"%{hello%}\" face."
  202. :group 'gnus-format
  203. :type 'face)
  204. (defcustom gnus-face-1 'italic
  205. "The \"%1{hello%}\" face."
  206. :group 'gnus-format
  207. :type 'face)
  208. (defcustom gnus-face-2 'bold-italic
  209. "The \"%2{hello%}\" face."
  210. :group 'gnus-format
  211. :type 'face)
  212. (defcustom gnus-face-3 'bold
  213. "The \"%3{hello%}\" face."
  214. :group 'gnus-format
  215. :type 'face)
  216. (defcustom gnus-face-4 'bold
  217. "The \"%4{hello%}\" face."
  218. :group 'gnus-format
  219. :type 'face)
  220. (defun gnus-face-face-function (form type)
  221. `(gnus-add-text-properties
  222. (point) (progn ,@form (point))
  223. (cons 'face
  224. (cons
  225. ;; Delay consing the value of the `face' property until
  226. ;; `gnus-add-text-properties' runs, since it will be modified
  227. ;; by `gnus-put-text-property-excluding-characters-with-faces'.
  228. (list ',(symbol-value (intern (format "gnus-face-%d" type))) 'default)
  229. ;; Redundant now, but still convenient.
  230. '(gnus-face t)))))
  231. (defun gnus-balloon-face-function (form type)
  232. `(gnus-put-text-property
  233. (point) (progn ,@form (point))
  234. ,(if (fboundp 'balloon-help-mode)
  235. ''balloon-help
  236. ''help-echo)
  237. ,(intern (format "gnus-balloon-face-%d" type))))
  238. (defun gnus-spec-tab (column)
  239. (if (> column 0)
  240. `(insert-char ? (max (- ,column (current-column)) 0))
  241. (let ((column (abs column)))
  242. `(if (> (current-column) ,column)
  243. (let ((end (point)))
  244. (if (= (move-to-column ,column) ,column)
  245. (delete-region (point) end)
  246. (delete-region (1- (point)) end)
  247. (insert " ")))
  248. (insert-char ? (max (- ,column (current-column)) 0))))))
  249. (defun gnus-correct-length (string)
  250. "Return the correct width of STRING."
  251. (apply #'+ (mapcar #'char-width string)))
  252. (defun gnus-correct-substring (string start &optional end)
  253. (let ((wstart 0)
  254. (wend 0)
  255. (wseek 0)
  256. (seek 0)
  257. (length (length string))
  258. (string (concat string "\0")))
  259. ;; Find the start position.
  260. (while (and (< seek length)
  261. (< wseek start))
  262. (incf wseek (char-width (aref string seek)))
  263. (incf seek))
  264. (setq wstart seek)
  265. ;; Find the end position.
  266. (while (and (<= seek length)
  267. (or (not end)
  268. (<= wseek end)))
  269. (incf wseek (char-width (aref string seek)))
  270. (incf seek))
  271. (setq wend seek)
  272. (substring string wstart (1- wend))))
  273. (defun gnus-string-width-function ()
  274. (cond
  275. (gnus-use-correct-string-widths
  276. 'gnus-correct-length)
  277. ((fboundp 'string-width)
  278. 'string-width)
  279. (t
  280. 'length)))
  281. (defun gnus-substring-function ()
  282. (cond
  283. (gnus-use-correct-string-widths
  284. 'gnus-correct-substring)
  285. ((fboundp 'string-width)
  286. 'gnus-correct-substring)
  287. (t
  288. 'substring)))
  289. (defun gnus-tilde-max-form (el max-width)
  290. "Return a form that limits EL to MAX-WIDTH."
  291. (let ((max (abs max-width))
  292. (length-fun (gnus-string-width-function))
  293. (substring-fun (gnus-substring-function)))
  294. (if (symbolp el)
  295. `(if (> (,length-fun ,el) ,max)
  296. ,(if (< max-width 0)
  297. `(,substring-fun ,el (- (,length-fun ,el) ,max))
  298. `(if (gnus-lrm-string-p ,el)
  299. (concat (,substring-fun ,el 0 ,max) ,gnus-lrm-string)
  300. (,substring-fun ,el 0 ,max)))
  301. ,el)
  302. `(let ((val (eval ,el)))
  303. (if (> (,length-fun val) ,max)
  304. ,(if (< max-width 0)
  305. `(,substring-fun val (- (,length-fun val) ,max))
  306. `(if (gnus-lrm-string-p val)
  307. (concat (,substring-fun val 0 ,max) ,gnus-lrm-string)
  308. (,substring-fun val 0 ,max)))
  309. val)))))
  310. (defun gnus-tilde-cut-form (el cut-width)
  311. "Return a form that cuts CUT-WIDTH off of EL."
  312. (let ((cut (abs cut-width))
  313. (length-fun (gnus-string-width-function))
  314. (substring-fun (gnus-substring-function)))
  315. (if (symbolp el)
  316. `(if (> (,length-fun ,el) ,cut)
  317. ,(if (< cut-width 0)
  318. `(,substring-fun ,el 0 (- (,length-fun ,el) ,cut))
  319. `(,substring-fun ,el ,cut))
  320. ,el)
  321. `(let ((val (eval ,el)))
  322. (if (> (,length-fun val) ,cut)
  323. ,(if (< cut-width 0)
  324. `(,substring-fun val 0 (- (,length-fun val) ,cut))
  325. `(,substring-fun val ,cut))
  326. val)))))
  327. (defun gnus-tilde-ignore-form (el ignore-value)
  328. "Return a form that is blank when EL is IGNORE-VALUE."
  329. (if (symbolp el)
  330. `(if (equal ,el ,ignore-value)
  331. "" ,el)
  332. `(let ((val (eval ,el)))
  333. (if (equal val ,ignore-value)
  334. "" val))))
  335. (defun gnus-pad-form (el pad-width)
  336. "Return a form that pads EL to PAD-WIDTH accounting for multi-column
  337. characters correctly. This is because `format' may pad to columns or to
  338. characters when given a pad value."
  339. (let ((pad (abs pad-width))
  340. (side (< 0 pad-width))
  341. (length-fun (gnus-string-width-function)))
  342. (if (symbolp el)
  343. `(let ((need (- ,pad (,length-fun ,el))))
  344. (if (> need 0)
  345. (concat ,(when side '(make-string need ?\ ))
  346. ,el
  347. ,(when (not side) '(make-string need ?\ )))
  348. ,el))
  349. `(let* ((val (eval ,el))
  350. (need (- ,pad (,length-fun val))))
  351. (if (> need 0)
  352. (concat ,(when side '(make-string need ?\ ))
  353. val
  354. ,(when (not side) '(make-string need ?\ )))
  355. val)))))
  356. (defun gnus-parse-format (format spec-alist &optional insert)
  357. ;; This function parses the FORMAT string with the help of the
  358. ;; SPEC-ALIST and returns a list that can be eval'ed to return the
  359. ;; string. If the FORMAT string contains the specifiers %( and %)
  360. ;; the text between them will have the mouse-face text property.
  361. ;; If the FORMAT string contains the specifiers %[ and %], the text between
  362. ;; them will have the balloon-help text property.
  363. (let ((case-fold-search nil))
  364. (if (string-match
  365. "\\`\\(.*\\)%[0-9]?[{(«]\\(.*\\)%[0-9]?[»})]\\(.*\n?\\)\\'\\|%[-0-9]*=\\|%[-0-9]*\\*"
  366. format)
  367. (gnus-parse-complex-format format spec-alist)
  368. ;; This is a simple format.
  369. (gnus-parse-simple-format format spec-alist insert))))
  370. (defun gnus-parse-complex-format (format spec-alist)
  371. (let ((cursor-spec nil))
  372. (save-excursion
  373. (gnus-set-work-buffer)
  374. (insert format)
  375. (goto-char (point-min))
  376. (while (re-search-forward "\"" nil t)
  377. (replace-match "\\\"" nil t))
  378. (goto-char (point-min))
  379. (insert "(\"")
  380. ;; Convert all font specs into font spec lists.
  381. (while (re-search-forward "%\\([0-9]+\\)?\\([«»{}()]\\)" nil t)
  382. (let ((number (if (match-beginning 1)
  383. (match-string 1) "0"))
  384. (delim (aref (match-string 2) 0)))
  385. (if (or (= delim ?\()
  386. (= delim ?\{)
  387. (= delim 171)) ; «
  388. (replace-match (concat "\"("
  389. (cond ((= delim ?\() "mouse")
  390. ((= delim ?\{) "face")
  391. (t "balloon"))
  392. " " number " \"")
  393. t t)
  394. (replace-match "\")\""))))
  395. (goto-char (point-max))
  396. (insert "\")")
  397. ;; Convert point position commands.
  398. (goto-char (point-min))
  399. (let ((case-fold-search nil))
  400. (while (re-search-forward "%\\([-0-9]+\\)?\\*" nil t)
  401. (replace-match "\"(point)\"" t t)
  402. (setq cursor-spec t)))
  403. ;; Convert TAB commands.
  404. (goto-char (point-min))
  405. (while (re-search-forward "%\\([-0-9]+\\)=" nil t)
  406. (replace-match (format "\"(tab %s)\"" (match-string 1)) t t))
  407. ;; Convert the buffer into the spec.
  408. (goto-char (point-min))
  409. (let ((form (read (current-buffer))))
  410. (if cursor-spec
  411. `(let (gnus-position)
  412. ,@(gnus-complex-form-to-spec form spec-alist)
  413. (if gnus-position
  414. (gnus-put-text-property gnus-position (1+ gnus-position)
  415. 'gnus-position t)))
  416. `(progn
  417. ,@(gnus-complex-form-to-spec form spec-alist)))))))
  418. (defun gnus-complex-form-to-spec (form spec-alist)
  419. (delq nil
  420. (mapcar
  421. (lambda (sform)
  422. (cond
  423. ((stringp sform)
  424. (gnus-parse-simple-format sform spec-alist t))
  425. ((eq (car sform) 'point)
  426. '(setq gnus-position (point)))
  427. ((eq (car sform) 'tab)
  428. (gnus-spec-tab (cadr sform)))
  429. (t
  430. (funcall (intern (format "gnus-%s-face-function" (car sform)))
  431. (gnus-complex-form-to-spec (cddr sform) spec-alist)
  432. (nth 1 sform)))))
  433. form)))
  434. (defun gnus-xmas-format (fstring &rest args)
  435. "A version of `format' which preserves text properties.
  436. Required for XEmacs, where the built in `format' function strips all text
  437. properties from both the format string and any inserted strings.
  438. Only supports the format sequence %s, and %% for inserting
  439. literal % characters. A pad width and an optional - (to right pad)
  440. are supported for %s."
  441. (let ((re "%%\\|%\\(-\\)?\\([1-9][0-9]*\\)?s")
  442. (n (length args)))
  443. (with-temp-buffer
  444. (insert fstring)
  445. (goto-char (point-min))
  446. (while (re-search-forward re nil t)
  447. (goto-char (match-end 0))
  448. (cond
  449. ((string= (match-string 0) "%%")
  450. (delete-char -1))
  451. (t
  452. (if (null args)
  453. (signal 'wrong-number-of-arguments
  454. (list #'gnus-xmas-format n fstring)))
  455. (let* ((minlen (string-to-number (or (match-string 2) "")))
  456. (arg (car args))
  457. (str (if (stringp arg) arg (format "%s" arg)))
  458. (lpad (null (match-string 1)))
  459. (padlen (max 0 (- minlen (length str)))))
  460. (replace-match "")
  461. (if lpad (insert-char ?\ padlen))
  462. (insert str)
  463. (unless lpad (insert-char ?\ padlen))
  464. (setq args (cdr args))))))
  465. (buffer-string))))
  466. (defun gnus-parse-simple-format (format spec-alist &optional insert)
  467. ;; This function parses the FORMAT string with the help of the
  468. ;; SPEC-ALIST and returns a list that can be eval'ed to return a
  469. ;; string.
  470. (let ((max-width 0)
  471. spec flist fstring elem result dontinsert user-defined
  472. type value pad-width spec-beg cut-width ignore-value
  473. tilde-form tilde elem-type extended-spec)
  474. (save-excursion
  475. (gnus-set-work-buffer)
  476. (insert format)
  477. (goto-char (point-min))
  478. (while (re-search-forward "%" nil t)
  479. (setq user-defined nil
  480. spec-beg nil
  481. pad-width nil
  482. max-width nil
  483. cut-width nil
  484. ignore-value nil
  485. tilde-form nil
  486. extended-spec nil)
  487. (setq spec-beg (1- (point)))
  488. ;; Parse this spec fully.
  489. (while
  490. (cond
  491. ((looking-at "\\([-.0-9]+\\)\\(,[-0-9]+\\)?")
  492. (setq pad-width (string-to-number (match-string 1)))
  493. (when (match-beginning 2)
  494. (setq max-width (string-to-number (buffer-substring
  495. (1+ (match-beginning 2))
  496. (match-end 2)))))
  497. (goto-char (match-end 0)))
  498. ((looking-at "~")
  499. (forward-char 1)
  500. (setq tilde (read (current-buffer))
  501. type (car tilde)
  502. value (cadr tilde))
  503. (cond
  504. ((memq type '(pad pad-left))
  505. (setq pad-width value))
  506. ((eq type 'pad-right)
  507. (setq pad-width (- value)))
  508. ((memq type '(max-right max))
  509. (setq max-width value))
  510. ((eq type 'max-left)
  511. (setq max-width (- value)))
  512. ((memq type '(cut cut-left))
  513. (setq cut-width value))
  514. ((eq type 'cut-right)
  515. (setq cut-width (- value)))
  516. ((eq type 'ignore)
  517. (setq ignore-value
  518. (if (stringp value) value (format "%s" value))))
  519. ((eq type 'form)
  520. (setq tilde-form value))
  521. (t
  522. (error "Unknown tilde type: %s" tilde)))
  523. t)
  524. (t
  525. nil)))
  526. (cond
  527. ;; User-defined spec -- find the spec name.
  528. ((eq (setq spec (char-after)) ?u)
  529. (forward-char 1)
  530. (when (and (eq (setq user-defined (char-after)) ?&)
  531. (looking-at "&\\([^;]+\\);"))
  532. (setq user-defined (match-string 1))
  533. (goto-char (match-end 1))))
  534. ;; extended spec
  535. ((and (eq spec ?&) (looking-at "&\\([^;]+\\);"))
  536. (setq extended-spec (intern (match-string 1)))
  537. (goto-char (match-end 1))))
  538. (forward-char 1)
  539. (delete-region spec-beg (point))
  540. ;; Now we have all the relevant data on this spec, so
  541. ;; we start doing stuff.
  542. (insert "%")
  543. (if (eq spec ?%)
  544. ;; "%%" just results in a "%".
  545. (insert "%")
  546. (cond
  547. ;; Do tilde forms.
  548. ((eq spec ?@)
  549. (setq elem (list tilde-form ?s)))
  550. ;; Treat user defined format specifiers specially.
  551. (user-defined
  552. (setq elem
  553. (list
  554. (list (intern (format
  555. (if (stringp user-defined)
  556. "gnus-user-format-function-%s"
  557. "gnus-user-format-function-%c")
  558. user-defined))
  559. 'gnus-tmp-header)
  560. ?s)))
  561. ;; Find the specification from `spec-alist'.
  562. ((setq elem (cdr (assq (or extended-spec spec) spec-alist))))
  563. ;; We used to use "%l" for displaying the grouplens score.
  564. ((eq spec ?l)
  565. (setq elem '("" ?s)))
  566. (t
  567. (setq elem '("*" ?s))))
  568. (setq elem-type (cadr elem))
  569. ;; Insert the new format elements.
  570. (when (and pad-width
  571. (not (and (featurep 'xemacs)
  572. gnus-use-correct-string-widths)))
  573. (insert (number-to-string pad-width)))
  574. ;; Create the form to be evalled.
  575. (if (or max-width cut-width ignore-value
  576. (and (featurep 'xemacs)
  577. gnus-use-correct-string-widths))
  578. (progn
  579. (insert ?s)
  580. (let ((el (car elem)))
  581. (cond ((= (cadr elem) ?c)
  582. (setq el (list 'char-to-string el)))
  583. ((= (cadr elem) ?d)
  584. (setq el (list 'int-to-string el))))
  585. (when ignore-value
  586. (setq el (gnus-tilde-ignore-form el ignore-value)))
  587. (when cut-width
  588. (setq el (gnus-tilde-cut-form el cut-width)))
  589. (when max-width
  590. (setq el (gnus-tilde-max-form el max-width)))
  591. (when pad-width
  592. (setq el (gnus-pad-form el pad-width)))
  593. (push el flist)))
  594. (insert elem-type)
  595. (push (car elem) flist))))
  596. (setq fstring (buffer-substring-no-properties (point-min) (point-max))))
  597. ;; Do some postprocessing to increase efficiency.
  598. (setq
  599. result
  600. (cond
  601. ;; Emptiness.
  602. ((string= fstring "")
  603. nil)
  604. ;; Not a format string.
  605. ((not (string-match "%" fstring))
  606. (list fstring))
  607. ;; A format string with just a single string spec.
  608. ((string= fstring "%s")
  609. (list (car flist)))
  610. ;; A single character.
  611. ((string= fstring "%c")
  612. (list (car flist)))
  613. ;; A single number.
  614. ((string= fstring "%d")
  615. (setq dontinsert t)
  616. (if insert
  617. `(insert (int-to-string ,(car flist)))
  618. (list `(int-to-string ,(car flist)))))
  619. ;; Just lots of chars and strings.
  620. ((string-match "\\`\\(%[cs]\\)+\\'" fstring)
  621. (nreverse flist))
  622. ;; A single string spec at the beginning of the spec.
  623. ((string-match "\\`%[sc][^%]+\\'" fstring)
  624. (list (car flist) (substring fstring 2)))
  625. ;; A single string spec in the middle of the spec.
  626. ((string-match "\\`\\([^%]+\\)%[sc]\\([^%]+\\)\\'" fstring)
  627. (list (match-string 1 fstring) (car flist) (match-string 2 fstring)))
  628. ;; A single string spec in the end of the spec.
  629. ((string-match "\\`\\([^%]+\\)%[sc]\\'" fstring)
  630. (list (match-string 1 fstring) (car flist)))
  631. ;; Only string (and %) specs (XEmacs only!)
  632. ((and (featurep 'xemacs)
  633. gnus-make-format-preserve-properties
  634. (string-match
  635. "\\`\\([^%]*\\(%%\\|%-?\\([1-9][0-9]*\\)?s\\)\\)*[^%]*\\'"
  636. fstring))
  637. (list (cons 'gnus-xmas-format (cons fstring (nreverse flist)))))
  638. ;; A more complex spec.
  639. (t
  640. (list (cons 'format (cons fstring (nreverse flist)))))))
  641. (if insert
  642. (when result
  643. (if dontinsert
  644. result
  645. (cons 'insert result)))
  646. (cond ((stringp result)
  647. result)
  648. ((consp result)
  649. (cons 'concat result))
  650. (t "")))))
  651. (defun gnus-eval-format (format &optional alist props)
  652. "Eval the format variable FORMAT, using ALIST.
  653. If PROPS, insert the result."
  654. (let ((form (gnus-parse-format format alist props)))
  655. (if props
  656. (gnus-add-text-properties (point) (progn (eval form) (point)) props)
  657. (eval form))))
  658. (defun gnus-set-format (type &optional insertable)
  659. (set (intern (format "gnus-%s-line-format-spec" type))
  660. (gnus-parse-format
  661. (symbol-value (intern (format "gnus-%s-line-format" type)))
  662. (symbol-value (intern (format "gnus-%s-line-format-alist" type)))
  663. insertable)))
  664. (provide 'gnus-spec)
  665. ;; Local Variables:
  666. ;; coding: utf-8
  667. ;; End:
  668. ;;; gnus-spec.el ends here