1 "===============================================================
2 " File : Mail_Re_set.vim
3 " Initial Author: Dominique Baldo
4 " Maintained by : Luc Hermitte <hermitte {at} free {dot} fr>
5 " <URL:http://hermitte.free.fr/vim/>
6 " URL: http://hermitte.free.fr/vim/ressources/vimfiles/ftplugin/mail/Mail_Re_set.vim
8 " Last update : 15th Oct 2003
9 "---------------------------------------------------------------
10 " Purpose : Merge the occurencies of "Re:" and "Re[n]" within
11 " e-mails'subject in only one.
13 " Calling conv. : Source this file and invoke :MailCondenseRe
14 " define -> nnoremap ,re :MailCondenseRe<cr>
16 " * Drop this file into {rtp}/ftplugin/mail/
17 " * There are two available options you can customize into your .vimrc :
18 " + Customize the way you want the "Re" string to be built thanks to
19 " g:mail_re_fmt or b:mail_re_fmt :
21 " "Re[]:" -- use "Re[n]:" when {n}>1 ; "Re:" when {n}=1
22 " "Re[1]:" -- use "Re[n]:" even when {n}>1
23 " "Mimic:" -- use what the correspondant used [default]
24 " + Customize the placement order between "Re:" and "[TAGS]" thanks to
25 " g:mail_tag_placement or b:mail_tag_placement :
26 " "detect_news" -- use "Re: [TAG]" for usenet, "[TAG] Re:" otherwise
28 " "tag_second" -- use "Re: [TAG]" ; seems to be the right option on
29 " usenet according to trn
30 " "tag_first" -- use "[TAG] Re:" ; seems to be the option used on
31 " tagged mailing lists
33 "---------------------------------------------------------------
35 " * Ver 2.0 : Also merges the various forward indicators
36 " * Ver 1.4d : Fix a bug when g:mail_tag_placement is set
37 " Use "normal!" instead of "normal"
38 " New kind of whitespace managed: some people composing
39 " their messages from yahogroups web site may use very odd
40 " whitespace characters to write "Re :"
41 " * Ver 1.4c : Fix a small bug
42 " * Ver 1.4b : "Rép:" added to the list of "Re:" equivalents
43 " * ver 1.4 : "Re:" can be placed before or after the first [tag] present
44 " according to g:mail_tag_placement
45 " * ver 1.3 : Requires VIM 6.x
46 " Export a command: :MailCondenseRe
47 " count the Re: thanks to the global length of strings
48 " The subject build can be customized thanks to g:mail_re_fmt
50 " The "Re:" will be placed after the [Tags] if present.
51 " * ver 1.2 : Translation of the comments
52 " * ver 1.1 : Handle "Réf" produced by some french e-mailers
53 " Answer by "Re:" most of the time
54 " * ver 1.0 : Replace "Re: Re: ..." n times by "Re[n]:"
57 " * Add an option to add equivalents to "Re:" -- only "Réf.:" and "Rép:" are
58 " supported at this time.
59 " * Support "Re: Fwd: Fw: "
61 "===============================================================
62 if exists("b:loaded_local_Mail_Re_set_vim") | finish | endif
63 let b:loaded_local_Mail_Re_set_vim = 1
65 command! -b -nargs=0 MailCondenseRe :call <sid>Mail_Merge_Re_n_Fwd(1,0)
66 command! -b -nargs=0 MailCondenseFwd :call <sid>Mail_Merge_Re_n_Fwd(0,1)
67 command! -b -nargs=0 MailCondenseReAndFwd :call <sid>Mail_Merge_Re_n_Fwd(1,1)
68 "===============================================================
69 " Global definitions {{{1
70 if exists("g:loaded_Mail_Re_set_vim") | finish | endif
71 let g:loaded_Mail_Re_set_vim = 1
72 "===============================================================
74 "---------------------------------------------------------------
75 " Function: s:SetVar() to global or default {{{3
76 function! s:SetVar(var,default)
77 if exists('b:'.a:var) | let s:{a:var} = b:{a:var}
78 elseif exists('g:'.a:var) | let s:{a:var} = g:{a:var}
79 else | exe "let s:{a:var} =".a:default
80 " Rem: doing :exe to dequote a:default
83 command! -nargs=+ SetVar :call <sid>SetVar(<f-args>)
85 "---------------------------------------------------------------
86 SetVar mail_re_fmt 'Mimic:'
87 " SetVar mail_re_fmt 'Re[1]:'
90 " "Re[]:" -- use "Re[n]:" when {n}>1 ; "Re:" when {n}=1
91 " "Re[1]:" -- use "Re[n]:" even when {n}>1
92 " "Mimic:" -- use what the correspondant used
93 " In order to override the default, set g:mail_re_fmt or b:mail_re_fmt to one
94 " of the previous values.
96 SetVar mail_tag_placement 'tag_second'
98 " "detect_news" -- use "Re: [TAG]" for usenet, "[TAG] Re:" otherwise
99 " "tag_second" -- use "Re: [TAG]" ; seems the right option on usenet
101 " "tag_first" -- use "[TAG] Re:" ; seems the option used on taggued
104 "---------------------------------------------------------------
107 "===============================================================
109 "---------------------------------------------------------------
110 " Function: ErrorMsg(msg) {{{3
111 function! s:ErrorMsg(msg)
112 if has('gui_running')
113 call confirm(a:msg, '&Ok', 1, "Error")
120 command! -buffer -nargs=1 MailErrMsg :call <sid>ErrorMsg(<args>)
122 "---------------------------------------------------------------
123 " Function: InsertReToSubject(Re_expr, Subject, Tag) {{{3
124 " Purpose: Add the Re expression to the subject according to the possible
125 " presence of tags in the subject.
126 " So, "[Tag] subject" will be changed to "[Tag] Re: subject" and
127 " not "Re: [Tag] subject"
128 function! s:InsertReToSubject(Re_expr, Subject, Tag)
129 let tag = (''!=a:Tag) ? a:Tag . ' ' : a:Tag
130 if "detect_news" == s:mail_tag_placement
132 let l = search('^\s*$')
133 let l = search('^Newsgroups:', 'bW')
134 let tag_after = (0!=l) ? 1 : 0
135 elseif "tag_second" == s:mail_tag_placement | let tag_after = 1
136 elseif "tag_first" == s:mail_tag_placement | let tag_after = 0
137 else | let tag_after = 0
138 MailErrMsg "<".s:mail_tag_placement."> is not a valid value for mail_tag_placement"
139 " behave as tag first !
142 return 'Subject: '.a:Re_expr. tag . a:Subject
144 return 'Subject: '. tag .a:Re_expr. a:Subject
146 " return substitute(a:Subject, '^\(\[.*\] \=\)\=', ' \1'.a:Re_expr, '')
149 "---------------------------------------------------------------
150 " Function : MergeRe(string) {{{3
151 " Purpose : Substitute "Re:Re:Re:Re" by "Re:" and "Re:Re[2]" by "Re[3]"
152 let s:tag = '\[.\{-}\]'
154 let s:Res = s:Re.'\s*'
155 let s:ReN = '\<Re\s*\[\s*\d\+\s*\]\s*:'
156 let s:ReNs = s:ReN.'\s*'
157 let s:anyRes = '\('.s:Res.'\|'.s:ReNs.'\)'
159 function! s:ChangeReVariantsToRe(subject) " {{{4
160 " We replace all the "Réf.:", "Re :" or "Rép:" by "Re:" and erase spaces
162 let subject = substitute( a:subject, '\c\<r[ée]f\s*\.\s*:\s*', 'Re:', 'g' )
163 let subject = substitute( subject, '\c\<r[ée]p\s*:\s*', 'Re:', 'g' )
164 let subject = substitute( subject, '\c\<re\%(\s\|'.nr2char(160).'\)*\(\[\d\+\]\)\=\s*:\s*', 'Re\1:', 'g' )
165 " Note: the nr2char(160) is used because some people uses very odd
166 " whitespace characters...
172 func! s:MergeRe(string)
173 " We retrieve the "real" subjet (discarding of all the "Re:")
174 " But first, we replace all the "Réf.:", "Re :" or "Rép:" by "Re:" {{{4
175 " and erase spaces between "Re:"
176 let subject = s:ChangeReVariantsToRe( a:string )
178 " Strip the first found [TAG] {{{4
179 let s2 = substitute(subject, '^\(Subject:\s*'.s:anyRes.'*\)'.s:tag, '\1', '')
180 " We search for the first "Re:" {{{4
181 let rank=match(s2, '^Subject:\s*\('.s:Re.'\|'.s:ReN.'\)')
182 if -1 == rank " If none, it means we have finished
184 else " If there are "Re:" to discard ... {{{5
187 " Strip "Subject:\s*"
188 let tag = substitute(subject, '^Subject:\s*'.s:anyRes.'*\('.s:tag.'\)\=.*', '\2', '')
189 " call confirm("S2=".s2."\ntag=".tag, 'ok')
190 let subject = substitute(s2, '^Subject:\s*', '', '')
191 " Strip all the "Re:"
192 let Re_s = substitute(matchstr(subject, '^'.s:anyRes.'*'), '\s\+', '', 'g')
193 let subject = substitute(subject, '^'.s:anyRes.'*', '', '')
194 let Re_n = substitute(Re_s, s:Re, '', 'g')
195 " Compute the number of "Re:" stripped
196 let n = (strlen(Re_s)-strlen(Re_n))/3
197 " let subject = Re_n.subject
199 " If there are "Re:" to discard ...
201 " Once at the end, we check for the presence of a "Re[x]:" {{{4
202 let end = matchend(Re_n, s:ReNs)
205 let num = strpart(Re_n,0,end)
206 " We extract the number x from "Re[x]" ...
207 let n2 = n2 + matchstr(num,'[0-9]\+')
208 let Re_n = strpart(Re_n, end)
209 let end = matchend(Re_n, s:ReNs)
212 " Determine the format of the subject for the reponse {{{4
213 if "Mimic:"==s:mail_re_fmt | let do_num = (0!=n2) ? 1 : 0
214 elseif "Re[1]:"==s:mail_re_fmt | let do_num = 1
215 elseif "Re[]:" ==s:mail_re_fmt | let do_num = ((n+n2)>1) ? 1 : 0
216 elseif "Re:" ==s:mail_re_fmt | let do_num = 0
217 else | MailErrMsg "<".s:mail_re_fmt."> is not a valid value for mail_re_fmt"
218 " behave as "mimic" !
219 let do_num = (""!=num) ? 1 : 0
222 " Build the subject of the response {{{4
224 " add num to n (the number of "Re:")
226 let subject = s:InsertReToSubject('Re['.n.']: ', subject, tag)
227 " let subject = 'Subject: '.tag.'Re['.n.']: ' .subject
228 " let subject = 'Subject: '.'Re['.n.']: '.tag .subject
230 let subject = s:InsertReToSubject('Re: ', subject, tag)
231 " let subject = 'Subject: '.tag.'Re: ' .subject
232 " let subject = 'Subject: '.'Re: '.tag .subject
235 " Finally, we return the new subject {{{4
239 "---------------------------------------------------------------
240 " Function : MergeFwd(string) {{{3
241 " Purpose : Substitute "Fwd:Fwd:Fwd:Fwd" by "Fwd:"
243 let s:Fwds = s:Fwd.'\s*'
244 let s:anyFwds= '\%(' . s:Fwds . '\)'
246 function! s:ChangeFwdVariantsToFwd(subject) " {{{4
247 " We replace all the "(fwd)", "Fw:", "Fwd :" or "Tr:" by "Fwd:" and erase
248 " spaces between "Fwd:"
249 let subject = substitute( a:subject, '\c\<fwd\=\s*:\s*', 'Fwd:', 'g' )
250 let subject = substitute( subject, '\c\<tr\s*:\s*', 'Fwd:', 'g' )
251 let subject = substitute( subject, '\c(fw\=d)\s*', 'Fwd:', 'g' )
252 let subject = substitute( subject, '\c\<fwd\%(\s\|'.nr2char(160).'\)*:\s*',
254 " Note: the nr2char(160) is used because some people uses very odd
255 " whitespace characters...
261 func! s:MergeFwd(string)
262 " We retrieve the "real" subjet (discarding of all the "Fwd:")
263 " First, we replace all the "(fwd)", "Fw:", "Fwd :" or "Tr:" by "Fwd:" {{{4
264 " and erase spaces between "Fwd:"
265 let subject = s:ChangeFwdVariantsToFwd( a:string )
267 " We search for the first "Fwd:" {{{4
268 let rank=match(subject, '^Subject:\s*'.s:Fwd)
269 if -1 == rank " If none, it means we have finished
271 else " If there are "Fwd:" to discard ... {{{5
272 " Strip "^Subject:\s*"
273 let subject = matchstr(subject, '^Subject:\s*\zs.*')
274 " Store the forward-string used by the MUA
275 let fwd = matchstr(a:string, '^Subject:\s*\zs\S\{-}:\= ')
276 " Strip all the "Fwd:"
277 let subject = substitute(subject, '^'.s:anyFwds.'*', '', '')
280 " Build the subject of the response {{{4
281 let subject = 'Subject: '. fwd . subject
283 " Finally, we return the new subject {{{4
287 "---------------------------------------------------------------
288 " Function: Mail_Merge_Re_n_Fwd() {{{3
289 " Purpose: Main function ; goto start of file and execute the replacement of
291 function! s:Mail_Merge_Re_n_Fwd(do_re, do_fwd)
294 let l = search('^Subject:')
296 " Get the corresponding line
298 " And call MergeRe() & MergeFwd() on it.
299 if a:do_re | let c = s:MergeRe(c) | endif
300 if a:do_fwd | let c = s:MergeFwd(c) | endif
305 "---------------------------------------------------------------
307 "===============================================================
309 "===============================================================
310 " vim600: set fdm=marker: