c051c769f84d00d933e12224dfaebd7b.js 262 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090
  1. ace.define("ace/ext/hardwrap",["require","exports","module","ace/range","ace/editor","ace/config"], function(require, exports, module){"use strict";
  2. var Range = require("../range").Range;
  3. function hardWrap(editor, options) {
  4. var max = options.column || editor.getOption("printMarginColumn");
  5. var allowMerge = options.allowMerge != false;
  6. var row = Math.min(options.startRow, options.endRow);
  7. var endRow = Math.max(options.startRow, options.endRow);
  8. var session = editor.session;
  9. while (row <= endRow) {
  10. var line = session.getLine(row);
  11. if (line.length > max) {
  12. var space = findSpace(line, max, 5);
  13. if (space) {
  14. var indentation = /^\s*/.exec(line)[0];
  15. session.replace(new Range(row, space.start, row, space.end), "\n" + indentation);
  16. }
  17. endRow++;
  18. }
  19. else if (allowMerge && /\S/.test(line) && row != endRow) {
  20. var nextLine = session.getLine(row + 1);
  21. if (nextLine && /\S/.test(nextLine)) {
  22. var trimmedLine = line.replace(/\s+$/, "");
  23. var trimmedNextLine = nextLine.replace(/^\s+/, "");
  24. var mergedLine = trimmedLine + " " + trimmedNextLine;
  25. var space = findSpace(mergedLine, max, 5);
  26. if (space && space.start > trimmedLine.length || mergedLine.length < max) {
  27. var replaceRange = new Range(row, trimmedLine.length, row + 1, nextLine.length - trimmedNextLine.length);
  28. session.replace(replaceRange, " ");
  29. row--;
  30. endRow--;
  31. }
  32. else if (trimmedLine.length < line.length) {
  33. session.remove(new Range(row, trimmedLine.length, row, line.length));
  34. }
  35. }
  36. }
  37. row++;
  38. }
  39. function findSpace(line, max, min) {
  40. if (line.length < max)
  41. return;
  42. var before = line.slice(0, max);
  43. var after = line.slice(max);
  44. var spaceAfter = /^(?:(\s+)|(\S+)(\s+))/.exec(after);
  45. var spaceBefore = /(?:(\s+)|(\s+)(\S+))$/.exec(before);
  46. var start = 0;
  47. var end = 0;
  48. if (spaceBefore && !spaceBefore[2]) {
  49. start = max - spaceBefore[1].length;
  50. end = max;
  51. }
  52. if (spaceAfter && !spaceAfter[2]) {
  53. if (!start)
  54. start = max;
  55. end = max + spaceAfter[1].length;
  56. }
  57. if (start) {
  58. return {
  59. start: start,
  60. end: end
  61. };
  62. }
  63. if (spaceBefore && spaceBefore[2] && spaceBefore.index > min) {
  64. return {
  65. start: spaceBefore.index,
  66. end: spaceBefore.index + spaceBefore[2].length
  67. };
  68. }
  69. if (spaceAfter && spaceAfter[2]) {
  70. start = max + spaceAfter[2].length;
  71. return {
  72. start: start,
  73. end: start + spaceAfter[3].length
  74. };
  75. }
  76. }
  77. }
  78. function wrapAfterInput(e) {
  79. if (e.command.name == "insertstring" && /\S/.test(e.args)) {
  80. var editor = e.editor;
  81. var cursor = editor.selection.cursor;
  82. if (cursor.column <= editor.renderer.$printMarginColumn)
  83. return;
  84. var lastDelta = editor.session.$undoManager.$lastDelta;
  85. hardWrap(editor, {
  86. startRow: cursor.row, endRow: cursor.row,
  87. allowMerge: false
  88. });
  89. if (lastDelta != editor.session.$undoManager.$lastDelta)
  90. editor.session.markUndoGroup();
  91. }
  92. }
  93. var Editor = require("../editor").Editor;
  94. require("../config").defineOptions(Editor.prototype, "editor", {
  95. hardWrap: {
  96. set: function (val) {
  97. if (val) {
  98. this.commands.on("afterExec", wrapAfterInput);
  99. }
  100. else {
  101. this.commands.off("afterExec", wrapAfterInput);
  102. }
  103. },
  104. value: false
  105. }
  106. });
  107. exports.hardWrap = hardWrap;
  108. });
  109. ace.define("ace/keyboard/vim",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/dom","ace/lib/oop","ace/lib/keys","ace/lib/event","ace/search","ace/lib/useragent","ace/search_highlight","ace/commands/multi_select_commands","ace/mode/text","ace/ext/hardwrap","ace/multi_select"], function(require, exports, module){// CodeMirror, copyright (c) by Marijn Haverbeke and others
  110. 'use strict';
  111. function log() {
  112. var d = "";
  113. function format(p) {
  114. if (typeof p != "object")
  115. return p + "";
  116. if ("line" in p) {
  117. return p.line + ":" + p.ch;
  118. }
  119. if ("anchor" in p) {
  120. return format(p.anchor) + "->" + format(p.head);
  121. }
  122. if (Array.isArray(p))
  123. return "[" + p.map(function (x) {
  124. return format(x);
  125. }) + "]";
  126. return JSON.stringify(p);
  127. }
  128. for (var i = 0; i < arguments.length; i++) {
  129. var p = arguments[i];
  130. var f = format(p);
  131. d += f + " ";
  132. }
  133. console.log(d);
  134. }
  135. var Range = require("../range").Range;
  136. var EventEmitter = require("../lib/event_emitter").EventEmitter;
  137. var domLib = require("../lib/dom");
  138. var oop = require("../lib/oop");
  139. var KEYS = require("../lib/keys");
  140. var event = require("../lib/event");
  141. var Search = require("../search").Search;
  142. var useragent = require("../lib/useragent");
  143. var SearchHighlight = require("../search_highlight").SearchHighlight;
  144. var multiSelectCommands = require("../commands/multi_select_commands");
  145. var TextModeTokenRe = require("../mode/text").Mode.prototype.tokenRe;
  146. var hardWrap = require("../ext/hardwrap").hardWrap;
  147. require("../multi_select");
  148. var CodeMirror = function (ace) {
  149. this.ace = ace;
  150. this.state = {};
  151. this.marks = {};
  152. this.options = {};
  153. this.$uid = 0;
  154. this.onChange = this.onChange.bind(this);
  155. this.onSelectionChange = this.onSelectionChange.bind(this);
  156. this.onBeforeEndOperation = this.onBeforeEndOperation.bind(this);
  157. this.ace.on('change', this.onChange);
  158. this.ace.on('changeSelection', this.onSelectionChange);
  159. this.ace.on('beforeEndOperation', this.onBeforeEndOperation);
  160. };
  161. CodeMirror.Pos = function (line, ch) {
  162. if (!(this instanceof Pos))
  163. return new Pos(line, ch);
  164. this.line = line;
  165. this.ch = ch;
  166. };
  167. CodeMirror.defineOption = function (name, val, setter) { };
  168. CodeMirror.commands = {
  169. redo: function (cm) { cm.ace.redo(); },
  170. undo: function (cm) { cm.ace.undo(); },
  171. newlineAndIndent: function (cm) { cm.ace.insert("\n"); },
  172. goLineLeft: function (cm) { cm.ace.selection.moveCursorLineStart(); },
  173. goLineRight: function (cm) { cm.ace.selection.moveCursorLineEnd(); }
  174. };
  175. CodeMirror.keyMap = {};
  176. CodeMirror.addClass = CodeMirror.rmClass = function () { };
  177. CodeMirror.e_stop = CodeMirror.e_preventDefault = event.stopEvent;
  178. CodeMirror.keyName = function (e) {
  179. var key = (KEYS[e.keyCode] || e.key || "");
  180. if (key.length == 1)
  181. key = key.toUpperCase();
  182. key = event.getModifierString(e).replace(/(^|-)\w/g, function (m) {
  183. return m.toUpperCase();
  184. }) + key;
  185. return key;
  186. };
  187. CodeMirror.keyMap['default'] = function (key) {
  188. return function (cm) {
  189. var cmd = cm.ace.commands.commandKeyBinding[key.toLowerCase()];
  190. return cmd && cm.ace.execCommand(cmd) !== false;
  191. };
  192. };
  193. CodeMirror.lookupKey = function lookupKey(key, map, handle) {
  194. if (!map)
  195. map = "default";
  196. if (typeof map == "string")
  197. map = CodeMirror.keyMap[map] || CodeMirror.keyMap['default'];
  198. var found = typeof map == "function" ? map(key) : map[key];
  199. if (found === false)
  200. return "nothing";
  201. if (found === "...")
  202. return "multi";
  203. if (found != null && handle(found))
  204. return "handled";
  205. if (map.fallthrough) {
  206. if (!Array.isArray(map.fallthrough))
  207. return lookupKey(key, map.fallthrough, handle);
  208. for (var i = 0; i < map.fallthrough.length; i++) {
  209. var result = lookupKey(key, map.fallthrough[i], handle);
  210. if (result)
  211. return result;
  212. }
  213. }
  214. };
  215. CodeMirror.findMatchingTag = function (cm, head) {
  216. return cm.findMatchingTag(head);
  217. };
  218. CodeMirror.findEnclosingTag = function (cm, head) {
  219. };
  220. CodeMirror.signal = function (o, name, e) { return o._signal(name, e); };
  221. CodeMirror.on = event.addListener;
  222. CodeMirror.off = event.removeListener;
  223. CodeMirror.isWordChar = function (ch) {
  224. if (ch < "\x7f")
  225. return /^\w$/.test(ch);
  226. TextModeTokenRe.lastIndex = 0;
  227. return TextModeTokenRe.test(ch);
  228. };
  229. (function () {
  230. oop.implement(CodeMirror.prototype, EventEmitter);
  231. this.destroy = function () {
  232. this.ace.off('change', this.onChange);
  233. this.ace.off('changeSelection', this.onSelectionChange);
  234. this.ace.off('beforeEndOperation', this.onBeforeEndOperation);
  235. this.removeOverlay();
  236. };
  237. this.virtualSelectionMode = function () {
  238. return this.ace.inVirtualSelectionMode && this.ace.selection.index;
  239. };
  240. this.onChange = function (delta) {
  241. if (this.$lineHandleChanges) {
  242. this.$lineHandleChanges.push(delta);
  243. }
  244. var change = { text: delta.action[0] == 'i' ? delta.lines : [] };
  245. var curOp = this.curOp = this.curOp || {};
  246. if (!curOp.changeHandlers)
  247. curOp.changeHandlers = this._eventRegistry["change"] && this._eventRegistry["change"].slice();
  248. if (!curOp.lastChange) {
  249. curOp.lastChange = curOp.change = change;
  250. }
  251. else {
  252. curOp.lastChange.next = curOp.lastChange = change;
  253. }
  254. this.$updateMarkers(delta);
  255. };
  256. this.onSelectionChange = function () {
  257. var curOp = this.curOp = this.curOp || {};
  258. if (!curOp.cursorActivityHandlers)
  259. curOp.cursorActivityHandlers = this._eventRegistry["cursorActivity"] && this._eventRegistry["cursorActivity"].slice();
  260. this.curOp.cursorActivity = true;
  261. if (this.ace.inMultiSelectMode) {
  262. this.ace.keyBinding.removeKeyboardHandler(multiSelectCommands.keyboardHandler);
  263. }
  264. };
  265. this.operation = function (fn, force) {
  266. if (!force && this.curOp || force && this.curOp && this.curOp.force) {
  267. return fn();
  268. }
  269. if (force || !this.ace.curOp) {
  270. if (this.curOp)
  271. this.onBeforeEndOperation();
  272. }
  273. if (!this.ace.curOp) {
  274. var prevOp = this.ace.prevOp;
  275. this.ace.startOperation({
  276. command: { name: "vim", scrollIntoView: "cursor" }
  277. });
  278. }
  279. var curOp = this.curOp = this.curOp || {};
  280. this.curOp.force = force;
  281. var result = fn();
  282. if (this.ace.curOp && this.ace.curOp.command.name == "vim") {
  283. if (this.state.dialog)
  284. this.ace.curOp.command.scrollIntoView = this.ace.curOp.vimDialogScroll;
  285. this.ace.endOperation();
  286. if (!curOp.cursorActivity && !curOp.lastChange && prevOp)
  287. this.ace.prevOp = prevOp;
  288. }
  289. if (force || !this.ace.curOp) {
  290. if (this.curOp)
  291. this.onBeforeEndOperation();
  292. }
  293. return result;
  294. };
  295. this.onBeforeEndOperation = function () {
  296. var op = this.curOp;
  297. if (op) {
  298. if (op.change) {
  299. this.signal("change", op.change, op);
  300. }
  301. if (op && op.cursorActivity) {
  302. this.signal("cursorActivity", null, op);
  303. }
  304. this.curOp = null;
  305. }
  306. };
  307. this.signal = function (eventName, e, handlers) {
  308. var listeners = handlers ? handlers[eventName + "Handlers"]
  309. : (this._eventRegistry || {})[eventName];
  310. if (!listeners)
  311. return;
  312. listeners = listeners.slice();
  313. for (var i = 0; i < listeners.length; i++)
  314. listeners[i](this, e);
  315. };
  316. this.firstLine = function () { return 0; };
  317. this.lastLine = function () { return this.ace.session.getLength() - 1; };
  318. this.lineCount = function () { return this.ace.session.getLength(); };
  319. this.setCursor = function (line, ch) {
  320. if (typeof line === 'object') {
  321. ch = line.ch;
  322. line = line.line;
  323. }
  324. var shouldScroll = !this.curOp && !this.ace.inVirtualSelectionMode;
  325. if (!this.ace.inVirtualSelectionMode)
  326. this.ace.exitMultiSelectMode();
  327. this.ace.session.unfold({ row: line, column: ch });
  328. this.ace.selection.moveTo(line, ch);
  329. if (shouldScroll) {
  330. this.ace.renderer.scrollCursorIntoView();
  331. this.ace.endOperation();
  332. }
  333. };
  334. this.getCursor = function (p) {
  335. var sel = this.ace.selection;
  336. var pos = p == 'anchor' ? (sel.isEmpty() ? sel.lead : sel.anchor) :
  337. p == 'head' || !p ? sel.lead : sel.getRange()[p];
  338. return toCmPos(pos);
  339. };
  340. this.listSelections = function (p) {
  341. var ranges = this.ace.multiSelect.rangeList.ranges;
  342. if (!ranges.length || this.ace.inVirtualSelectionMode)
  343. return [{ anchor: this.getCursor('anchor'), head: this.getCursor('head') }];
  344. return ranges.map(function (r) {
  345. return {
  346. anchor: this.clipPos(toCmPos(r.cursor == r.end ? r.start : r.end)),
  347. head: this.clipPos(toCmPos(r.cursor))
  348. };
  349. }, this);
  350. };
  351. this.setSelections = function (p, primIndex) {
  352. var sel = this.ace.multiSelect;
  353. var ranges = p.map(function (x) {
  354. var anchor = toAcePos(x.anchor);
  355. var head = toAcePos(x.head);
  356. var r = Range.comparePoints(anchor, head) < 0
  357. ? new Range.fromPoints(anchor, head)
  358. : new Range.fromPoints(head, anchor);
  359. r.cursor = Range.comparePoints(r.start, head) ? r.end : r.start;
  360. return r;
  361. });
  362. if (this.ace.inVirtualSelectionMode) {
  363. this.ace.selection.fromOrientedRange(ranges[0]);
  364. return;
  365. }
  366. if (!primIndex) {
  367. ranges = ranges.reverse();
  368. }
  369. else if (ranges[primIndex]) {
  370. ranges.push(ranges.splice(primIndex, 1)[0]);
  371. }
  372. sel.toSingleRange(ranges[0].clone());
  373. var session = this.ace.session;
  374. for (var i = 0; i < ranges.length; i++) {
  375. var range = session.$clipRangeToDocument(ranges[i]); // todo why ace doesn't do this?
  376. sel.addRange(range);
  377. }
  378. };
  379. this.setSelection = function (a, h, options) {
  380. var sel = this.ace.selection;
  381. sel.moveTo(a.line, a.ch);
  382. sel.selectTo(h.line, h.ch);
  383. if (options && options.origin == '*mouse') {
  384. this.onBeforeEndOperation();
  385. }
  386. };
  387. this.somethingSelected = function (p) {
  388. return !this.ace.selection.isEmpty();
  389. };
  390. this.clipPos = function (p) {
  391. var pos = this.ace.session.$clipPositionToDocument(p.line, p.ch);
  392. return toCmPos(pos);
  393. };
  394. this.foldCode = function (pos) {
  395. this.ace.session.$toggleFoldWidget(pos.line, {});
  396. };
  397. this.markText = function (cursor) {
  398. return { clear: function () { }, find: function () { } };
  399. };
  400. this.$updateMarkers = function (delta) {
  401. var isInsert = delta.action == "insert";
  402. var start = delta.start;
  403. var end = delta.end;
  404. var rowShift = (end.row - start.row) * (isInsert ? 1 : -1);
  405. var colShift = (end.column - start.column) * (isInsert ? 1 : -1);
  406. if (isInsert)
  407. end = start;
  408. for (var i in this.marks) {
  409. var point = this.marks[i];
  410. var cmp = Range.comparePoints(point, start);
  411. if (cmp < 0) {
  412. continue; // delta starts after the range
  413. }
  414. if (cmp === 0) {
  415. if (isInsert) {
  416. if (!point.$insertRight) {
  417. cmp = 1;
  418. }
  419. else if (point.bias == 1) {
  420. cmp = 1;
  421. }
  422. else {
  423. point.bias = -1;
  424. continue;
  425. }
  426. }
  427. }
  428. var cmp2 = isInsert ? cmp : Range.comparePoints(point, end);
  429. if (cmp2 > 0) {
  430. point.row += rowShift;
  431. point.column += point.row == end.row ? colShift : 0;
  432. continue;
  433. }
  434. if (!isInsert && cmp2 <= 0) {
  435. point.row = start.row;
  436. point.column = start.column;
  437. if (cmp2 === 0)
  438. point.bias = 1;
  439. }
  440. }
  441. };
  442. var Marker = function (cm, id, row, column) {
  443. this.cm = cm;
  444. this.id = id;
  445. this.row = row;
  446. this.column = column;
  447. cm.marks[this.id] = this;
  448. };
  449. Marker.prototype.clear = function () { delete this.cm.marks[this.id]; };
  450. Marker.prototype.find = function () { return toCmPos(this); };
  451. this.setBookmark = function (cursor, options) {
  452. var bm = new Marker(this, this.$uid++, cursor.line, cursor.ch);
  453. if (!options || !options.insertLeft)
  454. bm.$insertRight = true;
  455. this.marks[bm.id] = bm;
  456. return bm;
  457. };
  458. this.moveH = function (increment, unit) {
  459. if (unit == 'char') {
  460. var sel = this.ace.selection;
  461. sel.clearSelection();
  462. sel.moveCursorBy(0, increment);
  463. }
  464. };
  465. this.findPosV = function (start, amount, unit, goalColumn) {
  466. if (unit == 'page') {
  467. var renderer = this.ace.renderer;
  468. var config = renderer.layerConfig;
  469. amount = amount * Math.floor(config.height / config.lineHeight);
  470. unit = 'line';
  471. }
  472. if (unit == 'line') {
  473. var screenPos = this.ace.session.documentToScreenPosition(start.line, start.ch);
  474. if (goalColumn != null)
  475. screenPos.column = goalColumn;
  476. screenPos.row += amount;
  477. screenPos.row = Math.min(Math.max(0, screenPos.row), this.ace.session.getScreenLength() - 1);
  478. var pos = this.ace.session.screenToDocumentPosition(screenPos.row, screenPos.column);
  479. return toCmPos(pos);
  480. }
  481. else {
  482. debugger;
  483. }
  484. };
  485. this.charCoords = function (pos, mode) {
  486. if (mode == 'div' || !mode) {
  487. var sc = this.ace.session.documentToScreenPosition(pos.line, pos.ch);
  488. return { left: sc.column, top: sc.row };
  489. }
  490. if (mode == 'local') {
  491. var renderer = this.ace.renderer;
  492. var sc = this.ace.session.documentToScreenPosition(pos.line, pos.ch);
  493. var lh = renderer.layerConfig.lineHeight;
  494. var cw = renderer.layerConfig.characterWidth;
  495. var top = lh * sc.row;
  496. return { left: sc.column * cw, top: top, bottom: top + lh };
  497. }
  498. };
  499. this.coordsChar = function (pos, mode) {
  500. var renderer = this.ace.renderer;
  501. if (mode == 'local') {
  502. var row = Math.max(0, Math.floor(pos.top / renderer.lineHeight));
  503. var col = Math.max(0, Math.floor(pos.left / renderer.characterWidth));
  504. var ch = renderer.session.screenToDocumentPosition(row, col);
  505. return toCmPos(ch);
  506. }
  507. else if (mode == 'div') {
  508. throw "not implemented";
  509. }
  510. };
  511. this.getSearchCursor = function (query, pos, caseFold) {
  512. var caseSensitive = false;
  513. var isRegexp = false;
  514. if (query instanceof RegExp && !query.global) {
  515. caseSensitive = !query.ignoreCase;
  516. query = query.source;
  517. isRegexp = true;
  518. }
  519. if (query == "\\n") {
  520. query = "\n";
  521. isRegexp = false;
  522. }
  523. var search = new Search();
  524. if (pos.ch == undefined)
  525. pos.ch = Number.MAX_VALUE;
  526. var acePos = { row: pos.line, column: pos.ch };
  527. var cm = this;
  528. var last = null;
  529. return {
  530. findNext: function () { return this.find(false); },
  531. findPrevious: function () { return this.find(true); },
  532. find: function (back) {
  533. search.setOptions({
  534. needle: query,
  535. caseSensitive: caseSensitive,
  536. wrap: false,
  537. backwards: back,
  538. regExp: isRegexp,
  539. start: last || acePos
  540. });
  541. var range = search.find(cm.ace.session);
  542. last = range;
  543. return last && [!last.isEmpty()];
  544. },
  545. from: function () { return last && toCmPos(last.start); },
  546. to: function () { return last && toCmPos(last.end); },
  547. replace: function (text) {
  548. if (last) {
  549. last.end = cm.ace.session.doc.replace(last, text);
  550. }
  551. }
  552. };
  553. };
  554. this.scrollTo = function (x, y) {
  555. var renderer = this.ace.renderer;
  556. var config = renderer.layerConfig;
  557. var maxHeight = config.maxHeight;
  558. maxHeight -= (renderer.$size.scrollerHeight - renderer.lineHeight) * renderer.$scrollPastEnd;
  559. if (y != null)
  560. this.ace.session.setScrollTop(Math.max(0, Math.min(y, maxHeight)));
  561. if (x != null)
  562. this.ace.session.setScrollLeft(Math.max(0, Math.min(x, config.width)));
  563. };
  564. this.scrollInfo = function () { return 0; };
  565. this.scrollIntoView = function (pos, margin) {
  566. if (pos) {
  567. var renderer = this.ace.renderer;
  568. var viewMargin = { "top": 0, "bottom": margin };
  569. renderer.scrollCursorIntoView(toAcePos(pos), (renderer.lineHeight * 2) / renderer.$size.scrollerHeight, viewMargin);
  570. }
  571. };
  572. this.getLine = function (row) { return this.ace.session.getLine(row); };
  573. this.getRange = function (s, e) {
  574. return this.ace.session.getTextRange(new Range(s.line, s.ch, e.line, e.ch));
  575. };
  576. this.replaceRange = function (text, s, e) {
  577. if (!e)
  578. e = s;
  579. var range = new Range(s.line, s.ch, e.line, e.ch);
  580. this.ace.session.$clipRangeToDocument(range);
  581. return this.ace.session.replace(range, text);
  582. };
  583. this.replaceSelection =
  584. this.replaceSelections = function (p) {
  585. var strings = Array.isArray(p) && p;
  586. var sel = this.ace.selection;
  587. if (this.ace.inVirtualSelectionMode) {
  588. this.ace.session.replace(sel.getRange(), strings ? p[0] || "" : p);
  589. return;
  590. }
  591. sel.inVirtualSelectionMode = true;
  592. var ranges = sel.rangeList.ranges;
  593. if (!ranges.length)
  594. ranges = [this.ace.multiSelect.getRange()];
  595. for (var i = ranges.length; i--;)
  596. this.ace.session.replace(ranges[i], strings ? p[i] || "" : p);
  597. sel.inVirtualSelectionMode = false;
  598. };
  599. this.getSelection = function () {
  600. return this.ace.getSelectedText();
  601. };
  602. this.getSelections = function () {
  603. return this.listSelections().map(function (x) {
  604. return this.getRange(x.anchor, x.head);
  605. }, this);
  606. };
  607. this.getInputField = function () {
  608. return this.ace.textInput.getElement();
  609. };
  610. this.getWrapperElement = function () {
  611. return this.ace.container;
  612. };
  613. var optMap = {
  614. indentWithTabs: "useSoftTabs",
  615. indentUnit: "tabSize",
  616. tabSize: "tabSize",
  617. firstLineNumber: "firstLineNumber",
  618. readOnly: "readOnly"
  619. };
  620. this.setOption = function (name, val) {
  621. this.state[name] = val;
  622. switch (name) {
  623. case 'indentWithTabs':
  624. name = optMap[name];
  625. val = !val;
  626. break;
  627. case 'keyMap':
  628. this.state.$keyMap = val;
  629. return;
  630. break;
  631. default:
  632. name = optMap[name];
  633. }
  634. if (name)
  635. this.ace.setOption(name, val);
  636. };
  637. this.getOption = function (name) {
  638. var val;
  639. var aceOpt = optMap[name];
  640. if (aceOpt)
  641. val = this.ace.getOption(aceOpt);
  642. switch (name) {
  643. case 'indentWithTabs':
  644. name = optMap[name];
  645. return !val;
  646. case 'keyMap':
  647. return this.state.$keyMap || 'vim';
  648. }
  649. return aceOpt ? val : this.state[name];
  650. };
  651. this.toggleOverwrite = function (on) {
  652. this.state.overwrite = on;
  653. return this.ace.setOverwrite(on);
  654. };
  655. this.addOverlay = function (o) {
  656. if (!this.$searchHighlight || !this.$searchHighlight.session) {
  657. var highlight = new SearchHighlight(null, "ace_highlight-marker", "text");
  658. var marker = this.ace.session.addDynamicMarker(highlight);
  659. highlight.id = marker.id;
  660. highlight.session = this.ace.session;
  661. highlight.destroy = function (o) {
  662. highlight.session.off("change", highlight.updateOnChange);
  663. highlight.session.off("changeEditor", highlight.destroy);
  664. highlight.session.removeMarker(highlight.id);
  665. highlight.session = null;
  666. };
  667. highlight.updateOnChange = function (delta) {
  668. var row = delta.start.row;
  669. if (row == delta.end.row)
  670. highlight.cache[row] = undefined;
  671. else
  672. highlight.cache.splice(row, highlight.cache.length);
  673. };
  674. highlight.session.on("changeEditor", highlight.destroy);
  675. highlight.session.on("change", highlight.updateOnChange);
  676. }
  677. var re = new RegExp(o.query.source, "gmi");
  678. this.$searchHighlight = o.highlight = highlight;
  679. this.$searchHighlight.setRegexp(re);
  680. this.ace.renderer.updateBackMarkers();
  681. };
  682. this.removeOverlay = function (o) {
  683. if (this.$searchHighlight && this.$searchHighlight.session) {
  684. this.$searchHighlight.destroy();
  685. }
  686. };
  687. this.getScrollInfo = function () {
  688. var renderer = this.ace.renderer;
  689. var config = renderer.layerConfig;
  690. return {
  691. left: renderer.scrollLeft,
  692. top: renderer.scrollTop,
  693. height: config.maxHeight,
  694. width: config.width,
  695. clientHeight: config.height,
  696. clientWidth: config.width
  697. };
  698. };
  699. this.getValue = function () {
  700. return this.ace.getValue();
  701. };
  702. this.setValue = function (v) {
  703. return this.ace.setValue(v, -1);
  704. };
  705. this.getTokenTypeAt = function (pos) {
  706. var token = this.ace.session.getTokenAt(pos.line, pos.ch);
  707. return token && /comment|string/.test(token.type) ? "string" : "";
  708. };
  709. this.findMatchingBracket = function (pos) {
  710. var m = this.ace.session.findMatchingBracket(toAcePos(pos));
  711. return { to: m && toCmPos(m) };
  712. };
  713. this.findMatchingTag = function (pos) {
  714. var m = this.ace.session.getMatchingTags(toAcePos(pos));
  715. if (!m)
  716. return;
  717. return {
  718. open: {
  719. from: toCmPos(m.openTag.start),
  720. to: toCmPos(m.openTag.end)
  721. },
  722. close: {
  723. from: toCmPos(m.closeTag.start),
  724. to: toCmPos(m.closeTag.end)
  725. }
  726. };
  727. };
  728. this.indentLine = function (line, method) {
  729. if (method === true)
  730. this.ace.session.indentRows(line, line, "\t");
  731. else if (method === false)
  732. this.ace.session.outdentRows(new Range(line, 0, line, 0));
  733. };
  734. this.indexFromPos = function (pos) {
  735. return this.ace.session.doc.positionToIndex(toAcePos(pos));
  736. };
  737. this.posFromIndex = function (index) {
  738. return toCmPos(this.ace.session.doc.indexToPosition(index));
  739. };
  740. this.focus = function (index) {
  741. return this.ace.textInput.focus();
  742. };
  743. this.blur = function (index) {
  744. return this.ace.blur();
  745. };
  746. this.defaultTextHeight = function (index) {
  747. return this.ace.renderer.layerConfig.lineHeight;
  748. };
  749. this.scanForBracket = function (pos, dir, _, options) {
  750. var re = options.bracketRegex.source;
  751. var tokenRe = /paren|text|operator|tag/;
  752. if (dir == 1) {
  753. var m = this.ace.session.$findClosingBracket(re.slice(1, 2), toAcePos(pos), tokenRe);
  754. }
  755. else {
  756. var m = this.ace.session.$findOpeningBracket(re.slice(-2, -1), { row: pos.line, column: pos.ch + 1 }, tokenRe);
  757. if (!m && options.bracketRegex && options.bracketRegex.test(this.getLine(pos.line)[pos.ch - 1])) {
  758. m = { row: pos.line, column: pos.ch - 1 };
  759. }
  760. }
  761. return m && { pos: toCmPos(m) };
  762. };
  763. this.refresh = function () {
  764. return this.ace.resize(true);
  765. };
  766. this.getMode = function () {
  767. return { name: this.getOption("mode") };
  768. };
  769. this.execCommand = function (name) {
  770. if (CodeMirror.commands.hasOwnProperty(name))
  771. return CodeMirror.commands[name](this);
  772. if (name == "indentAuto")
  773. return this.ace.execCommand("autoindent");
  774. console.log(name + " is not implemented");
  775. };
  776. this.getLineNumber = function (handle) {
  777. var deltas = this.$lineHandleChanges;
  778. if (!deltas)
  779. return null;
  780. var row = handle.row;
  781. for (var i = 0; i < deltas.length; i++) {
  782. var delta = deltas[i];
  783. if (delta.start.row != delta.end.row) {
  784. if (delta.action[0] == "i") {
  785. if (delta.start.row < row)
  786. row += delta.end.row - delta.start.row;
  787. }
  788. else {
  789. if (delta.start.row < row) {
  790. if (row < delta.end.row || row == delta.end.row && delta.start.column > 0) {
  791. return null;
  792. }
  793. row -= delta.end.row - delta.start.row;
  794. }
  795. }
  796. }
  797. }
  798. return row;
  799. };
  800. this.getLineHandle = function (row) {
  801. if (!this.$lineHandleChanges)
  802. this.$lineHandleChanges = [];
  803. return { text: this.ace.session.getLine(row), row: row };
  804. };
  805. this.releaseLineHandles = function () {
  806. this.$lineHandleChanges = undefined;
  807. };
  808. this.getLastEditEnd = function () {
  809. var undoManager = this.ace.session.$undoManager;
  810. if (undoManager && undoManager.$lastDelta)
  811. return toCmPos(undoManager.$lastDelta.end);
  812. };
  813. }).call(CodeMirror.prototype);
  814. function toAcePos(cmPos) {
  815. return { row: cmPos.line, column: cmPos.ch };
  816. }
  817. function toCmPos(acePos) {
  818. return new Pos(acePos.row, acePos.column);
  819. }
  820. var StringStream = CodeMirror.StringStream = function (string, tabSize) {
  821. this.pos = this.start = 0;
  822. this.string = string;
  823. this.tabSize = tabSize || 8;
  824. this.lastColumnPos = this.lastColumnValue = 0;
  825. this.lineStart = 0;
  826. };
  827. StringStream.prototype = {
  828. eol: function () { return this.pos >= this.string.length; },
  829. sol: function () { return this.pos == this.lineStart; },
  830. peek: function () { return this.string.charAt(this.pos) || undefined; },
  831. next: function () {
  832. if (this.pos < this.string.length)
  833. return this.string.charAt(this.pos++);
  834. },
  835. eat: function (match) {
  836. var ch = this.string.charAt(this.pos);
  837. if (typeof match == "string")
  838. var ok = ch == match;
  839. else
  840. var ok = ch && (match.test ? match.test(ch) : match(ch));
  841. if (ok) {
  842. ++this.pos;
  843. return ch;
  844. }
  845. },
  846. eatWhile: function (match) {
  847. var start = this.pos;
  848. while (this.eat(match)) { }
  849. return this.pos > start;
  850. },
  851. eatSpace: function () {
  852. var start = this.pos;
  853. while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
  854. ++this.pos;
  855. return this.pos > start;
  856. },
  857. skipToEnd: function () { this.pos = this.string.length; },
  858. skipTo: function (ch) {
  859. var found = this.string.indexOf(ch, this.pos);
  860. if (found > -1) {
  861. this.pos = found;
  862. return true;
  863. }
  864. },
  865. backUp: function (n) { this.pos -= n; },
  866. column: function () {
  867. throw "not implemented";
  868. },
  869. indentation: function () {
  870. throw "not implemented";
  871. },
  872. match: function (pattern, consume, caseInsensitive) {
  873. if (typeof pattern == "string") {
  874. var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
  875. var substr = this.string.substr(this.pos, pattern.length);
  876. if (cased(substr) == cased(pattern)) {
  877. if (consume !== false)
  878. this.pos += pattern.length;
  879. return true;
  880. }
  881. }
  882. else {
  883. var match = this.string.slice(this.pos).match(pattern);
  884. if (match && match.index > 0)
  885. return null;
  886. if (match && consume !== false)
  887. this.pos += match[0].length;
  888. return match;
  889. }
  890. },
  891. current: function () { return this.string.slice(this.start, this.pos); },
  892. hideFirstChars: function (n, inner) {
  893. this.lineStart += n;
  894. try {
  895. return inner();
  896. }
  897. finally {
  898. this.lineStart -= n;
  899. }
  900. }
  901. };
  902. CodeMirror.defineExtension = function (name, fn) {
  903. CodeMirror.prototype[name] = fn;
  904. };
  905. domLib.importCssString(".normal-mode .ace_cursor{\n border: none;\n background-color: rgba(255,0,0,0.5);\n}\n.normal-mode .ace_hidden-cursors .ace_cursor{\n background-color: transparent;\n border: 1px solid red;\n opacity: 0.7\n}\n.ace_dialog {\n position: absolute;\n left: 0; right: 0;\n background: inherit;\n z-index: 15;\n padding: .1em .8em;\n overflow: hidden;\n color: inherit;\n}\n.ace_dialog-top {\n border-bottom: 1px solid #444;\n top: 0;\n}\n.ace_dialog-bottom {\n border-top: 1px solid #444;\n bottom: 0;\n}\n.ace_dialog input {\n border: none;\n outline: none;\n background: transparent;\n width: 20em;\n color: inherit;\n font-family: monospace;\n}", "vimMode", false);
  906. (function () {
  907. function dialogDiv(cm, template, bottom) {
  908. var wrap = cm.ace.container;
  909. var dialog;
  910. dialog = wrap.appendChild(document.createElement("div"));
  911. if (bottom)
  912. dialog.className = "ace_dialog ace_dialog-bottom";
  913. else
  914. dialog.className = "ace_dialog ace_dialog-top";
  915. if (typeof template == "string") {
  916. dialog.innerHTML = template;
  917. }
  918. else { // Assuming it's a detached DOM element.
  919. dialog.appendChild(template);
  920. }
  921. return dialog;
  922. }
  923. function closeNotification(cm, newVal) {
  924. if (cm.state.currentNotificationClose)
  925. cm.state.currentNotificationClose();
  926. cm.state.currentNotificationClose = newVal;
  927. }
  928. CodeMirror.defineExtension("openDialog", function (template, callback, options) {
  929. if (this.virtualSelectionMode())
  930. return;
  931. if (!options)
  932. options = {};
  933. closeNotification(this, null);
  934. var dialog = dialogDiv(this, template, options.bottom);
  935. var closed = false, me = this;
  936. this.state.dialog = dialog;
  937. function close(newVal) {
  938. if (typeof newVal == 'string') {
  939. inp.value = newVal;
  940. }
  941. else {
  942. if (closed)
  943. return;
  944. if (newVal && newVal.type == "blur") {
  945. if (document.activeElement === inp)
  946. return;
  947. }
  948. if (me.state.dialog == dialog) {
  949. me.state.dialog = null;
  950. me.focus();
  951. }
  952. closed = true;
  953. dialog.remove();
  954. if (options.onClose)
  955. options.onClose(dialog);
  956. var cm = me;
  957. if (cm.state.vim) {
  958. cm.state.vim.status = null;
  959. cm.ace._signal("changeStatus");
  960. cm.ace.renderer.$loop.schedule(cm.ace.renderer.CHANGE_CURSOR);
  961. }
  962. }
  963. }
  964. var inp = dialog.getElementsByTagName("input")[0], button;
  965. if (inp) {
  966. if (options.value) {
  967. inp.value = options.value;
  968. if (options.selectValueOnOpen !== false)
  969. inp.select();
  970. }
  971. if (options.onInput)
  972. CodeMirror.on(inp, "input", function (e) { options.onInput(e, inp.value, close); });
  973. if (options.onKeyUp)
  974. CodeMirror.on(inp, "keyup", function (e) { options.onKeyUp(e, inp.value, close); });
  975. CodeMirror.on(inp, "keydown", function (e) {
  976. if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) {
  977. return;
  978. }
  979. if (e.keyCode == 13)
  980. callback(inp.value);
  981. if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
  982. CodeMirror.e_stop(e);
  983. close();
  984. }
  985. });
  986. if (options.closeOnBlur !== false)
  987. CodeMirror.on(inp, "blur", close);
  988. inp.focus();
  989. }
  990. else if (button = dialog.getElementsByTagName("button")[0]) {
  991. CodeMirror.on(button, "click", function () {
  992. close();
  993. me.focus();
  994. });
  995. if (options.closeOnBlur !== false)
  996. CodeMirror.on(button, "blur", close);
  997. button.focus();
  998. }
  999. return close;
  1000. });
  1001. CodeMirror.defineExtension("openNotification", function (template, options) {
  1002. if (this.virtualSelectionMode())
  1003. return;
  1004. closeNotification(this, close);
  1005. var dialog = dialogDiv(this, template, options && options.bottom);
  1006. var closed = false, doneTimer;
  1007. var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
  1008. function close() {
  1009. if (closed)
  1010. return;
  1011. closed = true;
  1012. clearTimeout(doneTimer);
  1013. dialog.remove();
  1014. }
  1015. CodeMirror.on(dialog, 'click', function (e) {
  1016. CodeMirror.e_preventDefault(e);
  1017. close();
  1018. });
  1019. if (duration)
  1020. doneTimer = setTimeout(close, duration);
  1021. return close;
  1022. });
  1023. })();
  1024. var Pos = CodeMirror.Pos;
  1025. function updateSelectionForSurrogateCharacters(cm, curStart, curEnd) {
  1026. if (curStart.line === curEnd.line && curStart.ch >= curEnd.ch - 1) {
  1027. var text = cm.getLine(curStart.line);
  1028. var charCode = text.charCodeAt(curStart.ch);
  1029. if (0xD800 <= charCode && charCode <= 0xD8FF) {
  1030. curEnd.ch += 1;
  1031. }
  1032. }
  1033. return { start: curStart, end: curEnd };
  1034. }
  1035. var defaultKeymap = [
  1036. { keys: '<Left>', type: 'keyToKey', toKeys: 'h' },
  1037. { keys: '<Right>', type: 'keyToKey', toKeys: 'l' },
  1038. { keys: '<Up>', type: 'keyToKey', toKeys: 'k' },
  1039. { keys: '<Down>', type: 'keyToKey', toKeys: 'j' },
  1040. { keys: 'g<Up>', type: 'keyToKey', toKeys: 'gk' },
  1041. { keys: 'g<Down>', type: 'keyToKey', toKeys: 'gj' },
  1042. { keys: '<Space>', type: 'keyToKey', toKeys: 'l' },
  1043. { keys: '<BS>', type: 'keyToKey', toKeys: 'h' },
  1044. { keys: '<Del>', type: 'keyToKey', toKeys: 'x' },
  1045. { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },
  1046. { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B' },
  1047. { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },
  1048. { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b' },
  1049. { keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },
  1050. { keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },
  1051. { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },
  1052. { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },
  1053. { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
  1054. { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
  1055. { keys: '<C-Esc>', type: 'keyToKey', toKeys: '<Esc>' }, // ipad keyboard sends C-Esc instead of C-[
  1056. { keys: '<C-Esc>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
  1057. { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },
  1058. { keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual' },
  1059. { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },
  1060. { keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual' },
  1061. { keys: '<Home>', type: 'keyToKey', toKeys: '0' },
  1062. { keys: '<End>', type: 'keyToKey', toKeys: '$' },
  1063. { keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },
  1064. { keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },
  1065. { keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
  1066. { keys: '<Ins>', type: 'keyToKey', toKeys: 'i', context: 'normal' },
  1067. { keys: '<Ins>', type: 'action', action: 'toggleOverwrite', context: 'insert' },
  1068. { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true } },
  1069. { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true } },
  1070. { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true } },
  1071. { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false } },
  1072. { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true } },
  1073. { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true } },
  1074. { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true } },
  1075. { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true } },
  1076. { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false } },
  1077. { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false } },
  1078. { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true } },
  1079. { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true } },
  1080. { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true } },
  1081. { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false } },
  1082. { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true } },
  1083. { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true } },
  1084. { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true } },
  1085. { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true } },
  1086. { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true } },
  1087. { keys: '(', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: false } },
  1088. { keys: ')', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: true } },
  1089. { keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true } },
  1090. { keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false } },
  1091. { keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true } },
  1092. { keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true } },
  1093. { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true } },
  1094. { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true } },
  1095. { keys: "g$", type: "motion", motion: "moveToEndOfDisplayLine" },
  1096. { keys: "g^", type: "motion", motion: "moveToStartOfDisplayLine" },
  1097. { keys: "g0", type: "motion", motion: "moveToStartOfDisplayLine" },
  1098. { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },
  1099. { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },
  1100. { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar: true } },
  1101. { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar: true } },
  1102. { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar: true, repeatOffset: -1 } },
  1103. { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true } },
  1104. { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true } },
  1105. { keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true, inclusive: true } },
  1106. { keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false } },
  1107. { keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true } },
  1108. { keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false } },
  1109. { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true } },
  1110. { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false } },
  1111. { keys: '\'<register>', type: 'motion', motion: 'goToMark', motionArgs: { toJumplist: true, linewise: true } },
  1112. { keys: '`<register>', type: 'motion', motion: 'goToMark', motionArgs: { toJumplist: true } },
  1113. { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
  1114. { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
  1115. { keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
  1116. { keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
  1117. { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true } },
  1118. { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true } },
  1119. { keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true } },
  1120. { keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true } },
  1121. { keys: '|', type: 'motion', motion: 'moveToColumn' },
  1122. { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context: 'visual' },
  1123. { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: { sameLine: true }, context: 'visual' },
  1124. { keys: 'd', type: 'operator', operator: 'delete' },
  1125. { keys: 'y', type: 'operator', operator: 'yank' },
  1126. { keys: 'c', type: 'operator', operator: 'change' },
  1127. { keys: '=', type: 'operator', operator: 'indentAuto' },
  1128. { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true } },
  1129. { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false } },
  1130. { keys: 'g~', type: 'operator', operator: 'changeCase' },
  1131. { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: true }, isEdit: true },
  1132. { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: false }, isEdit: true },
  1133. { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true } },
  1134. { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true } },
  1135. { keys: 'gn', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: true } },
  1136. { keys: 'gN', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: false } },
  1137. { keys: 'gq', type: 'operator', operator: 'hardWrap' },
  1138. { keys: 'gw', type: 'operator', operator: 'hardWrap', operatorArgs: { keepCursor: true } },
  1139. { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false } },
  1140. { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true } },
  1141. { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal' },
  1142. { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual' },
  1143. { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'expandToLine', motionArgs: { linewise: true }, context: 'normal' },
  1144. { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual' },
  1145. { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal' },
  1146. { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual' },
  1147. { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal' },
  1148. { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual' },
  1149. { keys: '<C-u>', type: 'operatorMotion', operator: 'delete', motion: 'moveToStartOfLine', context: 'insert' },
  1150. { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
  1151. { keys: '<C-w>', type: 'idle', context: 'normal' },
  1152. { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true } },
  1153. { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false } },
  1154. { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true } },
  1155. { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true } },
  1156. { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
  1157. { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
  1158. { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
  1159. { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
  1160. { keys: 'gi', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'lastEdit' }, context: 'normal' },
  1161. { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank' }, context: 'normal' },
  1162. { keys: 'gI', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'bol' }, context: 'normal' },
  1163. { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
  1164. { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
  1165. { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
  1166. { keys: 'v', type: 'action', action: 'toggleVisualMode' },
  1167. { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true } },
  1168. { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true } },
  1169. { keys: '<C-q>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true } },
  1170. { keys: 'gv', type: 'action', action: 'reselectLastSelection' },
  1171. { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
  1172. { keys: 'gJ', type: 'action', action: 'joinLines', actionArgs: { keepSpaces: true }, isEdit: true },
  1173. { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true } },
  1174. { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true } },
  1175. { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },
  1176. { keys: '@<register>', type: 'action', action: 'replayMacro' },
  1177. { keys: 'q<register>', type: 'action', action: 'enterMacroRecordMode' },
  1178. { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }, context: 'normal' },
  1179. { keys: 'R', type: 'operator', operator: 'change', operatorArgs: { linewise: true, fullLine: true }, context: 'visual', exitVisualBlock: true },
  1180. { keys: 'u', type: 'action', action: 'undo', context: 'normal' },
  1181. { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: true }, context: 'visual', isEdit: true },
  1182. { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: { toLower: false }, context: 'visual', isEdit: true },
  1183. { keys: '<C-r>', type: 'action', action: 'redo' },
  1184. { keys: 'm<register>', type: 'action', action: 'setMark' },
  1185. { keys: '"<register>', type: 'action', action: 'setRegister' },
  1186. { keys: '<C-r><register>', type: 'action', action: 'insertRegister', context: 'insert', isEdit: true },
  1187. { keys: '<C-o>', type: 'action', action: 'oneNormalCommand', context: 'insert' },
  1188. { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' } },
  1189. { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
  1190. { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' } },
  1191. { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
  1192. { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' } },
  1193. { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
  1194. { keys: '.', type: 'action', action: 'repeatLastEdit' },
  1195. { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: { increase: true, backtrack: false } },
  1196. { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: { increase: false, backtrack: false } },
  1197. { keys: '<C-t>', type: 'action', action: 'indent', actionArgs: { indentRight: true }, context: 'insert' },
  1198. { keys: '<C-d>', type: 'action', action: 'indent', actionArgs: { indentRight: false }, context: 'insert' },
  1199. { keys: 'a<register>', type: 'motion', motion: 'textObjectManipulation' },
  1200. { keys: 'i<register>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true } },
  1201. { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true } },
  1202. { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true } },
  1203. { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true } },
  1204. { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true } },
  1205. { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true } },
  1206. { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true } },
  1207. { keys: ':', type: 'ex' }
  1208. ];
  1209. var defaultKeymapLength = defaultKeymap.length;
  1210. var defaultExCommandMap = [
  1211. { name: 'colorscheme', shortName: 'colo' },
  1212. { name: 'map' },
  1213. { name: 'imap', shortName: 'im' },
  1214. { name: 'nmap', shortName: 'nm' },
  1215. { name: 'vmap', shortName: 'vm' },
  1216. { name: 'omap', shortName: 'om' },
  1217. { name: 'noremap', shortName: 'no' },
  1218. { name: 'nnoremap', shortName: 'nn' },
  1219. { name: 'vnoremap', shortName: 'vn' },
  1220. { name: 'inoremap', shortName: 'ino' },
  1221. { name: 'onoremap', shortName: 'ono' },
  1222. { name: 'unmap' },
  1223. { name: 'mapclear', shortName: 'mapc' },
  1224. { name: 'nmapclear', shortName: 'nmapc' },
  1225. { name: 'vmapclear', shortName: 'vmapc' },
  1226. { name: 'imapclear', shortName: 'imapc' },
  1227. { name: 'omapclear', shortName: 'omapc' },
  1228. { name: 'write', shortName: 'w' },
  1229. { name: 'undo', shortName: 'u' },
  1230. { name: 'redo', shortName: 'red' },
  1231. { name: 'set', shortName: 'se' },
  1232. { name: 'setlocal', shortName: 'setl' },
  1233. { name: 'setglobal', shortName: 'setg' },
  1234. { name: 'sort', shortName: 'sor' },
  1235. { name: 'substitute', shortName: 's', possiblyAsync: true },
  1236. { name: 'startinsert', shortName: 'start' },
  1237. { name: 'nohlsearch', shortName: 'noh' },
  1238. { name: 'yank', shortName: 'y' },
  1239. { name: 'delmarks', shortName: 'delm' },
  1240. { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
  1241. { name: 'vglobal', shortName: 'v' },
  1242. { name: 'delete', shortName: 'd' },
  1243. { name: 'join', shortName: 'j' },
  1244. { name: 'normal', shortName: 'norm' },
  1245. { name: 'global', shortName: 'g' }
  1246. ];
  1247. var langmap = parseLangmap('');
  1248. function enterVimMode(cm) {
  1249. cm.setOption('disableInput', true);
  1250. cm.setOption('showCursorWhenSelecting', false);
  1251. CodeMirror.signal(cm, "vim-mode-change", { mode: "normal" });
  1252. cm.on('cursorActivity', onCursorActivity);
  1253. maybeInitVimState(cm);
  1254. CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
  1255. }
  1256. function leaveVimMode(cm) {
  1257. cm.setOption('disableInput', false);
  1258. cm.off('cursorActivity', onCursorActivity);
  1259. CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
  1260. cm.state.vim = null;
  1261. if (highlightTimeout)
  1262. clearTimeout(highlightTimeout);
  1263. }
  1264. function getOnPasteFn(cm) {
  1265. var vim = cm.state.vim;
  1266. if (!vim.onPasteFn) {
  1267. vim.onPasteFn = function () {
  1268. if (!vim.insertMode) {
  1269. cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
  1270. actions.enterInsertMode(cm, {}, vim);
  1271. }
  1272. };
  1273. }
  1274. return vim.onPasteFn;
  1275. }
  1276. var numberRegex = /[\d]/;
  1277. var wordCharTest = [CodeMirror.isWordChar, function (ch) {
  1278. return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch);
  1279. }], bigWordCharTest = [function (ch) {
  1280. return /\S/.test(ch);
  1281. }];
  1282. var validMarks = ['<', '>'];
  1283. var validRegisters = ['-', '"', '.', ':', '_', '/', '+'];
  1284. var latinCharRegex = /^\w$/;
  1285. var upperCaseChars;
  1286. try {
  1287. upperCaseChars = new RegExp("^[\\p{Lu}]$", "u");
  1288. }
  1289. catch (_) {
  1290. upperCaseChars = /^[A-Z]$/;
  1291. }
  1292. function isLine(cm, line) {
  1293. return line >= cm.firstLine() && line <= cm.lastLine();
  1294. }
  1295. function isLowerCase(k) {
  1296. return (/^[a-z]$/).test(k);
  1297. }
  1298. function isMatchableSymbol(k) {
  1299. return '()[]{}'.indexOf(k) != -1;
  1300. }
  1301. function isNumber(k) {
  1302. return numberRegex.test(k);
  1303. }
  1304. function isUpperCase(k) {
  1305. return upperCaseChars.test(k);
  1306. }
  1307. function isWhiteSpaceString(k) {
  1308. return (/^\s*$/).test(k);
  1309. }
  1310. function isEndOfSentenceSymbol(k) {
  1311. return '.?!'.indexOf(k) != -1;
  1312. }
  1313. function inArray(val, arr) {
  1314. for (var i = 0; i < arr.length; i++) {
  1315. if (arr[i] == val) {
  1316. return true;
  1317. }
  1318. }
  1319. return false;
  1320. }
  1321. var options = {};
  1322. function defineOption(name, defaultValue, type, aliases, callback) {
  1323. if (defaultValue === undefined && !callback) {
  1324. throw Error('defaultValue is required unless callback is provided');
  1325. }
  1326. if (!type) {
  1327. type = 'string';
  1328. }
  1329. options[name] = {
  1330. type: type,
  1331. defaultValue: defaultValue,
  1332. callback: callback
  1333. };
  1334. if (aliases) {
  1335. for (var i = 0; i < aliases.length; i++) {
  1336. options[aliases[i]] = options[name];
  1337. }
  1338. }
  1339. if (defaultValue) {
  1340. setOption(name, defaultValue);
  1341. }
  1342. }
  1343. function setOption(name, value, cm, cfg) {
  1344. var option = options[name];
  1345. cfg = cfg || {};
  1346. var scope = cfg.scope;
  1347. if (!option) {
  1348. return new Error('Unknown option: ' + name);
  1349. }
  1350. if (option.type == 'boolean') {
  1351. if (value && value !== true) {
  1352. return new Error('Invalid argument: ' + name + '=' + value);
  1353. }
  1354. else if (value !== false) {
  1355. value = true;
  1356. }
  1357. }
  1358. if (option.callback) {
  1359. if (scope !== 'local') {
  1360. option.callback(value, undefined);
  1361. }
  1362. if (scope !== 'global' && cm) {
  1363. option.callback(value, cm);
  1364. }
  1365. }
  1366. else {
  1367. if (scope !== 'local') {
  1368. option.value = option.type == 'boolean' ? !!value : value;
  1369. }
  1370. if (scope !== 'global' && cm) {
  1371. cm.state.vim.options[name] = { value: value };
  1372. }
  1373. }
  1374. }
  1375. function getOption(name, cm, cfg) {
  1376. var option = options[name];
  1377. cfg = cfg || {};
  1378. var scope = cfg.scope;
  1379. if (!option) {
  1380. return new Error('Unknown option: ' + name);
  1381. }
  1382. if (option.callback) {
  1383. var local = cm && option.callback(undefined, cm);
  1384. if (scope !== 'global' && local !== undefined) {
  1385. return local;
  1386. }
  1387. if (scope !== 'local') {
  1388. return option.callback();
  1389. }
  1390. return;
  1391. }
  1392. else {
  1393. var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);
  1394. return (local || (scope !== 'local') && option || {}).value;
  1395. }
  1396. }
  1397. defineOption('filetype', undefined, 'string', ['ft'], function (name, cm) {
  1398. if (cm === undefined) {
  1399. return;
  1400. }
  1401. if (name === undefined) {
  1402. var mode = cm.getOption('mode');
  1403. return mode == 'null' ? '' : mode;
  1404. }
  1405. else {
  1406. var mode = name == '' ? 'null' : name;
  1407. cm.setOption('mode', mode);
  1408. }
  1409. });
  1410. defineOption('textwidth', 80, 'number', ['tw'], function (width, cm) {
  1411. if (cm === undefined) {
  1412. return;
  1413. }
  1414. if (width === undefined) {
  1415. var value = cm.getOption('textwidth');
  1416. return value;
  1417. }
  1418. else {
  1419. var column = Math.round(width);
  1420. if (column > 1) {
  1421. cm.setOption('textwidth', column);
  1422. }
  1423. }
  1424. });
  1425. var createCircularJumpList = function () {
  1426. var size = 100;
  1427. var pointer = -1;
  1428. var head = 0;
  1429. var tail = 0;
  1430. var buffer = new Array(size);
  1431. function add(cm, oldCur, newCur) {
  1432. var current = pointer % size;
  1433. var curMark = buffer[current];
  1434. function useNextSlot(cursor) {
  1435. var next = ++pointer % size;
  1436. var trashMark = buffer[next];
  1437. if (trashMark) {
  1438. trashMark.clear();
  1439. }
  1440. buffer[next] = cm.setBookmark(cursor);
  1441. }
  1442. if (curMark) {
  1443. var markPos = curMark.find();
  1444. if (markPos && !cursorEqual(markPos, oldCur)) {
  1445. useNextSlot(oldCur);
  1446. }
  1447. }
  1448. else {
  1449. useNextSlot(oldCur);
  1450. }
  1451. useNextSlot(newCur);
  1452. head = pointer;
  1453. tail = pointer - size + 1;
  1454. if (tail < 0) {
  1455. tail = 0;
  1456. }
  1457. }
  1458. function move(cm, offset) {
  1459. pointer += offset;
  1460. if (pointer > head) {
  1461. pointer = head;
  1462. }
  1463. else if (pointer < tail) {
  1464. pointer = tail;
  1465. }
  1466. var mark = buffer[(size + pointer) % size];
  1467. if (mark && !mark.find()) {
  1468. var inc = offset > 0 ? 1 : -1;
  1469. var newCur;
  1470. var oldCur = cm.getCursor();
  1471. do {
  1472. pointer += inc;
  1473. mark = buffer[(size + pointer) % size];
  1474. if (mark &&
  1475. (newCur = mark.find()) &&
  1476. !cursorEqual(oldCur, newCur)) {
  1477. break;
  1478. }
  1479. } while (pointer < head && pointer > tail);
  1480. }
  1481. return mark;
  1482. }
  1483. function find(cm, offset) {
  1484. var oldPointer = pointer;
  1485. var mark = move(cm, offset);
  1486. pointer = oldPointer;
  1487. return mark && mark.find();
  1488. }
  1489. return {
  1490. cachedCursor: undefined, //used for # and * jumps
  1491. add: add,
  1492. find: find,
  1493. move: move
  1494. };
  1495. };
  1496. var createInsertModeChanges = function (c) {
  1497. if (c) {
  1498. return {
  1499. changes: c.changes,
  1500. expectCursorActivityForChange: c.expectCursorActivityForChange
  1501. };
  1502. }
  1503. return {
  1504. changes: [],
  1505. expectCursorActivityForChange: false
  1506. };
  1507. };
  1508. function MacroModeState() {
  1509. this.latestRegister = undefined;
  1510. this.isPlaying = false;
  1511. this.isRecording = false;
  1512. this.replaySearchQueries = [];
  1513. this.onRecordingDone = undefined;
  1514. this.lastInsertModeChanges = createInsertModeChanges();
  1515. }
  1516. MacroModeState.prototype = {
  1517. exitMacroRecordMode: function () {
  1518. var macroModeState = vimGlobalState.macroModeState;
  1519. if (macroModeState.onRecordingDone) {
  1520. macroModeState.onRecordingDone(); // close dialog
  1521. }
  1522. macroModeState.onRecordingDone = undefined;
  1523. macroModeState.isRecording = false;
  1524. },
  1525. enterMacroRecordMode: function (cm, registerName) {
  1526. var register = vimGlobalState.registerController.getRegister(registerName);
  1527. if (register) {
  1528. register.clear();
  1529. this.latestRegister = registerName;
  1530. if (cm.openDialog) {
  1531. var template = dom('span', { class: 'cm-vim-message' }, 'recording @' + registerName);
  1532. this.onRecordingDone = cm.openDialog(template, null, { bottom: true });
  1533. }
  1534. this.isRecording = true;
  1535. }
  1536. }
  1537. };
  1538. function maybeInitVimState(cm) {
  1539. if (!cm.state.vim) {
  1540. cm.state.vim = {
  1541. inputState: new InputState(),
  1542. lastEditInputState: undefined,
  1543. lastEditActionCommand: undefined,
  1544. lastHPos: -1,
  1545. lastHSPos: -1,
  1546. lastMotion: null,
  1547. marks: {},
  1548. insertMode: false,
  1549. insertModeReturn: false,
  1550. insertModeRepeat: undefined,
  1551. visualMode: false,
  1552. visualLine: false,
  1553. visualBlock: false,
  1554. lastSelection: null,
  1555. lastPastedText: null,
  1556. sel: {},
  1557. options: {},
  1558. expectLiteralNext: false
  1559. };
  1560. }
  1561. return cm.state.vim;
  1562. }
  1563. var vimGlobalState;
  1564. function resetVimGlobalState() {
  1565. vimGlobalState = {
  1566. searchQuery: null,
  1567. searchIsReversed: false,
  1568. lastSubstituteReplacePart: undefined,
  1569. jumpList: createCircularJumpList(),
  1570. macroModeState: new MacroModeState,
  1571. lastCharacterSearch: { increment: 0, forward: true, selectedCharacter: '' },
  1572. registerController: new RegisterController({}),
  1573. searchHistoryController: new HistoryController(),
  1574. exCommandHistoryController: new HistoryController()
  1575. };
  1576. for (var optionName in options) {
  1577. var option = options[optionName];
  1578. option.value = option.defaultValue;
  1579. }
  1580. }
  1581. var lastInsertModeKeyTimer;
  1582. var vimApi = {
  1583. enterVimMode: enterVimMode,
  1584. leaveVimMode: leaveVimMode,
  1585. buildKeyMap: function () {
  1586. },
  1587. getRegisterController: function () {
  1588. return vimGlobalState.registerController;
  1589. },
  1590. resetVimGlobalState_: resetVimGlobalState,
  1591. getVimGlobalState_: function () {
  1592. return vimGlobalState;
  1593. },
  1594. maybeInitVimState_: maybeInitVimState,
  1595. suppressErrorLogging: false,
  1596. InsertModeKey: InsertModeKey,
  1597. map: function (lhs, rhs, ctx) {
  1598. exCommandDispatcher.map(lhs, rhs, ctx);
  1599. },
  1600. unmap: function (lhs, ctx) {
  1601. return exCommandDispatcher.unmap(lhs, ctx);
  1602. },
  1603. noremap: function (lhs, rhs, ctx) {
  1604. exCommandDispatcher.map(lhs, rhs, ctx, true);
  1605. },
  1606. mapclear: function (ctx) {
  1607. var actualLength = defaultKeymap.length, origLength = defaultKeymapLength;
  1608. var userKeymap = defaultKeymap.slice(0, actualLength - origLength);
  1609. defaultKeymap = defaultKeymap.slice(actualLength - origLength);
  1610. if (ctx) {
  1611. for (var i = userKeymap.length - 1; i >= 0; i--) {
  1612. var mapping = userKeymap[i];
  1613. if (ctx !== mapping.context) {
  1614. if (mapping.context) {
  1615. this._mapCommand(mapping);
  1616. }
  1617. else {
  1618. var contexts = ['normal', 'insert', 'visual'];
  1619. for (var j in contexts) {
  1620. if (contexts[j] !== ctx) {
  1621. var newMapping = {};
  1622. for (var key in mapping) {
  1623. newMapping[key] = mapping[key];
  1624. }
  1625. newMapping.context = contexts[j];
  1626. this._mapCommand(newMapping);
  1627. }
  1628. }
  1629. }
  1630. }
  1631. }
  1632. }
  1633. },
  1634. langmap: updateLangmap,
  1635. vimKeyFromEvent: vimKeyFromEvent,
  1636. setOption: setOption,
  1637. getOption: getOption,
  1638. defineOption: defineOption,
  1639. defineEx: function (name, prefix, func) {
  1640. if (!prefix) {
  1641. prefix = name;
  1642. }
  1643. else if (name.indexOf(prefix) !== 0) {
  1644. throw new Error('(Vim.defineEx) "' + prefix + '" is not a prefix of "' + name + '", command not registered');
  1645. }
  1646. exCommands[name] = func;
  1647. exCommandDispatcher.commandMap_[prefix] = { name: name, shortName: prefix, type: 'api' };
  1648. },
  1649. handleKey: function (cm, key, origin) {
  1650. var command = this.findKey(cm, key, origin);
  1651. if (typeof command === 'function') {
  1652. return command();
  1653. }
  1654. },
  1655. multiSelectHandleKey: multiSelectHandleKey,
  1656. findKey: function (cm, key, origin) {
  1657. var vim = maybeInitVimState(cm);
  1658. function handleMacroRecording() {
  1659. var macroModeState = vimGlobalState.macroModeState;
  1660. if (macroModeState.isRecording) {
  1661. if (key == 'q') {
  1662. macroModeState.exitMacroRecordMode();
  1663. clearInputState(cm);
  1664. return true;
  1665. }
  1666. if (origin != 'mapping') {
  1667. logKey(macroModeState, key);
  1668. }
  1669. }
  1670. }
  1671. function handleEsc() {
  1672. if (key == '<Esc>') {
  1673. if (vim.visualMode) {
  1674. exitVisualMode(cm);
  1675. }
  1676. else if (vim.insertMode) {
  1677. exitInsertMode(cm);
  1678. }
  1679. else {
  1680. return;
  1681. }
  1682. clearInputState(cm);
  1683. return true;
  1684. }
  1685. }
  1686. function handleKeyInsertMode() {
  1687. if (handleEsc()) {
  1688. return true;
  1689. }
  1690. vim.inputState.keyBuffer.push(key);
  1691. var keys = vim.inputState.keyBuffer.join("");
  1692. var keysAreChars = key.length == 1;
  1693. var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
  1694. var changeQueue = vim.inputState.changeQueue;
  1695. if (match.type == 'none') {
  1696. clearInputState(cm);
  1697. return false;
  1698. }
  1699. else if (match.type == 'partial') {
  1700. if (match.expectLiteralNext)
  1701. vim.expectLiteralNext = true;
  1702. if (lastInsertModeKeyTimer) {
  1703. window.clearTimeout(lastInsertModeKeyTimer);
  1704. }
  1705. lastInsertModeKeyTimer = keysAreChars && window.setTimeout(function () { if (vim.insertMode && vim.inputState.keyBuffer.length) {
  1706. clearInputState(cm);
  1707. } }, getOption('insertModeEscKeysTimeout'));
  1708. if (keysAreChars) {
  1709. var selections = cm.listSelections();
  1710. if (!changeQueue || changeQueue.removed.length != selections.length)
  1711. changeQueue = vim.inputState.changeQueue = new ChangeQueue;
  1712. changeQueue.inserted += key;
  1713. for (var i = 0; i < selections.length; i++) {
  1714. var from = cursorMin(selections[i].anchor, selections[i].head);
  1715. var to = cursorMax(selections[i].anchor, selections[i].head);
  1716. var text = cm.getRange(from, cm.state.overwrite ? offsetCursor(to, 0, 1) : to);
  1717. changeQueue.removed[i] = (changeQueue.removed[i] || "") + text;
  1718. }
  1719. }
  1720. return !keysAreChars;
  1721. }
  1722. vim.expectLiteralNext = false;
  1723. if (lastInsertModeKeyTimer) {
  1724. window.clearTimeout(lastInsertModeKeyTimer);
  1725. }
  1726. if (match.command && changeQueue) {
  1727. var selections = cm.listSelections();
  1728. for (var i = 0; i < selections.length; i++) {
  1729. var here = selections[i].head;
  1730. cm.replaceRange(changeQueue.removed[i] || "", offsetCursor(here, 0, -changeQueue.inserted.length), here, '+input');
  1731. }
  1732. vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
  1733. }
  1734. if (!match.command)
  1735. clearInputState(cm);
  1736. return match.command;
  1737. }
  1738. function handleKeyNonInsertMode() {
  1739. if (handleMacroRecording() || handleEsc()) {
  1740. return true;
  1741. }
  1742. vim.inputState.keyBuffer.push(key);
  1743. var keys = vim.inputState.keyBuffer.join("");
  1744. if (/^[1-9]\d*$/.test(keys)) {
  1745. return true;
  1746. }
  1747. var keysMatcher = /^(\d*)(.*)$/.exec(keys);
  1748. if (!keysMatcher) {
  1749. clearInputState(cm);
  1750. return false;
  1751. }
  1752. var context = vim.visualMode ? 'visual' :
  1753. 'normal';
  1754. var mainKey = keysMatcher[2] || keysMatcher[1];
  1755. if (vim.inputState.operatorShortcut && vim.inputState.operatorShortcut.slice(-1) == mainKey) {
  1756. mainKey = vim.inputState.operatorShortcut;
  1757. }
  1758. var match = commandDispatcher.matchCommand(mainKey, defaultKeymap, vim.inputState, context);
  1759. if (match.type == 'none') {
  1760. clearInputState(cm);
  1761. return false;
  1762. }
  1763. else if (match.type == 'partial') {
  1764. if (match.expectLiteralNext)
  1765. vim.expectLiteralNext = true;
  1766. return true;
  1767. }
  1768. else if (match.type == 'clear') {
  1769. clearInputState(cm);
  1770. return true;
  1771. }
  1772. vim.expectLiteralNext = false;
  1773. vim.inputState.keyBuffer.length = 0;
  1774. keysMatcher = /^(\d*)(.*)$/.exec(keys);
  1775. if (keysMatcher[1] && keysMatcher[1] != '0') {
  1776. vim.inputState.pushRepeatDigit(keysMatcher[1]);
  1777. }
  1778. return match.command;
  1779. }
  1780. var command;
  1781. if (vim.insertMode) {
  1782. command = handleKeyInsertMode();
  1783. }
  1784. else {
  1785. command = handleKeyNonInsertMode();
  1786. }
  1787. if (command === false) {
  1788. return !vim.insertMode && key.length === 1 ? function () { return true; } : undefined;
  1789. }
  1790. else if (command === true) {
  1791. return function () { return true; };
  1792. }
  1793. else {
  1794. return function () {
  1795. if ((command.operator || command.isEdit) && cm.getOption('readOnly'))
  1796. return; // ace_patch
  1797. return cm.operation(function () {
  1798. cm.curOp.isVimOp = true;
  1799. try {
  1800. if (command.type == 'keyToKey') {
  1801. doKeyToKey(cm, command.toKeys, command);
  1802. }
  1803. else {
  1804. commandDispatcher.processCommand(cm, vim, command);
  1805. }
  1806. }
  1807. catch (e) {
  1808. cm.state.vim = undefined;
  1809. maybeInitVimState(cm);
  1810. if (!vimApi.suppressErrorLogging) {
  1811. console['log'](e);
  1812. }
  1813. throw e;
  1814. }
  1815. return true;
  1816. });
  1817. };
  1818. }
  1819. },
  1820. handleEx: function (cm, input) {
  1821. exCommandDispatcher.processCommand(cm, input);
  1822. },
  1823. defineMotion: defineMotion,
  1824. defineAction: defineAction,
  1825. defineOperator: defineOperator,
  1826. mapCommand: mapCommand,
  1827. _mapCommand: _mapCommand,
  1828. defineRegister: defineRegister,
  1829. exitVisualMode: exitVisualMode,
  1830. exitInsertMode: exitInsertMode
  1831. };
  1832. var keyToKeyStack = [];
  1833. var noremap = false;
  1834. var virtualPrompt;
  1835. function sendKeyToPrompt(key) {
  1836. if (key[0] == "<") {
  1837. var lowerKey = key.toLowerCase().slice(1, -1);
  1838. var parts = lowerKey.split('-');
  1839. lowerKey = parts.pop() || '';
  1840. if (lowerKey == 'lt')
  1841. key = '<';
  1842. else if (lowerKey == 'space')
  1843. key = ' ';
  1844. else if (lowerKey == 'cr')
  1845. key = '\n';
  1846. else if (vimToCmKeyMap[lowerKey]) {
  1847. var value = virtualPrompt.value;
  1848. var event = {
  1849. key: vimToCmKeyMap[lowerKey],
  1850. target: {
  1851. value: value,
  1852. selectionEnd: value.length,
  1853. selectionStart: value.length
  1854. }
  1855. };
  1856. if (virtualPrompt.onKeyDown) {
  1857. virtualPrompt.onKeyDown(event, virtualPrompt.value, close);
  1858. }
  1859. if (virtualPrompt && virtualPrompt.onKeyUp) {
  1860. virtualPrompt.onKeyUp(event, virtualPrompt.value, close);
  1861. }
  1862. return;
  1863. }
  1864. }
  1865. if (key == '\n') {
  1866. var prompt = virtualPrompt;
  1867. virtualPrompt = null;
  1868. prompt.onClose && prompt.onClose(prompt.value);
  1869. }
  1870. else {
  1871. virtualPrompt.value = (virtualPrompt.value || '') + key;
  1872. }
  1873. function close(value) {
  1874. if (typeof value == 'string') {
  1875. virtualPrompt.value = value;
  1876. }
  1877. else {
  1878. virtualPrompt = null;
  1879. }
  1880. }
  1881. }
  1882. function doKeyToKey(cm, keys, fromKey) {
  1883. var noremapBefore = noremap;
  1884. if (fromKey) {
  1885. if (keyToKeyStack.indexOf(fromKey) != -1)
  1886. return;
  1887. keyToKeyStack.push(fromKey);
  1888. noremap = fromKey.noremap != false;
  1889. }
  1890. try {
  1891. var vim = maybeInitVimState(cm);
  1892. var keyRe = /<(?:[CSMA]-)*\w+>|./gi;
  1893. var match;
  1894. while ((match = keyRe.exec(keys))) {
  1895. var key = match[0];
  1896. var wasInsert = vim.insertMode;
  1897. if (virtualPrompt) {
  1898. sendKeyToPrompt(key);
  1899. continue;
  1900. }
  1901. var result = vimApi.handleKey(cm, key, 'mapping');
  1902. if (!result && wasInsert && vim.insertMode) {
  1903. if (key[0] == "<") {
  1904. var lowerKey = key.toLowerCase().slice(1, -1);
  1905. var parts = lowerKey.split('-');
  1906. lowerKey = parts.pop() || '';
  1907. if (lowerKey == 'lt')
  1908. key = '<';
  1909. else if (lowerKey == 'space')
  1910. key = ' ';
  1911. else if (lowerKey == 'cr')
  1912. key = '\n';
  1913. else if (vimToCmKeyMap.hasOwnProperty(lowerKey)) {
  1914. key = vimToCmKeyMap[lowerKey];
  1915. sendCmKey(cm, key);
  1916. continue;
  1917. }
  1918. else {
  1919. key = key[0];
  1920. keyRe.lastIndex = match.index + 1;
  1921. }
  1922. }
  1923. cm.replaceSelection(key);
  1924. }
  1925. }
  1926. }
  1927. finally {
  1928. keyToKeyStack.pop();
  1929. noremap = keyToKeyStack.length ? noremapBefore : false;
  1930. if (!keyToKeyStack.length && virtualPrompt) {
  1931. var promptOptions = virtualPrompt;
  1932. virtualPrompt = null;
  1933. showPrompt(cm, promptOptions);
  1934. }
  1935. }
  1936. }
  1937. var specialKey = {
  1938. Return: 'CR', Backspace: 'BS', 'Delete': 'Del', Escape: 'Esc', Insert: 'Ins',
  1939. ArrowLeft: 'Left', ArrowRight: 'Right', ArrowUp: 'Up', ArrowDown: 'Down',
  1940. Enter: 'CR', ' ': 'Space'
  1941. };
  1942. var ignoredKeys = { Shift: 1, Alt: 1, Command: 1, Control: 1,
  1943. CapsLock: 1, AltGraph: 1, Dead: 1, Unidentified: 1 };
  1944. var vimToCmKeyMap = {};
  1945. 'Left|Right|Up|Down|End|Home'.split('|').concat(Object.keys(specialKey)).forEach(function (x) {
  1946. vimToCmKeyMap[(specialKey[x] || '').toLowerCase()]
  1947. = vimToCmKeyMap[x.toLowerCase()] = x;
  1948. });
  1949. function vimKeyFromEvent(e, vim) {
  1950. var key = e.key;
  1951. if (ignoredKeys[key])
  1952. return;
  1953. if (key.length > 1 && key[0] == "n") {
  1954. key = key.replace("Numpad", "");
  1955. }
  1956. key = specialKey[key] || key;
  1957. var name = '';
  1958. if (e.ctrlKey) {
  1959. name += 'C-';
  1960. }
  1961. if (e.altKey) {
  1962. name += 'A-';
  1963. }
  1964. if (e.metaKey) {
  1965. name += 'M-';
  1966. }
  1967. if (CodeMirror.isMac && e.altKey && !e.metaKey && !e.ctrlKey) {
  1968. name = name.slice(2);
  1969. }
  1970. if ((name || key.length > 1) && e.shiftKey) {
  1971. name += 'S-';
  1972. }
  1973. if (vim && !vim.expectLiteralNext && key.length == 1) {
  1974. if (langmap.keymap && key in langmap.keymap) {
  1975. if (langmap.remapCtrl != false || !name)
  1976. key = langmap.keymap[key];
  1977. }
  1978. else if (key.charCodeAt(0) > 255) {
  1979. var code = e.code && e.code.slice(-1) || "";
  1980. if (!e.shiftKey)
  1981. code = code.toLowerCase();
  1982. if (code)
  1983. key = code;
  1984. }
  1985. }
  1986. name += key;
  1987. if (name.length > 1) {
  1988. name = '<' + name + '>';
  1989. }
  1990. return name;
  1991. }
  1992. ;
  1993. function updateLangmap(langmapString, remapCtrl) {
  1994. if (langmap.string !== langmapString) {
  1995. langmap = parseLangmap(langmapString);
  1996. }
  1997. langmap.remapCtrl = remapCtrl;
  1998. }
  1999. function parseLangmap(langmapString) {
  2000. var keymap = {};
  2001. if (!langmapString)
  2002. return { keymap: keymap, string: '' };
  2003. function getEscaped(list) {
  2004. return list.split(/\\?(.)/).filter(Boolean);
  2005. }
  2006. langmapString.split(/((?:[^\\,]|\\.)+),/).map(function (part) {
  2007. if (!part)
  2008. return;
  2009. var semicolon = part.split(/((?:[^\\;]|\\.)+);/);
  2010. if (semicolon.length == 3) {
  2011. var from = getEscaped(semicolon[1]);
  2012. var to = getEscaped(semicolon[2]);
  2013. if (from.length !== to.length)
  2014. return; // skip over malformed part
  2015. for (var i = 0; i < from.length; ++i)
  2016. keymap[from[i]] = to[i];
  2017. }
  2018. else if (semicolon.length == 1) {
  2019. var pairs = getEscaped(part);
  2020. if (pairs.length % 2 !== 0)
  2021. return; // skip over malformed part
  2022. for (var i = 0; i < pairs.length; i += 2)
  2023. keymap[pairs[i]] = pairs[i + 1];
  2024. }
  2025. });
  2026. return { keymap: keymap, string: langmapString };
  2027. }
  2028. defineOption('langmap', undefined, 'string', ['lmap'], function (name, cm) {
  2029. if (name === undefined) {
  2030. return langmap.string;
  2031. }
  2032. else {
  2033. updateLangmap(name);
  2034. }
  2035. });
  2036. function InputState() {
  2037. this.prefixRepeat = [];
  2038. this.motionRepeat = [];
  2039. this.operator = null;
  2040. this.operatorArgs = null;
  2041. this.motion = null;
  2042. this.motionArgs = null;
  2043. this.keyBuffer = []; // For matching multi-key commands.
  2044. this.registerName = null; // Defaults to the unnamed register.
  2045. this.changeQueue = null; // For restoring text used by insert mode keybindings
  2046. }
  2047. InputState.prototype.pushRepeatDigit = function (n) {
  2048. if (!this.operator) {
  2049. this.prefixRepeat = this.prefixRepeat.concat(n);
  2050. }
  2051. else {
  2052. this.motionRepeat = this.motionRepeat.concat(n);
  2053. }
  2054. };
  2055. InputState.prototype.getRepeat = function () {
  2056. var repeat = 0;
  2057. if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
  2058. repeat = 1;
  2059. if (this.prefixRepeat.length > 0) {
  2060. repeat *= parseInt(this.prefixRepeat.join(''), 10);
  2061. }
  2062. if (this.motionRepeat.length > 0) {
  2063. repeat *= parseInt(this.motionRepeat.join(''), 10);
  2064. }
  2065. }
  2066. return repeat;
  2067. };
  2068. function clearInputState(cm, reason) {
  2069. cm.state.vim.inputState = new InputState();
  2070. cm.state.vim.expectLiteralNext = false;
  2071. CodeMirror.signal(cm, 'vim-command-done', reason);
  2072. }
  2073. function ChangeQueue() {
  2074. this.removed = [];
  2075. this.inserted = "";
  2076. }
  2077. function Register(text, linewise, blockwise) {
  2078. this.clear();
  2079. this.keyBuffer = [text || ''];
  2080. this.insertModeChanges = [];
  2081. this.searchQueries = [];
  2082. this.linewise = !!linewise;
  2083. this.blockwise = !!blockwise;
  2084. }
  2085. Register.prototype = {
  2086. setText: function (text, linewise, blockwise) {
  2087. this.keyBuffer = [text || ''];
  2088. this.linewise = !!linewise;
  2089. this.blockwise = !!blockwise;
  2090. },
  2091. pushText: function (text, linewise) {
  2092. if (linewise) {
  2093. if (!this.linewise) {
  2094. this.keyBuffer.push('\n');
  2095. }
  2096. this.linewise = true;
  2097. }
  2098. this.keyBuffer.push(text);
  2099. },
  2100. pushInsertModeChanges: function (changes) {
  2101. this.insertModeChanges.push(createInsertModeChanges(changes));
  2102. },
  2103. pushSearchQuery: function (query) {
  2104. this.searchQueries.push(query);
  2105. },
  2106. clear: function () {
  2107. this.keyBuffer = [];
  2108. this.insertModeChanges = [];
  2109. this.searchQueries = [];
  2110. this.linewise = false;
  2111. },
  2112. toString: function () {
  2113. return this.keyBuffer.join('');
  2114. }
  2115. };
  2116. function defineRegister(name, register) {
  2117. var registers = vimGlobalState.registerController.registers;
  2118. if (!name || name.length != 1) {
  2119. throw Error('Register name must be 1 character');
  2120. }
  2121. registers[name] = register;
  2122. validRegisters.push(name);
  2123. }
  2124. function RegisterController(registers) {
  2125. this.registers = registers;
  2126. this.unnamedRegister = registers['"'] = new Register();
  2127. registers['.'] = new Register();
  2128. registers[':'] = new Register();
  2129. registers['/'] = new Register();
  2130. registers['+'] = new Register();
  2131. }
  2132. RegisterController.prototype = {
  2133. pushText: function (registerName, operator, text, linewise, blockwise) {
  2134. if (registerName === '_')
  2135. return;
  2136. if (linewise && text.charAt(text.length - 1) !== '\n') {
  2137. text += '\n';
  2138. }
  2139. var register = this.isValidRegister(registerName) ?
  2140. this.getRegister(registerName) : null;
  2141. if (!register) {
  2142. switch (operator) {
  2143. case 'yank':
  2144. this.registers['0'] = new Register(text, linewise, blockwise);
  2145. break;
  2146. case 'delete':
  2147. case 'change':
  2148. if (text.indexOf('\n') == -1) {
  2149. this.registers['-'] = new Register(text, linewise);
  2150. }
  2151. else {
  2152. this.shiftNumericRegisters_();
  2153. this.registers['1'] = new Register(text, linewise);
  2154. }
  2155. break;
  2156. }
  2157. this.unnamedRegister.setText(text, linewise, blockwise);
  2158. return;
  2159. }
  2160. var append = isUpperCase(registerName);
  2161. if (append) {
  2162. register.pushText(text, linewise);
  2163. }
  2164. else {
  2165. register.setText(text, linewise, blockwise);
  2166. }
  2167. if (registerName === '+' && typeof navigator !== 'undefined' &&
  2168. typeof navigator.clipboard !== 'undefined' &&
  2169. typeof navigator.clipboard.readText === 'function') {
  2170. navigator.clipboard.writeText(text);
  2171. }
  2172. this.unnamedRegister.setText(register.toString(), linewise);
  2173. },
  2174. getRegister: function (name) {
  2175. if (!this.isValidRegister(name)) {
  2176. return this.unnamedRegister;
  2177. }
  2178. name = name.toLowerCase();
  2179. if (!this.registers[name]) {
  2180. this.registers[name] = new Register();
  2181. }
  2182. return this.registers[name];
  2183. },
  2184. isValidRegister: function (name) {
  2185. return name && (inArray(name, validRegisters) || latinCharRegex.test(name));
  2186. },
  2187. shiftNumericRegisters_: function () {
  2188. for (var i = 9; i >= 2; i--) {
  2189. this.registers[i] = this.getRegister('' + (i - 1));
  2190. }
  2191. }
  2192. };
  2193. function HistoryController() {
  2194. this.historyBuffer = [];
  2195. this.iterator = 0;
  2196. this.initialPrefix = null;
  2197. }
  2198. HistoryController.prototype = {
  2199. nextMatch: function (input, up) {
  2200. var historyBuffer = this.historyBuffer;
  2201. var dir = up ? -1 : 1;
  2202. if (this.initialPrefix === null)
  2203. this.initialPrefix = input;
  2204. for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i += dir) {
  2205. var element = historyBuffer[i];
  2206. for (var j = 0; j <= element.length; j++) {
  2207. if (this.initialPrefix == element.substring(0, j)) {
  2208. this.iterator = i;
  2209. return element;
  2210. }
  2211. }
  2212. }
  2213. if (i >= historyBuffer.length) {
  2214. this.iterator = historyBuffer.length;
  2215. return this.initialPrefix;
  2216. }
  2217. if (i < 0)
  2218. return input;
  2219. },
  2220. pushInput: function (input) {
  2221. var index = this.historyBuffer.indexOf(input);
  2222. if (index > -1)
  2223. this.historyBuffer.splice(index, 1);
  2224. if (input.length)
  2225. this.historyBuffer.push(input);
  2226. },
  2227. reset: function () {
  2228. this.initialPrefix = null;
  2229. this.iterator = this.historyBuffer.length;
  2230. }
  2231. };
  2232. var commandDispatcher = {
  2233. matchCommand: function (keys, keyMap, inputState, context) {
  2234. var matches = commandMatches(keys, keyMap, context, inputState);
  2235. if (!matches.full && !matches.partial) {
  2236. return { type: 'none' };
  2237. }
  2238. else if (!matches.full && matches.partial) {
  2239. return {
  2240. type: 'partial',
  2241. expectLiteralNext: matches.partial.length == 1 && matches.partial[0].keys.slice(-11) == '<character>' // langmap literal logic
  2242. };
  2243. }
  2244. var bestMatch;
  2245. for (var i = 0; i < matches.full.length; i++) {
  2246. var match = matches.full[i];
  2247. if (!bestMatch) {
  2248. bestMatch = match;
  2249. }
  2250. }
  2251. if (bestMatch.keys.slice(-11) == '<character>' || bestMatch.keys.slice(-10) == '<register>') {
  2252. var character = lastChar(keys);
  2253. if (!character || character.length > 1)
  2254. return { type: 'clear' };
  2255. inputState.selectedCharacter = character;
  2256. }
  2257. return { type: 'full', command: bestMatch };
  2258. },
  2259. processCommand: function (cm, vim, command) {
  2260. vim.inputState.repeatOverride = command.repeatOverride;
  2261. switch (command.type) {
  2262. case 'motion':
  2263. this.processMotion(cm, vim, command);
  2264. break;
  2265. case 'operator':
  2266. this.processOperator(cm, vim, command);
  2267. break;
  2268. case 'operatorMotion':
  2269. this.processOperatorMotion(cm, vim, command);
  2270. break;
  2271. case 'action':
  2272. this.processAction(cm, vim, command);
  2273. break;
  2274. case 'search':
  2275. this.processSearch(cm, vim, command);
  2276. break;
  2277. case 'ex':
  2278. case 'keyToEx':
  2279. this.processEx(cm, vim, command);
  2280. break;
  2281. default:
  2282. break;
  2283. }
  2284. },
  2285. processMotion: function (cm, vim, command) {
  2286. vim.inputState.motion = command.motion;
  2287. vim.inputState.motionArgs = copyArgs(command.motionArgs);
  2288. this.evalInput(cm, vim);
  2289. },
  2290. processOperator: function (cm, vim, command) {
  2291. var inputState = vim.inputState;
  2292. if (inputState.operator) {
  2293. if (inputState.operator == command.operator) {
  2294. inputState.motion = 'expandToLine';
  2295. inputState.motionArgs = { linewise: true };
  2296. this.evalInput(cm, vim);
  2297. return;
  2298. }
  2299. else {
  2300. clearInputState(cm);
  2301. }
  2302. }
  2303. inputState.operator = command.operator;
  2304. inputState.operatorArgs = copyArgs(command.operatorArgs);
  2305. if (command.keys.length > 1) {
  2306. inputState.operatorShortcut = command.keys;
  2307. }
  2308. if (command.exitVisualBlock) {
  2309. vim.visualBlock = false;
  2310. updateCmSelection(cm);
  2311. }
  2312. if (vim.visualMode) {
  2313. this.evalInput(cm, vim);
  2314. }
  2315. },
  2316. processOperatorMotion: function (cm, vim, command) {
  2317. var visualMode = vim.visualMode;
  2318. var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
  2319. if (operatorMotionArgs) {
  2320. if (visualMode && operatorMotionArgs.visualLine) {
  2321. vim.visualLine = true;
  2322. }
  2323. }
  2324. this.processOperator(cm, vim, command);
  2325. if (!visualMode) {
  2326. this.processMotion(cm, vim, command);
  2327. }
  2328. },
  2329. processAction: function (cm, vim, command) {
  2330. var inputState = vim.inputState;
  2331. var repeat = inputState.getRepeat();
  2332. var repeatIsExplicit = !!repeat;
  2333. var actionArgs = copyArgs(command.actionArgs) || {};
  2334. if (inputState.selectedCharacter) {
  2335. actionArgs.selectedCharacter = inputState.selectedCharacter;
  2336. }
  2337. if (command.operator) {
  2338. this.processOperator(cm, vim, command);
  2339. }
  2340. if (command.motion) {
  2341. this.processMotion(cm, vim, command);
  2342. }
  2343. if (command.motion || command.operator) {
  2344. this.evalInput(cm, vim);
  2345. }
  2346. actionArgs.repeat = repeat || 1;
  2347. actionArgs.repeatIsExplicit = repeatIsExplicit;
  2348. actionArgs.registerName = inputState.registerName;
  2349. clearInputState(cm);
  2350. vim.lastMotion = null;
  2351. if (command.isEdit) {
  2352. this.recordLastEdit(vim, inputState, command);
  2353. }
  2354. actions[command.action](cm, actionArgs, vim);
  2355. },
  2356. processSearch: function (cm, vim, command) {
  2357. if (!cm.getSearchCursor) {
  2358. return;
  2359. }
  2360. var forward = command.searchArgs.forward;
  2361. var wholeWordOnly = command.searchArgs.wholeWordOnly;
  2362. getSearchState(cm).setReversed(!forward);
  2363. var promptPrefix = (forward) ? '/' : '?';
  2364. var originalQuery = getSearchState(cm).getQuery();
  2365. var originalScrollPos = cm.getScrollInfo();
  2366. function handleQuery(query, ignoreCase, smartCase) {
  2367. vimGlobalState.searchHistoryController.pushInput(query);
  2368. vimGlobalState.searchHistoryController.reset();
  2369. try {
  2370. updateSearchQuery(cm, query, ignoreCase, smartCase);
  2371. }
  2372. catch (e) {
  2373. showConfirm(cm, 'Invalid regex: ' + query);
  2374. clearInputState(cm);
  2375. return;
  2376. }
  2377. commandDispatcher.processMotion(cm, vim, {
  2378. type: 'motion',
  2379. motion: 'findNext',
  2380. motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
  2381. });
  2382. }
  2383. function onPromptClose(query) {
  2384. handleQuery(query, true /** ignoreCase */, true /** smartCase */);
  2385. var macroModeState = vimGlobalState.macroModeState;
  2386. if (macroModeState.isRecording) {
  2387. logSearchQuery(macroModeState, query);
  2388. }
  2389. }
  2390. function onPromptKeyUp(e, query, close) {
  2391. var keyName = vimKeyFromEvent(e), up, offset;
  2392. if (keyName == '<Up>' || keyName == '<Down>') {
  2393. up = keyName == '<Up>' ? true : false;
  2394. offset = e.target ? e.target.selectionEnd : 0;
  2395. query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
  2396. close(query);
  2397. if (offset && e.target)
  2398. e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
  2399. }
  2400. else if (keyName && keyName != '<Left>' && keyName != '<Right>') {
  2401. vimGlobalState.searchHistoryController.reset();
  2402. }
  2403. var parsedQuery;
  2404. try {
  2405. parsedQuery = updateSearchQuery(cm, query, true /** ignoreCase */, true /** smartCase */);
  2406. }
  2407. catch (e) {
  2408. }
  2409. if (parsedQuery) {
  2410. cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
  2411. }
  2412. else {
  2413. clearSearchHighlight(cm);
  2414. cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
  2415. }
  2416. }
  2417. function onPromptKeyDown(e, query, close) {
  2418. var keyName = vimKeyFromEvent(e);
  2419. if (keyName == '<Esc>' || keyName == '<C-c>' || keyName == '<C-[>' ||
  2420. (keyName == '<BS>' && query == '')) {
  2421. vimGlobalState.searchHistoryController.pushInput(query);
  2422. vimGlobalState.searchHistoryController.reset();
  2423. updateSearchQuery(cm, originalQuery);
  2424. clearSearchHighlight(cm);
  2425. cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
  2426. CodeMirror.e_stop(e);
  2427. clearInputState(cm);
  2428. close();
  2429. cm.focus();
  2430. }
  2431. else if (keyName == '<Up>' || keyName == '<Down>') {
  2432. CodeMirror.e_stop(e);
  2433. }
  2434. else if (keyName == '<C-u>') {
  2435. CodeMirror.e_stop(e);
  2436. close('');
  2437. }
  2438. }
  2439. switch (command.searchArgs.querySrc) {
  2440. case 'prompt':
  2441. var macroModeState = vimGlobalState.macroModeState;
  2442. if (macroModeState.isPlaying) {
  2443. var query = macroModeState.replaySearchQueries.shift();
  2444. handleQuery(query, true /** ignoreCase */, false /** smartCase */);
  2445. }
  2446. else {
  2447. showPrompt(cm, {
  2448. onClose: onPromptClose,
  2449. prefix: promptPrefix,
  2450. desc: '(JavaScript regexp)',
  2451. onKeyUp: onPromptKeyUp,
  2452. onKeyDown: onPromptKeyDown
  2453. });
  2454. }
  2455. break;
  2456. case 'wordUnderCursor':
  2457. var word = expandWordUnderCursor(cm, { noSymbol: true });
  2458. var isKeyword = true;
  2459. if (!word) {
  2460. word = expandWordUnderCursor(cm, { noSymbol: false });
  2461. isKeyword = false;
  2462. }
  2463. if (!word) {
  2464. showConfirm(cm, 'No word under cursor');
  2465. clearInputState(cm);
  2466. return;
  2467. }
  2468. var query = cm.getLine(word.start.line).substring(word.start.ch, word.end.ch);
  2469. if (isKeyword && wholeWordOnly) {
  2470. query = '\\b' + query + '\\b';
  2471. }
  2472. else {
  2473. query = escapeRegex(query);
  2474. }
  2475. vimGlobalState.jumpList.cachedCursor = cm.getCursor();
  2476. cm.setCursor(word.start);
  2477. handleQuery(query, true /** ignoreCase */, false /** smartCase */);
  2478. break;
  2479. }
  2480. },
  2481. processEx: function (cm, vim, command) {
  2482. function onPromptClose(input) {
  2483. vimGlobalState.exCommandHistoryController.pushInput(input);
  2484. vimGlobalState.exCommandHistoryController.reset();
  2485. exCommandDispatcher.processCommand(cm, input);
  2486. if (cm.state.vim)
  2487. clearInputState(cm);
  2488. }
  2489. function onPromptKeyDown(e, input, close) {
  2490. var keyName = vimKeyFromEvent(e), up, offset;
  2491. if (keyName == '<Esc>' || keyName == '<C-c>' || keyName == '<C-[>' ||
  2492. (keyName == '<BS>' && input == '')) {
  2493. vimGlobalState.exCommandHistoryController.pushInput(input);
  2494. vimGlobalState.exCommandHistoryController.reset();
  2495. CodeMirror.e_stop(e);
  2496. clearInputState(cm);
  2497. close();
  2498. cm.focus();
  2499. }
  2500. if (keyName == '<Up>' || keyName == '<Down>') {
  2501. CodeMirror.e_stop(e);
  2502. up = keyName == '<Up>' ? true : false;
  2503. offset = e.target ? e.target.selectionEnd : 0;
  2504. input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
  2505. close(input);
  2506. if (offset && e.target)
  2507. e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
  2508. }
  2509. else if (keyName == '<C-u>') {
  2510. CodeMirror.e_stop(e);
  2511. close('');
  2512. }
  2513. else if (keyName && keyName != '<Left>' && keyName != '<Right>') {
  2514. vimGlobalState.exCommandHistoryController.reset();
  2515. }
  2516. }
  2517. if (command.type == 'keyToEx') {
  2518. exCommandDispatcher.processCommand(cm, command.exArgs.input);
  2519. }
  2520. else {
  2521. if (vim.visualMode) {
  2522. showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
  2523. onKeyDown: onPromptKeyDown, selectValueOnOpen: false });
  2524. }
  2525. else {
  2526. showPrompt(cm, { onClose: onPromptClose, prefix: ':',
  2527. onKeyDown: onPromptKeyDown });
  2528. }
  2529. }
  2530. },
  2531. evalInput: function (cm, vim) {
  2532. var inputState = vim.inputState;
  2533. var motion = inputState.motion;
  2534. var motionArgs = inputState.motionArgs || {};
  2535. var operator = inputState.operator;
  2536. var operatorArgs = inputState.operatorArgs || {};
  2537. var registerName = inputState.registerName;
  2538. var sel = vim.sel;
  2539. var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head) : cm.getCursor('head'));
  2540. var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));
  2541. var oldHead = copyCursor(origHead);
  2542. var oldAnchor = copyCursor(origAnchor);
  2543. var newHead, newAnchor;
  2544. var repeat;
  2545. if (operator) {
  2546. this.recordLastEdit(vim, inputState);
  2547. }
  2548. if (inputState.repeatOverride !== undefined) {
  2549. repeat = inputState.repeatOverride;
  2550. }
  2551. else {
  2552. repeat = inputState.getRepeat();
  2553. }
  2554. if (repeat > 0 && motionArgs.explicitRepeat) {
  2555. motionArgs.repeatIsExplicit = true;
  2556. }
  2557. else if (motionArgs.noRepeat ||
  2558. (!motionArgs.explicitRepeat && repeat === 0)) {
  2559. repeat = 1;
  2560. motionArgs.repeatIsExplicit = false;
  2561. }
  2562. if (inputState.selectedCharacter) {
  2563. motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
  2564. inputState.selectedCharacter;
  2565. }
  2566. motionArgs.repeat = repeat;
  2567. clearInputState(cm);
  2568. if (motion) {
  2569. var motionResult = motions[motion](cm, origHead, motionArgs, vim, inputState);
  2570. vim.lastMotion = motions[motion];
  2571. if (!motionResult) {
  2572. return;
  2573. }
  2574. if (motionArgs.toJumplist) {
  2575. if (!operator && cm.ace.curOp != null)
  2576. cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace_patch
  2577. var jumpList = vimGlobalState.jumpList;
  2578. var cachedCursor = jumpList.cachedCursor;
  2579. if (cachedCursor) {
  2580. recordJumpPosition(cm, cachedCursor, motionResult);
  2581. delete jumpList.cachedCursor;
  2582. }
  2583. else {
  2584. recordJumpPosition(cm, origHead, motionResult);
  2585. }
  2586. }
  2587. if (motionResult instanceof Array) {
  2588. newAnchor = motionResult[0];
  2589. newHead = motionResult[1];
  2590. }
  2591. else {
  2592. newHead = motionResult;
  2593. }
  2594. if (!newHead) {
  2595. newHead = copyCursor(origHead);
  2596. }
  2597. if (vim.visualMode) {
  2598. if (!(vim.visualBlock && newHead.ch === Infinity)) {
  2599. newHead = clipCursorToContent(cm, newHead, oldHead);
  2600. }
  2601. if (newAnchor) {
  2602. newAnchor = clipCursorToContent(cm, newAnchor);
  2603. }
  2604. newAnchor = newAnchor || oldAnchor;
  2605. sel.anchor = newAnchor;
  2606. sel.head = newHead;
  2607. updateCmSelection(cm);
  2608. updateMark(cm, vim, '<', cursorIsBefore(newAnchor, newHead) ? newAnchor
  2609. : newHead);
  2610. updateMark(cm, vim, '>', cursorIsBefore(newAnchor, newHead) ? newHead
  2611. : newAnchor);
  2612. }
  2613. else if (!operator) {
  2614. if (cm.ace.curOp)
  2615. cm.ace.curOp.vimDialogScroll = "center-animate"; // ace_patch
  2616. newHead = clipCursorToContent(cm, newHead, oldHead);
  2617. cm.setCursor(newHead.line, newHead.ch);
  2618. }
  2619. }
  2620. if (operator) {
  2621. if (operatorArgs.lastSel) {
  2622. newAnchor = oldAnchor;
  2623. var lastSel = operatorArgs.lastSel;
  2624. var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
  2625. var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
  2626. if (lastSel.visualLine) {
  2627. newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
  2628. }
  2629. else if (lastSel.visualBlock) {
  2630. newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
  2631. }
  2632. else if (lastSel.head.line == lastSel.anchor.line) {
  2633. newHead = new Pos(oldAnchor.line, oldAnchor.ch + chOffset);
  2634. }
  2635. else {
  2636. newHead = new Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
  2637. }
  2638. vim.visualMode = true;
  2639. vim.visualLine = lastSel.visualLine;
  2640. vim.visualBlock = lastSel.visualBlock;
  2641. sel = vim.sel = {
  2642. anchor: newAnchor,
  2643. head: newHead
  2644. };
  2645. updateCmSelection(cm);
  2646. }
  2647. else if (vim.visualMode) {
  2648. operatorArgs.lastSel = {
  2649. anchor: copyCursor(sel.anchor),
  2650. head: copyCursor(sel.head),
  2651. visualBlock: vim.visualBlock,
  2652. visualLine: vim.visualLine
  2653. };
  2654. }
  2655. var curStart, curEnd, linewise, mode;
  2656. var cmSel;
  2657. if (vim.visualMode) {
  2658. curStart = cursorMin(sel.head, sel.anchor);
  2659. curEnd = cursorMax(sel.head, sel.anchor);
  2660. linewise = vim.visualLine || operatorArgs.linewise;
  2661. mode = vim.visualBlock ? 'block' :
  2662. linewise ? 'line' :
  2663. 'char';
  2664. var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
  2665. cmSel = makeCmSelection(cm, {
  2666. anchor: newPositions.start,
  2667. head: newPositions.end
  2668. }, mode);
  2669. if (linewise) {
  2670. var ranges = cmSel.ranges;
  2671. if (mode == 'block') {
  2672. for (var i = 0; i < ranges.length; i++) {
  2673. ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
  2674. }
  2675. }
  2676. else if (mode == 'line') {
  2677. ranges[0].head = new Pos(ranges[0].head.line + 1, 0);
  2678. }
  2679. }
  2680. }
  2681. else {
  2682. curStart = copyCursor(newAnchor || oldAnchor);
  2683. curEnd = copyCursor(newHead || oldHead);
  2684. if (cursorIsBefore(curEnd, curStart)) {
  2685. var tmp = curStart;
  2686. curStart = curEnd;
  2687. curEnd = tmp;
  2688. }
  2689. linewise = motionArgs.linewise || operatorArgs.linewise;
  2690. if (linewise) {
  2691. expandSelectionToLine(cm, curStart, curEnd);
  2692. }
  2693. else if (motionArgs.forward) {
  2694. clipToLine(cm, curStart, curEnd);
  2695. }
  2696. mode = 'char';
  2697. var exclusive = !motionArgs.inclusive || linewise;
  2698. var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
  2699. cmSel = makeCmSelection(cm, {
  2700. anchor: newPositions.start,
  2701. head: newPositions.end
  2702. }, mode, exclusive);
  2703. }
  2704. cm.setSelections(cmSel.ranges, cmSel.primary);
  2705. vim.lastMotion = null;
  2706. operatorArgs.repeat = repeat; // For indent in visual mode.
  2707. operatorArgs.registerName = registerName;
  2708. operatorArgs.linewise = linewise;
  2709. var operatorMoveTo = operators[operator](cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
  2710. if (vim.visualMode) {
  2711. exitVisualMode(cm, operatorMoveTo != null);
  2712. }
  2713. if (operatorMoveTo) {
  2714. cm.setCursor(operatorMoveTo);
  2715. }
  2716. }
  2717. },
  2718. recordLastEdit: function (vim, inputState, actionCommand) {
  2719. var macroModeState = vimGlobalState.macroModeState;
  2720. if (macroModeState.isPlaying) {
  2721. return;
  2722. }
  2723. vim.lastEditInputState = inputState;
  2724. vim.lastEditActionCommand = actionCommand;
  2725. macroModeState.lastInsertModeChanges.changes = [];
  2726. macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
  2727. macroModeState.lastInsertModeChanges.visualBlock = vim.visualBlock ? vim.sel.head.line - vim.sel.anchor.line : 0;
  2728. }
  2729. };
  2730. var motions = {
  2731. moveToTopLine: function (cm, _head, motionArgs) {
  2732. var line = getUserVisibleLines(cm).top + motionArgs.repeat - 1;
  2733. return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
  2734. },
  2735. moveToMiddleLine: function (cm) {
  2736. var range = getUserVisibleLines(cm);
  2737. var line = Math.floor((range.top + range.bottom) * 0.5);
  2738. return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
  2739. },
  2740. moveToBottomLine: function (cm, _head, motionArgs) {
  2741. var line = getUserVisibleLines(cm).bottom - motionArgs.repeat + 1;
  2742. return new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
  2743. },
  2744. expandToLine: function (_cm, head, motionArgs) {
  2745. var cur = head;
  2746. return new Pos(cur.line + motionArgs.repeat - 1, Infinity);
  2747. },
  2748. findNext: function (cm, _head, motionArgs) {
  2749. var state = getSearchState(cm);
  2750. var query = state.getQuery();
  2751. if (!query) {
  2752. return;
  2753. }
  2754. var prev = !motionArgs.forward;
  2755. prev = (state.isReversed()) ? !prev : prev;
  2756. highlightSearchMatches(cm, query);
  2757. return findNext(cm, prev /** prev */, query, motionArgs.repeat);
  2758. },
  2759. findAndSelectNextInclusive: function (cm, _head, motionArgs, vim, prevInputState) {
  2760. var state = getSearchState(cm);
  2761. var query = state.getQuery();
  2762. if (!query) {
  2763. return;
  2764. }
  2765. var prev = !motionArgs.forward;
  2766. prev = (state.isReversed()) ? !prev : prev;
  2767. var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim);
  2768. if (!next) {
  2769. return;
  2770. }
  2771. if (prevInputState.operator) {
  2772. return next;
  2773. }
  2774. var from = next[0];
  2775. var to = new Pos(next[1].line, next[1].ch - 1);
  2776. if (vim.visualMode) {
  2777. if (vim.visualLine || vim.visualBlock) {
  2778. vim.visualLine = false;
  2779. vim.visualBlock = false;
  2780. CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" });
  2781. }
  2782. var anchor = vim.sel.anchor;
  2783. if (anchor) {
  2784. if (state.isReversed()) {
  2785. if (motionArgs.forward) {
  2786. return [anchor, from];
  2787. }
  2788. return [anchor, to];
  2789. }
  2790. else {
  2791. if (motionArgs.forward) {
  2792. return [anchor, to];
  2793. }
  2794. return [anchor, from];
  2795. }
  2796. }
  2797. }
  2798. else {
  2799. vim.visualMode = true;
  2800. vim.visualLine = false;
  2801. vim.visualBlock = false;
  2802. CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" });
  2803. }
  2804. return prev ? [to, from] : [from, to];
  2805. },
  2806. goToMark: function (cm, _head, motionArgs, vim) {
  2807. var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);
  2808. if (pos) {
  2809. return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
  2810. }
  2811. return null;
  2812. },
  2813. moveToOtherHighlightedEnd: function (cm, _head, motionArgs, vim) {
  2814. if (vim.visualBlock && motionArgs.sameLine) {
  2815. var sel = vim.sel;
  2816. return [
  2817. clipCursorToContent(cm, new Pos(sel.anchor.line, sel.head.ch)),
  2818. clipCursorToContent(cm, new Pos(sel.head.line, sel.anchor.ch))
  2819. ];
  2820. }
  2821. else {
  2822. return ([vim.sel.head, vim.sel.anchor]);
  2823. }
  2824. },
  2825. jumpToMark: function (cm, head, motionArgs, vim) {
  2826. var best = head;
  2827. for (var i = 0; i < motionArgs.repeat; i++) {
  2828. var cursor = best;
  2829. for (var key in vim.marks) {
  2830. if (!isLowerCase(key)) {
  2831. continue;
  2832. }
  2833. var mark = vim.marks[key].find();
  2834. var isWrongDirection = (motionArgs.forward) ?
  2835. cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);
  2836. if (isWrongDirection) {
  2837. continue;
  2838. }
  2839. if (motionArgs.linewise && (mark.line == cursor.line)) {
  2840. continue;
  2841. }
  2842. var equal = cursorEqual(cursor, best);
  2843. var between = (motionArgs.forward) ?
  2844. cursorIsBetween(cursor, mark, best) :
  2845. cursorIsBetween(best, mark, cursor);
  2846. if (equal || between) {
  2847. best = mark;
  2848. }
  2849. }
  2850. }
  2851. if (motionArgs.linewise) {
  2852. best = new Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));
  2853. }
  2854. return best;
  2855. },
  2856. moveByCharacters: function (_cm, head, motionArgs) {
  2857. var cur = head;
  2858. var repeat = motionArgs.repeat;
  2859. var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
  2860. return new Pos(cur.line, ch);
  2861. },
  2862. moveByLines: function (cm, head, motionArgs, vim) {
  2863. var cur = head;
  2864. var endCh = cur.ch;
  2865. switch (vim.lastMotion) {
  2866. case this.moveByLines:
  2867. case this.moveByDisplayLines:
  2868. case this.moveByScroll:
  2869. case this.moveToColumn:
  2870. case this.moveToEol:
  2871. endCh = vim.lastHPos;
  2872. break;
  2873. default:
  2874. vim.lastHPos = endCh;
  2875. }
  2876. var repeat = motionArgs.repeat + (motionArgs.repeatOffset || 0);
  2877. var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
  2878. var first = cm.firstLine();
  2879. var last = cm.lastLine();
  2880. if (line < first && cur.line == first) {
  2881. return this.moveToStartOfLine(cm, head, motionArgs, vim);
  2882. }
  2883. else if (line > last && cur.line == last) {
  2884. return moveToEol(cm, head, motionArgs, vim, true);
  2885. }
  2886. var fold = cm.ace.session.getFoldLine(line);
  2887. if (fold) {
  2888. if (motionArgs.forward) {
  2889. if (line > fold.start.row)
  2890. line = fold.end.row + 1;
  2891. }
  2892. else {
  2893. line = fold.start.row;
  2894. }
  2895. }
  2896. if (motionArgs.toFirstChar) {
  2897. endCh = findFirstNonWhiteSpaceCharacter(cm.getLine(line));
  2898. vim.lastHPos = endCh;
  2899. }
  2900. vim.lastHSPos = cm.charCoords(new Pos(line, endCh), 'div').left;
  2901. return new Pos(line, endCh);
  2902. },
  2903. moveByDisplayLines: function (cm, head, motionArgs, vim) {
  2904. var cur = head;
  2905. switch (vim.lastMotion) {
  2906. case this.moveByDisplayLines:
  2907. case this.moveByScroll:
  2908. case this.moveByLines:
  2909. case this.moveToColumn:
  2910. case this.moveToEol:
  2911. break;
  2912. default:
  2913. vim.lastHSPos = cm.charCoords(cur, 'div').left;
  2914. }
  2915. var repeat = motionArgs.repeat;
  2916. var res = cm.findPosV(cur, (motionArgs.forward ? repeat : -repeat), 'line', vim.lastHSPos);
  2917. if (res.hitSide) {
  2918. if (motionArgs.forward) {
  2919. var lastCharCoords = cm.charCoords(res, 'div');
  2920. var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };
  2921. var res = cm.coordsChar(goalCoords, 'div');
  2922. }
  2923. else {
  2924. var resCoords = cm.charCoords(new Pos(cm.firstLine(), 0), 'div');
  2925. resCoords.left = vim.lastHSPos;
  2926. res = cm.coordsChar(resCoords, 'div');
  2927. }
  2928. }
  2929. vim.lastHPos = res.ch;
  2930. return res;
  2931. },
  2932. moveByPage: function (cm, head, motionArgs) {
  2933. var curStart = head;
  2934. var repeat = motionArgs.repeat;
  2935. return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
  2936. },
  2937. moveByParagraph: function (cm, head, motionArgs) {
  2938. var dir = motionArgs.forward ? 1 : -1;
  2939. return findParagraph(cm, head, motionArgs.repeat, dir);
  2940. },
  2941. moveBySentence: function (cm, head, motionArgs) {
  2942. var dir = motionArgs.forward ? 1 : -1;
  2943. return findSentence(cm, head, motionArgs.repeat, dir);
  2944. },
  2945. moveByScroll: function (cm, head, motionArgs, vim) {
  2946. var scrollbox = cm.getScrollInfo();
  2947. var curEnd = null;
  2948. var repeat = motionArgs.repeat;
  2949. if (!repeat) {
  2950. repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
  2951. }
  2952. var orig = cm.charCoords(head, 'local');
  2953. motionArgs.repeat = repeat;
  2954. curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
  2955. if (!curEnd) {
  2956. return null;
  2957. }
  2958. var dest = cm.charCoords(curEnd, 'local');
  2959. cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
  2960. return curEnd;
  2961. },
  2962. moveByWords: function (cm, head, motionArgs) {
  2963. return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward, !!motionArgs.wordEnd, !!motionArgs.bigWord);
  2964. },
  2965. moveTillCharacter: function (cm, head, motionArgs) {
  2966. var repeat = motionArgs.repeat;
  2967. var curEnd = moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter, head);
  2968. var increment = motionArgs.forward ? -1 : 1;
  2969. recordLastCharacterSearch(increment, motionArgs);
  2970. if (!curEnd)
  2971. return null;
  2972. curEnd.ch += increment;
  2973. return curEnd;
  2974. },
  2975. moveToCharacter: function (cm, head, motionArgs) {
  2976. var repeat = motionArgs.repeat;
  2977. recordLastCharacterSearch(0, motionArgs);
  2978. return moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter, head) || head;
  2979. },
  2980. moveToSymbol: function (cm, head, motionArgs) {
  2981. var repeat = motionArgs.repeat;
  2982. return findSymbol(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || head;
  2983. },
  2984. moveToColumn: function (cm, head, motionArgs, vim) {
  2985. var repeat = motionArgs.repeat;
  2986. vim.lastHPos = repeat - 1;
  2987. vim.lastHSPos = cm.charCoords(head, 'div').left;
  2988. return moveToColumn(cm, repeat);
  2989. },
  2990. moveToEol: function (cm, head, motionArgs, vim) {
  2991. return moveToEol(cm, head, motionArgs, vim, false);
  2992. },
  2993. moveToFirstNonWhiteSpaceCharacter: function (cm, head) {
  2994. var cursor = head;
  2995. return new Pos(cursor.line, findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
  2996. },
  2997. moveToMatchedSymbol: function (cm, head) {
  2998. var cursor = head;
  2999. var line = cursor.line;
  3000. var ch = cursor.ch;
  3001. var lineText = cm.getLine(line);
  3002. var symbol;
  3003. for (; ch < lineText.length; ch++) {
  3004. symbol = lineText.charAt(ch);
  3005. if (symbol && isMatchableSymbol(symbol)) {
  3006. var style = cm.getTokenTypeAt(new Pos(line, ch + 1));
  3007. if (style !== "string" && style !== "comment") {
  3008. break;
  3009. }
  3010. }
  3011. }
  3012. if (ch < lineText.length) {
  3013. var re = /[<>]/.test(lineText[ch]) ? /[(){}[\]<>]/ : /[(){}[\]]/; //ace_patch?
  3014. var matched = cm.findMatchingBracket(new Pos(line, ch + 1), { bracketRegex: re });
  3015. return matched.to;
  3016. }
  3017. else {
  3018. return cursor;
  3019. }
  3020. },
  3021. moveToStartOfLine: function (_cm, head) {
  3022. return new Pos(head.line, 0);
  3023. },
  3024. moveToLineOrEdgeOfDocument: function (cm, _head, motionArgs) {
  3025. var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
  3026. if (motionArgs.repeatIsExplicit) {
  3027. lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
  3028. }
  3029. return new Pos(lineNum, findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
  3030. },
  3031. moveToStartOfDisplayLine: function (cm) {
  3032. cm.execCommand("goLineLeft");
  3033. return cm.getCursor();
  3034. },
  3035. moveToEndOfDisplayLine: function (cm) {
  3036. cm.execCommand("goLineRight");
  3037. var head = cm.getCursor();
  3038. if (head.sticky == "before")
  3039. head.ch--;
  3040. return head;
  3041. },
  3042. textObjectManipulation: function (cm, head, motionArgs, vim) {
  3043. var mirroredPairs = { '(': ')', ')': '(',
  3044. '{': '}', '}': '{',
  3045. '[': ']', ']': '[',
  3046. '<': '>', '>': '<' };
  3047. var selfPaired = { '\'': true, '"': true, '`': true };
  3048. var character = motionArgs.selectedCharacter;
  3049. if (character == 'b') {
  3050. character = '(';
  3051. }
  3052. else if (character == 'B') {
  3053. character = '{';
  3054. }
  3055. var inclusive = !motionArgs.textObjectInner;
  3056. var tmp, move;
  3057. if (mirroredPairs[character]) {
  3058. move = true;
  3059. tmp = selectCompanionObject(cm, head, character, inclusive);
  3060. if (!tmp) {
  3061. var sc = cm.getSearchCursor(new RegExp("\\" + character, "g"), head);
  3062. if (sc.find()) {
  3063. tmp = selectCompanionObject(cm, sc.from(), character, inclusive);
  3064. }
  3065. }
  3066. }
  3067. else if (selfPaired[character]) {
  3068. move = true;
  3069. tmp = findBeginningAndEnd(cm, head, character, inclusive);
  3070. }
  3071. else if (character === 'W' || character === 'w') {
  3072. var repeat = motionArgs.repeat || 1;
  3073. while (repeat-- > 0) {
  3074. var repeated = expandWordUnderCursor(cm, {
  3075. inclusive: inclusive,
  3076. innerWord: !inclusive,
  3077. bigWord: character === 'W',
  3078. noSymbol: character === 'W',
  3079. multiline: true
  3080. }, tmp && tmp.end);
  3081. if (repeated) {
  3082. if (!tmp)
  3083. tmp = repeated;
  3084. tmp.end = repeated.end;
  3085. }
  3086. }
  3087. }
  3088. else if (character === 'p') {
  3089. tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
  3090. motionArgs.linewise = true;
  3091. if (vim.visualMode) {
  3092. if (!vim.visualLine) {
  3093. vim.visualLine = true;
  3094. }
  3095. }
  3096. else {
  3097. var operatorArgs = vim.inputState.operatorArgs;
  3098. if (operatorArgs) {
  3099. operatorArgs.linewise = true;
  3100. }
  3101. tmp.end.line--;
  3102. }
  3103. }
  3104. else if (character === 't') {
  3105. tmp = expandTagUnderCursor(cm, head, inclusive);
  3106. }
  3107. else if (character === 's') {
  3108. var content = cm.getLine(head.line);
  3109. if (head.ch > 0 && isEndOfSentenceSymbol(content[head.ch])) {
  3110. head.ch -= 1;
  3111. }
  3112. var end = getSentence(cm, head, motionArgs.repeat, 1, inclusive);
  3113. var start = getSentence(cm, head, motionArgs.repeat, -1, inclusive);
  3114. if (isWhiteSpaceString(cm.getLine(start.line)[start.ch])
  3115. && isWhiteSpaceString(cm.getLine(end.line)[end.ch - 1])) {
  3116. start = { line: start.line, ch: start.ch + 1 };
  3117. }
  3118. tmp = { start: start, end: end };
  3119. }
  3120. if (!tmp) {
  3121. return null;
  3122. }
  3123. if (!cm.state.vim.visualMode) {
  3124. return [tmp.start, tmp.end];
  3125. }
  3126. else {
  3127. return expandSelection(cm, tmp.start, tmp.end, move);
  3128. }
  3129. },
  3130. repeatLastCharacterSearch: function (cm, head, motionArgs) {
  3131. var lastSearch = vimGlobalState.lastCharacterSearch;
  3132. var repeat = motionArgs.repeat;
  3133. var forward = motionArgs.forward === lastSearch.forward;
  3134. var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
  3135. cm.moveH(-increment, 'char');
  3136. motionArgs.inclusive = forward ? true : false;
  3137. var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
  3138. if (!curEnd) {
  3139. cm.moveH(increment, 'char');
  3140. return head;
  3141. }
  3142. curEnd.ch += increment;
  3143. return curEnd;
  3144. }
  3145. };
  3146. function defineMotion(name, fn) {
  3147. motions[name] = fn;
  3148. }
  3149. function fillArray(val, times) {
  3150. var arr = [];
  3151. for (var i = 0; i < times; i++) {
  3152. arr.push(val);
  3153. }
  3154. return arr;
  3155. }
  3156. var operators = {
  3157. change: function (cm, args, ranges) {
  3158. var finalHead, text;
  3159. var vim = cm.state.vim;
  3160. var anchor = ranges[0].anchor, head = ranges[0].head;
  3161. if (!vim.visualMode) {
  3162. text = cm.getRange(anchor, head);
  3163. var lastState = vim.lastEditInputState || {};
  3164. if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) {
  3165. var match = (/\s+$/).exec(text);
  3166. if (match && lastState.motionArgs && lastState.motionArgs.forward) {
  3167. head = offsetCursor(head, 0, -match[0].length);
  3168. text = text.slice(0, -match[0].length);
  3169. }
  3170. }
  3171. if (args.linewise) {
  3172. anchor = new Pos(anchor.line, findFirstNonWhiteSpaceCharacter(cm.getLine(anchor.line)));
  3173. if (head.line > anchor.line) {
  3174. head = new Pos(head.line - 1, Number.MAX_VALUE);
  3175. }
  3176. }
  3177. cm.replaceRange('', anchor, head);
  3178. finalHead = anchor;
  3179. }
  3180. else if (args.fullLine) {
  3181. head.ch = Number.MAX_VALUE;
  3182. head.line--;
  3183. cm.setSelection(anchor, head);
  3184. text = cm.getSelection();
  3185. cm.replaceSelection("");
  3186. finalHead = anchor;
  3187. }
  3188. else {
  3189. text = cm.getSelection();
  3190. var replacement = fillArray('', ranges.length);
  3191. cm.replaceSelections(replacement);
  3192. finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
  3193. }
  3194. vimGlobalState.registerController.pushText(args.registerName, 'change', text, args.linewise, ranges.length > 1);
  3195. actions.enterInsertMode(cm, { head: finalHead }, cm.state.vim);
  3196. },
  3197. 'delete': function (cm, args, ranges) {
  3198. var finalHead, text;
  3199. var vim = cm.state.vim;
  3200. if (!vim.visualBlock) {
  3201. var anchor = ranges[0].anchor, head = ranges[0].head;
  3202. if (args.linewise &&
  3203. head.line != cm.firstLine() &&
  3204. anchor.line == cm.lastLine() &&
  3205. anchor.line == head.line - 1) {
  3206. if (anchor.line == cm.firstLine()) {
  3207. anchor.ch = 0;
  3208. }
  3209. else {
  3210. anchor = new Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
  3211. }
  3212. }
  3213. text = cm.getRange(anchor, head);
  3214. cm.replaceRange('', anchor, head);
  3215. finalHead = anchor;
  3216. if (args.linewise) {
  3217. finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
  3218. }
  3219. }
  3220. else {
  3221. text = cm.getSelection();
  3222. var replacement = fillArray('', ranges.length);
  3223. cm.replaceSelections(replacement);
  3224. finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
  3225. }
  3226. vimGlobalState.registerController.pushText(args.registerName, 'delete', text, args.linewise, vim.visualBlock);
  3227. return clipCursorToContent(cm, finalHead);
  3228. },
  3229. indent: function (cm, args, ranges) {
  3230. var vim = cm.state.vim;
  3231. var repeat = (vim.visualMode) ? args.repeat : 1;
  3232. if (vim.visualBlock) {
  3233. var tabSize = cm.getOption('tabSize');
  3234. var indent = cm.getOption('indentWithTabs') ? '\t' : ' '.repeat(tabSize);
  3235. var cursor;
  3236. for (var i = ranges.length - 1; i >= 0; i--) {
  3237. cursor = cursorMin(ranges[i].anchor, ranges[i].head);
  3238. if (args.indentRight) {
  3239. cm.replaceRange(indent.repeat(repeat), cursor, cursor);
  3240. }
  3241. else {
  3242. var text = cm.getLine(cursor.line);
  3243. var end = 0;
  3244. for (var j = 0; j < repeat; j++) {
  3245. var ch = text[cursor.ch + end];
  3246. if (ch == '\t') {
  3247. end++;
  3248. }
  3249. else if (ch == ' ') {
  3250. end++;
  3251. for (var k = 1; k < indent.length; k++) {
  3252. ch = text[cursor.ch + end];
  3253. if (ch !== ' ')
  3254. break;
  3255. end++;
  3256. }
  3257. }
  3258. else {
  3259. break;
  3260. }
  3261. }
  3262. cm.replaceRange('', cursor, offsetCursor(cursor, 0, end));
  3263. }
  3264. }
  3265. return cursor;
  3266. }
  3267. else if (cm.indentMore) {
  3268. for (var j = 0; j < repeat; j++) {
  3269. if (args.indentRight)
  3270. cm.indentMore();
  3271. else
  3272. cm.indentLess();
  3273. }
  3274. }
  3275. else {
  3276. var startLine = ranges[0].anchor.line;
  3277. var endLine = vim.visualBlock ?
  3278. ranges[ranges.length - 1].anchor.line :
  3279. ranges[0].head.line;
  3280. if (args.linewise) {
  3281. endLine--;
  3282. }
  3283. for (var i = startLine; i <= endLine; i++) {
  3284. for (var j = 0; j < repeat; j++) {
  3285. cm.indentLine(i, args.indentRight);
  3286. }
  3287. }
  3288. }
  3289. return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
  3290. },
  3291. indentAuto: function (cm, _args, ranges) {
  3292. cm.execCommand("indentAuto");
  3293. return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
  3294. },
  3295. hardWrap: function (cm, operatorArgs, ranges, oldAnchor, newHead) {
  3296. if (!cm.hardWrap)
  3297. return;
  3298. var from = ranges[0].anchor.line;
  3299. var to = ranges[0].head.line;
  3300. if (operatorArgs.linewise)
  3301. to--;
  3302. var endRow = cm.hardWrap({ from: from, to: to });
  3303. if (endRow > from && operatorArgs.linewise)
  3304. endRow--;
  3305. return operatorArgs.keepCursor ? oldAnchor : new Pos(endRow, 0);
  3306. },
  3307. changeCase: function (cm, args, ranges, oldAnchor, newHead) {
  3308. var selections = cm.getSelections();
  3309. var swapped = [];
  3310. var toLower = args.toLower;
  3311. for (var j = 0; j < selections.length; j++) {
  3312. var toSwap = selections[j];
  3313. var text = '';
  3314. if (toLower === true) {
  3315. text = toSwap.toLowerCase();
  3316. }
  3317. else if (toLower === false) {
  3318. text = toSwap.toUpperCase();
  3319. }
  3320. else {
  3321. for (var i = 0; i < toSwap.length; i++) {
  3322. var character = toSwap.charAt(i);
  3323. text += isUpperCase(character) ? character.toLowerCase() :
  3324. character.toUpperCase();
  3325. }
  3326. }
  3327. swapped.push(text);
  3328. }
  3329. cm.replaceSelections(swapped);
  3330. if (args.shouldMoveCursor) {
  3331. return newHead;
  3332. }
  3333. else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
  3334. return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
  3335. }
  3336. else if (args.linewise) {
  3337. return oldAnchor;
  3338. }
  3339. else {
  3340. return cursorMin(ranges[0].anchor, ranges[0].head);
  3341. }
  3342. },
  3343. yank: function (cm, args, ranges, oldAnchor) {
  3344. var vim = cm.state.vim;
  3345. var text = cm.getSelection();
  3346. var endPos = vim.visualMode
  3347. ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
  3348. : oldAnchor;
  3349. vimGlobalState.registerController.pushText(args.registerName, 'yank', text, args.linewise, vim.visualBlock);
  3350. return endPos;
  3351. }
  3352. };
  3353. function defineOperator(name, fn) {
  3354. operators[name] = fn;
  3355. }
  3356. var actions = {
  3357. jumpListWalk: function (cm, actionArgs, vim) {
  3358. if (vim.visualMode) {
  3359. return;
  3360. }
  3361. var repeat = actionArgs.repeat;
  3362. var forward = actionArgs.forward;
  3363. var jumpList = vimGlobalState.jumpList;
  3364. var mark = jumpList.move(cm, forward ? repeat : -repeat);
  3365. var markPos = mark ? mark.find() : undefined;
  3366. markPos = markPos ? markPos : cm.getCursor();
  3367. cm.setCursor(markPos);
  3368. cm.ace.curOp.command.scrollIntoView = "center-animate"; // ace_patch
  3369. },
  3370. scroll: function (cm, actionArgs, vim) {
  3371. if (vim.visualMode) {
  3372. return;
  3373. }
  3374. var repeat = actionArgs.repeat || 1;
  3375. var lineHeight = cm.defaultTextHeight();
  3376. var top = cm.getScrollInfo().top;
  3377. var delta = lineHeight * repeat;
  3378. var newPos = actionArgs.forward ? top + delta : top - delta;
  3379. var cursor = copyCursor(cm.getCursor());
  3380. var cursorCoords = cm.charCoords(cursor, 'local');
  3381. if (actionArgs.forward) {
  3382. if (newPos > cursorCoords.top) {
  3383. cursor.line += (newPos - cursorCoords.top) / lineHeight;
  3384. cursor.line = Math.ceil(cursor.line);
  3385. cm.setCursor(cursor);
  3386. cursorCoords = cm.charCoords(cursor, 'local');
  3387. cm.scrollTo(null, cursorCoords.top);
  3388. }
  3389. else {
  3390. cm.scrollTo(null, newPos);
  3391. }
  3392. }
  3393. else {
  3394. var newBottom = newPos + cm.getScrollInfo().clientHeight;
  3395. if (newBottom < cursorCoords.bottom) {
  3396. cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
  3397. cursor.line = Math.floor(cursor.line);
  3398. cm.setCursor(cursor);
  3399. cursorCoords = cm.charCoords(cursor, 'local');
  3400. cm.scrollTo(null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);
  3401. }
  3402. else {
  3403. cm.scrollTo(null, newPos);
  3404. }
  3405. }
  3406. },
  3407. scrollToCursor: function (cm, actionArgs) {
  3408. var lineNum = cm.getCursor().line;
  3409. var charCoords = cm.charCoords(new Pos(lineNum, 0), 'local');
  3410. var height = cm.getScrollInfo().clientHeight;
  3411. var y = charCoords.top;
  3412. switch (actionArgs.position) {
  3413. case 'center':
  3414. y = charCoords.bottom - height / 2;
  3415. break;
  3416. case 'bottom':
  3417. var lineLastCharPos = new Pos(lineNum, cm.getLine(lineNum).length - 1);
  3418. var lineLastCharCoords = cm.charCoords(lineLastCharPos, 'local');
  3419. var lineHeight = lineLastCharCoords.bottom - y;
  3420. y = y - height + lineHeight;
  3421. break;
  3422. }
  3423. cm.scrollTo(null, y);
  3424. },
  3425. replayMacro: function (cm, actionArgs, vim) {
  3426. var registerName = actionArgs.selectedCharacter;
  3427. var repeat = actionArgs.repeat;
  3428. var macroModeState = vimGlobalState.macroModeState;
  3429. if (registerName == '@') {
  3430. registerName = macroModeState.latestRegister;
  3431. }
  3432. else {
  3433. macroModeState.latestRegister = registerName;
  3434. }
  3435. while (repeat--) {
  3436. executeMacroRegister(cm, vim, macroModeState, registerName);
  3437. }
  3438. },
  3439. enterMacroRecordMode: function (cm, actionArgs) {
  3440. var macroModeState = vimGlobalState.macroModeState;
  3441. var registerName = actionArgs.selectedCharacter;
  3442. if (vimGlobalState.registerController.isValidRegister(registerName)) {
  3443. macroModeState.enterMacroRecordMode(cm, registerName);
  3444. }
  3445. },
  3446. toggleOverwrite: function (cm) {
  3447. if (!cm.state.overwrite) {
  3448. cm.toggleOverwrite(true);
  3449. cm.setOption('keyMap', 'vim-replace');
  3450. CodeMirror.signal(cm, "vim-mode-change", { mode: "replace" });
  3451. }
  3452. else {
  3453. cm.toggleOverwrite(false);
  3454. cm.setOption('keyMap', 'vim-insert');
  3455. CodeMirror.signal(cm, "vim-mode-change", { mode: "insert" });
  3456. }
  3457. },
  3458. enterInsertMode: function (cm, actionArgs, vim) {
  3459. if (cm.getOption('readOnly')) {
  3460. return;
  3461. }
  3462. vim.insertMode = true;
  3463. vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
  3464. var insertAt = (actionArgs) ? actionArgs.insertAt : null;
  3465. var sel = vim.sel;
  3466. var head = actionArgs.head || cm.getCursor('head');
  3467. var height = cm.listSelections().length;
  3468. if (insertAt == 'eol') {
  3469. head = new Pos(head.line, lineLength(cm, head.line));
  3470. }
  3471. else if (insertAt == 'bol') {
  3472. head = new Pos(head.line, 0);
  3473. }
  3474. else if (insertAt == 'charAfter') {
  3475. var newPosition = updateSelectionForSurrogateCharacters(cm, head, offsetCursor(head, 0, 1));
  3476. head = newPosition.end;
  3477. }
  3478. else if (insertAt == 'firstNonBlank') {
  3479. var newPosition = updateSelectionForSurrogateCharacters(cm, head, motions.moveToFirstNonWhiteSpaceCharacter(cm, head));
  3480. head = newPosition.end;
  3481. }
  3482. else if (insertAt == 'startOfSelectedArea') {
  3483. if (!vim.visualMode)
  3484. return;
  3485. if (!vim.visualBlock) {
  3486. if (sel.head.line < sel.anchor.line) {
  3487. head = sel.head;
  3488. }
  3489. else {
  3490. head = new Pos(sel.anchor.line, 0);
  3491. }
  3492. }
  3493. else {
  3494. head = new Pos(Math.min(sel.head.line, sel.anchor.line), Math.min(sel.head.ch, sel.anchor.ch));
  3495. height = Math.abs(sel.head.line - sel.anchor.line) + 1;
  3496. }
  3497. }
  3498. else if (insertAt == 'endOfSelectedArea') {
  3499. if (!vim.visualMode)
  3500. return;
  3501. if (!vim.visualBlock) {
  3502. if (sel.head.line >= sel.anchor.line) {
  3503. head = offsetCursor(sel.head, 0, 1);
  3504. }
  3505. else {
  3506. head = new Pos(sel.anchor.line, 0);
  3507. }
  3508. }
  3509. else {
  3510. head = new Pos(Math.min(sel.head.line, sel.anchor.line), Math.max(sel.head.ch, sel.anchor.ch) + 1);
  3511. height = Math.abs(sel.head.line - sel.anchor.line) + 1;
  3512. }
  3513. }
  3514. else if (insertAt == 'inplace') {
  3515. if (vim.visualMode) {
  3516. return;
  3517. }
  3518. }
  3519. else if (insertAt == 'lastEdit') {
  3520. head = getLastEditPos(cm) || head;
  3521. }
  3522. cm.setOption('disableInput', false);
  3523. if (actionArgs && actionArgs.replace) {
  3524. cm.toggleOverwrite(true);
  3525. cm.setOption('keyMap', 'vim-replace');
  3526. CodeMirror.signal(cm, "vim-mode-change", { mode: "replace" });
  3527. }
  3528. else {
  3529. cm.toggleOverwrite(false);
  3530. cm.setOption('keyMap', 'vim-insert');
  3531. CodeMirror.signal(cm, "vim-mode-change", { mode: "insert" });
  3532. }
  3533. if (!vimGlobalState.macroModeState.isPlaying) {
  3534. cm.on('change', onChange);
  3535. if (vim.insertEnd)
  3536. vim.insertEnd.clear();
  3537. vim.insertEnd = cm.setBookmark(head, { insertLeft: true });
  3538. CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
  3539. }
  3540. if (vim.visualMode) {
  3541. exitVisualMode(cm);
  3542. }
  3543. selectForInsert(cm, head, height);
  3544. },
  3545. toggleVisualMode: function (cm, actionArgs, vim) {
  3546. var repeat = actionArgs.repeat;
  3547. var anchor = cm.getCursor();
  3548. var head;
  3549. if (!vim.visualMode) {
  3550. vim.visualMode = true;
  3551. vim.visualLine = !!actionArgs.linewise;
  3552. vim.visualBlock = !!actionArgs.blockwise;
  3553. head = clipCursorToContent(cm, new Pos(anchor.line, anchor.ch + repeat - 1));
  3554. var newPosition = updateSelectionForSurrogateCharacters(cm, anchor, head);
  3555. vim.sel = {
  3556. anchor: newPosition.start,
  3557. head: newPosition.end
  3558. };
  3559. CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : "" });
  3560. updateCmSelection(cm);
  3561. updateMark(cm, vim, '<', cursorMin(anchor, head));
  3562. updateMark(cm, vim, '>', cursorMax(anchor, head));
  3563. }
  3564. else if (vim.visualLine ^ actionArgs.linewise ||
  3565. vim.visualBlock ^ actionArgs.blockwise) {
  3566. vim.visualLine = !!actionArgs.linewise;
  3567. vim.visualBlock = !!actionArgs.blockwise;
  3568. CodeMirror.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : "" });
  3569. updateCmSelection(cm);
  3570. }
  3571. else {
  3572. exitVisualMode(cm);
  3573. }
  3574. },
  3575. reselectLastSelection: function (cm, _actionArgs, vim) {
  3576. var lastSelection = vim.lastSelection;
  3577. if (vim.visualMode) {
  3578. updateLastSelection(cm, vim);
  3579. }
  3580. if (lastSelection) {
  3581. var anchor = lastSelection.anchorMark.find();
  3582. var head = lastSelection.headMark.find();
  3583. if (!anchor || !head) {
  3584. return;
  3585. }
  3586. vim.sel = {
  3587. anchor: anchor,
  3588. head: head
  3589. };
  3590. vim.visualMode = true;
  3591. vim.visualLine = lastSelection.visualLine;
  3592. vim.visualBlock = lastSelection.visualBlock;
  3593. updateCmSelection(cm);
  3594. updateMark(cm, vim, '<', cursorMin(anchor, head));
  3595. updateMark(cm, vim, '>', cursorMax(anchor, head));
  3596. CodeMirror.signal(cm, 'vim-mode-change', {
  3597. mode: 'visual',
  3598. subMode: vim.visualLine ? 'linewise' :
  3599. vim.visualBlock ? 'blockwise' : ''
  3600. });
  3601. }
  3602. },
  3603. joinLines: function (cm, actionArgs, vim) {
  3604. var curStart, curEnd;
  3605. if (vim.visualMode) {
  3606. curStart = cm.getCursor('anchor');
  3607. curEnd = cm.getCursor('head');
  3608. if (cursorIsBefore(curEnd, curStart)) {
  3609. var tmp = curEnd;
  3610. curEnd = curStart;
  3611. curStart = tmp;
  3612. }
  3613. curEnd.ch = lineLength(cm, curEnd.line) - 1;
  3614. }
  3615. else {
  3616. var repeat = Math.max(actionArgs.repeat, 2);
  3617. curStart = cm.getCursor();
  3618. curEnd = clipCursorToContent(cm, new Pos(curStart.line + repeat - 1, Infinity));
  3619. }
  3620. var finalCh = 0;
  3621. for (var i = curStart.line; i < curEnd.line; i++) {
  3622. finalCh = lineLength(cm, curStart.line);
  3623. var text = '';
  3624. var nextStartCh = 0;
  3625. if (!actionArgs.keepSpaces) {
  3626. var nextLine = cm.getLine(curStart.line + 1);
  3627. nextStartCh = nextLine.search(/\S/);
  3628. if (nextStartCh == -1) {
  3629. nextStartCh = nextLine.length;
  3630. }
  3631. else {
  3632. text = " ";
  3633. }
  3634. }
  3635. cm.replaceRange(text, new Pos(curStart.line, finalCh), new Pos(curStart.line + 1, nextStartCh));
  3636. }
  3637. var curFinalPos = clipCursorToContent(cm, new Pos(curStart.line, finalCh));
  3638. if (vim.visualMode) {
  3639. exitVisualMode(cm, false);
  3640. }
  3641. cm.setCursor(curFinalPos);
  3642. },
  3643. newLineAndEnterInsertMode: function (cm, actionArgs, vim) {
  3644. vim.insertMode = true;
  3645. var insertAt = copyCursor(cm.getCursor());
  3646. if (insertAt.line === cm.firstLine() && !actionArgs.after) {
  3647. cm.replaceRange('\n', new Pos(cm.firstLine(), 0));
  3648. cm.setCursor(cm.firstLine(), 0);
  3649. }
  3650. else {
  3651. insertAt.line = (actionArgs.after) ? insertAt.line :
  3652. insertAt.line - 1;
  3653. insertAt.ch = lineLength(cm, insertAt.line);
  3654. cm.setCursor(insertAt);
  3655. var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
  3656. CodeMirror.commands.newlineAndIndent;
  3657. newlineFn(cm);
  3658. }
  3659. this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
  3660. },
  3661. paste: function (cm, actionArgs, vim) {
  3662. var _this = this;
  3663. var register = vimGlobalState.registerController.getRegister(actionArgs.registerName);
  3664. var fallback = function () {
  3665. var text = register.toString();
  3666. _this.continuePaste(cm, actionArgs, vim, text, register);
  3667. };
  3668. if (actionArgs.registerName === '+' &&
  3669. typeof navigator !== 'undefined' &&
  3670. typeof navigator.clipboard !== 'undefined' &&
  3671. typeof navigator.clipboard.readText === 'function') {
  3672. navigator.clipboard.readText().then(function (value) {
  3673. _this.continuePaste(cm, actionArgs, vim, value, register);
  3674. }, function () { fallback(); });
  3675. }
  3676. else {
  3677. fallback();
  3678. }
  3679. },
  3680. continuePaste: function (cm, actionArgs, vim, text, register) {
  3681. var cur = copyCursor(cm.getCursor());
  3682. if (!text) {
  3683. return;
  3684. }
  3685. if (actionArgs.matchIndent) {
  3686. var tabSize = cm.getOption("tabSize");
  3687. var whitespaceLength = function (str) {
  3688. var tabs = (str.split("\t").length - 1);
  3689. var spaces = (str.split(" ").length - 1);
  3690. return tabs * tabSize + spaces * 1;
  3691. };
  3692. var currentLine = cm.getLine(cm.getCursor().line);
  3693. var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
  3694. var chompedText = text.replace(/\n$/, '');
  3695. var wasChomped = text !== chompedText;
  3696. var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
  3697. var text = chompedText.replace(/^\s*/gm, function (wspace) {
  3698. var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
  3699. if (newIndent < 0) {
  3700. return "";
  3701. }
  3702. else if (cm.getOption("indentWithTabs")) {
  3703. var quotient = Math.floor(newIndent / tabSize);
  3704. return Array(quotient + 1).join('\t');
  3705. }
  3706. else {
  3707. return Array(newIndent + 1).join(' ');
  3708. }
  3709. });
  3710. text += wasChomped ? "\n" : "";
  3711. }
  3712. if (actionArgs.repeat > 1) {
  3713. var text = Array(actionArgs.repeat + 1).join(text);
  3714. }
  3715. var linewise = register.linewise;
  3716. var blockwise = register.blockwise;
  3717. if (blockwise) {
  3718. text = text.split('\n');
  3719. if (linewise) {
  3720. text.pop();
  3721. }
  3722. for (var i = 0; i < text.length; i++) {
  3723. text[i] = (text[i] == '') ? ' ' : text[i];
  3724. }
  3725. cur.ch += actionArgs.after ? 1 : 0;
  3726. cur.ch = Math.min(lineLength(cm, cur.line), cur.ch);
  3727. }
  3728. else if (linewise) {
  3729. if (vim.visualMode) {
  3730. text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
  3731. }
  3732. else if (actionArgs.after) {
  3733. text = '\n' + text.slice(0, text.length - 1);
  3734. cur.ch = lineLength(cm, cur.line);
  3735. }
  3736. else {
  3737. cur.ch = 0;
  3738. }
  3739. }
  3740. else {
  3741. cur.ch += actionArgs.after ? 1 : 0;
  3742. }
  3743. var curPosFinal;
  3744. if (vim.visualMode) {
  3745. vim.lastPastedText = text;
  3746. var lastSelectionCurEnd;
  3747. var selectedArea = getSelectedAreaRange(cm, vim);
  3748. var selectionStart = selectedArea[0];
  3749. var selectionEnd = selectedArea[1];
  3750. var selectedText = cm.getSelection();
  3751. var selections = cm.listSelections();
  3752. var emptyStrings = new Array(selections.length).join('1').split('1');
  3753. if (vim.lastSelection) {
  3754. lastSelectionCurEnd = vim.lastSelection.headMark.find();
  3755. }
  3756. vimGlobalState.registerController.unnamedRegister.setText(selectedText);
  3757. if (blockwise) {
  3758. cm.replaceSelections(emptyStrings);
  3759. selectionEnd = new Pos(selectionStart.line + text.length - 1, selectionStart.ch);
  3760. cm.setCursor(selectionStart);
  3761. selectBlock(cm, selectionEnd);
  3762. cm.replaceSelections(text);
  3763. curPosFinal = selectionStart;
  3764. }
  3765. else if (vim.visualBlock) {
  3766. cm.replaceSelections(emptyStrings);
  3767. cm.setCursor(selectionStart);
  3768. cm.replaceRange(text, selectionStart, selectionStart);
  3769. curPosFinal = selectionStart;
  3770. }
  3771. else {
  3772. cm.replaceRange(text, selectionStart, selectionEnd);
  3773. curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
  3774. }
  3775. if (lastSelectionCurEnd) {
  3776. vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
  3777. }
  3778. if (linewise) {
  3779. curPosFinal.ch = 0;
  3780. }
  3781. }
  3782. else {
  3783. if (blockwise) {
  3784. cm.setCursor(cur);
  3785. for (var i = 0; i < text.length; i++) {
  3786. var line = cur.line + i;
  3787. if (line > cm.lastLine()) {
  3788. cm.replaceRange('\n', new Pos(line, 0));
  3789. }
  3790. var lastCh = lineLength(cm, line);
  3791. if (lastCh < cur.ch) {
  3792. extendLineToColumn(cm, line, cur.ch);
  3793. }
  3794. }
  3795. cm.setCursor(cur);
  3796. selectBlock(cm, new Pos(cur.line + text.length - 1, cur.ch));
  3797. cm.replaceSelections(text);
  3798. curPosFinal = cur;
  3799. }
  3800. else {
  3801. cm.replaceRange(text, cur);
  3802. if (linewise) {
  3803. var line = actionArgs.after ? cur.line + 1 : cur.line;
  3804. curPosFinal = new Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
  3805. }
  3806. else {
  3807. curPosFinal = copyCursor(cur);
  3808. if (!/\n/.test(text)) {
  3809. curPosFinal.ch += text.length - (actionArgs.after ? 1 : 0);
  3810. }
  3811. }
  3812. }
  3813. }
  3814. if (vim.visualMode) {
  3815. exitVisualMode(cm, false);
  3816. }
  3817. cm.setCursor(curPosFinal);
  3818. },
  3819. undo: function (cm, actionArgs) {
  3820. cm.operation(function () {
  3821. repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
  3822. cm.setCursor(clipCursorToContent(cm, cm.getCursor('start')));
  3823. });
  3824. },
  3825. redo: function (cm, actionArgs) {
  3826. repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
  3827. },
  3828. setRegister: function (_cm, actionArgs, vim) {
  3829. vim.inputState.registerName = actionArgs.selectedCharacter;
  3830. },
  3831. insertRegister: function (cm, actionArgs, vim) {
  3832. var registerName = actionArgs.selectedCharacter;
  3833. var register = vimGlobalState.registerController.getRegister(registerName);
  3834. var text = register && register.toString();
  3835. if (text) {
  3836. cm.replaceSelection(text);
  3837. }
  3838. },
  3839. oneNormalCommand: function (cm, actionArgs, vim) {
  3840. exitInsertMode(cm, true);
  3841. vim.insertModeReturn = true;
  3842. CodeMirror.on(cm, 'vim-command-done', function handler() {
  3843. if (vim.visualMode)
  3844. return;
  3845. if (vim.insertModeReturn) {
  3846. vim.insertModeReturn = false;
  3847. if (!vim.insertMode) {
  3848. actions.enterInsertMode(cm, {}, vim);
  3849. }
  3850. }
  3851. CodeMirror.off(cm, 'vim-command-done', handler);
  3852. });
  3853. },
  3854. setMark: function (cm, actionArgs, vim) {
  3855. var markName = actionArgs.selectedCharacter;
  3856. updateMark(cm, vim, markName, cm.getCursor());
  3857. },
  3858. replace: function (cm, actionArgs, vim) {
  3859. var replaceWith = actionArgs.selectedCharacter;
  3860. var curStart = cm.getCursor();
  3861. var replaceTo;
  3862. var curEnd;
  3863. var selections = cm.listSelections();
  3864. if (vim.visualMode) {
  3865. curStart = cm.getCursor('start');
  3866. curEnd = cm.getCursor('end');
  3867. }
  3868. else {
  3869. var line = cm.getLine(curStart.line);
  3870. replaceTo = curStart.ch + actionArgs.repeat;
  3871. if (replaceTo > line.length) {
  3872. replaceTo = line.length;
  3873. }
  3874. curEnd = new Pos(curStart.line, replaceTo);
  3875. }
  3876. var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
  3877. curStart = newPositions.start;
  3878. curEnd = newPositions.end;
  3879. if (replaceWith == '\n') {
  3880. if (!vim.visualMode)
  3881. cm.replaceRange('', curStart, curEnd);
  3882. (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
  3883. }
  3884. else {
  3885. var replaceWithStr = cm.getRange(curStart, curEnd);
  3886. replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith);
  3887. replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
  3888. if (vim.visualBlock) {
  3889. var spaces = new Array(cm.getOption("tabSize") + 1).join(' ');
  3890. replaceWithStr = cm.getSelection();
  3891. replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith);
  3892. replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
  3893. cm.replaceSelections(replaceWithStr);
  3894. }
  3895. else {
  3896. cm.replaceRange(replaceWithStr, curStart, curEnd);
  3897. }
  3898. if (vim.visualMode) {
  3899. curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
  3900. selections[0].anchor : selections[0].head;
  3901. cm.setCursor(curStart);
  3902. exitVisualMode(cm, false);
  3903. }
  3904. else {
  3905. cm.setCursor(offsetCursor(curEnd, 0, -1));
  3906. }
  3907. }
  3908. },
  3909. incrementNumberToken: function (cm, actionArgs) {
  3910. var cur = cm.getCursor();
  3911. var lineStr = cm.getLine(cur.line);
  3912. var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
  3913. var match;
  3914. var start;
  3915. var end;
  3916. var numberStr;
  3917. while ((match = re.exec(lineStr)) !== null) {
  3918. start = match.index;
  3919. end = start + match[0].length;
  3920. if (cur.ch < end)
  3921. break;
  3922. }
  3923. if (!actionArgs.backtrack && (end <= cur.ch))
  3924. return;
  3925. if (match) {
  3926. var baseStr = match[2] || match[4];
  3927. var digits = match[3] || match[5];
  3928. var increment = actionArgs.increase ? 1 : -1;
  3929. var base = { '0b': 2, '0': 8, '': 10, '0x': 16 }[baseStr.toLowerCase()];
  3930. var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
  3931. numberStr = number.toString(base);
  3932. var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : '';
  3933. if (numberStr.charAt(0) === '-') {
  3934. numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
  3935. }
  3936. else {
  3937. numberStr = baseStr + zeroPadding + numberStr;
  3938. }
  3939. var from = new Pos(cur.line, start);
  3940. var to = new Pos(cur.line, end);
  3941. cm.replaceRange(numberStr, from, to);
  3942. }
  3943. else {
  3944. return;
  3945. }
  3946. cm.setCursor(new Pos(cur.line, start + numberStr.length - 1));
  3947. },
  3948. repeatLastEdit: function (cm, actionArgs, vim) {
  3949. var lastEditInputState = vim.lastEditInputState;
  3950. if (!lastEditInputState) {
  3951. return;
  3952. }
  3953. var repeat = actionArgs.repeat;
  3954. if (repeat && actionArgs.repeatIsExplicit) {
  3955. vim.lastEditInputState.repeatOverride = repeat;
  3956. }
  3957. else {
  3958. repeat = vim.lastEditInputState.repeatOverride || repeat;
  3959. }
  3960. repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
  3961. },
  3962. indent: function (cm, actionArgs) {
  3963. cm.indentLine(cm.getCursor().line, actionArgs.indentRight);
  3964. },
  3965. exitInsertMode: exitInsertMode
  3966. };
  3967. function defineAction(name, fn) {
  3968. actions[name] = fn;
  3969. }
  3970. function clipCursorToContent(cm, cur, oldCur) {
  3971. var vim = cm.state.vim;
  3972. var includeLineBreak = vim.insertMode || vim.visualMode;
  3973. var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine());
  3974. var text = cm.getLine(line);
  3975. var maxCh = text.length - 1 + Number(!!includeLineBreak);
  3976. var ch = Math.min(Math.max(0, cur.ch), maxCh);
  3977. var charCode = text.charCodeAt(ch);
  3978. if (0xDC00 <= charCode && charCode <= 0xDFFF) {
  3979. var direction = 1;
  3980. if (oldCur && oldCur.line == line && oldCur.ch > ch) {
  3981. direction = -1;
  3982. }
  3983. ch += direction;
  3984. if (ch > maxCh)
  3985. ch -= 2;
  3986. }
  3987. return new Pos(line, ch);
  3988. }
  3989. function copyArgs(args) {
  3990. var ret = {};
  3991. for (var prop in args) {
  3992. if (args.hasOwnProperty(prop)) {
  3993. ret[prop] = args[prop];
  3994. }
  3995. }
  3996. return ret;
  3997. }
  3998. function offsetCursor(cur, offsetLine, offsetCh) {
  3999. if (typeof offsetLine === 'object') {
  4000. offsetCh = offsetLine.ch;
  4001. offsetLine = offsetLine.line;
  4002. }
  4003. return new Pos(cur.line + offsetLine, cur.ch + offsetCh);
  4004. }
  4005. function commandMatches(keys, keyMap, context, inputState) {
  4006. if (inputState.operator)
  4007. context = "operatorPending";
  4008. var match, partial = [], full = [];
  4009. var startIndex = noremap ? keyMap.length - defaultKeymapLength : 0;
  4010. for (var i = startIndex; i < keyMap.length; i++) {
  4011. var command = keyMap[i];
  4012. if (context == 'insert' && command.context != 'insert' ||
  4013. (command.context && command.context != context) ||
  4014. inputState.operator && command.type == 'action' ||
  4015. !(match = commandMatch(keys, command.keys))) {
  4016. continue;
  4017. }
  4018. if (match == 'partial') {
  4019. partial.push(command);
  4020. }
  4021. if (match == 'full') {
  4022. full.push(command);
  4023. }
  4024. }
  4025. return {
  4026. partial: partial.length && partial,
  4027. full: full.length && full
  4028. };
  4029. }
  4030. function commandMatch(pressed, mapped) {
  4031. var isLastCharacter = mapped.slice(-11) == '<character>';
  4032. var isLastRegister = mapped.slice(-10) == '<register>';
  4033. if (isLastCharacter || isLastRegister) {
  4034. var prefixLen = mapped.length - (isLastCharacter ? 11 : 10);
  4035. var pressedPrefix = pressed.slice(0, prefixLen);
  4036. var mappedPrefix = mapped.slice(0, prefixLen);
  4037. return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
  4038. mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
  4039. }
  4040. else {
  4041. return pressed == mapped ? 'full' :
  4042. mapped.indexOf(pressed) == 0 ? 'partial' : false;
  4043. }
  4044. }
  4045. function lastChar(keys) {
  4046. var match = /^.*(<[^>]+>)$/.exec(keys);
  4047. var selectedCharacter = match ? match[1] : keys.slice(-1);
  4048. if (selectedCharacter.length > 1) {
  4049. switch (selectedCharacter) {
  4050. case '<CR>':
  4051. selectedCharacter = '\n';
  4052. break;
  4053. case '<Space>':
  4054. selectedCharacter = ' ';
  4055. break;
  4056. default:
  4057. selectedCharacter = '';
  4058. break;
  4059. }
  4060. }
  4061. return selectedCharacter;
  4062. }
  4063. function repeatFn(cm, fn, repeat) {
  4064. return function () {
  4065. for (var i = 0; i < repeat; i++) {
  4066. fn(cm);
  4067. }
  4068. };
  4069. }
  4070. function copyCursor(cur) {
  4071. return new Pos(cur.line, cur.ch);
  4072. }
  4073. function cursorEqual(cur1, cur2) {
  4074. return cur1.ch == cur2.ch && cur1.line == cur2.line;
  4075. }
  4076. function cursorIsBefore(cur1, cur2) {
  4077. if (cur1.line < cur2.line) {
  4078. return true;
  4079. }
  4080. if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
  4081. return true;
  4082. }
  4083. return false;
  4084. }
  4085. function cursorMin(cur1, cur2) {
  4086. if (arguments.length > 2) {
  4087. cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
  4088. }
  4089. return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
  4090. }
  4091. function cursorMax(cur1, cur2) {
  4092. if (arguments.length > 2) {
  4093. cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
  4094. }
  4095. return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
  4096. }
  4097. function cursorIsBetween(cur1, cur2, cur3) {
  4098. var cur1before2 = cursorIsBefore(cur1, cur2);
  4099. var cur2before3 = cursorIsBefore(cur2, cur3);
  4100. return cur1before2 && cur2before3;
  4101. }
  4102. function lineLength(cm, lineNum) {
  4103. return cm.getLine(lineNum).length;
  4104. }
  4105. function trim(s) {
  4106. if (s.trim) {
  4107. return s.trim();
  4108. }
  4109. return s.replace(/^\s+|\s+$/g, '');
  4110. }
  4111. function escapeRegex(s) {
  4112. return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
  4113. }
  4114. function extendLineToColumn(cm, lineNum, column) {
  4115. var endCh = lineLength(cm, lineNum);
  4116. var spaces = new Array(column - endCh + 1).join(' ');
  4117. cm.setCursor(new Pos(lineNum, endCh));
  4118. cm.replaceRange(spaces, cm.getCursor());
  4119. }
  4120. function selectBlock(cm, selectionEnd) {
  4121. var selections = [], ranges = cm.listSelections();
  4122. var head = copyCursor(cm.clipPos(selectionEnd));
  4123. var isClipped = !cursorEqual(selectionEnd, head);
  4124. var curHead = cm.getCursor('head');
  4125. var primIndex = getIndex(ranges, curHead);
  4126. var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
  4127. var max = ranges.length - 1;
  4128. var index = max - primIndex > primIndex ? max : 0;
  4129. var base = ranges[index].anchor;
  4130. var firstLine = Math.min(base.line, head.line);
  4131. var lastLine = Math.max(base.line, head.line);
  4132. var baseCh = base.ch, headCh = head.ch;
  4133. var dir = ranges[index].head.ch - baseCh;
  4134. var newDir = headCh - baseCh;
  4135. if (dir > 0 && newDir <= 0) {
  4136. baseCh++;
  4137. if (!isClipped) {
  4138. headCh--;
  4139. }
  4140. }
  4141. else if (dir < 0 && newDir >= 0) {
  4142. baseCh--;
  4143. if (!wasClipped) {
  4144. headCh++;
  4145. }
  4146. }
  4147. else if (dir < 0 && newDir == -1) {
  4148. baseCh--;
  4149. headCh++;
  4150. }
  4151. for (var line = firstLine; line <= lastLine; line++) {
  4152. var range = { anchor: new Pos(line, baseCh), head: new Pos(line, headCh) };
  4153. selections.push(range);
  4154. }
  4155. cm.setSelections(selections);
  4156. selectionEnd.ch = headCh;
  4157. base.ch = baseCh;
  4158. return base;
  4159. }
  4160. function selectForInsert(cm, head, height) {
  4161. var sel = [];
  4162. for (var i = 0; i < height; i++) {
  4163. var lineHead = offsetCursor(head, i, 0);
  4164. sel.push({ anchor: lineHead, head: lineHead });
  4165. }
  4166. cm.setSelections(sel, 0);
  4167. }
  4168. function getIndex(ranges, cursor, end) {
  4169. for (var i = 0; i < ranges.length; i++) {
  4170. var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
  4171. var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
  4172. if (atAnchor || atHead) {
  4173. return i;
  4174. }
  4175. }
  4176. return -1;
  4177. }
  4178. function getSelectedAreaRange(cm, vim) {
  4179. var lastSelection = vim.lastSelection;
  4180. var getCurrentSelectedAreaRange = function () {
  4181. var selections = cm.listSelections();
  4182. var start = selections[0];
  4183. var end = selections[selections.length - 1];
  4184. var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
  4185. var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
  4186. return [selectionStart, selectionEnd];
  4187. };
  4188. var getLastSelectedAreaRange = function () {
  4189. var selectionStart = cm.getCursor();
  4190. var selectionEnd = cm.getCursor();
  4191. var block = lastSelection.visualBlock;
  4192. if (block) {
  4193. var width = block.width;
  4194. var height = block.height;
  4195. selectionEnd = new Pos(selectionStart.line + height, selectionStart.ch + width);
  4196. var selections = [];
  4197. for (var i = selectionStart.line; i < selectionEnd.line; i++) {
  4198. var anchor = new Pos(i, selectionStart.ch);
  4199. var head = new Pos(i, selectionEnd.ch);
  4200. var range = { anchor: anchor, head: head };
  4201. selections.push(range);
  4202. }
  4203. cm.setSelections(selections);
  4204. }
  4205. else {
  4206. var start = lastSelection.anchorMark.find();
  4207. var end = lastSelection.headMark.find();
  4208. var line = end.line - start.line;
  4209. var ch = end.ch - start.ch;
  4210. selectionEnd = { line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch };
  4211. if (lastSelection.visualLine) {
  4212. selectionStart = new Pos(selectionStart.line, 0);
  4213. selectionEnd = new Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
  4214. }
  4215. cm.setSelection(selectionStart, selectionEnd);
  4216. }
  4217. return [selectionStart, selectionEnd];
  4218. };
  4219. if (!vim.visualMode) {
  4220. return getLastSelectedAreaRange();
  4221. }
  4222. else {
  4223. return getCurrentSelectedAreaRange();
  4224. }
  4225. }
  4226. function updateLastSelection(cm, vim) {
  4227. var anchor = vim.sel.anchor;
  4228. var head = vim.sel.head;
  4229. if (vim.lastPastedText) {
  4230. head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
  4231. vim.lastPastedText = null;
  4232. }
  4233. vim.lastSelection = { 'anchorMark': cm.setBookmark(anchor),
  4234. 'headMark': cm.setBookmark(head),
  4235. 'anchor': copyCursor(anchor),
  4236. 'head': copyCursor(head),
  4237. 'visualMode': vim.visualMode,
  4238. 'visualLine': vim.visualLine,
  4239. 'visualBlock': vim.visualBlock };
  4240. }
  4241. function expandSelection(cm, start, end, move) {
  4242. var sel = cm.state.vim.sel;
  4243. var head = move ? start : sel.head;
  4244. var anchor = move ? start : sel.anchor;
  4245. var tmp;
  4246. if (cursorIsBefore(end, start)) {
  4247. tmp = end;
  4248. end = start;
  4249. start = tmp;
  4250. }
  4251. if (cursorIsBefore(head, anchor)) {
  4252. head = cursorMin(start, head);
  4253. anchor = cursorMax(anchor, end);
  4254. }
  4255. else {
  4256. anchor = cursorMin(start, anchor);
  4257. head = cursorMax(head, end);
  4258. head = offsetCursor(head, 0, -1);
  4259. if (head.ch == -1 && head.line != cm.firstLine()) {
  4260. head = new Pos(head.line - 1, lineLength(cm, head.line - 1));
  4261. }
  4262. }
  4263. return [anchor, head];
  4264. }
  4265. function updateCmSelection(cm, sel, mode) {
  4266. var vim = cm.state.vim;
  4267. sel = sel || vim.sel;
  4268. var mode = mode ||
  4269. vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
  4270. var cmSel = makeCmSelection(cm, sel, mode);
  4271. cm.setSelections(cmSel.ranges, cmSel.primary);
  4272. }
  4273. function makeCmSelection(cm, sel, mode, exclusive) {
  4274. var head = copyCursor(sel.head);
  4275. var anchor = copyCursor(sel.anchor);
  4276. if (mode == 'char') {
  4277. var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
  4278. var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
  4279. head = offsetCursor(sel.head, 0, headOffset);
  4280. anchor = offsetCursor(sel.anchor, 0, anchorOffset);
  4281. return {
  4282. ranges: [{ anchor: anchor, head: head }],
  4283. primary: 0
  4284. };
  4285. }
  4286. else if (mode == 'line') {
  4287. if (!cursorIsBefore(sel.head, sel.anchor)) {
  4288. anchor.ch = 0;
  4289. var lastLine = cm.lastLine();
  4290. if (head.line > lastLine) {
  4291. head.line = lastLine;
  4292. }
  4293. head.ch = lineLength(cm, head.line);
  4294. }
  4295. else {
  4296. head.ch = 0;
  4297. anchor.ch = lineLength(cm, anchor.line);
  4298. }
  4299. return {
  4300. ranges: [{ anchor: anchor, head: head }],
  4301. primary: 0
  4302. };
  4303. }
  4304. else if (mode == 'block') {
  4305. var top = Math.min(anchor.line, head.line), fromCh = anchor.ch, bottom = Math.max(anchor.line, head.line), toCh = head.ch;
  4306. if (fromCh < toCh) {
  4307. toCh += 1;
  4308. }
  4309. else {
  4310. fromCh += 1;
  4311. }
  4312. ;
  4313. var height = bottom - top + 1;
  4314. var primary = head.line == top ? 0 : height - 1;
  4315. var ranges = [];
  4316. for (var i = 0; i < height; i++) {
  4317. ranges.push({
  4318. anchor: new Pos(top + i, fromCh),
  4319. head: new Pos(top + i, toCh)
  4320. });
  4321. }
  4322. return {
  4323. ranges: ranges,
  4324. primary: primary
  4325. };
  4326. }
  4327. }
  4328. function getHead(cm) {
  4329. var cur = cm.getCursor('head');
  4330. if (cm.getSelection().length == 1) {
  4331. cur = cursorMin(cur, cm.getCursor('anchor'));
  4332. }
  4333. return cur;
  4334. }
  4335. function exitVisualMode(cm, moveHead) {
  4336. var vim = cm.state.vim;
  4337. if (moveHead !== false) {
  4338. cm.setCursor(clipCursorToContent(cm, vim.sel.head));
  4339. }
  4340. updateLastSelection(cm, vim);
  4341. vim.visualMode = false;
  4342. vim.visualLine = false;
  4343. vim.visualBlock = false;
  4344. if (!vim.insertMode)
  4345. CodeMirror.signal(cm, "vim-mode-change", { mode: "normal" });
  4346. }
  4347. function clipToLine(cm, curStart, curEnd) {
  4348. var selection = cm.getRange(curStart, curEnd);
  4349. if (/\n\s*$/.test(selection)) {
  4350. var lines = selection.split('\n');
  4351. lines.pop();
  4352. var line;
  4353. for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
  4354. curEnd.line--;
  4355. curEnd.ch = 0;
  4356. }
  4357. if (line) {
  4358. curEnd.line--;
  4359. curEnd.ch = lineLength(cm, curEnd.line);
  4360. }
  4361. else {
  4362. curEnd.ch = 0;
  4363. }
  4364. }
  4365. }
  4366. function expandSelectionToLine(_cm, curStart, curEnd) {
  4367. curStart.ch = 0;
  4368. curEnd.ch = 0;
  4369. curEnd.line++;
  4370. }
  4371. function findFirstNonWhiteSpaceCharacter(text) {
  4372. if (!text) {
  4373. return 0;
  4374. }
  4375. var firstNonWS = text.search(/\S/);
  4376. return firstNonWS == -1 ? text.length : firstNonWS;
  4377. }
  4378. function expandWordUnderCursor(cm, _a, cursor) {
  4379. var inclusive = _a.inclusive, innerWord = _a.innerWord, bigWord = _a.bigWord, noSymbol = _a.noSymbol, multiline = _a.multiline;
  4380. var cur = cursor || getHead(cm);
  4381. var line = cm.getLine(cur.line);
  4382. var endLine = line;
  4383. var startLineNumber = cur.line;
  4384. var endLineNumber = startLineNumber;
  4385. var idx = cur.ch;
  4386. var wordOnNextLine;
  4387. var test = noSymbol ? wordCharTest[0] : bigWordCharTest[0];
  4388. if (innerWord && /\s/.test(line.charAt(idx))) {
  4389. test = function (ch) { return /\s/.test(ch); };
  4390. }
  4391. else {
  4392. while (!test(line.charAt(idx))) {
  4393. idx++;
  4394. if (idx >= line.length) {
  4395. if (!multiline)
  4396. return null;
  4397. idx--;
  4398. wordOnNextLine = findWord(cm, cur, true, bigWord, true);
  4399. break;
  4400. }
  4401. }
  4402. if (bigWord) {
  4403. test = bigWordCharTest[0];
  4404. }
  4405. else {
  4406. test = wordCharTest[0];
  4407. if (!test(line.charAt(idx))) {
  4408. test = wordCharTest[1];
  4409. }
  4410. }
  4411. }
  4412. var end = idx, start = idx;
  4413. while (test(line.charAt(start)) && start >= 0) {
  4414. start--;
  4415. }
  4416. start++;
  4417. if (wordOnNextLine) {
  4418. end = wordOnNextLine.to;
  4419. endLineNumber = wordOnNextLine.line;
  4420. endLine = cm.getLine(endLineNumber);
  4421. if (!endLine && end == 0)
  4422. end++;
  4423. }
  4424. else {
  4425. while (test(line.charAt(end)) && end < line.length) {
  4426. end++;
  4427. }
  4428. }
  4429. if (inclusive) {
  4430. var wordEnd = end;
  4431. var startsWithSpace = cur.ch <= start && /\s/.test(line.charAt(cur.ch));
  4432. if (!startsWithSpace) {
  4433. while (/\s/.test(endLine.charAt(end)) && end < endLine.length) {
  4434. end++;
  4435. }
  4436. }
  4437. if (wordEnd == end || startsWithSpace) {
  4438. var wordStart = start;
  4439. while (/\s/.test(line.charAt(start - 1)) && start > 0) {
  4440. start--;
  4441. }
  4442. if (!start && !startsWithSpace) {
  4443. start = wordStart;
  4444. }
  4445. }
  4446. }
  4447. return { start: new Pos(startLineNumber, start), end: new Pos(endLineNumber, end) };
  4448. }
  4449. function expandTagUnderCursor(cm, head, inclusive) {
  4450. var cur = head;
  4451. if (!CodeMirror.findMatchingTag || !CodeMirror.findEnclosingTag) {
  4452. return { start: cur, end: cur };
  4453. }
  4454. var tags = CodeMirror.findMatchingTag(cm, head) || CodeMirror.findEnclosingTag(cm, head);
  4455. if (!tags || !tags.open || !tags.close) {
  4456. return { start: cur, end: cur };
  4457. }
  4458. if (inclusive) {
  4459. return { start: tags.open.from, end: tags.close.to };
  4460. }
  4461. return { start: tags.open.to, end: tags.close.from };
  4462. }
  4463. function recordJumpPosition(cm, oldCur, newCur) {
  4464. if (!cursorEqual(oldCur, newCur)) {
  4465. vimGlobalState.jumpList.add(cm, oldCur, newCur);
  4466. }
  4467. }
  4468. function recordLastCharacterSearch(increment, args) {
  4469. vimGlobalState.lastCharacterSearch.increment = increment;
  4470. vimGlobalState.lastCharacterSearch.forward = args.forward;
  4471. vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter;
  4472. }
  4473. var symbolToMode = {
  4474. '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',
  4475. '[': 'section', ']': 'section',
  4476. '*': 'comment', '/': 'comment',
  4477. 'm': 'method', 'M': 'method',
  4478. '#': 'preprocess'
  4479. };
  4480. var findSymbolModes = {
  4481. bracket: {
  4482. isComplete: function (state) {
  4483. if (state.nextCh === state.symb) {
  4484. state.depth++;
  4485. if (state.depth >= 1)
  4486. return true;
  4487. }
  4488. else if (state.nextCh === state.reverseSymb) {
  4489. state.depth--;
  4490. }
  4491. return false;
  4492. }
  4493. },
  4494. section: {
  4495. init: function (state) {
  4496. state.curMoveThrough = true;
  4497. state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';
  4498. },
  4499. isComplete: function (state) {
  4500. return state.index === 0 && state.nextCh === state.symb;
  4501. }
  4502. },
  4503. comment: {
  4504. isComplete: function (state) {
  4505. var found = state.lastCh === '*' && state.nextCh === '/';
  4506. state.lastCh = state.nextCh;
  4507. return found;
  4508. }
  4509. },
  4510. method: {
  4511. init: function (state) {
  4512. state.symb = (state.symb === 'm' ? '{' : '}');
  4513. state.reverseSymb = state.symb === '{' ? '}' : '{';
  4514. },
  4515. isComplete: function (state) {
  4516. if (state.nextCh === state.symb)
  4517. return true;
  4518. return false;
  4519. }
  4520. },
  4521. preprocess: {
  4522. init: function (state) {
  4523. state.index = 0;
  4524. },
  4525. isComplete: function (state) {
  4526. if (state.nextCh === '#') {
  4527. var token = state.lineText.match(/^#(\w+)/)[1];
  4528. if (token === 'endif') {
  4529. if (state.forward && state.depth === 0) {
  4530. return true;
  4531. }
  4532. state.depth++;
  4533. }
  4534. else if (token === 'if') {
  4535. if (!state.forward && state.depth === 0) {
  4536. return true;
  4537. }
  4538. state.depth--;
  4539. }
  4540. if (token === 'else' && state.depth === 0)
  4541. return true;
  4542. }
  4543. return false;
  4544. }
  4545. }
  4546. };
  4547. function findSymbol(cm, repeat, forward, symb) {
  4548. var cur = copyCursor(cm.getCursor());
  4549. var increment = forward ? 1 : -1;
  4550. var endLine = forward ? cm.lineCount() : -1;
  4551. var curCh = cur.ch;
  4552. var line = cur.line;
  4553. var lineText = cm.getLine(line);
  4554. var state = {
  4555. lineText: lineText,
  4556. nextCh: lineText.charAt(curCh),
  4557. lastCh: null,
  4558. index: curCh,
  4559. symb: symb,
  4560. reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
  4561. forward: forward,
  4562. depth: 0,
  4563. curMoveThrough: false
  4564. };
  4565. var mode = symbolToMode[symb];
  4566. if (!mode)
  4567. return cur;
  4568. var init = findSymbolModes[mode].init;
  4569. var isComplete = findSymbolModes[mode].isComplete;
  4570. if (init) {
  4571. init(state);
  4572. }
  4573. while (line !== endLine && repeat) {
  4574. state.index += increment;
  4575. state.nextCh = state.lineText.charAt(state.index);
  4576. if (!state.nextCh) {
  4577. line += increment;
  4578. state.lineText = cm.getLine(line) || '';
  4579. if (increment > 0) {
  4580. state.index = 0;
  4581. }
  4582. else {
  4583. var lineLen = state.lineText.length;
  4584. state.index = (lineLen > 0) ? (lineLen - 1) : 0;
  4585. }
  4586. state.nextCh = state.lineText.charAt(state.index);
  4587. }
  4588. if (isComplete(state)) {
  4589. cur.line = line;
  4590. cur.ch = state.index;
  4591. repeat--;
  4592. }
  4593. }
  4594. if (state.nextCh || state.curMoveThrough) {
  4595. return new Pos(line, state.index);
  4596. }
  4597. return cur;
  4598. }
  4599. function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {
  4600. var lineNum = cur.line;
  4601. var pos = cur.ch;
  4602. var line = cm.getLine(lineNum);
  4603. var dir = forward ? 1 : -1;
  4604. var charTests = bigWord ? bigWordCharTest : wordCharTest;
  4605. if (emptyLineIsWord && line == '') {
  4606. lineNum += dir;
  4607. line = cm.getLine(lineNum);
  4608. if (!isLine(cm, lineNum)) {
  4609. return null;
  4610. }
  4611. pos = (forward) ? 0 : line.length;
  4612. }
  4613. while (true) {
  4614. if (emptyLineIsWord && line == '') {
  4615. return { from: 0, to: 0, line: lineNum };
  4616. }
  4617. var stop = (dir > 0) ? line.length : -1;
  4618. var wordStart = stop, wordEnd = stop;
  4619. while (pos != stop) {
  4620. var foundWord = false;
  4621. for (var i = 0; i < charTests.length && !foundWord; ++i) {
  4622. if (charTests[i](line.charAt(pos))) {
  4623. wordStart = pos;
  4624. while (pos != stop && charTests[i](line.charAt(pos))) {
  4625. pos += dir;
  4626. }
  4627. wordEnd = pos;
  4628. foundWord = wordStart != wordEnd;
  4629. if (wordStart == cur.ch && lineNum == cur.line &&
  4630. wordEnd == wordStart + dir) {
  4631. continue;
  4632. }
  4633. else {
  4634. return {
  4635. from: Math.min(wordStart, wordEnd + 1),
  4636. to: Math.max(wordStart, wordEnd),
  4637. line: lineNum
  4638. };
  4639. }
  4640. }
  4641. }
  4642. if (!foundWord) {
  4643. pos += dir;
  4644. }
  4645. }
  4646. lineNum += dir;
  4647. if (!isLine(cm, lineNum)) {
  4648. return null;
  4649. }
  4650. line = cm.getLine(lineNum);
  4651. pos = (dir > 0) ? 0 : line.length;
  4652. }
  4653. }
  4654. function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
  4655. var curStart = copyCursor(cur);
  4656. var words = [];
  4657. if (forward && !wordEnd || !forward && wordEnd) {
  4658. repeat++;
  4659. }
  4660. var emptyLineIsWord = !(forward && wordEnd);
  4661. for (var i = 0; i < repeat; i++) {
  4662. var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);
  4663. if (!word) {
  4664. var eodCh = lineLength(cm, cm.lastLine());
  4665. words.push(forward
  4666. ? { line: cm.lastLine(), from: eodCh, to: eodCh }
  4667. : { line: 0, from: 0, to: 0 });
  4668. break;
  4669. }
  4670. words.push(word);
  4671. cur = new Pos(word.line, forward ? (word.to - 1) : word.from);
  4672. }
  4673. var shortCircuit = words.length != repeat;
  4674. var firstWord = words[0];
  4675. var lastWord = words.pop();
  4676. if (forward && !wordEnd) {
  4677. if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
  4678. lastWord = words.pop();
  4679. }
  4680. return new Pos(lastWord.line, lastWord.from);
  4681. }
  4682. else if (forward && wordEnd) {
  4683. return new Pos(lastWord.line, lastWord.to - 1);
  4684. }
  4685. else if (!forward && wordEnd) {
  4686. if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
  4687. lastWord = words.pop();
  4688. }
  4689. return new Pos(lastWord.line, lastWord.to);
  4690. }
  4691. else {
  4692. return new Pos(lastWord.line, lastWord.from);
  4693. }
  4694. }
  4695. function moveToEol(cm, head, motionArgs, vim, keepHPos) {
  4696. var cur = head;
  4697. var retval = new Pos(cur.line + motionArgs.repeat - 1, Infinity);
  4698. var end = cm.clipPos(retval);
  4699. end.ch--;
  4700. if (!keepHPos) {
  4701. vim.lastHPos = Infinity;
  4702. vim.lastHSPos = cm.charCoords(end, 'div').left;
  4703. }
  4704. return retval;
  4705. }
  4706. function moveToCharacter(cm, repeat, forward, character, head) {
  4707. var cur = head || cm.getCursor();
  4708. var start = cur.ch;
  4709. var idx;
  4710. for (var i = 0; i < repeat; i++) {
  4711. var line = cm.getLine(cur.line);
  4712. idx = charIdxInLine(start, line, character, forward, true);
  4713. if (idx == -1) {
  4714. return null;
  4715. }
  4716. start = idx;
  4717. }
  4718. return new Pos(cm.getCursor().line, idx);
  4719. }
  4720. function moveToColumn(cm, repeat) {
  4721. var line = cm.getCursor().line;
  4722. return clipCursorToContent(cm, new Pos(line, repeat - 1));
  4723. }
  4724. function updateMark(cm, vim, markName, pos) {
  4725. if (!inArray(markName, validMarks) && !latinCharRegex.test(markName)) {
  4726. return;
  4727. }
  4728. if (vim.marks[markName]) {
  4729. vim.marks[markName].clear();
  4730. }
  4731. vim.marks[markName] = cm.setBookmark(pos);
  4732. }
  4733. function charIdxInLine(start, line, character, forward, includeChar) {
  4734. var idx;
  4735. if (forward) {
  4736. idx = line.indexOf(character, start + 1);
  4737. if (idx != -1 && !includeChar) {
  4738. idx -= 1;
  4739. }
  4740. }
  4741. else {
  4742. idx = line.lastIndexOf(character, start - 1);
  4743. if (idx != -1 && !includeChar) {
  4744. idx += 1;
  4745. }
  4746. }
  4747. return idx;
  4748. }
  4749. function findParagraph(cm, head, repeat, dir, inclusive) {
  4750. var line = head.line;
  4751. var min = cm.firstLine();
  4752. var max = cm.lastLine();
  4753. var start, end, i = line;
  4754. function isEmpty(i) { return !/\S/.test(cm.getLine(i)); } // ace_patch
  4755. function isBoundary(i, dir, any) {
  4756. if (any) {
  4757. return isEmpty(i) != isEmpty(i + dir);
  4758. }
  4759. return !isEmpty(i) && isEmpty(i + dir);
  4760. }
  4761. function skipFold(i) {
  4762. dir = dir > 0 ? 1 : -1;
  4763. var foldLine = cm.ace.session.getFoldLine(i);
  4764. if (foldLine) {
  4765. if (i + dir > foldLine.start.row && i + dir < foldLine.end.row)
  4766. dir = (dir > 0 ? foldLine.end.row : foldLine.start.row) - i;
  4767. }
  4768. }
  4769. if (dir) {
  4770. while (min <= i && i <= max && repeat > 0) {
  4771. skipFold(i);
  4772. if (isBoundary(i, dir)) {
  4773. repeat--;
  4774. }
  4775. i += dir;
  4776. }
  4777. return new Pos(i, 0);
  4778. }
  4779. var vim = cm.state.vim;
  4780. if (vim.visualLine && isBoundary(line, 1, true)) {
  4781. var anchor = vim.sel.anchor;
  4782. if (isBoundary(anchor.line, -1, true)) {
  4783. if (!inclusive || anchor.line != line) {
  4784. line += 1;
  4785. }
  4786. }
  4787. }
  4788. var startState = isEmpty(line);
  4789. for (i = line; i <= max && repeat; i++) {
  4790. if (isBoundary(i, 1, true)) {
  4791. if (!inclusive || isEmpty(i) != startState) {
  4792. repeat--;
  4793. }
  4794. }
  4795. }
  4796. end = new Pos(i, 0);
  4797. if (i > max && !startState) {
  4798. startState = true;
  4799. }
  4800. else {
  4801. inclusive = false;
  4802. }
  4803. for (i = line; i > min; i--) {
  4804. if (!inclusive || isEmpty(i) == startState || i == line) {
  4805. if (isBoundary(i, -1, true)) {
  4806. break;
  4807. }
  4808. }
  4809. }
  4810. start = new Pos(i, 0);
  4811. return { start: start, end: end };
  4812. }
  4813. function getSentence(cm, cur, repeat, dir, inclusive /*includes whitespace*/) {
  4814. function nextChar(curr) {
  4815. if (curr.pos + curr.dir < 0 || curr.pos + curr.dir >= curr.line.length) {
  4816. curr.line = null;
  4817. }
  4818. else {
  4819. curr.pos += curr.dir;
  4820. }
  4821. }
  4822. function forward(cm, ln, pos, dir) {
  4823. var line = cm.getLine(ln);
  4824. var curr = {
  4825. line: line,
  4826. ln: ln,
  4827. pos: pos,
  4828. dir: dir,
  4829. };
  4830. if (curr.line === "") {
  4831. return { ln: curr.ln, pos: curr.pos };
  4832. }
  4833. var lastSentencePos = curr.pos;
  4834. nextChar(curr);
  4835. while (curr.line !== null) {
  4836. lastSentencePos = curr.pos;
  4837. if (isEndOfSentenceSymbol(curr.line[curr.pos])) {
  4838. if (!inclusive) {
  4839. return { ln: curr.ln, pos: curr.pos + 1 };
  4840. }
  4841. else {
  4842. nextChar(curr);
  4843. while (curr.line !== null) {
  4844. if (isWhiteSpaceString(curr.line[curr.pos])) {
  4845. lastSentencePos = curr.pos;
  4846. nextChar(curr);
  4847. }
  4848. else {
  4849. break;
  4850. }
  4851. }
  4852. return { ln: curr.ln, pos: lastSentencePos + 1 };
  4853. }
  4854. }
  4855. nextChar(curr);
  4856. }
  4857. return { ln: curr.ln, pos: lastSentencePos + 1 };
  4858. }
  4859. function reverse(cm, ln, pos, dir) {
  4860. var line = cm.getLine(ln);
  4861. var curr = {
  4862. line: line,
  4863. ln: ln,
  4864. pos: pos,
  4865. dir: dir,
  4866. };
  4867. if (curr.line === "") {
  4868. return { ln: curr.ln, pos: curr.pos };
  4869. }
  4870. var lastSentencePos = curr.pos;
  4871. nextChar(curr);
  4872. while (curr.line !== null) {
  4873. if (!isWhiteSpaceString(curr.line[curr.pos]) && !isEndOfSentenceSymbol(curr.line[curr.pos])) {
  4874. lastSentencePos = curr.pos;
  4875. }
  4876. else if (isEndOfSentenceSymbol(curr.line[curr.pos])) {
  4877. if (!inclusive) {
  4878. return { ln: curr.ln, pos: lastSentencePos };
  4879. }
  4880. else {
  4881. if (isWhiteSpaceString(curr.line[curr.pos + 1])) {
  4882. return { ln: curr.ln, pos: curr.pos + 1 };
  4883. }
  4884. else {
  4885. return { ln: curr.ln, pos: lastSentencePos };
  4886. }
  4887. }
  4888. }
  4889. nextChar(curr);
  4890. }
  4891. curr.line = line;
  4892. if (inclusive && isWhiteSpaceString(curr.line[curr.pos])) {
  4893. return { ln: curr.ln, pos: curr.pos };
  4894. }
  4895. else {
  4896. return { ln: curr.ln, pos: lastSentencePos };
  4897. }
  4898. }
  4899. var curr_index = {
  4900. ln: cur.line,
  4901. pos: cur.ch,
  4902. };
  4903. while (repeat > 0) {
  4904. if (dir < 0) {
  4905. curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);
  4906. }
  4907. else {
  4908. curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);
  4909. }
  4910. repeat--;
  4911. }
  4912. return new Pos(curr_index.ln, curr_index.pos);
  4913. }
  4914. function findSentence(cm, cur, repeat, dir) {
  4915. function nextChar(cm, idx) {
  4916. if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) {
  4917. idx.ln += idx.dir;
  4918. if (!isLine(cm, idx.ln)) {
  4919. idx.line = null;
  4920. idx.ln = null;
  4921. idx.pos = null;
  4922. return;
  4923. }
  4924. idx.line = cm.getLine(idx.ln);
  4925. idx.pos = (idx.dir > 0) ? 0 : idx.line.length - 1;
  4926. }
  4927. else {
  4928. idx.pos += idx.dir;
  4929. }
  4930. }
  4931. function forward(cm, ln, pos, dir) {
  4932. var line = cm.getLine(ln);
  4933. var stop = (line === "");
  4934. var curr = {
  4935. line: line,
  4936. ln: ln,
  4937. pos: pos,
  4938. dir: dir,
  4939. };
  4940. var last_valid = {
  4941. ln: curr.ln,
  4942. pos: curr.pos,
  4943. };
  4944. var skip_empty_lines = (curr.line === "");
  4945. nextChar(cm, curr);
  4946. while (curr.line !== null) {
  4947. last_valid.ln = curr.ln;
  4948. last_valid.pos = curr.pos;
  4949. if (curr.line === "" && !skip_empty_lines) {
  4950. return { ln: curr.ln, pos: curr.pos, };
  4951. }
  4952. else if (stop && curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
  4953. return { ln: curr.ln, pos: curr.pos, };
  4954. }
  4955. else if (isEndOfSentenceSymbol(curr.line[curr.pos])
  4956. && !stop
  4957. && (curr.pos === curr.line.length - 1
  4958. || isWhiteSpaceString(curr.line[curr.pos + 1]))) {
  4959. stop = true;
  4960. }
  4961. nextChar(cm, curr);
  4962. }
  4963. var line = cm.getLine(last_valid.ln);
  4964. last_valid.pos = 0;
  4965. for (var i = line.length - 1; i >= 0; --i) {
  4966. if (!isWhiteSpaceString(line[i])) {
  4967. last_valid.pos = i;
  4968. break;
  4969. }
  4970. }
  4971. return last_valid;
  4972. }
  4973. function reverse(cm, ln, pos, dir) {
  4974. var line = cm.getLine(ln);
  4975. var curr = {
  4976. line: line,
  4977. ln: ln,
  4978. pos: pos,
  4979. dir: dir,
  4980. };
  4981. var last_valid = {
  4982. ln: curr.ln,
  4983. pos: null,
  4984. };
  4985. var skip_empty_lines = (curr.line === "");
  4986. nextChar(cm, curr);
  4987. while (curr.line !== null) {
  4988. if (curr.line === "" && !skip_empty_lines) {
  4989. if (last_valid.pos !== null) {
  4990. return last_valid;
  4991. }
  4992. else {
  4993. return { ln: curr.ln, pos: curr.pos };
  4994. }
  4995. }
  4996. else if (isEndOfSentenceSymbol(curr.line[curr.pos])
  4997. && last_valid.pos !== null
  4998. && !(curr.ln === last_valid.ln && curr.pos + 1 === last_valid.pos)) {
  4999. return last_valid;
  5000. }
  5001. else if (curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
  5002. skip_empty_lines = false;
  5003. last_valid = { ln: curr.ln, pos: curr.pos };
  5004. }
  5005. nextChar(cm, curr);
  5006. }
  5007. var line = cm.getLine(last_valid.ln);
  5008. last_valid.pos = 0;
  5009. for (var i = 0; i < line.length; ++i) {
  5010. if (!isWhiteSpaceString(line[i])) {
  5011. last_valid.pos = i;
  5012. break;
  5013. }
  5014. }
  5015. return last_valid;
  5016. }
  5017. var curr_index = {
  5018. ln: cur.line,
  5019. pos: cur.ch,
  5020. };
  5021. while (repeat > 0) {
  5022. if (dir < 0) {
  5023. curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);
  5024. }
  5025. else {
  5026. curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);
  5027. }
  5028. repeat--;
  5029. }
  5030. return new Pos(curr_index.ln, curr_index.pos);
  5031. }
  5032. function selectCompanionObject(cm, head, symb, inclusive) {
  5033. var cur = head, start, end;
  5034. var bracketRegexp = ({
  5035. '(': /[()]/, ')': /[()]/,
  5036. '[': /[[\]]/, ']': /[[\]]/,
  5037. '{': /[{}]/, '}': /[{}]/,
  5038. '<': /[<>]/, '>': /[<>]/
  5039. })[symb];
  5040. var openSym = ({
  5041. '(': '(', ')': '(',
  5042. '[': '[', ']': '[',
  5043. '{': '{', '}': '{',
  5044. '<': '<', '>': '<'
  5045. })[symb];
  5046. var curChar = cm.getLine(cur.line).charAt(cur.ch);
  5047. var offset = curChar === openSym ? 1 : 0;
  5048. start = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), -1, undefined, { 'bracketRegex': bracketRegexp });
  5049. end = cm.scanForBracket(new Pos(cur.line, cur.ch + offset), 1, undefined, { 'bracketRegex': bracketRegexp });
  5050. if (!start || !end)
  5051. return null;
  5052. start = start.pos;
  5053. end = end.pos;
  5054. if ((start.line == end.line && start.ch > end.ch)
  5055. || (start.line > end.line)) {
  5056. var tmp = start;
  5057. start = end;
  5058. end = tmp;
  5059. }
  5060. if (inclusive) {
  5061. end.ch += 1;
  5062. }
  5063. else {
  5064. start.ch += 1;
  5065. }
  5066. return { start: start, end: end };
  5067. }
  5068. function findBeginningAndEnd(cm, head, symb, inclusive) {
  5069. var cur = copyCursor(head);
  5070. var line = cm.getLine(cur.line);
  5071. var chars = line.split('');
  5072. var start, end, i, len;
  5073. var firstIndex = chars.indexOf(symb);
  5074. if (cur.ch < firstIndex) {
  5075. cur.ch = firstIndex;
  5076. }
  5077. else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
  5078. var stringAfter = /string/.test(cm.getTokenTypeAt(offsetCursor(head, 0, 1)));
  5079. var stringBefore = /string/.test(cm.getTokenTypeAt(head));
  5080. var isStringStart = stringAfter && !stringBefore;
  5081. if (!isStringStart) {
  5082. end = cur.ch; // assign end to the current cursor
  5083. --cur.ch; // make sure to look backwards
  5084. }
  5085. }
  5086. if (chars[cur.ch] == symb && !end) {
  5087. start = cur.ch + 1; // assign start to ahead of the cursor
  5088. }
  5089. else {
  5090. for (i = cur.ch; i > -1 && !start; i--) {
  5091. if (chars[i] == symb) {
  5092. start = i + 1;
  5093. }
  5094. }
  5095. }
  5096. if (start && !end) {
  5097. for (i = start, len = chars.length; i < len && !end; i++) {
  5098. if (chars[i] == symb) {
  5099. end = i;
  5100. }
  5101. }
  5102. }
  5103. if (!start || !end) {
  5104. return { start: cur, end: cur };
  5105. }
  5106. if (inclusive) {
  5107. --start;
  5108. ++end;
  5109. }
  5110. return {
  5111. start: new Pos(cur.line, start),
  5112. end: new Pos(cur.line, end)
  5113. };
  5114. }
  5115. defineOption('pcre', true, 'boolean');
  5116. function SearchState() { }
  5117. SearchState.prototype = {
  5118. getQuery: function () {
  5119. return vimGlobalState.query;
  5120. },
  5121. setQuery: function (query) {
  5122. vimGlobalState.query = query;
  5123. },
  5124. getOverlay: function () {
  5125. return this.searchOverlay;
  5126. },
  5127. setOverlay: function (overlay) {
  5128. this.searchOverlay = overlay;
  5129. },
  5130. isReversed: function () {
  5131. return vimGlobalState.isReversed;
  5132. },
  5133. setReversed: function (reversed) {
  5134. vimGlobalState.isReversed = reversed;
  5135. },
  5136. getScrollbarAnnotate: function () {
  5137. return this.annotate;
  5138. },
  5139. setScrollbarAnnotate: function (annotate) {
  5140. this.annotate = annotate;
  5141. }
  5142. };
  5143. function getSearchState(cm) {
  5144. var vim = cm.state.vim;
  5145. return vim.searchState_ || (vim.searchState_ = new SearchState());
  5146. }
  5147. function splitBySlash(argString) {
  5148. return splitBySeparator(argString, '/');
  5149. }
  5150. function findUnescapedSlashes(argString) {
  5151. return findUnescapedSeparators(argString, '/');
  5152. }
  5153. function splitBySeparator(argString, separator) {
  5154. var slashes = findUnescapedSeparators(argString, separator) || [];
  5155. if (!slashes.length)
  5156. return [];
  5157. var tokens = [];
  5158. if (slashes[0] !== 0)
  5159. return;
  5160. for (var i = 0; i < slashes.length; i++) {
  5161. if (typeof slashes[i] == 'number')
  5162. tokens.push(argString.substring(slashes[i] + 1, slashes[i + 1]));
  5163. }
  5164. return tokens;
  5165. }
  5166. function findUnescapedSeparators(str, separator) {
  5167. if (!separator)
  5168. separator = '/';
  5169. var escapeNextChar = false;
  5170. var slashes = [];
  5171. for (var i = 0; i < str.length; i++) {
  5172. var c = str.charAt(i);
  5173. if (!escapeNextChar && c == separator) {
  5174. slashes.push(i);
  5175. }
  5176. escapeNextChar = !escapeNextChar && (c == '\\');
  5177. }
  5178. return slashes;
  5179. }
  5180. function translateRegex(str) {
  5181. var specials = '|(){';
  5182. var unescape = '}';
  5183. var escapeNextChar = false;
  5184. var out = [];
  5185. for (var i = -1; i < str.length; i++) {
  5186. var c = str.charAt(i) || '';
  5187. var n = str.charAt(i + 1) || '';
  5188. var specialComesNext = (n && specials.indexOf(n) != -1);
  5189. if (escapeNextChar) {
  5190. if (c !== '\\' || !specialComesNext) {
  5191. out.push(c);
  5192. }
  5193. escapeNextChar = false;
  5194. }
  5195. else {
  5196. if (c === '\\') {
  5197. escapeNextChar = true;
  5198. if (n && unescape.indexOf(n) != -1) {
  5199. specialComesNext = true;
  5200. }
  5201. if (!specialComesNext || n === '\\') {
  5202. out.push(c);
  5203. }
  5204. }
  5205. else {
  5206. out.push(c);
  5207. if (specialComesNext && n !== '\\') {
  5208. out.push('\\');
  5209. }
  5210. }
  5211. }
  5212. }
  5213. return out.join('');
  5214. }
  5215. var charUnescapes = { '\\n': '\n', '\\r': '\r', '\\t': '\t' };
  5216. function translateRegexReplace(str) {
  5217. var escapeNextChar = false;
  5218. var out = [];
  5219. for (var i = -1; i < str.length; i++) {
  5220. var c = str.charAt(i) || '';
  5221. var n = str.charAt(i + 1) || '';
  5222. if (charUnescapes[c + n]) {
  5223. out.push(charUnescapes[c + n]);
  5224. i++;
  5225. }
  5226. else if (escapeNextChar) {
  5227. out.push(c);
  5228. escapeNextChar = false;
  5229. }
  5230. else {
  5231. if (c === '\\') {
  5232. escapeNextChar = true;
  5233. if ((isNumber(n) || n === '$')) {
  5234. out.push('$');
  5235. }
  5236. else if (n !== '/' && n !== '\\') {
  5237. out.push('\\');
  5238. }
  5239. }
  5240. else {
  5241. if (c === '$') {
  5242. out.push('$');
  5243. }
  5244. out.push(c);
  5245. if (n === '/') {
  5246. out.push('\\');
  5247. }
  5248. }
  5249. }
  5250. }
  5251. return out.join('');
  5252. }
  5253. var unescapes = { '\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t', '\\&': '&' };
  5254. function unescapeRegexReplace(str) {
  5255. var stream = new CodeMirror.StringStream(str);
  5256. var output = [];
  5257. while (!stream.eol()) {
  5258. while (stream.peek() && stream.peek() != '\\') {
  5259. output.push(stream.next());
  5260. }
  5261. var matched = false;
  5262. for (var matcher in unescapes) {
  5263. if (stream.match(matcher, true)) {
  5264. matched = true;
  5265. output.push(unescapes[matcher]);
  5266. break;
  5267. }
  5268. }
  5269. if (!matched) {
  5270. output.push(stream.next());
  5271. }
  5272. }
  5273. return output.join('');
  5274. }
  5275. function parseQuery(query, ignoreCase, smartCase) {
  5276. var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
  5277. lastSearchRegister.setText(query);
  5278. if (query instanceof RegExp) {
  5279. return query;
  5280. }
  5281. var slashes = findUnescapedSlashes(query);
  5282. var regexPart;
  5283. var forceIgnoreCase;
  5284. if (!slashes.length) {
  5285. regexPart = query;
  5286. }
  5287. else {
  5288. regexPart = query.substring(0, slashes[0]);
  5289. var flagsPart = query.substring(slashes[0]);
  5290. forceIgnoreCase = (flagsPart.indexOf('i') != -1);
  5291. }
  5292. if (!regexPart) {
  5293. return null;
  5294. }
  5295. if (!getOption('pcre')) {
  5296. regexPart = translateRegex(regexPart);
  5297. }
  5298. if (smartCase) {
  5299. ignoreCase = (/^[^A-Z]*$/).test(regexPart);
  5300. }
  5301. var regexp = new RegExp(regexPart, (ignoreCase || forceIgnoreCase) ? 'im' : 'm');
  5302. return regexp;
  5303. }
  5304. function dom(n) {
  5305. if (typeof n === 'string')
  5306. n = document.createElement(n);
  5307. for (var a, i = 1; i < arguments.length; i++) {
  5308. if (!(a = arguments[i]))
  5309. continue;
  5310. if (typeof a !== 'object')
  5311. a = document.createTextNode(a);
  5312. if (a.nodeType)
  5313. n.appendChild(a);
  5314. else
  5315. for (var key in a) {
  5316. if (!Object.prototype.hasOwnProperty.call(a, key))
  5317. continue;
  5318. if (key[0] === '$')
  5319. n.style[key.slice(1)] = a[key];
  5320. else
  5321. n.setAttribute(key, a[key]);
  5322. }
  5323. }
  5324. return n;
  5325. }
  5326. function showConfirm(cm, template) {
  5327. var pre = dom('div', { $color: 'red', $whiteSpace: 'pre', class: 'cm-vim-message' }, template);
  5328. if (cm.openNotification) {
  5329. cm.openNotification(pre, { bottom: true, duration: 5000 });
  5330. }
  5331. else {
  5332. alert(pre.innerText);
  5333. }
  5334. }
  5335. function makePrompt(prefix, desc) {
  5336. return dom('div', { $display: 'flex' }, dom('span', { $fontFamily: 'monospace', $whiteSpace: 'pre', $flex: 1 }, prefix, dom('input', { type: 'text', autocorrect: 'off',
  5337. autocapitalize: 'off', spellcheck: 'false', $width: '100%' })), desc && dom('span', { $color: '#888' }, desc));
  5338. }
  5339. function showPrompt(cm, options) {
  5340. if (keyToKeyStack.length) {
  5341. if (!options.value)
  5342. options.value = '';
  5343. virtualPrompt = options;
  5344. return;
  5345. }
  5346. var template = makePrompt(options.prefix, options.desc);
  5347. if (cm.openDialog) {
  5348. cm.openDialog(template, options.onClose, {
  5349. onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
  5350. bottom: true, selectValueOnOpen: false, value: options.value
  5351. });
  5352. }
  5353. else {
  5354. var shortText = '';
  5355. if (typeof options.prefix != "string" && options.prefix)
  5356. shortText += options.prefix.textContent;
  5357. if (options.desc)
  5358. shortText += " " + options.desc;
  5359. options.onClose(prompt(shortText, ''));
  5360. }
  5361. }
  5362. function regexEqual(r1, r2) {
  5363. if (r1 instanceof RegExp && r2 instanceof RegExp) {
  5364. var props = ['global', 'multiline', 'ignoreCase', 'source'];
  5365. for (var i = 0; i < props.length; i++) {
  5366. var prop = props[i];
  5367. if (r1[prop] !== r2[prop]) {
  5368. return false;
  5369. }
  5370. }
  5371. return true;
  5372. }
  5373. return false;
  5374. }
  5375. function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
  5376. if (!rawQuery) {
  5377. return;
  5378. }
  5379. var state = getSearchState(cm);
  5380. var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
  5381. if (!query) {
  5382. return;
  5383. }
  5384. highlightSearchMatches(cm, query);
  5385. if (regexEqual(query, state.getQuery())) {
  5386. return query;
  5387. }
  5388. state.setQuery(query);
  5389. return query;
  5390. }
  5391. function searchOverlay(query) {
  5392. if (query.source.charAt(0) == '^') {
  5393. var matchSol = true;
  5394. }
  5395. return {
  5396. token: function (stream) {
  5397. if (matchSol && !stream.sol()) {
  5398. stream.skipToEnd();
  5399. return;
  5400. }
  5401. var match = stream.match(query, false);
  5402. if (match) {
  5403. if (match[0].length == 0) {
  5404. stream.next();
  5405. return 'searching';
  5406. }
  5407. if (!stream.sol()) {
  5408. stream.backUp(1);
  5409. if (!query.exec(stream.next() + match[0])) {
  5410. stream.next();
  5411. return null;
  5412. }
  5413. }
  5414. stream.match(query);
  5415. return 'searching';
  5416. }
  5417. while (!stream.eol()) {
  5418. stream.next();
  5419. if (stream.match(query, false))
  5420. break;
  5421. }
  5422. },
  5423. query: query
  5424. };
  5425. }
  5426. var highlightTimeout = 0;
  5427. function highlightSearchMatches(cm, query) {
  5428. clearTimeout(highlightTimeout);
  5429. var searchState = getSearchState(cm);
  5430. searchState.highlightTimeout = highlightTimeout;
  5431. highlightTimeout = setTimeout(function () {
  5432. if (!cm.state.vim)
  5433. return;
  5434. var searchState = getSearchState(cm);
  5435. searchState.highlightTimeout = null;
  5436. var overlay = searchState.getOverlay();
  5437. if (!overlay || query != overlay.query) {
  5438. if (overlay) {
  5439. cm.removeOverlay(overlay);
  5440. }
  5441. overlay = searchOverlay(query);
  5442. cm.addOverlay(overlay);
  5443. if (cm.showMatchesOnScrollbar) {
  5444. if (searchState.getScrollbarAnnotate()) {
  5445. searchState.getScrollbarAnnotate().clear();
  5446. }
  5447. searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
  5448. }
  5449. searchState.setOverlay(overlay);
  5450. }
  5451. }, 50);
  5452. }
  5453. function findNext(cm, prev, query, repeat) {
  5454. if (repeat === undefined) {
  5455. repeat = 1;
  5456. }
  5457. return cm.operation(function () {
  5458. var pos = cm.getCursor();
  5459. var cursor = cm.getSearchCursor(query, pos);
  5460. for (var i = 0; i < repeat; i++) {
  5461. var found = cursor.find(prev);
  5462. if (i == 0 && found && cursorEqual(cursor.from(), pos)) {
  5463. var lastEndPos = prev ? cursor.from() : cursor.to();
  5464. found = cursor.find(prev);
  5465. if (found && !found[0] && cursorEqual(cursor.from(), lastEndPos)) {
  5466. if (cm.getLine(lastEndPos.line).length == lastEndPos.ch)
  5467. found = cursor.find(prev);
  5468. }
  5469. }
  5470. if (!found) {
  5471. cursor = cm.getSearchCursor(query, (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0));
  5472. if (!cursor.find(prev)) {
  5473. return;
  5474. }
  5475. }
  5476. }
  5477. return cursor.from();
  5478. });
  5479. }
  5480. function findNextFromAndToInclusive(cm, prev, query, repeat, vim) {
  5481. if (repeat === undefined) {
  5482. repeat = 1;
  5483. }
  5484. return cm.operation(function () {
  5485. var pos = cm.getCursor();
  5486. var cursor = cm.getSearchCursor(query, pos);
  5487. var found = cursor.find(!prev);
  5488. if (!vim.visualMode && found && cursorEqual(cursor.from(), pos)) {
  5489. cursor.find(!prev);
  5490. }
  5491. for (var i = 0; i < repeat; i++) {
  5492. found = cursor.find(prev);
  5493. if (!found) {
  5494. cursor = cm.getSearchCursor(query, (prev) ? new Pos(cm.lastLine()) : new Pos(cm.firstLine(), 0));
  5495. if (!cursor.find(prev)) {
  5496. return;
  5497. }
  5498. }
  5499. }
  5500. return [cursor.from(), cursor.to()];
  5501. });
  5502. }
  5503. function clearSearchHighlight(cm) {
  5504. var state = getSearchState(cm);
  5505. if (state.highlightTimeout) {
  5506. clearTimeout(state.highlightTimeout);
  5507. state.highlightTimeout = null;
  5508. }
  5509. cm.removeOverlay(getSearchState(cm).getOverlay());
  5510. state.setOverlay(null);
  5511. if (state.getScrollbarAnnotate()) {
  5512. state.getScrollbarAnnotate().clear();
  5513. state.setScrollbarAnnotate(null);
  5514. }
  5515. }
  5516. function isInRange(pos, start, end) {
  5517. if (typeof pos != 'number') {
  5518. pos = pos.line;
  5519. }
  5520. if (start instanceof Array) {
  5521. return inArray(pos, start);
  5522. }
  5523. else {
  5524. if (typeof end == 'number') {
  5525. return (pos >= start && pos <= end);
  5526. }
  5527. else {
  5528. return pos == start;
  5529. }
  5530. }
  5531. }
  5532. function getUserVisibleLines(cm) {
  5533. var renderer = cm.ace.renderer;
  5534. return {
  5535. top: renderer.getFirstFullyVisibleRow(),
  5536. bottom: renderer.getLastFullyVisibleRow()
  5537. };
  5538. }
  5539. function getMarkPos(cm, vim, markName) {
  5540. if (markName == '\'' || markName == '`') {
  5541. return vimGlobalState.jumpList.find(cm, -1) || new Pos(0, 0);
  5542. }
  5543. else if (markName == '.') {
  5544. return getLastEditPos(cm);
  5545. }
  5546. var mark = vim.marks[markName];
  5547. return mark && mark.find();
  5548. }
  5549. function getLastEditPos(cm) {
  5550. if (cm.getLastEditEnd) {
  5551. return cm.getLastEditEnd();
  5552. }
  5553. var done = cm.doc.history.done;
  5554. for (var i = done.length; i--;) {
  5555. if (done[i].changes) {
  5556. return copyCursor(done[i].changes[0].to);
  5557. }
  5558. }
  5559. }
  5560. var ExCommandDispatcher = function () {
  5561. this.buildCommandMap_();
  5562. };
  5563. ExCommandDispatcher.prototype = {
  5564. processCommand: function (cm, input, opt_params) {
  5565. var that = this;
  5566. cm.operation(function () {
  5567. cm.curOp.isVimOp = true;
  5568. that._processCommand(cm, input, opt_params);
  5569. });
  5570. },
  5571. _processCommand: function (cm, input, opt_params) {
  5572. var vim = cm.state.vim;
  5573. var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
  5574. var previousCommand = commandHistoryRegister.toString();
  5575. var inputStream = new CodeMirror.StringStream(input);
  5576. commandHistoryRegister.setText(input);
  5577. var params = opt_params || {};
  5578. params.input = input;
  5579. try {
  5580. this.parseInput_(cm, inputStream, params);
  5581. }
  5582. catch (e) {
  5583. showConfirm(cm, e.toString());
  5584. throw e;
  5585. }
  5586. if (vim.visualMode) {
  5587. exitVisualMode(cm);
  5588. }
  5589. var command;
  5590. var commandName;
  5591. if (!params.commandName) {
  5592. if (params.line !== undefined) {
  5593. commandName = 'move';
  5594. }
  5595. }
  5596. else {
  5597. command = this.matchCommand_(params.commandName);
  5598. if (command) {
  5599. commandName = command.name;
  5600. if (command.excludeFromCommandHistory) {
  5601. commandHistoryRegister.setText(previousCommand);
  5602. }
  5603. this.parseCommandArgs_(inputStream, params, command);
  5604. if (command.type == 'exToKey') {
  5605. doKeyToKey(cm, command.toKeys, command);
  5606. return;
  5607. }
  5608. else if (command.type == 'exToEx') {
  5609. this.processCommand(cm, command.toInput);
  5610. return;
  5611. }
  5612. }
  5613. }
  5614. if (!commandName) {
  5615. showConfirm(cm, 'Not an editor command ":' + input + '"');
  5616. return;
  5617. }
  5618. try {
  5619. exCommands[commandName](cm, params);
  5620. if ((!command || !command.possiblyAsync) && params.callback) {
  5621. params.callback();
  5622. }
  5623. }
  5624. catch (e) {
  5625. showConfirm(cm, e.toString());
  5626. throw e;
  5627. }
  5628. },
  5629. parseInput_: function (cm, inputStream, result) {
  5630. inputStream.eatWhile(':');
  5631. if (inputStream.eat('%')) {
  5632. result.line = cm.firstLine();
  5633. result.lineEnd = cm.lastLine();
  5634. }
  5635. else {
  5636. result.line = this.parseLineSpec_(cm, inputStream);
  5637. if (result.line !== undefined && inputStream.eat(',')) {
  5638. result.lineEnd = this.parseLineSpec_(cm, inputStream);
  5639. }
  5640. }
  5641. if (result.line == undefined) {
  5642. if (cm.state.vim.visualMode) {
  5643. var pos = getMarkPos(cm, cm.state.vim, '<');
  5644. result.selectionLine = pos && pos.line;
  5645. pos = getMarkPos(cm, cm.state.vim, '>');
  5646. result.selectionLineEnd = pos && pos.line;
  5647. }
  5648. else {
  5649. result.selectionLine = cm.getCursor().line;
  5650. }
  5651. }
  5652. else {
  5653. result.selectionLine = result.line;
  5654. result.selectionLineEnd = result.lineEnd;
  5655. }
  5656. var commandMatch = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/);
  5657. if (commandMatch) {
  5658. result.commandName = commandMatch[1];
  5659. }
  5660. else {
  5661. result.commandName = inputStream.match(/.*/)[0];
  5662. }
  5663. return result;
  5664. },
  5665. parseLineSpec_: function (cm, inputStream) {
  5666. var numberMatch = inputStream.match(/^(\d+)/);
  5667. if (numberMatch) {
  5668. return parseInt(numberMatch[1], 10) - 1;
  5669. }
  5670. switch (inputStream.next()) {
  5671. case '.':
  5672. return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
  5673. case '$':
  5674. return this.parseLineSpecOffset_(inputStream, cm.lastLine());
  5675. case '\'':
  5676. var markName = inputStream.next();
  5677. var markPos = getMarkPos(cm, cm.state.vim, markName);
  5678. if (!markPos)
  5679. throw new Error('Mark not set');
  5680. return this.parseLineSpecOffset_(inputStream, markPos.line);
  5681. case '-':
  5682. case '+':
  5683. inputStream.backUp(1);
  5684. return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
  5685. default:
  5686. inputStream.backUp(1);
  5687. return undefined;
  5688. }
  5689. },
  5690. parseLineSpecOffset_: function (inputStream, line) {
  5691. var offsetMatch = inputStream.match(/^([+-])?(\d+)/);
  5692. if (offsetMatch) {
  5693. var offset = parseInt(offsetMatch[2], 10);
  5694. if (offsetMatch[1] == "-") {
  5695. line -= offset;
  5696. }
  5697. else {
  5698. line += offset;
  5699. }
  5700. }
  5701. return line;
  5702. },
  5703. parseCommandArgs_: function (inputStream, params, command) {
  5704. if (inputStream.eol()) {
  5705. return;
  5706. }
  5707. params.argString = inputStream.match(/.*/)[0];
  5708. var delim = command.argDelimiter || /\s+/;
  5709. var args = trim(params.argString).split(delim);
  5710. if (args.length && args[0]) {
  5711. params.args = args;
  5712. }
  5713. },
  5714. matchCommand_: function (commandName) {
  5715. for (var i = commandName.length; i > 0; i--) {
  5716. var prefix = commandName.substring(0, i);
  5717. if (this.commandMap_[prefix]) {
  5718. var command = this.commandMap_[prefix];
  5719. if (command.name.indexOf(commandName) === 0) {
  5720. return command;
  5721. }
  5722. }
  5723. }
  5724. return null;
  5725. },
  5726. buildCommandMap_: function () {
  5727. this.commandMap_ = {};
  5728. for (var i = 0; i < defaultExCommandMap.length; i++) {
  5729. var command = defaultExCommandMap[i];
  5730. var key = command.shortName || command.name;
  5731. this.commandMap_[key] = command;
  5732. }
  5733. },
  5734. map: function (lhs, rhs, ctx, noremap) {
  5735. if (lhs != ':' && lhs.charAt(0) == ':') {
  5736. if (ctx) {
  5737. throw Error('Mode not supported for ex mappings');
  5738. }
  5739. var commandName = lhs.substring(1);
  5740. if (rhs != ':' && rhs.charAt(0) == ':') {
  5741. this.commandMap_[commandName] = {
  5742. name: commandName,
  5743. type: 'exToEx',
  5744. toInput: rhs.substring(1),
  5745. user: true
  5746. };
  5747. }
  5748. else {
  5749. this.commandMap_[commandName] = {
  5750. name: commandName,
  5751. type: 'exToKey',
  5752. toKeys: rhs,
  5753. user: true
  5754. };
  5755. }
  5756. }
  5757. else {
  5758. var mapping = {
  5759. keys: lhs,
  5760. type: 'keyToKey',
  5761. toKeys: rhs,
  5762. noremap: !!noremap
  5763. };
  5764. if (ctx) {
  5765. mapping.context = ctx;
  5766. }
  5767. defaultKeymap.unshift(mapping);
  5768. }
  5769. },
  5770. unmap: function (lhs, ctx) {
  5771. if (lhs != ':' && lhs.charAt(0) == ':') {
  5772. if (ctx) {
  5773. throw Error('Mode not supported for ex mappings');
  5774. }
  5775. var commandName = lhs.substring(1);
  5776. if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
  5777. delete this.commandMap_[commandName];
  5778. return true;
  5779. }
  5780. }
  5781. else {
  5782. var keys = lhs;
  5783. for (var i = 0; i < defaultKeymap.length; i++) {
  5784. if (keys == defaultKeymap[i].keys
  5785. && defaultKeymap[i].context === ctx) {
  5786. defaultKeymap.splice(i, 1);
  5787. return true;
  5788. }
  5789. }
  5790. }
  5791. }
  5792. };
  5793. var exCommands = {
  5794. colorscheme: function (cm, params) {
  5795. if (!params.args || params.args.length < 1) {
  5796. showConfirm(cm, cm.getOption('theme'));
  5797. return;
  5798. }
  5799. cm.setOption('theme', params.args[0]);
  5800. },
  5801. map: function (cm, params, ctx, defaultOnly) {
  5802. var mapArgs = params.args;
  5803. if (!mapArgs || mapArgs.length < 2) {
  5804. if (cm) {
  5805. showConfirm(cm, 'Invalid mapping: ' + params.input);
  5806. }
  5807. return;
  5808. }
  5809. exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx, defaultOnly);
  5810. },
  5811. imap: function (cm, params) { this.map(cm, params, 'insert'); },
  5812. nmap: function (cm, params) { this.map(cm, params, 'normal'); },
  5813. vmap: function (cm, params) { this.map(cm, params, 'visual'); },
  5814. omap: function (cm, params) { this.map(cm, params, 'operatorPending'); },
  5815. noremap: function (cm, params) { this.map(cm, params, undefined, true); },
  5816. inoremap: function (cm, params) { this.map(cm, params, 'insert', true); },
  5817. nnoremap: function (cm, params) { this.map(cm, params, 'normal', true); },
  5818. vnoremap: function (cm, params) { this.map(cm, params, 'visual', true); },
  5819. onoremap: function (cm, params) { this.map(cm, params, 'operatorPending', true); },
  5820. unmap: function (cm, params, ctx) {
  5821. var mapArgs = params.args;
  5822. if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) {
  5823. if (cm) {
  5824. showConfirm(cm, 'No such mapping: ' + params.input);
  5825. }
  5826. }
  5827. },
  5828. mapclear: function (cm, params) { vimApi.mapclear(); },
  5829. imapclear: function (cm, params) { vimApi.mapclear('insert'); },
  5830. nmapclear: function (cm, params) { vimApi.mapclear('normal'); },
  5831. vmapclear: function (cm, params) { vimApi.mapclear('visual'); },
  5832. omapclear: function (cm, params) { vimApi.mapclear('operatorPending'); },
  5833. move: function (cm, params) {
  5834. commandDispatcher.processCommand(cm, cm.state.vim, {
  5835. type: 'motion',
  5836. motion: 'moveToLineOrEdgeOfDocument',
  5837. motionArgs: { forward: false, explicitRepeat: true,
  5838. linewise: true },
  5839. repeatOverride: params.line + 1
  5840. });
  5841. },
  5842. set: function (cm, params) {
  5843. var setArgs = params.args;
  5844. var setCfg = params.setCfg || {};
  5845. if (!setArgs || setArgs.length < 1) {
  5846. if (cm) {
  5847. showConfirm(cm, 'Invalid mapping: ' + params.input);
  5848. }
  5849. return;
  5850. }
  5851. var expr = setArgs[0].split('=');
  5852. var optionName = expr[0];
  5853. var value = expr[1];
  5854. var forceGet = false;
  5855. var forceToggle = false;
  5856. if (optionName.charAt(optionName.length - 1) == '?') {
  5857. if (value) {
  5858. throw Error('Trailing characters: ' + params.argString);
  5859. }
  5860. optionName = optionName.substring(0, optionName.length - 1);
  5861. forceGet = true;
  5862. }
  5863. else if (optionName.charAt(optionName.length - 1) == '!') {
  5864. optionName = optionName.substring(0, optionName.length - 1);
  5865. forceToggle = true;
  5866. }
  5867. if (value === undefined && optionName.substring(0, 2) == 'no') {
  5868. optionName = optionName.substring(2);
  5869. value = false;
  5870. }
  5871. var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
  5872. if (optionIsBoolean) {
  5873. if (forceToggle) {
  5874. value = !getOption(optionName, cm, setCfg);
  5875. }
  5876. else if (value == undefined) {
  5877. value = true;
  5878. }
  5879. }
  5880. if (!optionIsBoolean && value === undefined || forceGet) {
  5881. var oldValue = getOption(optionName, cm, setCfg);
  5882. if (oldValue instanceof Error) {
  5883. showConfirm(cm, oldValue.message);
  5884. }
  5885. else if (oldValue === true || oldValue === false) {
  5886. showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
  5887. }
  5888. else {
  5889. showConfirm(cm, ' ' + optionName + '=' + oldValue);
  5890. }
  5891. }
  5892. else {
  5893. var setOptionReturn = setOption(optionName, value, cm, setCfg);
  5894. if (setOptionReturn instanceof Error) {
  5895. showConfirm(cm, setOptionReturn.message);
  5896. }
  5897. }
  5898. },
  5899. setlocal: function (cm, params) {
  5900. params.setCfg = { scope: 'local' };
  5901. this.set(cm, params);
  5902. },
  5903. setglobal: function (cm, params) {
  5904. params.setCfg = { scope: 'global' };
  5905. this.set(cm, params);
  5906. },
  5907. registers: function (cm, params) {
  5908. var regArgs = params.args;
  5909. var registers = vimGlobalState.registerController.registers;
  5910. var regInfo = '----------Registers----------\n\n';
  5911. if (!regArgs) {
  5912. for (var registerName in registers) {
  5913. var text = registers[registerName].toString();
  5914. if (text.length) {
  5915. regInfo += '"' + registerName + ' ' + text + '\n';
  5916. }
  5917. }
  5918. }
  5919. else {
  5920. var registerName;
  5921. regArgs = regArgs.join('');
  5922. for (var i = 0; i < regArgs.length; i++) {
  5923. registerName = regArgs.charAt(i);
  5924. if (!vimGlobalState.registerController.isValidRegister(registerName)) {
  5925. continue;
  5926. }
  5927. var register = registers[registerName] || new Register();
  5928. regInfo += '"' + registerName + ' ' + register.toString() + '\n';
  5929. }
  5930. }
  5931. showConfirm(cm, regInfo);
  5932. },
  5933. sort: function (cm, params) {
  5934. var reverse, ignoreCase, unique, number, pattern;
  5935. function parseArgs() {
  5936. if (params.argString) {
  5937. var args = new CodeMirror.StringStream(params.argString);
  5938. if (args.eat('!')) {
  5939. reverse = true;
  5940. }
  5941. if (args.eol()) {
  5942. return;
  5943. }
  5944. if (!args.eatSpace()) {
  5945. return 'Invalid arguments';
  5946. }
  5947. var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/);
  5948. if (!opts && !args.eol()) {
  5949. return 'Invalid arguments';
  5950. }
  5951. if (opts[1]) {
  5952. ignoreCase = opts[1].indexOf('i') != -1;
  5953. unique = opts[1].indexOf('u') != -1;
  5954. var decimal = opts[1].indexOf('d') != -1 || opts[1].indexOf('n') != -1 && 1;
  5955. var hex = opts[1].indexOf('x') != -1 && 1;
  5956. var octal = opts[1].indexOf('o') != -1 && 1;
  5957. if (decimal + hex + octal > 1) {
  5958. return 'Invalid arguments';
  5959. }
  5960. number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
  5961. }
  5962. if (opts[2]) {
  5963. pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? 'i' : '');
  5964. }
  5965. }
  5966. }
  5967. var err = parseArgs();
  5968. if (err) {
  5969. showConfirm(cm, err + ': ' + params.argString);
  5970. return;
  5971. }
  5972. var lineStart = params.line || cm.firstLine();
  5973. var lineEnd = params.lineEnd || params.line || cm.lastLine();
  5974. if (lineStart == lineEnd) {
  5975. return;
  5976. }
  5977. var curStart = new Pos(lineStart, 0);
  5978. var curEnd = new Pos(lineEnd, lineLength(cm, lineEnd));
  5979. var text = cm.getRange(curStart, curEnd).split('\n');
  5980. var numberRegex = pattern ? pattern :
  5981. (number == 'decimal') ? /(-?)([\d]+)/ :
  5982. (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :
  5983. (number == 'octal') ? /([0-7]+)/ : null;
  5984. var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;
  5985. var numPart = [], textPart = [];
  5986. if (number || pattern) {
  5987. for (var i = 0; i < text.length; i++) {
  5988. var matchPart = pattern ? text[i].match(pattern) : null;
  5989. if (matchPart && matchPart[0] != '') {
  5990. numPart.push(matchPart);
  5991. }
  5992. else if (!pattern && numberRegex.exec(text[i])) {
  5993. numPart.push(text[i]);
  5994. }
  5995. else {
  5996. textPart.push(text[i]);
  5997. }
  5998. }
  5999. }
  6000. else {
  6001. textPart = text;
  6002. }
  6003. function compareFn(a, b) {
  6004. if (reverse) {
  6005. var tmp;
  6006. tmp = a;
  6007. a = b;
  6008. b = tmp;
  6009. }
  6010. if (ignoreCase) {
  6011. a = a.toLowerCase();
  6012. b = b.toLowerCase();
  6013. }
  6014. var anum = number && numberRegex.exec(a);
  6015. var bnum = number && numberRegex.exec(b);
  6016. if (!anum) {
  6017. return a < b ? -1 : 1;
  6018. }
  6019. anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);
  6020. bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);
  6021. return anum - bnum;
  6022. }
  6023. function comparePatternFn(a, b) {
  6024. if (reverse) {
  6025. var tmp;
  6026. tmp = a;
  6027. a = b;
  6028. b = tmp;
  6029. }
  6030. if (ignoreCase) {
  6031. a[0] = a[0].toLowerCase();
  6032. b[0] = b[0].toLowerCase();
  6033. }
  6034. return (a[0] < b[0]) ? -1 : 1;
  6035. }
  6036. numPart.sort(pattern ? comparePatternFn : compareFn);
  6037. if (pattern) {
  6038. for (var i = 0; i < numPart.length; i++) {
  6039. numPart[i] = numPart[i].input;
  6040. }
  6041. }
  6042. else if (!number) {
  6043. textPart.sort(compareFn);
  6044. }
  6045. text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);
  6046. if (unique) { // Remove duplicate lines
  6047. var textOld = text;
  6048. var lastLine;
  6049. text = [];
  6050. for (var i = 0; i < textOld.length; i++) {
  6051. if (textOld[i] != lastLine) {
  6052. text.push(textOld[i]);
  6053. }
  6054. lastLine = textOld[i];
  6055. }
  6056. }
  6057. cm.replaceRange(text.join('\n'), curStart, curEnd);
  6058. },
  6059. vglobal: function (cm, params) {
  6060. this.global(cm, params);
  6061. },
  6062. normal: function (cm, params) {
  6063. var argString = params.argString;
  6064. if (argString && argString[0] == '!') {
  6065. argString = argString.slice(1);
  6066. noremap = true;
  6067. }
  6068. argString = argString.trimStart();
  6069. if (!argString) {
  6070. showConfirm(cm, 'Argument is required.');
  6071. return;
  6072. }
  6073. var line = params.line;
  6074. if (typeof line == 'number') {
  6075. var lineEnd = isNaN(params.lineEnd) ? line : params.lineEnd;
  6076. for (var i = line; i <= lineEnd; i++) {
  6077. cm.setCursor(i, 0);
  6078. doKeyToKey(cm, params.argString.trimStart());
  6079. if (cm.state.vim.insertMode) {
  6080. exitInsertMode(cm, true);
  6081. }
  6082. }
  6083. }
  6084. else {
  6085. doKeyToKey(cm, params.argString.trimStart());
  6086. if (cm.state.vim.insertMode) {
  6087. exitInsertMode(cm, true);
  6088. }
  6089. }
  6090. },
  6091. global: function (cm, params) {
  6092. var argString = params.argString;
  6093. if (!argString) {
  6094. showConfirm(cm, 'Regular Expression missing from global');
  6095. return;
  6096. }
  6097. var inverted = params.commandName[0] === 'v';
  6098. if (argString[0] === '!' && params.commandName[0] === 'g') {
  6099. inverted = true;
  6100. argString = argString.slice(1);
  6101. }
  6102. var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
  6103. var lineEnd = params.lineEnd || params.line || cm.lastLine();
  6104. var tokens = splitBySlash(argString);
  6105. var regexPart = argString, cmd;
  6106. if (tokens.length) {
  6107. regexPart = tokens[0];
  6108. cmd = tokens.slice(1, tokens.length).join('/');
  6109. }
  6110. if (regexPart) {
  6111. try {
  6112. updateSearchQuery(cm, regexPart, true /** ignoreCase */, true /** smartCase */);
  6113. }
  6114. catch (e) {
  6115. showConfirm(cm, 'Invalid regex: ' + regexPart);
  6116. return;
  6117. }
  6118. }
  6119. var query = getSearchState(cm).getQuery();
  6120. var matchedLines = [];
  6121. for (var i = lineStart; i <= lineEnd; i++) {
  6122. var line = cm.getLine(i);
  6123. var matched = query.test(line);
  6124. if (matched !== inverted) {
  6125. matchedLines.push(cmd ? cm.getLineHandle(i) : line);
  6126. }
  6127. }
  6128. if (!cmd) {
  6129. showConfirm(cm, matchedLines.join('\n'));
  6130. return;
  6131. }
  6132. var index = 0;
  6133. var nextCommand = function () {
  6134. if (index < matchedLines.length) {
  6135. var lineHandle = matchedLines[index++];
  6136. var lineNum = cm.getLineNumber(lineHandle);
  6137. if (lineNum == null) {
  6138. nextCommand();
  6139. return;
  6140. }
  6141. var command = (lineNum + 1) + cmd;
  6142. exCommandDispatcher.processCommand(cm, command, {
  6143. callback: nextCommand
  6144. });
  6145. }
  6146. else if (cm.releaseLineHandles) {
  6147. cm.releaseLineHandles();
  6148. }
  6149. };
  6150. nextCommand();
  6151. },
  6152. substitute: function (cm, params) {
  6153. if (!cm.getSearchCursor) {
  6154. throw new Error('Search feature not available. Requires searchcursor.js or ' +
  6155. 'any other getSearchCursor implementation.');
  6156. }
  6157. var argString = params.argString;
  6158. var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
  6159. var regexPart, replacePart = '', trailing, flagsPart, count;
  6160. var confirm = false; // Whether to confirm each replace.
  6161. var global = false; // True to replace all instances on a line, false to replace only 1.
  6162. if (tokens.length) {
  6163. regexPart = tokens[0];
  6164. if (getOption('pcre') && regexPart !== '') {
  6165. regexPart = new RegExp(regexPart).source; //normalize not escaped characters
  6166. }
  6167. replacePart = tokens[1];
  6168. if (replacePart !== undefined) {
  6169. if (getOption('pcre')) {
  6170. replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g, "$1$$&"));
  6171. }
  6172. else {
  6173. replacePart = translateRegexReplace(replacePart);
  6174. }
  6175. vimGlobalState.lastSubstituteReplacePart = replacePart;
  6176. }
  6177. trailing = tokens[2] ? tokens[2].split(' ') : [];
  6178. }
  6179. else {
  6180. if (argString && argString.length) {
  6181. showConfirm(cm, 'Substitutions should be of the form ' +
  6182. ':s/pattern/replace/');
  6183. return;
  6184. }
  6185. }
  6186. if (trailing) {
  6187. flagsPart = trailing[0];
  6188. count = parseInt(trailing[1]);
  6189. if (flagsPart) {
  6190. if (flagsPart.indexOf('c') != -1) {
  6191. confirm = true;
  6192. }
  6193. if (flagsPart.indexOf('g') != -1) {
  6194. global = true;
  6195. }
  6196. if (getOption('pcre')) {
  6197. regexPart = regexPart + '/' + flagsPart;
  6198. }
  6199. else {
  6200. regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;
  6201. }
  6202. }
  6203. }
  6204. if (regexPart) {
  6205. try {
  6206. updateSearchQuery(cm, regexPart, true /** ignoreCase */, true /** smartCase */);
  6207. }
  6208. catch (e) {
  6209. showConfirm(cm, 'Invalid regex: ' + regexPart);
  6210. return;
  6211. }
  6212. }
  6213. replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
  6214. if (replacePart === undefined) {
  6215. showConfirm(cm, 'No previous substitute regular expression');
  6216. return;
  6217. }
  6218. var state = getSearchState(cm);
  6219. var query = state.getQuery();
  6220. var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
  6221. var lineEnd = params.lineEnd || lineStart;
  6222. if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {
  6223. lineEnd = Infinity;
  6224. }
  6225. if (count) {
  6226. lineStart = lineEnd;
  6227. lineEnd = lineStart + count - 1;
  6228. }
  6229. var startPos = clipCursorToContent(cm, new Pos(lineStart, 0));
  6230. var cursor = cm.getSearchCursor(query, startPos);
  6231. doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
  6232. },
  6233. startinsert: function (cm, params) {
  6234. doKeyToKey(cm, params.argString == '!' ? 'A' : 'i', {});
  6235. },
  6236. redo: CodeMirror.commands.redo,
  6237. undo: CodeMirror.commands.undo,
  6238. write: function (cm) {
  6239. if (CodeMirror.commands.save) {
  6240. CodeMirror.commands.save(cm);
  6241. }
  6242. else if (cm.save) {
  6243. cm.save();
  6244. }
  6245. },
  6246. nohlsearch: function (cm) {
  6247. clearSearchHighlight(cm);
  6248. },
  6249. yank: function (cm) {
  6250. var cur = copyCursor(cm.getCursor());
  6251. var line = cur.line;
  6252. var lineText = cm.getLine(line);
  6253. vimGlobalState.registerController.pushText('0', 'yank', lineText, true, true);
  6254. },
  6255. delete: function (cm, params) {
  6256. var line = params.selectionLine;
  6257. var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd;
  6258. operators.delete(cm, { linewise: true }, [
  6259. { anchor: new Pos(line, 0),
  6260. head: new Pos(lineEnd + 1, 0) }
  6261. ]);
  6262. },
  6263. join: function (cm, params) {
  6264. var line = params.selectionLine;
  6265. var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd;
  6266. cm.setCursor(new Pos(line, 0));
  6267. actions.joinLines(cm, { repeat: lineEnd - line }, cm.state.vim);
  6268. },
  6269. delmarks: function (cm, params) {
  6270. if (!params.argString || !trim(params.argString)) {
  6271. showConfirm(cm, 'Argument required');
  6272. return;
  6273. }
  6274. var state = cm.state.vim;
  6275. var stream = new CodeMirror.StringStream(trim(params.argString));
  6276. while (!stream.eol()) {
  6277. stream.eatSpace();
  6278. var count = stream.pos;
  6279. if (!stream.match(/[a-zA-Z]/, false)) {
  6280. showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
  6281. return;
  6282. }
  6283. var sym = stream.next();
  6284. if (stream.match('-', true)) {
  6285. if (!stream.match(/[a-zA-Z]/, false)) {
  6286. showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
  6287. return;
  6288. }
  6289. var startMark = sym;
  6290. var finishMark = stream.next();
  6291. if (isLowerCase(startMark) && isLowerCase(finishMark) ||
  6292. isUpperCase(startMark) && isUpperCase(finishMark)) {
  6293. var start = startMark.charCodeAt(0);
  6294. var finish = finishMark.charCodeAt(0);
  6295. if (start >= finish) {
  6296. showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
  6297. return;
  6298. }
  6299. for (var j = 0; j <= finish - start; j++) {
  6300. var mark = String.fromCharCode(start + j);
  6301. delete state.marks[mark];
  6302. }
  6303. }
  6304. else {
  6305. showConfirm(cm, 'Invalid argument: ' + startMark + '-');
  6306. return;
  6307. }
  6308. }
  6309. else {
  6310. delete state.marks[sym];
  6311. }
  6312. }
  6313. }
  6314. };
  6315. var exCommandDispatcher = new ExCommandDispatcher();
  6316. function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query, replaceWith, callback) {
  6317. cm.state.vim.exMode = true;
  6318. var done = false;
  6319. var lastPos, modifiedLineNumber, joined;
  6320. function replaceAll() {
  6321. cm.operation(function () {
  6322. while (!done) {
  6323. replace();
  6324. next();
  6325. }
  6326. stop();
  6327. });
  6328. }
  6329. function replace() {
  6330. var text = cm.getRange(searchCursor.from(), searchCursor.to());
  6331. var newText = text.replace(query, replaceWith);
  6332. var unmodifiedLineNumber = searchCursor.to().line;
  6333. searchCursor.replace(newText);
  6334. modifiedLineNumber = searchCursor.to().line;
  6335. lineEnd += modifiedLineNumber - unmodifiedLineNumber;
  6336. joined = modifiedLineNumber < unmodifiedLineNumber;
  6337. }
  6338. function findNextValidMatch() {
  6339. var lastMatchTo = lastPos && copyCursor(searchCursor.to());
  6340. var match = searchCursor.findNext();
  6341. if (match && !match[0] && lastMatchTo && cursorEqual(searchCursor.from(), lastMatchTo)) {
  6342. match = searchCursor.findNext();
  6343. }
  6344. return match;
  6345. }
  6346. function next() {
  6347. while (findNextValidMatch() &&
  6348. isInRange(searchCursor.from(), lineStart, lineEnd)) {
  6349. if (!global && searchCursor.from().line == modifiedLineNumber && !joined) {
  6350. continue;
  6351. }
  6352. cm.scrollIntoView(searchCursor.from(), 30);
  6353. cm.setSelection(searchCursor.from(), searchCursor.to());
  6354. lastPos = searchCursor.from();
  6355. done = false;
  6356. return;
  6357. }
  6358. done = true;
  6359. }
  6360. function stop(close) {
  6361. if (close) {
  6362. close();
  6363. }
  6364. cm.focus();
  6365. if (lastPos) {
  6366. cm.setCursor(lastPos);
  6367. var vim = cm.state.vim;
  6368. vim.exMode = false;
  6369. vim.lastHPos = vim.lastHSPos = lastPos.ch;
  6370. }
  6371. if (callback) {
  6372. callback();
  6373. }
  6374. }
  6375. function onPromptKeyDown(e, _value, close) {
  6376. CodeMirror.e_stop(e);
  6377. var keyName = vimKeyFromEvent(e);
  6378. switch (keyName) {
  6379. case 'y':
  6380. replace();
  6381. next();
  6382. break;
  6383. case 'n':
  6384. next();
  6385. break;
  6386. case 'a':
  6387. var savedCallback = callback;
  6388. callback = undefined;
  6389. cm.operation(replaceAll);
  6390. callback = savedCallback;
  6391. break;
  6392. case 'l':
  6393. replace();
  6394. case 'q':
  6395. case '<Esc>':
  6396. case '<C-c>':
  6397. case '<C-[>':
  6398. stop(close);
  6399. break;
  6400. }
  6401. if (done) {
  6402. stop(close);
  6403. }
  6404. return true;
  6405. }
  6406. next();
  6407. if (done) {
  6408. showConfirm(cm, 'No matches for ' + query.source);
  6409. return;
  6410. }
  6411. if (!confirm) {
  6412. replaceAll();
  6413. if (callback) {
  6414. callback();
  6415. }
  6416. return;
  6417. }
  6418. showPrompt(cm, {
  6419. prefix: dom('span', 'replace with ', dom('strong', replaceWith), ' (y/n/a/q/l)'),
  6420. onKeyDown: onPromptKeyDown
  6421. });
  6422. }
  6423. function exitInsertMode(cm, keepCursor) {
  6424. var vim = cm.state.vim;
  6425. var macroModeState = vimGlobalState.macroModeState;
  6426. var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
  6427. var isPlaying = macroModeState.isPlaying;
  6428. var lastChange = macroModeState.lastInsertModeChanges;
  6429. if (!isPlaying) {
  6430. cm.off('change', onChange);
  6431. if (vim.insertEnd)
  6432. vim.insertEnd.clear();
  6433. vim.insertEnd = null;
  6434. CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
  6435. }
  6436. if (!isPlaying && vim.insertModeRepeat > 1) {
  6437. repeatLastEdit(cm, vim, vim.insertModeRepeat - 1, true /** repeatForInsert */);
  6438. vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
  6439. }
  6440. delete vim.insertModeRepeat;
  6441. vim.insertMode = false;
  6442. if (!keepCursor) {
  6443. cm.setCursor(cm.getCursor().line, cm.getCursor().ch - 1);
  6444. }
  6445. cm.setOption('keyMap', 'vim');
  6446. cm.setOption('disableInput', true);
  6447. cm.toggleOverwrite(false); // exit replace mode if we were in it.
  6448. insertModeChangeRegister.setText(lastChange.changes.join(''));
  6449. CodeMirror.signal(cm, "vim-mode-change", { mode: "normal" });
  6450. if (macroModeState.isRecording) {
  6451. logInsertModeChange(macroModeState);
  6452. }
  6453. }
  6454. function _mapCommand(command) {
  6455. defaultKeymap.unshift(command);
  6456. }
  6457. function mapCommand(keys, type, name, args, extra) {
  6458. var command = { keys: keys, type: type };
  6459. command[type] = name;
  6460. command[type + "Args"] = args;
  6461. for (var key in extra)
  6462. command[key] = extra[key];
  6463. _mapCommand(command);
  6464. }
  6465. defineOption('insertModeEscKeysTimeout', 200, 'number');
  6466. function executeMacroRegister(cm, vim, macroModeState, registerName) {
  6467. var register = vimGlobalState.registerController.getRegister(registerName);
  6468. if (registerName == ':') {
  6469. if (register.keyBuffer[0]) {
  6470. exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);
  6471. }
  6472. macroModeState.isPlaying = false;
  6473. return;
  6474. }
  6475. var keyBuffer = register.keyBuffer;
  6476. var imc = 0;
  6477. macroModeState.isPlaying = true;
  6478. macroModeState.replaySearchQueries = register.searchQueries.slice(0);
  6479. for (var i = 0; i < keyBuffer.length; i++) {
  6480. var text = keyBuffer[i];
  6481. var match, key;
  6482. while (text) {
  6483. match = (/<\w+-.+?>|<\w+>|./).exec(text);
  6484. key = match[0];
  6485. text = text.substring(match.index + key.length);
  6486. vimApi.handleKey(cm, key, 'macro');
  6487. if (vim.insertMode) {
  6488. var changes = register.insertModeChanges[imc++].changes;
  6489. vimGlobalState.macroModeState.lastInsertModeChanges.changes =
  6490. changes;
  6491. repeatInsertModeChanges(cm, changes, 1);
  6492. exitInsertMode(cm);
  6493. }
  6494. }
  6495. }
  6496. macroModeState.isPlaying = false;
  6497. }
  6498. function logKey(macroModeState, key) {
  6499. if (macroModeState.isPlaying) {
  6500. return;
  6501. }
  6502. var registerName = macroModeState.latestRegister;
  6503. var register = vimGlobalState.registerController.getRegister(registerName);
  6504. if (register) {
  6505. register.pushText(key);
  6506. }
  6507. }
  6508. function logInsertModeChange(macroModeState) {
  6509. if (macroModeState.isPlaying) {
  6510. return;
  6511. }
  6512. var registerName = macroModeState.latestRegister;
  6513. var register = vimGlobalState.registerController.getRegister(registerName);
  6514. if (register && register.pushInsertModeChanges) {
  6515. register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
  6516. }
  6517. }
  6518. function logSearchQuery(macroModeState, query) {
  6519. if (macroModeState.isPlaying) {
  6520. return;
  6521. }
  6522. var registerName = macroModeState.latestRegister;
  6523. var register = vimGlobalState.registerController.getRegister(registerName);
  6524. if (register && register.pushSearchQuery) {
  6525. register.pushSearchQuery(query);
  6526. }
  6527. }
  6528. function onChange(cm, changeObj) {
  6529. var macroModeState = vimGlobalState.macroModeState;
  6530. var lastChange = macroModeState.lastInsertModeChanges;
  6531. if (!macroModeState.isPlaying) {
  6532. var vim = cm.state.vim;
  6533. while (changeObj) {
  6534. lastChange.expectCursorActivityForChange = true;
  6535. if (lastChange.ignoreCount > 1) {
  6536. lastChange.ignoreCount--;
  6537. }
  6538. else if (changeObj.origin == '+input' || changeObj.origin == 'paste'
  6539. || changeObj.origin === undefined /* only in testing */) {
  6540. var selectionCount = cm.listSelections().length;
  6541. if (selectionCount > 1)
  6542. lastChange.ignoreCount = selectionCount;
  6543. var text = changeObj.text.join('\n');
  6544. if (lastChange.maybeReset) {
  6545. lastChange.changes = [];
  6546. lastChange.maybeReset = false;
  6547. }
  6548. if (text) {
  6549. if (cm.state.overwrite && !/\n/.test(text)) {
  6550. lastChange.changes.push([text]);
  6551. }
  6552. else {
  6553. if (text.length > 1) {
  6554. var insertEnd = vim && vim.insertEnd && vim.insertEnd.find();
  6555. var cursor = cm.getCursor();
  6556. if (insertEnd && insertEnd.line == cursor.line) {
  6557. var offset = insertEnd.ch - cursor.ch;
  6558. if (offset > 0 && offset < text.length) {
  6559. lastChange.changes.push([text, offset]);
  6560. text = '';
  6561. }
  6562. }
  6563. }
  6564. if (text)
  6565. lastChange.changes.push(text);
  6566. }
  6567. }
  6568. }
  6569. changeObj = changeObj.next;
  6570. }
  6571. }
  6572. }
  6573. function onCursorActivity(cm) {
  6574. var vim = cm.state.vim;
  6575. if (vim.insertMode) {
  6576. var macroModeState = vimGlobalState.macroModeState;
  6577. if (macroModeState.isPlaying) {
  6578. return;
  6579. }
  6580. var lastChange = macroModeState.lastInsertModeChanges;
  6581. if (lastChange.expectCursorActivityForChange) {
  6582. lastChange.expectCursorActivityForChange = false;
  6583. }
  6584. else {
  6585. lastChange.maybeReset = true;
  6586. if (vim.insertEnd)
  6587. vim.insertEnd.clear();
  6588. vim.insertEnd = cm.setBookmark(cm.getCursor(), { insertLeft: true });
  6589. }
  6590. }
  6591. else if (!cm.curOp.isVimOp) {
  6592. handleExternalSelection(cm, vim);
  6593. }
  6594. }
  6595. function handleExternalSelection(cm, vim, keepHPos) {
  6596. var anchor = cm.getCursor('anchor');
  6597. var head = cm.getCursor('head');
  6598. if (vim.visualMode && !cm.somethingSelected()) {
  6599. exitVisualMode(cm, false);
  6600. }
  6601. else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
  6602. vim.visualMode = true;
  6603. vim.visualLine = false;
  6604. CodeMirror.signal(cm, "vim-mode-change", { mode: "visual" });
  6605. }
  6606. if (vim.visualMode) {
  6607. var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
  6608. var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
  6609. head = offsetCursor(head, 0, headOffset);
  6610. anchor = offsetCursor(anchor, 0, anchorOffset);
  6611. vim.sel = {
  6612. anchor: anchor,
  6613. head: head
  6614. };
  6615. updateMark(cm, vim, '<', cursorMin(head, anchor));
  6616. updateMark(cm, vim, '>', cursorMax(head, anchor));
  6617. }
  6618. else if (!vim.insertMode && !keepHPos) {
  6619. vim.lastHPos = cm.getCursor().ch;
  6620. }
  6621. }
  6622. function InsertModeKey(keyName, e) {
  6623. this.keyName = keyName;
  6624. this.key = e.key;
  6625. this.ctrlKey = e.ctrlKey;
  6626. this.altKey = e.altKey;
  6627. this.metaKey = e.metaKey;
  6628. this.shiftKey = e.shiftKey;
  6629. }
  6630. function onKeyEventTargetKeyDown(e) {
  6631. var macroModeState = vimGlobalState.macroModeState;
  6632. var lastChange = macroModeState.lastInsertModeChanges;
  6633. var keyName = CodeMirror.keyName ? CodeMirror.keyName(e) : e.key;
  6634. if (!keyName) {
  6635. return;
  6636. }
  6637. if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
  6638. if (lastChange.maybeReset) {
  6639. lastChange.changes = [];
  6640. lastChange.maybeReset = false;
  6641. }
  6642. lastChange.changes.push(new InsertModeKey(keyName, e));
  6643. }
  6644. }
  6645. function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
  6646. var macroModeState = vimGlobalState.macroModeState;
  6647. macroModeState.isPlaying = true;
  6648. var isAction = !!vim.lastEditActionCommand;
  6649. var cachedInputState = vim.inputState;
  6650. function repeatCommand() {
  6651. if (isAction) {
  6652. commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);
  6653. }
  6654. else {
  6655. commandDispatcher.evalInput(cm, vim);
  6656. }
  6657. }
  6658. function repeatInsert(repeat) {
  6659. if (macroModeState.lastInsertModeChanges.changes.length > 0) {
  6660. repeat = !vim.lastEditActionCommand ? 1 : repeat;
  6661. var changeObject = macroModeState.lastInsertModeChanges;
  6662. repeatInsertModeChanges(cm, changeObject.changes, repeat);
  6663. }
  6664. }
  6665. vim.inputState = vim.lastEditInputState;
  6666. if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {
  6667. for (var i = 0; i < repeat; i++) {
  6668. repeatCommand();
  6669. repeatInsert(1);
  6670. }
  6671. }
  6672. else {
  6673. if (!repeatForInsert) {
  6674. repeatCommand();
  6675. }
  6676. repeatInsert(repeat);
  6677. }
  6678. vim.inputState = cachedInputState;
  6679. if (vim.insertMode && !repeatForInsert) {
  6680. exitInsertMode(cm);
  6681. }
  6682. macroModeState.isPlaying = false;
  6683. }
  6684. function sendCmKey(cm, key) {
  6685. CodeMirror.lookupKey(key, 'vim-insert', function keyHandler(binding) {
  6686. if (typeof binding == 'string') {
  6687. CodeMirror.commands[binding](cm);
  6688. }
  6689. else {
  6690. binding(cm);
  6691. }
  6692. return true;
  6693. });
  6694. }
  6695. function repeatInsertModeChanges(cm, changes, repeat) {
  6696. var head = cm.getCursor('head');
  6697. var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock;
  6698. if (visualBlock) {
  6699. selectForInsert(cm, head, visualBlock + 1);
  6700. repeat = cm.listSelections().length;
  6701. cm.setCursor(head);
  6702. }
  6703. for (var i = 0; i < repeat; i++) {
  6704. if (visualBlock) {
  6705. cm.setCursor(offsetCursor(head, i, 0));
  6706. }
  6707. for (var j = 0; j < changes.length; j++) {
  6708. var change = changes[j];
  6709. if (change instanceof InsertModeKey) {
  6710. sendCmKey(cm, change.keyName, change);
  6711. }
  6712. else if (typeof change == "string") {
  6713. cm.replaceSelection(change);
  6714. }
  6715. else {
  6716. var start = cm.getCursor();
  6717. var end = offsetCursor(start, 0, change[0].length - (change[1] || 0));
  6718. cm.replaceRange(change[0], start, change[1] ? start : end);
  6719. cm.setCursor(end);
  6720. }
  6721. }
  6722. }
  6723. if (visualBlock) {
  6724. cm.setCursor(offsetCursor(head, 0, 1));
  6725. }
  6726. }
  6727. CodeMirror.Vim = vimApi;
  6728. var specialKeyAce = { 'return': 'CR', backspace: 'BS', 'delete': 'Del', esc: 'Esc',
  6729. left: 'Left', right: 'Right', up: 'Up', down: 'Down', space: 'Space', insert: 'Ins',
  6730. home: 'Home', end: 'End', pageup: 'PageUp', pagedown: 'PageDown', enter: 'CR'
  6731. };
  6732. function lookupKey(hashId, key, e, vim) {
  6733. if (key.length > 1 && key[0] == "n") {
  6734. key = key.replace("numpad", "");
  6735. }
  6736. key = specialKeyAce[key] || key;
  6737. var name = '';
  6738. if (e.ctrlKey) {
  6739. name += 'C-';
  6740. }
  6741. if (e.altKey) {
  6742. name += 'A-';
  6743. }
  6744. if ((name || key.length > 1) && e.shiftKey) {
  6745. name += 'S-';
  6746. }
  6747. if (vim && !vim.expectLiteralNext && key.length == 1) {
  6748. if (langmap.keymap && key in langmap.keymap) {
  6749. if (langmap.remapCtrl !== false || !name)
  6750. key = langmap.keymap[key];
  6751. }
  6752. else if (key.charCodeAt(0) > 255) {
  6753. var code = e.code && e.code.slice(-1) || "";
  6754. if (!e.shiftKey)
  6755. code = code.toLowerCase();
  6756. if (code)
  6757. key = code;
  6758. }
  6759. }
  6760. name += key;
  6761. if (name.length > 1) {
  6762. name = '<' + name + '>';
  6763. }
  6764. return name;
  6765. }
  6766. var handleKey = vimApi.handleKey.bind(vimApi);
  6767. vimApi.handleKey = function (cm, key, origin) {
  6768. return cm.operation(function () {
  6769. return handleKey(cm, key, origin);
  6770. }, true);
  6771. };
  6772. function cloneVimState(state) {
  6773. var n = new state.constructor();
  6774. Object.keys(state).forEach(function (key) {
  6775. if (key == "insertEnd")
  6776. return;
  6777. var o = state[key];
  6778. if (Array.isArray(o))
  6779. o = o.slice();
  6780. else if (o && typeof o == "object" && o.constructor != Object)
  6781. o = cloneVimState(o);
  6782. n[key] = o;
  6783. });
  6784. if (state.sel) {
  6785. n.sel = {
  6786. head: state.sel.head && copyCursor(state.sel.head),
  6787. anchor: state.sel.anchor && copyCursor(state.sel.anchor)
  6788. };
  6789. }
  6790. return n;
  6791. }
  6792. function multiSelectHandleKey(cm, key, origin) {
  6793. var isHandled = false;
  6794. var vim = vimApi.maybeInitVimState_(cm);
  6795. var visualBlock = vim.visualBlock || vim.wasInVisualBlock;
  6796. var wasMultiselect = cm.ace.inMultiSelectMode;
  6797. if (vim.wasInVisualBlock && !wasMultiselect) {
  6798. vim.wasInVisualBlock = false;
  6799. }
  6800. else if (wasMultiselect && vim.visualBlock) {
  6801. vim.wasInVisualBlock = true;
  6802. }
  6803. if (key == '<Esc>' && !vim.insertMode && !vim.visualMode && wasMultiselect) {
  6804. cm.ace.exitMultiSelectMode();
  6805. }
  6806. else if (visualBlock || !wasMultiselect || cm.ace.inVirtualSelectionMode) {
  6807. isHandled = vimApi.handleKey(cm, key, origin);
  6808. }
  6809. else {
  6810. var old = cloneVimState(vim);
  6811. var changeQueueList = vim.inputState.changeQueueList || [];
  6812. cm.operation(function () {
  6813. cm.curOp.isVimOp = true;
  6814. var index = 0;
  6815. cm.ace.forEachSelection(function () {
  6816. var sel = cm.ace.selection;
  6817. cm.state.vim.lastHPos = sel.$desiredColumn == null ? sel.lead.column : sel.$desiredColumn;
  6818. cm.state.vim.inputState.changeQueue = changeQueueList[index];
  6819. var head = cm.getCursor("head");
  6820. var anchor = cm.getCursor("anchor");
  6821. var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
  6822. var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
  6823. head = offsetCursor(head, 0, headOffset);
  6824. anchor = offsetCursor(anchor, 0, anchorOffset);
  6825. cm.state.vim.sel.head = head;
  6826. cm.state.vim.sel.anchor = anchor;
  6827. isHandled = handleKey(cm, key, origin);
  6828. sel.$desiredColumn = cm.state.vim.lastHPos == -1 ? null : cm.state.vim.lastHPos;
  6829. if (cm.ace.inVirtualSelectionMode) {
  6830. changeQueueList[index] = cm.state.vim.inputState.changeQueue;
  6831. }
  6832. if (cm.virtualSelectionMode()) {
  6833. cm.state.vim = cloneVimState(old);
  6834. }
  6835. index++;
  6836. });
  6837. if (cm.curOp.cursorActivity && !isHandled)
  6838. cm.curOp.cursorActivity = false;
  6839. vim.status = cm.state.vim.status;
  6840. cm.state.vim = vim;
  6841. vim.inputState.changeQueueList = changeQueueList;
  6842. vim.inputState.changeQueue = null;
  6843. }, true);
  6844. }
  6845. if (isHandled && !vim.visualMode && !vim.insert && vim.visualMode != cm.somethingSelected()) {
  6846. handleExternalSelection(cm, vim, true);
  6847. }
  6848. return isHandled;
  6849. }
  6850. resetVimGlobalState();
  6851. exports.CodeMirror = CodeMirror;
  6852. var getVim = vimApi.maybeInitVimState_;
  6853. exports.handler = {
  6854. $id: "ace/keyboard/vim",
  6855. drawCursor: function (element, pixelPos, config, sel, session) {
  6856. var vim = this.state.vim || {};
  6857. var w = config.characterWidth;
  6858. var h = config.lineHeight;
  6859. var top = pixelPos.top;
  6860. var left = pixelPos.left;
  6861. if (!vim.insertMode) {
  6862. var isbackwards = !sel.cursor
  6863. ? session.selection.isBackwards() || session.selection.isEmpty()
  6864. : Range.comparePoints(sel.cursor, sel.start) <= 0;
  6865. if (!isbackwards && left > w)
  6866. left -= w;
  6867. }
  6868. if (!vim.insertMode && vim.status) {
  6869. h = h / 2;
  6870. top += h;
  6871. }
  6872. domLib.translate(element, left, top);
  6873. domLib.setStyle(element.style, "width", w + "px");
  6874. domLib.setStyle(element.style, "height", h + "px");
  6875. },
  6876. $getDirectionForHighlight: function (editor) {
  6877. var cm = editor.state.cm;
  6878. var vim = getVim(cm);
  6879. if (!vim.insertMode) {
  6880. return editor.session.selection.isBackwards() || editor.session.selection.isEmpty();
  6881. }
  6882. },
  6883. handleKeyboard: function (data, hashId, key, keyCode, e) {
  6884. var editor = data.editor;
  6885. var cm = editor.state.cm;
  6886. var vim = getVim(cm);
  6887. if (keyCode == -1)
  6888. return;
  6889. if (!vim.insertMode) {
  6890. if (hashId == -1) {
  6891. if (key.charCodeAt(0) > 0xFF) {
  6892. if (data.inputKey) {
  6893. key = data.inputKey;
  6894. if (key && data.inputHash == 4)
  6895. key = key.toUpperCase();
  6896. }
  6897. }
  6898. data.inputChar = key;
  6899. }
  6900. else if (hashId == 4 || hashId == 0) {
  6901. if (data.inputKey == key && data.inputHash == hashId && data.inputChar) {
  6902. key = data.inputChar;
  6903. hashId = -1;
  6904. }
  6905. else {
  6906. data.inputChar = null;
  6907. data.inputKey = key;
  6908. data.inputHash = hashId;
  6909. }
  6910. }
  6911. else {
  6912. data.inputChar = data.inputKey = null;
  6913. }
  6914. }
  6915. if (cm.state.overwrite && vim.insertMode && key == "backspace" && hashId == 0) {
  6916. return { command: "gotoleft" };
  6917. }
  6918. if (key == "c" && hashId == 1) { // key == "ctrl-c"
  6919. if (!useragent.isMac && editor.getCopyText()) {
  6920. editor.once("copy", function () {
  6921. if (vim.insertMode)
  6922. editor.selection.clearSelection();
  6923. else
  6924. cm.operation(function () { exitVisualMode(cm); });
  6925. });
  6926. return { command: "null", passEvent: true };
  6927. }
  6928. }
  6929. if (key == "esc" && !vim.insertMode && !vim.visualMode && !cm.ace.inMultiSelectMode) {
  6930. var searchState = getSearchState(cm);
  6931. var overlay = searchState.getOverlay();
  6932. if (overlay)
  6933. cm.removeOverlay(overlay);
  6934. }
  6935. if (hashId == -1 || hashId & 1 || hashId === 0 && key.length > 1) {
  6936. var insertMode = vim.insertMode;
  6937. var name = lookupKey(hashId, key, e || {}, vim);
  6938. if (vim.status == null)
  6939. vim.status = "";
  6940. var isHandled = multiSelectHandleKey(cm, name, 'user');
  6941. vim = getVim(cm); // may be changed by multiSelectHandleKey
  6942. if (isHandled && vim.status != null)
  6943. vim.status += name;
  6944. else if (vim.status == null)
  6945. vim.status = "";
  6946. cm._signal("changeStatus");
  6947. if (!isHandled && (hashId != -1 || insertMode))
  6948. return;
  6949. return { command: "null", passEvent: !isHandled };
  6950. }
  6951. },
  6952. attach: function (editor) {
  6953. if (!editor.state)
  6954. editor.state = {};
  6955. var cm = new CodeMirror(editor);
  6956. editor.state.cm = cm;
  6957. editor.$vimModeHandler = this;
  6958. enterVimMode(cm);
  6959. getVim(cm).status = null;
  6960. cm.on('vim-command-done', function () {
  6961. if (cm.virtualSelectionMode())
  6962. return;
  6963. getVim(cm).status = null;
  6964. cm.ace._signal("changeStatus");
  6965. cm.ace.session.markUndoGroup();
  6966. });
  6967. cm.on("changeStatus", function () {
  6968. cm.ace.renderer.updateCursor();
  6969. cm.ace._signal("changeStatus");
  6970. });
  6971. cm.on("vim-mode-change", function () {
  6972. if (cm.virtualSelectionMode())
  6973. return;
  6974. updateInputMode();
  6975. cm._signal("changeStatus");
  6976. });
  6977. function updateInputMode() {
  6978. var isIntsert = getVim(cm).insertMode;
  6979. cm.ace.renderer.setStyle("normal-mode", !isIntsert);
  6980. editor.textInput.setCommandMode(!isIntsert);
  6981. editor.renderer.$keepTextAreaAtCursor = isIntsert;
  6982. editor.renderer.$blockCursor = !isIntsert;
  6983. }
  6984. updateInputMode();
  6985. editor.renderer.$cursorLayer.drawCursor = this.drawCursor.bind(cm);
  6986. },
  6987. detach: function (editor) {
  6988. var cm = editor.state.cm;
  6989. leaveVimMode(cm);
  6990. cm.destroy();
  6991. editor.state.cm = null;
  6992. editor.$vimModeHandler = null;
  6993. editor.renderer.$cursorLayer.drawCursor = null;
  6994. editor.renderer.setStyle("normal-mode", false);
  6995. editor.textInput.setCommandMode(false);
  6996. editor.renderer.$keepTextAreaAtCursor = true;
  6997. },
  6998. getStatusText: function (editor) {
  6999. var cm = editor.state.cm;
  7000. var vim = getVim(cm);
  7001. if (vim.insertMode)
  7002. return "INSERT";
  7003. var status = "";
  7004. if (vim.visualMode) {
  7005. status += "VISUAL";
  7006. if (vim.visualLine)
  7007. status += " LINE";
  7008. if (vim.visualBlock)
  7009. status += " BLOCK";
  7010. }
  7011. if (vim.status)
  7012. status += (status ? " " : "") + vim.status;
  7013. return status;
  7014. }
  7015. };
  7016. vimApi.defineOption({
  7017. name: "wrap",
  7018. set: function (value, cm) {
  7019. if (cm) {
  7020. cm.ace.setOption("wrap", value);
  7021. }
  7022. },
  7023. type: "boolean"
  7024. }, false);
  7025. vimApi.defineEx('write', 'w', function () {
  7026. console.log(':write is not implemented');
  7027. });
  7028. defaultKeymap.push({ keys: 'zc', type: 'action', action: 'fold', actionArgs: { open: false } }, { keys: 'zC', type: 'action', action: 'fold', actionArgs: { open: false, all: true } }, { keys: 'zo', type: 'action', action: 'fold', actionArgs: { open: true } }, { keys: 'zO', type: 'action', action: 'fold', actionArgs: { open: true, all: true } }, { keys: 'za', type: 'action', action: 'fold', actionArgs: { toggle: true } }, { keys: 'zA', type: 'action', action: 'fold', actionArgs: { toggle: true, all: true } }, { keys: 'zf', type: 'action', action: 'fold', actionArgs: { open: true, all: true } }, { keys: 'zd', type: 'action', action: 'fold', actionArgs: { open: true, all: true } }, { keys: '<C-A-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAbove" } }, { keys: '<C-A-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelow" } }, { keys: '<C-A-S-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAboveSkipCurrent" } }, { keys: '<C-A-S-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelowSkipCurrent" } }, { keys: '<C-A-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreBefore" } }, { keys: '<C-A-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectMoreAfter" } }, { keys: '<C-A-S-h>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextBefore" } }, { keys: '<C-A-S-l>', type: 'action', action: 'aceCommand', actionArgs: { name: "selectNextAfter" } });
  7029. defaultKeymap.push({
  7030. keys: 'gq',
  7031. type: 'operator',
  7032. operator: 'hardWrap'
  7033. });
  7034. vimApi.defineOperator("hardWrap", function (cm, operatorArgs, ranges, oldAnchor, newHead) {
  7035. var anchor = ranges[0].anchor.line;
  7036. var head = ranges[0].head.line;
  7037. if (operatorArgs.linewise)
  7038. head--;
  7039. hardWrap(cm.ace, { startRow: anchor, endRow: head });
  7040. return Pos(head, 0);
  7041. });
  7042. defineOption('textwidth', undefined, 'number', ['tw'], function (width, cm) {
  7043. if (cm === undefined) {
  7044. return;
  7045. }
  7046. if (width === undefined) {
  7047. var value = cm.ace.getOption('printMarginColumn');
  7048. return value;
  7049. }
  7050. else {
  7051. var column = Math.round(width);
  7052. if (column > 1) {
  7053. cm.ace.setOption('printMarginColumn', column);
  7054. }
  7055. }
  7056. });
  7057. actions.aceCommand = function (cm, actionArgs, vim) {
  7058. cm.vimCmd = actionArgs;
  7059. if (cm.ace.inVirtualSelectionMode)
  7060. cm.ace.on("beforeEndOperation", delayedExecAceCommand);
  7061. else
  7062. delayedExecAceCommand(null, cm.ace);
  7063. };
  7064. function delayedExecAceCommand(op, ace) {
  7065. ace.off("beforeEndOperation", delayedExecAceCommand);
  7066. var cmd = ace.state.cm.vimCmd;
  7067. if (cmd) {
  7068. ace.execCommand(cmd.exec ? cmd : cmd.name, cmd.args);
  7069. }
  7070. ace.curOp = ace.prevOp;
  7071. }
  7072. actions.fold = function (cm, actionArgs, vim) {
  7073. cm.ace.execCommand(['toggleFoldWidget', 'toggleFoldWidget', 'foldOther', 'unfoldall'
  7074. ][(actionArgs.all ? 2 : 0) + (actionArgs.open ? 1 : 0)]);
  7075. };
  7076. defaultKeymapLength = defaultKeymap.length; // ace_patch
  7077. exports.handler.defaultKeymap = defaultKeymap;
  7078. exports.handler.actions = actions;
  7079. exports.Vim = vimApi;
  7080. }); (function() {
  7081. ace.require(["ace/keyboard/vim"], function(m) {
  7082. if (typeof module == "object" && typeof exports == "object" && module) {
  7083. module.exports = m;
  7084. }
  7085. });
  7086. })();