import all my dotfiles, awesome included.
[~madcoder/dotfiles.git] / vim / ftplugin / mail / Mail_Re_set.vim
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
7 " Version       : 2.0
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.
12 "
13 " Calling conv. : Source this file and invoke :MailCondenseRe
14 "                 define -> nnoremap ,re :MailCondenseRe<cr>
15 " Installation  : {{{1
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 : 
20 "       "Re:"    -- use "Re:"
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
27 "                               [default]
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
32 " }}}1
33 "---------------------------------------------------------------
34 " History: {{{1
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
49 "                 or b: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]:"
55
56 " TODO: {{{1
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: "
60 " }}}1
61 "===============================================================
62 if exists("b:loaded_local_Mail_Re_set_vim") | finish | endif
63 let b:loaded_local_Mail_Re_set_vim = 1
64
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 "===============================================================
73 " Options {{{2
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
81   endif
82 endfunction
83 command! -nargs=+ SetVar :call <sid>SetVar(<f-args>)
84 " }}}3
85 "---------------------------------------------------------------
86 SetVar mail_re_fmt 'Mimic:'
87 " SetVar mail_re_fmt 'Re[1]:'
88 " Possible formats :
89 "       "Re:"    -- use "Re:"
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.
95
96 SetVar mail_tag_placement 'tag_second'
97 " Possible values :
98 "       "detect_news"   -- use "Re: [TAG]" for usenet, "[TAG] Re:" otherwise
99 "       "tag_second"    -- use "Re: [TAG]" ; seems the right option on usenet
100 "                                            accordingto trn
101 "       "tag_first"     -- use "[TAG] Re:" ; seems the option used on taggued
102 "                                            mailing lists
103
104 "---------------------------------------------------------------
105 delcommand SetVar
106 " Options }}}2
107 "===============================================================
108 " Functions {{{2
109 "---------------------------------------------------------------
110 " Function:     ErrorMsg(msg) {{{3
111 function! s:ErrorMsg(msg)
112   if has('gui_running')
113     call confirm(a:msg, '&Ok', 1, "Error")
114   else
115     echohl ErrorMsg
116     echo a:msg
117     echohl None
118   endif
119 endfunction
120 command! -buffer -nargs=1 MailErrMsg :call <sid>ErrorMsg(<args>)
121 " }}}3
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
131     normal! 1G
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 !
140   endif
141   if tag_after
142     return 'Subject: '.a:Re_expr. tag . a:Subject
143   else
144     return 'Subject: '. tag .a:Re_expr. a:Subject
145   endif
146   " return substitute(a:Subject, '^\(\[.*\] \=\)\=', ' \1'.a:Re_expr, '')
147 endfunction
148 " }}}3
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    = '\[.\{-}\]'
153 let s:Re     = '\<Re:'
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.'\)'
158
159 function! s:ChangeReVariantsToRe(subject) " {{{4
160   " We replace all the "Réf.:", "Re :" or "Rép:" by "Re:" and erase spaces
161   " between "Re:"
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...
167
168   return subject
169 endfunction
170 " }}}4
171
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 )
177
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
183     return subject 
184   else          " If there are "Re:" to discard ... {{{5
185     " n: "Re:" counter
186     let n=0
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
198   endif
199   " If there are "Re:" to discard ...
200
201   " Once at the end, we check for the presence of a "Re[x]:" {{{4
202   let end = matchend(Re_n, s:ReNs)
203   let n2 = 0
204   while -1!=end 
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)
210   endwhile
211
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
220   endif
221
222   " Build the subject of the response {{{4
223   if 1==do_num
224     " add num to n (the number of "Re:")
225     let n = n + n2
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
229   else
230     let subject = s:InsertReToSubject('Re: ', subject, tag)
231     " let subject = 'Subject: '.tag.'Re: ' .subject
232     " let subject = 'Subject: '.'Re: '.tag .subject
233   endif
234
235   " Finally, we return the new subject {{{4
236   return subject
237 endf
238 " }}}3
239 "---------------------------------------------------------------
240 " Function      : MergeFwd(string) {{{3
241 " Purpose       : Substitute "Fwd:Fwd:Fwd:Fwd" by "Fwd:"
242 let s:Fwd    = '\<Fwd:'
243 let s:Fwds   = s:Fwd.'\s*'
244 let s:anyFwds= '\%(' . s:Fwds . '\)'
245
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*', 
253         \ 'Fwd:', 'g' )
254   " Note: the nr2char(160) is used because some people uses very odd
255   " whitespace characters...
256
257   return subject
258 endfunction
259 " }}}4
260
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 )
266
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
270     return subject 
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.'*', '', '')
278   endif
279
280   " Build the subject of the response  {{{4
281   let subject = 'Subject: '. fwd . subject
282
283   " Finally, we return the new subject {{{4
284   return subject
285 endf
286 " }}}3
287 "---------------------------------------------------------------
288 " Function: Mail_Merge_Re_n_Fwd() {{{3
289 " Purpose: Main function ; goto start of file and execute the replacement of
290 " the subject.
291 function! s:Mail_Merge_Re_n_Fwd(do_re, do_fwd)
292   " Search the subject
293   normal! 1G
294   let l = search('^Subject:')
295   if 0 != l
296     " Get the corresponding line
297     let c=getline(l)
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
301     call setline(l, c)
302   endif
303 endf
304 " }}}3
305 "---------------------------------------------------------------
306 " Functions }}}2
307 "===============================================================
308 " }}}1
309 "===============================================================
310 " vim600: set fdm=marker: