ÿØÿà JFIF    ÿÛ „ !.%+&8&+/1555$;@;4?.451 4,$,44444444444414444444444444444444444444444444444444ÿÀ  á á" ÿÄ     ÿÄ ?    !1AQaq"2‘¡±ÁðBRbrÑá#‚’¢²3S CñÿÄ   ÿÄ !    !1QAa‘2ÿÚ   ? 5˜Z¯V¦cø)›t/? z¨±>Õ5€¶‹Á¤·¼z¼Ü¬+ñ®v¤¨_ˆR­BFn©—˜ý®ç̝P8gýt·ÉSTŦˆìät?þé¼íìN/Þa)ì–í6ô… Ï¿øÃj´¿KÇü]ÿ ªô¹-eKànëÕHTx}ýSÜ›ÿ ”7Ø×&µ<¦  ¥ÑO¶[Ù¯ä¨ÞÃÿ PZ-¬;#õ|•oaÿ ©CìÞz3˜öː/¤­ñTûIØ}š^ mÓ%ªxˆ¥ÉŸu=Z+ISe¿45™¼u;ú&WØ÷€æßQ™®{|íx*TC“#ZŠìZ§²‹ 6pv…³¿¡äª*áZÐ%ÒOáˆo"x«OHk w±æ+¬V(kMúŸ5Vö«$ ÁrÏbàb57/luR ¸ÑÛj Òµì`Мq­û žICÀÊ•©4€Âcà¨Ï€O´<èÐ:›ù(Ë^L8þ‘ÍÌ#¸Ð_Ì©ÙK(Öz 4¬û+¸;ü’V’84‘¬ÃŽ:[â‡ÔÌáõp¢~§ªlæ£ö{®G>J¼"°‡7¯ÆÉèßû ‹É‹§ÁòÃýâßî ^ƾÙõ‹×óH#«LP½ïX=xÑÍ$|W?•~• îëÔ©ª‹ {ÝT…Kÿ ”hûâá)J*ö˜–ÔU;iÇ€/ ÆþjóZ\ýwØ=Ìm ºèËL9 ýèÆð/¨’¥öo=nË.%Îì ŽÕ¯È|{Oj²ƒE6e/ßdÄõ²Ìâ1O®ò×TsəԸhOMýíMˆ¿¼H˜l²,7Â¥#MF/Úf°Ö½± ¸–dr‹NýÊ íjqx{œÉ ä-È ¦ øÄër¨q°ð †nцýÑÄÆ’mä…n<0È™;ÁÝá¯ÁZƒ7FÀmì­ É&9ˆîéi¶ùN§Y• ÃZãAâ?•‡©‰ , ó¾IŸŠc1 4â&y­&pŠ­6;M À 0¹qç»p.á …ŸÅáK@%6·y6ƒ‰3?”úºŽ‰éX5ªPT §µ!=Mž«Ú½‹ÅgÂSâÉaþÓoö–¯ÁÔìR>5éÿ üs¶ÆUcÌ kÇR ]ÿ ù¬¼«VŽ;Â|‡~¢¦”ÏŰæ {L™Õ°Óv¹ò¸írޡעCÃ!íVÕ {¶»sŒNPg/ "uÕbkm²“$ďå¿é¹§°½æz¯6 †s¿!s–wÚÝ“™Œ °.ûj>·+™Òa…©Œ&rÝÎtÛë긪Ît’LAVp%c Úý[ÄzJ¾ÇàXXç@˜ó<êL]·T˜¾¥1Ó©V‡g´æ½¦Ý@¹óø!_@´ÞâSÁ —S3™•& ]@JHÚý©ZŽ €×æÔr»Áf!‡yÞ4Mv*èÓã_{‘åóUuљØ«Oïé*®EvÑ Œ÷‡U \"㪒ÍK+À 4“M¡ï:0¥5í!'<@î´”>Ç»&Z–ïCCV˜Ì5Šo&îhè.žû |ÓK©h$s6KìŒëã)¹hI¦GïOåóI;ììü#É$Š0…Ææ¥TØ.5­¾gn´ “ÂÖ\:hœ89G)J@„}œ:’Ò{/Š"¦_Æ×7Æ3VÇŠÊa]ÚŒÙ€Ä–=®uÁßâACZƒ§§£ Qnâ:«,×{tyø¬iÛcœÜÄ€H½ÄÍCk´÷šß .W'b¤Íåh]÷€=,Žv×cÚEÚHXJX¶îo¨FÒtèöŸ>ªª6[J®Fµ£sGÁeqõfe\íjÒÐïÄÐGˆe1Ø‹.Ø”‘Ëuø Y­ˆÜ ŽG|zùªüMpDnQWÄ”%JŠ™)â*p@Örš«ÕT2Ð%ˆG#ª„ ·¤!°ŸOTÂT¸aÚ%4&h™LµšØüÐ.F¿²ÐÞ_Ç‚¾ÅÃaÜ÷09Æ q€öy˜v‡85õN÷]¬äѼóS{°_MެúÔ#°Ç¸0åÞè2ëôPcvÆw9®ií1Ä8F™˜à‰´+‰Ik1òÝ7“Ñ×ÒsÝ\x‚h`ÞÑ`ó"|µEcý£n˜h`}GÞ !±ù²Ápü²ß6 0ïi󜵩SÈÇ7˜-ÕURO˜¦´f$ªž-Í6(œ}<„ éc øs]ŽŽ„*—¾ ìdŽ„)méª\¿êÎIg¾ØÞ~I#C/¼¼´EÁÈŽi8“©õådô·>euä ƒ'Ê×लR1ÉJE1ÐAát`t;ÇР%Ý<‡¥„ÍÆ`×Oyó)õiI€ñQaŸ4Ûù\áàaÃÔ¹HÃu¹*k€¦<„e S‡&õÏ B!ŽhüÞ`yj}mªf×\¿ Ç~æ­9‡û\՞Ǖg²1Žû5V7 !àöšm° c`ܬøÇìµÒ'P"?…´Ö,"§^•õލsÔ)6˜sæéÍR¼ ò|Sl”‹7 nPW Gòú÷½§O¯‡„l¡kSÞŒr½PÊ@æ¢pŽ-mÿ #Ÿ˜Àº¶Áä¦;ïÔæ$1££`“Õ>„—·ž)ßð³ñ#Ï Ô$¶œ‰ÊE‹À;÷º ¯«P:Ñ”8–IÊtpÞ3ª“>ê“þës4ò2OÏÕ­±zô†Õ§‰.÷ä¸;¿˜“'œ›žª}«Œ{ª±Ì 9ÔóÞÕ‡0 $íWV3Üì¬ —@kÝ4@¿r¼±½¬™›?øØæ´'Áé®CË3-g$˜ö‡×auÚi´Žp/êÛ æF›Ú2v‹ã¿¿,nB1̨ƃqÞa5͝@&Æû“él÷ \C²½UÍc ¯k×¢U ÖéQå™—-r wô ÞÏ<Ò=&=ÿ Ôê Òêˈt,i—;LîÜ á¸*ÚÃ1$êL•LÍ <É)ýÐà’ ;F™{ƒ™˜€&'}‚ãÄK`¡ÞT@I;®žZóè‚s’7®°›+§O­Åq©é»²9<Ô J ¼9O’HL»Ùïì¸rk¼Ž_ý‘TŸu[²ßÚŒ·ü÷B%¯E ŸÔX5êO´ Ç•€’I0 ÉJX` ñ¹õ%;µŸD‘«´€àwÒ™U ûئžÖö\×®×´8 ½‡ºÐÆÓ§?Àkmœ=;d5*@-ì0F Rªýš[Ü6âö̃ڸr*KA9· u*µæ£?U¸Âêí†8@¦X4 e-ò„0s{ HâUpU?¼mñRa°®a%Ð'tÉ×’\¾ÊÉ]t›h>·(Ë@R¼¡Ãt h}’O÷au<+nT…Ö…MӐ??Óe95 q>í/;&JSû °¯ÊéÞ øƒ*Ã2½Ài&:nôUl=¾¿5eˆ3”ñc|Ú2V”>„»&eE;«ÚäC p¢Û úy 9š[ŒÌx¼擼A&DåÒ¯ˆ¤ÀÌ;"˜ ÏQä¸åhÊ}Ûq«Û0WžÒ|»€ø®öCm5•\ÇÀ§Pe3£]0ÃàLDÉ‰1øªxjgwT‚÷¿LΨK‹›ùs—xˆÜ±µ kæ¸f‰‰ÜGk/LÛØ6d9ò¶ùA{ƒA3š/¬D¬khÓk‰`˜"㯒r¿±Óã jx‡°e}<Ñø\3y:'À•/h½Í€Ç4~g ?Û(¼]v‘ªlKÎâ~?O‚W%{Ì:“'©úNq¾›úo(X’¥¯ˆ nFê{Ç€ü?º'ë ø‹ì Þ09ŒÌç9Æ —ËC`j@ÓÄ(+a‹un¸#ÂꟋ{K`‘ÑÍÍ'à´»/Û,KW;Þ4²þð ï Nm|~fGÏ(…³Ã)«1ö­Õ ¥‡¨©ƒÃ™ü-s=à=U66Ï«Ýc蓦W¹íž®›nÔ%êÇìŒ<#Ü×84ån®Ð ÒåOC` ñânÑs‡¢ç 1õ%Îhì½Ã½® e:ݼUZo™`  ÅZŸŒÊ«ê1ÏÄo$q¹Þ€©ˆhÐÉä¯ñ[!…Ú˜àJ:x2$Íß&PåT£6ç— ‡Í*4Ýšçjÿ ‰É nófÐ ó(L5C•åÆ\rMÒ@ò }y-W}™üýVù—ú¢=Ù”c®‘< M ž ´Phr ¦©TD ‘ù.$´÷O‡‘V2Æò.=IUŒ=ž‡â¬i™aþÓåÙ?òUø'ØÖ•.~* šTŒ!•-×áºTâ®ä#õü'´ eýlYÅÓeÕKÂrT"CÚ@u!Óxƒ{š3€}1¿(r}%«nËamjÑ%ÑNEò v ˜à  σöK³,*º.àzù¨™Ó ÚçâU¦*¿ 9{%Ö¹ njûdaXöb) kÛÆ±ûÓ\°M7ˆÂ=û›ç¿Ã‚­V»Cg–8ÙêE- j)k$º`Ã-ùEýeBÆÇ]c¡°ñty&Òd0nõ'¡W+ƒ*|–øµFa\GQªEAÔp5\Ǽ·¼Ç8·õ -â§Ú[ ‡ uZeÖ 3}×d'+¹:ð+K†Û®s!Ï$úe€<Û”x)1»a­¡LC]¸µík…ÚàA»AYº{†ªS[¦5HÒ7ù --,ísòDØ€èk ÞÀîÜ ò@â( ËNˆë›4ô½•/¦o‡€Û7 ê•ÆêòðÜy'Án½µ á˜ݦ ndeo…[ì¶Ê,¥R³Ä=À±—–ß;£™´ñSâ*g§”ïaið‘Jå~™ÓÞ ß³Õ¢»8x埒²52>AÊb&-÷\7´éÄù€T˜,w;3{ï˜k…à¹ÄqÀ«œ{€\ ˆ¾[´¨јr &Úé„Ívˆ±8†¿]|¬ņ4I×pÞS1ÈÖz‰#Ìv‡G!YNògñ:màTz¢Ý1ô©^O=~ë|5Bã™ç•¼µõ•bÆ@úÕS¬ÈŒ#¬zünrŸ û” Z²•èðV"ÁHÚý©wÝ €7¼Ìu1hÑa3Éä û f$o¿É ™Ú›ÝçnpÒ3äÌ3†Í§,Äï]$‰/pê †«À¼¸e9­Æê_C]žƒ·ý·frÁN«, E=›Çq -‰öŒ:aÏ¿±í&£Í:-} 84‘ÿ eƒQÑeëSsuiA ³g㟥ú£?ÿ ʼn*”“÷aühe:ÊWa@ÒÞk±eØ] F Ô—r.åä˜ @ö¥ªZoÐýYL·¥S²G/‡ñ <~*ZÆ´è>JlòàÛÆ½ÿ 窘ìGN¢:I®KšJp/`íIÁÀõ#Ä-€ö­šµŒoF4|ÆQØÆ@Ì|£Ô…¢À{9˜è½Üó›€ôYÒÎYsið;ís¤€à²ˆ‚4qÉVŒI$ ‰"° æµ8cXGjœˏ¡Aâý•ËÜ¢ûï e·çLx']á"oÅÎê3¯Ç—¹”ó0nå‚âg{Œñ> S´˜îè°g238‚ãköÝfÚd´6Ò€;ò÷±¢™¼›º ¢Æ'¥Ðx'e¬ç ]bÈÆV¢ó‹kýBO ðÊâ$Ÿ!×T 3Mýמ žìٍàÌü‘8÷€àæØ8æ©6‰©L´«…oãpð„~Çk‰!ñ;‹”ÛžÍ àž±z Ÿôû øŸÝužÏ;ÿ #|u6™Þ¬ÚˆÐõA4¶â|ôl|Ê2ŽÇ¤ÝÅÇY.<#Aí.k§hóF‚”Y; M½Ö4hŸ4&›­¿tès´%FìL¥£Ãk‰ÇT¤haÁ¤ÚxfÉ`ÑìË›>i 3t‚:,–+^÷´–{Û–Nxi"x‘Ûg î¨>¥Õ܁ùZH,2Û“:8xÊ¢Çí9.É-Ìâã-=çjwµS˜dütžçwýGòú®®ûº_ˆýx$–¡ãøO EÚÛÏ÷R„×w+3£Á£öUMyR²¹âŒ°š›¸Ñãò9§Ó_Dl+Ùßc›úšGÅÌc†Ž!Ko=¶.‘Îÿ c²(2®V mª.ÿ ¹B›¹å ù„öŸSV>™ü¯$y:G¢Z×àøúdî¹û­·ýÇ´:•c LÍõi_‹ö+ÎæGÊè>OŠ•äž´§Þ{X}¨1ÚTc›»Qþ•êô°t¿OP?eæ~É{5]•ÙR£r5†nZ\ã@ &îJõ ¾àC°þV>fé¥/ü5ñÊIº_é5 ;e­h<@ Ä&æÃëE%;X,ÒãÆÞ`Oò¦kŸm#˜!ÀyÄ¢| óLšò¥Ä` ¶R=|ÈCâh5ò3DˆïF†ðÒ#ÅìÛœ?¸yhBãœí ZxßÎÄhºRK„`Þödvײ™ÀÈÑÒgŒuY w³%†ƒÓzõ ÖÏp‚dH®¦A´ù§»ÓÇMæ~)ˆð‡û:ù&Ä •vGD´À n ݇¼Ö8Fö óáà£~Ë¥x`oK|Ä?fxiØü%pìR>éò+Û±éÎ>núlFŤ'tq8LZÏvÃ?„¡ß±È⽆¯³íü@x|PöUäèØã¡ð‚ŒAìÏ"vÍwóŸÍ{ ý0.z È•Ö{,N¡£¡ŸKÕÙž>Ýœþ ÍÀ°<×EA!Å‚D™IúOÍ¡>ôG}Â` ÍßkÜL™Ž Þð™ {IøF²¹òQ3&!ÃÂÞz.d&Ï-sH¸,Ôõ˜ŽP€ 77ˆÝ¼ÊëÜw =cÕ Ú,ØÐ5ÎYÐ)ì´öœgŒ[¤ßv㙑8心>h]§µháYš£²ºÑ.{Ï7Sð•?´~×SÃKýJÛ˜ ™Íäiúu<µX¶1õ^kâçIÑ£sZ4h>j*ÔšD:4­¿_ ÷¸ Õxæÿ ¸?Mù _•­ÊÐ ä ÷ý ÑwL œ­ïnTkÛUÍN©ë:¦fV ¶ÜÔÜMªÅâA½–¿R×TXš-%iTÊT•‡Ù‚JôϐZxWÑè‰f‰òG º ×Õû2aZ7OU3[“×AT–ÞŒ…-‘¤”Ì ì&(ˆ¿­•ƒkï’:ðY¦W‘ Å)“†‘˜³Åtcø˜ñTÂwÚÇ4|üLÇªí–v- qˆèU qPE.†â‘˜µ Æ,ÐÅs]8¾„oúÑ i>ÜxxÈó)ƒ ´æÁâØ$À‰vžŸf$Ž |ãw;ÀÁIJ»b` {¦Ó¤Ú$©YÀ‘n@Óïž«9J¼êG m¤ ܯ¹ÌW4€ÐÒÅÛ‡#褕Ÿn-?í|с¥÷Ú¹¬'´ÞÜ9ÓK `hê£SÄSà?7—Wí_´…óB›»:=Ãïq`<8ñÓŒÑlú2d¬ê³£hÖ[l|$vÝro~'R®‰§°ñmY ͧäP |PUª¹·:3Œ[Û{Xÿ ºâ@‚W–Äé u‚ ¯´*=íή.pûÒdt @G‰¬ s¸ ëÉücr ÞæÑ¨Ê@>¤¢Ö±. Þ'¯°ÌME[YéïĵÂCå½ Ué©Áû'Ê9%eÔðNU”ë‘ÌsD3/®+UI˜9h.WC”빓$#:pz:YÓ ¿xž* ³$Í +$kñAŠ‹†¢ Uê>¸)_š¬÷©ßAÂÔb9ÇU ¯¾á•9¯ÏÏ÷O÷¼¼Fähal1‰3Ì[Ïr•´UCksNÐ] R‘¸¥H+§Šé†c©vÖÞ0iÓ76s†î!§=ß ¼~Ô'°Ãmäoäš³ªøi1úÉ)³yV8 CLÄØÁ‘WYïi€H6ÖÑiámø^ÈY´°Ñ7¥Û*—Ñ©L«Qƒï—Ùrÿ ›£Ð*š¸ˆL©ˆ$ˆ ÷¾D§9È®«qbqC)–ˆïv´çñsÑVT­Ø, <àïºÀO«Jý·õ àfPìð .wFšir´þ’2_Y *Æ€x\« ì€9š@ Ž|F⇥ˆkZ@hÖÄ0t¿-<“‹qµ¾*ZL¤Ú)&BJpÓF5=$„at*Zš$’ÑtdûÝRI1 2މ$€$I$#‰SÞ’Hë¬ï;Á$¡t$’`<(ñÇt)$‡Ð.Êf¢X’Kt=Éé$‚ˆªè¢oÝëòI%Rgcª÷ŠyI%¡‰ÿ !ñ)´õ $¤ Ô’IIGÿÙ" Language: OCaml " Maintainer: David Baelde " Mike Leary " Markus Mottl " Pierre Vittet " Stefano Zacchiroli " Vincent Aravantinos " URL: https://github.com/ocaml/vim-ocaml " Last Change: " 2013 Oct 27 - Added commentstring (MM) " 2013 Jul 26 - load default compiler settings (MM) " 2013 Jul 24 - removed superfluous efm-setting (MM) " 2013 Jul 22 - applied fixes supplied by Hirotaka Hamada (MM) if exists("b:did_ftplugin") finish endif let b:did_ftplugin=1 " Use standard compiler settings unless user wants otherwise if !exists("current_compiler") :compiler ocaml endif " some macro if exists('*fnameescape') function! s:Fnameescape(s) return fnameescape(a:s) endfun else function! s:Fnameescape(s) return escape(a:s," \t\n*?[{`$\\%#'\"|!<") endfun endif " Error handling -- helps moving where the compiler wants you to go let s:cposet=&cpoptions set cpo&vim " Comment string setlocal comments=sr:(*\ ,mb:\ ,ex:*) setlocal comments^=sr:(**,mb:\ \ ,ex:*) setlocal commentstring=(*%s*) " Add mappings, unless the user didn't want this. if !exists("no_plugin_maps") && !exists("no_ocaml_maps") " (un)commenting if !hasmapto('Comment') nmap c LUncomOn xmap c BUncomOn nmap C LUncomOff xmap C BUncomOff endif nnoremap LUncomOn gI(* *) nnoremap LUncomOff :s/^(\* \(.*\) \*)/\1/:noh xnoremap BUncomOn :'<,'>`0i(*`>o0i*)`< xnoremap BUncomOff :'<,'>`dd`< nmap s OCamlSwitchEdit nmap S OCamlSwitchNewWin nmap t OCamlPrintType xmap t OCamlPrintType endif " Let % jump between structure elements (due to Issac Trotts) let b:mw = '\:\:\(\\|;;\)' let b:mw = b:mw . ',\:\:\' let b:mw = b:mw . ',\<\(for\|while\)\>:\:\' let b:mw = b:mw . ',\<\(object\|sig\|struct\|begin\)\>:\' let b:mw = b:mw . ',\<\(match\|try\)\>:\' let b:match_words = b:mw let b:match_ignorecase=0 function! s:OcpGrep(bang,args) abort let grepprg = &l:grepprg let grepformat = &l:grepformat let shellpipe = &shellpipe try let &l:grepprg = "ocp-grep -c never" setlocal grepformat=%f:%l:%m if &shellpipe ==# '2>&1| tee' || &shellpipe ==# '|& tee' let &shellpipe = "| tee" endif execute 'grep! '.a:args if empty(a:bang) && !empty(getqflist()) return 'cfirst' else return '' endif finally let &l:grepprg = grepprg let &l:grepformat = grepformat let &shellpipe = shellpipe endtry endfunction command! -bar -bang -complete=file -nargs=+ Ocpgrep exe s:OcpGrep(, ) " switching between interfaces (.mli) and implementations (.ml) if !exists("g:did_ocaml_switch") let g:did_ocaml_switch = 1 nnoremap OCamlSwitchEdit :call OCaml_switch(0) nnoremap OCamlSwitchNewWin :call OCaml_switch(1) fun OCaml_switch(newwin) if (match(bufname(""), "\\.mli$") >= 0) let fname = s:Fnameescape(substitute(bufname(""), "\\.mli$", ".ml", "")) if (a:newwin == 1) exec "new " . fname else exec "arge " . fname endif elseif (match(bufname(""), "\\.ml$") >= 0) let fname = s:Fnameescape(bufname("")) . "i" if (a:newwin == 1) exec "new " . fname else exec "arge " . fname endif endif endfun endif " Folding support " Get the modeline because folding depends on indentation let lnum = search('^\s*(\*:o\?caml:', 'n') let s:modeline = lnum? getline(lnum): "" " Get the indentation params let s:m = matchstr(s:modeline,'default\s*=\s*\d\+') if s:m != "" let s:idef = matchstr(s:m,'\d\+') elseif exists("g:omlet_indent") let s:idef = g:omlet_indent else let s:idef = 2 endif let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+') if s:m != "" let s:i = matchstr(s:m,'\d\+') elseif exists("g:omlet_indent_struct") let s:i = g:omlet_indent_struct else let s:i = s:idef endif " Set the folding method if exists("g:ocaml_folding") setlocal foldmethod=expr setlocal foldexpr=OMLetFoldLevel(v:lnum) endif let b:undo_ftplugin = "setlocal efm< foldmethod< foldexpr<" \ . "| unlet! b:mw b:match_words b:match_ignorecase" " - Only definitions below, executed once ------------------------------------- if exists("*OMLetFoldLevel") let &cpoptions = s:cposet unlet s:cposet finish endif function s:topindent(lnum) let l = a:lnum while l > 0 if getline(l) =~ '\s*\%(\\|\\|\\)' return indent(l) endif let l = l-1 endwhile return -s:i endfunction function OMLetFoldLevel(l) " This is for not merging blank lines around folds to them if getline(a:l) !~ '\S' return -1 endif " We start folds for modules, classes, and every toplevel definition if getline(a:l) =~ '^\s*\%(\\|\\|\\|\\|\\|\\|\\|\\|\\)' exe 'return ">' (indent(a:l)/s:i)+1 '"' endif " Toplevel let are detected thanks to the indentation if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l) exe 'return ">' (indent(a:l)/s:i)+1 '"' endif " We close fold on end which are associated to struct, sig or object. " We use syntax information to do that. if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword" return (indent(a:l)/s:i)+1 endif " Folds end on ;; if getline(a:l) =~ '^\s*;;' exe 'return "<' (indent(a:l)/s:i)+1 '"' endif " Comments around folds aren't merged to them. if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment" return -1 endif return '=' endfunction " Vim support for OCaml .annot files " " Last Change: 2007 Jul 17 " Maintainer: Vincent Aravantinos " License: public domain " " Originally inspired by 'ocaml-dtypes.vim' by Stefano Zacchiroli. " The source code is quite radically different for we not use python anymore. " However this plugin should have the exact same behaviour, that's why the " following lines are the quite exact copy of Stefano's original plugin : " " << " Executing Ocaml_print_type() function will display in the Vim bottom " line(s) the type of an ocaml value getting it from the corresponding .annot " file (if any). If Vim is in visual mode, should be "visual" and the " selected ocaml value correspond to the highlighted text, otherwise ( " can be anything else) it corresponds to the literal found at the current " cursor position. " " Typing 't' (LocalLeader defaults to '\', see :h LocalLeader) " will cause " Ocaml_print_type function to be invoked with the right " argument depending on the current mode (visual or not). " >> " " If you find something not matching this behaviour, please signal it. " " Differences are: " - no need for python support " + plus : more portable " + minus: no more lazy parsing, it looks very fast however " " - ocamlbuild support, ie. " + the plugin finds the _build directory and looks for the " corresponding file inside; " + if the user decides to change the name of the _build directory thanks " to the '-build-dir' option of ocamlbuild, the plugin will manage in " most cases to find it out (most cases = if the source file has a unique " name among your whole project); " + if ocamlbuild is not used, the usual behaviour holds; ie. the .annot " file should be in the same directory as the source file; " + for vim plugin programmers: " the variable 'b:_build_dir' contains the inferred path to the build " directory, even if this one is not named '_build'. " " Bonus : " - latin1 accents are handled " - lists are handled, even on multiple lines, you don't need the visual mode " (the cursor must be on the first bracket) " - parenthesized expressions, arrays, and structures (ie. '(...)', '[|...|]', " and '{...}') are handled the same way " Copied from Stefano's original plugin : " << " .annot ocaml file representation " " File format (copied verbatim from caml-types.el) " " file ::= block * " block ::= position position annotation * " position ::= filename num num num " annotation ::= keyword open-paren data close-paren " " is a space character (ASCII 0x20) " is a line-feed character (ASCII 0x0A) " num is a sequence of decimal digits " filename is a string with the lexical conventions of O'Caml " open-paren is an open parenthesis (ASCII 0x28) " close-paren is a closed parenthesis (ASCII 0x29) " data is any sequence of characters where is always followed by " at least two space characters. " " - in each block, the two positions are respectively the start and the " end of the range described by the block. " - in a position, the filename is the name of the file, the first num " is the line number, the second num is the offset of the beginning " of the line, the third num is the offset of the position itself. " - the char number within the line is the difference between the third " and second nums. " " For the moment, the only possible keyword is \"type\"." " >> " 1. Finding the annotation file even if we use ocamlbuild " In: two strings representing paths " Out: one string representing the common prefix between the two paths function! s:Find_common_path (p1,p2) let temp = a:p2 while matchstr(a:p1,temp) == '' let temp = substitute(temp,'/[^/]*$','','') endwhile return temp endfun " After call: " " Following information have been put in s:annot_file_list, using " annot_file_name name as key: " - annot_file_path : " path to the .annot file corresponding to the " source file (dealing with ocamlbuild stuff) " - _build_path: " path to the build directory even if this one is " not named '_build' " - date_of_last annot: " Set to 0 until we load the file. It contains the " date at which the file has been loaded. function! s:Locate_annotation() let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' if !exists ("s:annot_file_list[annot_file_name]") silent exe 'cd' s:Fnameescape(expand('%:p:h')) " 1st case : the annot file is in the same directory as the buffer (no ocamlbuild) let annot_file_path = findfile(annot_file_name,'.') if annot_file_path != '' let annot_file_path = getcwd().'/'.annot_file_path let _build_path = '' else " 2nd case : the buffer and the _build directory are in the same directory " .. " / \ " / \ " _build .ml " let _build_path = finddir('_build','.') if _build_path != '' let _build_path = getcwd().'/'._build_path let annot_file_path = findfile(annot_file_name,'_build') if annot_file_path != '' let annot_file_path = getcwd().'/'.annot_file_path endif else " 3rd case : the _build directory is in a directory higher in the file hierarchy " (it can't be deeper by ocamlbuild requirements) " .. " / \ " / \ " _build ... " \ " \ " .ml " let _build_path = finddir('_build',';') if _build_path != '' let project_path = substitute(_build_path,'/_build$','','') let path_relative_to_project = s:Fnameescape(substitute(expand('%:p:h'),project_path.'/','','')) let annot_file_path = findfile(annot_file_name,project_path.'/_build/'.path_relative_to_project) else let annot_file_path = findfile(annot_file_name,'**') "4th case : what if the user decided to change the name of the _build directory ? " -> we relax the constraints, it should work in most cases if annot_file_path != '' " 4a. we suppose the renamed _build directory is in the current directory let _build_path = matchstr(annot_file_path,'^[^/]*') if annot_file_path != '' let annot_file_path = getcwd().'/'.annot_file_path let _build_path = getcwd().'/'._build_path endif else let annot_file_name = '' "(Pierre Vittet: I have commented 4b because this was crashing "my vim (it produced infinite loop)) " " 4b. anarchy : the renamed _build directory may be higher in the hierarchy " this will work if the file for which we are looking annotations has a unique name in the whole project " if this is not the case, it may still work, but no warranty here "let annot_file_path = findfile(annot_file_name,'**;') "let project_path = s:Find_common_path(annot_file_path,expand('%:p:h')) "let _build_path = matchstr(annot_file_path,project_path.'/[^/]*') endif endif endif endif if annot_file_path == '' throw 'E484: no annotation file found' endif silent exe 'cd' '-' let s:annot_file_list[annot_file_name]= [annot_file_path, _build_path, 0] endif endfun " This variable contains a dictionary of lists. Each element of the dictionary " represents an annotation system. An annotation system is a list with: " - annotation file name as its key " - annotation file path as first element of the contained list " - build path as second element of the contained list " - annot_file_last_mod (contain the date of .annot file) as third element let s:annot_file_list = {} " 2. Finding the type information in the annotation file " a. The annotation file is opened in vim as a buffer that " should be (almost) invisible to the user. " After call: " The current buffer is now the one containing the .annot file. " We manage to keep all this hidden to the user's eye. function! s:Enter_annotation_buffer(annot_file_path) let s:current_pos = getpos('.') let s:current_hidden = &l:hidden set hidden let s:current_buf = bufname('%') if bufloaded(a:annot_file_path) silent exe 'keepj keepalt' 'buffer' s:Fnameescape(a:annot_file_path) else silent exe 'keepj keepalt' 'view' s:Fnameescape(a:annot_file_path) endif call setpos(".", [0, 0 , 0 , 0]) endfun " After call: " The original buffer has been restored in the exact same state as before. function! s:Exit_annotation_buffer() silent exe 'keepj keepalt' 'buffer' s:Fnameescape(s:current_buf) let &l:hidden = s:current_hidden call setpos('.',s:current_pos) endfun " After call: " The annot file is loaded and assigned to a buffer. " This also handles the modification date of the .annot file, eg. after a " compilation (return an updated annot_file_list). function! s:Load_annotation(annot_file_name) let annot = s:annot_file_list[a:annot_file_name] let annot_file_path = annot[0] let annot_file_last_mod = 0 if exists("annot[2]") let annot_file_last_mod = annot[2] endif if bufloaded(annot_file_path) && annot_file_last_mod < getftime(annot_file_path) " if there is a more recent file let nr = bufnr(annot_file_path) silent exe 'keepj keepalt' 'bunload' nr endif if !bufloaded(annot_file_path) call s:Enter_annotation_buffer(annot_file_path) setlocal nobuflisted setlocal bufhidden=hide setlocal noswapfile setlocal buftype=nowrite call s:Exit_annotation_buffer() let annot[2] = getftime(annot_file_path) " List updated with the new date let s:annot_file_list[a:annot_file_name] = annot endif endfun "b. 'search' and 'match' work to find the type information "In: - lin1,col1: position of expression first char " - lin2,col2: position of expression last char "Out: - the pattern to be looked for to find the block " Must be called in the source buffer (use of line2byte) function! s:Block_pattern(lin1,lin2,col1,col2) let start_num1 = a:lin1 let start_num2 = line2byte(a:lin1) - 1 let start_num3 = start_num2 + a:col1 let path = '"\(\\"\|[^"]\)\+"' let start_pos = path.' '.start_num1.' '.start_num2.' '.start_num3 let end_num1 = a:lin2 let end_num2 = line2byte(a:lin2) - 1 let end_num3 = end_num2 + a:col2 let end_pos = path.' '.end_num1.' '.end_num2.' '.end_num3 return '^'.start_pos.' '.end_pos."$" " rq: the '^' here is not totally correct regarding the annot file "grammar" " but currently the annotation file respects this, and it's a little bit faster with the '^'; " can be removed safely. endfun "In: (the cursor position should be at the start of an annotation) "Out: the type information " Must be called in the annotation buffer (use of search) function! s:Match_data() " rq: idem as previously, in the following, the '^' at start of patterns is not necessary keepj while search('^type($','ce',line(".")) == 0 keepj if search('^.\{-}($','e') == 0 throw "no_annotation" endif keepj if searchpair('(','',')') == 0 throw "malformed_annot_file" endif endwhile let begin = line(".") + 1 keepj if searchpair('(','',')') == 0 throw "malformed_annot_file" endif let end = line(".") - 1 return join(getline(begin,end),"\n") endfun "In: the pattern to look for in order to match the block "Out: the type information (calls s:Match_data) " Should be called in the annotation buffer function! s:Extract_type_data(block_pattern, annot_file_name) let annot_file_path = s:annot_file_list[a:annot_file_name][0] call s:Enter_annotation_buffer(annot_file_path) try if search(a:block_pattern,'e') == 0 throw "no_annotation" endif call cursor(line(".") + 1,1) let annotation = s:Match_data() finally call s:Exit_annotation_buffer() endtry return annotation endfun "c. link this stuff with what the user wants " ie. get the expression selected/under the cursor let s:ocaml_word_char = '\w|[\xc0-\xff]|''' "In: the current mode (eg. "visual", "normal", etc.) "Out: the borders of the expression we are looking for the type function! s:Match_borders(mode) if a:mode == "visual" let cur = getpos(".") normal `< let col1 = col(".") let lin1 = line(".") normal `> let col2 = col(".") let lin2 = line(".") call cursor(cur[1],cur[2]) return [lin1,lin2,col1-1,col2] else let cursor_line = line(".") let cursor_col = col(".") let line = getline('.') if line[cursor_col-1:cursor_col] == '[|' let [lin2,col2] = searchpairpos('\[|','','|\]','n') return [cursor_line,lin2,cursor_col-1,col2+1] elseif line[cursor_col-1] == '[' let [lin2,col2] = searchpairpos('\[','','\]','n') return [cursor_line,lin2,cursor_col-1,col2] elseif line[cursor_col-1] == '(' let [lin2,col2] = searchpairpos('(','',')','n') return [cursor_line,lin2,cursor_col-1,col2] elseif line[cursor_col-1] == '{' let [lin2,col2] = searchpairpos('{','','}','n') return [cursor_line,lin2,cursor_col-1,col2] else let [lin1,col1] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','ncb') let [lin2,col2] = searchpos('\v%('.s:ocaml_word_char.'|\.)*','nce') if col1 == 0 || col2 == 0 throw "no_expression" endif return [cursor_line,cursor_line,col1-1,col2] endif endif endfun "In: the current mode (eg. "visual", "normal", etc.) "Out: the type information (calls s:Extract_type_data) function! s:Get_type(mode, annot_file_name) let [lin1,lin2,col1,col2] = s:Match_borders(a:mode) return s:Extract_type_data(s:Block_pattern(lin1,lin2,col1,col2), a:annot_file_name) endfun "In: A string destined to be printed in the 'echo buffer'. It has line "break and 2 space at each line beginning. "Out: A string destined to be yanked, without space and double space. function s:unformat_ocaml_type(res) "Remove end of line. let res = substitute (a:res, "\n", "", "g" ) "remove double space let res =substitute(res , " ", " ", "g") "remove space at beginning of string. let res = substitute(res, "^ *", "", "g") return res endfunction "d. main "In: the current mode (eg. "visual", "normal", etc.) "After call: the type information is displayed if !exists("*Ocaml_get_type") function Ocaml_get_type(mode) let annot_file_name = s:Fnameescape(expand('%:t:r')).'.annot' call s:Locate_annotation() call s:Load_annotation(annot_file_name) let res = s:Get_type(a:mode, annot_file_name) " Copy result in the unnamed buffer let @" = s:unformat_ocaml_type(res) return res endfun endif if !exists("*Ocaml_get_type_or_not") function Ocaml_get_type_or_not(mode) let t=reltime() try let res = Ocaml_get_type(a:mode) return res catch return "" endtry endfun endif if !exists("*Ocaml_print_type") function Ocaml_print_type(mode) if expand("%:e") == "mli" echohl ErrorMsg | echo "No annotations for interface (.mli) files" | echohl None return endif try echo Ocaml_get_type(a:mode) catch /E484:/ echohl ErrorMsg | echo "No type annotations (.annot) file found" | echohl None catch /no_expression/ echohl ErrorMsg | echo "No expression found under the cursor" | echohl None catch /no_annotation/ echohl ErrorMsg | echo "No type annotation found for the given text" | echohl None catch /malformed_annot_file/ echohl ErrorMsg | echo "Malformed .annot file" | echohl None endtry endfun endif " Maps nnoremap OCamlPrintType :call Ocaml_print_type("normal") xnoremap OCamlPrintType :call Ocaml_print_type("visual")`< let &cpoptions = s:cposet unlet s:cposet " vim:sw=2 fdm=indent