| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718 | // CodeMirror, copyright (c) by Marijn Haverbeke and others// Distributed under an MIT license: https://codemirror.net/LICENSE/** * Supported keybindings: *   Too many to list. Refer to defaultKeymap below. * * Supported Ex commands: *   Refer to defaultExCommandMap below. * * Registers: unnamed, -, ., :, /, _, a-z, A-Z, 0-9 *   (Does not respect the special case for number registers when delete *    operator is made with these commands: %, (, ),  , /, ?, n, N, {, } ) *   TODO: Implement the remaining registers. * * Marks: a-z, A-Z, and 0-9 *   TODO: Implement the remaining special marks. They have more complex *       behavior. * * Events: *  'vim-mode-change' - raised on the editor anytime the current mode changes, *                      Event object: {mode: "visual", subMode: "linewise"} * * Code structure: *  1. Default keymap *  2. Variable declarations and short basic helpers *  3. Instance (External API) implementation *  4. Internal state tracking objects (input state, counter) implementation *     and instantiation *  5. Key handler (the main command dispatcher) implementation *  6. Motion, operator, and action implementations *  7. Helper functions for the key handler, motions, operators, and actions *  8. Set up Vim to work as a keymap for CodeMirror. *  9. Ex command implementations. */(function(mod) {  if (typeof exports == "object" && typeof module == "object") // CommonJS    mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/dialog/dialog"), require("../addon/edit/matchbrackets.js"));  else if (typeof define == "function" && define.amd) // AMD    define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog", "../addon/edit/matchbrackets"], mod);  else // Plain browser env    mod(CodeMirror);})(function(CodeMirror) {  'use strict';  var Pos = CodeMirror.Pos;  function transformCursor(cm, range) {    var vim = cm.state.vim;    if (!vim || vim.insertMode) return range.head;    var head = vim.sel.head;    if (!head)  return range.head;    if (vim.visualBlock) {      if (range.head.line != head.line) {        return;      }    }    if (range.from() == range.anchor && !range.empty()) {      if (range.head.line == head.line && range.head.ch != head.ch)        return new Pos(range.head.line, range.head.ch - 1);    }    return range.head;  }  var defaultKeymap = [    // Key to key mapping. This goes first to make it possible to override    // existing mappings.    { keys: '<Left>', type: 'keyToKey', toKeys: 'h' },    { keys: '<Right>', type: 'keyToKey', toKeys: 'l' },    { keys: '<Up>', type: 'keyToKey', toKeys: 'k' },    { keys: '<Down>', type: 'keyToKey', toKeys: 'j' },    { keys: '<Space>', type: 'keyToKey', toKeys: 'l' },    { keys: '<BS>', type: 'keyToKey', toKeys: 'h', context: 'normal'},    { keys: '<Del>', type: 'keyToKey', toKeys: 'x', context: 'normal'},    { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },    { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B', context: 'normal' },    { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },    { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b', context: 'normal' },    { keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },    { keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },    { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },    { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },    { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },    { keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual'},    { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },    { keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual' },    { keys: '<Home>', type: 'keyToKey', toKeys: '0' },    { keys: '<End>', type: 'keyToKey', toKeys: '$' },    { keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },    { keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },    { keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },    { keys: '<Ins>', type: 'action', action: 'toggleOverwrite', context: 'insert' },    // Motions    { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},    { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},    { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},    { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},    { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},    { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},    { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},    { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},    { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},    { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},    { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},    { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},    { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},    { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},    { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},    { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},    { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},    { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},    { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},    { keys: '(', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: false }},    { keys: ')', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: true }},    { keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},    { keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},    { keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},    { keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},    { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},    { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},    { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },    { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },    { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},    { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},    { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},    { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},    { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},    { keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},    { keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},    { keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},    { keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},    { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},    { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},    { keys: '\'<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},    { keys: '`<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},    { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },    { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },    { keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },    { keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },    // the next two aren't motions but must come before more general motion declarations    { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},    { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},    { keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},    { keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},    { keys: '|', type: 'motion', motion: 'moveToColumn'},    { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},    { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},    // Operators    { keys: 'd', type: 'operator', operator: 'delete' },    { keys: 'y', type: 'operator', operator: 'yank' },    { keys: 'c', type: 'operator', operator: 'change' },    { keys: '=', type: 'operator', operator: 'indentAuto' },    { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},    { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},    { keys: 'g~', type: 'operator', operator: 'changeCase' },    { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },    { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },    { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},    { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},    { keys: 'gn', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: true }},    { keys: 'gN', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: false }},    // Operator-Motion dual commands    { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},    { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},    { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},    { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},    { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'expandToLine', motionArgs: { linewise: true }, context: 'normal'},    { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},    { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},    { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},    { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},    { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},    { keys: '<C-u>', type: 'operatorMotion', operator: 'delete', motion: 'moveToStartOfLine', context: 'insert' },    { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },    //ignore C-w in normal mode    { keys: '<C-w>', type: 'idle', context: 'normal' },    // Actions    { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},    { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},    { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},    { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},    { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },    { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },    { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },    { keys: 'gi', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'lastEdit' }, context: 'normal' },    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },    { keys: 'gI', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'bol'}, context: 'normal' },    { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },    { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },    { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },    { keys: 'v', type: 'action', action: 'toggleVisualMode' },    { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},    { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},    { keys: '<C-q>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},    { keys: 'gv', type: 'action', action: 'reselectLastSelection' },    { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },    { keys: 'gJ', type: 'action', action: 'joinLines', actionArgs: { keepSpaces: true }, isEdit: true },    { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},    { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},    { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },    { keys: '@<character>', type: 'action', action: 'replayMacro' },    { keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },    // Handle Replace-mode as a special case of insert mode.    { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }, context: 'normal'},    { keys: 'R', type: 'operator', operator: 'change', operatorArgs: { linewise: true, fullLine: true }, context: 'visual', exitVisualBlock: true},    { keys: 'u', type: 'action', action: 'undo', context: 'normal' },    { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },    { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },    { keys: '<C-r>', type: 'action', action: 'redo' },    { keys: 'm<character>', type: 'action', action: 'setMark' },    { keys: '"<character>', type: 'action', action: 'setRegister' },    { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},    { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },    { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},    { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },    { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},    { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },    { keys: '.', type: 'action', action: 'repeatLastEdit' },    { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},    { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},    { keys: '<C-t>', type: 'action', action: 'indent', actionArgs: { indentRight: true }, context: 'insert' },    { keys: '<C-d>', type: 'action', action: 'indent', actionArgs: { indentRight: false }, context: 'insert' },    // Text object motions    { keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },    { keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},    // Search    { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},    { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},    { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},    { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},    { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},    { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},    // Ex command    { keys: ':', type: 'ex' }  ];  var defaultKeymapLength = defaultKeymap.length;  /**   * Ex commands   * Care must be taken when adding to the default Ex command map. For any   * pair of commands that have a shared prefix, at least one of their   * shortNames must not match the prefix of the other command.   */  var defaultExCommandMap = [    { name: 'colorscheme', shortName: 'colo' },    { name: 'map' },    { name: 'imap', shortName: 'im' },    { name: 'nmap', shortName: 'nm' },    { name: 'vmap', shortName: 'vm' },    { name: 'unmap' },    { name: 'write', shortName: 'w' },    { name: 'undo', shortName: 'u' },    { name: 'redo', shortName: 'red' },    { name: 'set', shortName: 'se' },    { name: 'setlocal', shortName: 'setl' },    { name: 'setglobal', shortName: 'setg' },    { name: 'sort', shortName: 'sor' },    { name: 'substitute', shortName: 's', possiblyAsync: true },    { name: 'nohlsearch', shortName: 'noh' },    { name: 'yank', shortName: 'y' },    { name: 'delmarks', shortName: 'delm' },    { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },    { name: 'vglobal', shortName: 'v' },    { name: 'global', shortName: 'g' }  ];  var Vim = function() {    function enterVimMode(cm) {      cm.setOption('disableInput', true);      cm.setOption('showCursorWhenSelecting', false);      CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});      cm.on('cursorActivity', onCursorActivity);      maybeInitVimState(cm);      CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));    }    function leaveVimMode(cm) {      cm.setOption('disableInput', false);      cm.off('cursorActivity', onCursorActivity);      CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));      cm.state.vim = null;      if (highlightTimeout) clearTimeout(highlightTimeout);    }    function detachVimMap(cm, next) {      if (this == CodeMirror.keyMap.vim) {        cm.options.$customCursor = null;        CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");      }      if (!next || next.attach != attachVimMap)        leaveVimMode(cm);    }    function attachVimMap(cm, prev) {      if (this == CodeMirror.keyMap.vim) {        if (cm.curOp) cm.curOp.selectionChanged = true;        cm.options.$customCursor = transformCursor;        CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");      }      if (!prev || prev.attach != attachVimMap)        enterVimMode(cm);    }    // Deprecated, simply setting the keymap works again.    CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {      if (val && cm.getOption("keyMap") != "vim")        cm.setOption("keyMap", "vim");      else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))        cm.setOption("keyMap", "default");    });    function cmKey(key, cm) {      if (!cm) { return undefined; }      if (this[key]) { return this[key]; }      var vimKey = cmKeyToVimKey(key);      if (!vimKey) {        return false;      }      var cmd = vimApi.findKey(cm, vimKey);      if (typeof cmd == 'function') {        CodeMirror.signal(cm, 'vim-keypress', vimKey);      }      return cmd;    }    var modifiers = {Shift:'S',Ctrl:'C',Alt:'A',Cmd:'D',Mod:'A',CapsLock:''};    var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del',Insert:'Ins'};    function cmKeyToVimKey(key) {      if (key.charAt(0) == '\'') {        // Keypress character binding of format "'a'"        return key.charAt(1);      }      var pieces = key.split(/-(?!$)/);      var lastPiece = pieces[pieces.length - 1];      if (pieces.length == 1 && pieces[0].length == 1) {        // No-modifier bindings use literal character bindings above. Skip.        return false;      } else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {        // Ignore Shift+char bindings as they should be handled by literal character.        return false;      }      var hasCharacter = false;      for (var i = 0; i < pieces.length; i++) {        var piece = pieces[i];        if (piece in modifiers) { pieces[i] = modifiers[piece]; }        else { hasCharacter = true; }        if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }      }      if (!hasCharacter) {        // Vim does not support modifier only keys.        return false;      }      // TODO: Current bindings expect the character to be lower case, but      // it looks like vim key notation uses upper case.      if (isUpperCase(lastPiece)) {        pieces[pieces.length - 1] = lastPiece.toLowerCase();      }      return '<' + pieces.join('-') + '>';    }    function getOnPasteFn(cm) {      var vim = cm.state.vim;      if (!vim.onPasteFn) {        vim.onPasteFn = function() {          if (!vim.insertMode) {            cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));            actions.enterInsertMode(cm, {}, vim);          }        };      }      return vim.onPasteFn;    }    var numberRegex = /[\d]/;    var wordCharTest = [CodeMirror.isWordChar, function(ch) {      return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch);    }], bigWordCharTest = [function(ch) {      return /\S/.test(ch);    }];    function makeKeyRange(start, size) {      var keys = [];      for (var i = start; i < start + size; i++) {        keys.push(String.fromCharCode(i));      }      return keys;    }    var upperCaseAlphabet = makeKeyRange(65, 26);    var lowerCaseAlphabet = makeKeyRange(97, 26);    var numbers = makeKeyRange(48, 10);    var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);    var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '_', '/']);    var upperCaseChars;    try { upperCaseChars = new RegExp("^[\\p{Lu}]$", "u"); }    catch (_) { upperCaseChars = /^[A-Z]$/; }    function isLine(cm, line) {      return line >= cm.firstLine() && line <= cm.lastLine();    }    function isLowerCase(k) {      return (/^[a-z]$/).test(k);    }    function isMatchableSymbol(k) {      return '()[]{}'.indexOf(k) != -1;    }    function isNumber(k) {      return numberRegex.test(k);    }    function isUpperCase(k) {      return upperCaseChars.test(k);    }    function isWhiteSpaceString(k) {      return (/^\s*$/).test(k);    }    function isEndOfSentenceSymbol(k) {      return '.?!'.indexOf(k) != -1;    }    function inArray(val, arr) {      for (var i = 0; i < arr.length; i++) {        if (arr[i] == val) {          return true;        }      }      return false;    }    var options = {};    function defineOption(name, defaultValue, type, aliases, callback) {      if (defaultValue === undefined && !callback) {        throw Error('defaultValue is required unless callback is provided');      }      if (!type) { type = 'string'; }      options[name] = {        type: type,        defaultValue: defaultValue,        callback: callback      };      if (aliases) {        for (var i = 0; i < aliases.length; i++) {          options[aliases[i]] = options[name];        }      }      if (defaultValue) {        setOption(name, defaultValue);      }    }    function setOption(name, value, cm, cfg) {      var option = options[name];      cfg = cfg || {};      var scope = cfg.scope;      if (!option) {        return new Error('Unknown option: ' + name);      }      if (option.type == 'boolean') {        if (value && value !== true) {          return new Error('Invalid argument: ' + name + '=' + value);        } else if (value !== false) {          // Boolean options are set to true if value is not defined.          value = true;        }      }      if (option.callback) {        if (scope !== 'local') {          option.callback(value, undefined);        }        if (scope !== 'global' && cm) {          option.callback(value, cm);        }      } else {        if (scope !== 'local') {          option.value = option.type == 'boolean' ? !!value : value;        }        if (scope !== 'global' && cm) {          cm.state.vim.options[name] = {value: value};        }      }    }    function getOption(name, cm, cfg) {      var option = options[name];      cfg = cfg || {};      var scope = cfg.scope;      if (!option) {        return new Error('Unknown option: ' + name);      }      if (option.callback) {        var local = cm && option.callback(undefined, cm);        if (scope !== 'global' && local !== undefined) {          return local;        }        if (scope !== 'local') {          return option.callback();        }        return;      } else {        var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);        return (local || (scope !== 'local') && option || {}).value;      }    }    defineOption('filetype', undefined, 'string', ['ft'], function(name, cm) {      // Option is local. Do nothing for global.      if (cm === undefined) {        return;      }      // The 'filetype' option proxies to the CodeMirror 'mode' option.      if (name === undefined) {        var mode = cm.getOption('mode');        return mode == 'null' ? '' : mode;      } else {        var mode = name == '' ? 'null' : name;        cm.setOption('mode', mode);      }    });    var createCircularJumpList = function() {      var size = 100;      var pointer = -1;      var head = 0;      var tail = 0;      var buffer = new Array(size);      function add(cm, oldCur, newCur) {        var current = pointer % size;        var curMark = buffer[current];        function useNextSlot(cursor) {          var next = ++pointer % size;          var trashMark = buffer[next];          if (trashMark) {            trashMark.clear();          }          buffer[next] = cm.setBookmark(cursor);        }        if (curMark) {          var markPos = curMark.find();          // avoid recording redundant cursor position          if (markPos && !cursorEqual(markPos, oldCur)) {            useNextSlot(oldCur);          }        } else {          useNextSlot(oldCur);        }        useNextSlot(newCur);        head = pointer;        tail = pointer - size + 1;        if (tail < 0) {          tail = 0;        }      }      function move(cm, offset) {        pointer += offset;        if (pointer > head) {          pointer = head;        } else if (pointer < tail) {          pointer = tail;        }        var mark = buffer[(size + pointer) % size];        // skip marks that are temporarily removed from text buffer        if (mark && !mark.find()) {          var inc = offset > 0 ? 1 : -1;          var newCur;          var oldCur = cm.getCursor();          do {            pointer += inc;            mark = buffer[(size + pointer) % size];            // skip marks that are the same as current position            if (mark &&                (newCur = mark.find()) &&                !cursorEqual(oldCur, newCur)) {              break;            }          } while (pointer < head && pointer > tail);        }        return mark;      }      function find(cm, offset) {        var oldPointer = pointer;        var mark = move(cm, offset);        pointer = oldPointer;        return mark && mark.find();      }      return {        cachedCursor: undefined, //used for # and * jumps        add: add,        find: find,        move: move      };    };    // Returns an object to track the changes associated insert mode.  It    // clones the object that is passed in, or creates an empty object one if    // none is provided.    var createInsertModeChanges = function(c) {      if (c) {        // Copy construction        return {          changes: c.changes,          expectCursorActivityForChange: c.expectCursorActivityForChange        };      }      return {        // Change list        changes: [],        // Set to true on change, false on cursorActivity.        expectCursorActivityForChange: false      };    };    function MacroModeState() {      this.latestRegister = undefined;      this.isPlaying = false;      this.isRecording = false;      this.replaySearchQueries = [];      this.onRecordingDone = undefined;      this.lastInsertModeChanges = createInsertModeChanges();    }    MacroModeState.prototype = {      exitMacroRecordMode: function() {        var macroModeState = vimGlobalState.macroModeState;        if (macroModeState.onRecordingDone) {          macroModeState.onRecordingDone(); // close dialog        }        macroModeState.onRecordingDone = undefined;        macroModeState.isRecording = false;      },      enterMacroRecordMode: function(cm, registerName) {        var register =            vimGlobalState.registerController.getRegister(registerName);        if (register) {          register.clear();          this.latestRegister = registerName;          if (cm.openDialog) {            this.onRecordingDone = cm.openDialog(                document.createTextNode('(recording)['+registerName+']'), null, {bottom:true});          }          this.isRecording = true;        }      }    };    function maybeInitVimState(cm) {      if (!cm.state.vim) {        // Store instance state in the CodeMirror object.        cm.state.vim = {          inputState: new InputState(),          // Vim's input state that triggered the last edit, used to repeat          // motions and operators with '.'.          lastEditInputState: undefined,          // Vim's action command before the last edit, used to repeat actions          // with '.' and insert mode repeat.          lastEditActionCommand: undefined,          // When using jk for navigation, if you move from a longer line to a          // shorter line, the cursor may clip to the end of the shorter line.          // If j is pressed again and cursor goes to the next line, the          // cursor should go back to its horizontal position on the longer          // line if it can. This is to keep track of the horizontal position.          lastHPos: -1,          // Doing the same with screen-position for gj/gk          lastHSPos: -1,          // The last motion command run. Cleared if a non-motion command gets          // executed in between.          lastMotion: null,          marks: {},          insertMode: false,          // Repeat count for changes made in insert mode, triggered by key          // sequences like 3,i. Only exists when insertMode is true.          insertModeRepeat: undefined,          visualMode: false,          // If we are in visual line mode. No effect if visualMode is false.          visualLine: false,          visualBlock: false,          lastSelection: null,          lastPastedText: null,          sel: {},          // Buffer-local/window-local values of vim options.          options: {}        };      }      return cm.state.vim;    }    var vimGlobalState;    function resetVimGlobalState() {      vimGlobalState = {        // The current search query.        searchQuery: null,        // Whether we are searching backwards.        searchIsReversed: false,        // Replace part of the last substituted pattern        lastSubstituteReplacePart: undefined,        jumpList: createCircularJumpList(),        macroModeState: new MacroModeState,        // Recording latest f, t, F or T motion command.        lastCharacterSearch: {increment:0, forward:true, selectedCharacter:''},        registerController: new RegisterController({}),        // search history buffer        searchHistoryController: new HistoryController(),        // ex Command history buffer        exCommandHistoryController : new HistoryController()      };      for (var optionName in options) {        var option = options[optionName];        option.value = option.defaultValue;      }    }    var lastInsertModeKeyTimer;    var vimApi= {      buildKeyMap: function() {        // TODO: Convert keymap into dictionary format for fast lookup.      },      // Testing hook, though it might be useful to expose the register      // controller anyway.      getRegisterController: function() {        return vimGlobalState.registerController;      },      // Testing hook.      resetVimGlobalState_: resetVimGlobalState,      // Testing hook.      getVimGlobalState_: function() {        return vimGlobalState;      },      // Testing hook.      maybeInitVimState_: maybeInitVimState,      suppressErrorLogging: false,      InsertModeKey: InsertModeKey,      map: function(lhs, rhs, ctx) {        // Add user defined key bindings.        exCommandDispatcher.map(lhs, rhs, ctx);      },      unmap: function(lhs, ctx) {        return exCommandDispatcher.unmap(lhs, ctx);      },      // Non-recursive map function.      // NOTE: This will not create mappings to key maps that aren't present      // in the default key map. See TODO at bottom of function.      noremap: function(lhs, rhs, ctx) {        function toCtxArray(ctx) {          return ctx ? [ctx] : ['normal', 'insert', 'visual'];        }        var ctxsToMap = toCtxArray(ctx);        // Look through all actual defaults to find a map candidate.        var actualLength = defaultKeymap.length, origLength = defaultKeymapLength;        for (var i = actualLength - origLength;             i < actualLength && ctxsToMap.length;             i++) {          var mapping = defaultKeymap[i];          // Omit mappings that operate in the wrong context(s) and those of invalid type.          if (mapping.keys == rhs &&              (!ctx || !mapping.context || mapping.context === ctx) &&              mapping.type.substr(0, 2) !== 'ex' &&              mapping.type.substr(0, 3) !== 'key') {            // Make a shallow copy of the original keymap entry.            var newMapping = {};            for (var key in mapping) {              newMapping[key] = mapping[key];            }            // Modify it point to the new mapping with the proper context.            newMapping.keys = lhs;            if (ctx && !newMapping.context) {              newMapping.context = ctx;            }            // Add it to the keymap with a higher priority than the original.            this._mapCommand(newMapping);            // Record the mapped contexts as complete.            var mappedCtxs = toCtxArray(mapping.context);            ctxsToMap = ctxsToMap.filter(function(el) { return mappedCtxs.indexOf(el) === -1; });          }        }        // TODO: Create non-recursive keyToKey mappings for the unmapped contexts once those exist.      },      // Remove all user-defined mappings for the provided context.      mapclear: function(ctx) {        // Partition the existing keymap into user-defined and true defaults.        var actualLength = defaultKeymap.length,            origLength = defaultKeymapLength;        var userKeymap = defaultKeymap.slice(0, actualLength - origLength);        defaultKeymap = defaultKeymap.slice(actualLength - origLength);        if (ctx) {          // If a specific context is being cleared, we need to keep mappings          // from all other contexts.          for (var i = userKeymap.length - 1; i >= 0; i--) {            var mapping = userKeymap[i];            if (ctx !== mapping.context) {              if (mapping.context) {                this._mapCommand(mapping);              } else {                // `mapping` applies to all contexts so create keymap copies                // for each context except the one being cleared.                var contexts = ['normal', 'insert', 'visual'];                for (var j in contexts) {                  if (contexts[j] !== ctx) {                    var newMapping = {};                    for (var key in mapping) {                      newMapping[key] = mapping[key];                    }                    newMapping.context = contexts[j];                    this._mapCommand(newMapping);                  }                }              }            }          }        }      },      // TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace      // them, or somehow make them work with the existing CodeMirror setOption/getOption API.      setOption: setOption,      getOption: getOption,      defineOption: defineOption,      defineEx: function(name, prefix, func){        if (!prefix) {          prefix = name;        } else if (name.indexOf(prefix) !== 0) {          throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');        }        exCommands[name]=func;        exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};      },      handleKey: function (cm, key, origin) {        var command = this.findKey(cm, key, origin);        if (typeof command === 'function') {          return command();        }      },      /**       * This is the outermost function called by CodeMirror, after keys have       * been mapped to their Vim equivalents.       *       * Finds a command based on the key (and cached keys if there is a       * multi-key sequence). Returns `undefined` if no key is matched, a noop       * function if a partial match is found (multi-key), and a function to       * execute the bound command if a a key is matched. The function always       * returns true.       */      findKey: function(cm, key, origin) {        var vim = maybeInitVimState(cm);        function handleMacroRecording() {          var macroModeState = vimGlobalState.macroModeState;          if (macroModeState.isRecording) {            if (key == 'q') {              macroModeState.exitMacroRecordMode();              clearInputState(cm);              return true;            }            if (origin != 'mapping') {              logKey(macroModeState, key);            }          }        }        function handleEsc() {          if (key == '<Esc>') {            // Clear input state and get back to normal mode.            clearInputState(cm);            if (vim.visualMode) {              exitVisualMode(cm);            } else if (vim.insertMode) {              exitInsertMode(cm);            }            return true;          }        }        function doKeyToKey(keys) {          // TODO: prevent infinite recursion.          var match;          while (keys) {            // Pull off one command key, which is either a single character            // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.            match = (/<\w+-.+?>|<\w+>|./).exec(keys);            key = match[0];            keys = keys.substring(match.index + key.length);            vimApi.handleKey(cm, key, 'mapping');          }        }        function handleKeyInsertMode() {          if (handleEsc()) { return true; }          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;          var keysAreChars = key.length == 1;          var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');          // Need to check all key substrings in insert mode.          while (keys.length > 1 && match.type != 'full') {            var keys = vim.inputState.keyBuffer = keys.slice(1);            var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');            if (thisMatch.type != 'none') { match = thisMatch; }          }          if (match.type == 'none') { clearInputState(cm); return false; }          else if (match.type == 'partial') {            if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }            lastInsertModeKeyTimer = window.setTimeout(              function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },              getOption('insertModeEscKeysTimeout'));            return !keysAreChars;          }          if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }          if (keysAreChars) {            var selections = cm.listSelections();            for (var i = 0; i < selections.length; i++) {              var here = selections[i].head;              cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');            }            vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();          }          clearInputState(cm);          return match.command;        }        function handleKeyNonInsertMode() {          if (handleMacroRecording() || handleEsc()) { return true; }          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;          if (/^[1-9]\d*$/.test(keys)) { return true; }          var keysMatcher = /^(\d*)(.*)$/.exec(keys);          if (!keysMatcher) { clearInputState(cm); return false; }          var context = vim.visualMode ? 'visual' :                                         'normal';          var mainKey = keysMatcher[2] || keysMatcher[1];          if (vim.inputState.operatorShortcut && vim.inputState.operatorShortcut.slice(-1) == mainKey) {            // multikey operators act linewise by repeating only the last character            mainKey = vim.inputState.operatorShortcut;          }          var match = commandDispatcher.matchCommand(mainKey, defaultKeymap, vim.inputState, context);          if (match.type == 'none') { clearInputState(cm); return false; }          else if (match.type == 'partial') { return true; }          vim.inputState.keyBuffer = '';          var keysMatcher = /^(\d*)(.*)$/.exec(keys);          if (keysMatcher[1] && keysMatcher[1] != '0') {            vim.inputState.pushRepeatDigit(keysMatcher[1]);          }          return match.command;        }        var command;        if (vim.insertMode) { command = handleKeyInsertMode(); }        else { command = handleKeyNonInsertMode(); }        if (command === false) {          return !vim.insertMode && key.length === 1 ? function() { return true; } : undefined;        } else if (command === true) {          // TODO: Look into using CodeMirror's multi-key handling.          // Return no-op since we are caching the key. Counts as handled, but          // don't want act on it just yet.          return function() { return true; };        } else {          return function() {            return cm.operation(function() {              cm.curOp.isVimOp = true;              try {                if (command.type == 'keyToKey') {                  doKeyToKey(command.toKeys);                } else {                  commandDispatcher.processCommand(cm, vim, command);                }              } catch (e) {                // clear VIM state in case it's in a bad state.                cm.state.vim = undefined;                maybeInitVimState(cm);                if (!vimApi.suppressErrorLogging) {                  console['log'](e);                }                throw e;              }              return true;            });          };        }      },      handleEx: function(cm, input) {        exCommandDispatcher.processCommand(cm, input);      },      defineMotion: defineMotion,      defineAction: defineAction,      defineOperator: defineOperator,      mapCommand: mapCommand,      _mapCommand: _mapCommand,      defineRegister: defineRegister,      exitVisualMode: exitVisualMode,      exitInsertMode: exitInsertMode    };    // Represents the current input state.    function InputState() {      this.prefixRepeat = [];      this.motionRepeat = [];      this.operator = null;      this.operatorArgs = null;      this.motion = null;      this.motionArgs = null;      this.keyBuffer = []; // For matching multi-key commands.      this.registerName = null; // Defaults to the unnamed register.    }    InputState.prototype.pushRepeatDigit = function(n) {      if (!this.operator) {        this.prefixRepeat = this.prefixRepeat.concat(n);      } else {        this.motionRepeat = this.motionRepeat.concat(n);      }    };    InputState.prototype.getRepeat = function() {      var repeat = 0;      if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {        repeat = 1;        if (this.prefixRepeat.length > 0) {          repeat *= parseInt(this.prefixRepeat.join(''), 10);        }        if (this.motionRepeat.length > 0) {          repeat *= parseInt(this.motionRepeat.join(''), 10);        }      }      return repeat;    };    function clearInputState(cm, reason) {      cm.state.vim.inputState = new InputState();      CodeMirror.signal(cm, 'vim-command-done', reason);    }    /*     * Register stores information about copy and paste registers.  Besides     * text, a register must store whether it is linewise (i.e., when it is     * pasted, should it insert itself into a new line, or should the text be     * inserted at the cursor position.)     */    function Register(text, linewise, blockwise) {      this.clear();      this.keyBuffer = [text || ''];      this.insertModeChanges = [];      this.searchQueries = [];      this.linewise = !!linewise;      this.blockwise = !!blockwise;    }    Register.prototype = {      setText: function(text, linewise, blockwise) {        this.keyBuffer = [text || ''];        this.linewise = !!linewise;        this.blockwise = !!blockwise;      },      pushText: function(text, linewise) {        // if this register has ever been set to linewise, use linewise.        if (linewise) {          if (!this.linewise) {            this.keyBuffer.push('\n');          }          this.linewise = true;        }        this.keyBuffer.push(text);      },      pushInsertModeChanges: function(changes) {        this.insertModeChanges.push(createInsertModeChanges(changes));      },      pushSearchQuery: function(query) {        this.searchQueries.push(query);      },      clear: function() {        this.keyBuffer = [];        this.insertModeChanges = [];        this.searchQueries = [];        this.linewise = false;      },      toString: function() {        return this.keyBuffer.join('');      }    };    /**     * Defines an external register.     *     * The name should be a single character that will be used to reference the register.     * The register should support setText, pushText, clear, and toString(). See Register     * for a reference implementation.     */    function defineRegister(name, register) {      var registers = vimGlobalState.registerController.registers;      if (!name || name.length != 1) {        throw Error('Register name must be 1 character');      }      if (registers[name]) {        throw Error('Register already defined ' + name);      }      registers[name] = register;      validRegisters.push(name);    }    /*     * vim registers allow you to keep many independent copy and paste buffers.     * See http://usevim.com/2012/04/13/registers/ for an introduction.     *     * RegisterController keeps the state of all the registers.  An initial     * state may be passed in.  The unnamed register '"' will always be     * overridden.     */    function RegisterController(registers) {      this.registers = registers;      this.unnamedRegister = registers['"'] = new Register();      registers['.'] = new Register();      registers[':'] = new Register();      registers['/'] = new Register();    }    RegisterController.prototype = {      pushText: function(registerName, operator, text, linewise, blockwise) {        // The black hole register, "_, means delete/yank to nowhere.        if (registerName === '_') return;        if (linewise && text.charAt(text.length - 1) !== '\n'){          text += '\n';        }        // Lowercase and uppercase registers refer to the same register.        // Uppercase just means append.        var register = this.isValidRegister(registerName) ?            this.getRegister(registerName) : null;        // if no register/an invalid register was specified, things go to the        // default registers        if (!register) {          switch (operator) {            case 'yank':              // The 0 register contains the text from the most recent yank.              this.registers['0'] = new Register(text, linewise, blockwise);              break;            case 'delete':            case 'change':              if (text.indexOf('\n') == -1) {                // Delete less than 1 line. Update the small delete register.                this.registers['-'] = new Register(text, linewise);              } else {                // Shift down the contents of the numbered registers and put the                // deleted text into register 1.                this.shiftNumericRegisters_();                this.registers['1'] = new Register(text, linewise);              }              break;          }          // Make sure the unnamed register is set to what just happened          this.unnamedRegister.setText(text, linewise, blockwise);          return;        }        // If we've gotten to this point, we've actually specified a register        var append = isUpperCase(registerName);        if (append) {          register.pushText(text, linewise);        } else {          register.setText(text, linewise, blockwise);        }        // The unnamed register always has the same value as the last used        // register.        this.unnamedRegister.setText(register.toString(), linewise);      },      // Gets the register named @name.  If one of @name doesn't already exist,      // create it.  If @name is invalid, return the unnamedRegister.      getRegister: function(name) {        if (!this.isValidRegister(name)) {          return this.unnamedRegister;        }        name = name.toLowerCase();        if (!this.registers[name]) {          this.registers[name] = new Register();        }        return this.registers[name];      },      isValidRegister: function(name) {        return name && inArray(name, validRegisters);      },      shiftNumericRegisters_: function() {        for (var i = 9; i >= 2; i--) {          this.registers[i] = this.getRegister('' + (i - 1));        }      }    };    function HistoryController() {        this.historyBuffer = [];        this.iterator = 0;        this.initialPrefix = null;    }    HistoryController.prototype = {      // the input argument here acts a user entered prefix for a small time      // until we start autocompletion in which case it is the autocompleted.      nextMatch: function (input, up) {        var historyBuffer = this.historyBuffer;        var dir = up ? -1 : 1;        if (this.initialPrefix === null) this.initialPrefix = input;        for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {          var element = historyBuffer[i];          for (var j = 0; j <= element.length; j++) {            if (this.initialPrefix == element.substring(0, j)) {              this.iterator = i;              return element;            }          }        }        // should return the user input in case we reach the end of buffer.        if (i >= historyBuffer.length) {          this.iterator = historyBuffer.length;          return this.initialPrefix;        }        // return the last autocompleted query or exCommand as it is.        if (i < 0 ) return input;      },      pushInput: function(input) {        var index = this.historyBuffer.indexOf(input);        if (index > -1) this.historyBuffer.splice(index, 1);        if (input.length) this.historyBuffer.push(input);      },      reset: function() {        this.initialPrefix = null;        this.iterator = this.historyBuffer.length;      }    };    var commandDispatcher = {      matchCommand: function(keys, keyMap, inputState, context) {        var matches = commandMatches(keys, keyMap, context, inputState);        if (!matches.full && !matches.partial) {          return {type: 'none'};        } else if (!matches.full && matches.partial) {          return {type: 'partial'};        }        var bestMatch;        for (var i = 0; i < matches.full.length; i++) {          var match = matches.full[i];          if (!bestMatch) {            bestMatch = match;          }        }        if (bestMatch.keys.slice(-11) == '<character>') {          var character = lastChar(keys);          if (!character) return {type: 'none'};          inputState.selectedCharacter = character;        }        return {type: 'full', command: bestMatch};      },      processCommand: function(cm, vim, command) {        vim.inputState.repeatOverride = command.repeatOverride;        switch (command.type) {          case 'motion':            this.processMotion(cm, vim, command);            break;          case 'operator':            this.processOperator(cm, vim, command);            break;          case 'operatorMotion':            this.processOperatorMotion(cm, vim, command);            break;          case 'action':            this.processAction(cm, vim, command);            break;          case 'search':            this.processSearch(cm, vim, command);            break;          case 'ex':          case 'keyToEx':            this.processEx(cm, vim, command);            break;          default:            break;        }      },      processMotion: function(cm, vim, command) {        vim.inputState.motion = command.motion;        vim.inputState.motionArgs = copyArgs(command.motionArgs);        this.evalInput(cm, vim);      },      processOperator: function(cm, vim, command) {        var inputState = vim.inputState;        if (inputState.operator) {          if (inputState.operator == command.operator) {            // Typing an operator twice like 'dd' makes the operator operate            // linewise            inputState.motion = 'expandToLine';            inputState.motionArgs = { linewise: true };            this.evalInput(cm, vim);            return;          } else {            // 2 different operators in a row doesn't make sense.            clearInputState(cm);          }        }        inputState.operator = command.operator;        inputState.operatorArgs = copyArgs(command.operatorArgs);        if (command.keys.length > 1) {          inputState.operatorShortcut = command.keys;        }        if (command.exitVisualBlock) {            vim.visualBlock = false;            updateCmSelection(cm);        }        if (vim.visualMode) {          // Operating on a selection in visual mode. We don't need a motion.          this.evalInput(cm, vim);        }      },      processOperatorMotion: function(cm, vim, command) {        var visualMode = vim.visualMode;        var operatorMotionArgs = copyArgs(command.operatorMotionArgs);        if (operatorMotionArgs) {          // Operator motions may have special behavior in visual mode.          if (visualMode && operatorMotionArgs.visualLine) {            vim.visualLine = true;          }        }        this.processOperator(cm, vim, command);        if (!visualMode) {          this.processMotion(cm, vim, command);        }      },      processAction: function(cm, vim, command) {        var inputState = vim.inputState;        var repeat = inputState.getRepeat();        var repeatIsExplicit = !!repeat;        var actionArgs = copyArgs(command.actionArgs) || {};        if (inputState.selectedCharacter) {          actionArgs.selectedCharacter = inputState.selectedCharacter;        }        // Actions may or may not have motions and operators. Do these first.        if (command.operator) {          this.processOperator(cm, vim, command);        }        if (command.motion) {          this.processMotion(cm, vim, command);        }        if (command.motion || command.operator) {          this.evalInput(cm, vim);        }        actionArgs.repeat = repeat || 1;        actionArgs.repeatIsExplicit = repeatIsExplicit;        actionArgs.registerName = inputState.registerName;        clearInputState(cm);        vim.lastMotion = null;        if (command.isEdit) {          this.recordLastEdit(vim, inputState, command);        }        actions[command.action](cm, actionArgs, vim);      },      processSearch: function(cm, vim, command) {        if (!cm.getSearchCursor) {          // Search depends on SearchCursor.          return;        }        var forward = command.searchArgs.forward;        var wholeWordOnly = command.searchArgs.wholeWordOnly;        getSearchState(cm).setReversed(!forward);        var promptPrefix = (forward) ? '/' : '?';        var originalQuery = getSearchState(cm).getQuery();        var originalScrollPos = cm.getScrollInfo();        function handleQuery(query, ignoreCase, smartCase) {          vimGlobalState.searchHistoryController.pushInput(query);          vimGlobalState.searchHistoryController.reset();          try {            updateSearchQuery(cm, query, ignoreCase, smartCase);          } catch (e) {            showConfirm(cm, 'Invalid regex: ' + query);            clearInputState(cm);            return;          }          commandDispatcher.processMotion(cm, vim, {            type: 'motion',            motion: 'findNext',            motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }          });        }        function onPromptClose(query) {          cm.scrollTo(originalScrollPos.left, originalScrollPos.top);          handleQuery(query, true /** ignoreCase */, true /** smartCase */);          var macroModeState = vimGlobalState.macroModeState;          if (macroModeState.isRecording) {            logSearchQuery(macroModeState, query);          }        }        function onPromptKeyUp(e, query, close) {          var keyName = CodeMirror.keyName(e), up, offset;          if (keyName == 'Up' || keyName == 'Down') {            up = keyName == 'Up' ? true : false;            offset = e.target ? e.target.selectionEnd : 0;            query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';            close(query);            if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);          } else {            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')              vimGlobalState.searchHistoryController.reset();          }          var parsedQuery;          try {            parsedQuery = updateSearchQuery(cm, query,                true /** ignoreCase */, true /** smartCase */);          } catch (e) {            // Swallow bad regexes for incremental search.          }          if (parsedQuery) {            cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);          } else {            clearSearchHighlight(cm);            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);          }        }        function onPromptKeyDown(e, query, close) {          var keyName = CodeMirror.keyName(e);          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||              (keyName == 'Backspace' && query == '')) {            vimGlobalState.searchHistoryController.pushInput(query);            vimGlobalState.searchHistoryController.reset();            updateSearchQuery(cm, originalQuery);            clearSearchHighlight(cm);            cm.scrollTo(originalScrollPos.left, originalScrollPos.top);            CodeMirror.e_stop(e);            clearInputState(cm);            close();            cm.focus();          } else if (keyName == 'Up' || keyName == 'Down') {            CodeMirror.e_stop(e);          } else if (keyName == 'Ctrl-U') {            // Ctrl-U clears input.            CodeMirror.e_stop(e);            close('');          }        }        switch (command.searchArgs.querySrc) {          case 'prompt':            var macroModeState = vimGlobalState.macroModeState;            if (macroModeState.isPlaying) {              var query = macroModeState.replaySearchQueries.shift();              handleQuery(query, true /** ignoreCase */, false /** smartCase */);            } else {              showPrompt(cm, {                  onClose: onPromptClose,                  prefix: promptPrefix,                  desc: '(JavaScript regexp)',                  onKeyUp: onPromptKeyUp,                  onKeyDown: onPromptKeyDown              });            }            break;          case 'wordUnderCursor':            var word = expandWordUnderCursor(cm, false /** inclusive */,                true /** forward */, false /** bigWord */,                true /** noSymbol */);            var isKeyword = true;            if (!word) {              word = expandWordUnderCursor(cm, false /** inclusive */,                  true /** forward */, false /** bigWord */,                  false /** noSymbol */);              isKeyword = false;            }            if (!word) {              return;            }            var query = cm.getLine(word.start.line).substring(word.start.ch,                word.end.ch);            if (isKeyword && wholeWordOnly) {                query = '\\b' + query + '\\b';            } else {              query = escapeRegex(query);            }            // cachedCursor is used to save the old position of the cursor            // when * or # causes vim to seek for the nearest word and shift            // the cursor before entering the motion.            vimGlobalState.jumpList.cachedCursor = cm.getCursor();            cm.setCursor(word.start);            handleQuery(query, true /** ignoreCase */, false /** smartCase */);            break;        }      },      processEx: function(cm, vim, command) {        function onPromptClose(input) {          // Give the prompt some time to close so that if processCommand shows          // an error, the elements don't overlap.          vimGlobalState.exCommandHistoryController.pushInput(input);          vimGlobalState.exCommandHistoryController.reset();          exCommandDispatcher.processCommand(cm, input);        }        function onPromptKeyDown(e, input, close) {          var keyName = CodeMirror.keyName(e), up, offset;          if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||              (keyName == 'Backspace' && input == '')) {            vimGlobalState.exCommandHistoryController.pushInput(input);            vimGlobalState.exCommandHistoryController.reset();            CodeMirror.e_stop(e);            clearInputState(cm);            close();            cm.focus();          }          if (keyName == 'Up' || keyName == 'Down') {            CodeMirror.e_stop(e);            up = keyName == 'Up' ? true : false;            offset = e.target ? e.target.selectionEnd : 0;            input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';            close(input);            if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);          } else if (keyName == 'Ctrl-U') {            // Ctrl-U clears input.            CodeMirror.e_stop(e);            close('');          } else {            if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')              vimGlobalState.exCommandHistoryController.reset();          }        }        if (command.type == 'keyToEx') {          // Handle user defined Ex to Ex mappings          exCommandDispatcher.processCommand(cm, command.exArgs.input);        } else {          if (vim.visualMode) {            showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',                onKeyDown: onPromptKeyDown, selectValueOnOpen: false});          } else {            showPrompt(cm, { onClose: onPromptClose, prefix: ':',                onKeyDown: onPromptKeyDown});          }        }      },      evalInput: function(cm, vim) {        // If the motion command is set, execute both the operator and motion.        // Otherwise return.        var inputState = vim.inputState;        var motion = inputState.motion;        var motionArgs = inputState.motionArgs || {};        var operator = inputState.operator;        var operatorArgs = inputState.operatorArgs || {};        var registerName = inputState.registerName;        var sel = vim.sel;        // TODO: Make sure cm and vim selections are identical outside visual mode.        var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head): cm.getCursor('head'));        var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));        var oldHead = copyCursor(origHead);        var oldAnchor = copyCursor(origAnchor);        var newHead, newAnchor;        var repeat;        if (operator) {          this.recordLastEdit(vim, inputState);        }        if (inputState.repeatOverride !== undefined) {          // If repeatOverride is specified, that takes precedence over the          // input state's repeat. Used by Ex mode and can be user defined.          repeat = inputState.repeatOverride;        } else {          repeat = inputState.getRepeat();        }        if (repeat > 0 && motionArgs.explicitRepeat) {          motionArgs.repeatIsExplicit = true;        } else if (motionArgs.noRepeat ||            (!motionArgs.explicitRepeat && repeat === 0)) {          repeat = 1;          motionArgs.repeatIsExplicit = false;        }        if (inputState.selectedCharacter) {          // If there is a character input, stick it in all of the arg arrays.          motionArgs.selectedCharacter = operatorArgs.selectedCharacter =              inputState.selectedCharacter;        }        motionArgs.repeat = repeat;        clearInputState(cm);        if (motion) {          var motionResult = motions[motion](cm, origHead, motionArgs, vim, inputState);          vim.lastMotion = motions[motion];          if (!motionResult) {            return;          }          if (motionArgs.toJumplist) {            var jumpList = vimGlobalState.jumpList;            // if the current motion is # or *, use cachedCursor            var cachedCursor = jumpList.cachedCursor;            if (cachedCursor) {              recordJumpPosition(cm, cachedCursor, motionResult);              delete jumpList.cachedCursor;            } else {              recordJumpPosition(cm, origHead, motionResult);            }          }          if (motionResult instanceof Array) {            newAnchor = motionResult[0];            newHead = motionResult[1];          } else {            newHead = motionResult;          }          // TODO: Handle null returns from motion commands better.          if (!newHead) {            newHead = copyCursor(origHead);          }          if (vim.visualMode) {            if (!(vim.visualBlock && newHead.ch === Infinity)) {              newHead = clipCursorToContent(cm, newHead);            }            if (newAnchor) {              newAnchor = clipCursorToContent(cm, newAnchor);            }            newAnchor = newAnchor || oldAnchor;            sel.anchor = newAnchor;            sel.head = newHead;            updateCmSelection(cm);            updateMark(cm, vim, '<',                cursorIsBefore(newAnchor, newHead) ? newAnchor                    : newHead);            updateMark(cm, vim, '>',                cursorIsBefore(newAnchor, newHead) ? newHead                    : newAnchor);          } else if (!operator) {            newHead = clipCursorToContent(cm, newHead);            cm.setCursor(newHead.line, newHead.ch);          }        }        if (operator) {          if (operatorArgs.lastSel) {            // Replaying a visual mode operation            newAnchor = oldAnchor;            var lastSel = operatorArgs.lastSel;            var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);            var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);            if (lastSel.visualLine) {              // Linewise Visual mode: The same number of lines.              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);            } else if (lastSel.visualBlock) {              // Blockwise Visual mode: The same number of lines and columns.              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);            } else if (lastSel.head.line == lastSel.anchor.line) {              // Normal Visual mode within one line: The same number of characters.              newHead = new Pos(oldAnchor.line, oldAnchor.ch + chOffset);            } else {              // Normal Visual mode with several lines: The same number of lines, in the              // last line the same number of characters as in the last line the last time.              newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);            }            vim.visualMode = true;            vim.visualLine = lastSel.visualLine;            vim.visualBlock = lastSel.visualBlock;            sel = vim.sel = {              anchor: newAnchor,              head: newHead            };            updateCmSelection(cm);          } else if (vim.visualMode) {            operatorArgs.lastSel = {              anchor: copyCursor(sel.anchor),              head: copyCursor(sel.head),              visualBlock: vim.visualBlock,              visualLine: vim.visualLine            };          }          var curStart, curEnd, linewise, mode;          var cmSel;          if (vim.visualMode) {            // Init visual op            curStart = cursorMin(sel.head, sel.anchor);            curEnd = cursorMax(sel.head, sel.anchor);            linewise = vim.visualLine || operatorArgs.linewise;            mode = vim.visualBlock ? 'block' :                   linewise ? 'line' :                   'char';            cmSel = makeCmSelection(cm, {              anchor: curStart,              head: curEnd            }, mode);            if (linewise) {              var ranges = cmSel.ranges;              if (mode == 'block') {                // Linewise operators in visual block mode extend to end of line                for (var i = 0; i < ranges.length; i++) {                  ranges[i].head.ch = lineLength(cm, ranges[i].head.line);                }              } else if (mode == 'line') {                ranges[0].head = new Pos(ranges[0].head.line + 1, 0);              }            }          } else {            // Init motion op            curStart = copyCursor(newAnchor || oldAnchor);            curEnd = copyCursor(newHead || oldHead);            if (cursorIsBefore(curEnd, curStart)) {              var tmp = curStart;              curStart = curEnd;              curEnd = tmp;            }            linewise = motionArgs.linewise || operatorArgs.linewise;            if (linewise) {              // Expand selection to entire line.              expandSelectionToLine(cm, curStart, curEnd);            } else if (motionArgs.forward) {              // Clip to trailing newlines only if the motion goes forward.              clipToLine(cm, curStart, curEnd);            }            mode = 'char';            var exclusive = !motionArgs.inclusive || linewise;            cmSel = makeCmSelection(cm, {              anchor: curStart,              head: curEnd            }, mode, exclusive);          }          cm.setSelections(cmSel.ranges, cmSel.primary);          vim.lastMotion = null;          operatorArgs.repeat = repeat; // For indent in visual mode.          operatorArgs.registerName = registerName;          // Keep track of linewise as it affects how paste and change behave.          operatorArgs.linewise = linewise;          var operatorMoveTo = operators[operator](            cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);          if (vim.visualMode) {            exitVisualMode(cm, operatorMoveTo != null);          }          if (operatorMoveTo) {            cm.setCursor(operatorMoveTo);          }        }      },      recordLastEdit: function(vim, inputState, actionCommand) {        var macroModeState = vimGlobalState.macroModeState;        if (macroModeState.isPlaying) { return; }        vim.lastEditInputState = inputState;        vim.lastEditActionCommand = actionCommand;        macroModeState.lastInsertModeChanges.changes = [];        macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;        macroModeState.lastInsertModeChanges.visualBlock = vim.visualBlock ? vim.sel.head.line - vim.sel.anchor.line : 0;      }    };    /**     * typedef {Object{line:number,ch:number}} Cursor An object containing the     *     position of the cursor.     */    // All of the functions below return Cursor objects.    var motions = {      moveToTopLine: function(cm, _head, motionArgs) {        var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));      },      moveToMiddleLine: function(cm) {        var range = getUserVisibleLines(cm);        var line = Math.floor((range.top + range.bottom) * 0.5);        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));      },      moveToBottomLine: function(cm, _head, motionArgs) {        var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;        return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));      },      expandToLine: function(_cm, head, motionArgs) {        // Expands forward to end of line, and then to next line if repeat is        // >1. Does not handle backward motion!        var cur = head;        return new Pos(cur.line + motionArgs.repeat - 1, Infinity);      },      findNext: function(cm, _head, motionArgs) {        var state = getSearchState(cm);        var query = state.getQuery();        if (!query) {          return;        }        var prev = !motionArgs.forward;        // If search is initiated with ? instead of /, negate direction.        prev = (state.isReversed()) ? !prev : prev;        highlightSearchMatches(cm, query);        return findNext(cm, prev/** prev */, query, motionArgs.repeat);      },      /**       * Find and select the next occurrence of the search query. If the cursor is currently       * within a match, then find and select the current match. Otherwise, find the next occurrence in the       * appropriate direction.       *       * This differs from `findNext` in the following ways:       *       * 1. Instead of only returning the "from", this returns a "from", "to" range.       * 2. If the cursor is currently inside a search match, this selects the current match       *    instead of the next match.       * 3. If there is no associated operator, this will turn on visual mode.       */      findAndSelectNextInclusive: function(cm, _head, motionArgs, vim, prevInputState) {        var state = getSearchState(cm);        var query = state.getQuery();        if (!query) {          return;        }        var prev = !motionArgs.forward;        prev = (state.isReversed()) ? !prev : prev;        // next: [from, to] | null        var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim);        // No matches.        if (!next) {          return;        }        // If there's an operator that will be executed, return the selection.        if (prevInputState.operator) {          return next;        }        // At this point, we know that there is no accompanying operator -- let's        // deal with visual mode in order to select an appropriate match.        var from = next[0];        // For whatever reason, when we use the "to" as returned by searchcursor.js directly,        // the resulting selection is extended by 1 char. Let's shrink it so that only the        // match is selected.        var to = new Pos(next[1].line, next[1].ch - 1);        if (vim.visualMode) {          // If we were in visualLine or visualBlock mode, get out of it.          if (vim.visualLine || vim.visualBlock) {            vim.visualLine = false;            vim.visualBlock = false;            CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: ""});          }          // If we're currently in visual mode, we should extend the selection to include          // the search result.          var anchor = vim.sel.anchor;          if (anchor) {            if (state.isReversed()) {              if (motionArgs.forward) {                return [anchor, from];              }              return [anchor, to];            } else {              if (motionArgs.forward) {                return [anchor, to];              }              return [anchor, from];            }          }        } else {          // Let's turn visual mode on.          vim.visualMode = true;          vim.visualLine = false;          vim.visualBlock = false;          CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: ""});        }        return prev ? [to, from] : [from, to];      },      goToMark: function(cm, _head, motionArgs, vim) {        var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);        if (pos) {          return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;        }        return null;      },      moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {        if (vim.visualBlock && motionArgs.sameLine) {          var sel = vim.sel;          return [            clipCursorToContent(cm, new Pos(sel.anchor.line, sel.head.ch)),            clipCursorToContent(cm, new Pos(sel.head.line, sel.anchor.ch))          ];        } else {          return ([vim.sel.head, vim.sel.anchor]);        }      },      jumpToMark: function(cm, head, motionArgs, vim) {        var best = head;        for (var i = 0; i < motionArgs.repeat; i++) {          var cursor = best;          for (var key in vim.marks) {            if (!isLowerCase(key)) {              continue;            }            var mark = vim.marks[key].find();            var isWrongDirection = (motionArgs.forward) ?              cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);            if (isWrongDirection) {              continue;            }            if (motionArgs.linewise && (mark.line == cursor.line)) {              continue;            }            var equal = cursorEqual(cursor, best);            var between = (motionArgs.forward) ?              cursorIsBetween(cursor, mark, best) :              cursorIsBetween(best, mark, cursor);            if (equal || between) {              best = mark;            }          }        }        if (motionArgs.linewise) {          // Vim places the cursor on the first non-whitespace character of          // the line if there is one, else it places the cursor at the end          // of the line, regardless of whether a mark was found.          best = new Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));        }        return best;      },      moveByCharacters: function(_cm, head, motionArgs) {        var cur = head;        var repeat = motionArgs.repeat;        var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;        return new Pos(cur.line, ch);      },      moveByLines: function(cm, head, motionArgs, vim) {        var cur = head;        var endCh = cur.ch;        // Depending what our last motion was, we may want to do different        // things. If our last motion was moving vertically, we want to        // preserve the HPos from our last horizontal move.  If our last motion        // was going to the end of a line, moving vertically we should go to        // the end of the line, etc.        switch (vim.lastMotion) {          case this.moveByLines:          case this.moveByDisplayLines:          case this.moveByScroll:          case this.moveToColumn:          case this.moveToEol:            endCh = vim.lastHPos;            break;          default:            vim.lastHPos = endCh;        }        var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);        var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;        var first = cm.firstLine();        var last = cm.lastLine();        var posV = cm.findPosV(cur, (motionArgs.forward ? repeat : -repeat), 'line', vim.lastHSPos);        var hasMarkedText = motionArgs.forward ? posV.line > line : posV.line < line;        if (hasMarkedText) {          line = posV.line;          endCh = posV.ch;        }        // Vim go to line begin or line end when cursor at first/last line and        // move to previous/next line is triggered.        if (line < first && cur.line == first){          return this.moveToStartOfLine(cm, head, motionArgs, vim);        } else if (line > last && cur.line == last){            return moveToEol(cm, head, motionArgs, vim, true);        }        if (motionArgs.toFirstChar){          endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));          vim.lastHPos = endCh;        }        vim.lastHSPos = cm.charCoords(new Pos(line, endCh),'div').left;        return new Pos(line, endCh);      },      moveByDisplayLines: function(cm, head, motionArgs, vim) {        var cur = head;        switch (vim.lastMotion) {          case this.moveByDisplayLines:          case this.moveByScroll:          case this.moveByLines:          case this.moveToColumn:          case this.moveToEol:            break;          default:            vim.lastHSPos = cm.charCoords(cur,'div').left;        }        var repeat = motionArgs.repeat;        var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);        if (res.hitSide) {          if (motionArgs.forward) {            var lastCharCoords = cm.charCoords(res, 'div');            var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };            var res = cm.coordsChar(goalCoords, 'div');          } else {            var resCoords = cm.charCoords(new Pos(cm.firstLine(), 0), 'div');            resCoords.left = vim.lastHSPos;            res = cm.coordsChar(resCoords, 'div');          }        }        vim.lastHPos = res.ch;        return res;      },      moveByPage: function(cm, head, motionArgs) {        // CodeMirror only exposes functions that move the cursor page down, so        // doing this bad hack to move the cursor and move it back. evalInput        // will move the cursor to where it should be in the end.        var curStart = head;        var repeat = motionArgs.repeat;        return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');      },      moveByParagraph: function(cm, head, motionArgs) {        var dir = motionArgs.forward ? 1 : -1;        return findParagraph(cm, head, motionArgs.repeat, dir);      },      moveBySentence: function(cm, head, motionArgs) {        var dir = motionArgs.forward ? 1 : -1;        return findSentence(cm, head, motionArgs.repeat, dir);      },      moveByScroll: function(cm, head, motionArgs, vim) {        var scrollbox = cm.getScrollInfo();        var curEnd = null;        var repeat = motionArgs.repeat;        if (!repeat) {          repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());        }        var orig = cm.charCoords(head, 'local');        motionArgs.repeat = repeat;        var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);        if (!curEnd) {          return null;        }        var dest = cm.charCoords(curEnd, 'local');        cm.scrollTo(null, scrollbox.top + dest.top - orig.top);        return curEnd;      },      moveByWords: function(cm, head, motionArgs) {        return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,            !!motionArgs.wordEnd, !!motionArgs.bigWord);      },      moveTillCharacter: function(cm, _head, motionArgs) {        var repeat = motionArgs.repeat;        var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,            motionArgs.selectedCharacter);        var increment = motionArgs.forward ? -1 : 1;        recordLastCharacterSearch(increment, motionArgs);        if (!curEnd) return null;        curEnd.ch += increment;        return curEnd;      },      moveToCharacter: function(cm, head, motionArgs) {        var repeat = motionArgs.repeat;        recordLastCharacterSearch(0, motionArgs);        return moveToCharacter(cm, repeat, motionArgs.forward,            motionArgs.selectedCharacter) || head;      },      moveToSymbol: function(cm, head, motionArgs) {        var repeat = motionArgs.repeat;        return findSymbol(cm, repeat, motionArgs.forward,            motionArgs.selectedCharacter) || head;      },      moveToColumn: function(cm, head, motionArgs, vim) {        var repeat = motionArgs.repeat;        // repeat is equivalent to which column we want to move to!        vim.lastHPos = repeat - 1;        vim.lastHSPos = cm.charCoords(head,'div').left;        return moveToColumn(cm, repeat);      },      moveToEol: function(cm, head, motionArgs, vim) {        return moveToEol(cm, head, motionArgs, vim, false);      },      moveToFirstNonWhiteSpaceCharacter: function(cm, head) {        // Go to the start of the line where the text begins, or the end for        // whitespace-only lines        var cursor = head;        return new Pos(cursor.line,                   findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));      },      moveToMatchedSymbol: function(cm, head) {        var cursor = head;        var line = cursor.line;        var ch = cursor.ch;        var lineText = cm.getLine(line);        var symbol;        for (; ch < lineText.length; ch++) {          symbol = lineText.charAt(ch);          if (symbol && isMatchableSymbol(symbol)) {            var style = cm.getTokenTypeAt(new Pos(line, ch + 1));            if (style !== "string" && style !== "comment") {              break;            }          }        }        if (ch < lineText.length) {          // Only include angle brackets in analysis if they are being matched.          var re = (ch === '<' || ch === '>') ? /[(){}[\]<>]/ : /[(){}[\]]/;          var matched = cm.findMatchingBracket(new Pos(line, ch), {bracketRegex: re});          return matched.to;        } else {          return cursor;        }      },      moveToStartOfLine: function(_cm, head) {        return new Pos(head.line, 0);      },      moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {        var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();        if (motionArgs.repeatIsExplicit) {          lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');        }        return new Pos(lineNum,                   findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));      },      textObjectManipulation: function(cm, head, motionArgs, vim) {        // TODO: lots of possible exceptions that can be thrown here. Try da(        //     outside of a () block.        var mirroredPairs = {'(': ')', ')': '(',                             '{': '}', '}': '{',                             '[': ']', ']': '[',                             '<': '>', '>': '<'};        var selfPaired = {'\'': true, '"': true, '`': true};        var character = motionArgs.selectedCharacter;        // 'b' refers to  '()' block.        // 'B' refers to  '{}' block.        if (character == 'b') {          character = '(';        } else if (character == 'B') {          character = '{';        }        // Inclusive is the difference between a and i        // TODO: Instead of using the additional text object map to perform text        //     object operations, merge the map into the defaultKeyMap and use        //     motionArgs to define behavior. Define separate entries for 'aw',        //     'iw', 'a[', 'i[', etc.        var inclusive = !motionArgs.textObjectInner;        var tmp;        if (mirroredPairs[character]) {          tmp = selectCompanionObject(cm, head, character, inclusive);        } else if (selfPaired[character]) {          tmp = findBeginningAndEnd(cm, head, character, inclusive);        } else if (character === 'W') {          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,                                                     true /** bigWord */);        } else if (character === 'w') {          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,                                                     false /** bigWord */);        } else if (character === 'p') {          tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);          motionArgs.linewise = true;          if (vim.visualMode) {            if (!vim.visualLine) { vim.visualLine = true; }          } else {            var operatorArgs = vim.inputState.operatorArgs;            if (operatorArgs) { operatorArgs.linewise = true; }            tmp.end.line--;          }        } else if (character === 't') {          tmp = expandTagUnderCursor(cm, head, inclusive);        } else {          // No text object defined for this, don't move.          return null;        }        if (!cm.state.vim.visualMode) {          return [tmp.start, tmp.end];        } else {          return expandSelection(cm, tmp.start, tmp.end);        }      },      repeatLastCharacterSearch: function(cm, head, motionArgs) {        var lastSearch = vimGlobalState.lastCharacterSearch;        var repeat = motionArgs.repeat;        var forward = motionArgs.forward === lastSearch.forward;        var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);        cm.moveH(-increment, 'char');        motionArgs.inclusive = forward ? true : false;        var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);        if (!curEnd) {          cm.moveH(increment, 'char');          return head;        }        curEnd.ch += increment;        return curEnd;      }    };    function defineMotion(name, fn) {      motions[name] = fn;    }    function fillArray(val, times) {      var arr = [];      for (var i = 0; i < times; i++) {        arr.push(val);      }      return arr;    }    /**     * An operator acts on a text selection. It receives the list of selections     * as input. The corresponding CodeMirror selection is guaranteed to    * match the input selection.     */    var operators = {      change: function(cm, args, ranges) {        var finalHead, text;        var vim = cm.state.vim;        var anchor = ranges[0].anchor,            head = ranges[0].head;        if (!vim.visualMode) {          text = cm.getRange(anchor, head);          var lastState = vim.lastEditInputState || {};          if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) {            // Exclude trailing whitespace if the range is not all whitespace.            var match = (/\s+$/).exec(text);            if (match && lastState.motionArgs && lastState.motionArgs.forward) {              head = offsetCursor(head, 0, - match[0].length);              text = text.slice(0, - match[0].length);            }          }          var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE);          var wasLastLine = cm.firstLine() == cm.lastLine();          if (head.line > cm.lastLine() && args.linewise && !wasLastLine) {            cm.replaceRange('', prevLineEnd, head);          } else {            cm.replaceRange('', anchor, head);          }          if (args.linewise) {            // Push the next line back down, if there is a next line.            if (!wasLastLine) {              cm.setCursor(prevLineEnd);              CodeMirror.commands.newlineAndIndent(cm);            }            // make sure cursor ends up at the end of the line.            anchor.ch = Number.MAX_VALUE;          }          finalHead = anchor;        } else if (args.fullLine) {            head.ch = Number.MAX_VALUE;            head.line--;            cm.setSelection(anchor, head)            text = cm.getSelection();            cm.replaceSelection("");            finalHead = anchor;        } else {          text = cm.getSelection();          var replacement = fillArray('', ranges.length);          cm.replaceSelections(replacement);          finalHead = cursorMin(ranges[0].head, ranges[0].anchor);        }        vimGlobalState.registerController.pushText(            args.registerName, 'change', text,            args.linewise, ranges.length > 1);        actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);      },      // delete is a javascript keyword.      'delete': function(cm, args, ranges) {        var finalHead, text;        var vim = cm.state.vim;        if (!vim.visualBlock) {          var anchor = ranges[0].anchor,              head = ranges[0].head;          if (args.linewise &&              head.line != cm.firstLine() &&              anchor.line == cm.lastLine() &&              anchor.line == head.line - 1) {            // Special case for dd on last line (and first line).            if (anchor.line == cm.firstLine()) {              anchor.ch = 0;            } else {              anchor = new Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));            }          }          text = cm.getRange(anchor, head);          cm.replaceRange('', anchor, head);          finalHead = anchor;          if (args.linewise) {            finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);          }        } else {          text = cm.getSelection();          var replacement = fillArray('', ranges.length);          cm.replaceSelections(replacement);          finalHead = cursorMin(ranges[0].head, ranges[0].anchor);        }        vimGlobalState.registerController.pushText(            args.registerName, 'delete', text,            args.linewise, vim.visualBlock);        return clipCursorToContent(cm, finalHead);      },      indent: function(cm, args, ranges) {        var vim = cm.state.vim;        var startLine = ranges[0].anchor.line;        var endLine = vim.visualBlock ?          ranges[ranges.length - 1].anchor.line :          ranges[0].head.line;        // In visual mode, n> shifts the selection right n times, instead of        // shifting n lines right once.        var repeat = (vim.visualMode) ? args.repeat : 1;        if (args.linewise) {          // The only way to delete a newline is to delete until the start of          // the next line, so in linewise mode evalInput will include the next          // line. We don't want this in indent, so we go back a line.          endLine--;        }        for (var i = startLine; i <= endLine; i++) {          for (var j = 0; j < repeat; j++) {            cm.indentLine(i, args.indentRight);          }        }        return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);      },      indentAuto: function(cm, _args, ranges) {        cm.execCommand("indentAuto");        return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);      },      changeCase: function(cm, args, ranges, oldAnchor, newHead) {        var selections = cm.getSelections();        var swapped = [];        var toLower = args.toLower;        for (var j = 0; j < selections.length; j++) {          var toSwap = selections[j];          var text = '';          if (toLower === true) {            text = toSwap.toLowerCase();          } else if (toLower === false) {            text = toSwap.toUpperCase();          } else {            for (var i = 0; i < toSwap.length; i++) {              var character = toSwap.charAt(i);              text += isUpperCase(character) ? character.toLowerCase() :                  character.toUpperCase();            }          }          swapped.push(text);        }        cm.replaceSelections(swapped);        if (args.shouldMoveCursor){          return newHead;        } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {          return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);        } else if (args.linewise){          return oldAnchor;        } else {          return cursorMin(ranges[0].anchor, ranges[0].head);        }      },      yank: function(cm, args, ranges, oldAnchor) {        var vim = cm.state.vim;        var text = cm.getSelection();        var endPos = vim.visualMode          ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)          : oldAnchor;        vimGlobalState.registerController.pushText(            args.registerName, 'yank',            text, args.linewise, vim.visualBlock);        return endPos;      }    };    function defineOperator(name, fn) {      operators[name] = fn;    }    var actions = {      jumpListWalk: function(cm, actionArgs, vim) {        if (vim.visualMode) {          return;        }        var repeat = actionArgs.repeat;        var forward = actionArgs.forward;        var jumpList = vimGlobalState.jumpList;        var mark = jumpList.move(cm, forward ? repeat : -repeat);        var markPos = mark ? mark.find() : undefined;        markPos = markPos ? markPos : cm.getCursor();        cm.setCursor(markPos);      },      scroll: function(cm, actionArgs, vim) {        if (vim.visualMode) {          return;        }        var repeat = actionArgs.repeat || 1;        var lineHeight = cm.defaultTextHeight();        var top = cm.getScrollInfo().top;        var delta = lineHeight * repeat;        var newPos = actionArgs.forward ? top + delta : top - delta;        var cursor = copyCursor(cm.getCursor());        var cursorCoords = cm.charCoords(cursor, 'local');        if (actionArgs.forward) {          if (newPos > cursorCoords.top) {             cursor.line += (newPos - cursorCoords.top) / lineHeight;             cursor.line = Math.ceil(cursor.line);             cm.setCursor(cursor);             cursorCoords = cm.charCoords(cursor, 'local');             cm.scrollTo(null, cursorCoords.top);          } else {             // Cursor stays within bounds.  Just reposition the scroll window.             cm.scrollTo(null, newPos);          }        } else {          var newBottom = newPos + cm.getScrollInfo().clientHeight;          if (newBottom < cursorCoords.bottom) {             cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;             cursor.line = Math.floor(cursor.line);             cm.setCursor(cursor);             cursorCoords = cm.charCoords(cursor, 'local');             cm.scrollTo(                 null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);          } else {             // Cursor stays within bounds.  Just reposition the scroll window.             cm.scrollTo(null, newPos);          }        }      },      scrollToCursor: function(cm, actionArgs) {        var lineNum = cm.getCursor().line;        var charCoords = cm.charCoords(new Pos(lineNum, 0), 'local');        var height = cm.getScrollInfo().clientHeight;        var y = charCoords.top;        var lineHeight = charCoords.bottom - y;        switch (actionArgs.position) {          case 'center': y = y - (height / 2) + lineHeight;            break;          case 'bottom': y = y - height + lineHeight;            break;        }        cm.scrollTo(null, y);      },      replayMacro: function(cm, actionArgs, vim) {        var registerName = actionArgs.selectedCharacter;        var repeat = actionArgs.repeat;        var macroModeState = vimGlobalState.macroModeState;        if (registerName == '@') {          registerName = macroModeState.latestRegister;        } else {          macroModeState.latestRegister = registerName;        }        while(repeat--){          executeMacroRegister(cm, vim, macroModeState, registerName);        }      },      enterMacroRecordMode: function(cm, actionArgs) {        var macroModeState = vimGlobalState.macroModeState;        var registerName = actionArgs.selectedCharacter;        if (vimGlobalState.registerController.isValidRegister(registerName)) {          macroModeState.enterMacroRecordMode(cm, registerName);        }      },      toggleOverwrite: function(cm) {        if (!cm.state.overwrite) {          cm.toggleOverwrite(true);          cm.setOption('keyMap', 'vim-replace');          CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});        } else {          cm.toggleOverwrite(false);          cm.setOption('keyMap', 'vim-insert');          CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});        }      },      enterInsertMode: function(cm, actionArgs, vim) {        if (cm.getOption('readOnly')) { return; }        vim.insertMode = true;        vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;        var insertAt = (actionArgs) ? actionArgs.insertAt : null;        var sel = vim.sel;        var head = actionArgs.head || cm.getCursor('head');        var height = cm.listSelections().length;        if (insertAt == 'eol') {          head = new Pos(head.line, lineLength(cm, head.line));        } else if (insertAt == 'bol') {          head = new Pos(head.line, 0);        } else if (insertAt == 'charAfter') {          head = offsetCursor(head, 0, 1);        } else if (insertAt == 'firstNonBlank') {          head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);        } else if (insertAt == 'startOfSelectedArea') {          if (!vim.visualMode)              return;          if (!vim.visualBlock) {            if (sel.head.line < sel.anchor.line) {              head = sel.head;            } else {              head = new Pos(sel.anchor.line, 0);            }          } else {            head = new Pos(                Math.min(sel.head.line, sel.anchor.line),                Math.min(sel.head.ch, sel.anchor.ch));            height = Math.abs(sel.head.line - sel.anchor.line) + 1;          }        } else if (insertAt == 'endOfSelectedArea') {            if (!vim.visualMode)              return;          if (!vim.visualBlock) {            if (sel.head.line >= sel.anchor.line) {              head = offsetCursor(sel.head, 0, 1);            } else {              head = new Pos(sel.anchor.line, 0);            }          } else {            head = new Pos(                Math.min(sel.head.line, sel.anchor.line),                Math.max(sel.head.ch, sel.anchor.ch) + 1);            height = Math.abs(sel.head.line - sel.anchor.line) + 1;          }        } else if (insertAt == 'inplace') {          if (vim.visualMode){            return;          }        } else if (insertAt == 'lastEdit') {          head = getLastEditPos(cm) || head;        }        cm.setOption('disableInput', false);        if (actionArgs && actionArgs.replace) {          // Handle Replace-mode as a special case of insert mode.          cm.toggleOverwrite(true);          cm.setOption('keyMap', 'vim-replace');          CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});        } else {          cm.toggleOverwrite(false);          cm.setOption('keyMap', 'vim-insert');          CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});        }        if (!vimGlobalState.macroModeState.isPlaying) {          // Only record if not replaying.          cm.on('change', onChange);          CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);        }        if (vim.visualMode) {          exitVisualMode(cm);        }        selectForInsert(cm, head, height);      },      toggleVisualMode: function(cm, actionArgs, vim) {        var repeat = actionArgs.repeat;        var anchor = cm.getCursor();        var head;        // TODO: The repeat should actually select number of characters/lines        //     equal to the repeat times the size of the previous visual        //     operation.        if (!vim.visualMode) {          // Entering visual mode          vim.visualMode = true;          vim.visualLine = !!actionArgs.linewise;          vim.visualBlock = !!actionArgs.blockwise;          head = clipCursorToContent(              cm, new Pos(anchor.line, anchor.ch + repeat - 1));          vim.sel = {            anchor: anchor,            head: head          };          CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});          updateCmSelection(cm);          updateMark(cm, vim, '<', cursorMin(anchor, head));          updateMark(cm, vim, '>', cursorMax(anchor, head));        } else if (vim.visualLine ^ actionArgs.linewise ||            vim.visualBlock ^ actionArgs.blockwise) {          // Toggling between modes          vim.visualLine = !!actionArgs.linewise;          vim.visualBlock = !!actionArgs.blockwise;          CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});          updateCmSelection(cm);        } else {          exitVisualMode(cm);        }      },      reselectLastSelection: function(cm, _actionArgs, vim) {        var lastSelection = vim.lastSelection;        if (vim.visualMode) {          updateLastSelection(cm, vim);        }        if (lastSelection) {          var anchor = lastSelection.anchorMark.find();          var head = lastSelection.headMark.find();          if (!anchor || !head) {            // If the marks have been destroyed due to edits, do nothing.            return;          }          vim.sel = {            anchor: anchor,            head: head          };          vim.visualMode = true;          vim.visualLine = lastSelection.visualLine;          vim.visualBlock = lastSelection.visualBlock;          updateCmSelection(cm);          updateMark(cm, vim, '<', cursorMin(anchor, head));          updateMark(cm, vim, '>', cursorMax(anchor, head));          CodeMirror.signal(cm, 'vim-mode-change', {            mode: 'visual',            subMode: vim.visualLine ? 'linewise' :                     vim.visualBlock ? 'blockwise' : ''});        }      },      joinLines: function(cm, actionArgs, vim) {        var curStart, curEnd;        if (vim.visualMode) {          curStart = cm.getCursor('anchor');          curEnd = cm.getCursor('head');          if (cursorIsBefore(curEnd, curStart)) {            var tmp = curEnd;            curEnd = curStart;            curStart = tmp;          }          curEnd.ch = lineLength(cm, curEnd.line) - 1;        } else {          // Repeat is the number of lines to join. Minimum 2 lines.          var repeat = Math.max(actionArgs.repeat, 2);          curStart = cm.getCursor();          curEnd = clipCursorToContent(cm, new Pos(curStart.line + repeat - 1,                                               Infinity));        }        var finalCh = 0;        for (var i = curStart.line; i < curEnd.line; i++) {          finalCh = lineLength(cm, curStart.line);          var tmp = new Pos(curStart.line + 1,                        lineLength(cm, curStart.line + 1));          var text = cm.getRange(curStart, tmp);          text = actionArgs.keepSpaces            ? text.replace(/\n\r?/g, '')            : text.replace(/\n\s*/g, ' ');          cm.replaceRange(text, curStart, tmp);        }        var curFinalPos = new Pos(curStart.line, finalCh);        if (vim.visualMode) {          exitVisualMode(cm, false);        }        cm.setCursor(curFinalPos);      },      newLineAndEnterInsertMode: function(cm, actionArgs, vim) {        vim.insertMode = true;        var insertAt = copyCursor(cm.getCursor());        if (insertAt.line === cm.firstLine() && !actionArgs.after) {          // Special case for inserting newline before start of document.          cm.replaceRange('\n', new Pos(cm.firstLine(), 0));          cm.setCursor(cm.firstLine(), 0);        } else {          insertAt.line = (actionArgs.after) ? insertAt.line :              insertAt.line - 1;          insertAt.ch = lineLength(cm, insertAt.line);          cm.setCursor(insertAt);          var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||              CodeMirror.commands.newlineAndIndent;          newlineFn(cm);        }        this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);      },      paste: function(cm, actionArgs, vim) {        var cur = copyCursor(cm.getCursor());        var register = vimGlobalState.registerController.getRegister(            actionArgs.registerName);        var text = register.toString();        if (!text) {          return;        }        if (actionArgs.matchIndent) {          var tabSize = cm.getOption("tabSize");          // length that considers tabs and tabSize          var whitespaceLength = function(str) {            var tabs = (str.split("\t").length - 1);            var spaces = (str.split(" ").length - 1);            return tabs * tabSize + spaces * 1;          };          var currentLine = cm.getLine(cm.getCursor().line);          var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);          // chomp last newline b/c don't want it to match /^\s*/gm          var chompedText = text.replace(/\n$/, '');          var wasChomped = text !== chompedText;          var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);          var text = chompedText.replace(/^\s*/gm, function(wspace) {            var newIndent = indent + (whitespaceLength(wspace) - firstIndent);            if (newIndent < 0) {              return "";            }            else if (cm.getOption("indentWithTabs")) {              var quotient = Math.floor(newIndent / tabSize);              return Array(quotient + 1).join('\t');            }            else {              return Array(newIndent + 1).join(' ');            }          });          text += wasChomped ? "\n" : "";        }        if (actionArgs.repeat > 1) {          var text = Array(actionArgs.repeat + 1).join(text);        }        var linewise = register.linewise;        var blockwise = register.blockwise;        if (blockwise) {          text = text.split('\n');          if (linewise) {              text.pop();          }          for (var i = 0; i < text.length; i++) {            text[i] = (text[i] == '') ? ' ' : text[i];          }          cur.ch += actionArgs.after ? 1 : 0;          cur.ch = Math.min(lineLength(cm, cur.line), cur.ch);        } else if (linewise) {          if(vim.visualMode) {            text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';          } else if (actionArgs.after) {            // Move the newline at the end to the start instead, and paste just            // before the newline character of the line we are on right now.            text = '\n' + text.slice(0, text.length - 1);            cur.ch = lineLength(cm, cur.line);          } else {            cur.ch = 0;          }        } else {          cur.ch += actionArgs.after ? 1 : 0;        }        var curPosFinal;        var idx;        if (vim.visualMode) {          //  save the pasted text for reselection if the need arises          vim.lastPastedText = text;          var lastSelectionCurEnd;          var selectedArea = getSelectedAreaRange(cm, vim);          var selectionStart = selectedArea[0];          var selectionEnd = selectedArea[1];          var selectedText = cm.getSelection();          var selections = cm.listSelections();          var emptyStrings = new Array(selections.length).join('1').split('1');          // save the curEnd marker before it get cleared due to cm.replaceRange.          if (vim.lastSelection) {            lastSelectionCurEnd = vim.lastSelection.headMark.find();          }          // push the previously selected text to unnamed register          vimGlobalState.registerController.unnamedRegister.setText(selectedText);          if (blockwise) {            // first delete the selected text            cm.replaceSelections(emptyStrings);            // Set new selections as per the block length of the yanked text            selectionEnd = new Pos(selectionStart.line + text.length-1, selectionStart.ch);            cm.setCursor(selectionStart);            selectBlock(cm, selectionEnd);            cm.replaceSelections(text);            curPosFinal = selectionStart;          } else if (vim.visualBlock) {            cm.replaceSelections(emptyStrings);            cm.setCursor(selectionStart);            cm.replaceRange(text, selectionStart, selectionStart);            curPosFinal = selectionStart;          } else {            cm.replaceRange(text, selectionStart, selectionEnd);            curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);          }          // restore the the curEnd marker          if(lastSelectionCurEnd) {            vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);          }          if (linewise) {            curPosFinal.ch=0;          }        } else {          if (blockwise) {            cm.setCursor(cur);            for (var i = 0; i < text.length; i++) {              var line = cur.line+i;              if (line > cm.lastLine()) {                cm.replaceRange('\n',  new Pos(line, 0));              }              var lastCh = lineLength(cm, line);              if (lastCh < cur.ch) {                extendLineToColumn(cm, line, cur.ch);              }            }            cm.setCursor(cur);            selectBlock(cm, new Pos(cur.line + text.length-1, cur.ch));            cm.replaceSelections(text);            curPosFinal = cur;          } else {            cm.replaceRange(text, cur);            // Now fine tune the cursor to where we want it.            if (linewise && actionArgs.after) {              curPosFinal = new Pos(              cur.line + 1,              findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));            } else if (linewise && !actionArgs.after) {              curPosFinal = new Pos(                cur.line,                findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));            } else if (!linewise && actionArgs.after) {              idx = cm.indexFromPos(cur);              curPosFinal = cm.posFromIndex(idx + text.length - 1);            } else {              idx = cm.indexFromPos(cur);              curPosFinal = cm.posFromIndex(idx + text.length);            }          }        }        if (vim.visualMode) {          exitVisualMode(cm, false);        }        cm.setCursor(curPosFinal);      },      undo: function(cm, actionArgs) {        cm.operation(function() {          repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();          cm.setCursor(cm.getCursor('anchor'));        });      },      redo: function(cm, actionArgs) {        repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();      },      setRegister: function(_cm, actionArgs, vim) {        vim.inputState.registerName = actionArgs.selectedCharacter;      },      setMark: function(cm, actionArgs, vim) {        var markName = actionArgs.selectedCharacter;        updateMark(cm, vim, markName, cm.getCursor());      },      replace: function(cm, actionArgs, vim) {        var replaceWith = actionArgs.selectedCharacter;        var curStart = cm.getCursor();        var replaceTo;        var curEnd;        var selections = cm.listSelections();        if (vim.visualMode) {          curStart = cm.getCursor('start');          curEnd = cm.getCursor('end');        } else {          var line = cm.getLine(curStart.line);          replaceTo = curStart.ch + actionArgs.repeat;          if (replaceTo > line.length) {            replaceTo=line.length;          }          curEnd = new Pos(curStart.line, replaceTo);        }        if (replaceWith=='\n') {          if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);          // special case, where vim help says to replace by just one line-break          (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);        } else {          var replaceWithStr = cm.getRange(curStart, curEnd);          //replace all characters in range by selected, but keep linebreaks          replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);          if (vim.visualBlock) {            // Tabs are split in visua block before replacing            var spaces = new Array(cm.getOption("tabSize")+1).join(' ');            replaceWithStr = cm.getSelection();            replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');            cm.replaceSelections(replaceWithStr);          } else {            cm.replaceRange(replaceWithStr, curStart, curEnd);          }          if (vim.visualMode) {            curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?                         selections[0].anchor : selections[0].head;            cm.setCursor(curStart);            exitVisualMode(cm, false);          } else {            cm.setCursor(offsetCursor(curEnd, 0, -1));          }        }      },      incrementNumberToken: function(cm, actionArgs) {        var cur = cm.getCursor();        var lineStr = cm.getLine(cur.line);        var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;        var match;        var start;        var end;        var numberStr;        while ((match = re.exec(lineStr)) !== null) {          start = match.index;          end = start + match[0].length;          if (cur.ch < end)break;        }        if (!actionArgs.backtrack && (end <= cur.ch))return;        if (match) {          var baseStr = match[2] || match[4]          var digits = match[3] || match[5]          var increment = actionArgs.increase ? 1 : -1;          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);          numberStr = number.toString(base);          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''          if (numberStr.charAt(0) === '-') {            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);          } else {            numberStr = baseStr + zeroPadding + numberStr;          }          var from = new Pos(cur.line, start);          var to = new Pos(cur.line, end);          cm.replaceRange(numberStr, from, to);        } else {          return;        }        cm.setCursor(new Pos(cur.line, start + numberStr.length - 1));      },      repeatLastEdit: function(cm, actionArgs, vim) {        var lastEditInputState = vim.lastEditInputState;        if (!lastEditInputState) { return; }        var repeat = actionArgs.repeat;        if (repeat && actionArgs.repeatIsExplicit) {          vim.lastEditInputState.repeatOverride = repeat;        } else {          repeat = vim.lastEditInputState.repeatOverride || repeat;        }        repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);      },      indent: function(cm, actionArgs) {        cm.indentLine(cm.getCursor().line, actionArgs.indentRight);      },      exitInsertMode: exitInsertMode    };    function defineAction(name, fn) {      actions[name] = fn;    }    /*     * Below are miscellaneous utility functions used by vim.js     */    /**     * Clips cursor to ensure that line is within the buffer's range     * If includeLineBreak is true, then allow cur.ch == lineLength.     */    function clipCursorToContent(cm, cur) {      var vim = cm.state.vim;      var includeLineBreak = vim.insertMode || vim.visualMode;      var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );      var maxCh = lineLength(cm, line) - 1 + !!includeLineBreak;      var ch = Math.min(Math.max(0, cur.ch), maxCh);      return new Pos(line, ch);    }    function copyArgs(args) {      var ret = {};      for (var prop in args) {        if (args.hasOwnProperty(prop)) {          ret[prop] = args[prop];        }      }      return ret;    }    function offsetCursor(cur, offsetLine, offsetCh) {      if (typeof offsetLine === 'object') {        offsetCh = offsetLine.ch;        offsetLine = offsetLine.line;      }      return new Pos(cur.line + offsetLine, cur.ch + offsetCh);    }    function commandMatches(keys, keyMap, context, inputState) {      // Partial matches are not applied. They inform the key handler      // that the current key sequence is a subsequence of a valid key      // sequence, so that the key buffer is not cleared.      var match, partial = [], full = [];      for (var i = 0; i < keyMap.length; i++) {        var command = keyMap[i];        if (context == 'insert' && command.context != 'insert' ||            command.context && command.context != context ||            inputState.operator && command.type == 'action' ||            !(match = commandMatch(keys, command.keys))) { continue; }        if (match == 'partial') { partial.push(command); }        if (match == 'full') { full.push(command); }      }      return {        partial: partial.length && partial,        full: full.length && full      };    }    function commandMatch(pressed, mapped) {      if (mapped.slice(-11) == '<character>') {        // Last character matches anything.        var prefixLen = mapped.length - 11;        var pressedPrefix = pressed.slice(0, prefixLen);        var mappedPrefix = mapped.slice(0, prefixLen);        return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :               mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;      } else {        return pressed == mapped ? 'full' :               mapped.indexOf(pressed) == 0 ? 'partial' : false;      }    }    function lastChar(keys) {      var match = /^.*(<[^>]+>)$/.exec(keys);      var selectedCharacter = match ? match[1] : keys.slice(-1);      if (selectedCharacter.length > 1){        switch(selectedCharacter){          case '<CR>':            selectedCharacter='\n';            break;          case '<Space>':            selectedCharacter=' ';            break;          default:            selectedCharacter='';            break;        }      }      return selectedCharacter;    }    function repeatFn(cm, fn, repeat) {      return function() {        for (var i = 0; i < repeat; i++) {          fn(cm);        }      };    }    function copyCursor(cur) {      return new Pos(cur.line, cur.ch);    }    function cursorEqual(cur1, cur2) {      return cur1.ch == cur2.ch && cur1.line == cur2.line;    }    function cursorIsBefore(cur1, cur2) {      if (cur1.line < cur2.line) {        return true;      }      if (cur1.line == cur2.line && cur1.ch < cur2.ch) {        return true;      }      return false;    }    function cursorMin(cur1, cur2) {      if (arguments.length > 2) {        cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));      }      return cursorIsBefore(cur1, cur2) ? cur1 : cur2;    }    function cursorMax(cur1, cur2) {      if (arguments.length > 2) {        cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));      }      return cursorIsBefore(cur1, cur2) ? cur2 : cur1;    }    function cursorIsBetween(cur1, cur2, cur3) {      // returns true if cur2 is between cur1 and cur3.      var cur1before2 = cursorIsBefore(cur1, cur2);      var cur2before3 = cursorIsBefore(cur2, cur3);      return cur1before2 && cur2before3;    }    function lineLength(cm, lineNum) {      return cm.getLine(lineNum).length;    }    function trim(s) {      if (s.trim) {        return s.trim();      }      return s.replace(/^\s+|\s+$/g, '');    }    function escapeRegex(s) {      return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');    }    function extendLineToColumn(cm, lineNum, column) {      var endCh = lineLength(cm, lineNum);      var spaces = new Array(column-endCh+1).join(' ');      cm.setCursor(new Pos(lineNum, endCh));      cm.replaceRange(spaces, cm.getCursor());    }    // This functions selects a rectangular block    // of text with selectionEnd as any of its corner    // Height of block:    // Difference in selectionEnd.line and first/last selection.line    // Width of the block:    // Distance between selectionEnd.ch and any(first considered here) selection.ch    function selectBlock(cm, selectionEnd) {      var selections = [], ranges = cm.listSelections();      var head = copyCursor(cm.clipPos(selectionEnd));      var isClipped = !cursorEqual(selectionEnd, head);      var curHead = cm.getCursor('head');      var primIndex = getIndex(ranges, curHead);      var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);      var max = ranges.length - 1;      var index = max - primIndex > primIndex ? max : 0;      var base = ranges[index].anchor;      var firstLine = Math.min(base.line, head.line);      var lastLine = Math.max(base.line, head.line);      var baseCh = base.ch, headCh = head.ch;      var dir = ranges[index].head.ch - baseCh;      var newDir = headCh - baseCh;      if (dir > 0 && newDir <= 0) {        baseCh++;        if (!isClipped) { headCh--; }      } else if (dir < 0 && newDir >= 0) {        baseCh--;        if (!wasClipped) { headCh++; }      } else if (dir < 0 && newDir == -1) {        baseCh--;        headCh++;      }      for (var line = firstLine; line <= lastLine; line++) {        var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};        selections.push(range);      }      cm.setSelections(selections);      selectionEnd.ch = headCh;      base.ch = baseCh;      return base;    }    function selectForInsert(cm, head, height) {      var sel = [];      for (var i = 0; i < height; i++) {        var lineHead = offsetCursor(head, i, 0);        sel.push({anchor: lineHead, head: lineHead});      }      cm.setSelections(sel, 0);    }    // getIndex returns the index of the cursor in the selections.    function getIndex(ranges, cursor, end) {      for (var i = 0; i < ranges.length; i++) {        var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);        var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);        if (atAnchor || atHead) {          return i;        }      }      return -1;    }    function getSelectedAreaRange(cm, vim) {      var lastSelection = vim.lastSelection;      var getCurrentSelectedAreaRange = function() {        var selections = cm.listSelections();        var start =  selections[0];        var end = selections[selections.length-1];        var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;        var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;        return [selectionStart, selectionEnd];      };      var getLastSelectedAreaRange = function() {        var selectionStart = cm.getCursor();        var selectionEnd = cm.getCursor();        var block = lastSelection.visualBlock;        if (block) {          var width = block.width;          var height = block.height;          selectionEnd = new Pos(selectionStart.line + height, selectionStart.ch + width);          var selections = [];          // selectBlock creates a 'proper' rectangular block.          // We do not want that in all cases, so we manually set selections.          for (var i = selectionStart.line; i < selectionEnd.line; i++) {            var anchor = new Pos(i, selectionStart.ch);            var head = new Pos(i, selectionEnd.ch);            var range = {anchor: anchor, head: head};            selections.push(range);          }          cm.setSelections(selections);        } else {          var start = lastSelection.anchorMark.find();          var end = lastSelection.headMark.find();          var line = end.line - start.line;          var ch = end.ch - start.ch;          selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};          if (lastSelection.visualLine) {            selectionStart = new Pos(selectionStart.line, 0);            selectionEnd = new Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));          }          cm.setSelection(selectionStart, selectionEnd);        }        return [selectionStart, selectionEnd];      };      if (!vim.visualMode) {      // In case of replaying the action.        return getLastSelectedAreaRange();      } else {        return getCurrentSelectedAreaRange();      }    }    // Updates the previous selection with the current selection's values. This    // should only be called in visual mode.    function updateLastSelection(cm, vim) {      var anchor = vim.sel.anchor;      var head = vim.sel.head;      // To accommodate the effect of lastPastedText in the last selection      if (vim.lastPastedText) {        head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);        vim.lastPastedText = null;      }      vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),                           'headMark': cm.setBookmark(head),                           'anchor': copyCursor(anchor),                           'head': copyCursor(head),                           'visualMode': vim.visualMode,                           'visualLine': vim.visualLine,                           'visualBlock': vim.visualBlock};    }    function expandSelection(cm, start, end) {      var sel = cm.state.vim.sel;      var head = sel.head;      var anchor = sel.anchor;      var tmp;      if (cursorIsBefore(end, start)) {        tmp = end;        end = start;        start = tmp;      }      if (cursorIsBefore(head, anchor)) {        head = cursorMin(start, head);        anchor = cursorMax(anchor, end);      } else {        anchor = cursorMin(start, anchor);        head = cursorMax(head, end);        head = offsetCursor(head, 0, -1);        if (head.ch == -1 && head.line != cm.firstLine()) {          head = new Pos(head.line - 1, lineLength(cm, head.line - 1));        }      }      return [anchor, head];    }    /**     * Updates the CodeMirror selection to match the provided vim selection.     * If no arguments are given, it uses the current vim selection state.     */    function updateCmSelection(cm, sel, mode) {      var vim = cm.state.vim;      sel = sel || vim.sel;      var mode = mode ||        vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';      var cmSel = makeCmSelection(cm, sel, mode);      cm.setSelections(cmSel.ranges, cmSel.primary);    }    function makeCmSelection(cm, sel, mode, exclusive) {      var head = copyCursor(sel.head);      var anchor = copyCursor(sel.anchor);      if (mode == 'char') {        var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;        var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;        head = offsetCursor(sel.head, 0, headOffset);        anchor = offsetCursor(sel.anchor, 0, anchorOffset);        return {          ranges: [{anchor: anchor, head: head}],          primary: 0        };      } else if (mode == 'line') {        if (!cursorIsBefore(sel.head, sel.anchor)) {          anchor.ch = 0;          var lastLine = cm.lastLine();          if (head.line > lastLine) {            head.line = lastLine;          }          head.ch = lineLength(cm, head.line);        } else {          head.ch = 0;          anchor.ch = lineLength(cm, anchor.line);        }        return {          ranges: [{anchor: anchor, head: head}],          primary: 0        };      } else if (mode == 'block') {        var top = Math.min(anchor.line, head.line),            fromCh = anchor.ch,            bottom = Math.max(anchor.line, head.line),            toCh = head.ch;        if (fromCh < toCh) { toCh += 1 }        else { fromCh += 1 };        var height = bottom - top + 1;        var primary = head.line == top ? 0 : height - 1;        var ranges = [];        for (var i = 0; i < height; i++) {          ranges.push({            anchor: new Pos(top + i, fromCh),            head: new Pos(top + i, toCh)          });        }        return {          ranges: ranges,          primary: primary        };      }    }    function getHead(cm) {      var cur = cm.getCursor('head');      if (cm.getSelection().length == 1) {        // Small corner case when only 1 character is selected. The "real"        // head is the left of head and anchor.        cur = cursorMin(cur, cm.getCursor('anchor'));      }      return cur;    }    /**     * If moveHead is set to false, the CodeMirror selection will not be     * touched. The caller assumes the responsibility of putting the cursor    * in the right place.     */    function exitVisualMode(cm, moveHead) {      var vim = cm.state.vim;      if (moveHead !== false) {        cm.setCursor(clipCursorToContent(cm, vim.sel.head));      }      updateLastSelection(cm, vim);      vim.visualMode = false;      vim.visualLine = false;      vim.visualBlock = false;      if (!vim.insertMode) CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});    }    // Remove any trailing newlines from the selection. For    // example, with the caret at the start of the last word on the line,    // 'dw' should word, but not the newline, while 'w' should advance the    // caret to the first character of the next line.    function clipToLine(cm, curStart, curEnd) {      var selection = cm.getRange(curStart, curEnd);      // Only clip if the selection ends with trailing newline + whitespace      if (/\n\s*$/.test(selection)) {        var lines = selection.split('\n');        // We know this is all whitespace.        lines.pop();        // Cases:        // 1. Last word is an empty line - do not clip the trailing '\n'        // 2. Last word is not an empty line - clip the trailing '\n'        var line;        // Find the line containing the last word, and clip all whitespace up        // to it.        for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {          curEnd.line--;          curEnd.ch = 0;        }        // If the last word is not an empty line, clip an additional newline        if (line) {          curEnd.line--;          curEnd.ch = lineLength(cm, curEnd.line);        } else {          curEnd.ch = 0;        }      }    }    // Expand the selection to line ends.    function expandSelectionToLine(_cm, curStart, curEnd) {      curStart.ch = 0;      curEnd.ch = 0;      curEnd.line++;    }    function findFirstNonWhiteSpaceCharacter(text) {      if (!text) {        return 0;      }      var firstNonWS = text.search(/\S/);      return firstNonWS == -1 ? text.length : firstNonWS;    }    function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {      var cur = getHead(cm);      var line = cm.getLine(cur.line);      var idx = cur.ch;      // Seek to first word or non-whitespace character, depending on if      // noSymbol is true.      var test = noSymbol ? wordCharTest[0] : bigWordCharTest [0];      while (!test(line.charAt(idx))) {        idx++;        if (idx >= line.length) { return null; }      }      if (bigWord) {        test = bigWordCharTest[0];      } else {        test = wordCharTest[0];        if (!test(line.charAt(idx))) {          test = wordCharTest[1];        }      }      var end = idx, start = idx;      while (test(line.charAt(end)) && end < line.length) { end++; }      while (test(line.charAt(start)) && start >= 0) { start--; }      start++;      if (inclusive) {        // If present, include all whitespace after word.        // Otherwise, include all whitespace before word, except indentation.        var wordEnd = end;        while (/\s/.test(line.charAt(end)) && end < line.length) { end++; }        if (wordEnd == end) {          var wordStart = start;          while (/\s/.test(line.charAt(start - 1)) && start > 0) { start--; }          if (!start) { start = wordStart; }        }      }      return { start: new Pos(cur.line, start), end: new Pos(cur.line, end) };    }    /**     * Depends on the following:     *     * - editor mode should be htmlmixedmode / xml     * - mode/xml/xml.js should be loaded     * - addon/fold/xml-fold.js should be loaded     *     * If any of the above requirements are not true, this function noops.     *     * This is _NOT_ a 100% accurate implementation of vim tag text objects.     * The following caveats apply (based off cursory testing, I'm sure there     * are other discrepancies):     *     * - Does not work inside comments:     *   ```     *   <!-- <div>broken</div> -->     *   ```     * - Does not work when tags have different cases:     *   ```     *   <div>broken</DIV>     *   ```     * - Does not work when cursor is inside a broken tag:     *   ```     *   <div><brok><en></div>     *   ```     */    function expandTagUnderCursor(cm, head, inclusive) {      var cur = head;      if (!CodeMirror.findMatchingTag || !CodeMirror.findEnclosingTag) {        return { start: cur, end: cur };      }      var tags = CodeMirror.findMatchingTag(cm, head) || CodeMirror.findEnclosingTag(cm, head);      if (!tags || !tags.open || !tags.close) {        return { start: cur, end: cur };      }      if (inclusive) {        return { start: tags.open.from, end: tags.close.to };      }      return { start: tags.open.to, end: tags.close.from };    }    function recordJumpPosition(cm, oldCur, newCur) {      if (!cursorEqual(oldCur, newCur)) {        vimGlobalState.jumpList.add(cm, oldCur, newCur);      }    }    function recordLastCharacterSearch(increment, args) {        vimGlobalState.lastCharacterSearch.increment = increment;        vimGlobalState.lastCharacterSearch.forward = args.forward;        vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter;    }    var symbolToMode = {        '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',        '[': 'section', ']': 'section',        '*': 'comment', '/': 'comment',        'm': 'method', 'M': 'method',        '#': 'preprocess'    };    var findSymbolModes = {      bracket: {        isComplete: function(state) {          if (state.nextCh === state.symb) {            state.depth++;            if (state.depth >= 1)return true;          } else if (state.nextCh === state.reverseSymb) {            state.depth--;          }          return false;        }      },      section: {        init: function(state) {          state.curMoveThrough = true;          state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';        },        isComplete: function(state) {          return state.index === 0 && state.nextCh === state.symb;        }      },      comment: {        isComplete: function(state) {          var found = state.lastCh === '*' && state.nextCh === '/';          state.lastCh = state.nextCh;          return found;        }      },      // TODO: The original Vim implementation only operates on level 1 and 2.      // The current implementation doesn't check for code block level and      // therefore it operates on any levels.      method: {        init: function(state) {          state.symb = (state.symb === 'm' ? '{' : '}');          state.reverseSymb = state.symb === '{' ? '}' : '{';        },        isComplete: function(state) {          if (state.nextCh === state.symb)return true;          return false;        }      },      preprocess: {        init: function(state) {          state.index = 0;        },        isComplete: function(state) {          if (state.nextCh === '#') {            var token = state.lineText.match(/^#(\w+)/)[1];            if (token === 'endif') {              if (state.forward && state.depth === 0) {                return true;              }              state.depth++;            } else if (token === 'if') {              if (!state.forward && state.depth === 0) {                return true;              }              state.depth--;            }            if (token === 'else' && state.depth === 0)return true;          }          return false;        }      }    };    function findSymbol(cm, repeat, forward, symb) {      var cur = copyCursor(cm.getCursor());      var increment = forward ? 1 : -1;      var endLine = forward ? cm.lineCount() : -1;      var curCh = cur.ch;      var line = cur.line;      var lineText = cm.getLine(line);      var state = {        lineText: lineText,        nextCh: lineText.charAt(curCh),        lastCh: null,        index: curCh,        symb: symb,        reverseSymb: (forward ?  { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],        forward: forward,        depth: 0,        curMoveThrough: false      };      var mode = symbolToMode[symb];      if (!mode)return cur;      var init = findSymbolModes[mode].init;      var isComplete = findSymbolModes[mode].isComplete;      if (init) { init(state); }      while (line !== endLine && repeat) {        state.index += increment;        state.nextCh = state.lineText.charAt(state.index);        if (!state.nextCh) {          line += increment;          state.lineText = cm.getLine(line) || '';          if (increment > 0) {            state.index = 0;          } else {            var lineLen = state.lineText.length;            state.index = (lineLen > 0) ? (lineLen-1) : 0;          }          state.nextCh = state.lineText.charAt(state.index);        }        if (isComplete(state)) {          cur.line = line;          cur.ch = state.index;          repeat--;        }      }      if (state.nextCh || state.curMoveThrough) {        return new Pos(line, state.index);      }      return cur;    }    /*     * Returns the boundaries of the next word. If the cursor in the middle of     * the word, then returns the boundaries of the current word, starting at     * the cursor. If the cursor is at the start/end of a word, and we are going     * forward/backward, respectively, find the boundaries of the next word.     *     * @param {CodeMirror} cm CodeMirror object.     * @param {Cursor} cur The cursor position.     * @param {boolean} forward True to search forward. False to search     *     backward.     * @param {boolean} bigWord True if punctuation count as part of the word.     *     False if only [a-zA-Z0-9] characters count as part of the word.     * @param {boolean} emptyLineIsWord True if empty lines should be treated     *     as words.     * @return {Object{from:number, to:number, line: number}} The boundaries of     *     the word, or null if there are no more words.     */    function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {      var lineNum = cur.line;      var pos = cur.ch;      var line = cm.getLine(lineNum);      var dir = forward ? 1 : -1;      var charTests = bigWord ? bigWordCharTest: wordCharTest;      if (emptyLineIsWord && line == '') {        lineNum += dir;        line = cm.getLine(lineNum);        if (!isLine(cm, lineNum)) {          return null;        }        pos = (forward) ? 0 : line.length;      }      while (true) {        if (emptyLineIsWord && line == '') {          return { from: 0, to: 0, line: lineNum };        }        var stop = (dir > 0) ? line.length : -1;        var wordStart = stop, wordEnd = stop;        // Find bounds of next word.        while (pos != stop) {          var foundWord = false;          for (var i = 0; i < charTests.length && !foundWord; ++i) {            if (charTests[i](line.charAt(pos))) {              wordStart = pos;              // Advance to end of word.              while (pos != stop && charTests[i](line.charAt(pos))) {                pos += dir;              }              wordEnd = pos;              foundWord = wordStart != wordEnd;              if (wordStart == cur.ch && lineNum == cur.line &&                  wordEnd == wordStart + dir) {                // We started at the end of a word. Find the next one.                continue;              } else {                return {                  from: Math.min(wordStart, wordEnd + 1),                  to: Math.max(wordStart, wordEnd),                  line: lineNum };              }            }          }          if (!foundWord) {            pos += dir;          }        }        // Advance to next/prev line.        lineNum += dir;        if (!isLine(cm, lineNum)) {          return null;        }        line = cm.getLine(lineNum);        pos = (dir > 0) ? 0 : line.length;      }    }    /**     * @param {CodeMirror} cm CodeMirror object.     * @param {Pos} cur The position to start from.     * @param {int} repeat Number of words to move past.     * @param {boolean} forward True to search forward. False to search     *     backward.     * @param {boolean} wordEnd True to move to end of word. False to move to     *     beginning of word.     * @param {boolean} bigWord True if punctuation count as part of the word.     *     False if only alphabet characters count as part of the word.     * @return {Cursor} The position the cursor should move to.     */    function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {      var curStart = copyCursor(cur);      var words = [];      if (forward && !wordEnd || !forward && wordEnd) {        repeat++;      }      // For 'e', empty lines are not considered words, go figure.      var emptyLineIsWord = !(forward && wordEnd);      for (var i = 0; i < repeat; i++) {        var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);        if (!word) {          var eodCh = lineLength(cm, cm.lastLine());          words.push(forward              ? {line: cm.lastLine(), from: eodCh, to: eodCh}              : {line: 0, from: 0, to: 0});          break;        }        words.push(word);        cur = new Pos(word.line, forward ? (word.to - 1) : word.from);      }      var shortCircuit = words.length != repeat;      var firstWord = words[0];      var lastWord = words.pop();      if (forward && !wordEnd) {        // w        if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {          // We did not start in the middle of a word. Discard the extra word at the end.          lastWord = words.pop();        }        return new Pos(lastWord.line, lastWord.from);      } else if (forward && wordEnd) {        return new Pos(lastWord.line, lastWord.to - 1);      } else if (!forward && wordEnd) {        // ge        if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {          // We did not start in the middle of a word. Discard the extra word at the end.          lastWord = words.pop();        }        return new Pos(lastWord.line, lastWord.to);      } else {        // b        return new Pos(lastWord.line, lastWord.from);      }    }    function moveToEol(cm, head, motionArgs, vim, keepHPos) {      var cur = head;      var retval= new Pos(cur.line + motionArgs.repeat - 1, Infinity);      var end=cm.clipPos(retval);      end.ch--;      if (!keepHPos) {        vim.lastHPos = Infinity;        vim.lastHSPos = cm.charCoords(end,'div').left;      }      return retval;    }    function moveToCharacter(cm, repeat, forward, character) {      var cur = cm.getCursor();      var start = cur.ch;      var idx;      for (var i = 0; i < repeat; i ++) {        var line = cm.getLine(cur.line);        idx = charIdxInLine(start, line, character, forward, true);        if (idx == -1) {          return null;        }        start = idx;      }      return new Pos(cm.getCursor().line, idx);    }    function moveToColumn(cm, repeat) {      // repeat is always >= 1, so repeat - 1 always corresponds      // to the column we want to go to.      var line = cm.getCursor().line;      return clipCursorToContent(cm, new Pos(line, repeat - 1));    }    function updateMark(cm, vim, markName, pos) {      if (!inArray(markName, validMarks)) {        return;      }      if (vim.marks[markName]) {        vim.marks[markName].clear();      }      vim.marks[markName] = cm.setBookmark(pos);    }    function charIdxInLine(start, line, character, forward, includeChar) {      // Search for char in line.      // motion_options: {forward, includeChar}      // If includeChar = true, include it too.      // If forward = true, search forward, else search backwards.      // If char is not found on this line, do nothing      var idx;      if (forward) {        idx = line.indexOf(character, start + 1);        if (idx != -1 && !includeChar) {          idx -= 1;        }      } else {        idx = line.lastIndexOf(character, start - 1);        if (idx != -1 && !includeChar) {          idx += 1;        }      }      return idx;    }    function findParagraph(cm, head, repeat, dir, inclusive) {      var line = head.line;      var min = cm.firstLine();      var max = cm.lastLine();      var start, end, i = line;      function isEmpty(i) { return !cm.getLine(i); }      function isBoundary(i, dir, any) {        if (any) { return isEmpty(i) != isEmpty(i + dir); }        return !isEmpty(i) && isEmpty(i + dir);      }      if (dir) {        while (min <= i && i <= max && repeat > 0) {          if (isBoundary(i, dir)) { repeat--; }          i += dir;        }        return new Pos(i, 0);      }      var vim = cm.state.vim;      if (vim.visualLine && isBoundary(line, 1, true)) {        var anchor = vim.sel.anchor;        if (isBoundary(anchor.line, -1, true)) {          if (!inclusive || anchor.line != line) {            line += 1;          }        }      }      var startState = isEmpty(line);      for (i = line; i <= max && repeat; i++) {        if (isBoundary(i, 1, true)) {          if (!inclusive || isEmpty(i) != startState) {            repeat--;          }        }      }      end = new Pos(i, 0);      // select boundary before paragraph for the last one      if (i > max && !startState) { startState = true; }      else { inclusive = false; }      for (i = line; i > min; i--) {        if (!inclusive || isEmpty(i) == startState || i == line) {          if (isBoundary(i, -1, true)) { break; }        }      }      start = new Pos(i, 0);      return { start: start, end: end };    }    function findSentence(cm, cur, repeat, dir) {      /*        Takes an index object        {          line: the line string,          ln: line number,          pos: index in line,          dir: direction of traversal (-1 or 1)        }        and modifies the line, ln, and pos members to represent the        next valid position or sets them to null if there are        no more valid positions.       */      function nextChar(cm, idx) {        if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) {          idx.ln += idx.dir;          if (!isLine(cm, idx.ln)) {            idx.line = null;            idx.ln = null;            idx.pos = null;            return;          }          idx.line = cm.getLine(idx.ln);          idx.pos = (idx.dir > 0) ? 0 : idx.line.length - 1;        }        else {          idx.pos += idx.dir;        }      }      /*        Performs one iteration of traversal in forward direction        Returns an index object of the new location       */      function forward(cm, ln, pos, dir) {        var line = cm.getLine(ln);        var stop = (line === "");        var curr = {          line: line,          ln: ln,          pos: pos,          dir: dir,        }        var last_valid = {          ln: curr.ln,          pos: curr.pos,        }        var skip_empty_lines = (curr.line === "");        // Move one step to skip character we start on        nextChar(cm, curr);        while (curr.line !== null) {          last_valid.ln = curr.ln;          last_valid.pos = curr.pos;          if (curr.line === "" && !skip_empty_lines) {            return { ln: curr.ln, pos: curr.pos, };          }          else if (stop && curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {            return { ln: curr.ln, pos: curr.pos, };          }          else if (isEndOfSentenceSymbol(curr.line[curr.pos])            && !stop            && (curr.pos === curr.line.length - 1              || isWhiteSpaceString(curr.line[curr.pos + 1]))) {            stop = true;          }          nextChar(cm, curr);        }        /*          Set the position to the last non whitespace character on the last          valid line in the case that we reach the end of the document.        */        var line = cm.getLine(last_valid.ln);        last_valid.pos = 0;        for(var i = line.length - 1; i >= 0; --i) {          if (!isWhiteSpaceString(line[i])) {            last_valid.pos = i;            break;          }        }        return last_valid;      }      /*        Performs one iteration of traversal in reverse direction        Returns an index object of the new location       */      function reverse(cm, ln, pos, dir) {        var line = cm.getLine(ln);        var curr = {          line: line,          ln: ln,          pos: pos,          dir: dir,        }        var last_valid = {          ln: curr.ln,          pos: null,        };        var skip_empty_lines = (curr.line === "");        // Move one step to skip character we start on        nextChar(cm, curr);        while (curr.line !== null) {          if (curr.line === "" && !skip_empty_lines) {            if (last_valid.pos !== null) {              return last_valid;            }            else {              return { ln: curr.ln, pos: curr.pos };            }          }          else if (isEndOfSentenceSymbol(curr.line[curr.pos])              && last_valid.pos !== null              && !(curr.ln === last_valid.ln && curr.pos + 1 === last_valid.pos)) {            return last_valid;          }          else if (curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {            skip_empty_lines = false;            last_valid = { ln: curr.ln, pos: curr.pos }          }          nextChar(cm, curr);        }        /*          Set the position to the first non whitespace character on the last          valid line in the case that we reach the beginning of the document.        */        var line = cm.getLine(last_valid.ln);        last_valid.pos = 0;        for(var i = 0; i < line.length; ++i) {          if (!isWhiteSpaceString(line[i])) {            last_valid.pos = i;            break;          }        }        return last_valid;      }      var curr_index = {        ln: cur.line,        pos: cur.ch,      };      while (repeat > 0) {        if (dir < 0) {          curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);        }        else {          curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);        }        repeat--;      }      return new Pos(curr_index.ln, curr_index.pos);    }    // TODO: perhaps this finagling of start and end positions belongs    // in codemirror/replaceRange?    function selectCompanionObject(cm, head, symb, inclusive) {      var cur = head, start, end;      var bracketRegexp = ({        '(': /[()]/, ')': /[()]/,        '[': /[[\]]/, ']': /[[\]]/,        '{': /[{}]/, '}': /[{}]/,        '<': /[<>]/, '>': /[<>]/})[symb];      var openSym = ({        '(': '(', ')': '(',        '[': '[', ']': '[',        '{': '{', '}': '{',        '<': '<', '>': '<'})[symb];      var curChar = cm.getLine(cur.line).charAt(cur.ch);      // Due to the behavior of scanForBracket, we need to add an offset if the      // cursor is on a matching open bracket.      var offset = curChar === openSym ? 1 : 0;      start = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), -1, undefined, {'bracketRegex': bracketRegexp});      end = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), 1, undefined, {'bracketRegex': bracketRegexp});      if (!start || !end) {        return { start: cur, end: cur };      }      start = start.pos;      end = end.pos;      if ((start.line == end.line && start.ch > end.ch)          || (start.line > end.line)) {        var tmp = start;        start = end;        end = tmp;      }      if (inclusive) {        end.ch += 1;      } else {        start.ch += 1;      }      return { start: start, end: end };    }    // Takes in a symbol and a cursor and tries to simulate text objects that    // have identical opening and closing symbols    // TODO support across multiple lines    function findBeginningAndEnd(cm, head, symb, inclusive) {      var cur = copyCursor(head);      var line = cm.getLine(cur.line);      var chars = line.split('');      var start, end, i, len;      var firstIndex = chars.indexOf(symb);      // the decision tree is to always look backwards for the beginning first,      // but if the cursor is in front of the first instance of the symb,      // then move the cursor forward      if (cur.ch < firstIndex) {        cur.ch = firstIndex;        // Why is this line even here???        // cm.setCursor(cur.line, firstIndex+1);      }      // otherwise if the cursor is currently on the closing symbol      else if (firstIndex < cur.ch && chars[cur.ch] == symb) {        end = cur.ch; // assign end to the current cursor        --cur.ch; // make sure to look backwards      }      // if we're currently on the symbol, we've got a start      if (chars[cur.ch] == symb && !end) {        start = cur.ch + 1; // assign start to ahead of the cursor      } else {        // go backwards to find the start        for (i = cur.ch; i > -1 && !start; i--) {          if (chars[i] == symb) {            start = i + 1;          }        }      }      // look forwards for the end symbol      if (start && !end) {        for (i = start, len = chars.length; i < len && !end; i++) {          if (chars[i] == symb) {            end = i;          }        }      }      // nothing found      if (!start || !end) {        return { start: cur, end: cur };      }      // include the symbols      if (inclusive) {        --start; ++end;      }      return {        start: new Pos(cur.line, start),        end: new Pos(cur.line, end)      };    }    // Search functions    defineOption('pcre', true, 'boolean');    function SearchState() {}    SearchState.prototype = {      getQuery: function() {        return vimGlobalState.query;      },      setQuery: function(query) {        vimGlobalState.query = query;      },      getOverlay: function() {        return this.searchOverlay;      },      setOverlay: function(overlay) {        this.searchOverlay = overlay;      },      isReversed: function() {        return vimGlobalState.isReversed;      },      setReversed: function(reversed) {        vimGlobalState.isReversed = reversed;      },      getScrollbarAnnotate: function() {        return this.annotate;      },      setScrollbarAnnotate: function(annotate) {        this.annotate = annotate;      }    };    function getSearchState(cm) {      var vim = cm.state.vim;      return vim.searchState_ || (vim.searchState_ = new SearchState());    }    function splitBySlash(argString) {      return splitBySeparator(argString, '/');    }    function findUnescapedSlashes(argString) {      return findUnescapedSeparators(argString, '/');    }    function splitBySeparator(argString, separator) {      var slashes = findUnescapedSeparators(argString, separator) || [];      if (!slashes.length) return [];      var tokens = [];      // in case of strings like foo/bar      if (slashes[0] !== 0) return;      for (var i = 0; i < slashes.length; i++) {        if (typeof slashes[i] == 'number')          tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));      }      return tokens;    }    function findUnescapedSeparators(str, separator) {      if (!separator)        separator = '/';      var escapeNextChar = false;      var slashes = [];      for (var i = 0; i < str.length; i++) {        var c = str.charAt(i);        if (!escapeNextChar && c == separator) {          slashes.push(i);        }        escapeNextChar = !escapeNextChar && (c == '\\');      }      return slashes;    }    // Translates a search string from ex (vim) syntax into javascript form.    function translateRegex(str) {      // When these match, add a '\' if unescaped or remove one if escaped.      var specials = '|(){';      // Remove, but never add, a '\' for these.      var unescape = '}';      var escapeNextChar = false;      var out = [];      for (var i = -1; i < str.length; i++) {        var c = str.charAt(i) || '';        var n = str.charAt(i+1) || '';        var specialComesNext = (n && specials.indexOf(n) != -1);        if (escapeNextChar) {          if (c !== '\\' || !specialComesNext) {            out.push(c);          }          escapeNextChar = false;        } else {          if (c === '\\') {            escapeNextChar = true;            // Treat the unescape list as special for removing, but not adding '\'.            if (n && unescape.indexOf(n) != -1) {              specialComesNext = true;            }            // Not passing this test means removing a '\'.            if (!specialComesNext || n === '\\') {              out.push(c);            }          } else {            out.push(c);            if (specialComesNext && n !== '\\') {              out.push('\\');            }          }        }      }      return out.join('');    }    // Translates the replace part of a search and replace from ex (vim) syntax into    // javascript form.  Similar to translateRegex, but additionally fixes back references    // (translates '\[0..9]' to '$[0..9]') and follows different rules for escaping '$'.    var charUnescapes = {'\\n': '\n', '\\r': '\r', '\\t': '\t'};    function translateRegexReplace(str) {      var escapeNextChar = false;      var out = [];      for (var i = -1; i < str.length; i++) {        var c = str.charAt(i) || '';        var n = str.charAt(i+1) || '';        if (charUnescapes[c + n]) {          out.push(charUnescapes[c+n]);          i++;        } else if (escapeNextChar) {          // At any point in the loop, escapeNextChar is true if the previous          // character was a '\' and was not escaped.          out.push(c);          escapeNextChar = false;        } else {          if (c === '\\') {            escapeNextChar = true;            if ((isNumber(n) || n === '$')) {              out.push('$');            } else if (n !== '/' && n !== '\\') {              out.push('\\');            }          } else {            if (c === '$') {              out.push('$');            }            out.push(c);            if (n === '/') {              out.push('\\');            }          }        }      }      return out.join('');    }    // Unescape \ and / in the replace part, for PCRE mode.    var unescapes = {'\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t', '\\&':'&'};    function unescapeRegexReplace(str) {      var stream = new CodeMirror.StringStream(str);      var output = [];      while (!stream.eol()) {        // Search for \.        while (stream.peek() && stream.peek() != '\\') {          output.push(stream.next());        }        var matched = false;        for (var matcher in unescapes) {          if (stream.match(matcher, true)) {            matched = true;            output.push(unescapes[matcher]);            break;          }        }        if (!matched) {          // Don't change anything          output.push(stream.next());        }      }      return output.join('');    }    /**     * Extract the regular expression from the query and return a Regexp object.     * Returns null if the query is blank.     * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.     * If smartCase is passed in, and the query contains upper case letters,     *   then ignoreCase is overridden, and the 'i' flag will not be set.     * If the query contains the /i in the flag part of the regular expression,     *   then both ignoreCase and smartCase are ignored, and 'i' will be passed     *   through to the Regex object.     */    function parseQuery(query, ignoreCase, smartCase) {      // First update the last search register      var lastSearchRegister = vimGlobalState.registerController.getRegister('/');      lastSearchRegister.setText(query);      // Check if the query is already a regex.      if (query instanceof RegExp) { return query; }      // First try to extract regex + flags from the input. If no flags found,      // extract just the regex. IE does not accept flags directly defined in      // the regex string in the form /regex/flags      var slashes = findUnescapedSlashes(query);      var regexPart;      var forceIgnoreCase;      if (!slashes.length) {        // Query looks like 'regexp'        regexPart = query;      } else {        // Query looks like 'regexp/...'        regexPart = query.substring(0, slashes[0]);        var flagsPart = query.substring(slashes[0]);        forceIgnoreCase = (flagsPart.indexOf('i') != -1);      }      if (!regexPart) {        return null;      }      if (!getOption('pcre')) {        regexPart = translateRegex(regexPart);      }      if (smartCase) {        ignoreCase = (/^[^A-Z]*$/).test(regexPart);      }      var regexp = new RegExp(regexPart,          (ignoreCase || forceIgnoreCase) ? 'im' : 'm');      return regexp;    }    /**     * dom - Document Object Manipulator     * Usage:     *   dom('<tag>'|<node>[, ...{<attributes>|<$styles>}|<child-node>|'<text>'])     * Examples:     *   dom('div', {id:'xyz'}, dom('p', 'CM rocks!', {$color:'red'}))     *   dom(document.head, dom('script', 'alert("hello!")'))     * Not supported:     *   dom('p', ['arrays are objects'], Error('objects specify attributes'))     */    function dom(n) {      if (typeof n === 'string') n = document.createElement(n);      for (var a, i = 1; i < arguments.length; i++) {        if (!(a = arguments[i])) continue;        if (typeof a !== 'object') a = document.createTextNode(a);        if (a.nodeType) n.appendChild(a);        else for (var key in a) {          if (!Object.prototype.hasOwnProperty.call(a, key)) continue;          if (key[0] === '$') n.style[key.slice(1)] = a[key];          else n.setAttribute(key, a[key]);        }      }      return n;    }    function showConfirm(cm, template) {      var pre = dom('pre', {$color: 'red', class: 'cm-vim-message'}, template);      if (cm.openNotification) {        cm.openNotification(pre, {bottom: true, duration: 5000});      } else {        alert(pre.innerText);      }    }    function makePrompt(prefix, desc) {      return dom(document.createDocumentFragment(),               dom('span', {$fontFamily: 'monospace', $whiteSpace: 'pre'},                 prefix,                 dom('input', {type: 'text', autocorrect: 'off',                               autocapitalize: 'off', spellcheck: 'false'})),               desc && dom('span', {$color: '#888'}, desc));    }    function showPrompt(cm, options) {      var template = makePrompt(options.prefix, options.desc);      if (cm.openDialog) {        cm.openDialog(template, options.onClose, {          onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,          bottom: true, selectValueOnOpen: false, value: options.value        });      }      else {        var shortText = '';        if (typeof options.prefix != "string" && options.prefix) shortText += options.prefix.textContent;        if (options.desc) shortText += " " + options.desc;        options.onClose(prompt(shortText, ''));      }    }    function regexEqual(r1, r2) {      if (r1 instanceof RegExp && r2 instanceof RegExp) {          var props = ['global', 'multiline', 'ignoreCase', 'source'];          for (var i = 0; i < props.length; i++) {              var prop = props[i];              if (r1[prop] !== r2[prop]) {                  return false;              }          }          return true;      }      return false;    }    // Returns true if the query is valid.    function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {      if (!rawQuery) {        return;      }      var state = getSearchState(cm);      var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);      if (!query) {        return;      }      highlightSearchMatches(cm, query);      if (regexEqual(query, state.getQuery())) {        return query;      }      state.setQuery(query);      return query;    }    function searchOverlay(query) {      if (query.source.charAt(0) == '^') {        var matchSol = true;      }      return {        token: function(stream) {          if (matchSol && !stream.sol()) {            stream.skipToEnd();            return;          }          var match = stream.match(query, false);          if (match) {            if (match[0].length == 0) {              // Matched empty string, skip to next.              stream.next();              return 'searching';            }            if (!stream.sol()) {              // Backtrack 1 to match \b              stream.backUp(1);              if (!query.exec(stream.next() + match[0])) {                stream.next();                return null;              }            }            stream.match(query);            return 'searching';          }          while (!stream.eol()) {            stream.next();            if (stream.match(query, false)) break;          }        },        query: query      };    }    var highlightTimeout = 0;    function highlightSearchMatches(cm, query) {      clearTimeout(highlightTimeout);      highlightTimeout = setTimeout(function() {        if (!cm.state.vim) return;        var searchState = getSearchState(cm);        var overlay = searchState.getOverlay();        if (!overlay || query != overlay.query) {          if (overlay) {            cm.removeOverlay(overlay);          }          overlay = searchOverlay(query);          cm.addOverlay(overlay);          if (cm.showMatchesOnScrollbar) {            if (searchState.getScrollbarAnnotate()) {              searchState.getScrollbarAnnotate().clear();            }            searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));          }          searchState.setOverlay(overlay);        }      }, 50);    }    function findNext(cm, prev, query, repeat) {      if (repeat === undefined) { repeat = 1; }      return cm.operation(function() {        var pos = cm.getCursor();        var cursor = cm.getSearchCursor(query, pos);        for (var i = 0; i < repeat; i++) {          var found = cursor.find(prev);          if (i == 0 && found && cursorEqual(cursor.from(), pos)) {            var lastEndPos = prev ? cursor.from() : cursor.to();            found = cursor.find(prev);            if (found && !found[0] && cursorEqual(cursor.from(), lastEndPos)) {              if (cm.getLine(lastEndPos.line).length == lastEndPos.ch)                found = cursor.find(prev);            }          }          if (!found) {            // SearchCursor may have returned null because it hit EOF, wrap            // around and try again.            cursor = cm.getSearchCursor(query,                (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0) );            if (!cursor.find(prev)) {              return;            }          }        }        return cursor.from();      });    }    /**     * Pretty much the same as `findNext`, except for the following differences:     *     * 1. Before starting the search, move to the previous search. This way if our cursor is     * already inside a match, we should return the current match.     * 2. Rather than only returning the cursor's from, we return the cursor's from and to as a tuple.     */    function findNextFromAndToInclusive(cm, prev, query, repeat, vim) {      if (repeat === undefined) { repeat = 1; }      return cm.operation(function() {        var pos = cm.getCursor();        var cursor = cm.getSearchCursor(query, pos);        // Go back one result to ensure that if the cursor is currently a match, we keep it.        var found = cursor.find(!prev);        // If we haven't moved, go back one more (similar to if i==0 logic in findNext).        if (!vim.visualMode && found && cursorEqual(cursor.from(), pos)) {          cursor.find(!prev);        }        for (var i = 0; i < repeat; i++) {          found = cursor.find(prev);          if (!found) {            // SearchCursor may have returned null because it hit EOF, wrap            // around and try again.            cursor = cm.getSearchCursor(query,                (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0) );            if (!cursor.find(prev)) {              return;            }          }        }        return [cursor.from(), cursor.to()];      });    }    function clearSearchHighlight(cm) {      var state = getSearchState(cm);      cm.removeOverlay(getSearchState(cm).getOverlay());      state.setOverlay(null);      if (state.getScrollbarAnnotate()) {        state.getScrollbarAnnotate().clear();        state.setScrollbarAnnotate(null);      }    }    /**     * Check if pos is in the specified range, INCLUSIVE.     * Range can be specified with 1 or 2 arguments.     * If the first range argument is an array, treat it as an array of line     * numbers. Match pos against any of the lines.     * If the first range argument is a number,     *   if there is only 1 range argument, check if pos has the same line     *       number     *   if there are 2 range arguments, then check if pos is in between the two     *       range arguments.     */    function isInRange(pos, start, end) {      if (typeof pos != 'number') {        // Assume it is a cursor position. Get the line number.        pos = pos.line;      }      if (start instanceof Array) {        return inArray(pos, start);      } else {        if (typeof end == 'number') {          return (pos >= start && pos <= end);        } else {          return pos == start;        }      }    }    function getUserVisibleLines(cm) {      var scrollInfo = cm.getScrollInfo();      var occludeToleranceTop = 6;      var occludeToleranceBottom = 10;      var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local');      var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;      var to = cm.coordsChar({left:0, top: bottomY}, 'local');      return {top: from.line, bottom: to.line};    }    function getMarkPos(cm, vim, markName) {      if (markName == '\'' || markName == '`') {        return vimGlobalState.jumpList.find(cm, -1) || new Pos(0, 0);      } else if (markName == '.') {        return getLastEditPos(cm);      }      var mark = vim.marks[markName];      return mark && mark.find();    }    function getLastEditPos(cm) {      var done = cm.doc.history.done;      for (var i = done.length; i--;) {        if (done[i].changes) {          return copyCursor(done[i].changes[0].to);        }      }    }    var ExCommandDispatcher = function() {      this.buildCommandMap_();    };    ExCommandDispatcher.prototype = {      processCommand: function(cm, input, opt_params) {        var that = this;        cm.operation(function () {          cm.curOp.isVimOp = true;          that._processCommand(cm, input, opt_params);        });      },      _processCommand: function(cm, input, opt_params) {        var vim = cm.state.vim;        var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');        var previousCommand = commandHistoryRegister.toString();        if (vim.visualMode) {          exitVisualMode(cm);        }        var inputStream = new CodeMirror.StringStream(input);        // update ": with the latest command whether valid or invalid        commandHistoryRegister.setText(input);        var params = opt_params || {};        params.input = input;        try {          this.parseInput_(cm, inputStream, params);        } catch(e) {          showConfirm(cm, e.toString());          throw e;        }        var command;        var commandName;        if (!params.commandName) {          // If only a line range is defined, move to the line.          if (params.line !== undefined) {            commandName = 'move';          }        } else {          command = this.matchCommand_(params.commandName);          if (command) {            commandName = command.name;            if (command.excludeFromCommandHistory) {              commandHistoryRegister.setText(previousCommand);            }            this.parseCommandArgs_(inputStream, params, command);            if (command.type == 'exToKey') {              // Handle Ex to Key mapping.              for (var i = 0; i < command.toKeys.length; i++) {                vimApi.handleKey(cm, command.toKeys[i], 'mapping');              }              return;            } else if (command.type == 'exToEx') {              // Handle Ex to Ex mapping.              this.processCommand(cm, command.toInput);              return;            }          }        }        if (!commandName) {          showConfirm(cm, 'Not an editor command ":' + input + '"');          return;        }        try {          exCommands[commandName](cm, params);          // Possibly asynchronous commands (e.g. substitute, which might have a          // user confirmation), are responsible for calling the callback when          // done. All others have it taken care of for them here.          if ((!command || !command.possiblyAsync) && params.callback) {            params.callback();          }        } catch(e) {          showConfirm(cm, e.toString());          throw e;        }      },      parseInput_: function(cm, inputStream, result) {        inputStream.eatWhile(':');        // Parse range.        if (inputStream.eat('%')) {          result.line = cm.firstLine();          result.lineEnd = cm.lastLine();        } else {          result.line = this.parseLineSpec_(cm, inputStream);          if (result.line !== undefined && inputStream.eat(',')) {            result.lineEnd = this.parseLineSpec_(cm, inputStream);          }        }        // Parse command name.        var commandMatch = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/);        if (commandMatch) {          result.commandName = commandMatch[1];        } else {          result.commandName = inputStream.match(/.*/)[0];        }        return result;      },      parseLineSpec_: function(cm, inputStream) {        var numberMatch = inputStream.match(/^(\d+)/);        if (numberMatch) {          // Absolute line number plus offset (N+M or N-M) is probably a typo,          // not something the user actually wanted. (NB: vim does allow this.)          return parseInt(numberMatch[1], 10) - 1;        }        switch (inputStream.next()) {          case '.':            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);          case '$':            return this.parseLineSpecOffset_(inputStream, cm.lastLine());          case '\'':            var markName = inputStream.next();            var markPos = getMarkPos(cm, cm.state.vim, markName);            if (!markPos) throw new Error('Mark not set');            return this.parseLineSpecOffset_(inputStream, markPos.line);          case '-':          case '+':            inputStream.backUp(1);            // Offset is relative to current line if not otherwise specified.            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);          default:            inputStream.backUp(1);            return undefined;        }      },      parseLineSpecOffset_: function(inputStream, line) {        var offsetMatch = inputStream.match(/^([+-])?(\d+)/);        if (offsetMatch) {          var offset = parseInt(offsetMatch[2], 10);          if (offsetMatch[1] == "-") {            line -= offset;          } else {            line += offset;          }        }        return line;      },      parseCommandArgs_: function(inputStream, params, command) {        if (inputStream.eol()) {          return;        }        params.argString = inputStream.match(/.*/)[0];        // Parse command-line arguments        var delim = command.argDelimiter || /\s+/;        var args = trim(params.argString).split(delim);        if (args.length && args[0]) {          params.args = args;        }      },      matchCommand_: function(commandName) {        // Return the command in the command map that matches the shortest        // prefix of the passed in command name. The match is guaranteed to be        // unambiguous if the defaultExCommandMap's shortNames are set up        // correctly. (see @code{defaultExCommandMap}).        for (var i = commandName.length; i > 0; i--) {          var prefix = commandName.substring(0, i);          if (this.commandMap_[prefix]) {            var command = this.commandMap_[prefix];            if (command.name.indexOf(commandName) === 0) {              return command;            }          }        }        return null;      },      buildCommandMap_: function() {        this.commandMap_ = {};        for (var i = 0; i < defaultExCommandMap.length; i++) {          var command = defaultExCommandMap[i];          var key = command.shortName || command.name;          this.commandMap_[key] = command;        }      },      map: function(lhs, rhs, ctx) {        if (lhs != ':' && lhs.charAt(0) == ':') {          if (ctx) { throw Error('Mode not supported for ex mappings'); }          var commandName = lhs.substring(1);          if (rhs != ':' && rhs.charAt(0) == ':') {            // Ex to Ex mapping            this.commandMap_[commandName] = {              name: commandName,              type: 'exToEx',              toInput: rhs.substring(1),              user: true            };          } else {            // Ex to key mapping            this.commandMap_[commandName] = {              name: commandName,              type: 'exToKey',              toKeys: rhs,              user: true            };          }        } else {          if (rhs != ':' && rhs.charAt(0) == ':') {            // Key to Ex mapping.            var mapping = {              keys: lhs,              type: 'keyToEx',              exArgs: { input: rhs.substring(1) }            };            if (ctx) { mapping.context = ctx; }            defaultKeymap.unshift(mapping);          } else {            // Key to key mapping            var mapping = {              keys: lhs,              type: 'keyToKey',              toKeys: rhs            };            if (ctx) { mapping.context = ctx; }            defaultKeymap.unshift(mapping);          }        }      },      unmap: function(lhs, ctx) {        if (lhs != ':' && lhs.charAt(0) == ':') {          // Ex to Ex or Ex to key mapping          if (ctx) { throw Error('Mode not supported for ex mappings'); }          var commandName = lhs.substring(1);          if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {            delete this.commandMap_[commandName];            return true;          }        } else {          // Key to Ex or key to key mapping          var keys = lhs;          for (var i = 0; i < defaultKeymap.length; i++) {            if (keys == defaultKeymap[i].keys                && defaultKeymap[i].context === ctx) {              defaultKeymap.splice(i, 1);              return true;            }          }        }      }    };    var exCommands = {      colorscheme: function(cm, params) {        if (!params.args || params.args.length < 1) {          showConfirm(cm, cm.getOption('theme'));          return;        }        cm.setOption('theme', params.args[0]);      },      map: function(cm, params, ctx) {        var mapArgs = params.args;        if (!mapArgs || mapArgs.length < 2) {          if (cm) {            showConfirm(cm, 'Invalid mapping: ' + params.input);          }          return;        }        exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);      },      imap: function(cm, params) { this.map(cm, params, 'insert'); },      nmap: function(cm, params) { this.map(cm, params, 'normal'); },      vmap: function(cm, params) { this.map(cm, params, 'visual'); },      unmap: function(cm, params, ctx) {        var mapArgs = params.args;        if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) {          if (cm) {            showConfirm(cm, 'No such mapping: ' + params.input);          }        }      },      move: function(cm, params) {        commandDispatcher.processCommand(cm, cm.state.vim, {            type: 'motion',            motion: 'moveToLineOrEdgeOfDocument',            motionArgs: { forward: false, explicitRepeat: true,              linewise: true },            repeatOverride: params.line+1});      },      set: function(cm, params) {        var setArgs = params.args;        // Options passed through to the setOption/getOption calls. May be passed in by the        // local/global versions of the set command        var setCfg = params.setCfg || {};        if (!setArgs || setArgs.length < 1) {          if (cm) {            showConfirm(cm, 'Invalid mapping: ' + params.input);          }          return;        }        var expr = setArgs[0].split('=');        var optionName = expr[0];        var value = expr[1];        var forceGet = false;        if (optionName.charAt(optionName.length - 1) == '?') {          // If post-fixed with ?, then the set is actually a get.          if (value) { throw Error('Trailing characters: ' + params.argString); }          optionName = optionName.substring(0, optionName.length - 1);          forceGet = true;        }        if (value === undefined && optionName.substring(0, 2) == 'no') {          // To set boolean options to false, the option name is prefixed with          // 'no'.          optionName = optionName.substring(2);          value = false;        }        var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';        if (optionIsBoolean && value == undefined) {          // Calling set with a boolean option sets it to true.          value = true;        }        // If no value is provided, then we assume this is a get.        if (!optionIsBoolean && value === undefined || forceGet) {          var oldValue = getOption(optionName, cm, setCfg);          if (oldValue instanceof Error) {            showConfirm(cm, oldValue.message);          } else if (oldValue === true || oldValue === false) {            showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);          } else {            showConfirm(cm, '  ' + optionName + '=' + oldValue);          }        } else {          var setOptionReturn = setOption(optionName, value, cm, setCfg);          if (setOptionReturn instanceof Error) {            showConfirm(cm, setOptionReturn.message);          }        }      },      setlocal: function (cm, params) {        // setCfg is passed through to setOption        params.setCfg = {scope: 'local'};        this.set(cm, params);      },      setglobal: function (cm, params) {        // setCfg is passed through to setOption        params.setCfg = {scope: 'global'};        this.set(cm, params);      },      registers: function(cm, params) {        var regArgs = params.args;        var registers = vimGlobalState.registerController.registers;        var regInfo = '----------Registers----------\n\n';        if (!regArgs) {          for (var registerName in registers) {            var text = registers[registerName].toString();            if (text.length) {              regInfo += '"' + registerName + '    ' + text + '\n'            }          }        } else {          var registerName;          regArgs = regArgs.join('');          for (var i = 0; i < regArgs.length; i++) {            registerName = regArgs.charAt(i);            if (!vimGlobalState.registerController.isValidRegister(registerName)) {              continue;            }            var register = registers[registerName] || new Register();            regInfo += '"' + registerName + '    ' + register.toString() + '\n'          }        }        showConfirm(cm, regInfo);      },      sort: function(cm, params) {        var reverse, ignoreCase, unique, number, pattern;        function parseArgs() {          if (params.argString) {            var args = new CodeMirror.StringStream(params.argString);            if (args.eat('!')) { reverse = true; }            if (args.eol()) { return; }            if (!args.eatSpace()) { return 'Invalid arguments'; }            var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/);            if (!opts && !args.eol()) { return 'Invalid arguments'; }            if (opts[1]) {              ignoreCase = opts[1].indexOf('i') != -1;              unique = opts[1].indexOf('u') != -1;              var decimal = opts[1].indexOf('d') != -1 || opts[1].indexOf('n') != -1 && 1;              var hex = opts[1].indexOf('x') != -1 && 1;              var octal = opts[1].indexOf('o') != -1 && 1;              if (decimal + hex + octal > 1) { return 'Invalid arguments'; }              number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';            }            if (opts[2]) {              pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? 'i' : '');            }          }        }        var err = parseArgs();        if (err) {          showConfirm(cm, err + ': ' + params.argString);          return;        }        var lineStart = params.line || cm.firstLine();        var lineEnd = params.lineEnd || params.line || cm.lastLine();        if (lineStart == lineEnd) { return; }        var curStart = new Pos(lineStart, 0);        var curEnd = new Pos(lineEnd, lineLength(cm, lineEnd));        var text = cm.getRange(curStart, curEnd).split('\n');        var numberRegex = pattern ? pattern :           (number == 'decimal') ? /(-?)([\d]+)/ :           (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :           (number == 'octal') ? /([0-7]+)/ : null;        var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;        var numPart = [], textPart = [];        if (number || pattern) {          for (var i = 0; i < text.length; i++) {            var matchPart = pattern ? text[i].match(pattern) : null;            if (matchPart && matchPart[0] != '') {              numPart.push(matchPart);            } else if (!pattern && numberRegex.exec(text[i])) {              numPart.push(text[i]);            } else {              textPart.push(text[i]);            }          }        } else {          textPart = text;        }        function compareFn(a, b) {          if (reverse) { var tmp; tmp = a; a = b; b = tmp; }          if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }          var anum = number && numberRegex.exec(a);          var bnum = number && numberRegex.exec(b);          if (!anum) { return a < b ? -1 : 1; }          anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);          bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);          return anum - bnum;        }        function comparePatternFn(a, b) {          if (reverse) { var tmp; tmp = a; a = b; b = tmp; }          if (ignoreCase) { a[0] = a[0].toLowerCase(); b[0] = b[0].toLowerCase(); }          return (a[0] < b[0]) ? -1 : 1;        }        numPart.sort(pattern ? comparePatternFn : compareFn);        if (pattern) {          for (var i = 0; i < numPart.length; i++) {            numPart[i] = numPart[i].input;          }        } else if (!number) { textPart.sort(compareFn); }        text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);        if (unique) { // Remove duplicate lines          var textOld = text;          var lastLine;          text = [];          for (var i = 0; i < textOld.length; i++) {            if (textOld[i] != lastLine) {              text.push(textOld[i]);            }            lastLine = textOld[i];          }        }        cm.replaceRange(text.join('\n'), curStart, curEnd);      },      vglobal: function(cm, params) {        // global inspects params.commandName        this.global(cm, params);      },      global: function(cm, params) {        // a global command is of the form        // :[range]g/pattern/[cmd]        // argString holds the string /pattern/[cmd]        var argString = params.argString;        if (!argString) {          showConfirm(cm, 'Regular Expression missing from global');          return;        }        var inverted = params.commandName[0] === 'v';        // range is specified here        var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();        var lineEnd = params.lineEnd || params.line || cm.lastLine();        // get the tokens from argString        var tokens = splitBySlash(argString);        var regexPart = argString, cmd;        if (tokens.length) {          regexPart = tokens[0];          cmd = tokens.slice(1, tokens.length).join('/');        }        if (regexPart) {          // If regex part is empty, then use the previous query. Otherwise          // use the regex part as the new query.          try {           updateSearchQuery(cm, regexPart, true /** ignoreCase */,             true /** smartCase */);          } catch (e) {           showConfirm(cm, 'Invalid regex: ' + regexPart);           return;          }        }        // now that we have the regexPart, search for regex matches in the        // specified range of lines        var query = getSearchState(cm).getQuery();        var matchedLines = [];        for (var i = lineStart; i <= lineEnd; i++) {          var line = cm.getLineHandle(i);          var matched = query.test(line.text);          if (matched !== inverted) {            matchedLines.push(cmd ? line : line.text);          }        }        // if there is no [cmd], just display the list of matched lines        if (!cmd) {          showConfirm(cm, matchedLines.join('\n'));          return;        }        var index = 0;        var nextCommand = function() {          if (index < matchedLines.length) {            var line = matchedLines[index++];            var lineNum = cm.getLineNumber(line);            if (lineNum == null) {              nextCommand();              return;            }            var command = (lineNum + 1) + cmd;            exCommandDispatcher.processCommand(cm, command, {              callback: nextCommand            });          }        };        nextCommand();      },      substitute: function(cm, params) {        if (!cm.getSearchCursor) {          throw new Error('Search feature not available. Requires searchcursor.js or ' +              'any other getSearchCursor implementation.');        }        var argString = params.argString;        var tokens = argString ? splitBySeparator(argString, argString[0]) : [];        var regexPart, replacePart = '', trailing, flagsPart, count;        var confirm = false; // Whether to confirm each replace.        var global = false; // True to replace all instances on a line, false to replace only 1.        if (tokens.length) {          regexPart = tokens[0];          if (getOption('pcre') && regexPart !== '') {              regexPart = new RegExp(regexPart).source; //normalize not escaped characters          }          replacePart = tokens[1];          if (replacePart !== undefined) {            if (getOption('pcre')) {              replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g,"$1$$&"));            } else {              replacePart = translateRegexReplace(replacePart);            }            vimGlobalState.lastSubstituteReplacePart = replacePart;          }          trailing = tokens[2] ? tokens[2].split(' ') : [];        } else {          // either the argString is empty or its of the form ' hello/world'          // actually splitBySlash returns a list of tokens          // only if the string starts with a '/'          if (argString && argString.length) {            showConfirm(cm, 'Substitutions should be of the form ' +                ':s/pattern/replace/');            return;          }        }        // After the 3rd slash, we can have flags followed by a space followed        // by count.        if (trailing) {          flagsPart = trailing[0];          count = parseInt(trailing[1]);          if (flagsPart) {            if (flagsPart.indexOf('c') != -1) {              confirm = true;            }            if (flagsPart.indexOf('g') != -1) {              global = true;            }            if (getOption('pcre')) {               regexPart = regexPart + '/' + flagsPart;            } else {               regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;            }          }        }        if (regexPart) {          // If regex part is empty, then use the previous query. Otherwise use          // the regex part as the new query.          try {            updateSearchQuery(cm, regexPart, true /** ignoreCase */,              true /** smartCase */);          } catch (e) {            showConfirm(cm, 'Invalid regex: ' + regexPart);            return;          }        }        replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;        if (replacePart === undefined) {          showConfirm(cm, 'No previous substitute regular expression');          return;        }        var state = getSearchState(cm);        var query = state.getQuery();        var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;        var lineEnd = params.lineEnd || lineStart;        if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {          lineEnd = Infinity;        }        if (count) {          lineStart = lineEnd;          lineEnd = lineStart + count - 1;        }        var startPos = clipCursorToContent(cm, new Pos(lineStart, 0));        var cursor = cm.getSearchCursor(query, startPos);        doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);      },      redo: CodeMirror.commands.redo,      undo: CodeMirror.commands.undo,      write: function(cm) {        if (CodeMirror.commands.save) {          // If a save command is defined, call it.          CodeMirror.commands.save(cm);        } else if (cm.save) {          // Saves to text area if no save command is defined and cm.save() is available.          cm.save();        }      },      nohlsearch: function(cm) {        clearSearchHighlight(cm);      },      yank: function (cm) {        var cur = copyCursor(cm.getCursor());        var line = cur.line;        var lineText = cm.getLine(line);        vimGlobalState.registerController.pushText(          '0', 'yank', lineText, true, true);      },      delmarks: function(cm, params) {        if (!params.argString || !trim(params.argString)) {          showConfirm(cm, 'Argument required');          return;        }        var state = cm.state.vim;        var stream = new CodeMirror.StringStream(trim(params.argString));        while (!stream.eol()) {          stream.eatSpace();          // Record the streams position at the beginning of the loop for use          // in error messages.          var count = stream.pos;          if (!stream.match(/[a-zA-Z]/, false)) {            showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));            return;          }          var sym = stream.next();          // Check if this symbol is part of a range          if (stream.match('-', true)) {            // This symbol is part of a range.            // The range must terminate at an alphabetic character.            if (!stream.match(/[a-zA-Z]/, false)) {              showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));              return;            }            var startMark = sym;            var finishMark = stream.next();            // The range must terminate at an alphabetic character which            // shares the same case as the start of the range.            if (isLowerCase(startMark) && isLowerCase(finishMark) ||                isUpperCase(startMark) && isUpperCase(finishMark)) {              var start = startMark.charCodeAt(0);              var finish = finishMark.charCodeAt(0);              if (start >= finish) {                showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));                return;              }              // Because marks are always ASCII values, and we have              // determined that they are the same case, we can use              // their char codes to iterate through the defined range.              for (var j = 0; j <= finish - start; j++) {                var mark = String.fromCharCode(start + j);                delete state.marks[mark];              }            } else {              showConfirm(cm, 'Invalid argument: ' + startMark + '-');              return;            }          } else {            // This symbol is a valid mark, and is not part of a range.            delete state.marks[sym];          }        }      }    };    var exCommandDispatcher = new ExCommandDispatcher();    /**    * @param {CodeMirror} cm CodeMirror instance we are in.    * @param {boolean} confirm Whether to confirm each replace.    * @param {Cursor} lineStart Line to start replacing from.    * @param {Cursor} lineEnd Line to stop replacing at.    * @param {RegExp} query Query for performing matches with.    * @param {string} replaceWith Text to replace matches with. May contain $1,    *     $2, etc for replacing captured groups using JavaScript replace.    * @param {function()} callback A callback for when the replace is done.    */    function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,        replaceWith, callback) {      // Set up all the functions.      cm.state.vim.exMode = true;      var done = false;      var lastPos, modifiedLineNumber, joined;      function replaceAll() {        cm.operation(function() {          while (!done) {            replace();            next();          }          stop();        });      }      function replace() {        var text = cm.getRange(searchCursor.from(), searchCursor.to());        var newText = text.replace(query, replaceWith);        var unmodifiedLineNumber = searchCursor.to().line;        searchCursor.replace(newText);        modifiedLineNumber = searchCursor.to().line;        lineEnd += modifiedLineNumber - unmodifiedLineNumber;        joined = modifiedLineNumber < unmodifiedLineNumber;      }      function findNextValidMatch() {        var lastMatchTo = lastPos && copyCursor(searchCursor.to());        var match = searchCursor.findNext();        if (match && !match[0] && lastMatchTo && cursorEqual(searchCursor.from(), lastMatchTo)) {          match = searchCursor.findNext();        }        return match;      }      function next() {        // The below only loops to skip over multiple occurrences on the same        // line when 'global' is not true.        while(findNextValidMatch() &&              isInRange(searchCursor.from(), lineStart, lineEnd)) {          if (!global && searchCursor.from().line == modifiedLineNumber && !joined) {            continue;          }          cm.scrollIntoView(searchCursor.from(), 30);          cm.setSelection(searchCursor.from(), searchCursor.to());          lastPos = searchCursor.from();          done = false;          return;        }        done = true;      }      function stop(close) {        if (close) { close(); }        cm.focus();        if (lastPos) {          cm.setCursor(lastPos);          var vim = cm.state.vim;          vim.exMode = false;          vim.lastHPos = vim.lastHSPos = lastPos.ch;        }        if (callback) { callback(); }      }      function onPromptKeyDown(e, _value, close) {        // Swallow all keys.        CodeMirror.e_stop(e);        var keyName = CodeMirror.keyName(e);        switch (keyName) {          case 'Y':            replace(); next(); break;          case 'N':            next(); break;          case 'A':            // replaceAll contains a call to close of its own. We don't want it            // to fire too early or multiple times.            var savedCallback = callback;            callback = undefined;            cm.operation(replaceAll);            callback = savedCallback;            break;          case 'L':            replace();            // fall through and exit.          case 'Q':          case 'Esc':          case 'Ctrl-C':          case 'Ctrl-[':            stop(close);            break;        }        if (done) { stop(close); }        return true;      }      // Actually do replace.      next();      if (done) {        showConfirm(cm, 'No matches for ' + query.source);        return;      }      if (!confirm) {        replaceAll();        if (callback) { callback(); }        return;      }      showPrompt(cm, {        prefix: dom('span', 'replace with ', dom('strong', replaceWith), ' (y/n/a/q/l)'),        onKeyDown: onPromptKeyDown      });    }    CodeMirror.keyMap.vim = {      attach: attachVimMap,      detach: detachVimMap,      call: cmKey    };    function exitInsertMode(cm) {      var vim = cm.state.vim;      var macroModeState = vimGlobalState.macroModeState;      var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');      var isPlaying = macroModeState.isPlaying;      var lastChange = macroModeState.lastInsertModeChanges;      if (!isPlaying) {        cm.off('change', onChange);        CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);      }      if (!isPlaying && vim.insertModeRepeat > 1) {        // Perform insert mode repeat for commands like 3,a and 3,o.        repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,            true /** repeatForInsert */);        vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;      }      delete vim.insertModeRepeat;      vim.insertMode = false;      cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);      cm.setOption('keyMap', 'vim');      cm.setOption('disableInput', true);      cm.toggleOverwrite(false); // exit replace mode if we were in it.      // update the ". register before exiting insert mode      insertModeChangeRegister.setText(lastChange.changes.join(''));      CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});      if (macroModeState.isRecording) {        logInsertModeChange(macroModeState);      }    }    function _mapCommand(command) {      defaultKeymap.unshift(command);    }    function mapCommand(keys, type, name, args, extra) {      var command = {keys: keys, type: type};      command[type] = name;      command[type + "Args"] = args;      for (var key in extra)        command[key] = extra[key];      _mapCommand(command);    }    // The timeout in milliseconds for the two-character ESC keymap should be    // adjusted according to your typing speed to prevent false positives.    defineOption('insertModeEscKeysTimeout', 200, 'number');    CodeMirror.keyMap['vim-insert'] = {      // TODO: override navigation keys so that Esc will cancel automatic      // indentation from o, O, i_<CR>      fallthrough: ['default'],      attach: attachVimMap,      detach: detachVimMap,      call: cmKey    };    CodeMirror.keyMap['vim-replace'] = {      'Backspace': 'goCharLeft',      fallthrough: ['vim-insert'],      attach: attachVimMap,      detach: detachVimMap,      call: cmKey    };    function executeMacroRegister(cm, vim, macroModeState, registerName) {      var register = vimGlobalState.registerController.getRegister(registerName);      if (registerName == ':') {        // Read-only register containing last Ex command.        if (register.keyBuffer[0]) {          exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);        }        macroModeState.isPlaying = false;        return;      }      var keyBuffer = register.keyBuffer;      var imc = 0;      macroModeState.isPlaying = true;      macroModeState.replaySearchQueries = register.searchQueries.slice(0);      for (var i = 0; i < keyBuffer.length; i++) {        var text = keyBuffer[i];        var match, key;        while (text) {          // Pull off one command key, which is either a single character          // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.          match = (/<\w+-.+?>|<\w+>|./).exec(text);          key = match[0];          text = text.substring(match.index + key.length);          vimApi.handleKey(cm, key, 'macro');          if (vim.insertMode) {            var changes = register.insertModeChanges[imc++].changes;            vimGlobalState.macroModeState.lastInsertModeChanges.changes =                changes;            repeatInsertModeChanges(cm, changes, 1);            exitInsertMode(cm);          }        }      }      macroModeState.isPlaying = false;    }    function logKey(macroModeState, key) {      if (macroModeState.isPlaying) { return; }      var registerName = macroModeState.latestRegister;      var register = vimGlobalState.registerController.getRegister(registerName);      if (register) {        register.pushText(key);      }    }    function logInsertModeChange(macroModeState) {      if (macroModeState.isPlaying) { return; }      var registerName = macroModeState.latestRegister;      var register = vimGlobalState.registerController.getRegister(registerName);      if (register && register.pushInsertModeChanges) {        register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);      }    }    function logSearchQuery(macroModeState, query) {      if (macroModeState.isPlaying) { return; }      var registerName = macroModeState.latestRegister;      var register = vimGlobalState.registerController.getRegister(registerName);      if (register && register.pushSearchQuery) {        register.pushSearchQuery(query);      }    }    /**     * Listens for changes made in insert mode.     * Should only be active in insert mode.     */    function onChange(cm, changeObj) {      var macroModeState = vimGlobalState.macroModeState;      var lastChange = macroModeState.lastInsertModeChanges;      if (!macroModeState.isPlaying) {        while(changeObj) {          lastChange.expectCursorActivityForChange = true;          if (lastChange.ignoreCount > 1) {            lastChange.ignoreCount--;          } else if (changeObj.origin == '+input' || changeObj.origin == 'paste'              || changeObj.origin === undefined /* only in testing */) {            var selectionCount = cm.listSelections().length;            if (selectionCount > 1)              lastChange.ignoreCount = selectionCount;            var text = changeObj.text.join('\n');            if (lastChange.maybeReset) {              lastChange.changes = [];              lastChange.maybeReset = false;            }            if (text) {              if (cm.state.overwrite && !/\n/.test(text)) {                lastChange.changes.push([text]);              } else {                lastChange.changes.push(text);              }            }          }          // Change objects may be chained with next.          changeObj = changeObj.next;        }      }    }    /**    * Listens for any kind of cursor activity on CodeMirror.    */    function onCursorActivity(cm) {      var vim = cm.state.vim;      if (vim.insertMode) {        // Tracking cursor activity in insert mode (for macro support).        var macroModeState = vimGlobalState.macroModeState;        if (macroModeState.isPlaying) { return; }        var lastChange = macroModeState.lastInsertModeChanges;        if (lastChange.expectCursorActivityForChange) {          lastChange.expectCursorActivityForChange = false;        } else {          // Cursor moved outside the context of an edit. Reset the change.          lastChange.maybeReset = true;        }      } else if (!cm.curOp.isVimOp) {        handleExternalSelection(cm, vim);      }    }    function handleExternalSelection(cm, vim) {      var anchor = cm.getCursor('anchor');      var head = cm.getCursor('head');      // Enter or exit visual mode to match mouse selection.      if (vim.visualMode && !cm.somethingSelected()) {        exitVisualMode(cm, false);      } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {        vim.visualMode = true;        vim.visualLine = false;        CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});      }      if (vim.visualMode) {        // Bind CodeMirror selection model to vim selection model.        // Mouse selections are considered visual characterwise.        var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;        var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;        head = offsetCursor(head, 0, headOffset);        anchor = offsetCursor(anchor, 0, anchorOffset);        vim.sel = {          anchor: anchor,          head: head        };        updateMark(cm, vim, '<', cursorMin(head, anchor));        updateMark(cm, vim, '>', cursorMax(head, anchor));      } else if (!vim.insertMode) {        // Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.        vim.lastHPos = cm.getCursor().ch;      }    }    /** Wrapper for special keys pressed in insert mode */    function InsertModeKey(keyName) {      this.keyName = keyName;    }    /**    * Handles raw key down events from the text area.    * - Should only be active in insert mode.    * - For recording deletes in insert mode.    */    function onKeyEventTargetKeyDown(e) {      var macroModeState = vimGlobalState.macroModeState;      var lastChange = macroModeState.lastInsertModeChanges;      var keyName = CodeMirror.keyName(e);      if (!keyName) { return; }      function onKeyFound() {        if (lastChange.maybeReset) {          lastChange.changes = [];          lastChange.maybeReset = false;        }        lastChange.changes.push(new InsertModeKey(keyName));        return true;      }      if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {        CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);      }    }    /**     * Repeats the last edit, which includes exactly 1 command and at most 1     * insert. Operator and motion commands are read from lastEditInputState,     * while action commands are read from lastEditActionCommand.     *     * If repeatForInsert is true, then the function was called by     * exitInsertMode to repeat the insert mode changes the user just made. The     * corresponding enterInsertMode call was made with a count.     */    function repeatLastEdit(cm, vim, repeat, repeatForInsert) {      var macroModeState = vimGlobalState.macroModeState;      macroModeState.isPlaying = true;      var isAction = !!vim.lastEditActionCommand;      var cachedInputState = vim.inputState;      function repeatCommand() {        if (isAction) {          commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);        } else {          commandDispatcher.evalInput(cm, vim);        }      }      function repeatInsert(repeat) {        if (macroModeState.lastInsertModeChanges.changes.length > 0) {          // For some reason, repeat cw in desktop VIM does not repeat          // insert mode changes. Will conform to that behavior.          repeat = !vim.lastEditActionCommand ? 1 : repeat;          var changeObject = macroModeState.lastInsertModeChanges;          repeatInsertModeChanges(cm, changeObject.changes, repeat);        }      }      vim.inputState = vim.lastEditInputState;      if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {        // o and O repeat have to be interlaced with insert repeats so that the        // insertions appear on separate lines instead of the last line.        for (var i = 0; i < repeat; i++) {          repeatCommand();          repeatInsert(1);        }      } else {        if (!repeatForInsert) {          // Hack to get the cursor to end up at the right place. If I is          // repeated in insert mode repeat, cursor will be 1 insert          // change set left of where it should be.          repeatCommand();        }        repeatInsert(repeat);      }      vim.inputState = cachedInputState;      if (vim.insertMode && !repeatForInsert) {        // Don't exit insert mode twice. If repeatForInsert is set, then we        // were called by an exitInsertMode call lower on the stack.        exitInsertMode(cm);      }      macroModeState.isPlaying = false;    }    function repeatInsertModeChanges(cm, changes, repeat) {      function keyHandler(binding) {        if (typeof binding == 'string') {          CodeMirror.commands[binding](cm);        } else {          binding(cm);        }        return true;      }      var head = cm.getCursor('head');      var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock;      if (visualBlock) {        // Set up block selection again for repeating the changes.        selectForInsert(cm, head, visualBlock + 1);        repeat = cm.listSelections().length;        cm.setCursor(head);      }      for (var i = 0; i < repeat; i++) {        if (visualBlock) {          cm.setCursor(offsetCursor(head, i, 0));        }        for (var j = 0; j < changes.length; j++) {          var change = changes[j];          if (change instanceof InsertModeKey) {            CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);          } else if (typeof change == "string") {            cm.replaceSelection(change);          } else {            var start = cm.getCursor();            var end = offsetCursor(start, 0, change[0].length);            cm.replaceRange(change[0], start, end);            cm.setCursor(end);          }        }      }      if (visualBlock) {        cm.setCursor(offsetCursor(head, 0, 1));      }    }    resetVimGlobalState();    return vimApi;  };  // Initialize Vim and make it available as an API.  CodeMirror.Vim = Vim();});
 |